]> git.lizzy.rs Git - rust.git/blob - editors/code/src/highlighting.ts
added decorations
[rust.git] / editors / code / src / highlighting.ts
1 import seedrandom = require('seedrandom');
2 import * as vscode from 'vscode';
3 import * as lc from 'vscode-languageclient';
4
5 import { Server } from './server';
6
7 export interface Decoration {
8     range: lc.Range;
9     tag: string;
10     bindingHash?: string;
11 }
12
13 // Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76
14 function fancify(seed: string, shade: 'light' | 'dark') {
15     const random = seedrandom(seed);
16     const randomInt = (min: number, max: number) => {
17         return Math.floor(random() * (max - min + 1)) + min;
18     };
19
20     const h = randomInt(0, 360);
21     const s = randomInt(42, 98);
22     const l = shade === 'light' ? randomInt(15, 40) : randomInt(40, 90);
23     return `hsl(${h},${s}%,${l}%)`;
24 }
25
26 export class Highlighter {
27     private static initDecorations(): Map<
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'),
56             decoration('type.self'),
57             decoration('type.generic'),
58             decoration('type.param'),
59             decoration('builtin'),
60             decoration('text'),
61             decoration('attribute'),
62             decoration('literal'),
63             decoration('literal.numeric'),
64             decoration('literal.char'),
65             decoration('literal.byte'),
66             decoration('macro'),
67             decoration('variable'),
68             decoration('variable.mut', 'underline'),
69             decoration('field'),
70             decoration('module'),
71         ];
72
73         return new Map<string, vscode.TextEditorDecorationType>(decorations);
74     }
75
76     private decorations: Map<
77         string,
78         vscode.TextEditorDecorationType
79     > | null = null;
80
81     public removeHighlights() {
82         if (this.decorations == null) {
83             return;
84         }
85
86         // Decorations are removed when the object is disposed
87         for (const decoration of this.decorations.values()) {
88             decoration.dispose();
89         }
90
91         this.decorations = null;
92     }
93
94     public setHighlights(editor: vscode.TextEditor, highlights: Decoration[]) {
95         // Initialize decorations if necessary
96         //
97         // Note: decoration objects need to be kept around so we can dispose them
98         // if the user disables syntax highlighting
99         if (this.decorations == null) {
100             this.decorations = Highlighter.initDecorations();
101         }
102
103         const byTag: Map<string, vscode.Range[]> = new Map();
104         const colorfulIdents: Map<
105             string,
106             [vscode.Range[], boolean]
107         > = new Map();
108         const rainbowTime = Server.config.rainbowHighlightingOn;
109
110         for (const tag of this.decorations.keys()) {
111             byTag.set(tag, []);
112         }
113
114         for (const d of highlights) {
115             if (!byTag.get(d.tag)) {
116                 continue;
117             }
118
119             if (rainbowTime && d.bindingHash) {
120                 if (!colorfulIdents.has(d.bindingHash)) {
121                     const mut = d.tag.endsWith('.mut');
122                     colorfulIdents.set(d.bindingHash, [[], mut]);
123                 }
124                 colorfulIdents
125                     .get(d.bindingHash)![0]
126                     .push(
127                         Server.client.protocol2CodeConverter.asRange(d.range),
128                     );
129             } else {
130                 byTag
131                     .get(d.tag)!
132                     .push(
133                         Server.client.protocol2CodeConverter.asRange(d.range),
134                     );
135             }
136         }
137
138         for (const tag of byTag.keys()) {
139             const dec = this.decorations.get(
140                 tag,
141             ) as vscode.TextEditorDecorationType;
142             const ranges = byTag.get(tag)!;
143             editor.setDecorations(dec, ranges);
144         }
145
146         for (const [hash, [ranges, mut]] of colorfulIdents.entries()) {
147             const textDecoration = mut ? 'underline' : undefined;
148             const dec = vscode.window.createTextEditorDecorationType({
149                 light: { color: fancify(hash, 'light'), textDecoration },
150                 dark: { color: fancify(hash, 'dark'), textDecoration },
151             });
152             editor.setDecorations(dec, ranges);
153         }
154     }
155 }