aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/highlighting.ts
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src/highlighting.ts')
-rw-r--r--editors/code/src/highlighting.ts209
1 files changed, 151 insertions, 58 deletions
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts
index e1b0d13e7..014e96f75 100644
--- a/editors/code/src/highlighting.ts
+++ b/editors/code/src/highlighting.ts
@@ -1,10 +1,69 @@
1import seedrandom = require('seedrandom');
2import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
3import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as seedrandom_ from 'seedrandom';
4const seedrandom = seedrandom_; // https://github.com/jvandemo/generator-angular2-library/issues/221#issuecomment-355945207
5
6import { ColorTheme, TextMateRuleSettings } from './color_theme';
7
8import { Ctx, sendRequestWithRetry } from './ctx';
9
10export function activateHighlighting(ctx: Ctx) {
11 const highlighter = new Highlighter(ctx);
12 ctx.onDidRestart(client => {
13 client.onNotification(
14 'rust-analyzer/publishDecorations',
15 (params: PublishDecorationsParams) => {
16 if (!ctx.config.highlightingOn) return;
17
18 const targetEditor = vscode.window.visibleTextEditors.find(
19 editor => {
20 const unescapedUri = unescape(
21 editor.document.uri.toString(),
22 );
23 // Unescaped URI looks like:
24 // file:///c:/Workspace/ra-test/src/main.rs
25 return unescapedUri === params.uri;
26 },
27 );
28 if (!targetEditor) return;
29
30 highlighter.setHighlights(targetEditor, params.decorations);
31 },
32 );
33 });
34
35 vscode.workspace.onDidChangeConfiguration(
36 _ => highlighter.removeHighlights(),
37 ctx.subscriptions,
38 );
39
40 vscode.window.onDidChangeActiveTextEditor(
41 async (editor: vscode.TextEditor | undefined) => {
42 if (!editor || editor.document.languageId !== 'rust') return;
43 if (!ctx.config.highlightingOn) return;
44 let client = ctx.client;
45 if (!client) return;
46
47 const params: lc.TextDocumentIdentifier = {
48 uri: editor.document.uri.toString(),
49 };
50 const decorations = await sendRequestWithRetry<Decoration[]>(
51 client,
52 'rust-analyzer/decorationsRequest',
53 params,
54 );
55 highlighter.setHighlights(editor, decorations);
56 },
57 ctx.subscriptions,
58 );
59}
4 60
5import { Server } from './server'; 61interface PublishDecorationsParams {
62 uri: string;
63 decorations: Decoration[];
64}
6 65
7export interface Decoration { 66interface Decoration {
8 range: lc.Range; 67 range: lc.Range;
9 tag: string; 68 tag: string;
10 bindingHash?: string; 69 bindingHash?: string;
@@ -23,62 +82,17 @@ function fancify(seed: string, shade: 'light' | 'dark') {
23 return `hsl(${h},${s}%,${l}%)`; 82 return `hsl(${h},${s}%,${l}%)`;
24} 83}
25 84
26export class Highlighter { 85class Highlighter {
27 private static initDecorations(): Map< 86 private ctx: Ctx;
28 string,
29 vscode.TextEditorDecorationType
30 > {
31 const decoration = (
32 tag: string,
33 textDecoration?: string,
34 ): [string, vscode.TextEditorDecorationType] => {
35 const color = new vscode.ThemeColor('ralsp.' + tag);
36 const decor = vscode.window.createTextEditorDecorationType({
37 color,
38 textDecoration,
39 });
40 return [tag, decor];
41 };
42
43 const decorations: Iterable<[
44 string,
45 vscode.TextEditorDecorationType,
46 ]> = [
47 decoration('comment'),
48 decoration('string'),
49 decoration('keyword'),
50 decoration('keyword.control'),
51 decoration('keyword.unsafe'),
52 decoration('function'),
53 decoration('parameter'),
54 decoration('constant'),
55 decoration('type.builtin'),
56 decoration('type.generic'),
57 decoration('type.lifetime'),
58 decoration('type.param'),
59 decoration('type.self'),
60 decoration('type'),
61 decoration('text'),
62 decoration('attribute'),
63 decoration('literal'),
64 decoration('literal.numeric'),
65 decoration('literal.char'),
66 decoration('literal.byte'),
67 decoration('macro'),
68 decoration('variable'),
69 decoration('variable.mut', 'underline'),
70 decoration('field'),
71 decoration('module'),
72 ];
73
74 return new Map<string, vscode.TextEditorDecorationType>(decorations);
75 }
76
77 private decorations: Map< 87 private decorations: Map<
78 string, 88 string,
79 vscode.TextEditorDecorationType 89 vscode.TextEditorDecorationType
80 > | null = null; 90 > | null = null;
81 91
92 constructor(ctx: Ctx) {
93 this.ctx = ctx;
94 }
95
82 public removeHighlights() { 96 public removeHighlights() {
83 if (this.decorations == null) { 97 if (this.decorations == null) {
84 return; 98 return;
@@ -93,12 +107,14 @@ export class Highlighter {
93 } 107 }
94 108
95 public setHighlights(editor: vscode.TextEditor, highlights: Decoration[]) { 109 public setHighlights(editor: vscode.TextEditor, highlights: Decoration[]) {
110 let client = this.ctx.client;
111 if (!client) return;
96 // Initialize decorations if necessary 112 // Initialize decorations if necessary
97 // 113 //
98 // Note: decoration objects need to be kept around so we can dispose them 114 // Note: decoration objects need to be kept around so we can dispose them
99 // if the user disables syntax highlighting 115 // if the user disables syntax highlighting
100 if (this.decorations == null) { 116 if (this.decorations == null) {
101 this.decorations = Highlighter.initDecorations(); 117 this.decorations = initDecorations();
102 } 118 }
103 119
104 const byTag: Map<string, vscode.Range[]> = new Map(); 120 const byTag: Map<string, vscode.Range[]> = new Map();
@@ -106,7 +122,7 @@ export class Highlighter {
106 string, 122 string,
107 [vscode.Range[], boolean] 123 [vscode.Range[], boolean]
108 > = new Map(); 124 > = new Map();
109 const rainbowTime = Server.config.rainbowHighlightingOn; 125 const rainbowTime = this.ctx.config.rainbowHighlightingOn;
110 126
111 for (const tag of this.decorations.keys()) { 127 for (const tag of this.decorations.keys()) {
112 byTag.set(tag, []); 128 byTag.set(tag, []);
@@ -125,13 +141,13 @@ export class Highlighter {
125 colorfulIdents 141 colorfulIdents
126 .get(d.bindingHash)![0] 142 .get(d.bindingHash)![0]
127 .push( 143 .push(
128 Server.client.protocol2CodeConverter.asRange(d.range), 144 client.protocol2CodeConverter.asRange(d.range),
129 ); 145 );
130 } else { 146 } else {
131 byTag 147 byTag
132 .get(d.tag)! 148 .get(d.tag)!
133 .push( 149 .push(
134 Server.client.protocol2CodeConverter.asRange(d.range), 150 client.protocol2CodeConverter.asRange(d.range),
135 ); 151 );
136 } 152 }
137 } 153 }
@@ -154,3 +170,80 @@ export class Highlighter {
154 } 170 }
155 } 171 }
156} 172}
173
174function initDecorations(): Map<string, vscode.TextEditorDecorationType> {
175 const theme = ColorTheme.load();
176 const res = new Map();
177 TAG_TO_SCOPES.forEach((scopes, tag) => {
178 if (!scopes) throw `unmapped tag: ${tag}`;
179 let rule = theme.lookup(scopes);
180 const decor = createDecorationFromTextmate(rule);
181 res.set(tag, decor);
182 });
183 return res;
184}
185
186function createDecorationFromTextmate(
187 themeStyle: TextMateRuleSettings,
188): vscode.TextEditorDecorationType {
189 const decorationOptions: vscode.DecorationRenderOptions = {};
190 decorationOptions.rangeBehavior = vscode.DecorationRangeBehavior.OpenOpen;
191
192 if (themeStyle.foreground) {
193 decorationOptions.color = themeStyle.foreground;
194 }
195
196 if (themeStyle.background) {
197 decorationOptions.backgroundColor = themeStyle.background;
198 }
199
200 if (themeStyle.fontStyle) {
201 const parts: string[] = themeStyle.fontStyle.split(' ');
202 parts.forEach(part => {
203 switch (part) {
204 case 'italic':
205 decorationOptions.fontStyle = 'italic';
206 break;
207 case 'bold':
208 decorationOptions.fontWeight = 'bold';
209 break;
210 case 'underline':
211 decorationOptions.textDecoration = 'underline';
212 break;
213 default:
214 break;
215 }
216 });
217 }
218 return vscode.window.createTextEditorDecorationType(decorationOptions);
219}
220
221// sync with tags from `syntax_highlighting.rs`.
222const TAG_TO_SCOPES = new Map<string, string[]>([
223 ["field", ["entity.name.field"]],
224 ["function", ["entity.name.function"]],
225 ["module", ["entity.name.module"]],
226 ["constant", ["entity.name.constant"]],
227 ["macro", ["entity.name.macro"]],
228
229 ["variable", ["variable"]],
230 ["variable.mut", ["variable", "meta.mutable"]],
231
232 ["type", ["entity.name.type"]],
233 ["type.builtin", ["entity.name.type", "support.type.primitive"]],
234 ["type.self", ["entity.name.type.parameter.self"]],
235 ["type.param", ["entity.name.type.parameter"]],
236 ["type.lifetime", ["entity.name.type.lifetime"]],
237
238 ["literal.byte", ["constant.character.byte"]],
239 ["literal.char", ["constant.character"]],
240 ["literal.numeric", ["constant.numeric"]],
241
242 ["comment", ["comment"]],
243 ["string", ["string.quoted"]],
244 ["attribute", ["meta.attribute"]],
245
246 ["keyword", ["keyword"]],
247 ["keyword.unsafe", ["keyword.other.unsafe"]],
248 ["keyword.control", ["keyword.control"]],
249]);