Skip to content

Commit c620cfd

Browse files
committed
fix(els): completion retrigger
1 parent 475eca9 commit c620cfd

File tree

5 files changed

+80
-44
lines changed

5 files changed

+80
-44
lines changed

Cargo.lock

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/els/completion.rs

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use lsp_types::{
3232
};
3333

3434
use crate::server::{ELSResult, RedirectableStdout, Server};
35-
use crate::util::{self, NormalizedUrl};
35+
use crate::util::{self, loc_to_pos, NormalizedUrl};
3636

3737
fn comp_item_kind(vi: &VarInfo) -> CompletionItemKind {
3838
match &vi.t {
@@ -51,20 +51,21 @@ fn comp_item_kind(vi: &VarInfo) -> CompletionItemKind {
5151

5252
#[derive(Debug, PartialEq, Eq)]
5353
pub enum CompletionKind {
54+
RetriggerLocal,
5455
Local,
55-
Space,
5656
LParen,
5757
Method,
58+
RetriggerMethod,
5859
// Colon, // :, Type ascription or private access `::`
5960
}
6061

6162
impl CompletionKind {
6263
pub const fn should_be_local(&self) -> bool {
63-
matches!(self, Self::Local | Self::Space | Self::LParen)
64+
matches!(self, Self::RetriggerLocal | Self::Local | Self::LParen)
6465
}
6566

6667
pub const fn should_be_method(&self) -> bool {
67-
matches!(self, Self::Method)
68+
matches!(self, Self::Method | Self::RetriggerMethod)
6869
}
6970

7071
pub const fn _is_lparen(&self) -> bool {
@@ -493,7 +494,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
493494
self.send_log(format!("completion requested: {params:?}"))?;
494495
let uri = NormalizedUrl::new(params.text_document_position.text_document.uri);
495496
let path = util::uri_to_path(&uri);
496-
let pos = params.text_document_position.position;
497+
let mut pos = params.text_document_position.position;
497498
// ignore comments
498499
// TODO: multiline comments
499500
if self
@@ -510,32 +511,39 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
510511
let comp_kind = match trigger {
511512
Some(".") => CompletionKind::Method,
512513
Some(":") => CompletionKind::Method,
513-
Some(" ") => CompletionKind::Space,
514+
Some(" ") => CompletionKind::Local,
514515
Some("(") => CompletionKind::LParen,
515-
_ => CompletionKind::Local,
516+
_ => {
517+
let offset = match self.file_cache.get_token(&uri, pos).map(|tk| tk.kind) {
518+
Some(TokenKind::Newline | TokenKind::EOF) => -2,
519+
_ => -1,
520+
};
521+
let prev_token = self.file_cache.get_token_relatively(&uri, pos, offset);
522+
match prev_token {
523+
Some(prev) if matches!(prev.kind, Dot | DblColon) => {
524+
if let Some(p) = loc_to_pos(prev.loc()) {
525+
pos = p;
526+
}
527+
CompletionKind::RetriggerMethod
528+
}
529+
_ => CompletionKind::RetriggerLocal,
530+
}
531+
}
516532
};
517533
self.send_log(format!("CompletionKind: {comp_kind:?}"))?;
518534
let mut result: Vec<CompletionItem> = vec![];
519535
let mut already_appeared = Set::new();
520536
let contexts = if comp_kind.should_be_local() {
521-
let prev_token = self.file_cache.get_token_relatively(&uri, pos, -1);
522-
match prev_token {
523-
Some(prev) if matches!(prev.kind, Dot | DblColon) => {
524-
let Some(dot_pos) = util::loc_to_pos(prev.loc()) else {
525-
return Ok(None);
526-
};
527-
self.get_receiver_ctxs(&uri, dot_pos)?
528-
}
529-
_ => self.get_local_ctx(&uri, pos),
530-
}
537+
self.get_local_ctx(&uri, pos)
531538
} else {
532539
self.get_receiver_ctxs(&uri, pos)?
533540
};
534541
let offset = match comp_kind {
535-
CompletionKind::Local => 0,
542+
CompletionKind::RetriggerLocal => 0,
536543
CompletionKind::Method => -1,
537-
CompletionKind::Space => -1,
544+
CompletionKind::Local => -1,
538545
CompletionKind::LParen => 0,
546+
CompletionKind::RetriggerMethod => -1,
539547
};
540548
let arg_pt = self
541549
.get_min_expr(&uri, pos, offset)
@@ -547,7 +555,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
547555
let nth = nth + additional;
548556
sig_t.non_default_params()?.get(nth).cloned()
549557
}
550-
other if comp_kind == CompletionKind::Space => {
558+
other if comp_kind == CompletionKind::Local => {
551559
match other.show_acc().as_deref() {
552560
Some("import") => {
553561
let insert = other

crates/els/server.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,11 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
918918
self.shared.clear(&path);
919919
}
920920

921+
#[allow(unused)]
922+
pub fn get_file_cache(&self) -> &FileCache {
923+
&self.file_cache
924+
}
925+
921926
pub fn remove_module_entry(&mut self, uri: &NormalizedUrl) -> Option<ModuleEntry> {
922927
let path = uri.to_file_path().ok()?;
923928
self.shared.mod_cache.remove(&path)

crates/els/tests/retrigger.er

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
l = [1, 2]
2+
for! l, i =>
3+
pri
4+
print! i.bi

crates/els/tests/test.rs

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ use std::path::Path;
22

33
use lsp_types::{
44
CompletionResponse, DiagnosticSeverity, DocumentSymbolResponse, FoldingRange, FoldingRangeKind,
5-
GotoDefinitionResponse, HoverContents, InlayHintLabel, MarkedString, PublishDiagnosticsParams,
5+
GotoDefinitionResponse, HoverContents, InlayHintLabel, MarkedString,
66
};
77
const FILE_A: &str = "tests/a.er";
88
const FILE_B: &str = "tests/b.er";
99
const FILE_C: &str = "tests/c.er";
1010
const FILE_IMPORTS: &str = "tests/imports.er";
1111
const FILE_INVALID_SYNTAX: &str = "tests/invalid_syntax.er";
12+
const FILE_RETRIGGER: &str = "tests/retrigger.er";
1213

1314
use els::{NormalizedUrl, Server};
1415
use erg_proc_macros::exec_new_thread;
1516
use molc::{add_char, delete_line, oneline_range};
16-
use serde::Deserialize;
1717

1818
#[test]
1919
fn test_open() -> Result<(), Box<dyn std::error::Error>> {
@@ -68,6 +68,34 @@ fn test_neighbor_completion() -> Result<(), Box<dyn std::error::Error>> {
6868
}
6969
}
7070

71+
#[test]
72+
fn test_completion_retrigger() -> Result<(), Box<dyn std::error::Error>> {
73+
let mut client = Server::bind_fake_client();
74+
client.request_initialize()?;
75+
client.notify_initialized()?;
76+
let uri = NormalizedUrl::from_file_path(Path::new(FILE_RETRIGGER).canonicalize()?)?;
77+
client.notify_open(FILE_RETRIGGER)?;
78+
let _ = client.wait_diagnostics()?;
79+
client.notify_change(uri.clone().raw(), add_char(2, 7, "n"))?;
80+
let resp = client.request_completion(uri.clone().raw(), 2, 7, "n")?;
81+
if let Some(CompletionResponse::Array(items)) = resp {
82+
assert!(!items.is_empty());
83+
assert!(items.iter().any(|item| item.label == "print!"));
84+
} else {
85+
return Err(format!("not items: {resp:?}").into());
86+
}
87+
client.notify_change(uri.clone().raw(), add_char(3, 15, "t"))?;
88+
let resp = client.request_completion(uri.raw(), 3, 15, "t")?;
89+
if let Some(CompletionResponse::Array(items)) = resp {
90+
assert!(!items.is_empty());
91+
assert!(items.iter().any(|item| item.label == "bit_count"));
92+
assert!(items.iter().any(|item| item.label == "bit_length"));
93+
} else {
94+
return Err(format!("not items: {resp:?}").into());
95+
}
96+
Ok(())
97+
}
98+
7199
#[test]
72100
fn test_rename() -> Result<(), Box<dyn std::error::Error>> {
73101
let mut client = Server::bind_fake_client();
@@ -251,11 +279,9 @@ fn test_dependents_check() -> Result<(), Box<dyn std::error::Error>> {
251279
client.wait_messages(2)?;
252280
client.responses.clear();
253281
client.notify_save(uri_b.clone().raw())?;
254-
client.wait_messages(9)?;
255-
assert!(client.responses.iter().any(|resp| resp
256-
.to_string()
257-
.contains("tests/b.er passed, found warns: 0")));
258-
let diags = PublishDiagnosticsParams::deserialize(&client.responses.last().unwrap()["params"])?;
282+
let diags = client.wait_diagnostics()?;
283+
assert!(diags.diagnostics.is_empty());
284+
let diags = client.wait_diagnostics()?;
259285
assert_eq!(diags.diagnostics.len(), 1);
260286
assert_eq!(
261287
diags.diagnostics[0].severity,
@@ -269,24 +295,17 @@ fn test_fix_error() -> Result<(), Box<dyn std::error::Error>> {
269295
let mut client = Server::bind_fake_client();
270296
client.request_initialize()?;
271297
client.notify_initialized()?;
272-
client.wait_messages(3)?;
273-
client.responses.clear();
274298
client.notify_open(FILE_INVALID_SYNTAX)?;
275-
client.wait_messages(6)?;
276-
let msg = client.responses.last().unwrap();
277-
let diags = PublishDiagnosticsParams::deserialize(&msg["params"])?;
299+
let diags = client.wait_diagnostics()?;
278300
assert_eq!(diags.diagnostics.len(), 1);
279301
assert_eq!(
280302
diags.diagnostics[0].severity,
281303
Some(DiagnosticSeverity::ERROR)
282304
);
283-
client.responses.clear();
284305
let uri = NormalizedUrl::from_file_path(Path::new(FILE_INVALID_SYNTAX).canonicalize()?)?;
285306
client.notify_change(uri.clone().raw(), add_char(0, 10, " 1"))?;
286307
client.notify_save(uri.clone().raw())?;
287-
client.wait_messages(4)?;
288-
let msg = client.responses.last().unwrap();
289-
let diags = PublishDiagnosticsParams::deserialize(&msg["params"])?;
308+
let diags = client.wait_diagnostics()?;
290309
assert_eq!(diags.diagnostics.len(), 0);
291310
Ok(())
292311
}

0 commit comments

Comments
 (0)