fix(els): file operation bugs

This commit is contained in:
Shunsuke Shibayama 2023-04-30 18:00:42 +09:00
parent 863f1f2fdb
commit 6c665157bc
6 changed files with 73 additions and 70 deletions

View file

@ -59,7 +59,7 @@ impl<Checker: BuildRunnable> Server<Checker> {
character: range.end.character + 1, 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[..]) { match code.as_ref().map(|s| &s[..]) {
None => { None => {
// \n // \n

View file

@ -18,7 +18,7 @@ use erg_compiler::erg_parser::token::{Token, TokenStream};
use crate::server::ELSResult; use crate::server::ELSResult;
use crate::util::{self, NormalizedUrl}; use crate::util::{self, NormalizedUrl};
pub fn _get_code_from_uri(uri: &Url) -> ELSResult<String> { fn _get_code_from_uri(uri: &Url) -> ELSResult<String> {
let path = uri let path = uri
.to_file_path() .to_file_path()
.or_else(|_| util::denormalize(uri.clone()).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<Option<String>> {
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<i32>) { pub(crate) fn update(&self, uri: &NormalizedUrl, code: String, ver: Option<i32>) {
let entry = unsafe { self.files.as_ref() }.get(uri); let entry = unsafe { self.files.as_ref() }.get(uri);
if let Some(entry) = entry { if let Some(entry) = entry {

View file

@ -5,6 +5,7 @@ use erg_compiler::hir::*;
use erg_compiler::varinfo::VarInfo; use erg_compiler::varinfo::VarInfo;
use lsp_types::Position; use lsp_types::Position;
use crate::file_cache::FileCache;
use crate::util::{self, NormalizedUrl}; use crate::util::{self, NormalizedUrl};
/// This struct provides: /// This struct provides:
@ -13,16 +14,18 @@ use crate::util::{self, NormalizedUrl};
/// * cursor(`Token`) -> `VarInfo` mapping (`get_info`) /// * cursor(`Token`) -> `VarInfo` mapping (`get_info`)
pub struct HIRVisitor<'a> { pub struct HIRVisitor<'a> {
hir: &'a HIR, hir: &'a HIR,
file_cache: &'a FileCache,
uri: NormalizedUrl, uri: NormalizedUrl,
strict_cmp: bool, strict_cmp: bool,
} }
impl<'a> HIRVisitor<'a> { 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 { Self {
hir, hir,
file_cache,
uri, 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 { fn is_new_final_line(&self, chunk: &Expr, pos: Position) -> bool {
let ln_end = chunk.ln_end().unwrap_or(0); 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 indent_len = line.len() - line.trim_start_matches(' ').len();
let cond = ln_end == pos.line && pos.character as usize == indent_len + 1; 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<Str>, chunk: &Expr, pos: Position) -> Option<Vec<Str>> { fn get_expr_ns(&self, cur_ns: Vec<Str>, chunk: &Expr, pos: Position) -> Option<Vec<Str>> {

View file

@ -129,7 +129,12 @@ impl<Checker: BuildRunnable> Server<Checker> {
}; };
// display the definition line // display the definition line
if vi.kind.is_defined() { 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() { match code_block.chars().last() {
Some('=' | '>') => { Some('=' | '>') => {
code_block += " ..."; code_block += " ...";

View file

@ -541,7 +541,7 @@ impl<Checker: BuildRunnable> Server<Checker> {
.get(uri)? .get(uri)?
.object .object
.as_ref() .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> { pub(crate) fn get_local_ctx(&self, uri: &NormalizedUrl, pos: Position) -> Vec<&Context> {

View file

@ -1,7 +1,6 @@
use std::fmt; use std::fmt;
use std::fs::File; use std::fs::{metadata, Metadata};
use std::io::{BufRead, BufReader, Read}; use std::path::{Path, PathBuf};
use std::path::Path;
use erg_common::normalize_path; use erg_common::normalize_path;
use erg_common::traits::{DequeStream, Locational}; 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 lsp_types::{Position, Range, Url};
use crate::file_cache::_get_code_from_uri;
use crate::server::ELSResult; use crate::server::ELSResult;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -60,13 +58,13 @@ impl NormalizedUrl {
} }
} }
pub fn loc_to_range(loc: erg_common::error::Location) -> Option<Range> { pub(crate) fn loc_to_range(loc: erg_common::error::Location) -> Option<Range> {
let start = Position::new(loc.ln_begin()?.saturating_sub(1), loc.col_begin()?); 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()?); let end = Position::new(loc.ln_end()?.saturating_sub(1), loc.col_end()?);
Some(Range::new(start, end)) Some(Range::new(start, end))
} }
pub fn loc_to_pos(loc: erg_common::error::Location) -> Option<Position> { pub(crate) fn loc_to_pos(loc: erg_common::error::Location) -> Option<Position> {
// FIXME: should `Position::new(loc.ln_begin()? - 1, loc.col_begin()?)` // FIXME: should `Position::new(loc.ln_begin()? - 1, loc.col_begin()?)`
// but completion doesn't work (because the newline will be included) // 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); 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<L: Locational>(loc: &L, pos: Position) -> bool { pub(crate) fn pos_in_loc<L: Locational>(loc: &L, pos: Position) -> bool {
let ln_begin = loc.ln_begin().unwrap_or(0); let ln_begin = loc.ln_begin().unwrap_or(0);
let ln_end = loc.ln_end().unwrap_or(0); let ln_end = loc.ln_end().unwrap_or(0);
let in_lines = (ln_begin..=ln_end).contains(&(pos.line + 1)); let in_lines = (ln_begin..=ln_end).contains(&(pos.line + 1));
@ -94,7 +92,7 @@ pub fn pos_in_loc<L: Locational>(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() { if src.is_empty() {
return 0; return 0;
} }
@ -115,7 +113,10 @@ pub fn pos_to_byte_index(src: &str, pos: Position) -> usize {
src.char_indices().last().unwrap().0 + 1 src.char_indices().last().unwrap().0 + 1
} }
pub fn get_token_from_stream(stream: &TokenStream, pos: Position) -> ELSResult<Option<Token>> { pub(crate) fn get_token_from_stream(
stream: &TokenStream,
pos: Position,
) -> ELSResult<Option<Token>> {
for token in stream.iter() { for token in stream.iter() {
if pos_in_loc(token, pos) { if pos_in_loc(token, pos) {
return Ok(Some(token.clone())); return Ok(Some(token.clone()));
@ -124,67 +125,18 @@ pub fn get_token_from_stream(stream: &TokenStream, pos: Position) -> ELSResult<O
Ok(None) Ok(None)
} }
pub fn get_ranged_code_from_uri(uri: &Url, range: Range) -> ELSResult<Option<String>> { pub(crate) fn get_metadata_from_uri(uri: &Url) -> ELSResult<Metadata> {
let path = uri.to_file_path().unwrap(); let path = uri.to_file_path().unwrap();
let file = File::open(path)?; Ok(metadata(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))
} }
pub fn get_line_from_uri(uri: &Url, line: u32) -> ELSResult<String> { pub(crate) fn uri_to_path(uri: &NormalizedUrl) -> PathBuf {
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<std::fs::Metadata> {
let path = uri.to_file_path().unwrap();
Ok(std::fs::metadata(path)?)
}
pub fn get_line_from_path(path: &Path, line: u32) -> ELSResult<String> {
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 {
normalize_path( normalize_path(
uri.to_file_path() uri.to_file_path()
.unwrap_or_else(|_| denormalize(uri.clone().raw()).to_file_path().unwrap()), .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() Url::parse(&uri.as_str().replace("c:", "file:///c%3A")).unwrap()
} }