1
1
mirror of https://github.com/oxalica/nil.git synced 2024-11-27 00:06:26 +03:00

Populate source roots during initialization

This commit is contained in:
oxalica 2022-08-10 02:47:07 +08:00
parent 2d0b9b0cf5
commit d80888be7f
5 changed files with 115 additions and 41 deletions

21
Cargo.lock generated
View File

@ -240,6 +240,7 @@ dependencies = [
"serde",
"serde_json",
"text-size",
"walkdir",
]
[[package]]
@ -551,6 +552,15 @@ 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"
@ -711,6 +721,17 @@ dependencies = [
"serde",
]
[[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 = "wasi"
version = "0.11.0+wasi-snapshot-preview1"

View File

@ -7,6 +7,8 @@ Super fast incremental analysis! Scans `all-packages.nix` in less than 0.1s and
## Features
- [x] Goto definition. `textDocument/definition`
- [x] Local bindings.
- [x] Target of relative paths.
- [x] Find references. `textDocument/reference`
- [x] Local binding references.
- [x] With expression references.

View File

@ -19,5 +19,6 @@ lsp-types = "0.93.0"
serde = "1.0.140"
serde_json = "1.0.82"
text-size = "1.1.0"
walkdir = "2.3.2"
nil = { path = ".." }

View File

@ -4,31 +4,54 @@ use crossbeam_channel::{Receiver, Sender};
use lsp_server::{ErrorCode, Message, Notification, Request, Response};
use lsp_types::notification::Notification as _;
use lsp_types::{notification as notif, request as req, PublishDiagnosticsParams, Url};
use nil::{Analysis, AnalysisHost};
use nil::{Analysis, AnalysisHost, VfsPath};
use std::collections::HashSet;
use std::fs;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use walkdir::WalkDir;
const MAX_DIAGNOSTICS_CNT: usize = 128;
pub struct State {
host: AnalysisHost,
vfs: Arc<RwLock<Vfs>>,
opened_files: Arc<RwLock<HashSet<Url>>>,
workspace_root: Option<PathBuf>,
sender: Sender<Message>,
is_shutdown: bool,
}
impl State {
pub fn new(responder: Sender<Message>, workspace_root: Option<PathBuf>) -> Self {
let vfs = Vfs::new(workspace_root.unwrap_or_else(|| PathBuf::from("/")));
let vfs = Vfs::new(workspace_root.clone().unwrap_or_else(|| PathBuf::from("/")));
Self {
host: Default::default(),
vfs: Arc::new(RwLock::new(vfs)),
opened_files: Default::default(),
workspace_root,
sender: responder,
is_shutdown: false,
}
}
pub fn run(&mut self, lsp_receiver: Receiver<Message>) -> Result<()> {
if let Some(root) = &self.workspace_root {
let mut vfs = self.vfs.write().unwrap();
for entry in WalkDir::new(root).follow_links(false).sort_by_file_name() {
(|| -> Option<()> {
let entry = entry.ok()?;
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();
vfs.set_path_content(vpath, text);
Some(())
})();
}
drop(vfs);
self.apply_vfs_change();
}
for msg in &lsp_receiver {
match msg {
Message::Request(req) => self.dispatch_request(req),
@ -68,10 +91,16 @@ impl State {
fn dispatch_notification(&mut self, notif: Notification) {
NotificationDispatcher(self, Some(notif))
.on_sync_mut::<notif::DidOpenTextDocument>(|st, params| {
st.set_vfs_file_content(&params.text_document.uri, Some(params.text_document.text));
let uri = &params.text_document.uri;
st.opened_files.write().unwrap().insert(uri.clone());
st.set_vfs_file_content(uri, Some(params.text_document.text));
})
.on_sync_mut::<notif::DidCloseTextDocument>(|st, params| {
st.set_vfs_file_content(&params.text_document.uri, None);
// N.B. Don't clear text here.
st.opened_files
.write()
.unwrap()
.remove(&params.text_document.uri);
})
.on_sync_mut::<notif::DidChangeTextDocument>(|st, params| {
if let Some(chg) = params.content_changes.into_iter().next() {
@ -95,31 +124,48 @@ impl State {
}
fn set_vfs_file_content(&mut self, uri: &Url, text: Option<String>) {
let mut vfs = self.vfs.write().unwrap();
let file = vfs.set_uri_content(uri, text);
self.vfs.write().unwrap().set_uri_content(uri, text);
self.apply_vfs_change();
}
fn apply_vfs_change(&mut self) {
let mut vfs = self.vfs.write().unwrap();
let change = vfs.take_change();
let file_changes = change
.file_changes
.iter()
.map(|(file, text)| (*file, text.is_some()))
.collect::<Vec<_>>();
log::debug!("Change: {:?}", change);
self.host.apply_change(change);
// Currently we push down changes immediately.
let diagnostics = file
.and_then(|file| {
let line_map = vfs.get_line_map(file)?;
let diags = self.host.snapshot().diagnostics(file).ok()?;
let diags = diags
.into_iter()
.take(MAX_DIAGNOSTICS_CNT)
.filter_map(|diag| convert::to_diagnostic(line_map, diag))
.collect::<Vec<_>>();
Some(diags)
})
.unwrap_or_default();
self.send_notification::<notif::PublishDiagnostics>(PublishDiagnosticsParams {
uri: uri.clone(),
diagnostics,
version: None,
});
let snap = self.host.snapshot();
let opened_files = self.opened_files.read().unwrap();
for (file, has_text) in file_changes {
let uri = vfs.get_uri_for_file(file).unwrap();
if !opened_files.contains(&uri) {
continue;
}
let diagnostics = has_text
.then(|| {
let line_map = vfs.get_line_map(file)?;
let diags = snap.diagnostics(file).ok()?;
let diags = diags
.into_iter()
.take(MAX_DIAGNOSTICS_CNT)
.filter_map(|diag| convert::to_diagnostic(line_map, diag))
.collect::<Vec<_>>();
Some(diags)
})
.flatten()
.unwrap_or_default();
self.send_notification::<notif::PublishDiagnostics>(PublishDiagnosticsParams {
uri,
diagnostics,
version: None,
});
}
}
}

View File

@ -49,24 +49,28 @@ impl Vfs {
pub fn set_uri_content(&mut self, uri: &Url, text: Option<String>) -> Option<FileId> {
let vpath = self.uri_to_vpath(uri)?;
self.set_path_content(vpath, text)
}
pub fn set_path_content(&mut self, path: VfsPath, text: Option<String>) -> Option<FileId> {
let content = text.and_then(LineMap::normalize);
let (file, (text, line_map)) =
match (self.local_file_set.get_file_for_path(&vpath), content) {
(Some(file), None) => {
self.local_file_set.remove_file(file);
self.root_changed = true;
self.files[file.0 as usize] = None;
return None;
}
(None, None) => return None,
(Some(file), Some(content)) => (file, content),
(None, Some(content)) => {
let file = self.alloc_file_id();
self.local_file_set.insert(file, vpath);
self.root_changed = true;
(file, content)
}
};
let (file, (text, line_map)) = match (self.local_file_set.get_file_for_path(&path), content)
{
(Some(file), None) => {
self.local_file_set.remove_file(file);
self.root_changed = true;
self.files[file.0 as usize] = None;
return None;
}
(None, None) => return None,
(Some(file), Some(content)) => (file, content),
(None, Some(content)) => {
let file = self.alloc_file_id();
self.local_file_set.insert(file, path);
self.root_changed = true;
(file, content)
}
};
let text = <Arc<str>>::from(text);
self.change.change_file(file, Some(text.clone()));
self.files[file.0 as usize] = Some((text, line_map));