aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editors/code/package-lock.json6
-rw-r--r--editors/code/src/config.ts8
-rw-r--r--editors/code/src/extension.ts10
-rw-r--r--editors/code/src/highlighting.ts96
-rw-r--r--editors/code/src/scopes.ts134
-rw-r--r--editors/code/src/scopes_mapper.ts48
6 files changed, 268 insertions, 34 deletions
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index e6bcbfa60..9c55c257f 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -598,9 +598,9 @@
598 } 598 }
599 }, 599 },
600 "https-proxy-agent": { 600 "https-proxy-agent": {
601 "version": "2.2.2", 601 "version": "2.2.3",
602 "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", 602 "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.3.tgz",
603 "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", 603 "integrity": "sha512-Ytgnz23gm2DVftnzqRRz2dOXZbGd2uiajSw/95bPp6v53zPRspQjLm/AfBgqbJ2qfeRXWIOMVLpp86+/5yX39Q==",
604 "dev": true, 604 "dev": true,
605 "requires": { 605 "requires": {
606 "agent-base": "^4.3.0", 606 "agent-base": "^4.3.0",
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 95c3f42e5..12823f319 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -1,5 +1,6 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2 2import * as scopes from './scopes';
3import * as scopesMapper from './scopes_mapper';
3import { Server } from './server'; 4import { Server } from './server';
4 5
5const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG; 6const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG;
@@ -45,7 +46,12 @@ export class Config {
45 46
46 public userConfigChanged() { 47 public userConfigChanged() {
47 const config = vscode.workspace.getConfiguration('rust-analyzer'); 48 const config = vscode.workspace.getConfiguration('rust-analyzer');
49
50 Server.highlighter.removeHighlights();
51 scopes.load()
52 scopesMapper.load()
48 if (config.has('highlightingOn')) { 53 if (config.has('highlightingOn')) {
54
49 this.highlightingOn = config.get('highlightingOn') as boolean; 55 this.highlightingOn = config.get('highlightingOn') as boolean;
50 } 56 }
51 57
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts
index c06928d12..07a5c59e8 100644
--- a/editors/code/src/extension.ts
+++ b/editors/code/src/extension.ts
@@ -91,11 +91,11 @@ export function activate(context: vscode.ExtensionContext) {
91 const allNotifications: Iterable< 91 const allNotifications: Iterable<
92 [string, lc.GenericNotificationHandler] 92 [string, lc.GenericNotificationHandler]
93 > = [ 93 > = [
94 [ 94 [
95 'rust-analyzer/publishDecorations', 95 'rust-analyzer/publishDecorations',
96 notifications.publishDecorations.handle 96 notifications.publishDecorations.handle
97 ] 97 ]
98 ]; 98 ];
99 const syntaxTreeContentProvider = new SyntaxTreeContentProvider(); 99 const syntaxTreeContentProvider = new SyntaxTreeContentProvider();
100 100
101 // The events below are plain old javascript events, triggered and handled by vscode 101 // The events below are plain old javascript events, triggered and handled by vscode
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts
index d21d8a06a..b7dffaff5 100644
--- a/editors/code/src/highlighting.ts
+++ b/editors/code/src/highlighting.ts
@@ -1,6 +1,8 @@
1import seedrandom = require('seedrandom'); 1import seedrandom = require('seedrandom');
2import * as vscode from 'vscode'; 2import * as vscode from 'vscode';
3import * as lc from 'vscode-languageclient'; 3import * as lc from 'vscode-languageclient';
4import * as scopes from './scopes'
5import * as scopesMapper from './scopes_mapper';
4 6
5import { Server } from './server'; 7import { Server } from './server';
6 8
@@ -23,6 +25,37 @@ function fancify(seed: string, shade: 'light' | 'dark') {
23 return `hsl(${h},${s}%,${l}%)`; 25 return `hsl(${h},${s}%,${l}%)`;
24} 26}
25 27
28function createDecorationFromTextmate(themeStyle: scopes.TextMateRuleSettings): vscode.TextEditorDecorationType {
29 const options: vscode.DecorationRenderOptions = {}
30 options.rangeBehavior = vscode.DecorationRangeBehavior.OpenOpen
31 if (themeStyle.foreground) {
32 options.color = themeStyle.foreground
33 }
34 if (themeStyle.background) {
35 options.backgroundColor = themeStyle.background
36 }
37 if (themeStyle.fontStyle) {
38 const parts: string[] = themeStyle.fontStyle.split(' ')
39 parts.forEach((part) => {
40 switch (part) {
41 case 'italic':
42 options.fontStyle = 'italic'
43 break
44 case 'bold':
45 options.fontWeight = 'bold'
46
47 break
48 case 'underline':
49 options.textDecoration = 'underline'
50 break
51 default:
52 break
53 }
54 })
55 }
56 return vscode.window.createTextEditorDecorationType(options)
57}
58
26export class Highlighter { 59export class Highlighter {
27 private static initDecorations(): Map< 60 private static initDecorations(): Map<
28 string, 61 string,
@@ -32,36 +65,46 @@ export class Highlighter {
32 tag: string, 65 tag: string,
33 textDecoration?: string 66 textDecoration?: string
34 ): [string, vscode.TextEditorDecorationType] => { 67 ): [string, vscode.TextEditorDecorationType] => {
35 const color = new vscode.ThemeColor('ralsp.' + tag); 68
36 const decor = vscode.window.createTextEditorDecorationType({ 69 const rule = scopesMapper.toRule(tag, scopes.find)
37 color, 70
38 textDecoration 71 if (rule) {
39 }); 72 const decor = createDecorationFromTextmate(rule);
40 return [tag, decor]; 73 return [tag, decor];
74 }
75 else {
76 console.log('Missing theme for: ' + tag);
77 const color = new vscode.ThemeColor('ralsp.' + tag);
78 const decor = vscode.window.createTextEditorDecorationType({
79 color,
80 textDecoration
81 });
82 return [tag, decor];
83 }
41 }; 84 };
42 85
43 const decorations: Iterable< 86 const decorations: Iterable<
44 [string, vscode.TextEditorDecorationType] 87 [string, vscode.TextEditorDecorationType]
45 > = [ 88 > = [
46 decoration('comment'), 89 decoration('comment'),
47 decoration('string'), 90 decoration('string'),
48 decoration('keyword'), 91 decoration('keyword'),
49 decoration('keyword.control'), 92 decoration('keyword.control'),
50 decoration('keyword.unsafe'), 93 decoration('keyword.unsafe'),
51 decoration('function'), 94 decoration('function'),
52 decoration('parameter'), 95 decoration('parameter'),
53 decoration('constant'), 96 decoration('constant'),
54 decoration('type'), 97 decoration('type'),
55 decoration('builtin'), 98 decoration('builtin'),
56 decoration('text'), 99 decoration('text'),
57 decoration('attribute'), 100 decoration('attribute'),
58 decoration('literal'), 101 decoration('literal'),
59 decoration('macro'), 102 decoration('macro'),
60 decoration('variable'), 103 decoration('variable'),
61 decoration('variable.mut', 'underline'), 104 decoration('variable.mut', 'underline'),
62 decoration('field'), 105 decoration('field'),
63 decoration('module') 106 decoration('module')
64 ]; 107 ];
65 108
66 return new Map<string, vscode.TextEditorDecorationType>(decorations); 109 return new Map<string, vscode.TextEditorDecorationType>(decorations);
67 } 110 }
@@ -89,6 +132,8 @@ export class Highlighter {
89 // 132 //
90 // Note: decoration objects need to be kept around so we can dispose them 133 // Note: decoration objects need to be kept around so we can dispose them
91 // if the user disables syntax highlighting 134 // if the user disables syntax highlighting
135
136
92 if (this.decorations == null) { 137 if (this.decorations == null) {
93 this.decorations = Highlighter.initDecorations(); 138 this.decorations = Highlighter.initDecorations();
94 } 139 }
@@ -133,6 +178,7 @@ export class Highlighter {
133 tag 178 tag
134 ) as vscode.TextEditorDecorationType; 179 ) as vscode.TextEditorDecorationType;
135 const ranges = byTag.get(tag)!; 180 const ranges = byTag.get(tag)!;
181
136 editor.setDecorations(dec, ranges); 182 editor.setDecorations(dec, ranges);
137 } 183 }
138 184
diff --git a/editors/code/src/scopes.ts b/editors/code/src/scopes.ts
new file mode 100644
index 000000000..470ee716f
--- /dev/null
+++ b/editors/code/src/scopes.ts
@@ -0,0 +1,134 @@
1import * as fs from 'fs'
2import * as path from 'path'
3import * as vscode from 'vscode'
4
5
6
7export interface TextMateRule {
8 scope: string | string[]
9 settings: TextMateRuleSettings
10}
11
12export interface TextMateRuleSettings {
13 foreground: string | undefined
14 background: string | undefined
15 fontStyle: string | undefined
16}
17
18// Current theme colors
19const rules = new Map<string, TextMateRuleSettings>()
20
21export function find(scope: string): TextMateRuleSettings | undefined {
22 return rules.get(scope)
23}
24
25// Load all textmate scopes in the currently active theme
26export function load() {
27 // Remove any previous theme
28 rules.clear()
29 // Find out current color theme
30 const themeName = vscode.workspace.getConfiguration('workbench').get('colorTheme')
31
32 if (typeof themeName !== 'string') {
33 // console.warn('workbench.colorTheme is', themeName)
34 return
35 }
36 // Try to load colors from that theme
37 try {
38 loadThemeNamed(themeName)
39 } catch (e) {
40 // console.warn('failed to load theme', themeName, e)
41 }
42}
43
44// Find current theme on disk
45function loadThemeNamed(themeName: string) {
46
47 const themePaths = vscode.extensions.all
48 .filter(extension => extension.extensionKind === vscode.ExtensionKind.UI)
49 .filter(extension => extension.packageJSON.contributes)
50 .filter(extension => extension.packageJSON.contributes.themes)
51 .reduce((list, extension) => {
52 const paths = extension.packageJSON.contributes.themes
53 .filter((element: any) => (element.id || element.label) === themeName)
54 .map((element: any) => path.join(extension.extensionPath, element.path))
55 return list.concat(paths)
56 }, Array<string>());
57
58
59 themePaths.forEach(loadThemeFile);
60
61 const customization: any = vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations');
62 if (customization && customization.textMateRules) {
63 loadColors(customization.textMateRules)
64 }
65}
66
67function loadThemeFile(themePath: string) {
68
69 if (checkFileExists(themePath)) {
70 const themeContentText: string = readFileText(themePath)
71
72 const themeContent: any = JSON.parse(themeContentText)
73
74 if (themeContent && themeContent.tokenColors) {
75 loadColors(themeContent.tokenColors)
76 if (themeContent.include) {
77 // parse included theme file
78 const includedThemePath: string = path.join(path.dirname(themePath), themeContent.include)
79 loadThemeFile(includedThemePath)
80 }
81 }
82 }
83}
84function mergeRuleSettings(defaultSetting: TextMateRuleSettings, override: TextMateRuleSettings): TextMateRuleSettings {
85 const mergedRule = defaultSetting;
86
87 mergedRule.background = override.background || defaultSetting.background
88 mergedRule.foreground = override.foreground || defaultSetting.foreground
89 mergedRule.fontStyle = override.fontStyle || defaultSetting.foreground;
90
91 return mergedRule;
92}
93
94function loadColors(textMateRules: TextMateRule[]): void {
95 for (const rule of textMateRules) {
96
97 if (typeof rule.scope === 'string') {
98 const existingRule = rules.get(rule.scope);
99 if (existingRule) {
100 rules.set(rule.scope, mergeRuleSettings(existingRule, rule.settings))
101 }
102 else {
103 rules.set(rule.scope, rule.settings)
104 }
105 } else if (rule.scope instanceof Array) {
106 for (const scope of rule.scope) {
107 const existingRule = rules.get(scope);
108 if (existingRule) {
109 rules.set(scope, mergeRuleSettings(existingRule, rule.settings))
110 }
111 else {
112 rules.set(scope, rule.settings)
113 }
114 }
115 }
116 }
117}
118
119function checkFileExists(filePath: string): boolean {
120
121 const stats = fs.statSync(filePath);
122 if (stats && stats.isFile()) {
123 return true;
124 } else {
125 // console.warn('no such file', filePath)
126 return false;
127 }
128
129
130}
131
132function readFileText(filePath: string, encoding: string = 'utf8'): string {
133 return fs.readFileSync(filePath, encoding);
134} \ No newline at end of file
diff --git a/editors/code/src/scopes_mapper.ts b/editors/code/src/scopes_mapper.ts
new file mode 100644
index 000000000..4534d8a32
--- /dev/null
+++ b/editors/code/src/scopes_mapper.ts
@@ -0,0 +1,48 @@
1import * as vscode from 'vscode'
2import { TextMateRuleSettings } from './scopes'
3
4
5
6
7let mappings = new Map<string, string[]>()
8
9
10const defaultMapping = new Map<string, string[]>([
11 ['comment', ['comment', 'comment.block', 'comment.line', 'comment.block.documentation']],
12 ['string', ['string']],
13 ['keyword', ['keyword']],
14 ['keyword.control', ['keyword.control', 'keyword', 'keyword.other']],
15 ['keyword.unsafe', ['storage.modifier', 'keyword.other', 'keyword.control', 'keyword']],
16 ['function', ['entity.name.function']],
17 ['parameter', ['variable.parameter']],
18 ['constant', ['constant', 'variable']],
19 ['type', ['entity.name.type']],
20 ['builtin', ['variable.language', 'support.type', 'support.type']],
21 ['text', ['string', 'string.quoted', 'string.regexp']],
22 ['attribute', ['keyword']],
23 ['literal', ['string', 'string.quoted', 'string.regexp']],
24 ['macro', ['support.other']],
25 ['variable', ['variable']],
26 ['variable.mut', ['variable', 'storage.modifier']],
27 ['field', ['variable.object.property', 'meta.field.declaration', 'meta.definition.property', 'variable.other',]],
28 ['module', ['entity.name.section', 'entity.other']]
29]
30)
31function find(scope: string): string[] {
32 return mappings.get(scope) || []
33}
34
35export function toRule(scope: string, intoRule: (scope: string) => TextMateRuleSettings | undefined): TextMateRuleSettings | undefined {
36 return find(scope).map(intoRule).filter(rule => rule !== undefined)[0];
37}
38
39
40export function load() {
41 const configuration = vscode.workspace
42 .getConfiguration('rust-analyzer')
43 .get('scopeMappings') as Map<string, string[]> | undefined || new Map()
44
45 mappings = new Map([...Array.from(defaultMapping.entries()), ...Array.from(configuration.entries())]);
46
47
48} \ No newline at end of file