diff --git a/crates/els/completion.rs b/crates/els/completion.rs index 1c97c6daf..6f7472c3e 100644 --- a/crates/els/completion.rs +++ b/crates/els/completion.rs @@ -1,6 +1,5 @@ use std::path::Path; -use lsp_types::CompletionResponse; use serde_json::Value; use erg_common::config::ErgConfig; @@ -27,13 +26,13 @@ use erg_compiler::varinfo::{AbsLocation, Mutability, VarInfo, VarKind}; use TokenKind::*; use lsp_types::{ - CompletionItem, CompletionItemKind, CompletionParams, Documentation, MarkedString, - MarkupContent, MarkupKind, Position, Range, TextEdit, + CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse, Documentation, + MarkedString, MarkupContent, MarkupKind, Position, Range, TextEdit, }; use crate::_log; use crate::server::{ELSResult, Flags, RedirectableStdout, Server}; -use crate::util::{self, loc_to_pos, NormalizedUrl}; +use crate::util::{self, loc_to_pos, loc_to_range, NormalizedUrl}; fn comp_item_kind(t: &Type, muty: Mutability) -> CompletionItemKind { match t { @@ -682,6 +681,20 @@ impl Server { .set(&mut item); item.kind = Some(comp_item_kind(&vi.t, vi.muty)); item.data = Some(Value::String(vi.def_loc.to_string())); + // s.`Function::map` => map(s) + if comp_kind.should_be_method() && item.label.starts_with("Function::") { + let receiver = self.get_receiver(&uri, pos)?; + if let Some(mut range) = receiver.as_ref().and_then(|expr| loc_to_range(expr.loc())) + { + // FIXME: + let s_receiver = self.file_cache.get_ranged(&uri, range)?.unwrap_or_default(); + range.end.character += 1; + let name = item.label.trim_start_matches("Function::"); + let remove = TextEdit::new(range, "".to_string()); + item.insert_text = Some(format!("{name}({s_receiver})")); + item.additional_text_edits = Some(vec![remove]); + } + } already_appeared.insert(item.label.clone()); result.push(item); } diff --git a/crates/els/server.rs b/crates/els/server.rs index 37518521e..6c7e5cd09 100644 --- a/crates/els/server.rs +++ b/crates/els/server.rs @@ -26,7 +26,7 @@ use erg_compiler::context::{Context, ModuleContext}; use erg_compiler::erg_parser::ast::Module; use erg_compiler::erg_parser::parse::{Parsable, SimpleParser}; use erg_compiler::error::CompileWarning; -use erg_compiler::hir::HIR; +use erg_compiler::hir::{Expr, HIR}; use erg_compiler::lower::ASTLowerer; use erg_compiler::module::{IRs, ModuleEntry, SharedCompilerResource}; use erg_compiler::ty::{HasType, Type}; @@ -1096,6 +1096,32 @@ impl Server { ctxs } + pub(crate) fn get_receiver( + &self, + uri: &NormalizedUrl, + attr_marker_pos: Position, + ) -> ELSResult> { + let maybe_token = self.file_cache.get_receiver(uri, attr_marker_pos); + if let Some(token) = maybe_token { + let expr = if let Some(visitor) = self.get_visitor(uri) { + if let Some(expr) = + loc_to_pos(token.loc()).and_then(|pos| visitor.get_min_expr(pos).cloned()) + { + Some(expr) + } else { + _log!(self, "expr not found: {token}"); + None + } + } else { + None + }; + Ok(expr) + } else { + self.send_log("token not found")?; + Ok(None) + } + } + pub(crate) fn get_receiver_and_ctxs( &self, uri: &NormalizedUrl, diff --git a/crates/els/util.rs b/crates/els/util.rs index a47595e93..8d78667ce 100644 --- a/crates/els/util.rs +++ b/crates/els/util.rs @@ -91,6 +91,15 @@ pub(crate) fn loc_to_range(loc: erg_common::error::Location) -> Option { Some(Range::new(start, end)) } +pub(crate) fn _range_to_loc(range: Range) -> erg_common::error::Location { + erg_common::error::Location::range( + range.start.line + 1, + range.start.character, + range.end.line + 1, + range.end.character, + ) +} + pub(crate) fn loc_to_pos(loc: erg_common::error::Location) -> Option { // FIXME: should `Position::new(loc.ln_begin()? - 1, loc.col_begin()?)` // but completion doesn't work (because the newline will be included)