mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 17:58:17 +00:00
feat: optionally make symbol completion stepless (#1313)
* feat: support to steplessly complete symbols * fix: default enum * feat: implement it * fix: bad nest * dev: order * fix: modifier
This commit is contained in:
parent
258cf85601
commit
e26d817b79
7 changed files with 97 additions and 44 deletions
|
@ -83,6 +83,9 @@ pub struct CompletionFeat {
|
|||
#[serde(default)]
|
||||
pub trigger_suggest_and_parameter_hints: bool,
|
||||
|
||||
/// The Way to complete symbols.
|
||||
pub symbol: Option<SymbolCompletionWay>,
|
||||
|
||||
/// Whether to enable postfix completion.
|
||||
pub postfix: Option<bool>,
|
||||
/// Whether to enable ufcs completion.
|
||||
|
@ -127,6 +130,22 @@ impl CompletionFeat {
|
|||
.as_ref()
|
||||
.unwrap_or(&DEFAULT_POSTFIX_SNIPPET)
|
||||
}
|
||||
|
||||
pub(crate) fn is_stepless(&self) -> bool {
|
||||
matches!(self.symbol, Some(SymbolCompletionWay::Stepless))
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether to make symbol completion stepless. For example, `$ar|$` will be
|
||||
/// completed to `$arrow.r$`. Hint: Restarting the editor is required to change
|
||||
/// this setting.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum SymbolCompletionWay {
|
||||
/// Complete symbols step by step
|
||||
Step,
|
||||
/// Complete symbols steplessly
|
||||
Stepless,
|
||||
}
|
||||
|
||||
/// The struct describing how a completion worker views the editor's cursor.
|
||||
|
|
|
@ -73,16 +73,7 @@ impl CompletionPair<'_, '_, '_> {
|
|||
|
||||
match value {
|
||||
Value::Symbol(symbol) => {
|
||||
for modifier in symbol.modifiers() {
|
||||
if let Ok(modified) = symbol.clone().modified(modifier) {
|
||||
self.push_completion(Completion {
|
||||
kind: CompletionKind::Symbol(modified.get()),
|
||||
label: modifier.into(),
|
||||
label_details: Some(symbol_label_detail(modified.get())),
|
||||
..Completion::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
self.symbol_var_completions(&symbol, None);
|
||||
|
||||
if valid_postfix_target {
|
||||
self.ufcs_completions(target);
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
//! Completion kind analysis.
|
||||
|
||||
use typst::foundations::Symbol;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub(crate) struct CompletionKindChecker {
|
||||
pub symbols: HashSet<char>,
|
||||
pub functions: HashSet<Ty>,
|
||||
pub(crate) struct CompletionKindChecker<'a> {
|
||||
pub symbols: HashSet<&'a Symbol>,
|
||||
pub functions: HashSet<&'a Ty>,
|
||||
}
|
||||
|
||||
impl CompletionKindChecker {
|
||||
impl<'a> CompletionKindChecker<'a> {
|
||||
/// Reset the checker status for a fresh checking.
|
||||
fn reset(&mut self) {
|
||||
self.symbols.clear();
|
||||
|
@ -15,29 +17,29 @@ impl CompletionKindChecker {
|
|||
}
|
||||
|
||||
/// Check the completion kind of a type.
|
||||
pub fn check(&mut self, ty: &Ty) {
|
||||
pub fn check(&mut self, ty: &'a Ty) {
|
||||
self.reset();
|
||||
match ty {
|
||||
Ty::Value(val) => match &val.val {
|
||||
Value::Type(t) if t.constructor().is_ok() => {
|
||||
self.functions.insert(ty.clone());
|
||||
self.functions.insert(ty);
|
||||
}
|
||||
Value::Func(..) => {
|
||||
self.functions.insert(ty.clone());
|
||||
self.functions.insert(ty);
|
||||
}
|
||||
Value::Symbol(s) => {
|
||||
self.symbols.insert(s.get());
|
||||
self.symbols.insert(s);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Ty::Func(..) | Ty::With(..) => {
|
||||
self.functions.insert(ty.clone());
|
||||
self.functions.insert(ty);
|
||||
}
|
||||
Ty::Builtin(BuiltinTy::TypeType(t)) if t.constructor().is_ok() => {
|
||||
self.functions.insert(ty.clone());
|
||||
self.functions.insert(ty);
|
||||
}
|
||||
Ty::Builtin(BuiltinTy::Element(..)) => {
|
||||
self.functions.insert(ty.clone());
|
||||
self.functions.insert(ty);
|
||||
}
|
||||
Ty::Let(bounds) => {
|
||||
for bound in bounds.ubs.iter().chain(bounds.lbs.iter()) {
|
||||
|
|
|
@ -151,30 +151,23 @@ impl CompletionPair<'_, '_, '_> {
|
|||
};
|
||||
|
||||
// we don't check literal type here for faster completion
|
||||
for (name, ty) in defines {
|
||||
for (name, ty) in &defines {
|
||||
if name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
kind_checker.check(&ty);
|
||||
kind_checker.check(ty);
|
||||
if !filter(&kind_checker) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(ch) = kind_checker.symbols.iter().min().copied() {
|
||||
// todo: describe all chars
|
||||
let kind = CompletionKind::Symbol(ch);
|
||||
self.push_completion(Completion {
|
||||
kind,
|
||||
label: name,
|
||||
label_details: Some(symbol_label_detail(ch)),
|
||||
detail: Some(symbol_detail(ch)),
|
||||
..Completion::default()
|
||||
});
|
||||
// todo: describe all chars
|
||||
if let Some(sym) = kind_checker.symbols.iter().min_by_key(|s| s.get()) {
|
||||
self.symbol_completions(name.clone(), sym);
|
||||
continue;
|
||||
}
|
||||
|
||||
let docs = default_docs.get(&name).cloned();
|
||||
let docs = default_docs.get(name).cloned();
|
||||
|
||||
let label_details = ty.describe().or_else(|| Some("any".into()));
|
||||
|
||||
|
@ -182,16 +175,17 @@ impl CompletionPair<'_, '_, '_> {
|
|||
let detail = docs.or_else(|| label_details.clone());
|
||||
|
||||
if !kind_checker.functions.is_empty() {
|
||||
let fn_feat = FnCompletionFeat::default().check(kind_checker.functions.iter());
|
||||
let fn_feat =
|
||||
FnCompletionFeat::default().check(kind_checker.functions.iter().copied());
|
||||
crate::log_debug_ct!("fn_feat: {name} {ty:?} -> {fn_feat:?}");
|
||||
self.func_completion(mode, fn_feat, name, label_details, detail, parens);
|
||||
self.func_completion(mode, fn_feat, name.clone(), label_details, detail, parens);
|
||||
continue;
|
||||
}
|
||||
|
||||
let kind = type_to_completion_kind(&ty);
|
||||
let kind = type_to_completion_kind(ty);
|
||||
self.push_completion(Completion {
|
||||
kind,
|
||||
label: name,
|
||||
label: name.clone(),
|
||||
label_details,
|
||||
detail,
|
||||
..Completion::default()
|
||||
|
|
|
@ -213,15 +213,15 @@ impl CompletionPair<'_, '_, '_> {
|
|||
let rb = if is_content_block { "" } else { ")" };
|
||||
|
||||
// we don't check literal type here for faster completion
|
||||
for (name, ty) in defines.defines {
|
||||
for (name, ty) in &defines.defines {
|
||||
// todo: filter ty
|
||||
if name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
kind_checker.check(&ty);
|
||||
kind_checker.check(ty);
|
||||
|
||||
if kind_checker.symbols.iter().min().copied().is_some() {
|
||||
if !kind_checker.symbols.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if kind_checker.functions.is_empty() {
|
||||
|
@ -242,7 +242,7 @@ impl CompletionPair<'_, '_, '_> {
|
|||
.map(From::from),
|
||||
..Default::default()
|
||||
};
|
||||
let fn_feat = FnCompletionFeat::default().check(kind_checker.functions.iter());
|
||||
let fn_feat = FnCompletionFeat::default().check(kind_checker.functions.iter().copied());
|
||||
|
||||
crate::log_debug_ct!("fn_feat: {name} {ty:?} -> {fn_feat:?}");
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//! Completion by typst specific semantics, like `font`, `package`, `label`, or
|
||||
//! `typst::foundations::Value`.
|
||||
|
||||
use typst::foundations::Symbol;
|
||||
|
||||
use super::*;
|
||||
impl CompletionPair<'_, '_, '_> {
|
||||
/// Add completions for all font families.
|
||||
|
@ -206,14 +208,15 @@ impl CompletionPair<'_, '_, '_> {
|
|||
let mut apply = None;
|
||||
if parens && matches!(value, Value::Func(_)) {
|
||||
let mode = self.cursor.leaf_mode();
|
||||
let ty = Ty::Value(InsTy::new(value.clone()));
|
||||
let kind_checker = CompletionKindChecker {
|
||||
symbols: HashSet::default(),
|
||||
functions: HashSet::from_iter([Ty::Value(InsTy::new(value.clone()))]),
|
||||
functions: HashSet::from_iter([&ty]),
|
||||
};
|
||||
let mut fn_feat = FnCompletionFeat::default();
|
||||
// todo: unify bound self checking
|
||||
fn_feat.bound_self = bound_self;
|
||||
let fn_feat = fn_feat.check(kind_checker.functions.iter());
|
||||
let fn_feat = fn_feat.check(kind_checker.functions.iter().copied());
|
||||
self.func_completion(mode, fn_feat, label, label_details, detail, parens);
|
||||
return;
|
||||
} else if at {
|
||||
|
@ -246,6 +249,36 @@ impl CompletionPair<'_, '_, '_> {
|
|||
..Completion::default()
|
||||
});
|
||||
}
|
||||
|
||||
pub fn symbol_completions(&mut self, label: EcoString, symbol: &Symbol) {
|
||||
let ch = symbol.get();
|
||||
let kind = CompletionKind::Symbol(ch);
|
||||
self.push_completion(Completion {
|
||||
kind,
|
||||
label: label.clone(),
|
||||
label_details: Some(symbol_label_detail(ch)),
|
||||
detail: Some(symbol_detail(ch)),
|
||||
..Completion::default()
|
||||
});
|
||||
|
||||
let is_stepless = self.cursor.ctx.analysis.completion_feat.is_stepless();
|
||||
if is_stepless {
|
||||
self.symbol_var_completions(symbol, Some(&label));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol_var_completions(&mut self, symbol: &Symbol, prefix: Option<&str>) {
|
||||
for modifier in symbol.modifiers() {
|
||||
if let Ok(modified) = symbol.clone().modified(modifier) {
|
||||
let label = match &prefix {
|
||||
Some(prefix) => eco_format!("{prefix}.{modifier}"),
|
||||
None => modifier.into(),
|
||||
};
|
||||
|
||||
self.symbol_completions(label, &modified);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue