summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/rust-analyzer/src
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/rust-analyzer/src')
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs37
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs25
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs22
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs33
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs7
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs70
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs42
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs13
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs82
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs66
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs5
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs41
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs88
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs61
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs46
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs277
21 files changed, 525 insertions, 422 deletions
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
index 53710749d..4de022b6e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
@@ -10,7 +10,6 @@ mod rustc_wrapper;
use std::{env, fs, path::Path, process};
use lsp_server::Connection;
-use project_model::ProjectManifest;
use rust_analyzer::{cli::flags, config::Config, from_json, Result};
use vfs::AbsPathBuf;
@@ -168,7 +167,18 @@ fn run_server() -> Result<()> {
}
};
- let mut config = Config::new(root_path, initialize_params.capabilities);
+ let workspace_roots = initialize_params
+ .workspace_folders
+ .map(|workspaces| {
+ workspaces
+ .into_iter()
+ .filter_map(|it| it.uri.to_file_path().ok())
+ .filter_map(|it| AbsPathBuf::try_from(it).ok())
+ .collect::<Vec<_>>()
+ })
+ .filter(|workspaces| !workspaces.is_empty())
+ .unwrap_or_else(|| vec![root_path.clone()]);
+ let mut config = Config::new(root_path, initialize_params.capabilities, workspace_roots);
if let Some(json) = initialize_params.initialization_options {
if let Err(e) = config.update(json) {
use lsp_types::{
@@ -183,8 +193,6 @@ fn run_server() -> Result<()> {
}
}
- config.client_specific_adjustments(&initialize_params.client_info);
-
let server_capabilities = rust_analyzer::server_capabilities(&config);
let initialize_result = lsp_types::InitializeResult {
@@ -204,25 +212,8 @@ fn run_server() -> Result<()> {
tracing::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
}
- if config.linked_projects().is_empty() && config.detached_files().is_empty() {
- let workspace_roots = initialize_params
- .workspace_folders
- .map(|workspaces| {
- workspaces
- .into_iter()
- .filter_map(|it| it.uri.to_file_path().ok())
- .filter_map(|it| AbsPathBuf::try_from(it).ok())
- .collect::<Vec<_>>()
- })
- .filter(|workspaces| !workspaces.is_empty())
- .unwrap_or_else(|| vec![config.root_path().clone()]);
-
- let discovered = ProjectManifest::discover_all(&workspace_roots);
- tracing::info!("discovered projects: {:?}", discovered);
- if discovered.is_empty() {
- tracing::error!("failed to find any projects in {:?}", workspace_roots);
- }
- config.discovered_projects = Some(discovered);
+ if !config.has_linked_projects() && config.detached_files().is_empty() {
+ config.rediscover_workspaces();
}
rust_analyzer::main_loop(config, connection)?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
index 122d2e6ff..3628670ac 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
@@ -1,4 +1,5 @@
//! Advertises the capabilities of the LSP Server.
+use ide_db::line_index::WideEncoding;
use lsp_types::{
CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
@@ -10,21 +11,25 @@ use lsp_types::{
SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities,
SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions,
- WorkspaceFileOperationsServerCapabilities, WorkspaceServerCapabilities,
+ WorkspaceFileOperationsServerCapabilities, WorkspaceFoldersServerCapabilities,
+ WorkspaceServerCapabilities,
};
use serde_json::json;
use crate::config::{Config, RustfmtConfig};
-use crate::lsp_ext::supports_utf8;
+use crate::line_index::PositionEncoding;
+use crate::lsp_ext::negotiated_encoding;
use crate::semantic_tokens;
pub fn server_capabilities(config: &Config) -> ServerCapabilities {
ServerCapabilities {
- position_encoding: if supports_utf8(config.caps()) {
- Some(PositionEncodingKind::UTF8)
- } else {
- None
- },
+ position_encoding: Some(match negotiated_encoding(config.caps()) {
+ PositionEncoding::Utf8 => PositionEncodingKind::UTF8,
+ PositionEncoding::Wide(wide) => match wide {
+ WideEncoding::Utf16 => PositionEncodingKind::UTF16,
+ WideEncoding::Utf32 => PositionEncodingKind::UTF32,
+ },
+ }),
text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
open_close: Some(true),
change: Some(TextDocumentSyncKind::INCREMENTAL),
@@ -80,7 +85,10 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
color_provider: None,
execute_command_provider: None,
workspace: Some(WorkspaceServerCapabilities {
- workspace_folders: None,
+ workspace_folders: Some(WorkspaceFoldersServerCapabilities {
+ supported: Some(true),
+ change_notifications: Some(OneOf::Left(true)),
+ }),
file_operations: Some(WorkspaceFileOperationsServerCapabilities {
did_create: None,
will_create: None,
@@ -130,6 +138,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
resolve_provider: Some(true),
},
))),
+ inline_value_provider: None,
experimental: Some(json!({
"externalDocs": true,
"hoverRange": true,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 053db5fc5..93297faa6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -15,7 +15,7 @@ use hir_def::{
expr::ExprId,
FunctionId,
};
-use hir_ty::{TyExt, TypeWalk};
+use hir_ty::{Interner, TyExt, TypeFlags};
use ide::{Analysis, AnalysisHost, LineCol, RootDatabase};
use ide_db::base_db::{
salsa::{self, debug::DebugQueryTable, ParallelDatabase},
@@ -33,7 +33,7 @@ use vfs::{AbsPathBuf, Vfs, VfsPath};
use crate::cli::{
flags::{self, OutputFormat},
- load_cargo::{load_workspace, LoadCargoConfig},
+ load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice},
print_memory_usage,
progress_report::ProgressReport,
report_metric, Result, Verbosity,
@@ -59,11 +59,6 @@ impl flags::AnalysisStats {
true => None,
false => Some(RustcSource::Discover),
};
- let load_cargo_config = LoadCargoConfig {
- load_out_dirs_from_check: !self.disable_build_scripts,
- with_proc_macro: !self.disable_proc_macros,
- prefill_caches: false,
- };
let no_progress = &|_| ();
let mut db_load_sw = self.stop_watch();
@@ -73,6 +68,11 @@ impl flags::AnalysisStats {
let mut workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
let metadata_time = db_load_sw.elapsed();
+ let load_cargo_config = LoadCargoConfig {
+ load_out_dirs_from_check: !self.disable_build_scripts,
+ with_proc_macro_server: ProcMacroServerChoice::Sysroot,
+ prefill_caches: false,
+ };
let build_scripts_time = if self.disable_build_scripts {
None
@@ -280,12 +280,8 @@ impl flags::AnalysisStats {
}
true
} else {
- let mut is_partially_unknown = false;
- ty.walk(&mut |ty| {
- if ty.is_unknown() {
- is_partially_unknown = true;
- }
- });
+ let is_partially_unknown =
+ ty.data(Interner).flags.contains(TypeFlags::HAS_ERROR);
if is_partially_unknown {
num_exprs_partially_unknown += 1;
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
index fd5b3ce61..ff821be53 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -9,7 +9,7 @@ use ide_db::base_db::SourceDatabaseExt;
use crate::cli::{
flags,
- load_cargo::{load_workspace_at, LoadCargoConfig},
+ load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice},
};
impl flags::Diagnostics {
@@ -17,7 +17,7 @@ impl flags::Diagnostics {
let cargo_config = Default::default();
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: !self.disable_build_scripts,
- with_proc_macro: !self.disable_proc_macros,
+ with_proc_macro_server: ProcMacroServerChoice::Sysroot,
prefill_caches: false,
};
let (host, _vfs, _proc_macro) =
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
index 762d7d3a1..5a958d963 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -1,6 +1,6 @@
//! Loads a Cargo project into a static instance of analysis, without support
//! for incorporating changes.
-use std::{path::Path, sync::Arc};
+use std::{convert::identity, path::Path, sync::Arc};
use anyhow::Result;
use crossbeam_channel::{unbounded, Receiver};
@@ -17,10 +17,17 @@ use crate::reload::{load_proc_macro, ProjectFolders, SourceRootConfig};
// what otherwise would be `pub(crate)` has to be `pub` here instead.
pub struct LoadCargoConfig {
pub load_out_dirs_from_check: bool,
- pub with_proc_macro: bool,
+ pub with_proc_macro_server: ProcMacroServerChoice,
pub prefill_caches: bool,
}
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ProcMacroServerChoice {
+ Sysroot,
+ Explicit(AbsPathBuf, Vec<String>),
+ None,
+}
+
// Note: Since this function is used by external tools that use rust-analyzer as a library
// what otherwise would be `pub(crate)` has to be `pub` here instead.
pub fn load_workspace_at(
@@ -59,15 +66,17 @@ pub fn load_workspace(
Box::new(loader)
};
- let proc_macro_client = if load_config.with_proc_macro {
- let (server_path, args): (_, &[_]) = match ws.find_sysroot_proc_macro_srv() {
- Some(server_path) => (server_path, &[]),
- None => (AbsPathBuf::assert(std::env::current_exe()?), &["proc-macro"]),
- };
-
- ProcMacroServer::spawn(server_path, args).map_err(|e| e.to_string())
- } else {
- Err("proc macro server disabled".to_owned())
+ let proc_macro_client = match &load_config.with_proc_macro_server {
+ ProcMacroServerChoice::Sysroot => ws
+ .find_sysroot_proc_macro_srv()
+ .ok_or_else(|| "failed to find sysroot proc-macro server".to_owned())
+ .and_then(|it| {
+ ProcMacroServer::spawn(it, identity::<&[&str]>(&[])).map_err(|e| e.to_string())
+ }),
+ ProcMacroServerChoice::Explicit(path, args) => {
+ ProcMacroServer::spawn(path.clone(), args).map_err(|e| e.to_string())
+ }
+ ProcMacroServerChoice::None => Err("proc macro server disabled".to_owned()),
};
let crate_graph = ws.to_crate_graph(
@@ -157,7 +166,7 @@ mod tests {
let cargo_config = CargoConfig::default();
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: false,
- with_proc_macro: false,
+ with_proc_macro_server: ProcMacroServerChoice::None,
prefill_caches: false,
};
let (host, _vfs, _proc_macro) =
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
index af8356d04..3fc1aa4ea 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
@@ -11,10 +11,12 @@ use ide::{
use ide_db::LineIndexDatabase;
use ide_db::base_db::salsa::{self, ParallelDatabase};
+use ide_db::line_index::WideEncoding;
use lsp_types::{self, lsif};
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
use vfs::{AbsPathBuf, Vfs};
+use crate::cli::load_cargo::ProcMacroServerChoice;
use crate::cli::{
flags,
load_cargo::{load_workspace, LoadCargoConfig},
@@ -126,7 +128,7 @@ impl LsifManager<'_> {
let line_index = self.db.line_index(file_id);
let line_index = LineIndex {
index: line_index,
- encoding: PositionEncoding::Utf16,
+ encoding: PositionEncoding::Wide(WideEncoding::Utf16),
endings: LineEndings::Unix,
};
let range_id = self.add_vertex(lsif::Vertex::Range {
@@ -248,7 +250,7 @@ impl LsifManager<'_> {
let line_index = self.db.line_index(file_id);
let line_index = LineIndex {
index: line_index,
- encoding: PositionEncoding::Utf16,
+ encoding: PositionEncoding::Wide(WideEncoding::Utf16),
endings: LineEndings::Unix,
};
let result = folds
@@ -291,7 +293,7 @@ impl flags::Lsif {
let no_progress = &|_| ();
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: true,
- with_proc_macro: true,
+ with_proc_macro_server: ProcMacroServerChoice::Sysroot,
prefill_caches: false,
};
let path = AbsPathBuf::assert(env::current_dir()?.join(&self.path));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index b050d1e95..9a04fbea7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -5,7 +5,10 @@ use std::{
time::Instant,
};
-use crate::line_index::{LineEndings, LineIndex, PositionEncoding};
+use crate::{
+ cli::load_cargo::ProcMacroServerChoice,
+ line_index::{LineEndings, LineIndex, PositionEncoding},
+};
use hir::Name;
use ide::{
LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId,
@@ -31,7 +34,7 @@ impl flags::Scip {
let no_progress = &|s| (eprintln!("rust-analyzer: Loading {s}"));
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: true,
- with_proc_macro: true,
+ with_proc_macro_server: ProcMacroServerChoice::Sysroot,
prefill_caches: true,
};
let path = vfs::AbsPathBuf::assert(env::current_dir()?.join(&self.path));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
index 84c489171..3552f840a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
@@ -5,7 +5,7 @@ use project_model::CargoConfig;
use crate::cli::{
flags,
- load_cargo::{load_workspace_at, LoadCargoConfig},
+ load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice},
Result,
};
@@ -15,7 +15,7 @@ impl flags::Ssr {
let cargo_config = CargoConfig::default();
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: true,
- with_proc_macro: true,
+ with_proc_macro_server: ProcMacroServerChoice::Sysroot,
prefill_caches: false,
};
let (host, vfs, _proc_macro) = load_workspace_at(
@@ -51,7 +51,7 @@ impl flags::Search {
let cargo_config = CargoConfig::default();
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: true,
- with_proc_macro: true,
+ with_proc_macro_server: ProcMacroServerChoice::Sysroot,
prefill_caches: false,
};
let (host, _vfs, _proc_macro) = load_workspace_at(
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index b0afbdc9a..f609a50a0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -20,7 +20,7 @@ use ide_db::{
SnippetCap,
};
use itertools::Itertools;
-use lsp_types::{ClientCapabilities, ClientInfo, MarkupKind};
+use lsp_types::{ClientCapabilities, MarkupKind};
use project_model::{
CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource,
UnsetTestCrates,
@@ -33,7 +33,7 @@ use crate::{
caps::completion_item_edit_resolve,
diagnostics::DiagnosticsMapConfig,
line_index::PositionEncoding,
- lsp_ext::{self, supports_utf8, WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope},
+ lsp_ext::{self, negotiated_encoding, WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope},
};
mod patch_old_style;
@@ -117,6 +117,11 @@ config_data! {
///
/// This option does not take effect until rust-analyzer is restarted.
cargo_sysroot: Option<String> = "\"discover\"",
+ /// Relative path to the sysroot library sources. If left unset, this will default to
+ /// `{cargo.sysroot}/lib/rustlib/src/rust/library`.
+ ///
+ /// This option does not take effect until rust-analyzer is restarted.
+ cargo_sysrootSrc: Option<String> = "null",
/// Compilation target override (target triple).
// FIXME(@poliorcetics): move to multiple targets here too, but this will need more work
// than `checkOnSave_target`
@@ -195,6 +200,8 @@ config_data! {
completion_autoself_enable: bool = "true",
/// Whether to add parenthesis and argument snippets when completing function.
completion_callable_snippets: CallableCompletionDef = "\"fill_arguments\"",
+ /// Maximum number of completions to return. If `None`, the limit is infinite.
+ completion_limit: Option<usize> = "null",
/// Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
completion_postfix_enable: bool = "true",
/// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.
@@ -342,8 +349,6 @@ config_data! {
inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
/// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
inlayHints_lifetimeElisionHints_useParameterNames: bool = "false",
- /// Whether to use location links for parts of type mentioned in inlay hints.
- inlayHints_locationLinks: bool = "true",
/// Maximum length for inlay hints. Set to null to have an unlimited length.
inlayHints_maxLength: Option<usize> = "25",
/// Whether to show function parameter name inlay hints at the call
@@ -521,6 +526,7 @@ impl Default for ConfigData {
#[derive(Debug, Clone)]
pub struct Config {
pub discovered_projects: Option<Vec<ProjectManifest>>,
+ pub workspace_roots: Vec<AbsPathBuf>,
caps: lsp_types::ClientCapabilities,
root_path: AbsPathBuf,
data: ConfigData,
@@ -717,7 +723,11 @@ impl fmt::Display for ConfigUpdateError {
}
impl Config {
- pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self {
+ pub fn new(
+ root_path: AbsPathBuf,
+ caps: ClientCapabilities,
+ workspace_roots: Vec<AbsPathBuf>,
+ ) -> Self {
Config {
caps,
data: ConfigData::default(),
@@ -725,20 +735,17 @@ impl Config {
discovered_projects: None,
root_path,
snippets: Default::default(),
+ workspace_roots,
}
}
- pub fn client_specific_adjustments(&mut self, client_info: &Option<ClientInfo>) {
- // FIXME: remove this when we drop support for vscode 1.65 and below
- if let Some(client) = client_info {
- if client.name.contains("Code") || client.name.contains("Codium") {
- if let Some(version) = &client.version {
- if version.as_str() < "1.76" {
- self.data.inlayHints_locationLinks = false;
- }
- }
- }
+ pub fn rediscover_workspaces(&mut self) {
+ let discovered = ProjectManifest::discover_all(&self.workspace_roots);
+ tracing::info!("discovered projects: {:?}", discovered);
+ if discovered.is_empty() {
+ tracing::error!("failed to find any projects in {:?}", &self.workspace_roots);
}
+ self.discovered_projects = Some(discovered);
}
pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigUpdateError> {
@@ -837,6 +844,9 @@ macro_rules! try_or_def {
}
impl Config {
+ pub fn has_linked_projects(&self) -> bool {
+ !self.data.linkedProjects.is_empty()
+ }
pub fn linked_projects(&self) -> Vec<LinkedProject> {
match self.data.linkedProjects.as_slice() {
[] => match self.discovered_projects.as_ref() {
@@ -989,11 +999,7 @@ impl Config {
}
pub fn position_encoding(&self) -> PositionEncoding {
- if supports_utf8(&self.caps) {
- PositionEncoding::Utf8
- } else {
- PositionEncoding::Utf16
- }
+ negotiated_encoding(&self.caps)
}
fn experimental(&self, index: &'static str) -> bool {
@@ -1004,6 +1010,10 @@ impl Config {
self.experimental("codeActionGroup")
}
+ pub fn open_server_logs(&self) -> bool {
+ self.experimental("openServerLogs")
+ }
+
pub fn server_status_notification(&self) -> bool {
self.experimental("serverStatusNotification")
}
@@ -1044,7 +1054,7 @@ impl Config {
&self.data.cargo_extraEnv
}
- pub fn check_on_save_extra_env(&self) -> FxHashMap<String, String> {
+ pub fn check_extra_env(&self) -> FxHashMap<String, String> {
let mut extra_env = self.data.cargo_extraEnv.clone();
extra_env.extend(self.data.check_extraEnv.clone());
extra_env
@@ -1114,6 +1124,8 @@ impl Config {
RustcSource::Path(self.root_path.join(sysroot))
}
});
+ let sysroot_src =
+ self.data.cargo_sysrootSrc.as_ref().map(|sysroot| self.root_path.join(sysroot));
CargoConfig {
features: match &self.data.cargo_features {
@@ -1125,6 +1137,7 @@ impl Config {
},
target: self.data.cargo_target.clone(),
sysroot,
+ sysroot_src,
rustc_source,
unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()),
wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper,
@@ -1165,7 +1178,7 @@ impl Config {
FlycheckConfig::CustomCommand {
command,
args,
- extra_env: self.check_on_save_extra_env(),
+ extra_env: self.check_extra_env(),
invocation_strategy: match self.data.check_invocationStrategy {
InvocationStrategy::Once => flycheck::InvocationStrategy::Once,
InvocationStrategy::PerWorkspace => {
@@ -1210,7 +1223,7 @@ impl Config {
CargoFeaturesDef::Selected(it) => it,
},
extra_args: self.data.check_extraArgs.clone(),
- extra_env: self.check_on_save_extra_env(),
+ extra_env: self.check_extra_env(),
ansi_color_output: self.color_diagnostic_output(),
},
}
@@ -1229,7 +1242,6 @@ impl Config {
pub fn inlay_hints(&self) -> InlayHintsConfig {
InlayHintsConfig {
- location_links: self.data.inlayHints_locationLinks,
render_colons: self.data.inlayHints_renderColons,
type_hints: self.data.inlayHints_typeHints_enable,
parameter_hints: self.data.inlayHints_parameterHints_enable,
@@ -1329,6 +1341,7 @@ impl Config {
.snippet_support?
)),
snippets: self.snippets.clone(),
+ limit: self.data.completion_limit,
}
}
@@ -1409,7 +1422,8 @@ impl Config {
pub fn hover(&self) -> HoverConfig {
HoverConfig {
links_in_hover: self.data.hover_links_enable,
- documentation: self.data.hover_documentation_enable.then(|| {
+ documentation: self.data.hover_documentation_enable,
+ format: {
let is_markdown = try_or_def!(self
.caps
.text_document
@@ -1425,7 +1439,7 @@ impl Config {
} else {
HoverDocFormat::PlainText
}
- }),
+ },
keywords: self.data.hover_documentation_keywords_enable,
}
}
@@ -1454,6 +1468,10 @@ impl Config {
try_or_def!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?)
}
+ pub fn inlay_hints_refresh(&self) -> bool {
+ try_or_def!(self.caps.workspace.as_ref()?.inlay_hint.as_ref()?.refresh_support?)
+ }
+
pub fn insert_replace_support(&self) -> bool {
try_or_def!(
self.caps
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs
index de6ac946a..73d2ed329 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs
@@ -114,16 +114,18 @@ pub(super) fn patch_json_for_outdated_configs(json: &mut Value) {
}
// completion_addCallArgumentSnippets completion_addCallParenthesis -> completion_callable_snippets
- let res = match (
- copy.pointer("/completion/addCallArgumentSnippets"),
- copy.pointer("/completion/addCallParenthesis"),
- ) {
- (Some(Value::Bool(true)), Some(Value::Bool(true))) => json!("fill_arguments"),
- (_, Some(Value::Bool(true))) => json!("add_parentheses"),
- (Some(Value::Bool(false)), Some(Value::Bool(false))) => json!("none"),
- (_, _) => return,
- };
- merge(json, json!({ "completion": { "callable": {"snippets": res }} }));
+ 'completion: {
+ let res = match (
+ copy.pointer("/completion/addCallArgumentSnippets"),
+ copy.pointer("/completion/addCallParenthesis"),
+ ) {
+ (Some(Value::Bool(true)), Some(Value::Bool(true))) => json!("fill_arguments"),
+ (_, Some(Value::Bool(true))) => json!("add_parentheses"),
+ (Some(Value::Bool(false)), Some(Value::Bool(false))) => json!("none"),
+ (_, _) => break 'completion,
+ };
+ merge(json, json!({ "completion": { "callable": {"snippets": res }} }));
+ }
// We need to do this due to the checkOnSave_enable -> checkOnSave change, as that key now can either be an object or a bool
// checkOnSave_* -> check_*
@@ -146,3 +148,23 @@ fn merge(dst: &mut Value, src: Value) {
(dst, src) => *dst = src,
}
}
+
+#[test]
+fn check_on_save_patching() {
+ let mut json = json!({ "checkOnSave": { "overrideCommand": "foo" }});
+ patch_json_for_outdated_configs(&mut json);
+ assert_eq!(
+ json,
+ json!({ "checkOnSave": { "overrideCommand": "foo" }, "check": { "overrideCommand": "foo" }})
+ );
+}
+
+#[test]
+fn check_on_save_patching_enable() {
+ let mut json = json!({ "checkOnSave": { "enable": true, "overrideCommand": "foo" }});
+ patch_json_for_outdated_configs(&mut json);
+ assert_eq!(
+ json,
+ json!({ "checkOnSave": true, "check": { "enable": true, "overrideCommand": "foo" }})
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
index acb416a06..415fa4e02 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -3,6 +3,7 @@
use std::collections::HashMap;
use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan};
+use ide_db::line_index::WideEncoding;
use itertools::Itertools;
use stdx::format_to;
use vfs::{AbsPath, AbsPathBuf};
@@ -95,7 +96,8 @@ fn position(
let mut char_offset = 0;
let len_func = match position_encoding {
PositionEncoding::Utf8 => char::len_utf8,
- PositionEncoding::Utf16 => char::len_utf16,
+ PositionEncoding::Wide(WideEncoding::Utf16) => char::len_utf16,
+ PositionEncoding::Wide(WideEncoding::Utf32) => |_| 1,
};
for c in line.text.chars() {
char_offset += 1;
@@ -534,7 +536,7 @@ mod tests {
let (sender, _) = crossbeam_channel::unbounded();
let state = GlobalState::new(
sender,
- Config::new(workspace_root.to_path_buf(), ClientCapabilities::default()),
+ Config::new(workspace_root.to_path_buf(), ClientCapabilities::default(), Vec::new()),
);
let snap = state.snapshot();
let mut actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root, &snap);
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
index 2dbb14fcd..50af38cd6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
@@ -1,7 +1,10 @@
//! Conversion lsp_types types to rust-analyzer specific ones.
use anyhow::format_err;
-use ide::{Annotation, AnnotationKind, AssistKind, LineCol, LineColUtf16};
-use ide_db::base_db::{FileId, FilePosition, FileRange};
+use ide::{Annotation, AnnotationKind, AssistKind, LineCol};
+use ide_db::{
+ base_db::{FileId, FilePosition, FileRange},
+ line_index::WideLineCol,
+};
use syntax::{TextRange, TextSize};
use vfs::AbsPathBuf;
@@ -26,9 +29,9 @@ pub(crate) fn vfs_path(url: &lsp_types::Url) -> Result<vfs::VfsPath> {
pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> Result<TextSize> {
let line_col = match line_index.encoding {
PositionEncoding::Utf8 => LineCol { line: position.line, col: position.character },
- PositionEncoding::Utf16 => {
- let line_col = LineColUtf16 { line: position.line, col: position.character };
- line_index.index.to_utf8(line_col)
+ PositionEncoding::Wide(enc) => {
+ let line_col = WideLineCol { line: position.line, col: position.character };
+ line_index.index.to_utf8(enc, line_col)
}
};
let text_size =
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index c6f4e9ce0..aca6c9235 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -179,10 +179,9 @@ impl GlobalState {
pub(crate) fn process_changes(&mut self) -> bool {
let _p = profile::span("GlobalState::process_changes");
- // A file was added or deleted
- let mut has_structure_changes = false;
let mut workspace_structure_change = None;
+ let mut file_changes = FxHashMap::default();
let (change, changed_files) = {
let mut change = Change::new();
let (vfs, line_endings_map) = &mut *self.vfs.write();
@@ -191,43 +190,56 @@ impl GlobalState {
return false;
}
- // important: this needs to be a stable sort, the order between changes is relevant
- // for the same file ids
- changed_files.sort_by_key(|file| file.file_id);
- // We need to fix up the changed events a bit, if we have a create or modify for a file
- // id that is followed by a delete we actually no longer observe the file text from the
- // create or modify which may cause problems later on
- changed_files.dedup_by(|a, b| {
+ // We need to fix up the changed events a bit. If we have a create or modify for a file
+ // id that is followed by a delete we actually skip observing the file text from the
+ // earlier event, to avoid problems later on.
+ for changed_file in &changed_files {
use vfs::ChangeKind::*;
- if a.file_id != b.file_id {
- return false;
- }
+ file_changes
+ .entry(changed_file.file_id)
+ .and_modify(|(change, just_created)| {
+ // None -> Delete => keep
+ // Create -> Delete => collapse
+ //
+ match (change, just_created, changed_file.change_kind) {
+ // latter `Delete` wins
+ (change, _, Delete) => *change = Delete,
+ // merge `Create` with `Create` or `Modify`
+ (Create, _, Create | Modify) => {}
+ // collapse identical `Modify`es
+ (Modify, _, Modify) => {}
+ // equivalent to `Modify`
+ (change @ Delete, just_created, Create) => {
+ *change = Modify;
+ *just_created = true;
+ }
+ // shouldn't occur, but collapse into `Create`
+ (change @ Delete, just_created, Modify) => {
+ *change = Create;
+ *just_created = true;
+ }
+ // shouldn't occur, but collapse into `Modify`
+ (Modify, _, Create) => {}
+ }
+ })
+ .or_insert((
+ changed_file.change_kind,
+ matches!(changed_file.change_kind, Create),
+ ));
+ }
- match (a.change_kind, b.change_kind) {
- // duplicate can be merged
- (Create, Create) | (Modify, Modify) | (Delete, Delete) => true,
- // just leave the create, modify is irrelevant
- (Create, Modify) => {
- std::mem::swap(a, b);
- true
- }
- // modify becomes irrelevant if the file is deleted
- (Modify, Delete) => true,
- // we should fully remove this occurrence,
- // but leaving just a delete works as well
- (Create, Delete) => true,
- // this is equivalent to a modify
- (Delete, Create) => {
- a.change_kind = Modify;
- true
- }
- // can't really occur
- (Modify, Create) => false,
- (Delete, Modify) => false,
- }
- });
+ changed_files.extend(
+ file_changes
+ .into_iter()
+ .filter(|(_, (change_kind, just_created))| {
+ !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true))
+ })
+ .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind }),
+ );
+ // A file was added or deleted
+ let mut has_structure_changes = false;
for file in &changed_files {
if let Some(path) = vfs.file_path(file.file_id).as_path() {
let path = path.to_path_buf();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
index 59bdd3061..4e08bd0a7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
@@ -29,7 +29,6 @@ use project_model::{ManifestPath, ProjectWorkspace, TargetKind};
use serde_json::json;
use stdx::{format_to, never};
use syntax::{algo, ast, AstNode, TextRange, TextSize};
-use tracing::error;
use vfs::AbsPathBuf;
use crate::{
@@ -937,8 +936,7 @@ pub(crate) fn handle_hover(
let line_index = snap.file_line_index(file_range.file_id)?;
let range = to_proto::range(&line_index, info.range);
- let markup_kind =
- snap.config.hover().documentation.map_or(ide::HoverDocFormat::Markdown, |kind| kind);
+ let markup_kind = snap.config.hover().format;
let hover = lsp_ext::Hover {
hover: lsp_types::Hover {
contents: HoverContents::Markup(to_proto::markup_content(
@@ -1360,55 +1358,10 @@ pub(crate) fn handle_inlay_hints(
}
pub(crate) fn handle_inlay_hints_resolve(
- snap: GlobalStateSnapshot,
- mut hint: InlayHint,
+ _snap: GlobalStateSnapshot,
+ hint: InlayHint,
) -> Result<InlayHint> {
let _p = profile::span("handle_inlay_hints_resolve");
- let data = match hint.data.take() {
- Some(it) => it,
- None => return Ok(hint),
- };
-
- let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?;
-
- match snap.url_file_version(&resolve_data.text_document.uri) {
- Some(version) if version == resolve_data.text_document.version => {}
- Some(version) => {
- error!(
- "attempted inlayHints/resolve of '{}' at version {} while server version is {}",
- resolve_data.text_document.uri, resolve_data.text_document.version, version,
- );
- return Ok(hint);
- }
- None => {
- error!(
- "attempted inlayHints/resolve of unknown file '{}' at version {}",
- resolve_data.text_document.uri, resolve_data.text_document.version,
- );
- return Ok(hint);
- }
- }
- let file_range = from_proto::file_range_uri(
- &snap,
- &resolve_data.text_document.uri,
- match resolve_data.position {
- PositionOrRange::Position(pos) => Range::new(pos, pos),
- PositionOrRange::Range(range) => range,
- },
- )?;
- let info = match snap.analysis.hover(&snap.config.hover(), file_range)? {
- None => return Ok(hint),
- Some(info) => info,
- };
-
- let markup_kind =
- snap.config.hover().documentation.map_or(ide::HoverDocFormat::Markdown, |kind| kind);
-
- // FIXME: hover actions?
- hint.tooltip = Some(lsp_types::InlayHintTooltip::MarkupContent(to_proto::markup_content(
- info.info.markup,
- markup_kind,
- )));
Ok(hint)
}
@@ -1516,7 +1469,8 @@ pub(crate) fn handle_semantic_tokens_full(
let mut highlight_config = snap.config.highlighting_config();
// Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
- highlight_config.syntactic_name_ref_highlighting = !snap.proc_macros_loaded;
+ highlight_config.syntactic_name_ref_highlighting =
+ snap.workspaces.is_empty() || !snap.proc_macros_loaded;
let highlights = snap.analysis.highlight(highlight_config, file_id)?;
let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
@@ -1539,7 +1493,8 @@ pub(crate) fn handle_semantic_tokens_full_delta(
let mut highlight_config = snap.config.highlighting_config();
// Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
- highlight_config.syntactic_name_ref_highlighting = !snap.proc_macros_loaded;
+ highlight_config.syntactic_name_ref_highlighting =
+ snap.workspaces.is_empty() || !snap.proc_macros_loaded;
let highlights = snap.analysis.highlight(highlight_config, file_id)?;
let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
@@ -1570,7 +1525,12 @@ pub(crate) fn handle_semantic_tokens_range(
let text = snap.analysis.file_text(frange.file_id)?;
let line_index = snap.file_line_index(frange.file_id)?;
- let highlights = snap.analysis.highlight_range(snap.config.highlighting_config(), frange)?;
+ let mut highlight_config = snap.config.highlighting_config();
+ // Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
+ highlight_config.syntactic_name_ref_highlighting =
+ snap.workspaces.is_empty() || !snap.proc_macros_loaded;
+
+ let highlights = snap.analysis.highlight_range(highlight_config, frange)?;
let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
Ok(Some(semantic_tokens.into()))
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index 405d261db..e8912b907 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -21,7 +21,7 @@ use project_model::CargoConfig;
use test_utils::project_root;
use vfs::{AbsPathBuf, VfsPath};
-use crate::cli::load_cargo::{load_workspace_at, LoadCargoConfig};
+use crate::cli::load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
#[test]
fn integrated_highlighting_benchmark() {
@@ -36,7 +36,7 @@ fn integrated_highlighting_benchmark() {
let cargo_config = CargoConfig::default();
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: true,
- with_proc_macro: false,
+ with_proc_macro_server: ProcMacroServerChoice::None,
prefill_caches: false,
};
@@ -90,7 +90,7 @@ fn integrated_completion_benchmark() {
let cargo_config = CargoConfig::default();
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: true,
- with_proc_macro: false,
+ with_proc_macro_server: ProcMacroServerChoice::None,
prefill_caches: true,
};
@@ -146,6 +146,7 @@ fn integrated_completion_benchmark() {
},
snippets: Vec::new(),
prefer_no_std: false,
+ limit: None,
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -184,6 +185,7 @@ fn integrated_completion_benchmark() {
},
snippets: Vec::new(),
prefer_no_std: false,
+ limit: None,
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs
index 2945dba12..791cd931d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs
@@ -7,9 +7,12 @@
use std::sync::Arc;
+use ide_db::line_index::WideEncoding;
+
+#[derive(Clone, Copy)]
pub enum PositionEncoding {
Utf8,
- Utf16,
+ Wide(WideEncoding),
}
pub(crate) struct LineIndex {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
index 65620b420..e33589cc5 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
@@ -2,14 +2,17 @@
use std::{collections::HashMap, path::PathBuf};
+use ide_db::line_index::WideEncoding;
use lsp_types::request::Request;
+use lsp_types::PositionEncodingKind;
use lsp_types::{
notification::Notification, CodeActionKind, DocumentOnTypeFormattingParams,
PartialResultParams, Position, Range, TextDocumentIdentifier, WorkDoneProgressParams,
};
-use lsp_types::{PositionEncodingKind, VersionedTextDocumentIdentifier};
use serde::{Deserialize, Serialize};
+use crate::line_index::PositionEncoding;
+
pub enum AnalyzerStatus {}
impl Request for AnalyzerStatus {
@@ -151,6 +154,13 @@ impl Notification for ClearFlycheck {
const METHOD: &'static str = "rust-analyzer/clearFlycheck";
}
+pub enum OpenServerLogs {}
+
+impl Notification for OpenServerLogs {
+ type Params = ();
+ const METHOD: &'static str = "rust-analyzer/openServerLogs";
+}
+
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RunFlycheckParams {
@@ -474,16 +484,22 @@ pub(crate) enum CodeLensResolveData {
References(lsp_types::TextDocumentPositionParams),
}
-pub fn supports_utf8(caps: &lsp_types::ClientCapabilities) -> bool {
- match &caps.general {
- Some(general) => general
- .position_encodings
- .as_deref()
- .unwrap_or_default()
- .iter()
- .any(|it| it == &PositionEncodingKind::UTF8),
- _ => false,
+pub fn negotiated_encoding(caps: &lsp_types::ClientCapabilities) -> PositionEncoding {
+ let client_encodings = match &caps.general {
+ Some(general) => general.position_encodings.as_deref().unwrap_or_default(),
+ None => &[],
+ };
+
+ for enc in client_encodings {
+ if enc == &PositionEncodingKind::UTF8 {
+ return PositionEncoding::Utf8;
+ } else if enc == &PositionEncodingKind::UTF32 {
+ return PositionEncoding::Wide(WideEncoding::Utf32);
+ }
+ // NB: intentionally prefer just about anything else to utf-16.
}
+
+ PositionEncoding::Wide(WideEncoding::Utf16)
}
pub enum MoveItem {}
@@ -568,10 +584,7 @@ pub struct CompletionResolveData {
}
#[derive(Debug, Serialize, Deserialize)]
-pub struct InlayHintResolveData {
- pub text_document: VersionedTextDocumentIdentifier,
- pub position: PositionOrRange,
-}
+pub struct InlayHintResolveData {}
#[derive(Debug, Serialize, Deserialize)]
pub struct CompletionImport {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
index dcaee9285..30f1c53c1 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
@@ -2,12 +2,13 @@
use std::{mem, ops::Range, sync::Arc};
use lsp_server::Notification;
+use lsp_types::request::Request;
use crate::{
from_proto,
global_state::GlobalState,
line_index::{LineEndings, LineIndex, PositionEncoding},
- LspError,
+ lsp_ext, LspError,
};
pub(crate) fn invalid_params_error(message: String) -> LspError {
@@ -46,20 +47,47 @@ impl GlobalState {
/// If `additional_info` is [`Some`], appends a note to the notification telling to check the logs.
/// This will always log `message` + `additional_info` to the server's error log.
pub(crate) fn show_and_log_error(&mut self, message: String, additional_info: Option<String>) {
- let mut message = message;
match additional_info {
Some(additional_info) => {
- tracing::error!("{}\n\n{}", &message, &additional_info);
- if tracing::enabled!(tracing::Level::ERROR) {
- message.push_str("\n\nCheck the server logs for additional info.");
+ tracing::error!("{}:\n{}", &message, &additional_info);
+ match self.config.open_server_logs() && tracing::enabled!(tracing::Level::ERROR) {
+ true => self.send_request::<lsp_types::request::ShowMessageRequest>(
+ lsp_types::ShowMessageRequestParams {
+ typ: lsp_types::MessageType::ERROR,
+ message,
+ actions: Some(vec![lsp_types::MessageActionItem {
+ title: "Open server logs".to_owned(),
+ properties: Default::default(),
+ }]),
+ },
+ |this, resp| {
+ let lsp_server::Response { error: None, result: Some(result), .. } = resp
+ else { return };
+ if let Ok(Some(_item)) = crate::from_json::<
+ <lsp_types::request::ShowMessageRequest as lsp_types::request::Request>::Result,
+ >(
+ lsp_types::request::ShowMessageRequest::METHOD, &result
+ ) {
+ this.send_notification::<lsp_ext::OpenServerLogs>(());
+ }
+ },
+ ),
+ false => self.send_notification::<lsp_types::notification::ShowMessage>(
+ lsp_types::ShowMessageParams {
+ typ: lsp_types::MessageType::ERROR,
+ message,
+ },
+ ),
}
}
- None => tracing::error!("{}", &message),
- }
+ None => {
+ tracing::error!("{}", &message);
- self.send_notification::<lsp_types::notification::ShowMessage>(
- lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, message },
- )
+ self.send_notification::<lsp_types::notification::ShowMessage>(
+ lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, message },
+ );
+ }
+ }
}
/// rust-analyzer is resilient -- if it fails, this doesn't usually affect
@@ -77,7 +105,7 @@ impl GlobalState {
let from_source_build = option_env!("POKE_RA_DEVS").is_some();
let profiling_enabled = std::env::var("RA_PROFILE").is_ok();
if from_source_build || profiling_enabled {
- self.show_message(lsp_types::MessageType::ERROR, message)
+ self.show_and_log_error(message, None);
}
}
@@ -133,6 +161,7 @@ impl GlobalState {
}
pub(crate) fn apply_document_changes(
+ encoding: PositionEncoding,
file_contents: impl FnOnce() -> String,
mut content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>,
) -> String {
@@ -164,9 +193,9 @@ pub(crate) fn apply_document_changes(
let mut line_index = LineIndex {
// the index will be overwritten in the bottom loop's first iteration
index: Arc::new(ide::LineIndex::new(&text)),
- // We don't care about line endings or offset encoding here.
+ // We don't care about line endings here.
endings: LineEndings::Unix,
- encoding: PositionEncoding::Utf16,
+ encoding,
};
// The changes we got must be applied sequentially, but can cross lines so we
@@ -228,6 +257,7 @@ pub(crate) fn all_edits_are_disjoint(
#[cfg(test)]
mod tests {
+ use ide_db::line_index::WideEncoding;
use lsp_types::{
CompletionItem, CompletionTextEdit, InsertReplaceEdit, Position, Range,
TextDocumentContentChangeEvent,
@@ -250,9 +280,11 @@ mod tests {
};
}
- let text = apply_document_changes(|| String::new(), vec![]);
+ let encoding = PositionEncoding::Wide(WideEncoding::Utf16);
+ let text = apply_document_changes(encoding, || String::new(), vec![]);
assert_eq!(text, "");
let text = apply_document_changes(
+ encoding,
|| text,
vec![TextDocumentContentChangeEvent {
range: None,
@@ -261,39 +293,49 @@ mod tests {
}],
);
assert_eq!(text, "the");
- let text = apply_document_changes(|| text, c![0, 3; 0, 3 => " quick"]);
+ let text = apply_document_changes(encoding, || text, c![0, 3; 0, 3 => " quick"]);
assert_eq!(text, "the quick");
- let text = apply_document_changes(|| text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]);
+ let text =
+ apply_document_changes(encoding, || text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]);
assert_eq!(text, "quick foxes");
- let text = apply_document_changes(|| text, c![0, 11; 0, 11 => "\ndream"]);
+ let text = apply_document_changes(encoding, || text, c![0, 11; 0, 11 => "\ndream"]);
assert_eq!(text, "quick foxes\ndream");
- let text = apply_document_changes(|| text, c![1, 0; 1, 0 => "have "]);
+ let text = apply_document_changes(encoding, || text, c![1, 0; 1, 0 => "have "]);
assert_eq!(text, "quick foxes\nhave dream");
let text = apply_document_changes(
+ encoding,
|| text,
c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"],
);
assert_eq!(text, "the quick foxes\nhave quiet dreams\n");
- let text = apply_document_changes(|| text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]);
+ let text = apply_document_changes(
+ encoding,
+ || text,
+ c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"],
+ );
assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n");
let text = apply_document_changes(
+ encoding,
|| text,
c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"],
);
assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n");
- let text = apply_document_changes(|| text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]);
+ let text =
+ apply_document_changes(encoding, || text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]);
assert_eq!(text, "the quick \nthey have quiet dreams\n");
let text = String::from("❤️");
- let text = apply_document_changes(|| text, c![0, 0; 0, 0 => "a"]);
+ let text = apply_document_changes(encoding, || text, c![0, 0; 0, 0 => "a"]);
assert_eq!(text, "a❤️");
let text = String::from("a\nb");
- let text = apply_document_changes(|| text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
+ let text =
+ apply_document_changes(encoding, || text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
assert_eq!(text, "adcb");
let text = String::from("a\nb");
- let text = apply_document_changes(|| text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
+ let text =
+ apply_document_changes(encoding, || text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
assert_eq!(text, "ațc\ncb");
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 0bc940dfe..d1e38b33c 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -14,7 +14,7 @@ use ide_db::base_db::{SourceDatabaseExt, VfsPath};
use itertools::Itertools;
use lsp_server::{Connection, Notification, Request};
use lsp_types::notification::Notification as _;
-use vfs::{ChangeKind, FileId};
+use vfs::{AbsPathBuf, ChangeKind, FileId};
use crate::{
config::Config,
@@ -287,8 +287,10 @@ impl GlobalState {
|| self.fetch_build_data_queue.op_requested());
if became_quiescent {
- // Project has loaded properly, kick off initial flycheck
- self.flycheck.iter().for_each(FlycheckHandle::restart);
+ if self.config.check_on_save() {
+ // Project has loaded properly, kick off initial flycheck
+ self.flycheck.iter().for_each(FlycheckHandle::restart);
+ }
if self.config.prefill_caches() {
self.prime_caches_queue.request_op("became quiescent".to_string());
}
@@ -305,13 +307,18 @@ impl GlobalState {
if self.config.code_lens_refresh() {
self.send_request::<lsp_types::request::CodeLensRefresh>((), |_, _| ());
}
- }
- if !was_quiescent || state_changed || memdocs_added_or_removed {
- if self.config.publish_diagnostics() {
- self.update_diagnostics()
+ // Refresh inlay hints if the client supports it.
+ if self.config.inlay_hints_refresh() {
+ self.send_request::<lsp_types::request::InlayHintRefreshRequest>((), |_, _| ());
}
}
+
+ if (!was_quiescent || state_changed || memdocs_added_or_removed)
+ && self.config.publish_diagnostics()
+ {
+ self.update_diagnostics()
+ }
}
if let Some(diagnostic_changes) = self.diagnostics.take_changes() {
@@ -604,8 +611,8 @@ impl GlobalState {
Ok(())
});
- if let RequestDispatcher { req: Some(req), global_state: this } = &mut dispatcher {
- if this.shutdown_requested {
+ match &mut dispatcher {
+ RequestDispatcher { req: Some(req), global_state: this } if this.shutdown_requested => {
this.respond(lsp_server::Response::new_err(
req.id.clone(),
lsp_server::ErrorCode::InvalidRequest as i32,
@@ -613,16 +620,7 @@ impl GlobalState {
));
return;
}
-
- // Avoid flashing a bunch of unresolved references during initial load.
- if this.workspaces.is_empty() && !this.is_quiescent() {
- this.respond(lsp_server::Response::new_err(
- req.id.clone(),
- lsp_server::ErrorCode::ContentModified as i32,
- "waiting for cargo metadata or cargo check".to_owned(),
- ));
- return;
- }
+ _ => (),
}
dispatcher
@@ -833,6 +831,7 @@ impl GlobalState {
let vfs = &mut this.vfs.write().0;
let file_id = vfs.file_id(&path).unwrap();
let text = apply_document_changes(
+ this.config.position_encoding(),
|| std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(),
params.content_changes,
);
@@ -935,6 +934,30 @@ impl GlobalState {
Ok(())
})?
+ .on::<lsp_types::notification::DidChangeWorkspaceFolders>(|this, params| {
+ let config = Arc::make_mut(&mut this.config);
+
+ for workspace in params.event.removed {
+ let Ok(path) = workspace.uri.to_file_path() else { continue };
+ let Ok(path) = AbsPathBuf::try_from(path) else { continue };
+ let Some(position) = config.workspace_roots.iter().position(|it| it == &path) else { continue };
+ config.workspace_roots.remove(position);
+ }
+
+ let added = params
+ .event
+ .added
+ .into_iter()
+ .filter_map(|it| it.uri.to_file_path().ok())
+ .filter_map(|it| AbsPathBuf::try_from(it).ok());
+ config.workspace_roots.extend(added);
+ if !config.has_linked_projects() && config.detached_files().is_empty() {
+ config.rediscover_workspaces();
+ this.fetch_workspaces_queue.request_op("client workspaces changed".to_string())
+ }
+
+ Ok(())
+ })?
.on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| {
for change in params.changes {
if let Ok(path) = from_proto::abs_path(&change.uri) {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 9bbce70ec..abce0d737 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -34,6 +34,8 @@ use crate::{
op_queue::Cause,
};
+use ::tt::token_id as tt;
+
#[derive(Debug)]
pub(crate) enum ProjectWorkspaceProgress {
Begin,
@@ -148,11 +150,11 @@ impl GlobalState {
)
}
LinkedProject::InlineJsonProject(it) => {
- project_model::ProjectWorkspace::load_inline(
+ Ok(project_model::ProjectWorkspace::load_inline(
it.clone(),
cargo_config.target.as_deref(),
&cargo_config.extra_env,
- )
+ ))
}
})
.collect::<Vec<_>>();
@@ -212,35 +214,11 @@ impl GlobalState {
let workspaces =
workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::<Vec<_>>();
- fn eq_ignore_build_data<'a>(
- left: &'a ProjectWorkspace,
- right: &'a ProjectWorkspace,
- ) -> bool {
- let key = |p: &'a ProjectWorkspace| match p {
- ProjectWorkspace::Cargo {
- cargo,
- sysroot,
- rustc,
- rustc_cfg,
- cfg_overrides,
-
- build_scripts: _,
- toolchain: _,
- target_layout: _,
- } => Some((cargo, sysroot, rustc, rustc_cfg, cfg_overrides)),
- _ => None,
- };
- match (key(left), key(right)) {
- (Some(lk), Some(rk)) => lk == rk,
- _ => left == right,
- }
- }
-
let same_workspaces = workspaces.len() == self.workspaces.len()
&& workspaces
.iter()
.zip(self.workspaces.iter())
- .all(|(l, r)| eq_ignore_build_data(l, r));
+ .all(|(l, r)| l.eq_ignore_build_data(r));
if same_workspaces {
let (workspaces, build_scripts) = self.fetch_build_data_queue.last_op_result();
@@ -270,7 +248,8 @@ impl GlobalState {
// Here, we completely changed the workspace (Cargo.toml edit), so
// we don't care about build-script results, they are stale.
- self.workspaces = Arc::new(workspaces)
+ // FIXME: can we abort the build scripts here?
+ self.workspaces = Arc::new(workspaces);
}
if let FilesWatcher::Client = self.config.files().watcher {
@@ -289,7 +268,10 @@ impl GlobalState {
]
})
})
- .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
+ .map(|glob_pattern| lsp_types::FileSystemWatcher {
+ glob_pattern: lsp_types::GlobPattern::String(glob_pattern),
+ kind: None,
+ })
.collect(),
};
let registration = lsp_types::Registration {
@@ -362,7 +344,7 @@ impl GlobalState {
let loader = &mut self.loader;
let mem_docs = &self.mem_docs;
let mut load = move |path: &AbsPath| {
- let _p = profile::span("GlobalState::load");
+ let _p = profile::span("switch_workspaces::load");
let vfs_path = vfs::VfsPath::from(path.to_path_buf());
if !mem_docs.contains(&vfs_path) {
let contents = loader.handle.load_sync(path);
@@ -584,10 +566,10 @@ pub(crate) fn load_proc_macro(
path: &AbsPath,
dummy_replace: &[Box<str>],
) -> ProcMacroLoadResult {
+ let server = server.map_err(ToOwned::to_owned)?;
let res: Result<Vec<_>, String> = (|| {
let dylib = MacroDylib::new(path.to_path_buf())
.map_err(|io| format!("Proc-macro dylib loading failed: {io}"))?;
- let server = server.map_err(ToOwned::to_owned)?;
let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?;
if vec.is_empty() {
return Err("proc macro library returned no proc macros".to_string());
@@ -679,7 +661,7 @@ pub(crate) fn load_proc_macro(
_: Option<&tt::Subtree>,
_: &Env,
) -> Result<tt::Subtree, ProcMacroExpansionError> {
- Ok(tt::Subtree::default())
+ Ok(tt::Subtree::empty())
}
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
index e736b2ff9..92029dc1d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
@@ -9,9 +9,9 @@ use ide::{
Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem,
CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
- InlayHintLabel, InlayKind, Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable,
- Severity, SignatureHelp, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange,
- TextSize,
+ InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, NavigationTarget, ReferenceCategory,
+ RenameError, Runnable, Severity, SignatureHelp, SourceChange, StructureNodeKind, SymbolKind,
+ TextEdit, TextRange, TextSize,
};
use itertools::Itertools;
use serde_json::to_value;
@@ -31,8 +31,8 @@ pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::P
let line_col = line_index.index.line_col(offset);
match line_index.encoding {
PositionEncoding::Utf8 => lsp_types::Position::new(line_col.line, line_col.col),
- PositionEncoding::Utf16 => {
- let line_col = line_index.index.to_utf16(line_col);
+ PositionEncoding::Wide(enc) => {
+ let line_col = line_index.index.to_wide(enc, line_col);
lsp_types::Position::new(line_col.line, line_col.col)
}
}
@@ -212,11 +212,17 @@ pub(crate) fn completion_items(
tdpp: lsp_types::TextDocumentPositionParams,
items: Vec<CompletionItem>,
) -> Vec<lsp_types::CompletionItem> {
- let max_relevance = items.iter().map(|it| it.relevance().score()).max().unwrap_or_default();
+ let max_relevance = items.iter().map(|it| it.relevance.score()).max().unwrap_or_default();
let mut res = Vec::with_capacity(items.len());
for item in items {
- completion_item(&mut res, config, line_index, &tdpp, max_relevance, item)
+ completion_item(&mut res, config, line_index, &tdpp, max_relevance, item);
}
+
+ if let Some(limit) = config.completion().limit {
+ res.sort_by(|item1, item2| item1.sort_text.cmp(&item2.sort_text));
+ res.truncate(limit);
+ }
+
res
}
@@ -229,22 +235,26 @@ fn completion_item(
item: CompletionItem,
) {
let insert_replace_support = config.insert_replace_support().then_some(tdpp.position);
+ let ref_match = item.ref_match();
+ let lookup = item.lookup().to_string();
+
let mut additional_text_edits = Vec::new();
// LSP does not allow arbitrary edits in completion, so we have to do a
// non-trivial mapping here.
let text_edit = {
let mut text_edit = None;
- let source_range = item.source_range();
- for indel in item.text_edit().iter() {
+ let source_range = item.source_range;
+ for indel in item.text_edit {
if indel.delete.contains_range(source_range) {
+ // Extract this indel as the main edit
text_edit = Some(if indel.delete == source_range {
self::completion_text_edit(line_index, insert_replace_support, indel.clone())
} else {
assert!(source_range.end() == indel.delete.end());
let range1 = TextRange::new(indel.delete.start(), source_range.start());
let range2 = source_range;
- let indel1 = Indel::replace(range1, String::new());
+ let indel1 = Indel::delete(range1);
let indel2 = Indel::replace(range2, indel.insert.clone());
additional_text_edits.push(self::text_edit(line_index, indel1));
self::completion_text_edit(line_index, insert_replace_support, indel2)
@@ -258,23 +268,23 @@ fn completion_item(
text_edit.unwrap()
};
- let insert_text_format = item.is_snippet().then_some(lsp_types::InsertTextFormat::SNIPPET);
- let tags = item.deprecated().then(|| vec![lsp_types::CompletionItemTag::DEPRECATED]);
- let command = if item.trigger_call_info() && config.client_commands().trigger_parameter_hints {
+ let insert_text_format = item.is_snippet.then_some(lsp_types::InsertTextFormat::SNIPPET);
+ let tags = item.deprecated.then(|| vec![lsp_types::CompletionItemTag::DEPRECATED]);
+ let command = if item.trigger_call_info && config.client_commands().trigger_parameter_hints {
Some(command::trigger_parameter_hints())
} else {
None
};
let mut lsp_item = lsp_types::CompletionItem {
- label: item.label().to_string(),
- detail: item.detail().map(|it| it.to_string()),
- filter_text: Some(item.lookup().to_string()),
- kind: Some(completion_item_kind(item.kind())),
+ label: item.label.to_string(),
+ detail: item.detail.map(|it| it.to_string()),
+ filter_text: Some(lookup),
+ kind: Some(completion_item_kind(item.kind)),
text_edit: Some(text_edit),
additional_text_edits: Some(additional_text_edits),
- documentation: item.documentation().map(documentation),
- deprecated: Some(item.deprecated()),
+ documentation: item.documentation.map(documentation),
+ deprecated: Some(item.deprecated),
tags,
command,
insert_text_format,
@@ -288,12 +298,13 @@ fn completion_item(
});
}
- set_score(&mut lsp_item, max_relevance, item.relevance());
+ set_score(&mut lsp_item, max_relevance, item.relevance);
if config.completion().enable_imports_on_the_fly {
- if let imports @ [_, ..] = item.imports_to_add() {
- let imports: Vec<_> = imports
- .iter()
+ if !item.import_to_add.is_empty() {
+ let imports: Vec<_> = item
+ .import_to_add
+ .into_iter()
.filter_map(|import_edit| {
let import_path = &import_edit.import_path;
let import_name = import_path.segments().last()?;
@@ -310,18 +321,13 @@ fn completion_item(
}
}
- if let Some((mutability, offset, relevance)) = item.ref_match() {
- let mut lsp_item_with_ref = lsp_item.clone();
+ if let Some((label, indel, relevance)) = ref_match {
+ let mut lsp_item_with_ref = lsp_types::CompletionItem { label, ..lsp_item.clone() };
+ lsp_item_with_ref
+ .additional_text_edits
+ .get_or_insert_with(Default::default)
+ .push(self::text_edit(line_index, indel));
set_score(&mut lsp_item_with_ref, max_relevance, relevance);
- lsp_item_with_ref.label =
- format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label);
- lsp_item_with_ref.additional_text_edits.get_or_insert_with(Default::default).push(
- self::text_edit(
- line_index,
- Indel::insert(offset, format!("&{}", mutability.as_keyword_for_ref())),
- ),
- );
-
acc.push(lsp_item_with_ref);
};
@@ -431,137 +437,140 @@ pub(crate) fn inlay_hint(
mut inlay_hint: InlayHint,
) -> Cancellable<lsp_types::InlayHint> {
match inlay_hint.kind {
- InlayKind::ParameterHint if render_colons => inlay_hint.label.append_str(":"),
- InlayKind::TypeHint if render_colons => inlay_hint.label.prepend_str(": "),
- InlayKind::ClosureReturnTypeHint => inlay_hint.label.prepend_str(" -> "),
- InlayKind::DiscriminantHint => inlay_hint.label.prepend_str(" = "),
+ InlayKind::Parameter if render_colons => inlay_hint.label.append_str(":"),
+ InlayKind::Type if render_colons => inlay_hint.label.prepend_str(": "),
+ InlayKind::ClosureReturnType => inlay_hint.label.prepend_str(" -> "),
+ InlayKind::Discriminant => inlay_hint.label.prepend_str(" = "),
_ => {}
}
+ let (label, tooltip) = inlay_hint_label(snap, inlay_hint.label)?;
+
Ok(lsp_types::InlayHint {
position: match inlay_hint.kind {
// before annotated thing
InlayKind::OpeningParenthesis
- | InlayKind::ParameterHint
- | InlayKind::AdjustmentHint
- | InlayKind::BindingModeHint => position(line_index, inlay_hint.range.start()),
+ | InlayKind::Parameter
+ | InlayKind::Adjustment
+ | InlayKind::BindingMode => position(line_index, inlay_hint.range.start()),
// after annotated thing
- InlayKind::ClosureReturnTypeHint
- | InlayKind::TypeHint
- | InlayKind::DiscriminantHint
- | InlayKind::ChainingHint
- | InlayKind::GenericParamListHint
+ InlayKind::ClosureReturnType
+ | InlayKind::Type
+ | InlayKind::Discriminant
+ | InlayKind::Chaining
+ | InlayKind::GenericParamList
| InlayKind::ClosingParenthesis
- | InlayKind::AdjustmentHintPostfix
- | InlayKind::LifetimeHint
- | InlayKind::ClosingBraceHint => position(line_index, inlay_hint.range.end()),
+ | InlayKind::AdjustmentPostfix
+ | InlayKind::Lifetime
+ | InlayKind::ClosingBrace => position(line_index, inlay_hint.range.end()),
},
padding_left: Some(match inlay_hint.kind {
- InlayKind::TypeHint => !render_colons,
- InlayKind::ChainingHint | InlayKind::ClosingBraceHint => true,
+ InlayKind::Type => !render_colons,
+ InlayKind::Chaining | InlayKind::ClosingBrace => true,
InlayKind::ClosingParenthesis
- | InlayKind::DiscriminantHint
+ | InlayKind::Discriminant
| InlayKind::OpeningParenthesis
- | InlayKind::BindingModeHint
- | InlayKind::ClosureReturnTypeHint
- | InlayKind::GenericParamListHint
- | InlayKind::AdjustmentHint
- | InlayKind::AdjustmentHintPostfix
- | InlayKind::LifetimeHint
- | InlayKind::ParameterHint => false,
+ | InlayKind::BindingMode
+ | InlayKind::ClosureReturnType
+ | InlayKind::GenericParamList
+ | InlayKind::Adjustment
+ | InlayKind::AdjustmentPostfix
+ | InlayKind::Lifetime
+ | InlayKind::Parameter => false,
}),
padding_right: Some(match inlay_hint.kind {
InlayKind::ClosingParenthesis
| InlayKind::OpeningParenthesis
- | InlayKind::ChainingHint
- | InlayKind::ClosureReturnTypeHint
- | InlayKind::GenericParamListHint
- | InlayKind::AdjustmentHint
- | InlayKind::AdjustmentHintPostfix
- | InlayKind::TypeHint
- | InlayKind::DiscriminantHint
- | InlayKind::ClosingBraceHint => false,
- InlayKind::BindingModeHint => inlay_hint.label.as_simple_str() != Some("&"),
- InlayKind::ParameterHint | InlayKind::LifetimeHint => true,
+ | InlayKind::Chaining
+ | InlayKind::ClosureReturnType
+ | InlayKind::GenericParamList
+ | InlayKind::Adjustment
+ | InlayKind::AdjustmentPostfix
+ | InlayKind::Type
+ | InlayKind::Discriminant
+ | InlayKind::ClosingBrace => false,
+ InlayKind::BindingMode => {
+ matches!(&label, lsp_types::InlayHintLabel::String(s) if s != "&")
+ }
+ InlayKind::Parameter | InlayKind::Lifetime => true,
}),
kind: match inlay_hint.kind {
- InlayKind::ParameterHint => Some(lsp_types::InlayHintKind::PARAMETER),
- InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
+ InlayKind::Parameter => Some(lsp_types::InlayHintKind::PARAMETER),
+ InlayKind::ClosureReturnType | InlayKind::Type | InlayKind::Chaining => {
Some(lsp_types::InlayHintKind::TYPE)
}
InlayKind::ClosingParenthesis
- | InlayKind::DiscriminantHint
+ | InlayKind::Discriminant
| InlayKind::OpeningParenthesis
- | InlayKind::BindingModeHint
- | InlayKind::GenericParamListHint
- | InlayKind::LifetimeHint
- | InlayKind::AdjustmentHint
- | InlayKind::AdjustmentHintPostfix
- | InlayKind::ClosingBraceHint => None,
+ | InlayKind::BindingMode
+ | InlayKind::GenericParamList
+ | InlayKind::Lifetime
+ | InlayKind::Adjustment
+ | InlayKind::AdjustmentPostfix
+ | InlayKind::ClosingBrace => None,
},
text_edits: None,
- data: (|| match inlay_hint.tooltip {
- Some(ide::InlayTooltip::HoverOffset(file_id, offset)) => {
- let uri = url(snap, file_id);
- let line_index = snap.file_line_index(file_id).ok()?;
-
- let text_document = lsp_types::VersionedTextDocumentIdentifier {
- version: snap.url_file_version(&uri)?,
- uri,
- };
- to_value(lsp_ext::InlayHintResolveData {
- text_document,
- position: lsp_ext::PositionOrRange::Position(position(&line_index, offset)),
- })
- .ok()
- }
- Some(ide::InlayTooltip::HoverRanged(file_id, text_range)) => {
- let uri = url(snap, file_id);
- let text_document = lsp_types::VersionedTextDocumentIdentifier {
- version: snap.url_file_version(&uri)?,
- uri,
- };
- let line_index = snap.file_line_index(file_id).ok()?;
- to_value(lsp_ext::InlayHintResolveData {
- text_document,
- position: lsp_ext::PositionOrRange::Range(range(&line_index, text_range)),
- })
- .ok()
- }
- _ => None,
- })(),
- tooltip: Some(match inlay_hint.tooltip {
- Some(ide::InlayTooltip::String(s)) => lsp_types::InlayHintTooltip::String(s),
- _ => lsp_types::InlayHintTooltip::String(inlay_hint.label.to_string()),
- }),
- label: inlay_hint_label(snap, inlay_hint.label)?,
+ data: None,
+ tooltip,
+ label,
})
}
fn inlay_hint_label(
snap: &GlobalStateSnapshot,
- label: InlayHintLabel,
-) -> Cancellable<lsp_types::InlayHintLabel> {
- Ok(match label.as_simple_str() {
- Some(s) => lsp_types::InlayHintLabel::String(s.into()),
- None => lsp_types::InlayHintLabel::LabelParts(
- label
+ mut label: InlayHintLabel,
+) -> Cancellable<(lsp_types::InlayHintLabel, Option<lsp_types::InlayHintTooltip>)> {
+ let res = match &*label.parts {
+ [InlayHintLabelPart { linked_location: None, .. }] => {
+ let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap();
+ (
+ lsp_types::InlayHintLabel::String(text),
+ match tooltip {
+ Some(ide::InlayTooltip::String(s)) => {
+ Some(lsp_types::InlayHintTooltip::String(s))
+ }
+ Some(ide::InlayTooltip::Markdown(s)) => {
+ Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent {
+ kind: lsp_types::MarkupKind::Markdown,
+ value: s,
+ }))
+ }
+ None => None,
+ },
+ )
+ }
+ _ => {
+ let parts = label
.parts
.into_iter()
.map(|part| {
- Ok(lsp_types::InlayHintLabelPart {
- value: part.text,
- tooltip: None,
- location: part
- .linked_location
- .map(|range| location(snap, range))
- .transpose()?,
- command: None,
- })
+ part.linked_location.map(|range| location(snap, range)).transpose().map(
+ |location| lsp_types::InlayHintLabelPart {
+ value: part.text,
+ tooltip: match part.tooltip {
+ Some(ide::InlayTooltip::String(s)) => {
+ Some(lsp_types::InlayHintLabelPartTooltip::String(s))
+ }
+ Some(ide::InlayTooltip::Markdown(s)) => {
+ Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent(
+ lsp_types::MarkupContent {
+ kind: lsp_types::MarkupKind::Markdown,
+ value: s,
+ },
+ ))
+ }
+ None => None,
+ },
+ location,
+ command: None,
+ },
+ )
})
- .collect::<Cancellable<Vec<_>>>()?,
- ),
- })
+ .collect::<Cancellable<_>>()?;
+ (lsp_types::InlayHintLabel::LabelParts(parts), None)
+ }
+ };
+ Ok(res)
}
static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);
@@ -757,6 +766,7 @@ pub(crate) fn folding_range(
end_line,
end_character: None,
kind,
+ collapsed_text: None,
}
} else {
lsp_types::FoldingRange {
@@ -765,6 +775,7 @@ pub(crate) fn folding_range(
end_line: range.end.line,
end_character: Some(range.end.character),
kind,
+ collapsed_text: None,
}
}
}
@@ -1351,7 +1362,7 @@ pub(crate) mod command {
pub(crate) fn trigger_parameter_hints() -> lsp_types::Command {
lsp_types::Command {
title: "triggerParameterHints".into(),
- command: "editor.action.triggerParameterHints".into(),
+ command: "rust-analyzer.triggerParameterHints".into(),
arguments: None,
}
}
@@ -1420,7 +1431,7 @@ fn main() {
let line_index = LineIndex {
index: Arc::new(ide::LineIndex::new(text)),
endings: LineEndings::Unix,
- encoding: PositionEncoding::Utf16,
+ encoding: PositionEncoding::Utf8,
};
let converted: Vec<lsp_types::FoldingRange> =
folds.into_iter().map(|it| folding_range(text, &line_index, true, it)).collect();