Skip to content

Commit cf7eadb

Browse files
authored
Fix code highlighting in copilot (microsoft#2243)
This improves the syntax highlighting for Q# (and Python by way of improved styling) in the Copilot pane. It now closely matches the syntax highlighting in VS Code in the 'Modern' (default) themes. Example in dark mode <img width="683" alt="To entangle 3 qubits, you can create a Greenberger-Horne-Zeilinger (GHZ) state" src="https://github.com/user-attachments/assets/ba697bb2-dc3e-4c52-a2d9-893faf916dd5" /> Same in light mode <img width="683" alt="To entangle 3 qubits, you can create a Greenberger-Horne-Zeilinger (GHZ) state" src="https://github.com/user-attachments/assets/84de7a28-4d87-4362-98ba-929df0015327" />
1 parent be4d825 commit cf7eadb

File tree

5 files changed

+276
-125
lines changed

5 files changed

+276
-125
lines changed

Diff for: vscode/src/copilot/webview/copilot.css

+108
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,111 @@ pre code {
171171
.hljs-copy-button {
172172
font-family: system-ui;
173173
}
174+
175+
/* The below are styles to set the highlight.js colors to match the 'Modern' light and dark themes in vscode */
176+
/* Default colors, or theme set explicitly to light */
177+
:root,
178+
[data-vscode-theme-kind="vscode-light"],
179+
[data-vscode-theme-kind="vscode-high-contrast-light"] {
180+
--qs-code-background: #ffffff;
181+
--qs-code-default: #001080;
182+
--qs-code-keyword: #af00db;
183+
--qs-code-declare: #0000ff;
184+
--qs-code-punctuation: #af00db;
185+
--qs-code-operator: #af00db;
186+
--qs-code-comments: #008000;
187+
--qs-code-strings: #a31515;
188+
--qs-code-numbers: #098658;
189+
--qs-code-calls: #795e26;
190+
}
191+
192+
/* Explicit dark theme set */
193+
[data-vscode-theme-kind="vscode-dark"],
194+
[data-vscode-theme-kind="vscode-high-contrast"] {
195+
--qs-code-background: #1f1f1f;
196+
--qs-code-default: #9cdcfe;
197+
--qs-code-keyword: #c586c0;
198+
--qs-code-declare: #569cd6;
199+
--qs-code-punctuation: #c586c0;
200+
--qs-code-operator: #c586c0;
201+
--qs-code-comments: #6a9955;
202+
--qs-code-strings: #ce9178;
203+
--qs-code-numbers: #b5cea8;
204+
--qs-code-calls: #dcdcaa;
205+
}
206+
207+
/* Code block styles for highlight.js */
208+
209+
pre code.hljs {
210+
display: block;
211+
overflow-x: auto;
212+
padding: 1em;
213+
}
214+
code.hljs {
215+
padding: 3px 5px;
216+
}
217+
218+
.hljs {
219+
color: var(--qs-code-default);
220+
background: var(--qs-code-background);
221+
}
222+
.hljs-keyword,
223+
.hljs-selector-tag,
224+
.hljs-literal,
225+
.hljs-section,
226+
.hljs-link {
227+
color: var(--qs-code-keyword);
228+
}
229+
.hljs-punctuation {
230+
color: var(--qs-code-punctuation);
231+
}
232+
.hljs-type,
233+
.hljs-declare {
234+
color: var(--qs-code-declare);
235+
}
236+
237+
.hljs-operator {
238+
color: var(--qs-code-operator);
239+
}
240+
241+
.hljs-title {
242+
color: var(--qs-code-calls);
243+
}
244+
245+
.hljs-number {
246+
color: var(--qs-code-numbers);
247+
}
248+
249+
.hljs-string,
250+
.hljs-name,
251+
.hljs-attribute,
252+
.hljs-symbol,
253+
.hljs-bullet,
254+
.hljs-built_in,
255+
.hljs-addition,
256+
.hljs-variable,
257+
.hljs-template-tag,
258+
.hljs-template-variable {
259+
color: var(--qs-code-strings);
260+
}
261+
.hljs-comment,
262+
.hljs-quote,
263+
.hljs-deletion,
264+
.hljs-meta {
265+
color: var(--qs-code-comments);
266+
}
267+
.hljs-keyword,
268+
.hljs-selector-tag,
269+
.hljs-literal,
270+
.hljs-title,
271+
.hljs-section,
272+
.hljs-doctag,
273+
.hljs-built_in,
274+
.hljs-type,
275+
.hljs-name,
276+
.hljs-strong {
277+
font-weight: normal;
278+
}
279+
.hljs-emphasis {
280+
font-style: italic;
281+
}

Diff for: vscode/src/copilot/webview/copilot.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
ShowPayload,
3030
ToolMessage,
3131
} from "./debugUi";
32-
import hlsjQsharp from "./hlsj-qsharp";
32+
import hlsjQsharp from "./hljs-qsharp";
3333
import { WebviewApi } from "vscode-webview";
3434

3535
const vscodeApi: WebviewApi<ChatElement[]> = acquireVsCodeApi();

Diff for: vscode/src/copilot/webview/hljs-qsharp.ts

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
import { HLJSApi, Mode, type Language } from "highlight.js";
5+
6+
// NOTE: This has a bunch of differences from how we structure the language in <vscode/syntaxes/qsharp.tmLanguage.json>
7+
// We should align with that as much as possible eventually.
8+
9+
const KEYWORDS_GENERAL = [
10+
"use",
11+
"borrow",
12+
"mutable",
13+
"let",
14+
"set",
15+
"if",
16+
"elif",
17+
"else",
18+
"repeat",
19+
"until",
20+
"fixup",
21+
"for",
22+
"in",
23+
"while",
24+
"return",
25+
"fail",
26+
"within",
27+
"apply",
28+
];
29+
30+
const KEYWORDS_DECL = [
31+
"namespace",
32+
"open",
33+
"import",
34+
"export",
35+
"as",
36+
"internal",
37+
"newtype",
38+
"struct",
39+
"operation",
40+
"function",
41+
"new",
42+
"body",
43+
"adjoint",
44+
"Adjoint",
45+
"controlled",
46+
"Controlled",
47+
"self",
48+
"auto",
49+
"distribute",
50+
"invert",
51+
"intrinsic",
52+
];
53+
54+
const KEYWORDS_TYPE = [
55+
"Int",
56+
"BigInt",
57+
"Double",
58+
"Bool",
59+
"Qubit",
60+
"Pauli",
61+
"Result",
62+
"Range",
63+
"String",
64+
"Unit",
65+
"Ctl",
66+
"Adj",
67+
"is",
68+
];
69+
70+
const KEYWORDS_CONSTANTS = [
71+
"true",
72+
"false",
73+
"PauliI",
74+
"PauliX",
75+
"PauliY",
76+
"PauliZ",
77+
"One",
78+
"Zero",
79+
];
80+
81+
export default function (hljs: HLJSApi): Language {
82+
const QSHARP_KEYWORDS = {
83+
keyword: KEYWORDS_GENERAL,
84+
declare: KEYWORDS_DECL,
85+
literal: KEYWORDS_CONSTANTS,
86+
type: KEYWORDS_TYPE,
87+
};
88+
89+
const QSHARP_OPERATOR_MODE: Mode = {
90+
scope: "operator",
91+
match:
92+
"\\b(not|and|or)\\b|\\b(w/)|(=)|(!)|(<)|(>)|(\\+)|(-)|(\\*)|(\\/)|(\\^)|(%)|(\\|)|(\\&\\&\\&)|(\\~\\~\\~)|(\\.\\.\\.)|(\\.\\.)|(\\?)",
93+
};
94+
95+
const decimalDigits = "[0-9](_?[0-9])*";
96+
const frac = `\\.(${decimalDigits})`;
97+
const decimalInteger = `0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*`;
98+
const NUMBER = {
99+
className: "number",
100+
variants: [
101+
// DecimalLiteral
102+
{
103+
begin:
104+
`(\\b(${decimalInteger})((${frac})|\\.)?|(${frac}))` +
105+
`[eE][+-]?(${decimalDigits})\\b`,
106+
},
107+
{ begin: `\\b(${decimalInteger})\\b((${frac})\\b|\\.)?|(${frac})\\b` },
108+
109+
// DecimalBigIntegerLiteral
110+
{ begin: `\\b(0|[1-9](_?[0-9])*)L\\b` },
111+
112+
// NonDecimalIntegerLiteral
113+
{ begin: "\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*L?\\b" },
114+
{ begin: "\\b0[bB][0-1](_?[0-1])*L?\\b" },
115+
{ begin: "\\b0[oO][0-7](_?[0-7])*L?\\b" },
116+
],
117+
};
118+
119+
const QSHARP_CALL_MODE: Mode = {
120+
scope: "title.function",
121+
match: /\b[a-zA-Z_][a-zA-Z0-9_]*\s*(?=\()/,
122+
};
123+
124+
const QSHARP_PUNCTUATION_MODE = {
125+
match: /[;:(){},]/,
126+
className: "punctuation",
127+
};
128+
129+
const SUBST: Mode = {
130+
className: "subst",
131+
begin: /\{/,
132+
end: /\}/,
133+
keywords: QSHARP_KEYWORDS,
134+
contains: [
135+
hljs.QUOTE_STRING_MODE,
136+
QSHARP_OPERATOR_MODE,
137+
NUMBER,
138+
QSHARP_CALL_MODE,
139+
],
140+
};
141+
142+
const QSHARP_INTERP_STRING_MODE: Mode = {
143+
scope: "string",
144+
begin: /\$"/,
145+
end: /"/,
146+
contains: [hljs.BACKSLASH_ESCAPE, SUBST],
147+
};
148+
SUBST.contains?.push(QSHARP_INTERP_STRING_MODE);
149+
150+
const QSHARP_CONTAINS = [
151+
hljs.C_LINE_COMMENT_MODE,
152+
hljs.QUOTE_STRING_MODE,
153+
QSHARP_INTERP_STRING_MODE,
154+
QSHARP_OPERATOR_MODE,
155+
NUMBER,
156+
QSHARP_CALL_MODE,
157+
QSHARP_PUNCTUATION_MODE,
158+
];
159+
160+
return {
161+
name: "qsharp",
162+
case_insensitive: false,
163+
keywords: QSHARP_KEYWORDS,
164+
contains: QSHARP_CONTAINS,
165+
};
166+
}

0 commit comments

Comments
 (0)