mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 20:43:21 +00:00
Merge #3820
3820: Remove old syntax highlighting r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
98f7842e40
9 changed files with 3 additions and 359 deletions
|
@ -16,7 +16,6 @@ use serde::Deserialize;
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
pub client_caps: ClientCapsConfig,
|
||||
pub publish_decorations: bool,
|
||||
pub publish_diagnostics: bool,
|
||||
pub notifications: NotificationsConfig,
|
||||
pub inlay_hints: InlayHintsConfig,
|
||||
|
@ -60,7 +59,6 @@ pub struct ClientCapsConfig {
|
|||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
publish_decorations: false,
|
||||
publish_diagnostics: true,
|
||||
notifications: NotificationsConfig {
|
||||
workspace_loaded: true,
|
||||
|
@ -105,7 +103,6 @@ impl Config {
|
|||
*self = Default::default();
|
||||
self.client_caps = client_caps;
|
||||
|
||||
set(value, "/publishDecorations", &mut self.publish_decorations);
|
||||
set(value, "/excludeGlobs", &mut self.exclude_globs);
|
||||
set(value, "/useClientWatching", &mut self.use_client_watching);
|
||||
set(value, "/lruCapacity", &mut self.lru_capacity);
|
||||
|
|
|
@ -250,9 +250,7 @@ impl fmt::Debug for Event {
|
|||
}
|
||||
}
|
||||
Event::Task(Task::Notify(not)) => {
|
||||
if notification_is::<req::PublishDecorations>(not)
|
||||
|| notification_is::<req::PublishDiagnostics>(not)
|
||||
{
|
||||
if notification_is::<req::PublishDiagnostics>(not) {
|
||||
return debug_verbose_not(not, f);
|
||||
}
|
||||
}
|
||||
|
@ -427,7 +425,6 @@ fn loop_turn(
|
|||
update_file_notifications_on_threadpool(
|
||||
pool,
|
||||
world_state.snapshot(),
|
||||
world_state.config.publish_decorations,
|
||||
task_sender.clone(),
|
||||
loop_state.subscriptions.subscriptions(),
|
||||
)
|
||||
|
@ -508,7 +505,6 @@ fn on_request(
|
|||
.on::<req::GotoTypeDefinition>(handlers::handle_goto_type_definition)?
|
||||
.on::<req::ParentModule>(handlers::handle_parent_module)?
|
||||
.on::<req::Runnables>(handlers::handle_runnables)?
|
||||
.on::<req::DecorationsRequest>(handlers::handle_decorations)?
|
||||
.on::<req::Completion>(handlers::handle_completion)?
|
||||
.on::<req::CodeActionRequest>(handlers::handle_code_action)?
|
||||
.on::<req::CodeLensRequest>(handlers::handle_code_lens)?
|
||||
|
@ -884,7 +880,6 @@ where
|
|||
fn update_file_notifications_on_threadpool(
|
||||
pool: &ThreadPool,
|
||||
world: WorldSnapshot,
|
||||
publish_decorations: bool,
|
||||
task_sender: Sender<Task>,
|
||||
subscriptions: Vec<FileId>,
|
||||
) {
|
||||
|
@ -904,19 +899,6 @@ fn update_file_notifications_on_threadpool(
|
|||
}
|
||||
}
|
||||
}
|
||||
if publish_decorations {
|
||||
match handlers::publish_decorations(&world, file_id) {
|
||||
Err(e) => {
|
||||
if !is_canceled(&e) {
|
||||
log::error!("failed to compute decorations: {:?}", e);
|
||||
}
|
||||
}
|
||||
Ok(params) => {
|
||||
let not = notification_new::<req::PublishDecorations>(params);
|
||||
task_sender.send(Task::Notify(not)).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ use crate::{
|
|||
},
|
||||
diagnostics::DiagnosticTask,
|
||||
from_json,
|
||||
req::{self, Decoration, InlayHint, InlayHintsParams},
|
||||
req::{self, InlayHint, InlayHintsParams},
|
||||
semantic_tokens::SemanticTokensBuilder,
|
||||
world::WorldSnapshot,
|
||||
LspError, Result,
|
||||
|
@ -389,15 +389,6 @@ pub fn handle_runnables(
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn handle_decorations(
|
||||
world: WorldSnapshot,
|
||||
params: TextDocumentIdentifier,
|
||||
) -> Result<Vec<Decoration>> {
|
||||
let _p = profile("handle_decorations");
|
||||
let file_id = params.try_conv_with(&world)?;
|
||||
highlight(&world, file_id)
|
||||
}
|
||||
|
||||
pub fn handle_completion(
|
||||
world: WorldSnapshot,
|
||||
params: req::CompletionParams,
|
||||
|
@ -970,15 +961,6 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia
|
|||
Ok(DiagnosticTask::SetNative(file_id, diagnostics))
|
||||
}
|
||||
|
||||
pub fn publish_decorations(
|
||||
world: &WorldSnapshot,
|
||||
file_id: FileId,
|
||||
) -> Result<req::PublishDecorationsParams> {
|
||||
let _p = profile("publish_decorations");
|
||||
let uri = world.file_id_to_uri(file_id)?;
|
||||
Ok(req::PublishDecorationsParams { uri, decorations: highlight(&world, file_id)? })
|
||||
}
|
||||
|
||||
fn to_lsp_runnable(
|
||||
world: &WorldSnapshot,
|
||||
file_id: FileId,
|
||||
|
@ -1008,21 +990,6 @@ fn to_lsp_runnable(
|
|||
})
|
||||
}
|
||||
|
||||
fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>> {
|
||||
let line_index = world.analysis().file_line_index(file_id)?;
|
||||
let res = world
|
||||
.analysis()
|
||||
.highlight(file_id)?
|
||||
.into_iter()
|
||||
.map(|h| Decoration {
|
||||
range: h.range.conv_with(&line_index),
|
||||
tag: h.highlight.to_string(),
|
||||
binding_hash: h.binding_hash.map(|x| x.to_string()),
|
||||
})
|
||||
.collect();
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn handle_inlay_hints(
|
||||
world: WorldSnapshot,
|
||||
params: InlayHintsParams,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Defines `rust-analyzer` specific custom messages.
|
||||
|
||||
use lsp_types::{Location, Position, Range, TextDocumentIdentifier, Url};
|
||||
use lsp_types::{Location, Position, Range, TextDocumentIdentifier};
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -86,36 +86,6 @@ pub struct FindMatchingBraceParams {
|
|||
pub offsets: Vec<Position>,
|
||||
}
|
||||
|
||||
pub enum DecorationsRequest {}
|
||||
|
||||
impl Request for DecorationsRequest {
|
||||
type Params = TextDocumentIdentifier;
|
||||
type Result = Vec<Decoration>;
|
||||
const METHOD: &'static str = "rust-analyzer/decorationsRequest";
|
||||
}
|
||||
|
||||
pub enum PublishDecorations {}
|
||||
|
||||
impl Notification for PublishDecorations {
|
||||
type Params = PublishDecorationsParams;
|
||||
const METHOD: &'static str = "rust-analyzer/publishDecorations";
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PublishDecorationsParams {
|
||||
pub uri: Url,
|
||||
pub decorations: Vec<Decoration>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Decoration {
|
||||
pub range: Range,
|
||||
pub tag: String,
|
||||
pub binding_hash: Option<String>,
|
||||
}
|
||||
|
||||
pub enum ParentModule {}
|
||||
|
||||
impl Request for ParentModule {
|
||||
|
|
|
@ -182,16 +182,6 @@
|
|||
"default": false,
|
||||
"description": "Use proposed semantic tokens API for syntax highlighting"
|
||||
},
|
||||
"rust-analyzer.highlightingOn": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Highlight Rust code (overrides built-in syntax highlighting)"
|
||||
},
|
||||
"rust-analyzer.rainbowHighlightingOn": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "When highlighting Rust code, use a unique color per identifier"
|
||||
},
|
||||
"rust-analyzer.featureFlags": {
|
||||
"type": "object",
|
||||
"default": {},
|
||||
|
|
|
@ -7,7 +7,6 @@ import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-
|
|||
|
||||
export function configToServerOptions(config: Config) {
|
||||
return {
|
||||
publishDecorations: !config.highlightingSemanticTokens,
|
||||
lruCapacity: config.lruCapacity,
|
||||
|
||||
inlayHintsType: config.inlayHints.typeHints,
|
||||
|
|
|
@ -71,8 +71,6 @@ export class Config {
|
|||
get channel() { return this.cfg.get<UpdatesChannel>("updates.channel")!; }
|
||||
get askBeforeDownload() { return this.cfg.get<boolean>("updates.askBeforeDownload")!; }
|
||||
get highlightingSemanticTokens() { return this.cfg.get<boolean>("highlighting.semanticTokens")!; }
|
||||
get highlightingOn() { return this.cfg.get<boolean>("highlightingOn")!; }
|
||||
get rainbowHighlightingOn() { return this.cfg.get<boolean>("rainbowHighlightingOn")!; }
|
||||
get lruCapacity() { return this.cfg.get<null | number>("lruCapacity")!; }
|
||||
get excludeGlobs() { return this.cfg.get<string[]>("excludeGlobs")!; }
|
||||
get useClientWatching() { return this.cfg.get<boolean>("useClientWatching")!; }
|
||||
|
|
|
@ -1,255 +0,0 @@
|
|||
import * as vscode from 'vscode';
|
||||
import * as ra from './rust-analyzer-api';
|
||||
|
||||
import { ColorTheme, TextMateRuleSettings } from './color_theme';
|
||||
|
||||
import { Ctx } from './ctx';
|
||||
import { sendRequestWithRetry, isRustDocument } from './util';
|
||||
|
||||
export function activateHighlighting(ctx: Ctx) {
|
||||
const highlighter = new Highlighter(ctx);
|
||||
|
||||
ctx.client.onNotification(ra.publishDecorations, params => {
|
||||
if (!ctx.config.highlightingOn) return;
|
||||
|
||||
const targetEditor = vscode.window.visibleTextEditors.find(
|
||||
editor => {
|
||||
const unescapedUri = unescape(
|
||||
editor.document.uri.toString(),
|
||||
);
|
||||
// Unescaped URI looks like:
|
||||
// file:///c:/Workspace/ra-test/src/main.rs
|
||||
return unescapedUri === params.uri;
|
||||
},
|
||||
);
|
||||
if (!targetEditor) return;
|
||||
|
||||
highlighter.setHighlights(targetEditor, params.decorations);
|
||||
});
|
||||
|
||||
|
||||
vscode.workspace.onDidChangeConfiguration(
|
||||
_ => highlighter.removeHighlights(),
|
||||
null,
|
||||
ctx.subscriptions,
|
||||
);
|
||||
|
||||
vscode.window.onDidChangeActiveTextEditor(
|
||||
async (editor: vscode.TextEditor | undefined) => {
|
||||
if (!editor || !isRustDocument(editor.document)) return;
|
||||
if (!ctx.config.highlightingOn) return;
|
||||
const client = ctx.client;
|
||||
if (!client) return;
|
||||
|
||||
const decorations = await sendRequestWithRetry(
|
||||
client,
|
||||
ra.decorationsRequest,
|
||||
{ uri: editor.document.uri.toString() },
|
||||
);
|
||||
highlighter.setHighlights(editor, decorations);
|
||||
},
|
||||
null,
|
||||
ctx.subscriptions,
|
||||
);
|
||||
}
|
||||
|
||||
// Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76
|
||||
function fancify(seed: string, shade: 'light' | 'dark') {
|
||||
const random = randomU32Numbers(hashString(seed));
|
||||
const randomInt = (min: number, max: number) => {
|
||||
return Math.abs(random()) % (max - min + 1) + min;
|
||||
};
|
||||
|
||||
const h = randomInt(0, 360);
|
||||
const s = randomInt(42, 98);
|
||||
const l = shade === 'light' ? randomInt(15, 40) : randomInt(40, 90);
|
||||
return `hsl(${h},${s}%,${l}%)`;
|
||||
}
|
||||
|
||||
class Highlighter {
|
||||
private ctx: Ctx;
|
||||
private decorations: Map<
|
||||
string,
|
||||
vscode.TextEditorDecorationType
|
||||
> | null = null;
|
||||
|
||||
constructor(ctx: Ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
public removeHighlights() {
|
||||
if (this.decorations == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Decorations are removed when the object is disposed
|
||||
for (const decoration of this.decorations.values()) {
|
||||
decoration.dispose();
|
||||
}
|
||||
|
||||
this.decorations = null;
|
||||
}
|
||||
|
||||
public setHighlights(editor: vscode.TextEditor, highlights: ra.Decoration[]) {
|
||||
const client = this.ctx.client;
|
||||
if (!client) return;
|
||||
// Initialize decorations if necessary
|
||||
//
|
||||
// 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 = initDecorations();
|
||||
}
|
||||
|
||||
const byTag: Map<string, vscode.Range[]> = new Map();
|
||||
const colorfulIdents: Map<
|
||||
string,
|
||||
[vscode.Range[], boolean]
|
||||
> = new Map();
|
||||
const rainbowTime = this.ctx.config.rainbowHighlightingOn;
|
||||
|
||||
for (const tag of this.decorations.keys()) {
|
||||
byTag.set(tag, []);
|
||||
}
|
||||
|
||||
for (const d of highlights) {
|
||||
if (!byTag.get(d.tag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rainbowTime && d.bindingHash) {
|
||||
if (!colorfulIdents.has(d.bindingHash)) {
|
||||
const mut = d.tag.endsWith('.mut');
|
||||
colorfulIdents.set(d.bindingHash, [[], mut]);
|
||||
}
|
||||
colorfulIdents
|
||||
.get(d.bindingHash)![0]
|
||||
.push(
|
||||
client.protocol2CodeConverter.asRange(d.range),
|
||||
);
|
||||
} else {
|
||||
byTag
|
||||
.get(d.tag)!
|
||||
.push(
|
||||
client.protocol2CodeConverter.asRange(d.range),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const tag of byTag.keys()) {
|
||||
const dec = this.decorations.get(
|
||||
tag,
|
||||
) as vscode.TextEditorDecorationType;
|
||||
const ranges = byTag.get(tag)!;
|
||||
editor.setDecorations(dec, ranges);
|
||||
}
|
||||
|
||||
for (const [hash, [ranges, mut]] of colorfulIdents.entries()) {
|
||||
const textDecoration = mut ? 'underline' : undefined;
|
||||
const dec = vscode.window.createTextEditorDecorationType({
|
||||
light: { color: fancify(hash, 'light'), textDecoration },
|
||||
dark: { color: fancify(hash, 'dark'), textDecoration },
|
||||
});
|
||||
editor.setDecorations(dec, ranges);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initDecorations(): Map<string, vscode.TextEditorDecorationType> {
|
||||
const theme = ColorTheme.load();
|
||||
const res = new Map();
|
||||
TAG_TO_SCOPES.forEach((scopes, tag) => {
|
||||
// We are going to axe this soon, so don't try to detect unknown tags.
|
||||
// Users should switch to the new semantic tokens implementation.
|
||||
if (!scopes) return;
|
||||
const rule = theme.lookup(scopes);
|
||||
const decor = createDecorationFromTextmate(rule);
|
||||
res.set(tag, decor);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
function createDecorationFromTextmate(
|
||||
themeStyle: TextMateRuleSettings,
|
||||
): vscode.TextEditorDecorationType {
|
||||
const decorationOptions: vscode.DecorationRenderOptions = {};
|
||||
decorationOptions.rangeBehavior = vscode.DecorationRangeBehavior.OpenOpen;
|
||||
|
||||
if (themeStyle.foreground) {
|
||||
decorationOptions.color = themeStyle.foreground;
|
||||
}
|
||||
|
||||
if (themeStyle.background) {
|
||||
decorationOptions.backgroundColor = themeStyle.background;
|
||||
}
|
||||
|
||||
if (themeStyle.fontStyle) {
|
||||
const parts: string[] = themeStyle.fontStyle.split(' ');
|
||||
parts.forEach(part => {
|
||||
switch (part) {
|
||||
case 'italic':
|
||||
decorationOptions.fontStyle = 'italic';
|
||||
break;
|
||||
case 'bold':
|
||||
decorationOptions.fontWeight = 'bold';
|
||||
break;
|
||||
case 'underline':
|
||||
decorationOptions.textDecoration = 'underline';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
return vscode.window.createTextEditorDecorationType(decorationOptions);
|
||||
}
|
||||
|
||||
// sync with tags from `syntax_highlighting.rs`.
|
||||
const TAG_TO_SCOPES = new Map<string, string[]>([
|
||||
["field", ["entity.name.field"]],
|
||||
["function", ["entity.name.function"]],
|
||||
["module", ["entity.name.module"]],
|
||||
["constant", ["entity.name.constant"]],
|
||||
["macro", ["entity.name.macro"]],
|
||||
|
||||
["variable", ["variable"]],
|
||||
["variable.mutable", ["variable", "meta.mutable"]],
|
||||
|
||||
["type", ["entity.name.type"]],
|
||||
["type.builtin", ["entity.name.type", "support.type.primitive"]],
|
||||
["type.self", ["entity.name.type.parameter.self"]],
|
||||
["type.param", ["entity.name.type.parameter", "entity.name.type.param.rust"]],
|
||||
["type.lifetime", ["entity.name.type.lifetime", "entity.name.lifetime.rust"]],
|
||||
|
||||
["literal.byte", ["constant.character.byte"]],
|
||||
["literal.char", ["constant.character.rust"]],
|
||||
["numeric_literal", ["constant.numeric"]],
|
||||
|
||||
["comment", ["comment"]],
|
||||
["string_literal", ["string.quoted"]],
|
||||
["attribute", ["meta.attribute.rust"]],
|
||||
|
||||
["keyword", ["keyword"]],
|
||||
["keyword.unsafe", ["keyword.other.unsafe"]],
|
||||
["keyword.control", ["keyword.control"]],
|
||||
]);
|
||||
|
||||
function randomU32Numbers(seed: number) {
|
||||
let random = seed | 0;
|
||||
return () => {
|
||||
random ^= random << 13;
|
||||
random ^= random >> 17;
|
||||
random ^= random << 5;
|
||||
random |= 0;
|
||||
return random;
|
||||
};
|
||||
}
|
||||
|
||||
function hashString(str: string): number {
|
||||
let res = 0;
|
||||
for (let i = 0; i < str.length; ++i) {
|
||||
const c = str.codePointAt(i)!;
|
||||
res = (res * 31 + c) & ~0;
|
||||
}
|
||||
return res;
|
||||
}
|
|
@ -7,7 +7,6 @@ import * as commands from './commands';
|
|||
import { activateInlayHints } from './inlay_hints';
|
||||
import { activateStatusDisplay } from './status_display';
|
||||
import { Ctx } from './ctx';
|
||||
import { activateHighlighting } from './highlighting';
|
||||
import { Config, NIGHTLY_TAG } from './config';
|
||||
import { log, assert } from './util';
|
||||
import { PersistentState } from './persistent_state';
|
||||
|
@ -97,9 +96,6 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||
|
||||
activateStatusDisplay(ctx);
|
||||
|
||||
if (!ctx.config.highlightingSemanticTokens) {
|
||||
activateHighlighting(ctx);
|
||||
}
|
||||
activateInlayHints(ctx);
|
||||
|
||||
vscode.workspace.onDidChangeConfiguration(
|
||||
|
|
Loading…
Reference in a new issue