aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs9
-rw-r--r--crates/rust-analyzer/src/config.rs143
-rw-r--r--crates/rust-analyzer/src/main_loop.rs48
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs65
-rw-r--r--crates/rust-analyzer/src/req.rs36
-rw-r--r--docs/dev/README.md10
-rw-r--r--docs/user/features.md6
-rw-r--r--editors/code/package-lock.json6
-rw-r--r--editors/code/package.json370
-rw-r--r--editors/code/ra_syntax_tree.tmGrammar.json31
-rw-r--r--editors/code/src/client.ts27
-rw-r--r--editors/code/src/commands/runnables.ts4
-rw-r--r--editors/code/src/commands/syntax_tree.ts111
-rw-r--r--editors/code/src/config.ts32
-rw-r--r--editors/code/src/ctx.ts3
-rw-r--r--editors/code/src/highlighting.ts255
-rw-r--r--editors/code/src/main.ts4
-rw-r--r--editors/code/src/status_display.ts2
18 files changed, 458 insertions, 704 deletions
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index c1b6e1ddc..b50cda06f 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -75,7 +75,7 @@ pub type Target = Idx<TargetData>;
75 75
76#[derive(Debug, Clone)] 76#[derive(Debug, Clone)]
77pub struct PackageData { 77pub struct PackageData {
78 pub id: String, 78 pub version: String,
79 pub name: String, 79 pub name: String,
80 pub manifest: PathBuf, 80 pub manifest: PathBuf,
81 pub targets: Vec<Target>, 81 pub targets: Vec<Target>,
@@ -174,14 +174,15 @@ impl CargoWorkspace {
174 let ws_members = &meta.workspace_members; 174 let ws_members = &meta.workspace_members;
175 175
176 for meta_pkg in meta.packages { 176 for meta_pkg in meta.packages {
177 let cargo_metadata::Package { id, edition, name, manifest_path, .. } = meta_pkg; 177 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
178 meta_pkg;
178 let is_member = ws_members.contains(&id); 179 let is_member = ws_members.contains(&id);
179 let edition = edition 180 let edition = edition
180 .parse::<Edition>() 181 .parse::<Edition>()
181 .with_context(|| format!("Failed to parse edition {}", edition))?; 182 .with_context(|| format!("Failed to parse edition {}", edition))?;
182 let pkg = packages.alloc(PackageData { 183 let pkg = packages.alloc(PackageData {
183 name, 184 name,
184 id: id.to_string(), 185 version: version.to_string(),
185 manifest: manifest_path, 186 manifest: manifest_path,
186 targets: Vec::new(), 187 targets: Vec::new(),
187 is_member, 188 is_member,
@@ -256,7 +257,7 @@ impl CargoWorkspace {
256 if self.is_unique(&*package.name) { 257 if self.is_unique(&*package.name) {
257 package.name.clone() 258 package.name.clone()
258 } else { 259 } else {
259 package.id.clone() 260 format!("{}:{}", package.name, package.version)
260 } 261 }
261 } 262 }
262 263
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 3c8f55f1e..04f5bb473 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -16,21 +16,33 @@ use serde::Deserialize;
16#[derive(Debug, Clone)] 16#[derive(Debug, Clone)]
17pub struct Config { 17pub struct Config {
18 pub client_caps: ClientCapsConfig, 18 pub client_caps: ClientCapsConfig,
19 pub publish_decorations: bool, 19
20 pub with_sysroot: bool,
20 pub publish_diagnostics: bool, 21 pub publish_diagnostics: bool,
22 pub lru_capacity: Option<usize>,
23 pub proc_macro_srv: Option<String>,
24 pub files: FilesConfig,
21 pub notifications: NotificationsConfig, 25 pub notifications: NotificationsConfig,
26
27 pub cargo: CargoConfig,
28 pub rustfmt: RustfmtConfig,
29 pub check: Option<FlycheckConfig>,
30
22 pub inlay_hints: InlayHintsConfig, 31 pub inlay_hints: InlayHintsConfig,
23 pub completion: CompletionConfig, 32 pub completion: CompletionConfig,
24 pub call_info_full: bool, 33 pub call_info_full: bool,
25 pub rustfmt: RustfmtConfig, 34}
26 pub check: Option<FlycheckConfig>, 35
27 pub vscode_lldb: bool, 36#[derive(Debug, Clone)]
28 pub proc_macro_srv: Option<String>, 37pub struct FilesConfig {
29 pub lru_capacity: Option<usize>, 38 pub watcher: FilesWatcher,
30 pub use_client_watching: bool, 39 pub exclude: Vec<String>,
31 pub exclude_globs: Vec<String>, 40}
32 pub cargo: CargoConfig, 41
33 pub with_sysroot: bool, 42#[derive(Debug, Clone)]
43pub enum FilesWatcher {
44 Client,
45 Notify,
34} 46}
35 47
36#[derive(Debug, Clone)] 48#[derive(Debug, Clone)]
@@ -60,13 +72,26 @@ pub struct ClientCapsConfig {
60impl Default for Config { 72impl Default for Config {
61 fn default() -> Self { 73 fn default() -> Self {
62 Config { 74 Config {
63 publish_decorations: false, 75 client_caps: ClientCapsConfig::default(),
76
77 with_sysroot: true,
64 publish_diagnostics: true, 78 publish_diagnostics: true,
79 lru_capacity: None,
80 proc_macro_srv: None,
81 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
65 notifications: NotificationsConfig { 82 notifications: NotificationsConfig {
66 workspace_loaded: true, 83 workspace_loaded: true,
67 cargo_toml_not_found: true, 84 cargo_toml_not_found: true,
68 }, 85 },
69 client_caps: ClientCapsConfig::default(), 86
87 cargo: CargoConfig::default(),
88 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
89 check: Some(FlycheckConfig::CargoCommand {
90 command: "check".to_string(),
91 all_targets: true,
92 extra_args: Vec::new(),
93 }),
94
70 inlay_hints: InlayHintsConfig { 95 inlay_hints: InlayHintsConfig {
71 type_hints: true, 96 type_hints: true,
72 parameter_hints: true, 97 parameter_hints: true,
@@ -79,19 +104,6 @@ impl Default for Config {
79 add_call_argument_snippets: true, 104 add_call_argument_snippets: true,
80 }, 105 },
81 call_info_full: true, 106 call_info_full: true,
82 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
83 check: Some(FlycheckConfig::CargoCommand {
84 command: "check".to_string(),
85 all_targets: true,
86 extra_args: Vec::new(),
87 }),
88 vscode_lldb: false,
89 proc_macro_srv: None,
90 lru_capacity: None,
91 use_client_watching: false,
92 exclude_globs: Vec::new(),
93 cargo: CargoConfig::default(),
94 with_sysroot: true,
95 } 107 }
96 } 108 }
97} 109}
@@ -105,46 +117,61 @@ impl Config {
105 *self = Default::default(); 117 *self = Default::default();
106 self.client_caps = client_caps; 118 self.client_caps = client_caps;
107 119
108 set(value, "/publishDecorations", &mut self.publish_decorations); 120 set(value, "/withSysroot", &mut self.with_sysroot);
109 set(value, "/excludeGlobs", &mut self.exclude_globs); 121 set(value, "/featureFlags/lsp.diagnostics", &mut self.publish_diagnostics);
110 set(value, "/useClientWatching", &mut self.use_client_watching);
111 set(value, "/lruCapacity", &mut self.lru_capacity); 122 set(value, "/lruCapacity", &mut self.lru_capacity);
112 123 if let Some(watcher) = get::<String>(value, "/files/watcher") {
113 set(value, "/inlayHintsType", &mut self.inlay_hints.type_hints); 124 self.files.watcher = match watcher.as_str() {
114 set(value, "/inlayHintsParameter", &mut self.inlay_hints.parameter_hints); 125 "client" => FilesWatcher::Client,
115 set(value, "/inlayHintsChaining", &mut self.inlay_hints.chaining_hints); 126 "notify"| _ => FilesWatcher::Notify,
116 set(value, "/inlayHintsMaxLength", &mut self.inlay_hints.max_length); 127 }
117 128 }
118 if let Some(false) = get(value, "cargo_watch_enable") { 129 set(value, "/notifications/workspaceLoaded", &mut self.notifications.workspace_loaded);
130 set(value, "/notifications/cargoTomlNotFound", &mut self.notifications.cargo_toml_not_found);
131
132 set(value, "/cargo/noDefaultFeatures", &mut self.cargo.no_default_features);
133 set(value, "/cargo/allFeatures", &mut self.cargo.all_features);
134 set(value, "/cargo/features", &mut self.cargo.features);
135 set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
136 if let Some(mut args) = get::<Vec<String>>(value, "/rustfmt/overrideCommand") {
137 if !args.is_empty() {
138 let command = args.remove(0);
139 self.rustfmt = RustfmtConfig::CustomCommand {
140 command,
141 args,
142 }
143 }
144 } else if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt {
145 set(value, "/rustfmt/extraArgs", extra_args);
146 }
147 if let Some(false) = get(value, "/checkOnSave/enable") {
119 self.check = None 148 self.check = None
120 } else { 149 } else {
121 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets }) = &mut self.check 150 if let Some(mut args) = get::<Vec<String>>(value, "/checkOnSave/overrideCommand") {
151 if !args.is_empty() {
152 let command = args.remove(0);
153 self.check = Some(FlycheckConfig::CustomCommand {
154 command,
155 args,
156 })
157 }
158
159 } else if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets }) = &mut self.check
122 { 160 {
123 set(value, "/cargoWatchArgs", extra_args); 161 set(value, "/checkOnSave/extraArgs", extra_args);
124 set(value, "/cargoWatchCommand", command); 162 set(value, "/checkOnSave/command", command);
125 set(value, "/cargoWatchAllTargets", all_targets); 163 set(value, "/checkOnSave/allTargets", all_targets);
126 } 164 }
127 }; 165 };
128 166
129 set(value, "/withSysroot", &mut self.with_sysroot); 167 set(value, "/inlayHints/typeHints", &mut self.inlay_hints.type_hints);
130 if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt { 168 set(value, "/inlayHints/parameterHints", &mut self.inlay_hints.parameter_hints);
131 set(value, "/rustfmtArgs", extra_args); 169 set(value, "/inlayHints/chainingHints", &mut self.inlay_hints.chaining_hints);
132 } 170 set(value, "/inlayHints/maxLength", &mut self.inlay_hints.max_length);
133 171 set(value, "/completion/postfix/enable", &mut self.completion.enable_postfix_completions);
134 set(value, "/cargoFeatures/noDefaultFeatures", &mut self.cargo.no_default_features); 172 set(value, "/completion/addCallParenthesis", &mut self.completion.add_call_parenthesis);
135 set(value, "/cargoFeatures/allFeatures", &mut self.cargo.all_features); 173 set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets);
136 set(value, "/cargoFeatures/features", &mut self.cargo.features); 174 set(value, "/callInfo/full", &mut self.call_info_full);
137 set(value, "/cargoFeatures/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
138
139 set(value, "/vscodeLldb", &mut self.vscode_lldb);
140
141 set(value, "/featureFlags/lsp.diagnostics", &mut self.publish_diagnostics);
142 set(value, "/featureFlags/notifications.workspace-loaded", &mut self.notifications.workspace_loaded);
143 set(value, "/featureFlags/notifications.cargo-toml-not-found", &mut self.notifications.cargo_toml_not_found);
144 set(value, "/featureFlags/completion.enable-postfix", &mut self.completion.enable_postfix_completions);
145 set(value, "/featureFlags/completion.insertion.add-call-parenthesis", &mut self.completion.add_call_parenthesis);
146 set(value, "/featureFlags/completion.insertion.add-argument-snippets", &mut self.completion.add_call_argument_snippets);
147 set(value, "/featureFlags/call-info.full", &mut self.call_info_full);
148 175
149 log::info!("Config::update() = {:#?}", self); 176 log::info!("Config::update() = {:#?}", self);
150 177
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 45ae0ad9d..95e676e0f 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -30,7 +30,7 @@ use serde::{de::DeserializeOwned, Serialize};
30use threadpool::ThreadPool; 30use threadpool::ThreadPool;
31 31
32use crate::{ 32use crate::{
33 config::Config, 33 config::{Config, FilesWatcher},
34 diagnostics::DiagnosticTask, 34 diagnostics::DiagnosticTask,
35 main_loop::{ 35 main_loop::{
36 pending_requests::{PendingRequest, PendingRequests}, 36 pending_requests::{PendingRequest, PendingRequests},
@@ -40,7 +40,6 @@ use crate::{
40 world::{WorldSnapshot, WorldState}, 40 world::{WorldSnapshot, WorldState},
41 Result, 41 Result,
42}; 42};
43use req::ConfigurationParams;
44 43
45#[derive(Debug)] 44#[derive(Debug)]
46pub struct LspError { 45pub struct LspError {
@@ -122,12 +121,13 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
122 }; 121 };
123 122
124 let globs = config 123 let globs = config
125 .exclude_globs 124 .files
125 .exclude
126 .iter() 126 .iter()
127 .map(|glob| crate::vfs_glob::Glob::new(glob)) 127 .map(|glob| crate::vfs_glob::Glob::new(glob))
128 .collect::<std::result::Result<Vec<_>, _>>()?; 128 .collect::<std::result::Result<Vec<_>, _>>()?;
129 129
130 if config.use_client_watching { 130 if let FilesWatcher::Client = config.files.watcher {
131 let registration_options = req::DidChangeWatchedFilesRegistrationOptions { 131 let registration_options = req::DidChangeWatchedFilesRegistrationOptions {
132 watchers: workspaces 132 watchers: workspaces
133 .iter() 133 .iter()
@@ -153,7 +153,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
153 workspaces, 153 workspaces,
154 config.lru_capacity, 154 config.lru_capacity,
155 &globs, 155 &globs,
156 Watch(!config.use_client_watching), 156 Watch(matches!(config.files.watcher, FilesWatcher::Notify)),
157 config, 157 config,
158 ) 158 )
159 }; 159 };
@@ -250,9 +250,7 @@ impl fmt::Debug for Event {
250 } 250 }
251 } 251 }
252 Event::Task(Task::Notify(not)) => { 252 Event::Task(Task::Notify(not)) => {
253 if notification_is::<req::PublishDecorations>(not) 253 if notification_is::<req::PublishDiagnostics>(not) {
254 || notification_is::<req::PublishDiagnostics>(not)
255 {
256 return debug_verbose_not(not, f); 254 return debug_verbose_not(not, f);
257 } 255 }
258 } 256 }
@@ -427,7 +425,6 @@ fn loop_turn(
427 update_file_notifications_on_threadpool( 425 update_file_notifications_on_threadpool(
428 pool, 426 pool,
429 world_state.snapshot(), 427 world_state.snapshot(),
430 world_state.config.publish_decorations,
431 task_sender.clone(), 428 task_sender.clone(),
432 loop_state.subscriptions.subscriptions(), 429 loop_state.subscriptions.subscriptions(),
433 ) 430 )
@@ -508,7 +505,6 @@ fn on_request(
508 .on::<req::GotoTypeDefinition>(handlers::handle_goto_type_definition)? 505 .on::<req::GotoTypeDefinition>(handlers::handle_goto_type_definition)?
509 .on::<req::ParentModule>(handlers::handle_parent_module)? 506 .on::<req::ParentModule>(handlers::handle_parent_module)?
510 .on::<req::Runnables>(handlers::handle_runnables)? 507 .on::<req::Runnables>(handlers::handle_runnables)?
511 .on::<req::DecorationsRequest>(handlers::handle_decorations)?
512 .on::<req::Completion>(handlers::handle_completion)? 508 .on::<req::Completion>(handlers::handle_completion)?
513 .on::<req::CodeActionRequest>(handlers::handle_code_action)? 509 .on::<req::CodeActionRequest>(handlers::handle_code_action)?
514 .on::<req::CodeLensRequest>(handlers::handle_code_lens)? 510 .on::<req::CodeLensRequest>(handlers::handle_code_lens)?
@@ -611,7 +607,12 @@ fn on_notification(
611 let request_id = loop_state.next_request_id(); 607 let request_id = loop_state.next_request_id();
612 let request = request_new::<req::WorkspaceConfiguration>( 608 let request = request_new::<req::WorkspaceConfiguration>(
613 request_id.clone(), 609 request_id.clone(),
614 ConfigurationParams::default(), 610 req::ConfigurationParams {
611 items: vec![req::ConfigurationItem {
612 scope_uri: None,
613 section: Some("rust-analyzer".to_string()),
614 }],
615 },
615 ); 616 );
616 msg_sender.send(request.into())?; 617 msg_sender.send(request.into())?;
617 loop_state.configuration_request_id = Some(request_id); 618 loop_state.configuration_request_id = Some(request_id);
@@ -884,15 +885,13 @@ where
884fn update_file_notifications_on_threadpool( 885fn update_file_notifications_on_threadpool(
885 pool: &ThreadPool, 886 pool: &ThreadPool,
886 world: WorldSnapshot, 887 world: WorldSnapshot,
887 publish_decorations: bool,
888 task_sender: Sender<Task>, 888 task_sender: Sender<Task>,
889 subscriptions: Vec<FileId>, 889 subscriptions: Vec<FileId>,
890) { 890) {
891 log::trace!("updating notifications for {:?}", subscriptions); 891 log::trace!("updating notifications for {:?}", subscriptions);
892 let publish_diagnostics = world.config.publish_diagnostics; 892 if world.config.publish_diagnostics {
893 pool.execute(move || { 893 pool.execute(move || {
894 for file_id in subscriptions { 894 for file_id in subscriptions {
895 if publish_diagnostics {
896 match handlers::publish_diagnostics(&world, file_id) { 895 match handlers::publish_diagnostics(&world, file_id) {
897 Err(e) => { 896 Err(e) => {
898 if !is_canceled(&e) { 897 if !is_canceled(&e) {
@@ -904,21 +903,8 @@ fn update_file_notifications_on_threadpool(
904 } 903 }
905 } 904 }
906 } 905 }
907 if publish_decorations { 906 })
908 match handlers::publish_decorations(&world, file_id) { 907 }
909 Err(e) => {
910 if !is_canceled(&e) {
911 log::error!("failed to compute decorations: {:?}", e);
912 }
913 }
914 Ok(params) => {
915 let not = notification_new::<req::PublishDecorations>(params);
916 task_sender.send(Task::Notify(not)).unwrap();
917 }
918 }
919 }
920 }
921 });
922} 908}
923 909
924pub fn show_message(typ: req::MessageType, message: impl Into<String>, sender: &Sender<Message>) { 910pub fn show_message(typ: req::MessageType, message: impl Into<String>, sender: &Sender<Message>) {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 23e48c089..b207f0764 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -38,7 +38,7 @@ use crate::{
38 }, 38 },
39 diagnostics::DiagnosticTask, 39 diagnostics::DiagnosticTask,
40 from_json, 40 from_json,
41 req::{self, Decoration, InlayHint, InlayHintsParams}, 41 req::{self, InlayHint, InlayHintsParams},
42 semantic_tokens::SemanticTokensBuilder, 42 semantic_tokens::SemanticTokensBuilder,
43 world::WorldSnapshot, 43 world::WorldSnapshot,
44 LspError, Result, 44 LspError, Result,
@@ -389,15 +389,6 @@ pub fn handle_runnables(
389 Ok(res) 389 Ok(res)
390} 390}
391 391
392pub fn handle_decorations(
393 world: WorldSnapshot,
394 params: TextDocumentIdentifier,
395) -> Result<Vec<Decoration>> {
396 let _p = profile("handle_decorations");
397 let file_id = params.try_conv_with(&world)?;
398 highlight(&world, file_id)
399}
400
401pub fn handle_completion( 392pub fn handle_completion(
402 world: WorldSnapshot, 393 world: WorldSnapshot,
403 params: req::CompletionParams, 394 params: req::CompletionParams,
@@ -819,23 +810,21 @@ pub fn handle_code_lens(
819 }; 810 };
820 lenses.push(lens); 811 lenses.push(lens);
821 812
822 if world.config.vscode_lldb { 813 if r.args[0] == "run" {
823 if r.args[0] == "run" { 814 r.args[0] = "build".into();
824 r.args[0] = "build".into(); 815 } else {
825 } else { 816 r.args.push("--no-run".into());
826 r.args.push("--no-run".into());
827 }
828 let debug_lens = CodeLens {
829 range: r.range,
830 command: Some(Command {
831 title: "Debug".into(),
832 command: "rust-analyzer.debugSingle".into(),
833 arguments: Some(vec![to_value(r).unwrap()]),
834 }),
835 data: None,
836 };
837 lenses.push(debug_lens);
838 } 817 }
818 let debug_lens = CodeLens {
819 range: r.range,
820 command: Some(Command {
821 title: "Debug".into(),
822 command: "rust-analyzer.debugSingle".into(),
823 arguments: Some(vec![to_value(r).unwrap()]),
824 }),
825 data: None,
826 };
827 lenses.push(debug_lens);
839 } 828 }
840 829
841 // Handle impls 830 // Handle impls
@@ -970,15 +959,6 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia
970 Ok(DiagnosticTask::SetNative(file_id, diagnostics)) 959 Ok(DiagnosticTask::SetNative(file_id, diagnostics))
971} 960}
972 961
973pub fn publish_decorations(
974 world: &WorldSnapshot,
975 file_id: FileId,
976) -> Result<req::PublishDecorationsParams> {
977 let _p = profile("publish_decorations");
978 let uri = world.file_id_to_uri(file_id)?;
979 Ok(req::PublishDecorationsParams { uri, decorations: highlight(&world, file_id)? })
980}
981
982fn to_lsp_runnable( 962fn to_lsp_runnable(
983 world: &WorldSnapshot, 963 world: &WorldSnapshot,
984 file_id: FileId, 964 file_id: FileId,
@@ -1008,21 +988,6 @@ fn to_lsp_runnable(
1008 }) 988 })
1009} 989}
1010 990
1011fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>> {
1012 let line_index = world.analysis().file_line_index(file_id)?;
1013 let res = world
1014 .analysis()
1015 .highlight(file_id)?
1016 .into_iter()
1017 .map(|h| Decoration {
1018 range: h.range.conv_with(&line_index),
1019 tag: h.highlight.to_string(),
1020 binding_hash: h.binding_hash.map(|x| x.to_string()),
1021 })
1022 .collect();
1023 Ok(res)
1024}
1025
1026pub fn handle_inlay_hints( 991pub fn handle_inlay_hints(
1027 world: WorldSnapshot, 992 world: WorldSnapshot,
1028 params: InlayHintsParams, 993 params: InlayHintsParams,
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs
index 994f0ed61..b8b627e28 100644
--- a/crates/rust-analyzer/src/req.rs
+++ b/crates/rust-analyzer/src/req.rs
@@ -1,13 +1,13 @@
1//! Defines `rust-analyzer` specific custom messages. 1//! Defines `rust-analyzer` specific custom messages.
2 2
3use lsp_types::{Location, Position, Range, TextDocumentIdentifier, Url}; 3use lsp_types::{Location, Position, Range, TextDocumentIdentifier};
4use rustc_hash::FxHashMap; 4use rustc_hash::FxHashMap;
5use serde::{Deserialize, Serialize}; 5use serde::{Deserialize, Serialize};
6 6
7pub use lsp_types::{ 7pub use lsp_types::{
8 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, 8 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens,
9 CodeLensParams, CompletionParams, CompletionResponse, ConfigurationParams, DiagnosticTag, 9 CodeLensParams, CompletionParams, CompletionResponse, ConfigurationItem, ConfigurationParams,
10 DidChangeConfigurationParams, DidChangeWatchedFilesParams, 10 DiagnosticTag, DidChangeConfigurationParams, DidChangeWatchedFilesParams,
11 DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams, 11 DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams,
12 DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType, 12 DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType,
13 PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken, 13 PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken,
@@ -86,36 +86,6 @@ pub struct FindMatchingBraceParams {
86 pub offsets: Vec<Position>, 86 pub offsets: Vec<Position>,
87} 87}
88 88
89pub enum DecorationsRequest {}
90
91impl Request for DecorationsRequest {
92 type Params = TextDocumentIdentifier;
93 type Result = Vec<Decoration>;
94 const METHOD: &'static str = "rust-analyzer/decorationsRequest";
95}
96
97pub enum PublishDecorations {}
98
99impl Notification for PublishDecorations {
100 type Params = PublishDecorationsParams;
101 const METHOD: &'static str = "rust-analyzer/publishDecorations";
102}
103
104#[derive(Deserialize, Serialize, Debug)]
105#[serde(rename_all = "camelCase")]
106pub struct PublishDecorationsParams {
107 pub uri: Url,
108 pub decorations: Vec<Decoration>,
109}
110
111#[derive(Deserialize, Serialize, Debug)]
112#[serde(rename_all = "camelCase")]
113pub struct Decoration {
114 pub range: Range,
115 pub tag: String,
116 pub binding_hash: Option<String>,
117}
118
119pub enum ParentModule {} 89pub enum ParentModule {}
120 90
121impl Request for ParentModule { 91impl Request for ParentModule {
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 8d7e18010..f230dc1db 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -155,6 +155,16 @@ There's also two VS Code commands which might be of interest:
155 155
156* `Rust Analyzer: Syntax Tree` shows syntax tree of the current file/selection. 156* `Rust Analyzer: Syntax Tree` shows syntax tree of the current file/selection.
157 157
158 You can hover over syntax nodes in the opened text file to see the appropriate
159 rust code that it refers to and the rust editor will also highlight the proper
160 text range.
161
162 If you press <kbd>Ctrl</kbd> (i.e. trigger goto definition) in the inspected
163 Rust source file the syntax tree read-only editor should scroll to and select the
164 appropriate syntax node token.
165
166 ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png)
167
158# Profiling 168# Profiling
159 169
160We have a built-in hierarchical profiler, you can enable it by using `RA_PROFILE` env-var: 170We have a built-in hierarchical profiler, you can enable it by using `RA_PROFILE` env-var:
diff --git a/docs/user/features.md b/docs/user/features.md
index 8aeec2e81..56d2969fd 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -81,12 +81,6 @@ Join selected lines into one, smartly fixing up whitespace and trailing commas.
81Shows the parse tree of the current file. It exists mostly for debugging 81Shows the parse tree of the current file. It exists mostly for debugging
82rust-analyzer itself. 82rust-analyzer itself.
83 83
84You can hover over syntax nodes in the opened text file to see the appropriate
85rust code that it refers to and the rust editor will also highlight the proper
86text range.
87
88<img src="https://user-images.githubusercontent.com/36276403/78043783-7425e180-737c-11ea-8653-b02b773c5aa1.png" alt="demo" height="200px" >
89
90#### Expand Macro Recursively 84#### Expand Macro Recursively
91 85
92Shows the full macro expansion of the macro at current cursor. 86Shows the full macro expansion of the macro at current cursor.
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 955a05066..2d8b432a7 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -1399,9 +1399,9 @@
1399 } 1399 }
1400 }, 1400 },
1401 "rollup": { 1401 "rollup": {
1402 "version": "2.3.1", 1402 "version": "2.3.2",
1403 "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.3.1.tgz", 1403 "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.3.2.tgz",
1404 "integrity": "sha512-BRjzOauORe+R0U0I6SkMTSG22nYmtztR/TaBl0SvbXgc3VAxBDrZoB6HROiK0S5px1pUBnLnjBkbzmVuwC9Q1Q==", 1404 "integrity": "sha512-p66+fbfaUUOGE84sHXAOgfeaYQMslgAazoQMp//nlR519R61213EPFgrMZa48j31jNacJwexSAR1Q8V/BwGKBA==",
1405 "dev": true, 1405 "dev": true,
1406 "requires": { 1406 "requires": {
1407 "fsevents": "~2.1.2" 1407 "fsevents": "~2.1.2"
diff --git a/editors/code/package.json b/editors/code/package.json
index ba31c4e63..14bdec8c2 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -45,7 +45,7 @@
45 "@typescript-eslint/eslint-plugin": "^2.26.0", 45 "@typescript-eslint/eslint-plugin": "^2.26.0",
46 "@typescript-eslint/parser": "^2.26.0", 46 "@typescript-eslint/parser": "^2.26.0",
47 "eslint": "^6.8.0", 47 "eslint": "^6.8.0",
48 "rollup": "^2.3.1", 48 "rollup": "^2.3.2",
49 "tslib": "^1.11.1", 49 "tslib": "^1.11.1",
50 "typescript": "^3.8.3", 50 "typescript": "^3.8.3",
51 "typescript-formatter": "^7.2.2", 51 "typescript-formatter": "^7.2.2",
@@ -177,91 +177,70 @@
177 "type": "object", 177 "type": "object",
178 "title": "Rust Analyzer", 178 "title": "Rust Analyzer",
179 "properties": { 179 "properties": {
180 "rust-analyzer.highlighting.semanticTokens": { 180 "rust-analyzer.diagnostics.enable": {
181 "type": "boolean", 181 "type": "boolean",
182 "default": false, 182 "default": true,
183 "description": "Use proposed semantic tokens API for syntax highlighting" 183 "markdownDescription": "Whether to show native rust-analyzer diagnostics."
184 },
185 "rust-analyzer.highlightingOn": {
186 "type": "boolean",
187 "default": false,
188 "description": "Highlight Rust code (overrides built-in syntax highlighting)"
189 },
190 "rust-analyzer.rainbowHighlightingOn": {
191 "type": "boolean",
192 "default": false,
193 "description": "When highlighting Rust code, use a unique color per identifier"
194 }, 184 },
195 "rust-analyzer.featureFlags": { 185 "rust-analyzer.lruCapacity": {
196 "type": "object", 186 "type": [
197 "default": {}, 187 "null",
198 "description": "Fine grained feature flags to disable annoying features", 188 "integer"
199 "properties": { 189 ],
200 "lsp.diagnostics": { 190 "default": null,
201 "type": "boolean", 191 "minimum": 0,
202 "markdownDescription": "Whether to show diagnostics from `cargo check`" 192 "exclusiveMinimum": true,
203 }, 193 "description": "Number of syntax trees rust-analyzer keeps in memory."
204 "completion.insertion.add-call-parenthesis": {
205 "type": "boolean",
206 "description": "Whether to add parenthesis when completing functions"
207 },
208 "completion.insertion.add-argument-snippets": {
209 "type": "boolean",
210 "description": "Whether to add argument snippets when completing functions"
211 },
212 "completion.enable-postfix": {
213 "type": "boolean",
214 "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc."
215 },
216 "call-info.full": {
217 "type": "boolean",
218 "description": "Show function name and docs in parameter hints"
219 },
220 "notifications.workspace-loaded": {
221 "type": "boolean",
222 "markdownDescription": "Whether to show `workspace loaded` message"
223 },
224 "notifications.cargo-toml-not-found": {
225 "type": "boolean",
226 "markdownDescription": "Whether to show `can't find Cargo.toml` error message"
227 }
228 }
229 }, 194 },
230 "rust-analyzer.updates.channel": { 195 "rust-analyzer.files.watcher": {
231 "type": "string", 196 "type": "string",
232 "enum": [ 197 "enum": [
233 "stable", 198 "client",
234 "nightly" 199 "notify"
235 ],
236 "default": "stable",
237 "markdownEnumDescriptions": [
238 "`\"stable\"` updates are shipped weekly, they don't contain cutting-edge features from VSCode proposed APIs but have less bugs in general",
239 "`\"nightly\"` updates are shipped daily (extension updates automatically by downloading artifacts directly from GitHub), they contain cutting-edge features and latest bug fixes. These releases help us get your feedback very quickly and speed up rust-analyzer development **drastically**"
240 ], 200 ],
241 "markdownDescription": "Choose `\"nightly\"` updates to get the latest features and bug fixes every day. While `\"stable\"` releases occur weekly and don't contain cutting-edge features from VSCode proposed APIs" 201 "default": "client",
202 "description": "Controls file watching implementation."
242 }, 203 },
243 "rust-analyzer.updates.askBeforeDownload": { 204 "rust-analyzer.files.exclude": {
205 "type": "array",
206 "items": {
207 "type": "string"
208 },
209 "default": [],
210 "description": "Paths to exclude from analysis."
211 },
212 "rust-analyzer.notifications.workspaceLoaded": {
244 "type": "boolean", 213 "type": "boolean",
245 "default": true, 214 "markdownDescription": "Whether to show `workspace loaded` message."
246 "description": "Whether to ask for permission before downloading any files from the Internet"
247 }, 215 },
248 "rust-analyzer.serverPath": { 216 "rust-analyzer.notifications.cargoTomlNotFound": {
249 "type": [ 217 "type": "boolean",
250 "null", 218 "markdownDescription": "Whether to show `can't find Cargo.toml` error message"
251 "string" 219 },
252 ], 220 "rust-analyzer.cargo.noDefaultFeatures": {
253 "default": null, 221 "type": "boolean",
254 "description": "Path to rust-analyzer executable (points to bundled binary by default). If this is set, then \"rust-analyzer.updates.channel\" setting is not used" 222 "default": false,
223 "markdownDescription": "Do not activate the `default` feature"
224 },
225 "rust-analyzer.cargo.allFeatures": {
226 "type": "boolean",
227 "default": true,
228 "description": "Activate all available features"
255 }, 229 },
256 "rust-analyzer.excludeGlobs": { 230 "rust-analyzer.cargo.features": {
257 "type": "array", 231 "type": "array",
258 "items": { 232 "items": {
259 "type": "string" 233 "type": "string"
260 }, 234 },
261 "default": [], 235 "default": [],
262 "description": "Paths to exclude from analysis" 236 "description": "List of features to activate"
237 },
238 "rust-analyzer.cargo.loadOutDirsFromCheck": {
239 "type": "boolean",
240 "default": false,
241 "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs"
263 }, 242 },
264 "rust-analyzer.rustfmtArgs": { 243 "rust-analyzer.rustfmt.extraArgs": {
265 "type": "array", 244 "type": "array",
266 "items": { 245 "items": {
267 "type": "string" 246 "type": "string"
@@ -269,64 +248,42 @@
269 "default": [], 248 "default": [],
270 "description": "Additional arguments to rustfmt" 249 "description": "Additional arguments to rustfmt"
271 }, 250 },
272 "rust-analyzer.useClientWatching": { 251 "rust-analyzer.rustfmt.overrideCommand": {
273 "type": "boolean", 252 "type": "array",
274 "default": true, 253 "items": {
275 "description": "client provided file watching instead of notify watching." 254 "type": "string"
255 },
256 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for formatting."
276 }, 257 },
277 "rust-analyzer.cargo-watch.enable": { 258 "rust-analyzer.checkOnSave.enable": {
278 "type": "boolean", 259 "type": "boolean",
279 "default": true, 260 "default": true,
280 "markdownDescription": "Run specified `cargo-watch` command for diagnostics on save" 261 "markdownDescription": "Run specified `cargo check` command for diagnostics on save"
281 }, 262 },
282 "rust-analyzer.cargo-watch.arguments": { 263 "rust-analyzer.checkOnSave.extraArgs": {
283 "type": "array", 264 "type": "array",
284 "items": { 265 "items": {
285 "type": "string" 266 "type": "string"
286 }, 267 },
287 "markdownDescription": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )", 268 "markdownDescription": "Extra arguments for `cargo check`",
288 "default": [] 269 "default": []
289 }, 270 },
290 "rust-analyzer.cargo-watch.command": { 271 "rust-analyzer.checkOnSave.command": {
291 "type": "string", 272 "type": "string",
292 "markdownDescription": "`cargo-watch` command. (e.g: `clippy` will run as `cargo watch -x clippy` )", 273 "default": "check",
293 "default": "check" 274 "markdownDescription": "Cargo command to use for `cargo check`"
294 }, 275 },
295 "rust-analyzer.cargo-watch.allTargets": { 276 "rust-analyzer.checkOnSave.overrideCommand": {
296 "type": "boolean", 277 "type": "array",
297 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)", 278 "items": {
298 "default": true 279 "type": "string"
299 }, 280 },
300 "rust-analyzer.trace.server": { 281 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message=format=json` or similar option."
301 "type": "string",
302 "scope": "window",
303 "enum": [
304 "off",
305 "messages",
306 "verbose"
307 ],
308 "enumDescriptions": [
309 "No traces",
310 "Error only",
311 "Full log"
312 ],
313 "default": "off",
314 "description": "Trace requests to the rust-analyzer"
315 }, 282 },
316 "rust-analyzer.trace.extension": { 283 "rust-analyzer.checkOnSave.allTargets": {
317 "description": "Enable logging of VS Code extensions itself",
318 "type": "boolean", 284 "type": "boolean",
319 "default": false 285 "default": true,
320 }, 286 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)"
321 "rust-analyzer.lruCapacity": {
322 "type": [
323 "null",
324 "integer"
325 ],
326 "default": null,
327 "minimum": 0,
328 "exclusiveMinimum": true,
329 "description": "Number of syntax trees rust-analyzer keeps in memory"
330 }, 287 },
331 "rust-analyzer.inlayHints.typeHints": { 288 "rust-analyzer.inlayHints.typeHints": {
332 "type": "boolean", 289 "type": "boolean",
@@ -353,28 +310,76 @@
353 "exclusiveMinimum": true, 310 "exclusiveMinimum": true,
354 "description": "Maximum length for inlay hints" 311 "description": "Maximum length for inlay hints"
355 }, 312 },
356 "rust-analyzer.cargoFeatures.noDefaultFeatures": { 313 "rust-analyzer.completion.addCallParenthesis": {
357 "type": "boolean", 314 "type": "boolean",
358 "default": false, 315 "default": true,
359 "markdownDescription": "Do not activate the `default` feature" 316 "description": "Whether to add parenthesis when completing functions"
360 }, 317 },
361 "rust-analyzer.cargoFeatures.allFeatures": { 318 "rust-analyzer.completion.addCallArgumentSnippets": {
362 "type": "boolean", 319 "type": "boolean",
363 "default": true, 320 "default": true,
364 "description": "Activate all available features" 321 "description": "Whether to add argument snippets when completing functions"
365 }, 322 },
366 "rust-analyzer.cargoFeatures.features": { 323 "rust-analyzer.completion.postfix.enable": {
367 "type": "array", 324 "type": "boolean",
368 "items": { 325 "default": true,
369 "type": "string" 326 "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc."
370 },
371 "default": [],
372 "description": "List of features to activate"
373 }, 327 },
374 "rust-analyzer.cargoFeatures.loadOutDirsFromCheck": { 328 "rust-analyzer.callInfo.full": {
329 "type": "boolean",
330 "description": "Show function name and docs in parameter hints"
331 },
332 "rust-analyzer.highlighting.semanticTokens": {
375 "type": "boolean", 333 "type": "boolean",
376 "default": false, 334 "default": false,
377 "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs" 335 "description": "Use proposed semantic tokens API for syntax highlighting"
336 },
337 "rust-analyzer.updates.channel": {
338 "type": "string",
339 "enum": [
340 "stable",
341 "nightly"
342 ],
343 "default": "stable",
344 "markdownEnumDescriptions": [
345 "`\"stable\"` updates are shipped weekly, they don't contain cutting-edge features from VSCode proposed APIs but have less bugs in general",
346 "`\"nightly\"` updates are shipped daily (extension updates automatically by downloading artifacts directly from GitHub), they contain cutting-edge features and latest bug fixes. These releases help us get your feedback very quickly and speed up rust-analyzer development **drastically**"
347 ],
348 "markdownDescription": "Choose `\"nightly\"` updates to get the latest features and bug fixes every day. While `\"stable\"` releases occur weekly and don't contain cutting-edge features from VSCode proposed APIs"
349 },
350 "rust-analyzer.updates.askBeforeDownload": {
351 "type": "boolean",
352 "default": true,
353 "description": "Whether to ask for permission before downloading any files from the Internet"
354 },
355 "rust-analyzer.serverPath": {
356 "type": [
357 "null",
358 "string"
359 ],
360 "default": null,
361 "description": "Path to rust-analyzer executable (points to bundled binary by default). If this is set, then \"rust-analyzer.updates.channel\" setting is not used"
362 },
363 "rust-analyzer.trace.server": {
364 "type": "string",
365 "scope": "window",
366 "enum": [
367 "off",
368 "messages",
369 "verbose"
370 ],
371 "enumDescriptions": [
372 "No traces",
373 "Error only",
374 "Full log"
375 ],
376 "default": "off",
377 "description": "Trace requests to the rust-analyzer"
378 },
379 "rust-analyzer.trace.extension": {
380 "description": "Enable logging of VS Code extensions itself",
381 "type": "boolean",
382 "default": false
378 } 383 }
379 } 384 }
380 }, 385 },
@@ -411,6 +416,21 @@
411 ] 416 ]
412 } 417 }
413 ], 418 ],
419 "languages": [
420 {
421 "id": "ra_syntax_tree",
422 "extensions": [
423 ".rast"
424 ]
425 }
426 ],
427 "grammars": [
428 {
429 "language": "ra_syntax_tree",
430 "scopeName": "source.ra_syntax_tree",
431 "path": "ra_syntax_tree.tmGrammar.json"
432 }
433 ],
414 "problemMatchers": [ 434 "problemMatchers": [
415 { 435 {
416 "name": "rustc", 436 "name": "rustc",
@@ -450,6 +470,15 @@
450 "light": "#747474", 470 "light": "#747474",
451 "highContrast": "#BEBEBE" 471 "highContrast": "#BEBEBE"
452 } 472 }
473 },
474 {
475 "id": "rust_analyzer.syntaxTreeBorder",
476 "description": "Color of the border displayed in the Rust source code for the selected syntax node (see \"Show Syntax Tree\" command)",
477 "defaults": {
478 "dark": "#ffffff",
479 "light": "#b700ff",
480 "highContrast": "#b700ff"
481 }
453 } 482 }
454 ], 483 ],
455 "semanticTokenTypes": [ 484 "semanticTokenTypes": [
@@ -492,65 +521,34 @@
492 "description": "Style for unsafe operations" 521 "description": "Style for unsafe operations"
493 } 522 }
494 ], 523 ],
495 "semanticTokenStyleDefaults": [ 524 "semanticTokenScopes": [
496 { 525 {
497 "selector": "attribute", 526 "language": "rust",
498 "scope": [ 527 "scopes": {
499 "meta.attribute" 528 "attribute": [
500 ] 529 "meta.attribute"
501 }, 530 ],
502 { 531 "builtinType": [
503 "selector": "builtinType", 532 "support.type.primitive"
504 "scope": [ 533 ],
505 "support.type.primitive" 534 "lifetime": [
506 ] 535 "entity.name.lifetime.rust"
507 }, 536 ],
508 { 537 "typeAlias": [
509 "selector": "lifetime", 538 "entity.name.typeAlias"
510 "scope": [ 539 ],
511 "entity.name.lifetime.rust" 540 "union": [
512 ] 541 "entity.name.union"
513 }, 542 ],
514 { 543 "keyword.unsafe": [
515 "selector": "typeAlias", 544 "keyword.other.unsafe"
516 "scope": [ 545 ],
517 "entity.name.typeAlias" 546 "keyword.control": [
518 ] 547 "keyword.control"
519 }, 548 ],
520 { 549 "variable.constant": [
521 "selector": "union", 550 "entity.name.constant"
522 "scope": [ 551 ]
523 "entity.name.union"
524 ]
525 },
526 {
527 "selector": "keyword.unsafe",
528 "scope": [
529 "keyword.other.unsafe"
530 ]
531 },
532 {
533 "selector": "keyword.control",
534 "scope": [
535 "keyword.control"
536 ]
537 },
538 {
539 "selector": "variable.constant",
540 "scope": [
541 "entity.name.constant"
542 ]
543 },
544 {
545 "selector": "*.mutable",
546 "light": {
547 "fontStyle": "underline"
548 },
549 "dark": {
550 "fontStyle": "underline"
551 },
552 "highContrast": {
553 "fontStyle": "underline"
554 } 552 }
555 } 553 }
556 ] 554 ]
diff --git a/editors/code/ra_syntax_tree.tmGrammar.json b/editors/code/ra_syntax_tree.tmGrammar.json
new file mode 100644
index 000000000..0d72a3e36
--- /dev/null
+++ b/editors/code/ra_syntax_tree.tmGrammar.json
@@ -0,0 +1,31 @@
1{
2 "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
3
4 "scopeName": "source.ra_syntax_tree",
5 "patterns": [
6 { "include": "#node_type" },
7 { "include": "#node_range_index" },
8 { "include": "#token_text" }
9 ],
10 "repository": {
11 "node_type": {
12 "match": "^\\s*([A-Z_]+?)@",
13 "captures": {
14 "1": {
15 "name": "entity.name.class"
16 }
17 }
18 },
19 "node_range_index": {
20 "match": "\\d+",
21 "name": "constant.numeric"
22 },
23 "token_text": {
24 "match": "\".+\"",
25 "name": "string"
26 }
27 },
28 "fileTypes": [
29 "rast"
30 ]
31}
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index f909f8db2..3b1d00bca 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -5,31 +5,6 @@ import { Config } from './config';
5import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; 5import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed';
6import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; 6import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed';
7 7
8export function configToServerOptions(config: Config) {
9 return {
10 publishDecorations: !config.highlightingSemanticTokens,
11 lruCapacity: config.lruCapacity,
12
13 inlayHintsType: config.inlayHints.typeHints,
14 inlayHintsParameter: config.inlayHints.parameterHints,
15 inlayHintsChaining: config.inlayHints.chainingHints,
16 inlayHintsMaxLength: config.inlayHints.maxLength,
17
18 cargoWatchEnable: config.cargoWatchOptions.enable,
19 cargoWatchArgs: config.cargoWatchOptions.arguments,
20 cargoWatchCommand: config.cargoWatchOptions.command,
21 cargoWatchAllTargets: config.cargoWatchOptions.allTargets,
22
23 excludeGlobs: config.excludeGlobs,
24 useClientWatching: config.useClientWatching,
25 featureFlags: config.featureFlags,
26 withSysroot: config.withSysroot,
27 cargoFeatures: config.cargoFeatures,
28 rustfmtArgs: config.rustfmtArgs,
29 vscodeLldb: vscode.extensions.getExtension("vadimcn.vscode-lldb") != null,
30 };
31}
32
33export async function createClient(config: Config, serverPath: string, cwd: string): Promise<lc.LanguageClient> { 8export async function createClient(config: Config, serverPath: string, cwd: string): Promise<lc.LanguageClient> {
34 // '.' Is the fallback if no folder is open 9 // '.' Is the fallback if no folder is open
35 // TODO?: Workspace folders support Uri's (eg: file://test.txt). 10 // TODO?: Workspace folders support Uri's (eg: file://test.txt).
@@ -49,7 +24,7 @@ export async function createClient(config: Config, serverPath: string, cwd: stri
49 24
50 const clientOptions: lc.LanguageClientOptions = { 25 const clientOptions: lc.LanguageClientOptions = {
51 documentSelector: [{ scheme: 'file', language: 'rust' }], 26 documentSelector: [{ scheme: 'file', language: 'rust' }],
52 initializationOptions: configToServerOptions(config), 27 initializationOptions: vscode.workspace.getConfiguration("rust-analyzer"),
53 traceOutputChannel, 28 traceOutputChannel,
54 middleware: { 29 middleware: {
55 // Workaround for https://github.com/microsoft/vscode-languageserver-node/issues/576 30 // Workaround for https://github.com/microsoft/vscode-languageserver-node/issues/576
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index 357155163..2635a1440 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -66,6 +66,10 @@ export function debugSingle(ctx: Ctx): Cmd {
66 return async (config: ra.Runnable) => { 66 return async (config: ra.Runnable) => {
67 const editor = ctx.activeRustEditor; 67 const editor = ctx.activeRustEditor;
68 if (!editor) return; 68 if (!editor) return;
69 if (!vscode.extensions.getExtension("vadimcn.vscode-lldb")) {
70 vscode.window.showErrorMessage("Install `vadimcn.vscode-lldb` extension for debugging");
71 return;
72 }
69 73
70 const debugConfig = { 74 const debugConfig = {
71 type: "lldb", 75 type: "lldb",
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts
index 996c7a716..b7a397414 100644
--- a/editors/code/src/commands/syntax_tree.ts
+++ b/editors/code/src/commands/syntax_tree.ts
@@ -15,6 +15,9 @@ export function syntaxTree(ctx: Ctx): Cmd {
15 void new AstInspector(ctx); 15 void new AstInspector(ctx);
16 16
17 ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp)); 17 ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp));
18 ctx.pushCleanup(vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
19 brackets: [["[", ")"]],
20 }));
18 21
19 return async () => { 22 return async () => {
20 const editor = vscode.window.activeTextEditor; 23 const editor = vscode.window.activeTextEditor;
@@ -36,7 +39,7 @@ export function syntaxTree(ctx: Ctx): Cmd {
36} 39}
37 40
38class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { 41class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
39 readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree'); 42 readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree/tree.rast');
40 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); 43 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
41 44
42 45
@@ -79,16 +82,41 @@ class TextDocumentContentProvider implements vscode.TextDocumentContentProvider
79 82
80// FIXME: consider implementing this via the Tree View API? 83// FIXME: consider implementing this via the Tree View API?
81// https://code.visualstudio.com/api/extension-guides/tree-view 84// https://code.visualstudio.com/api/extension-guides/tree-view
82class AstInspector implements vscode.HoverProvider, Disposable { 85class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable {
83 private static readonly astDecorationType = vscode.window.createTextEditorDecorationType({ 86 private readonly astDecorationType = vscode.window.createTextEditorDecorationType({
84 fontStyle: "normal", 87 borderColor: new vscode.ThemeColor('rust_analyzer.syntaxTreeBorder'),
85 border: "#ffffff 1px solid", 88 borderStyle: "solid",
89 borderWidth: "2px",
90
86 }); 91 });
87 private rustEditor: undefined | RustEditor; 92 private rustEditor: undefined | RustEditor;
88 93
94 // Lazy rust token range -> syntax tree file range.
95 private readonly rust2Ast = new Lazy(() => {
96 const astEditor = this.findAstTextEditor();
97 if (!this.rustEditor || !astEditor) return undefined;
98
99 const buf: [vscode.Range, vscode.Range][] = [];
100 for (let i = 0; i < astEditor.document.lineCount; ++i) {
101 const astLine = astEditor.document.lineAt(i);
102
103 // Heuristically look for nodes with quoted text (which are token nodes)
104 const isTokenNode = astLine.text.lastIndexOf('"') >= 0;
105 if (!isTokenNode) continue;
106
107 const rustRange = this.parseRustTextRange(this.rustEditor.document, astLine.text);
108 if (!rustRange) continue;
109
110 buf.push([rustRange, this.findAstNodeRange(astLine)]);
111 }
112 return buf;
113 });
114
89 constructor(ctx: Ctx) { 115 constructor(ctx: Ctx) {
90 ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this)); 116 ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this));
117 ctx.pushCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this));
91 vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions); 118 vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions);
119 vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
92 vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions); 120 vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions);
93 121
94 ctx.pushCleanup(this); 122 ctx.pushCleanup(this);
@@ -97,6 +125,12 @@ class AstInspector implements vscode.HoverProvider, Disposable {
97 this.setRustEditor(undefined); 125 this.setRustEditor(undefined);
98 } 126 }
99 127
128 private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
129 if (this.rustEditor && event.document.uri.toString() === this.rustEditor.document.uri.toString()) {
130 this.rust2Ast.reset();
131 }
132 }
133
100 private onDidCloseTextDocument(doc: vscode.TextDocument) { 134 private onDidCloseTextDocument(doc: vscode.TextDocument) {
101 if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) { 135 if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) {
102 this.setRustEditor(undefined); 136 this.setRustEditor(undefined);
@@ -104,38 +138,67 @@ class AstInspector implements vscode.HoverProvider, Disposable {
104 } 138 }
105 139
106 private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) { 140 private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) {
107 if (editors.every(suspect => suspect.document.uri.scheme !== AST_FILE_SCHEME)) { 141 if (!this.findAstTextEditor()) {
108 this.setRustEditor(undefined); 142 this.setRustEditor(undefined);
109 return; 143 return;
110 } 144 }
111 this.setRustEditor(editors.find(isRustEditor)); 145 this.setRustEditor(editors.find(isRustEditor));
112 } 146 }
113 147
148 private findAstTextEditor(): undefined | vscode.TextEditor {
149 return vscode.window.visibleTextEditors.find(it => it.document.uri.scheme === AST_FILE_SCHEME);
150 }
151
114 private setRustEditor(newRustEditor: undefined | RustEditor) { 152 private setRustEditor(newRustEditor: undefined | RustEditor) {
115 if (newRustEditor !== this.rustEditor) { 153 if (this.rustEditor && this.rustEditor !== newRustEditor) {
116 this.rustEditor?.setDecorations(AstInspector.astDecorationType, []); 154 this.rustEditor.setDecorations(this.astDecorationType, []);
155 this.rust2Ast.reset();
117 } 156 }
118 this.rustEditor = newRustEditor; 157 this.rustEditor = newRustEditor;
119 } 158 }
120 159
160 // additional positional params are omitted
161 provideDefinition(doc: vscode.TextDocument, pos: vscode.Position): vscode.ProviderResult<vscode.DefinitionLink[]> {
162 if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString()) return;
163
164 const astEditor = this.findAstTextEditor();
165 if (!astEditor) return;
166
167 const rust2AstRanges = this.rust2Ast.get()?.find(([rustRange, _]) => rustRange.contains(pos));
168 if (!rust2AstRanges) return;
169
170 const [rustFileRange, astFileRange] = rust2AstRanges;
171
172 astEditor.revealRange(astFileRange);
173 astEditor.selection = new vscode.Selection(astFileRange.start, astFileRange.end);
174
175 return [{
176 targetRange: astFileRange,
177 targetUri: astEditor.document.uri,
178 originSelectionRange: rustFileRange,
179 targetSelectionRange: astFileRange,
180 }];
181 }
182
183 // additional positional params are omitted
121 provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> { 184 provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> {
122 if (!this.rustEditor) return; 185 if (!this.rustEditor) return;
123 186
124 const astTextLine = doc.lineAt(hoverPosition.line); 187 const astFileLine = doc.lineAt(hoverPosition.line);
125 188
126 const rustTextRange = this.parseRustTextRange(this.rustEditor.document, astTextLine.text); 189 const rustFileRange = this.parseRustTextRange(this.rustEditor.document, astFileLine.text);
127 if (!rustTextRange) return; 190 if (!rustFileRange) return;
128 191
129 this.rustEditor.setDecorations(AstInspector.astDecorationType, [rustTextRange]); 192 this.rustEditor.setDecorations(this.astDecorationType, [rustFileRange]);
130 this.rustEditor.revealRange(rustTextRange); 193 this.rustEditor.revealRange(rustFileRange);
131 194
132 const rustSourceCode = this.rustEditor.document.getText(rustTextRange); 195 const rustSourceCode = this.rustEditor.document.getText(rustFileRange);
133 const astTextRange = this.findAstRange(astTextLine); 196 const astFileRange = this.findAstNodeRange(astFileLine);
134 197
135 return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astTextRange); 198 return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astFileRange);
136 } 199 }
137 200
138 private findAstRange(astLine: vscode.TextLine) { 201 private findAstNodeRange(astLine: vscode.TextLine) {
139 const lineOffset = astLine.range.start; 202 const lineOffset = astLine.range.start;
140 const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex); 203 const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex);
141 const end = lineOffset.translate(undefined, astLine.text.trimEnd().length); 204 const end = lineOffset.translate(undefined, astLine.text.trimEnd().length);
@@ -151,3 +214,17 @@ class AstInspector implements vscode.HoverProvider, Disposable {
151 return new vscode.Range(begin, end); 214 return new vscode.Range(begin, end);
152 } 215 }
153} 216}
217
218class Lazy<T> {
219 val: undefined | T;
220
221 constructor(private readonly compute: () => undefined | T) { }
222
223 get() {
224 return this.val ?? (this.val = this.compute());
225 }
226
227 reset() {
228 this.val = undefined;
229 }
230}
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 501997fef..1f45f1de0 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -11,9 +11,8 @@ export class Config {
11 private readonly rootSection = "rust-analyzer"; 11 private readonly rootSection = "rust-analyzer";
12 private readonly requiresReloadOpts = [ 12 private readonly requiresReloadOpts = [
13 "serverPath", 13 "serverPath",
14 "cargoFeatures", 14 "cargo",
15 "excludeGlobs", 15 "files",
16 "useClientWatching",
17 "highlighting", 16 "highlighting",
18 "updates.channel", 17 "updates.channel",
19 ] 18 ]
@@ -71,19 +70,8 @@ export class Config {
71 get channel() { return this.cfg.get<UpdatesChannel>("updates.channel")!; } 70 get channel() { return this.cfg.get<UpdatesChannel>("updates.channel")!; }
72 get askBeforeDownload() { return this.cfg.get<boolean>("updates.askBeforeDownload")!; } 71 get askBeforeDownload() { return this.cfg.get<boolean>("updates.askBeforeDownload")!; }
73 get highlightingSemanticTokens() { return this.cfg.get<boolean>("highlighting.semanticTokens")!; } 72 get highlightingSemanticTokens() { return this.cfg.get<boolean>("highlighting.semanticTokens")!; }
74 get highlightingOn() { return this.cfg.get<boolean>("highlightingOn")!; }
75 get rainbowHighlightingOn() { return this.cfg.get<boolean>("rainbowHighlightingOn")!; }
76 get lruCapacity() { return this.cfg.get<null | number>("lruCapacity")!; }
77 get excludeGlobs() { return this.cfg.get<string[]>("excludeGlobs")!; }
78 get useClientWatching() { return this.cfg.get<boolean>("useClientWatching")!; }
79 get featureFlags() { return this.cfg.get<Record<string, boolean>>("featureFlags")!; }
80 get rustfmtArgs() { return this.cfg.get<string[]>("rustfmtArgs")!; }
81 get loadOutDirsFromCheck() { return this.cfg.get<boolean>("loadOutDirsFromCheck")!; }
82 get traceExtension() { return this.cfg.get<boolean>("trace.extension")!; } 73 get traceExtension() { return this.cfg.get<boolean>("trace.extension")!; }
83 74
84 // for internal use
85 get withSysroot() { return this.cfg.get<boolean>("withSysroot", true)!; }
86
87 get inlayHints() { 75 get inlayHints() {
88 return { 76 return {
89 typeHints: this.cfg.get<boolean>("inlayHints.typeHints")!, 77 typeHints: this.cfg.get<boolean>("inlayHints.typeHints")!,
@@ -93,21 +81,9 @@ export class Config {
93 }; 81 };
94 } 82 }
95 83
96 get cargoWatchOptions() { 84 get checkOnSave() {
97 return {
98 enable: this.cfg.get<boolean>("cargo-watch.enable")!,
99 arguments: this.cfg.get<string[]>("cargo-watch.arguments")!,
100 allTargets: this.cfg.get<boolean>("cargo-watch.allTargets")!,
101 command: this.cfg.get<string>("cargo-watch.command")!,
102 };
103 }
104
105 get cargoFeatures() {
106 return { 85 return {
107 noDefaultFeatures: this.cfg.get<boolean>("cargoFeatures.noDefaultFeatures")!, 86 command: this.cfg.get<string>("checkOnSave.command")!,
108 allFeatures: this.cfg.get<boolean>("cargoFeatures.allFeatures")!,
109 features: this.cfg.get<string[]>("cargoFeatures.features")!,
110 loadOutDirsFromCheck: this.cfg.get<boolean>("cargoFeatures.loadOutDirsFromCheck")!,
111 }; 87 };
112 } 88 }
113} 89}
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 86b5f3629..bd1c3de07 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3 3
4import { Config } from './config'; 4import { Config } from './config';
5import { createClient, configToServerOptions } from './client'; 5import { createClient } from './client';
6import { isRustEditor, RustEditor } from './util'; 6import { isRustEditor, RustEditor } from './util';
7 7
8export class Ctx { 8export class Ctx {
@@ -25,7 +25,6 @@ export class Ctx {
25 const res = new Ctx(config, extCtx, client, serverPath); 25 const res = new Ctx(config, extCtx, client, serverPath);
26 res.pushCleanup(client.start()); 26 res.pushCleanup(client.start());
27 await client.onReady(); 27 await client.onReady();
28 client.onRequest('workspace/configuration', _ => [configToServerOptions(config)]);
29 return res; 28 return res;
30 } 29 }
31 30
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts
deleted file mode 100644
index ea2dfc0e3..000000000
--- a/editors/code/src/highlighting.ts
+++ /dev/null
@@ -1,255 +0,0 @@
1import * as vscode from 'vscode';
2import * as ra from './rust-analyzer-api';
3
4import { ColorTheme, TextMateRuleSettings } from './color_theme';
5
6import { Ctx } from './ctx';
7import { sendRequestWithRetry, isRustDocument } from './util';
8
9export function activateHighlighting(ctx: Ctx) {
10 const highlighter = new Highlighter(ctx);
11
12 ctx.client.onNotification(ra.publishDecorations, params => {
13 if (!ctx.config.highlightingOn) return;
14
15 const targetEditor = vscode.window.visibleTextEditors.find(
16 editor => {
17 const unescapedUri = unescape(
18 editor.document.uri.toString(),
19 );
20 // Unescaped URI looks like:
21 // file:///c:/Workspace/ra-test/src/main.rs
22 return unescapedUri === params.uri;
23 },
24 );
25 if (!targetEditor) return;
26
27 highlighter.setHighlights(targetEditor, params.decorations);
28 });
29
30
31 vscode.workspace.onDidChangeConfiguration(
32 _ => highlighter.removeHighlights(),
33 null,
34 ctx.subscriptions,
35 );
36
37 vscode.window.onDidChangeActiveTextEditor(
38 async (editor: vscode.TextEditor | undefined) => {
39 if (!editor || !isRustDocument(editor.document)) return;
40 if (!ctx.config.highlightingOn) return;
41 const client = ctx.client;
42 if (!client) return;
43
44 const decorations = await sendRequestWithRetry(
45 client,
46 ra.decorationsRequest,
47 { uri: editor.document.uri.toString() },
48 );
49 highlighter.setHighlights(editor, decorations);
50 },
51 null,
52 ctx.subscriptions,
53 );
54}
55
56// Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76
57function fancify(seed: string, shade: 'light' | 'dark') {
58 const random = randomU32Numbers(hashString(seed));
59 const randomInt = (min: number, max: number) => {
60 return Math.abs(random()) % (max - min + 1) + min;
61 };
62
63 const h = randomInt(0, 360);
64 const s = randomInt(42, 98);
65 const l = shade === 'light' ? randomInt(15, 40) : randomInt(40, 90);
66 return `hsl(${h},${s}%,${l}%)`;
67}
68
69class Highlighter {
70 private ctx: Ctx;
71 private decorations: Map<
72 string,
73 vscode.TextEditorDecorationType
74 > | null = null;
75
76 constructor(ctx: Ctx) {
77 this.ctx = ctx;
78 }
79
80 public removeHighlights() {
81 if (this.decorations == null) {
82 return;
83 }
84
85 // Decorations are removed when the object is disposed
86 for (const decoration of this.decorations.values()) {
87 decoration.dispose();
88 }
89
90 this.decorations = null;
91 }
92
93 public setHighlights(editor: vscode.TextEditor, highlights: ra.Decoration[]) {
94 const client = this.ctx.client;
95 if (!client) return;
96 // Initialize decorations if necessary
97 //
98 // Note: decoration objects need to be kept around so we can dispose them
99 // if the user disables syntax highlighting
100 if (this.decorations == null) {
101 this.decorations = initDecorations();
102 }
103
104 const byTag: Map<string, vscode.Range[]> = new Map();
105 const colorfulIdents: Map<
106 string,
107 [vscode.Range[], boolean]
108 > = new Map();
109 const rainbowTime = this.ctx.config.rainbowHighlightingOn;
110
111 for (const tag of this.decorations.keys()) {
112 byTag.set(tag, []);
113 }
114
115 for (const d of highlights) {
116 if (!byTag.get(d.tag)) {
117 continue;
118 }
119
120 if (rainbowTime && d.bindingHash) {
121 if (!colorfulIdents.has(d.bindingHash)) {
122 const mut = d.tag.endsWith('.mut');
123 colorfulIdents.set(d.bindingHash, [[], mut]);
124 }
125 colorfulIdents
126 .get(d.bindingHash)![0]
127 .push(
128 client.protocol2CodeConverter.asRange(d.range),
129 );
130 } else {
131 byTag
132 .get(d.tag)!
133 .push(
134 client.protocol2CodeConverter.asRange(d.range),
135 );
136 }
137 }
138
139 for (const tag of byTag.keys()) {
140 const dec = this.decorations.get(
141 tag,
142 ) as vscode.TextEditorDecorationType;
143 const ranges = byTag.get(tag)!;
144 editor.setDecorations(dec, ranges);
145 }
146
147 for (const [hash, [ranges, mut]] of colorfulIdents.entries()) {
148 const textDecoration = mut ? 'underline' : undefined;
149 const dec = vscode.window.createTextEditorDecorationType({
150 light: { color: fancify(hash, 'light'), textDecoration },
151 dark: { color: fancify(hash, 'dark'), textDecoration },
152 });
153 editor.setDecorations(dec, ranges);
154 }
155 }
156}
157
158function initDecorations(): Map<string, vscode.TextEditorDecorationType> {
159 const theme = ColorTheme.load();
160 const res = new Map();
161 TAG_TO_SCOPES.forEach((scopes, tag) => {
162 // We are going to axe this soon, so don't try to detect unknown tags.
163 // Users should switch to the new semantic tokens implementation.
164 if (!scopes) return;
165 const rule = theme.lookup(scopes);
166 const decor = createDecorationFromTextmate(rule);
167 res.set(tag, decor);
168 });
169 return res;
170}
171
172function createDecorationFromTextmate(
173 themeStyle: TextMateRuleSettings,
174): vscode.TextEditorDecorationType {
175 const decorationOptions: vscode.DecorationRenderOptions = {};
176 decorationOptions.rangeBehavior = vscode.DecorationRangeBehavior.OpenOpen;
177
178 if (themeStyle.foreground) {
179 decorationOptions.color = themeStyle.foreground;
180 }
181
182 if (themeStyle.background) {
183 decorationOptions.backgroundColor = themeStyle.background;
184 }
185
186 if (themeStyle.fontStyle) {
187 const parts: string[] = themeStyle.fontStyle.split(' ');
188 parts.forEach(part => {
189 switch (part) {
190 case 'italic':
191 decorationOptions.fontStyle = 'italic';
192 break;
193 case 'bold':
194 decorationOptions.fontWeight = 'bold';
195 break;
196 case 'underline':
197 decorationOptions.textDecoration = 'underline';
198 break;
199 default:
200 break;
201 }
202 });
203 }
204 return vscode.window.createTextEditorDecorationType(decorationOptions);
205}
206
207// sync with tags from `syntax_highlighting.rs`.
208const TAG_TO_SCOPES = new Map<string, string[]>([
209 ["field", ["entity.name.field"]],
210 ["function", ["entity.name.function"]],
211 ["module", ["entity.name.module"]],
212 ["constant", ["entity.name.constant"]],
213 ["macro", ["entity.name.macro"]],
214
215 ["variable", ["variable"]],
216 ["variable.mutable", ["variable", "meta.mutable"]],
217
218 ["type", ["entity.name.type"]],
219 ["type.builtin", ["entity.name.type", "support.type.primitive"]],
220 ["type.self", ["entity.name.type.parameter.self"]],
221 ["type.param", ["entity.name.type.parameter", "entity.name.type.param.rust"]],
222 ["type.lifetime", ["entity.name.type.lifetime", "entity.name.lifetime.rust"]],
223
224 ["literal.byte", ["constant.character.byte"]],
225 ["literal.char", ["constant.character.rust"]],
226 ["numeric_literal", ["constant.numeric"]],
227
228 ["comment", ["comment"]],
229 ["string_literal", ["string.quoted"]],
230 ["attribute", ["meta.attribute.rust"]],
231
232 ["keyword", ["keyword"]],
233 ["keyword.unsafe", ["keyword.other.unsafe"]],
234 ["keyword.control", ["keyword.control"]],
235]);
236
237function randomU32Numbers(seed: number) {
238 let random = seed | 0;
239 return () => {
240 random ^= random << 13;
241 random ^= random >> 17;
242 random ^= random << 5;
243 random |= 0;
244 return random;
245 };
246}
247
248function hashString(str: string): number {
249 let res = 0;
250 for (let i = 0; i < str.length; ++i) {
251 const c = str.codePointAt(i)!;
252 res = (res * 31 + c) & ~0;
253 }
254 return res;
255}
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 7ba16120c..4f3b89f44 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -7,7 +7,6 @@ import * as commands from './commands';
7import { activateInlayHints } from './inlay_hints'; 7import { activateInlayHints } from './inlay_hints';
8import { activateStatusDisplay } from './status_display'; 8import { activateStatusDisplay } from './status_display';
9import { Ctx } from './ctx'; 9import { Ctx } from './ctx';
10import { activateHighlighting } from './highlighting';
11import { Config, NIGHTLY_TAG } from './config'; 10import { Config, NIGHTLY_TAG } from './config';
12import { log, assert } from './util'; 11import { log, assert } from './util';
13import { PersistentState } from './persistent_state'; 12import { PersistentState } from './persistent_state';
@@ -97,9 +96,6 @@ export async function activate(context: vscode.ExtensionContext) {
97 96
98 activateStatusDisplay(ctx); 97 activateStatusDisplay(ctx);
99 98
100 if (!ctx.config.highlightingSemanticTokens) {
101 activateHighlighting(ctx);
102 }
103 activateInlayHints(ctx); 99 activateInlayHints(ctx);
104 100
105 vscode.workspace.onDidChangeConfiguration( 101 vscode.workspace.onDidChangeConfiguration(
diff --git a/editors/code/src/status_display.ts b/editors/code/src/status_display.ts
index 0f5f6ef99..f9cadc8a2 100644
--- a/editors/code/src/status_display.ts
+++ b/editors/code/src/status_display.ts
@@ -7,7 +7,7 @@ import { Ctx } from './ctx';
7const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; 7const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
8 8
9export function activateStatusDisplay(ctx: Ctx) { 9export function activateStatusDisplay(ctx: Ctx) {
10 const statusDisplay = new StatusDisplay(ctx.config.cargoWatchOptions.command); 10 const statusDisplay = new StatusDisplay(ctx.config.checkOnSave.command);
11 ctx.pushCleanup(statusDisplay); 11 ctx.pushCleanup(statusDisplay);
12 const client = ctx.client; 12 const client = ctx.client;
13 if (client != null) { 13 if (client != null) {