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
|
|
|
|
//! `ra_ide` crate.
|
2019-09-30 11:58:53 +03:00
|
|
|
|
2020-01-31 13:34:44 +01:00
|
|
|
use std::{
|
|
|
|
io::Write as _,
|
|
|
|
process::{self, Stdio},
|
|
|
|
};
|
2019-05-29 15:42:14 +03:00
|
|
|
|
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,
|
2020-02-26 13:24:00 +03:00
|
|
|
CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic,
|
|
|
|
DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
|
|
|
|
Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse,
|
2020-03-30 12:07:27 -04:00
|
|
|
Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams,
|
2020-02-26 13:24:00 +03:00
|
|
|
SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier,
|
|
|
|
TextEdit, WorkspaceEdit,
|
2019-01-06 00:58:03 +01:00
|
|
|
};
|
2019-11-27 21:32:33 +03:00
|
|
|
use ra_ide::{
|
2020-04-01 17:00:37 +02:00
|
|
|
Assist, AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
|
|
|
|
SearchScope,
|
2019-10-24 14:01:02 +03:00
|
|
|
};
|
2019-04-14 23:28:10 +03:00
|
|
|
use ra_prof::profile;
|
2019-11-29 15:52:12 +01:00
|
|
|
use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit};
|
2018-10-31 23:41:43 +03:00
|
|
|
use rustc_hash::FxHashMap;
|
2019-07-04 23:05:17 +03:00
|
|
|
use serde::{Deserialize, Serialize};
|
2018-08-29 18:03:14 +03:00
|
|
|
use serde_json::to_value;
|
2020-03-28 11:20:34 +01:00
|
|
|
use stdx::format_to;
|
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,
|
2019-12-30 09:12:06 -05:00
|
|
|
conv::{
|
|
|
|
to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith,
|
|
|
|
TryConvWithToVec,
|
|
|
|
},
|
2020-01-31 19:23:25 +01:00
|
|
|
diagnostics::DiagnosticTask,
|
2020-02-11 09:46:45 +01:00
|
|
|
from_json,
|
2020-04-02 09:52:27 +02:00
|
|
|
req::{self, InlayHint, InlayHintsParams},
|
2020-02-14 17:56:28 -05:00
|
|
|
semantic_tokens::SemanticTokensBuilder,
|
2020-04-01 14:32:04 +02:00
|
|
|
world::WorldSnapshot,
|
2018-10-31 23:41:43 +03:00
|
|
|
LspError, Result,
|
2018-08-11 00:12:31 +03:00
|
|
|
};
|
2018-08-10 21:13:39 +03:00
|
|
|
|
2019-06-01 10:31:40 +03:00
|
|
|
pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_analyzer_status");
|
2019-05-29 15:42:14 +03:00
|
|
|
let mut buf = world.status();
|
2020-03-28 11:20:34 +01:00
|
|
|
format_to!(buf, "\n\nrequests:");
|
2019-05-31 20:14:54 +03:00
|
|
|
let requests = world.latest_requests.read();
|
|
|
|
for (is_last, r) in requests.iter() {
|
|
|
|
let mark = if is_last { "*" } else { " " };
|
2020-03-28 11:20:34 +01:00
|
|
|
format_to!(buf, "{}{:4} {:<36}{}ms", mark, r.id, r.method, r.duration.as_millis());
|
2019-05-29 15:42:14 +03:00
|
|
|
}
|
|
|
|
Ok(buf)
|
2019-01-23 00:15:03 +03:00
|
|
|
}
|
|
|
|
|
2019-06-01 10:31:40 +03:00
|
|
|
pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) -> Result<String> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_syntax_tree");
|
2018-08-17 19:54:08 +03:00
|
|
|
let id = params.text_document.try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(id)?;
|
2019-03-03 12:02:55 +02:00
|
|
|
let text_range = params.range.map(|p| p.conv_with(&line_index));
|
2019-07-25 20:22:41 +03:00
|
|
|
let res = world.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
|
|
|
|
2019-11-18 02:47:50 +08:00
|
|
|
pub fn handle_expand_macro(
|
|
|
|
world: WorldSnapshot,
|
|
|
|
params: req::ExpandMacroParams,
|
2019-11-19 22:56:48 +08:00
|
|
|
) -> Result<Option<req::ExpandedMacro>> {
|
2019-11-18 02:47:50 +08:00
|
|
|
let _p = profile("handle_expand_macro");
|
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
|
|
|
let offset = params.position.map(|p| p.conv_with(&line_index));
|
|
|
|
|
|
|
|
match offset {
|
|
|
|
None => Ok(None),
|
2019-11-19 22:56:48 +08:00
|
|
|
Some(offset) => {
|
|
|
|
let res = world.analysis().expand_macro(FilePosition { file_id, offset })?;
|
|
|
|
Ok(res.map(|it| req::ExpandedMacro { name: it.name, expansion: it.expansion }))
|
|
|
|
}
|
2019-11-18 02:47:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-21 12:13:48 +03:00
|
|
|
pub fn handle_selection_range(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2019-04-21 12:13:48 +03:00
|
|
|
params: req::SelectionRangeParams,
|
2020-03-09 10:17:16 -04:00
|
|
|
) -> Result<Option<Vec<req::SelectionRange>>> {
|
2019-05-27 11:48:23 +03:00
|
|
|
let _p = profile("handle_selection_range");
|
2019-04-21 12:13:48 +03:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
2020-03-09 10:17:16 -04:00
|
|
|
let res: Result<Vec<req::SelectionRange>> = params
|
2019-04-21 12:13:48 +03:00
|
|
|
.positions
|
|
|
|
.into_iter()
|
|
|
|
.map_conv_with(&line_index)
|
|
|
|
.map(|position| {
|
|
|
|
let mut ranges = Vec::new();
|
|
|
|
{
|
|
|
|
let mut range = TextRange::from_to(position, position);
|
|
|
|
loop {
|
|
|
|
ranges.push(range);
|
|
|
|
let frange = FileRange { file_id, range };
|
|
|
|
let next = world.analysis().extend_selection(frange)?;
|
|
|
|
if next == range {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
range = next
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let mut range = req::SelectionRange {
|
|
|
|
range: ranges.last().unwrap().conv_with(&line_index),
|
|
|
|
parent: None,
|
|
|
|
};
|
|
|
|
for r in ranges.iter().rev().skip(1) {
|
|
|
|
range = req::SelectionRange {
|
|
|
|
range: r.conv_with(&line_index),
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-08-16 00:23:22 +03:00
|
|
|
pub fn handle_find_matching_brace(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-08-16 00:23:22 +03:00
|
|
|
params: req::FindMatchingBraceParams,
|
|
|
|
) -> Result<Vec<Position>> {
|
2019-05-27 11:48:23 +03:00
|
|
|
let _p = profile("handle_find_matching_brace");
|
2018-08-17 19:54:08 +03:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
2018-10-15 17:44:23 -04:00
|
|
|
let res = params
|
|
|
|
.offsets
|
2018-08-16 00:23:22 +03:00
|
|
|
.into_iter()
|
|
|
|
.map_conv_with(&line_index)
|
|
|
|
.map(|offset| {
|
2019-07-25 20:22:41 +03:00
|
|
|
if let Ok(Some(matching_brace_offset)) =
|
|
|
|
world.analysis().matching_brace(FilePosition { file_id, offset })
|
|
|
|
{
|
|
|
|
matching_brace_offset
|
|
|
|
} else {
|
|
|
|
offset
|
|
|
|
}
|
2018-08-16 00:23:22 +03:00
|
|
|
})
|
|
|
|
.map_conv_with(&line_index)
|
|
|
|
.collect();
|
|
|
|
Ok(res)
|
|
|
|
}
|
|
|
|
|
2018-08-23 22:14:51 +03:00
|
|
|
pub fn handle_join_lines(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-08-23 22:14:51 +03:00
|
|
|
params: req::JoinLinesParams,
|
2018-08-29 18:03:14 +03:00
|
|
|
) -> Result<req::SourceChange> {
|
2019-05-27 11:48:23 +03:00
|
|
|
let _p = profile("handle_join_lines");
|
2018-12-28 18:15:19 +03:00
|
|
|
let frange = (¶ms.text_document, params.range).try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
world.analysis().join_lines(frange)?.try_conv_with(&world)
|
2018-08-29 18:03:14 +03:00
|
|
|
}
|
|
|
|
|
2018-10-09 16:00:20 +03:00
|
|
|
pub fn handle_on_enter(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-10-09 16:00:20 +03:00
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<req::SourceChange>> {
|
2019-05-27 11:48:23 +03:00
|
|
|
let _p = profile("handle_on_enter");
|
2018-11-05 14:57:41 +03:00
|
|
|
let position = params.try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
match world.analysis().on_enter(position)? {
|
2018-10-09 16:00:20 +03:00
|
|
|
None => Ok(None),
|
2018-10-15 17:44:23 -04:00
|
|
|
Some(edit) => Ok(Some(edit.try_conv_with(&world)?)),
|
2018-10-09 16:00:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-25 12:16:56 +03:00
|
|
|
// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
|
2018-08-29 18:03:14 +03:00
|
|
|
pub fn handle_on_type_formatting(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-08-29 18:03:14 +03:00
|
|
|
params: req::DocumentOnTypeFormattingParams,
|
|
|
|
) -> Result<Option<Vec<TextEdit>>> {
|
2019-05-27 11:48:23 +03:00
|
|
|
let _p = profile("handle_on_type_formatting");
|
2019-07-07 14:13:13 -04:00
|
|
|
let mut position = params.text_document_position.try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(position.file_id)?;
|
2019-08-20 18:53:59 +03:00
|
|
|
let line_endings = world.file_line_endings(position.file_id);
|
2019-07-07 14:13:13 -04:00
|
|
|
|
2019-11-27 21:32:33 +03:00
|
|
|
// in `ra_ide`, the `on_type` invariant is that
|
2019-07-07 14:13:13 -04:00
|
|
|
// `text.char_at(position) == typed_char`.
|
2019-12-20 15:14:30 -05:00
|
|
|
position.offset -= TextUnit::of_char('.');
|
2019-10-25 11:19:26 +03:00
|
|
|
let char_typed = params.ch.chars().next().unwrap_or('\0');
|
2020-03-02 13:45:26 +01:00
|
|
|
assert!({
|
|
|
|
let text = world.analysis().file_text(position.file_id)?;
|
|
|
|
text[position.offset.to_usize()..].starts_with(char_typed)
|
|
|
|
});
|
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);
|
|
|
|
}
|
|
|
|
|
2019-10-25 11:19:26 +03:00
|
|
|
let edit = world.analysis().on_char_typed(position, char_typed)?;
|
2019-01-11 15:05:40 +03:00
|
|
|
let mut edit = match edit {
|
|
|
|
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
|
|
|
|
let edit = edit.source_file_edits.pop().unwrap();
|
2019-01-06 00:58:03 +01:00
|
|
|
|
2019-08-20 18:53:59 +03:00
|
|
|
let change: Vec<TextEdit> = edit.edit.conv_with((&line_index, line_endings));
|
2019-02-06 15:50:26 -05:00
|
|
|
Ok(Some(change))
|
2018-08-23 22:14:51 +03:00
|
|
|
}
|
|
|
|
|
2018-08-11 14:44:12 +03:00
|
|
|
pub fn handle_document_symbol(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-08-11 14:44:12 +03:00
|
|
|
params: req::DocumentSymbolParams,
|
|
|
|
) -> Result<Option<req::DocumentSymbolResponse>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_document_symbol");
|
2018-08-17 19:54:08 +03:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
2018-08-11 14:44:12 +03:00
|
|
|
|
2018-08-14 11:20:09 +03:00
|
|
|
let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
|
2018-08-11 14:44:12 +03:00
|
|
|
|
2019-07-25 20:22:41 +03:00
|
|
|
for symbol in world.analysis().file_structure(file_id)? {
|
2018-08-11 14:44:12 +03:00
|
|
|
let doc_symbol = DocumentSymbol {
|
2018-08-14 11:20:09 +03:00
|
|
|
name: symbol.label,
|
2019-01-24 18:21:17 +01:00
|
|
|
detail: symbol.detail,
|
2018-08-12 21:02:56 +03:00
|
|
|
kind: symbol.kind.conv(),
|
2019-02-05 17:05:46 -05:00
|
|
|
deprecated: Some(symbol.deprecated),
|
2018-08-12 21:02:56 +03:00
|
|
|
range: symbol.node_range.conv_with(&line_index),
|
2018-08-14 11:20:09 +03:00
|
|
|
selection_range: symbol.navigation_range.conv_with(&line_index),
|
2018-08-11 14:44:12 +03:00
|
|
|
children: None,
|
|
|
|
};
|
2018-08-14 11:20:09 +03:00
|
|
|
parents.push((doc_symbol, symbol.parent));
|
|
|
|
}
|
|
|
|
let mut res = Vec::new();
|
|
|
|
while let Some((node, parent)) = parents.pop() {
|
|
|
|
match parent {
|
|
|
|
None => res.push(node),
|
|
|
|
Some(i) => {
|
|
|
|
let children = &mut parents[i].0.children;
|
|
|
|
if children.is_none() {
|
|
|
|
*children = Some(Vec::new());
|
|
|
|
}
|
|
|
|
children.as_mut().unwrap().push(node);
|
2018-08-11 14:44:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-14 11:20:09 +03:00
|
|
|
|
2019-07-07 17:28:21 -04:00
|
|
|
Ok(Some(res.into()))
|
2018-08-11 14:44:12 +03:00
|
|
|
}
|
|
|
|
|
2018-08-13 15:35:53 +03:00
|
|
|
pub fn handle_workspace_symbol(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-08-13 15:35:53 +03:00
|
|
|
params: req::WorkspaceSymbolParams,
|
|
|
|
) -> Result<Option<Vec<SymbolInformation>>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_workspace_symbol");
|
2018-10-16 11:45:10 -04:00
|
|
|
let all_symbols = params.query.contains('#');
|
|
|
|
let libs = params.query.contains('*');
|
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
|
|
|
|
};
|
2018-10-20 22:02:41 +03:00
|
|
|
let mut res = exec_query(&world, 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);
|
2018-10-20 22:02:41 +03:00
|
|
|
res = exec_query(&world, 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));
|
|
|
|
|
2019-06-01 10:31:40 +03:00
|
|
|
fn exec_query(world: &WorldSnapshot, query: Query) -> Result<Vec<SymbolInformation>> {
|
2018-08-14 13:33:44 +03:00
|
|
|
let mut res = Vec::new();
|
2019-01-02 17:09:39 +03:00
|
|
|
for nav in world.analysis().symbol_search(query)? {
|
2018-08-14 13:33:44 +03:00
|
|
|
let info = SymbolInformation {
|
2019-01-02 23:35:51 +03:00
|
|
|
name: nav.name().to_string(),
|
2019-01-02 17:09:39 +03:00
|
|
|
kind: nav.kind().conv(),
|
|
|
|
location: nav.try_conv_with(world)?,
|
2019-02-12 21:47:51 +02:00
|
|
|
container_name: nav.container_name().map(|v| v.to_string()),
|
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
|
|
|
}
|
|
|
|
|
2018-08-13 16:35:17 +03:00
|
|
|
pub fn handle_goto_definition(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-08-13 16:35:17 +03:00
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<req::GotoDefinitionResponse>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_goto_definition");
|
2018-11-05 14:57:41 +03:00
|
|
|
let position = params.try_conv_with(&world)?;
|
2019-01-11 14:14:09 +03:00
|
|
|
let nav_info = match world.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,
|
|
|
|
};
|
2019-07-08 13:47:02 +03:00
|
|
|
let res = (position.file_id, nav_info).try_conv_with(&world)?;
|
|
|
|
Ok(Some(res))
|
2018-08-13 16:35:17 +03:00
|
|
|
}
|
|
|
|
|
2019-01-28 09:26:32 -05:00
|
|
|
pub fn handle_goto_implementation(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2019-01-28 09:26:32 -05:00
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<req::GotoImplementationResponse>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_goto_implementation");
|
2019-01-28 09:26:32 -05:00
|
|
|
let position = params.try_conv_with(&world)?;
|
|
|
|
let nav_info = match world.analysis().goto_implementation(position)? {
|
|
|
|
None => return Ok(None),
|
|
|
|
Some(it) => it,
|
|
|
|
};
|
2019-07-08 13:47:02 +03:00
|
|
|
let res = (position.file_id, nav_info).try_conv_with(&world)?;
|
|
|
|
Ok(Some(res))
|
2019-04-23 14:11:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn handle_goto_type_definition(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2019-04-23 14:11:27 -04:00
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<req::GotoTypeDefinitionResponse>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_goto_type_definition");
|
2019-04-23 14:11:27 -04:00
|
|
|
let position = params.try_conv_with(&world)?;
|
|
|
|
let nav_info = match world.analysis().goto_type_definition(position)? {
|
|
|
|
None => return Ok(None),
|
|
|
|
Some(it) => it,
|
|
|
|
};
|
2019-07-08 13:47:02 +03:00
|
|
|
let res = (position.file_id, nav_info).try_conv_with(&world)?;
|
|
|
|
Ok(Some(res))
|
2018-08-13 16:35:17 +03:00
|
|
|
}
|
|
|
|
|
2018-08-22 10:18:58 +03:00
|
|
|
pub fn handle_parent_module(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-11-05 14:10:20 +03:00
|
|
|
params: req::TextDocumentPositionParams,
|
2018-08-22 10:18:58 +03:00
|
|
|
) -> Result<Vec<Location>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_parent_module");
|
2018-11-05 14:57:41 +03:00
|
|
|
let position = params.try_conv_with(&world)?;
|
2019-07-08 13:39:16 +03:00
|
|
|
world.analysis().parent_module(position)?.iter().try_conv_with_to_vec(&world)
|
2018-08-22 10:18:58 +03:00
|
|
|
}
|
|
|
|
|
2018-08-29 18:03:14 +03:00
|
|
|
pub fn handle_runnables(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-08-29 18:03:14 +03:00
|
|
|
params: req::RunnablesParams,
|
|
|
|
) -> Result<Vec<req::Runnable>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_runnables");
|
2018-08-29 18:03:14 +03:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
2018-08-29 18:03:14 +03:00
|
|
|
let offset = params.position.map(|it| it.conv_with(&line_index));
|
|
|
|
let mut res = Vec::new();
|
2019-04-13 19:45:21 +02:00
|
|
|
let workspace_root = world.workspace_root_for(file_id);
|
2018-10-20 22:02:41 +03:00
|
|
|
for runnable in world.analysis().runnables(file_id)? {
|
2018-08-29 18:03:14 +03:00
|
|
|
if let Some(offset) = offset {
|
2019-01-08 21:50:04 +03:00
|
|
|
if !runnable.range.contains_inclusive(offset) {
|
2018-08-29 18:03:14 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2019-08-09 23:34:14 +03:00
|
|
|
res.push(to_lsp_runnable(&world, file_id, runnable)?);
|
2018-08-29 18:03:14 +03:00
|
|
|
}
|
2018-10-25 10:56:12 +03:00
|
|
|
let mut check_args = vec!["check".to_string()];
|
2018-10-25 11:01:37 +03:00
|
|
|
let label;
|
2018-10-25 10:56:12 +03:00
|
|
|
match CargoTargetSpec::for_file(&world, file_id)? {
|
2018-10-25 11:01:37 +03:00
|
|
|
Some(spec) => {
|
|
|
|
label = format!("cargo check -p {}", spec.package);
|
|
|
|
spec.push_to(&mut check_args);
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
label = "cargo check --all".to_string();
|
|
|
|
check_args.push("--all".to_string())
|
|
|
|
}
|
2018-10-25 10:56:12 +03:00
|
|
|
}
|
2018-10-25 10:29:39 +03:00
|
|
|
// Always add `cargo check`.
|
|
|
|
res.push(req::Runnable {
|
|
|
|
range: Default::default(),
|
2018-10-25 11:01:37 +03:00
|
|
|
label,
|
2018-10-25 10:29:39 +03:00
|
|
|
bin: "cargo".to_string(),
|
2018-10-25 10:56:12 +03:00
|
|
|
args: check_args,
|
2020-03-12 20:56:34 +01:00
|
|
|
extra_args: Vec::new(),
|
2018-10-25 10:29:39 +03:00
|
|
|
env: FxHashMap::default(),
|
2019-04-13 19:45:21 +02:00
|
|
|
cwd: workspace_root.map(|root| root.to_string_lossy().to_string()),
|
2018-10-25 10:29:39 +03:00
|
|
|
});
|
2019-02-06 15:50:26 -05:00
|
|
|
Ok(res)
|
2018-08-29 18:03:14 +03:00
|
|
|
}
|
|
|
|
|
2018-08-26 12:51:45 +03:00
|
|
|
pub fn handle_completion(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-08-26 12:51:45 +03:00
|
|
|
params: req::CompletionParams,
|
|
|
|
) -> Result<Option<req::CompletionResponse>> {
|
2019-04-14 23:28:10 +03:00
|
|
|
let _p = profile("handle_completion");
|
2019-07-07 14:13:13 -04:00
|
|
|
let position = params.text_document_position.try_conv_with(&world)?;
|
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() == ":" {
|
2019-07-25 20:22:41 +03:00
|
|
|
let source_file = world.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) {
|
|
|
|
let diff = TextUnit::of_char(next_char) + TextUnit::of_char(':');
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-04-01 17:00:37 +02:00
|
|
|
let items = match world.analysis().completions(position, &world.config.completion)? {
|
2018-08-26 12:51:45 +03:00
|
|
|
None => return Ok(None),
|
|
|
|
Some(items) => items,
|
|
|
|
};
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(position.file_id)?;
|
2019-08-20 18:53:59 +03:00
|
|
|
let line_endings = world.file_line_endings(position.file_id);
|
2020-04-24 01:52:26 +02:00
|
|
|
let items: Vec<CompletionItem> =
|
|
|
|
items.into_iter().map(|item| item.conv_with((&line_index, line_endings))).collect();
|
2018-08-26 12:51:45 +03:00
|
|
|
|
2019-07-07 17:28:21 -04:00
|
|
|
Ok(Some(items.into()))
|
2018-08-26 12:51:45 +03:00
|
|
|
}
|
|
|
|
|
2018-09-23 11:13:27 -04:00
|
|
|
pub fn handle_folding_range(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-09-23 11:13:27 -04:00
|
|
|
params: FoldingRangeParams,
|
|
|
|
) -> Result<Option<Vec<FoldingRange>>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_folding_range");
|
2018-09-23 11:13:27 -04:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2019-09-19 18:23:12 +03:00
|
|
|
let folds = world.analysis().folding_ranges(file_id)?;
|
2019-10-21 02:04:55 +03:00
|
|
|
let text = world.analysis().file_text(file_id)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
2019-10-21 02:04:55 +03:00
|
|
|
let ctx = FoldConvCtx {
|
|
|
|
text: &text,
|
|
|
|
line_index: &line_index,
|
2020-04-01 18:46:26 +02:00
|
|
|
line_folding_only: world.config.client_caps.line_folding_only,
|
2019-10-21 02:04:55 +03:00
|
|
|
};
|
|
|
|
let res = Some(folds.into_iter().map_conv_with(&ctx).collect());
|
2018-09-24 09:52:33 -04:00
|
|
|
Ok(res)
|
2018-09-23 11:13:27 -04:00
|
|
|
}
|
|
|
|
|
2018-10-09 10:08:17 -04:00
|
|
|
pub fn handle_signature_help(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-10-09 10:08:17 -04:00
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<req::SignatureHelp>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_signature_help");
|
2018-11-05 14:57:41 +03:00
|
|
|
let position = params.try_conv_with(&world)?;
|
2019-01-08 18:27:44 +03:00
|
|
|
if let Some(call_info) = world.analysis().call_info(position)? {
|
2020-04-01 17:00:37 +02:00
|
|
|
let concise = !world.config.call_info_full;
|
2020-03-06 18:32:40 +01:00
|
|
|
let mut active_parameter = call_info.active_parameter.map(|it| it as i64);
|
|
|
|
if concise && call_info.signature.has_self_param {
|
|
|
|
active_parameter = active_parameter.map(|it| it.saturating_sub(1));
|
|
|
|
}
|
|
|
|
let sig_info = call_info.signature.conv_with(concise);
|
2019-01-29 21:39:09 -05:00
|
|
|
|
2018-10-09 10:08:17 -04:00
|
|
|
Ok(Some(req::SignatureHelp {
|
|
|
|
signatures: vec![sig_info],
|
|
|
|
active_signature: Some(0),
|
2019-03-12 09:24:46 +02:00
|
|
|
active_parameter,
|
2018-10-09 10:08:17 -04:00
|
|
|
}))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-05 16:37:27 -05:00
|
|
|
pub fn handle_hover(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-11-05 16:37:27 -05:00
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<Hover>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_hover");
|
2018-11-05 16:37:27 -05:00
|
|
|
let position = params.try_conv_with(&world)?;
|
2019-01-05 17:22:41 +03:00
|
|
|
let info = match world.analysis().hover(position)? {
|
2018-12-08 16:02:23 +00:00
|
|
|
None => return Ok(None),
|
2019-01-05 17:22:41 +03:00
|
|
|
Some(info) => info,
|
2018-12-08 16:02:23 +00:00
|
|
|
};
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis.file_line_index(position.file_id)?;
|
2019-01-05 17:22:41 +03:00
|
|
|
let range = info.range.conv_with(&line_index);
|
|
|
|
let res = Hover {
|
2019-01-11 21:45:16 -05:00
|
|
|
contents: HoverContents::Markup(MarkupContent {
|
|
|
|
kind: MarkupKind::Markdown,
|
2019-07-30 03:46:38 +03:00
|
|
|
value: crate::markdown::format_docs(&info.info.to_markup()),
|
2019-01-11 21:45:16 -05:00
|
|
|
}),
|
2019-01-05 17:22:41 +03:00
|
|
|
range: Some(range),
|
2019-01-04 19:06:46 +09:00
|
|
|
};
|
2019-01-05 17:22:41 +03:00
|
|
|
Ok(Some(res))
|
2018-11-05 16:37:27 -05:00
|
|
|
}
|
|
|
|
|
2018-10-19 15:25:10 -04:00
|
|
|
pub fn handle_prepare_rename(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-10-19 15:25:10 -04:00
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<PrepareRenameResponse>> {
|
2019-10-23 18:26:43 +03:00
|
|
|
let _p = profile("handle_prepare_rename");
|
2018-11-05 14:57:41 +03:00
|
|
|
let position = params.try_conv_with(&world)?;
|
2018-10-19 15:25:10 -04:00
|
|
|
|
2019-09-05 21:36:40 +03:00
|
|
|
let optional_change = world.analysis().rename(position, "dummy")?;
|
|
|
|
let range = match optional_change {
|
2018-11-05 14:57:41 +03:00
|
|
|
None => return Ok(None),
|
2019-09-05 21:36:40 +03:00
|
|
|
Some(it) => it.range,
|
2018-11-05 14:57:41 +03:00
|
|
|
};
|
2019-02-17 13:38:32 +02:00
|
|
|
|
2018-11-05 14:57:41 +03:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
2019-09-05 21:36:40 +03:00
|
|
|
let range = range.conv_with(&line_index);
|
|
|
|
Ok(Some(PrepareRenameResponse::Range(range)))
|
2018-10-19 15:25:10 -04:00
|
|
|
}
|
|
|
|
|
2019-06-01 10:31:40 +03:00
|
|
|
pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> {
|
2019-10-23 18:26:43 +03:00
|
|
|
let _p = profile("handle_rename");
|
2019-07-07 14:13:13 -04:00
|
|
|
let position = params.text_document_position.try_conv_with(&world)?;
|
2018-10-18 17:56:22 -04:00
|
|
|
|
|
|
|
if params.new_name.is_empty() {
|
2018-10-31 23:41:43 +03:00
|
|
|
return Err(LspError::new(
|
|
|
|
ErrorCode::InvalidParams as i32,
|
|
|
|
"New Name cannot be empty".into(),
|
|
|
|
)
|
|
|
|
.into());
|
2018-10-18 17:56:22 -04:00
|
|
|
}
|
|
|
|
|
2019-07-07 14:13:13 -04:00
|
|
|
let optional_change = world.analysis().rename(position, &*params.new_name)?;
|
2019-01-18 16:32:36 +08:00
|
|
|
let change = match optional_change {
|
|
|
|
None => return Ok(None),
|
2019-09-05 21:36:40 +03:00
|
|
|
Some(it) => it.info,
|
2019-01-18 16:32:36 +08:00
|
|
|
};
|
2018-10-18 17:56:22 -04:00
|
|
|
|
2019-01-18 16:32:36 +08:00
|
|
|
let source_change_req = change.try_conv_with(&world)?;
|
2019-01-15 00:09:03 +08:00
|
|
|
|
2019-01-17 19:57:24 +08:00
|
|
|
Ok(Some(source_change_req.workspace_edit))
|
2018-10-18 17:56:22 -04:00
|
|
|
}
|
|
|
|
|
2018-10-18 13:40:12 -04:00
|
|
|
pub fn handle_references(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-10-18 13:40:12 -04:00
|
|
|
params: req::ReferenceParams,
|
|
|
|
) -> Result<Option<Vec<Location>>> {
|
2019-10-23 18:26:43 +03:00
|
|
|
let _p = profile("handle_references");
|
2019-07-07 14:13:13 -04:00
|
|
|
let position = params.text_document_position.try_conv_with(&world)?;
|
2019-10-12 18:47:17 +03:00
|
|
|
|
2019-10-24 14:01:02 +03:00
|
|
|
let refs = match world.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
|
|
|
|
2019-02-17 13:38:32 +02:00
|
|
|
let locations = if params.context.include_declaration {
|
|
|
|
refs.into_iter()
|
2020-01-04 17:46:01 -05:00
|
|
|
.filter_map(|reference| {
|
|
|
|
let line_index =
|
|
|
|
world.analysis().file_line_index(reference.file_range.file_id).ok()?;
|
|
|
|
to_location(
|
|
|
|
reference.file_range.file_id,
|
|
|
|
reference.file_range.range,
|
|
|
|
&world,
|
|
|
|
&line_index,
|
|
|
|
)
|
|
|
|
.ok()
|
2019-10-12 13:39:02 +03:00
|
|
|
})
|
2019-02-17 13:38:32 +02:00
|
|
|
.collect()
|
|
|
|
} else {
|
|
|
|
// Only iterate over the references if include_declaration was false
|
|
|
|
refs.references()
|
|
|
|
.iter()
|
2020-01-04 17:46:01 -05:00
|
|
|
.filter_map(|reference| {
|
|
|
|
let line_index =
|
|
|
|
world.analysis().file_line_index(reference.file_range.file_id).ok()?;
|
|
|
|
to_location(
|
|
|
|
reference.file_range.file_id,
|
|
|
|
reference.file_range.range,
|
|
|
|
&world,
|
|
|
|
&line_index,
|
|
|
|
)
|
|
|
|
.ok()
|
2019-10-12 13:39:02 +03:00
|
|
|
})
|
2019-02-17 13:38:32 +02:00
|
|
|
.collect()
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Some(locations))
|
2018-10-18 13:40:12 -04:00
|
|
|
}
|
|
|
|
|
2018-12-29 20:09:42 +01:00
|
|
|
pub fn handle_formatting(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-12-29 20:09:42 +01:00
|
|
|
params: DocumentFormattingParams,
|
|
|
|
) -> Result<Option<Vec<TextEdit>>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_formatting");
|
2018-12-29 20:09:42 +01:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
let file = world.analysis().file_text(file_id)?;
|
2019-12-04 23:05:01 +01:00
|
|
|
let crate_ids = world.analysis().crate_for(file_id)?;
|
2018-12-29 20:09:42 +01:00
|
|
|
|
2019-07-25 20:22:41 +03:00
|
|
|
let file_line_index = world.analysis().file_line_index(file_id)?;
|
2018-12-29 20:09:42 +01:00
|
|
|
let end_position = TextUnit::of_str(&file).conv_with(&file_line_index);
|
|
|
|
|
2020-04-01 13:31:12 +02:00
|
|
|
let mut rustfmt = match &world.config.rustfmt {
|
|
|
|
RustfmtConfig::Rustfmt { extra_args } => {
|
|
|
|
let mut cmd = process::Command::new("rustfmt");
|
|
|
|
cmd.args(extra_args);
|
|
|
|
if let Some(&crate_id) = crate_ids.first() {
|
|
|
|
// Assume all crates are in the same edition
|
|
|
|
let edition = world.analysis().crate_edition(crate_id)?;
|
|
|
|
cmd.arg("--edition");
|
|
|
|
cmd.arg(edition.to_string());
|
|
|
|
}
|
|
|
|
cmd
|
|
|
|
}
|
|
|
|
RustfmtConfig::CustomCommand { command, args } => {
|
|
|
|
let mut cmd = process::Command::new(command);
|
|
|
|
cmd.args(args);
|
|
|
|
cmd
|
|
|
|
}
|
|
|
|
};
|
2019-01-26 20:16:15 +00:00
|
|
|
|
|
|
|
if let Ok(path) = params.text_document.uri.to_file_path() {
|
|
|
|
if let Some(parent) = path.parent() {
|
|
|
|
rustfmt.current_dir(parent);
|
|
|
|
}
|
|
|
|
}
|
2020-01-31 13:34:44 +01:00
|
|
|
let mut rustfmt = rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?;
|
2018-12-29 20:09:42 +01:00
|
|
|
|
|
|
|
rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
|
|
|
|
|
|
|
|
let output = rustfmt.wait_with_output()?;
|
|
|
|
let captured_stdout = String::from_utf8(output.stdout)?;
|
2019-06-27 07:52:19 +10:00
|
|
|
|
2018-12-29 20:09:42 +01:00
|
|
|
if !output.status.success() {
|
2019-06-27 07:52:19 +10:00
|
|
|
match output.status.code() {
|
|
|
|
Some(1) => {
|
|
|
|
// 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.
|
|
|
|
log::info!("rustfmt exited with status 1, assuming parse error and ignoring");
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
// Something else happened - e.g. `rustfmt` is missing or caught a signal
|
|
|
|
return Err(LspError::new(
|
|
|
|
-32900,
|
|
|
|
format!(
|
|
|
|
r#"rustfmt exited with:
|
|
|
|
Status: {}
|
|
|
|
stdout: {}"#,
|
|
|
|
output.status, captured_stdout,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
}
|
2018-12-29 20:09:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Some(vec![TextEdit {
|
|
|
|
range: Range::new(Position::new(0, 0), end_position),
|
|
|
|
new_text: captured_stdout,
|
|
|
|
}]))
|
|
|
|
}
|
|
|
|
|
2020-02-26 13:24:00 +03:00
|
|
|
fn create_single_code_action(assist: Assist, world: &WorldSnapshot) -> Result<CodeAction> {
|
|
|
|
let arg = to_value(assist.source_change.try_conv_with(world)?)?;
|
|
|
|
let title = assist.label;
|
|
|
|
let command = Command {
|
|
|
|
title: title.clone(),
|
|
|
|
command: "rust-analyzer.applySourceChange".to_string(),
|
|
|
|
arguments: Some(vec![arg]),
|
|
|
|
};
|
|
|
|
|
|
|
|
let kind = match assist.id {
|
|
|
|
AssistId("introduce_variable") => Some("refactor.extract.variable".to_string()),
|
|
|
|
AssistId("add_custom_impl") => Some("refactor.rewrite.add_custom_impl".to_string()),
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(CodeAction {
|
|
|
|
title,
|
|
|
|
kind,
|
|
|
|
diagnostics: None,
|
|
|
|
edit: None,
|
|
|
|
command: Some(command),
|
|
|
|
is_preferred: None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-08-29 18:03:14 +03:00
|
|
|
pub fn handle_code_action(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-08-29 18:03:14 +03:00
|
|
|
params: req::CodeActionParams,
|
2018-09-23 11:10:57 -04:00
|
|
|
) -> Result<Option<CodeActionResponse>> {
|
2019-04-14 23:28:10 +03:00
|
|
|
let _p = profile("handle_code_action");
|
2018-08-28 11:12:42 +03:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
2018-09-06 00:59:07 +03:00
|
|
|
let range = params.range.conv_with(&line_index);
|
2018-08-13 02:38:34 +03:00
|
|
|
|
2019-02-24 13:53:35 +03:00
|
|
|
let diagnostics = world.analysis().diagnostics(file_id)?;
|
2019-07-04 16:52:29 -04:00
|
|
|
let mut res = CodeActionResponse::default();
|
2019-02-24 13:53:35 +03:00
|
|
|
|
|
|
|
let fixes_from_diagnostics = diagnostics
|
2018-10-15 17:44:23 -04:00
|
|
|
.into_iter()
|
2018-08-29 18:03:14 +03:00
|
|
|
.filter_map(|d| Some((d.range, d.fix?)))
|
2019-01-08 21:50:04 +03:00
|
|
|
.filter(|(diag_range, _fix)| diag_range.intersection(&range).is_some())
|
2018-08-29 18:03:14 +03:00
|
|
|
.map(|(_range, fix)| fix);
|
2018-08-13 02:38:34 +03:00
|
|
|
|
2019-02-24 13:53:35 +03:00
|
|
|
for source_edit in fixes_from_diagnostics {
|
2018-08-29 18:03:14 +03:00
|
|
|
let title = source_edit.label.clone();
|
|
|
|
let edit = source_edit.try_conv_with(&world)?;
|
2019-02-04 10:26:43 -05:00
|
|
|
|
2019-02-24 13:53:35 +03:00
|
|
|
let command = Command {
|
2018-08-29 18:03:14 +03:00
|
|
|
title,
|
2019-01-28 14:43:07 +03:00
|
|
|
command: "rust-analyzer.applySourceChange".to_string(),
|
2018-08-29 18:03:14 +03:00
|
|
|
arguments: Some(vec![to_value(edit).unwrap()]),
|
|
|
|
};
|
2019-02-24 13:53:35 +03:00
|
|
|
let action = CodeAction {
|
|
|
|
title: command.title.clone(),
|
|
|
|
kind: None,
|
|
|
|
diagnostics: None,
|
|
|
|
edit: None,
|
|
|
|
command: Some(command),
|
2019-12-11 18:34:01 +01:00
|
|
|
is_preferred: None,
|
2019-02-24 13:53:35 +03:00
|
|
|
};
|
2019-07-04 18:04:46 -04:00
|
|
|
res.push(action.into());
|
2019-02-24 13:53:35 +03:00
|
|
|
}
|
|
|
|
|
2020-01-31 19:23:25 +01:00
|
|
|
for fix in world.check_fixes.get(&file_id).into_iter().flatten() {
|
|
|
|
let fix_range = fix.range.conv_with(&line_index);
|
2019-12-25 12:21:38 +01:00
|
|
|
if fix_range.intersection(&range).is_none() {
|
|
|
|
continue;
|
|
|
|
}
|
2020-01-31 19:23:25 +01:00
|
|
|
res.push(fix.action.clone());
|
2019-12-25 12:21:38 +01:00
|
|
|
}
|
|
|
|
|
2020-03-25 15:45:52 +01:00
|
|
|
let mut grouped_assists: FxHashMap<String, (usize, Vec<Assist>)> = FxHashMap::default();
|
2020-01-02 01:39:01 +02:00
|
|
|
for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() {
|
2020-02-26 13:24:00 +03:00
|
|
|
match &assist.group_label {
|
2020-03-25 15:45:52 +01:00
|
|
|
Some(label) => grouped_assists
|
|
|
|
.entry(label.to_owned())
|
|
|
|
.or_insert_with(|| {
|
|
|
|
let idx = res.len();
|
|
|
|
let dummy = Command::new(String::new(), String::new(), None);
|
|
|
|
res.push(dummy.into());
|
|
|
|
(idx, Vec::new())
|
|
|
|
})
|
|
|
|
.1
|
|
|
|
.push(assist),
|
|
|
|
None => {
|
|
|
|
res.push(create_single_code_action(assist, &world)?.into());
|
|
|
|
}
|
2020-02-26 13:24:00 +03:00
|
|
|
}
|
|
|
|
}
|
2020-02-09 15:32:53 +01:00
|
|
|
|
2020-03-25 15:45:52 +01:00
|
|
|
for (group_label, (idx, assists)) in grouped_assists {
|
2020-02-26 13:24:00 +03:00
|
|
|
if assists.len() == 1 {
|
2020-03-25 15:45:52 +01:00
|
|
|
res[idx] =
|
|
|
|
create_single_code_action(assists.into_iter().next().unwrap(), &world)?.into();
|
2020-02-26 13:24:00 +03:00
|
|
|
} else {
|
|
|
|
let title = group_label;
|
|
|
|
|
|
|
|
let mut arguments = Vec::with_capacity(assists.len());
|
|
|
|
for assist in assists {
|
|
|
|
arguments.push(to_value(assist.source_change.try_conv_with(&world)?)?);
|
|
|
|
}
|
2020-01-12 00:40:36 +02:00
|
|
|
|
2020-02-26 13:24:00 +03:00
|
|
|
let command = Some(Command {
|
|
|
|
title: title.clone(),
|
|
|
|
command: "rust-analyzer.selectAndApplySourceChange".to_string(),
|
|
|
|
arguments: Some(vec![serde_json::Value::Array(arguments)]),
|
|
|
|
});
|
2020-03-25 15:45:52 +01:00
|
|
|
res[idx] = CodeAction {
|
|
|
|
title,
|
|
|
|
kind: None,
|
|
|
|
diagnostics: None,
|
|
|
|
edit: None,
|
|
|
|
command,
|
|
|
|
is_preferred: None,
|
|
|
|
}
|
|
|
|
.into();
|
2020-02-26 13:24:00 +03:00
|
|
|
}
|
2018-08-12 21:02:56 +03:00
|
|
|
}
|
|
|
|
|
2019-07-04 14:57:14 -04:00
|
|
|
Ok(Some(res))
|
2018-08-11 14:44:12 +03:00
|
|
|
}
|
|
|
|
|
2019-01-11 15:16:55 -05:00
|
|
|
pub fn handle_code_lens(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2019-01-11 15:16:55 -05:00
|
|
|
params: req::CodeLensParams,
|
|
|
|
) -> Result<Option<Vec<CodeLens>>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_code_lens");
|
2019-01-11 15:16:55 -05:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
2019-01-11 15:16:55 -05:00
|
|
|
|
|
|
|
let mut lenses: Vec<CodeLens> = Default::default();
|
|
|
|
|
2019-02-01 08:44:23 -05:00
|
|
|
// Gather runnables
|
2019-07-21 23:48:54 +03:00
|
|
|
for runnable in world.analysis().runnables(file_id)? {
|
2019-01-12 18:40:54 -05:00
|
|
|
let title = match &runnable.kind {
|
2020-02-13 00:17:35 +07:00
|
|
|
RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️\u{fe0e}Run Test",
|
2019-08-09 22:18:47 +03:00
|
|
|
RunnableKind::Bench { .. } => "Run Bench",
|
|
|
|
RunnableKind::Bin => "Run",
|
2019-08-09 23:34:14 +03:00
|
|
|
}
|
|
|
|
.to_string();
|
2020-03-12 21:28:26 +01:00
|
|
|
let mut r = to_lsp_runnable(&world, file_id, runnable)?;
|
2019-08-09 22:18:47 +03:00
|
|
|
let lens = CodeLens {
|
|
|
|
range: r.range,
|
|
|
|
command: Some(Command {
|
2019-08-09 23:34:14 +03:00
|
|
|
title,
|
2019-08-09 22:18:47 +03:00
|
|
|
command: "rust-analyzer.runSingle".into(),
|
2020-03-12 21:28:26 +01:00
|
|
|
arguments: Some(vec![to_value(&r).unwrap()]),
|
2019-08-09 22:18:47 +03:00
|
|
|
}),
|
|
|
|
data: None,
|
2019-01-12 18:40:54 -05:00
|
|
|
};
|
2019-08-09 22:18:47 +03:00
|
|
|
lenses.push(lens);
|
2020-03-12 22:31:47 +01:00
|
|
|
|
2020-04-02 12:56:14 +02:00
|
|
|
if r.args[0] == "run" {
|
|
|
|
r.args[0] = "build".into();
|
|
|
|
} else {
|
|
|
|
r.args.push("--no-run".into());
|
2020-03-12 22:31:47 +01:00
|
|
|
}
|
2020-04-02 12:56:14 +02:00
|
|
|
let debug_lens = CodeLens {
|
|
|
|
range: r.range,
|
|
|
|
command: Some(Command {
|
|
|
|
title: "Debug".into(),
|
|
|
|
command: "rust-analyzer.debugSingle".into(),
|
|
|
|
arguments: Some(vec![to_value(r).unwrap()]),
|
|
|
|
}),
|
|
|
|
data: None,
|
|
|
|
};
|
|
|
|
lenses.push(debug_lens);
|
2019-01-11 15:16:55 -05:00
|
|
|
}
|
|
|
|
|
2019-07-20 00:20:09 +03:00
|
|
|
// Handle impls
|
|
|
|
lenses.extend(
|
2019-07-21 23:48:54 +03:00
|
|
|
world
|
|
|
|
.analysis()
|
2019-07-25 20:22:41 +03:00
|
|
|
.file_structure(file_id)?
|
2019-07-20 00:20:09 +03:00
|
|
|
.into_iter()
|
|
|
|
.filter(|it| match it.kind {
|
|
|
|
SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true,
|
|
|
|
_ => false,
|
|
|
|
})
|
|
|
|
.map(|it| {
|
2019-02-01 08:44:23 -05:00
|
|
|
let range = it.node_range.conv_with(&line_index);
|
|
|
|
let pos = range.start;
|
|
|
|
let lens_params =
|
|
|
|
req::TextDocumentPositionParams::new(params.text_document.clone(), pos);
|
2019-07-20 00:20:09 +03:00
|
|
|
CodeLens {
|
2019-02-01 08:44:23 -05:00
|
|
|
range,
|
|
|
|
command: None,
|
|
|
|
data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()),
|
2019-07-20 00:20:09 +03:00
|
|
|
}
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
2019-02-06 15:50:26 -05:00
|
|
|
Ok(Some(lenses))
|
2019-01-11 15:16:55 -05:00
|
|
|
}
|
|
|
|
|
2019-02-01 08:44:23 -05:00
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
enum CodeLensResolveData {
|
|
|
|
Impls(req::TextDocumentPositionParams),
|
|
|
|
}
|
|
|
|
|
2019-06-01 10:31:40 +03:00
|
|
|
pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_code_lens_resolve");
|
2019-02-01 08:44:23 -05:00
|
|
|
let data = code_lens.data.unwrap();
|
2020-02-11 09:46:45 +01:00
|
|
|
let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?;
|
2019-02-01 08:44:23 -05:00
|
|
|
match resolve {
|
|
|
|
Some(CodeLensResolveData::Impls(lens_params)) => {
|
|
|
|
let locations: Vec<Location> =
|
|
|
|
match handle_goto_implementation(world, lens_params.clone())? {
|
|
|
|
Some(req::GotoDefinitionResponse::Scalar(loc)) => vec![loc],
|
|
|
|
Some(req::GotoDefinitionResponse::Array(locs)) => locs,
|
|
|
|
Some(req::GotoDefinitionResponse::Link(links)) => links
|
|
|
|
.into_iter()
|
|
|
|
.map(|link| Location::new(link.target_uri, link.target_selection_range))
|
|
|
|
.collect(),
|
|
|
|
_ => vec![],
|
|
|
|
};
|
|
|
|
|
|
|
|
let title = if locations.len() == 1 {
|
|
|
|
"1 implementation".into()
|
|
|
|
} else {
|
|
|
|
format!("{} implementations", locations.len())
|
|
|
|
};
|
|
|
|
|
2019-02-04 16:34:02 -05:00
|
|
|
// We cannot use the 'editor.action.showReferences' command directly
|
|
|
|
// because that command requires vscode types which we convert in the handler
|
|
|
|
// on the client side.
|
|
|
|
let cmd = Command {
|
|
|
|
title,
|
|
|
|
command: "rust-analyzer.showReferences".into(),
|
|
|
|
arguments: Some(vec![
|
2019-08-17 21:34:31 +03:00
|
|
|
to_value(&lens_params.text_document.uri).unwrap(),
|
2019-02-04 16:34:02 -05:00
|
|
|
to_value(code_lens.range.start).unwrap(),
|
|
|
|
to_value(locations).unwrap(),
|
|
|
|
]),
|
|
|
|
};
|
2019-02-08 14:49:43 +03:00
|
|
|
Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
|
2019-02-01 08:44:23 -05:00
|
|
|
}
|
2019-02-06 15:50:26 -05:00
|
|
|
None => Ok(CodeLens {
|
|
|
|
range: code_lens.range,
|
2019-02-08 14:49:43 +03:00
|
|
|
command: Some(Command { title: "Error".into(), ..Default::default() }),
|
2019-02-06 15:50:26 -05:00
|
|
|
data: None,
|
|
|
|
}),
|
2019-02-01 08:44:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-31 11:08:44 +00:00
|
|
|
pub fn handle_document_highlight(
|
2019-06-01 10:31:40 +03:00
|
|
|
world: WorldSnapshot,
|
2018-12-31 11:08:44 +00:00
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<Vec<DocumentHighlight>>> {
|
2019-10-23 18:26:43 +03:00
|
|
|
let _p = profile("handle_document_highlight");
|
2018-12-31 11:08:44 +00:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
2018-12-31 11:08:44 +00:00
|
|
|
|
2019-10-24 14:01:02 +03:00
|
|
|
let refs = match world
|
|
|
|
.analysis()
|
|
|
|
.find_all_refs(params.try_conv_with(&world)?, Some(SearchScope::single_file(file_id)))?
|
|
|
|
{
|
2019-02-17 13:38:32 +02:00
|
|
|
None => return Ok(None),
|
|
|
|
Some(refs) => refs,
|
|
|
|
};
|
2018-12-31 11:08:44 +00:00
|
|
|
|
|
|
|
Ok(Some(
|
|
|
|
refs.into_iter()
|
2020-01-04 17:46:01 -05:00
|
|
|
.filter(|reference| reference.file_range.file_id == file_id)
|
|
|
|
.map(|reference| DocumentHighlight {
|
|
|
|
range: reference.file_range.range.conv_with(&line_index),
|
|
|
|
kind: reference.access.map(|it| it.conv()),
|
2020-01-03 15:30:45 +00:00
|
|
|
})
|
2018-12-31 11:08:44 +00:00
|
|
|
.collect(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2020-02-10 22:45:38 +00:00
|
|
|
pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> {
|
|
|
|
let _p = profile("handle_ssr");
|
2020-03-15 21:23:18 +00:00
|
|
|
world
|
|
|
|
.analysis()
|
|
|
|
.structural_search_replace(¶ms.query, params.parse_only)??
|
|
|
|
.try_conv_with(&world)
|
2020-02-10 22:45:38 +00:00
|
|
|
}
|
|
|
|
|
2020-01-31 19:23:25 +01:00
|
|
|
pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("publish_diagnostics");
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
2020-01-31 19:23:25 +01:00
|
|
|
let diagnostics: Vec<Diagnostic> = world
|
2018-10-15 17:44:23 -04:00
|
|
|
.analysis()
|
2018-10-20 22:02:41 +03:00
|
|
|
.diagnostics(file_id)?
|
2018-08-10 23:30:11 +03:00
|
|
|
.into_iter()
|
2018-08-29 18:03:14 +03:00
|
|
|
.map(|d| Diagnostic {
|
2018-08-12 21:02:56 +03:00
|
|
|
range: d.range.conv_with(&line_index),
|
2019-08-06 09:29:06 +02:00
|
|
|
severity: Some(d.severity.conv()),
|
2018-08-10 23:30:11 +03:00
|
|
|
code: None,
|
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,
|
2019-12-11 18:34:01 +01:00
|
|
|
tags: None,
|
2018-10-15 17:44:23 -04:00
|
|
|
})
|
|
|
|
.collect();
|
2020-01-31 19:23:25 +01:00
|
|
|
Ok(DiagnosticTask::SetNative(file_id, diagnostics))
|
2018-08-10 23:30:11 +03:00
|
|
|
}
|
|
|
|
|
2019-08-09 22:18:47 +03:00
|
|
|
fn to_lsp_runnable(
|
|
|
|
world: &WorldSnapshot,
|
|
|
|
file_id: FileId,
|
|
|
|
runnable: Runnable,
|
|
|
|
) -> Result<req::Runnable> {
|
2020-02-18 12:17:47 +01:00
|
|
|
let spec = CargoTargetSpec::for_file(world, file_id)?;
|
2020-03-09 22:06:45 +01:00
|
|
|
let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?;
|
2019-08-09 22:18:47 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
2019-08-09 23:34:14 +03:00
|
|
|
let label = match &runnable.kind {
|
2020-04-22 22:52:12 +02:00
|
|
|
RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
|
2019-08-09 23:34:14 +03:00
|
|
|
RunnableKind::TestMod { path } => format!("test-mod {}", path),
|
2020-02-15 01:06:14 +02:00
|
|
|
RunnableKind::Bench { test_id } => format!("bench {}", test_id),
|
2019-08-09 23:34:14 +03:00
|
|
|
RunnableKind::Bin => "run binary".to_string(),
|
|
|
|
};
|
2019-08-09 22:18:47 +03:00
|
|
|
Ok(req::Runnable {
|
|
|
|
range: runnable.range.conv_with(&line_index),
|
|
|
|
label,
|
|
|
|
bin: "cargo".to_string(),
|
|
|
|
args,
|
2020-03-09 22:06:45 +01:00
|
|
|
extra_args,
|
2019-08-09 22:18:47 +03:00
|
|
|
env: {
|
|
|
|
let mut m = FxHashMap::default();
|
|
|
|
m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
|
|
|
|
m
|
|
|
|
},
|
|
|
|
cwd: world.workspace_root_for(file_id).map(|root| root.to_string_lossy().to_string()),
|
|
|
|
})
|
|
|
|
}
|
2020-03-09 22:06:45 +01:00
|
|
|
|
2019-07-22 21:52:47 +03:00
|
|
|
pub fn handle_inlay_hints(
|
|
|
|
world: WorldSnapshot,
|
|
|
|
params: InlayHintsParams,
|
|
|
|
) -> Result<Vec<InlayHint>> {
|
2019-10-29 16:08:36 -04:00
|
|
|
let _p = profile("handle_inlay_hints");
|
2019-07-22 21:52:47 +03:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
|
|
|
let analysis = world.analysis();
|
2019-07-25 20:22:41 +03:00
|
|
|
let line_index = analysis.file_line_index(file_id)?;
|
2019-07-22 21:52:47 +03:00
|
|
|
Ok(analysis
|
2020-03-31 16:02:55 +02:00
|
|
|
.inlay_hints(file_id, &world.config.inlay_hints)?
|
2019-07-22 21:52:47 +03:00
|
|
|
.into_iter()
|
2020-03-12 18:01:36 +01:00
|
|
|
.map_conv_with(&line_index)
|
2019-07-22 21:52:47 +03:00
|
|
|
.collect())
|
|
|
|
}
|
2019-12-30 09:12:06 -05:00
|
|
|
|
|
|
|
pub fn handle_call_hierarchy_prepare(
|
|
|
|
world: WorldSnapshot,
|
|
|
|
params: CallHierarchyPrepareParams,
|
|
|
|
) -> Result<Option<Vec<CallHierarchyItem>>> {
|
|
|
|
let _p = profile("handle_call_hierarchy_prepare");
|
|
|
|
let position = params.text_document_position_params.try_conv_with(&world)?;
|
|
|
|
let file_id = position.file_id;
|
|
|
|
|
|
|
|
let nav_info = match world.analysis().call_hierarchy(position)? {
|
|
|
|
None => return Ok(None),
|
|
|
|
Some(it) => it,
|
|
|
|
};
|
|
|
|
|
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
|
|
|
let RangeInfo { range, info: navs } = nav_info;
|
|
|
|
let res = navs
|
|
|
|
.into_iter()
|
|
|
|
.filter(|it| it.kind() == SyntaxKind::FN_DEF)
|
|
|
|
.filter_map(|it| to_call_hierarchy_item(file_id, range, &world, &line_index, it).ok())
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
Ok(Some(res))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn handle_call_hierarchy_incoming(
|
|
|
|
world: WorldSnapshot,
|
|
|
|
params: CallHierarchyIncomingCallsParams,
|
|
|
|
) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
|
|
|
|
let _p = profile("handle_call_hierarchy_incoming");
|
|
|
|
let item = params.item;
|
|
|
|
|
|
|
|
let doc = TextDocumentIdentifier::new(item.uri);
|
|
|
|
let frange: FileRange = (&doc, item.range).try_conv_with(&world)?;
|
|
|
|
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
|
|
|
|
|
|
|
|
let call_items = match world.analysis().incoming_calls(fpos)? {
|
|
|
|
None => return Ok(None),
|
|
|
|
Some(it) => it,
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut res = vec![];
|
|
|
|
|
|
|
|
for call_item in call_items.into_iter() {
|
|
|
|
let file_id = call_item.target.file_id();
|
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
|
|
|
let range = call_item.target.range();
|
|
|
|
let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?;
|
|
|
|
res.push(CallHierarchyIncomingCall {
|
|
|
|
from: item,
|
|
|
|
from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Some(res))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn handle_call_hierarchy_outgoing(
|
|
|
|
world: WorldSnapshot,
|
|
|
|
params: CallHierarchyOutgoingCallsParams,
|
|
|
|
) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
|
|
|
|
let _p = profile("handle_call_hierarchy_outgoing");
|
|
|
|
let item = params.item;
|
|
|
|
|
|
|
|
let doc = TextDocumentIdentifier::new(item.uri);
|
|
|
|
let frange: FileRange = (&doc, item.range).try_conv_with(&world)?;
|
|
|
|
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
|
|
|
|
|
|
|
|
let call_items = match world.analysis().outgoing_calls(fpos)? {
|
|
|
|
None => return Ok(None),
|
|
|
|
Some(it) => it,
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut res = vec![];
|
|
|
|
|
|
|
|
for call_item in call_items.into_iter() {
|
|
|
|
let file_id = call_item.target.file_id();
|
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
|
|
|
let range = call_item.target.range();
|
|
|
|
let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?;
|
|
|
|
res.push(CallHierarchyOutgoingCall {
|
|
|
|
to: item,
|
|
|
|
from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Some(res))
|
|
|
|
}
|
2020-02-14 17:56:28 -05:00
|
|
|
|
|
|
|
pub fn handle_semantic_tokens(
|
|
|
|
world: WorldSnapshot,
|
|
|
|
params: SemanticTokensParams,
|
|
|
|
) -> Result<Option<SemanticTokensResult>> {
|
|
|
|
let _p = profile("handle_semantic_tokens");
|
|
|
|
|
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2020-02-27 14:54:31 +01:00
|
|
|
let text = world.analysis().file_text(file_id)?;
|
2020-02-14 17:56:28 -05:00
|
|
|
let line_index = world.analysis().file_line_index(file_id)?;
|
|
|
|
|
|
|
|
let mut builder = SemanticTokensBuilder::default();
|
|
|
|
|
2020-02-26 19:39:32 +01:00
|
|
|
for highlight_range in world.analysis().highlight(file_id)?.into_iter() {
|
2020-02-27 14:54:31 +01:00
|
|
|
let (token_index, modifier_bitset) = highlight_range.highlight.conv();
|
|
|
|
for mut range in line_index.lines(highlight_range.range) {
|
|
|
|
if text[range].ends_with('\n') {
|
|
|
|
range = TextRange::from_to(range.start(), range.end() - TextUnit::of_char('\n'));
|
|
|
|
}
|
|
|
|
let range = range.conv_with(&line_index);
|
|
|
|
builder.push(range, token_index, modifier_bitset);
|
|
|
|
}
|
2020-02-14 17:56:28 -05:00
|
|
|
}
|
|
|
|
|
2020-03-30 12:07:27 -04:00
|
|
|
let tokens = builder.build();
|
2020-02-14 17:56:28 -05:00
|
|
|
|
|
|
|
Ok(Some(tokens.into()))
|
|
|
|
}
|
2020-02-25 08:38:50 -05:00
|
|
|
|
|
|
|
pub fn handle_semantic_tokens_range(
|
|
|
|
world: WorldSnapshot,
|
|
|
|
params: SemanticTokensRangeParams,
|
|
|
|
) -> Result<Option<SemanticTokensRangeResult>> {
|
|
|
|
let _p = profile("handle_semantic_tokens_range");
|
|
|
|
|
|
|
|
let frange = (¶ms.text_document, params.range).try_conv_with(&world)?;
|
|
|
|
let line_index = world.analysis().file_line_index(frange.file_id)?;
|
|
|
|
|
|
|
|
let mut builder = SemanticTokensBuilder::default();
|
|
|
|
|
2020-02-26 19:39:32 +01:00
|
|
|
for highlight_range in world.analysis().highlight_range(frange)?.into_iter() {
|
|
|
|
let (token_type, token_modifiers) = highlight_range.highlight.conv();
|
|
|
|
builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers);
|
2020-02-25 08:38:50 -05:00
|
|
|
}
|
|
|
|
|
2020-03-30 12:07:27 -04:00
|
|
|
let tokens = builder.build();
|
2020-02-25 08:38:50 -05:00
|
|
|
|
|
|
|
Ok(Some(tokens.into()))
|
|
|
|
}
|