aboutsummaryrefslogtreecommitdiff
path: root/crates/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/server/src')
-rw-r--r--crates/server/src/main.rs26
-rw-r--r--crates/server/src/main_loop/mod.rs61
-rw-r--r--crates/server/src/vfs.rs79
3 files changed, 154 insertions, 12 deletions
diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs
index c2952465e..8dca32183 100644
--- a/crates/server/src/main.rs
+++ b/crates/server/src/main.rs
@@ -13,6 +13,7 @@ extern crate threadpool;
13extern crate log; 13extern crate log;
14extern crate url_serde; 14extern crate url_serde;
15extern crate flexi_logger; 15extern crate flexi_logger;
16extern crate walkdir;
16extern crate libeditor; 17extern crate libeditor;
17extern crate libanalysis; 18extern crate libanalysis;
18extern crate libsyntax2; 19extern crate libsyntax2;
@@ -24,6 +25,9 @@ mod dispatch;
24mod util; 25mod util;
25mod conv; 26mod conv;
26mod main_loop; 27mod main_loop;
28mod vfs;
29
30use std::path::PathBuf;
27 31
28use threadpool::ThreadPool; 32use threadpool::ThreadPool;
29use crossbeam_channel::bounded; 33use crossbeam_channel::bounded;
@@ -114,13 +118,29 @@ fn initialized(io: &mut Io) -> Result<()> {
114 { 118 {
115 let mut world = WorldState::new(); 119 let mut world = WorldState::new();
116 let mut pool = ThreadPool::new(4); 120 let mut pool = ThreadPool::new(4);
117 let (sender, receiver) = bounded::<Task>(16); 121 let (task_sender, task_receiver) = bounded::<Task>(16);
122 let (fs_events_receiver, watcher) = vfs::watch(vec![
123 PathBuf::from("./")
124 ]);
118 info!("lifecycle: handshake finished, server ready to serve requests"); 125 info!("lifecycle: handshake finished, server ready to serve requests");
119 let res = main_loop::main_loop(io, &mut world, &mut pool, sender, receiver.clone()); 126 let res = main_loop::main_loop(
127 io,
128 &mut world,
129 &mut pool,
130 task_sender,
131 task_receiver.clone(),
132 fs_events_receiver,
133 );
134
120 info!("waiting for background jobs to finish..."); 135 info!("waiting for background jobs to finish...");
121 receiver.for_each(drop); 136 task_receiver.for_each(drop);
122 pool.join(); 137 pool.join();
123 info!("...background jobs have finished"); 138 info!("...background jobs have finished");
139
140 info!("waiting for file watcher to finish...");
141 watcher.stop()?;
142 info!("...file watcher has finished");
143
124 res 144 res
125 }?; 145 }?;
126 146
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs
index 5b7093ad7..f954e632c 100644
--- a/crates/server/src/main_loop/mod.rs
+++ b/crates/server/src/main_loop/mod.rs
@@ -1,6 +1,9 @@
1mod handlers; 1mod handlers;
2 2
3use std::collections::HashSet; 3use std::{
4 path::PathBuf,
5 collections::{HashSet, HashMap},
6};
4 7
5use threadpool::ThreadPool; 8use threadpool::ThreadPool;
6use crossbeam_channel::{Sender, Receiver}; 9use crossbeam_channel::{Sender, Receiver};
@@ -13,6 +16,7 @@ use {
13 Task, Result, 16 Task, Result,
14 io::{Io, RawMsg, RawRequest, RawNotification}, 17 io::{Io, RawMsg, RawRequest, RawNotification},
15 util::FilePath, 18 util::FilePath,
19 vfs::{FileEvent, FileEventKind},
16 main_loop::handlers::{ 20 main_loop::handlers::{
17 handle_syntax_tree, 21 handle_syntax_tree,
18 handle_extend_selection, 22 handle_extend_selection,
@@ -28,24 +32,33 @@ pub(super) fn main_loop(
28 io: &mut Io, 32 io: &mut Io,
29 world: &mut WorldState, 33 world: &mut WorldState,
30 pool: &mut ThreadPool, 34 pool: &mut ThreadPool,
31 sender: Sender<Task>, 35 task_sender: Sender<Task>,
32 receiver: Receiver<Task>, 36 task_receiver: Receiver<Task>,
37 fs_events_receiver: Receiver<Vec<FileEvent>>,
33) -> Result<()> { 38) -> Result<()> {
34 info!("server initialized, serving requests"); 39 info!("server initialized, serving requests");
35 let mut next_request_id = 0; 40 let mut next_request_id = 0;
36 let mut pending_requests: HashSet<u64> = HashSet::new(); 41 let mut pending_requests: HashSet<u64> = HashSet::new();
42 let mut mem_map: HashMap<PathBuf, Option<String>> = HashMap::new();
43 let mut fs_events_receiver = Some(&fs_events_receiver);
37 loop { 44 loop {
38 enum Event { 45 enum Event {
39 Msg(RawMsg), 46 Msg(RawMsg),
40 Task(Task), 47 Task(Task),
48 Fs(Vec<FileEvent>),
41 ReceiverDead, 49 ReceiverDead,
50 FsWatcherDead,
42 } 51 }
43 let event = select! { 52 let event = select! {
44 recv(io.receiver(), msg) => match msg { 53 recv(io.receiver(), msg) => match msg {
45 Some(msg) => Event::Msg(msg), 54 Some(msg) => Event::Msg(msg),
46 None => Event::ReceiverDead, 55 None => Event::ReceiverDead,
47 }, 56 },
48 recv(receiver, task) => Event::Task(task.unwrap()), 57 recv(task_receiver, task) => Event::Task(task.unwrap()),
58 recv(fs_events_receiver, events) => match events {
59 Some(events) => Event::Fs(events),
60 None => Event::FsWatcherDead,
61 }
49 }; 62 };
50 63
51 match event { 64 match event {
@@ -53,6 +66,9 @@ pub(super) fn main_loop(
53 io.cleanup_receiver()?; 66 io.cleanup_receiver()?;
54 unreachable!(); 67 unreachable!();
55 } 68 }
69 Event::FsWatcherDead => {
70 fs_events_receiver = None;
71 }
56 Event::Task(task) => { 72 Event::Task(task) => {
57 match task { 73 match task {
58 Task::Request(mut request) => { 74 Task::Request(mut request) => {
@@ -70,15 +86,36 @@ pub(super) fn main_loop(
70 } 86 }
71 continue; 87 continue;
72 } 88 }
89 Event::Fs(events) => {
90 trace!("fs change, {} events", events.len());
91 let changes = events.into_iter()
92 .map(|event| {
93 let text = match event.kind {
94 FileEventKind::Add(text) => Some(text),
95 FileEventKind::Remove => None,
96 };
97 (event.path, text)
98 })
99 .filter_map(|(path, text)| {
100 if mem_map.contains_key(path.as_path()) {
101 mem_map.insert(path, text);
102 None
103 } else {
104 Some((path, text))
105 }
106 });
107
108 world.change_files(changes);
109 }
73 Event::Msg(msg) => { 110 Event::Msg(msg) => {
74 match msg { 111 match msg {
75 RawMsg::Request(req) => { 112 RawMsg::Request(req) => {
76 if !on_request(io, world, pool, &sender, req)? { 113 if !on_request(io, world, pool, &task_sender, req)? {
77 return Ok(()); 114 return Ok(());
78 } 115 }
79 } 116 }
80 RawMsg::Notification(not) => { 117 RawMsg::Notification(not) => {
81 on_notification(io, world, pool, &sender, not)? 118 on_notification(io, world, pool, &task_sender, not, &mut mem_map)?
82 } 119 }
83 RawMsg::Response(resp) => { 120 RawMsg::Response(resp) => {
84 if !pending_requests.remove(&resp.id) { 121 if !pending_requests.remove(&resp.id) {
@@ -160,11 +197,13 @@ fn on_notification(
160 pool: &ThreadPool, 197 pool: &ThreadPool,
161 sender: &Sender<Task>, 198 sender: &Sender<Task>,
162 not: RawNotification, 199 not: RawNotification,
200 mem_map: &mut HashMap<PathBuf, Option<String>>,
163) -> Result<()> { 201) -> Result<()> {
164 let mut not = Some(not); 202 let mut not = Some(not);
165 dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| { 203 dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| {
166 let path = params.text_document.file_path()?; 204 let path = params.text_document.file_path()?;
167 world.change_overlay(path, Some(params.text_document.text)); 205 mem_map.insert(path.clone(), None);
206 world.change_file(path, Some(params.text_document.text));
168 update_file_notifications_on_threadpool( 207 update_file_notifications_on_threadpool(
169 pool, world.snapshot(), sender.clone(), params.text_document.uri, 208 pool, world.snapshot(), sender.clone(), params.text_document.uri,
170 ); 209 );
@@ -175,7 +214,7 @@ fn on_notification(
175 let text = params.content_changes.pop() 214 let text = params.content_changes.pop()
176 .ok_or_else(|| format_err!("empty changes"))? 215 .ok_or_else(|| format_err!("empty changes"))?
177 .text; 216 .text;
178 world.change_overlay(path, Some(text)); 217 world.change_file(path, Some(text));
179 update_file_notifications_on_threadpool( 218 update_file_notifications_on_threadpool(
180 pool, world.snapshot(), sender.clone(), params.text_document.uri, 219 pool, world.snapshot(), sender.clone(), params.text_document.uri,
181 ); 220 );
@@ -183,7 +222,11 @@ fn on_notification(
183 })?; 222 })?;
184 dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| { 223 dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| {
185 let path = params.text_document.file_path()?; 224 let path = params.text_document.file_path()?;
186 world.change_overlay(path, None); 225 let text = match mem_map.remove(&path) {
226 Some(text) => text,
227 None => bail!("unmatched close notification"),
228 };
229 world.change_file(path, text);
187 let not = req::PublishDiagnosticsParams { 230 let not = req::PublishDiagnosticsParams {
188 uri: params.text_document.uri, 231 uri: params.text_document.uri,
189 diagnostics: Vec::new(), 232 diagnostics: Vec::new(),
diff --git a/crates/server/src/vfs.rs b/crates/server/src/vfs.rs
new file mode 100644
index 000000000..a5c367494
--- /dev/null
+++ b/crates/server/src/vfs.rs
@@ -0,0 +1,79 @@
1use std::{
2 path::PathBuf,
3 thread,
4 fs,
5};
6
7use crossbeam_channel::{Sender, Receiver, bounded};
8use drop_bomb::DropBomb;
9use walkdir::WalkDir;
10
11use Result;
12
13
14pub struct FileEvent {
15 pub path: PathBuf,
16 pub kind: FileEventKind,
17}
18
19pub enum FileEventKind {
20 Add(String),
21 #[allow(unused)]
22 Remove,
23}
24
25pub struct Watcher {
26 thread: thread::JoinHandle<()>,
27 bomb: DropBomb,
28}
29
30impl Watcher {
31 pub fn stop(mut self) -> Result<()> {
32 self.bomb.defuse();
33 self.thread.join()
34 .map_err(|_| format_err!("file watcher died"))
35 }
36}
37
38pub fn watch(roots: Vec<PathBuf>) -> (Receiver<Vec<FileEvent>>, Watcher) {
39 let (sender, receiver) = bounded(16);
40 let thread = thread::spawn(move || run(roots, sender));
41 (receiver, Watcher {
42 thread,
43 bomb: DropBomb::new("Watcher should be stopped explicitly"),
44 })
45}
46
47fn run(roots: Vec<PathBuf>, sender: Sender<Vec<FileEvent>>) {
48 for root in roots {
49 let mut events = Vec::new();
50 for entry in WalkDir::new(root.as_path()) {
51 let entry = match entry {
52 Ok(entry) => entry,
53 Err(e) => {
54 warn!("watcher error: {}", e);
55 continue;
56 }
57 };
58 if !entry.file_type().is_file() {
59 continue;
60 }
61 let path = entry.path();
62 if path.extension().and_then(|os| os.to_str()) != Some("rs") {
63 continue;
64 }
65 let text = match fs::read_to_string(path) {
66 Ok(text) => text,
67 Err(e) => {
68 warn!("watcher error: {}", e);
69 continue;
70 }
71 };
72 events.push(FileEvent {
73 path: path.to_owned(),
74 kind: FileEventKind::Add(text),
75 })
76 }
77 sender.send(events)
78 }
79}