diff --git a/codeless/src/extension.ts b/codeless/src/extension.ts index 792af5a735..99cbfc78e7 100644 --- a/codeless/src/extension.ts +++ b/codeless/src/extension.ts @@ -1,17 +1,9 @@ 'use strict'; import * as vscode from 'vscode'; -import { - LanguageClient, - LanguageClientOptions, - ServerOptions, - TransportKind, - Executable, - TextDocumentIdentifier, - Range -} from 'vscode-languageclient'; +import * as lc from 'vscode-languageclient' -let client: LanguageClient; +let client: lc.LanguageClient; let uris = { syntaxTree: vscode.Uri.parse('libsyntax-rust://syntaxtree') @@ -34,8 +26,7 @@ export function activate(context: vscode.ExtensionContext) { let request: ExtendSelectionParams = { textDocument: { uri: editor.document.uri.toString() }, selections: editor.selections.map((s) => { - let r: Range = { start: s.start, end: s.end } - return r; + return { start: s.start, end: s.end }; }) } let response = await client.sendRequest("m/extendSelection", request) @@ -71,26 +62,46 @@ export function deactivate(): Thenable { } function startServer() { - let run: Executable = { + let run: lc.Executable = { command: "cargo", args: ["run", "--package", "m"], options: { cwd: "." } } - let serverOptions: ServerOptions = { + let serverOptions: lc.ServerOptions = { run, debug: run }; - let clientOptions: LanguageClientOptions = { + let clientOptions: lc.LanguageClientOptions = { documentSelector: [{ scheme: 'file', language: 'rust' }], }; - client = new LanguageClient( + client = new lc.LanguageClient( 'm', 'm languge server', serverOptions, clientOptions, ); + client.onReady().then(() => { + client.onNotification( + new lc.NotificationType("m/publishDecorations"), + (params: PublishDecorationsParams) => { + console.log("A"); + console.log(params.uri); + console.log(vscode.window.activeTextEditor.document.uri.toString()); + console.log("B"); + + let editor = vscode.window.visibleTextEditors.find( + (editor) => editor.document.uri.toString() == params.uri + ) + if (editor == null) return; + setHighlights( + editor, + params.decorations, + ) + } + ) + }) client.start(); } @@ -117,17 +128,72 @@ class TextDocumentContentProvider implements vscode.TextDocumentContentProvider } } + +const decorations = (() => { + const decor = (obj) => vscode.window.createTextEditorDecorationType({ color: obj }) + return { + background: decor("#3F3F3F"), + error: vscode.window.createTextEditorDecorationType({ + borderColor: "red", + borderStyle: "none none dashed none", + }), + comment: decor("#7F9F7F"), + string: decor("#CC9393"), + keyword: decor("#F0DFAF"), + function: decor("#93E0E3"), + parameter: decor("#94BFF3"), + builtin: decor("#DD6718"), + text: decor("#DCDCCC"), + attribute: decor("#BFEBBF"), + literal: decor("#DFAF8F"), + } +})() + +function setHighlights( + editor: vscode.TextEditor, + highlihgs: Array +) { + let byTag = {} + for (let tag in decorations) { + byTag[tag] = [] + } + + for (let d of highlihgs) { + if (!byTag[d.tag]) { + console.log(`unknown tag ${d.tag}`) + continue + } + byTag[d.tag].push(d.range) + } + + for (let tag in byTag) { + let dec = decorations[tag] + let ranges = byTag[tag] + editor.setDecorations(dec, ranges) + } +} + interface SyntaxTreeParams { - textDocument: TextDocumentIdentifier; + textDocument: lc.TextDocumentIdentifier; } type SyntaxTreeResult = string interface ExtendSelectionParams { - textDocument: TextDocumentIdentifier; - selections: Range[]; + textDocument: lc.TextDocumentIdentifier; + selections: lc.Range[]; } interface ExtendSelectionResult { - selections: Range[]; + selections: lc.Range[]; +} + +interface PublishDecorationsParams { + uri: string, + decorations: Decoration[], +} + +interface Decoration { + range: lc.Range, + tag: string, } diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index 6a946a0b09..74f043a9b7 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs @@ -24,6 +24,7 @@ pub struct WorldState { data: Arc } +#[derive(Clone, Debug)] pub struct World { data: Arc, } @@ -119,12 +120,13 @@ impl World { } -#[derive(Default)] +#[derive(Default, Debug)] struct WorldData { mem_map: HashMap>, file_map: RwLock>>, } +#[derive(Debug)] struct FileData { text: Option, syntax: OnceCell, diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index b2a7ce5b44..b5e4e19264 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml @@ -15,6 +15,7 @@ threadpool = "1.7.1" flexi_logger = "0.9.0" log = "0.4.3" url = "1.1.0" +url_serde = "0.2.0" libeditor = { path = "../libeditor" } libanalysis = { path = "../libanalysis" } diff --git a/crates/server/src/handlers.rs b/crates/server/src/handlers.rs index de1fd557d2..8b7e00c928 100644 --- a/crates/server/src/handlers.rs +++ b/crates/server/src/handlers.rs @@ -4,7 +4,7 @@ use libanalysis::World; use libeditor::{self, LineIndex, LineCol, TextRange, TextUnit}; use ::{ - req, Result, + req::{self, Decoration}, Result, util::FilePath, }; @@ -51,6 +51,18 @@ pub fn publish_diagnostics(world: World, uri: Url) -> Result Result { + let path = uri.file_path()?; + let file = world.file_syntax(&path)?; + let line_index = world.file_line_index(&path)?; + let decorations = libeditor::highlight(&file) + .into_iter() + .map(|h| Decoration { + range: to_vs_range(&line_index, h.range), + tag: h.tag, + }).collect(); + Ok(req::PublishDecorationsParams { uri, decorations }) +} fn to_text_range(line_index: &LineIndex, range: Range) -> TextRange { TextRange::from_to( diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index 0e4f5f86a1..900ee555fd 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs @@ -12,6 +12,7 @@ extern crate threadpool; #[macro_use] extern crate log; extern crate url; +extern crate url_serde; extern crate flexi_logger; extern crate libeditor; extern crate libanalysis; @@ -31,7 +32,7 @@ use libanalysis::{WorldState, World}; use ::{ io::{Io, RawMsg, RawRequest}, - handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics}, + handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics, publish_decorations}, util::{FilePath, FnBox} }; @@ -198,7 +199,7 @@ fn main_loop( dispatch::handle_notification::(&mut not, |params| { let path = params.text_document.file_path()?; world.change_overlay(path, Some(params.text_document.text)); - update_diagnostics_on_threadpool( + update_file_notifications_on_threadpool( pool, world.snapshot(), sender.clone(), params.text_document.uri, ); Ok(()) @@ -209,7 +210,7 @@ fn main_loop( .ok_or_else(|| format_err!("empty changes"))? .text; world.change_overlay(path, Some(text)); - update_diagnostics_on_threadpool( + update_file_notifications_on_threadpool( pool, world.snapshot(), sender.clone(), params.text_document.uri, ); Ok(()) @@ -254,14 +255,14 @@ fn handle_request_on_threadpool( }) } -fn update_diagnostics_on_threadpool( +fn update_file_notifications_on_threadpool( pool: &ThreadPool, world: World, sender: Sender, uri: Url, ) { pool.execute(move || { - match publish_diagnostics(world, uri) { + match publish_diagnostics(world.clone(), uri.clone()) { Err(e) => { error!("failed to compute diagnostics: {:?}", e) } @@ -271,5 +272,15 @@ fn update_diagnostics_on_threadpool( })) } } + match publish_decorations(world, uri) { + Err(e) => { + error!("failed to compute decortions: {:?}", e) + } + Ok(params) => { + sender.send(Box::new(|io: &mut Io| { + dispatch::send_notification::(io, params) + })) + } + } }); } diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs index 645a173064..480fbabcd3 100644 --- a/crates/server/src/req.rs +++ b/crates/server/src/req.rs @@ -1,5 +1,7 @@ use serde::{ser::Serialize, de::DeserializeOwned}; +use url::Url; use languageserver_types::{TextDocumentIdentifier, Range}; +use url_serde; pub use languageserver_types::{ request::*, notification::*, @@ -58,3 +60,25 @@ pub struct ExtendSelectionParams { pub struct ExtendSelectionResult { pub selections: Vec, } + +pub enum PublishDecorations {} + +impl Notification for PublishDecorations { + type Params = PublishDecorationsParams; + const METHOD: &'static str = "m/publishDecorations"; +} + +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct PublishDecorationsParams { + #[serde(with = "url_serde")] + pub uri: Url, + pub decorations: Vec, +} + +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Decoration { + pub range: Range, + pub tag: &'static str +}