2020-02-18 12:25:26 +01:00
|
|
|
//! This module is responsible for implementing handlers for Language Server
|
|
|
|
|
//! Protocol. The majority of requests are fulfilled by calling into the
|
2020-08-13 17:42:52 +02:00
|
|
|
//! `ide` crate.
|
2019-09-30 11:58:53 +03:00
|
|
|
|
2020-01-31 13:34:44 +01:00
|
|
|
use std::{
|
2021-08-09 17:45:42 +00:00
|
|
|
io::Write as _,
|
2021-08-10 13:35:37 +00:00
|
|
|
process::{self, Stdio},
|
2020-01-31 13:34:44 +01:00
|
|
|
};
|
2019-05-29 15:42:14 +03:00
|
|
|
|
2020-08-13 17:42:52 +02:00
|
|
|
use ide::{
|
2021-05-03 18:03:28 +03:00
|
|
|
AnnotationConfig, AssistKind, AssistResolveStrategy, FileId, FilePosition, FileRange,
|
2021-06-22 19:39:59 +02:00
|
|
|
HoverAction, HoverGotoTypeData, Query, RangeInfo, Runnable, RunnableKind, SingleResolve,
|
|
|
|
|
SourceChange, TextEdit,
|
2020-08-13 17:42:52 +02:00
|
|
|
};
|
2021-03-14 19:05:09 +02:00
|
|
|
use ide_db::SymbolKind;
|
2020-09-01 16:33:02 +03:00
|
|
|
use itertools::Itertools;
|
2019-08-30 17:24:11 +03:00
|
|
|
use lsp_server::ErrorCode;
|
2019-01-14 13:55:56 +03:00
|
|
|
use lsp_types::{
|
2019-12-30 09:12:06 -05:00
|
|
|
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
|
|
|
|
|
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
|
2021-06-22 19:39:59 +02:00
|
|
|
CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams, FoldingRange,
|
|
|
|
|
FoldingRangeParams, HoverContents, Location, NumberOrString, Position, PrepareRenameResponse,
|
|
|
|
|
Range, RenameParams, SemanticTokensDeltaParams, SemanticTokensFullDeltaResult,
|
|
|
|
|
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
|
2021-07-04 14:08:33 +03:00
|
|
|
SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
|
2019-01-06 00:58:03 +01:00
|
|
|
};
|
2020-08-13 12:05:30 +02:00
|
|
|
use project_model::TargetKind;
|
2021-07-04 14:08:33 +03:00
|
|
|
use serde_json::json;
|
2021-07-11 13:34:22 +03:00
|
|
|
use stdx::{format_to, never};
|
2020-12-17 14:29:05 +03:00
|
|
|
use syntax::{algo, ast, AstNode, TextRange, TextSize};
|
2018-08-10 23:30:11 +03:00
|
|
|
|
2018-10-15 20:15:53 +03:00
|
|
|
use crate::{
|
2020-02-18 12:17:47 +01:00
|
|
|
cargo_target_spec::CargoTargetSpec,
|
2020-04-01 14:32:04 +02:00
|
|
|
config::RustfmtConfig,
|
2021-01-05 22:29:53 +01:00
|
|
|
diff::diff,
|
2021-02-13 13:07:47 +02:00
|
|
|
from_proto,
|
2020-12-03 15:58:18 +02:00
|
|
|
global_state::{GlobalState, GlobalStateSnapshot},
|
2021-04-12 23:08:56 +03:00
|
|
|
line_index::LineEndings,
|
2021-07-26 12:14:14 -04:00
|
|
|
lsp_ext::{
|
|
|
|
|
self, InlayHint, InlayHintsParams, PositionOrRange, ViewCrateGraphParams,
|
|
|
|
|
WorkspaceSymbolParams,
|
|
|
|
|
},
|
2020-12-04 22:55:36 +02:00
|
|
|
lsp_utils::all_edits_are_disjoint,
|
2020-12-03 00:27:26 +02:00
|
|
|
to_proto, LspError, Result,
|
2018-08-11 00:12:31 +03:00
|
|
|
};
|
2018-08-10 21:13:39 +03:00
|
|
|
|
2020-09-29 22:05:18 +02:00
|
|
|
pub(crate) fn handle_analyzer_status(
|
|
|
|
|
snap: GlobalStateSnapshot,
|
|
|
|
|
params: lsp_ext::AnalyzerStatusParams,
|
|
|
|
|
) -> Result<String> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_analyzer_status");
|
2020-06-26 11:44:46 +02:00
|
|
|
|
|
|
|
|
let mut buf = String::new();
|
2020-09-29 22:05:18 +02:00
|
|
|
|
|
|
|
|
let mut file_id = None;
|
|
|
|
|
if let Some(tdi) = params.text_document {
|
|
|
|
|
match from_proto::file_id(&snap, &tdi.uri) {
|
|
|
|
|
Ok(it) => file_id = Some(it),
|
|
|
|
|
Err(_) => format_to!(buf, "file {} not found in vfs", tdi.uri),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-26 11:44:46 +02:00
|
|
|
if snap.workspaces.is_empty() {
|
2021-06-18 19:22:03 +01:00
|
|
|
buf.push_str("No workspaces\n")
|
2020-06-26 11:44:46 +02:00
|
|
|
} else {
|
2021-06-18 19:22:03 +01:00
|
|
|
buf.push_str("Workspaces:\n");
|
2021-06-18 23:20:30 +01:00
|
|
|
format_to!(
|
|
|
|
|
buf,
|
|
|
|
|
"Loaded {:?} packages across {} workspace{}.\n",
|
2021-06-19 10:14:15 +01:00
|
|
|
snap.workspaces.iter().map(|w| w.n_packages()).sum::<usize>(),
|
|
|
|
|
snap.workspaces.len(),
|
2021-06-18 23:20:30 +01:00
|
|
|
if snap.workspaces.len() == 1 { "" } else { "s" }
|
|
|
|
|
);
|
2020-06-26 11:44:46 +02:00
|
|
|
}
|
2021-06-18 19:22:03 +01:00
|
|
|
buf.push_str("\nAnalysis:\n");
|
2020-06-26 11:44:46 +02:00
|
|
|
buf.push_str(
|
2020-09-29 22:05:18 +02:00
|
|
|
&snap
|
|
|
|
|
.analysis
|
|
|
|
|
.status(file_id)
|
|
|
|
|
.unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
|
2020-06-26 11:44:46 +02:00
|
|
|
);
|
2019-05-29 15:42:14 +03:00
|
|
|
Ok(buf)
|
2019-01-23 00:15:03 +03:00
|
|
|
}
|
|
|
|
|
|
2020-07-07 12:10:14 +02:00
|
|
|
pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_memory_usage");
|
2021-04-01 23:01:07 +02:00
|
|
|
let mut mem = state.analysis_host.per_query_memory_usage();
|
|
|
|
|
mem.push(("Remaining".into(), profile::memory_usage().allocated));
|
2020-07-07 12:10:14 +02:00
|
|
|
|
|
|
|
|
let mut out = String::new();
|
|
|
|
|
for (name, bytes) in mem {
|
|
|
|
|
format_to!(out, "{:>8} {}\n", bytes, name);
|
|
|
|
|
}
|
|
|
|
|
Ok(out)
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_syntax_tree(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:25:37 +02:00
|
|
|
params: lsp_ext::SyntaxTreeParams,
|
|
|
|
|
) -> Result<String> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_syntax_tree");
|
2020-06-03 11:16:08 +02:00
|
|
|
let id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(id)?;
|
2020-05-10 13:55:24 +02:00
|
|
|
let text_range = params.range.map(|r| from_proto::text_range(&line_index, r));
|
2020-06-25 00:41:08 +02:00
|
|
|
let res = snap.analysis.syntax_tree(id, text_range)?;
|
2018-08-29 18:03:14 +03:00
|
|
|
Ok(res)
|
2018-08-10 21:13:39 +03:00
|
|
|
}
|
2018-08-10 22:23:17 +03:00
|
|
|
|
2020-12-28 18:29:58 +00:00
|
|
|
pub(crate) fn handle_view_hir(
|
|
|
|
|
snap: GlobalStateSnapshot,
|
|
|
|
|
params: lsp_types::TextDocumentPositionParams,
|
|
|
|
|
) -> Result<String> {
|
|
|
|
|
let _p = profile::span("handle_view_hir");
|
|
|
|
|
let position = from_proto::file_position(&snap, params)?;
|
|
|
|
|
let res = snap.analysis.view_hir(position)?;
|
|
|
|
|
Ok(res)
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 23:59:52 +02:00
|
|
|
pub(crate) fn handle_view_item_tree(
|
|
|
|
|
snap: GlobalStateSnapshot,
|
|
|
|
|
params: lsp_ext::ViewItemTreeParams,
|
|
|
|
|
) -> Result<String> {
|
|
|
|
|
let _p = profile::span("handle_view_item_tree");
|
|
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
|
|
|
|
let res = snap.analysis.view_item_tree(file_id)?;
|
|
|
|
|
Ok(res)
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-02 00:08:05 +02:00
|
|
|
pub(crate) fn handle_view_crate_graph(
|
|
|
|
|
snap: GlobalStateSnapshot,
|
|
|
|
|
params: ViewCrateGraphParams,
|
|
|
|
|
) -> Result<String> {
|
2021-05-11 16:15:31 +02:00
|
|
|
let _p = profile::span("handle_view_crate_graph");
|
2021-07-02 00:08:05 +02:00
|
|
|
let dot = snap.analysis.view_crate_graph(params.full)??;
|
2021-08-10 13:34:30 +00:00
|
|
|
Ok(dot)
|
2021-05-11 16:15:31 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_expand_macro(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:25:37 +02:00
|
|
|
params: lsp_ext::ExpandMacroParams,
|
|
|
|
|
) -> Result<Option<lsp_ext::ExpandedMacro>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_expand_macro");
|
2020-06-03 11:16:08 +02:00
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2020-05-25 14:56:26 +02:00
|
|
|
let offset = from_proto::offset(&line_index, params.position);
|
2019-11-18 02:47:50 +08:00
|
|
|
|
2020-06-25 00:41:08 +02:00
|
|
|
let res = snap.analysis.expand_macro(FilePosition { file_id, offset })?;
|
2020-05-25 14:56:26 +02:00
|
|
|
Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
|
2019-11-18 02:47:50 +08:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_selection_range(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::SelectionRangeParams,
|
|
|
|
|
) -> Result<Option<Vec<lsp_types::SelectionRange>>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_selection_range");
|
2020-06-03 11:16:08 +02:00
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2020-05-10 19:24:02 +02:00
|
|
|
let res: Result<Vec<lsp_types::SelectionRange>> = params
|
2019-04-21 12:13:48 +03:00
|
|
|
.positions
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|position| {
|
2020-05-10 13:55:24 +02:00
|
|
|
let offset = from_proto::offset(&line_index, position);
|
2019-04-21 12:13:48 +03:00
|
|
|
let mut ranges = Vec::new();
|
|
|
|
|
{
|
2020-05-10 13:55:24 +02:00
|
|
|
let mut range = TextRange::new(offset, offset);
|
2019-04-21 12:13:48 +03:00
|
|
|
loop {
|
|
|
|
|
ranges.push(range);
|
|
|
|
|
let frange = FileRange { file_id, range };
|
2020-06-25 00:41:08 +02:00
|
|
|
let next = snap.analysis.extend_selection(frange)?;
|
2019-04-21 12:13:48 +03:00
|
|
|
if next == range {
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
range = next
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-10 19:24:02 +02:00
|
|
|
let mut range = lsp_types::SelectionRange {
|
2020-05-10 13:55:24 +02:00
|
|
|
range: to_proto::range(&line_index, *ranges.last().unwrap()),
|
2019-04-21 12:13:48 +03:00
|
|
|
parent: None,
|
|
|
|
|
};
|
2020-05-10 13:55:24 +02:00
|
|
|
for &r in ranges.iter().rev().skip(1) {
|
2020-05-10 19:24:02 +02:00
|
|
|
range = lsp_types::SelectionRange {
|
2020-05-10 13:55:24 +02:00
|
|
|
range: to_proto::range(&line_index, r),
|
2019-04-21 12:13:48 +03:00
|
|
|
parent: Some(Box::new(range)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(range)
|
|
|
|
|
})
|
2020-03-09 10:17:16 -04:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
Ok(Some(res?))
|
2019-04-21 12:13:48 +03:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_matching_brace(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-24 16:18:46 +02:00
|
|
|
params: lsp_ext::MatchingBraceParams,
|
2018-08-16 00:23:22 +03:00
|
|
|
) -> Result<Vec<Position>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_matching_brace");
|
2020-06-03 11:16:08 +02:00
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2018-10-15 17:44:23 -04:00
|
|
|
let res = params
|
2020-05-24 16:18:46 +02:00
|
|
|
.positions
|
2018-08-16 00:23:22 +03:00
|
|
|
.into_iter()
|
2020-05-10 13:55:24 +02:00
|
|
|
.map(|position| {
|
|
|
|
|
let offset = from_proto::offset(&line_index, position);
|
2020-06-25 00:41:08 +02:00
|
|
|
let offset = match snap.analysis.matching_brace(FilePosition { file_id, offset }) {
|
2020-05-10 13:55:24 +02:00
|
|
|
Ok(Some(matching_brace_offset)) => matching_brace_offset,
|
|
|
|
|
Err(_) | Ok(None) => offset,
|
|
|
|
|
};
|
|
|
|
|
to_proto::position(&line_index, offset)
|
2018-08-16 00:23:22 +03:00
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
Ok(res)
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_join_lines(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:25:37 +02:00
|
|
|
params: lsp_ext::JoinLinesParams,
|
2020-05-21 19:50:23 +02:00
|
|
|
) -> Result<Vec<lsp_types::TextEdit>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_join_lines");
|
2021-07-05 23:31:44 +03:00
|
|
|
|
|
|
|
|
let config = snap.config.join_lines();
|
2020-06-03 11:16:08 +02:00
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2021-07-05 23:31:44 +03:00
|
|
|
|
2020-05-21 19:50:23 +02:00
|
|
|
let mut res = TextEdit::default();
|
|
|
|
|
for range in params.ranges {
|
|
|
|
|
let range = from_proto::text_range(&line_index, range);
|
2021-07-05 23:31:44 +03:00
|
|
|
let edit = snap.analysis.join_lines(&config, FileRange { file_id, range })?;
|
2020-05-21 19:50:23 +02:00
|
|
|
match res.union(edit) {
|
|
|
|
|
Ok(()) => (),
|
|
|
|
|
Err(_edit) => {
|
|
|
|
|
// just ignore overlapping edits
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-05 23:31:44 +03:00
|
|
|
|
|
|
|
|
Ok(to_proto::text_edit_vec(&line_index, res))
|
2018-08-29 18:03:14 +03:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_on_enter(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::TextDocumentPositionParams,
|
2020-05-25 14:12:53 +02:00
|
|
|
) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_on_enter");
|
2020-06-03 11:16:08 +02:00
|
|
|
let position = from_proto::file_position(&snap, params)?;
|
2020-06-25 00:41:08 +02:00
|
|
|
let edit = match snap.analysis.on_enter(position)? {
|
2020-05-25 14:12:53 +02:00
|
|
|
None => return Ok(None),
|
|
|
|
|
Some(it) => it,
|
|
|
|
|
};
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(position.file_id)?;
|
|
|
|
|
let edit = to_proto::snippet_text_edit_vec(&line_index, true, edit);
|
2020-05-25 14:12:53 +02:00
|
|
|
Ok(Some(edit))
|
2018-10-09 16:00:20 +03:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_on_type_formatting(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::DocumentOnTypeFormattingParams,
|
2020-05-21 19:50:23 +02:00
|
|
|
) -> Result<Option<Vec<lsp_types::TextEdit>>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_on_type_formatting");
|
2020-06-03 11:16:08 +02:00
|
|
|
let mut position = from_proto::file_position(&snap, params.text_document_position)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(position.file_id)?;
|
2019-07-07 14:13:13 -04:00
|
|
|
|
2020-08-13 17:42:52 +02:00
|
|
|
// in `ide`, the `on_type` invariant is that
|
2019-07-07 14:13:13 -04:00
|
|
|
// `text.char_at(position) == typed_char`.
|
2020-04-24 23:40:41 +02:00
|
|
|
position.offset -= TextSize::of('.');
|
2019-10-25 11:19:26 +03:00
|
|
|
let char_typed = params.ch.chars().next().unwrap_or('\0');
|
2021-08-24 13:13:38 +03:00
|
|
|
|
|
|
|
|
let text = snap.analysis.file_text(position.file_id)?;
|
2021-08-30 10:25:44 +03:00
|
|
|
if !text[usize::from(position.offset)..].starts_with(char_typed) {
|
|
|
|
|
// Add `always!` here once VS Code bug is fixed:
|
|
|
|
|
// https://github.com/rust-analyzer/rust-analyzer/issues/10002
|
2021-08-24 13:13:38 +03:00
|
|
|
return Ok(None);
|
|
|
|
|
}
|
2019-10-25 13:03:57 +03:00
|
|
|
|
|
|
|
|
// We have an assist that inserts ` ` after typing `->` in `fn foo() ->{`,
|
|
|
|
|
// but it requires precise cursor positioning to work, and one can't
|
|
|
|
|
// position the cursor with on_type formatting. So, let's just toggle this
|
|
|
|
|
// feature off here, hoping that we'll enable it one day, 😿.
|
|
|
|
|
if char_typed == '>' {
|
|
|
|
|
return Ok(None);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-25 00:41:08 +02:00
|
|
|
let edit = snap.analysis.on_char_typed(position, char_typed)?;
|
2021-01-14 18:35:22 +01:00
|
|
|
let edit = match edit {
|
2019-01-11 15:05:40 +03:00
|
|
|
Some(it) => it,
|
|
|
|
|
None => return Ok(None),
|
|
|
|
|
};
|
2019-01-06 00:58:03 +01:00
|
|
|
|
2019-01-11 15:05:40 +03:00
|
|
|
// This should be a single-file edit
|
2021-01-14 22:43:36 +01:00
|
|
|
let (_, edit) = edit.source_file_edits.into_iter().next().unwrap();
|
2019-01-06 00:58:03 +01:00
|
|
|
|
2021-02-13 00:44:28 +03:00
|
|
|
let change = to_proto::text_edit_vec(&line_index, edit);
|
2019-02-06 15:50:26 -05:00
|
|
|
Ok(Some(change))
|
2018-08-23 22:14:51 +03:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_document_symbol(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::DocumentSymbolParams,
|
|
|
|
|
) -> Result<Option<lsp_types::DocumentSymbolResponse>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_document_symbol");
|
2020-06-03 11:16:08 +02:00
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2018-08-11 14:44:12 +03:00
|
|
|
|
2021-01-07 13:05:51 +03:00
|
|
|
let mut parents: Vec<(lsp_types::DocumentSymbol, Option<usize>)> = Vec::new();
|
2018-08-11 14:44:12 +03:00
|
|
|
|
2020-06-25 00:41:08 +02:00
|
|
|
for symbol in snap.analysis.file_structure(file_id)? {
|
2020-07-22 12:18:48 -04:00
|
|
|
let mut tags = Vec::new();
|
|
|
|
|
if symbol.deprecated {
|
|
|
|
|
tags.push(SymbolTag::Deprecated)
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-22 15:41:38 -04:00
|
|
|
#[allow(deprecated)]
|
2021-01-07 13:05:51 +03:00
|
|
|
let doc_symbol = lsp_types::DocumentSymbol {
|
2018-08-14 11:20:09 +03:00
|
|
|
name: symbol.label,
|
2019-01-24 18:21:17 +01:00
|
|
|
detail: symbol.detail,
|
2021-03-14 17:16:29 +02:00
|
|
|
kind: to_proto::structure_node_kind(symbol.kind),
|
2020-07-22 12:18:48 -04:00
|
|
|
tags: Some(tags),
|
2020-12-21 15:58:26 +02:00
|
|
|
deprecated: Some(symbol.deprecated),
|
2020-05-10 13:55:24 +02:00
|
|
|
range: to_proto::range(&line_index, symbol.node_range),
|
|
|
|
|
selection_range: to_proto::range(&line_index, symbol.navigation_range),
|
2018-08-11 14:44:12 +03:00
|
|
|
children: None,
|
|
|
|
|
};
|
2018-08-14 11:20:09 +03:00
|
|
|
parents.push((doc_symbol, symbol.parent));
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-18 14:27:17 +02:00
|
|
|
// Builds hierarchy from a flat list, in reverse order (so that indices
|
|
|
|
|
// makes sense)
|
|
|
|
|
let document_symbols = {
|
|
|
|
|
let mut acc = Vec::new();
|
|
|
|
|
while let Some((mut node, parent_idx)) = parents.pop() {
|
|
|
|
|
if let Some(children) = &mut node.children {
|
|
|
|
|
children.reverse();
|
|
|
|
|
}
|
|
|
|
|
let parent = match parent_idx {
|
|
|
|
|
None => &mut acc,
|
|
|
|
|
Some(i) => parents[i].0.children.get_or_insert_with(Vec::new),
|
|
|
|
|
};
|
|
|
|
|
parent.push(node);
|
2020-08-08 19:18:56 +09:00
|
|
|
}
|
2020-08-18 14:27:17 +02:00
|
|
|
acc.reverse();
|
|
|
|
|
acc
|
|
|
|
|
};
|
2020-08-08 19:18:56 +09:00
|
|
|
|
2021-01-05 16:57:05 +03:00
|
|
|
let res = if snap.config.hierarchical_symbols() {
|
2020-05-10 13:55:24 +02:00
|
|
|
document_symbols.into()
|
2020-04-24 10:11:57 -04:00
|
|
|
} else {
|
2020-06-13 11:00:06 +02:00
|
|
|
let url = to_proto::url(&snap, file_id);
|
2020-04-24 10:11:57 -04:00
|
|
|
let mut symbol_information = Vec::<SymbolInformation>::new();
|
|
|
|
|
for symbol in document_symbols {
|
|
|
|
|
flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
|
|
|
|
|
}
|
2020-05-10 13:55:24 +02:00
|
|
|
symbol_information.into()
|
|
|
|
|
};
|
|
|
|
|
return Ok(Some(res));
|
2020-04-24 10:11:57 -04:00
|
|
|
|
2020-05-10 13:55:24 +02:00
|
|
|
fn flatten_document_symbol(
|
2021-01-07 13:05:51 +03:00
|
|
|
symbol: &lsp_types::DocumentSymbol,
|
2020-05-10 13:55:24 +02:00
|
|
|
container_name: Option<String>,
|
|
|
|
|
url: &Url,
|
|
|
|
|
res: &mut Vec<SymbolInformation>,
|
|
|
|
|
) {
|
2020-07-22 12:18:48 -04:00
|
|
|
let mut tags = Vec::new();
|
2020-07-22 15:41:38 -04:00
|
|
|
|
|
|
|
|
#[allow(deprecated)]
|
2020-07-22 12:18:48 -04:00
|
|
|
match symbol.deprecated {
|
|
|
|
|
Some(true) => tags.push(SymbolTag::Deprecated),
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 15:41:38 -04:00
|
|
|
#[allow(deprecated)]
|
2020-05-10 13:55:24 +02:00
|
|
|
res.push(SymbolInformation {
|
|
|
|
|
name: symbol.name.clone(),
|
|
|
|
|
kind: symbol.kind,
|
2020-07-22 12:18:48 -04:00
|
|
|
tags: Some(tags),
|
2020-05-10 13:55:24 +02:00
|
|
|
deprecated: symbol.deprecated,
|
|
|
|
|
location: Location::new(url.clone(), symbol.range),
|
2020-05-22 17:26:31 -04:00
|
|
|
container_name,
|
2020-05-10 13:55:24 +02:00
|
|
|
});
|
2020-04-24 10:11:57 -04:00
|
|
|
|
2020-05-10 13:55:24 +02:00
|
|
|
for child in symbol.children.iter().flatten() {
|
|
|
|
|
flatten_document_symbol(child, Some(symbol.name.clone()), url, res);
|
|
|
|
|
}
|
2020-04-24 10:11:57 -04:00
|
|
|
}
|
2018-08-11 14:44:12 +03:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_workspace_symbol(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2021-02-23 14:03:31 +02:00
|
|
|
params: WorkspaceSymbolParams,
|
2018-08-13 15:35:53 +03:00
|
|
|
) -> Result<Option<Vec<SymbolInformation>>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_workspace_symbol");
|
2021-02-23 14:03:31 +02:00
|
|
|
|
|
|
|
|
let (all_symbols, libs) = decide_search_scope_and_kind(¶ms, &snap);
|
|
|
|
|
|
2018-08-13 16:07:05 +03:00
|
|
|
let query = {
|
2019-02-08 14:49:43 +03:00
|
|
|
let query: String = params.query.chars().filter(|&c| c != '#' && c != '*').collect();
|
2018-08-13 16:07:05 +03:00
|
|
|
let mut q = Query::new(query);
|
|
|
|
|
if !all_symbols {
|
|
|
|
|
q.only_types();
|
|
|
|
|
}
|
2018-09-03 21:03:37 +03:00
|
|
|
if libs {
|
|
|
|
|
q.libs();
|
|
|
|
|
}
|
2018-08-13 17:19:27 +03:00
|
|
|
q.limit(128);
|
2018-08-13 16:07:05 +03:00
|
|
|
q
|
|
|
|
|
};
|
2020-06-03 11:16:08 +02:00
|
|
|
let mut res = exec_query(&snap, query)?;
|
2018-08-14 13:33:44 +03:00
|
|
|
if res.is_empty() && !all_symbols {
|
|
|
|
|
let mut query = Query::new(params.query);
|
|
|
|
|
query.limit(128);
|
2020-06-03 11:16:08 +02:00
|
|
|
res = exec_query(&snap, query)?;
|
2018-08-14 13:33:44 +03:00
|
|
|
}
|
2018-08-13 16:07:05 +03:00
|
|
|
|
2018-08-14 13:33:44 +03:00
|
|
|
return Ok(Some(res));
|
|
|
|
|
|
2021-02-23 14:03:31 +02:00
|
|
|
fn decide_search_scope_and_kind(
|
|
|
|
|
params: &WorkspaceSymbolParams,
|
|
|
|
|
snap: &GlobalStateSnapshot,
|
|
|
|
|
) -> (bool, bool) {
|
|
|
|
|
// Support old-style parsing of markers in the query.
|
|
|
|
|
let mut all_symbols = params.query.contains('#');
|
|
|
|
|
let mut libs = params.query.contains('*');
|
|
|
|
|
|
|
|
|
|
let config = snap.config.workspace_symbol();
|
|
|
|
|
|
|
|
|
|
// If no explicit marker was set, check request params. If that's also empty
|
|
|
|
|
// use global config.
|
|
|
|
|
if !all_symbols {
|
|
|
|
|
let search_kind = if let Some(ref search_kind) = params.search_kind {
|
|
|
|
|
search_kind
|
|
|
|
|
} else {
|
|
|
|
|
&config.search_kind
|
|
|
|
|
};
|
|
|
|
|
all_symbols = match search_kind {
|
|
|
|
|
lsp_ext::WorkspaceSymbolSearchKind::OnlyTypes => false,
|
|
|
|
|
lsp_ext::WorkspaceSymbolSearchKind::AllSymbols => true,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !libs {
|
|
|
|
|
let search_scope = if let Some(ref search_scope) = params.search_scope {
|
|
|
|
|
search_scope
|
|
|
|
|
} else {
|
|
|
|
|
&config.search_scope
|
|
|
|
|
};
|
|
|
|
|
libs = match search_scope {
|
|
|
|
|
lsp_ext::WorkspaceSymbolSearchScope::Workspace => false,
|
|
|
|
|
lsp_ext::WorkspaceSymbolSearchScope::WorkspaceAndDependencies => true,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(all_symbols, libs)
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-03 11:16:08 +02:00
|
|
|
fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> {
|
2018-08-14 13:33:44 +03:00
|
|
|
let mut res = Vec::new();
|
2020-06-25 00:41:08 +02:00
|
|
|
for nav in snap.analysis.symbol_search(query)? {
|
2020-07-17 12:42:48 +02:00
|
|
|
let container_name = nav.container_name.as_ref().map(|v| v.to_string());
|
2020-07-22 15:41:38 -04:00
|
|
|
|
|
|
|
|
#[allow(deprecated)]
|
2018-08-14 13:33:44 +03:00
|
|
|
let info = SymbolInformation {
|
2020-07-17 12:42:48 +02:00
|
|
|
name: nav.name.to_string(),
|
2020-12-18 21:10:13 +03:00
|
|
|
kind: nav
|
|
|
|
|
.kind
|
|
|
|
|
.map(to_proto::symbol_kind)
|
|
|
|
|
.unwrap_or(lsp_types::SymbolKind::Variable),
|
2020-07-22 12:18:48 -04:00
|
|
|
tags: None,
|
2020-06-30 13:27:13 +02:00
|
|
|
location: to_proto::location_from_nav(snap, nav)?,
|
|
|
|
|
container_name,
|
2018-09-23 11:13:27 -04:00
|
|
|
deprecated: None,
|
2018-08-14 13:33:44 +03:00
|
|
|
};
|
|
|
|
|
res.push(info);
|
2018-10-15 17:44:23 -04:00
|
|
|
}
|
2018-08-14 13:33:44 +03:00
|
|
|
Ok(res)
|
|
|
|
|
}
|
2018-08-13 15:35:53 +03:00
|
|
|
}
|
|
|
|
|
|
2020-12-22 14:19:51 -05:00
|
|
|
pub(crate) fn handle_will_rename_files(
|
|
|
|
|
snap: GlobalStateSnapshot,
|
|
|
|
|
params: lsp_types::RenameFilesParams,
|
|
|
|
|
) -> Result<Option<lsp_types::WorkspaceEdit>> {
|
|
|
|
|
let _p = profile::span("handle_will_rename_files");
|
|
|
|
|
|
|
|
|
|
let source_changes: Vec<SourceChange> = params
|
|
|
|
|
.files
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter_map(|file_rename| {
|
|
|
|
|
let from = Url::parse(&file_rename.old_uri).ok()?;
|
|
|
|
|
let to = Url::parse(&file_rename.new_uri).ok()?;
|
|
|
|
|
|
|
|
|
|
let from_path = from.to_file_path().ok()?;
|
|
|
|
|
let to_path = to.to_file_path().ok()?;
|
|
|
|
|
|
|
|
|
|
// Limit to single-level moves for now.
|
|
|
|
|
match (from_path.parent(), to_path.parent()) {
|
|
|
|
|
(Some(p1), Some(p2)) if p1 == p2 => {
|
2021-01-10 01:29:08 +08:00
|
|
|
if from_path.is_dir() {
|
2021-01-11 14:45:35 +08:00
|
|
|
// add '/' to end of url -- from `file://path/to/folder` to `file://path/to/folder/`
|
2021-01-12 00:16:12 +08:00
|
|
|
let mut old_folder_name = from_path.file_stem()?.to_str()?.to_string();
|
2021-01-11 14:45:35 +08:00
|
|
|
old_folder_name.push('/');
|
|
|
|
|
let from_with_trailing_slash = from.join(&old_folder_name).ok()?;
|
|
|
|
|
|
|
|
|
|
let imitate_from_url = from_with_trailing_slash.join("mod.rs").ok()?;
|
2021-01-12 00:21:03 +08:00
|
|
|
let new_file_name = to_path.file_name()?.to_str()?;
|
2021-01-10 14:10:36 +08:00
|
|
|
Some((
|
|
|
|
|
snap.url_to_file_id(&imitate_from_url).ok()?,
|
2021-01-12 00:21:03 +08:00
|
|
|
new_file_name.to_string(),
|
2021-01-10 14:10:36 +08:00
|
|
|
))
|
|
|
|
|
} else {
|
2021-01-12 00:15:56 +08:00
|
|
|
let old_name = from_path.file_stem()?.to_str()?;
|
|
|
|
|
let new_name = to_path.file_stem()?.to_str()?;
|
2021-01-12 00:21:43 +08:00
|
|
|
match (old_name, new_name) {
|
2021-01-13 00:30:49 +08:00
|
|
|
("mod", _) => None,
|
|
|
|
|
(_, "mod") => None,
|
|
|
|
|
_ => Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())),
|
2021-01-10 14:10:36 +08:00
|
|
|
}
|
2021-01-10 01:29:08 +08:00
|
|
|
}
|
2020-12-22 14:19:51 -05:00
|
|
|
}
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.filter_map(|(file_id, new_name)| {
|
|
|
|
|
snap.analysis.will_rename_file(file_id, &new_name).ok()?
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
// Drop file system edits since we're just renaming things on the same level
|
2021-01-14 18:35:22 +01:00
|
|
|
let mut source_changes = source_changes.into_iter();
|
2021-01-14 22:43:36 +01:00
|
|
|
let mut source_change = source_changes.next().unwrap_or_default();
|
|
|
|
|
source_change.file_system_edits.clear();
|
2021-01-14 18:35:22 +01:00
|
|
|
// no collect here because we want to merge text edits on same file ids
|
2021-01-14 22:43:36 +01:00
|
|
|
source_change.extend(source_changes.map(|it| it.source_file_edits).flatten());
|
2021-03-20 13:44:39 +01:00
|
|
|
if source_change.source_file_edits.is_empty() {
|
|
|
|
|
Ok(None)
|
|
|
|
|
} else {
|
|
|
|
|
to_proto::workspace_edit(&snap, source_change).map(Some)
|
|
|
|
|
}
|
2020-12-22 14:19:51 -05:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_goto_definition(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::GotoDefinitionParams,
|
|
|
|
|
) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_goto_definition");
|
2020-06-03 11:16:08 +02:00
|
|
|
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
|
2020-06-25 00:41:08 +02:00
|
|
|
let nav_info = match snap.analysis.goto_definition(position)? {
|
2018-12-08 16:02:23 +00:00
|
|
|
None => return Ok(None),
|
2018-12-08 21:18:29 +03:00
|
|
|
Some(it) => it,
|
|
|
|
|
};
|
2020-05-25 15:55:25 +02:00
|
|
|
let src = FileRange { file_id: position.file_id, range: nav_info.range };
|
2020-06-03 11:16:08 +02:00
|
|
|
let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
|
2019-07-08 13:47:02 +03:00
|
|
|
Ok(Some(res))
|
2018-08-13 16:35:17 +03:00
|
|
|
}
|
|
|
|
|
|
2021-06-22 20:43:48 +02:00
|
|
|
pub(crate) fn handle_goto_declaration(
|
|
|
|
|
snap: GlobalStateSnapshot,
|
|
|
|
|
params: lsp_types::request::GotoDeclarationParams,
|
|
|
|
|
) -> Result<Option<lsp_types::request::GotoDeclarationResponse>> {
|
|
|
|
|
let _p = profile::span("handle_goto_declaration");
|
2021-06-24 00:22:02 +02:00
|
|
|
let position = from_proto::file_position(&snap, params.text_document_position_params.clone())?;
|
2021-06-22 20:43:48 +02:00
|
|
|
let nav_info = match snap.analysis.goto_declaration(position)? {
|
2021-06-24 00:22:02 +02:00
|
|
|
None => return handle_goto_definition(snap, params),
|
2021-06-22 20:43:48 +02:00
|
|
|
Some(it) => it,
|
|
|
|
|
};
|
|
|
|
|
let src = FileRange { file_id: position.file_id, range: nav_info.range };
|
|
|
|
|
let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
|
|
|
|
|
Ok(Some(res))
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_goto_implementation(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::request::GotoImplementationParams,
|
|
|
|
|
) -> Result<Option<lsp_types::request::GotoImplementationResponse>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_goto_implementation");
|
2020-06-03 11:16:08 +02:00
|
|
|
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
|
2020-06-25 00:41:08 +02:00
|
|
|
let nav_info = match snap.analysis.goto_implementation(position)? {
|
2019-01-28 09:26:32 -05:00
|
|
|
None => return Ok(None),
|
|
|
|
|
Some(it) => it,
|
|
|
|
|
};
|
2020-05-25 15:55:25 +02:00
|
|
|
let src = FileRange { file_id: position.file_id, range: nav_info.range };
|
2020-06-03 11:16:08 +02:00
|
|
|
let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
|
2019-07-08 13:47:02 +03:00
|
|
|
Ok(Some(res))
|
2019-04-23 14:11:27 -04:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_goto_type_definition(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::request::GotoTypeDefinitionParams,
|
|
|
|
|
) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_goto_type_definition");
|
2020-06-03 11:16:08 +02:00
|
|
|
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
|
2020-06-25 00:41:08 +02:00
|
|
|
let nav_info = match snap.analysis.goto_type_definition(position)? {
|
2019-04-23 14:11:27 -04:00
|
|
|
None => return Ok(None),
|
|
|
|
|
Some(it) => it,
|
|
|
|
|
};
|
2020-05-25 15:55:25 +02:00
|
|
|
let src = FileRange { file_id: position.file_id, range: nav_info.range };
|
2020-06-03 11:16:08 +02:00
|
|
|
let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
|
2019-07-08 13:47:02 +03:00
|
|
|
Ok(Some(res))
|
2018-08-13 16:35:17 +03:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_parent_module(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::TextDocumentPositionParams,
|
2020-05-25 15:55:25 +02:00
|
|
|
) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_parent_module");
|
2020-06-03 11:16:08 +02:00
|
|
|
let position = from_proto::file_position(&snap, params)?;
|
2020-06-25 00:41:08 +02:00
|
|
|
let navs = snap.analysis.parent_module(position)?;
|
2020-06-03 11:16:08 +02:00
|
|
|
let res = to_proto::goto_definition_response(&snap, None, navs)?;
|
2020-05-25 15:55:25 +02:00
|
|
|
Ok(Some(res))
|
2018-08-22 10:18:58 +03:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_runnables(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:25:37 +02:00
|
|
|
params: lsp_ext::RunnablesParams,
|
|
|
|
|
) -> Result<Vec<lsp_ext::Runnable>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_runnables");
|
2020-06-03 11:16:08 +02:00
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2020-05-10 13:55:24 +02:00
|
|
|
let offset = params.position.map(|it| from_proto::offset(&line_index, it));
|
2020-06-03 11:16:08 +02:00
|
|
|
let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
|
2020-06-27 17:53:50 +02:00
|
|
|
|
|
|
|
|
let expect_test = match offset {
|
|
|
|
|
Some(offset) => {
|
|
|
|
|
let source_file = snap.analysis.parse(file_id)?;
|
|
|
|
|
algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset)
|
2020-06-28 01:23:32 +02:00
|
|
|
.and_then(|it| it.path()?.segment()?.name_ref())
|
2020-07-01 11:19:40 +02:00
|
|
|
.map_or(false, |it| it.text() == "expect" || it.text() == "expect_file")
|
2020-06-27 17:53:50 +02:00
|
|
|
}
|
|
|
|
|
None => false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut res = Vec::new();
|
2020-06-25 00:41:08 +02:00
|
|
|
for runnable in snap.analysis.runnables(file_id)? {
|
2018-08-29 18:03:14 +03:00
|
|
|
if let Some(offset) = offset {
|
2020-07-17 12:42:48 +02:00
|
|
|
if !runnable.nav.full_range.contains_inclusive(offset) {
|
2018-08-29 18:03:14 +03:00
|
|
|
continue;
|
|
|
|
|
}
|
2020-06-06 14:30:29 +03:00
|
|
|
}
|
2020-06-06 22:11:17 +03:00
|
|
|
if should_skip_target(&runnable, cargo_spec.as_ref()) {
|
2020-06-06 12:00:46 +03:00
|
|
|
continue;
|
2018-08-29 18:03:14 +03:00
|
|
|
}
|
2021-02-27 19:07:14 +03:00
|
|
|
let mut runnable = to_proto::runnable(&snap, runnable)?;
|
2020-06-27 17:53:50 +02:00
|
|
|
if expect_test {
|
|
|
|
|
runnable.label = format!("{} + expect", runnable.label);
|
|
|
|
|
runnable.args.expect_test = Some(true);
|
|
|
|
|
}
|
|
|
|
|
res.push(runnable);
|
2018-08-29 18:03:14 +03:00
|
|
|
}
|
2020-05-07 16:52:14 +02:00
|
|
|
|
2020-07-29 15:35:27 +03:00
|
|
|
// Add `cargo check` and `cargo test` for all targets of the whole package
|
2021-01-06 13:54:28 +03:00
|
|
|
let config = snap.config.runnables();
|
2020-05-07 16:52:14 +02:00
|
|
|
match cargo_spec {
|
2018-10-25 11:01:37 +03:00
|
|
|
Some(spec) => {
|
2020-04-26 10:40:13 +02:00
|
|
|
for &cmd in ["check", "test"].iter() {
|
2020-05-10 19:25:37 +02:00
|
|
|
res.push(lsp_ext::Runnable {
|
2020-07-29 15:35:27 +03:00
|
|
|
label: format!("cargo {} -p {} --all-targets", cmd, spec.package),
|
2020-06-02 17:22:23 +02:00
|
|
|
location: None,
|
2020-05-31 05:13:08 +03:00
|
|
|
kind: lsp_ext::RunnableKind::Cargo,
|
2020-06-02 17:22:23 +02:00
|
|
|
args: lsp_ext::CargoRunnable {
|
2020-06-24 15:52:07 +02:00
|
|
|
workspace_root: Some(spec.workspace_root.clone().into()),
|
2020-09-05 16:20:33 +03:00
|
|
|
override_cargo: config.override_cargo.clone(),
|
2020-06-02 17:22:23 +02:00
|
|
|
cargo_args: vec![
|
|
|
|
|
cmd.to_string(),
|
|
|
|
|
"--package".to_string(),
|
|
|
|
|
spec.package.clone(),
|
2020-07-29 15:35:27 +03:00
|
|
|
"--all-targets".to_string(),
|
2020-06-02 17:22:23 +02:00
|
|
|
],
|
2020-09-05 12:52:27 +03:00
|
|
|
cargo_extra_args: config.cargo_extra_args.clone(),
|
2020-06-02 17:22:23 +02:00
|
|
|
executable_args: Vec::new(),
|
2020-06-27 17:53:50 +02:00
|
|
|
expect_test: None,
|
2020-06-02 17:22:23 +02:00
|
|
|
},
|
2020-04-26 10:40:13 +02:00
|
|
|
})
|
|
|
|
|
}
|
2018-10-25 11:01:37 +03:00
|
|
|
}
|
|
|
|
|
None => {
|
2021-05-23 23:10:05 +03:00
|
|
|
if !snap.config.linked_projects().is_empty()
|
|
|
|
|
|| !snap
|
|
|
|
|
.config
|
|
|
|
|
.discovered_projects
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|projects| projects.is_empty())
|
|
|
|
|
.unwrap_or(true)
|
|
|
|
|
{
|
|
|
|
|
res.push(lsp_ext::Runnable {
|
|
|
|
|
label: "cargo check --workspace".to_string(),
|
|
|
|
|
location: None,
|
|
|
|
|
kind: lsp_ext::RunnableKind::Cargo,
|
|
|
|
|
args: lsp_ext::CargoRunnable {
|
|
|
|
|
workspace_root: None,
|
|
|
|
|
override_cargo: config.override_cargo,
|
|
|
|
|
cargo_args: vec!["check".to_string(), "--workspace".to_string()],
|
|
|
|
|
cargo_extra_args: config.cargo_extra_args,
|
|
|
|
|
executable_args: Vec::new(),
|
|
|
|
|
expect_test: None,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
2018-10-25 11:01:37 +03:00
|
|
|
}
|
2018-10-25 10:56:12 +03:00
|
|
|
}
|
2019-02-06 15:50:26 -05:00
|
|
|
Ok(res)
|
2018-08-29 18:03:14 +03:00
|
|
|
}
|
|
|
|
|
|
2021-02-27 20:04:43 +03:00
|
|
|
pub(crate) fn handle_related_tests(
|
|
|
|
|
snap: GlobalStateSnapshot,
|
2021-03-11 17:39:41 +03:00
|
|
|
params: lsp_types::TextDocumentPositionParams,
|
2021-02-27 20:04:43 +03:00
|
|
|
) -> Result<Vec<lsp_ext::TestInfo>> {
|
|
|
|
|
let _p = profile::span("handle_related_tests");
|
2021-03-11 17:39:41 +03:00
|
|
|
let position = from_proto::file_position(&snap, params)?;
|
2021-02-27 20:04:43 +03:00
|
|
|
|
|
|
|
|
let tests = snap.analysis.related_tests(position, None)?;
|
|
|
|
|
let mut res = Vec::new();
|
|
|
|
|
for it in tests {
|
|
|
|
|
if let Ok(runnable) = to_proto::runnable(&snap, it) {
|
|
|
|
|
res.push(lsp_ext::TestInfo { runnable })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(res)
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_completion(
|
2020-12-04 10:02:22 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::CompletionParams,
|
|
|
|
|
) -> Result<Option<lsp_types::CompletionResponse>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_completion");
|
2020-12-04 16:03:22 +02:00
|
|
|
let text_document_position = params.text_document_position.clone();
|
2020-06-03 11:16:08 +02:00
|
|
|
let position = from_proto::file_position(&snap, params.text_document_position)?;
|
2018-11-21 19:04:33 +03:00
|
|
|
let completion_triggered_after_single_colon = {
|
|
|
|
|
let mut res = false;
|
|
|
|
|
if let Some(ctx) = params.context {
|
2018-11-29 15:30:49 -05:00
|
|
|
if ctx.trigger_character.unwrap_or_default() == ":" {
|
2020-06-25 00:41:08 +02:00
|
|
|
let source_file = snap.analysis.parse(position.file_id)?;
|
2018-11-21 19:04:33 +03:00
|
|
|
let syntax = source_file.syntax();
|
|
|
|
|
let text = syntax.text();
|
|
|
|
|
if let Some(next_char) = text.char_at(position.offset) {
|
2020-04-24 23:40:41 +02:00
|
|
|
let diff = TextSize::of(next_char) + TextSize::of(':');
|
2018-11-21 19:04:33 +03:00
|
|
|
let prev_char = position.offset - diff;
|
|
|
|
|
if text.char_at(prev_char) != Some(':') {
|
|
|
|
|
res = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
res
|
|
|
|
|
};
|
|
|
|
|
if completion_triggered_after_single_colon {
|
|
|
|
|
return Ok(None);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-06 13:54:28 +03:00
|
|
|
let completion_config = &snap.config.completion();
|
|
|
|
|
let items = match snap.analysis.completions(completion_config, position)? {
|
2018-08-26 12:51:45 +03:00
|
|
|
None => return Ok(None),
|
|
|
|
|
Some(items) => items,
|
|
|
|
|
};
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(position.file_id)?;
|
2020-11-30 22:28:19 +02:00
|
|
|
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 19:16:33 +03:00
|
|
|
let items =
|
|
|
|
|
to_proto::completion_items(&snap.config, &line_index, text_document_position, items);
|
2018-08-26 12:51:45 +03:00
|
|
|
|
2020-11-17 13:19:56 +02:00
|
|
|
let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
|
2020-11-16 17:27:36 +01:00
|
|
|
Ok(Some(completion_list.into()))
|
2018-08-26 12:51:45 +03:00
|
|
|
}
|
|
|
|
|
|
2020-12-03 11:13:28 +02:00
|
|
|
pub(crate) fn handle_completion_resolve(
|
2020-12-03 15:58:18 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-12-04 16:03:22 +02:00
|
|
|
mut original_completion: CompletionItem,
|
|
|
|
|
) -> Result<CompletionItem> {
|
2020-12-06 23:58:15 +02:00
|
|
|
let _p = profile::span("handle_completion_resolve");
|
2020-11-30 22:28:19 +02:00
|
|
|
|
2020-12-04 22:55:36 +02:00
|
|
|
if !all_edits_are_disjoint(&original_completion, &[]) {
|
|
|
|
|
return Err(LspError::new(
|
|
|
|
|
ErrorCode::InvalidParams as i32,
|
2020-12-06 23:58:15 +02:00
|
|
|
"Received a completion with overlapping edits, this is not LSP-compliant".into(),
|
2020-12-04 22:55:36 +02:00
|
|
|
)
|
|
|
|
|
.into());
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-04 14:08:33 +03:00
|
|
|
let data = match original_completion.data.take() {
|
|
|
|
|
Some(it) => it,
|
2020-12-02 01:02:15 +02:00
|
|
|
None => return Ok(original_completion),
|
|
|
|
|
};
|
|
|
|
|
|
2021-07-04 14:08:33 +03:00
|
|
|
let resolve_data: lsp_ext::CompletionResolveData = serde_json::from_value(data)?;
|
|
|
|
|
|
2020-12-04 16:03:22 +02:00
|
|
|
let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2020-12-04 16:03:22 +02:00
|
|
|
let offset = from_proto::offset(&line_index, resolve_data.position.position);
|
|
|
|
|
|
2020-12-06 23:58:15 +02:00
|
|
|
let additional_edits = snap
|
2020-12-04 16:03:22 +02:00
|
|
|
.analysis
|
|
|
|
|
.resolve_completion_edits(
|
2021-01-06 20:23:53 +03:00
|
|
|
&snap.config.completion(),
|
2020-12-04 16:03:22 +02:00
|
|
|
FilePosition { file_id, offset },
|
|
|
|
|
&resolve_data.full_import_path,
|
2020-12-29 14:35:49 +02:00
|
|
|
resolve_data.imported_name,
|
2020-12-04 16:03:22 +02:00
|
|
|
)?
|
|
|
|
|
.into_iter()
|
2021-02-13 00:44:28 +03:00
|
|
|
.flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
|
2020-12-04 16:03:22 +02:00
|
|
|
.collect_vec();
|
|
|
|
|
|
2020-12-04 22:55:36 +02:00
|
|
|
if !all_edits_are_disjoint(&original_completion, &additional_edits) {
|
|
|
|
|
return Err(LspError::new(
|
|
|
|
|
ErrorCode::InternalError as i32,
|
2020-12-06 23:58:15 +02:00
|
|
|
"Import edit overlaps with the original completion edits, this is not LSP-compliant"
|
|
|
|
|
.into(),
|
2020-12-04 22:55:36 +02:00
|
|
|
)
|
|
|
|
|
.into());
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-04 16:03:22 +02:00
|
|
|
if let Some(original_additional_edits) = original_completion.additional_text_edits.as_mut() {
|
2020-12-06 23:58:15 +02:00
|
|
|
original_additional_edits.extend(additional_edits.into_iter())
|
2020-12-04 16:03:22 +02:00
|
|
|
} else {
|
|
|
|
|
original_completion.additional_text_edits = Some(additional_edits);
|
|
|
|
|
}
|
2020-11-28 16:26:30 +02:00
|
|
|
|
2020-12-02 01:02:15 +02:00
|
|
|
Ok(original_completion)
|
2020-12-01 15:25:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_folding_range(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2018-09-23 11:13:27 -04:00
|
|
|
params: FoldingRangeParams,
|
|
|
|
|
) -> Result<Option<Vec<FoldingRange>>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_folding_range");
|
2020-06-03 11:16:08 +02:00
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
2020-06-25 00:41:08 +02:00
|
|
|
let folds = snap.analysis.folding_ranges(file_id)?;
|
|
|
|
|
let text = snap.analysis.file_text(file_id)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2021-01-05 16:57:05 +03:00
|
|
|
let line_folding_only = snap.config.line_folding_only();
|
2020-05-10 13:55:24 +02:00
|
|
|
let res = folds
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it))
|
|
|
|
|
.collect();
|
|
|
|
|
Ok(Some(res))
|
2018-09-23 11:13:27 -04:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_signature_help(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::SignatureHelpParams,
|
|
|
|
|
) -> Result<Option<lsp_types::SignatureHelp>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_signature_help");
|
2020-06-03 11:16:08 +02:00
|
|
|
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
|
2020-06-25 00:41:08 +02:00
|
|
|
let call_info = match snap.analysis.call_info(position)? {
|
2020-05-10 13:55:24 +02:00
|
|
|
Some(it) => it,
|
2020-07-16 13:00:56 +02:00
|
|
|
None => return Ok(None),
|
2020-05-10 13:55:24 +02:00
|
|
|
};
|
2021-01-06 13:54:28 +03:00
|
|
|
let concise = !snap.config.call_info_full();
|
2021-01-05 16:57:05 +03:00
|
|
|
let res =
|
|
|
|
|
to_proto::signature_help(call_info, concise, snap.config.signature_help_label_offsets());
|
2020-07-16 13:00:56 +02:00
|
|
|
Ok(Some(res))
|
2018-10-09 10:08:17 -04:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_hover(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2021-07-26 12:14:14 -04:00
|
|
|
params: lsp_ext::HoverParams,
|
2020-06-03 14:15:54 +03:00
|
|
|
) -> Result<Option<lsp_ext::Hover>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_hover");
|
2021-07-27 17:50:26 -04:00
|
|
|
let range = match params.position {
|
|
|
|
|
PositionOrRange::Position(position) => Range::new(position, position),
|
|
|
|
|
PositionOrRange::Range(range) => range,
|
2021-07-26 12:14:14 -04:00
|
|
|
};
|
2021-07-22 22:08:28 -04:00
|
|
|
|
2021-07-27 17:50:26 -04:00
|
|
|
let file_range = from_proto::file_range(&snap, params.text_document, range)?;
|
|
|
|
|
let info = match snap.analysis.hover(&snap.config.hover(), file_range)? {
|
2021-06-14 15:25:10 +02:00
|
|
|
None => return Ok(None),
|
|
|
|
|
Some(info) => info,
|
|
|
|
|
};
|
2021-07-22 22:08:28 -04:00
|
|
|
|
2021-07-27 18:21:47 -04:00
|
|
|
let line_index = snap.file_line_index(file_range.file_id)?;
|
2020-05-10 13:55:24 +02:00
|
|
|
let range = to_proto::range(&line_index, info.range);
|
2020-06-03 16:39:32 +03:00
|
|
|
let hover = lsp_ext::Hover {
|
|
|
|
|
hover: lsp_types::Hover {
|
2020-07-08 22:37:35 +02:00
|
|
|
contents: HoverContents::Markup(to_proto::markup_content(info.info.markup)),
|
2020-06-03 16:39:32 +03:00
|
|
|
range: Some(range),
|
|
|
|
|
},
|
2021-07-28 12:25:14 +03:00
|
|
|
actions: if snap.config.hover_actions().none() {
|
|
|
|
|
Vec::new()
|
|
|
|
|
} else {
|
|
|
|
|
prepare_hover_actions(&snap, &info.info.actions)
|
|
|
|
|
},
|
2019-01-04 19:06:46 +09:00
|
|
|
};
|
2020-06-03 16:39:32 +03:00
|
|
|
|
|
|
|
|
Ok(Some(hover))
|
2018-11-05 16:37:27 -05:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_prepare_rename(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::TextDocumentPositionParams,
|
2018-10-19 15:25:10 -04:00
|
|
|
) -> Result<Option<PrepareRenameResponse>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_prepare_rename");
|
2020-06-03 11:16:08 +02:00
|
|
|
let position = from_proto::file_position(&snap, params)?;
|
2018-10-19 15:25:10 -04:00
|
|
|
|
2021-01-13 14:09:51 +03:00
|
|
|
let change = snap.analysis.prepare_rename(position)?.map_err(to_proto::rename_error)?;
|
|
|
|
|
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(position.file_id)?;
|
2020-08-24 16:37:45 +02:00
|
|
|
let range = to_proto::range(&line_index, change.range);
|
2019-09-05 21:36:40 +03:00
|
|
|
Ok(Some(PrepareRenameResponse::Range(range)))
|
2018-10-19 15:25:10 -04:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_rename(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
|
|
|
|
params: RenameParams,
|
|
|
|
|
) -> Result<Option<WorkspaceEdit>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_rename");
|
2020-06-03 11:16:08 +02:00
|
|
|
let position = from_proto::file_position(&snap, params.text_document_position)?;
|
2018-10-18 17:56:22 -04:00
|
|
|
|
2021-03-10 15:49:01 +01:00
|
|
|
let mut change =
|
2021-01-13 14:09:51 +03:00
|
|
|
snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?;
|
2021-03-10 15:49:01 +01:00
|
|
|
|
|
|
|
|
// this is kind of a hack to prevent double edits from happening when moving files
|
|
|
|
|
// When a module gets renamed by renaming the mod declaration this causes the file to move
|
|
|
|
|
// which in turn will trigger a WillRenameFiles request to the server for which we reply with a
|
|
|
|
|
// a second identical set of renames, the client will then apply both edits causing incorrect edits
|
|
|
|
|
// with this we only emit source_file_edits in the WillRenameFiles response which will do the rename instead
|
|
|
|
|
// See https://github.com/microsoft/vscode-languageserver-node/issues/752 for more info
|
|
|
|
|
if !change.file_system_edits.is_empty() && snap.config.will_rename() {
|
|
|
|
|
change.source_file_edits.clear();
|
|
|
|
|
}
|
2021-01-18 20:25:40 +01:00
|
|
|
let workspace_edit = to_proto::workspace_edit(&snap, change)?;
|
2020-05-18 00:11:40 +02:00
|
|
|
Ok(Some(workspace_edit))
|
2018-10-18 17:56:22 -04:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_references(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::ReferenceParams,
|
2018-10-18 13:40:12 -04:00
|
|
|
) -> Result<Option<Vec<Location>>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_references");
|
2020-06-03 11:16:08 +02:00
|
|
|
let position = from_proto::file_position(&snap, params.text_document_position)?;
|
2019-10-12 18:47:17 +03:00
|
|
|
|
2020-06-25 00:41:08 +02:00
|
|
|
let refs = match snap.analysis.find_all_refs(position, None)? {
|
2019-02-17 13:38:32 +02:00
|
|
|
None => return Ok(None),
|
|
|
|
|
Some(refs) => refs,
|
|
|
|
|
};
|
2018-10-18 13:40:12 -04:00
|
|
|
|
2021-02-07 18:38:12 +01:00
|
|
|
let decl = if params.context.include_declaration {
|
2021-03-15 09:32:06 +01:00
|
|
|
refs.declaration.map(|decl| FileRange {
|
|
|
|
|
file_id: decl.nav.file_id,
|
|
|
|
|
range: decl.nav.focus_or_full_range(),
|
2021-02-07 18:38:12 +01:00
|
|
|
})
|
2019-02-17 13:38:32 +02:00
|
|
|
} else {
|
2021-02-07 18:38:12 +01:00
|
|
|
None
|
2019-02-17 13:38:32 +02:00
|
|
|
};
|
2021-02-07 18:38:12 +01:00
|
|
|
let locations = refs
|
|
|
|
|
.references
|
|
|
|
|
.into_iter()
|
|
|
|
|
.flat_map(|(file_id, refs)| {
|
|
|
|
|
refs.into_iter().map(move |(range, _)| FileRange { file_id, range })
|
|
|
|
|
})
|
|
|
|
|
.chain(decl)
|
|
|
|
|
.filter_map(|frange| to_proto::location(&snap, frange).ok())
|
|
|
|
|
.collect();
|
2019-02-17 13:38:32 +02:00
|
|
|
|
|
|
|
|
Ok(Some(locations))
|
2018-10-18 13:40:12 -04:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_formatting(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2018-12-29 20:09:42 +01:00
|
|
|
params: DocumentFormattingParams,
|
2020-05-21 19:50:23 +02:00
|
|
|
) -> Result<Option<Vec<lsp_types::TextEdit>>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_formatting");
|
2019-01-26 20:16:15 +00:00
|
|
|
|
2021-05-04 17:13:51 -04:00
|
|
|
run_rustfmt(&snap, params.text_document, None)
|
|
|
|
|
}
|
2018-12-29 20:09:42 +01:00
|
|
|
|
2021-05-04 17:13:51 -04:00
|
|
|
pub(crate) fn handle_range_formatting(
|
|
|
|
|
snap: GlobalStateSnapshot,
|
|
|
|
|
params: lsp_types::DocumentRangeFormattingParams,
|
|
|
|
|
) -> Result<Option<Vec<lsp_types::TextEdit>>> {
|
|
|
|
|
let _p = profile::span("handle_range_formatting");
|
2021-01-05 22:29:53 +01:00
|
|
|
|
2021-05-04 17:13:51 -04:00
|
|
|
run_rustfmt(&snap, params.text_document, Some(params.range))
|
2018-12-29 20:09:42 +01:00
|
|
|
}
|
|
|
|
|
|
2020-12-23 12:58:49 +03:00
|
|
|
pub(crate) fn handle_code_action(
|
2020-12-23 13:36:57 +03:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-12-23 12:58:49 +03:00
|
|
|
params: lsp_types::CodeActionParams,
|
|
|
|
|
) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
|
|
|
|
|
let _p = profile::span("handle_code_action");
|
2021-04-12 23:08:56 +03:00
|
|
|
|
2021-01-05 16:57:05 +03:00
|
|
|
if !snap.config.code_action_literals() {
|
2021-04-12 23:08:56 +03:00
|
|
|
// We intentionally don't support command-based actions, as those either
|
|
|
|
|
// require either custom client-code or server-initiated edits. Server
|
|
|
|
|
// initiated edits break causality, so we avoid those.
|
2020-12-23 12:58:49 +03:00
|
|
|
return Ok(None);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-12 23:08:56 +03:00
|
|
|
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)?;
|
2020-12-23 12:58:49 +03:00
|
|
|
|
2021-01-06 13:54:28 +03:00
|
|
|
let mut assists_config = snap.config.assist();
|
|
|
|
|
assists_config.allowed = params
|
|
|
|
|
.context
|
|
|
|
|
.only
|
2021-04-12 23:08:56 +03:00
|
|
|
.clone()
|
2021-01-06 13:54:28 +03:00
|
|
|
.map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
|
2020-12-23 12:58:49 +03:00
|
|
|
|
|
|
|
|
let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
|
|
|
|
|
|
2021-04-12 23:08:56 +03:00
|
|
|
let code_action_resolve_cap = snap.config.code_action_resolve();
|
2021-05-03 17:08:09 +03:00
|
|
|
let resolve = if code_action_resolve_cap {
|
|
|
|
|
AssistResolveStrategy::None
|
|
|
|
|
} else {
|
|
|
|
|
AssistResolveStrategy::All
|
|
|
|
|
};
|
2021-04-13 11:27:00 +03:00
|
|
|
let assists = snap.analysis.assists_with_fixes(
|
|
|
|
|
&assists_config,
|
|
|
|
|
&snap.config.diagnostics(),
|
2021-05-03 17:08:09 +03:00
|
|
|
resolve,
|
2021-04-13 11:27:00 +03:00
|
|
|
frange,
|
|
|
|
|
)?;
|
2021-04-12 23:08:56 +03:00
|
|
|
for (index, assist) in assists.into_iter().enumerate() {
|
|
|
|
|
let resolve_data =
|
|
|
|
|
if code_action_resolve_cap { Some((index, params.clone())) } else { None };
|
|
|
|
|
let code_action = to_proto::code_action(&snap, assist, resolve_data)?;
|
|
|
|
|
res.push(code_action)
|
2019-02-24 13:53:35 +03:00
|
|
|
}
|
|
|
|
|
|
2021-04-12 23:08:56 +03:00
|
|
|
// Fixes from `cargo check`.
|
2020-12-23 13:36:57 +03:00
|
|
|
for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() {
|
2021-04-12 23:08:56 +03:00
|
|
|
// FIXME: this mapping is awkward and shouldn't exist. Refactor
|
|
|
|
|
// `snap.check_fixes` to not convert to LSP prematurely.
|
2020-05-10 13:55:24 +02:00
|
|
|
let fix_range = from_proto::text_range(&line_index, fix.range);
|
2020-12-23 13:36:57 +03:00
|
|
|
if fix_range.intersect(frange.range).is_some() {
|
2021-04-12 23:08:56 +03:00
|
|
|
res.push(fix.action.clone());
|
2019-12-25 12:21:38 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-04-12 23:08:56 +03:00
|
|
|
|
|
|
|
|
Ok(Some(res))
|
2020-06-02 22:21:48 +02:00
|
|
|
}
|
2019-12-25 12:21:38 +01:00
|
|
|
|
2020-11-10 18:20:01 +01:00
|
|
|
pub(crate) fn handle_code_action_resolve(
|
2021-01-06 13:54:28 +03:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-11-10 18:20:01 +01:00
|
|
|
mut code_action: lsp_ext::CodeAction,
|
|
|
|
|
) -> Result<lsp_ext::CodeAction> {
|
|
|
|
|
let _p = profile::span("handle_code_action_resolve");
|
|
|
|
|
let params = match code_action.data.take() {
|
|
|
|
|
Some(it) => it,
|
|
|
|
|
None => Err("can't resolve code action without data")?,
|
|
|
|
|
};
|
|
|
|
|
|
2020-06-03 19:26:01 +02:00
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.code_action_params.text_document.uri)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2020-06-02 22:21:48 +02:00
|
|
|
let range = from_proto::text_range(&line_index, params.code_action_params.range);
|
|
|
|
|
let frange = FileRange { file_id, range };
|
2020-07-15 09:45:30 -04:00
|
|
|
|
2021-01-06 13:54:28 +03:00
|
|
|
let mut assists_config = snap.config.assist();
|
|
|
|
|
assists_config.allowed = params
|
2020-07-13 17:41:47 -04:00
|
|
|
.code_action_params
|
|
|
|
|
.context
|
|
|
|
|
.only
|
|
|
|
|
.map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
|
2020-06-02 22:21:48 +02:00
|
|
|
|
2021-05-03 22:58:53 +03:00
|
|
|
let (assist_index, assist_resolve) = match parse_action_id(¶ms.id) {
|
|
|
|
|
Ok(parsed_data) => parsed_data,
|
2021-05-03 18:03:28 +03:00
|
|
|
Err(e) => {
|
|
|
|
|
return Err(LspError::new(
|
|
|
|
|
ErrorCode::InvalidParams as i32,
|
2021-05-03 22:58:53 +03:00
|
|
|
format!("Failed to parse action id string '{}': {}", params.id, e),
|
2021-05-03 18:03:28 +03:00
|
|
|
)
|
|
|
|
|
.into())
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-03 22:58:53 +03:00
|
|
|
let expected_assist_id = assist_resolve.assist_id.clone();
|
|
|
|
|
let expected_kind = assist_resolve.assist_kind;
|
|
|
|
|
|
2021-04-13 11:27:00 +03:00
|
|
|
let assists = snap.analysis.assists_with_fixes(
|
|
|
|
|
&assists_config,
|
|
|
|
|
&snap.config.diagnostics(),
|
2021-05-03 22:58:53 +03:00
|
|
|
AssistResolveStrategy::Single(assist_resolve),
|
2021-04-13 11:27:00 +03:00
|
|
|
frange,
|
|
|
|
|
)?;
|
|
|
|
|
|
2021-05-03 22:58:53 +03:00
|
|
|
let assist = match assists.get(assist_index) {
|
2021-05-03 19:35:44 +03:00
|
|
|
Some(assist) => assist,
|
|
|
|
|
None => return Err(LspError::new(
|
|
|
|
|
ErrorCode::InvalidParams as i32,
|
|
|
|
|
format!(
|
2021-05-03 22:58:53 +03:00
|
|
|
"Failed to find the assist for index {} provided by the resolve request. Resolve request assist id: {}",
|
|
|
|
|
assist_index, params.id,
|
2021-05-03 19:35:44 +03:00
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.into())
|
|
|
|
|
};
|
2021-05-03 22:58:53 +03:00
|
|
|
if assist.id.0 != expected_assist_id || assist.id.1 != expected_kind {
|
2021-05-03 18:18:45 +03:00
|
|
|
return Err(LspError::new(
|
|
|
|
|
ErrorCode::InvalidParams as i32,
|
|
|
|
|
format!(
|
2021-05-03 22:58:53 +03:00
|
|
|
"Mismatching assist at index {} for the resolve parameters given. Resolve request assist id: {}, actual id: {:?}.",
|
|
|
|
|
assist_index, params.id, assist.id
|
2021-05-03 18:18:45 +03:00
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.into());
|
|
|
|
|
}
|
2021-04-12 23:08:56 +03:00
|
|
|
let edit = to_proto::code_action(&snap, assist.clone(), None)?.edit;
|
2020-11-10 18:20:01 +01:00
|
|
|
code_action.edit = edit;
|
|
|
|
|
Ok(code_action)
|
2020-06-02 22:21:48 +02:00
|
|
|
}
|
|
|
|
|
|
2021-05-03 22:58:53 +03:00
|
|
|
fn parse_action_id(action_id: &str) -> Result<(usize, SingleResolve), String> {
|
|
|
|
|
let id_parts = action_id.split(':').collect_vec();
|
|
|
|
|
match id_parts.as_slice() {
|
|
|
|
|
&[assist_id_string, assist_kind_string, index_string] => {
|
|
|
|
|
let assist_kind: AssistKind = assist_kind_string.parse()?;
|
|
|
|
|
let index: usize = match index_string.parse() {
|
|
|
|
|
Ok(index) => index,
|
|
|
|
|
Err(e) => return Err(format!("Incorrect index string: {}", e)),
|
|
|
|
|
};
|
|
|
|
|
Ok((index, SingleResolve { assist_id: assist_id_string.to_string(), assist_kind }))
|
|
|
|
|
}
|
|
|
|
|
_ => Err("Action id contains incorrect number of segments".to_string()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_code_lens(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::CodeLensParams,
|
2019-01-11 15:16:55 -05:00
|
|
|
) -> Result<Option<Vec<CodeLens>>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_code_lens");
|
2019-01-11 15:16:55 -05:00
|
|
|
|
2021-01-06 13:54:28 +03:00
|
|
|
let lens_config = snap.config.lens();
|
|
|
|
|
if lens_config.none() {
|
2020-05-17 19:51:44 +03:00
|
|
|
// early return before any db query!
|
2021-02-13 13:07:47 +02:00
|
|
|
return Ok(Some(Vec::default()));
|
2020-05-17 19:51:44 +03:00
|
|
|
}
|
|
|
|
|
|
2020-06-03 11:16:08 +02:00
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
2021-02-13 13:07:47 +02:00
|
|
|
let cargo_target_spec = CargoTargetSpec::for_file(&snap, file_id)?;
|
2021-01-23 16:56:20 +03:00
|
|
|
|
2021-07-11 13:34:22 +03:00
|
|
|
let annotations = snap.analysis.annotations(
|
|
|
|
|
&AnnotationConfig {
|
|
|
|
|
binary_target: cargo_target_spec
|
|
|
|
|
.map(|spec| {
|
|
|
|
|
matches!(
|
|
|
|
|
spec.target_kind,
|
|
|
|
|
TargetKind::Bin | TargetKind::Example | TargetKind::Test
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(false),
|
|
|
|
|
annotate_runnables: lens_config.runnable(),
|
|
|
|
|
annotate_impls: lens_config.implementations,
|
|
|
|
|
annotate_references: lens_config.refs,
|
|
|
|
|
annotate_method_references: lens_config.method_refs,
|
|
|
|
|
},
|
|
|
|
|
file_id,
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
let mut res = Vec::new();
|
|
|
|
|
for a in annotations {
|
|
|
|
|
to_proto::code_lens(&mut res, &snap, a)?;
|
|
|
|
|
}
|
2020-09-01 16:33:02 +03:00
|
|
|
|
2021-07-11 13:34:22 +03:00
|
|
|
Ok(Some(res))
|
2019-01-11 15:16:55 -05:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_code_lens_resolve(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
|
|
|
|
code_lens: CodeLens,
|
|
|
|
|
) -> Result<CodeLens> {
|
2021-07-11 13:34:22 +03:00
|
|
|
let annotation = from_proto::annotation(&snap, code_lens.clone())?;
|
|
|
|
|
let annotation = snap.analysis.resolve_annotation(annotation)?;
|
|
|
|
|
|
|
|
|
|
let mut acc = Vec::new();
|
|
|
|
|
to_proto::code_lens(&mut acc, &snap, annotation)?;
|
2020-09-02 16:03:05 +03:00
|
|
|
|
2021-07-11 13:34:22 +03:00
|
|
|
let res = match acc.pop() {
|
|
|
|
|
Some(it) if acc.is_empty() => it,
|
|
|
|
|
_ => {
|
|
|
|
|
never!();
|
|
|
|
|
code_lens
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(res)
|
2019-02-01 08:44:23 -05:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_document_highlight(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:24:02 +02:00
|
|
|
params: lsp_types::DocumentHighlightParams,
|
2021-06-22 19:39:59 +02:00
|
|
|
) -> Result<Option<Vec<lsp_types::DocumentHighlight>>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_document_highlight");
|
2020-06-03 11:16:08 +02:00
|
|
|
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(position.file_id)?;
|
2018-12-31 11:08:44 +00:00
|
|
|
|
2021-07-21 19:44:16 -06:00
|
|
|
let refs = match snap.analysis.highlight_related(snap.config.highlight_related(), position)? {
|
2019-02-17 13:38:32 +02:00
|
|
|
None => return Ok(None),
|
|
|
|
|
Some(refs) => refs,
|
|
|
|
|
};
|
2021-06-22 19:39:59 +02:00
|
|
|
let res = refs
|
|
|
|
|
.into_iter()
|
2021-06-24 01:32:56 +02:00
|
|
|
.map(|ide::HighlightedRange { range, access }| lsp_types::DocumentHighlight {
|
2021-06-22 19:39:59 +02:00
|
|
|
range: to_proto::range(&line_index, range),
|
|
|
|
|
kind: access.map(to_proto::document_highlight_kind),
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
2020-05-10 13:55:24 +02:00
|
|
|
Ok(Some(res))
|
2018-12-31 11:08:44 +00:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_ssr(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-05-10 19:25:37 +02:00
|
|
|
params: lsp_ext::SsrParams,
|
2020-05-22 00:28:49 +02:00
|
|
|
) -> Result<lsp_types::WorkspaceEdit> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_ssr");
|
2020-07-29 11:44:01 +10:00
|
|
|
let selections = params
|
|
|
|
|
.selections
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|range| from_proto::file_range(&snap, params.position.text_document.clone(), *range))
|
|
|
|
|
.collect::<Result<Vec<_>, _>>()?;
|
2020-07-22 15:00:28 +10:00
|
|
|
let position = from_proto::file_position(&snap, params.position)?;
|
2020-07-29 11:44:01 +10:00
|
|
|
let source_change = snap.analysis.structural_search_replace(
|
|
|
|
|
¶ms.query,
|
|
|
|
|
params.parse_only,
|
|
|
|
|
position,
|
|
|
|
|
selections,
|
|
|
|
|
)??;
|
2020-06-03 11:16:08 +02:00
|
|
|
to_proto::workspace_edit(&snap, source_change)
|
2020-02-10 22:45:38 +00:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn publish_diagnostics(
|
|
|
|
|
snap: &GlobalStateSnapshot,
|
|
|
|
|
file_id: FileId,
|
2020-06-25 22:45:35 +02:00
|
|
|
) -> Result<Vec<Diagnostic>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("publish_diagnostics");
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2020-08-18 16:03:15 +02:00
|
|
|
|
2020-06-03 11:16:08 +02:00
|
|
|
let diagnostics: Vec<Diagnostic> = snap
|
2020-06-25 00:41:08 +02:00
|
|
|
.analysis
|
2021-05-03 17:08:09 +03:00
|
|
|
.diagnostics(&snap.config.diagnostics(), AssistResolveStrategy::None, file_id)?
|
2018-08-10 23:30:11 +03:00
|
|
|
.into_iter()
|
2018-08-29 18:03:14 +03:00
|
|
|
.map(|d| Diagnostic {
|
2020-05-10 13:55:24 +02:00
|
|
|
range: to_proto::range(&line_index, d.range),
|
|
|
|
|
severity: Some(to_proto::diagnostic_severity(d.severity)),
|
2021-06-13 22:17:05 +03:00
|
|
|
code: Some(NumberOrString::String(d.code.as_str().to_string())),
|
|
|
|
|
code_description: Some(lsp_types::CodeDescription {
|
|
|
|
|
href: lsp_types::Url::parse(&format!(
|
2020-11-18 08:54:52 +01:00
|
|
|
"https://rust-analyzer.github.io/manual.html#{}",
|
2021-06-13 22:17:05 +03:00
|
|
|
d.code.as_str()
|
2020-11-18 08:54:52 +01:00
|
|
|
))
|
2021-06-13 22:17:05 +03:00
|
|
|
.unwrap(),
|
2020-11-18 08:54:52 +01:00
|
|
|
}),
|
2018-09-18 17:40:33 -04:00
|
|
|
source: Some("rust-analyzer".to_string()),
|
2018-08-29 18:03:14 +03:00
|
|
|
message: d.message,
|
2018-08-10 23:30:11 +03:00
|
|
|
related_information: None,
|
2020-10-20 17:48:43 +02:00
|
|
|
tags: if d.unused { Some(vec![DiagnosticTag::Unnecessary]) } else { None },
|
2020-10-21 14:57:55 -04:00
|
|
|
data: None,
|
2018-10-15 17:44:23 -04:00
|
|
|
})
|
|
|
|
|
.collect();
|
2020-06-25 22:45:35 +02:00
|
|
|
Ok(diagnostics)
|
2018-08-10 23:30:11 +03:00
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_inlay_hints(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2019-07-22 21:52:47 +03:00
|
|
|
params: InlayHintsParams,
|
|
|
|
|
) -> Result<Vec<InlayHint>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_inlay_hints");
|
2020-06-03 11:16:08 +02:00
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2020-06-25 00:41:08 +02:00
|
|
|
Ok(snap
|
|
|
|
|
.analysis
|
2021-07-06 00:00:39 +03:00
|
|
|
.inlay_hints(&snap.config.inlay_hints(), file_id)?
|
2019-07-22 21:52:47 +03:00
|
|
|
.into_iter()
|
2020-08-28 21:55:24 +03:00
|
|
|
.map(|it| to_proto::inlay_hint(&line_index, it))
|
2019-07-22 21:52:47 +03:00
|
|
|
.collect())
|
|
|
|
|
}
|
2019-12-30 09:12:06 -05:00
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_call_hierarchy_prepare(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2019-12-30 09:12:06 -05:00
|
|
|
params: CallHierarchyPrepareParams,
|
|
|
|
|
) -> Result<Option<Vec<CallHierarchyItem>>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_call_hierarchy_prepare");
|
2020-06-03 11:16:08 +02:00
|
|
|
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
|
2019-12-30 09:12:06 -05:00
|
|
|
|
2020-06-25 00:41:08 +02:00
|
|
|
let nav_info = match snap.analysis.call_hierarchy(position)? {
|
2019-12-30 09:12:06 -05:00
|
|
|
None => return Ok(None),
|
|
|
|
|
Some(it) => it,
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-10 13:55:24 +02:00
|
|
|
let RangeInfo { range: _, info: navs } = nav_info;
|
2019-12-30 09:12:06 -05:00
|
|
|
let res = navs
|
|
|
|
|
.into_iter()
|
2020-12-18 21:10:13 +03:00
|
|
|
.filter(|it| it.kind == Some(SymbolKind::Function))
|
2020-06-03 11:16:08 +02:00
|
|
|
.map(|it| to_proto::call_hierarchy_item(&snap, it))
|
2020-05-10 13:55:24 +02:00
|
|
|
.collect::<Result<Vec<_>>>()?;
|
2019-12-30 09:12:06 -05:00
|
|
|
|
|
|
|
|
Ok(Some(res))
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_call_hierarchy_incoming(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2019-12-30 09:12:06 -05:00
|
|
|
params: CallHierarchyIncomingCallsParams,
|
|
|
|
|
) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_call_hierarchy_incoming");
|
2019-12-30 09:12:06 -05:00
|
|
|
let item = params.item;
|
|
|
|
|
|
|
|
|
|
let doc = TextDocumentIdentifier::new(item.uri);
|
2020-06-28 14:43:02 -04:00
|
|
|
let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
|
2019-12-30 09:12:06 -05:00
|
|
|
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
|
|
|
|
|
|
2020-06-25 00:41:08 +02:00
|
|
|
let call_items = match snap.analysis.incoming_calls(fpos)? {
|
2019-12-30 09:12:06 -05:00
|
|
|
None => return Ok(None),
|
|
|
|
|
Some(it) => it,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut res = vec![];
|
|
|
|
|
|
|
|
|
|
for call_item in call_items.into_iter() {
|
2020-07-17 12:42:48 +02:00
|
|
|
let file_id = call_item.target.file_id;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2020-06-03 11:16:08 +02:00
|
|
|
let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
|
2019-12-30 09:12:06 -05:00
|
|
|
res.push(CallHierarchyIncomingCall {
|
|
|
|
|
from: item,
|
2020-05-10 13:55:24 +02:00
|
|
|
from_ranges: call_item
|
|
|
|
|
.ranges
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|it| to_proto::range(&line_index, it))
|
|
|
|
|
.collect(),
|
2019-12-30 09:12:06 -05:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(Some(res))
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_call_hierarchy_outgoing(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2019-12-30 09:12:06 -05:00
|
|
|
params: CallHierarchyOutgoingCallsParams,
|
|
|
|
|
) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_call_hierarchy_outgoing");
|
2019-12-30 09:12:06 -05:00
|
|
|
let item = params.item;
|
|
|
|
|
|
|
|
|
|
let doc = TextDocumentIdentifier::new(item.uri);
|
2020-06-28 14:43:02 -04:00
|
|
|
let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
|
2019-12-30 09:12:06 -05:00
|
|
|
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
|
|
|
|
|
|
2020-06-25 00:41:08 +02:00
|
|
|
let call_items = match snap.analysis.outgoing_calls(fpos)? {
|
2019-12-30 09:12:06 -05:00
|
|
|
None => return Ok(None),
|
|
|
|
|
Some(it) => it,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut res = vec![];
|
|
|
|
|
|
|
|
|
|
for call_item in call_items.into_iter() {
|
2020-07-17 12:42:48 +02:00
|
|
|
let file_id = call_item.target.file_id;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2020-06-03 11:16:08 +02:00
|
|
|
let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
|
2019-12-30 09:12:06 -05:00
|
|
|
res.push(CallHierarchyOutgoingCall {
|
|
|
|
|
to: item,
|
2020-05-10 13:55:24 +02:00
|
|
|
from_ranges: call_item
|
|
|
|
|
.ranges
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|it| to_proto::range(&line_index, it))
|
|
|
|
|
.collect(),
|
2019-12-30 09:12:06 -05:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(Some(res))
|
|
|
|
|
}
|
2020-02-14 17:56:28 -05:00
|
|
|
|
2020-09-01 12:53:07 -04:00
|
|
|
pub(crate) fn handle_semantic_tokens_full(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-02-14 17:56:28 -05:00
|
|
|
params: SemanticTokensParams,
|
|
|
|
|
) -> Result<Option<SemanticTokensResult>> {
|
2020-09-01 12:53:07 -04:00
|
|
|
let _p = profile::span("handle_semantic_tokens_full");
|
2020-02-14 17:56:28 -05:00
|
|
|
|
2020-06-03 11:16:08 +02:00
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
2020-06-25 00:41:08 +02:00
|
|
|
let text = snap.analysis.file_text(file_id)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2020-02-14 17:56:28 -05:00
|
|
|
|
2020-06-25 00:41:08 +02:00
|
|
|
let highlights = snap.analysis.highlight(file_id)?;
|
2021-05-17 18:37:06 +03:00
|
|
|
let highlight_strings = snap.config.highlighting_strings();
|
2021-05-10 13:34:09 -07:00
|
|
|
let semantic_tokens =
|
2021-05-17 18:37:06 +03:00
|
|
|
to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings);
|
2020-07-24 17:55:17 -04:00
|
|
|
|
|
|
|
|
// Unconditionally cache the tokens
|
2020-08-05 21:35:35 -04:00
|
|
|
snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone());
|
2020-07-24 17:55:17 -04:00
|
|
|
|
|
|
|
|
Ok(Some(semantic_tokens.into()))
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-01 12:53:07 -04:00
|
|
|
pub(crate) fn handle_semantic_tokens_full_delta(
|
2020-07-24 17:55:17 -04:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-09-01 12:53:07 -04:00
|
|
|
params: SemanticTokensDeltaParams,
|
|
|
|
|
) -> Result<Option<SemanticTokensFullDeltaResult>> {
|
|
|
|
|
let _p = profile::span("handle_semantic_tokens_full_delta");
|
2020-07-24 17:55:17 -04:00
|
|
|
|
|
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
|
|
|
|
let text = snap.analysis.file_text(file_id)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2020-07-24 17:55:17 -04:00
|
|
|
|
|
|
|
|
let highlights = snap.analysis.highlight(file_id)?;
|
2021-05-17 18:37:06 +03:00
|
|
|
let highlight_strings = snap.config.highlighting_strings();
|
2021-05-10 13:34:09 -07:00
|
|
|
let semantic_tokens =
|
2021-05-17 18:37:06 +03:00
|
|
|
to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings);
|
2020-07-24 17:55:17 -04:00
|
|
|
|
2020-08-05 21:35:35 -04:00
|
|
|
let mut cache = snap.semantic_tokens_cache.lock();
|
2020-07-24 17:55:17 -04:00
|
|
|
let cached_tokens = cache.entry(params.text_document.uri).or_default();
|
|
|
|
|
|
|
|
|
|
if let Some(prev_id) = &cached_tokens.result_id {
|
|
|
|
|
if *prev_id == params.previous_result_id {
|
2021-06-13 09:24:16 +05:30
|
|
|
let delta = to_proto::semantic_token_delta(cached_tokens, &semantic_tokens);
|
2020-07-24 17:55:17 -04:00
|
|
|
*cached_tokens = semantic_tokens;
|
2020-09-01 12:53:07 -04:00
|
|
|
return Ok(Some(delta.into()));
|
2020-07-24 17:55:17 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*cached_tokens = semantic_tokens.clone();
|
|
|
|
|
|
2020-05-10 19:09:22 +02:00
|
|
|
Ok(Some(semantic_tokens.into()))
|
2020-02-14 17:56:28 -05:00
|
|
|
}
|
2020-02-25 08:38:50 -05:00
|
|
|
|
2020-06-24 18:57:30 +02:00
|
|
|
pub(crate) fn handle_semantic_tokens_range(
|
2020-06-03 11:16:08 +02:00
|
|
|
snap: GlobalStateSnapshot,
|
2020-02-25 08:38:50 -05:00
|
|
|
params: SemanticTokensRangeParams,
|
|
|
|
|
) -> Result<Option<SemanticTokensRangeResult>> {
|
2020-08-12 16:32:36 +02:00
|
|
|
let _p = profile::span("handle_semantic_tokens_range");
|
2020-02-25 08:38:50 -05:00
|
|
|
|
2020-06-03 11:16:08 +02:00
|
|
|
let frange = from_proto::file_range(&snap, params.text_document, params.range)?;
|
2020-06-25 00:41:08 +02:00
|
|
|
let text = snap.analysis.file_text(frange.file_id)?;
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(frange.file_id)?;
|
2020-02-25 08:38:50 -05:00
|
|
|
|
2020-06-25 00:41:08 +02:00
|
|
|
let highlights = snap.analysis.highlight_range(frange)?;
|
2021-05-17 18:37:06 +03:00
|
|
|
let highlight_strings = snap.config.highlighting_strings();
|
2021-05-10 13:34:09 -07:00
|
|
|
let semantic_tokens =
|
2021-05-17 18:37:06 +03:00
|
|
|
to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings);
|
2020-05-10 19:09:22 +02:00
|
|
|
Ok(Some(semantic_tokens.into()))
|
2020-02-25 08:38:50 -05:00
|
|
|
}
|
2020-06-03 14:15:54 +03:00
|
|
|
|
2020-08-30 20:02:29 +12:00
|
|
|
pub(crate) fn handle_open_docs(
|
|
|
|
|
snap: GlobalStateSnapshot,
|
2020-09-01 11:38:32 +12:00
|
|
|
params: lsp_types::TextDocumentPositionParams,
|
|
|
|
|
) -> Result<Option<lsp_types::Url>> {
|
2020-08-30 20:02:29 +12:00
|
|
|
let _p = profile::span("handle_open_docs");
|
2020-09-01 11:38:32 +12:00
|
|
|
let position = from_proto::file_position(&snap, params)?;
|
2020-08-30 20:02:29 +12:00
|
|
|
|
2020-09-01 11:38:32 +12:00
|
|
|
let remote = snap.analysis.external_docs(position)?;
|
2020-08-30 20:02:29 +12:00
|
|
|
|
2020-09-01 11:38:32 +12:00
|
|
|
Ok(remote.and_then(|remote| Url::parse(&remote).ok()))
|
2020-08-30 20:02:29 +12:00
|
|
|
}
|
|
|
|
|
|
2020-11-12 17:48:07 -08:00
|
|
|
pub(crate) fn handle_open_cargo_toml(
|
|
|
|
|
snap: GlobalStateSnapshot,
|
|
|
|
|
params: lsp_ext::OpenCargoTomlParams,
|
|
|
|
|
) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
|
|
|
|
|
let _p = profile::span("handle_open_cargo_toml");
|
|
|
|
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
|
|
|
|
|
2020-12-08 16:17:46 +03:00
|
|
|
let cargo_spec = match CargoTargetSpec::for_file(&snap, file_id)? {
|
|
|
|
|
Some(it) => it,
|
|
|
|
|
None => return Ok(None),
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-08 16:28:21 +03:00
|
|
|
let cargo_toml_url = to_proto::url_from_abs_path(&cargo_spec.cargo_toml);
|
|
|
|
|
let res: lsp_types::GotoDefinitionResponse =
|
|
|
|
|
Location::new(cargo_toml_url, Range::default()).into();
|
2020-11-12 17:48:07 -08:00
|
|
|
Ok(Some(res))
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-16 14:37:00 +02:00
|
|
|
pub(crate) fn handle_move_item(
|
|
|
|
|
snap: GlobalStateSnapshot,
|
|
|
|
|
params: lsp_ext::MoveItemParams,
|
2021-04-13 20:32:45 +02:00
|
|
|
) -> Result<Vec<lsp_ext::SnippetTextEdit>> {
|
2021-03-16 14:37:00 +02:00
|
|
|
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 direction = match params.direction {
|
|
|
|
|
lsp_ext::MoveItemDirection::Up => ide::Direction::Up,
|
|
|
|
|
lsp_ext::MoveItemDirection::Down => ide::Direction::Down,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
match snap.analysis.move_item(range, direction)? {
|
2021-04-13 20:32:45 +02:00
|
|
|
Some(text_edit) => {
|
|
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
|
|
|
|
Ok(to_proto::snippet_text_edit_vec(&line_index, true, text_edit))
|
|
|
|
|
}
|
|
|
|
|
None => Ok(vec![]),
|
2021-03-16 14:37:00 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-14 19:36:44 +03:00
|
|
|
fn to_command_link(command: lsp_types::Command, tooltip: String) -> lsp_ext::CommandLink {
|
2020-06-03 17:35:26 +03:00
|
|
|
lsp_ext::CommandLink { tooltip: Some(tooltip), command }
|
2020-06-03 14:15:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn show_impl_command_link(
|
2020-06-03 15:13:26 +03:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-06-03 14:15:54 +03:00
|
|
|
position: &FilePosition,
|
|
|
|
|
) -> Option<lsp_ext::CommandLinkGroup> {
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 19:16:33 +03:00
|
|
|
if snap.config.hover_actions().implementations && snap.config.client_commands().show_reference {
|
2020-06-25 00:41:08 +02:00
|
|
|
if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) {
|
2020-06-13 11:00:06 +02:00
|
|
|
let uri = to_proto::url(snap, position.file_id);
|
2021-02-13 00:44:28 +03:00
|
|
|
let line_index = snap.file_line_index(position.file_id).ok()?;
|
2020-06-03 14:15:54 +03:00
|
|
|
let position = to_proto::position(&line_index, position.offset);
|
|
|
|
|
let locations: Vec<_> = nav_data
|
|
|
|
|
.info
|
2020-06-30 13:27:13 +02:00
|
|
|
.into_iter()
|
|
|
|
|
.filter_map(|nav| to_proto::location_from_nav(snap, nav).ok())
|
2020-06-03 14:15:54 +03:00
|
|
|
.collect();
|
2021-02-13 13:07:47 +02:00
|
|
|
let title = to_proto::implementation_title(locations.len());
|
2021-02-14 19:36:44 +03:00
|
|
|
let command = to_proto::command::show_references(title, &uri, position, locations);
|
2020-06-03 14:15:54 +03:00
|
|
|
|
|
|
|
|
return Some(lsp_ext::CommandLinkGroup {
|
|
|
|
|
commands: vec![to_command_link(command, "Go to implementations".into())],
|
|
|
|
|
..Default::default()
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-04 15:49:43 +02:00
|
|
|
fn show_ref_command_link(
|
|
|
|
|
snap: &GlobalStateSnapshot,
|
|
|
|
|
position: &FilePosition,
|
|
|
|
|
) -> Option<lsp_ext::CommandLinkGroup> {
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 19:16:33 +03:00
|
|
|
if snap.config.hover_actions().references && snap.config.client_commands().show_reference {
|
2021-06-04 15:49:43 +02:00
|
|
|
if let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) {
|
|
|
|
|
let uri = to_proto::url(snap, position.file_id);
|
|
|
|
|
let line_index = snap.file_line_index(position.file_id).ok()?;
|
|
|
|
|
let position = to_proto::position(&line_index, position.offset);
|
|
|
|
|
let locations: Vec<_> = ref_search_res
|
|
|
|
|
.references
|
|
|
|
|
.into_iter()
|
|
|
|
|
.flat_map(|(file_id, ranges)| {
|
|
|
|
|
ranges.into_iter().filter_map(move |(range, _)| {
|
|
|
|
|
to_proto::location(snap, FileRange { file_id, range }).ok()
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
let title = to_proto::reference_title(locations.len());
|
|
|
|
|
let command = to_proto::command::show_references(title, &uri, position, locations);
|
|
|
|
|
|
|
|
|
|
return Some(lsp_ext::CommandLinkGroup {
|
|
|
|
|
commands: vec![to_command_link(command, "Go to references".into())],
|
|
|
|
|
..Default::default()
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-10 21:24:36 +03:00
|
|
|
fn runnable_action_links(
|
2020-06-06 14:30:29 +03:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-06-08 13:56:31 +03:00
|
|
|
runnable: Runnable,
|
2020-06-06 14:30:29 +03:00
|
|
|
) -> Option<lsp_ext::CommandLinkGroup> {
|
2021-06-21 21:41:06 +02:00
|
|
|
let hover_actions_config = snap.config.hover_actions();
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 19:16:33 +03:00
|
|
|
if !hover_actions_config.runnable() {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let cargo_spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id).ok()?;
|
|
|
|
|
if should_skip_target(&runnable, cargo_spec.as_ref()) {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let client_commands_config = snap.config.client_commands();
|
|
|
|
|
if !(client_commands_config.run_single || client_commands_config.debug_single) {
|
2020-06-06 22:11:17 +03:00
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-01 20:40:31 +02:00
|
|
|
let title = runnable.title();
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 19:16:33 +03:00
|
|
|
let r = to_proto::runnable(snap, runnable).ok()?;
|
2020-06-06 14:30:29 +03:00
|
|
|
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 19:16:33 +03:00
|
|
|
let mut group = lsp_ext::CommandLinkGroup::default();
|
2020-06-06 14:30:29 +03:00
|
|
|
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 19:16:33 +03:00
|
|
|
if hover_actions_config.run && client_commands_config.run_single {
|
|
|
|
|
let run_command = to_proto::command::run_single(&r, &title);
|
|
|
|
|
group.commands.push(to_command_link(run_command, r.label.clone()));
|
|
|
|
|
}
|
2020-06-06 14:30:29 +03:00
|
|
|
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 19:16:33 +03:00
|
|
|
if hover_actions_config.debug && client_commands_config.debug_single {
|
|
|
|
|
let dbg_command = to_proto::command::debug_single(&r);
|
|
|
|
|
group.commands.push(to_command_link(dbg_command, r.label));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(group)
|
2020-06-06 14:30:29 +03:00
|
|
|
}
|
|
|
|
|
|
2020-06-10 21:24:36 +03:00
|
|
|
fn goto_type_action_links(
|
|
|
|
|
snap: &GlobalStateSnapshot,
|
|
|
|
|
nav_targets: &[HoverGotoTypeData],
|
|
|
|
|
) -> Option<lsp_ext::CommandLinkGroup> {
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 19:16:33 +03:00
|
|
|
if !snap.config.hover_actions().goto_type_def
|
|
|
|
|
|| nav_targets.is_empty()
|
|
|
|
|
|| !snap.config.client_commands().goto_location
|
|
|
|
|
{
|
2020-06-10 21:24:36 +03:00
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(lsp_ext::CommandLinkGroup {
|
|
|
|
|
title: Some("Go to ".into()),
|
|
|
|
|
commands: nav_targets
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|it| {
|
2021-02-14 19:36:44 +03:00
|
|
|
to_proto::command::goto_location(snap, &it.nav)
|
2020-06-10 21:24:36 +03:00
|
|
|
.map(|cmd| to_command_link(cmd, it.mod_path.clone()))
|
|
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-03 14:15:54 +03:00
|
|
|
fn prepare_hover_actions(
|
2020-06-03 15:13:26 +03:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-06-03 14:15:54 +03:00
|
|
|
actions: &[HoverAction],
|
|
|
|
|
) -> Vec<lsp_ext::CommandLinkGroup> {
|
|
|
|
|
actions
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|it| match it {
|
2021-01-04 14:24:37 +01:00
|
|
|
HoverAction::Implementation(position) => show_impl_command_link(snap, position),
|
2021-06-04 15:49:43 +02:00
|
|
|
HoverAction::Reference(position) => show_ref_command_link(snap, position),
|
2021-02-27 19:07:14 +03:00
|
|
|
HoverAction::Runnable(r) => runnable_action_links(snap, r.clone()),
|
2020-06-10 21:24:36 +03:00
|
|
|
HoverAction::GoToType(targets) => goto_type_action_links(snap, targets),
|
2020-06-03 14:15:54 +03:00
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
2020-06-06 12:00:46 +03:00
|
|
|
|
2020-06-06 22:11:17 +03:00
|
|
|
fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> bool {
|
|
|
|
|
match runnable.kind {
|
|
|
|
|
RunnableKind::Bin => {
|
|
|
|
|
// Do not suggest binary run on other target than binary
|
|
|
|
|
match &cargo_spec {
|
2020-08-26 17:33:03 +02:00
|
|
|
Some(spec) => !matches!(
|
|
|
|
|
spec.target_kind,
|
|
|
|
|
TargetKind::Bin | TargetKind::Example | TargetKind::Test
|
|
|
|
|
),
|
2020-06-06 22:11:17 +03:00
|
|
|
None => true,
|
2020-06-06 12:00:46 +03:00
|
|
|
}
|
|
|
|
|
}
|
2020-06-06 22:11:17 +03:00
|
|
|
_ => false,
|
2020-06-06 12:00:46 +03:00
|
|
|
}
|
2020-06-06 14:30:29 +03:00
|
|
|
}
|
2020-12-03 00:27:26 +02:00
|
|
|
|
2021-05-04 17:13:51 -04:00
|
|
|
fn run_rustfmt(
|
|
|
|
|
snap: &GlobalStateSnapshot,
|
|
|
|
|
text_document: TextDocumentIdentifier,
|
|
|
|
|
range: Option<lsp_types::Range>,
|
|
|
|
|
) -> Result<Option<Vec<lsp_types::TextEdit>>> {
|
2021-06-13 09:24:16 +05:30
|
|
|
let file_id = from_proto::file_id(snap, &text_document.uri)?;
|
2021-05-04 17:13:51 -04:00
|
|
|
let file = snap.analysis.file_text(file_id)?;
|
|
|
|
|
let crate_ids = snap.analysis.crate_for(file_id)?;
|
|
|
|
|
|
|
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
|
|
|
|
|
|
|
|
|
let mut rustfmt = match snap.config.rustfmt() {
|
|
|
|
|
RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => {
|
|
|
|
|
let mut cmd = process::Command::new(toolchain::rustfmt());
|
|
|
|
|
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(_) => {
|
2021-08-15 20:46:13 +08:00
|
|
|
tracing::error!(
|
2021-05-04 17:13:51 -04:00
|
|
|
"Unable to get file path for {}, rustfmt.toml might be ignored",
|
|
|
|
|
text_document.uri
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if let Some(&crate_id) = crate_ids.first() {
|
|
|
|
|
// Assume all crates are in the same edition
|
|
|
|
|
let edition = snap.analysis.crate_edition(crate_id)?;
|
|
|
|
|
cmd.arg("--edition");
|
|
|
|
|
cmd.arg(edition.to_string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(range) = range {
|
|
|
|
|
if !enable_range_formatting {
|
|
|
|
|
return Err(LspError::new(
|
|
|
|
|
ErrorCode::InvalidRequest as i32,
|
|
|
|
|
String::from(
|
|
|
|
|
"rustfmt range formatting is unstable. \
|
|
|
|
|
Opt-in by using a nightly build of rustfmt and setting \
|
|
|
|
|
`rustfmt.enableRangeFormatting` to true in your LSP configuration",
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.into());
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-13 09:24:16 +05:30
|
|
|
let frange = from_proto::file_range(snap, text_document, range)?;
|
2021-05-04 17:13:51 -04:00
|
|
|
let start_line = line_index.index.line_col(frange.range.start()).line;
|
|
|
|
|
let end_line = line_index.index.line_col(frange.range.end()).line;
|
|
|
|
|
|
|
|
|
|
cmd.arg("--unstable-features");
|
|
|
|
|
cmd.arg("--file-lines");
|
|
|
|
|
cmd.arg(
|
|
|
|
|
json!([{
|
|
|
|
|
"file": "stdin",
|
|
|
|
|
"range": [start_line, end_line]
|
|
|
|
|
}])
|
|
|
|
|
.to_string(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmd
|
|
|
|
|
}
|
|
|
|
|
RustfmtConfig::CustomCommand { command, args } => {
|
|
|
|
|
let mut cmd = process::Command::new(command);
|
|
|
|
|
cmd.args(args);
|
|
|
|
|
cmd
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut rustfmt =
|
|
|
|
|
rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?;
|
|
|
|
|
|
|
|
|
|
rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
|
|
|
|
|
|
|
|
|
|
let output = rustfmt.wait_with_output()?;
|
|
|
|
|
let captured_stdout = String::from_utf8(output.stdout)?;
|
|
|
|
|
let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
let rustfmt_not_installed =
|
|
|
|
|
captured_stderr.contains("not installed") || captured_stderr.contains("not available");
|
|
|
|
|
|
|
|
|
|
return match output.status.code() {
|
|
|
|
|
Some(1) if !rustfmt_not_installed => {
|
|
|
|
|
// While `rustfmt` doesn't have a specific exit code for parse errors this is the
|
|
|
|
|
// likely cause exiting with 1. Most Language Servers swallow parse errors on
|
|
|
|
|
// formatting because otherwise an error is surfaced to the user on top of the
|
|
|
|
|
// syntax error diagnostics they're already receiving. This is especially jarring
|
|
|
|
|
// if they have format on save enabled.
|
2021-08-15 20:46:13 +08:00
|
|
|
tracing::info!("rustfmt exited with status 1, assuming parse error and ignoring");
|
2021-05-04 17:13:51 -04:00
|
|
|
Ok(None)
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
// Something else happened - e.g. `rustfmt` is missing or caught a signal
|
|
|
|
|
Err(LspError::new(
|
|
|
|
|
-32900,
|
|
|
|
|
format!(
|
|
|
|
|
r#"rustfmt exited with:
|
|
|
|
|
Status: {}
|
|
|
|
|
stdout: {}
|
|
|
|
|
stderr: {}"#,
|
|
|
|
|
output.status, captured_stdout, captured_stderr,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.into())
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout);
|
|
|
|
|
|
|
|
|
|
if line_index.endings != new_line_endings {
|
|
|
|
|
// If line endings are different, send the entire file.
|
|
|
|
|
// Diffing would not work here, as the line endings might be the only
|
|
|
|
|
// difference.
|
|
|
|
|
Ok(Some(to_proto::text_edit_vec(
|
|
|
|
|
&line_index,
|
|
|
|
|
TextEdit::replace(TextRange::up_to(TextSize::of(&*file)), new_text),
|
|
|
|
|
)))
|
|
|
|
|
} else if *file == new_text {
|
|
|
|
|
// The document is already formatted correctly -- no edits needed.
|
|
|
|
|
Ok(None)
|
|
|
|
|
} else {
|
|
|
|
|
Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text))))
|
|
|
|
|
}
|
|
|
|
|
}
|