diff options
author | Seivan Heidari <[email protected]> | 2019-11-04 22:59:11 +0000 |
---|---|---|
committer | Seivan Heidari <[email protected]> | 2019-11-04 22:59:11 +0000 |
commit | c60f9bf4c6d6ddd341c673b228b6aa1add3da62b (patch) | |
tree | e411904c533ac37745397719bbfe983aeb21b372 /editors/code | |
parent | dad9bc6caad71e6aebb92ad9883c08d30431e9b1 (diff) |
* Adding scope mapping configuration manifest in `package.json`
* Loading configurable scope mappings from settings.
* Updating Readme with `rust-analyzer.scopeMappings`.
`rust-analyzer.scopeMappings` -- a scheme backed JSON object to tweak Rust Analyzer scopes to TextMate scopes.
```jsonc
{
//Will autocomplete keys to available RA scopes.
"keyword.unsafe": ["keyword", "keyword.control"],
//Values are string | TextMateScope | [string | TextMateScope]
"comments": "comment.block"
}
```
Diffstat (limited to 'editors/code')
-rw-r--r-- | editors/code/package.json | 64 | ||||
-rw-r--r-- | editors/code/src/config.ts | 11 | ||||
-rw-r--r-- | editors/code/src/highlighting.ts | 32 | ||||
-rw-r--r-- | editors/code/src/scopes.ts | 64 | ||||
-rw-r--r-- | editors/code/src/scopes_mapper.ts | 35 |
5 files changed, 140 insertions, 66 deletions
diff --git a/editors/code/package.json b/editors/code/package.json index ee997e58f..35f2f1e62 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -167,6 +167,68 @@ | |||
167 | "default": false, | 167 | "default": false, |
168 | "description": "Highlight Rust code (overrides built-in syntax highlighting)" | 168 | "description": "Highlight Rust code (overrides built-in syntax highlighting)" |
169 | }, | 169 | }, |
170 | "rust-analyzer.scopeMappings": { | ||
171 | "type": "object", | ||
172 | "definitions": {}, | ||
173 | "properties": { | ||
174 | "comment": { | ||
175 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
176 | }, | ||
177 | "string": { | ||
178 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
179 | }, | ||
180 | "keyword": { | ||
181 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
182 | }, | ||
183 | "keyword.control": { | ||
184 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
185 | }, | ||
186 | "keyword.unsafe": { | ||
187 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
188 | }, | ||
189 | "function": { | ||
190 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
191 | }, | ||
192 | "parameter": { | ||
193 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
194 | }, | ||
195 | "constant": { | ||
196 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
197 | }, | ||
198 | "type": { | ||
199 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
200 | }, | ||
201 | "builtin": { | ||
202 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
203 | }, | ||
204 | "text": { | ||
205 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
206 | }, | ||
207 | "attribute": { | ||
208 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
209 | }, | ||
210 | "literal": { | ||
211 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
212 | }, | ||
213 | "macro": { | ||
214 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
215 | }, | ||
216 | "variable": { | ||
217 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
218 | }, | ||
219 | "variable.mut": { | ||
220 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
221 | }, | ||
222 | "field": { | ||
223 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
224 | }, | ||
225 | "module": { | ||
226 | "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" | ||
227 | } | ||
228 | }, | ||
229 | "additionalProperties": false, | ||
230 | "description": "Mapping Rust Analyzer scopes to TextMateRule scopes list." | ||
231 | }, | ||
170 | "rust-analyzer.rainbowHighlightingOn": { | 232 | "rust-analyzer.rainbowHighlightingOn": { |
171 | "type": "boolean", | 233 | "type": "boolean", |
172 | "default": false, | 234 | "default": false, |
@@ -488,4 +550,4 @@ | |||
488 | } | 550 | } |
489 | ] | 551 | ] |
490 | } | 552 | } |
491 | } | 553 | } \ No newline at end of file |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 12823f319..234a390ac 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -48,11 +48,13 @@ export class Config { | |||
48 | const config = vscode.workspace.getConfiguration('rust-analyzer'); | 48 | const config = vscode.workspace.getConfiguration('rust-analyzer'); |
49 | 49 | ||
50 | Server.highlighter.removeHighlights(); | 50 | Server.highlighter.removeHighlights(); |
51 | scopes.load() | ||
52 | scopesMapper.load() | ||
53 | if (config.has('highlightingOn')) { | ||
54 | 51 | ||
52 | if (config.has('highlightingOn')) { | ||
55 | this.highlightingOn = config.get('highlightingOn') as boolean; | 53 | this.highlightingOn = config.get('highlightingOn') as boolean; |
54 | if (this.highlightingOn) { | ||
55 | scopes.load(); | ||
56 | scopesMapper.load(); | ||
57 | } | ||
56 | } | 58 | } |
57 | 59 | ||
58 | if (config.has('rainbowHighlightingOn')) { | 60 | if (config.has('rainbowHighlightingOn')) { |
@@ -61,9 +63,6 @@ export class Config { | |||
61 | ) as boolean; | 63 | ) as boolean; |
62 | } | 64 | } |
63 | 65 | ||
64 | if (!this.highlightingOn && Server) { | ||
65 | Server.highlighter.removeHighlights(); | ||
66 | } | ||
67 | 66 | ||
68 | if (config.has('enableEnhancedTyping')) { | 67 | if (config.has('enableEnhancedTyping')) { |
69 | this.enableEnhancedTyping = config.get( | 68 | this.enableEnhancedTyping = config.get( |
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts index dad99254e..1c67e5dc3 100644 --- a/editors/code/src/highlighting.ts +++ b/editors/code/src/highlighting.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import seedrandom = require('seedrandom'); | 1 | import seedrandom = require('seedrandom'); |
2 | import * as vscode from 'vscode'; | 2 | import * as vscode from 'vscode'; |
3 | import * as lc from 'vscode-languageclient'; | 3 | import * as lc from 'vscode-languageclient'; |
4 | import * as scopes from './scopes' | 4 | import * as scopes from './scopes'; |
5 | import * as scopesMapper from './scopes_mapper'; | 5 | import * as scopesMapper from './scopes_mapper'; |
6 | 6 | ||
7 | import { Server } from './server'; | 7 | import { Server } from './server'; |
@@ -25,35 +25,35 @@ function fancify(seed: string, shade: 'light' | 'dark') { | |||
25 | return `hsl(${h},${s}%,${l}%)`; | 25 | return `hsl(${h},${s}%,${l}%)`; |
26 | } | 26 | } |
27 | 27 | ||
28 | |||
28 | function createDecorationFromTextmate(themeStyle: scopes.TextMateRuleSettings): vscode.TextEditorDecorationType { | 29 | function createDecorationFromTextmate(themeStyle: scopes.TextMateRuleSettings): vscode.TextEditorDecorationType { |
29 | const options: vscode.DecorationRenderOptions = {} | 30 | const options: vscode.DecorationRenderOptions = {}; |
30 | options.rangeBehavior = vscode.DecorationRangeBehavior.OpenOpen | 31 | options.rangeBehavior = vscode.DecorationRangeBehavior.OpenOpen; |
31 | if (themeStyle.foreground) { | 32 | if (themeStyle.foreground) { |
32 | options.color = themeStyle.foreground | 33 | options.color = themeStyle.foreground; |
33 | } | 34 | } |
34 | if (themeStyle.background) { | 35 | if (themeStyle.background) { |
35 | options.backgroundColor = themeStyle.background | 36 | options.backgroundColor = themeStyle.background; |
36 | } | 37 | } |
37 | if (themeStyle.fontStyle) { | 38 | if (themeStyle.fontStyle) { |
38 | const parts: string[] = themeStyle.fontStyle.split(' ') | 39 | const parts: string[] = themeStyle.fontStyle.split(' '); |
39 | parts.forEach((part) => { | 40 | parts.forEach((part) => { |
40 | switch (part) { | 41 | switch (part) { |
41 | case 'italic': | 42 | case 'italic': |
42 | options.fontStyle = 'italic' | 43 | options.fontStyle = 'italic'; |
43 | break | 44 | break; |
44 | case 'bold': | 45 | case 'bold': |
45 | options.fontWeight = 'bold' | 46 | options.fontWeight = 'bold'; |
46 | 47 | break; | |
47 | break | ||
48 | case 'underline': | 48 | case 'underline': |
49 | options.textDecoration = 'underline' | 49 | options.textDecoration = 'underline'; |
50 | break | 50 | break; |
51 | default: | 51 | default: |
52 | break | 52 | break; |
53 | } | 53 | } |
54 | }) | 54 | }) |
55 | } | 55 | } |
56 | return vscode.window.createTextEditorDecorationType(options) | 56 | return vscode.window.createTextEditorDecorationType(options); |
57 | } | 57 | } |
58 | 58 | ||
59 | export class Highlighter { | 59 | export class Highlighter { |
@@ -66,7 +66,7 @@ export class Highlighter { | |||
66 | textDecoration?: string | 66 | textDecoration?: string |
67 | ): [string, vscode.TextEditorDecorationType] => { | 67 | ): [string, vscode.TextEditorDecorationType] => { |
68 | 68 | ||
69 | const rule = scopesMapper.toRule(tag, scopes.find) | 69 | const rule = scopesMapper.toRule(tag, scopes.find); |
70 | 70 | ||
71 | if (rule) { | 71 | if (rule) { |
72 | const decor = createDecorationFromTextmate(rule); | 72 | const decor = createDecorationFromTextmate(rule); |
diff --git a/editors/code/src/scopes.ts b/editors/code/src/scopes.ts index 5d4395930..a6138fad0 100644 --- a/editors/code/src/scopes.ts +++ b/editors/code/src/scopes.ts | |||
@@ -1,41 +1,41 @@ | |||
1 | import * as fs from 'fs' | 1 | import * as fs from 'fs'; |
2 | import * as path from 'path' | 2 | import * as path from 'path'; |
3 | import * as vscode from 'vscode' | 3 | import * as vscode from 'vscode'; |
4 | 4 | ||
5 | 5 | ||
6 | 6 | ||
7 | export interface TextMateRule { | 7 | export interface TextMateRule { |
8 | scope: string | string[] | 8 | scope: string | string[]; |
9 | settings: TextMateRuleSettings | 9 | settings: TextMateRuleSettings; |
10 | } | 10 | } |
11 | 11 | ||
12 | export interface TextMateRuleSettings { | 12 | export interface TextMateRuleSettings { |
13 | foreground: string | undefined | 13 | foreground: string | undefined; |
14 | background: string | undefined | 14 | background: string | undefined; |
15 | fontStyle: string | undefined | 15 | fontStyle: string | undefined; |
16 | } | 16 | } |
17 | 17 | ||
18 | // Current theme colors | 18 | // Current theme colors |
19 | const rules = new Map<string, TextMateRuleSettings>() | 19 | const rules = new Map<string, TextMateRuleSettings>(); |
20 | 20 | ||
21 | export function find(scope: string): TextMateRuleSettings | undefined { | 21 | export function find(scope: string): TextMateRuleSettings | undefined { |
22 | return rules.get(scope) | 22 | return rules.get(scope); |
23 | } | 23 | } |
24 | 24 | ||
25 | // Load all textmate scopes in the currently active theme | 25 | // Load all textmate scopes in the currently active theme |
26 | export function load() { | 26 | export function load() { |
27 | // Remove any previous theme | 27 | // Remove any previous theme |
28 | rules.clear() | 28 | rules.clear(); |
29 | // Find out current color theme | 29 | // Find out current color theme |
30 | const themeName = vscode.workspace.getConfiguration('workbench').get('colorTheme') | 30 | const themeName = vscode.workspace.getConfiguration('workbench').get('colorTheme'); |
31 | 31 | ||
32 | if (typeof themeName !== 'string') { | 32 | if (typeof themeName !== 'string') { |
33 | // console.warn('workbench.colorTheme is', themeName) | 33 | // console.warn('workbench.colorTheme is', themeName) |
34 | return | 34 | return; |
35 | } | 35 | } |
36 | // Try to load colors from that theme | 36 | // Try to load colors from that theme |
37 | try { | 37 | try { |
38 | loadThemeNamed(themeName) | 38 | loadThemeNamed(themeName); |
39 | } catch (e) { | 39 | } catch (e) { |
40 | // console.warn('failed to load theme', themeName, e) | 40 | // console.warn('failed to load theme', themeName, e) |
41 | } | 41 | } |
@@ -44,7 +44,7 @@ export function load() { | |||
44 | function filterThemeExtensions(extension: vscode.Extension<any>): boolean { | 44 | function filterThemeExtensions(extension: vscode.Extension<any>): boolean { |
45 | return extension.extensionKind === vscode.ExtensionKind.UI && | 45 | return extension.extensionKind === vscode.ExtensionKind.UI && |
46 | extension.packageJSON.contributes && | 46 | extension.packageJSON.contributes && |
47 | extension.packageJSON.contributes.themes | 47 | extension.packageJSON.contributes.themes; |
48 | } | 48 | } |
49 | 49 | ||
50 | 50 | ||
@@ -59,17 +59,17 @@ function loadThemeNamed(themeName: string) { | |||
59 | .filter((element: any) => (element.id || element.label) === themeName) | 59 | .filter((element: any) => (element.id || element.label) === themeName) |
60 | .map((element: any) => path.join(extension.extensionPath, element.path)) | 60 | .map((element: any) => path.join(extension.extensionPath, element.path)) |
61 | .concat(list) | 61 | .concat(list) |
62 | }, Array<string>()) | 62 | }, Array<string>()); |
63 | 63 | ||
64 | 64 | ||
65 | themePaths.forEach(loadThemeFile) | 65 | themePaths.forEach(loadThemeFile); |
66 | 66 | ||
67 | const tokenColorCustomizations: [any] = [vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations')] | 67 | const tokenColorCustomizations: [any] = [vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations')] |
68 | 68 | ||
69 | tokenColorCustomizations | 69 | tokenColorCustomizations |
70 | .filter(custom => custom && custom.textMateRules) | 70 | .filter(custom => custom && custom.textMateRules) |
71 | .map(custom => custom.textMateRules) | 71 | .map(custom => custom.textMateRules) |
72 | .forEach(loadColors) | 72 | .forEach(loadColors); |
73 | 73 | ||
74 | } | 74 | } |
75 | 75 | ||
@@ -79,26 +79,26 @@ function loadThemeFile(themePath: string) { | |||
79 | .filter(isFile) | 79 | .filter(isFile) |
80 | .map(readFileText) | 80 | .map(readFileText) |
81 | .map(parseJSON) | 81 | .map(parseJSON) |
82 | .filter(theme => theme) | 82 | .filter(theme => theme); |
83 | 83 | ||
84 | themeContent | 84 | themeContent |
85 | .filter(theme => theme.tokenColors) | 85 | .filter(theme => theme.tokenColors) |
86 | .map(theme => theme.tokenColors) | 86 | .map(theme => theme.tokenColors) |
87 | .forEach(loadColors) | 87 | .forEach(loadColors); |
88 | 88 | ||
89 | themeContent | 89 | themeContent |
90 | .filter(theme => theme.include) | 90 | .filter(theme => theme.include) |
91 | .map(theme => path.join(path.dirname(themePath), theme.include)) | 91 | .map(theme => path.join(path.dirname(themePath), theme.include)) |
92 | .forEach(loadThemeFile) | 92 | .forEach(loadThemeFile); |
93 | } | 93 | } |
94 | 94 | ||
95 | function mergeRuleSettings(defaultSetting: TextMateRuleSettings | undefined, override: TextMateRuleSettings): TextMateRuleSettings { | 95 | function mergeRuleSettings(defaultSetting: TextMateRuleSettings | undefined, override: TextMateRuleSettings): TextMateRuleSettings { |
96 | if (defaultSetting === undefined) { return override } | 96 | if (defaultSetting === undefined) { return override; } |
97 | const mergedRule = defaultSetting | 97 | const mergedRule = defaultSetting; |
98 | 98 | ||
99 | mergedRule.background = override.background || defaultSetting.background | 99 | mergedRule.background = override.background || defaultSetting.background; |
100 | mergedRule.foreground = override.foreground || defaultSetting.foreground | 100 | mergedRule.foreground = override.foreground || defaultSetting.foreground; |
101 | mergedRule.fontStyle = override.fontStyle || defaultSetting.foreground | 101 | mergedRule.fontStyle = override.fontStyle || defaultSetting.foreground; |
102 | 102 | ||
103 | return mergedRule | 103 | return mergedRule |
104 | } | 104 | } |
@@ -106,29 +106,29 @@ function mergeRuleSettings(defaultSetting: TextMateRuleSettings | undefined, ove | |||
106 | function updateRules(scope: string, updatedSettings: TextMateRuleSettings): void { | 106 | function updateRules(scope: string, updatedSettings: TextMateRuleSettings): void { |
107 | [rules.get(scope)] | 107 | [rules.get(scope)] |
108 | .map(settings => mergeRuleSettings(settings, updatedSettings)) | 108 | .map(settings => mergeRuleSettings(settings, updatedSettings)) |
109 | .forEach(settings => rules.set(scope, settings)) | 109 | .forEach(settings => rules.set(scope, settings)); |
110 | } | 110 | } |
111 | 111 | ||
112 | function loadColors(textMateRules: TextMateRule[]): void { | 112 | function loadColors(textMateRules: TextMateRule[]): void { |
113 | textMateRules.forEach(rule => { | 113 | textMateRules.forEach(rule => { |
114 | if (typeof rule.scope === 'string') { | 114 | if (typeof rule.scope === 'string') { |
115 | updateRules(rule.scope, rule.settings) | 115 | updateRules(rule.scope, rule.settings); |
116 | } | 116 | } |
117 | else if (rule.scope instanceof Array) { | 117 | else if (rule.scope instanceof Array) { |
118 | rule.scope.forEach(scope => updateRules(scope, rule.settings)) | 118 | rule.scope.forEach(scope => updateRules(scope, rule.settings)); |
119 | } | 119 | } |
120 | }) | 120 | }) |
121 | } | 121 | } |
122 | 122 | ||
123 | function isFile(filePath: string): boolean { | 123 | function isFile(filePath: string): boolean { |
124 | return [filePath].map(fs.statSync).every(stat => stat.isFile()) | 124 | return [filePath].map(fs.statSync).every(stat => stat.isFile()); |
125 | } | 125 | } |
126 | 126 | ||
127 | function readFileText(filePath: string): string { | 127 | function readFileText(filePath: string): string { |
128 | return fs.readFileSync(filePath, 'utf8') | 128 | return fs.readFileSync(filePath, 'utf8'); |
129 | } | 129 | } |
130 | 130 | ||
131 | // Might need to replace with JSONC if a theme contains comments. | 131 | // Might need to replace with JSONC if a theme contains comments. |
132 | function parseJSON(content: string): any { | 132 | function parseJSON(content: string): any { |
133 | return JSON.parse(content) | 133 | return JSON.parse(content); |
134 | } \ No newline at end of file | 134 | } \ No newline at end of file |
diff --git a/editors/code/src/scopes_mapper.ts b/editors/code/src/scopes_mapper.ts index 19a4213d4..7056a8e24 100644 --- a/editors/code/src/scopes_mapper.ts +++ b/editors/code/src/scopes_mapper.ts | |||
@@ -1,10 +1,9 @@ | |||
1 | import * as vscode from 'vscode' | 1 | import * as vscode from 'vscode'; |
2 | import { TextMateRuleSettings } from './scopes' | 2 | import { TextMateRuleSettings } from './scopes'; |
3 | 3 | ||
4 | 4 | ||
5 | 5 | ||
6 | 6 | let mappings = new Map<string, string[]>(); | |
7 | let mappings = new Map<string, string[]>() | ||
8 | 7 | ||
9 | 8 | ||
10 | const defaultMapping = new Map<string, string[]>([ | 9 | const defaultMapping = new Map<string, string[]>([ |
@@ -27,25 +26,39 @@ const defaultMapping = new Map<string, string[]>([ | |||
27 | ['field', ['variable.object.property', 'meta.field.declaration', 'meta.definition.property', 'variable.other',]], | 26 | ['field', ['variable.object.property', 'meta.field.declaration', 'meta.definition.property', 'variable.other',]], |
28 | ['module', ['entity.name.section', 'entity.other']] | 27 | ['module', ['entity.name.section', 'entity.other']] |
29 | ] | 28 | ] |
30 | ) | 29 | ); |
31 | 30 | ||
32 | // Temporary exported for debugging for now. | 31 | // Temporary exported for debugging for now. |
33 | export function find(scope: string): string[] { | 32 | export function find(scope: string): string[] { |
34 | return mappings.get(scope) || [] | 33 | return mappings.get(scope) || []; |
35 | } | 34 | } |
36 | 35 | ||
37 | export function toRule(scope: string, intoRule: (scope: string) => TextMateRuleSettings | undefined): TextMateRuleSettings | undefined { | 36 | export function toRule(scope: string, intoRule: (scope: string) => TextMateRuleSettings | undefined): TextMateRuleSettings | undefined { |
38 | return find(scope).map(intoRule).filter(rule => rule !== undefined)[0] | 37 | return find(scope).map(intoRule).filter(rule => rule !== undefined)[0]; |
38 | } | ||
39 | |||
40 | |||
41 | function isString(value: any): value is string { | ||
42 | return typeof value === 'string'; | ||
43 | } | ||
44 | |||
45 | function isArrayOfString(value: any): value is string[] { | ||
46 | return Array.isArray(value) && value.every(item => isString(item)); | ||
39 | } | 47 | } |
40 | 48 | ||
41 | 49 | ||
42 | export function load() { | 50 | export function load() { |
43 | const configuration = vscode.workspace | 51 | const rawConfig: { [key: string]: any } = vscode.workspace |
44 | .getConfiguration('rust-analyzer') | 52 | .getConfiguration('rust-analyzer') |
45 | .get('scopeMappings') as Map<string, string[]> | undefined | 53 | .get('scopeMappings') |
46 | || new Map() | 54 | || {}; |
47 | 55 | ||
48 | mappings = new Map([...Array.from(defaultMapping.entries()), ...Array.from(configuration.entries())]) | 56 | mappings = Object |
57 | .entries(rawConfig) | ||
58 | .filter(([_, value]) => isString(value) || isArrayOfString(value)) | ||
59 | .reduce((list, [key, value]: [string, string | string[]]) => { | ||
60 | return list.set(key, isString(value) ? [value] : value); | ||
49 | 61 | ||
62 | }, defaultMapping); | ||
50 | 63 | ||
51 | } \ No newline at end of file | 64 | } \ No newline at end of file |