From 865759925be6b72f7ef39124ed0e4c86c0412a69 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 18 Feb 2020 12:37:45 +0100 Subject: Rename folder --- crates/ra_lsp_server/src/main_loop/handlers.rs | 1070 -------------------- .../src/main_loop/pending_requests.rs | 75 -- .../ra_lsp_server/src/main_loop/subscriptions.rs | 22 - 3 files changed, 1167 deletions(-) delete mode 100644 crates/ra_lsp_server/src/main_loop/handlers.rs delete mode 100644 crates/ra_lsp_server/src/main_loop/pending_requests.rs delete mode 100644 crates/ra_lsp_server/src/main_loop/subscriptions.rs (limited to 'crates/ra_lsp_server/src/main_loop') diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs deleted file mode 100644 index bb7bab372..000000000 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ /dev/null @@ -1,1070 +0,0 @@ -//! This module is responsible for implementing handlers for Language Server -//! Protocol. The majority of requests are fulfilled by calling into the -//! `ra_ide` crate. - -use std::{ - collections::hash_map::Entry, - fmt::Write as _, - io::Write as _, - process::{self, Stdio}, -}; - -use lsp_server::ErrorCode; -use lsp_types::{ - CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, - CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, - CodeAction, CodeActionOrCommand, CodeActionResponse, CodeLens, Command, CompletionItem, - Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, - FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, - PrepareRenameResponse, Range, RenameParams, SymbolInformation, TextDocumentIdentifier, - TextEdit, WorkspaceEdit, -}; -use ra_ide::{ - AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, - SearchScope, -}; -use ra_prof::profile; -use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; -use rustc_hash::FxHashMap; -use serde::{Deserialize, Serialize}; -use serde_json::to_value; - -use crate::{ - cargo_target_spec::CargoTargetSpec, - conv::{ - to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith, - TryConvWithToVec, - }, - diagnostics::DiagnosticTask, - from_json, - req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind}, - world::WorldSnapshot, - LspError, Result, -}; - -pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result { - let _p = profile("handle_analyzer_status"); - let mut buf = world.status(); - writeln!(buf, "\n\nrequests:").unwrap(); - let requests = world.latest_requests.read(); - for (is_last, r) in requests.iter() { - let mark = if is_last { "*" } else { " " }; - writeln!(buf, "{}{:4} {:<36}{}ms", mark, r.id, r.method, r.duration.as_millis()).unwrap(); - } - Ok(buf) -} - -pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) -> Result { - let _p = profile("handle_syntax_tree"); - let id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(id)?; - let text_range = params.range.map(|p| p.conv_with(&line_index)); - let res = world.analysis().syntax_tree(id, text_range)?; - Ok(res) -} - -pub fn handle_expand_macro( - world: WorldSnapshot, - params: req::ExpandMacroParams, -) -> Result> { - 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), - Some(offset) => { - let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; - Ok(res.map(|it| req::ExpandedMacro { name: it.name, expansion: it.expansion })) - } - } -} - -pub fn handle_selection_range( - world: WorldSnapshot, - params: req::SelectionRangeParams, -) -> Result> { - let _p = profile("handle_selection_range"); - let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id)?; - params - .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) - }) - .collect() -} - -pub fn handle_find_matching_brace( - world: WorldSnapshot, - params: req::FindMatchingBraceParams, -) -> Result> { - let _p = profile("handle_find_matching_brace"); - let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id)?; - let res = params - .offsets - .into_iter() - .map_conv_with(&line_index) - .map(|offset| { - if let Ok(Some(matching_brace_offset)) = - world.analysis().matching_brace(FilePosition { file_id, offset }) - { - matching_brace_offset - } else { - offset - } - }) - .map_conv_with(&line_index) - .collect(); - Ok(res) -} - -pub fn handle_join_lines( - world: WorldSnapshot, - params: req::JoinLinesParams, -) -> Result { - let _p = profile("handle_join_lines"); - let frange = (¶ms.text_document, params.range).try_conv_with(&world)?; - world.analysis().join_lines(frange)?.try_conv_with(&world) -} - -pub fn handle_on_enter( - world: WorldSnapshot, - params: req::TextDocumentPositionParams, -) -> Result> { - let _p = profile("handle_on_enter"); - let position = params.try_conv_with(&world)?; - match world.analysis().on_enter(position)? { - None => Ok(None), - Some(edit) => Ok(Some(edit.try_conv_with(&world)?)), - } -} - -// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. -pub fn handle_on_type_formatting( - world: WorldSnapshot, - params: req::DocumentOnTypeFormattingParams, -) -> Result>> { - let _p = profile("handle_on_type_formatting"); - let mut position = params.text_document_position.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(position.file_id)?; - let line_endings = world.file_line_endings(position.file_id); - - // in `ra_ide`, the `on_type` invariant is that - // `text.char_at(position) == typed_char`. - position.offset -= TextUnit::of_char('.'); - let char_typed = params.ch.chars().next().unwrap_or('\0'); - - // 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); - } - - let edit = world.analysis().on_char_typed(position, char_typed)?; - let mut edit = match edit { - Some(it) => it, - None => return Ok(None), - }; - - // This should be a single-file edit - let edit = edit.source_file_edits.pop().unwrap(); - - let change: Vec = edit.edit.conv_with((&line_index, line_endings)); - Ok(Some(change)) -} - -pub fn handle_document_symbol( - world: WorldSnapshot, - params: req::DocumentSymbolParams, -) -> Result> { - let _p = profile("handle_document_symbol"); - let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id)?; - - let mut parents: Vec<(DocumentSymbol, Option)> = Vec::new(); - - for symbol in world.analysis().file_structure(file_id)? { - let doc_symbol = DocumentSymbol { - name: symbol.label, - detail: symbol.detail, - kind: symbol.kind.conv(), - deprecated: Some(symbol.deprecated), - range: symbol.node_range.conv_with(&line_index), - selection_range: symbol.navigation_range.conv_with(&line_index), - children: None, - }; - 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); - } - } - } - - Ok(Some(res.into())) -} - -pub fn handle_workspace_symbol( - world: WorldSnapshot, - params: req::WorkspaceSymbolParams, -) -> Result>> { - let _p = profile("handle_workspace_symbol"); - let all_symbols = params.query.contains('#'); - let libs = params.query.contains('*'); - let query = { - let query: String = params.query.chars().filter(|&c| c != '#' && c != '*').collect(); - let mut q = Query::new(query); - if !all_symbols { - q.only_types(); - } - if libs { - q.libs(); - } - q.limit(128); - q - }; - let mut res = exec_query(&world, query)?; - if res.is_empty() && !all_symbols { - let mut query = Query::new(params.query); - query.limit(128); - res = exec_query(&world, query)?; - } - - return Ok(Some(res)); - - fn exec_query(world: &WorldSnapshot, query: Query) -> Result> { - let mut res = Vec::new(); - for nav in world.analysis().symbol_search(query)? { - let info = SymbolInformation { - name: nav.name().to_string(), - kind: nav.kind().conv(), - location: nav.try_conv_with(world)?, - container_name: nav.container_name().map(|v| v.to_string()), - deprecated: None, - }; - res.push(info); - } - Ok(res) - } -} - -pub fn handle_goto_definition( - world: WorldSnapshot, - params: req::TextDocumentPositionParams, -) -> Result> { - let _p = profile("handle_goto_definition"); - let position = params.try_conv_with(&world)?; - let nav_info = match world.analysis().goto_definition(position)? { - None => return Ok(None), - Some(it) => it, - }; - let res = (position.file_id, nav_info).try_conv_with(&world)?; - Ok(Some(res)) -} - -pub fn handle_goto_implementation( - world: WorldSnapshot, - params: req::TextDocumentPositionParams, -) -> Result> { - let _p = profile("handle_goto_implementation"); - let position = params.try_conv_with(&world)?; - let nav_info = match world.analysis().goto_implementation(position)? { - None => return Ok(None), - Some(it) => it, - }; - let res = (position.file_id, nav_info).try_conv_with(&world)?; - Ok(Some(res)) -} - -pub fn handle_goto_type_definition( - world: WorldSnapshot, - params: req::TextDocumentPositionParams, -) -> Result> { - let _p = profile("handle_goto_type_definition"); - let position = params.try_conv_with(&world)?; - let nav_info = match world.analysis().goto_type_definition(position)? { - None => return Ok(None), - Some(it) => it, - }; - let res = (position.file_id, nav_info).try_conv_with(&world)?; - Ok(Some(res)) -} - -pub fn handle_parent_module( - world: WorldSnapshot, - params: req::TextDocumentPositionParams, -) -> Result> { - let _p = profile("handle_parent_module"); - let position = params.try_conv_with(&world)?; - world.analysis().parent_module(position)?.iter().try_conv_with_to_vec(&world) -} - -pub fn handle_runnables( - world: WorldSnapshot, - params: req::RunnablesParams, -) -> Result> { - let _p = profile("handle_runnables"); - 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(); - let workspace_root = world.workspace_root_for(file_id); - for runnable in world.analysis().runnables(file_id)? { - if let Some(offset) = offset { - if !runnable.range.contains_inclusive(offset) { - continue; - } - } - res.push(to_lsp_runnable(&world, file_id, runnable)?); - } - let mut check_args = vec!["check".to_string()]; - let label; - match CargoTargetSpec::for_file(&world, file_id)? { - 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()) - } - } - // Always add `cargo check`. - res.push(req::Runnable { - range: Default::default(), - label, - bin: "cargo".to_string(), - args: check_args, - env: FxHashMap::default(), - cwd: workspace_root.map(|root| root.to_string_lossy().to_string()), - }); - Ok(res) -} - -pub fn handle_decorations( - world: WorldSnapshot, - params: TextDocumentIdentifier, -) -> Result> { - let _p = profile("handle_decorations"); - let file_id = params.try_conv_with(&world)?; - highlight(&world, file_id) -} - -pub fn handle_completion( - world: WorldSnapshot, - params: req::CompletionParams, -) -> Result> { - let _p = profile("handle_completion"); - let position = params.text_document_position.try_conv_with(&world)?; - let completion_triggered_after_single_colon = { - let mut res = false; - if let Some(ctx) = params.context { - if ctx.trigger_character.unwrap_or_default() == ":" { - let source_file = world.analysis().parse(position.file_id)?; - 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); - } - - let items = match world.analysis().completions(position)? { - None => return Ok(None), - Some(items) => items, - }; - let line_index = world.analysis().file_line_index(position.file_id)?; - let line_endings = world.file_line_endings(position.file_id); - let items: Vec = - items.into_iter().map(|item| item.conv_with((&line_index, line_endings))).collect(); - - Ok(Some(items.into())) -} - -pub fn handle_folding_range( - world: WorldSnapshot, - params: FoldingRangeParams, -) -> Result>> { - let _p = profile("handle_folding_range"); - let file_id = params.text_document.try_conv_with(&world)?; - let folds = world.analysis().folding_ranges(file_id)?; - let text = world.analysis().file_text(file_id)?; - let line_index = world.analysis().file_line_index(file_id)?; - let ctx = FoldConvCtx { - text: &text, - line_index: &line_index, - line_folding_only: world.options.line_folding_only, - }; - let res = Some(folds.into_iter().map_conv_with(&ctx).collect()); - Ok(res) -} - -pub fn handle_signature_help( - world: WorldSnapshot, - params: req::TextDocumentPositionParams, -) -> Result> { - let _p = profile("handle_signature_help"); - let position = params.try_conv_with(&world)?; - if let Some(call_info) = world.analysis().call_info(position)? { - let active_parameter = call_info.active_parameter.map(|it| it as i64); - let sig_info = call_info.signature.conv(); - - Ok(Some(req::SignatureHelp { - signatures: vec![sig_info], - active_signature: Some(0), - active_parameter, - })) - } else { - Ok(None) - } -} - -pub fn handle_hover( - world: WorldSnapshot, - params: req::TextDocumentPositionParams, -) -> Result> { - let _p = profile("handle_hover"); - let position = params.try_conv_with(&world)?; - let info = match world.analysis().hover(position)? { - None => return Ok(None), - Some(info) => info, - }; - let line_index = world.analysis.file_line_index(position.file_id)?; - let range = info.range.conv_with(&line_index); - let res = Hover { - contents: HoverContents::Markup(MarkupContent { - kind: MarkupKind::Markdown, - value: crate::markdown::format_docs(&info.info.to_markup()), - }), - range: Some(range), - }; - Ok(Some(res)) -} - -pub fn handle_prepare_rename( - world: WorldSnapshot, - params: req::TextDocumentPositionParams, -) -> Result> { - let _p = profile("handle_prepare_rename"); - let position = params.try_conv_with(&world)?; - - let optional_change = world.analysis().rename(position, "dummy")?; - let range = match optional_change { - None => return Ok(None), - Some(it) => it.range, - }; - - let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id)?; - let range = range.conv_with(&line_index); - Ok(Some(PrepareRenameResponse::Range(range))) -} - -pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result> { - let _p = profile("handle_rename"); - let position = params.text_document_position.try_conv_with(&world)?; - - if params.new_name.is_empty() { - return Err(LspError::new( - ErrorCode::InvalidParams as i32, - "New Name cannot be empty".into(), - ) - .into()); - } - - let optional_change = world.analysis().rename(position, &*params.new_name)?; - let change = match optional_change { - None => return Ok(None), - Some(it) => it.info, - }; - - let source_change_req = change.try_conv_with(&world)?; - - Ok(Some(source_change_req.workspace_edit)) -} - -pub fn handle_references( - world: WorldSnapshot, - params: req::ReferenceParams, -) -> Result>> { - let _p = profile("handle_references"); - let position = params.text_document_position.try_conv_with(&world)?; - - let refs = match world.analysis().find_all_refs(position, None)? { - None => return Ok(None), - Some(refs) => refs, - }; - - let locations = if params.context.include_declaration { - refs.into_iter() - .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() - }) - .collect() - } else { - // Only iterate over the references if include_declaration was false - refs.references() - .iter() - .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() - }) - .collect() - }; - - Ok(Some(locations)) -} - -pub fn handle_formatting( - world: WorldSnapshot, - params: DocumentFormattingParams, -) -> Result>> { - let _p = profile("handle_formatting"); - let file_id = params.text_document.try_conv_with(&world)?; - let file = world.analysis().file_text(file_id)?; - let crate_ids = world.analysis().crate_for(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); - - let mut rustfmt = process::Command::new("rustfmt"); - rustfmt.args(&world.options.rustfmt_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)?; - rustfmt.args(&["--edition", &edition.to_string()]); - } - - if let Ok(path) = params.text_document.uri.to_file_path() { - if let Some(parent) = path.parent() { - rustfmt.current_dir(parent); - } - } - let mut rustfmt = rustfmt.stdin(Stdio::piped()).stdout(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() { - 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()); - } - } - } - - Ok(Some(vec![TextEdit { - range: Range::new(Position::new(0, 0), end_position), - new_text: captured_stdout, - }])) -} - -pub fn handle_code_action( - world: WorldSnapshot, - params: req::CodeActionParams, -) -> Result> { - let _p = profile("handle_code_action"); - let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id)?; - let range = params.range.conv_with(&line_index); - - let diagnostics = world.analysis().diagnostics(file_id)?; - let mut res = CodeActionResponse::default(); - - let fixes_from_diagnostics = diagnostics - .into_iter() - .filter_map(|d| Some((d.range, d.fix?))) - .filter(|(diag_range, _fix)| diag_range.intersection(&range).is_some()) - .map(|(_range, fix)| fix); - - for source_edit in fixes_from_diagnostics { - let title = source_edit.label.clone(); - let edit = source_edit.try_conv_with(&world)?; - - let command = Command { - title, - command: "rust-analyzer.applySourceChange".to_string(), - arguments: Some(vec![to_value(edit).unwrap()]), - }; - let action = CodeAction { - title: command.title.clone(), - kind: None, - diagnostics: None, - edit: None, - command: Some(command), - is_preferred: None, - }; - res.push(action.into()); - } - - for fix in world.check_fixes.get(&file_id).into_iter().flatten() { - let fix_range = fix.range.conv_with(&line_index); - if fix_range.intersection(&range).is_none() { - continue; - } - res.push(fix.action.clone()); - } - - let mut groups = FxHashMap::default(); - for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() { - let arg = to_value(assist.source_change.try_conv_with(&world)?)?; - - let (command, title, arg) = match assist.group_label { - None => ("rust-analyzer.applySourceChange", assist.label.clone(), arg), - - // Group all assists with the same `group_label` into a single CodeAction. - Some(group_label) => { - match groups.entry(group_label.clone()) { - Entry::Occupied(entry) => { - let idx: usize = *entry.get(); - match &mut res[idx] { - CodeActionOrCommand::CodeAction(CodeAction { - command: Some(Command { arguments: Some(arguments), .. }), - .. - }) => match arguments.as_mut_slice() { - [serde_json::Value::Array(arguments)] => arguments.push(arg), - _ => panic!("invalid group"), - }, - _ => panic!("invalid group"), - } - continue; - } - Entry::Vacant(entry) => { - entry.insert(res.len()); - } - } - ("rust-analyzer.selectAndApplySourceChange", group_label, to_value(vec![arg])?) - } - }; - - let command = Command { - title: assist.label.clone(), - command: command.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, - }; - - let action = CodeAction { - title, - kind, - diagnostics: None, - edit: None, - command: Some(command), - is_preferred: None, - }; - res.push(action.into()); - } - - Ok(Some(res)) -} - -pub fn handle_code_lens( - world: WorldSnapshot, - params: req::CodeLensParams, -) -> Result>> { - let _p = profile("handle_code_lens"); - let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id)?; - - let mut lenses: Vec = Default::default(); - - // Gather runnables - for runnable in world.analysis().runnables(file_id)? { - let title = match &runnable.kind { - RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️\u{fe0e}Run Test", - RunnableKind::Bench { .. } => "Run Bench", - RunnableKind::Bin => "Run", - } - .to_string(); - let r = to_lsp_runnable(&world, file_id, runnable)?; - let lens = CodeLens { - range: r.range, - command: Some(Command { - title, - command: "rust-analyzer.runSingle".into(), - arguments: Some(vec![to_value(r).unwrap()]), - }), - data: None, - }; - - lenses.push(lens); - } - - // Handle impls - lenses.extend( - world - .analysis() - .file_structure(file_id)? - .into_iter() - .filter(|it| match it.kind { - SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true, - _ => false, - }) - .map(|it| { - let range = it.node_range.conv_with(&line_index); - let pos = range.start; - let lens_params = - req::TextDocumentPositionParams::new(params.text_document.clone(), pos); - CodeLens { - range, - command: None, - data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()), - } - }), - ); - - Ok(Some(lenses)) -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -enum CodeLensResolveData { - Impls(req::TextDocumentPositionParams), -} - -pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result { - let _p = profile("handle_code_lens_resolve"); - let data = code_lens.data.unwrap(); - let resolve = from_json::>("CodeLensResolveData", data)?; - match resolve { - Some(CodeLensResolveData::Impls(lens_params)) => { - let locations: Vec = - 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()) - }; - - // 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![ - to_value(&lens_params.text_document.uri).unwrap(), - to_value(code_lens.range.start).unwrap(), - to_value(locations).unwrap(), - ]), - }; - Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) - } - None => Ok(CodeLens { - range: code_lens.range, - command: Some(Command { title: "Error".into(), ..Default::default() }), - data: None, - }), - } -} - -pub fn handle_document_highlight( - world: WorldSnapshot, - params: req::TextDocumentPositionParams, -) -> Result>> { - let _p = profile("handle_document_highlight"); - let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id)?; - - let refs = match world - .analysis() - .find_all_refs(params.try_conv_with(&world)?, Some(SearchScope::single_file(file_id)))? - { - None => return Ok(None), - Some(refs) => refs, - }; - - Ok(Some( - refs.into_iter() - .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()), - }) - .collect(), - )) -} - -pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result { - let _p = profile("handle_ssr"); - world.analysis().structural_search_replace(¶ms.arg)??.try_conv_with(&world) -} - -pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result { - let _p = profile("publish_diagnostics"); - let line_index = world.analysis().file_line_index(file_id)?; - let diagnostics: Vec = world - .analysis() - .diagnostics(file_id)? - .into_iter() - .map(|d| Diagnostic { - range: d.range.conv_with(&line_index), - severity: Some(d.severity.conv()), - code: None, - source: Some("rust-analyzer".to_string()), - message: d.message, - related_information: None, - tags: None, - }) - .collect(); - Ok(DiagnosticTask::SetNative(file_id, diagnostics)) -} - -pub fn publish_decorations( - world: &WorldSnapshot, - file_id: FileId, -) -> Result { - let _p = profile("publish_decorations"); - let uri = world.file_id_to_uri(file_id)?; - Ok(req::PublishDecorationsParams { uri, decorations: highlight(&world, file_id)? }) -} - -fn to_lsp_runnable( - world: &WorldSnapshot, - file_id: FileId, - runnable: Runnable, -) -> Result { - let spec = CargoTargetSpec::for_file(world, file_id)?; - let args = CargoTargetSpec::runnable_args(spec, &runnable.kind)?; - let line_index = world.analysis().file_line_index(file_id)?; - let label = match &runnable.kind { - RunnableKind::Test { test_id } => format!("test {}", test_id), - RunnableKind::TestMod { path } => format!("test-mod {}", path), - RunnableKind::Bench { test_id } => format!("bench {}", test_id), - RunnableKind::Bin => "run binary".to_string(), - }; - Ok(req::Runnable { - range: runnable.range.conv_with(&line_index), - label, - bin: "cargo".to_string(), - args, - 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()), - }) -} -fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result> { - let line_index = world.analysis().file_line_index(file_id)?; - let res = world - .analysis() - .highlight(file_id)? - .into_iter() - .map(|h| Decoration { - range: h.range.conv_with(&line_index), - tag: h.tag, - binding_hash: h.binding_hash.map(|x| x.to_string()), - }) - .collect(); - Ok(res) -} - -pub fn handle_inlay_hints( - world: WorldSnapshot, - params: InlayHintsParams, -) -> Result> { - let _p = profile("handle_inlay_hints"); - let file_id = params.text_document.try_conv_with(&world)?; - let analysis = world.analysis(); - let line_index = analysis.file_line_index(file_id)?; - Ok(analysis - .inlay_hints(file_id, world.options.max_inlay_hint_length)? - .into_iter() - .map(|api_type| InlayHint { - label: api_type.label.to_string(), - range: api_type.range.conv_with(&line_index), - kind: match api_type.kind { - ra_ide::InlayKind::TypeHint => InlayKind::TypeHint, - ra_ide::InlayKind::ParameterHint => InlayKind::ParameterHint, - }, - }) - .collect()) -} - -pub fn handle_call_hierarchy_prepare( - world: WorldSnapshot, - params: CallHierarchyPrepareParams, -) -> Result>> { - 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>> { - 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>> { - 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)) -} diff --git a/crates/ra_lsp_server/src/main_loop/pending_requests.rs b/crates/ra_lsp_server/src/main_loop/pending_requests.rs deleted file mode 100644 index 73b33e419..000000000 --- a/crates/ra_lsp_server/src/main_loop/pending_requests.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! Data structures that keep track of inflight requests. - -use std::time::{Duration, Instant}; - -use lsp_server::RequestId; -use rustc_hash::FxHashMap; - -#[derive(Debug)] -pub struct CompletedRequest { - pub id: RequestId, - pub method: String, - pub duration: Duration, -} - -#[derive(Debug)] -pub(crate) struct PendingRequest { - pub(crate) id: RequestId, - pub(crate) method: String, - pub(crate) received: Instant, -} - -impl From for CompletedRequest { - fn from(pending: PendingRequest) -> CompletedRequest { - CompletedRequest { - id: pending.id, - method: pending.method, - duration: pending.received.elapsed(), - } - } -} - -#[derive(Debug, Default)] -pub(crate) struct PendingRequests { - map: FxHashMap, -} - -impl PendingRequests { - pub(crate) fn start(&mut self, request: PendingRequest) { - let id = request.id.clone(); - let prev = self.map.insert(id.clone(), request); - assert!(prev.is_none(), "duplicate request with id {}", id); - } - pub(crate) fn cancel(&mut self, id: &RequestId) -> bool { - self.map.remove(id).is_some() - } - pub(crate) fn finish(&mut self, id: &RequestId) -> Option { - self.map.remove(id).map(CompletedRequest::from) - } -} - -const N_COMPLETED_REQUESTS: usize = 10; - -#[derive(Debug, Default)] -pub struct LatestRequests { - // hand-rolling VecDeque here to print things in a nicer way - buf: [Option; N_COMPLETED_REQUESTS], - idx: usize, -} - -impl LatestRequests { - pub(crate) fn record(&mut self, request: CompletedRequest) { - // special case: don't track status request itself - if request.method == "rust-analyzer/analyzerStatus" { - return; - } - let idx = self.idx; - self.buf[idx] = Some(request); - self.idx = (idx + 1) % N_COMPLETED_REQUESTS; - } - - pub(crate) fn iter(&self) -> impl Iterator { - let idx = self.idx; - self.buf.iter().enumerate().filter_map(move |(i, req)| Some((i == idx, req.as_ref()?))) - } -} diff --git a/crates/ra_lsp_server/src/main_loop/subscriptions.rs b/crates/ra_lsp_server/src/main_loop/subscriptions.rs deleted file mode 100644 index bee6437cf..000000000 --- a/crates/ra_lsp_server/src/main_loop/subscriptions.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! Keeps track of file subscriptions -- the set of currently opened files for -//! which we want to publish diagnostics, syntax highlighting, etc. - -use ra_ide::FileId; -use rustc_hash::FxHashSet; - -#[derive(Default, Debug)] -pub(crate) struct Subscriptions { - subs: FxHashSet, -} - -impl Subscriptions { - pub(crate) fn add_sub(&mut self, file_id: FileId) { - self.subs.insert(file_id); - } - pub(crate) fn remove_sub(&mut self, file_id: FileId) { - self.subs.remove(&file_id); - } - pub(crate) fn subscriptions(&self) -> Vec { - self.subs.iter().cloned().collect() - } -} -- cgit v1.2.3