diff --git a/crates/els/code_action.rs b/crates/els/code_action.rs index c4d87a1f..c9c5cd30 100644 --- a/crates/els/code_action.rs +++ b/crates/els/code_action.rs @@ -59,7 +59,7 @@ impl Server { character: range.end.character + 1, }, }; - let code = util::get_ranged_code_from_uri(&uri, next)?; + let code = self.file_cache.get_ranged(&uri, next)?; match code.as_ref().map(|s| &s[..]) { None => { // \n diff --git a/crates/els/file_cache.rs b/crates/els/file_cache.rs index 9ef55f1f..4b909697 100644 --- a/crates/els/file_cache.rs +++ b/crates/els/file_cache.rs @@ -18,7 +18,7 @@ use erg_compiler::erg_parser::token::{Token, TokenStream}; use crate::server::ELSResult; use crate::util::{self, NormalizedUrl}; -pub fn _get_code_from_uri(uri: &Url) -> ELSResult { +fn _get_code_from_uri(uri: &Url) -> ELSResult { let path = uri .to_file_path() .or_else(|_| util::denormalize(uri.clone()).to_file_path()) @@ -152,6 +152,43 @@ impl FileCache { } } + pub(crate) fn get_line(&self, uri: &NormalizedUrl, line: u32) -> Option<&str> { + self.get(uri).ok().and_then(|ent| ent.get_line(line)) + } + + pub(crate) fn get_ranged( + &self, + uri: &NormalizedUrl, + range: Range, + ) -> ELSResult> { + let file = self.get(uri)?; + let mut code = String::new(); + for (i, line) in file.code.lines().enumerate() { + if i >= range.start.line as usize && i <= range.end.line as usize { + if i == range.start.line as usize && i == range.end.line as usize { + if line.len() < range.end.character as usize { + return Ok(None); + } + code.push_str( + &line[range.start.character as usize..range.end.character as usize], + ); + } else if i == range.start.line as usize { + code.push_str(&line[range.start.character as usize..]); + code.push('\n'); + } else if i == range.end.line as usize { + if line.len() < range.end.character as usize { + return Ok(None); + } + code.push_str(&line[..range.end.character as usize]); + } else { + code.push_str(line); + code.push('\n'); + } + } + } + Ok(Some(code)) + } + pub(crate) fn update(&self, uri: &NormalizedUrl, code: String, ver: Option) { let entry = unsafe { self.files.as_ref() }.get(uri); if let Some(entry) = entry { diff --git a/crates/els/hir_visitor.rs b/crates/els/hir_visitor.rs index 5b7b4514..7f1635fe 100644 --- a/crates/els/hir_visitor.rs +++ b/crates/els/hir_visitor.rs @@ -5,6 +5,7 @@ use erg_compiler::hir::*; use erg_compiler::varinfo::VarInfo; use lsp_types::Position; +use crate::file_cache::FileCache; use crate::util::{self, NormalizedUrl}; /// This struct provides: @@ -13,16 +14,18 @@ use crate::util::{self, NormalizedUrl}; /// * cursor(`Token`) -> `VarInfo` mapping (`get_info`) pub struct HIRVisitor<'a> { hir: &'a HIR, + file_cache: &'a FileCache, uri: NormalizedUrl, strict_cmp: bool, } impl<'a> HIRVisitor<'a> { - pub fn new(hir: &'a HIR, uri: NormalizedUrl, strict_cmp: bool) -> Self { + pub fn new(hir: &'a HIR, file_cache: &'a FileCache, uri: NormalizedUrl) -> Self { Self { hir, + file_cache, uri, - strict_cmp, + strict_cmp: !cfg!(feature = "py_compat"), } } @@ -46,10 +49,16 @@ impl<'a> HIRVisitor<'a> { fn is_new_final_line(&self, chunk: &Expr, pos: Position) -> bool { let ln_end = chunk.ln_end().unwrap_or(0); - let line = util::get_line_from_uri(&self.uri, ln_end).unwrap(); + let line = self + .file_cache + .get_line(&self.uri, ln_end) + .unwrap_or_default(); let indent_len = line.len() - line.trim_start_matches(' ').len(); let cond = ln_end == pos.line && pos.character as usize == indent_len + 1; - matches!(chunk, Expr::Call(_) | Expr::Lambda(_) | Expr::Def(_) | Expr::ClassDef(_) if cond) + cond && matches!( + chunk, + Expr::Call(_) | Expr::Lambda(_) | Expr::Def(_) | Expr::ClassDef(_) + ) } fn get_expr_ns(&self, cur_ns: Vec, chunk: &Expr, pos: Position) -> Option> { diff --git a/crates/els/hover.rs b/crates/els/hover.rs index aef03b64..034b93bf 100644 --- a/crates/els/hover.rs +++ b/crates/els/hover.rs @@ -129,7 +129,12 @@ impl Server { }; // display the definition line if vi.kind.is_defined() { - code_block += util::get_line_from_path(file_path, line)?.trim_start(); + let uri = NormalizedUrl::try_from(file_path.as_path())?; + code_block += self + .file_cache + .get_line(&uri, line) + .unwrap_or_default() + .trim_start(); match code_block.chars().last() { Some('=' | '>') => { code_block += " ..."; diff --git a/crates/els/server.rs b/crates/els/server.rs index 4c7ca60d..d9cb90cb 100644 --- a/crates/els/server.rs +++ b/crates/els/server.rs @@ -541,7 +541,7 @@ impl Server { .get(uri)? .object .as_ref() - .map(|hir| HIRVisitor::new(hir, uri.clone(), !cfg!(feature = "py_compat"))) + .map(|hir| HIRVisitor::new(hir, &self.file_cache, uri.clone())) } pub(crate) fn get_local_ctx(&self, uri: &NormalizedUrl, pos: Position) -> Vec<&Context> { diff --git a/crates/els/util.rs b/crates/els/util.rs index 971c43c7..b9a8bf4c 100644 --- a/crates/els/util.rs +++ b/crates/els/util.rs @@ -1,7 +1,6 @@ use std::fmt; -use std::fs::File; -use std::io::{BufRead, BufReader, Read}; -use std::path::Path; +use std::fs::{metadata, Metadata}; +use std::path::{Path, PathBuf}; use erg_common::normalize_path; use erg_common::traits::{DequeStream, Locational}; @@ -10,7 +9,6 @@ use erg_compiler::erg_parser::token::{Token, TokenStream}; use lsp_types::{Position, Range, Url}; -use crate::file_cache::_get_code_from_uri; use crate::server::ELSResult; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -60,13 +58,13 @@ impl NormalizedUrl { } } -pub fn loc_to_range(loc: erg_common::error::Location) -> Option { +pub(crate) fn loc_to_range(loc: erg_common::error::Location) -> Option { let start = Position::new(loc.ln_begin()?.saturating_sub(1), loc.col_begin()?); let end = Position::new(loc.ln_end()?.saturating_sub(1), loc.col_end()?); Some(Range::new(start, end)) } -pub fn loc_to_pos(loc: erg_common::error::Location) -> Option { +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) let start = Position::new(loc.ln_begin()?.saturating_sub(1), loc.col_begin()? + 1); @@ -82,7 +80,7 @@ pub fn _pos_to_loc(pos: Position) -> erg_common::error::Location { ) } -pub fn pos_in_loc(loc: &L, pos: Position) -> bool { +pub(crate) fn pos_in_loc(loc: &L, pos: Position) -> bool { let ln_begin = loc.ln_begin().unwrap_or(0); let ln_end = loc.ln_end().unwrap_or(0); let in_lines = (ln_begin..=ln_end).contains(&(pos.line + 1)); @@ -94,7 +92,7 @@ pub fn pos_in_loc(loc: &L, pos: Position) -> bool { } } -pub fn pos_to_byte_index(src: &str, pos: Position) -> usize { +pub(crate) fn pos_to_byte_index(src: &str, pos: Position) -> usize { if src.is_empty() { return 0; } @@ -115,7 +113,10 @@ pub fn pos_to_byte_index(src: &str, pos: Position) -> usize { src.char_indices().last().unwrap().0 + 1 } -pub fn get_token_from_stream(stream: &TokenStream, pos: Position) -> ELSResult> { +pub(crate) fn get_token_from_stream( + stream: &TokenStream, + pos: Position, +) -> ELSResult> { for token in stream.iter() { if pos_in_loc(token, pos) { return Ok(Some(token.clone())); @@ -124,67 +125,18 @@ pub fn get_token_from_stream(stream: &TokenStream, pos: Position) -> ELSResult ELSResult> { +pub(crate) fn get_metadata_from_uri(uri: &Url) -> ELSResult { let path = uri.to_file_path().unwrap(); - let file = File::open(path)?; - let reader = BufReader::new(file); - let mut code = String::new(); - for (i, line) in reader.lines().enumerate() { - if i >= range.start.line as usize && i <= range.end.line as usize { - let line = line?; - if i == range.start.line as usize && i == range.end.line as usize { - if line.len() < range.end.character as usize { - return Ok(None); - } - code.push_str(&line[range.start.character as usize..range.end.character as usize]); - } else if i == range.start.line as usize { - code.push_str(&line[range.start.character as usize..]); - code.push('\n'); - } else if i == range.end.line as usize { - if line.len() < range.end.character as usize { - return Ok(None); - } - code.push_str(&line[..range.end.character as usize]); - } else { - code.push_str(&line); - code.push('\n'); - } - } - } - Ok(Some(code)) + Ok(metadata(path)?) } -pub fn get_line_from_uri(uri: &Url, line: u32) -> ELSResult { - let code = _get_code_from_uri(uri)?; - let line = code - .lines() - .nth(line.saturating_sub(1) as usize) - .unwrap_or(""); - Ok(line.to_string()) -} - -pub fn get_metadata_from_uri(uri: &Url) -> ELSResult { - let path = uri.to_file_path().unwrap(); - Ok(std::fs::metadata(path)?) -} - -pub fn get_line_from_path(path: &Path, line: u32) -> ELSResult { - let mut code = String::new(); - File::open(path)?.read_to_string(&mut code)?; - let line = code - .lines() - .nth(line.saturating_sub(1) as usize) - .unwrap_or(""); - Ok(line.to_string()) -} - -pub fn uri_to_path(uri: &NormalizedUrl) -> std::path::PathBuf { +pub(crate) fn uri_to_path(uri: &NormalizedUrl) -> PathBuf { normalize_path( uri.to_file_path() .unwrap_or_else(|_| denormalize(uri.clone().raw()).to_file_path().unwrap()), ) } -pub fn denormalize(uri: Url) -> Url { +pub(crate) fn denormalize(uri: Url) -> Url { Url::parse(&uri.as_str().replace("c:", "file:///c%3A")).unwrap() }