Use absolute VfsPath and remove file preloading

We currently don't enable any workspace features.
Reading file on demand can be more robust and memory efficient.

Fixes #21
This commit is contained in:
oxalica 2022-09-28 17:52:41 +08:00
parent ec7d3490f2
commit 31b714f3da
6 changed files with 20 additions and 157 deletions

87
Cargo.lock generated
View file

@ -2,15 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.65"
@ -29,15 +20,6 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"memchr",
]
[[package]]
name = "builtin"
version = "0.0.0"
@ -102,12 +84,6 @@ dependencies = [
"once_cell",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.1.0"
@ -117,19 +93,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "globset"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
dependencies = [
"aho-corasick",
"bstr",
"fnv",
"log",
"regex",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -172,24 +135,6 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "ignore"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
dependencies = [
"crossbeam-utils",
"globset",
"lazy_static",
"log",
"memchr",
"regex",
"same-file",
"thread_local",
"walkdir",
"winapi-util",
]
[[package]]
name = "indexmap"
version = "1.9.1"
@ -307,7 +252,6 @@ version = "0.0.0"
dependencies = [
"crossbeam-channel",
"ide",
"ignore",
"indexmap",
"log",
"lsp-server",
@ -472,8 +416,6 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
@ -556,15 +498,6 @@ dependencies = [
"syn",
]
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -800,17 +733,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -827,15 +749,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

View file

@ -12,6 +12,7 @@ pub struct FileId(pub u32);
pub struct SourceRootId(pub u32);
/// An absolute path in format `(/.+)*`
/// Currently, it represent an absolute filesytem path.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VfsPath(String);

View file

@ -8,7 +8,6 @@ rust-version = "1.62"
[dependencies]
crossbeam-channel = "0.5.6"
ide = { path = "../ide" }
ignore = "0.4.18"
indexmap = "1.9.1"
log = "0.4.17"
lsp-server = "0.6.0"

View file

@ -7,8 +7,7 @@ mod vfs;
use lsp_server::{Connection, ErrorCode};
use lsp_types::InitializeParams;
use std::path::PathBuf;
use std::{env, fmt};
use std::fmt;
pub(crate) use server::{Server, StateSnapshot};
pub(crate) use vfs::{LineMap, Vfs};
@ -36,18 +35,9 @@ pub fn main_loop(conn: Connection) -> Result<()> {
tracing::info!("Init params: {}", init_params);
let init_params = serde_json::from_value::<InitializeParams>(init_params)?;
let workspace_path = (|| -> Option<PathBuf> {
if let Some(folders) = &init_params.workspace_folders {
return folders.get(0)?.uri.to_file_path().ok();
}
if let Some(uri) = &init_params.root_uri {
return uri.to_file_path().ok();
}
env::current_dir().ok()
})();
let mut state = Server::new(conn.sender.clone(), workspace_path);
state.run(conn.receiver)?;
let mut server = Server::new(conn.sender.clone());
server.run(conn.receiver, init_params)?;
tracing::info!("Leaving main loop");
Ok(())

View file

@ -1,21 +1,20 @@
use crate::{convert, handler, Result, Vfs};
use crossbeam_channel::{Receiver, Sender};
use ide::{Analysis, AnalysisHost, Cancelled, VfsPath};
use ide::{Analysis, AnalysisHost, Cancelled};
use lsp_server::{ErrorCode, Message, Notification, ReqQueue, Request, RequestId, Response};
use lsp_types::notification::Notification as _;
use lsp_types::{
notification as notif, request as req, ConfigurationItem, ConfigurationParams, Diagnostic,
MessageType, NumberOrString, PublishDiagnosticsParams, ShowMessageParams, Url,
InitializeParams, MessageType, NumberOrString, PublishDiagnosticsParams, ShowMessageParams,
Url,
};
use serde::Deserialize;
use std::cell::Cell;
use std::collections::HashSet;
use std::panic::UnwindSafe;
use std::path::PathBuf;
use std::sync::{Arc, Once, RwLock};
use std::{fs, panic, thread};
use std::{panic, thread};
const FILTER_FILE_EXTENTION: &str = "nix";
const CONFIG_KEY: &str = "nil";
#[derive(Debug, Clone, Default, Deserialize)]
@ -47,17 +46,10 @@ pub struct Server {
task_tx: Sender<Task>,
event_tx: Sender<Event>,
event_rx: Receiver<Event>,
// Immutable settings.
workspace_root: Option<PathBuf>,
}
impl Server {
pub fn new(lsp_tx: Sender<Message>, workspace_root: Option<PathBuf>) -> Self {
// Vfs root must be absolute.
let workspace_root = workspace_root.and_then(|root| root.canonicalize().ok());
let vfs = Vfs::new(workspace_root.clone().unwrap_or_else(|| PathBuf::from("/")));
pub fn new(lsp_tx: Sender<Message>) -> Self {
let (task_tx, task_rx) = crossbeam_channel::unbounded();
let (event_tx, event_rx) = crossbeam_channel::unbounded();
let worker_cnt = thread::available_parallelism().map_or(1, |n| n.get());
@ -73,7 +65,7 @@ impl Server {
Self {
host: Default::default(),
vfs: Arc::new(RwLock::new(vfs)),
vfs: Arc::new(RwLock::new(Vfs::new())),
opened_files: Default::default(),
config: Arc::new(Config::default()),
is_shutdown: false,
@ -83,8 +75,6 @@ impl Server {
task_tx,
event_tx,
event_rx,
workspace_root,
}
}
@ -96,31 +86,7 @@ impl Server {
}
}
pub fn run(&mut self, lsp_rx: Receiver<Message>) -> Result<()> {
if let Some(root) = &self.workspace_root {
let mut vfs = self.vfs.write().unwrap();
for entry in ignore::WalkBuilder::new(root).follow_links(false).build() {
(|| -> Option<()> {
let entry = entry.ok()?;
if entry
.path()
.extension()
.map_or(true, |ext| ext != FILTER_FILE_EXTENTION)
{
return None;
}
let relative_path = entry.path().strip_prefix(root).ok()?;
let vpath = VfsPath::from_path(relative_path)?;
let text = fs::read_to_string(entry.path()).ok().unwrap_or_default();
vfs.set_path_content(vpath, text);
Some(())
})();
}
drop(vfs);
self.apply_vfs_change();
}
pub fn run(&mut self, lsp_rx: Receiver<Message>, _init_params: InitializeParams) -> Result<()> {
loop {
crossbeam_channel::select! {
recv(lsp_rx) -> msg => {

View file

@ -2,16 +2,16 @@ use crate::Result;
use ide::{Change, FileId, FileSet, SourceRoot, VfsPath};
use lsp_types::Url;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::{fmt, mem};
use text_size::{TextRange, TextSize};
/// Vfs stores file contents with line mapping, and a mapping between
/// filesystem paths and `FileId`s.
/// The query system is built on `FileId`'s.
pub struct Vfs {
// FIXME: Currently this list is append-only.
files: Vec<(Arc<str>, Arc<LineMap>)>,
/// The root directory, which must be absolute.
local_root: PathBuf,
local_file_set: FileSet,
root_changed: bool,
change: Change,
@ -21,17 +21,16 @@ impl fmt::Debug for Vfs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Vfs")
.field("file_cnt", &self.files.len())
.field("local_root", &self.local_root)
.field("root_changed", &self.root_changed)
.field("change", &self.change)
.finish_non_exhaustive()
}
}
impl Vfs {
pub fn new(local_root: PathBuf) -> Self {
assert!(local_root.is_absolute());
pub fn new() -> Self {
Self {
files: Vec::new(),
local_root,
local_file_set: FileSet::default(),
root_changed: false,
change: Change::default(),
@ -42,10 +41,7 @@ impl Vfs {
let path = uri
.to_file_path()
.map_err(|_| format!("Non-file URI: {}", uri))?;
let relative_path = path
.strip_prefix(&self.local_root)
.map_err(|_| format!("URI outside workspace: {}", uri))?;
Ok(VfsPath::from_path(relative_path).expect("URI is UTF-8"))
Ok(VfsPath::from_path(&path).expect("URI is UTF-8"))
}
pub fn set_uri_content(&mut self, uri: &Url, text: String) -> Result<()> {
@ -118,10 +114,8 @@ impl Vfs {
}
pub fn uri_for_file(&self, file: FileId) -> Url {
let vpath = self.local_file_set.path_for_file(file).as_str();
assert!(!vpath.is_empty(), "Root is a directory");
let path = self.local_root.join(vpath.strip_prefix('/').unwrap());
Url::from_file_path(path).expect("Root is absolute")
let vpath = self.local_file_set.path_for_file(file);
Url::from_file_path(vpath.as_str()).expect("VfsPath is absolute")
}
pub fn take_change(&mut self) -> Change {