mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-16 01:38:13 +00:00
scope-based copmletions on original file
This commit is contained in:
parent
2136e75c0b
commit
ccca5aae43
5 changed files with 223 additions and 322 deletions
|
@ -1,10 +1,10 @@
|
||||||
mod completion_item;
|
mod completion_item;
|
||||||
mod reference_completion;
|
|
||||||
|
|
||||||
mod complete_fn_param;
|
mod complete_fn_param;
|
||||||
mod complete_keyword;
|
mod complete_keyword;
|
||||||
mod complete_snippet;
|
mod complete_snippet;
|
||||||
mod complete_path;
|
mod complete_path;
|
||||||
|
mod complete_scope;
|
||||||
|
|
||||||
use ra_editor::find_node_at_offset;
|
use ra_editor::find_node_at_offset;
|
||||||
use ra_text_edit::AtomTextEdit;
|
use ra_text_edit::AtomTextEdit;
|
||||||
|
@ -33,26 +33,16 @@ pub(crate) fn completions(
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
) -> Cancelable<Option<Completions>> {
|
) -> Cancelable<Option<Completions>> {
|
||||||
let original_file = db.source_file(position.file_id);
|
let original_file = db.source_file(position.file_id);
|
||||||
// Insert a fake ident to get a valid parse tree
|
let ctx = ctry!(SyntaxContext::new(db, &original_file, position)?);
|
||||||
let file = {
|
|
||||||
let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string());
|
|
||||||
original_file.reparse(&edit)
|
|
||||||
};
|
|
||||||
let module = ctry!(source_binder::module_from_position(db, position)?);
|
|
||||||
|
|
||||||
let mut acc = Completions::default();
|
let mut acc = Completions::default();
|
||||||
|
|
||||||
// First, let's try to complete a reference to some declaration.
|
|
||||||
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
|
|
||||||
reference_completion::completions(&mut acc, db, &module, &file, name_ref)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ctx = ctry!(SyntaxContext::new(db, &original_file, position)?);
|
|
||||||
complete_fn_param::complete_fn_param(&mut acc, &ctx);
|
complete_fn_param::complete_fn_param(&mut acc, &ctx);
|
||||||
complete_keyword::complete_expr_keyword(&mut acc, &ctx);
|
complete_keyword::complete_expr_keyword(&mut acc, &ctx);
|
||||||
complete_snippet::complete_expr_snippet(&mut acc, &ctx);
|
complete_snippet::complete_expr_snippet(&mut acc, &ctx);
|
||||||
complete_snippet::complete_item_snippet(&mut acc, &ctx);
|
complete_snippet::complete_item_snippet(&mut acc, &ctx);
|
||||||
complete_path::complete_path(&mut acc, &ctx)?;
|
complete_path::complete_path(&mut acc, &ctx)?;
|
||||||
|
complete_scope::complete_scope(&mut acc, &ctx)?;
|
||||||
|
|
||||||
Ok(Some(acc))
|
Ok(Some(acc))
|
||||||
}
|
}
|
||||||
|
@ -62,6 +52,7 @@ pub(crate) fn completions(
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct SyntaxContext<'a> {
|
pub(super) struct SyntaxContext<'a> {
|
||||||
db: &'a db::RootDatabase,
|
db: &'a db::RootDatabase,
|
||||||
|
offset: TextUnit,
|
||||||
leaf: SyntaxNodeRef<'a>,
|
leaf: SyntaxNodeRef<'a>,
|
||||||
module: Option<hir::Module>,
|
module: Option<hir::Module>,
|
||||||
enclosing_fn: Option<ast::FnDef<'a>>,
|
enclosing_fn: Option<ast::FnDef<'a>>,
|
||||||
|
@ -88,6 +79,7 @@ impl<'a> SyntaxContext<'a> {
|
||||||
let mut ctx = SyntaxContext {
|
let mut ctx = SyntaxContext {
|
||||||
db,
|
db,
|
||||||
leaf,
|
leaf,
|
||||||
|
offset: position.offset,
|
||||||
module,
|
module,
|
||||||
enclosing_fn: None,
|
enclosing_fn: None,
|
||||||
is_param: false,
|
is_param: false,
|
||||||
|
|
|
@ -9,8 +9,8 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &SyntaxContext) -> Cance
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
let def_id = match module.resolve_path(ctx.db, path)? {
|
let def_id = match module.resolve_path(ctx.db, path)? {
|
||||||
None => return Ok(()),
|
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
let target_module = match def_id.resolve(ctx.db)? {
|
let target_module = match def_id.resolve(ctx.db)? {
|
||||||
hir::Def::Module(it) => it,
|
hir::Def::Module(it) => it,
|
||||||
|
|
171
crates/ra_analysis/src/completion/complete_scope.rs
Normal file
171
crates/ra_analysis/src/completion/complete_scope.rs
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
use rustc_hash::FxHashSet;
|
||||||
|
use ra_syntax::TextUnit;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
completion::{CompletionItem, Completions, CompletionKind::*, SyntaxContext},
|
||||||
|
Cancelable
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(super) fn complete_scope(acc: &mut Completions, ctx: &SyntaxContext) -> Cancelable<()> {
|
||||||
|
if !ctx.is_trivial_path {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if let Some(fn_def) = ctx.enclosing_fn {
|
||||||
|
let scopes = hir::FnScopes::new(fn_def);
|
||||||
|
complete_fn(acc, &scopes, ctx.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(module) = &ctx.module {
|
||||||
|
let module_scope = module.scope(ctx.db)?;
|
||||||
|
module_scope
|
||||||
|
.entries()
|
||||||
|
.filter(|(_name, res)| {
|
||||||
|
// Don't expose this item
|
||||||
|
match res.import {
|
||||||
|
None => true,
|
||||||
|
Some(import) => {
|
||||||
|
let range = import.range(ctx.db, module.source().file_id());
|
||||||
|
!range.is_subrange(&ctx.leaf.range())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.for_each(|(name, _res)| {
|
||||||
|
CompletionItem::new(name.to_string())
|
||||||
|
.kind(Reference)
|
||||||
|
.add_to(acc)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete_fn(acc: &mut Completions, scopes: &hir::FnScopes, offset: TextUnit) {
|
||||||
|
let mut shadowed = FxHashSet::default();
|
||||||
|
scopes
|
||||||
|
.scope_chain_for_offset(offset)
|
||||||
|
.flat_map(|scope| scopes.entries(scope).iter())
|
||||||
|
.filter(|entry| shadowed.insert(entry.name()))
|
||||||
|
.for_each(|entry| {
|
||||||
|
CompletionItem::new(entry.name().to_string())
|
||||||
|
.kind(Reference)
|
||||||
|
.add_to(acc)
|
||||||
|
});
|
||||||
|
if scopes.self_param.is_some() {
|
||||||
|
CompletionItem::new("self").kind(Reference).add_to(acc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::completion::{CompletionKind, check_completion};
|
||||||
|
|
||||||
|
fn check_reference_completion(code: &str, expected_completions: &str) {
|
||||||
|
check_completion(code, expected_completions, CompletionKind::Reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_completion_let_scope() {
|
||||||
|
check_reference_completion(
|
||||||
|
r"
|
||||||
|
fn quux(x: i32) {
|
||||||
|
let y = 92;
|
||||||
|
1 + <|>;
|
||||||
|
let z = ();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"y;x;quux",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_completion_if_let_scope() {
|
||||||
|
check_reference_completion(
|
||||||
|
r"
|
||||||
|
fn quux() {
|
||||||
|
if let Some(x) = foo() {
|
||||||
|
let y = 92;
|
||||||
|
};
|
||||||
|
if let Some(a) = bar() {
|
||||||
|
let b = 62;
|
||||||
|
1 + <|>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"b;a;quux",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_completion_for_scope() {
|
||||||
|
check_reference_completion(
|
||||||
|
r"
|
||||||
|
fn quux() {
|
||||||
|
for x in &[1, 2, 3] {
|
||||||
|
<|>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"x;quux",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_completion_mod_scope() {
|
||||||
|
check_reference_completion(
|
||||||
|
r"
|
||||||
|
struct Foo;
|
||||||
|
enum Baz {}
|
||||||
|
fn quux() {
|
||||||
|
<|>
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"quux;Foo;Baz",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_completion_mod_scope_nested() {
|
||||||
|
check_reference_completion(
|
||||||
|
r"
|
||||||
|
struct Foo;
|
||||||
|
mod m {
|
||||||
|
struct Bar;
|
||||||
|
fn quux() { <|> }
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"quux;Bar",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_complete_type() {
|
||||||
|
check_reference_completion(
|
||||||
|
r"
|
||||||
|
struct Foo;
|
||||||
|
fn x() -> <|>
|
||||||
|
",
|
||||||
|
"Foo;x",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_complete_shadowing() {
|
||||||
|
check_reference_completion(
|
||||||
|
r"
|
||||||
|
fn foo() -> {
|
||||||
|
let bar = 92;
|
||||||
|
{
|
||||||
|
let bar = 62;
|
||||||
|
<|>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"bar;foo",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_complete_self() {
|
||||||
|
check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self")
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,307 +0,0 @@
|
||||||
use rustc_hash::FxHashSet;
|
|
||||||
use ra_syntax::{
|
|
||||||
SourceFileNode, AstNode,
|
|
||||||
ast,
|
|
||||||
SyntaxKind::*,
|
|
||||||
};
|
|
||||||
use hir::{
|
|
||||||
self,
|
|
||||||
FnScopes, Path
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
db::RootDatabase,
|
|
||||||
completion::{CompletionItem, Completions, CompletionKind::*},
|
|
||||||
Cancelable
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(super) fn completions(
|
|
||||||
acc: &mut Completions,
|
|
||||||
db: &RootDatabase,
|
|
||||||
module: &hir::Module,
|
|
||||||
_file: &SourceFileNode,
|
|
||||||
name_ref: ast::NameRef,
|
|
||||||
) -> Cancelable<()> {
|
|
||||||
let kind = match classify_name_ref(name_ref) {
|
|
||||||
Some(it) => it,
|
|
||||||
None => return Ok(()),
|
|
||||||
};
|
|
||||||
|
|
||||||
match kind {
|
|
||||||
NameRefKind::LocalRef { enclosing_fn } => {
|
|
||||||
if let Some(fn_def) = enclosing_fn {
|
|
||||||
let scopes = FnScopes::new(fn_def);
|
|
||||||
complete_fn(name_ref, &scopes, acc);
|
|
||||||
}
|
|
||||||
|
|
||||||
let module_scope = module.scope(db)?;
|
|
||||||
module_scope
|
|
||||||
.entries()
|
|
||||||
.filter(|(_name, res)| {
|
|
||||||
// Don't expose this item
|
|
||||||
match res.import {
|
|
||||||
None => true,
|
|
||||||
Some(import) => {
|
|
||||||
let range = import.range(db, module.source().file_id());
|
|
||||||
!range.is_subrange(&name_ref.syntax().range())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.for_each(|(name, _res)| {
|
|
||||||
CompletionItem::new(name.to_string())
|
|
||||||
.kind(Reference)
|
|
||||||
.add_to(acc)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
NameRefKind::Path(_) => (),
|
|
||||||
NameRefKind::BareIdentInMod => (),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
enum NameRefKind<'a> {
|
|
||||||
/// NameRef is a part of single-segment path, for example, a refernece to a
|
|
||||||
/// local variable.
|
|
||||||
LocalRef {
|
|
||||||
enclosing_fn: Option<ast::FnDef<'a>>,
|
|
||||||
},
|
|
||||||
/// NameRef is the last segment in some path
|
|
||||||
Path(Path),
|
|
||||||
/// NameRef is bare identifier at the module's root.
|
|
||||||
/// Used for keyword completion
|
|
||||||
BareIdentInMod,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
|
|
||||||
let name_range = name_ref.syntax().range();
|
|
||||||
let top_node = name_ref
|
|
||||||
.syntax()
|
|
||||||
.ancestors()
|
|
||||||
.take_while(|it| it.range() == name_range)
|
|
||||||
.last()
|
|
||||||
.unwrap();
|
|
||||||
match top_node.parent().map(|it| it.kind()) {
|
|
||||||
Some(SOURCE_FILE) | Some(ITEM_LIST) => return Some(NameRefKind::BareIdentInMod),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
let parent = name_ref.syntax().parent()?;
|
|
||||||
if let Some(segment) = ast::PathSegment::cast(parent) {
|
|
||||||
let path = segment.parent_path();
|
|
||||||
if let Some(path) = Path::from_ast(path) {
|
|
||||||
if !path.is_ident() {
|
|
||||||
return Some(NameRefKind::Path(path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if path.qualifier().is_none() {
|
|
||||||
let enclosing_fn = name_ref
|
|
||||||
.syntax()
|
|
||||||
.ancestors()
|
|
||||||
.take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
|
|
||||||
.find_map(ast::FnDef::cast);
|
|
||||||
return Some(NameRefKind::LocalRef { enclosing_fn });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Completions) {
|
|
||||||
let mut shadowed = FxHashSet::default();
|
|
||||||
scopes
|
|
||||||
.scope_chain(name_ref.syntax())
|
|
||||||
.flat_map(|scope| scopes.entries(scope).iter())
|
|
||||||
.filter(|entry| shadowed.insert(entry.name()))
|
|
||||||
.for_each(|entry| {
|
|
||||||
CompletionItem::new(entry.name().to_string())
|
|
||||||
.kind(Reference)
|
|
||||||
.add_to(acc)
|
|
||||||
});
|
|
||||||
if scopes.self_param.is_some() {
|
|
||||||
CompletionItem::new("self").kind(Reference).add_to(acc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::completion::{CompletionKind, check_completion};
|
|
||||||
|
|
||||||
fn check_reference_completion(code: &str, expected_completions: &str) {
|
|
||||||
check_completion(code, expected_completions, CompletionKind::Reference);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_completion_let_scope() {
|
|
||||||
check_reference_completion(
|
|
||||||
r"
|
|
||||||
fn quux(x: i32) {
|
|
||||||
let y = 92;
|
|
||||||
1 + <|>;
|
|
||||||
let z = ();
|
|
||||||
}
|
|
||||||
",
|
|
||||||
"y;x;quux",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_completion_if_let_scope() {
|
|
||||||
check_reference_completion(
|
|
||||||
r"
|
|
||||||
fn quux() {
|
|
||||||
if let Some(x) = foo() {
|
|
||||||
let y = 92;
|
|
||||||
};
|
|
||||||
if let Some(a) = bar() {
|
|
||||||
let b = 62;
|
|
||||||
1 + <|>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
",
|
|
||||||
"b;a;quux",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_completion_for_scope() {
|
|
||||||
check_reference_completion(
|
|
||||||
r"
|
|
||||||
fn quux() {
|
|
||||||
for x in &[1, 2, 3] {
|
|
||||||
<|>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
",
|
|
||||||
"x;quux",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_completion_mod_scope() {
|
|
||||||
check_reference_completion(
|
|
||||||
r"
|
|
||||||
struct Foo;
|
|
||||||
enum Baz {}
|
|
||||||
fn quux() {
|
|
||||||
<|>
|
|
||||||
}
|
|
||||||
",
|
|
||||||
"quux;Foo;Baz",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_completion_mod_scope_no_self_use() {
|
|
||||||
check_reference_completion(
|
|
||||||
r"
|
|
||||||
use foo<|>;
|
|
||||||
",
|
|
||||||
"",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_completion_self_path() {
|
|
||||||
check_reference_completion(
|
|
||||||
r"
|
|
||||||
use self::m::<|>;
|
|
||||||
|
|
||||||
mod m {
|
|
||||||
struct Bar;
|
|
||||||
}
|
|
||||||
",
|
|
||||||
"Bar",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_completion_mod_scope_nested() {
|
|
||||||
check_reference_completion(
|
|
||||||
r"
|
|
||||||
struct Foo;
|
|
||||||
mod m {
|
|
||||||
struct Bar;
|
|
||||||
fn quux() { <|> }
|
|
||||||
}
|
|
||||||
",
|
|
||||||
"quux;Bar",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_complete_type() {
|
|
||||||
check_reference_completion(
|
|
||||||
r"
|
|
||||||
struct Foo;
|
|
||||||
fn x() -> <|>
|
|
||||||
",
|
|
||||||
"Foo;x",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_complete_shadowing() {
|
|
||||||
check_reference_completion(
|
|
||||||
r"
|
|
||||||
fn foo() -> {
|
|
||||||
let bar = 92;
|
|
||||||
{
|
|
||||||
let bar = 62;
|
|
||||||
<|>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
",
|
|
||||||
"bar;foo",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_complete_self() {
|
|
||||||
check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_complete_crate_path() {
|
|
||||||
check_reference_completion(
|
|
||||||
"
|
|
||||||
//- /lib.rs
|
|
||||||
mod foo;
|
|
||||||
struct Spam;
|
|
||||||
//- /foo.rs
|
|
||||||
use crate::Sp<|>
|
|
||||||
",
|
|
||||||
"Spam;foo",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_complete_crate_path_with_braces() {
|
|
||||||
check_reference_completion(
|
|
||||||
"
|
|
||||||
//- /lib.rs
|
|
||||||
mod foo;
|
|
||||||
struct Spam;
|
|
||||||
//- /foo.rs
|
|
||||||
use crate::{Sp<|>};
|
|
||||||
",
|
|
||||||
"Spam;foo",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_complete_crate_path_in_nested_tree() {
|
|
||||||
check_reference_completion(
|
|
||||||
"
|
|
||||||
//- /lib.rs
|
|
||||||
mod foo;
|
|
||||||
pub mod bar {
|
|
||||||
pub mod baz {
|
|
||||||
pub struct Spam;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//- /foo.rs
|
|
||||||
use crate::{bar::{baz::Sp<|>}};
|
|
||||||
",
|
|
||||||
"Spam",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
AstNode, SmolStr, SyntaxNodeRef, TextRange,
|
AstNode, SmolStr, SyntaxNodeRef, TextUnit, TextRange,
|
||||||
algo::generate,
|
algo::generate,
|
||||||
ast::{self, ArgListOwner, LoopBodyOwner, NameOwner},
|
ast::{self, ArgListOwner, LoopBodyOwner, NameOwner},
|
||||||
};
|
};
|
||||||
|
@ -57,6 +57,48 @@ impl FnScopes {
|
||||||
self.scopes[scope].parent
|
self.scopes[scope].parent
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
pub fn scope_chain_for_offset<'a>(
|
||||||
|
&'a self,
|
||||||
|
offset: TextUnit,
|
||||||
|
) -> impl Iterator<Item = ScopeId> + 'a {
|
||||||
|
let scope = self
|
||||||
|
.scope_for
|
||||||
|
.iter()
|
||||||
|
// find containin scope
|
||||||
|
.min_by_key(|(ptr, _scope)| {
|
||||||
|
(
|
||||||
|
!(ptr.range().start() <= offset && offset <= ptr.range().end()),
|
||||||
|
ptr.range().len(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|(ptr, scope)| self.adjust(*ptr, *scope, offset));
|
||||||
|
|
||||||
|
generate(scope, move |&scope| self.scopes[scope].parent)
|
||||||
|
}
|
||||||
|
// XXX: during completion, cursor might be outside of any particular
|
||||||
|
// expression. Try to figure out the correct scope...
|
||||||
|
fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
|
||||||
|
let r = ptr.range();
|
||||||
|
let child_scopes = self
|
||||||
|
.scope_for
|
||||||
|
.iter()
|
||||||
|
.map(|(ptr, scope)| (ptr.range(), scope))
|
||||||
|
.filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
|
||||||
|
|
||||||
|
child_scopes
|
||||||
|
.max_by(|(r1, _), (r2, _)| {
|
||||||
|
if r2.is_subrange(&r1) {
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
} else if r1.is_subrange(&r2) {
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
} else {
|
||||||
|
r1.start().cmp(&r2.start())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|(ptr, scope)| *scope)
|
||||||
|
.unwrap_or(original_scope)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> {
|
pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> {
|
||||||
let mut shadowed = FxHashSet::default();
|
let mut shadowed = FxHashSet::default();
|
||||||
let ret = self
|
let ret = self
|
||||||
|
@ -144,6 +186,8 @@ impl ScopeEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
|
fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
|
||||||
|
// A hack for completion :(
|
||||||
|
scopes.set_scope(block.syntax(), scope);
|
||||||
for stmt in block.statements() {
|
for stmt in block.statements() {
|
||||||
match stmt {
|
match stmt {
|
||||||
ast::Stmt::LetStmt(stmt) => {
|
ast::Stmt::LetStmt(stmt) => {
|
||||||
|
@ -165,6 +209,7 @@ fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: Sco
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(expr) = block.expr() {
|
if let Some(expr) = block.expr() {
|
||||||
|
eprintln!("{:?}", expr);
|
||||||
scopes.set_scope(expr.syntax(), scope);
|
scopes.set_scope(expr.syntax(), scope);
|
||||||
compute_expr_scopes(expr, scopes, scope);
|
compute_expr_scopes(expr, scopes, scope);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue