mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 06:11:35 +00:00
Merge commit '258b15c506
' into sync-from-ra
This commit is contained in:
parent
7e786ea4cf
commit
bcfc997eac
195 changed files with 5773 additions and 2750 deletions
|
@ -50,7 +50,6 @@ always-assert = "0.1.2"
|
|||
# These 3 deps are not used by r-a directly, but we list them here to lock in their versions
|
||||
# in our transitive deps to prevent them from pulling in windows-sys 0.45.0
|
||||
mio = "=0.8.5"
|
||||
filetime = "=0.2.19"
|
||||
parking_lot_core = "=0.9.6"
|
||||
|
||||
cfg.workspace = true
|
||||
|
|
|
@ -16,10 +16,12 @@ use lsp_types::{
|
|||
};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::config::{Config, RustfmtConfig};
|
||||
use crate::line_index::PositionEncoding;
|
||||
use crate::lsp_ext::negotiated_encoding;
|
||||
use crate::semantic_tokens;
|
||||
use crate::{
|
||||
config::{Config, RustfmtConfig},
|
||||
line_index::PositionEncoding,
|
||||
lsp::semantic_tokens,
|
||||
lsp_ext::negotiated_encoding,
|
||||
};
|
||||
|
||||
pub fn server_capabilities(config: &Config) -> ServerCapabilities {
|
||||
ServerCapabilities {
|
||||
|
@ -218,7 +220,7 @@ fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProvi
|
|||
}
|
||||
|
||||
fn more_trigger_character(config: &Config) -> Vec<String> {
|
||||
let mut res = vec![".".to_string(), ">".to_string(), "{".to_string()];
|
||||
let mut res = vec![".".to_string(), ">".to_string(), "{".to_string(), "(".to_string()];
|
||||
if config.snippet_cap() {
|
||||
res.push("<".to_string());
|
||||
}
|
||||
|
|
|
@ -15,7 +15,10 @@ use hir_def::{
|
|||
hir::{ExprId, PatId},
|
||||
};
|
||||
use hir_ty::{Interner, Substitution, TyExt, TypeFlags};
|
||||
use ide::{Analysis, AnnotationConfig, DiagnosticsConfig, InlayHintsConfig, LineCol, RootDatabase};
|
||||
use ide::{
|
||||
Analysis, AnnotationConfig, DiagnosticsConfig, InlayFieldsToResolve, InlayHintsConfig, LineCol,
|
||||
RootDatabase,
|
||||
};
|
||||
use ide_db::{
|
||||
base_db::{
|
||||
salsa::{self, debug::DebugQueryTable, ParallelDatabase},
|
||||
|
@ -317,9 +320,13 @@ impl flags::AnalysisStats {
|
|||
|
||||
fn run_mir_lowering(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) {
|
||||
let mut sw = self.stop_watch();
|
||||
let all = bodies.len() as u64;
|
||||
let mut all = 0;
|
||||
let mut fail = 0;
|
||||
for &body in bodies {
|
||||
if matches!(body, DefWithBody::Variant(_)) {
|
||||
continue;
|
||||
}
|
||||
all += 1;
|
||||
let Err(e) = db.mir_body(body.into()) else {
|
||||
continue;
|
||||
};
|
||||
|
@ -782,6 +789,7 @@ impl flags::AnalysisStats {
|
|||
closure_style: hir::ClosureStyle::ImplFn,
|
||||
max_length: Some(25),
|
||||
closing_brace_hints_min_lines: Some(20),
|
||||
fields_to_resolve: InlayFieldsToResolve::empty(),
|
||||
},
|
||||
file_id,
|
||||
None,
|
||||
|
|
|
@ -21,7 +21,7 @@ use vfs::{AbsPathBuf, Vfs};
|
|||
use crate::{
|
||||
cli::flags,
|
||||
line_index::{LineEndings, LineIndex, PositionEncoding},
|
||||
to_proto,
|
||||
lsp::to_proto,
|
||||
version::version,
|
||||
};
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ impl flags::Scip {
|
|||
version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
|
||||
tool_info: Some(scip_types::ToolInfo {
|
||||
name: "rust-analyzer".to_owned(),
|
||||
version: "0.1".to_owned(),
|
||||
version: format!("{}", crate::version::version()),
|
||||
arguments: vec![],
|
||||
special_fields: Default::default(),
|
||||
})
|
||||
|
|
|
@ -13,8 +13,9 @@ use cfg::{CfgAtom, CfgDiff};
|
|||
use flycheck::FlycheckConfig;
|
||||
use ide::{
|
||||
AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
|
||||
HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig,
|
||||
JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope,
|
||||
HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve,
|
||||
InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
|
||||
Snippet, SnippetScope,
|
||||
};
|
||||
use ide_db::{
|
||||
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
|
||||
|
@ -1335,6 +1336,18 @@ impl Config {
|
|||
}
|
||||
|
||||
pub fn inlay_hints(&self) -> InlayHintsConfig {
|
||||
let client_capability_fields = self
|
||||
.caps
|
||||
.text_document
|
||||
.as_ref()
|
||||
.and_then(|text| text.inlay_hint.as_ref())
|
||||
.and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref())
|
||||
.map(|inlay_resolve| inlay_resolve.properties.iter())
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect::<FxHashSet<_>>();
|
||||
|
||||
InlayHintsConfig {
|
||||
render_colons: self.data.inlayHints_renderColons,
|
||||
type_hints: self.data.inlayHints_typeHints_enable,
|
||||
|
@ -1395,6 +1408,13 @@ impl Config {
|
|||
} else {
|
||||
None
|
||||
},
|
||||
fields_to_resolve: InlayFieldsToResolve {
|
||||
resolve_text_edits: client_capability_fields.contains("textEdits"),
|
||||
resolve_hint_tooltip: client_capability_fields.contains("tooltip"),
|
||||
resolve_label_tooltip: client_capability_fields.contains("label.tooltip"),
|
||||
resolve_label_location: client_capability_fields.contains("label.location"),
|
||||
resolve_label_command: client_capability_fields.contains("label.command"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use nohash_hasher::{IntMap, IntSet};
|
|||
use rustc_hash::FxHashSet;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::lsp_ext;
|
||||
use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext};
|
||||
|
||||
pub(crate) type CheckFixes = Arc<IntMap<usize, IntMap<FileId, Vec<Fix>>>>;
|
||||
|
||||
|
@ -122,3 +122,41 @@ fn are_diagnostics_equal(left: &lsp_types::Diagnostic, right: &lsp_types::Diagno
|
|||
&& left.range == right.range
|
||||
&& left.message == right.message
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_native_diagnostics(
|
||||
snapshot: GlobalStateSnapshot,
|
||||
subscriptions: Vec<FileId>,
|
||||
) -> Vec<(FileId, Vec<lsp_types::Diagnostic>)> {
|
||||
let _p = profile::span("fetch_native_diagnostics");
|
||||
let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned());
|
||||
subscriptions
|
||||
.into_iter()
|
||||
.filter_map(|file_id| {
|
||||
let line_index = snapshot.file_line_index(file_id).ok()?;
|
||||
let diagnostics = snapshot
|
||||
.analysis
|
||||
.diagnostics(
|
||||
&snapshot.config.diagnostics(),
|
||||
ide::AssistResolveStrategy::None,
|
||||
file_id,
|
||||
)
|
||||
.ok()?
|
||||
.into_iter()
|
||||
.map(move |d| lsp_types::Diagnostic {
|
||||
range: lsp::to_proto::range(&line_index, d.range),
|
||||
severity: Some(lsp::to_proto::diagnostic_severity(d.severity)),
|
||||
code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_string())),
|
||||
code_description: Some(lsp_types::CodeDescription {
|
||||
href: lsp_types::Url::parse(&d.code.url()).unwrap(),
|
||||
}),
|
||||
source: Some("rust-analyzer".to_string()),
|
||||
message: d.message,
|
||||
related_information: None,
|
||||
tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]),
|
||||
data: None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
Some((file_id, diagnostics))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ use stdx::format_to;
|
|||
use vfs::{AbsPath, AbsPathBuf};
|
||||
|
||||
use crate::{
|
||||
global_state::GlobalStateSnapshot, line_index::PositionEncoding, lsp_ext,
|
||||
to_proto::url_from_abs_path,
|
||||
global_state::GlobalStateSnapshot, line_index::PositionEncoding,
|
||||
lsp::to_proto::url_from_abs_path, lsp_ext,
|
||||
};
|
||||
|
||||
use super::{DiagnosticsMapConfig, Fix};
|
||||
|
|
|
@ -8,9 +8,9 @@ use stdx::thread::ThreadIntent;
|
|||
|
||||
use crate::{
|
||||
global_state::{GlobalState, GlobalStateSnapshot},
|
||||
lsp::LspError,
|
||||
main_loop::Task,
|
||||
version::version,
|
||||
LspError,
|
||||
};
|
||||
|
||||
/// A visitor for routing a raw JSON request to an appropriate handler function.
|
||||
|
|
|
@ -12,25 +12,27 @@ use ide_db::base_db::{CrateId, FileLoader, ProcMacroPaths, SourceDatabase};
|
|||
use load_cargo::SourceRootConfig;
|
||||
use lsp_types::{SemanticTokens, Url};
|
||||
use nohash_hasher::IntMap;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use parking_lot::{
|
||||
MappedRwLockReadGuard, Mutex, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard,
|
||||
RwLockWriteGuard,
|
||||
};
|
||||
use proc_macro_api::ProcMacroServer;
|
||||
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use triomphe::Arc;
|
||||
use vfs::AnchoredPathBuf;
|
||||
use vfs::{AnchoredPathBuf, Vfs};
|
||||
|
||||
use crate::{
|
||||
config::{Config, ConfigError},
|
||||
diagnostics::{CheckFixes, DiagnosticCollection},
|
||||
from_proto,
|
||||
line_index::{LineEndings, LineIndex},
|
||||
lsp::{from_proto, to_proto::url_from_abs_path},
|
||||
lsp_ext,
|
||||
main_loop::Task,
|
||||
mem_docs::MemDocs,
|
||||
op_queue::OpQueue,
|
||||
reload,
|
||||
task_pool::TaskPool,
|
||||
to_proto::url_from_abs_path,
|
||||
};
|
||||
|
||||
// Enforces drop order
|
||||
|
@ -40,7 +42,7 @@ pub(crate) struct Handle<H, C> {
|
|||
}
|
||||
|
||||
pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
|
||||
pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
|
||||
type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
|
||||
|
||||
/// `GlobalState` is the primary mutable state of the language server
|
||||
///
|
||||
|
@ -49,6 +51,7 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
|
|||
/// incremental salsa database.
|
||||
///
|
||||
/// Note that this struct has more than one impl in various modules!
|
||||
#[doc(alias = "GlobalMess")]
|
||||
pub(crate) struct GlobalState {
|
||||
sender: Sender<lsp_server::Message>,
|
||||
req_queue: ReqQueue,
|
||||
|
@ -66,6 +69,7 @@ pub(crate) struct GlobalState {
|
|||
|
||||
// status
|
||||
pub(crate) shutdown_requested: bool,
|
||||
pub(crate) send_hint_refresh_query: bool,
|
||||
pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
|
||||
|
||||
// proc macros
|
||||
|
@ -177,6 +181,7 @@ impl GlobalState {
|
|||
mem_docs: MemDocs::default(),
|
||||
semantic_tokens_cache: Arc::new(Default::default()),
|
||||
shutdown_requested: false,
|
||||
send_hint_refresh_query: false,
|
||||
last_reported_status: None,
|
||||
source_root_config: SourceRootConfig::default(),
|
||||
config_errors: Default::default(),
|
||||
|
@ -216,12 +221,15 @@ impl GlobalState {
|
|||
let mut file_changes = FxHashMap::default();
|
||||
let (change, changed_files, workspace_structure_change) = {
|
||||
let mut change = Change::new();
|
||||
let (vfs, line_endings_map) = &mut *self.vfs.write();
|
||||
let changed_files = vfs.take_changes();
|
||||
let mut guard = self.vfs.write();
|
||||
let changed_files = guard.0.take_changes();
|
||||
if changed_files.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// downgrade to read lock to allow more readers while we are normalizing text
|
||||
let guard = RwLockWriteGuard::downgrade_to_upgradable(guard);
|
||||
let vfs: &Vfs = &guard.0;
|
||||
// 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.
|
||||
|
@ -272,6 +280,7 @@ impl GlobalState {
|
|||
let mut workspace_structure_change = None;
|
||||
// A file was added or deleted
|
||||
let mut has_structure_changes = false;
|
||||
let mut bytes = vec![];
|
||||
for file in &changed_files {
|
||||
let vfs_path = &vfs.file_path(file.file_id);
|
||||
if let Some(path) = vfs_path.as_path() {
|
||||
|
@ -293,16 +302,28 @@ impl GlobalState {
|
|||
|
||||
let text = if file.exists() {
|
||||
let bytes = vfs.file_contents(file.file_id).to_vec();
|
||||
|
||||
String::from_utf8(bytes).ok().and_then(|text| {
|
||||
// FIXME: Consider doing normalization in the `vfs` instead? That allows
|
||||
// getting rid of some locking
|
||||
let (text, line_endings) = LineEndings::normalize(text);
|
||||
line_endings_map.insert(file.file_id, line_endings);
|
||||
Some(Arc::from(text))
|
||||
Some((Arc::from(text), line_endings))
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
change.change_file(file.file_id, text);
|
||||
// delay `line_endings_map` changes until we are done normalizing the text
|
||||
// this allows delaying the re-acquisition of the write lock
|
||||
bytes.push((file.file_id, text));
|
||||
}
|
||||
let (vfs, line_endings_map) = &mut *RwLockUpgradableReadGuard::upgrade(guard);
|
||||
bytes.into_iter().for_each(|(file_id, text)| match text {
|
||||
None => change.change_file(file_id, None),
|
||||
Some((text, line_endings)) => {
|
||||
line_endings_map.insert(file_id, line_endings);
|
||||
change.change_file(file_id, Some(text));
|
||||
}
|
||||
});
|
||||
if has_structure_changes {
|
||||
let roots = self.source_root_config.partition(vfs);
|
||||
change.set_roots(roots);
|
||||
|
@ -422,12 +443,16 @@ impl Drop for GlobalState {
|
|||
}
|
||||
|
||||
impl GlobalStateSnapshot {
|
||||
fn vfs_read(&self) -> MappedRwLockReadGuard<'_, vfs::Vfs> {
|
||||
RwLockReadGuard::map(self.vfs.read(), |(it, _)| it)
|
||||
}
|
||||
|
||||
pub(crate) fn url_to_file_id(&self, url: &Url) -> anyhow::Result<FileId> {
|
||||
url_to_file_id(&self.vfs.read().0, url)
|
||||
url_to_file_id(&self.vfs_read(), url)
|
||||
}
|
||||
|
||||
pub(crate) fn file_id_to_url(&self, id: FileId) -> Url {
|
||||
file_id_to_url(&self.vfs.read().0, id)
|
||||
file_id_to_url(&self.vfs_read(), id)
|
||||
}
|
||||
|
||||
pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
|
||||
|
@ -443,7 +468,7 @@ impl GlobalStateSnapshot {
|
|||
}
|
||||
|
||||
pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url {
|
||||
let mut base = self.vfs.read().0.file_path(path.anchor);
|
||||
let mut base = self.vfs_read().file_path(path.anchor);
|
||||
base.pop();
|
||||
let path = base.join(&path.path).unwrap();
|
||||
let path = path.as_path().unwrap();
|
||||
|
@ -451,7 +476,7 @@ impl GlobalStateSnapshot {
|
|||
}
|
||||
|
||||
pub(crate) fn file_id_to_file_path(&self, file_id: FileId) -> vfs::VfsPath {
|
||||
self.vfs.read().0.file_path(file_id)
|
||||
self.vfs_read().file_path(file_id)
|
||||
}
|
||||
|
||||
pub(crate) fn cargo_target_for_crate_root(
|
||||
|
@ -459,7 +484,7 @@ impl GlobalStateSnapshot {
|
|||
crate_id: CrateId,
|
||||
) -> Option<(&CargoWorkspace, Target)> {
|
||||
let file_id = self.analysis.crate_root(crate_id).ok()?;
|
||||
let path = self.vfs.read().0.file_path(file_id);
|
||||
let path = self.vfs_read().file_path(file_id);
|
||||
let path = path.as_path()?;
|
||||
self.workspaces.iter().find_map(|ws| match ws {
|
||||
ProjectWorkspace::Cargo { cargo, .. } => {
|
||||
|
@ -471,7 +496,11 @@ impl GlobalStateSnapshot {
|
|||
}
|
||||
|
||||
pub(crate) fn vfs_memory_usage(&self) -> usize {
|
||||
self.vfs.read().0.memory_usage()
|
||||
self.vfs_read().memory_usage()
|
||||
}
|
||||
|
||||
pub(crate) fn file_exists(&self, file_id: FileId) -> bool {
|
||||
self.vfs.read().0.exists(file_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,12 @@ use triomphe::Arc;
|
|||
use vfs::{AbsPathBuf, ChangeKind, VfsPath};
|
||||
|
||||
use crate::{
|
||||
config::Config, from_proto, global_state::GlobalState, lsp_ext::RunFlycheckParams,
|
||||
lsp_utils::apply_document_changes, mem_docs::DocumentData, reload,
|
||||
config::Config,
|
||||
global_state::GlobalState,
|
||||
lsp::{from_proto, utils::apply_document_changes},
|
||||
lsp_ext::RunFlycheckParams,
|
||||
mem_docs::DocumentData,
|
||||
reload,
|
||||
};
|
||||
|
||||
pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> anyhow::Result<()> {
|
||||
|
@ -84,15 +88,16 @@ pub(crate) fn handle_did_change_text_document(
|
|||
}
|
||||
};
|
||||
|
||||
let vfs = &mut state.vfs.write().0;
|
||||
let file_id = vfs.file_id(&path).unwrap();
|
||||
let text = apply_document_changes(
|
||||
state.config.position_encoding(),
|
||||
|| std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(),
|
||||
|| {
|
||||
let vfs = &state.vfs.read().0;
|
||||
let file_id = vfs.file_id(&path).unwrap();
|
||||
std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into()
|
||||
},
|
||||
params.content_changes,
|
||||
);
|
||||
|
||||
vfs.set_file_contents(path, Some(text.into_bytes()));
|
||||
state.vfs.write().0.set_file_contents(path, Some(text.into_bytes()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -108,6 +113,10 @@ pub(crate) fn handle_did_close_text_document(
|
|||
tracing::error!("orphan DidCloseTextDocument: {}", path);
|
||||
}
|
||||
|
||||
if let Some(file_id) = state.vfs.read().0.file_id(&path) {
|
||||
state.diagnostics.clear_native_for(file_id);
|
||||
}
|
||||
|
||||
state.semantic_tokens_cache.lock().remove(¶ms.text_document.uri);
|
||||
|
||||
if let Some(path) = path.as_path() {
|
||||
|
|
|
@ -11,8 +11,8 @@ use anyhow::Context;
|
|||
|
||||
use ide::{
|
||||
AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange,
|
||||
HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind,
|
||||
SingleResolve, SourceChange, TextEdit,
|
||||
HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory,
|
||||
Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
|
||||
};
|
||||
use ide_db::SymbolKind;
|
||||
use lsp_server::ErrorCode;
|
||||
|
@ -30,21 +30,23 @@ use serde_json::json;
|
|||
use stdx::{format_to, never};
|
||||
use syntax::{algo, ast, AstNode, TextRange, TextSize};
|
||||
use triomphe::Arc;
|
||||
use vfs::{AbsPath, AbsPathBuf, VfsPath};
|
||||
use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
|
||||
|
||||
use crate::{
|
||||
cargo_target_spec::CargoTargetSpec,
|
||||
config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
|
||||
diff::diff,
|
||||
from_proto,
|
||||
global_state::{GlobalState, GlobalStateSnapshot},
|
||||
line_index::LineEndings,
|
||||
lsp::{
|
||||
from_proto, to_proto,
|
||||
utils::{all_edits_are_disjoint, invalid_params_error},
|
||||
LspError,
|
||||
},
|
||||
lsp_ext::{
|
||||
self, CrateInfoResult, ExternalDocsPair, ExternalDocsResponse, FetchDependencyListParams,
|
||||
FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams,
|
||||
},
|
||||
lsp_utils::{all_edits_are_disjoint, invalid_params_error},
|
||||
to_proto, LspError,
|
||||
};
|
||||
|
||||
pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> {
|
||||
|
@ -354,7 +356,7 @@ pub(crate) fn handle_on_type_formatting(
|
|||
|
||||
// This should be a single-file edit
|
||||
let (_, (text_edit, snippet_edit)) = edit.source_file_edits.into_iter().next().unwrap();
|
||||
stdx::never!(snippet_edit.is_none(), "on type formatting shouldn't use structured snippets");
|
||||
stdx::always!(snippet_edit.is_none(), "on type formatting shouldn't use structured snippets");
|
||||
|
||||
let change = to_proto::snippet_text_edit_vec(&line_index, edit.is_snippet, text_edit);
|
||||
Ok(Some(change))
|
||||
|
@ -972,7 +974,7 @@ pub(crate) fn handle_hover(
|
|||
PositionOrRange::Range(range) => range,
|
||||
};
|
||||
|
||||
let file_range = from_proto::file_range(&snap, params.text_document, range)?;
|
||||
let file_range = from_proto::file_range(&snap, ¶ms.text_document, range)?;
|
||||
let info = match snap.analysis.hover(&snap.config.hover(), file_range)? {
|
||||
None => return Ok(None),
|
||||
Some(info) => info,
|
||||
|
@ -1128,7 +1130,7 @@ pub(crate) fn handle_code_action(
|
|||
|
||||
let line_index =
|
||||
snap.file_line_index(from_proto::file_id(&snap, ¶ms.text_document.uri)?)?;
|
||||
let frange = from_proto::file_range(&snap, params.text_document.clone(), params.range)?;
|
||||
let frange = from_proto::file_range(&snap, ¶ms.text_document, params.range)?;
|
||||
|
||||
let mut assists_config = snap.config.assist();
|
||||
assists_config.allowed = params
|
||||
|
@ -1381,7 +1383,7 @@ pub(crate) fn handle_ssr(
|
|||
let selections = params
|
||||
.selections
|
||||
.iter()
|
||||
.map(|range| from_proto::file_range(&snap, params.position.text_document.clone(), *range))
|
||||
.map(|range| from_proto::file_range(&snap, ¶ms.position.text_document, *range))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let position = from_proto::file_position(&snap, params.position)?;
|
||||
let source_change = snap.analysis.structural_search_replace(
|
||||
|
@ -1401,7 +1403,7 @@ pub(crate) fn handle_inlay_hints(
|
|||
let document_uri = ¶ms.text_document.uri;
|
||||
let FileRange { file_id, range } = from_proto::file_range(
|
||||
&snap,
|
||||
TextDocumentIdentifier::new(document_uri.to_owned()),
|
||||
&TextDocumentIdentifier::new(document_uri.to_owned()),
|
||||
params.range,
|
||||
)?;
|
||||
let line_index = snap.file_line_index(file_id)?;
|
||||
|
@ -1410,17 +1412,73 @@ pub(crate) fn handle_inlay_hints(
|
|||
snap.analysis
|
||||
.inlay_hints(&inlay_hints_config, file_id, Some(range))?
|
||||
.into_iter()
|
||||
.map(|it| to_proto::inlay_hint(&snap, &line_index, it))
|
||||
.map(|it| {
|
||||
to_proto::inlay_hint(
|
||||
&snap,
|
||||
&inlay_hints_config.fields_to_resolve,
|
||||
&line_index,
|
||||
file_id,
|
||||
it,
|
||||
)
|
||||
})
|
||||
.collect::<Cancellable<Vec<_>>>()?,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn handle_inlay_hints_resolve(
|
||||
_snap: GlobalStateSnapshot,
|
||||
hint: InlayHint,
|
||||
snap: GlobalStateSnapshot,
|
||||
mut original_hint: InlayHint,
|
||||
) -> anyhow::Result<InlayHint> {
|
||||
let _p = profile::span("handle_inlay_hints_resolve");
|
||||
Ok(hint)
|
||||
|
||||
let data = match original_hint.data.take() {
|
||||
Some(it) => it,
|
||||
None => return Ok(original_hint),
|
||||
};
|
||||
|
||||
let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?;
|
||||
let file_id = FileId(resolve_data.file_id);
|
||||
anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data");
|
||||
|
||||
let line_index = snap.file_line_index(file_id)?;
|
||||
let range = from_proto::text_range(
|
||||
&line_index,
|
||||
lsp_types::Range { start: original_hint.position, end: original_hint.position },
|
||||
)?;
|
||||
let range_start = range.start();
|
||||
let range_end = range.end();
|
||||
let large_range = TextRange::new(
|
||||
range_start.checked_sub(1.into()).unwrap_or(range_start),
|
||||
range_end.checked_add(1.into()).unwrap_or(range_end),
|
||||
);
|
||||
let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints();
|
||||
forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty();
|
||||
let resolve_hints = snap.analysis.inlay_hints(
|
||||
&forced_resolve_inlay_hints_config,
|
||||
file_id,
|
||||
Some(large_range),
|
||||
)?;
|
||||
|
||||
let mut resolved_hints = resolve_hints
|
||||
.into_iter()
|
||||
.filter_map(|it| {
|
||||
to_proto::inlay_hint(
|
||||
&snap,
|
||||
&forced_resolve_inlay_hints_config.fields_to_resolve,
|
||||
&line_index,
|
||||
file_id,
|
||||
it,
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
.filter(|hint| hint.position == original_hint.position)
|
||||
.filter(|hint| hint.kind == original_hint.kind);
|
||||
if let Some(resolved_hint) = resolved_hints.next() {
|
||||
if resolved_hints.next().is_none() {
|
||||
return Ok(resolved_hint);
|
||||
}
|
||||
}
|
||||
Ok(original_hint)
|
||||
}
|
||||
|
||||
pub(crate) fn handle_call_hierarchy_prepare(
|
||||
|
@ -1453,7 +1511,7 @@ pub(crate) fn handle_call_hierarchy_incoming(
|
|||
let item = params.item;
|
||||
|
||||
let doc = TextDocumentIdentifier::new(item.uri);
|
||||
let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
|
||||
let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
|
||||
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
|
||||
|
||||
let call_items = match snap.analysis.incoming_calls(fpos)? {
|
||||
|
@ -1488,7 +1546,7 @@ pub(crate) fn handle_call_hierarchy_outgoing(
|
|||
let item = params.item;
|
||||
|
||||
let doc = TextDocumentIdentifier::new(item.uri);
|
||||
let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
|
||||
let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
|
||||
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
|
||||
|
||||
let call_items = match snap.analysis.outgoing_calls(fpos)? {
|
||||
|
@ -1569,18 +1627,21 @@ pub(crate) fn handle_semantic_tokens_full_delta(
|
|||
snap.config.highlighting_non_standard_tokens(),
|
||||
);
|
||||
|
||||
let mut cache = snap.semantic_tokens_cache.lock();
|
||||
let cached_tokens = cache.entry(params.text_document.uri).or_default();
|
||||
let cached_tokens = snap.semantic_tokens_cache.lock().remove(¶ms.text_document.uri);
|
||||
|
||||
if let Some(prev_id) = &cached_tokens.result_id {
|
||||
if let Some(cached_tokens @ lsp_types::SemanticTokens { result_id: Some(prev_id), .. }) =
|
||||
&cached_tokens
|
||||
{
|
||||
if *prev_id == params.previous_result_id {
|
||||
let delta = to_proto::semantic_token_delta(cached_tokens, &semantic_tokens);
|
||||
*cached_tokens = semantic_tokens;
|
||||
snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens);
|
||||
return Ok(Some(delta.into()));
|
||||
}
|
||||
}
|
||||
|
||||
*cached_tokens = semantic_tokens.clone();
|
||||
// Clone first to keep the lock short
|
||||
let semantic_tokens_clone = semantic_tokens.clone();
|
||||
snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens_clone);
|
||||
|
||||
Ok(Some(semantic_tokens.into()))
|
||||
}
|
||||
|
@ -1591,7 +1652,7 @@ pub(crate) fn handle_semantic_tokens_range(
|
|||
) -> anyhow::Result<Option<SemanticTokensRangeResult>> {
|
||||
let _p = profile::span("handle_semantic_tokens_range");
|
||||
|
||||
let frange = from_proto::file_range(&snap, params.text_document, params.range)?;
|
||||
let frange = from_proto::file_range(&snap, ¶ms.text_document, params.range)?;
|
||||
let text = snap.analysis.file_text(frange.file_id)?;
|
||||
let line_index = snap.file_line_index(frange.file_id)?;
|
||||
|
||||
|
@ -1674,7 +1735,7 @@ pub(crate) fn handle_move_item(
|
|||
) -> anyhow::Result<Vec<lsp_ext::SnippetTextEdit>> {
|
||||
let _p = profile::span("handle_move_item");
|
||||
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
||||
let range = from_proto::file_range(&snap, params.text_document, params.range)?;
|
||||
let range = from_proto::file_range(&snap, ¶ms.text_document, params.range)?;
|
||||
|
||||
let direction = match params.direction {
|
||||
lsp_ext::MoveItemDirection::Up => ide::Direction::Up,
|
||||
|
@ -1879,12 +1940,15 @@ fn run_rustfmt(
|
|||
|
||||
// Determine the edition of the crate the file belongs to (if there's multiple, we pick the
|
||||
// highest edition).
|
||||
let editions = snap
|
||||
let Ok(editions) = snap
|
||||
.analysis
|
||||
.relevant_crates_for(file_id)?
|
||||
.into_iter()
|
||||
.map(|crate_id| snap.analysis.crate_edition(crate_id))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let edition = editions.iter().copied().max();
|
||||
|
||||
let line_index = snap.file_line_index(file_id)?;
|
||||
|
@ -1894,23 +1958,7 @@ fn run_rustfmt(
|
|||
let mut cmd = process::Command::new(toolchain::rustfmt());
|
||||
cmd.envs(snap.config.extra_env());
|
||||
cmd.args(extra_args);
|
||||
// try to chdir to the file so we can respect `rustfmt.toml`
|
||||
// FIXME: use `rustfmt --config-path` once
|
||||
// https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
|
||||
match text_document.uri.to_file_path() {
|
||||
Ok(mut path) => {
|
||||
// pop off file name
|
||||
if path.pop() && path.is_dir() {
|
||||
cmd.current_dir(path);
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::error!(
|
||||
"Unable to get file path for {}, rustfmt.toml might be ignored",
|
||||
text_document.uri
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(edition) = edition {
|
||||
cmd.arg("--edition");
|
||||
cmd.arg(edition.to_string());
|
||||
|
@ -1929,7 +1977,7 @@ fn run_rustfmt(
|
|||
.into());
|
||||
}
|
||||
|
||||
let frange = from_proto::file_range(snap, text_document, range)?;
|
||||
let frange = from_proto::file_range(snap, &text_document, range)?;
|
||||
let start_line = line_index.index.line_col(frange.range.start()).line;
|
||||
let end_line = line_index.index.line_col(frange.range.end()).line;
|
||||
|
||||
|
@ -1948,12 +1996,31 @@ fn run_rustfmt(
|
|||
}
|
||||
RustfmtConfig::CustomCommand { command, args } => {
|
||||
let mut cmd = process::Command::new(command);
|
||||
|
||||
cmd.envs(snap.config.extra_env());
|
||||
cmd.args(args);
|
||||
cmd
|
||||
}
|
||||
};
|
||||
|
||||
// try to chdir to the file so we can respect `rustfmt.toml`
|
||||
// FIXME: use `rustfmt --config-path` once
|
||||
// https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
|
||||
match text_document.uri.to_file_path() {
|
||||
Ok(mut path) => {
|
||||
// pop off file name
|
||||
if path.pop() && path.is_dir() {
|
||||
command.current_dir(path);
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::error!(
|
||||
text_document = ?text_document.uri,
|
||||
"Unable to get path, rustfmt.toml might be ignored"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut rustfmt = command
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
|
|
|
@ -23,18 +23,13 @@ mod cargo_target_spec;
|
|||
mod diagnostics;
|
||||
mod diff;
|
||||
mod dispatch;
|
||||
mod from_proto;
|
||||
mod global_state;
|
||||
mod line_index;
|
||||
mod lsp_utils;
|
||||
mod main_loop;
|
||||
mod markdown;
|
||||
mod mem_docs;
|
||||
mod op_queue;
|
||||
mod reload;
|
||||
mod semantic_tokens;
|
||||
mod task_pool;
|
||||
mod to_proto;
|
||||
mod version;
|
||||
|
||||
mod handlers {
|
||||
|
@ -43,13 +38,12 @@ mod handlers {
|
|||
}
|
||||
|
||||
pub mod config;
|
||||
pub mod lsp_ext;
|
||||
pub mod lsp;
|
||||
use self::lsp::ext as lsp_ext;
|
||||
|
||||
#[cfg(test)]
|
||||
mod integrated_benchmarks;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
pub use crate::{caps::server_capabilities, main_loop::main_loop, version::version};
|
||||
|
@ -61,23 +55,3 @@ pub fn from_json<T: DeserializeOwned>(
|
|||
serde_json::from_value(json.clone())
|
||||
.map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}"))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LspError {
|
||||
code: i32,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl LspError {
|
||||
fn new(code: i32, message: String) -> LspError {
|
||||
LspError { code, message }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LspError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Language Server request failed with {}. ({})", self.code, self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for LspError {}
|
||||
|
|
29
crates/rust-analyzer/src/lsp.rs
Normal file
29
crates/rust-analyzer/src/lsp.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
//! Custom LSP definitions and protocol conversions.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
pub(crate) mod utils;
|
||||
pub(crate) mod semantic_tokens;
|
||||
pub mod ext;
|
||||
pub(crate) mod from_proto;
|
||||
pub(crate) mod to_proto;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct LspError {
|
||||
pub(crate) code: i32,
|
||||
pub(crate) message: String,
|
||||
}
|
||||
|
||||
impl LspError {
|
||||
pub(crate) fn new(code: i32, message: String) -> LspError {
|
||||
LspError { code, message }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LspError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Language Server request failed with {}. ({})", self.code, self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for LspError {}
|
|
@ -682,7 +682,9 @@ pub struct CompletionResolveData {
|
|||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct InlayHintResolveData {}
|
||||
pub struct InlayHintResolveData {
|
||||
pub file_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CompletionImport {
|
|
@ -12,8 +12,8 @@ use crate::{
|
|||
from_json,
|
||||
global_state::GlobalStateSnapshot,
|
||||
line_index::{LineIndex, PositionEncoding},
|
||||
lsp::utils::invalid_params_error,
|
||||
lsp_ext,
|
||||
lsp_utils::invalid_params_error,
|
||||
};
|
||||
|
||||
pub(crate) fn abs_path(url: &lsp_types::Url) -> anyhow::Result<AbsPathBuf> {
|
||||
|
@ -72,7 +72,7 @@ pub(crate) fn file_position(
|
|||
|
||||
pub(crate) fn file_range(
|
||||
snap: &GlobalStateSnapshot,
|
||||
text_document_identifier: lsp_types::TextDocumentIdentifier,
|
||||
text_document_identifier: &lsp_types::TextDocumentIdentifier,
|
||||
range: lsp_types::Range,
|
||||
) -> anyhow::Result<FileRange> {
|
||||
file_range_uri(snap, &text_document_identifier.uri, range)
|
|
@ -8,11 +8,12 @@ use std::{
|
|||
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, InlayHintLabelPart, InlayKind, Markup, NavigationTarget, ReferenceCategory,
|
||||
RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind,
|
||||
SymbolKind, TextEdit, TextRange, TextSize,
|
||||
Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel,
|
||||
InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup,
|
||||
NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp,
|
||||
SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
|
||||
};
|
||||
use ide_db::rust_doc::format_docs;
|
||||
use itertools::Itertools;
|
||||
use serde_json::to_value;
|
||||
use vfs::AbsPath;
|
||||
|
@ -22,9 +23,12 @@ use crate::{
|
|||
config::{CallInfoConfig, Config},
|
||||
global_state::GlobalStateSnapshot,
|
||||
line_index::{LineEndings, LineIndex, PositionEncoding},
|
||||
lsp::{
|
||||
semantic_tokens::{self, standard_fallback_type},
|
||||
utils::invalid_params_error,
|
||||
LspError,
|
||||
},
|
||||
lsp_ext::{self, SnippetTextEdit},
|
||||
lsp_utils::invalid_params_error,
|
||||
semantic_tokens::{self, standard_fallback_type},
|
||||
};
|
||||
|
||||
pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
|
||||
|
@ -102,7 +106,7 @@ pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSe
|
|||
}
|
||||
|
||||
pub(crate) fn documentation(documentation: Documentation) -> lsp_types::Documentation {
|
||||
let value = crate::markdown::format_docs(documentation.as_str());
|
||||
let value = format_docs(&documentation);
|
||||
let markup_content = lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value };
|
||||
lsp_types::Documentation::MarkupContent(markup_content)
|
||||
}
|
||||
|
@ -413,7 +417,7 @@ pub(crate) fn signature_help(
|
|||
let documentation = call_info.doc.filter(|_| config.docs).map(|doc| {
|
||||
lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
|
||||
kind: lsp_types::MarkupKind::Markdown,
|
||||
value: crate::markdown::format_docs(&doc),
|
||||
value: format_docs(&doc),
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -434,10 +438,25 @@ pub(crate) fn signature_help(
|
|||
|
||||
pub(crate) fn inlay_hint(
|
||||
snap: &GlobalStateSnapshot,
|
||||
fields_to_resolve: &InlayFieldsToResolve,
|
||||
line_index: &LineIndex,
|
||||
file_id: FileId,
|
||||
inlay_hint: InlayHint,
|
||||
) -> Cancellable<lsp_types::InlayHint> {
|
||||
let (label, tooltip) = inlay_hint_label(snap, inlay_hint.label)?;
|
||||
let needs_resolve = inlay_hint.needs_resolve;
|
||||
let (label, tooltip, mut something_to_resolve) =
|
||||
inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?;
|
||||
let text_edits = if needs_resolve && fields_to_resolve.resolve_text_edits {
|
||||
something_to_resolve |= inlay_hint.text_edit.is_some();
|
||||
None
|
||||
} else {
|
||||
inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it))
|
||||
};
|
||||
let data = if needs_resolve && something_to_resolve {
|
||||
Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.0 }).unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(lsp_types::InlayHint {
|
||||
position: match inlay_hint.position {
|
||||
|
@ -451,8 +470,8 @@ pub(crate) fn inlay_hint(
|
|||
InlayKind::Type | InlayKind::Chaining => Some(lsp_types::InlayHintKind::TYPE),
|
||||
_ => None,
|
||||
},
|
||||
text_edits: inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)),
|
||||
data: None,
|
||||
text_edits,
|
||||
data,
|
||||
tooltip,
|
||||
label,
|
||||
})
|
||||
|
@ -460,13 +479,18 @@ pub(crate) fn inlay_hint(
|
|||
|
||||
fn inlay_hint_label(
|
||||
snap: &GlobalStateSnapshot,
|
||||
fields_to_resolve: &InlayFieldsToResolve,
|
||||
needs_resolve: bool,
|
||||
mut label: InlayHintLabel,
|
||||
) -> Cancellable<(lsp_types::InlayHintLabel, Option<lsp_types::InlayHintTooltip>)> {
|
||||
let res = match &*label.parts {
|
||||
) -> Cancellable<(lsp_types::InlayHintLabel, Option<lsp_types::InlayHintTooltip>, bool)> {
|
||||
let mut something_to_resolve = false;
|
||||
let (label, tooltip) = match &*label.parts {
|
||||
[InlayHintLabelPart { linked_location: None, .. }] => {
|
||||
let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap();
|
||||
(
|
||||
lsp_types::InlayHintLabel::String(text),
|
||||
let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip {
|
||||
something_to_resolve |= tooltip.is_some();
|
||||
None
|
||||
} else {
|
||||
match tooltip {
|
||||
Some(ide::InlayTooltip::String(s)) => {
|
||||
Some(lsp_types::InlayHintTooltip::String(s))
|
||||
|
@ -478,41 +502,52 @@ fn inlay_hint_label(
|
|||
}))
|
||||
}
|
||||
None => None,
|
||||
},
|
||||
)
|
||||
}
|
||||
};
|
||||
(lsp_types::InlayHintLabel::String(text), hint_tooltip)
|
||||
}
|
||||
_ => {
|
||||
let parts = label
|
||||
.parts
|
||||
.into_iter()
|
||||
.map(|part| {
|
||||
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,
|
||||
},
|
||||
)
|
||||
let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip {
|
||||
something_to_resolve |= part.tooltip.is_some();
|
||||
None
|
||||
} else {
|
||||
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,
|
||||
}
|
||||
};
|
||||
let location = if needs_resolve && fields_to_resolve.resolve_label_location {
|
||||
something_to_resolve |= part.linked_location.is_some();
|
||||
None
|
||||
} else {
|
||||
part.linked_location.map(|range| location(snap, range)).transpose()?
|
||||
};
|
||||
Ok(lsp_types::InlayHintLabelPart {
|
||||
value: part.text,
|
||||
tooltip,
|
||||
location,
|
||||
command: None,
|
||||
})
|
||||
})
|
||||
.collect::<Cancellable<_>>()?;
|
||||
(lsp_types::InlayHintLabel::LabelParts(parts), None)
|
||||
}
|
||||
};
|
||||
Ok(res)
|
||||
Ok((label, tooltip, something_to_resolve))
|
||||
}
|
||||
|
||||
static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);
|
||||
|
@ -1323,17 +1358,18 @@ pub(crate) fn code_lens(
|
|||
})
|
||||
}
|
||||
}
|
||||
AnnotationKind::HasImpls { pos: file_range, data } => {
|
||||
AnnotationKind::HasImpls { pos, data } => {
|
||||
if !client_commands_config.show_reference {
|
||||
return Ok(());
|
||||
}
|
||||
let line_index = snap.file_line_index(file_range.file_id)?;
|
||||
let line_index = snap.file_line_index(pos.file_id)?;
|
||||
let annotation_range = range(&line_index, annotation.range);
|
||||
let url = url(snap, file_range.file_id);
|
||||
let url = url(snap, pos.file_id);
|
||||
let pos = position(&line_index, pos.offset);
|
||||
|
||||
let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
|
||||
|
||||
let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start);
|
||||
let doc_pos = lsp_types::TextDocumentPositionParams::new(id, pos);
|
||||
|
||||
let goto_params = lsp_types::request::GotoImplementationParams {
|
||||
text_document_position_params: doc_pos,
|
||||
|
@ -1356,7 +1392,7 @@ pub(crate) fn code_lens(
|
|||
command::show_references(
|
||||
implementation_title(locations.len()),
|
||||
&url,
|
||||
annotation_range.start,
|
||||
pos,
|
||||
locations,
|
||||
)
|
||||
});
|
||||
|
@ -1376,28 +1412,24 @@ pub(crate) fn code_lens(
|
|||
})(),
|
||||
})
|
||||
}
|
||||
AnnotationKind::HasReferences { pos: file_range, data } => {
|
||||
AnnotationKind::HasReferences { pos, data } => {
|
||||
if !client_commands_config.show_reference {
|
||||
return Ok(());
|
||||
}
|
||||
let line_index = snap.file_line_index(file_range.file_id)?;
|
||||
let line_index = snap.file_line_index(pos.file_id)?;
|
||||
let annotation_range = range(&line_index, annotation.range);
|
||||
let url = url(snap, file_range.file_id);
|
||||
let url = url(snap, pos.file_id);
|
||||
let pos = position(&line_index, pos.offset);
|
||||
|
||||
let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
|
||||
|
||||
let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start);
|
||||
let doc_pos = lsp_types::TextDocumentPositionParams::new(id, pos);
|
||||
|
||||
let command = data.map(|ranges| {
|
||||
let locations: Vec<lsp_types::Location> =
|
||||
ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect();
|
||||
|
||||
command::show_references(
|
||||
reference_title(locations.len()),
|
||||
&url,
|
||||
annotation_range.start,
|
||||
locations,
|
||||
)
|
||||
command::show_references(reference_title(locations.len()), &url, pos, locations)
|
||||
});
|
||||
|
||||
acc.push(lsp_types::CodeLens {
|
||||
|
@ -1425,8 +1457,8 @@ pub(crate) mod command {
|
|||
|
||||
use crate::{
|
||||
global_state::GlobalStateSnapshot,
|
||||
lsp::to_proto::{location, location_link},
|
||||
lsp_ext,
|
||||
to_proto::{location, location_link},
|
||||
};
|
||||
|
||||
pub(crate) fn show_references(
|
||||
|
@ -1528,11 +1560,11 @@ pub(crate) fn markup_content(
|
|||
ide::HoverDocFormat::Markdown => lsp_types::MarkupKind::Markdown,
|
||||
ide::HoverDocFormat::PlainText => lsp_types::MarkupKind::PlainText,
|
||||
};
|
||||
let value = crate::markdown::format_docs(markup.as_str());
|
||||
let value = format_docs(&Documentation::new(markup.into()));
|
||||
lsp_types::MarkupContent { kind, value }
|
||||
}
|
||||
|
||||
pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
|
||||
pub(crate) fn rename_error(err: RenameError) -> LspError {
|
||||
// This is wrong, but we don't have a better alternative I suppose?
|
||||
// https://github.com/microsoft/language-server-protocol/issues/1341
|
||||
invalid_params_error(err.to_string())
|
|
@ -6,10 +6,10 @@ use lsp_types::request::Request;
|
|||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
from_proto,
|
||||
global_state::GlobalState,
|
||||
line_index::{LineEndings, LineIndex, PositionEncoding},
|
||||
lsp_ext, LspError,
|
||||
lsp::{from_proto, LspError},
|
||||
lsp_ext,
|
||||
};
|
||||
|
||||
pub(crate) fn invalid_params_error(message: String) -> LspError {
|
|
@ -17,11 +17,14 @@ use vfs::FileId;
|
|||
|
||||
use crate::{
|
||||
config::Config,
|
||||
diagnostics::fetch_native_diagnostics,
|
||||
dispatch::{NotificationDispatcher, RequestDispatcher},
|
||||
from_proto,
|
||||
global_state::{file_id_to_url, url_to_file_id, GlobalState},
|
||||
lsp::{
|
||||
from_proto,
|
||||
utils::{notification_is, Progress},
|
||||
},
|
||||
lsp_ext,
|
||||
lsp_utils::{notification_is, Progress},
|
||||
reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress},
|
||||
};
|
||||
|
||||
|
@ -317,8 +320,11 @@ impl GlobalState {
|
|||
}
|
||||
|
||||
// Refresh inlay hints if the client supports it.
|
||||
if self.config.inlay_hints_refresh() {
|
||||
if (self.send_hint_refresh_query || self.proc_macro_changed)
|
||||
&& self.config.inlay_hints_refresh()
|
||||
{
|
||||
self.send_request::<lsp_types::request::InlayHintRefreshRequest>((), |_, _| ());
|
||||
self.send_hint_refresh_query = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -420,6 +426,32 @@ impl GlobalState {
|
|||
});
|
||||
}
|
||||
|
||||
fn update_diagnostics(&mut self) {
|
||||
let db = self.analysis_host.raw_database();
|
||||
let subscriptions = self
|
||||
.mem_docs
|
||||
.iter()
|
||||
.map(|path| self.vfs.read().0.file_id(path).unwrap())
|
||||
.filter(|&file_id| {
|
||||
let source_root = db.file_source_root(file_id);
|
||||
// Only publish diagnostics for files in the workspace, not from crates.io deps
|
||||
// or the sysroot.
|
||||
// While theoretically these should never have errors, we have quite a few false
|
||||
// positives particularly in the stdlib, and those diagnostics would stay around
|
||||
// forever if we emitted them here.
|
||||
!db.source_root(source_root).is_library
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
tracing::trace!("updating notifications for {:?}", subscriptions);
|
||||
|
||||
// Diagnostics are triggered by the user typing
|
||||
// so we run them on a latency sensitive thread.
|
||||
self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, {
|
||||
let snapshot = self.snapshot();
|
||||
move || Task::Diagnostics(fetch_native_diagnostics(snapshot, subscriptions))
|
||||
});
|
||||
}
|
||||
|
||||
fn update_status_or_notify(&mut self) {
|
||||
let status = self.current_status();
|
||||
if self.last_reported_status.as_ref() != Some(&status) {
|
||||
|
@ -509,6 +541,7 @@ impl GlobalState {
|
|||
}
|
||||
|
||||
self.switch_workspaces("fetched build data".to_string());
|
||||
self.send_hint_refresh_query = true;
|
||||
|
||||
(Some(Progress::End), None)
|
||||
}
|
||||
|
@ -525,7 +558,7 @@ impl GlobalState {
|
|||
ProcMacroProgress::End(proc_macro_load_result) => {
|
||||
self.fetch_proc_macros_queue.op_completed(true);
|
||||
self.set_proc_macros(proc_macro_load_result);
|
||||
|
||||
self.send_hint_refresh_query = true;
|
||||
(Some(Progress::End), None)
|
||||
}
|
||||
};
|
||||
|
@ -670,6 +703,7 @@ impl GlobalState {
|
|||
}
|
||||
|
||||
use crate::handlers::request as handlers;
|
||||
use lsp_types::request as lsp_request;
|
||||
|
||||
dispatcher
|
||||
// Request handlers that must run on the main thread
|
||||
|
@ -682,30 +716,30 @@ impl GlobalState {
|
|||
// are run on the main thread to reduce latency:
|
||||
.on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines)
|
||||
.on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter)
|
||||
.on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)
|
||||
.on_sync::<lsp_request::SelectionRangeRequest>(handlers::handle_selection_range)
|
||||
.on_sync::<lsp_ext::MatchingBrace>(handlers::handle_matching_brace)
|
||||
.on_sync::<lsp_ext::OnTypeFormatting>(handlers::handle_on_type_formatting)
|
||||
// Formatting should be done immediately as the editor might wait on it, but we can't
|
||||
// put it on the main thread as we do not want the main thread to block on rustfmt.
|
||||
// So we have an extra thread just for formatting requests to make sure it gets handled
|
||||
// as fast as possible.
|
||||
.on_fmt_thread::<lsp_types::request::Formatting>(handlers::handle_formatting)
|
||||
.on_fmt_thread::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting)
|
||||
.on_fmt_thread::<lsp_request::Formatting>(handlers::handle_formatting)
|
||||
.on_fmt_thread::<lsp_request::RangeFormatting>(handlers::handle_range_formatting)
|
||||
// We can’t run latency-sensitive request handlers which do semantic
|
||||
// analysis on the main thread because that would block other
|
||||
// requests. Instead, we run these request handlers on higher priority
|
||||
// threads in the threadpool.
|
||||
.on_latency_sensitive::<lsp_types::request::Completion>(handlers::handle_completion)
|
||||
.on_latency_sensitive::<lsp_types::request::ResolveCompletionItem>(
|
||||
.on_latency_sensitive::<lsp_request::Completion>(handlers::handle_completion)
|
||||
.on_latency_sensitive::<lsp_request::ResolveCompletionItem>(
|
||||
handlers::handle_completion_resolve,
|
||||
)
|
||||
.on_latency_sensitive::<lsp_types::request::SemanticTokensFullRequest>(
|
||||
.on_latency_sensitive::<lsp_request::SemanticTokensFullRequest>(
|
||||
handlers::handle_semantic_tokens_full,
|
||||
)
|
||||
.on_latency_sensitive::<lsp_types::request::SemanticTokensFullDeltaRequest>(
|
||||
.on_latency_sensitive::<lsp_request::SemanticTokensFullDeltaRequest>(
|
||||
handlers::handle_semantic_tokens_full_delta,
|
||||
)
|
||||
.on_latency_sensitive::<lsp_types::request::SemanticTokensRangeRequest>(
|
||||
.on_latency_sensitive::<lsp_request::SemanticTokensRangeRequest>(
|
||||
handlers::handle_semantic_tokens_range,
|
||||
)
|
||||
// All other request handlers
|
||||
|
@ -729,29 +763,25 @@ impl GlobalState {
|
|||
.on::<lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml)
|
||||
.on::<lsp_ext::MoveItem>(handlers::handle_move_item)
|
||||
.on::<lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol)
|
||||
.on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)
|
||||
.on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)
|
||||
.on::<lsp_types::request::GotoDeclaration>(handlers::handle_goto_declaration)
|
||||
.on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)
|
||||
.on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
|
||||
.on_no_retry::<lsp_types::request::InlayHintRequest>(handlers::handle_inlay_hints)
|
||||
.on::<lsp_types::request::InlayHintResolveRequest>(handlers::handle_inlay_hints_resolve)
|
||||
.on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)
|
||||
.on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)
|
||||
.on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)
|
||||
.on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)
|
||||
.on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)
|
||||
.on::<lsp_types::request::Rename>(handlers::handle_rename)
|
||||
.on::<lsp_types::request::References>(handlers::handle_references)
|
||||
.on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight)
|
||||
.on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)
|
||||
.on::<lsp_types::request::CallHierarchyIncomingCalls>(
|
||||
handlers::handle_call_hierarchy_incoming,
|
||||
)
|
||||
.on::<lsp_types::request::CallHierarchyOutgoingCalls>(
|
||||
handlers::handle_call_hierarchy_outgoing,
|
||||
)
|
||||
.on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files)
|
||||
.on::<lsp_request::DocumentSymbolRequest>(handlers::handle_document_symbol)
|
||||
.on::<lsp_request::GotoDefinition>(handlers::handle_goto_definition)
|
||||
.on::<lsp_request::GotoDeclaration>(handlers::handle_goto_declaration)
|
||||
.on::<lsp_request::GotoImplementation>(handlers::handle_goto_implementation)
|
||||
.on::<lsp_request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
|
||||
.on_no_retry::<lsp_request::InlayHintRequest>(handlers::handle_inlay_hints)
|
||||
.on::<lsp_request::InlayHintResolveRequest>(handlers::handle_inlay_hints_resolve)
|
||||
.on::<lsp_request::CodeLensRequest>(handlers::handle_code_lens)
|
||||
.on::<lsp_request::CodeLensResolve>(handlers::handle_code_lens_resolve)
|
||||
.on::<lsp_request::FoldingRangeRequest>(handlers::handle_folding_range)
|
||||
.on::<lsp_request::SignatureHelpRequest>(handlers::handle_signature_help)
|
||||
.on::<lsp_request::PrepareRenameRequest>(handlers::handle_prepare_rename)
|
||||
.on::<lsp_request::Rename>(handlers::handle_rename)
|
||||
.on::<lsp_request::References>(handlers::handle_references)
|
||||
.on::<lsp_request::DocumentHighlightRequest>(handlers::handle_document_highlight)
|
||||
.on::<lsp_request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)
|
||||
.on::<lsp_request::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)
|
||||
.on::<lsp_request::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)
|
||||
.on::<lsp_request::WillRenameFiles>(handlers::handle_will_rename_files)
|
||||
.on::<lsp_ext::Ssr>(handlers::handle_ssr)
|
||||
.on::<lsp_ext::ViewRecursiveMemoryLayout>(handlers::handle_view_recursive_memory_layout)
|
||||
.finish();
|
||||
|
@ -788,77 +818,4 @@ impl GlobalState {
|
|||
.finish();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_diagnostics(&mut self) {
|
||||
let db = self.analysis_host.raw_database();
|
||||
let subscriptions = self
|
||||
.mem_docs
|
||||
.iter()
|
||||
.map(|path| self.vfs.read().0.file_id(path).unwrap())
|
||||
.filter(|&file_id| {
|
||||
let source_root = db.file_source_root(file_id);
|
||||
// Only publish diagnostics for files in the workspace, not from crates.io deps
|
||||
// or the sysroot.
|
||||
// While theoretically these should never have errors, we have quite a few false
|
||||
// positives particularly in the stdlib, and those diagnostics would stay around
|
||||
// forever if we emitted them here.
|
||||
!db.source_root(source_root).is_library
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
tracing::trace!("updating notifications for {:?}", subscriptions);
|
||||
|
||||
let snapshot = self.snapshot();
|
||||
|
||||
// Diagnostics are triggered by the user typing
|
||||
// so we run them on a latency sensitive thread.
|
||||
self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, move || {
|
||||
let _p = profile::span("publish_diagnostics");
|
||||
let _ctx = stdx::panic_context::enter("publish_diagnostics".to_owned());
|
||||
let diagnostics = subscriptions
|
||||
.into_iter()
|
||||
.filter_map(|file_id| {
|
||||
let line_index = snapshot.file_line_index(file_id).ok()?;
|
||||
Some((
|
||||
file_id,
|
||||
line_index,
|
||||
snapshot
|
||||
.analysis
|
||||
.diagnostics(
|
||||
&snapshot.config.diagnostics(),
|
||||
ide::AssistResolveStrategy::None,
|
||||
file_id,
|
||||
)
|
||||
.ok()?,
|
||||
))
|
||||
})
|
||||
.map(|(file_id, line_index, it)| {
|
||||
(
|
||||
file_id,
|
||||
it.into_iter()
|
||||
.map(move |d| lsp_types::Diagnostic {
|
||||
range: crate::to_proto::range(&line_index, d.range),
|
||||
severity: Some(crate::to_proto::diagnostic_severity(d.severity)),
|
||||
code: Some(lsp_types::NumberOrString::String(
|
||||
d.code.as_str().to_string(),
|
||||
)),
|
||||
code_description: Some(lsp_types::CodeDescription {
|
||||
href: lsp_types::Url::parse(&d.code.url()).unwrap(),
|
||||
}),
|
||||
source: Some("rust-analyzer".to_string()),
|
||||
message: d.message,
|
||||
related_information: None,
|
||||
tags: if d.unused {
|
||||
Some(vec![lsp_types::DiagnosticTag::UNNECESSARY])
|
||||
} else {
|
||||
None
|
||||
},
|
||||
data: None,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
});
|
||||
Task::Diagnostics(diagnostics.collect())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,165 +0,0 @@
|
|||
//! Transforms markdown
|
||||
use ide_db::rust_doc::is_rust_fence;
|
||||
|
||||
const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
|
||||
|
||||
pub(crate) fn format_docs(src: &str) -> String {
|
||||
let mut processed_lines = Vec::new();
|
||||
let mut in_code_block = false;
|
||||
let mut is_rust = false;
|
||||
|
||||
for mut line in src.lines() {
|
||||
if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(header) = RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence))
|
||||
{
|
||||
in_code_block ^= true;
|
||||
|
||||
if in_code_block {
|
||||
is_rust = is_rust_fence(header);
|
||||
|
||||
if is_rust {
|
||||
line = "```rust";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if in_code_block {
|
||||
let trimmed = line.trim_start();
|
||||
if is_rust && trimmed.starts_with("##") {
|
||||
line = &trimmed[1..];
|
||||
}
|
||||
}
|
||||
|
||||
processed_lines.push(line);
|
||||
}
|
||||
processed_lines.join("\n")
|
||||
}
|
||||
|
||||
fn code_line_ignored_by_rustdoc(line: &str) -> bool {
|
||||
let trimmed = line.trim();
|
||||
trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_format_docs_adds_rust() {
|
||||
let comment = "```\nfn some_rust() {}\n```";
|
||||
assert_eq!(format_docs(comment), "```rust\nfn some_rust() {}\n```");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_docs_handles_plain_text() {
|
||||
let comment = "```text\nthis is plain text\n```";
|
||||
assert_eq!(format_docs(comment), "```text\nthis is plain text\n```");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_docs_handles_non_rust() {
|
||||
let comment = "```sh\nsupposedly shell code\n```";
|
||||
assert_eq!(format_docs(comment), "```sh\nsupposedly shell code\n```");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_docs_handles_rust_alias() {
|
||||
let comment = "```ignore\nlet z = 55;\n```";
|
||||
assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_docs_handles_complex_code_block_attrs() {
|
||||
let comment = "```rust,no_run\nlet z = 55;\n```";
|
||||
assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_docs_handles_error_codes() {
|
||||
let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```";
|
||||
assert_eq!(format_docs(comment), "```rust\nlet b = 0 as *const _;\n```");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_docs_skips_comments_in_rust_block() {
|
||||
let comment =
|
||||
"```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```";
|
||||
assert_eq!(format_docs(comment), "```rust\n#stay1\nstay2\n```");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_docs_does_not_skip_lines_if_plain_text() {
|
||||
let comment =
|
||||
"```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```";
|
||||
assert_eq!(
|
||||
format_docs(comment),
|
||||
"```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_docs_keeps_comments_outside_of_rust_block() {
|
||||
let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t";
|
||||
assert_eq!(format_docs(comment), comment);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_docs_preserves_newlines() {
|
||||
let comment = "this\nis\nmultiline";
|
||||
assert_eq!(format_docs(comment), comment);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code_blocks_in_comments_marked_as_rust() {
|
||||
let comment = r#"```rust
|
||||
fn main(){}
|
||||
```
|
||||
Some comment.
|
||||
```
|
||||
let a = 1;
|
||||
```"#;
|
||||
|
||||
assert_eq!(
|
||||
format_docs(comment),
|
||||
"```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code_blocks_in_comments_marked_as_text() {
|
||||
let comment = r#"```text
|
||||
filler
|
||||
text
|
||||
```
|
||||
Some comment.
|
||||
```
|
||||
let a = 1;
|
||||
```"#;
|
||||
|
||||
assert_eq!(
|
||||
format_docs(comment),
|
||||
"```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_docs_handles_escape_double_hashes() {
|
||||
let comment = r#"```rust
|
||||
let s = "foo
|
||||
## bar # baz";
|
||||
```"#;
|
||||
|
||||
assert_eq!(format_docs(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_docs_handles_double_hashes_non_rust() {
|
||||
let comment = r#"```markdown
|
||||
## A second-level heading
|
||||
```"#;
|
||||
assert_eq!(format_docs(comment), "```markdown\n## A second-level heading\n```");
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
//! correct. Instead, we try to provide a best-effort service. Even if the
|
||||
//! project is currently loading and we don't have a full project model, we
|
||||
//! still want to respond to various requests.
|
||||
// FIXME: This is a mess that needs some untangling work
|
||||
use std::{iter, mem};
|
||||
|
||||
use flycheck::{FlycheckConfig, FlycheckHandle};
|
||||
|
|
|
@ -29,7 +29,7 @@ use lsp_types::{
|
|||
PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem,
|
||||
TextDocumentPositionParams, WorkDoneProgressParams,
|
||||
};
|
||||
use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams};
|
||||
use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams};
|
||||
use serde_json::json;
|
||||
use test_utils::skip_slow_tests;
|
||||
|
||||
|
@ -861,6 +861,7 @@ edition = "2021"
|
|||
bar = {path = "../bar"}
|
||||
|
||||
//- /foo/src/main.rs
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustc_attrs, decl_macro)]
|
||||
use bar::Bar;
|
||||
|
||||
|
@ -938,7 +939,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
|
|||
let res = server.send_request::<HoverRequest>(HoverParams {
|
||||
text_document_position_params: TextDocumentPositionParams::new(
|
||||
server.doc_id("foo/src/main.rs"),
|
||||
Position::new(11, 9),
|
||||
Position::new(12, 9),
|
||||
),
|
||||
work_done_progress_params: Default::default(),
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::{
|
|||
use crossbeam_channel::{after, select, Receiver};
|
||||
use lsp_server::{Connection, Message, Notification, Request};
|
||||
use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url};
|
||||
use rust_analyzer::{config::Config, lsp_ext, main_loop};
|
||||
use rust_analyzer::{config::Config, lsp, main_loop};
|
||||
use serde::Serialize;
|
||||
use serde_json::{json, to_string_pretty, Value};
|
||||
use test_utils::FixtureWithProjectMeta;
|
||||
|
@ -260,9 +260,9 @@ impl Server {
|
|||
Message::Notification(n) if n.method == "experimental/serverStatus" => {
|
||||
let status = n
|
||||
.clone()
|
||||
.extract::<lsp_ext::ServerStatusParams>("experimental/serverStatus")
|
||||
.extract::<lsp::ext::ServerStatusParams>("experimental/serverStatus")
|
||||
.unwrap();
|
||||
if status.health != lsp_ext::Health::Ok {
|
||||
if status.health != lsp::ext::Health::Ok {
|
||||
panic!("server errored/warned while loading workspace: {:?}", status.message);
|
||||
}
|
||||
status.quiescent
|
||||
|
|
|
@ -35,7 +35,7 @@ fn check_lsp_extensions_docs() {
|
|||
|
||||
let expected_hash = {
|
||||
let lsp_ext_rs = sh
|
||||
.read_file(sourcegen::project_root().join("crates/rust-analyzer/src/lsp_ext.rs"))
|
||||
.read_file(sourcegen::project_root().join("crates/rust-analyzer/src/lsp/ext.rs"))
|
||||
.unwrap();
|
||||
stable_hash(lsp_ext_rs.as_str())
|
||||
};
|
||||
|
@ -45,7 +45,7 @@ fn check_lsp_extensions_docs() {
|
|||
sh.read_file(sourcegen::project_root().join("docs/dev/lsp-extensions.md")).unwrap();
|
||||
let text = lsp_extensions_md
|
||||
.lines()
|
||||
.find_map(|line| line.strip_prefix("lsp_ext.rs hash:"))
|
||||
.find_map(|line| line.strip_prefix("lsp/ext.rs hash:"))
|
||||
.unwrap()
|
||||
.trim();
|
||||
u64::from_str_radix(text, 16).unwrap()
|
||||
|
@ -54,7 +54,7 @@ fn check_lsp_extensions_docs() {
|
|||
if actual_hash != expected_hash {
|
||||
panic!(
|
||||
"
|
||||
lsp_ext.rs was changed without touching lsp-extensions.md.
|
||||
lsp/ext.rs was changed without touching lsp-extensions.md.
|
||||
|
||||
Expected hash: {expected_hash:x}
|
||||
Actual hash: {actual_hash:x}
|
||||
|
@ -158,7 +158,7 @@ Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT
|
|||
Apache-2.0/MIT
|
||||
BSD-3-Clause
|
||||
BlueOak-1.0.0 OR MIT OR Apache-2.0
|
||||
CC0-1.0 OR Artistic-2.0
|
||||
CC0-1.0
|
||||
ISC
|
||||
MIT
|
||||
MIT / Apache-2.0
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue