aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src
diff options
context:
space:
mode:
authorSeivan Heidari <[email protected]>2019-11-04 22:59:11 +0000
committerSeivan Heidari <[email protected]>2019-11-04 22:59:11 +0000
commitc60f9bf4c6d6ddd341c673b228b6aa1add3da62b (patch)
treee411904c533ac37745397719bbfe983aeb21b372 /editors/code/src
parentdad9bc6caad71e6aebb92ad9883c08d30431e9b1 (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/src')
-rw-r--r--editors/code/src/config.ts11
-rw-r--r--editors/code/src/highlighting.ts32
-rw-r--r--editors/code/src/scopes.ts64
-rw-r--r--editors/code/src/scopes_mapper.ts35
4 files changed, 77 insertions, 65 deletions
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 @@
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' 4import * as scopes from './scopes';
5import * as scopesMapper from './scopes_mapper'; 5import * as scopesMapper from './scopes_mapper';
6 6
7import { Server } from './server'; 7import { 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
28function createDecorationFromTextmate(themeStyle: scopes.TextMateRuleSettings): vscode.TextEditorDecorationType { 29function 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
59export class Highlighter { 59export 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 @@
1import * as fs from 'fs' 1import * as fs from 'fs';
2import * as path from 'path' 2import * as path from 'path';
3import * as vscode from 'vscode' 3import * as vscode from 'vscode';
4 4
5 5
6 6
7export interface TextMateRule { 7export interface TextMateRule {
8 scope: string | string[] 8 scope: string | string[];
9 settings: TextMateRuleSettings 9 settings: TextMateRuleSettings;
10} 10}
11 11
12export interface TextMateRuleSettings { 12export 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
19const rules = new Map<string, TextMateRuleSettings>() 19const rules = new Map<string, TextMateRuleSettings>();
20 20
21export function find(scope: string): TextMateRuleSettings | undefined { 21export 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
26export function load() { 26export 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() {
44function filterThemeExtensions(extension: vscode.Extension<any>): boolean { 44function 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
95function mergeRuleSettings(defaultSetting: TextMateRuleSettings | undefined, override: TextMateRuleSettings): TextMateRuleSettings { 95function 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
106function updateRules(scope: string, updatedSettings: TextMateRuleSettings): void { 106function 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
112function loadColors(textMateRules: TextMateRule[]): void { 112function 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
123function isFile(filePath: string): boolean { 123function 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
127function readFileText(filePath: string): string { 127function 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.
132function parseJSON(content: string): any { 132function 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 @@
1import * as vscode from 'vscode' 1import * as vscode from 'vscode';
2import { TextMateRuleSettings } from './scopes' 2import { TextMateRuleSettings } from './scopes';
3 3
4 4
5 5
6 6let mappings = new Map<string, string[]>();
7let mappings = new Map<string, string[]>()
8 7
9 8
10const defaultMapping = new Map<string, string[]>([ 9const 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.
33export function find(scope: string): string[] { 32export function find(scope: string): string[] {
34 return mappings.get(scope) || [] 33 return mappings.get(scope) || [];
35} 34}
36 35
37export function toRule(scope: string, intoRule: (scope: string) => TextMateRuleSettings | undefined): TextMateRuleSettings | undefined { 36export 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
41function isString(value: any): value is string {
42 return typeof value === 'string';
43}
44
45function isArrayOfString(value: any): value is string[] {
46 return Array.isArray(value) && value.every(item => isString(item));
39} 47}
40 48
41 49
42export function load() { 50export 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