feat: use field access classifier for completion (#1035)

* feat: use field access classifier for completion

* dev: more cases about dot access
This commit is contained in:
Myriad-Dreamin 2024-12-20 19:15:59 +08:00 committed by GitHub
parent 39243ba626
commit 980571ad61
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 20 additions and 58 deletions

View file

@ -17,7 +17,7 @@ snapshot_kind: text
"kind": 3,
"label": "table-prefix",
"textEdit": {
"newText": "table-prefix",
"newText": "table-prefix(${1:})",
"range": {
"end": {
"character": 24,

View file

@ -441,14 +441,15 @@ pub fn classify_syntax(node: LinkedNode, cursor: usize) -> Option<SyntaxClass<'_
node = node.prev_sibling()?;
}
let is_dot = matches!(node.kind(), SyntaxKind::Dot)
|| (matches!(node.kind(), SyntaxKind::Text | SyntaxKind::Error) && node.text() == ".");
let starts_with_dot = matches!(node.kind(), SyntaxKind::Dot)
|| (matches!(node.kind(), SyntaxKind::Text | SyntaxKind::Error)
&& node.text().starts_with("."));
if is_dot && node.offset() + 1 == cursor {
if starts_with_dot && node.offset() + 1 == cursor {
let dot_target = node.clone().prev_leaf().and_then(first_ancestor_expr);
if let Some(dots_target) = dot_target {
return Some(SyntaxClass::VarAccess(VarClass::DotAccess(dots_target)));
if let Some(dot_target) = dot_target {
return Some(SyntaxClass::VarAccess(VarClass::DotAccess(dot_target)));
}
}

View file

@ -46,7 +46,6 @@ pub fn autocomplete(
|| complete_type_and_syntax(&mut ctx).is_none() && {
crate::log_debug_ct!("continue after completing type and syntax");
complete_imports(&mut ctx)
|| complete_field_accesses(&mut ctx)
|| complete_markup(&mut ctx)
|| complete_math(&mut ctx)
|| complete_code(&mut ctx, false)
@ -244,53 +243,6 @@ fn complete_math(ctx: &mut CompletionContext) -> bool {
false
}
/// Complete field accesses.
fn complete_field_accesses(ctx: &mut CompletionContext) -> bool {
// Used to determine whether trivia nodes are allowed before '.'.
// During an inline expression in markup mode trivia nodes exit the inline
// expression.
let in_markup: bool = matches!(
ctx.leaf.parent_kind(),
None | Some(SyntaxKind::Markup) | Some(SyntaxKind::Ref)
);
// Behind an expression plus dot: "emoji.|".
if_chain! {
if ctx.leaf.kind() == SyntaxKind::Dot
|| (ctx.leaf.kind() == SyntaxKind::Text
&& ctx.leaf.text() == ".");
if ctx.leaf.range().end == ctx.cursor;
if let Some(prev) = ctx.leaf.prev_sibling();
if !in_markup || prev.range().end == ctx.leaf.range().start;
if prev.is::<ast::Expr>();
if prev.parent_kind() != Some(SyntaxKind::Markup) ||
prev.prev_sibling_kind() == Some(SyntaxKind::Hash);
if let Some((value, styles)) = ctx.ctx.analyze_expr(&prev).into_iter().next();
then {
ctx.from = ctx.cursor;
field_access_completions(ctx, &prev, &value, &styles);
return true;
}
}
// Behind a started field access: "emoji.fa|".
if_chain! {
if ctx.leaf.kind() == SyntaxKind::Ident;
if let Some(prev) = ctx.leaf.prev_sibling();
if prev.kind() == SyntaxKind::Dot;
if let Some(prev_prev) = prev.prev_sibling();
if prev_prev.is::<ast::Expr>();
if let Some((value, styles)) = ctx.ctx.analyze_expr(&prev_prev).into_iter().next();
then {
ctx.from = ctx.leaf.offset();
field_access_completions(ctx,&prev_prev, &value, &styles);
return true;
}
}
false
}
/// Add completions for all fields on a value.
fn field_access_completions(
ctx: &mut CompletionContext,

View file

@ -25,7 +25,7 @@ use crate::syntax::{
VarClass,
};
use crate::ty::{DynTypeBounds, Iface, IfaceChecker, InsTy, SigTy, TyCtx, TypeInfo, TypeVar};
use crate::upstream::complete::complete_code;
use crate::upstream::complete::{complete_code, field_access_completions};
use crate::{completion_kind, prelude::*, LspCompletion};
@ -1398,9 +1398,18 @@ pub(crate) fn complete_type_and_syntax(ctx: &mut CompletionContext) -> Option<()
args_node = Some(args.to_untyped().clone());
}
// todo: complete field by types
Some(CursorClass::VarAccess(VarClass::FieldAccess { .. }))
| Some(CursorClass::VarAccess(VarClass::DotAccess { .. })) => {
return None;
Some(CursorClass::VarAccess(
var @ (VarClass::FieldAccess { .. } | VarClass::DotAccess { .. }),
)) => {
let target = var.accessed_node()?;
let field = var.accessing_field()?;
let offset = field.offset(&ctx.ctx.source_by_id(target.span().id()?).ok()?)?;
ctx.from = offset;
let (value, styles) = ctx.ctx.analyze_expr(&target).into_iter().next()?;
field_access_completions(ctx, &target, &value, &styles);
return Some(());
}
Some(CursorClass::ImportPath(path) | CursorClass::IncludePath(path)) => {
let Some(ast::Expr::Str(str)) = path.cast() else {