From dbbb0beb3ec9f11a635f43e60f3b3a42ba61338a Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 25 Jul 2019 20:22:41 +0300 Subject: Make Analysis api cancellable --- crates/ra_cli/src/analysis_bench.rs | 2 +- crates/ra_ide_api/src/lib.rs | 86 ++++++++++++++------------ crates/ra_ide_api/src/references.rs | 3 +- crates/ra_ide_api/src/syntax_tree.rs | 14 ++--- crates/ra_ide_api/src/typing.rs | 4 +- crates/ra_lsp_server/src/conv.rs | 14 ++--- crates/ra_lsp_server/src/main_loop/handlers.rs | 64 ++++++++++--------- crates/ra_lsp_server/src/world.rs | 7 ++- 8 files changed, 107 insertions(+), 87 deletions(-) (limited to 'crates') diff --git a/crates/ra_cli/src/analysis_bench.rs b/crates/ra_cli/src/analysis_bench.rs index 639ac763e..5e9d0c16d 100644 --- a/crates/ra_cli/src/analysis_bench.rs +++ b/crates/ra_cli/src/analysis_bench.rs @@ -55,7 +55,7 @@ pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { Op::Complete { line, column, .. } => { let offset = host .analysis() - .file_line_index(file_id) + .file_line_index(file_id)? .offset(LineCol { line, col_utf16: column }); let file_postion = FilePosition { file_id, offset }; diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 16ffb03ce..edb646c11 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -317,24 +317,24 @@ impl Analysis { } /// Debug info about the current state of the analysis - pub fn status(&self) -> String { - status::status(&*self.db) + pub fn status(&self) -> Cancelable { + self.with_db(|db| status::status(&*db)) } /// Gets the text of the source file. - pub fn file_text(&self, file_id: FileId) -> Arc { - self.db.file_text(file_id) + pub fn file_text(&self, file_id: FileId) -> Cancelable> { + self.with_db(|db| db.file_text(file_id)) } /// Gets the syntax tree of the file. - pub fn parse(&self, file_id: FileId) -> SourceFile { - self.db.parse(file_id).tree() + pub fn parse(&self, file_id: FileId) -> Cancelable { + self.with_db(|db| db.parse(file_id).tree()) } /// Gets the file's `LineIndex`: data structure to convert between absolute /// offsets and line/column representation. - pub fn file_line_index(&self, file_id: FileId) -> Arc { - self.db.line_index(file_id) + pub fn file_line_index(&self, file_id: FileId) -> Cancelable> { + self.with_db(|db| db.line_index(file_id)) } /// Selects the next syntactic nodes encompassing the range. @@ -344,58 +344,67 @@ impl Analysis { /// Returns position of the matching brace (all types of braces are /// supported). - pub fn matching_brace(&self, position: FilePosition) -> Option { - let parse = self.db.parse(position.file_id); - let file = parse.tree(); - matching_brace::matching_brace(&file, position.offset) + pub fn matching_brace(&self, position: FilePosition) -> Cancelable> { + self.with_db(|db| { + let parse = db.parse(position.file_id); + let file = parse.tree(); + matching_brace::matching_brace(&file, position.offset) + }) } /// Returns a syntax tree represented as `String`, for debug purposes. // FIXME: use a better name here. - pub fn syntax_tree(&self, file_id: FileId, text_range: Option) -> String { - syntax_tree::syntax_tree(&self.db, file_id, text_range) + pub fn syntax_tree( + &self, + file_id: FileId, + text_range: Option, + ) -> Cancelable { + self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) } /// Returns an edit to remove all newlines in the range, cleaning up minor /// stuff like trailing commas. - pub fn join_lines(&self, frange: FileRange) -> SourceChange { - let parse = self.db.parse(frange.file_id); - let file_edit = SourceFileEdit { - file_id: frange.file_id, - edit: join_lines::join_lines(&parse.tree(), frange.range), - }; - SourceChange::source_file_edit("join lines", file_edit) + pub fn join_lines(&self, frange: FileRange) -> Cancelable { + self.with_db(|db| { + let parse = db.parse(frange.file_id); + let file_edit = SourceFileEdit { + file_id: frange.file_id, + edit: join_lines::join_lines(&parse.tree(), frange.range), + }; + SourceChange::source_file_edit("join lines", file_edit) + }) } /// Returns an edit which should be applied when opening a new line, fixing /// up minor stuff like continuing the comment. - pub fn on_enter(&self, position: FilePosition) -> Option { - typing::on_enter(&self.db, position) + pub fn on_enter(&self, position: FilePosition) -> Cancelable> { + self.with_db(|db| typing::on_enter(&db, position)) } /// Returns an edit which should be applied after `=` was typed. Primarily, /// this works when adding `let =`. // FIXME: use a snippet completion instead of this hack here. - pub fn on_eq_typed(&self, position: FilePosition) -> Option { - let parse = self.db.parse(position.file_id); - let file = parse.tree(); - let edit = typing::on_eq_typed(&file, position.offset)?; - Some(SourceChange::source_file_edit( - "add semicolon", - SourceFileEdit { edit, file_id: position.file_id }, - )) + pub fn on_eq_typed(&self, position: FilePosition) -> Cancelable> { + self.with_db(|db| { + let parse = db.parse(position.file_id); + let file = parse.tree(); + let edit = typing::on_eq_typed(&file, position.offset)?; + Some(SourceChange::source_file_edit( + "add semicolon", + SourceFileEdit { edit, file_id: position.file_id }, + )) + }) } /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. - pub fn on_dot_typed(&self, position: FilePosition) -> Option { - typing::on_dot_typed(&self.db, position) + pub fn on_dot_typed(&self, position: FilePosition) -> Cancelable> { + self.with_db(|db| typing::on_dot_typed(&db, position)) } /// Returns a tree representation of symbols in the file. Useful to draw a /// file outline. - pub fn file_structure(&self, file_id: FileId) -> Vec { - let parse = self.db.parse(file_id); - file_structure(&parse.tree()) + pub fn file_structure(&self, file_id: FileId) -> Cancelable> { + self.with_db(|db| file_structure(&db.parse(file_id).tree())) } /// Returns a list of the places in the file where type hints can be displayed. @@ -404,9 +413,8 @@ impl Analysis { } /// Returns the set of folding ranges. - pub fn folding_ranges(&self, file_id: FileId) -> Vec { - let parse = self.db.parse(file_id); - folding_ranges::folding_ranges(&parse.tree()) + pub fn folding_ranges(&self, file_id: FileId) -> Cancelable> { + self.with_db(|db| folding_ranges::folding_ranges(&db.parse(file_id).tree())) } /// Fuzzy searches for a symbol. diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index 5c74d3e36..89984e642 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs @@ -372,7 +372,8 @@ mod tests { } } } - let result = text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap())); + let result = + text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap()).unwrap()); assert_eq_text!(expected, &*result); } } diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs index a07e670fa..dd31b9093 100644 --- a/crates/ra_ide_api/src/syntax_tree.rs +++ b/crates/ra_ide_api/src/syntax_tree.rs @@ -101,7 +101,7 @@ mod tests { fn test_syntax_tree_without_range() { // Basic syntax let (analysis, file_id) = single_file(r#"fn foo() {}"#); - let syn = analysis.syntax_tree(file_id, None); + let syn = analysis.syntax_tree(file_id, None).unwrap(); assert_eq_text!( syn.trim(), @@ -133,7 +133,7 @@ fn test() { }"# .trim(), ); - let syn = analysis.syntax_tree(file_id, None); + let syn = analysis.syntax_tree(file_id, None).unwrap(); assert_eq_text!( syn.trim(), @@ -176,7 +176,7 @@ SOURCE_FILE@[0; 60) #[test] fn test_syntax_tree_with_range() { let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); - let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); assert_eq_text!( syn.trim(), @@ -206,7 +206,7 @@ FN_DEF@[0; 11) }"# .trim(), ); - let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); assert_eq_text!( syn.trim(), @@ -244,7 +244,7 @@ fn bar() { }"# .trim(), ); - let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); assert_eq_text!( syn.trim(), r#" @@ -278,7 +278,7 @@ fn bar() { }"### .trim(), ); - let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); assert_eq_text!( syn.trim(), r#" @@ -311,7 +311,7 @@ fn bar() { }"### .trim(), ); - let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); assert_eq_text!( syn.trim(), r#" diff --git a/crates/ra_ide_api/src/typing.rs b/crates/ra_ide_api/src/typing.rs index 6b3fd5904..2d4491442 100644 --- a/crates/ra_ide_api/src/typing.rs +++ b/crates/ra_ide_api/src/typing.rs @@ -195,7 +195,7 @@ fn foo() { edit.insert(offset, ".".to_string()); let before = edit.finish().apply(&before); let (analysis, file_id) = single_file(&before); - if let Some(result) = analysis.on_dot_typed(FilePosition { offset, file_id }) { + if let Some(result) = analysis.on_dot_typed(FilePosition { offset, file_id }).unwrap() { assert_eq!(result.source_file_edits.len(), 1); let actual = result.source_file_edits[0].edit.apply(&before); assert_eq_text!(after, &actual); @@ -377,7 +377,7 @@ fn foo() { fn apply_on_enter(before: &str) -> Option { let (offset, before) = extract_offset(before); let (analysis, file_id) = single_file(&before); - let result = analysis.on_enter(FilePosition { offset, file_id })?; + let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; assert_eq!(result.source_file_edits.len(), 1); let actual = result.source_file_edits[0].edit.apply(&before); diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 82c7e757f..6b3be444f 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -272,7 +272,7 @@ impl<'a> TryConvWith for &'a TextDocumentPositionParams { type Output = FilePosition; fn try_conv_with(self, world: &WorldSnapshot) -> Result { let file_id = self.text_document.try_conv_with(world)?; - let line_index = world.analysis().file_line_index(file_id); + let line_index = world.analysis().file_line_index(file_id)?; let offset = self.position.conv_with(&line_index); Ok(FilePosition { file_id, offset }) } @@ -283,7 +283,7 @@ impl<'a> TryConvWith for (&'a TextDocumentIdentifier, Range) { type Output = FileRange; fn try_conv_with(self, world: &WorldSnapshot) -> Result { let file_id = self.0.try_conv_with(world)?; - let line_index = world.analysis().file_line_index(file_id); + let line_index = world.analysis().file_line_index(file_id)?; let range = self.1.conv_with(&line_index); Ok(FileRange { file_id, range }) } @@ -308,7 +308,7 @@ impl TryConvWith for SourceChange { let cursor_position = match self.cursor_position { None => None, Some(pos) => { - let line_index = world.analysis().file_line_index(pos.file_id); + let line_index = world.analysis().file_line_index(pos.file_id)?; let edit = self .source_file_edits .iter() @@ -349,7 +349,7 @@ impl TryConvWith for SourceFileEdit { uri: self.file_id.try_conv_with(world)?, version: None, }; - let line_index = world.analysis().file_line_index(self.file_id); + let line_index = world.analysis().file_line_index(self.file_id)?; let edits = self.edit.as_atoms().iter().map_conv_with(&line_index).collect(); Ok(TextDocumentEdit { text_document, edits }) } @@ -378,7 +378,7 @@ impl TryConvWith for &NavigationTarget { type Ctx = WorldSnapshot; type Output = Location; fn try_conv_with(self, world: &WorldSnapshot) -> Result { - let line_index = world.analysis().file_line_index(self.file_id()); + let line_index = world.analysis().file_line_index(self.file_id())?; let range = self.range(); to_location(self.file_id(), range, &world, &line_index) } @@ -391,8 +391,8 @@ impl TryConvWith for (FileId, RangeInfo) { let (src_file_id, target) = self; let target_uri = target.info.file_id().try_conv_with(world)?; - let src_line_index = world.analysis().file_line_index(src_file_id); - let tgt_line_index = world.analysis().file_line_index(target.info.file_id()); + let src_line_index = world.analysis().file_line_index(src_file_id)?; + let tgt_line_index = world.analysis().file_line_index(target.info.file_id())?; let target_range = target.info.full_range().conv_with(&tgt_line_index); diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 5bf950a53..14619ede2 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -39,9 +39,9 @@ pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result { pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) -> Result { let id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(id); + 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); + let res = world.analysis().syntax_tree(id, text_range)?; Ok(res) } @@ -55,7 +55,7 @@ pub fn handle_extend_selection( use the new selection range API in LSP", ); let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id); + let line_index = world.analysis().file_line_index(file_id)?; let selections = params .selections .into_iter() @@ -72,7 +72,7 @@ pub fn handle_selection_range( ) -> 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); + let line_index = world.analysis().file_line_index(file_id)?; params .positions .into_iter() @@ -113,13 +113,19 @@ pub fn handle_find_matching_brace( ) -> 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 line_index = world.analysis().file_line_index(file_id)?; let res = params .offsets .into_iter() .map_conv_with(&line_index) .map(|offset| { - world.analysis().matching_brace(FilePosition { file_id, offset }).unwrap_or(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(); @@ -132,7 +138,7 @@ pub fn handle_join_lines( ) -> 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) + world.analysis().join_lines(frange)?.try_conv_with(&world) } pub fn handle_on_enter( @@ -141,7 +147,7 @@ pub fn handle_on_enter( ) -> Result> { let _p = profile("handle_on_enter"); let position = params.try_conv_with(&world)?; - match world.analysis().on_enter(position) { + match world.analysis().on_enter(position)? { None => Ok(None), Some(edit) => Ok(Some(edit.try_conv_with(&world)?)), } @@ -153,7 +159,7 @@ pub fn handle_on_type_formatting( ) -> 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_index = world.analysis().file_line_index(position.file_id)?; // in `ra_ide_api`, the `on_type` invariant is that // `text.char_at(position) == typed_char`. @@ -163,7 +169,7 @@ pub fn handle_on_type_formatting( "=" => 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), @@ -181,11 +187,11 @@ pub fn handle_document_symbol( params: req::DocumentSymbolParams, ) -> Result> { let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id); + 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) { + for symbol in world.analysis().file_structure(file_id)? { let doc_symbol = DocumentSymbol { name: symbol.label, detail: symbol.detail, @@ -309,7 +315,7 @@ pub fn handle_runnables( params: req::RunnablesParams, ) -> Result> { let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id); + 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); @@ -383,7 +389,7 @@ pub fn handle_completion( 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 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) { @@ -405,7 +411,7 @@ pub fn handle_completion( None => return Ok(None), Some(items) => items, }; - let line_index = world.analysis().file_line_index(position.file_id); + let line_index = world.analysis().file_line_index(position.file_id)?; let items: Vec = items.into_iter().map(|item| item.conv_with(&line_index)).collect(); @@ -417,12 +423,12 @@ pub fn handle_folding_range( params: FoldingRangeParams, ) -> Result>> { let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id); + let line_index = world.analysis().file_line_index(file_id)?; let res = Some( world .analysis() - .folding_ranges(file_id) + .folding_ranges(file_id)? .into_iter() .map(|fold| { let kind = match fold.kind { @@ -474,7 +480,7 @@ pub fn handle_hover( None => return Ok(None), Some(info) => info, }; - let line_index = world.analysis.file_line_index(position.file_id); + 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 { @@ -503,7 +509,7 @@ pub fn handle_prepare_rename( // Refs should always have a declaration let r = refs.declaration(); let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id); + let line_index = world.analysis().file_line_index(file_id)?; let loc = to_location(r.file_id(), r.range(), &world, &line_index)?; Ok(Some(PrepareRenameResponse::Range(loc.range))) @@ -536,7 +542,7 @@ pub fn handle_references( params: req::ReferenceParams, ) -> Result>> { let position = params.text_document_position.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(position.file_id); + let line_index = world.analysis().file_line_index(position.file_id)?; let refs = match world.analysis().find_all_refs(position)? { None => return Ok(None), @@ -563,9 +569,9 @@ pub fn handle_formatting( params: DocumentFormattingParams, ) -> Result>> { let file_id = params.text_document.try_conv_with(&world)?; - let file = world.analysis().file_text(file_id); + let file = world.analysis().file_text(file_id)?; - let file_line_index = world.analysis().file_line_index(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; @@ -623,7 +629,7 @@ pub fn handle_code_action( ) -> 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 line_index = world.analysis().file_line_index(file_id)?; let range = params.range.conv_with(&line_index); let assists = world.analysis().assists(FileRange { file_id, range })?.into_iter(); @@ -685,7 +691,7 @@ pub fn handle_code_lens( params: req::CodeLensParams, ) -> Result>> { let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id); + let line_index = world.analysis().file_line_index(file_id)?; let mut lenses: Vec = Default::default(); let workspace_root = world.workspace_root_for(file_id); @@ -730,7 +736,7 @@ pub fn handle_code_lens( lenses.extend( world .analysis() - .file_structure(file_id) + .file_structure(file_id)? .into_iter() .filter(|it| match it.kind { SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true, @@ -807,7 +813,7 @@ pub fn handle_document_highlight( params: req::TextDocumentPositionParams, ) -> Result>> { let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id); + let line_index = world.analysis().file_line_index(file_id)?; let refs = match world.analysis().find_all_refs(params.try_conv_with(&world)?)? { None => return Ok(None), @@ -826,7 +832,7 @@ pub fn publish_diagnostics( file_id: FileId, ) -> Result { let uri = world.file_id_to_uri(file_id)?; - let line_index = world.analysis().file_line_index(file_id); + let line_index = world.analysis().file_line_index(file_id)?; let diagnostics = world .analysis() .diagnostics(file_id)? @@ -852,7 +858,7 @@ pub fn publish_decorations( } fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result> { - let line_index = world.analysis().file_line_index(file_id); + let line_index = world.analysis().file_line_index(file_id)?; let res = world .analysis() .highlight(file_id)? @@ -881,7 +887,7 @@ pub fn handle_inlay_hints( ) -> Result> { let file_id = params.text_document.try_conv_with(&world)?; let analysis = world.analysis(); - let line_index = analysis.file_line_index(file_id); + let line_index = analysis.file_line_index(file_id)?; Ok(analysis .inlay_hints(file_id)? .into_iter() diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index 9fd654305..1d7755910 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs @@ -215,7 +215,12 @@ impl WorldSnapshot { } } res.push_str("\nanalysis:\n"); - res.push_str(&self.analysis.status()); + res.push_str( + &self + .analysis + .status() + .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()), + ); res } -- cgit v1.2.3