From 7509901fa0985f8fc4893a83e0275a063f072dda Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 18 Dec 2018 12:29:14 +0300 Subject: wip --- crates/ra_lsp_server/Cargo.toml | 2 +- crates/ra_vfs/Cargo.toml | 11 ++++ crates/ra_vfs/src/arena.rs | 48 +++++++++++++++ crates/ra_vfs/src/io.rs | 72 ++++++++++++++++++++++ crates/ra_vfs/src/lib.rs | 128 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 crates/ra_vfs/Cargo.toml create mode 100644 crates/ra_vfs/src/arena.rs create mode 100644 crates/ra_vfs/src/io.rs create mode 100644 crates/ra_vfs/src/lib.rs (limited to 'crates') diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index ce4f79d46..4e8e09584 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml @@ -19,7 +19,7 @@ flexi_logger = "0.10.0" log = "0.4.3" url_serde = "0.2.0" languageserver-types = "0.53.0" -walkdir = "2.2.0" +walkdir = "2.2.7" im = "12.0.0" cargo_metadata = "0.6.0" text_unit = { version = "0.1.2", features = ["serde"] } diff --git a/crates/ra_vfs/Cargo.toml b/crates/ra_vfs/Cargo.toml new file mode 100644 index 000000000..f8d49b3f5 --- /dev/null +++ b/crates/ra_vfs/Cargo.toml @@ -0,0 +1,11 @@ +[package] +edition = "2018" +name = "ra_vfs" +version = "0.1.0" +authors = ["Aleksey Kladov "] + +[dependencies] +walkdir = "2.2.7" +relative-path = "0.4.0" +rustc-hash = "1.0" +crossbeam-channel = "0.2.4" diff --git a/crates/ra_vfs/src/arena.rs b/crates/ra_vfs/src/arena.rs new file mode 100644 index 000000000..d6fad753b --- /dev/null +++ b/crates/ra_vfs/src/arena.rs @@ -0,0 +1,48 @@ +use std::{ + hash::{Hash, Hasher}, + marker::PhantomData, + ops::{Index, IndexMut}, +}; + +#[derive(Clone, Debug)] +pub(crate) struct Arena { + data: Vec, + _ty: PhantomData, +} + +pub(crate) trait ArenaId { + fn from_u32(id: u32) -> Self; + fn to_u32(self) -> u32; +} + +impl Arena { + pub fn alloc(&mut self, value: T) -> ID { + let id = self.data.len() as u32; + self.data.push(value); + ID::from_u32(id) + } +} + +impl Default for Arena { + fn default() -> Arena { + Arena { + data: Vec::new(), + _ty: PhantomData, + } + } +} + +impl Index for Arena { + type Output = T; + fn index(&self, idx: ID) -> &T { + let idx = idx.to_u32() as usize; + &self.data[idx] + } +} + +impl IndexMut for Arena { + fn index_mut(&mut self, idx: ID) -> &mut T { + let idx = idx.to_u32() as usize; + &mut self.data[idx] + } +} diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs new file mode 100644 index 000000000..f90fe0e84 --- /dev/null +++ b/crates/ra_vfs/src/io.rs @@ -0,0 +1,72 @@ +use std::{ + fs, + path::{Path, PathBuf}, + thread::JoinHandle, +}; + +use walkdir::WalkDir; +use crossbeam_channel::{Sender, Receiver}; + +pub(crate) fn start_io() -> (JoinHandle<(), Sender<()>, Receiver()>) {} + +// use crate::thread_watcher::{ThreadWatcher, Worker}; + +// #[derive(Debug)] +// pub struct FileEvent { +// pub path: PathBuf, +// pub kind: FileEventKind, +// } + +// #[derive(Debug)] +// pub enum FileEventKind { +// Add(String), +// } + +// pub fn roots_loader() -> (Worker)>, ThreadWatcher) { +// Worker::)>::spawn( +// "roots loader", +// 128, +// |input_receiver, output_sender| { +// input_receiver +// .map(|path| { +// log::debug!("loading {} ...", path.as_path().display()); +// let events = load_root(path.as_path()); +// log::debug!("... loaded {}", path.as_path().display()); +// (path, events) +// }) +// .for_each(|it| output_sender.send(it)) +// }, +// ) +// } + +// fn load_root(path: &Path) -> Vec { +// let mut res = Vec::new(); +// for entry in WalkDir::new(path) { +// let entry = match entry { +// Ok(entry) => entry, +// Err(e) => { +// log::warn!("watcher error: {}", e); +// continue; +// } +// }; +// if !entry.file_type().is_file() { +// continue; +// } +// let path = entry.path(); +// if path.extension().and_then(|os| os.to_str()) != Some("rs") { +// continue; +// } +// let text = match fs::read_to_string(path) { +// Ok(text) => text, +// Err(e) => { +// log::warn!("watcher error: {}", e); +// continue; +// } +// }; +// res.push(FileEvent { +// path: path.to_owned(), +// kind: FileEventKind::Add(text), +// }) +// } +// res +// } diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs new file mode 100644 index 000000000..8f6abadb7 --- /dev/null +++ b/crates/ra_vfs/src/lib.rs @@ -0,0 +1,128 @@ +//! VFS stands for Virtual File System. +//! +//! When doing analysis, we don't want to do any IO, we want to keep all source +//! code in memory. However, the actual source code is stored on disk, so you +//! need to get it into the memory in the first place somehow. VFS is the +//! component which does this. +//! +//! It also is responsible for watching the disk for changes, and for merging +//! editor state (modified, unsaved files) with disk state. +//! +//! VFS is based on a concept of roots: a set of directories on the file system +//! whihc are watched for changes. Typically, there will be a root for each +//! Cargo package. +mod arena; +mod io; + +use std::{ + cmp::Reverse, + path::{Path, PathBuf}, + ffi::OsStr, + sync::Arc, +}; + +use relative_path::RelativePathBuf; +use crate::arena::{ArenaId, Arena}; + +/// `RootFilter` is a predicate that checks if a file can belong to a root +struct RootFilter { + root: PathBuf, + file_filter: fn(&Path) -> bool, +} + +impl RootFilter { + fn new(root: PathBuf) -> RootFilter { + RootFilter { + root, + file_filter: rs_extension_filter, + } + } + fn can_contain(&self, path: &Path) -> bool { + (self.file_filter)(path) && path.starts_with(&self.root) + } +} + +fn rs_extension_filter(p: &Path) -> bool { + p.extension() == Some(OsStr::new("rs")) +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +pub struct VfsRoot(u32); + +impl ArenaId for VfsRoot { + fn from_u32(idx: u32) -> VfsRoot { + VfsRoot(idx) + } + fn to_u32(self) -> u32 { + self.0 + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +pub struct VfsFile(u32); + +impl ArenaId for VfsFile { + fn from_u32(idx: u32) -> VfsFile { + VfsFile(idx) + } + fn to_u32(self) -> u32 { + self.0 + } +} + +struct VfsFileData { + root: VfsRoot, + path: RelativePathBuf, + text: Arc, +} + +#[derive(Default)] +struct Vfs { + roots: Arena, + files: Arena, + // pending_changes: Vec, +} + +impl Vfs { + pub fn new(mut roots: Vec) -> Vfs { + let mut res = Vfs::default(); + + roots.sort_by_key(|it| Reverse(it.as_os_str().len())); + + for path in roots { + res.roots.alloc(RootFilter::new(path)); + } + res + } + + pub fn add_file_overlay(&mut self, path: &Path, content: String) {} + + pub fn change_file_overlay(&mut self, path: &Path, new_content: String) {} + + pub fn remove_file_overlay(&mut self, path: &Path) {} + + pub fn commit_changes(&mut self) -> Vec { + unimplemented!() + } +} + +#[derive(Debug, Clone)] +pub enum VfsChange { + AddRoot { + root: VfsRoot, + files: Vec<(VfsFile, RelativePathBuf, Arc)>, + }, + AddFile { + file: VfsFile, + root: VfsRoot, + path: RelativePathBuf, + text: Arc, + }, + RemoveFile { + file: VfsFile, + }, + ChangeFile { + file: VfsFile, + text: Arc, + }, +} -- cgit v1.2.3