Skip to content

Commit

Permalink
fix(els): completion retrigger
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Sep 29, 2023
1 parent 475eca9 commit c620cfd
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 44 deletions.
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 28 additions & 20 deletions crates/els/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use lsp_types::{
};

use crate::server::{ELSResult, RedirectableStdout, Server};
use crate::util::{self, NormalizedUrl};
use crate::util::{self, loc_to_pos, NormalizedUrl};

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

#[derive(Debug, PartialEq, Eq)]
pub enum CompletionKind {
RetriggerLocal,
Local,
Space,
LParen,
Method,
RetriggerMethod,
// Colon, // :, Type ascription or private access `::`
}

impl CompletionKind {
pub const fn should_be_local(&self) -> bool {
matches!(self, Self::Local | Self::Space | Self::LParen)
matches!(self, Self::RetriggerLocal | Self::Local | Self::LParen)
}

pub const fn should_be_method(&self) -> bool {
matches!(self, Self::Method)
matches!(self, Self::Method | Self::RetriggerMethod)
}

pub const fn _is_lparen(&self) -> bool {
Expand Down Expand Up @@ -493,7 +494,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
self.send_log(format!("completion requested: {params:?}"))?;
let uri = NormalizedUrl::new(params.text_document_position.text_document.uri);
let path = util::uri_to_path(&uri);
let pos = params.text_document_position.position;
let mut pos = params.text_document_position.position;
// ignore comments
// TODO: multiline comments
if self
Expand All @@ -510,32 +511,39 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
let comp_kind = match trigger {
Some(".") => CompletionKind::Method,
Some(":") => CompletionKind::Method,
Some(" ") => CompletionKind::Space,
Some(" ") => CompletionKind::Local,
Some("(") => CompletionKind::LParen,
_ => CompletionKind::Local,
_ => {
let offset = match self.file_cache.get_token(&uri, pos).map(|tk| tk.kind) {
Some(TokenKind::Newline | TokenKind::EOF) => -2,
_ => -1,
};
let prev_token = self.file_cache.get_token_relatively(&uri, pos, offset);
match prev_token {
Some(prev) if matches!(prev.kind, Dot | DblColon) => {
if let Some(p) = loc_to_pos(prev.loc()) {
pos = p;
}
CompletionKind::RetriggerMethod
}
_ => CompletionKind::RetriggerLocal,
}
}
};
self.send_log(format!("CompletionKind: {comp_kind:?}"))?;
let mut result: Vec<CompletionItem> = vec![];
let mut already_appeared = Set::new();
let contexts = if comp_kind.should_be_local() {
let prev_token = self.file_cache.get_token_relatively(&uri, pos, -1);
match prev_token {
Some(prev) if matches!(prev.kind, Dot | DblColon) => {
let Some(dot_pos) = util::loc_to_pos(prev.loc()) else {
return Ok(None);
};
self.get_receiver_ctxs(&uri, dot_pos)?
}
_ => self.get_local_ctx(&uri, pos),
}
self.get_local_ctx(&uri, pos)
} else {
self.get_receiver_ctxs(&uri, pos)?
};
let offset = match comp_kind {
CompletionKind::Local => 0,
CompletionKind::RetriggerLocal => 0,
CompletionKind::Method => -1,
CompletionKind::Space => -1,
CompletionKind::Local => -1,
CompletionKind::LParen => 0,
CompletionKind::RetriggerMethod => -1,
};
let arg_pt = self
.get_min_expr(&uri, pos, offset)
Expand All @@ -547,7 +555,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
let nth = nth + additional;
sig_t.non_default_params()?.get(nth).cloned()
}
other if comp_kind == CompletionKind::Space => {
other if comp_kind == CompletionKind::Local => {
match other.show_acc().as_deref() {
Some("import") => {
let insert = other
Expand Down
5 changes: 5 additions & 0 deletions crates/els/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,11 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
self.shared.clear(&path);
}

#[allow(unused)]
pub fn get_file_cache(&self) -> &FileCache {
&self.file_cache
}

pub fn remove_module_entry(&mut self, uri: &NormalizedUrl) -> Option<ModuleEntry> {
let path = uri.to_file_path().ok()?;
self.shared.mod_cache.remove(&path)
Expand Down
4 changes: 4 additions & 0 deletions crates/els/tests/retrigger.er
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
l = [1, 2]
for! l, i =>
pri
print! i.bi
51 changes: 35 additions & 16 deletions crates/els/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ use std::path::Path;

use lsp_types::{
CompletionResponse, DiagnosticSeverity, DocumentSymbolResponse, FoldingRange, FoldingRangeKind,
GotoDefinitionResponse, HoverContents, InlayHintLabel, MarkedString, PublishDiagnosticsParams,
GotoDefinitionResponse, HoverContents, InlayHintLabel, MarkedString,
};
const FILE_A: &str = "tests/a.er";
const FILE_B: &str = "tests/b.er";
const FILE_C: &str = "tests/c.er";
const FILE_IMPORTS: &str = "tests/imports.er";
const FILE_INVALID_SYNTAX: &str = "tests/invalid_syntax.er";
const FILE_RETRIGGER: &str = "tests/retrigger.er";

use els::{NormalizedUrl, Server};
use erg_proc_macros::exec_new_thread;
use molc::{add_char, delete_line, oneline_range};
use serde::Deserialize;

#[test]
fn test_open() -> Result<(), Box<dyn std::error::Error>> {
Expand Down Expand Up @@ -68,6 +68,34 @@ fn test_neighbor_completion() -> Result<(), Box<dyn std::error::Error>> {
}
}

#[test]
fn test_completion_retrigger() -> Result<(), Box<dyn std::error::Error>> {
let mut client = Server::bind_fake_client();
client.request_initialize()?;
client.notify_initialized()?;
let uri = NormalizedUrl::from_file_path(Path::new(FILE_RETRIGGER).canonicalize()?)?;
client.notify_open(FILE_RETRIGGER)?;
let _ = client.wait_diagnostics()?;
client.notify_change(uri.clone().raw(), add_char(2, 7, "n"))?;
let resp = client.request_completion(uri.clone().raw(), 2, 7, "n")?;
if let Some(CompletionResponse::Array(items)) = resp {
assert!(!items.is_empty());
assert!(items.iter().any(|item| item.label == "print!"));
} else {
return Err(format!("not items: {resp:?}").into());
}
client.notify_change(uri.clone().raw(), add_char(3, 15, "t"))?;
let resp = client.request_completion(uri.raw(), 3, 15, "t")?;
if let Some(CompletionResponse::Array(items)) = resp {
assert!(!items.is_empty());
assert!(items.iter().any(|item| item.label == "bit_count"));
assert!(items.iter().any(|item| item.label == "bit_length"));
} else {
return Err(format!("not items: {resp:?}").into());
}
Ok(())
}

#[test]
fn test_rename() -> Result<(), Box<dyn std::error::Error>> {
let mut client = Server::bind_fake_client();
Expand Down Expand Up @@ -251,11 +279,9 @@ fn test_dependents_check() -> Result<(), Box<dyn std::error::Error>> {
client.wait_messages(2)?;
client.responses.clear();
client.notify_save(uri_b.clone().raw())?;
client.wait_messages(9)?;
assert!(client.responses.iter().any(|resp| resp
.to_string()
.contains("tests/b.er passed, found warns: 0")));
let diags = PublishDiagnosticsParams::deserialize(&client.responses.last().unwrap()["params"])?;
let diags = client.wait_diagnostics()?;
assert!(diags.diagnostics.is_empty());
let diags = client.wait_diagnostics()?;
assert_eq!(diags.diagnostics.len(), 1);
assert_eq!(
diags.diagnostics[0].severity,
Expand All @@ -269,24 +295,17 @@ fn test_fix_error() -> Result<(), Box<dyn std::error::Error>> {
let mut client = Server::bind_fake_client();
client.request_initialize()?;
client.notify_initialized()?;
client.wait_messages(3)?;
client.responses.clear();
client.notify_open(FILE_INVALID_SYNTAX)?;
client.wait_messages(6)?;
let msg = client.responses.last().unwrap();
let diags = PublishDiagnosticsParams::deserialize(&msg["params"])?;
let diags = client.wait_diagnostics()?;
assert_eq!(diags.diagnostics.len(), 1);
assert_eq!(
diags.diagnostics[0].severity,
Some(DiagnosticSeverity::ERROR)
);
client.responses.clear();
let uri = NormalizedUrl::from_file_path(Path::new(FILE_INVALID_SYNTAX).canonicalize()?)?;
client.notify_change(uri.clone().raw(), add_char(0, 10, " 1"))?;
client.notify_save(uri.clone().raw())?;
client.wait_messages(4)?;
let msg = client.responses.last().unwrap();
let diags = PublishDiagnosticsParams::deserialize(&msg["params"])?;
let diags = client.wait_diagnostics()?;
assert_eq!(diags.diagnostics.len(), 0);
Ok(())
}

0 comments on commit c620cfd

Please sign in to comment.