Proof of concept theming and 'tokenColorCustomizations' support.
This commit is contained in:
parent
95cf5c86fa
commit
3e8616cf6d
11
editors/code/package-lock.json
generated
11
editors/code/package-lock.json
generated
@ -598,9 +598,9 @@
|
||||
}
|
||||
},
|
||||
"https-proxy-agent": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz",
|
||||
"integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.3.tgz",
|
||||
"integrity": "sha512-Ytgnz23gm2DVftnzqRRz2dOXZbGd2uiajSw/95bPp6v53zPRspQjLm/AfBgqbJ2qfeRXWIOMVLpp86+/5yX39Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"agent-base": "^4.3.0",
|
||||
@ -720,6 +720,11 @@
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"jsonc-parser": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.1.1.tgz",
|
||||
"integrity": "sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g=="
|
||||
},
|
||||
"lcid": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
|
||||
|
@ -32,7 +32,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"seedrandom": "^3.0.1",
|
||||
"vscode-languageclient": "^5.3.0-next.4"
|
||||
"vscode-languageclient": "^5.3.0-next.4",
|
||||
"jsonc-parser": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^7.1.1",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as scopes from './scopes';
|
||||
import { Server } from './server';
|
||||
|
||||
const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG;
|
||||
@ -46,7 +46,11 @@ export class Config {
|
||||
|
||||
public userConfigChanged() {
|
||||
const config = vscode.workspace.getConfiguration('rust-analyzer');
|
||||
|
||||
Server.highlighter.removeHighlights();
|
||||
scopes.load()
|
||||
if (config.has('highlightingOn')) {
|
||||
|
||||
this.highlightingOn = config.get('highlightingOn') as boolean;
|
||||
}
|
||||
|
||||
|
@ -91,11 +91,11 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
const allNotifications: Iterable<
|
||||
[string, lc.GenericNotificationHandler]
|
||||
> = [
|
||||
[
|
||||
'rust-analyzer/publishDecorations',
|
||||
notifications.publishDecorations.handle
|
||||
]
|
||||
];
|
||||
[
|
||||
'rust-analyzer/publishDecorations',
|
||||
notifications.publishDecorations.handle
|
||||
]
|
||||
];
|
||||
const syntaxTreeContentProvider = new SyntaxTreeContentProvider();
|
||||
|
||||
// The events below are plain old javascript events, triggered and handled by vscode
|
||||
|
@ -1,6 +1,8 @@
|
||||
import seedrandom = require('seedrandom');
|
||||
import * as vscode from 'vscode';
|
||||
import * as lc from 'vscode-languageclient';
|
||||
import * as scopes from './scopes'
|
||||
|
||||
|
||||
import { Server } from './server';
|
||||
|
||||
@ -23,6 +25,37 @@ 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
|
||||
if (themeStyle.foreground) {
|
||||
options.color = themeStyle.foreground
|
||||
}
|
||||
if (themeStyle.background) {
|
||||
options.backgroundColor = themeStyle.background
|
||||
}
|
||||
if (themeStyle.fontStyle) {
|
||||
const parts: string[] = themeStyle.fontStyle.split(' ')
|
||||
parts.forEach((part) => {
|
||||
switch (part) {
|
||||
case 'italic':
|
||||
options.fontStyle = 'italic'
|
||||
break
|
||||
case 'bold':
|
||||
options.fontWeight = 'bold'
|
||||
|
||||
break
|
||||
case 'underline':
|
||||
options.textDecoration = 'underline'
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
return vscode.window.createTextEditorDecorationType(options)
|
||||
}
|
||||
|
||||
export class Highlighter {
|
||||
private static initDecorations(): Map<
|
||||
string,
|
||||
@ -32,36 +65,44 @@ export class Highlighter {
|
||||
tag: string,
|
||||
textDecoration?: string
|
||||
): [string, vscode.TextEditorDecorationType] => {
|
||||
const color = new vscode.ThemeColor('ralsp.' + tag);
|
||||
const decor = vscode.window.createTextEditorDecorationType({
|
||||
color,
|
||||
textDecoration
|
||||
});
|
||||
return [tag, decor];
|
||||
const scope = scopes.find(tag)
|
||||
|
||||
if (scope) {
|
||||
const decor = createDecorationFromTextmate(scope);
|
||||
return [tag, decor];
|
||||
}
|
||||
else {
|
||||
const color = new vscode.ThemeColor('ralsp.' + tag);
|
||||
const decor = vscode.window.createTextEditorDecorationType({
|
||||
color,
|
||||
textDecoration
|
||||
});
|
||||
return [tag, decor];
|
||||
}
|
||||
};
|
||||
|
||||
const decorations: Iterable<
|
||||
[string, vscode.TextEditorDecorationType]
|
||||
> = [
|
||||
decoration('comment'),
|
||||
decoration('string'),
|
||||
decoration('keyword'),
|
||||
decoration('keyword.control'),
|
||||
decoration('keyword.unsafe'),
|
||||
decoration('function'),
|
||||
decoration('parameter'),
|
||||
decoration('constant'),
|
||||
decoration('type'),
|
||||
decoration('builtin'),
|
||||
decoration('text'),
|
||||
decoration('attribute'),
|
||||
decoration('literal'),
|
||||
decoration('macro'),
|
||||
decoration('variable'),
|
||||
decoration('variable.mut', 'underline'),
|
||||
decoration('field'),
|
||||
decoration('module')
|
||||
];
|
||||
decoration('comment'),
|
||||
decoration('string'),
|
||||
decoration('keyword'),
|
||||
decoration('keyword.control'),
|
||||
decoration('keyword.unsafe'),
|
||||
decoration('function'),
|
||||
decoration('parameter'),
|
||||
decoration('constant'),
|
||||
decoration('type'),
|
||||
decoration('builtin'),
|
||||
decoration('text'),
|
||||
decoration('attribute'),
|
||||
decoration('literal'),
|
||||
decoration('macro'),
|
||||
decoration('variable'),
|
||||
decoration('variable.mut', 'underline'),
|
||||
decoration('field'),
|
||||
decoration('module')
|
||||
];
|
||||
|
||||
return new Map<string, vscode.TextEditorDecorationType>(decorations);
|
||||
}
|
||||
@ -89,6 +130,8 @@ export class Highlighter {
|
||||
//
|
||||
// Note: decoration objects need to be kept around so we can dispose them
|
||||
// if the user disables syntax highlighting
|
||||
|
||||
|
||||
if (this.decorations == null) {
|
||||
this.decorations = Highlighter.initDecorations();
|
||||
}
|
||||
@ -133,6 +176,7 @@ export class Highlighter {
|
||||
tag
|
||||
) as vscode.TextEditorDecorationType;
|
||||
const ranges = byTag.get(tag)!;
|
||||
|
||||
editor.setDecorations(dec, ranges);
|
||||
}
|
||||
|
||||
|
142
editors/code/src/scopes.ts
Normal file
142
editors/code/src/scopes.ts
Normal file
@ -0,0 +1,142 @@
|
||||
import * as fs from 'fs'
|
||||
import * as jsonc from 'jsonc-parser'
|
||||
import * as path from 'path'
|
||||
import * as vscode from 'vscode'
|
||||
|
||||
|
||||
|
||||
export interface TextMateRule {
|
||||
scope: string | string[]
|
||||
settings: TextMateRuleSettings
|
||||
}
|
||||
|
||||
export interface TextMateRuleSettings {
|
||||
foreground: string | undefined
|
||||
background: string | undefined
|
||||
fontStyle: string | undefined
|
||||
}
|
||||
|
||||
// Current theme colors
|
||||
const colors = new Map<string, TextMateRuleSettings>()
|
||||
|
||||
export function find(scope: string): TextMateRuleSettings | undefined {
|
||||
return colors.get(scope)
|
||||
}
|
||||
|
||||
// Load all textmate scopes in the currently active theme
|
||||
export function load() {
|
||||
// Remove any previous theme
|
||||
colors.clear()
|
||||
// Find out current color theme
|
||||
const themeName = vscode.workspace.getConfiguration('workbench').get('colorTheme')
|
||||
|
||||
if (typeof themeName !== 'string') {
|
||||
console.warn('workbench.colorTheme is', themeName)
|
||||
return
|
||||
}
|
||||
// Try to load colors from that theme
|
||||
try {
|
||||
loadThemeNamed(themeName)
|
||||
} catch (e) {
|
||||
console.warn('failed to load theme', themeName, e)
|
||||
}
|
||||
}
|
||||
|
||||
// Find current theme on disk
|
||||
function loadThemeNamed(themeName: string) {
|
||||
for (const extension of vscode.extensions.all) {
|
||||
const extensionPath: string = extension.extensionPath
|
||||
const extensionPackageJsonPath: string = path.join(extensionPath, 'package.json')
|
||||
if (!checkFileExists(extensionPackageJsonPath)) {
|
||||
continue
|
||||
}
|
||||
const packageJsonText: string = readFileText(extensionPackageJsonPath)
|
||||
const packageJson: any = jsonc.parse(packageJsonText)
|
||||
if (packageJson.contributes && packageJson.contributes.themes) {
|
||||
for (const theme of packageJson.contributes.themes) {
|
||||
const id = theme.id || theme.label
|
||||
if (id === themeName) {
|
||||
const themeRelativePath: string = theme.path
|
||||
const themeFullPath: string = path.join(extensionPath, themeRelativePath)
|
||||
loadThemeFile(themeFullPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const customization: any = vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations');
|
||||
if (customization && customization.textMateRules) {
|
||||
loadColors(customization.textMateRules)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadThemeFile(themePath: string) {
|
||||
if (checkFileExists(themePath)) {
|
||||
const themeContentText: string = readFileText(themePath)
|
||||
const themeContent: any = jsonc.parse(themeContentText)
|
||||
|
||||
if (themeContent && themeContent.tokenColors) {
|
||||
loadColors(themeContent.tokenColors)
|
||||
if (themeContent.include) {
|
||||
// parse included theme file
|
||||
const includedThemePath: string = path.join(path.dirname(themePath), themeContent.include)
|
||||
loadThemeFile(includedThemePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function mergeRuleSettings(defaultRule: TextMateRuleSettings, override: TextMateRuleSettings): TextMateRuleSettings {
|
||||
const mergedRule = defaultRule;
|
||||
if (override.background) {
|
||||
mergedRule.background = override.background
|
||||
}
|
||||
if (override.foreground) {
|
||||
mergedRule.foreground = override.foreground
|
||||
}
|
||||
if (override.background) {
|
||||
mergedRule.fontStyle = override.fontStyle
|
||||
}
|
||||
return mergedRule;
|
||||
}
|
||||
|
||||
function loadColors(textMateRules: TextMateRule[]): void {
|
||||
for (const rule of textMateRules) {
|
||||
|
||||
if (typeof rule.scope === 'string') {
|
||||
const existingRule = colors.get(rule.scope);
|
||||
if (existingRule) {
|
||||
colors.set(rule.scope, mergeRuleSettings(existingRule, rule.settings))
|
||||
}
|
||||
else {
|
||||
colors.set(rule.scope, rule.settings)
|
||||
}
|
||||
} else if (rule.scope instanceof Array) {
|
||||
for (const scope of rule.scope) {
|
||||
const existingRule = colors.get(scope);
|
||||
if (existingRule) {
|
||||
colors.set(scope, mergeRuleSettings(existingRule, rule.settings))
|
||||
}
|
||||
else {
|
||||
colors.set(scope, rule.settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkFileExists(filePath: string): boolean {
|
||||
|
||||
const stats = fs.statSync(filePath);
|
||||
if (stats && stats.isFile()) {
|
||||
return true;
|
||||
} else {
|
||||
console.warn('no such file', filePath)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function readFileText(filePath: string, encoding: string = 'utf8'): string {
|
||||
return fs.readFileSync(filePath, encoding);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user