From f320af4d63302d2933b37794826f705f13caf8a0 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Tue, 21 Jan 2020 00:06:47 +0800 Subject: Implement Syntax Highlight inside Macro --- crates/ra_ide/src/expand.rs | 8 + crates/ra_ide/src/snapshots/highlighting.html | 10 + .../ra_ide/src/snapshots/rainbow_highlighting.html | 12 +- crates/ra_ide/src/syntax_highlighting.rs | 289 +++++++++++++-------- 4 files changed, 208 insertions(+), 111 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs index b82259a3d..831438c09 100644 --- a/crates/ra_ide/src/expand.rs +++ b/crates/ra_ide/src/expand.rs @@ -79,6 +79,14 @@ pub(crate) fn descend_into_macros( let source_analyzer = hir::SourceAnalyzer::new(db, src.with_value(src.value.parent()).as_ref(), None); + descend_into_macros_with_analyzer(db, &source_analyzer, src) +} + +pub(crate) fn descend_into_macros_with_analyzer( + db: &RootDatabase, + source_analyzer: &hir::SourceAnalyzer, + src: InFile, +) -> InFile { successors(Some(src), |token| { let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; let tt = macro_call.token_tree()?; diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index 1d130544f..1cc55e78b 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html @@ -34,6 +34,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd foo::<i32>(); } +macro_rules! def_fn { + ($($tt:tt)*) => {$($tt)*} +} + +def_fn!{ + fn bar() -> u32 { + 100 + } +} + // comment fn main() { println!("Hello, {}!", 92); diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index d90ee8540..918fd4b97 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html @@ -24,14 +24,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword\.control { color: #F0DFAF; font-weight: bold; }
fn main() {
-    let hello = "hello";
-    let x = hello.to_string();
-    let y = hello.to_string();
+    let hello = "hello";
+    let x = hello.to_string();
+    let y = hello.to_string();
 
-    let x = "other color please!";
-    let y = x.to_string();
+    let x = "other color please!";
+    let y = x.to_string();
 }
 
 fn bar() {
-    let mut hello = "hello";
+    let mut hello = "hello";
 }
\ No newline at end of file diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 0411977b9..530b984fc 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -1,14 +1,18 @@ //! FIXME: write short doc here -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashMap; -use hir::{InFile, Name, SourceBinder}; +use hir::{HirFileId, InFile, Name, SourceAnalyzer, SourceBinder}; use ra_db::SourceDatabase; use ra_prof::profile; -use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T}; +use ra_syntax::{ + ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, TextRange, + WalkEvent, T, +}; use crate::{ db::RootDatabase, + expand::descend_into_macros_with_analyzer, references::{ classify_name, classify_name_ref, NameKind::{self, *}, @@ -72,121 +76,186 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec u64 { - fn hash(x: T) -> u64 { - use std::{collections::hash_map::DefaultHasher, hash::Hasher}; + let mut sb = SourceBinder::new(db); + let mut bindings_shadow_count: FxHashMap = FxHashMap::default(); + let mut res = Vec::new(); + let analyzer = sb.analyze(InFile::new(file_id.into(), &root), None); - let mut hasher = DefaultHasher::new(); - x.hash(&mut hasher); - hasher.finish() + let mut in_macro_call = None; + + for event in root.preorder_with_tokens() { + match event { + WalkEvent::Enter(node) => match node.kind() { + MACRO_CALL => { + in_macro_call = Some(node.clone()); + if let Some(range) = highlight_macro(InFile::new(file_id.into(), node)) { + res.push(HighlightedRange { range, tag: tags::MACRO, binding_hash: None }); + } + } + _ if in_macro_call.is_some() => { + if let Some(token) = node.as_token() { + if let Some((tag, binding_hash)) = highlight_token_tree( + db, + &mut sb, + &analyzer, + &mut bindings_shadow_count, + InFile::new(file_id.into(), token.clone()), + ) { + res.push(HighlightedRange { + range: node.text_range(), + tag, + binding_hash, + }); + } + } + } + _ => { + if let Some((tag, binding_hash)) = highlight_node( + db, + &mut sb, + &mut bindings_shadow_count, + InFile::new(file_id.into(), node.clone()), + ) { + res.push(HighlightedRange { range: node.text_range(), tag, binding_hash }); + } + } + }, + WalkEvent::Leave(node) => { + if let Some(m) = in_macro_call.as_ref() { + if *m == node { + in_macro_call = None; + } + } + } } + } - hash((file_id, name, shadow_count)) + res +} + +fn highlight_macro(node: InFile) -> Option { + let macro_call = ast::MacroCall::cast(node.value.as_node()?.clone())?; + let path = macro_call.path()?; + let name_ref = path.segment()?.name_ref()?; + + let range_start = name_ref.syntax().text_range().start(); + let mut range_end = name_ref.syntax().text_range().end(); + for sibling in path.syntax().siblings_with_tokens(Direction::Next) { + match sibling.kind() { + T![!] | IDENT => range_end = sibling.text_range().end(), + _ => (), + } } - let mut sb = SourceBinder::new(db); + Some(TextRange::from_to(range_start, range_end)) +} - // Visited nodes to handle highlighting priorities - // FIXME: retain only ranges here - let mut highlighted: FxHashSet = FxHashSet::default(); - let mut bindings_shadow_count: FxHashMap = FxHashMap::default(); +fn highlight_token_tree( + db: &RootDatabase, + sb: &mut SourceBinder, + analyzer: &SourceAnalyzer, + bindings_shadow_count: &mut FxHashMap, + token: InFile, +) -> Option<(&'static str, Option)> { + if token.value.parent().kind() != TOKEN_TREE { + return None; + } + let token = descend_into_macros_with_analyzer(db, analyzer, token); + let expanded = { + let parent = token.value.parent(); + // We only care Name and Name_ref + match (token.value.kind(), parent.kind()) { + (IDENT, NAME) | (IDENT, NAME_REF) => token.with_value(parent.into()), + _ => token.map(|it| it.into()), + } + }; - let mut res = Vec::new(); - for node in root.descendants_with_tokens() { - if highlighted.contains(&node) { - continue; + highlight_node(db, sb, bindings_shadow_count, expanded) +} + +fn highlight_node( + db: &RootDatabase, + sb: &mut SourceBinder, + bindings_shadow_count: &mut FxHashMap, + node: InFile, +) -> Option<(&'static str, Option)> { + let mut binding_hash = None; + let tag = match node.value.kind() { + FN_DEF => { + bindings_shadow_count.clear(); + return None; } - let mut binding_hash = None; - let tag = match node.kind() { - FN_DEF => { - bindings_shadow_count.clear(); - continue; - } - COMMENT => tags::LITERAL_COMMENT, - STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, - ATTR => tags::LITERAL_ATTRIBUTE, - // Special-case field init shorthand - NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, - NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => continue, - NAME_REF => { - let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); - let name_kind = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) - .map(|d| d.kind); - match name_kind { - Some(name_kind) => { - if let Local(local) = &name_kind { - if let Some(name) = local.name(db) { - let shadow_count = - bindings_shadow_count.entry(name.clone()).or_default(); - binding_hash = - Some(calc_binding_hash(file_id, &name, *shadow_count)) - } - }; - - highlight_name(db, name_kind) - } - _ => continue, - } - } - NAME => { - let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); - let name_kind = - classify_name(&mut sb, InFile::new(file_id.into(), &name)).map(|d| d.kind); - - if let Some(Local(local)) = &name_kind { - if let Some(name) = local.name(db) { - let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); - *shadow_count += 1; - binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count)) - } - }; - - match name_kind { - Some(name_kind) => highlight_name(db, name_kind), - None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() { - STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE, - TYPE_PARAM => tags::TYPE_PARAM, - RECORD_FIELD_DEF => tags::FIELD, - _ => tags::FUNCTION, - }), + COMMENT => tags::LITERAL_COMMENT, + STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, + ATTR => tags::LITERAL_ATTRIBUTE, + // Special-case field init shorthand + NAME_REF if node.value.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, + NAME_REF if node.value.ancestors().any(|it| it.kind() == ATTR) => return None, + NAME_REF => { + let name_ref = node.value.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); + let name_kind = classify_name_ref(sb, node.with_value(&name_ref)).map(|d| d.kind); + match name_kind { + Some(name_kind) => { + if let Local(local) = &name_kind { + if let Some(name) = local.name(db) { + let shadow_count = + bindings_shadow_count.entry(name.clone()).or_default(); + binding_hash = + Some(calc_binding_hash(node.file_id, &name, *shadow_count)) + } + }; + + highlight_name(db, name_kind) } + _ => return None, } - INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC, - BYTE => tags::LITERAL_BYTE, - CHAR => tags::LITERAL_CHAR, - LIFETIME => tags::TYPE_LIFETIME, - T![unsafe] => tags::KEYWORD_UNSAFE, - k if is_control_keyword(k) => tags::KEYWORD_CONTROL, - k if k.is_keyword() => tags::KEYWORD, - _ => { - if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) { - if let Some(path) = macro_call.path() { - if let Some(segment) = path.segment() { - if let Some(name_ref) = segment.name_ref() { - highlighted.insert(name_ref.syntax().clone().into()); - let range_start = name_ref.syntax().text_range().start(); - let mut range_end = name_ref.syntax().text_range().end(); - for sibling in path.syntax().siblings_with_tokens(Direction::Next) { - match sibling.kind() { - T![!] | IDENT => range_end = sibling.text_range().end(), - _ => (), - } - } - res.push(HighlightedRange { - range: TextRange::from_to(range_start, range_end), - tag: tags::MACRO, - binding_hash: None, - }) - } - } - } + } + NAME => { + let name = node.value.as_node().cloned().and_then(ast::Name::cast).unwrap(); + let name_kind = classify_name(sb, node.with_value(&name)).map(|d| d.kind); + + if let Some(Local(local)) = &name_kind { + if let Some(name) = local.name(db) { + let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); + *shadow_count += 1; + binding_hash = Some(calc_binding_hash(node.file_id, &name, *shadow_count)) } - continue; + }; + + match name_kind { + Some(name_kind) => highlight_name(db, name_kind), + None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() { + STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE, + TYPE_PARAM => tags::TYPE_PARAM, + RECORD_FIELD_DEF => tags::FIELD, + _ => tags::FUNCTION, + }), } - }; - res.push(HighlightedRange { range: node.text_range(), tag, binding_hash }) + } + INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC, + BYTE => tags::LITERAL_BYTE, + CHAR => tags::LITERAL_CHAR, + LIFETIME => tags::TYPE_LIFETIME, + T![unsafe] => tags::KEYWORD_UNSAFE, + k if is_control_keyword(k) => tags::KEYWORD_CONTROL, + k if k.is_keyword() => tags::KEYWORD, + + _ => return None, + }; + + return Some((tag, binding_hash)); + + fn calc_binding_hash(file_id: HirFileId, name: &Name, shadow_count: u32) -> u64 { + fn hash(x: T) -> u64 { + use std::{collections::hash_map::DefaultHasher, hash::Hasher}; + + let mut hasher = DefaultHasher::new(); + x.hash(&mut hasher); + hasher.finish() + } + + hash((file_id, name, shadow_count)) } - res } pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { @@ -331,6 +400,16 @@ fn foo() -> T { foo::(); } +macro_rules! def_fn { + ($($tt:tt)*) => {$($tt)*} +} + +def_fn!{ + fn bar() -> u32 { + 100 + } +} + // comment fn main() { println!("Hello, {}!", 92); -- cgit v1.2.3 From 3137215e8db7aafb17860d30a2d2b39178112366 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 23 Jan 2020 13:39:14 +0200 Subject: Provide more runners for potential tests --- crates/ra_ide/src/runnables.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 7533692f6..8622dd956 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -43,7 +43,7 @@ fn runnable_fn(fn_def: ast::FnDef) -> Option { let name = fn_def.name()?.text().clone(); let kind = if name == "main" { RunnableKind::Bin - } else if fn_def.has_atom_attr("test") { + } else if has_test_related_attribute(&fn_def) { RunnableKind::Test { name: name.to_string() } } else if fn_def.has_atom_attr("bench") { RunnableKind::Bench { name: name.to_string() } @@ -53,6 +53,20 @@ fn runnable_fn(fn_def: ast::FnDef) -> Option { Some(Runnable { range: fn_def.syntax().text_range(), kind }) } +/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as +/// `#[test_case(...)]`, `#[tokio::test]` and similar. +/// Also a regular `#[test]` annotation is supported. +/// +/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, +/// but it's better than not to have the runnables for the tests at all. +fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool { + fn_def + .attrs() + .filter_map(|attr| attr.path()) + .map(|path| path.syntax().to_string().to_lowercase()) + .any(|attribute_text| attribute_text.contains("test")) +} + fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option { let has_test_function = module .item_list()? -- cgit v1.2.3 From b90ea640e6484788f8728be6e48cc55d90e488ba Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 24 Jan 2020 16:35:37 +0100 Subject: Cancel requests during shutdown --- crates/ra_ide/src/change.rs | 10 ++++++---- crates/ra_ide/src/lib.rs | 3 +++ 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs index b0aa2c8e0..ce617840c 100644 --- a/crates/ra_ide/src/change.rs +++ b/crates/ra_ide/src/change.rs @@ -166,13 +166,15 @@ impl LibraryData { const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); impl RootDatabase { + pub(crate) fn request_cancellation(&mut self) { + let _p = profile("RootDatabase::request_cancellation"); + self.salsa_runtime_mut().synthetic_write(Durability::LOW); + } + pub(crate) fn apply_change(&mut self, change: AnalysisChange) { let _p = profile("RootDatabase::apply_change"); + self.request_cancellation(); log::info!("apply_change {:?}", change); - { - let _p = profile("RootDatabase::apply_change/cancellation"); - self.salsa_runtime_mut().synthetic_write(Durability::LOW); - } if !change.new_roots.is_empty() { let mut local_roots = Vec::clone(&self.local_roots()); for (root_id, is_local) in change.new_roots { diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 4d8deb21c..62fe6d2a9 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -202,6 +202,9 @@ impl AnalysisHost { pub fn per_query_memory_usage(&mut self) -> Vec<(String, ra_prof::Bytes)> { self.db.per_query_memory_usage() } + pub fn request_cancellation(&mut self) { + self.db.request_cancellation(); + } pub fn raw_database( &self, ) -> &(impl hir::db::HirDatabase + salsa::Database + ra_db::SourceDatabaseExt) { -- cgit v1.2.3 From 316795e074dff8f627f8c70c85d236420ecfb3a6 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 24 Dec 2019 02:19:09 +0200 Subject: Initial auto import action implementation --- crates/ra_ide/src/assists.rs | 7 +-- crates/ra_ide/src/imports_locator.rs | 97 ++++++++++++++++++++++++++++++++++++ crates/ra_ide/src/lib.rs | 1 + 3 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 crates/ra_ide/src/imports_locator.rs (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index a936900da..c43c45c65 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs @@ -2,8 +2,9 @@ use ra_db::{FilePosition, FileRange}; -use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit}; - +use crate::{ + db::RootDatabase, imports_locator::ImportsLocatorIde, FileId, SourceChange, SourceFileEdit, +}; use either::Either; pub use ra_assists::AssistId; use ra_assists::{AssistAction, AssistLabel}; @@ -16,7 +17,7 @@ pub struct Assist { } pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec { - ra_assists::assists(db, frange) + ra_assists::assists_with_imports_locator(db, frange, ImportsLocatorIde::new(db)) .into_iter() .map(|assist| { let file_id = frange.file_id; diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs new file mode 100644 index 000000000..23391ac3b --- /dev/null +++ b/crates/ra_ide/src/imports_locator.rs @@ -0,0 +1,97 @@ +//! This module contains an import search funcionality that is provided to the ra_assists module. +//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. + +use crate::{ + db::RootDatabase, + references::{classify_name, classify_name_ref, NameDefinition, NameKind}, + symbol_index::{self, FileSymbol}, + Query, +}; +use ast::NameRef; +use hir::{db::HirDatabase, InFile, ModPath, Module, SourceBinder}; +use itertools::Itertools; +use ra_assists::ImportsLocator; +use ra_prof::profile; +use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; + +pub(crate) struct ImportsLocatorIde<'a> { + source_binder: SourceBinder<'a, RootDatabase>, +} + +impl<'a> ImportsLocatorIde<'a> { + pub(crate) fn new(db: &'a RootDatabase) -> Self { + Self { source_binder: SourceBinder::new(db) } + } + + fn search_for_imports( + &mut self, + name_to_import: &ast::NameRef, + module_with_name_to_import: Module, + ) -> Vec { + let _p = profile("search_for_imports"); + let db = self.source_binder.db; + let name_to_import = name_to_import.text(); + + let project_results = { + let mut query = Query::new(name_to_import.to_string()); + query.exact(); + query.limit(10); + symbol_index::world_symbols(db, query) + }; + let lib_results = { + let mut query = Query::new(name_to_import.to_string()); + query.libs(); + query.exact(); + query.limit(10); + symbol_index::world_symbols(db, query) + }; + + project_results + .into_iter() + .chain(lib_results.into_iter()) + .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate)) + .filter_map(|name_definition_to_import| { + if let NameKind::Def(module_def) = name_definition_to_import.kind { + module_with_name_to_import.find_use_path(db, module_def) + } else { + None + } + }) + .filter(|use_path| !use_path.segments.is_empty()) + .unique() + .collect() + } + + fn get_name_definition( + &mut self, + db: &impl HirDatabase, + import_candidate: &FileSymbol, + ) -> Option { + let _p = profile("get_name_definition"); + let file_id = import_candidate.file_id.into(); + let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?); + let candidate_name_node = if candidate_node.kind() != NAME { + candidate_node.children().find(|it| it.kind() == NAME)? + } else { + candidate_node + }; + classify_name( + &mut self.source_binder, + hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? }, + ) + } +} + +impl<'a> ImportsLocator<'a> for ImportsLocatorIde<'a> { + fn find_imports( + &mut self, + name_to_import: InFile<&NameRef>, + module_with_name_to_import: Module, + ) -> Option> { + if classify_name_ref(&mut self.source_binder, name_to_import).is_none() { + Some(self.search_for_imports(name_to_import.value, module_with_name_to_import)) + } else { + None + } + } +} diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 62fe6d2a9..03ad6b2c1 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -30,6 +30,7 @@ mod syntax_highlighting; mod parent_module; mod references; mod impls; +mod imports_locator; mod assists; mod diagnostics; mod syntax_tree; -- cgit v1.2.3 From f57239729cb9d6f60eb09d8cfd8244066b5e182c Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 23 Jan 2020 21:15:16 +0200 Subject: Remove unnecessary lifetime parameter --- crates/ra_ide/src/imports_locator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs index 23391ac3b..ab9cd7990 100644 --- a/crates/ra_ide/src/imports_locator.rs +++ b/crates/ra_ide/src/imports_locator.rs @@ -82,7 +82,7 @@ impl<'a> ImportsLocatorIde<'a> { } } -impl<'a> ImportsLocator<'a> for ImportsLocatorIde<'a> { +impl<'a> ImportsLocator for ImportsLocatorIde<'a> { fn find_imports( &mut self, name_to_import: InFile<&NameRef>, -- cgit v1.2.3 From bef5cf0b9929539e8d9fece006bfd3db1b68bec4 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 23 Jan 2020 21:30:59 +0200 Subject: Raise the import search query cap --- crates/ra_ide/src/imports_locator.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs index ab9cd7990..e69fb4070 100644 --- a/crates/ra_ide/src/imports_locator.rs +++ b/crates/ra_ide/src/imports_locator.rs @@ -35,14 +35,14 @@ impl<'a> ImportsLocatorIde<'a> { let project_results = { let mut query = Query::new(name_to_import.to_string()); query.exact(); - query.limit(10); + query.limit(40); symbol_index::world_symbols(db, query) }; let lib_results = { let mut query = Query::new(name_to_import.to_string()); query.libs(); query.exact(); - query.limit(10); + query.limit(40); symbol_index::world_symbols(db, query) }; @@ -59,6 +59,7 @@ impl<'a> ImportsLocatorIde<'a> { }) .filter(|use_path| !use_path.segments.is_empty()) .unique() + .take(20) .collect() } -- cgit v1.2.3 From d0a782ef1c53ef5c5d3b49b02c498f7a688c3a4d Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 24 Jan 2020 09:33:18 +0200 Subject: Have a better trait interface --- crates/ra_ide/src/imports_locator.rs | 70 +++++++++++++----------------------- 1 file changed, 24 insertions(+), 46 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs index e69fb4070..b2fc48159 100644 --- a/crates/ra_ide/src/imports_locator.rs +++ b/crates/ra_ide/src/imports_locator.rs @@ -3,13 +3,11 @@ use crate::{ db::RootDatabase, - references::{classify_name, classify_name_ref, NameDefinition, NameKind}, + references::{classify_name, NameDefinition, NameKind}, symbol_index::{self, FileSymbol}, Query, }; -use ast::NameRef; -use hir::{db::HirDatabase, InFile, ModPath, Module, SourceBinder}; -use itertools::Itertools; +use hir::{db::HirDatabase, ModuleDef, SourceBinder}; use ra_assists::ImportsLocator; use ra_prof::profile; use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; @@ -23,14 +21,30 @@ impl<'a> ImportsLocatorIde<'a> { Self { source_binder: SourceBinder::new(db) } } - fn search_for_imports( + fn get_name_definition( &mut self, - name_to_import: &ast::NameRef, - module_with_name_to_import: Module, - ) -> Vec { + db: &impl HirDatabase, + import_candidate: &FileSymbol, + ) -> Option { + let _p = profile("get_name_definition"); + let file_id = import_candidate.file_id.into(); + let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?); + let candidate_name_node = if candidate_node.kind() != NAME { + candidate_node.children().find(|it| it.kind() == NAME)? + } else { + candidate_node + }; + classify_name( + &mut self.source_binder, + hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? }, + ) + } +} + +impl<'a> ImportsLocator for ImportsLocatorIde<'a> { + fn find_imports(&mut self, name_to_import: &str) -> Vec { let _p = profile("search_for_imports"); let db = self.source_binder.db; - let name_to_import = name_to_import.text(); let project_results = { let mut query = Query::new(name_to_import.to_string()); @@ -52,47 +66,11 @@ impl<'a> ImportsLocatorIde<'a> { .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate)) .filter_map(|name_definition_to_import| { if let NameKind::Def(module_def) = name_definition_to_import.kind { - module_with_name_to_import.find_use_path(db, module_def) + Some(module_def) } else { None } }) - .filter(|use_path| !use_path.segments.is_empty()) - .unique() - .take(20) .collect() } - - fn get_name_definition( - &mut self, - db: &impl HirDatabase, - import_candidate: &FileSymbol, - ) -> Option { - let _p = profile("get_name_definition"); - let file_id = import_candidate.file_id.into(); - let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?); - let candidate_name_node = if candidate_node.kind() != NAME { - candidate_node.children().find(|it| it.kind() == NAME)? - } else { - candidate_node - }; - classify_name( - &mut self.source_binder, - hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? }, - ) - } -} - -impl<'a> ImportsLocator for ImportsLocatorIde<'a> { - fn find_imports( - &mut self, - name_to_import: InFile<&NameRef>, - module_with_name_to_import: Module, - ) -> Option> { - if classify_name_ref(&mut self.source_binder, name_to_import).is_none() { - Some(self.search_for_imports(name_to_import.value, module_with_name_to_import)) - } else { - None - } - } } -- cgit v1.2.3 From 9be1ab7ff948d89334a8acbc309c8235d4ab374f Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 27 Jan 2020 14:42:45 +0200 Subject: Code review fixes --- crates/ra_ide/src/imports_locator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs index b2fc48159..48b014c7d 100644 --- a/crates/ra_ide/src/imports_locator.rs +++ b/crates/ra_ide/src/imports_locator.rs @@ -41,7 +41,7 @@ impl<'a> ImportsLocatorIde<'a> { } } -impl<'a> ImportsLocator for ImportsLocatorIde<'a> { +impl ImportsLocator for ImportsLocatorIde<'_> { fn find_imports(&mut self, name_to_import: &str) -> Vec { let _p = profile("search_for_imports"); let db = self.source_binder.db; -- cgit v1.2.3 From fbc3ffcee6eec3d89e27417b3d3543327d810299 Mon Sep 17 00:00:00 2001 From: Mikhail Modin Date: Sat, 18 Jan 2020 12:17:34 +0000 Subject: Improves reference search by StructLiteral --- crates/ra_ide/src/references.rs | 138 ++++++++++++++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 27 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 5e2fe1905..ebded715d 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -112,25 +112,20 @@ impl IntoIterator for ReferenceSearchResult { pub(crate) fn find_all_refs( db: &RootDatabase, - mut position: FilePosition, + position: FilePosition, search_scope: Option, ) -> Option> { let parse = db.parse(position.file_id); let syntax = parse.tree().syntax().clone(); - let token = syntax.token_at_offset(position.offset); - let mut search_kind = ReferenceKind::Other; + let (opt_name, search_kind) = + if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) { + (Some(name), ReferenceKind::StructLiteral) + } else { + (find_node_at_offset::(&syntax, position.offset), ReferenceKind::Other) + }; - if let TokenAtOffset::Between(ref left, ref right) = token { - if (right.kind() == SyntaxKind::L_CURLY || right.kind() == SyntaxKind::L_PAREN) - && left.kind() != SyntaxKind::IDENT - { - position = FilePosition { offset: left.text_range().start(), ..position }; - search_kind = ReferenceKind::StructLiteral; - } - } - - let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?; + let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?; let declaration = match def.kind { NameKind::Macro(mac) => mac.to_nav(db), @@ -170,9 +165,10 @@ fn find_name( db: &RootDatabase, syntax: &SyntaxNode, position: FilePosition, + opt_name: Option, ) -> Option> { let mut sb = SourceBinder::new(db); - if let Some(name) = find_node_at_offset::(&syntax, position.offset) { + if let Some(name) = opt_name { let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; let range = name.syntax().text_range(); return Some(RangeInfo::new(range, (name.text().to_string(), def))); @@ -218,15 +214,8 @@ fn process_definition( if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) { if d == def { - let kind = if name_ref - .syntax() - .ancestors() - .find_map(ast::RecordLit::cast) - .and_then(|l| l.path()) - .and_then(|p| p.segment()) - .and_then(|p| p.name_ref()) - .map(|n| n == name_ref) - .unwrap_or(false) + let kind = if is_record_lit_name_ref(&name_ref) + || is_call_expr_name_ref(&name_ref) { ReferenceKind::StructLiteral } else { @@ -301,6 +290,49 @@ fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option bool { + name_ref + .syntax() + .ancestors() + .find_map(ast::RecordLit::cast) + .and_then(|l| l.path()) + .and_then(|p| p.segment()) + .map(|p| p.name_ref().as_ref() == Some(name_ref)) + .unwrap_or(false) +} + +fn get_struct_def_name_for_struc_litetal_search( + syntax: &SyntaxNode, + position: FilePosition, +) -> Option { + if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { + if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { + return None; + } + if let Some(name) = find_node_at_offset::(&syntax, left.text_range().start()) { + return name.syntax().ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name()); + } + if find_node_at_offset::(&syntax, left.text_range().start()).is_some() { + return left.ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name()); + } + } + None +} + +fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { + name_ref + .syntax() + .ancestors() + .find_map(ast::CallExpr::cast) + .and_then(|c| match c.expr()? { + ast::Expr::PathExpr(p) => { + Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref)) + } + _ => None, + }) + .unwrap_or(false) +} + #[cfg(test)] mod tests { use crate::{ @@ -309,7 +341,7 @@ mod tests { }; #[test] - fn test_struct_literal() { + fn test_struct_literal_after_space() { let code = r#" struct Foo <|>{ a: i32, @@ -330,6 +362,58 @@ mod tests { ); } + #[test] + fn test_struct_literal_befor_space() { + let code = r#" + struct Foo<|> {} + fn main() { + let f: Foo; + f = Foo {}; + }"#; + + let refs = get_all_refs(code); + check_result( + refs, + "Foo STRUCT_DEF FileId(1) [5; 18) [12; 15) Other", + &["FileId(1) [54; 57) Other", "FileId(1) [71; 74) StructLiteral"], + ); + } + + #[test] + fn test_struct_literal_with_generic_type() { + let code = r#" + struct Foo <|>{} + fn main() { + let f: Foo::; + f = Foo {}; + }"#; + + let refs = get_all_refs(code); + check_result( + refs, + "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other", + &["FileId(1) [81; 84) StructLiteral"], + ); + } + + #[test] + fn test_struct_literal_for_tuple() { + let code = r#" + struct Foo<|>(i32); + + fn main() { + let f: Foo; + f = Foo(1); + }"#; + + let refs = get_all_refs(code); + check_result( + refs, + "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other", + &["FileId(1) [71; 74) StructLiteral"], + ); + } + #[test] fn test_find_all_refs_for_local() { let code = r#" @@ -564,7 +648,7 @@ mod tests { check_result( refs, "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", - &["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"], + &["FileId(2) [16; 20) StructLiteral", "FileId(3) [16; 20) StructLiteral"], ); let refs = @@ -572,7 +656,7 @@ mod tests { check_result( refs, "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", - &["FileId(3) [16; 20) Other"], + &["FileId(3) [16; 20) StructLiteral"], ); } @@ -591,7 +675,7 @@ mod tests { check_result( refs, "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other", - &["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"], + &["FileId(1) [96; 98) StructLiteral", "FileId(1) [114; 116) StructLiteral"], ); } -- cgit v1.2.3 From f1720d7983b15a404dd3025c90afde9cc3773222 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 29 Jan 2020 16:10:46 +0100 Subject: Re-sync queries for memory usage measurnment --- crates/ra_ide/src/change.rs | 51 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 11 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs index ce617840c..45a58690b 100644 --- a/crates/ra_ide/src/change.rs +++ b/crates/ra_ide/src/change.rs @@ -301,45 +301,74 @@ impl RootDatabase { )*} } sweep_each_query![ + // SourceDatabase ra_db::ParseQuery ra_db::SourceRootCratesQuery + + // AstDatabase hir::db::AstIdMapQuery - hir::db::ParseMacroQuery - hir::db::MacroDefQuery + hir::db::InternMacroQuery hir::db::MacroArgQuery + hir::db::MacroDefQuery + hir::db::ParseMacroQuery hir::db::MacroExpandQuery + + // DefDatabase + hir::db::RawItemsQuery + hir::db::ComputeCrateDefMapQuery hir::db::StructDataQuery + hir::db::UnionDataQuery hir::db::EnumDataQuery + hir::db::ImplDataQuery hir::db::TraitDataQuery - hir::db::RawItemsQuery - hir::db::ComputeCrateDefMapQuery - hir::db::GenericParamsQuery - hir::db::FunctionDataQuery hir::db::TypeAliasDataQuery + hir::db::FunctionDataQuery hir::db::ConstDataQuery hir::db::StaticDataQuery + hir::db::BodyWithSourceMapQuery + hir::db::BodyQuery + hir::db::ExprScopesQuery + hir::db::GenericParamsQuery + hir::db::AttrsQuery hir::db::ModuleLangItemsQuery hir::db::CrateLangItemsQuery hir::db::LangItemQuery hir::db::DocumentationQuery - hir::db::ExprScopesQuery + + // InternDatabase + hir::db::InternFunctionQuery + hir::db::InternStructQuery + hir::db::InternUnionQuery + hir::db::InternEnumQuery + hir::db::InternConstQuery + hir::db::InternStaticQuery + hir::db::InternTraitQuery + hir::db::InternTypeAliasQuery + hir::db::InternImplQuery + + // HirDatabase hir::db::DoInferQuery hir::db::TyQuery hir::db::ValueTyQuery + hir::db::ImplSelfTyQuery + hir::db::ImplTraitQuery hir::db::FieldTypesQuery hir::db::CallableItemSignatureQuery + hir::db::GenericPredicatesForParamQuery hir::db::GenericPredicatesQuery hir::db::GenericDefaultsQuery - hir::db::BodyWithSourceMapQuery - hir::db::BodyQuery hir::db::ImplsInCrateQuery hir::db::ImplsForTraitQuery + hir::db::TraitSolverQuery + hir::db::InternTypeCtorQuery + hir::db::InternChalkImplQuery + hir::db::InternAssocTyValueQuery hir::db::AssociatedTyDataQuery + hir::db::AssociatedTyValueQuery + hir::db::TraitSolveQuery hir::db::TraitDatumQuery hir::db::StructDatumQuery hir::db::ImplDatumQuery - hir::db::ImplDataQuery - hir::db::TraitSolveQuery ]; acc.sort_by_key(|it| std::cmp::Reverse(it.1)); acc -- cgit v1.2.3 From 6dae5cbb1190cde6a20aa1758c7d87e84933378e Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sun, 2 Feb 2020 14:06:51 +0200 Subject: Require ModPath for importing --- crates/ra_ide/src/completion/complete_scope.rs | 71 +++++++++++++++++--------- 1 file changed, 47 insertions(+), 24 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 458d7525e..fe0795984 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs @@ -6,6 +6,7 @@ use ra_text_edit::TextEditBuilder; use rustc_hash::FxHashMap; use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; +use hir::{ModPath, PathKind}; pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { if !ctx.is_trivial_path { @@ -54,58 +55,80 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { } } -fn build_import_label(name: &str, path: &[SmolStr]) -> String { +fn build_import_label(name: &str, path: &ModPath) -> String { let mut buf = String::with_capacity(64); buf.push_str(name); buf.push_str(" ("); - fmt_import_path(path, &mut buf); + buf.push_str(&path.to_string()); buf.push_str(")"); buf } -fn fmt_import_path(path: &[SmolStr], buf: &mut String) { - let mut segments = path.iter(); - if let Some(s) = segments.next() { - buf.push_str(&s); - } - for s in segments { - buf.push_str("::"); - buf.push_str(&s); - } -} - #[derive(Debug, Clone, Default)] pub(crate) struct ImportResolver { // todo: use fst crate or something like that - dummy_names: Vec<(SmolStr, Vec)>, + dummy_names: Vec<(SmolStr, ModPath)>, } impl ImportResolver { pub(crate) fn new() -> Self { let dummy_names = vec![ - (SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]), - (SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]), - (SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]), - (SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]), + ( + SmolStr::new("fmt"), + ModPath { kind: PathKind::Plain, segments: vec![hir::known::std, hir::known::fmt] }, + ), + ( + SmolStr::new("io"), + ModPath { kind: PathKind::Plain, segments: vec![hir::known::std, hir::known::io] }, + ), + ( + SmolStr::new("iter"), + ModPath { + kind: PathKind::Plain, + segments: vec![hir::known::std, hir::known::iter], + }, + ), + ( + SmolStr::new("hash"), + ModPath { + kind: PathKind::Plain, + segments: vec![hir::known::std, hir::known::hash], + }, + ), ( SmolStr::new("Debug"), - vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")], + ModPath { + kind: PathKind::Plain, + segments: vec![hir::known::std, hir::known::fmt, hir::known::Debug], + }, ), ( SmolStr::new("Display"), - vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")], + ModPath { + kind: PathKind::Plain, + segments: vec![hir::known::std, hir::known::fmt, hir::known::Display], + }, ), ( SmolStr::new("Hash"), - vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")], + ModPath { + kind: PathKind::Plain, + segments: vec![hir::known::std, hir::known::hash, hir::known::Hash], + }, ), ( SmolStr::new("Hasher"), - vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")], + ModPath { + kind: PathKind::Plain, + segments: vec![hir::known::std, hir::known::hash, hir::known::Hasher], + }, ), ( SmolStr::new("Iterator"), - vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")], + ModPath { + kind: PathKind::Plain, + segments: vec![hir::known::std, hir::known::iter, hir::known::Iterator], + }, ), ]; @@ -115,7 +138,7 @@ impl ImportResolver { // Returns a map of importable items filtered by name. // The map associates item name with its full path. // todo: should return Resolutions - pub(crate) fn all_names(&self, name: &str) -> FxHashMap> { + pub(crate) fn all_names(&self, name: &str) -> FxHashMap { if name.len() > 1 { self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect() } else { -- cgit v1.2.3 From c669b2f489cb551fbe53fd01f7532b5d55ffe704 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sun, 2 Feb 2020 14:27:52 +0200 Subject: Code review fixes --- crates/ra_ide/src/completion/complete_scope.rs | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index fe0795984..64b04ec2b 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs @@ -72,62 +72,58 @@ pub(crate) struct ImportResolver { impl ImportResolver { pub(crate) fn new() -> Self { + use hir::name; + let dummy_names = vec![ ( SmolStr::new("fmt"), - ModPath { kind: PathKind::Plain, segments: vec![hir::known::std, hir::known::fmt] }, + ModPath { kind: PathKind::Plain, segments: vec![name![std], name![fmt]] }, ), ( SmolStr::new("io"), - ModPath { kind: PathKind::Plain, segments: vec![hir::known::std, hir::known::io] }, + ModPath { kind: PathKind::Plain, segments: vec![name![std], name![io]] }, ), ( SmolStr::new("iter"), - ModPath { - kind: PathKind::Plain, - segments: vec![hir::known::std, hir::known::iter], - }, + ModPath { kind: PathKind::Plain, segments: vec![name![std], name![iter]] }, ), ( SmolStr::new("hash"), - ModPath { - kind: PathKind::Plain, - segments: vec![hir::known::std, hir::known::hash], - }, + ModPath { kind: PathKind::Plain, segments: vec![name![std], name![hash]] }, ), ( SmolStr::new("Debug"), ModPath { kind: PathKind::Plain, - segments: vec![hir::known::std, hir::known::fmt, hir::known::Debug], + segments: vec![name![std], name![fmt], name![Debug]], }, ), ( SmolStr::new("Display"), ModPath { kind: PathKind::Plain, - segments: vec![hir::known::std, hir::known::fmt, hir::known::Display], + segments: vec![name![std], name![fmt], name![Display]], }, ), ( SmolStr::new("Hash"), ModPath { kind: PathKind::Plain, - segments: vec![hir::known::std, hir::known::hash, hir::known::Hash], + segments: vec![name![std], name![hash], name![Hash]], }, ), ( SmolStr::new("Hasher"), ModPath { kind: PathKind::Plain, - segments: vec![hir::known::std, hir::known::hash, hir::known::Hasher], + segments: vec![name![std], name![hash], name![Hasher]], }, ), ( SmolStr::new("Iterator"), ModPath { kind: PathKind::Plain, - segments: vec![hir::known::std, hir::known::iter, hir::known::Iterator], + segments: vec![name![std], name![iter], name![Iterator]], }, ), ]; -- cgit v1.2.3 From 24ad1cce2c3cf2c0ce8288fc02c4c55529598990 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 2 Feb 2020 18:54:26 +0100 Subject: Avoid premature pessimization The extra allocation for message should not matter here at all, but using a static string is just as ergonomic, if not more, and there's no reason to write deliberately slow code --- crates/ra_ide/src/change.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs index 45a58690b..18dad2ea3 100644 --- a/crates/ra_ide/src/change.rs +++ b/crates/ra_ide/src/change.rs @@ -145,6 +145,8 @@ impl LibraryData { root_id: SourceRootId, files: Vec<(FileId, RelativePathBuf, Arc)>, ) -> LibraryData { + let _p = profile("LibraryData::prepare"); + #[cfg(not(feature = "wasm"))] let iter = files.par_iter(); #[cfg(feature = "wasm")] -- cgit v1.2.3 From fcf5bbbbeb1ab48e80d45483c810bae708f848a1 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 3 Feb 2020 13:35:14 +0200 Subject: Fix inlay hints test snippet compilation --- crates/ra_ide/src/inlay_hints.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 393ca9447..de447a5aa 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -376,7 +376,7 @@ fn main() { let mut start = 0; (0..2).for_each(|increment| { start += increment; - }) + }); let multiply = |a, b, c, d| a * b * c * d; let _: i32 = multiply(1, 2, 3, 4); @@ -399,37 +399,37 @@ fn main() { label: "i32", }, InlayHint { - range: [114; 122), + range: [115; 123), kind: TypeHint, label: "|…| -> i32", }, InlayHint { - range: [126; 127), + range: [127; 128), kind: TypeHint, label: "i32", }, InlayHint { - range: [129; 130), + range: [130; 131), kind: TypeHint, label: "i32", }, InlayHint { - range: [132; 133), + range: [133; 134), kind: TypeHint, label: "i32", }, InlayHint { - range: [135; 136), + range: [136; 137), kind: TypeHint, label: "i32", }, InlayHint { - range: [200; 212), + range: [201; 213), kind: TypeHint, label: "&|…| -> i32", }, InlayHint { - range: [235; 244), + range: [236; 245), kind: TypeHint, label: "|| -> i32", }, -- cgit v1.2.3 From ac37a11f04b31f792068a1cb50dbbf5ccd4d982d Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 26 Jan 2020 20:44:49 +0200 Subject: Reimplemented lexer with vectors instead of iterators --- crates/ra_ide/src/references/rename.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 626efb603..ad3e86f7c 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs @@ -2,7 +2,7 @@ use hir::ModuleSource; use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; -use ra_syntax::{algo::find_node_at_offset, ast, tokenize, AstNode, SyntaxKind, SyntaxNode}; +use ra_syntax::{algo::find_node_at_offset, ast, single_token, AstNode, SyntaxKind, SyntaxNode}; use ra_text_edit::TextEdit; use crate::{ @@ -17,11 +17,9 @@ pub(crate) fn rename( position: FilePosition, new_name: &str, ) -> Option> { - let tokens = tokenize(new_name); - if tokens.len() != 1 - || (tokens[0].kind != SyntaxKind::IDENT && tokens[0].kind != SyntaxKind::UNDERSCORE) - { - return None; + match single_token(new_name)?.token.kind { + SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), + _ => return None, } let parse = db.parse(position.file_id); -- cgit v1.2.3 From 9e7eaa959f9dc368a55f1a80b35651b78b3d0883 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Tue, 28 Jan 2020 07:09:13 +0200 Subject: ra_syntax: refactored the lexer design as per @matklad and @kiljacken PR review --- crates/ra_ide/src/references/rename.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index ad3e86f7c..9a84c1c88 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs @@ -2,7 +2,9 @@ use hir::ModuleSource; use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; -use ra_syntax::{algo::find_node_at_offset, ast, single_token, AstNode, SyntaxKind, SyntaxNode}; +use ra_syntax::{ + algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode, +}; use ra_text_edit::TextEdit; use crate::{ @@ -17,7 +19,7 @@ pub(crate) fn rename( position: FilePosition, new_name: &str, ) -> Option> { - match single_token(new_name)?.token.kind { + match lex_single_valid_syntax_kind(new_name)? { SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), _ => return None, } -- cgit v1.2.3 From f5a20014ce8787ebe4c910b9f0b4cf52d6130121 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 4 Feb 2020 13:41:56 +0100 Subject: minor, if let else -> match --- crates/ra_ide/src/imports_locator.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs index 48b014c7d..9e1a1c1ec 100644 --- a/crates/ra_ide/src/imports_locator.rs +++ b/crates/ra_ide/src/imports_locator.rs @@ -64,12 +64,9 @@ impl ImportsLocator for ImportsLocatorIde<'_> { .into_iter() .chain(lib_results.into_iter()) .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate)) - .filter_map(|name_definition_to_import| { - if let NameKind::Def(module_def) = name_definition_to_import.kind { - Some(module_def) - } else { - None - } + .filter_map(|name_definition_to_import| match name_definition_to_import.kind { + NameKind::Def(module_def) => Some(module_def), + _ => None, }) .collect() } -- cgit v1.2.3 From 78092c7c66a665ada55ea09476a387014f6665b0 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 5 Feb 2020 12:47:28 +0200 Subject: Apply the reviews suggestions --- crates/ra_ide/src/mock_analysis.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index bf8a54932..081aaee8c 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use ra_cfg::CfgOptions; -use ra_db::{Env, RelativePathBuf}; +use ra_db::{CrateName, Env, RelativePathBuf}; use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; use crate::{ @@ -107,7 +107,9 @@ impl MockAnalysis { crate_graph.add_crate_root(file_id, Edition2018, cfg_options, Env::default()); let crate_name = path.parent().unwrap().file_name().unwrap(); if let Some(root_crate) = root_crate { - crate_graph.add_dep(root_crate, crate_name.into(), other_crate).unwrap(); + crate_graph + .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate) + .unwrap(); } } change.add_file(source_root, file_id, path, Arc::new(contents)); -- cgit v1.2.3 From 896906fea8c6ba7f9672ced51658b1e742868f1a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 12:07:06 +0100 Subject: Start ide_db --- crates/ra_ide/src/ide_db/mod.rs | 1 + crates/ra_ide/src/lib.rs | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 crates/ra_ide/src/ide_db/mod.rs (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/ide_db/mod.rs b/crates/ra_ide/src/ide_db/mod.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/crates/ra_ide/src/ide_db/mod.rs @@ -0,0 +1 @@ + diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 03ad6b2c1..f10c871b8 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -10,6 +10,8 @@ // For proving that RootDatabase is RefUnwindSafe. #![recursion_limit = "128"] +mod ide_db; + mod db; pub mod mock_analysis; mod symbol_index; -- cgit v1.2.3 From 551f33d754c5131f548baee4f58fd5892a9c7efc Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 12:08:08 +0100 Subject: Move ide-db --- crates/ra_ide/src/db.rs | 133 +--------------------------------------- crates/ra_ide/src/ide_db/mod.rs | 131 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 132 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/db.rs b/crates/ra_ide/src/db.rs index 47d0aed6f..2849cdb02 100644 --- a/crates/ra_ide/src/db.rs +++ b/crates/ra_ide/src/db.rs @@ -1,132 +1 @@ -//! FIXME: write short doc here - -use std::sync::Arc; - -use ra_db::{ - salsa::{self, Database, Durability}, - Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, - SourceDatabase, SourceRootId, -}; -use rustc_hash::FxHashMap; - -use crate::{ - symbol_index::{self, SymbolsDatabase}, - FeatureFlags, LineIndex, -}; - -#[salsa::database( - ra_db::SourceDatabaseStorage, - ra_db::SourceDatabaseExtStorage, - LineIndexDatabaseStorage, - symbol_index::SymbolsDatabaseStorage, - hir::db::InternDatabaseStorage, - hir::db::AstDatabaseStorage, - hir::db::DefDatabaseStorage, - hir::db::HirDatabaseStorage -)] -#[derive(Debug)] -pub(crate) struct RootDatabase { - runtime: salsa::Runtime, - pub(crate) feature_flags: Arc, - pub(crate) debug_data: Arc, - pub(crate) last_gc: crate::wasm_shims::Instant, - pub(crate) last_gc_check: crate::wasm_shims::Instant, -} - -impl FileLoader for RootDatabase { - fn file_text(&self, file_id: FileId) -> Arc { - FileLoaderDelegate(self).file_text(file_id) - } - fn resolve_relative_path( - &self, - anchor: FileId, - relative_path: &RelativePath, - ) -> Option { - FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) - } - fn relevant_crates(&self, file_id: FileId) -> Arc> { - FileLoaderDelegate(self).relevant_crates(file_id) - } -} - -impl salsa::Database for RootDatabase { - fn salsa_runtime(&self) -> &salsa::Runtime { - &self.runtime - } - fn salsa_runtime_mut(&mut self) -> &mut salsa::Runtime { - &mut self.runtime - } - fn on_propagated_panic(&self) -> ! { - Canceled::throw() - } - fn salsa_event(&self, event: impl Fn() -> salsa::Event) { - match event().kind { - salsa::EventKind::DidValidateMemoizedValue { .. } - | salsa::EventKind::WillExecute { .. } => { - self.check_canceled(); - } - _ => (), - } - } -} - -impl Default for RootDatabase { - fn default() -> RootDatabase { - RootDatabase::new(None, FeatureFlags::default()) - } -} - -impl RootDatabase { - pub fn new(lru_capacity: Option, feature_flags: FeatureFlags) -> RootDatabase { - let mut db = RootDatabase { - runtime: salsa::Runtime::default(), - last_gc: crate::wasm_shims::Instant::now(), - last_gc_check: crate::wasm_shims::Instant::now(), - feature_flags: Arc::new(feature_flags), - debug_data: Default::default(), - }; - db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); - db.set_local_roots_with_durability(Default::default(), Durability::HIGH); - db.set_library_roots_with_durability(Default::default(), Durability::HIGH); - let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP); - db.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity); - db.query_mut(hir::db::ParseMacroQuery).set_lru_capacity(lru_capacity); - db.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity); - db - } -} - -impl salsa::ParallelDatabase for RootDatabase { - fn snapshot(&self) -> salsa::Snapshot { - salsa::Snapshot::new(RootDatabase { - runtime: self.runtime.snapshot(self), - last_gc: self.last_gc, - last_gc_check: self.last_gc_check, - feature_flags: Arc::clone(&self.feature_flags), - debug_data: Arc::clone(&self.debug_data), - }) - } -} - -#[salsa::query_group(LineIndexDatabaseStorage)] -pub(crate) trait LineIndexDatabase: ra_db::SourceDatabase + CheckCanceled { - fn line_index(&self, file_id: FileId) -> Arc; -} - -fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc { - let text = db.file_text(file_id); - Arc::new(LineIndex::new(&*text)) -} - -#[derive(Debug, Default, Clone)] -pub(crate) struct DebugData { - pub(crate) root_paths: FxHashMap, - pub(crate) crate_names: FxHashMap, -} - -impl DebugData { - pub(crate) fn merge(&mut self, other: DebugData) { - self.root_paths.extend(other.root_paths.into_iter()); - self.crate_names.extend(other.crate_names.into_iter()); - } -} +pub(crate) use crate::ide_db::*; diff --git a/crates/ra_ide/src/ide_db/mod.rs b/crates/ra_ide/src/ide_db/mod.rs index 8b1378917..47d0aed6f 100644 --- a/crates/ra_ide/src/ide_db/mod.rs +++ b/crates/ra_ide/src/ide_db/mod.rs @@ -1 +1,132 @@ +//! FIXME: write short doc here +use std::sync::Arc; + +use ra_db::{ + salsa::{self, Database, Durability}, + Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, + SourceDatabase, SourceRootId, +}; +use rustc_hash::FxHashMap; + +use crate::{ + symbol_index::{self, SymbolsDatabase}, + FeatureFlags, LineIndex, +}; + +#[salsa::database( + ra_db::SourceDatabaseStorage, + ra_db::SourceDatabaseExtStorage, + LineIndexDatabaseStorage, + symbol_index::SymbolsDatabaseStorage, + hir::db::InternDatabaseStorage, + hir::db::AstDatabaseStorage, + hir::db::DefDatabaseStorage, + hir::db::HirDatabaseStorage +)] +#[derive(Debug)] +pub(crate) struct RootDatabase { + runtime: salsa::Runtime, + pub(crate) feature_flags: Arc, + pub(crate) debug_data: Arc, + pub(crate) last_gc: crate::wasm_shims::Instant, + pub(crate) last_gc_check: crate::wasm_shims::Instant, +} + +impl FileLoader for RootDatabase { + fn file_text(&self, file_id: FileId) -> Arc { + FileLoaderDelegate(self).file_text(file_id) + } + fn resolve_relative_path( + &self, + anchor: FileId, + relative_path: &RelativePath, + ) -> Option { + FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) + } + fn relevant_crates(&self, file_id: FileId) -> Arc> { + FileLoaderDelegate(self).relevant_crates(file_id) + } +} + +impl salsa::Database for RootDatabase { + fn salsa_runtime(&self) -> &salsa::Runtime { + &self.runtime + } + fn salsa_runtime_mut(&mut self) -> &mut salsa::Runtime { + &mut self.runtime + } + fn on_propagated_panic(&self) -> ! { + Canceled::throw() + } + fn salsa_event(&self, event: impl Fn() -> salsa::Event) { + match event().kind { + salsa::EventKind::DidValidateMemoizedValue { .. } + | salsa::EventKind::WillExecute { .. } => { + self.check_canceled(); + } + _ => (), + } + } +} + +impl Default for RootDatabase { + fn default() -> RootDatabase { + RootDatabase::new(None, FeatureFlags::default()) + } +} + +impl RootDatabase { + pub fn new(lru_capacity: Option, feature_flags: FeatureFlags) -> RootDatabase { + let mut db = RootDatabase { + runtime: salsa::Runtime::default(), + last_gc: crate::wasm_shims::Instant::now(), + last_gc_check: crate::wasm_shims::Instant::now(), + feature_flags: Arc::new(feature_flags), + debug_data: Default::default(), + }; + db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); + db.set_local_roots_with_durability(Default::default(), Durability::HIGH); + db.set_library_roots_with_durability(Default::default(), Durability::HIGH); + let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP); + db.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity); + db.query_mut(hir::db::ParseMacroQuery).set_lru_capacity(lru_capacity); + db.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity); + db + } +} + +impl salsa::ParallelDatabase for RootDatabase { + fn snapshot(&self) -> salsa::Snapshot { + salsa::Snapshot::new(RootDatabase { + runtime: self.runtime.snapshot(self), + last_gc: self.last_gc, + last_gc_check: self.last_gc_check, + feature_flags: Arc::clone(&self.feature_flags), + debug_data: Arc::clone(&self.debug_data), + }) + } +} + +#[salsa::query_group(LineIndexDatabaseStorage)] +pub(crate) trait LineIndexDatabase: ra_db::SourceDatabase + CheckCanceled { + fn line_index(&self, file_id: FileId) -> Arc; +} + +fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc { + let text = db.file_text(file_id); + Arc::new(LineIndex::new(&*text)) +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct DebugData { + pub(crate) root_paths: FxHashMap, + pub(crate) crate_names: FxHashMap, +} + +impl DebugData { + pub(crate) fn merge(&mut self, other: DebugData) { + self.root_paths.extend(other.root_paths.into_iter()); + self.crate_names.extend(other.crate_names.into_iter()); + } +} -- cgit v1.2.3 From ee2ee1a8ff7ad13211d2f66d2f2f1daaf3a00bd9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 12:17:40 +0100 Subject: Move line_index --- crates/ra_ide/src/ide_db/line_index.rs | 283 +++++++++++++++++++++++ crates/ra_ide/src/ide_db/line_index_utils.rs | 332 +++++++++++++++++++++++++++ crates/ra_ide/src/ide_db/mod.rs | 6 +- crates/ra_ide/src/lib.rs | 8 +- crates/ra_ide/src/line_index.rs | 283 ----------------------- crates/ra_ide/src/line_index_utils.rs | 331 -------------------------- 6 files changed, 624 insertions(+), 619 deletions(-) create mode 100644 crates/ra_ide/src/ide_db/line_index.rs create mode 100644 crates/ra_ide/src/ide_db/line_index_utils.rs delete mode 100644 crates/ra_ide/src/line_index.rs delete mode 100644 crates/ra_ide/src/line_index_utils.rs (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/ide_db/line_index.rs b/crates/ra_ide/src/ide_db/line_index.rs new file mode 100644 index 000000000..6f99ca3a7 --- /dev/null +++ b/crates/ra_ide/src/ide_db/line_index.rs @@ -0,0 +1,283 @@ +//! FIXME: write short doc here + +use ra_syntax::TextUnit; +use rustc_hash::FxHashMap; +use superslice::Ext; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct LineIndex { + pub(crate) newlines: Vec, + pub(crate) utf16_lines: FxHashMap>, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct LineCol { + /// Zero-based + pub line: u32, + /// Zero-based + pub col_utf16: u32, +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub(crate) struct Utf16Char { + pub(crate) start: TextUnit, + pub(crate) end: TextUnit, +} + +impl Utf16Char { + fn len(&self) -> TextUnit { + self.end - self.start + } +} + +impl LineIndex { + pub fn new(text: &str) -> LineIndex { + let mut utf16_lines = FxHashMap::default(); + let mut utf16_chars = Vec::new(); + + let mut newlines = vec![0.into()]; + let mut curr_row = 0.into(); + let mut curr_col = 0.into(); + let mut line = 0; + for c in text.chars() { + curr_row += TextUnit::of_char(c); + if c == '\n' { + newlines.push(curr_row); + + // Save any utf-16 characters seen in the previous line + if !utf16_chars.is_empty() { + utf16_lines.insert(line, utf16_chars); + utf16_chars = Vec::new(); + } + + // Prepare for processing the next line + curr_col = 0.into(); + line += 1; + continue; + } + + let char_len = TextUnit::of_char(c); + if char_len.to_usize() > 1 { + utf16_chars.push(Utf16Char { start: curr_col, end: curr_col + char_len }); + } + + curr_col += char_len; + } + + // Save any utf-16 characters seen in the last line + if !utf16_chars.is_empty() { + utf16_lines.insert(line, utf16_chars); + } + + LineIndex { newlines, utf16_lines } + } + + pub fn line_col(&self, offset: TextUnit) -> LineCol { + let line = self.newlines.upper_bound(&offset) - 1; + let line_start_offset = self.newlines[line]; + let col = offset - line_start_offset; + + LineCol { line: line as u32, col_utf16: self.utf8_to_utf16_col(line as u32, col) as u32 } + } + + pub fn offset(&self, line_col: LineCol) -> TextUnit { + //FIXME: return Result + let col = self.utf16_to_utf8_col(line_col.line, line_col.col_utf16); + self.newlines[line_col.line as usize] + col + } + + fn utf8_to_utf16_col(&self, line: u32, mut col: TextUnit) -> usize { + if let Some(utf16_chars) = self.utf16_lines.get(&line) { + let mut correction = TextUnit::from_usize(0); + for c in utf16_chars { + if col >= c.end { + correction += c.len() - TextUnit::from_usize(1); + } else { + // From here on, all utf16 characters come *after* the character we are mapping, + // so we don't need to take them into account + break; + } + } + + col -= correction; + } + + col.to_usize() + } + + fn utf16_to_utf8_col(&self, line: u32, col: u32) -> TextUnit { + let mut col: TextUnit = col.into(); + if let Some(utf16_chars) = self.utf16_lines.get(&line) { + for c in utf16_chars { + if col >= c.start { + col += c.len() - TextUnit::from_usize(1); + } else { + // From here on, all utf16 characters come *after* the character we are mapping, + // so we don't need to take them into account + break; + } + } + } + + col + } +} + +#[cfg(test)] +/// Simple reference implementation to use in proptests +pub fn to_line_col(text: &str, offset: TextUnit) -> LineCol { + let mut res = LineCol { line: 0, col_utf16: 0 }; + for (i, c) in text.char_indices() { + if i + c.len_utf8() > offset.to_usize() { + // if it's an invalid offset, inside a multibyte char + // return as if it was at the start of the char + break; + } + if c == '\n' { + res.line += 1; + res.col_utf16 = 0; + } else { + res.col_utf16 += 1; + } + } + res +} + +#[cfg(test)] +mod test_line_index { + use super::*; + use proptest::{prelude::*, proptest}; + use ra_text_edit::test_utils::{arb_offset, arb_text}; + + #[test] + fn test_line_index() { + let text = "hello\nworld"; + let index = LineIndex::new(text); + assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); + assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 }); + assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 }); + assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 }); + assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 }); + assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 }); + assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 }); + assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 }); + assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 }); + + let text = "\nhello\nworld"; + let index = LineIndex::new(text); + assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); + assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 }); + assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 }); + assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 }); + assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 }); + } + + fn arb_text_with_offset() -> BoxedStrategy<(TextUnit, String)> { + arb_text().prop_flat_map(|text| (arb_offset(&text), Just(text))).boxed() + } + + fn to_line_col(text: &str, offset: TextUnit) -> LineCol { + let mut res = LineCol { line: 0, col_utf16: 0 }; + for (i, c) in text.char_indices() { + if i + c.len_utf8() > offset.to_usize() { + // if it's an invalid offset, inside a multibyte char + // return as if it was at the start of the char + break; + } + if c == '\n' { + res.line += 1; + res.col_utf16 = 0; + } else { + res.col_utf16 += 1; + } + } + res + } + + proptest! { + #[test] + fn test_line_index_proptest((offset, text) in arb_text_with_offset()) { + let expected = to_line_col(&text, offset); + let line_index = LineIndex::new(&text); + let actual = line_index.line_col(offset); + + assert_eq!(actual, expected); + } + } +} + +#[cfg(test)] +mod test_utf8_utf16_conv { + use super::*; + + #[test] + fn test_char_len() { + assert_eq!('メ'.len_utf8(), 3); + assert_eq!('メ'.len_utf16(), 1); + } + + #[test] + fn test_empty_index() { + let col_index = LineIndex::new( + " +const C: char = 'x'; +", + ); + assert_eq!(col_index.utf16_lines.len(), 0); + } + + #[test] + fn test_single_char() { + let col_index = LineIndex::new( + " +const C: char = 'メ'; +", + ); + + assert_eq!(col_index.utf16_lines.len(), 1); + assert_eq!(col_index.utf16_lines[&1].len(), 1); + assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() }); + + // UTF-8 to UTF-16, no changes + assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15); + + // UTF-8 to UTF-16 + assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20); + + // UTF-16 to UTF-8, no changes + assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextUnit::from(15)); + + // UTF-16 to UTF-8 + assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextUnit::from(21)); + } + + #[test] + fn test_string() { + let col_index = LineIndex::new( + " +const C: char = \"メ メ\"; +", + ); + + assert_eq!(col_index.utf16_lines.len(), 1); + assert_eq!(col_index.utf16_lines[&1].len(), 2); + assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() }); + assert_eq!(col_index.utf16_lines[&1][1], Utf16Char { start: 21.into(), end: 24.into() }); + + // UTF-8 to UTF-16 + assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15); + + assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19); + assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21); + + assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15); + + // UTF-16 to UTF-8 + assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextUnit::from_usize(15)); + + assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextUnit::from_usize(20)); + assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextUnit::from_usize(23)); + + assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextUnit::from_usize(15)); + } +} diff --git a/crates/ra_ide/src/ide_db/line_index_utils.rs b/crates/ra_ide/src/ide_db/line_index_utils.rs new file mode 100644 index 000000000..70bf7253c --- /dev/null +++ b/crates/ra_ide/src/ide_db/line_index_utils.rs @@ -0,0 +1,332 @@ +//! FIXME: write short doc here + +use ra_syntax::{TextRange, TextUnit}; +use ra_text_edit::{AtomTextEdit, TextEdit}; + +use crate::ide_db::line_index::{LineCol, LineIndex, Utf16Char}; + +#[derive(Debug, Clone)] +enum Step { + Newline(TextUnit), + Utf16Char(TextRange), +} + +#[derive(Debug)] +struct LineIndexStepIter<'a> { + line_index: &'a LineIndex, + next_newline_idx: usize, + utf16_chars: Option<(TextUnit, std::slice::Iter<'a, Utf16Char>)>, +} + +impl<'a> LineIndexStepIter<'a> { + fn from(line_index: &LineIndex) -> LineIndexStepIter { + let mut x = LineIndexStepIter { line_index, next_newline_idx: 0, utf16_chars: None }; + // skip first newline since it's not real + x.next(); + x + } +} + +impl<'a> Iterator for LineIndexStepIter<'a> { + type Item = Step; + fn next(&mut self) -> Option { + self.utf16_chars + .as_mut() + .and_then(|(newline, x)| { + let x = x.next()?; + Some(Step::Utf16Char(TextRange::from_to(*newline + x.start, *newline + x.end))) + }) + .or_else(|| { + let next_newline = *self.line_index.newlines.get(self.next_newline_idx)?; + self.utf16_chars = self + .line_index + .utf16_lines + .get(&(self.next_newline_idx as u32)) + .map(|x| (next_newline, x.iter())); + self.next_newline_idx += 1; + Some(Step::Newline(next_newline)) + }) + } +} + +#[derive(Debug)] +struct OffsetStepIter<'a> { + text: &'a str, + offset: TextUnit, +} + +impl<'a> Iterator for OffsetStepIter<'a> { + type Item = Step; + fn next(&mut self) -> Option { + let (next, next_offset) = self + .text + .char_indices() + .filter_map(|(i, c)| { + if c == '\n' { + let next_offset = self.offset + TextUnit::from_usize(i + 1); + let next = Step::Newline(next_offset); + Some((next, next_offset)) + } else { + let char_len = TextUnit::of_char(c); + if char_len.to_usize() > 1 { + let start = self.offset + TextUnit::from_usize(i); + let end = start + char_len; + let next = Step::Utf16Char(TextRange::from_to(start, end)); + let next_offset = end; + Some((next, next_offset)) + } else { + None + } + } + }) + .next()?; + let next_idx = (next_offset - self.offset).to_usize(); + self.text = &self.text[next_idx..]; + self.offset = next_offset; + Some(next) + } +} + +#[derive(Debug)] +enum NextSteps<'a> { + Use, + ReplaceMany(OffsetStepIter<'a>), + AddMany(OffsetStepIter<'a>), +} + +#[derive(Debug)] +struct TranslatedEdit<'a> { + delete: TextRange, + insert: &'a str, + diff: i64, +} + +struct Edits<'a> { + edits: &'a [AtomTextEdit], + current: Option>, + acc_diff: i64, +} + +impl<'a> Edits<'a> { + fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> { + let mut x = Edits { edits: text_edit.as_atoms(), current: None, acc_diff: 0 }; + x.advance_edit(); + x + } + fn advance_edit(&mut self) { + self.acc_diff += self.current.as_ref().map_or(0, |x| x.diff); + match self.edits.split_first() { + Some((next, rest)) => { + let delete = self.translate_range(next.delete); + let diff = next.insert.len() as i64 - next.delete.len().to_usize() as i64; + self.current = Some(TranslatedEdit { delete, insert: &next.insert, diff }); + self.edits = rest; + } + None => { + self.current = None; + } + } + } + + fn next_inserted_steps(&mut self) -> Option> { + let cur = self.current.as_ref()?; + let res = Some(OffsetStepIter { offset: cur.delete.start(), text: &cur.insert }); + self.advance_edit(); + res + } + + fn next_steps(&mut self, step: &Step) -> NextSteps { + let step_pos = match *step { + Step::Newline(n) => n, + Step::Utf16Char(r) => r.end(), + }; + match &mut self.current { + Some(edit) => { + if step_pos <= edit.delete.start() { + NextSteps::Use + } else if step_pos <= edit.delete.end() { + let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert }; + // empty slice to avoid returning steps again + edit.insert = &edit.insert[edit.insert.len()..]; + NextSteps::ReplaceMany(iter) + } else { + let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert }; + // empty slice to avoid returning steps again + edit.insert = &edit.insert[edit.insert.len()..]; + self.advance_edit(); + NextSteps::AddMany(iter) + } + } + None => NextSteps::Use, + } + } + + fn translate_range(&self, range: TextRange) -> TextRange { + if self.acc_diff == 0 { + range + } else { + let start = self.translate(range.start()); + let end = self.translate(range.end()); + TextRange::from_to(start, end) + } + } + + fn translate(&self, x: TextUnit) -> TextUnit { + if self.acc_diff == 0 { + x + } else { + TextUnit::from((x.to_usize() as i64 + self.acc_diff) as u32) + } + } + + fn translate_step(&self, x: &Step) -> Step { + if self.acc_diff == 0 { + x.clone() + } else { + match *x { + Step::Newline(n) => Step::Newline(self.translate(n)), + Step::Utf16Char(r) => Step::Utf16Char(self.translate_range(r)), + } + } + } +} + +#[derive(Debug)] +struct RunningLineCol { + line: u32, + last_newline: TextUnit, + col_adjust: TextUnit, +} + +impl RunningLineCol { + fn new() -> RunningLineCol { + RunningLineCol { line: 0, last_newline: TextUnit::from(0), col_adjust: TextUnit::from(0) } + } + + fn to_line_col(&self, offset: TextUnit) -> LineCol { + LineCol { + line: self.line, + col_utf16: ((offset - self.last_newline) - self.col_adjust).into(), + } + } + + fn add_line(&mut self, newline: TextUnit) { + self.line += 1; + self.last_newline = newline; + self.col_adjust = TextUnit::from(0); + } + + fn adjust_col(&mut self, range: TextRange) { + self.col_adjust += range.len() - TextUnit::from(1); + } +} + +pub fn translate_offset_with_edit( + line_index: &LineIndex, + offset: TextUnit, + text_edit: &TextEdit, +) -> LineCol { + let mut state = Edits::from_text_edit(&text_edit); + + let mut res = RunningLineCol::new(); + + macro_rules! test_step { + ($x:ident) => { + match &$x { + Step::Newline(n) => { + if offset < *n { + return res.to_line_col(offset); + } else { + res.add_line(*n); + } + } + Step::Utf16Char(x) => { + if offset < x.end() { + // if the offset is inside a multibyte char it's invalid + // clamp it to the start of the char + let clamp = offset.min(x.start()); + return res.to_line_col(clamp); + } else { + res.adjust_col(*x); + } + } + } + }; + } + + for orig_step in LineIndexStepIter::from(line_index) { + loop { + let translated_step = state.translate_step(&orig_step); + match state.next_steps(&translated_step) { + NextSteps::Use => { + test_step!(translated_step); + break; + } + NextSteps::ReplaceMany(ns) => { + for n in ns { + test_step!(n); + } + break; + } + NextSteps::AddMany(ns) => { + for n in ns { + test_step!(n); + } + } + } + } + } + + loop { + match state.next_inserted_steps() { + None => break, + Some(ns) => { + for n in ns { + test_step!(n); + } + } + } + } + + res.to_line_col(offset) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::line_index; + use proptest::{prelude::*, proptest}; + use ra_text_edit::test_utils::{arb_offset, arb_text_with_edit}; + use ra_text_edit::TextEdit; + + #[derive(Debug)] + struct ArbTextWithEditAndOffset { + text: String, + edit: TextEdit, + edited_text: String, + offset: TextUnit, + } + + fn arb_text_with_edit_and_offset() -> BoxedStrategy { + arb_text_with_edit() + .prop_flat_map(|x| { + let edited_text = x.edit.apply(&x.text); + let arb_offset = arb_offset(&edited_text); + (Just(x), Just(edited_text), arb_offset).prop_map(|(x, edited_text, offset)| { + ArbTextWithEditAndOffset { text: x.text, edit: x.edit, edited_text, offset } + }) + }) + .boxed() + } + + proptest! { + #[test] + fn test_translate_offset_with_edit(x in arb_text_with_edit_and_offset()) { + let expected = line_index::to_line_col(&x.edited_text, x.offset); + let line_index = LineIndex::new(&x.text); + let actual = translate_offset_with_edit(&line_index, x.offset, &x.edit); + + assert_eq!(actual, expected); + } + } +} diff --git a/crates/ra_ide/src/ide_db/mod.rs b/crates/ra_ide/src/ide_db/mod.rs index 47d0aed6f..cd47132ce 100644 --- a/crates/ra_ide/src/ide_db/mod.rs +++ b/crates/ra_ide/src/ide_db/mod.rs @@ -1,5 +1,8 @@ //! FIXME: write short doc here +pub mod line_index; +pub mod line_index_utils; + use std::sync::Arc; use ra_db::{ @@ -10,8 +13,9 @@ use ra_db::{ use rustc_hash::FxHashMap; use crate::{ + ide_db::line_index::LineIndex, symbol_index::{self, SymbolsDatabase}, - FeatureFlags, LineIndex, + FeatureFlags, }; #[salsa::database( diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index f10c871b8..00d608269 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -37,8 +37,6 @@ mod assists; mod diagnostics; mod syntax_tree; mod folding_ranges; -mod line_index; -mod line_index_utils; mod join_lines; mod typing; mod matching_brace; @@ -75,9 +73,11 @@ pub use crate::{ feature_flags::FeatureFlags, folding_ranges::{Fold, FoldKind}, hover::HoverResult, + ide_db::{ + line_index::{LineCol, LineIndex}, + line_index_utils::translate_offset_with_edit, + }, inlay_hints::{InlayHint, InlayKind}, - line_index::{LineCol, LineIndex}, - line_index_utils::translate_offset_with_edit, references::{ Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, }, diff --git a/crates/ra_ide/src/line_index.rs b/crates/ra_ide/src/line_index.rs deleted file mode 100644 index 710890d27..000000000 --- a/crates/ra_ide/src/line_index.rs +++ /dev/null @@ -1,283 +0,0 @@ -//! FIXME: write short doc here - -use crate::TextUnit; -use rustc_hash::FxHashMap; -use superslice::Ext; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct LineIndex { - pub(crate) newlines: Vec, - pub(crate) utf16_lines: FxHashMap>, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct LineCol { - /// Zero-based - pub line: u32, - /// Zero-based - pub col_utf16: u32, -} - -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub(crate) struct Utf16Char { - pub(crate) start: TextUnit, - pub(crate) end: TextUnit, -} - -impl Utf16Char { - fn len(&self) -> TextUnit { - self.end - self.start - } -} - -impl LineIndex { - pub fn new(text: &str) -> LineIndex { - let mut utf16_lines = FxHashMap::default(); - let mut utf16_chars = Vec::new(); - - let mut newlines = vec![0.into()]; - let mut curr_row = 0.into(); - let mut curr_col = 0.into(); - let mut line = 0; - for c in text.chars() { - curr_row += TextUnit::of_char(c); - if c == '\n' { - newlines.push(curr_row); - - // Save any utf-16 characters seen in the previous line - if !utf16_chars.is_empty() { - utf16_lines.insert(line, utf16_chars); - utf16_chars = Vec::new(); - } - - // Prepare for processing the next line - curr_col = 0.into(); - line += 1; - continue; - } - - let char_len = TextUnit::of_char(c); - if char_len.to_usize() > 1 { - utf16_chars.push(Utf16Char { start: curr_col, end: curr_col + char_len }); - } - - curr_col += char_len; - } - - // Save any utf-16 characters seen in the last line - if !utf16_chars.is_empty() { - utf16_lines.insert(line, utf16_chars); - } - - LineIndex { newlines, utf16_lines } - } - - pub fn line_col(&self, offset: TextUnit) -> LineCol { - let line = self.newlines.upper_bound(&offset) - 1; - let line_start_offset = self.newlines[line]; - let col = offset - line_start_offset; - - LineCol { line: line as u32, col_utf16: self.utf8_to_utf16_col(line as u32, col) as u32 } - } - - pub fn offset(&self, line_col: LineCol) -> TextUnit { - //FIXME: return Result - let col = self.utf16_to_utf8_col(line_col.line, line_col.col_utf16); - self.newlines[line_col.line as usize] + col - } - - fn utf8_to_utf16_col(&self, line: u32, mut col: TextUnit) -> usize { - if let Some(utf16_chars) = self.utf16_lines.get(&line) { - let mut correction = TextUnit::from_usize(0); - for c in utf16_chars { - if col >= c.end { - correction += c.len() - TextUnit::from_usize(1); - } else { - // From here on, all utf16 characters come *after* the character we are mapping, - // so we don't need to take them into account - break; - } - } - - col -= correction; - } - - col.to_usize() - } - - fn utf16_to_utf8_col(&self, line: u32, col: u32) -> TextUnit { - let mut col: TextUnit = col.into(); - if let Some(utf16_chars) = self.utf16_lines.get(&line) { - for c in utf16_chars { - if col >= c.start { - col += c.len() - TextUnit::from_usize(1); - } else { - // From here on, all utf16 characters come *after* the character we are mapping, - // so we don't need to take them into account - break; - } - } - } - - col - } -} - -#[cfg(test)] -/// Simple reference implementation to use in proptests -pub fn to_line_col(text: &str, offset: TextUnit) -> LineCol { - let mut res = LineCol { line: 0, col_utf16: 0 }; - for (i, c) in text.char_indices() { - if i + c.len_utf8() > offset.to_usize() { - // if it's an invalid offset, inside a multibyte char - // return as if it was at the start of the char - break; - } - if c == '\n' { - res.line += 1; - res.col_utf16 = 0; - } else { - res.col_utf16 += 1; - } - } - res -} - -#[cfg(test)] -mod test_line_index { - use super::*; - use proptest::{prelude::*, proptest}; - use ra_text_edit::test_utils::{arb_offset, arb_text}; - - #[test] - fn test_line_index() { - let text = "hello\nworld"; - let index = LineIndex::new(text); - assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); - assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 }); - assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 }); - assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 }); - assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 }); - assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 }); - assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 }); - assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 }); - assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 }); - - let text = "\nhello\nworld"; - let index = LineIndex::new(text); - assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); - assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 }); - assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 }); - assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 }); - assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 }); - } - - fn arb_text_with_offset() -> BoxedStrategy<(TextUnit, String)> { - arb_text().prop_flat_map(|text| (arb_offset(&text), Just(text))).boxed() - } - - fn to_line_col(text: &str, offset: TextUnit) -> LineCol { - let mut res = LineCol { line: 0, col_utf16: 0 }; - for (i, c) in text.char_indices() { - if i + c.len_utf8() > offset.to_usize() { - // if it's an invalid offset, inside a multibyte char - // return as if it was at the start of the char - break; - } - if c == '\n' { - res.line += 1; - res.col_utf16 = 0; - } else { - res.col_utf16 += 1; - } - } - res - } - - proptest! { - #[test] - fn test_line_index_proptest((offset, text) in arb_text_with_offset()) { - let expected = to_line_col(&text, offset); - let line_index = LineIndex::new(&text); - let actual = line_index.line_col(offset); - - assert_eq!(actual, expected); - } - } -} - -#[cfg(test)] -mod test_utf8_utf16_conv { - use super::*; - - #[test] - fn test_char_len() { - assert_eq!('メ'.len_utf8(), 3); - assert_eq!('メ'.len_utf16(), 1); - } - - #[test] - fn test_empty_index() { - let col_index = LineIndex::new( - " -const C: char = 'x'; -", - ); - assert_eq!(col_index.utf16_lines.len(), 0); - } - - #[test] - fn test_single_char() { - let col_index = LineIndex::new( - " -const C: char = 'メ'; -", - ); - - assert_eq!(col_index.utf16_lines.len(), 1); - assert_eq!(col_index.utf16_lines[&1].len(), 1); - assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() }); - - // UTF-8 to UTF-16, no changes - assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15); - - // UTF-8 to UTF-16 - assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20); - - // UTF-16 to UTF-8, no changes - assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextUnit::from(15)); - - // UTF-16 to UTF-8 - assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextUnit::from(21)); - } - - #[test] - fn test_string() { - let col_index = LineIndex::new( - " -const C: char = \"メ メ\"; -", - ); - - assert_eq!(col_index.utf16_lines.len(), 1); - assert_eq!(col_index.utf16_lines[&1].len(), 2); - assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() }); - assert_eq!(col_index.utf16_lines[&1][1], Utf16Char { start: 21.into(), end: 24.into() }); - - // UTF-8 to UTF-16 - assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15); - - assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19); - assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21); - - assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15); - - // UTF-16 to UTF-8 - assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextUnit::from_usize(15)); - - assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextUnit::from_usize(20)); - assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextUnit::from_usize(23)); - - assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextUnit::from_usize(15)); - } -} diff --git a/crates/ra_ide/src/line_index_utils.rs b/crates/ra_ide/src/line_index_utils.rs deleted file mode 100644 index bd1e08feb..000000000 --- a/crates/ra_ide/src/line_index_utils.rs +++ /dev/null @@ -1,331 +0,0 @@ -//! FIXME: write short doc here - -use crate::{line_index::Utf16Char, LineCol, LineIndex}; -use ra_syntax::{TextRange, TextUnit}; -use ra_text_edit::{AtomTextEdit, TextEdit}; - -#[derive(Debug, Clone)] -enum Step { - Newline(TextUnit), - Utf16Char(TextRange), -} - -#[derive(Debug)] -struct LineIndexStepIter<'a> { - line_index: &'a LineIndex, - next_newline_idx: usize, - utf16_chars: Option<(TextUnit, std::slice::Iter<'a, Utf16Char>)>, -} - -impl<'a> LineIndexStepIter<'a> { - fn from(line_index: &LineIndex) -> LineIndexStepIter { - let mut x = LineIndexStepIter { line_index, next_newline_idx: 0, utf16_chars: None }; - // skip first newline since it's not real - x.next(); - x - } -} - -impl<'a> Iterator for LineIndexStepIter<'a> { - type Item = Step; - fn next(&mut self) -> Option { - self.utf16_chars - .as_mut() - .and_then(|(newline, x)| { - let x = x.next()?; - Some(Step::Utf16Char(TextRange::from_to(*newline + x.start, *newline + x.end))) - }) - .or_else(|| { - let next_newline = *self.line_index.newlines.get(self.next_newline_idx)?; - self.utf16_chars = self - .line_index - .utf16_lines - .get(&(self.next_newline_idx as u32)) - .map(|x| (next_newline, x.iter())); - self.next_newline_idx += 1; - Some(Step::Newline(next_newline)) - }) - } -} - -#[derive(Debug)] -struct OffsetStepIter<'a> { - text: &'a str, - offset: TextUnit, -} - -impl<'a> Iterator for OffsetStepIter<'a> { - type Item = Step; - fn next(&mut self) -> Option { - let (next, next_offset) = self - .text - .char_indices() - .filter_map(|(i, c)| { - if c == '\n' { - let next_offset = self.offset + TextUnit::from_usize(i + 1); - let next = Step::Newline(next_offset); - Some((next, next_offset)) - } else { - let char_len = TextUnit::of_char(c); - if char_len.to_usize() > 1 { - let start = self.offset + TextUnit::from_usize(i); - let end = start + char_len; - let next = Step::Utf16Char(TextRange::from_to(start, end)); - let next_offset = end; - Some((next, next_offset)) - } else { - None - } - } - }) - .next()?; - let next_idx = (next_offset - self.offset).to_usize(); - self.text = &self.text[next_idx..]; - self.offset = next_offset; - Some(next) - } -} - -#[derive(Debug)] -enum NextSteps<'a> { - Use, - ReplaceMany(OffsetStepIter<'a>), - AddMany(OffsetStepIter<'a>), -} - -#[derive(Debug)] -struct TranslatedEdit<'a> { - delete: TextRange, - insert: &'a str, - diff: i64, -} - -struct Edits<'a> { - edits: &'a [AtomTextEdit], - current: Option>, - acc_diff: i64, -} - -impl<'a> Edits<'a> { - fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> { - let mut x = Edits { edits: text_edit.as_atoms(), current: None, acc_diff: 0 }; - x.advance_edit(); - x - } - fn advance_edit(&mut self) { - self.acc_diff += self.current.as_ref().map_or(0, |x| x.diff); - match self.edits.split_first() { - Some((next, rest)) => { - let delete = self.translate_range(next.delete); - let diff = next.insert.len() as i64 - next.delete.len().to_usize() as i64; - self.current = Some(TranslatedEdit { delete, insert: &next.insert, diff }); - self.edits = rest; - } - None => { - self.current = None; - } - } - } - - fn next_inserted_steps(&mut self) -> Option> { - let cur = self.current.as_ref()?; - let res = Some(OffsetStepIter { offset: cur.delete.start(), text: &cur.insert }); - self.advance_edit(); - res - } - - fn next_steps(&mut self, step: &Step) -> NextSteps { - let step_pos = match *step { - Step::Newline(n) => n, - Step::Utf16Char(r) => r.end(), - }; - match &mut self.current { - Some(edit) => { - if step_pos <= edit.delete.start() { - NextSteps::Use - } else if step_pos <= edit.delete.end() { - let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert }; - // empty slice to avoid returning steps again - edit.insert = &edit.insert[edit.insert.len()..]; - NextSteps::ReplaceMany(iter) - } else { - let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert }; - // empty slice to avoid returning steps again - edit.insert = &edit.insert[edit.insert.len()..]; - self.advance_edit(); - NextSteps::AddMany(iter) - } - } - None => NextSteps::Use, - } - } - - fn translate_range(&self, range: TextRange) -> TextRange { - if self.acc_diff == 0 { - range - } else { - let start = self.translate(range.start()); - let end = self.translate(range.end()); - TextRange::from_to(start, end) - } - } - - fn translate(&self, x: TextUnit) -> TextUnit { - if self.acc_diff == 0 { - x - } else { - TextUnit::from((x.to_usize() as i64 + self.acc_diff) as u32) - } - } - - fn translate_step(&self, x: &Step) -> Step { - if self.acc_diff == 0 { - x.clone() - } else { - match *x { - Step::Newline(n) => Step::Newline(self.translate(n)), - Step::Utf16Char(r) => Step::Utf16Char(self.translate_range(r)), - } - } - } -} - -#[derive(Debug)] -struct RunningLineCol { - line: u32, - last_newline: TextUnit, - col_adjust: TextUnit, -} - -impl RunningLineCol { - fn new() -> RunningLineCol { - RunningLineCol { line: 0, last_newline: TextUnit::from(0), col_adjust: TextUnit::from(0) } - } - - fn to_line_col(&self, offset: TextUnit) -> LineCol { - LineCol { - line: self.line, - col_utf16: ((offset - self.last_newline) - self.col_adjust).into(), - } - } - - fn add_line(&mut self, newline: TextUnit) { - self.line += 1; - self.last_newline = newline; - self.col_adjust = TextUnit::from(0); - } - - fn adjust_col(&mut self, range: TextRange) { - self.col_adjust += range.len() - TextUnit::from(1); - } -} - -pub fn translate_offset_with_edit( - line_index: &LineIndex, - offset: TextUnit, - text_edit: &TextEdit, -) -> LineCol { - let mut state = Edits::from_text_edit(&text_edit); - - let mut res = RunningLineCol::new(); - - macro_rules! test_step { - ($x:ident) => { - match &$x { - Step::Newline(n) => { - if offset < *n { - return res.to_line_col(offset); - } else { - res.add_line(*n); - } - } - Step::Utf16Char(x) => { - if offset < x.end() { - // if the offset is inside a multibyte char it's invalid - // clamp it to the start of the char - let clamp = offset.min(x.start()); - return res.to_line_col(clamp); - } else { - res.adjust_col(*x); - } - } - } - }; - } - - for orig_step in LineIndexStepIter::from(line_index) { - loop { - let translated_step = state.translate_step(&orig_step); - match state.next_steps(&translated_step) { - NextSteps::Use => { - test_step!(translated_step); - break; - } - NextSteps::ReplaceMany(ns) => { - for n in ns { - test_step!(n); - } - break; - } - NextSteps::AddMany(ns) => { - for n in ns { - test_step!(n); - } - } - } - } - } - - loop { - match state.next_inserted_steps() { - None => break, - Some(ns) => { - for n in ns { - test_step!(n); - } - } - } - } - - res.to_line_col(offset) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::line_index; - use proptest::{prelude::*, proptest}; - use ra_text_edit::test_utils::{arb_offset, arb_text_with_edit}; - use ra_text_edit::TextEdit; - - #[derive(Debug)] - struct ArbTextWithEditAndOffset { - text: String, - edit: TextEdit, - edited_text: String, - offset: TextUnit, - } - - fn arb_text_with_edit_and_offset() -> BoxedStrategy { - arb_text_with_edit() - .prop_flat_map(|x| { - let edited_text = x.edit.apply(&x.text); - let arb_offset = arb_offset(&edited_text); - (Just(x), Just(edited_text), arb_offset).prop_map(|(x, edited_text, offset)| { - ArbTextWithEditAndOffset { text: x.text, edit: x.edit, edited_text, offset } - }) - }) - .boxed() - } - - proptest! { - #[test] - fn test_translate_offset_with_edit(x in arb_text_with_edit_and_offset()) { - let expected = line_index::to_line_col(&x.edited_text, x.offset); - let line_index = LineIndex::new(&x.text); - let actual = translate_offset_with_edit(&line_index, x.offset, &x.edit); - - assert_eq!(actual, expected); - } - } -} -- cgit v1.2.3 From ec95152a4ec1ed617452c8578df128a117ab0b5d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 12:18:52 +0100 Subject: Move FeatureFlags --- crates/ra_ide/src/feature_flags.rs | 71 ------------------------------- crates/ra_ide/src/ide_db/feature_flags.rs | 71 +++++++++++++++++++++++++++++++ crates/ra_ide/src/ide_db/mod.rs | 4 +- crates/ra_ide/src/lib.rs | 3 +- 4 files changed, 74 insertions(+), 75 deletions(-) delete mode 100644 crates/ra_ide/src/feature_flags.rs create mode 100644 crates/ra_ide/src/ide_db/feature_flags.rs (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/feature_flags.rs b/crates/ra_ide/src/feature_flags.rs deleted file mode 100644 index 85617640d..000000000 --- a/crates/ra_ide/src/feature_flags.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! FIXME: write short doc here - -use rustc_hash::FxHashMap; - -/// Feature flags hold fine-grained toggles for all *user-visible* features of -/// rust-analyzer. -/// -/// The exists such that users are able to disable any annoying feature (and, -/// with many users and many features, some features are bound to be annoying -/// for some users) -/// -/// Note that we purposefully use run-time checked strings, and not something -/// checked at compile time, to keep things simple and flexible. -/// -/// Also note that, at the moment, `FeatureFlags` also store features for -/// `ra_lsp_server`. This should be benign layering violation. -#[derive(Debug)] -pub struct FeatureFlags { - flags: FxHashMap, -} - -impl FeatureFlags { - fn new(flags: &[(&str, bool)]) -> FeatureFlags { - let flags = flags - .iter() - .map(|&(name, value)| { - check_flag_name(name); - (name.to_string(), value) - }) - .collect(); - FeatureFlags { flags } - } - - pub fn set(&mut self, flag: &str, value: bool) -> Result<(), ()> { - match self.flags.get_mut(flag) { - None => Err(()), - Some(slot) => { - *slot = value; - Ok(()) - } - } - } - - pub fn get(&self, flag: &str) -> bool { - match self.flags.get(flag) { - None => panic!("unknown flag: {:?}", flag), - Some(value) => *value, - } - } -} - -impl Default for FeatureFlags { - fn default() -> FeatureFlags { - FeatureFlags::new(&[ - ("lsp.diagnostics", true), - ("completion.insertion.add-call-parenthesis", true), - ("completion.enable-postfix", true), - ("notifications.workspace-loaded", true), - ("notifications.cargo-toml-not-found", true), - ]) - } -} - -fn check_flag_name(flag: &str) { - for c in flag.bytes() { - match c { - b'a'..=b'z' | b'-' | b'.' => (), - _ => panic!("flag name does not match conventions: {:?}", flag), - } - } -} diff --git a/crates/ra_ide/src/ide_db/feature_flags.rs b/crates/ra_ide/src/ide_db/feature_flags.rs new file mode 100644 index 000000000..85617640d --- /dev/null +++ b/crates/ra_ide/src/ide_db/feature_flags.rs @@ -0,0 +1,71 @@ +//! FIXME: write short doc here + +use rustc_hash::FxHashMap; + +/// Feature flags hold fine-grained toggles for all *user-visible* features of +/// rust-analyzer. +/// +/// The exists such that users are able to disable any annoying feature (and, +/// with many users and many features, some features are bound to be annoying +/// for some users) +/// +/// Note that we purposefully use run-time checked strings, and not something +/// checked at compile time, to keep things simple and flexible. +/// +/// Also note that, at the moment, `FeatureFlags` also store features for +/// `ra_lsp_server`. This should be benign layering violation. +#[derive(Debug)] +pub struct FeatureFlags { + flags: FxHashMap, +} + +impl FeatureFlags { + fn new(flags: &[(&str, bool)]) -> FeatureFlags { + let flags = flags + .iter() + .map(|&(name, value)| { + check_flag_name(name); + (name.to_string(), value) + }) + .collect(); + FeatureFlags { flags } + } + + pub fn set(&mut self, flag: &str, value: bool) -> Result<(), ()> { + match self.flags.get_mut(flag) { + None => Err(()), + Some(slot) => { + *slot = value; + Ok(()) + } + } + } + + pub fn get(&self, flag: &str) -> bool { + match self.flags.get(flag) { + None => panic!("unknown flag: {:?}", flag), + Some(value) => *value, + } + } +} + +impl Default for FeatureFlags { + fn default() -> FeatureFlags { + FeatureFlags::new(&[ + ("lsp.diagnostics", true), + ("completion.insertion.add-call-parenthesis", true), + ("completion.enable-postfix", true), + ("notifications.workspace-loaded", true), + ("notifications.cargo-toml-not-found", true), + ]) + } +} + +fn check_flag_name(flag: &str) { + for c in flag.bytes() { + match c { + b'a'..=b'z' | b'-' | b'.' => (), + _ => panic!("flag name does not match conventions: {:?}", flag), + } + } +} diff --git a/crates/ra_ide/src/ide_db/mod.rs b/crates/ra_ide/src/ide_db/mod.rs index cd47132ce..834ad0135 100644 --- a/crates/ra_ide/src/ide_db/mod.rs +++ b/crates/ra_ide/src/ide_db/mod.rs @@ -2,6 +2,7 @@ pub mod line_index; pub mod line_index_utils; +pub mod feature_flags; use std::sync::Arc; @@ -13,9 +14,8 @@ use ra_db::{ use rustc_hash::FxHashMap; use crate::{ - ide_db::line_index::LineIndex, + ide_db::{feature_flags::FeatureFlags, line_index::LineIndex}, symbol_index::{self, SymbolsDatabase}, - FeatureFlags, }; #[salsa::database( diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 00d608269..003a5e528 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -17,7 +17,6 @@ pub mod mock_analysis; mod symbol_index; mod change; mod source_change; -mod feature_flags; mod status; mod completion; @@ -70,10 +69,10 @@ pub use crate::{ diagnostics::Severity, display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, expand_macro::ExpandedMacro, - feature_flags::FeatureFlags, folding_ranges::{Fold, FoldKind}, hover::HoverResult, ide_db::{ + feature_flags::FeatureFlags, line_index::{LineCol, LineIndex}, line_index_utils::translate_offset_with_edit, }, -- cgit v1.2.3 From ad247aa67061f4dcba85e20b82ca47e9a86eff56 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 12:22:35 +0100 Subject: Move symbol_index --- crates/ra_ide/src/change.rs | 2 +- crates/ra_ide/src/goto_definition.rs | 3 +- crates/ra_ide/src/ide_db/mod.rs | 6 +- crates/ra_ide/src/ide_db/symbol_index.rs | 405 +++++++++++++++++++++++++++++++ crates/ra_ide/src/imports_locator.rs | 2 +- crates/ra_ide/src/lib.rs | 7 +- crates/ra_ide/src/status.rs | 2 +- crates/ra_ide/src/symbol_index.rs | 405 ------------------------------- 8 files changed, 418 insertions(+), 414 deletions(-) create mode 100644 crates/ra_ide/src/ide_db/symbol_index.rs delete mode 100644 crates/ra_ide/src/symbol_index.rs (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs index 18dad2ea3..a0aeee1f7 100644 --- a/crates/ra_ide/src/change.rs +++ b/crates/ra_ide/src/change.rs @@ -15,7 +15,7 @@ use rustc_hash::FxHashMap; use crate::{ db::{DebugData, RootDatabase}, - symbol_index::{SymbolIndex, SymbolsDatabase}, + ide_db::symbol_index::{SymbolIndex, SymbolsDatabase}, }; #[derive(Default)] diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 5a12a619c..b67e32626 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -12,6 +12,7 @@ use crate::{ db::RootDatabase, display::{ShortLabel, ToNav}, expand::descend_into_macros, + ide_db::symbol_index, references::{classify_name_ref, NameKind::*}, FilePosition, NavigationTarget, RangeInfo, }; @@ -94,7 +95,7 @@ pub(crate) fn reference_definition( }; // Fallback index based approach: - let navs = crate::symbol_index::index_resolve(sb.db, name_ref.value) + let navs = symbol_index::index_resolve(sb.db, name_ref.value) .into_iter() .map(|s| s.to_nav(sb.db)) .collect(); diff --git a/crates/ra_ide/src/ide_db/mod.rs b/crates/ra_ide/src/ide_db/mod.rs index 834ad0135..924ee9968 100644 --- a/crates/ra_ide/src/ide_db/mod.rs +++ b/crates/ra_ide/src/ide_db/mod.rs @@ -3,6 +3,7 @@ pub mod line_index; pub mod line_index_utils; pub mod feature_flags; +pub mod symbol_index; use std::sync::Arc; @@ -13,9 +14,8 @@ use ra_db::{ }; use rustc_hash::FxHashMap; -use crate::{ - ide_db::{feature_flags::FeatureFlags, line_index::LineIndex}, - symbol_index::{self, SymbolsDatabase}, +use crate::ide_db::{ + feature_flags::FeatureFlags, line_index::LineIndex, symbol_index::SymbolsDatabase, }; #[salsa::database( diff --git a/crates/ra_ide/src/ide_db/symbol_index.rs b/crates/ra_ide/src/ide_db/symbol_index.rs new file mode 100644 index 000000000..4ceb5e66f --- /dev/null +++ b/crates/ra_ide/src/ide_db/symbol_index.rs @@ -0,0 +1,405 @@ +//! This module handles fuzzy-searching of functions, structs and other symbols +//! by name across the whole workspace and dependencies. +//! +//! It works by building an incrementally-updated text-search index of all +//! symbols. The backbone of the index is the **awesome** `fst` crate by +//! @BurntSushi. +//! +//! In a nutshell, you give a set of strings to `fst`, and it builds a +//! finite state machine describing this set of strings. The strings which +//! could fuzzy-match a pattern can also be described by a finite state machine. +//! What is freaking cool is that you can now traverse both state machines in +//! lock-step to enumerate the strings which are both in the input set and +//! fuzz-match the query. Or, more formally, given two languages described by +//! FSTs, one can build a product FST which describes the intersection of the +//! languages. +//! +//! `fst` does not support cheap updating of the index, but it supports unioning +//! of state machines. So, to account for changing source code, we build an FST +//! for each library (which is assumed to never change) and an FST for each Rust +//! file in the current workspace, and run a query against the union of all +//! those FSTs. +use std::{ + fmt, + hash::{Hash, Hasher}, + mem, + sync::Arc, +}; + +use fst::{self, Streamer}; +use ra_db::{ + salsa::{self, ParallelDatabase}, + FileId, SourceDatabaseExt, SourceRootId, +}; +use ra_syntax::{ + ast::{self, NameOwner}, + match_ast, AstNode, Parse, SmolStr, SourceFile, + SyntaxKind::{self, *}, + SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent, +}; +#[cfg(not(feature = "wasm"))] +use rayon::prelude::*; + +use crate::{ide_db::RootDatabase, Query}; + +#[salsa::query_group(SymbolsDatabaseStorage)] +pub(crate) trait SymbolsDatabase: hir::db::HirDatabase { + fn file_symbols(&self, file_id: FileId) -> Arc; + #[salsa::input] + fn library_symbols(&self, id: SourceRootId) -> Arc; + /// The set of "local" (that is, from the current workspace) roots. + /// Files in local roots are assumed to change frequently. + #[salsa::input] + fn local_roots(&self) -> Arc>; + /// The set of roots for crates.io libraries. + /// Files in libraries are assumed to never change. + #[salsa::input] + fn library_roots(&self) -> Arc>; +} + +fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc { + db.check_canceled(); + let parse = db.parse(file_id); + + let symbols = source_file_to_file_symbols(&parse.tree(), file_id); + + // FIXME: add macros here + + Arc::new(SymbolIndex::new(symbols)) +} + +pub(crate) fn world_symbols(db: &RootDatabase, query: Query) -> Vec { + /// Need to wrap Snapshot to provide `Clone` impl for `map_with` + struct Snap(salsa::Snapshot); + impl Clone for Snap { + fn clone(&self) -> Snap { + Snap(self.0.snapshot()) + } + } + + let buf: Vec> = if query.libs { + let snap = Snap(db.snapshot()); + #[cfg(not(feature = "wasm"))] + let buf = db + .library_roots() + .par_iter() + .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id)) + .collect(); + + #[cfg(feature = "wasm")] + let buf = db.library_roots().iter().map(|&lib_id| snap.0.library_symbols(lib_id)).collect(); + + buf + } else { + let mut files = Vec::new(); + for &root in db.local_roots().iter() { + let sr = db.source_root(root); + files.extend(sr.walk()) + } + + let snap = Snap(db.snapshot()); + #[cfg(not(feature = "wasm"))] + let buf = + files.par_iter().map_with(snap, |db, &file_id| db.0.file_symbols(file_id)).collect(); + + #[cfg(feature = "wasm")] + let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect(); + + buf + }; + query.search(&buf) +} + +pub(crate) fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec { + let name = name_ref.text(); + let mut query = Query::new(name.to_string()); + query.exact(); + query.limit(4); + world_symbols(db, query) +} + +#[derive(Default)] +pub(crate) struct SymbolIndex { + symbols: Vec, + map: fst::Map, +} + +impl fmt::Debug for SymbolIndex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("SymbolIndex").field("n_symbols", &self.symbols.len()).finish() + } +} + +impl PartialEq for SymbolIndex { + fn eq(&self, other: &SymbolIndex) -> bool { + self.symbols == other.symbols + } +} + +impl Eq for SymbolIndex {} + +impl Hash for SymbolIndex { + fn hash(&self, hasher: &mut H) { + self.symbols.hash(hasher) + } +} + +impl SymbolIndex { + fn new(mut symbols: Vec) -> SymbolIndex { + fn cmp_key<'a>(s1: &'a FileSymbol) -> impl Ord + 'a { + unicase::Ascii::new(s1.name.as_str()) + } + #[cfg(not(feature = "wasm"))] + symbols.par_sort_by(|s1, s2| cmp_key(s1).cmp(&cmp_key(s2))); + + #[cfg(feature = "wasm")] + symbols.sort_by(|s1, s2| cmp_key(s1).cmp(&cmp_key(s2))); + + let mut builder = fst::MapBuilder::memory(); + + let mut last_batch_start = 0; + + for idx in 0..symbols.len() { + if symbols.get(last_batch_start).map(cmp_key) == symbols.get(idx + 1).map(cmp_key) { + continue; + } + + let start = last_batch_start; + let end = idx + 1; + last_batch_start = end; + + let key = symbols[start].name.as_str().to_lowercase(); + let value = SymbolIndex::range_to_map_value(start, end); + + builder.insert(key, value).unwrap(); + } + + let map = fst::Map::from_bytes(builder.into_inner().unwrap()).unwrap(); + SymbolIndex { symbols, map } + } + + pub(crate) fn len(&self) -> usize { + self.symbols.len() + } + + pub(crate) fn memory_size(&self) -> usize { + self.map.as_fst().size() + self.symbols.len() * mem::size_of::() + } + + #[cfg(not(feature = "wasm"))] + pub(crate) fn for_files( + files: impl ParallelIterator)>, + ) -> SymbolIndex { + let symbols = files + .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id)) + .collect::>(); + SymbolIndex::new(symbols) + } + + #[cfg(feature = "wasm")] + pub(crate) fn for_files( + files: impl Iterator)>, + ) -> SymbolIndex { + let symbols = files + .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id)) + .collect::>(); + SymbolIndex::new(symbols) + } + + fn range_to_map_value(start: usize, end: usize) -> u64 { + debug_assert![start <= (std::u32::MAX as usize)]; + debug_assert![end <= (std::u32::MAX as usize)]; + + ((start as u64) << 32) | end as u64 + } + + fn map_value_to_range(value: u64) -> (usize, usize) { + let end = value as u32 as usize; + let start = (value >> 32) as usize; + (start, end) + } +} + +impl Query { + pub(crate) fn search(self, indices: &[Arc]) -> Vec { + let mut op = fst::map::OpBuilder::new(); + for file_symbols in indices.iter() { + let automaton = fst::automaton::Subsequence::new(&self.lowercased); + op = op.add(file_symbols.map.search(automaton)) + } + let mut stream = op.union(); + let mut res = Vec::new(); + while let Some((_, indexed_values)) = stream.next() { + if res.len() >= self.limit { + break; + } + for indexed_value in indexed_values { + let symbol_index = &indices[indexed_value.index]; + let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); + + for symbol in &symbol_index.symbols[start..end] { + if self.only_types && !is_type(symbol.ptr.kind()) { + continue; + } + if self.exact && symbol.name != self.query { + continue; + } + res.push(symbol.clone()); + } + } + } + res + } +} + +fn is_type(kind: SyntaxKind) -> bool { + match kind { + STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => true, + _ => false, + } +} + +/// The actual data that is stored in the index. It should be as compact as +/// possible. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub(crate) struct FileSymbol { + pub(crate) file_id: FileId, + pub(crate) name: SmolStr, + pub(crate) ptr: SyntaxNodePtr, + pub(crate) name_range: Option, + pub(crate) container_name: Option, +} + +fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec { + let mut symbols = Vec::new(); + let mut stack = Vec::new(); + + for event in source_file.syntax().preorder() { + match event { + WalkEvent::Enter(node) => { + if let Some(mut symbol) = to_file_symbol(&node, file_id) { + symbol.container_name = stack.last().cloned(); + + stack.push(symbol.name.clone()); + symbols.push(symbol); + } + } + + WalkEvent::Leave(node) => { + if to_symbol(&node).is_some() { + stack.pop(); + } + } + } + } + + symbols +} + +fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { + fn decl(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { + let name = node.name()?; + let name_range = name.syntax().text_range(); + let name = name.text().clone(); + let ptr = SyntaxNodePtr::new(node.syntax()); + + Some((name, ptr, name_range)) + } + match_ast! { + match node { + ast::FnDef(it) => { decl(it) }, + ast::StructDef(it) => { decl(it) }, + ast::EnumDef(it) => { decl(it) }, + ast::TraitDef(it) => { decl(it) }, + ast::Module(it) => { decl(it) }, + ast::TypeAliasDef(it) => { decl(it) }, + ast::ConstDef(it) => { decl(it) }, + ast::StaticDef(it) => { decl(it) }, + _ => None, + } + } +} + +fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option { + to_symbol(node).map(move |(name, ptr, name_range)| FileSymbol { + name, + ptr, + file_id, + name_range: Some(name_range), + container_name: None, + }) +} + +#[cfg(test)] +mod tests { + use crate::{display::NavigationTarget, mock_analysis::single_file, Query}; + use ra_syntax::{ + SmolStr, + SyntaxKind::{FN_DEF, STRUCT_DEF}, + }; + + #[test] + fn test_world_symbols_with_no_container() { + let code = r#" + enum FooInner { } + "#; + + let mut symbols = get_symbols_matching(code, "FooInner"); + + let s = symbols.pop().unwrap(); + + assert_eq!(s.name(), "FooInner"); + assert!(s.container_name().is_none()); + } + + #[test] + fn test_world_symbols_include_container_name() { + let code = r#" +fn foo() { + enum FooInner { } +} + "#; + + let mut symbols = get_symbols_matching(code, "FooInner"); + + let s = symbols.pop().unwrap(); + + assert_eq!(s.name(), "FooInner"); + assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); + + let code = r#" +mod foo { + struct FooInner; +} + "#; + + let mut symbols = get_symbols_matching(code, "FooInner"); + + let s = symbols.pop().unwrap(); + + assert_eq!(s.name(), "FooInner"); + assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); + } + + #[test] + fn test_world_symbols_are_case_sensitive() { + let code = r#" +fn foo() {} + +struct Foo; + "#; + + let symbols = get_symbols_matching(code, "Foo"); + + let fn_match = symbols.iter().find(|s| s.name() == "foo").map(|s| s.kind()); + let struct_match = symbols.iter().find(|s| s.name() == "Foo").map(|s| s.kind()); + + assert_eq!(fn_match, Some(FN_DEF)); + assert_eq!(struct_match, Some(STRUCT_DEF)); + } + + fn get_symbols_matching(text: &str, query: &str) -> Vec { + let (analysis, _) = single_file(text); + analysis.symbol_search(Query::new(query.into())).unwrap() + } +} diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs index 9e1a1c1ec..9e5e6cadf 100644 --- a/crates/ra_ide/src/imports_locator.rs +++ b/crates/ra_ide/src/imports_locator.rs @@ -3,8 +3,8 @@ use crate::{ db::RootDatabase, + ide_db::symbol_index::{self, FileSymbol}, references::{classify_name, NameDefinition, NameKind}, - symbol_index::{self, FileSymbol}, Query, }; use hir::{db::HirDatabase, ModuleDef, SourceBinder}; diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 003a5e528..3926bc00f 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -14,7 +14,6 @@ mod ide_db; mod db; pub mod mock_analysis; -mod symbol_index; mod change; mod source_change; @@ -59,7 +58,11 @@ use ra_db::{ }; use ra_syntax::{SourceFile, TextRange, TextUnit}; -use crate::{db::LineIndexDatabase, display::ToNav, symbol_index::FileSymbol}; +use crate::{ + db::LineIndexDatabase, + display::ToNav, + ide_db::symbol_index::{self, FileSymbol}, +}; pub use crate::{ assists::{Assist, AssistId}, diff --git a/crates/ra_ide/src/status.rs b/crates/ra_ide/src/status.rs index 1bb27eb85..538312086 100644 --- a/crates/ra_ide/src/status.rs +++ b/crates/ra_ide/src/status.rs @@ -15,7 +15,7 @@ use ra_syntax::{ast, Parse, SyntaxNode}; use crate::{ db::RootDatabase, - symbol_index::{LibrarySymbolsQuery, SymbolIndex}, + ide_db::symbol_index::{LibrarySymbolsQuery, SymbolIndex}, FileId, }; diff --git a/crates/ra_ide/src/symbol_index.rs b/crates/ra_ide/src/symbol_index.rs deleted file mode 100644 index 5729eb5b3..000000000 --- a/crates/ra_ide/src/symbol_index.rs +++ /dev/null @@ -1,405 +0,0 @@ -//! This module handles fuzzy-searching of functions, structs and other symbols -//! by name across the whole workspace and dependencies. -//! -//! It works by building an incrementally-updated text-search index of all -//! symbols. The backbone of the index is the **awesome** `fst` crate by -//! @BurntSushi. -//! -//! In a nutshell, you give a set of strings to `fst`, and it builds a -//! finite state machine describing this set of strings. The strings which -//! could fuzzy-match a pattern can also be described by a finite state machine. -//! What is freaking cool is that you can now traverse both state machines in -//! lock-step to enumerate the strings which are both in the input set and -//! fuzz-match the query. Or, more formally, given two languages described by -//! FSTs, one can build a product FST which describes the intersection of the -//! languages. -//! -//! `fst` does not support cheap updating of the index, but it supports unioning -//! of state machines. So, to account for changing source code, we build an FST -//! for each library (which is assumed to never change) and an FST for each Rust -//! file in the current workspace, and run a query against the union of all -//! those FSTs. -use std::{ - fmt, - hash::{Hash, Hasher}, - mem, - sync::Arc, -}; - -use fst::{self, Streamer}; -use ra_db::{ - salsa::{self, ParallelDatabase}, - SourceDatabaseExt, SourceRootId, -}; -use ra_syntax::{ - ast::{self, NameOwner}, - match_ast, AstNode, Parse, SmolStr, SourceFile, - SyntaxKind::{self, *}, - SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent, -}; -#[cfg(not(feature = "wasm"))] -use rayon::prelude::*; - -use crate::{db::RootDatabase, FileId, Query}; - -#[salsa::query_group(SymbolsDatabaseStorage)] -pub(crate) trait SymbolsDatabase: hir::db::HirDatabase { - fn file_symbols(&self, file_id: FileId) -> Arc; - #[salsa::input] - fn library_symbols(&self, id: SourceRootId) -> Arc; - /// The set of "local" (that is, from the current workspace) roots. - /// Files in local roots are assumed to change frequently. - #[salsa::input] - fn local_roots(&self) -> Arc>; - /// The set of roots for crates.io libraries. - /// Files in libraries are assumed to never change. - #[salsa::input] - fn library_roots(&self) -> Arc>; -} - -fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc { - db.check_canceled(); - let parse = db.parse(file_id); - - let symbols = source_file_to_file_symbols(&parse.tree(), file_id); - - // FIXME: add macros here - - Arc::new(SymbolIndex::new(symbols)) -} - -pub(crate) fn world_symbols(db: &RootDatabase, query: Query) -> Vec { - /// Need to wrap Snapshot to provide `Clone` impl for `map_with` - struct Snap(salsa::Snapshot); - impl Clone for Snap { - fn clone(&self) -> Snap { - Snap(self.0.snapshot()) - } - } - - let buf: Vec> = if query.libs { - let snap = Snap(db.snapshot()); - #[cfg(not(feature = "wasm"))] - let buf = db - .library_roots() - .par_iter() - .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id)) - .collect(); - - #[cfg(feature = "wasm")] - let buf = db.library_roots().iter().map(|&lib_id| snap.0.library_symbols(lib_id)).collect(); - - buf - } else { - let mut files = Vec::new(); - for &root in db.local_roots().iter() { - let sr = db.source_root(root); - files.extend(sr.walk()) - } - - let snap = Snap(db.snapshot()); - #[cfg(not(feature = "wasm"))] - let buf = - files.par_iter().map_with(snap, |db, &file_id| db.0.file_symbols(file_id)).collect(); - - #[cfg(feature = "wasm")] - let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect(); - - buf - }; - query.search(&buf) -} - -pub(crate) fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec { - let name = name_ref.text(); - let mut query = Query::new(name.to_string()); - query.exact(); - query.limit(4); - crate::symbol_index::world_symbols(db, query) -} - -#[derive(Default)] -pub(crate) struct SymbolIndex { - symbols: Vec, - map: fst::Map, -} - -impl fmt::Debug for SymbolIndex { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("SymbolIndex").field("n_symbols", &self.symbols.len()).finish() - } -} - -impl PartialEq for SymbolIndex { - fn eq(&self, other: &SymbolIndex) -> bool { - self.symbols == other.symbols - } -} - -impl Eq for SymbolIndex {} - -impl Hash for SymbolIndex { - fn hash(&self, hasher: &mut H) { - self.symbols.hash(hasher) - } -} - -impl SymbolIndex { - fn new(mut symbols: Vec) -> SymbolIndex { - fn cmp_key<'a>(s1: &'a FileSymbol) -> impl Ord + 'a { - unicase::Ascii::new(s1.name.as_str()) - } - #[cfg(not(feature = "wasm"))] - symbols.par_sort_by(|s1, s2| cmp_key(s1).cmp(&cmp_key(s2))); - - #[cfg(feature = "wasm")] - symbols.sort_by(|s1, s2| cmp_key(s1).cmp(&cmp_key(s2))); - - let mut builder = fst::MapBuilder::memory(); - - let mut last_batch_start = 0; - - for idx in 0..symbols.len() { - if symbols.get(last_batch_start).map(cmp_key) == symbols.get(idx + 1).map(cmp_key) { - continue; - } - - let start = last_batch_start; - let end = idx + 1; - last_batch_start = end; - - let key = symbols[start].name.as_str().to_lowercase(); - let value = SymbolIndex::range_to_map_value(start, end); - - builder.insert(key, value).unwrap(); - } - - let map = fst::Map::from_bytes(builder.into_inner().unwrap()).unwrap(); - SymbolIndex { symbols, map } - } - - pub(crate) fn len(&self) -> usize { - self.symbols.len() - } - - pub(crate) fn memory_size(&self) -> usize { - self.map.as_fst().size() + self.symbols.len() * mem::size_of::() - } - - #[cfg(not(feature = "wasm"))] - pub(crate) fn for_files( - files: impl ParallelIterator)>, - ) -> SymbolIndex { - let symbols = files - .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id)) - .collect::>(); - SymbolIndex::new(symbols) - } - - #[cfg(feature = "wasm")] - pub(crate) fn for_files( - files: impl Iterator)>, - ) -> SymbolIndex { - let symbols = files - .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id)) - .collect::>(); - SymbolIndex::new(symbols) - } - - fn range_to_map_value(start: usize, end: usize) -> u64 { - debug_assert![start <= (std::u32::MAX as usize)]; - debug_assert![end <= (std::u32::MAX as usize)]; - - ((start as u64) << 32) | end as u64 - } - - fn map_value_to_range(value: u64) -> (usize, usize) { - let end = value as u32 as usize; - let start = (value >> 32) as usize; - (start, end) - } -} - -impl Query { - pub(crate) fn search(self, indices: &[Arc]) -> Vec { - let mut op = fst::map::OpBuilder::new(); - for file_symbols in indices.iter() { - let automaton = fst::automaton::Subsequence::new(&self.lowercased); - op = op.add(file_symbols.map.search(automaton)) - } - let mut stream = op.union(); - let mut res = Vec::new(); - while let Some((_, indexed_values)) = stream.next() { - if res.len() >= self.limit { - break; - } - for indexed_value in indexed_values { - let symbol_index = &indices[indexed_value.index]; - let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); - - for symbol in &symbol_index.symbols[start..end] { - if self.only_types && !is_type(symbol.ptr.kind()) { - continue; - } - if self.exact && symbol.name != self.query { - continue; - } - res.push(symbol.clone()); - } - } - } - res - } -} - -fn is_type(kind: SyntaxKind) -> bool { - match kind { - STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => true, - _ => false, - } -} - -/// The actual data that is stored in the index. It should be as compact as -/// possible. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) struct FileSymbol { - pub(crate) file_id: FileId, - pub(crate) name: SmolStr, - pub(crate) ptr: SyntaxNodePtr, - pub(crate) name_range: Option, - pub(crate) container_name: Option, -} - -fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec { - let mut symbols = Vec::new(); - let mut stack = Vec::new(); - - for event in source_file.syntax().preorder() { - match event { - WalkEvent::Enter(node) => { - if let Some(mut symbol) = to_file_symbol(&node, file_id) { - symbol.container_name = stack.last().cloned(); - - stack.push(symbol.name.clone()); - symbols.push(symbol); - } - } - - WalkEvent::Leave(node) => { - if to_symbol(&node).is_some() { - stack.pop(); - } - } - } - } - - symbols -} - -fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { - fn decl(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { - let name = node.name()?; - let name_range = name.syntax().text_range(); - let name = name.text().clone(); - let ptr = SyntaxNodePtr::new(node.syntax()); - - Some((name, ptr, name_range)) - } - match_ast! { - match node { - ast::FnDef(it) => { decl(it) }, - ast::StructDef(it) => { decl(it) }, - ast::EnumDef(it) => { decl(it) }, - ast::TraitDef(it) => { decl(it) }, - ast::Module(it) => { decl(it) }, - ast::TypeAliasDef(it) => { decl(it) }, - ast::ConstDef(it) => { decl(it) }, - ast::StaticDef(it) => { decl(it) }, - _ => None, - } - } -} - -fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option { - to_symbol(node).map(move |(name, ptr, name_range)| FileSymbol { - name, - ptr, - file_id, - name_range: Some(name_range), - container_name: None, - }) -} - -#[cfg(test)] -mod tests { - use crate::{display::NavigationTarget, mock_analysis::single_file, Query}; - use ra_syntax::{ - SmolStr, - SyntaxKind::{FN_DEF, STRUCT_DEF}, - }; - - #[test] - fn test_world_symbols_with_no_container() { - let code = r#" - enum FooInner { } - "#; - - let mut symbols = get_symbols_matching(code, "FooInner"); - - let s = symbols.pop().unwrap(); - - assert_eq!(s.name(), "FooInner"); - assert!(s.container_name().is_none()); - } - - #[test] - fn test_world_symbols_include_container_name() { - let code = r#" -fn foo() { - enum FooInner { } -} - "#; - - let mut symbols = get_symbols_matching(code, "FooInner"); - - let s = symbols.pop().unwrap(); - - assert_eq!(s.name(), "FooInner"); - assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); - - let code = r#" -mod foo { - struct FooInner; -} - "#; - - let mut symbols = get_symbols_matching(code, "FooInner"); - - let s = symbols.pop().unwrap(); - - assert_eq!(s.name(), "FooInner"); - assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); - } - - #[test] - fn test_world_symbols_are_case_sensitive() { - let code = r#" -fn foo() {} - -struct Foo; - "#; - - let symbols = get_symbols_matching(code, "Foo"); - - let fn_match = symbols.iter().find(|s| s.name() == "foo").map(|s| s.kind()); - let struct_match = symbols.iter().find(|s| s.name() == "Foo").map(|s| s.kind()); - - assert_eq!(fn_match, Some(FN_DEF)); - assert_eq!(struct_match, Some(STRUCT_DEF)); - } - - fn get_symbols_matching(text: &str, query: &str) -> Vec { - let (analysis, _) = single_file(text); - analysis.symbol_search(Query::new(query.into())).unwrap() - } -} -- cgit v1.2.3 From 0509a0a34e9b171d7ed7ea3e2a706c85c9b69433 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 12:24:13 +0100 Subject: Move Query --- crates/ra_ide/src/ide_db/symbol_index.rs | 42 +++++++++++++++++++++++++++++++- crates/ra_ide/src/lib.rs | 41 +------------------------------ 2 files changed, 42 insertions(+), 41 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/ide_db/symbol_index.rs b/crates/ra_ide/src/ide_db/symbol_index.rs index 4ceb5e66f..c66eeb8e2 100644 --- a/crates/ra_ide/src/ide_db/symbol_index.rs +++ b/crates/ra_ide/src/ide_db/symbol_index.rs @@ -40,7 +40,47 @@ use ra_syntax::{ #[cfg(not(feature = "wasm"))] use rayon::prelude::*; -use crate::{ide_db::RootDatabase, Query}; +use crate::ide_db::RootDatabase; + +#[derive(Debug)] +pub struct Query { + query: String, + lowercased: String, + only_types: bool, + libs: bool, + exact: bool, + limit: usize, +} + +impl Query { + pub fn new(query: String) -> Query { + let lowercased = query.to_lowercase(); + Query { + query, + lowercased, + only_types: false, + libs: false, + exact: false, + limit: usize::max_value(), + } + } + + pub fn only_types(&mut self) { + self.only_types = true; + } + + pub fn libs(&mut self) { + self.libs = true; + } + + pub fn exact(&mut self) { + self.exact = true; + } + + pub fn limit(&mut self, limit: usize) { + self.limit = limit + } +} #[salsa::query_group(SymbolsDatabaseStorage)] pub(crate) trait SymbolsDatabase: hir::db::HirDatabase { diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 3926bc00f..109b5ad9f 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -78,6 +78,7 @@ pub use crate::{ feature_flags::FeatureFlags, line_index::{LineCol, LineIndex}, line_index_utils::translate_offset_with_edit, + symbol_index::Query, }, inlay_hints::{InlayHint, InlayKind}, references::{ @@ -103,46 +104,6 @@ pub struct Diagnostic { pub severity: Severity, } -#[derive(Debug)] -pub struct Query { - query: String, - lowercased: String, - only_types: bool, - libs: bool, - exact: bool, - limit: usize, -} - -impl Query { - pub fn new(query: String) -> Query { - let lowercased = query.to_lowercase(); - Query { - query, - lowercased, - only_types: false, - libs: false, - exact: false, - limit: usize::max_value(), - } - } - - pub fn only_types(&mut self) { - self.only_types = true; - } - - pub fn libs(&mut self) { - self.libs = true; - } - - pub fn exact(&mut self) { - self.exact = true; - } - - pub fn limit(&mut self, limit: usize) { - self.limit = limit - } -} - /// Info associated with a text range. #[derive(Debug)] pub struct RangeInfo { -- cgit v1.2.3 From ee76e6141e3a6d2dd9c80e260294a4c6360e9fc7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 12:26:10 +0100 Subject: Fix test imports --- crates/ra_ide/src/ide_db/line_index_utils.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/ide_db/line_index_utils.rs b/crates/ra_ide/src/ide_db/line_index_utils.rs index 70bf7253c..faa3d665f 100644 --- a/crates/ra_ide/src/ide_db/line_index_utils.rs +++ b/crates/ra_ide/src/ide_db/line_index_utils.rs @@ -293,12 +293,14 @@ pub fn translate_offset_with_edit( #[cfg(test)] mod test { - use super::*; - use crate::line_index; use proptest::{prelude::*, proptest}; use ra_text_edit::test_utils::{arb_offset, arb_text_with_edit}; use ra_text_edit::TextEdit; + use crate::ide_db::line_index; + + use super::*; + #[derive(Debug)] struct ArbTextWithEditAndOffset { text: String, -- cgit v1.2.3 From 1bfb111cf9ab938a8795f1ad2089cdd8b8c4b7a5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 12:35:40 +0100 Subject: Move change to ide-db --- crates/ra_ide/src/change.rs | 386 ------------------------------------- crates/ra_ide/src/ide_db/change.rs | 386 +++++++++++++++++++++++++++++++++++++ crates/ra_ide/src/ide_db/mod.rs | 1 + crates/ra_ide/src/lib.rs | 3 +- 4 files changed, 388 insertions(+), 388 deletions(-) delete mode 100644 crates/ra_ide/src/change.rs create mode 100644 crates/ra_ide/src/ide_db/change.rs (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs deleted file mode 100644 index a0aeee1f7..000000000 --- a/crates/ra_ide/src/change.rs +++ /dev/null @@ -1,386 +0,0 @@ -//! FIXME: write short doc here - -use std::{fmt, sync::Arc, time}; - -use ra_db::{ - salsa::{Database, Durability, SweepStrategy}, - CrateGraph, CrateId, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, - SourceRootId, -}; -use ra_prof::{memory_usage, profile, Bytes}; -use ra_syntax::SourceFile; -#[cfg(not(feature = "wasm"))] -use rayon::prelude::*; -use rustc_hash::FxHashMap; - -use crate::{ - db::{DebugData, RootDatabase}, - ide_db::symbol_index::{SymbolIndex, SymbolsDatabase}, -}; - -#[derive(Default)] -pub struct AnalysisChange { - new_roots: Vec<(SourceRootId, bool)>, - roots_changed: FxHashMap, - files_changed: Vec<(FileId, Arc)>, - libraries_added: Vec, - crate_graph: Option, - debug_data: DebugData, -} - -impl fmt::Debug for AnalysisChange { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let mut d = fmt.debug_struct("AnalysisChange"); - if !self.new_roots.is_empty() { - d.field("new_roots", &self.new_roots); - } - if !self.roots_changed.is_empty() { - d.field("roots_changed", &self.roots_changed); - } - if !self.files_changed.is_empty() { - d.field("files_changed", &self.files_changed.len()); - } - if !self.libraries_added.is_empty() { - d.field("libraries_added", &self.libraries_added.len()); - } - if !self.crate_graph.is_none() { - d.field("crate_graph", &self.crate_graph); - } - d.finish() - } -} - -impl AnalysisChange { - pub fn new() -> AnalysisChange { - AnalysisChange::default() - } - - pub fn add_root(&mut self, root_id: SourceRootId, is_local: bool) { - self.new_roots.push((root_id, is_local)); - } - - pub fn add_file( - &mut self, - root_id: SourceRootId, - file_id: FileId, - path: RelativePathBuf, - text: Arc, - ) { - let file = AddFile { file_id, path, text }; - self.roots_changed.entry(root_id).or_default().added.push(file); - } - - pub fn change_file(&mut self, file_id: FileId, new_text: Arc) { - self.files_changed.push((file_id, new_text)) - } - - pub fn remove_file(&mut self, root_id: SourceRootId, file_id: FileId, path: RelativePathBuf) { - let file = RemoveFile { file_id, path }; - self.roots_changed.entry(root_id).or_default().removed.push(file); - } - - pub fn add_library(&mut self, data: LibraryData) { - self.libraries_added.push(data) - } - - pub fn set_crate_graph(&mut self, graph: CrateGraph) { - self.crate_graph = Some(graph); - } - - pub fn set_debug_crate_name(&mut self, crate_id: CrateId, name: String) { - self.debug_data.crate_names.insert(crate_id, name); - } - - pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) { - self.debug_data.root_paths.insert(source_root_id, path); - } -} - -#[derive(Debug)] -struct AddFile { - file_id: FileId, - path: RelativePathBuf, - text: Arc, -} - -#[derive(Debug)] -struct RemoveFile { - file_id: FileId, - path: RelativePathBuf, -} - -#[derive(Default)] -struct RootChange { - added: Vec, - removed: Vec, -} - -impl fmt::Debug for RootChange { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("AnalysisChange") - .field("added", &self.added.len()) - .field("removed", &self.removed.len()) - .finish() - } -} - -pub struct LibraryData { - root_id: SourceRootId, - root_change: RootChange, - symbol_index: SymbolIndex, -} - -impl fmt::Debug for LibraryData { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("LibraryData") - .field("root_id", &self.root_id) - .field("root_change", &self.root_change) - .field("n_symbols", &self.symbol_index.len()) - .finish() - } -} - -impl LibraryData { - pub fn prepare( - root_id: SourceRootId, - files: Vec<(FileId, RelativePathBuf, Arc)>, - ) -> LibraryData { - let _p = profile("LibraryData::prepare"); - - #[cfg(not(feature = "wasm"))] - let iter = files.par_iter(); - #[cfg(feature = "wasm")] - let iter = files.iter(); - - let symbol_index = SymbolIndex::for_files(iter.map(|(file_id, _, text)| { - let parse = SourceFile::parse(text); - (*file_id, parse) - })); - let mut root_change = RootChange::default(); - root_change.added = files - .into_iter() - .map(|(file_id, path, text)| AddFile { file_id, path, text }) - .collect(); - LibraryData { root_id, root_change, symbol_index } - } -} - -const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); - -impl RootDatabase { - pub(crate) fn request_cancellation(&mut self) { - let _p = profile("RootDatabase::request_cancellation"); - self.salsa_runtime_mut().synthetic_write(Durability::LOW); - } - - pub(crate) fn apply_change(&mut self, change: AnalysisChange) { - let _p = profile("RootDatabase::apply_change"); - self.request_cancellation(); - log::info!("apply_change {:?}", change); - if !change.new_roots.is_empty() { - let mut local_roots = Vec::clone(&self.local_roots()); - for (root_id, is_local) in change.new_roots { - let root = - if is_local { SourceRoot::new_local() } else { SourceRoot::new_library() }; - let durability = durability(&root); - self.set_source_root_with_durability(root_id, Arc::new(root), durability); - if is_local { - local_roots.push(root_id); - } - } - self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); - } - - for (root_id, root_change) in change.roots_changed { - self.apply_root_change(root_id, root_change); - } - for (file_id, text) in change.files_changed { - let source_root_id = self.file_source_root(file_id); - let source_root = self.source_root(source_root_id); - let durability = durability(&source_root); - self.set_file_text_with_durability(file_id, text, durability) - } - if !change.libraries_added.is_empty() { - let mut libraries = Vec::clone(&self.library_roots()); - for library in change.libraries_added { - libraries.push(library.root_id); - self.set_source_root_with_durability( - library.root_id, - Arc::new(SourceRoot::new_library()), - Durability::HIGH, - ); - self.set_library_symbols_with_durability( - library.root_id, - Arc::new(library.symbol_index), - Durability::HIGH, - ); - self.apply_root_change(library.root_id, library.root_change); - } - self.set_library_roots_with_durability(Arc::new(libraries), Durability::HIGH); - } - if let Some(crate_graph) = change.crate_graph { - self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) - } - - Arc::make_mut(&mut self.debug_data).merge(change.debug_data) - } - - fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { - let mut source_root = SourceRoot::clone(&self.source_root(root_id)); - let durability = durability(&source_root); - for add_file in root_change.added { - self.set_file_text_with_durability(add_file.file_id, add_file.text, durability); - self.set_file_relative_path_with_durability( - add_file.file_id, - add_file.path.clone(), - durability, - ); - self.set_file_source_root_with_durability(add_file.file_id, root_id, durability); - source_root.insert_file(add_file.path, add_file.file_id); - } - for remove_file in root_change.removed { - self.set_file_text_with_durability(remove_file.file_id, Default::default(), durability); - source_root.remove_file(&remove_file.path); - } - self.set_source_root_with_durability(root_id, Arc::new(source_root), durability); - } - - pub(crate) fn maybe_collect_garbage(&mut self) { - if cfg!(feature = "wasm") { - return; - } - - if self.last_gc_check.elapsed() > GC_COOLDOWN { - self.last_gc_check = crate::wasm_shims::Instant::now(); - } - } - - pub(crate) fn collect_garbage(&mut self) { - if cfg!(feature = "wasm") { - return; - } - - let _p = profile("RootDatabase::collect_garbage"); - self.last_gc = crate::wasm_shims::Instant::now(); - - let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); - - self.query(ra_db::ParseQuery).sweep(sweep); - self.query(hir::db::ParseMacroQuery).sweep(sweep); - - // Macros do take significant space, but less then the syntax trees - // self.query(hir::db::MacroDefQuery).sweep(sweep); - // self.query(hir::db::MacroArgQuery).sweep(sweep); - // self.query(hir::db::MacroExpandQuery).sweep(sweep); - - self.query(hir::db::AstIdMapQuery).sweep(sweep); - - self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); - - self.query(hir::db::ExprScopesQuery).sweep(sweep); - self.query(hir::db::DoInferQuery).sweep(sweep); - self.query(hir::db::BodyQuery).sweep(sweep); - } - - pub(crate) fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> { - let mut acc: Vec<(String, Bytes)> = vec![]; - let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); - macro_rules! sweep_each_query { - ($($q:path)*) => {$( - let before = memory_usage().allocated; - self.query($q).sweep(sweep); - let after = memory_usage().allocated; - let q: $q = Default::default(); - let name = format!("{:?}", q); - acc.push((name, before - after)); - - let before = memory_usage().allocated; - self.query($q).sweep(sweep.discard_everything()); - let after = memory_usage().allocated; - let q: $q = Default::default(); - let name = format!("{:?} (deps)", q); - acc.push((name, before - after)); - )*} - } - sweep_each_query![ - // SourceDatabase - ra_db::ParseQuery - ra_db::SourceRootCratesQuery - - // AstDatabase - hir::db::AstIdMapQuery - hir::db::InternMacroQuery - hir::db::MacroArgQuery - hir::db::MacroDefQuery - hir::db::ParseMacroQuery - hir::db::MacroExpandQuery - - // DefDatabase - hir::db::RawItemsQuery - hir::db::ComputeCrateDefMapQuery - hir::db::StructDataQuery - hir::db::UnionDataQuery - hir::db::EnumDataQuery - hir::db::ImplDataQuery - hir::db::TraitDataQuery - hir::db::TypeAliasDataQuery - hir::db::FunctionDataQuery - hir::db::ConstDataQuery - hir::db::StaticDataQuery - hir::db::BodyWithSourceMapQuery - hir::db::BodyQuery - hir::db::ExprScopesQuery - hir::db::GenericParamsQuery - hir::db::AttrsQuery - hir::db::ModuleLangItemsQuery - hir::db::CrateLangItemsQuery - hir::db::LangItemQuery - hir::db::DocumentationQuery - - // InternDatabase - hir::db::InternFunctionQuery - hir::db::InternStructQuery - hir::db::InternUnionQuery - hir::db::InternEnumQuery - hir::db::InternConstQuery - hir::db::InternStaticQuery - hir::db::InternTraitQuery - hir::db::InternTypeAliasQuery - hir::db::InternImplQuery - - // HirDatabase - hir::db::DoInferQuery - hir::db::TyQuery - hir::db::ValueTyQuery - hir::db::ImplSelfTyQuery - hir::db::ImplTraitQuery - hir::db::FieldTypesQuery - hir::db::CallableItemSignatureQuery - hir::db::GenericPredicatesForParamQuery - hir::db::GenericPredicatesQuery - hir::db::GenericDefaultsQuery - hir::db::ImplsInCrateQuery - hir::db::ImplsForTraitQuery - hir::db::TraitSolverQuery - hir::db::InternTypeCtorQuery - hir::db::InternChalkImplQuery - hir::db::InternAssocTyValueQuery - hir::db::AssociatedTyDataQuery - hir::db::AssociatedTyValueQuery - hir::db::TraitSolveQuery - hir::db::TraitDatumQuery - hir::db::StructDatumQuery - hir::db::ImplDatumQuery - ]; - acc.sort_by_key(|it| std::cmp::Reverse(it.1)); - acc - } -} - -fn durability(source_root: &SourceRoot) -> Durability { - if source_root.is_library { - Durability::HIGH - } else { - Durability::LOW - } -} diff --git a/crates/ra_ide/src/ide_db/change.rs b/crates/ra_ide/src/ide_db/change.rs new file mode 100644 index 000000000..62ffa6920 --- /dev/null +++ b/crates/ra_ide/src/ide_db/change.rs @@ -0,0 +1,386 @@ +//! FIXME: write short doc here + +use std::{fmt, sync::Arc, time}; + +use ra_db::{ + salsa::{Database, Durability, SweepStrategy}, + CrateGraph, CrateId, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, + SourceRootId, +}; +use ra_prof::{memory_usage, profile, Bytes}; +use ra_syntax::SourceFile; +#[cfg(not(feature = "wasm"))] +use rayon::prelude::*; +use rustc_hash::FxHashMap; + +use crate::ide_db::{ + symbol_index::{SymbolIndex, SymbolsDatabase}, + DebugData, RootDatabase, +}; + +#[derive(Default)] +pub struct AnalysisChange { + new_roots: Vec<(SourceRootId, bool)>, + roots_changed: FxHashMap, + files_changed: Vec<(FileId, Arc)>, + libraries_added: Vec, + crate_graph: Option, + debug_data: DebugData, +} + +impl fmt::Debug for AnalysisChange { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let mut d = fmt.debug_struct("AnalysisChange"); + if !self.new_roots.is_empty() { + d.field("new_roots", &self.new_roots); + } + if !self.roots_changed.is_empty() { + d.field("roots_changed", &self.roots_changed); + } + if !self.files_changed.is_empty() { + d.field("files_changed", &self.files_changed.len()); + } + if !self.libraries_added.is_empty() { + d.field("libraries_added", &self.libraries_added.len()); + } + if !self.crate_graph.is_none() { + d.field("crate_graph", &self.crate_graph); + } + d.finish() + } +} + +impl AnalysisChange { + pub fn new() -> AnalysisChange { + AnalysisChange::default() + } + + pub fn add_root(&mut self, root_id: SourceRootId, is_local: bool) { + self.new_roots.push((root_id, is_local)); + } + + pub fn add_file( + &mut self, + root_id: SourceRootId, + file_id: FileId, + path: RelativePathBuf, + text: Arc, + ) { + let file = AddFile { file_id, path, text }; + self.roots_changed.entry(root_id).or_default().added.push(file); + } + + pub fn change_file(&mut self, file_id: FileId, new_text: Arc) { + self.files_changed.push((file_id, new_text)) + } + + pub fn remove_file(&mut self, root_id: SourceRootId, file_id: FileId, path: RelativePathBuf) { + let file = RemoveFile { file_id, path }; + self.roots_changed.entry(root_id).or_default().removed.push(file); + } + + pub fn add_library(&mut self, data: LibraryData) { + self.libraries_added.push(data) + } + + pub fn set_crate_graph(&mut self, graph: CrateGraph) { + self.crate_graph = Some(graph); + } + + pub fn set_debug_crate_name(&mut self, crate_id: CrateId, name: String) { + self.debug_data.crate_names.insert(crate_id, name); + } + + pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) { + self.debug_data.root_paths.insert(source_root_id, path); + } +} + +#[derive(Debug)] +struct AddFile { + file_id: FileId, + path: RelativePathBuf, + text: Arc, +} + +#[derive(Debug)] +struct RemoveFile { + file_id: FileId, + path: RelativePathBuf, +} + +#[derive(Default)] +struct RootChange { + added: Vec, + removed: Vec, +} + +impl fmt::Debug for RootChange { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("AnalysisChange") + .field("added", &self.added.len()) + .field("removed", &self.removed.len()) + .finish() + } +} + +pub struct LibraryData { + root_id: SourceRootId, + root_change: RootChange, + symbol_index: SymbolIndex, +} + +impl fmt::Debug for LibraryData { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("LibraryData") + .field("root_id", &self.root_id) + .field("root_change", &self.root_change) + .field("n_symbols", &self.symbol_index.len()) + .finish() + } +} + +impl LibraryData { + pub fn prepare( + root_id: SourceRootId, + files: Vec<(FileId, RelativePathBuf, Arc)>, + ) -> LibraryData { + let _p = profile("LibraryData::prepare"); + + #[cfg(not(feature = "wasm"))] + let iter = files.par_iter(); + #[cfg(feature = "wasm")] + let iter = files.iter(); + + let symbol_index = SymbolIndex::for_files(iter.map(|(file_id, _, text)| { + let parse = SourceFile::parse(text); + (*file_id, parse) + })); + let mut root_change = RootChange::default(); + root_change.added = files + .into_iter() + .map(|(file_id, path, text)| AddFile { file_id, path, text }) + .collect(); + LibraryData { root_id, root_change, symbol_index } + } +} + +const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); + +impl RootDatabase { + pub(crate) fn request_cancellation(&mut self) { + let _p = profile("RootDatabase::request_cancellation"); + self.salsa_runtime_mut().synthetic_write(Durability::LOW); + } + + pub(crate) fn apply_change(&mut self, change: AnalysisChange) { + let _p = profile("RootDatabase::apply_change"); + self.request_cancellation(); + log::info!("apply_change {:?}", change); + if !change.new_roots.is_empty() { + let mut local_roots = Vec::clone(&self.local_roots()); + for (root_id, is_local) in change.new_roots { + let root = + if is_local { SourceRoot::new_local() } else { SourceRoot::new_library() }; + let durability = durability(&root); + self.set_source_root_with_durability(root_id, Arc::new(root), durability); + if is_local { + local_roots.push(root_id); + } + } + self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); + } + + for (root_id, root_change) in change.roots_changed { + self.apply_root_change(root_id, root_change); + } + for (file_id, text) in change.files_changed { + let source_root_id = self.file_source_root(file_id); + let source_root = self.source_root(source_root_id); + let durability = durability(&source_root); + self.set_file_text_with_durability(file_id, text, durability) + } + if !change.libraries_added.is_empty() { + let mut libraries = Vec::clone(&self.library_roots()); + for library in change.libraries_added { + libraries.push(library.root_id); + self.set_source_root_with_durability( + library.root_id, + Arc::new(SourceRoot::new_library()), + Durability::HIGH, + ); + self.set_library_symbols_with_durability( + library.root_id, + Arc::new(library.symbol_index), + Durability::HIGH, + ); + self.apply_root_change(library.root_id, library.root_change); + } + self.set_library_roots_with_durability(Arc::new(libraries), Durability::HIGH); + } + if let Some(crate_graph) = change.crate_graph { + self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) + } + + Arc::make_mut(&mut self.debug_data).merge(change.debug_data) + } + + fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { + let mut source_root = SourceRoot::clone(&self.source_root(root_id)); + let durability = durability(&source_root); + for add_file in root_change.added { + self.set_file_text_with_durability(add_file.file_id, add_file.text, durability); + self.set_file_relative_path_with_durability( + add_file.file_id, + add_file.path.clone(), + durability, + ); + self.set_file_source_root_with_durability(add_file.file_id, root_id, durability); + source_root.insert_file(add_file.path, add_file.file_id); + } + for remove_file in root_change.removed { + self.set_file_text_with_durability(remove_file.file_id, Default::default(), durability); + source_root.remove_file(&remove_file.path); + } + self.set_source_root_with_durability(root_id, Arc::new(source_root), durability); + } + + pub(crate) fn maybe_collect_garbage(&mut self) { + if cfg!(feature = "wasm") { + return; + } + + if self.last_gc_check.elapsed() > GC_COOLDOWN { + self.last_gc_check = crate::wasm_shims::Instant::now(); + } + } + + pub(crate) fn collect_garbage(&mut self) { + if cfg!(feature = "wasm") { + return; + } + + let _p = profile("RootDatabase::collect_garbage"); + self.last_gc = crate::wasm_shims::Instant::now(); + + let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); + + self.query(ra_db::ParseQuery).sweep(sweep); + self.query(hir::db::ParseMacroQuery).sweep(sweep); + + // Macros do take significant space, but less then the syntax trees + // self.query(hir::db::MacroDefQuery).sweep(sweep); + // self.query(hir::db::MacroArgQuery).sweep(sweep); + // self.query(hir::db::MacroExpandQuery).sweep(sweep); + + self.query(hir::db::AstIdMapQuery).sweep(sweep); + + self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); + + self.query(hir::db::ExprScopesQuery).sweep(sweep); + self.query(hir::db::DoInferQuery).sweep(sweep); + self.query(hir::db::BodyQuery).sweep(sweep); + } + + pub(crate) fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> { + let mut acc: Vec<(String, Bytes)> = vec![]; + let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); + macro_rules! sweep_each_query { + ($($q:path)*) => {$( + let before = memory_usage().allocated; + self.query($q).sweep(sweep); + let after = memory_usage().allocated; + let q: $q = Default::default(); + let name = format!("{:?}", q); + acc.push((name, before - after)); + + let before = memory_usage().allocated; + self.query($q).sweep(sweep.discard_everything()); + let after = memory_usage().allocated; + let q: $q = Default::default(); + let name = format!("{:?} (deps)", q); + acc.push((name, before - after)); + )*} + } + sweep_each_query![ + // SourceDatabase + ra_db::ParseQuery + ra_db::SourceRootCratesQuery + + // AstDatabase + hir::db::AstIdMapQuery + hir::db::InternMacroQuery + hir::db::MacroArgQuery + hir::db::MacroDefQuery + hir::db::ParseMacroQuery + hir::db::MacroExpandQuery + + // DefDatabase + hir::db::RawItemsQuery + hir::db::ComputeCrateDefMapQuery + hir::db::StructDataQuery + hir::db::UnionDataQuery + hir::db::EnumDataQuery + hir::db::ImplDataQuery + hir::db::TraitDataQuery + hir::db::TypeAliasDataQuery + hir::db::FunctionDataQuery + hir::db::ConstDataQuery + hir::db::StaticDataQuery + hir::db::BodyWithSourceMapQuery + hir::db::BodyQuery + hir::db::ExprScopesQuery + hir::db::GenericParamsQuery + hir::db::AttrsQuery + hir::db::ModuleLangItemsQuery + hir::db::CrateLangItemsQuery + hir::db::LangItemQuery + hir::db::DocumentationQuery + + // InternDatabase + hir::db::InternFunctionQuery + hir::db::InternStructQuery + hir::db::InternUnionQuery + hir::db::InternEnumQuery + hir::db::InternConstQuery + hir::db::InternStaticQuery + hir::db::InternTraitQuery + hir::db::InternTypeAliasQuery + hir::db::InternImplQuery + + // HirDatabase + hir::db::DoInferQuery + hir::db::TyQuery + hir::db::ValueTyQuery + hir::db::ImplSelfTyQuery + hir::db::ImplTraitQuery + hir::db::FieldTypesQuery + hir::db::CallableItemSignatureQuery + hir::db::GenericPredicatesForParamQuery + hir::db::GenericPredicatesQuery + hir::db::GenericDefaultsQuery + hir::db::ImplsInCrateQuery + hir::db::ImplsForTraitQuery + hir::db::TraitSolverQuery + hir::db::InternTypeCtorQuery + hir::db::InternChalkImplQuery + hir::db::InternAssocTyValueQuery + hir::db::AssociatedTyDataQuery + hir::db::AssociatedTyValueQuery + hir::db::TraitSolveQuery + hir::db::TraitDatumQuery + hir::db::StructDatumQuery + hir::db::ImplDatumQuery + ]; + acc.sort_by_key(|it| std::cmp::Reverse(it.1)); + acc + } +} + +fn durability(source_root: &SourceRoot) -> Durability { + if source_root.is_library { + Durability::HIGH + } else { + Durability::LOW + } +} diff --git a/crates/ra_ide/src/ide_db/mod.rs b/crates/ra_ide/src/ide_db/mod.rs index 924ee9968..0df4d510f 100644 --- a/crates/ra_ide/src/ide_db/mod.rs +++ b/crates/ra_ide/src/ide_db/mod.rs @@ -4,6 +4,7 @@ pub mod line_index; pub mod line_index_utils; pub mod feature_flags; pub mod symbol_index; +pub mod change; use std::sync::Arc; diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 109b5ad9f..66f365cc3 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -14,7 +14,6 @@ mod ide_db; mod db; pub mod mock_analysis; -mod change; mod source_change; mod status; @@ -67,7 +66,6 @@ use crate::{ pub use crate::{ assists::{Assist, AssistId}, call_hierarchy::CallItem, - change::{AnalysisChange, LibraryData}, completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, diagnostics::Severity, display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, @@ -75,6 +73,7 @@ pub use crate::{ folding_ranges::{Fold, FoldKind}, hover::HoverResult, ide_db::{ + change::{AnalysisChange, LibraryData}, feature_flags::FeatureFlags, line_index::{LineCol, LineIndex}, line_index_utils::translate_offset_with_edit, -- cgit v1.2.3 From 939f05f3e33e9f00d5205d60af3a862ae4d58bd6 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 12:43:56 +0100 Subject: Move to a crate --- crates/ra_ide/src/ide_db/change.rs | 386 ----------------------- crates/ra_ide/src/ide_db/feature_flags.rs | 71 ----- crates/ra_ide/src/ide_db/line_index.rs | 283 ----------------- crates/ra_ide/src/ide_db/line_index_utils.rs | 334 -------------------- crates/ra_ide/src/ide_db/mod.rs | 137 --------- crates/ra_ide/src/ide_db/symbol_index.rs | 445 --------------------------- crates/ra_ide/src/lib.rs | 5 +- crates/ra_ide/src/wasm_shims.rs | 19 -- 8 files changed, 3 insertions(+), 1677 deletions(-) delete mode 100644 crates/ra_ide/src/ide_db/change.rs delete mode 100644 crates/ra_ide/src/ide_db/feature_flags.rs delete mode 100644 crates/ra_ide/src/ide_db/line_index.rs delete mode 100644 crates/ra_ide/src/ide_db/line_index_utils.rs delete mode 100644 crates/ra_ide/src/ide_db/mod.rs delete mode 100644 crates/ra_ide/src/ide_db/symbol_index.rs delete mode 100644 crates/ra_ide/src/wasm_shims.rs (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/ide_db/change.rs b/crates/ra_ide/src/ide_db/change.rs deleted file mode 100644 index 62ffa6920..000000000 --- a/crates/ra_ide/src/ide_db/change.rs +++ /dev/null @@ -1,386 +0,0 @@ -//! FIXME: write short doc here - -use std::{fmt, sync::Arc, time}; - -use ra_db::{ - salsa::{Database, Durability, SweepStrategy}, - CrateGraph, CrateId, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, - SourceRootId, -}; -use ra_prof::{memory_usage, profile, Bytes}; -use ra_syntax::SourceFile; -#[cfg(not(feature = "wasm"))] -use rayon::prelude::*; -use rustc_hash::FxHashMap; - -use crate::ide_db::{ - symbol_index::{SymbolIndex, SymbolsDatabase}, - DebugData, RootDatabase, -}; - -#[derive(Default)] -pub struct AnalysisChange { - new_roots: Vec<(SourceRootId, bool)>, - roots_changed: FxHashMap, - files_changed: Vec<(FileId, Arc)>, - libraries_added: Vec, - crate_graph: Option, - debug_data: DebugData, -} - -impl fmt::Debug for AnalysisChange { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let mut d = fmt.debug_struct("AnalysisChange"); - if !self.new_roots.is_empty() { - d.field("new_roots", &self.new_roots); - } - if !self.roots_changed.is_empty() { - d.field("roots_changed", &self.roots_changed); - } - if !self.files_changed.is_empty() { - d.field("files_changed", &self.files_changed.len()); - } - if !self.libraries_added.is_empty() { - d.field("libraries_added", &self.libraries_added.len()); - } - if !self.crate_graph.is_none() { - d.field("crate_graph", &self.crate_graph); - } - d.finish() - } -} - -impl AnalysisChange { - pub fn new() -> AnalysisChange { - AnalysisChange::default() - } - - pub fn add_root(&mut self, root_id: SourceRootId, is_local: bool) { - self.new_roots.push((root_id, is_local)); - } - - pub fn add_file( - &mut self, - root_id: SourceRootId, - file_id: FileId, - path: RelativePathBuf, - text: Arc, - ) { - let file = AddFile { file_id, path, text }; - self.roots_changed.entry(root_id).or_default().added.push(file); - } - - pub fn change_file(&mut self, file_id: FileId, new_text: Arc) { - self.files_changed.push((file_id, new_text)) - } - - pub fn remove_file(&mut self, root_id: SourceRootId, file_id: FileId, path: RelativePathBuf) { - let file = RemoveFile { file_id, path }; - self.roots_changed.entry(root_id).or_default().removed.push(file); - } - - pub fn add_library(&mut self, data: LibraryData) { - self.libraries_added.push(data) - } - - pub fn set_crate_graph(&mut self, graph: CrateGraph) { - self.crate_graph = Some(graph); - } - - pub fn set_debug_crate_name(&mut self, crate_id: CrateId, name: String) { - self.debug_data.crate_names.insert(crate_id, name); - } - - pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) { - self.debug_data.root_paths.insert(source_root_id, path); - } -} - -#[derive(Debug)] -struct AddFile { - file_id: FileId, - path: RelativePathBuf, - text: Arc, -} - -#[derive(Debug)] -struct RemoveFile { - file_id: FileId, - path: RelativePathBuf, -} - -#[derive(Default)] -struct RootChange { - added: Vec, - removed: Vec, -} - -impl fmt::Debug for RootChange { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("AnalysisChange") - .field("added", &self.added.len()) - .field("removed", &self.removed.len()) - .finish() - } -} - -pub struct LibraryData { - root_id: SourceRootId, - root_change: RootChange, - symbol_index: SymbolIndex, -} - -impl fmt::Debug for LibraryData { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("LibraryData") - .field("root_id", &self.root_id) - .field("root_change", &self.root_change) - .field("n_symbols", &self.symbol_index.len()) - .finish() - } -} - -impl LibraryData { - pub fn prepare( - root_id: SourceRootId, - files: Vec<(FileId, RelativePathBuf, Arc)>, - ) -> LibraryData { - let _p = profile("LibraryData::prepare"); - - #[cfg(not(feature = "wasm"))] - let iter = files.par_iter(); - #[cfg(feature = "wasm")] - let iter = files.iter(); - - let symbol_index = SymbolIndex::for_files(iter.map(|(file_id, _, text)| { - let parse = SourceFile::parse(text); - (*file_id, parse) - })); - let mut root_change = RootChange::default(); - root_change.added = files - .into_iter() - .map(|(file_id, path, text)| AddFile { file_id, path, text }) - .collect(); - LibraryData { root_id, root_change, symbol_index } - } -} - -const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); - -impl RootDatabase { - pub(crate) fn request_cancellation(&mut self) { - let _p = profile("RootDatabase::request_cancellation"); - self.salsa_runtime_mut().synthetic_write(Durability::LOW); - } - - pub(crate) fn apply_change(&mut self, change: AnalysisChange) { - let _p = profile("RootDatabase::apply_change"); - self.request_cancellation(); - log::info!("apply_change {:?}", change); - if !change.new_roots.is_empty() { - let mut local_roots = Vec::clone(&self.local_roots()); - for (root_id, is_local) in change.new_roots { - let root = - if is_local { SourceRoot::new_local() } else { SourceRoot::new_library() }; - let durability = durability(&root); - self.set_source_root_with_durability(root_id, Arc::new(root), durability); - if is_local { - local_roots.push(root_id); - } - } - self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); - } - - for (root_id, root_change) in change.roots_changed { - self.apply_root_change(root_id, root_change); - } - for (file_id, text) in change.files_changed { - let source_root_id = self.file_source_root(file_id); - let source_root = self.source_root(source_root_id); - let durability = durability(&source_root); - self.set_file_text_with_durability(file_id, text, durability) - } - if !change.libraries_added.is_empty() { - let mut libraries = Vec::clone(&self.library_roots()); - for library in change.libraries_added { - libraries.push(library.root_id); - self.set_source_root_with_durability( - library.root_id, - Arc::new(SourceRoot::new_library()), - Durability::HIGH, - ); - self.set_library_symbols_with_durability( - library.root_id, - Arc::new(library.symbol_index), - Durability::HIGH, - ); - self.apply_root_change(library.root_id, library.root_change); - } - self.set_library_roots_with_durability(Arc::new(libraries), Durability::HIGH); - } - if let Some(crate_graph) = change.crate_graph { - self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) - } - - Arc::make_mut(&mut self.debug_data).merge(change.debug_data) - } - - fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { - let mut source_root = SourceRoot::clone(&self.source_root(root_id)); - let durability = durability(&source_root); - for add_file in root_change.added { - self.set_file_text_with_durability(add_file.file_id, add_file.text, durability); - self.set_file_relative_path_with_durability( - add_file.file_id, - add_file.path.clone(), - durability, - ); - self.set_file_source_root_with_durability(add_file.file_id, root_id, durability); - source_root.insert_file(add_file.path, add_file.file_id); - } - for remove_file in root_change.removed { - self.set_file_text_with_durability(remove_file.file_id, Default::default(), durability); - source_root.remove_file(&remove_file.path); - } - self.set_source_root_with_durability(root_id, Arc::new(source_root), durability); - } - - pub(crate) fn maybe_collect_garbage(&mut self) { - if cfg!(feature = "wasm") { - return; - } - - if self.last_gc_check.elapsed() > GC_COOLDOWN { - self.last_gc_check = crate::wasm_shims::Instant::now(); - } - } - - pub(crate) fn collect_garbage(&mut self) { - if cfg!(feature = "wasm") { - return; - } - - let _p = profile("RootDatabase::collect_garbage"); - self.last_gc = crate::wasm_shims::Instant::now(); - - let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); - - self.query(ra_db::ParseQuery).sweep(sweep); - self.query(hir::db::ParseMacroQuery).sweep(sweep); - - // Macros do take significant space, but less then the syntax trees - // self.query(hir::db::MacroDefQuery).sweep(sweep); - // self.query(hir::db::MacroArgQuery).sweep(sweep); - // self.query(hir::db::MacroExpandQuery).sweep(sweep); - - self.query(hir::db::AstIdMapQuery).sweep(sweep); - - self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); - - self.query(hir::db::ExprScopesQuery).sweep(sweep); - self.query(hir::db::DoInferQuery).sweep(sweep); - self.query(hir::db::BodyQuery).sweep(sweep); - } - - pub(crate) fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> { - let mut acc: Vec<(String, Bytes)> = vec![]; - let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); - macro_rules! sweep_each_query { - ($($q:path)*) => {$( - let before = memory_usage().allocated; - self.query($q).sweep(sweep); - let after = memory_usage().allocated; - let q: $q = Default::default(); - let name = format!("{:?}", q); - acc.push((name, before - after)); - - let before = memory_usage().allocated; - self.query($q).sweep(sweep.discard_everything()); - let after = memory_usage().allocated; - let q: $q = Default::default(); - let name = format!("{:?} (deps)", q); - acc.push((name, before - after)); - )*} - } - sweep_each_query![ - // SourceDatabase - ra_db::ParseQuery - ra_db::SourceRootCratesQuery - - // AstDatabase - hir::db::AstIdMapQuery - hir::db::InternMacroQuery - hir::db::MacroArgQuery - hir::db::MacroDefQuery - hir::db::ParseMacroQuery - hir::db::MacroExpandQuery - - // DefDatabase - hir::db::RawItemsQuery - hir::db::ComputeCrateDefMapQuery - hir::db::StructDataQuery - hir::db::UnionDataQuery - hir::db::EnumDataQuery - hir::db::ImplDataQuery - hir::db::TraitDataQuery - hir::db::TypeAliasDataQuery - hir::db::FunctionDataQuery - hir::db::ConstDataQuery - hir::db::StaticDataQuery - hir::db::BodyWithSourceMapQuery - hir::db::BodyQuery - hir::db::ExprScopesQuery - hir::db::GenericParamsQuery - hir::db::AttrsQuery - hir::db::ModuleLangItemsQuery - hir::db::CrateLangItemsQuery - hir::db::LangItemQuery - hir::db::DocumentationQuery - - // InternDatabase - hir::db::InternFunctionQuery - hir::db::InternStructQuery - hir::db::InternUnionQuery - hir::db::InternEnumQuery - hir::db::InternConstQuery - hir::db::InternStaticQuery - hir::db::InternTraitQuery - hir::db::InternTypeAliasQuery - hir::db::InternImplQuery - - // HirDatabase - hir::db::DoInferQuery - hir::db::TyQuery - hir::db::ValueTyQuery - hir::db::ImplSelfTyQuery - hir::db::ImplTraitQuery - hir::db::FieldTypesQuery - hir::db::CallableItemSignatureQuery - hir::db::GenericPredicatesForParamQuery - hir::db::GenericPredicatesQuery - hir::db::GenericDefaultsQuery - hir::db::ImplsInCrateQuery - hir::db::ImplsForTraitQuery - hir::db::TraitSolverQuery - hir::db::InternTypeCtorQuery - hir::db::InternChalkImplQuery - hir::db::InternAssocTyValueQuery - hir::db::AssociatedTyDataQuery - hir::db::AssociatedTyValueQuery - hir::db::TraitSolveQuery - hir::db::TraitDatumQuery - hir::db::StructDatumQuery - hir::db::ImplDatumQuery - ]; - acc.sort_by_key(|it| std::cmp::Reverse(it.1)); - acc - } -} - -fn durability(source_root: &SourceRoot) -> Durability { - if source_root.is_library { - Durability::HIGH - } else { - Durability::LOW - } -} diff --git a/crates/ra_ide/src/ide_db/feature_flags.rs b/crates/ra_ide/src/ide_db/feature_flags.rs deleted file mode 100644 index 85617640d..000000000 --- a/crates/ra_ide/src/ide_db/feature_flags.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! FIXME: write short doc here - -use rustc_hash::FxHashMap; - -/// Feature flags hold fine-grained toggles for all *user-visible* features of -/// rust-analyzer. -/// -/// The exists such that users are able to disable any annoying feature (and, -/// with many users and many features, some features are bound to be annoying -/// for some users) -/// -/// Note that we purposefully use run-time checked strings, and not something -/// checked at compile time, to keep things simple and flexible. -/// -/// Also note that, at the moment, `FeatureFlags` also store features for -/// `ra_lsp_server`. This should be benign layering violation. -#[derive(Debug)] -pub struct FeatureFlags { - flags: FxHashMap, -} - -impl FeatureFlags { - fn new(flags: &[(&str, bool)]) -> FeatureFlags { - let flags = flags - .iter() - .map(|&(name, value)| { - check_flag_name(name); - (name.to_string(), value) - }) - .collect(); - FeatureFlags { flags } - } - - pub fn set(&mut self, flag: &str, value: bool) -> Result<(), ()> { - match self.flags.get_mut(flag) { - None => Err(()), - Some(slot) => { - *slot = value; - Ok(()) - } - } - } - - pub fn get(&self, flag: &str) -> bool { - match self.flags.get(flag) { - None => panic!("unknown flag: {:?}", flag), - Some(value) => *value, - } - } -} - -impl Default for FeatureFlags { - fn default() -> FeatureFlags { - FeatureFlags::new(&[ - ("lsp.diagnostics", true), - ("completion.insertion.add-call-parenthesis", true), - ("completion.enable-postfix", true), - ("notifications.workspace-loaded", true), - ("notifications.cargo-toml-not-found", true), - ]) - } -} - -fn check_flag_name(flag: &str) { - for c in flag.bytes() { - match c { - b'a'..=b'z' | b'-' | b'.' => (), - _ => panic!("flag name does not match conventions: {:?}", flag), - } - } -} diff --git a/crates/ra_ide/src/ide_db/line_index.rs b/crates/ra_ide/src/ide_db/line_index.rs deleted file mode 100644 index 6f99ca3a7..000000000 --- a/crates/ra_ide/src/ide_db/line_index.rs +++ /dev/null @@ -1,283 +0,0 @@ -//! FIXME: write short doc here - -use ra_syntax::TextUnit; -use rustc_hash::FxHashMap; -use superslice::Ext; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct LineIndex { - pub(crate) newlines: Vec, - pub(crate) utf16_lines: FxHashMap>, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct LineCol { - /// Zero-based - pub line: u32, - /// Zero-based - pub col_utf16: u32, -} - -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub(crate) struct Utf16Char { - pub(crate) start: TextUnit, - pub(crate) end: TextUnit, -} - -impl Utf16Char { - fn len(&self) -> TextUnit { - self.end - self.start - } -} - -impl LineIndex { - pub fn new(text: &str) -> LineIndex { - let mut utf16_lines = FxHashMap::default(); - let mut utf16_chars = Vec::new(); - - let mut newlines = vec![0.into()]; - let mut curr_row = 0.into(); - let mut curr_col = 0.into(); - let mut line = 0; - for c in text.chars() { - curr_row += TextUnit::of_char(c); - if c == '\n' { - newlines.push(curr_row); - - // Save any utf-16 characters seen in the previous line - if !utf16_chars.is_empty() { - utf16_lines.insert(line, utf16_chars); - utf16_chars = Vec::new(); - } - - // Prepare for processing the next line - curr_col = 0.into(); - line += 1; - continue; - } - - let char_len = TextUnit::of_char(c); - if char_len.to_usize() > 1 { - utf16_chars.push(Utf16Char { start: curr_col, end: curr_col + char_len }); - } - - curr_col += char_len; - } - - // Save any utf-16 characters seen in the last line - if !utf16_chars.is_empty() { - utf16_lines.insert(line, utf16_chars); - } - - LineIndex { newlines, utf16_lines } - } - - pub fn line_col(&self, offset: TextUnit) -> LineCol { - let line = self.newlines.upper_bound(&offset) - 1; - let line_start_offset = self.newlines[line]; - let col = offset - line_start_offset; - - LineCol { line: line as u32, col_utf16: self.utf8_to_utf16_col(line as u32, col) as u32 } - } - - pub fn offset(&self, line_col: LineCol) -> TextUnit { - //FIXME: return Result - let col = self.utf16_to_utf8_col(line_col.line, line_col.col_utf16); - self.newlines[line_col.line as usize] + col - } - - fn utf8_to_utf16_col(&self, line: u32, mut col: TextUnit) -> usize { - if let Some(utf16_chars) = self.utf16_lines.get(&line) { - let mut correction = TextUnit::from_usize(0); - for c in utf16_chars { - if col >= c.end { - correction += c.len() - TextUnit::from_usize(1); - } else { - // From here on, all utf16 characters come *after* the character we are mapping, - // so we don't need to take them into account - break; - } - } - - col -= correction; - } - - col.to_usize() - } - - fn utf16_to_utf8_col(&self, line: u32, col: u32) -> TextUnit { - let mut col: TextUnit = col.into(); - if let Some(utf16_chars) = self.utf16_lines.get(&line) { - for c in utf16_chars { - if col >= c.start { - col += c.len() - TextUnit::from_usize(1); - } else { - // From here on, all utf16 characters come *after* the character we are mapping, - // so we don't need to take them into account - break; - } - } - } - - col - } -} - -#[cfg(test)] -/// Simple reference implementation to use in proptests -pub fn to_line_col(text: &str, offset: TextUnit) -> LineCol { - let mut res = LineCol { line: 0, col_utf16: 0 }; - for (i, c) in text.char_indices() { - if i + c.len_utf8() > offset.to_usize() { - // if it's an invalid offset, inside a multibyte char - // return as if it was at the start of the char - break; - } - if c == '\n' { - res.line += 1; - res.col_utf16 = 0; - } else { - res.col_utf16 += 1; - } - } - res -} - -#[cfg(test)] -mod test_line_index { - use super::*; - use proptest::{prelude::*, proptest}; - use ra_text_edit::test_utils::{arb_offset, arb_text}; - - #[test] - fn test_line_index() { - let text = "hello\nworld"; - let index = LineIndex::new(text); - assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); - assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 }); - assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 }); - assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 }); - assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 }); - assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 }); - assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 }); - assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 }); - assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 }); - - let text = "\nhello\nworld"; - let index = LineIndex::new(text); - assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 }); - assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 }); - assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 }); - assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 }); - assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 }); - } - - fn arb_text_with_offset() -> BoxedStrategy<(TextUnit, String)> { - arb_text().prop_flat_map(|text| (arb_offset(&text), Just(text))).boxed() - } - - fn to_line_col(text: &str, offset: TextUnit) -> LineCol { - let mut res = LineCol { line: 0, col_utf16: 0 }; - for (i, c) in text.char_indices() { - if i + c.len_utf8() > offset.to_usize() { - // if it's an invalid offset, inside a multibyte char - // return as if it was at the start of the char - break; - } - if c == '\n' { - res.line += 1; - res.col_utf16 = 0; - } else { - res.col_utf16 += 1; - } - } - res - } - - proptest! { - #[test] - fn test_line_index_proptest((offset, text) in arb_text_with_offset()) { - let expected = to_line_col(&text, offset); - let line_index = LineIndex::new(&text); - let actual = line_index.line_col(offset); - - assert_eq!(actual, expected); - } - } -} - -#[cfg(test)] -mod test_utf8_utf16_conv { - use super::*; - - #[test] - fn test_char_len() { - assert_eq!('メ'.len_utf8(), 3); - assert_eq!('メ'.len_utf16(), 1); - } - - #[test] - fn test_empty_index() { - let col_index = LineIndex::new( - " -const C: char = 'x'; -", - ); - assert_eq!(col_index.utf16_lines.len(), 0); - } - - #[test] - fn test_single_char() { - let col_index = LineIndex::new( - " -const C: char = 'メ'; -", - ); - - assert_eq!(col_index.utf16_lines.len(), 1); - assert_eq!(col_index.utf16_lines[&1].len(), 1); - assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() }); - - // UTF-8 to UTF-16, no changes - assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15); - - // UTF-8 to UTF-16 - assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20); - - // UTF-16 to UTF-8, no changes - assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextUnit::from(15)); - - // UTF-16 to UTF-8 - assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextUnit::from(21)); - } - - #[test] - fn test_string() { - let col_index = LineIndex::new( - " -const C: char = \"メ メ\"; -", - ); - - assert_eq!(col_index.utf16_lines.len(), 1); - assert_eq!(col_index.utf16_lines[&1].len(), 2); - assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() }); - assert_eq!(col_index.utf16_lines[&1][1], Utf16Char { start: 21.into(), end: 24.into() }); - - // UTF-8 to UTF-16 - assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15); - - assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19); - assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21); - - assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15); - - // UTF-16 to UTF-8 - assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextUnit::from_usize(15)); - - assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextUnit::from_usize(20)); - assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextUnit::from_usize(23)); - - assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextUnit::from_usize(15)); - } -} diff --git a/crates/ra_ide/src/ide_db/line_index_utils.rs b/crates/ra_ide/src/ide_db/line_index_utils.rs deleted file mode 100644 index faa3d665f..000000000 --- a/crates/ra_ide/src/ide_db/line_index_utils.rs +++ /dev/null @@ -1,334 +0,0 @@ -//! FIXME: write short doc here - -use ra_syntax::{TextRange, TextUnit}; -use ra_text_edit::{AtomTextEdit, TextEdit}; - -use crate::ide_db::line_index::{LineCol, LineIndex, Utf16Char}; - -#[derive(Debug, Clone)] -enum Step { - Newline(TextUnit), - Utf16Char(TextRange), -} - -#[derive(Debug)] -struct LineIndexStepIter<'a> { - line_index: &'a LineIndex, - next_newline_idx: usize, - utf16_chars: Option<(TextUnit, std::slice::Iter<'a, Utf16Char>)>, -} - -impl<'a> LineIndexStepIter<'a> { - fn from(line_index: &LineIndex) -> LineIndexStepIter { - let mut x = LineIndexStepIter { line_index, next_newline_idx: 0, utf16_chars: None }; - // skip first newline since it's not real - x.next(); - x - } -} - -impl<'a> Iterator for LineIndexStepIter<'a> { - type Item = Step; - fn next(&mut self) -> Option { - self.utf16_chars - .as_mut() - .and_then(|(newline, x)| { - let x = x.next()?; - Some(Step::Utf16Char(TextRange::from_to(*newline + x.start, *newline + x.end))) - }) - .or_else(|| { - let next_newline = *self.line_index.newlines.get(self.next_newline_idx)?; - self.utf16_chars = self - .line_index - .utf16_lines - .get(&(self.next_newline_idx as u32)) - .map(|x| (next_newline, x.iter())); - self.next_newline_idx += 1; - Some(Step::Newline(next_newline)) - }) - } -} - -#[derive(Debug)] -struct OffsetStepIter<'a> { - text: &'a str, - offset: TextUnit, -} - -impl<'a> Iterator for OffsetStepIter<'a> { - type Item = Step; - fn next(&mut self) -> Option { - let (next, next_offset) = self - .text - .char_indices() - .filter_map(|(i, c)| { - if c == '\n' { - let next_offset = self.offset + TextUnit::from_usize(i + 1); - let next = Step::Newline(next_offset); - Some((next, next_offset)) - } else { - let char_len = TextUnit::of_char(c); - if char_len.to_usize() > 1 { - let start = self.offset + TextUnit::from_usize(i); - let end = start + char_len; - let next = Step::Utf16Char(TextRange::from_to(start, end)); - let next_offset = end; - Some((next, next_offset)) - } else { - None - } - } - }) - .next()?; - let next_idx = (next_offset - self.offset).to_usize(); - self.text = &self.text[next_idx..]; - self.offset = next_offset; - Some(next) - } -} - -#[derive(Debug)] -enum NextSteps<'a> { - Use, - ReplaceMany(OffsetStepIter<'a>), - AddMany(OffsetStepIter<'a>), -} - -#[derive(Debug)] -struct TranslatedEdit<'a> { - delete: TextRange, - insert: &'a str, - diff: i64, -} - -struct Edits<'a> { - edits: &'a [AtomTextEdit], - current: Option>, - acc_diff: i64, -} - -impl<'a> Edits<'a> { - fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> { - let mut x = Edits { edits: text_edit.as_atoms(), current: None, acc_diff: 0 }; - x.advance_edit(); - x - } - fn advance_edit(&mut self) { - self.acc_diff += self.current.as_ref().map_or(0, |x| x.diff); - match self.edits.split_first() { - Some((next, rest)) => { - let delete = self.translate_range(next.delete); - let diff = next.insert.len() as i64 - next.delete.len().to_usize() as i64; - self.current = Some(TranslatedEdit { delete, insert: &next.insert, diff }); - self.edits = rest; - } - None => { - self.current = None; - } - } - } - - fn next_inserted_steps(&mut self) -> Option> { - let cur = self.current.as_ref()?; - let res = Some(OffsetStepIter { offset: cur.delete.start(), text: &cur.insert }); - self.advance_edit(); - res - } - - fn next_steps(&mut self, step: &Step) -> NextSteps { - let step_pos = match *step { - Step::Newline(n) => n, - Step::Utf16Char(r) => r.end(), - }; - match &mut self.current { - Some(edit) => { - if step_pos <= edit.delete.start() { - NextSteps::Use - } else if step_pos <= edit.delete.end() { - let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert }; - // empty slice to avoid returning steps again - edit.insert = &edit.insert[edit.insert.len()..]; - NextSteps::ReplaceMany(iter) - } else { - let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert }; - // empty slice to avoid returning steps again - edit.insert = &edit.insert[edit.insert.len()..]; - self.advance_edit(); - NextSteps::AddMany(iter) - } - } - None => NextSteps::Use, - } - } - - fn translate_range(&self, range: TextRange) -> TextRange { - if self.acc_diff == 0 { - range - } else { - let start = self.translate(range.start()); - let end = self.translate(range.end()); - TextRange::from_to(start, end) - } - } - - fn translate(&self, x: TextUnit) -> TextUnit { - if self.acc_diff == 0 { - x - } else { - TextUnit::from((x.to_usize() as i64 + self.acc_diff) as u32) - } - } - - fn translate_step(&self, x: &Step) -> Step { - if self.acc_diff == 0 { - x.clone() - } else { - match *x { - Step::Newline(n) => Step::Newline(self.translate(n)), - Step::Utf16Char(r) => Step::Utf16Char(self.translate_range(r)), - } - } - } -} - -#[derive(Debug)] -struct RunningLineCol { - line: u32, - last_newline: TextUnit, - col_adjust: TextUnit, -} - -impl RunningLineCol { - fn new() -> RunningLineCol { - RunningLineCol { line: 0, last_newline: TextUnit::from(0), col_adjust: TextUnit::from(0) } - } - - fn to_line_col(&self, offset: TextUnit) -> LineCol { - LineCol { - line: self.line, - col_utf16: ((offset - self.last_newline) - self.col_adjust).into(), - } - } - - fn add_line(&mut self, newline: TextUnit) { - self.line += 1; - self.last_newline = newline; - self.col_adjust = TextUnit::from(0); - } - - fn adjust_col(&mut self, range: TextRange) { - self.col_adjust += range.len() - TextUnit::from(1); - } -} - -pub fn translate_offset_with_edit( - line_index: &LineIndex, - offset: TextUnit, - text_edit: &TextEdit, -) -> LineCol { - let mut state = Edits::from_text_edit(&text_edit); - - let mut res = RunningLineCol::new(); - - macro_rules! test_step { - ($x:ident) => { - match &$x { - Step::Newline(n) => { - if offset < *n { - return res.to_line_col(offset); - } else { - res.add_line(*n); - } - } - Step::Utf16Char(x) => { - if offset < x.end() { - // if the offset is inside a multibyte char it's invalid - // clamp it to the start of the char - let clamp = offset.min(x.start()); - return res.to_line_col(clamp); - } else { - res.adjust_col(*x); - } - } - } - }; - } - - for orig_step in LineIndexStepIter::from(line_index) { - loop { - let translated_step = state.translate_step(&orig_step); - match state.next_steps(&translated_step) { - NextSteps::Use => { - test_step!(translated_step); - break; - } - NextSteps::ReplaceMany(ns) => { - for n in ns { - test_step!(n); - } - break; - } - NextSteps::AddMany(ns) => { - for n in ns { - test_step!(n); - } - } - } - } - } - - loop { - match state.next_inserted_steps() { - None => break, - Some(ns) => { - for n in ns { - test_step!(n); - } - } - } - } - - res.to_line_col(offset) -} - -#[cfg(test)] -mod test { - use proptest::{prelude::*, proptest}; - use ra_text_edit::test_utils::{arb_offset, arb_text_with_edit}; - use ra_text_edit::TextEdit; - - use crate::ide_db::line_index; - - use super::*; - - #[derive(Debug)] - struct ArbTextWithEditAndOffset { - text: String, - edit: TextEdit, - edited_text: String, - offset: TextUnit, - } - - fn arb_text_with_edit_and_offset() -> BoxedStrategy { - arb_text_with_edit() - .prop_flat_map(|x| { - let edited_text = x.edit.apply(&x.text); - let arb_offset = arb_offset(&edited_text); - (Just(x), Just(edited_text), arb_offset).prop_map(|(x, edited_text, offset)| { - ArbTextWithEditAndOffset { text: x.text, edit: x.edit, edited_text, offset } - }) - }) - .boxed() - } - - proptest! { - #[test] - fn test_translate_offset_with_edit(x in arb_text_with_edit_and_offset()) { - let expected = line_index::to_line_col(&x.edited_text, x.offset); - let line_index = LineIndex::new(&x.text); - let actual = translate_offset_with_edit(&line_index, x.offset, &x.edit); - - assert_eq!(actual, expected); - } - } -} diff --git a/crates/ra_ide/src/ide_db/mod.rs b/crates/ra_ide/src/ide_db/mod.rs deleted file mode 100644 index 0df4d510f..000000000 --- a/crates/ra_ide/src/ide_db/mod.rs +++ /dev/null @@ -1,137 +0,0 @@ -//! FIXME: write short doc here - -pub mod line_index; -pub mod line_index_utils; -pub mod feature_flags; -pub mod symbol_index; -pub mod change; - -use std::sync::Arc; - -use ra_db::{ - salsa::{self, Database, Durability}, - Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, - SourceDatabase, SourceRootId, -}; -use rustc_hash::FxHashMap; - -use crate::ide_db::{ - feature_flags::FeatureFlags, line_index::LineIndex, symbol_index::SymbolsDatabase, -}; - -#[salsa::database( - ra_db::SourceDatabaseStorage, - ra_db::SourceDatabaseExtStorage, - LineIndexDatabaseStorage, - symbol_index::SymbolsDatabaseStorage, - hir::db::InternDatabaseStorage, - hir::db::AstDatabaseStorage, - hir::db::DefDatabaseStorage, - hir::db::HirDatabaseStorage -)] -#[derive(Debug)] -pub(crate) struct RootDatabase { - runtime: salsa::Runtime, - pub(crate) feature_flags: Arc, - pub(crate) debug_data: Arc, - pub(crate) last_gc: crate::wasm_shims::Instant, - pub(crate) last_gc_check: crate::wasm_shims::Instant, -} - -impl FileLoader for RootDatabase { - fn file_text(&self, file_id: FileId) -> Arc { - FileLoaderDelegate(self).file_text(file_id) - } - fn resolve_relative_path( - &self, - anchor: FileId, - relative_path: &RelativePath, - ) -> Option { - FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) - } - fn relevant_crates(&self, file_id: FileId) -> Arc> { - FileLoaderDelegate(self).relevant_crates(file_id) - } -} - -impl salsa::Database for RootDatabase { - fn salsa_runtime(&self) -> &salsa::Runtime { - &self.runtime - } - fn salsa_runtime_mut(&mut self) -> &mut salsa::Runtime { - &mut self.runtime - } - fn on_propagated_panic(&self) -> ! { - Canceled::throw() - } - fn salsa_event(&self, event: impl Fn() -> salsa::Event) { - match event().kind { - salsa::EventKind::DidValidateMemoizedValue { .. } - | salsa::EventKind::WillExecute { .. } => { - self.check_canceled(); - } - _ => (), - } - } -} - -impl Default for RootDatabase { - fn default() -> RootDatabase { - RootDatabase::new(None, FeatureFlags::default()) - } -} - -impl RootDatabase { - pub fn new(lru_capacity: Option, feature_flags: FeatureFlags) -> RootDatabase { - let mut db = RootDatabase { - runtime: salsa::Runtime::default(), - last_gc: crate::wasm_shims::Instant::now(), - last_gc_check: crate::wasm_shims::Instant::now(), - feature_flags: Arc::new(feature_flags), - debug_data: Default::default(), - }; - db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); - db.set_local_roots_with_durability(Default::default(), Durability::HIGH); - db.set_library_roots_with_durability(Default::default(), Durability::HIGH); - let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP); - db.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity); - db.query_mut(hir::db::ParseMacroQuery).set_lru_capacity(lru_capacity); - db.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity); - db - } -} - -impl salsa::ParallelDatabase for RootDatabase { - fn snapshot(&self) -> salsa::Snapshot { - salsa::Snapshot::new(RootDatabase { - runtime: self.runtime.snapshot(self), - last_gc: self.last_gc, - last_gc_check: self.last_gc_check, - feature_flags: Arc::clone(&self.feature_flags), - debug_data: Arc::clone(&self.debug_data), - }) - } -} - -#[salsa::query_group(LineIndexDatabaseStorage)] -pub(crate) trait LineIndexDatabase: ra_db::SourceDatabase + CheckCanceled { - fn line_index(&self, file_id: FileId) -> Arc; -} - -fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc { - let text = db.file_text(file_id); - Arc::new(LineIndex::new(&*text)) -} - -#[derive(Debug, Default, Clone)] -pub(crate) struct DebugData { - pub(crate) root_paths: FxHashMap, - pub(crate) crate_names: FxHashMap, -} - -impl DebugData { - pub(crate) fn merge(&mut self, other: DebugData) { - self.root_paths.extend(other.root_paths.into_iter()); - self.crate_names.extend(other.crate_names.into_iter()); - } -} diff --git a/crates/ra_ide/src/ide_db/symbol_index.rs b/crates/ra_ide/src/ide_db/symbol_index.rs deleted file mode 100644 index c66eeb8e2..000000000 --- a/crates/ra_ide/src/ide_db/symbol_index.rs +++ /dev/null @@ -1,445 +0,0 @@ -//! This module handles fuzzy-searching of functions, structs and other symbols -//! by name across the whole workspace and dependencies. -//! -//! It works by building an incrementally-updated text-search index of all -//! symbols. The backbone of the index is the **awesome** `fst` crate by -//! @BurntSushi. -//! -//! In a nutshell, you give a set of strings to `fst`, and it builds a -//! finite state machine describing this set of strings. The strings which -//! could fuzzy-match a pattern can also be described by a finite state machine. -//! What is freaking cool is that you can now traverse both state machines in -//! lock-step to enumerate the strings which are both in the input set and -//! fuzz-match the query. Or, more formally, given two languages described by -//! FSTs, one can build a product FST which describes the intersection of the -//! languages. -//! -//! `fst` does not support cheap updating of the index, but it supports unioning -//! of state machines. So, to account for changing source code, we build an FST -//! for each library (which is assumed to never change) and an FST for each Rust -//! file in the current workspace, and run a query against the union of all -//! those FSTs. -use std::{ - fmt, - hash::{Hash, Hasher}, - mem, - sync::Arc, -}; - -use fst::{self, Streamer}; -use ra_db::{ - salsa::{self, ParallelDatabase}, - FileId, SourceDatabaseExt, SourceRootId, -}; -use ra_syntax::{ - ast::{self, NameOwner}, - match_ast, AstNode, Parse, SmolStr, SourceFile, - SyntaxKind::{self, *}, - SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent, -}; -#[cfg(not(feature = "wasm"))] -use rayon::prelude::*; - -use crate::ide_db::RootDatabase; - -#[derive(Debug)] -pub struct Query { - query: String, - lowercased: String, - only_types: bool, - libs: bool, - exact: bool, - limit: usize, -} - -impl Query { - pub fn new(query: String) -> Query { - let lowercased = query.to_lowercase(); - Query { - query, - lowercased, - only_types: false, - libs: false, - exact: false, - limit: usize::max_value(), - } - } - - pub fn only_types(&mut self) { - self.only_types = true; - } - - pub fn libs(&mut self) { - self.libs = true; - } - - pub fn exact(&mut self) { - self.exact = true; - } - - pub fn limit(&mut self, limit: usize) { - self.limit = limit - } -} - -#[salsa::query_group(SymbolsDatabaseStorage)] -pub(crate) trait SymbolsDatabase: hir::db::HirDatabase { - fn file_symbols(&self, file_id: FileId) -> Arc; - #[salsa::input] - fn library_symbols(&self, id: SourceRootId) -> Arc; - /// The set of "local" (that is, from the current workspace) roots. - /// Files in local roots are assumed to change frequently. - #[salsa::input] - fn local_roots(&self) -> Arc>; - /// The set of roots for crates.io libraries. - /// Files in libraries are assumed to never change. - #[salsa::input] - fn library_roots(&self) -> Arc>; -} - -fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc { - db.check_canceled(); - let parse = db.parse(file_id); - - let symbols = source_file_to_file_symbols(&parse.tree(), file_id); - - // FIXME: add macros here - - Arc::new(SymbolIndex::new(symbols)) -} - -pub(crate) fn world_symbols(db: &RootDatabase, query: Query) -> Vec { - /// Need to wrap Snapshot to provide `Clone` impl for `map_with` - struct Snap(salsa::Snapshot); - impl Clone for Snap { - fn clone(&self) -> Snap { - Snap(self.0.snapshot()) - } - } - - let buf: Vec> = if query.libs { - let snap = Snap(db.snapshot()); - #[cfg(not(feature = "wasm"))] - let buf = db - .library_roots() - .par_iter() - .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id)) - .collect(); - - #[cfg(feature = "wasm")] - let buf = db.library_roots().iter().map(|&lib_id| snap.0.library_symbols(lib_id)).collect(); - - buf - } else { - let mut files = Vec::new(); - for &root in db.local_roots().iter() { - let sr = db.source_root(root); - files.extend(sr.walk()) - } - - let snap = Snap(db.snapshot()); - #[cfg(not(feature = "wasm"))] - let buf = - files.par_iter().map_with(snap, |db, &file_id| db.0.file_symbols(file_id)).collect(); - - #[cfg(feature = "wasm")] - let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect(); - - buf - }; - query.search(&buf) -} - -pub(crate) fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec { - let name = name_ref.text(); - let mut query = Query::new(name.to_string()); - query.exact(); - query.limit(4); - world_symbols(db, query) -} - -#[derive(Default)] -pub(crate) struct SymbolIndex { - symbols: Vec, - map: fst::Map, -} - -impl fmt::Debug for SymbolIndex { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("SymbolIndex").field("n_symbols", &self.symbols.len()).finish() - } -} - -impl PartialEq for SymbolIndex { - fn eq(&self, other: &SymbolIndex) -> bool { - self.symbols == other.symbols - } -} - -impl Eq for SymbolIndex {} - -impl Hash for SymbolIndex { - fn hash(&self, hasher: &mut H) { - self.symbols.hash(hasher) - } -} - -impl SymbolIndex { - fn new(mut symbols: Vec) -> SymbolIndex { - fn cmp_key<'a>(s1: &'a FileSymbol) -> impl Ord + 'a { - unicase::Ascii::new(s1.name.as_str()) - } - #[cfg(not(feature = "wasm"))] - symbols.par_sort_by(|s1, s2| cmp_key(s1).cmp(&cmp_key(s2))); - - #[cfg(feature = "wasm")] - symbols.sort_by(|s1, s2| cmp_key(s1).cmp(&cmp_key(s2))); - - let mut builder = fst::MapBuilder::memory(); - - let mut last_batch_start = 0; - - for idx in 0..symbols.len() { - if symbols.get(last_batch_start).map(cmp_key) == symbols.get(idx + 1).map(cmp_key) { - continue; - } - - let start = last_batch_start; - let end = idx + 1; - last_batch_start = end; - - let key = symbols[start].name.as_str().to_lowercase(); - let value = SymbolIndex::range_to_map_value(start, end); - - builder.insert(key, value).unwrap(); - } - - let map = fst::Map::from_bytes(builder.into_inner().unwrap()).unwrap(); - SymbolIndex { symbols, map } - } - - pub(crate) fn len(&self) -> usize { - self.symbols.len() - } - - pub(crate) fn memory_size(&self) -> usize { - self.map.as_fst().size() + self.symbols.len() * mem::size_of::() - } - - #[cfg(not(feature = "wasm"))] - pub(crate) fn for_files( - files: impl ParallelIterator)>, - ) -> SymbolIndex { - let symbols = files - .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id)) - .collect::>(); - SymbolIndex::new(symbols) - } - - #[cfg(feature = "wasm")] - pub(crate) fn for_files( - files: impl Iterator)>, - ) -> SymbolIndex { - let symbols = files - .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id)) - .collect::>(); - SymbolIndex::new(symbols) - } - - fn range_to_map_value(start: usize, end: usize) -> u64 { - debug_assert![start <= (std::u32::MAX as usize)]; - debug_assert![end <= (std::u32::MAX as usize)]; - - ((start as u64) << 32) | end as u64 - } - - fn map_value_to_range(value: u64) -> (usize, usize) { - let end = value as u32 as usize; - let start = (value >> 32) as usize; - (start, end) - } -} - -impl Query { - pub(crate) fn search(self, indices: &[Arc]) -> Vec { - let mut op = fst::map::OpBuilder::new(); - for file_symbols in indices.iter() { - let automaton = fst::automaton::Subsequence::new(&self.lowercased); - op = op.add(file_symbols.map.search(automaton)) - } - let mut stream = op.union(); - let mut res = Vec::new(); - while let Some((_, indexed_values)) = stream.next() { - if res.len() >= self.limit { - break; - } - for indexed_value in indexed_values { - let symbol_index = &indices[indexed_value.index]; - let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); - - for symbol in &symbol_index.symbols[start..end] { - if self.only_types && !is_type(symbol.ptr.kind()) { - continue; - } - if self.exact && symbol.name != self.query { - continue; - } - res.push(symbol.clone()); - } - } - } - res - } -} - -fn is_type(kind: SyntaxKind) -> bool { - match kind { - STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => true, - _ => false, - } -} - -/// The actual data that is stored in the index. It should be as compact as -/// possible. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) struct FileSymbol { - pub(crate) file_id: FileId, - pub(crate) name: SmolStr, - pub(crate) ptr: SyntaxNodePtr, - pub(crate) name_range: Option, - pub(crate) container_name: Option, -} - -fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec { - let mut symbols = Vec::new(); - let mut stack = Vec::new(); - - for event in source_file.syntax().preorder() { - match event { - WalkEvent::Enter(node) => { - if let Some(mut symbol) = to_file_symbol(&node, file_id) { - symbol.container_name = stack.last().cloned(); - - stack.push(symbol.name.clone()); - symbols.push(symbol); - } - } - - WalkEvent::Leave(node) => { - if to_symbol(&node).is_some() { - stack.pop(); - } - } - } - } - - symbols -} - -fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { - fn decl(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { - let name = node.name()?; - let name_range = name.syntax().text_range(); - let name = name.text().clone(); - let ptr = SyntaxNodePtr::new(node.syntax()); - - Some((name, ptr, name_range)) - } - match_ast! { - match node { - ast::FnDef(it) => { decl(it) }, - ast::StructDef(it) => { decl(it) }, - ast::EnumDef(it) => { decl(it) }, - ast::TraitDef(it) => { decl(it) }, - ast::Module(it) => { decl(it) }, - ast::TypeAliasDef(it) => { decl(it) }, - ast::ConstDef(it) => { decl(it) }, - ast::StaticDef(it) => { decl(it) }, - _ => None, - } - } -} - -fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option { - to_symbol(node).map(move |(name, ptr, name_range)| FileSymbol { - name, - ptr, - file_id, - name_range: Some(name_range), - container_name: None, - }) -} - -#[cfg(test)] -mod tests { - use crate::{display::NavigationTarget, mock_analysis::single_file, Query}; - use ra_syntax::{ - SmolStr, - SyntaxKind::{FN_DEF, STRUCT_DEF}, - }; - - #[test] - fn test_world_symbols_with_no_container() { - let code = r#" - enum FooInner { } - "#; - - let mut symbols = get_symbols_matching(code, "FooInner"); - - let s = symbols.pop().unwrap(); - - assert_eq!(s.name(), "FooInner"); - assert!(s.container_name().is_none()); - } - - #[test] - fn test_world_symbols_include_container_name() { - let code = r#" -fn foo() { - enum FooInner { } -} - "#; - - let mut symbols = get_symbols_matching(code, "FooInner"); - - let s = symbols.pop().unwrap(); - - assert_eq!(s.name(), "FooInner"); - assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); - - let code = r#" -mod foo { - struct FooInner; -} - "#; - - let mut symbols = get_symbols_matching(code, "FooInner"); - - let s = symbols.pop().unwrap(); - - assert_eq!(s.name(), "FooInner"); - assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); - } - - #[test] - fn test_world_symbols_are_case_sensitive() { - let code = r#" -fn foo() {} - -struct Foo; - "#; - - let symbols = get_symbols_matching(code, "Foo"); - - let fn_match = symbols.iter().find(|s| s.name() == "foo").map(|s| s.kind()); - let struct_match = symbols.iter().find(|s| s.name() == "Foo").map(|s| s.kind()); - - assert_eq!(fn_match, Some(FN_DEF)); - assert_eq!(struct_match, Some(STRUCT_DEF)); - } - - fn get_symbols_matching(text: &str, query: &str) -> Vec { - let (analysis, _) = single_file(text); - analysis.symbol_search(Query::new(query.into())).unwrap() - } -} diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 66f365cc3..1527b27d4 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -10,7 +10,9 @@ // For proving that RootDatabase is RefUnwindSafe. #![recursion_limit = "128"] -mod ide_db; +mod ide_db { + pub use ra_ide_db::*; +} mod db; pub mod mock_analysis; @@ -39,7 +41,6 @@ mod typing; mod matching_brace; mod display; mod inlay_hints; -mod wasm_shims; mod expand; mod expand_macro; diff --git a/crates/ra_ide/src/wasm_shims.rs b/crates/ra_ide/src/wasm_shims.rs deleted file mode 100644 index 088cc9be4..000000000 --- a/crates/ra_ide/src/wasm_shims.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! FIXME: write short doc here - -#[cfg(not(feature = "wasm"))] -pub use std::time::Instant; - -#[cfg(feature = "wasm")] -#[derive(Clone, Copy, Debug)] -pub struct Instant; - -#[cfg(feature = "wasm")] -impl Instant { - pub fn now() -> Self { - Self - } - - pub fn elapsed(&self) -> std::time::Duration { - std::time::Duration::new(0, 0) - } -} -- cgit v1.2.3 From 88267c86c0c49de395973574d2516ab904091cfb Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 12:52:32 +0100 Subject: cleanup imports --- crates/ra_ide/src/assists.rs | 10 +++--- crates/ra_ide/src/call_hierarchy.rs | 2 +- crates/ra_ide/src/call_info.rs | 6 ++-- crates/ra_ide/src/completion.rs | 5 +-- crates/ra_ide/src/completion/completion_context.rs | 7 ++-- crates/ra_ide/src/db.rs | 1 - crates/ra_ide/src/diagnostics.rs | 3 +- crates/ra_ide/src/display/function_signature.rs | 17 ++++------ crates/ra_ide/src/display/navigation_target.rs | 3 +- crates/ra_ide/src/expand.rs | 3 +- crates/ra_ide/src/expand_macro.rs | 25 +++++++------- crates/ra_ide/src/extend_selection.rs | 12 ++++--- crates/ra_ide/src/goto_definition.rs | 3 +- crates/ra_ide/src/goto_type_definition.rs | 4 +-- crates/ra_ide/src/hover.rs | 2 +- crates/ra_ide/src/impls.rs | 3 +- crates/ra_ide/src/imports_locator.rs | 15 +++++---- crates/ra_ide/src/inlay_hints.rs | 3 +- crates/ra_ide/src/lib.rs | 38 ++++++++++------------ crates/ra_ide/src/parent_module.rs | 3 +- crates/ra_ide/src/references.rs | 5 ++- crates/ra_ide/src/references/classify.rs | 2 +- crates/ra_ide/src/references/name_definition.rs | 2 +- crates/ra_ide/src/references/rename.rs | 4 +-- crates/ra_ide/src/references/search_scope.rs | 2 +- crates/ra_ide/src/runnables.rs | 3 +- crates/ra_ide/src/status.rs | 10 +++--- crates/ra_ide/src/syntax_highlighting.rs | 2 +- crates/ra_ide/src/syntax_tree.rs | 2 +- crates/ra_ide/src/typing.rs | 3 +- 30 files changed, 101 insertions(+), 99 deletions(-) delete mode 100644 crates/ra_ide/src/db.rs (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index c43c45c65..f26047570 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs @@ -1,13 +1,13 @@ //! FIXME: write short doc here +use either::Either; +use ra_assists::{AssistAction, AssistLabel}; use ra_db::{FilePosition, FileRange}; +use ra_ide_db::RootDatabase; + +use crate::{imports_locator::ImportsLocatorIde, FileId, SourceChange, SourceFileEdit}; -use crate::{ - db::RootDatabase, imports_locator::ImportsLocatorIde, FileId, SourceChange, SourceFileEdit, -}; -use either::Either; pub use ra_assists::AssistId; -use ra_assists::{AssistAction, AssistLabel}; #[derive(Debug)] pub struct Assist { diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs index aa5d60c7b..f984f40ad 100644 --- a/crates/ra_ide/src/call_hierarchy.rs +++ b/crates/ra_ide/src/call_hierarchy.rs @@ -3,6 +3,7 @@ use indexmap::IndexMap; use hir::db::AstDatabase; +use ra_ide_db::RootDatabase; use ra_syntax::{ ast::{self, DocCommentsOwner}, match_ast, AstNode, TextRange, @@ -10,7 +11,6 @@ use ra_syntax::{ use crate::{ call_info::FnCallNode, - db::RootDatabase, display::{ShortLabel, ToNav}, expand::descend_into_macros, goto_definition, references, FilePosition, NavigationTarget, RangeInfo, diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index 72a68522e..f2b29306e 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs @@ -1,15 +1,13 @@ //! FIXME: write short doc here use hir::db::AstDatabase; +use ra_ide_db::RootDatabase; use ra_syntax::{ ast::{self, ArgListOwner}, match_ast, AstNode, SyntaxNode, }; - use test_utils::tested_by; -use crate::{ - db::RootDatabase, expand::descend_into_macros, CallInfo, FilePosition, FunctionSignature, -}; +use crate::{expand::descend_into_macros, CallInfo, FilePosition, FunctionSignature}; /// Computes parameter information for the given call expression. pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option { diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index abe1f36ce..fedc02e14 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs @@ -17,6 +17,7 @@ mod complete_postfix; mod complete_macro_in_item_position; use ra_db::SourceDatabase; +use ra_ide_db::RootDatabase; #[cfg(test)] use crate::completion::completion_item::do_completion; @@ -25,7 +26,7 @@ use crate::{ completion_context::CompletionContext, completion_item::{CompletionKind, Completions}, }, - db, FilePosition, + FilePosition, }; pub use crate::completion::completion_item::{ @@ -54,7 +55,7 @@ pub use crate::completion::completion_item::{ /// `foo` *should* be present among the completion variants. Filtering by /// identifier prefix/fuzzy match should be done higher in the stack, together /// with ordering of completions (currently this is done by the client). -pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Option { +pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option { let original_parse = db.parse(position.file_id); let ctx = CompletionContext::new(db, &original_parse, position)?; diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index deaacda6c..5a0407fd7 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -1,5 +1,6 @@ //! FIXME: write short doc here +use ra_ide_db::RootDatabase; use ra_syntax::{ algo::{find_covering_element, find_node_at_offset}, ast, AstNode, Parse, SourceFile, @@ -8,13 +9,13 @@ use ra_syntax::{ }; use ra_text_edit::AtomTextEdit; -use crate::{db, FilePosition}; +use crate::FilePosition; /// `CompletionContext` is created early during completion to figure out, where /// exactly is the cursor, syntax-wise. #[derive(Debug)] pub(crate) struct CompletionContext<'a> { - pub(super) db: &'a db::RootDatabase, + pub(super) db: &'a RootDatabase, pub(super) analyzer: hir::SourceAnalyzer, pub(super) offset: TextUnit, pub(super) token: SyntaxToken, @@ -48,7 +49,7 @@ pub(crate) struct CompletionContext<'a> { impl<'a> CompletionContext<'a> { pub(super) fn new( - db: &'a db::RootDatabase, + db: &'a RootDatabase, original_parse: &'a Parse, position: FilePosition, ) -> Option> { diff --git a/crates/ra_ide/src/db.rs b/crates/ra_ide/src/db.rs deleted file mode 100644 index 2849cdb02..000000000 --- a/crates/ra_ide/src/db.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) use crate::ide_db::*; diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index f403b3bcf..22bd49723 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -5,6 +5,7 @@ use std::cell::RefCell; use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}; use itertools::Itertools; use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; +use ra_ide_db::RootDatabase; use ra_prof::profile; use ra_syntax::{ algo, @@ -13,7 +14,7 @@ use ra_syntax::{ }; use ra_text_edit::{TextEdit, TextEditBuilder}; -use crate::{db::RootDatabase, Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; +use crate::{Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; #[derive(Debug, Copy, Clone)] pub enum Severity { diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index 1e4a472b4..c23e08e9a 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -4,13 +4,11 @@ use std::fmt::{self, Display}; use hir::{Docs, Documentation, HasSource, HirDisplay}; use join_to_string::join; +use ra_ide_db::RootDatabase; use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; use std::convert::From; -use crate::{ - db, - display::{generic_parameters, where_predicates}, -}; +use crate::display::{generic_parameters, where_predicates}; #[derive(Debug)] pub enum CallableKind { @@ -48,13 +46,13 @@ impl FunctionSignature { self } - pub(crate) fn from_hir(db: &db::RootDatabase, function: hir::Function) -> Self { + pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self { let doc = function.docs(db); let ast_node = function.source(db).value; FunctionSignature::from(&ast_node).with_doc_opt(doc) } - pub(crate) fn from_struct(db: &db::RootDatabase, st: hir::Struct) -> Option { + pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option { let node: ast::StructDef = st.source(db).value; match node.kind() { ast::StructKind::Record(_) => return None, @@ -86,10 +84,7 @@ impl FunctionSignature { ) } - pub(crate) fn from_enum_variant( - db: &db::RootDatabase, - variant: hir::EnumVariant, - ) -> Option { + pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option { let node: ast::EnumVariant = variant.source(db).value; match node.kind() { ast::StructKind::Record(_) | ast::StructKind::Unit => return None, @@ -126,7 +121,7 @@ impl FunctionSignature { ) } - pub(crate) fn from_macro(db: &db::RootDatabase, macro_def: hir::MacroDef) -> Option { + pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option { let node: ast::MacroCall = macro_def.source(db).value; let params = vec![]; diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index b2af3479c..906aab1eb 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs @@ -3,6 +3,7 @@ use either::Either; use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource}; use ra_db::{FileId, SourceDatabase}; +use ra_ide_db::RootDatabase; use ra_syntax::{ ast::{self, DocCommentsOwner, NameOwner}, match_ast, AstNode, SmolStr, @@ -10,7 +11,7 @@ use ra_syntax::{ TextRange, }; -use crate::{db::RootDatabase, expand::original_range, FileSymbol}; +use crate::{expand::original_range, FileSymbol}; use super::short_label::ShortLabel; diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs index 831438c09..9f3aaa3a3 100644 --- a/crates/ra_ide/src/expand.rs +++ b/crates/ra_ide/src/expand.rs @@ -3,9 +3,10 @@ use std::iter::successors; use hir::{InFile, Origin}; use ra_db::FileId; +use ra_ide_db::RootDatabase; use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange}; -use crate::{db::RootDatabase, FileRange}; +use crate::FileRange; pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange { if let Some((range, Origin::Call)) = original_range_and_origin(db, node) { diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs index 0f7b6e875..af2783bef 100644 --- a/crates/ra_ide/src/expand_macro.rs +++ b/crates/ra_ide/src/expand_macro.rs @@ -1,14 +1,15 @@ //! This modules implements "expand macro" functionality in the IDE -use crate::{db::RootDatabase, FilePosition}; use hir::db::AstDatabase; use ra_db::SourceDatabase; -use rustc_hash::FxHashMap; - +use ra_ide_db::RootDatabase; use ra_syntax::{ algo::{find_node_at_offset, replace_descendants}, ast, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, WalkEvent, T, }; +use rustc_hash::FxHashMap; + +use crate::FilePosition; pub struct ExpandedMacro { pub name: String, @@ -185,7 +186,7 @@ fn some_thing() -> u32 { //- /lib.rs macro_rules! match_ast { (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; - + (match ($node:expr) { $( ast::$ast:ident($it:ident) => $res:block, )* _ => $catch_all:expr $(,)? @@ -193,7 +194,7 @@ fn some_thing() -> u32 { $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* { $catch_all } }}; - } + } fn main() { mat<|>ch_ast! { @@ -227,11 +228,11 @@ fn some_thing() -> u32 { r#" //- /lib.rs macro_rules! match_ast { - (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; + (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; (match ($node:expr) {}) => {{}}; - } + } - fn main() { + fn main() { let p = f(|it| { let res = mat<|>ch_ast! { match c {}}; Some(res) @@ -254,9 +255,9 @@ fn some_thing() -> u32 { } macro_rules! foo { () => {bar!()}; - } + } - fn main() { + fn main() { let res = fo<|>o!(); } "#, @@ -277,9 +278,9 @@ fn some_thing() -> u32 { } macro_rules! foo { () => {$crate::bar!()}; - } + } - fn main() { + fn main() { let res = fo<|>o!(); } "#, diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs index 930e0c4c2..726963a33 100644 --- a/crates/ra_ide/src/extend_selection.rs +++ b/crates/ra_ide/src/extend_selection.rs @@ -1,6 +1,10 @@ //! FIXME: write short doc here +use std::iter::successors; + +use hir::db::AstDatabase; use ra_db::SourceDatabase; +use ra_ide_db::RootDatabase; use ra_syntax::{ algo::find_covering_element, ast::{self, AstNode, AstToken}, @@ -9,9 +13,7 @@ use ra_syntax::{ SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T, }; -use crate::{db::RootDatabase, expand::descend_into_macros, FileId, FileRange}; -use hir::db::AstDatabase; -use std::iter::successors; +use crate::{expand::descend_into_macros, FileId, FileRange}; pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { let src = db.parse(frange.file_id).tree(); @@ -512,8 +514,8 @@ fn bar(){} fn test_extend_trait_bounds_list_in_where_clause() { do_check( r#" -fn foo() - where +fn foo() + where R: req::Request + 'static, R::Params: DeserializeOwned<|> + panic::UnwindSafe + 'static, R::Result: Serialize + 'static, diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index b67e32626..e9329a72c 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -1,6 +1,7 @@ //! FIXME: write short doc here use hir::{db::AstDatabase, InFile, SourceBinder}; +use ra_ide_db::{symbol_index, RootDatabase}; use ra_syntax::{ ast::{self, DocCommentsOwner}, match_ast, AstNode, @@ -9,10 +10,8 @@ use ra_syntax::{ }; use crate::{ - db::RootDatabase, display::{ShortLabel, ToNav}, expand::descend_into_macros, - ide_db::symbol_index, references::{classify_name_ref, NameKind::*}, FilePosition, NavigationTarget, RangeInfo, }; diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs index ce8b6c72a..11ad6d137 100644 --- a/crates/ra_ide/src/goto_type_definition.rs +++ b/crates/ra_ide/src/goto_type_definition.rs @@ -1,11 +1,11 @@ //! FIXME: write short doc here use hir::db::AstDatabase; +use ra_ide_db::RootDatabase; use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; use crate::{ - db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, - RangeInfo, + display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, RangeInfo, }; pub(crate) fn goto_type_definition( diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 6661e5cb2..315b88190 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -2,6 +2,7 @@ use hir::{db::AstDatabase, Adt, HasSource, HirDisplay, SourceBinder}; use ra_db::SourceDatabase; +use ra_ide_db::RootDatabase; use ra_syntax::{ algo::find_covering_element, ast::{self, DocCommentsOwner}, @@ -11,7 +12,6 @@ use ra_syntax::{ }; use crate::{ - db::RootDatabase, display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, expand::descend_into_macros, references::{classify_name, classify_name_ref, NameKind, NameKind::*}, diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/impls.rs index 9834025d3..64a2dadc8 100644 --- a/crates/ra_ide/src/impls.rs +++ b/crates/ra_ide/src/impls.rs @@ -2,9 +2,10 @@ use hir::{Crate, ImplBlock, SourceBinder}; use ra_db::SourceDatabase; +use ra_ide_db::RootDatabase; use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; -use crate::{db::RootDatabase, display::ToNav, FilePosition, NavigationTarget, RangeInfo}; +use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; pub(crate) fn goto_implementation( db: &RootDatabase, diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs index 9e5e6cadf..cfd58aafe 100644 --- a/crates/ra_ide/src/imports_locator.rs +++ b/crates/ra_ide/src/imports_locator.rs @@ -1,17 +1,20 @@ //! This module contains an import search funcionality that is provided to the ra_assists module. //! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. -use crate::{ - db::RootDatabase, - ide_db::symbol_index::{self, FileSymbol}, - references::{classify_name, NameDefinition, NameKind}, - Query, -}; use hir::{db::HirDatabase, ModuleDef, SourceBinder}; use ra_assists::ImportsLocator; +use ra_ide_db::{ + symbol_index::{self, FileSymbol}, + RootDatabase, +}; use ra_prof::profile; use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; +use crate::{ + references::{classify_name, NameDefinition, NameKind}, + Query, +}; + pub(crate) struct ImportsLocatorIde<'a> { source_binder: SourceBinder<'a, RootDatabase>, } diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index de447a5aa..6b0d3d996 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -2,13 +2,14 @@ use hir::{HirDisplay, SourceAnalyzer, SourceBinder}; use once_cell::unsync::Lazy; +use ra_ide_db::RootDatabase; use ra_prof::profile; use ra_syntax::{ ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, }; -use crate::{db::RootDatabase, FileId, FunctionSignature}; +use crate::{FileId, FunctionSignature}; #[derive(Debug, PartialEq, Eq)] pub enum InlayKind { diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 1527b27d4..013b960c1 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -10,11 +10,6 @@ // For proving that RootDatabase is RefUnwindSafe. #![recursion_limit = "128"] -mod ide_db { - pub use ra_ide_db::*; -} - -mod db; pub mod mock_analysis; mod source_change; @@ -56,13 +51,13 @@ use ra_db::{ salsa::{self, ParallelDatabase}, CheckCanceled, Env, FileLoader, SourceDatabase, }; +use ra_ide_db::{ + symbol_index::{self, FileSymbol}, + LineIndexDatabase, +}; use ra_syntax::{SourceFile, TextRange, TextUnit}; -use crate::{ - db::LineIndexDatabase, - display::ToNav, - ide_db::symbol_index::{self, FileSymbol}, -}; +use crate::display::ToNav; pub use crate::{ assists::{Assist, AssistId}, @@ -73,13 +68,6 @@ pub use crate::{ expand_macro::ExpandedMacro, folding_ranges::{Fold, FoldKind}, hover::HoverResult, - ide_db::{ - change::{AnalysisChange, LibraryData}, - feature_flags::FeatureFlags, - line_index::{LineCol, LineIndex}, - line_index_utils::translate_offset_with_edit, - symbol_index::Query, - }, inlay_hints::{InlayHint, InlayKind}, references::{ Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, @@ -93,6 +81,14 @@ pub use hir::Documentation; pub use ra_db::{ Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, }; +pub use ra_ide_db::{ + change::{AnalysisChange, LibraryData}, + feature_flags::FeatureFlags, + line_index::{LineCol, LineIndex}, + line_index_utils::translate_offset_with_edit, + symbol_index::Query, + RootDatabase, +}; pub type Cancelable = Result; @@ -128,7 +124,7 @@ pub struct CallInfo { /// `AnalysisHost` stores the current state of the world. #[derive(Debug)] pub struct AnalysisHost { - db: db::RootDatabase, + db: RootDatabase, } impl Default for AnalysisHost { @@ -139,7 +135,7 @@ impl Default for AnalysisHost { impl AnalysisHost { pub fn new(lru_capcity: Option, feature_flags: FeatureFlags) -> AnalysisHost { - AnalysisHost { db: db::RootDatabase::new(lru_capcity, feature_flags) } + AnalysisHost { db: RootDatabase::new(lru_capcity, feature_flags) } } /// Returns a snapshot of the current state, which you can query for /// semantic information. @@ -189,7 +185,7 @@ impl AnalysisHost { /// `Analysis` are canceled (most method return `Err(Canceled)`). #[derive(Debug)] pub struct Analysis { - db: salsa::Snapshot, + db: salsa::Snapshot, } // As a general design guideline, `Analysis` API are intended to be independent @@ -470,7 +466,7 @@ impl Analysis { } /// Performs an operation on that may be Canceled. - fn with_db T + std::panic::UnwindSafe, T>( + fn with_db T + std::panic::UnwindSafe, T>( &self, f: F, ) -> Cancelable { diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index 2dbccfc3b..e0332da88 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs @@ -1,12 +1,13 @@ //! FIXME: write short doc here use ra_db::{CrateId, FileId, FilePosition, SourceDatabase}; +use ra_ide_db::RootDatabase; use ra_syntax::{ algo::find_node_at_offset, ast::{self, AstNode}, }; -use crate::{db::RootDatabase, NavigationTarget}; +use crate::NavigationTarget; /// This returns `Vec` because a module may be included from several places. We /// don't handle this case yet though, so the Vec has length at most one. diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index ebded715d..b47f8bcd9 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -17,6 +17,7 @@ mod search_scope; use hir::{InFile, SourceBinder}; use once_cell::unsync::Lazy; use ra_db::{SourceDatabase, SourceDatabaseExt}; +use ra_ide_db::RootDatabase; use ra_prof::profile; use ra_syntax::{ algo::find_node_at_offset, @@ -24,9 +25,7 @@ use ra_syntax::{ match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, }; -use crate::{ - db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, -}; +use crate::{display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; pub(crate) use self::{ classify::{classify_name, classify_name_ref}, diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs index 46cba30a3..758ea4e8b 100644 --- a/crates/ra_ide/src/references/classify.rs +++ b/crates/ra_ide/src/references/classify.rs @@ -9,7 +9,7 @@ use super::{ name_definition::{from_assoc_item, from_module_def, from_struct_field}, NameDefinition, NameKind, }; -use crate::db::RootDatabase; +use ra_ide_db::RootDatabase; pub(crate) fn classify_name( sb: &mut SourceBinder, diff --git a/crates/ra_ide/src/references/name_definition.rs b/crates/ra_ide/src/references/name_definition.rs index 1e4226ab9..71565e6d3 100644 --- a/crates/ra_ide/src/references/name_definition.rs +++ b/crates/ra_ide/src/references/name_definition.rs @@ -9,7 +9,7 @@ use hir::{ }; use ra_syntax::{ast, ast::VisibilityOwner}; -use crate::db::RootDatabase; +use ra_ide_db::RootDatabase; #[derive(Debug, PartialEq, Eq)] pub enum NameKind { diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 9a84c1c88..08e77c01f 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs @@ -2,14 +2,14 @@ use hir::ModuleSource; use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; +use ra_ide_db::RootDatabase; use ra_syntax::{ algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode, }; use ra_text_edit::TextEdit; use crate::{ - db::RootDatabase, FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange, - SourceFileEdit, TextRange, + FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange, SourceFileEdit, TextRange, }; use super::find_all_refs; diff --git a/crates/ra_ide/src/references/search_scope.rs b/crates/ra_ide/src/references/search_scope.rs index f8211a746..97c65c2cd 100644 --- a/crates/ra_ide/src/references/search_scope.rs +++ b/crates/ra_ide/src/references/search_scope.rs @@ -10,7 +10,7 @@ use ra_prof::profile; use ra_syntax::{AstNode, TextRange}; use rustc_hash::FxHashMap; -use crate::db::RootDatabase; +use ra_ide_db::RootDatabase; use super::{NameDefinition, NameKind}; diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 8622dd956..b6b0c70f9 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -3,12 +3,13 @@ use hir::InFile; use itertools::Itertools; use ra_db::SourceDatabase; +use ra_ide_db::RootDatabase; use ra_syntax::{ ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, match_ast, SyntaxNode, TextRange, }; -use crate::{db::RootDatabase, FileId}; +use crate::FileId; #[derive(Debug)] pub struct Runnable { diff --git a/crates/ra_ide/src/status.rs b/crates/ra_ide/src/status.rs index 538312086..30eb5c995 100644 --- a/crates/ra_ide/src/status.rs +++ b/crates/ra_ide/src/status.rs @@ -10,14 +10,14 @@ use ra_db::{ }, FileTextQuery, SourceRootId, }; +use ra_ide_db::{ + symbol_index::{LibrarySymbolsQuery, SymbolIndex}, + RootDatabase, +}; use ra_prof::{memory_usage, Bytes}; use ra_syntax::{ast, Parse, SyntaxNode}; -use crate::{ - db::RootDatabase, - ide_db::symbol_index::{LibrarySymbolsQuery, SymbolIndex}, - FileId, -}; +use crate::FileId; fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { db.query(ra_db::ParseQuery).entries::() diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 530b984fc..c5d249fe8 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -4,6 +4,7 @@ use rustc_hash::FxHashMap; use hir::{HirFileId, InFile, Name, SourceAnalyzer, SourceBinder}; use ra_db::SourceDatabase; +use ra_ide_db::RootDatabase; use ra_prof::profile; use ra_syntax::{ ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, TextRange, @@ -11,7 +12,6 @@ use ra_syntax::{ }; use crate::{ - db::RootDatabase, expand::descend_into_macros_with_analyzer, references::{ classify_name, classify_name_ref, diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs index 4d0f0fc47..55966daf3 100644 --- a/crates/ra_ide/src/syntax_tree.rs +++ b/crates/ra_ide/src/syntax_tree.rs @@ -1,7 +1,7 @@ //! FIXME: write short doc here -use crate::db::RootDatabase; use ra_db::SourceDatabase; +use ra_ide_db::RootDatabase; use ra_syntax::{ algo, AstNode, NodeOrToken, SourceFile, SyntaxKind::{RAW_STRING, STRING}, diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index 21e5be9b3..e5d1779fd 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs @@ -15,6 +15,7 @@ use ra_db::{FilePosition, SourceDatabase}; use ra_fmt::leading_indent; +use ra_ide_db::RootDatabase; use ra_syntax::{ algo::find_node_at_offset, ast::{self, AstToken}, @@ -24,7 +25,7 @@ use ra_syntax::{ }; use ra_text_edit::TextEdit; -use crate::{db::RootDatabase, source_change::SingleFileChange, SourceChange, SourceFileEdit}; +use crate::{source_change::SingleFileChange, SourceChange, SourceFileEdit}; pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option { let parse = db.parse(position.file_id); -- cgit v1.2.3 From 8a39519e1cef6a11a418692a8ca1dc5131a80974 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 14:43:46 +0100 Subject: Cleanup --- crates/ra_ide/src/lib.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 013b960c1..5fb111a90 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -479,3 +479,77 @@ fn analysis_is_send() { fn is_send() {} is_send::(); } + +#[cfg(test)] +mod tests { + use crate::{display::NavigationTarget, mock_analysis::single_file, Query}; + use ra_syntax::{ + SmolStr, + SyntaxKind::{FN_DEF, STRUCT_DEF}, + }; + + #[test] + fn test_world_symbols_with_no_container() { + let code = r#" + enum FooInner { } + "#; + + let mut symbols = get_symbols_matching(code, "FooInner"); + + let s = symbols.pop().unwrap(); + + assert_eq!(s.name(), "FooInner"); + assert!(s.container_name().is_none()); + } + + #[test] + fn test_world_symbols_include_container_name() { + let code = r#" +fn foo() { + enum FooInner { } +} + "#; + + let mut symbols = get_symbols_matching(code, "FooInner"); + + let s = symbols.pop().unwrap(); + + assert_eq!(s.name(), "FooInner"); + assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); + + let code = r#" +mod foo { + struct FooInner; +} + "#; + + let mut symbols = get_symbols_matching(code, "FooInner"); + + let s = symbols.pop().unwrap(); + + assert_eq!(s.name(), "FooInner"); + assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); + } + + #[test] + fn test_world_symbols_are_case_sensitive() { + let code = r#" +fn foo() {} + +struct Foo; + "#; + + let symbols = get_symbols_matching(code, "Foo"); + + let fn_match = symbols.iter().find(|s| s.name() == "foo").map(|s| s.kind()); + let struct_match = symbols.iter().find(|s| s.name() == "Foo").map(|s| s.kind()); + + assert_eq!(fn_match, Some(FN_DEF)); + assert_eq!(struct_match, Some(STRUCT_DEF)); + } + + fn get_symbols_matching(text: &str, query: &str) -> Vec { + let (analysis, _) = single_file(text); + analysis.symbol_search(Query::new(query.into())).unwrap() + } +} -- cgit v1.2.3 From 832dfae25024b8e6da097bfbf99a6b749e5e0ad0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 15:36:32 +0100 Subject: Tweak goto parent module --- crates/ra_ide/src/marks.rs | 1 + crates/ra_ide/src/parent_module.rs | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs index 077a44473..5bf4d2062 100644 --- a/crates/ra_ide/src/marks.rs +++ b/crates/ra_ide/src/marks.rs @@ -11,4 +11,5 @@ test_utils::marks!( call_info_bad_offset dont_complete_current_use dont_complete_primitive_in_use + test_resolve_parent_module_on_module_decl ); diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index e0332da88..af14d6ab3 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs @@ -6,6 +6,7 @@ use ra_syntax::{ algo::find_node_at_offset, ast::{self, AstNode}, }; +use test_utils::tested_by; use crate::NavigationTarget; @@ -14,7 +15,21 @@ use crate::NavigationTarget; pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec { let mut sb = hir::SourceBinder::new(db); let parse = db.parse(position.file_id); - let module = match find_node_at_offset::(parse.tree().syntax(), position.offset) { + + let mut module = find_node_at_offset::(parse.tree().syntax(), position.offset); + + // If cursor is literally on `mod foo`, go to the grandpa. + if let Some(m) = &module { + if !m + .item_list() + .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset)) + { + tested_by!(test_resolve_parent_module_on_module_decl); + module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); + } + } + + let module = match module { Some(module) => sb.to_def(hir::InFile::new(position.file_id.into(), module)), None => sb.to_module_def(position.file_id), }; @@ -41,6 +56,7 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec { mod tests { use ra_cfg::CfgOptions; use ra_db::Env; + use test_utils::covers; use crate::{ mock_analysis::{analysis_and_position, MockAnalysis}, @@ -62,6 +78,25 @@ mod tests { nav.assert_match("foo MODULE FileId(1) [0; 8)"); } + #[test] + fn test_resolve_parent_module_on_module_decl() { + covers!(test_resolve_parent_module_on_module_decl); + let (analysis, pos) = analysis_and_position( + " + //- /lib.rs + mod foo; + + //- /foo.rs + mod <|>bar; + + //- /foo/bar.rs + // empty + ", + ); + let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); + nav.assert_match("foo MODULE FileId(1) [0; 8)"); + } + #[test] fn test_resolve_parent_module_for_inline() { let (analysis, pos) = analysis_and_position( -- cgit v1.2.3 From f8dde21fe9ccceea0b3dea83d646a45a409a0e3f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 16:00:39 +0100 Subject: Simplify --- crates/ra_ide/src/imports_locator.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs index cfd58aafe..23bf43b07 100644 --- a/crates/ra_ide/src/imports_locator.rs +++ b/crates/ra_ide/src/imports_locator.rs @@ -11,7 +11,7 @@ use ra_prof::profile; use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; use crate::{ - references::{classify_name, NameDefinition, NameKind}, + references::{classify_name, NameKind}, Query, }; @@ -28,7 +28,7 @@ impl<'a> ImportsLocatorIde<'a> { &mut self, db: &impl HirDatabase, import_candidate: &FileSymbol, - ) -> Option { + ) -> Option { let _p = profile("get_name_definition"); let file_id = import_candidate.file_id.into(); let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?); @@ -41,6 +41,7 @@ impl<'a> ImportsLocatorIde<'a> { &mut self.source_binder, hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? }, ) + .map(|it| it.kind) } } @@ -67,7 +68,7 @@ impl ImportsLocator for ImportsLocatorIde<'_> { .into_iter() .chain(lib_results.into_iter()) .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate)) - .filter_map(|name_definition_to_import| match name_definition_to_import.kind { + .filter_map(|name_definition_to_import| match name_definition_to_import { NameKind::Def(module_def) => Some(module_def), _ => None, }) -- cgit v1.2.3 From 271017e6bf5b5f46464d09db7fc869c92998fc80 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 16:23:28 +0100 Subject: Move NameKind up --- crates/ra_ide/src/imports_locator.rs | 6 +- crates/ra_ide/src/references.rs | 5 +- crates/ra_ide/src/references/classify.rs | 112 +----------------------- crates/ra_ide/src/references/name_definition.rs | 85 ------------------ crates/ra_ide/src/references/search_scope.rs | 102 +++++++++++---------- 5 files changed, 57 insertions(+), 253 deletions(-) delete mode 100644 crates/ra_ide/src/references/name_definition.rs (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs index 23bf43b07..0dca0c86c 100644 --- a/crates/ra_ide/src/imports_locator.rs +++ b/crates/ra_ide/src/imports_locator.rs @@ -4,16 +4,14 @@ use hir::{db::HirDatabase, ModuleDef, SourceBinder}; use ra_assists::ImportsLocator; use ra_ide_db::{ + defs::NameKind, symbol_index::{self, FileSymbol}, RootDatabase, }; use ra_prof::profile; use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; -use crate::{ - references::{classify_name, NameKind}, - Query, -}; +use crate::{references::classify_name, Query}; pub(crate) struct ImportsLocatorIde<'a> { source_binder: SourceBinder<'a, RootDatabase>, diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index b47f8bcd9..c215040f4 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -10,7 +10,6 @@ //! resolved to the search element definition, we get a reference. mod classify; -mod name_definition; mod rename; mod search_scope; @@ -29,9 +28,9 @@ use crate::{display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo pub(crate) use self::{ classify::{classify_name, classify_name_ref}, - name_definition::{NameDefinition, NameKind}, rename::rename, }; +pub(crate) use ra_ide_db::defs::{NameDefinition, NameKind}; pub use self::search_scope::SearchScope; @@ -137,7 +136,7 @@ pub(crate) fn find_all_refs( }; let search_scope = { - let base = def.search_scope(db); + let base = SearchScope::for_def(&def, db); match search_scope { None => base, Some(scope) => base.intersection(&scope), diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs index 758ea4e8b..0326fd379 100644 --- a/crates/ra_ide/src/references/classify.rs +++ b/crates/ra_ide/src/references/classify.rs @@ -2,119 +2,13 @@ use hir::{InFile, PathResolution, SourceBinder}; use ra_prof::profile; -use ra_syntax::{ast, match_ast, AstNode}; +use ra_syntax::{ast, AstNode}; use test_utils::tested_by; -use super::{ - name_definition::{from_assoc_item, from_module_def, from_struct_field}, - NameDefinition, NameKind, -}; +use super::{NameDefinition, NameKind}; use ra_ide_db::RootDatabase; -pub(crate) fn classify_name( - sb: &mut SourceBinder, - name: InFile<&ast::Name>, -) -> Option { - let _p = profile("classify_name"); - let parent = name.value.syntax().parent()?; - - match_ast! { - match parent { - ast::BindPat(it) => { - let src = name.with_value(it); - let local = sb.to_def(src)?; - Some(NameDefinition { - visibility: None, - container: local.module(sb.db), - kind: NameKind::Local(local), - }) - }, - ast::RecordFieldDef(it) => { - let src = name.with_value(it); - let field: hir::StructField = sb.to_def(src)?; - Some(from_struct_field(sb.db, field)) - }, - ast::Module(it) => { - let def = sb.to_def(name.with_value(it))?; - Some(from_module_def(sb.db, def.into(), None)) - }, - ast::StructDef(it) => { - let src = name.with_value(it); - let def: hir::Struct = sb.to_def(src)?; - Some(from_module_def(sb.db, def.into(), None)) - }, - ast::EnumDef(it) => { - let src = name.with_value(it); - let def: hir::Enum = sb.to_def(src)?; - Some(from_module_def(sb.db, def.into(), None)) - }, - ast::TraitDef(it) => { - let src = name.with_value(it); - let def: hir::Trait = sb.to_def(src)?; - Some(from_module_def(sb.db, def.into(), None)) - }, - ast::StaticDef(it) => { - let src = name.with_value(it); - let def: hir::Static = sb.to_def(src)?; - Some(from_module_def(sb.db, def.into(), None)) - }, - ast::EnumVariant(it) => { - let src = name.with_value(it); - let def: hir::EnumVariant = sb.to_def(src)?; - Some(from_module_def(sb.db, def.into(), None)) - }, - ast::FnDef(it) => { - let src = name.with_value(it); - let def: hir::Function = sb.to_def(src)?; - if parent.parent().and_then(ast::ItemList::cast).is_some() { - Some(from_assoc_item(sb.db, def.into())) - } else { - Some(from_module_def(sb.db, def.into(), None)) - } - }, - ast::ConstDef(it) => { - let src = name.with_value(it); - let def: hir::Const = sb.to_def(src)?; - if parent.parent().and_then(ast::ItemList::cast).is_some() { - Some(from_assoc_item(sb.db, def.into())) - } else { - Some(from_module_def(sb.db, def.into(), None)) - } - }, - ast::TypeAliasDef(it) => { - let src = name.with_value(it); - let def: hir::TypeAlias = sb.to_def(src)?; - if parent.parent().and_then(ast::ItemList::cast).is_some() { - Some(from_assoc_item(sb.db, def.into())) - } else { - Some(from_module_def(sb.db, def.into(), None)) - } - }, - ast::MacroCall(it) => { - let src = name.with_value(it); - let def = sb.to_def(src.clone())?; - - let module = sb.to_module_def(src.file_id.original_file(sb.db))?; - - Some(NameDefinition { - visibility: None, - container: module, - kind: NameKind::Macro(def), - }) - }, - ast::TypeParam(it) => { - let src = name.with_value(it); - let def = sb.to_def(src)?; - Some(NameDefinition { - visibility: None, - container: def.module(sb.db), - kind: NameKind::TypeParam(def), - }) - }, - _ => None, - } - } -} +pub use ra_ide_db::defs::{classify_name, from_assoc_item, from_module_def, from_struct_field}; pub(crate) fn classify_name_ref( sb: &mut SourceBinder, diff --git a/crates/ra_ide/src/references/name_definition.rs b/crates/ra_ide/src/references/name_definition.rs deleted file mode 100644 index 71565e6d3..000000000 --- a/crates/ra_ide/src/references/name_definition.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! `NameDefinition` keeps information about the element we want to search references for. -//! The element is represented by `NameKind`. It's located inside some `container` and -//! has a `visibility`, which defines a search scope. -//! Note that the reference search is possible for not all of the classified items. - -use hir::{ - Adt, AssocItem, HasSource, ImplBlock, Local, MacroDef, Module, ModuleDef, StructField, - TypeParam, VariantDef, -}; -use ra_syntax::{ast, ast::VisibilityOwner}; - -use ra_ide_db::RootDatabase; - -#[derive(Debug, PartialEq, Eq)] -pub enum NameKind { - Macro(MacroDef), - Field(StructField), - AssocItem(AssocItem), - Def(ModuleDef), - SelfType(ImplBlock), - Local(Local), - TypeParam(TypeParam), -} - -#[derive(PartialEq, Eq)] -pub(crate) struct NameDefinition { - pub visibility: Option, - /// FIXME: this doesn't really make sense. For example, builtin types don't - /// really have a module. - pub container: Module, - pub kind: NameKind, -} - -pub(super) fn from_assoc_item(db: &RootDatabase, item: AssocItem) -> NameDefinition { - let container = item.module(db); - let visibility = match item { - AssocItem::Function(f) => f.source(db).value.visibility(), - AssocItem::Const(c) => c.source(db).value.visibility(), - AssocItem::TypeAlias(a) => a.source(db).value.visibility(), - }; - let kind = NameKind::AssocItem(item); - NameDefinition { kind, container, visibility } -} - -pub(super) fn from_struct_field(db: &RootDatabase, field: StructField) -> NameDefinition { - let kind = NameKind::Field(field); - let parent = field.parent_def(db); - let container = parent.module(db); - let visibility = match parent { - VariantDef::Struct(s) => s.source(db).value.visibility(), - VariantDef::Union(e) => e.source(db).value.visibility(), - VariantDef::EnumVariant(e) => e.source(db).value.parent_enum().visibility(), - }; - NameDefinition { kind, container, visibility } -} - -pub(super) fn from_module_def( - db: &RootDatabase, - def: ModuleDef, - module: Option, -) -> NameDefinition { - let kind = NameKind::Def(def); - let (container, visibility) = match def { - ModuleDef::Module(it) => { - let container = it.parent(db).or_else(|| Some(it)).unwrap(); - let visibility = it.declaration_source(db).and_then(|s| s.value.visibility()); - (container, visibility) - } - ModuleDef::EnumVariant(it) => { - let container = it.module(db); - let visibility = it.source(db).value.parent_enum().visibility(); - (container, visibility) - } - ModuleDef::Function(it) => (it.module(db), it.source(db).value.visibility()), - ModuleDef::Const(it) => (it.module(db), it.source(db).value.visibility()), - ModuleDef::Static(it) => (it.module(db), it.source(db).value.visibility()), - ModuleDef::Trait(it) => (it.module(db), it.source(db).value.visibility()), - ModuleDef::TypeAlias(it) => (it.module(db), it.source(db).value.visibility()), - ModuleDef::Adt(Adt::Struct(it)) => (it.module(db), it.source(db).value.visibility()), - ModuleDef::Adt(Adt::Union(it)) => (it.module(db), it.source(db).value.visibility()), - ModuleDef::Adt(Adt::Enum(it)) => (it.module(db), it.source(db).value.visibility()), - ModuleDef::BuiltinType(..) => (module.unwrap(), None), - }; - NameDefinition { kind, container, visibility } -} diff --git a/crates/ra_ide/src/references/search_scope.rs b/crates/ra_ide/src/references/search_scope.rs index 97c65c2cd..279f57be0 100644 --- a/crates/ra_ide/src/references/search_scope.rs +++ b/crates/ra_ide/src/references/search_scope.rs @@ -19,59 +19,13 @@ pub struct SearchScope { } impl SearchScope { - fn new(entries: FxHashMap>) -> SearchScope { - SearchScope { entries } - } - pub fn single_file(file: FileId) -> SearchScope { - SearchScope::new(std::iter::once((file, None)).collect()) - } - pub(crate) fn intersection(&self, other: &SearchScope) -> SearchScope { - let (mut small, mut large) = (&self.entries, &other.entries); - if small.len() > large.len() { - mem::swap(&mut small, &mut large) - } - - let res = small - .iter() - .filter_map(|(file_id, r1)| { - let r2 = large.get(file_id)?; - let r = intersect_ranges(*r1, *r2)?; - Some((*file_id, r)) - }) - .collect(); - return SearchScope::new(res); - - fn intersect_ranges( - r1: Option, - r2: Option, - ) -> Option> { - match (r1, r2) { - (None, r) | (r, None) => Some(r), - (Some(r1), Some(r2)) => { - let r = r1.intersection(&r2)?; - Some(Some(r)) - } - } - } - } -} - -impl IntoIterator for SearchScope { - type Item = (FileId, Option); - type IntoIter = std::collections::hash_map::IntoIter>; - fn into_iter(self) -> Self::IntoIter { - self.entries.into_iter() - } -} - -impl NameDefinition { - pub(crate) fn search_scope(&self, db: &RootDatabase) -> SearchScope { + pub(crate) fn for_def(def: &NameDefinition, db: &RootDatabase) -> SearchScope { let _p = profile("search_scope"); - let module_src = self.container.definition_source(db); + let module_src = def.container.definition_source(db); let file_id = module_src.file_id.original_file(db); - if let NameKind::Local(var) = self.kind { + if let NameKind::Local(var) = def.kind { let range = match var.parent(db) { DefWithBody::Function(f) => f.source(db).value.syntax().text_range(), DefWithBody::Const(c) => c.source(db).value.syntax().text_range(), @@ -82,10 +36,10 @@ impl NameDefinition { return SearchScope::new(res); } - let vis = self.visibility.as_ref().map(|v| v.syntax().to_string()).unwrap_or_default(); + let vis = def.visibility.as_ref().map(|v| v.syntax().to_string()).unwrap_or_default(); if vis.as_str() == "pub(super)" { - if let Some(parent_module) = self.container.parent(db) { + if let Some(parent_module) = def.container.parent(db) { let mut res = FxHashMap::default(); let parent_src = parent_module.definition_source(db); let file_id = parent_src.file_id.original_file(db); @@ -118,7 +72,7 @@ impl NameDefinition { return SearchScope::new(res); } if vis.as_str() == "pub" { - let krate = self.container.krate(); + let krate = def.container.krate(); for rev_dep in krate.reverse_dependencies(db) { let root_file = rev_dep.root_file(db); let source_root_id = db.file_source_root(root_file); @@ -137,4 +91,48 @@ impl NameDefinition { res.insert(file_id, range); SearchScope::new(res) } + + fn new(entries: FxHashMap>) -> SearchScope { + SearchScope { entries } + } + pub fn single_file(file: FileId) -> SearchScope { + SearchScope::new(std::iter::once((file, None)).collect()) + } + pub(crate) fn intersection(&self, other: &SearchScope) -> SearchScope { + let (mut small, mut large) = (&self.entries, &other.entries); + if small.len() > large.len() { + mem::swap(&mut small, &mut large) + } + + let res = small + .iter() + .filter_map(|(file_id, r1)| { + let r2 = large.get(file_id)?; + let r = intersect_ranges(*r1, *r2)?; + Some((*file_id, r)) + }) + .collect(); + return SearchScope::new(res); + + fn intersect_ranges( + r1: Option, + r2: Option, + ) -> Option> { + match (r1, r2) { + (None, r) | (r, None) => Some(r), + (Some(r1), Some(r2)) => { + let r = r1.intersection(&r2)?; + Some(Some(r)) + } + } + } + } +} + +impl IntoIterator for SearchScope { + type Item = (FileId, Option); + type IntoIter = std::collections::hash_map::IntoIter>; + fn into_iter(self) -> Self::IntoIter { + self.entries.into_iter() + } } -- cgit v1.2.3 From dfbe96750b69fc69e64f3a6094e2c1d574ab42fa Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 16:26:43 +0100 Subject: Move imports locator to ide_db --- crates/ra_ide/src/assists.rs | 4 +- crates/ra_ide/src/imports_locator.rs | 75 ------------------------------------ crates/ra_ide/src/lib.rs | 1 - 3 files changed, 2 insertions(+), 78 deletions(-) delete mode 100644 crates/ra_ide/src/imports_locator.rs (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index f26047570..4a7d8cfa9 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs @@ -3,9 +3,9 @@ use either::Either; use ra_assists::{AssistAction, AssistLabel}; use ra_db::{FilePosition, FileRange}; -use ra_ide_db::RootDatabase; +use ra_ide_db::{imports_locator::ImportsLocatorIde, RootDatabase}; -use crate::{imports_locator::ImportsLocatorIde, FileId, SourceChange, SourceFileEdit}; +use crate::{FileId, SourceChange, SourceFileEdit}; pub use ra_assists::AssistId; diff --git a/crates/ra_ide/src/imports_locator.rs b/crates/ra_ide/src/imports_locator.rs deleted file mode 100644 index 0dca0c86c..000000000 --- a/crates/ra_ide/src/imports_locator.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! This module contains an import search funcionality that is provided to the ra_assists module. -//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. - -use hir::{db::HirDatabase, ModuleDef, SourceBinder}; -use ra_assists::ImportsLocator; -use ra_ide_db::{ - defs::NameKind, - symbol_index::{self, FileSymbol}, - RootDatabase, -}; -use ra_prof::profile; -use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; - -use crate::{references::classify_name, Query}; - -pub(crate) struct ImportsLocatorIde<'a> { - source_binder: SourceBinder<'a, RootDatabase>, -} - -impl<'a> ImportsLocatorIde<'a> { - pub(crate) fn new(db: &'a RootDatabase) -> Self { - Self { source_binder: SourceBinder::new(db) } - } - - fn get_name_definition( - &mut self, - db: &impl HirDatabase, - import_candidate: &FileSymbol, - ) -> Option { - let _p = profile("get_name_definition"); - let file_id = import_candidate.file_id.into(); - let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?); - let candidate_name_node = if candidate_node.kind() != NAME { - candidate_node.children().find(|it| it.kind() == NAME)? - } else { - candidate_node - }; - classify_name( - &mut self.source_binder, - hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? }, - ) - .map(|it| it.kind) - } -} - -impl ImportsLocator for ImportsLocatorIde<'_> { - fn find_imports(&mut self, name_to_import: &str) -> Vec { - let _p = profile("search_for_imports"); - let db = self.source_binder.db; - - let project_results = { - let mut query = Query::new(name_to_import.to_string()); - query.exact(); - query.limit(40); - symbol_index::world_symbols(db, query) - }; - let lib_results = { - let mut query = Query::new(name_to_import.to_string()); - query.libs(); - query.exact(); - query.limit(40); - symbol_index::world_symbols(db, query) - }; - - project_results - .into_iter() - .chain(lib_results.into_iter()) - .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate)) - .filter_map(|name_definition_to_import| match name_definition_to_import { - NameKind::Def(module_def) => Some(module_def), - _ => None, - }) - .collect() - } -} diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 5fb111a90..689921f3f 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -26,7 +26,6 @@ mod syntax_highlighting; mod parent_module; mod references; mod impls; -mod imports_locator; mod assists; mod diagnostics; mod syntax_tree; -- cgit v1.2.3 From a173e31890c1eb03d9d4c123986baae4154cd4fa Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 16:40:28 +0100 Subject: Make assists use ImportsLocator directly --- crates/ra_ide/src/assists.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index 4a7d8cfa9..6ee617e79 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs @@ -3,7 +3,7 @@ use either::Either; use ra_assists::{AssistAction, AssistLabel}; use ra_db::{FilePosition, FileRange}; -use ra_ide_db::{imports_locator::ImportsLocatorIde, RootDatabase}; +use ra_ide_db::RootDatabase; use crate::{FileId, SourceChange, SourceFileEdit}; @@ -17,7 +17,7 @@ pub struct Assist { } pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec { - ra_assists::assists_with_imports_locator(db, frange, ImportsLocatorIde::new(db)) + ra_assists::assists_with_imports_locator(db, frange) .into_iter() .map(|assist| { let file_id = frange.file_id; -- cgit v1.2.3 From ad204f7562747150c4f570d7ce648f2539530b76 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 17:17:51 +0100 Subject: Mostly remove ImoportLocator infra --- crates/ra_ide/src/assists.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index 6ee617e79..c3b2c638b 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs @@ -17,7 +17,7 @@ pub struct Assist { } pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec { - ra_assists::assists_with_imports_locator(db, frange) + ra_assists::assists(db, frange) .into_iter() .map(|assist| { let file_id = frange.file_id; -- cgit v1.2.3 From 7e73b7a5f8d594a85627786a13e76d9d70163770 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Feb 2020 18:46:11 +0100 Subject: Minor rename --- crates/ra_ide/src/assists.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index c3b2c638b..b60b1a60d 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs @@ -1,7 +1,7 @@ //! FIXME: write short doc here use either::Either; -use ra_assists::{AssistAction, AssistLabel}; +use ra_assists::{resolved_assists, AssistAction, AssistLabel}; use ra_db::{FilePosition, FileRange}; use ra_ide_db::RootDatabase; @@ -17,7 +17,7 @@ pub struct Assist { } pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec { - ra_assists::assists(db, frange) + resolved_assists(db, frange) .into_iter() .map(|assist| { let file_id = frange.file_id; -- cgit v1.2.3 From f55be75a17dab2ca23b34c45e7597fe19a5fc8e4 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 7 Feb 2020 12:51:12 +0100 Subject: Remove irrelevant distinction --- crates/ra_ide/src/goto_definition.rs | 1 - crates/ra_ide/src/hover.rs | 5 ----- crates/ra_ide/src/references.rs | 1 - crates/ra_ide/src/references/classify.rs | 28 ++++++++++++++++++---------- crates/ra_ide/src/syntax_highlighting.rs | 3 --- 5 files changed, 18 insertions(+), 20 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index e9329a72c..080cc302b 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -78,7 +78,6 @@ pub(crate) fn reference_definition( Some(Macro(it)) => return Exact(it.to_nav(sb.db)), Some(Field(it)) => return Exact(it.to_nav(sb.db)), Some(TypeParam(it)) => return Exact(it.to_nav(sb.db)), - Some(AssocItem(it)) => return Exact(it.to_nav(sb.db)), Some(Local(it)) => return Exact(it.to_nav(sb.db)), Some(Def(def)) => match NavigationTarget::from_def(sb.db, def) { Some(nav) => return Exact(nav), diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 315b88190..ab3f513f2 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -105,11 +105,6 @@ fn hover_text_from_name_kind(db: &RootDatabase, name_kind: NameKind) -> Option None, } } - AssocItem(it) => match it { - hir::AssocItem::Function(it) => from_def_source(db, it), - hir::AssocItem::Const(it) => from_def_source(db, it), - hir::AssocItem::TypeAlias(it) => from_def_source(db, it), - }, Def(it) => match it { hir::ModuleDef::Module(it) => match it.definition_source(db).value { hir::ModuleSource::Module(it) => { diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index c215040f4..612ed84b7 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -128,7 +128,6 @@ pub(crate) fn find_all_refs( let declaration = match def.kind { NameKind::Macro(mac) => mac.to_nav(db), NameKind::Field(field) => field.to_nav(db), - NameKind::AssocItem(assoc) => assoc.to_nav(db), NameKind::Def(def) => NavigationTarget::from_def(db, def)?, NameKind::SelfType(imp) => imp.to_nav(db), NameKind::Local(local) => local.to_nav(db), diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs index 0326fd379..d0f03d8a8 100644 --- a/crates/ra_ide/src/references/classify.rs +++ b/crates/ra_ide/src/references/classify.rs @@ -8,7 +8,7 @@ use test_utils::tested_by; use super::{NameDefinition, NameKind}; use ra_ide_db::RootDatabase; -pub use ra_ide_db::defs::{classify_name, from_assoc_item, from_module_def, from_struct_field}; +pub use ra_ide_db::defs::{classify_name, from_module_def, from_struct_field}; pub(crate) fn classify_name_ref( sb: &mut SourceBinder, @@ -22,7 +22,7 @@ pub(crate) fn classify_name_ref( if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { tested_by!(goto_def_for_methods); if let Some(func) = analyzer.resolve_method_call(&method_call) { - return Some(from_assoc_item(sb.db, func.into())); + return Some(from_module_def(sb.db, func.into(), None)); } } @@ -57,27 +57,35 @@ pub(crate) fn classify_name_ref( let path = name_ref.value.syntax().ancestors().find_map(ast::Path::cast)?; let resolved = analyzer.resolve_path(sb.db, &path)?; - match resolved { - PathResolution::Def(def) => Some(from_module_def(sb.db, def, Some(container))), - PathResolution::AssocItem(item) => Some(from_assoc_item(sb.db, item)), + let res = match resolved { + PathResolution::Def(def) => from_module_def(sb.db, def, Some(container)), + PathResolution::AssocItem(item) => { + let def = match item { + hir::AssocItem::Function(it) => it.into(), + hir::AssocItem::Const(it) => it.into(), + hir::AssocItem::TypeAlias(it) => it.into(), + }; + from_module_def(sb.db, def, Some(container)) + } PathResolution::Local(local) => { let kind = NameKind::Local(local); let container = local.module(sb.db); - Some(NameDefinition { kind, container, visibility: None }) + NameDefinition { kind, container, visibility: None } } PathResolution::TypeParam(par) => { let kind = NameKind::TypeParam(par); let container = par.module(sb.db); - Some(NameDefinition { kind, container, visibility }) + NameDefinition { kind, container, visibility } } PathResolution::Macro(def) => { let kind = NameKind::Macro(def); - Some(NameDefinition { kind, container, visibility }) + NameDefinition { kind, container, visibility } } PathResolution::SelfType(impl_block) => { let kind = NameKind::SelfType(impl_block); let container = impl_block.module(sb.db); - Some(NameDefinition { kind, container, visibility }) + NameDefinition { kind, container, visibility } } - } + }; + Some(res) } diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index c5d249fe8..c970f5d34 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -321,9 +321,6 @@ fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str { match name_kind { Macro(_) => tags::MACRO, Field(_) => tags::FIELD, - AssocItem(hir::AssocItem::Function(_)) => tags::FUNCTION, - AssocItem(hir::AssocItem::Const(_)) => tags::CONSTANT, - AssocItem(hir::AssocItem::TypeAlias(_)) => tags::TYPE, Def(hir::ModuleDef::Module(_)) => tags::MODULE, Def(hir::ModuleDef::Function(_)) => tags::FUNCTION, Def(hir::ModuleDef::Adt(_)) => tags::TYPE, -- cgit v1.2.3 From ae70d072374f3c4d14abdccbe61661cf02b41b33 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 7 Feb 2020 14:26:59 +0100 Subject: Rename --- crates/ra_ide/src/goto_definition.rs | 4 ++-- crates/ra_ide/src/hover.rs | 4 ++-- crates/ra_ide/src/references.rs | 8 ++++---- crates/ra_ide/src/syntax_highlighting.rs | 20 ++++++++++---------- 4 files changed, 18 insertions(+), 18 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 080cc302b..de5551a4c 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -76,10 +76,10 @@ pub(crate) fn reference_definition( let name_kind = classify_name_ref(sb, name_ref).map(|d| d.kind); match name_kind { Some(Macro(it)) => return Exact(it.to_nav(sb.db)), - Some(Field(it)) => return Exact(it.to_nav(sb.db)), + Some(StructField(it)) => return Exact(it.to_nav(sb.db)), Some(TypeParam(it)) => return Exact(it.to_nav(sb.db)), Some(Local(it)) => return Exact(it.to_nav(sb.db)), - Some(Def(def)) => match NavigationTarget::from_def(sb.db, def) { + Some(ModuleDef(def)) => match NavigationTarget::from_def(sb.db, def) { Some(nav) => return Exact(nav), None => return Approximate(vec![]), }, diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index ab3f513f2..3f88bb260 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -98,14 +98,14 @@ fn hover_text_from_name_kind(db: &RootDatabase, name_kind: NameKind) -> Option { + StructField(it) => { let src = it.source(db); match src.value { hir::FieldSource::Named(it) => hover_text(it.doc_comment_text(), it.short_label()), _ => None, } } - Def(it) => match it { + ModuleDef(it) => match it { hir::ModuleDef::Module(it) => match it.definition_source(db).value { hir::ModuleSource::Module(it) => { hover_text(it.doc_comment_text(), it.short_label()) diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 612ed84b7..a6320bd2f 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -127,8 +127,8 @@ pub(crate) fn find_all_refs( let declaration = match def.kind { NameKind::Macro(mac) => mac.to_nav(db), - NameKind::Field(field) => field.to_nav(db), - NameKind::Def(def) => NavigationTarget::from_def(db, def)?, + NameKind::StructField(field) => field.to_nav(db), + NameKind::ModuleDef(def) => NavigationTarget::from_def(db, def)?, NameKind::SelfType(imp) => imp.to_nav(db), NameKind::Local(local) => local.to_nav(db), NameKind::TypeParam(_) => return None, @@ -239,7 +239,7 @@ fn decl_access( range: TextRange, ) -> Option { match kind { - NameKind::Local(_) | NameKind::Field(_) => {} + NameKind::Local(_) | NameKind::StructField(_) => {} _ => return None, }; @@ -259,7 +259,7 @@ fn decl_access( fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option { // Only Locals and Fields have accesses for now. match kind { - NameKind::Local(_) | NameKind::Field(_) => {} + NameKind::Local(_) | NameKind::StructField(_) => {} _ => return None, }; diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index c970f5d34..174e13595 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -320,16 +320,16 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str { match name_kind { Macro(_) => tags::MACRO, - Field(_) => tags::FIELD, - Def(hir::ModuleDef::Module(_)) => tags::MODULE, - Def(hir::ModuleDef::Function(_)) => tags::FUNCTION, - Def(hir::ModuleDef::Adt(_)) => tags::TYPE, - Def(hir::ModuleDef::EnumVariant(_)) => tags::CONSTANT, - Def(hir::ModuleDef::Const(_)) => tags::CONSTANT, - Def(hir::ModuleDef::Static(_)) => tags::CONSTANT, - Def(hir::ModuleDef::Trait(_)) => tags::TYPE, - Def(hir::ModuleDef::TypeAlias(_)) => tags::TYPE, - Def(hir::ModuleDef::BuiltinType(_)) => tags::TYPE_BUILTIN, + StructField(_) => tags::FIELD, + ModuleDef(hir::ModuleDef::Module(_)) => tags::MODULE, + ModuleDef(hir::ModuleDef::Function(_)) => tags::FUNCTION, + ModuleDef(hir::ModuleDef::Adt(_)) => tags::TYPE, + ModuleDef(hir::ModuleDef::EnumVariant(_)) => tags::CONSTANT, + ModuleDef(hir::ModuleDef::Const(_)) => tags::CONSTANT, + ModuleDef(hir::ModuleDef::Static(_)) => tags::CONSTANT, + ModuleDef(hir::ModuleDef::Trait(_)) => tags::TYPE, + ModuleDef(hir::ModuleDef::TypeAlias(_)) => tags::TYPE, + ModuleDef(hir::ModuleDef::BuiltinType(_)) => tags::TYPE_BUILTIN, SelfType(_) => tags::TYPE_SELF, TypeParam(_) => tags::TYPE_PARAM, Local(local) => { -- cgit v1.2.3 From 740a26b7d26a68cc46becda3cca39091e8da67fc Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 7 Feb 2020 23:35:34 +0200 Subject: Rename add import assist --- crates/ra_ide/src/completion/complete_scope.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 64b04ec2b..aaa9985d4 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -use ra_assists::auto_import_text_edit; +use ra_assists::insert_use_statement; use ra_syntax::{ast, AstNode, SmolStr}; use ra_text_edit::TextEditBuilder; use rustc_hash::FxHashMap; @@ -26,12 +26,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { let edit = { let mut builder = TextEditBuilder::default(); builder.replace(ctx.source_range(), name.to_string()); - auto_import_text_edit( - &ctx.token.parent(), - &ctx.token.parent(), - &path, - &mut builder, - ); + insert_use_statement(&ctx.token.parent(), &ctx.token.parent(), &path, &mut builder); builder.finish() }; -- cgit v1.2.3 From 409f8e1fb058748ddd11fa10389ac4f710ea4a4c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 9 Feb 2020 10:04:47 +0100 Subject: Remove hard-coded auto-import during completion We now have a real auto-import system, so we can do a proper thing. --- crates/ra_ide/src/completion/complete_scope.rs | 131 +------------------------ 1 file changed, 3 insertions(+), 128 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index aaa9985d4..e2ee86dd1 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs @@ -1,12 +1,6 @@ //! FIXME: write short doc here -use ra_assists::insert_use_statement; -use ra_syntax::{ast, AstNode, SmolStr}; -use ra_text_edit::TextEditBuilder; -use rustc_hash::FxHashMap; - -use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; -use hir::{ModPath, PathKind}; +use crate::completion::{CompletionContext, Completions}; pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { if !ctx.is_trivial_path { @@ -16,133 +10,14 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { acc.add_resolution(ctx, name.to_string(), &res) }); - - // auto-import - // We fetch ident from the original file, because we need to pre-filter auto-imports - if ast::NameRef::cast(ctx.token.parent()).is_some() { - let import_resolver = ImportResolver::new(); - let import_names = import_resolver.all_names(ctx.token.text()); - import_names.into_iter().for_each(|(name, path)| { - let edit = { - let mut builder = TextEditBuilder::default(); - builder.replace(ctx.source_range(), name.to_string()); - insert_use_statement(&ctx.token.parent(), &ctx.token.parent(), &path, &mut builder); - builder.finish() - }; - - // Hack: copied this check form conv.rs beacause auto import can produce edits - // that invalidate assert in conv_with. - if edit - .as_atoms() - .iter() - .filter(|atom| !ctx.source_range().is_subrange(&atom.delete)) - .all(|atom| ctx.source_range().intersection(&atom.delete).is_none()) - { - CompletionItem::new( - CompletionKind::Reference, - ctx.source_range(), - build_import_label(&name, &path), - ) - .text_edit(edit) - .add_to(acc); - } - }); - } -} - -fn build_import_label(name: &str, path: &ModPath) -> String { - let mut buf = String::with_capacity(64); - buf.push_str(name); - buf.push_str(" ("); - buf.push_str(&path.to_string()); - buf.push_str(")"); - buf -} - -#[derive(Debug, Clone, Default)] -pub(crate) struct ImportResolver { - // todo: use fst crate or something like that - dummy_names: Vec<(SmolStr, ModPath)>, -} - -impl ImportResolver { - pub(crate) fn new() -> Self { - use hir::name; - - let dummy_names = vec![ - ( - SmolStr::new("fmt"), - ModPath { kind: PathKind::Plain, segments: vec![name![std], name![fmt]] }, - ), - ( - SmolStr::new("io"), - ModPath { kind: PathKind::Plain, segments: vec![name![std], name![io]] }, - ), - ( - SmolStr::new("iter"), - ModPath { kind: PathKind::Plain, segments: vec![name![std], name![iter]] }, - ), - ( - SmolStr::new("hash"), - ModPath { kind: PathKind::Plain, segments: vec![name![std], name![hash]] }, - ), - ( - SmolStr::new("Debug"), - ModPath { - kind: PathKind::Plain, - segments: vec![name![std], name![fmt], name![Debug]], - }, - ), - ( - SmolStr::new("Display"), - ModPath { - kind: PathKind::Plain, - segments: vec![name![std], name![fmt], name![Display]], - }, - ), - ( - SmolStr::new("Hash"), - ModPath { - kind: PathKind::Plain, - segments: vec![name![std], name![hash], name![Hash]], - }, - ), - ( - SmolStr::new("Hasher"), - ModPath { - kind: PathKind::Plain, - segments: vec![name![std], name![hash], name![Hasher]], - }, - ), - ( - SmolStr::new("Iterator"), - ModPath { - kind: PathKind::Plain, - segments: vec![name![std], name![iter], name![Iterator]], - }, - ), - ]; - - ImportResolver { dummy_names } - } - - // Returns a map of importable items filtered by name. - // The map associates item name with its full path. - // todo: should return Resolutions - pub(crate) fn all_names(&self, name: &str) -> FxHashMap { - if name.len() > 1 { - self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect() - } else { - FxHashMap::default() - } - } } #[cfg(test)] mod tests { - use crate::completion::{do_completion, CompletionItem, CompletionKind}; use insta::assert_debug_snapshot; + use crate::completion::{do_completion, CompletionItem, CompletionKind}; + fn do_reference_completion(code: &str) -> Vec { do_completion(code, CompletionKind::Reference) } -- cgit v1.2.3 From 9769c5140c9c406a4cc880e698593a6c4bcc6826 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 9 Feb 2020 15:32:53 +0100 Subject: Simplify Assists interface Instead of building a physical tree structure, just "tag" related assists with the same group --- crates/ra_ide/src/assists.rs | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) (limited to 'crates/ra_ide/src') diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index b60b1a60d..40d56a4f7 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs @@ -1,6 +1,5 @@ //! FIXME: write short doc here -use either::Either; use ra_assists::{resolved_assists, AssistAction, AssistLabel}; use ra_db::{FilePosition, FileRange}; use ra_ide_db::RootDatabase; @@ -13,7 +12,8 @@ pub use ra_assists::AssistId; pub struct Assist { pub id: AssistId, pub label: String, - pub change_data: Either>, + pub group_label: Option, + pub source_change: SourceChange, } pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec { @@ -25,17 +25,8 @@ pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec { Assist { id: assist_label.id, label: assist_label.label.clone(), - change_data: match assist.action_data { - Either::Left(action) => { - Either::Left(action_to_edit(action, file_id, assist_label)) - } - Either::Right(actions) => Either::Right( - actions - .into_iter() - .map(|action| action_to_edit(action, file_id, assist_label)) - .collect(), - ), - }, + group_label: assist.group_label.map(|it| it.0), + source_change: action_to_edit(assist.action, file_id, assist_label), } }) .collect() @@ -47,9 +38,6 @@ fn action_to_edit( assist_label: &AssistLabel, ) -> SourceChange { let file_edit = SourceFileEdit { file_id, edit: action.edit }; - SourceChange::source_file_edit( - action.label.unwrap_or_else(|| assist_label.label.clone()), - file_edit, - ) - .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id })) + SourceChange::source_file_edit(assist_label.label.clone(), file_edit) + .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id })) } -- cgit v1.2.3