1 import * as vscode from 'vscode';
2 import * as lc from 'vscode-languageclient';
3 import * as seedrandom_ from 'seedrandom';
4 const seedrandom = seedrandom_; // https://github.com/jvandemo/generator-angular2-library/issues/221#issuecomment-355945207
6 import { loadThemeColors, TextMateRuleSettings } from './load_theme_colors';
7 import * as scopesMapper from './scopes_mapper';
9 import { Ctx } from './ctx';
11 export function activateHighlighting(ctx: Ctx) {
12 const highlighter = new Highlighter(ctx);
14 ctx.client.onReady().then(() => {
15 ctx.client.onNotification(
16 'rust-analyzer/publishDecorations',
17 (params: PublishDecorationsParams) => {
18 if (!ctx.config.highlightingOn) return;
20 const targetEditor = vscode.window.visibleTextEditors.find(
22 const unescapedUri = unescape(
23 editor.document.uri.toString(),
25 // Unescaped URI looks like:
26 // file:///c:/Workspace/ra-test/src/main.rs
27 return unescapedUri === params.uri;
30 if (!targetEditor) return;
32 highlighter.setHighlights(targetEditor, params.decorations);
37 vscode.workspace.onDidChangeConfiguration(
38 _ => highlighter.removeHighlights(),
42 vscode.window.onDidChangeActiveTextEditor(
43 async (editor: vscode.TextEditor | undefined) => {
44 if (!editor || editor.document.languageId !== 'rust') return;
45 if (!ctx.config.highlightingOn) return;
47 const params: lc.TextDocumentIdentifier = {
48 uri: editor.document.uri.toString(),
50 const decorations = await ctx.sendRequestWithRetry<Decoration[]>(
51 'rust-analyzer/decorationsRequest',
54 highlighter.setHighlights(editor, decorations);
60 interface PublishDecorationsParams {
62 decorations: Decoration[];
65 interface Decoration {
71 // Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76
72 function fancify(seed: string, shade: 'light' | 'dark') {
73 const random = seedrandom(seed);
74 const randomInt = (min: number, max: number) => {
75 return Math.floor(random() * (max - min + 1)) + min;
78 const h = randomInt(0, 360);
79 const s = randomInt(42, 98);
80 const l = shade === 'light' ? randomInt(15, 40) : randomInt(40, 90);
81 return `hsl(${h},${s}%,${l}%)`;
86 private decorations: Map<
88 vscode.TextEditorDecorationType
91 constructor(ctx: Ctx) {
95 public removeHighlights() {
96 if (this.decorations == null) {
100 // Decorations are removed when the object is disposed
101 for (const decoration of this.decorations.values()) {
102 decoration.dispose();
105 this.decorations = null;
108 public setHighlights(editor: vscode.TextEditor, highlights: Decoration[]) {
109 // Initialize decorations if necessary
111 // Note: decoration objects need to be kept around so we can dispose them
112 // if the user disables syntax highlighting
113 if (this.decorations == null) {
114 this.decorations = initDecorations();
117 const byTag: Map<string, vscode.Range[]> = new Map();
118 const colorfulIdents: Map<
120 [vscode.Range[], boolean]
122 const rainbowTime = this.ctx.config.rainbowHighlightingOn;
124 for (const tag of this.decorations.keys()) {
128 for (const d of highlights) {
129 if (!byTag.get(d.tag)) {
133 if (rainbowTime && d.bindingHash) {
134 if (!colorfulIdents.has(d.bindingHash)) {
135 const mut = d.tag.endsWith('.mut');
136 colorfulIdents.set(d.bindingHash, [[], mut]);
139 .get(d.bindingHash)![0]
141 this.ctx.client.protocol2CodeConverter.asRange(d.range),
147 this.ctx.client.protocol2CodeConverter.asRange(d.range),
152 for (const tag of byTag.keys()) {
153 const dec = this.decorations.get(
155 ) as vscode.TextEditorDecorationType;
156 const ranges = byTag.get(tag)!;
157 editor.setDecorations(dec, ranges);
160 for (const [hash, [ranges, mut]] of colorfulIdents.entries()) {
161 const textDecoration = mut ? 'underline' : undefined;
162 const dec = vscode.window.createTextEditorDecorationType({
163 light: { color: fancify(hash, 'light'), textDecoration },
164 dark: { color: fancify(hash, 'dark'), textDecoration },
166 editor.setDecorations(dec, ranges);
171 function initDecorations(): Map<
173 vscode.TextEditorDecorationType
175 const themeColors = loadThemeColors();
179 textDecoration?: string,
180 ): [string, vscode.TextEditorDecorationType] => {
181 const rule = scopesMapper.toRule(tag, it => themeColors.get(it));
184 const decor = createDecorationFromTextmate(rule);
187 const fallBackTag = 'ralsp.' + tag;
189 // console.log('Missing theme for: <"' + tag + '"> for following mapped scopes:');
190 // console.log(scopesMapper.find(tag));
191 // console.log('Falling back to values defined in: ' + fallBackTag);
193 const color = new vscode.ThemeColor(fallBackTag);
194 const decor = vscode.window.createTextEditorDecorationType({
202 const decorations: Iterable<[
204 vscode.TextEditorDecorationType,
206 decoration('comment'),
207 decoration('string'),
208 decoration('keyword'),
209 decoration('keyword.control'),
210 decoration('keyword.unsafe'),
211 decoration('function'),
212 decoration('parameter'),
213 decoration('constant'),
214 decoration('type.builtin'),
215 decoration('type.generic'),
216 decoration('type.lifetime'),
217 decoration('type.param'),
218 decoration('type.self'),
221 decoration('attribute'),
222 decoration('literal'),
223 decoration('literal.numeric'),
224 decoration('literal.char'),
225 decoration('literal.byte'),
227 decoration('variable'),
228 decoration('variable.mut', 'underline'),
230 decoration('module'),
233 return new Map<string, vscode.TextEditorDecorationType>(decorations);
236 function createDecorationFromTextmate(
237 themeStyle: TextMateRuleSettings,
238 ): vscode.TextEditorDecorationType {
239 const decorationOptions: vscode.DecorationRenderOptions = {};
240 decorationOptions.rangeBehavior = vscode.DecorationRangeBehavior.OpenOpen;
242 if (themeStyle.foreground) {
243 decorationOptions.color = themeStyle.foreground;
246 if (themeStyle.background) {
247 decorationOptions.backgroundColor = themeStyle.background;
250 if (themeStyle.fontStyle) {
251 const parts: string[] = themeStyle.fontStyle.split(' ');
252 parts.forEach(part => {
255 decorationOptions.fontStyle = 'italic';
258 decorationOptions.fontWeight = 'bold';
261 decorationOptions.textDecoration = 'underline';
268 return vscode.window.createTextEditorDecorationType(decorationOptions);