Skip to content

Commit 45afd98

Browse files
committed
Include files in vfs on demand
1 parent 73ab709 commit 45afd98

File tree

9 files changed

+166
-29
lines changed

9 files changed

+166
-29
lines changed

crates/hir-expand/src/builtin_fn_macro.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -582,19 +582,28 @@ fn include_expand(
582582
}
583583

584584
fn include_bytes_expand(
585-
_db: &dyn AstDatabase,
586-
_arg_id: MacroCallId,
585+
db: &dyn AstDatabase,
586+
arg_id: MacroCallId,
587587
tt: &tt::Subtree,
588588
) -> ExpandResult<ExpandedEager> {
589-
if let Err(e) = parse_string(tt) {
590-
return ExpandResult::only_err(e);
591-
}
589+
let path = match parse_string(tt) {
590+
Ok(it) => it,
591+
Err(e) => return ExpandResult::only_err(e),
592+
};
593+
594+
let file_id = match relative_file(db, arg_id, &path, true) {
595+
Ok(file_id) => file_id,
596+
Err(_) => {
597+
return ExpandResult::ok(ExpandedEager::new(quote!(&[0u8; _])));
598+
}
599+
};
600+
601+
let text = db.file_text(file_id);
592602

593-
// FIXME: actually read the file here if the user asked for macro expansion
594603
let res = tt::Subtree {
595604
delimiter: None,
596605
token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
597-
text: r#"b"""#.into(),
606+
text: format!("b{text:?}").into(),
598607
id: tt::TokenId::unspecified(),
599608
}))],
600609
};
@@ -611,10 +620,6 @@ fn include_str_expand(
611620
Err(e) => return ExpandResult::only_err(e),
612621
};
613622

614-
// FIXME: we're not able to read excluded files (which is most of them because
615-
// it's unusual to `include_str!` a Rust file), but we can return an empty string.
616-
// Ideally, we'd be able to offer a precise expansion if the user asks for macro
617-
// expansion.
618623
let file_id = match relative_file(db, arg_id, &path, true) {
619624
Ok(file_id) => file_id,
620625
Err(_) => {

crates/hir-expand/src/quote.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ macro_rules! __quote {
105105
( < ) => {$crate::__quote!(@PUNCT '<')};
106106
( > ) => {$crate::__quote!(@PUNCT '>')};
107107
( ! ) => {$crate::__quote!(@PUNCT '!')};
108+
( _ ) => {$crate::__quote!(@PUNCT '_')};
108109

109110
( $first:tt $($tail:tt)+ ) => {
110111
{
@@ -188,6 +189,7 @@ macro_rules! impl_to_to_tokentrees {
188189
}
189190

190191
impl_to_to_tokentrees! {
192+
u8 => self { tt::Literal{text: format!("{self}u8").into(), id: tt::TokenId::unspecified()} };
191193
u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
192194
usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
193195
i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };

crates/rust-analyzer/src/cli/load_cargo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub fn load_workspace(
5353
) -> Result<(AnalysisHost, vfs::Vfs, Option<ProcMacroServer>)> {
5454
let (sender, receiver) = unbounded();
5555
let mut vfs = vfs::Vfs::default();
56-
let mut loader = {
56+
let loader = {
5757
let loader =
5858
vfs_notify::NotifyHandle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
5959
Box::new(loader)

crates/rust-analyzer/src/global_state.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub(crate) struct GlobalState {
5252
sender: Sender<lsp_server::Message>,
5353
req_queue: ReqQueue,
5454
pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
55-
pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
55+
pub(crate) loader: Handle<Arc<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
5656
pub(crate) config: Arc<Config>,
5757
pub(crate) analysis_host: AnalysisHost,
5858
pub(crate) diagnostics: DiagnosticCollection,
@@ -123,11 +123,15 @@ impl std::panic::UnwindSafe for GlobalStateSnapshot {}
123123

124124
impl GlobalState {
125125
pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
126+
let vfs = Arc::new(RwLock::new((vfs::Vfs::default(), NoHashHashMap::default())));
126127
let loader = {
127128
let (sender, receiver) = unbounded::<vfs::loader::Message>();
128129
let handle: vfs_notify::NotifyHandle =
129130
vfs::loader::Handle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
130-
let handle = Box::new(handle) as Box<dyn vfs::loader::Handle>;
131+
let (vfs, _) = &mut *vfs.write();
132+
let handle = Arc::new(handle);
133+
vfs.handle = Some(handle.clone());
134+
let handle = handle as Arc<dyn vfs::loader::Handle>;
131135
Handle { handle, receiver }
132136
};
133137

@@ -159,7 +163,7 @@ impl GlobalState {
159163
flycheck_sender,
160164
flycheck_receiver,
161165

162-
vfs: Arc::new(RwLock::new((vfs::Vfs::default(), NoHashHashMap::default()))),
166+
vfs,
163167
vfs_config_version: 0,
164168
vfs_progress_config_version: 0,
165169
vfs_progress_n_total: 0,

crates/rust-analyzer/tests/slow-tests/main.rs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ mod support;
1616
mod testdir;
1717
mod tidy;
1818

19-
use std::{collections::HashMap, path::PathBuf, time::Instant};
19+
use std::{
20+
collections::HashMap,
21+
path::PathBuf,
22+
thread::sleep,
23+
time::{Duration, Instant},
24+
};
2025

2126
use lsp_types::{
2227
notification::DidOpenTextDocument,
@@ -684,6 +689,78 @@ version = \"0.0.0\"
684689
);
685690
}
686691

692+
#[test]
693+
fn include_non_rs_file() {
694+
if skip_slow_tests() {
695+
return;
696+
}
697+
698+
let server = Project::with_fixture(
699+
r###"
700+
//- /Cargo.toml
701+
[package]
702+
name = "foo"
703+
version = "0.0.0"
704+
705+
//- /src/hello.non_rs
706+
fn hello_world() -> &'static str {
707+
"hello world"
708+
}
709+
//- /src/some_bytes.txt
710+
12345
711+
//- /src/main.rs
712+
#[rustc_builtin_macro] macro_rules! include {}
713+
#[rustc_builtin_macro] macro_rules! include_bytes {}
714+
#[rustc_builtin_macro] macro_rules! include_str {}
715+
716+
include!("hello.non_rs");
717+
718+
fn main() {
719+
let my_source = include_bytes!("main.rs");
720+
let some_bytes = include_bytes!("some_bytes.txt");
721+
let some_str = hello_world();
722+
let some_other_bytes = include_bytes!("non_existent.txt");
723+
const SOME_STRING: &str = include_str!("some_bytes.txt");
724+
}
725+
"###,
726+
)
727+
.with_config(serde_json::json!({
728+
"cargo": {
729+
"buildScripts": {
730+
"enable": true
731+
},
732+
"noSysroot": true,
733+
}
734+
}))
735+
.server()
736+
.wait_until_workspace_is_loaded();
737+
738+
// For some reason, `wait_until_workspace_is_loaded` doesn't wait for loading non rs
739+
// files, so here we sleep to make sure test will pass
740+
sleep(Duration::from_secs(1));
741+
742+
let checker = |line, hover: &str| {
743+
let res = server.send_request::<HoverRequest>(HoverParams {
744+
text_document_position_params: TextDocumentPositionParams::new(
745+
server.doc_id("src/main.rs"),
746+
Position::new(line, 10),
747+
),
748+
work_done_progress_params: Default::default(),
749+
});
750+
let res = res.to_string();
751+
assert!(
752+
res.contains(hover),
753+
"{hover} not found in hover of line {line}.\nHover text is:\n{res}"
754+
);
755+
};
756+
757+
checker(7, "&[u8; 454]");
758+
checker(8, "&[u8; 6]");
759+
checker(9, "&str");
760+
checker(10, "&[u8; _]");
761+
checker(11, r#"\"12345\\n\""#);
762+
}
763+
687764
#[test]
688765
fn out_dirs_check() {
689766
if skip_slow_tests() {

crates/vfs-notify/src/lib.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
1010
#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
1111

12-
use std::fs;
12+
use std::{
13+
fs,
14+
panic::{RefUnwindSafe, UnwindSafe},
15+
};
1316

1417
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
1518
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
@@ -24,10 +27,14 @@ pub struct NotifyHandle {
2427
_thread: jod_thread::JoinHandle,
2528
}
2629

30+
impl UnwindSafe for NotifyHandle {}
31+
impl RefUnwindSafe for NotifyHandle {}
32+
2733
#[derive(Debug)]
2834
enum Message {
2935
Config(loader::Config),
3036
Invalidate(AbsPathBuf),
37+
Subscribe(AbsPathBuf),
3138
}
3239

3340
impl loader::Handle for NotifyHandle {
@@ -41,15 +48,19 @@ impl loader::Handle for NotifyHandle {
4148
NotifyHandle { sender, _thread: thread }
4249
}
4350

44-
fn set_config(&mut self, config: loader::Config) {
51+
fn set_config(&self, config: loader::Config) {
4552
self.sender.send(Message::Config(config)).unwrap();
4653
}
4754

48-
fn invalidate(&mut self, path: AbsPathBuf) {
55+
fn subscribe(&self, path: AbsPathBuf) {
56+
self.sender.send(Message::Subscribe(path)).unwrap();
57+
}
58+
59+
fn invalidate(&self, path: AbsPathBuf) {
4960
self.sender.send(Message::Invalidate(path)).unwrap();
5061
}
5162

52-
fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>> {
63+
fn load_sync(&self, path: &AbsPath) -> Option<Vec<u8>> {
5364
read(path)
5465
}
5566
}
@@ -126,6 +137,12 @@ impl NotifyActor {
126137
let files = vec![(path, contents)];
127138
self.send(loader::Message::Loaded { files });
128139
}
140+
Message::Subscribe(path) => {
141+
let contents = read(path.as_path());
142+
let files = vec![(path.clone(), contents)];
143+
self.send(loader::Message::Loaded { files });
144+
self.watched_entries.push(loader::Entry::Files(vec![path]));
145+
}
129146
},
130147
Event::NotifyEvent(event) => {
131148
if let Some(event) = log_notify_error(event) {

crates/vfs/src/file_set.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,34 @@
22
//!
33
//! Files which do not belong to any explicitly configured `FileSet` belong to
44
//! the default `FileSet`.
5-
use std::fmt;
5+
use std::{
6+
fmt,
7+
panic::{RefUnwindSafe, UnwindSafe},
8+
sync::Arc,
9+
};
610

711
use fst::{IntoStreamer, Streamer};
812
use rustc_hash::FxHashMap;
913
use stdx::hash::NoHashHashMap;
1014

11-
use crate::{AnchoredPath, FileId, Vfs, VfsPath};
15+
use crate::{loader::Handle, AnchoredPath, FileId, Vfs, VfsPath};
1216

1317
/// A set of [`VfsPath`]s identified by [`FileId`]s.
14-
#[derive(Default, Clone, Eq, PartialEq)]
18+
#[derive(Default, Clone)]
1519
pub struct FileSet {
1620
files: FxHashMap<VfsPath, FileId>,
1721
paths: NoHashHashMap<FileId, VfsPath>,
22+
handle: Option<Arc<dyn Handle + Sync + Send + UnwindSafe + RefUnwindSafe>>,
1823
}
1924

25+
impl PartialEq for FileSet {
26+
fn eq(&self, other: &Self) -> bool {
27+
self.files == other.files && self.paths == other.paths
28+
}
29+
}
30+
31+
impl Eq for FileSet {}
32+
2033
impl FileSet {
2134
/// Returns the number of stored paths.
2235
pub fn len(&self) -> usize {
@@ -31,7 +44,15 @@ impl FileSet {
3144
let mut base = self.paths[&path.anchor].clone();
3245
base.pop();
3346
let path = base.join(path.path)?;
34-
self.files.get(&path).copied()
47+
let answer = self.files.get(&path);
48+
if answer.is_none() {
49+
if let Some(handle) = &self.handle {
50+
if let Some(x) = path.as_path() {
51+
handle.subscribe(x.to_owned());
52+
}
53+
}
54+
}
55+
answer.copied()
3556
}
3657

3758
/// Get the id corresponding to `path` if it exists in the set.
@@ -110,7 +131,9 @@ impl FileSetConfig {
110131
/// Creates a new [`FileSet`] for every set of prefixes in `self`.
111132
pub fn partition(&self, vfs: &Vfs) -> Vec<FileSet> {
112133
let mut scratch_space = Vec::new();
113-
let mut res = vec![FileSet::default(); self.len()];
134+
let mut file_set = FileSet::default();
135+
file_set.handle = vfs.handle.clone();
136+
let mut res = vec![file_set; self.len()];
114137
for (file_id, path) in vfs.iter() {
115138
let root = self.classify(path, &mut scratch_space);
116139
res[root].insert(file_id, path.clone());

crates/vfs/src/lib.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,19 @@ pub mod loader;
4646
mod path_interner;
4747
mod vfs_path;
4848

49-
use std::{fmt, mem};
49+
use std::{
50+
fmt, mem,
51+
panic::{RefUnwindSafe, UnwindSafe},
52+
sync::Arc,
53+
};
5054

5155
use crate::path_interner::PathInterner;
5256

5357
pub use crate::{
5458
anchored_path::{AnchoredPath, AnchoredPathBuf},
5559
vfs_path::VfsPath,
5660
};
61+
use loader::Handle;
5762
pub use paths::{AbsPath, AbsPathBuf};
5863

5964
/// Handle to a file in [`Vfs`]
@@ -77,6 +82,7 @@ pub struct Vfs {
7782
interner: PathInterner,
7883
data: Vec<Option<Vec<u8>>>,
7984
changes: Vec<ChangedFile>,
85+
pub handle: Option<Arc<dyn Handle + Sync + Send + UnwindSafe + RefUnwindSafe>>,
8086
}
8187

8288
/// Changed file in the [`Vfs`].

crates/vfs/src/loader.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,17 @@ pub trait Handle: fmt::Debug {
6464
Self: Sized;
6565

6666
/// Set this handle's configuration.
67-
fn set_config(&mut self, config: Config);
67+
fn set_config(&self, config: Config);
6868

6969
/// The file's content at `path` has been modified, and should be reloaded.
70-
fn invalidate(&mut self, path: AbsPathBuf);
70+
fn invalidate(&self, path: AbsPathBuf);
7171

7272
/// Load the content of the given file, returning [`None`] if it does not
7373
/// exists.
74-
fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>;
74+
fn load_sync(&self, path: &AbsPath) -> Option<Vec<u8>>;
75+
76+
/// Add a file to watch list
77+
fn subscribe(&self, path: AbsPathBuf);
7578
}
7679

7780
impl Entry {

0 commit comments

Comments
 (0)