2018-10-31 23:41:43 +03:00
|
|
|
use gen_lsp_server::ErrorCode;
|
2019-01-14 13:55:56 +03:00
|
|
|
use lsp_types::{
|
2019-01-12 18:40:54 -05:00
|
|
|
CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity,
|
|
|
|
DocumentFormattingParams, DocumentHighlight, DocumentSymbol, Documentation, FoldingRange,
|
|
|
|
FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent,
|
|
|
|
MarkupKind, ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range,
|
|
|
|
RenameParams, SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit,
|
2019-01-17 19:57:24 +08:00
|
|
|
WorkspaceEdit
|
2018-08-12 21:02:56 +03:00
|
|
|
};
|
2019-01-08 22:33:36 +03:00
|
|
|
use ra_ide_api::{
|
2019-01-20 21:05:01 +03:00
|
|
|
FileId, FilePosition, FileRange, FoldKind, Query, RangeInfo, RunnableKind, Severity, Cancelable,
|
2019-01-06 00:58:03 +01:00
|
|
|
};
|
2019-01-12 18:40:54 -05:00
|
|
|
use ra_syntax::{AstNode, TextUnit};
|
2018-10-31 23:41:43 +03:00
|
|
|
use rustc_hash::FxHashMap;
|
2018-08-29 18:03:14 +03:00
|
|
|
use serde_json::to_value;
|
2018-12-29 20:09:42 +01:00
|
|
|
use std::io::Write;
|
2018-08-10 23:30:11 +03:00
|
|
|
|
2018-10-15 20:15:53 +03:00
|
|
|
use crate::{
|
2019-01-12 18:40:54 -05:00
|
|
|
cargo_target_spec::{runnable_args, CargoTargetSpec},
|
2019-01-11 16:01:48 +03:00
|
|
|
conv::{to_location, to_location_link, Conv, ConvWith, MapConvWith, TryConvWith},
|
2018-10-15 17:44:23 -04:00
|
|
|
req::{self, Decoration},
|
|
|
|
server_world::ServerWorld,
|
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-01-23 00:15:03 +03:00
|
|
|
pub fn handle_analyzer_status(world: ServerWorld, _: ()) -> Result<String> {
|
|
|
|
Ok(world.status())
|
|
|
|
}
|
|
|
|
|
2018-10-31 23:41:43 +03:00
|
|
|
pub fn handle_syntax_tree(world: ServerWorld, params: req::SyntaxTreeParams) -> Result<String> {
|
2018-08-17 19:54:08 +03:00
|
|
|
let id = params.text_document.try_conv_with(&world)?;
|
2018-08-29 18:03:14 +03:00
|
|
|
let res = world.analysis().syntax_tree(id);
|
|
|
|
Ok(res)
|
2018-08-10 21:13:39 +03:00
|
|
|
}
|
2018-08-10 22:23:17 +03:00
|
|
|
|
|
|
|
pub fn handle_extend_selection(
|
2018-08-17 19:54:08 +03:00
|
|
|
world: ServerWorld,
|
2018-08-10 22:23:17 +03:00
|
|
|
params: req::ExtendSelectionParams,
|
|
|
|
) -> Result<req::ExtendSelectionResult> {
|
2018-08-17 19:54:08 +03:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2018-08-29 18:03:14 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id);
|
2018-10-15 17:44:23 -04:00
|
|
|
let selections = params
|
|
|
|
.selections
|
|
|
|
.into_iter()
|
2018-08-13 02:38:34 +03:00
|
|
|
.map_conv_with(&line_index)
|
2018-12-28 18:15:19 +03:00
|
|
|
.map(|range| FileRange { file_id, range })
|
2019-01-20 21:05:01 +03:00
|
|
|
.map(|frange| {
|
|
|
|
world
|
|
|
|
.analysis()
|
|
|
|
.extend_selection(frange)
|
|
|
|
.map(|it| it.conv_with(&line_index))
|
|
|
|
})
|
|
|
|
.collect::<Cancelable<Vec<_>>>()?;
|
2018-08-10 22:23:17 +03:00
|
|
|
Ok(req::ExtendSelectionResult { selections })
|
|
|
|
}
|
|
|
|
|
2018-08-16 00:23:22 +03:00
|
|
|
pub fn handle_find_matching_brace(
|
2018-08-17 19:54:08 +03:00
|
|
|
world: ServerWorld,
|
2018-08-16 00:23:22 +03:00
|
|
|
params: req::FindMatchingBraceParams,
|
|
|
|
) -> Result<Vec<Position>> {
|
2018-08-17 19:54:08 +03:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2018-08-29 18:03:14 +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| {
|
2018-10-15 17:44:23 -04:00
|
|
|
world
|
|
|
|
.analysis()
|
2019-01-20 20:59:46 +03:00
|
|
|
.matching_brace(FilePosition { file_id, offset })
|
2018-10-15 17:44:23 -04:00
|
|
|
.unwrap_or(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(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: req::JoinLinesParams,
|
2018-08-29 18:03:14 +03:00
|
|
|
) -> Result<req::SourceChange> {
|
2018-12-28 18:15:19 +03:00
|
|
|
let frange = (¶ms.text_document, params.range).try_conv_with(&world)?;
|
|
|
|
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(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<req::SourceChange>> {
|
2018-11-05 14:57:41 +03:00
|
|
|
let position = params.try_conv_with(&world)?;
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-29 18:03:14 +03:00
|
|
|
pub fn handle_on_type_formatting(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: req::DocumentOnTypeFormattingParams,
|
|
|
|
) -> Result<Option<Vec<TextEdit>>> {
|
2019-01-11 15:05:40 +03:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
|
|
|
let line_index = world.analysis().file_line_index(file_id);
|
|
|
|
let position = FilePosition {
|
|
|
|
file_id,
|
|
|
|
/// in `ra_ide_api`, the `on_type` invariant is that
|
|
|
|
/// `text.char_at(position) == typed_char`.
|
|
|
|
offset: params.position.conv_with(&line_index) - TextUnit::of_char('.'),
|
2018-11-05 14:57:41 +03:00
|
|
|
};
|
2019-01-06 00:58:03 +01:00
|
|
|
|
2019-01-11 15:05:40 +03:00
|
|
|
let edit = match params.ch.as_str() {
|
|
|
|
"=" => world.analysis().on_eq_typed(position),
|
|
|
|
"." => world.analysis().on_dot_typed(position),
|
|
|
|
_ => return Ok(None),
|
|
|
|
};
|
|
|
|
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-01-11 15:05:40 +03:00
|
|
|
let change: Vec<TextEdit> = edit.edit.conv_with(&line_index);
|
|
|
|
return Ok(Some(change));
|
2018-08-23 22:14:51 +03:00
|
|
|
}
|
|
|
|
|
2018-08-11 14:44:12 +03:00
|
|
|
pub fn handle_document_symbol(
|
2018-08-17 19:54:08 +03:00
|
|
|
world: ServerWorld,
|
2018-08-11 14:44:12 +03:00
|
|
|
params: req::DocumentSymbolParams,
|
|
|
|
) -> Result<Option<req::DocumentSymbolResponse>> {
|
2018-08-17 19:54:08 +03:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2018-08-29 18:03:14 +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
|
|
|
|
2018-08-29 18:03:14 +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(),
|
2018-08-11 14:44:12 +03:00
|
|
|
deprecated: None,
|
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
|
|
|
|
2018-08-11 14:44:12 +03:00
|
|
|
Ok(Some(req::DocumentSymbolResponse::Nested(res)))
|
|
|
|
}
|
|
|
|
|
2018-08-13 15:35:53 +03:00
|
|
|
pub fn handle_workspace_symbol(
|
2018-08-17 19:54:08 +03:00
|
|
|
world: ServerWorld,
|
2018-08-13 15:35:53 +03:00
|
|
|
params: req::WorkspaceSymbolParams,
|
|
|
|
) -> Result<Option<Vec<SymbolInformation>>> {
|
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 = {
|
2018-10-15 17:44:23 -04:00
|
|
|
let query: String = params
|
|
|
|
.query
|
|
|
|
.chars()
|
2018-09-03 21:03:37 +03:00
|
|
|
.filter(|&c| c != '#' && c != '*')
|
2018-08-13 16:07:05 +03:00
|
|
|
.collect();
|
|
|
|
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));
|
|
|
|
|
2018-10-31 23:41:43 +03:00
|
|
|
fn exec_query(world: &ServerWorld, 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)?,
|
2018-08-14 13:33:44 +03:00
|
|
|
container_name: None,
|
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(
|
2018-08-17 19:54:08 +03:00
|
|
|
world: ServerWorld,
|
2018-08-13 16:35:17 +03:00
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<req::GotoDefinitionResponse>> {
|
2018-11-05 14:57:41 +03:00
|
|
|
let position = params.try_conv_with(&world)?;
|
2019-01-11 16:01:48 +03:00
|
|
|
let line_index = world.analysis().file_line_index(position.file_id);
|
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-01-11 14:14:09 +03:00
|
|
|
let nav_range = nav_info.range;
|
|
|
|
let res = nav_info
|
|
|
|
.info
|
2019-01-02 17:09:39 +03:00
|
|
|
.into_iter()
|
2019-01-11 14:14:09 +03:00
|
|
|
.map(|nav| RangeInfo::new(nav_range, nav))
|
2019-01-11 16:01:48 +03:00
|
|
|
.map(|nav| to_location_link(&nav, &world, &line_index))
|
2019-01-02 17:09:39 +03:00
|
|
|
.collect::<Result<Vec<_>>>()?;
|
2019-01-11 16:01:48 +03:00
|
|
|
Ok(Some(req::GotoDefinitionResponse::Link(res)))
|
2018-08-13 16:35:17 +03:00
|
|
|
}
|
|
|
|
|
2018-08-22 10:18:58 +03:00
|
|
|
pub fn handle_parent_module(
|
|
|
|
world: ServerWorld,
|
2018-11-05 14:10:20 +03:00
|
|
|
params: req::TextDocumentPositionParams,
|
2018-08-22 10:18:58 +03:00
|
|
|
) -> Result<Vec<Location>> {
|
2018-11-05 14:57:41 +03:00
|
|
|
let position = params.try_conv_with(&world)?;
|
2019-01-02 17:09:39 +03:00
|
|
|
world
|
|
|
|
.analysis()
|
|
|
|
.parent_module(position)?
|
|
|
|
.into_iter()
|
|
|
|
.map(|nav| nav.try_conv_with(&world))
|
|
|
|
.collect::<Result<Vec<_>>>()
|
2018-08-22 10:18:58 +03:00
|
|
|
}
|
|
|
|
|
2018-08-29 18:03:14 +03:00
|
|
|
pub fn handle_runnables(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: req::RunnablesParams,
|
|
|
|
) -> Result<Vec<req::Runnable>> {
|
|
|
|
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(|it| it.conv_with(&line_index));
|
|
|
|
let mut res = Vec::new();
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-20 22:02:41 +03:00
|
|
|
let args = runnable_args(&world, file_id, &runnable.kind)?;
|
2018-09-02 16:36:03 +03:00
|
|
|
|
2018-08-29 18:03:14 +03:00
|
|
|
let r = req::Runnable {
|
|
|
|
range: runnable.range.conv_with(&line_index),
|
|
|
|
label: match &runnable.kind {
|
2018-10-15 17:44:23 -04:00
|
|
|
RunnableKind::Test { name } => format!("test {}", name),
|
2018-12-27 21:45:16 +01:00
|
|
|
RunnableKind::TestMod { path } => format!("test-mod {}", path),
|
2019-01-12 18:40:54 -05:00
|
|
|
RunnableKind::Bench { name } => format!("bench {}", name),
|
2018-10-15 17:44:23 -04:00
|
|
|
RunnableKind::Bin => "run binary".to_string(),
|
2018-08-29 18:03:14 +03:00
|
|
|
},
|
|
|
|
bin: "cargo".to_string(),
|
2018-09-02 16:36:03 +03:00
|
|
|
args,
|
2018-08-29 18:03:14 +03:00
|
|
|
env: {
|
2018-10-12 00:07:44 +06:00
|
|
|
let mut m = FxHashMap::default();
|
2018-10-15 17:44:23 -04:00
|
|
|
m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
|
2018-08-29 18:03:14 +03:00
|
|
|
m
|
2018-10-15 17:44:23 -04:00
|
|
|
},
|
2018-08-29 18:03:14 +03:00
|
|
|
};
|
|
|
|
res.push(r);
|
|
|
|
}
|
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,
|
2018-10-25 10:29:39 +03:00
|
|
|
env: FxHashMap::default(),
|
|
|
|
});
|
2018-08-29 18:03:14 +03:00
|
|
|
return Ok(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn handle_decorations(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: TextDocumentIdentifier,
|
|
|
|
) -> Result<Vec<Decoration>> {
|
|
|
|
let file_id = params.try_conv_with(&world)?;
|
2018-10-20 22:02:41 +03:00
|
|
|
highlight(&world, file_id)
|
2018-08-29 18:03:14 +03:00
|
|
|
}
|
|
|
|
|
2018-08-26 12:51:45 +03:00
|
|
|
pub fn handle_completion(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: req::CompletionParams,
|
|
|
|
) -> Result<Option<req::CompletionResponse>> {
|
2018-11-05 14:57:41 +03:00
|
|
|
let position = {
|
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
|
|
|
let line_index = world.analysis().file_line_index(file_id);
|
|
|
|
let offset = params.position.conv_with(&line_index);
|
|
|
|
FilePosition { file_id, offset }
|
|
|
|
};
|
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-01-20 21:01:11 +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);
|
|
|
|
}
|
|
|
|
|
2018-11-05 14:57:41 +03:00
|
|
|
let items = match world.analysis().completions(position)? {
|
2018-08-26 12:51:45 +03:00
|
|
|
None => return Ok(None),
|
|
|
|
Some(items) => items,
|
|
|
|
};
|
2019-01-19 22:02:50 +08:00
|
|
|
let line_index = world.analysis().file_line_index(position.file_id);
|
|
|
|
let items = items
|
|
|
|
.into_iter()
|
|
|
|
.map(|item| item.conv_with(&line_index))
|
|
|
|
.collect();
|
2018-08-26 12:51:45 +03:00
|
|
|
|
|
|
|
Ok(Some(req::CompletionResponse::Array(items)))
|
|
|
|
}
|
|
|
|
|
2018-09-23 11:13:27 -04:00
|
|
|
pub fn handle_folding_range(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: FoldingRangeParams,
|
|
|
|
) -> Result<Option<Vec<FoldingRange>>> {
|
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
|
|
|
let line_index = world.analysis().file_line_index(file_id);
|
|
|
|
|
2018-10-15 17:44:23 -04:00
|
|
|
let res = Some(
|
|
|
|
world
|
|
|
|
.analysis()
|
|
|
|
.folding_ranges(file_id)
|
|
|
|
.into_iter()
|
|
|
|
.map(|fold| {
|
|
|
|
let kind = match fold.kind {
|
2018-12-20 22:13:16 +03:00
|
|
|
FoldKind::Comment => Some(FoldingRangeKind::Comment),
|
|
|
|
FoldKind::Imports => Some(FoldingRangeKind::Imports),
|
2019-01-25 23:37:45 +01:00
|
|
|
FoldKind::Mods => None,
|
2018-12-20 22:13:16 +03:00
|
|
|
FoldKind::Block => None,
|
2018-10-15 17:44:23 -04:00
|
|
|
};
|
|
|
|
let range = fold.range.conv_with(&line_index);
|
|
|
|
FoldingRange {
|
|
|
|
start_line: range.start.line,
|
|
|
|
start_character: Some(range.start.character),
|
|
|
|
end_line: range.end.line,
|
|
|
|
end_character: Some(range.start.character),
|
2018-12-20 22:13:16 +03:00
|
|
|
kind,
|
2018-10-15 17:44:23 -04:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
);
|
2018-09-23 11:13:27 -04:00
|
|
|
|
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(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<req::SignatureHelp>> {
|
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)? {
|
|
|
|
let parameters: Vec<ParameterInformation> = call_info
|
|
|
|
.parameters
|
|
|
|
.into_iter()
|
2018-10-15 17:44:23 -04:00
|
|
|
.map(|param| ParameterInformation {
|
2018-12-19 17:32:44 -05:00
|
|
|
label: ParameterLabel::Simple(param.clone()),
|
2018-10-15 17:44:23 -04:00
|
|
|
documentation: None,
|
|
|
|
})
|
|
|
|
.collect();
|
2019-01-08 18:27:44 +03:00
|
|
|
let documentation = call_info.doc.map(|value| {
|
|
|
|
Documentation::MarkupContent(MarkupContent {
|
2018-10-30 14:07:14 -04:00
|
|
|
kind: MarkupKind::Markdown,
|
2019-01-08 18:27:44 +03:00
|
|
|
value,
|
|
|
|
})
|
|
|
|
});
|
2018-10-09 10:08:17 -04:00
|
|
|
let sig_info = SignatureInformation {
|
2019-01-08 18:27:44 +03:00
|
|
|
label: call_info.label,
|
2018-10-30 14:07:14 -04:00
|
|
|
documentation,
|
2018-10-15 17:44:23 -04:00
|
|
|
parameters: Some(parameters),
|
2018-10-09 10:08:17 -04:00
|
|
|
};
|
|
|
|
Ok(Some(req::SignatureHelp {
|
|
|
|
signatures: vec![sig_info],
|
|
|
|
active_signature: Some(0),
|
2019-01-08 18:27:44 +03:00
|
|
|
active_parameter: call_info.active_parameter.map(|it| it as u64),
|
2018-10-09 10:08:17 -04:00
|
|
|
}))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-05 16:37:27 -05:00
|
|
|
pub fn handle_hover(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<Hover>> {
|
|
|
|
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-01-05 17:22:41 +03:00
|
|
|
let line_index = world.analysis.file_line_index(position.file_id);
|
|
|
|
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,
|
|
|
|
value: info.info,
|
|
|
|
}),
|
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-12-08 16:02:23 +00:00
|
|
|
/// Test doc comment
|
2018-10-19 15:25:10 -04:00
|
|
|
pub fn handle_prepare_rename(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<PrepareRenameResponse>> {
|
2018-11-05 14:57:41 +03:00
|
|
|
let position = params.try_conv_with(&world)?;
|
2018-10-19 15:25:10 -04:00
|
|
|
|
|
|
|
// We support renaming references like handle_rename does.
|
|
|
|
// In the future we may want to reject the renaming of things like keywords here too.
|
2018-11-05 14:57:41 +03:00
|
|
|
let refs = world.analysis().find_all_refs(position)?;
|
|
|
|
let r = match refs.first() {
|
|
|
|
Some(r) => r,
|
|
|
|
None => return Ok(None),
|
|
|
|
};
|
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
|
|
|
let line_index = world.analysis().file_line_index(file_id);
|
2018-10-19 15:25:10 -04:00
|
|
|
let loc = to_location(r.0, r.1, &world, &line_index)?;
|
|
|
|
|
|
|
|
Ok(Some(PrepareRenameResponse::Range(loc.range)))
|
|
|
|
}
|
|
|
|
|
2018-10-31 23:41:43 +03:00
|
|
|
pub fn handle_rename(world: ServerWorld, params: RenameParams) -> Result<Option<WorkspaceEdit>> {
|
2018-10-18 17:56:22 -04:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
|
|
|
let line_index = world.analysis().file_line_index(file_id);
|
|
|
|
let offset = params.position.conv_with(&line_index);
|
|
|
|
|
|
|
|
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-01-18 16:32:36 +08:00
|
|
|
let optional_change = world
|
2018-11-05 14:57:41 +03:00
|
|
|
.analysis()
|
2018-12-30 18:14:55 +00:00
|
|
|
.rename(FilePosition { file_id, offset }, &*params.new_name)?;
|
2019-01-18 16:32:36 +08:00
|
|
|
let change = match optional_change {
|
|
|
|
None => return Ok(None),
|
|
|
|
Some(it) => it,
|
|
|
|
};
|
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(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: req::ReferenceParams,
|
|
|
|
) -> Result<Option<Vec<Location>>> {
|
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
|
|
|
let line_index = world.analysis().file_line_index(file_id);
|
|
|
|
let offset = params.position.conv_with(&line_index);
|
|
|
|
|
2018-11-05 14:57:41 +03:00
|
|
|
let refs = world
|
|
|
|
.analysis()
|
|
|
|
.find_all_refs(FilePosition { file_id, offset })?;
|
2018-10-18 13:40:12 -04:00
|
|
|
|
2018-10-31 23:41:43 +03:00
|
|
|
Ok(Some(
|
|
|
|
refs.into_iter()
|
|
|
|
.filter_map(|r| to_location(r.0, r.1, &world, &line_index).ok())
|
|
|
|
.collect(),
|
|
|
|
))
|
2018-10-18 13:40:12 -04:00
|
|
|
}
|
|
|
|
|
2018-12-29 20:09:42 +01:00
|
|
|
pub fn handle_formatting(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: DocumentFormattingParams,
|
|
|
|
) -> Result<Option<Vec<TextEdit>>> {
|
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
|
|
|
let file = world.analysis().file_text(file_id);
|
|
|
|
|
|
|
|
let file_line_index = world.analysis().file_line_index(file_id);
|
|
|
|
let end_position = TextUnit::of_str(&file).conv_with(&file_line_index);
|
|
|
|
|
|
|
|
use std::process;
|
|
|
|
let mut rustfmt = process::Command::new("rustfmt")
|
|
|
|
.stdin(process::Stdio::piped())
|
|
|
|
.stdout(process::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)?;
|
|
|
|
if !output.status.success() {
|
2018-12-30 00:21:15 +01:00
|
|
|
failure::bail!(
|
2018-12-29 22:19:43 +01:00
|
|
|
"rustfmt exited with error code {}: {}.",
|
2018-12-30 00:21:15 +01:00
|
|
|
output.status,
|
|
|
|
captured_stdout,
|
|
|
|
);
|
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,
|
|
|
|
}]))
|
|
|
|
}
|
|
|
|
|
2018-08-29 18:03:14 +03:00
|
|
|
pub fn handle_code_action(
|
2018-08-28 11:12:42 +03:00
|
|
|
world: ServerWorld,
|
2018-08-29 18:03:14 +03:00
|
|
|
params: req::CodeActionParams,
|
2018-09-23 11:10:57 -04:00
|
|
|
) -> Result<Option<CodeActionResponse>> {
|
2018-08-28 11:12:42 +03:00
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
2018-08-29 18:03:14 +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
|
|
|
|
2018-12-28 18:15:19 +03:00
|
|
|
let assists = world
|
|
|
|
.analysis()
|
|
|
|
.assists(FileRange { file_id, range })?
|
|
|
|
.into_iter();
|
2018-10-15 17:44:23 -04:00
|
|
|
let fixes = world
|
|
|
|
.analysis()
|
2018-10-20 22:02:41 +03:00
|
|
|
.diagnostics(file_id)?
|
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
|
|
|
|
2018-08-29 18:03:14 +03:00
|
|
|
let mut res = Vec::new();
|
|
|
|
for source_edit in assists.chain(fixes) {
|
|
|
|
let title = source_edit.label.clone();
|
|
|
|
let edit = source_edit.try_conv_with(&world)?;
|
|
|
|
let cmd = Command {
|
|
|
|
title,
|
2018-09-29 21:59:34 +03:00
|
|
|
command: "ra-lsp.applySourceChange".to_string(),
|
2018-08-29 18:03:14 +03:00
|
|
|
arguments: Some(vec![to_value(edit).unwrap()]),
|
|
|
|
};
|
|
|
|
res.push(cmd);
|
2018-08-12 21:02:56 +03:00
|
|
|
}
|
|
|
|
|
2018-09-23 11:10:57 -04:00
|
|
|
Ok(Some(CodeActionResponse::Commands(res)))
|
2018-08-11 14:44:12 +03:00
|
|
|
}
|
|
|
|
|
2019-01-11 15:16:55 -05:00
|
|
|
pub fn handle_code_lens(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: req::CodeLensParams,
|
|
|
|
) -> Result<Option<Vec<CodeLens>>> {
|
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
|
|
|
let line_index = world.analysis().file_line_index(file_id);
|
|
|
|
|
|
|
|
let mut lenses: Vec<CodeLens> = Default::default();
|
|
|
|
|
|
|
|
for runnable in world.analysis().runnables(file_id)? {
|
2019-01-12 18:40:54 -05:00
|
|
|
let title = match &runnable.kind {
|
|
|
|
RunnableKind::Test { name: _ } | RunnableKind::TestMod { path: _ } => Some("Run Test"),
|
|
|
|
RunnableKind::Bench { name: _ } => Some("Run Bench"),
|
|
|
|
_ => None,
|
|
|
|
};
|
2019-01-11 15:16:55 -05:00
|
|
|
|
2019-01-12 18:40:54 -05:00
|
|
|
if let Some(title) = title {
|
|
|
|
let args = runnable_args(&world, file_id, &runnable.kind)?;
|
|
|
|
let range = runnable.range.conv_with(&line_index);
|
|
|
|
|
|
|
|
// This represents the actual command that will be run.
|
|
|
|
let r: req::Runnable = req::Runnable {
|
|
|
|
range,
|
|
|
|
label: Default::default(),
|
|
|
|
bin: "cargo".into(),
|
|
|
|
args,
|
|
|
|
env: Default::default(),
|
|
|
|
};
|
2019-01-11 15:16:55 -05:00
|
|
|
|
2019-01-12 18:40:54 -05:00
|
|
|
let lens = CodeLens {
|
|
|
|
range,
|
|
|
|
command: Some(Command {
|
|
|
|
title: title.into(),
|
|
|
|
command: "ra-lsp.run-single".into(),
|
|
|
|
arguments: Some(vec![to_value(r).unwrap()]),
|
|
|
|
}),
|
|
|
|
data: None,
|
|
|
|
};
|
|
|
|
|
|
|
|
lenses.push(lens);
|
|
|
|
}
|
2019-01-11 15:16:55 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return Ok(Some(lenses));
|
|
|
|
}
|
|
|
|
|
2018-12-31 11:08:44 +00:00
|
|
|
pub fn handle_document_highlight(
|
|
|
|
world: ServerWorld,
|
|
|
|
params: req::TextDocumentPositionParams,
|
|
|
|
) -> Result<Option<Vec<DocumentHighlight>>> {
|
|
|
|
let file_id = params.text_document.try_conv_with(&world)?;
|
|
|
|
let line_index = world.analysis().file_line_index(file_id);
|
|
|
|
|
|
|
|
let refs = world
|
|
|
|
.analysis()
|
2018-12-31 12:17:05 +00:00
|
|
|
.find_all_refs(params.try_conv_with(&world)?)?;
|
2018-12-31 11:08:44 +00:00
|
|
|
|
|
|
|
Ok(Some(
|
|
|
|
refs.into_iter()
|
|
|
|
.map(|r| DocumentHighlight {
|
|
|
|
range: r.1.conv_with(&line_index),
|
|
|
|
kind: None,
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2018-08-15 17:24:20 +03:00
|
|
|
pub fn publish_diagnostics(
|
2018-10-15 22:36:08 +03:00
|
|
|
world: &ServerWorld,
|
2018-08-30 16:27:09 +03:00
|
|
|
file_id: FileId,
|
2018-08-15 17:24:20 +03:00
|
|
|
) -> Result<req::PublishDiagnosticsParams> {
|
2018-08-30 16:27:09 +03:00
|
|
|
let uri = world.file_id_to_uri(file_id)?;
|
2018-08-29 18:03:14 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id);
|
2018-10-15 17:44:23 -04:00
|
|
|
let diagnostics = world
|
|
|
|
.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),
|
2018-12-24 23:00:18 +08:00
|
|
|
severity: Some(to_diagnostic_severity(d.severity)),
|
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,
|
2018-10-15 17:44:23 -04:00
|
|
|
})
|
|
|
|
.collect();
|
2018-08-10 23:30:11 +03:00
|
|
|
Ok(req::PublishDiagnosticsParams { uri, diagnostics })
|
|
|
|
}
|
|
|
|
|
2018-08-15 17:24:20 +03:00
|
|
|
pub fn publish_decorations(
|
2018-10-15 22:36:08 +03:00
|
|
|
world: &ServerWorld,
|
2018-08-30 16:27:09 +03:00
|
|
|
file_id: FileId,
|
2018-08-15 17:24:20 +03:00
|
|
|
) -> Result<req::PublishDecorationsParams> {
|
2018-08-30 16:27:09 +03:00
|
|
|
let uri = world.file_id_to_uri(file_id)?;
|
2018-08-28 00:20:59 +03:00
|
|
|
Ok(req::PublishDecorationsParams {
|
|
|
|
uri,
|
2018-10-20 22:02:41 +03:00
|
|
|
decorations: highlight(&world, file_id)?,
|
2018-08-28 00:20:59 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-10-20 22:02:41 +03:00
|
|
|
fn highlight(world: &ServerWorld, file_id: FileId) -> Result<Vec<Decoration>> {
|
2018-08-29 18:03:14 +03:00
|
|
|
let line_index = world.analysis().file_line_index(file_id);
|
2018-10-20 22:02:41 +03:00
|
|
|
let res = world
|
2018-10-15 17:44:23 -04:00
|
|
|
.analysis()
|
2018-10-20 22:02:41 +03:00
|
|
|
.highlight(file_id)?
|
2018-08-11 00:55:32 +03:00
|
|
|
.into_iter()
|
|
|
|
.map(|h| Decoration {
|
2018-08-12 21:02:56 +03:00
|
|
|
range: h.range.conv_with(&line_index),
|
2018-08-11 00:55:32 +03:00
|
|
|
tag: h.tag,
|
2018-10-15 17:44:23 -04:00
|
|
|
})
|
2018-10-20 22:02:41 +03:00
|
|
|
.collect();
|
|
|
|
Ok(res)
|
2018-08-11 00:55:32 +03:00
|
|
|
}
|
2018-12-24 00:39:33 +08:00
|
|
|
|
|
|
|
fn to_diagnostic_severity(severity: Severity) -> DiagnosticSeverity {
|
2019-01-08 22:33:36 +03:00
|
|
|
use ra_ide_api::Severity::*;
|
2018-12-24 00:39:33 +08:00
|
|
|
|
|
|
|
match severity {
|
|
|
|
Error => DiagnosticSeverity::Error,
|
2018-12-25 02:07:30 +03:00
|
|
|
WeakWarning => DiagnosticSeverity::Hint,
|
2018-12-24 00:39:33 +08:00
|
|
|
}
|
|
|
|
}
|