1 import seedrandom = require('seedrandom');
2 import * as vscode from 'vscode';
3 import * as lc from 'vscode-languageclient';
5 import { Server } from './server';
7 export interface Decoration {
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;
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}%)`;
26 export class Highlighter {
27 private static initDecorations(): Map<
29 vscode.TextEditorDecorationType
33 textDecoration?: string,
34 ): [string, vscode.TextEditorDecorationType] => {
35 const color = new vscode.ThemeColor('ralsp.' + tag);
36 const decor = vscode.window.createTextEditorDecorationType({
43 const decorations: Iterable<[
45 vscode.TextEditorDecorationType,
47 decoration('comment'),
49 decoration('keyword'),
50 decoration('keyword.control'),
51 decoration('keyword.unsafe'),
52 decoration('function'),
53 decoration('parameter'),
54 decoration('constant'),
56 decoration('type.self'),
57 decoration('type.generic'),
58 decoration('type.param'),
59 decoration('type.lifetime'),
60 decoration('builtin'),
62 decoration('attribute'),
63 decoration('literal'),
64 decoration('literal.numeric'),
65 decoration('literal.char'),
66 decoration('literal.byte'),
68 decoration('variable'),
69 decoration('variable.mut', 'underline'),
74 return new Map<string, vscode.TextEditorDecorationType>(decorations);
77 private decorations: Map<
79 vscode.TextEditorDecorationType
82 public removeHighlights() {
83 if (this.decorations == null) {
87 // Decorations are removed when the object is disposed
88 for (const decoration of this.decorations.values()) {
92 this.decorations = null;
95 public setHighlights(editor: vscode.TextEditor, highlights: Decoration[]) {
96 // Initialize decorations if necessary
98 // Note: decoration objects need to be kept around so we can dispose them
99 // if the user disables syntax highlighting
100 if (this.decorations == null) {
101 this.decorations = Highlighter.initDecorations();
104 const byTag: Map<string, vscode.Range[]> = new Map();
105 const colorfulIdents: Map<
107 [vscode.Range[], boolean]
109 const rainbowTime = Server.config.rainbowHighlightingOn;
111 for (const tag of this.decorations.keys()) {
115 for (const d of highlights) {
116 if (!byTag.get(d.tag)) {
120 if (rainbowTime && d.bindingHash) {
121 if (!colorfulIdents.has(d.bindingHash)) {
122 const mut = d.tag.endsWith('.mut');
123 colorfulIdents.set(d.bindingHash, [[], mut]);
126 .get(d.bindingHash)![0]
128 Server.client.protocol2CodeConverter.asRange(d.range),
134 Server.client.protocol2CodeConverter.asRange(d.range),
139 for (const tag of byTag.keys()) {
140 const dec = this.decorations.get(
142 ) as vscode.TextEditorDecorationType;
143 const ranges = byTag.get(tag)!;
144 editor.setDecorations(dec, ranges);
147 for (const [hash, [ranges, mut]] of colorfulIdents.entries()) {
148 const textDecoration = mut ? 'underline' : undefined;
149 const dec = vscode.window.createTextEditorDecorationType({
150 light: { color: fancify(hash, 'light'), textDecoration },
151 dark: { color: fancify(hash, 'dark'), textDecoration },
153 editor.setDecorations(dec, ranges);