From c60f9bf4c6d6ddd341c673b228b6aa1add3da62b Mon Sep 17 00:00:00 2001 From: Seivan Heidari Date: Mon, 4 Nov 2019 23:59:11 +0100 Subject: * 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" } ``` --- editors/code/package.json | 64 ++++++++++++++++++++++++++++++++++++++- editors/code/src/config.ts | 11 +++---- editors/code/src/highlighting.ts | 32 ++++++++++---------- editors/code/src/scopes.ts | 64 +++++++++++++++++++-------------------- editors/code/src/scopes_mapper.ts | 35 ++++++++++++++------- 5 files changed, 140 insertions(+), 66 deletions(-) (limited to 'editors/code') 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 @@ "default": false, "description": "Highlight Rust code (overrides built-in syntax highlighting)" }, + "rust-analyzer.scopeMappings": { + "type": "object", + "definitions": {}, + "properties": { + "comment": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "string": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "keyword": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "keyword.control": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "keyword.unsafe": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "function": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "parameter": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "constant": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "type": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "builtin": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "text": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "attribute": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "literal": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "macro": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "variable": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "variable.mut": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "field": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + }, + "module": { + "$ref": "vscode://schemas/textmate-colors#/items/properties/scope" + } + }, + "additionalProperties": false, + "description": "Mapping Rust Analyzer scopes to TextMateRule scopes list." + }, "rust-analyzer.rainbowHighlightingOn": { "type": "boolean", "default": false, @@ -488,4 +550,4 @@ } ] } -} +} \ 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 { const config = vscode.workspace.getConfiguration('rust-analyzer'); Server.highlighter.removeHighlights(); - scopes.load() - scopesMapper.load() - if (config.has('highlightingOn')) { + if (config.has('highlightingOn')) { this.highlightingOn = config.get('highlightingOn') as boolean; + if (this.highlightingOn) { + scopes.load(); + scopesMapper.load(); + } } if (config.has('rainbowHighlightingOn')) { @@ -61,9 +63,6 @@ export class Config { ) as boolean; } - if (!this.highlightingOn && Server) { - Server.highlighter.removeHighlights(); - } if (config.has('enableEnhancedTyping')) { 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 @@ import seedrandom = require('seedrandom'); import * as vscode from 'vscode'; import * as lc from 'vscode-languageclient'; -import * as scopes from './scopes' +import * as scopes from './scopes'; import * as scopesMapper from './scopes_mapper'; import { Server } from './server'; @@ -25,35 +25,35 @@ function fancify(seed: string, shade: 'light' | 'dark') { return `hsl(${h},${s}%,${l}%)`; } + function createDecorationFromTextmate(themeStyle: scopes.TextMateRuleSettings): vscode.TextEditorDecorationType { - const options: vscode.DecorationRenderOptions = {} - options.rangeBehavior = vscode.DecorationRangeBehavior.OpenOpen + const options: vscode.DecorationRenderOptions = {}; + options.rangeBehavior = vscode.DecorationRangeBehavior.OpenOpen; if (themeStyle.foreground) { - options.color = themeStyle.foreground + options.color = themeStyle.foreground; } if (themeStyle.background) { - options.backgroundColor = themeStyle.background + options.backgroundColor = themeStyle.background; } if (themeStyle.fontStyle) { - const parts: string[] = themeStyle.fontStyle.split(' ') + const parts: string[] = themeStyle.fontStyle.split(' '); parts.forEach((part) => { switch (part) { case 'italic': - options.fontStyle = 'italic' - break + options.fontStyle = 'italic'; + break; case 'bold': - options.fontWeight = 'bold' - - break + options.fontWeight = 'bold'; + break; case 'underline': - options.textDecoration = 'underline' - break + options.textDecoration = 'underline'; + break; default: - break + break; } }) } - return vscode.window.createTextEditorDecorationType(options) + return vscode.window.createTextEditorDecorationType(options); } export class Highlighter { @@ -66,7 +66,7 @@ export class Highlighter { textDecoration?: string ): [string, vscode.TextEditorDecorationType] => { - const rule = scopesMapper.toRule(tag, scopes.find) + const rule = scopesMapper.toRule(tag, scopes.find); if (rule) { 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 @@ -import * as fs from 'fs' -import * as path from 'path' -import * as vscode from 'vscode' +import * as fs from 'fs'; +import * as path from 'path'; +import * as vscode from 'vscode'; export interface TextMateRule { - scope: string | string[] - settings: TextMateRuleSettings + scope: string | string[]; + settings: TextMateRuleSettings; } export interface TextMateRuleSettings { - foreground: string | undefined - background: string | undefined - fontStyle: string | undefined + foreground: string | undefined; + background: string | undefined; + fontStyle: string | undefined; } // Current theme colors -const rules = new Map() +const rules = new Map(); export function find(scope: string): TextMateRuleSettings | undefined { - return rules.get(scope) + return rules.get(scope); } // Load all textmate scopes in the currently active theme export function load() { // Remove any previous theme - rules.clear() + rules.clear(); // Find out current color theme - const themeName = vscode.workspace.getConfiguration('workbench').get('colorTheme') + const themeName = vscode.workspace.getConfiguration('workbench').get('colorTheme'); if (typeof themeName !== 'string') { // console.warn('workbench.colorTheme is', themeName) - return + return; } // Try to load colors from that theme try { - loadThemeNamed(themeName) + loadThemeNamed(themeName); } catch (e) { // console.warn('failed to load theme', themeName, e) } @@ -44,7 +44,7 @@ export function load() { function filterThemeExtensions(extension: vscode.Extension): boolean { return extension.extensionKind === vscode.ExtensionKind.UI && extension.packageJSON.contributes && - extension.packageJSON.contributes.themes + extension.packageJSON.contributes.themes; } @@ -59,17 +59,17 @@ function loadThemeNamed(themeName: string) { .filter((element: any) => (element.id || element.label) === themeName) .map((element: any) => path.join(extension.extensionPath, element.path)) .concat(list) - }, Array()) + }, Array()); - themePaths.forEach(loadThemeFile) + themePaths.forEach(loadThemeFile); const tokenColorCustomizations: [any] = [vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations')] tokenColorCustomizations .filter(custom => custom && custom.textMateRules) .map(custom => custom.textMateRules) - .forEach(loadColors) + .forEach(loadColors); } @@ -79,26 +79,26 @@ function loadThemeFile(themePath: string) { .filter(isFile) .map(readFileText) .map(parseJSON) - .filter(theme => theme) + .filter(theme => theme); themeContent .filter(theme => theme.tokenColors) .map(theme => theme.tokenColors) - .forEach(loadColors) + .forEach(loadColors); themeContent .filter(theme => theme.include) .map(theme => path.join(path.dirname(themePath), theme.include)) - .forEach(loadThemeFile) + .forEach(loadThemeFile); } function mergeRuleSettings(defaultSetting: TextMateRuleSettings | undefined, override: TextMateRuleSettings): TextMateRuleSettings { - if (defaultSetting === undefined) { return override } - const mergedRule = defaultSetting + if (defaultSetting === undefined) { return override; } + const mergedRule = defaultSetting; - mergedRule.background = override.background || defaultSetting.background - mergedRule.foreground = override.foreground || defaultSetting.foreground - mergedRule.fontStyle = override.fontStyle || defaultSetting.foreground + mergedRule.background = override.background || defaultSetting.background; + mergedRule.foreground = override.foreground || defaultSetting.foreground; + mergedRule.fontStyle = override.fontStyle || defaultSetting.foreground; return mergedRule } @@ -106,29 +106,29 @@ function mergeRuleSettings(defaultSetting: TextMateRuleSettings | undefined, ove function updateRules(scope: string, updatedSettings: TextMateRuleSettings): void { [rules.get(scope)] .map(settings => mergeRuleSettings(settings, updatedSettings)) - .forEach(settings => rules.set(scope, settings)) + .forEach(settings => rules.set(scope, settings)); } function loadColors(textMateRules: TextMateRule[]): void { textMateRules.forEach(rule => { if (typeof rule.scope === 'string') { - updateRules(rule.scope, rule.settings) + updateRules(rule.scope, rule.settings); } else if (rule.scope instanceof Array) { - rule.scope.forEach(scope => updateRules(scope, rule.settings)) + rule.scope.forEach(scope => updateRules(scope, rule.settings)); } }) } function isFile(filePath: string): boolean { - return [filePath].map(fs.statSync).every(stat => stat.isFile()) + return [filePath].map(fs.statSync).every(stat => stat.isFile()); } function readFileText(filePath: string): string { - return fs.readFileSync(filePath, 'utf8') + return fs.readFileSync(filePath, 'utf8'); } // Might need to replace with JSONC if a theme contains comments. function parseJSON(content: string): any { - return JSON.parse(content) + return JSON.parse(content); } \ 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 @@ -import * as vscode from 'vscode' -import { TextMateRuleSettings } from './scopes' +import * as vscode from 'vscode'; +import { TextMateRuleSettings } from './scopes'; - -let mappings = new Map() +let mappings = new Map(); const defaultMapping = new Map([ @@ -27,25 +26,39 @@ const defaultMapping = new Map([ ['field', ['variable.object.property', 'meta.field.declaration', 'meta.definition.property', 'variable.other',]], ['module', ['entity.name.section', 'entity.other']] ] -) +); // Temporary exported for debugging for now. export function find(scope: string): string[] { - return mappings.get(scope) || [] + return mappings.get(scope) || []; } export function toRule(scope: string, intoRule: (scope: string) => TextMateRuleSettings | undefined): TextMateRuleSettings | undefined { - return find(scope).map(intoRule).filter(rule => rule !== undefined)[0] + return find(scope).map(intoRule).filter(rule => rule !== undefined)[0]; +} + + +function isString(value: any): value is string { + return typeof value === 'string'; +} + +function isArrayOfString(value: any): value is string[] { + return Array.isArray(value) && value.every(item => isString(item)); } export function load() { - const configuration = vscode.workspace + const rawConfig: { [key: string]: any } = vscode.workspace .getConfiguration('rust-analyzer') - .get('scopeMappings') as Map | undefined - || new Map() + .get('scopeMappings') + || {}; - mappings = new Map([...Array.from(defaultMapping.entries()), ...Array.from(configuration.entries())]) + mappings = Object + .entries(rawConfig) + .filter(([_, value]) => isString(value) || isArrayOfString(value)) + .reduce((list, [key, value]: [string, string | string[]]) => { + return list.set(key, isString(value) ? [value] : value); + }, defaultMapping); } \ No newline at end of file -- cgit v1.2.3