diff options
-rw-r--r-- | crates/libanalysis/src/imp.rs | 20 | ||||
-rw-r--r-- | crates/libanalysis/src/lib.rs | 8 | ||||
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 10 | ||||
-rw-r--r-- | crates/server/src/main_loop/mod.rs | 80 | ||||
-rw-r--r-- | crates/server/src/main_loop/subscriptions.rs | 21 | ||||
-rw-r--r-- | crates/server/src/server_world.rs | 7 |
6 files changed, 85 insertions, 61 deletions
diff --git a/crates/libanalysis/src/imp.rs b/crates/libanalysis/src/imp.rs index 004942e72..97802bd50 100644 --- a/crates/libanalysis/src/imp.rs +++ b/crates/libanalysis/src/imp.rs | |||
@@ -39,11 +39,11 @@ impl AnalysisHostImpl { | |||
39 | 39 | ||
40 | pub fn analysis( | 40 | pub fn analysis( |
41 | &self, | 41 | &self, |
42 | file_resolver: impl FileResolver, | 42 | file_resolver: Arc<dyn FileResolver>, |
43 | ) -> AnalysisImpl { | 43 | ) -> AnalysisImpl { |
44 | AnalysisImpl { | 44 | AnalysisImpl { |
45 | needs_reindex: AtomicBool::new(false), | 45 | needs_reindex: AtomicBool::new(false), |
46 | file_resolver: Arc::new(file_resolver), | 46 | file_resolver, |
47 | data: self.data.clone(), | 47 | data: self.data.clone(), |
48 | } | 48 | } |
49 | } | 49 | } |
@@ -78,7 +78,7 @@ impl AnalysisHostImpl { | |||
78 | 78 | ||
79 | pub(crate) struct AnalysisImpl { | 79 | pub(crate) struct AnalysisImpl { |
80 | needs_reindex: AtomicBool, | 80 | needs_reindex: AtomicBool, |
81 | file_resolver: Arc<FileResolver>, | 81 | file_resolver: Arc<dyn FileResolver>, |
82 | data: Arc<WorldData>, | 82 | data: Arc<WorldData>, |
83 | } | 83 | } |
84 | 84 | ||
@@ -236,15 +236,13 @@ impl AnalysisImpl { | |||
236 | ("add `#[derive]`", libeditor::add_derive(&file, offset).map(|f| f())), | 236 | ("add `#[derive]`", libeditor::add_derive(&file, offset).map(|f| f())), |
237 | ("add impl", libeditor::add_impl(&file, offset).map(|f| f())), | 237 | ("add impl", libeditor::add_impl(&file, offset).map(|f| f())), |
238 | ]; | 238 | ]; |
239 | let mut res = Vec::new(); | 239 | actions.into_iter() |
240 | for (name, local_edit) in actions { | 240 | .filter_map(|(name, local_edit)| { |
241 | if let Some(local_edit) = local_edit { | 241 | Some(SourceChange::from_local_edit( |
242 | res.push(SourceChange::from_local_edit( | 242 | file_id, name, local_edit?, |
243 | file_id, name, local_edit | ||
244 | )) | 243 | )) |
245 | } | 244 | }) |
246 | } | 245 | .collect() |
247 | res | ||
248 | } | 246 | } |
249 | 247 | ||
250 | fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> { | 248 | fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> { |
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index 810228632..c25d31f4b 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs | |||
@@ -12,6 +12,8 @@ mod symbol_index; | |||
12 | mod module_map; | 12 | mod module_map; |
13 | mod imp; | 13 | mod imp; |
14 | 14 | ||
15 | use std::sync::Arc; | ||
16 | |||
15 | use relative_path::{RelativePath, RelativePathBuf}; | 17 | use relative_path::{RelativePath, RelativePathBuf}; |
16 | use libsyntax2::{File, TextRange, TextUnit, AtomEdit}; | 18 | use libsyntax2::{File, TextRange, TextUnit, AtomEdit}; |
17 | use imp::{AnalysisImpl, AnalysisHostImpl}; | 19 | use imp::{AnalysisImpl, AnalysisHostImpl}; |
@@ -31,7 +33,7 @@ pub trait FileResolver: Send + Sync + 'static { | |||
31 | 33 | ||
32 | #[derive(Debug)] | 34 | #[derive(Debug)] |
33 | pub struct AnalysisHost { | 35 | pub struct AnalysisHost { |
34 | pub(crate) imp: AnalysisHostImpl | 36 | imp: AnalysisHostImpl |
35 | } | 37 | } |
36 | 38 | ||
37 | impl AnalysisHost { | 39 | impl AnalysisHost { |
@@ -39,7 +41,7 @@ impl AnalysisHost { | |||
39 | AnalysisHost { imp: AnalysisHostImpl::new() } | 41 | AnalysisHost { imp: AnalysisHostImpl::new() } |
40 | } | 42 | } |
41 | pub fn analysis(&self, file_resolver: impl FileResolver) -> Analysis { | 43 | pub fn analysis(&self, file_resolver: impl FileResolver) -> Analysis { |
42 | Analysis { imp: self.imp.analysis(file_resolver) } | 44 | Analysis { imp: self.imp.analysis(Arc::new(file_resolver)) } |
43 | } | 45 | } |
44 | pub fn change_file(&mut self, file_id: FileId, text: Option<String>) { | 46 | pub fn change_file(&mut self, file_id: FileId, text: Option<String>) { |
45 | self.change_files(::std::iter::once((file_id, text))); | 47 | self.change_files(::std::iter::once((file_id, text))); |
@@ -121,7 +123,7 @@ impl Query { | |||
121 | 123 | ||
122 | #[derive(Clone, Debug)] | 124 | #[derive(Clone, Debug)] |
123 | pub struct Analysis { | 125 | pub struct Analysis { |
124 | pub(crate) imp: AnalysisImpl | 126 | imp: AnalysisImpl |
125 | } | 127 | } |
126 | 128 | ||
127 | impl Analysis { | 129 | impl Analysis { |
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index 45083b084..6b70399b0 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use std::collections::HashMap; | 1 | use std::collections::HashMap; |
2 | 2 | ||
3 | use languageserver_types::{ | 3 | use languageserver_types::{ |
4 | Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, | 4 | Diagnostic, DiagnosticSeverity, DocumentSymbol, |
5 | Command, TextDocumentIdentifier, | 5 | Command, TextDocumentIdentifier, |
6 | SymbolInformation, Position, Location, TextEdit, | 6 | SymbolInformation, Position, Location, TextEdit, |
7 | CompletionItem, InsertTextFormat, CompletionItemKind, | 7 | CompletionItem, InsertTextFormat, CompletionItemKind, |
@@ -325,9 +325,9 @@ pub fn handle_code_action( | |||
325 | 325 | ||
326 | pub fn publish_diagnostics( | 326 | pub fn publish_diagnostics( |
327 | world: ServerWorld, | 327 | world: ServerWorld, |
328 | uri: Url | 328 | file_id: FileId, |
329 | ) -> Result<req::PublishDiagnosticsParams> { | 329 | ) -> Result<req::PublishDiagnosticsParams> { |
330 | let file_id = world.uri_to_file_id(&uri)?; | 330 | let uri = world.file_id_to_uri(file_id)?; |
331 | let line_index = world.analysis().file_line_index(file_id); | 331 | let line_index = world.analysis().file_line_index(file_id); |
332 | let diagnostics = world.analysis().diagnostics(file_id) | 332 | let diagnostics = world.analysis().diagnostics(file_id) |
333 | .into_iter() | 333 | .into_iter() |
@@ -344,9 +344,9 @@ pub fn publish_diagnostics( | |||
344 | 344 | ||
345 | pub fn publish_decorations( | 345 | pub fn publish_decorations( |
346 | world: ServerWorld, | 346 | world: ServerWorld, |
347 | uri: Url | 347 | file_id: FileId, |
348 | ) -> Result<req::PublishDecorationsParams> { | 348 | ) -> Result<req::PublishDecorationsParams> { |
349 | let file_id = world.uri_to_file_id(&uri)?; | 349 | let uri = world.file_id_to_uri(file_id)?; |
350 | Ok(req::PublishDecorationsParams { | 350 | Ok(req::PublishDecorationsParams { |
351 | uri, | 351 | uri, |
352 | decorations: highlight(&world, file_id), | 352 | decorations: highlight(&world, file_id), |
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index 0f66248a5..cd17cab56 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | mod handlers; | 1 | mod handlers; |
2 | mod subscriptions; | ||
2 | 3 | ||
3 | use std::{ | 4 | use std::{ |
4 | collections::{HashSet}, | 5 | collections::{HashSet}, |
@@ -6,7 +7,7 @@ use std::{ | |||
6 | 7 | ||
7 | use threadpool::ThreadPool; | 8 | use threadpool::ThreadPool; |
8 | use crossbeam_channel::{Sender, Receiver}; | 9 | use crossbeam_channel::{Sender, Receiver}; |
9 | use languageserver_types::Url; | 10 | use libanalysis::FileId; |
10 | 11 | ||
11 | use { | 12 | use { |
12 | req, dispatch, | 13 | req, dispatch, |
@@ -14,6 +15,7 @@ use { | |||
14 | io::{Io, RawMsg, RawRequest, RawNotification}, | 15 | io::{Io, RawMsg, RawRequest, RawNotification}, |
15 | vfs::FileEvent, | 16 | vfs::FileEvent, |
16 | server_world::{ServerWorldState, ServerWorld}, | 17 | server_world::{ServerWorldState, ServerWorld}, |
18 | main_loop::subscriptions::{Subscriptions}, | ||
17 | }; | 19 | }; |
18 | 20 | ||
19 | pub(super) fn main_loop( | 21 | pub(super) fn main_loop( |
@@ -28,6 +30,7 @@ pub(super) fn main_loop( | |||
28 | 30 | ||
29 | let mut pending_requests: HashSet<u64> = HashSet::new(); | 31 | let mut pending_requests: HashSet<u64> = HashSet::new(); |
30 | let mut fs_events_receiver = Some(&fs_events_receiver); | 32 | let mut fs_events_receiver = Some(&fs_events_receiver); |
33 | let mut subs = Subscriptions::new(); | ||
31 | loop { | 34 | loop { |
32 | enum Event { | 35 | enum Event { |
33 | Msg(RawMsg), | 36 | Msg(RawMsg), |
@@ -47,7 +50,7 @@ pub(super) fn main_loop( | |||
47 | None => Event::FsWatcherDead, | 50 | None => Event::FsWatcherDead, |
48 | } | 51 | } |
49 | }; | 52 | }; |
50 | 53 | let mut state_changed = false; | |
51 | match event { | 54 | match event { |
52 | Event::ReceiverDead => { | 55 | Event::ReceiverDead => { |
53 | io.cleanup_receiver()?; | 56 | io.cleanup_receiver()?; |
@@ -70,6 +73,7 @@ pub(super) fn main_loop( | |||
70 | Event::Fs(events) => { | 73 | Event::Fs(events) => { |
71 | trace!("fs change, {} events", events.len()); | 74 | trace!("fs change, {} events", events.len()); |
72 | state.apply_fs_changes(events); | 75 | state.apply_fs_changes(events); |
76 | state_changed = true; | ||
73 | } | 77 | } |
74 | Event::Msg(msg) => { | 78 | Event::Msg(msg) => { |
75 | match msg { | 79 | match msg { |
@@ -79,7 +83,8 @@ pub(super) fn main_loop( | |||
79 | } | 83 | } |
80 | } | 84 | } |
81 | RawMsg::Notification(not) => { | 85 | RawMsg::Notification(not) => { |
82 | on_notification(io, &mut state, pool, &task_sender, not)? | 86 | on_notification(io, &mut state, &mut subs, not)?; |
87 | state_changed = true; | ||
83 | } | 88 | } |
84 | RawMsg::Response(resp) => { | 89 | RawMsg::Response(resp) => { |
85 | if !pending_requests.remove(&resp.id) { | 90 | if !pending_requests.remove(&resp.id) { |
@@ -89,6 +94,15 @@ pub(super) fn main_loop( | |||
89 | } | 94 | } |
90 | } | 95 | } |
91 | }; | 96 | }; |
97 | |||
98 | if state_changed { | ||
99 | update_file_notifications_on_threadpool( | ||
100 | pool, | ||
101 | state.snapshot(), | ||
102 | task_sender.clone(), | ||
103 | subs.subscriptions(), | ||
104 | ) | ||
105 | } | ||
92 | } | 106 | } |
93 | } | 107 | } |
94 | 108 | ||
@@ -140,8 +154,7 @@ fn on_request( | |||
140 | fn on_notification( | 154 | fn on_notification( |
141 | io: &mut Io, | 155 | io: &mut Io, |
142 | state: &mut ServerWorldState, | 156 | state: &mut ServerWorldState, |
143 | pool: &ThreadPool, | 157 | subs: &mut Subscriptions, |
144 | sender: &Sender<Task>, | ||
145 | not: RawNotification, | 158 | not: RawNotification, |
146 | ) -> Result<()> { | 159 | ) -> Result<()> { |
147 | let mut not = Some(not); | 160 | let mut not = Some(not); |
@@ -149,13 +162,8 @@ fn on_notification( | |||
149 | let uri = params.text_document.uri; | 162 | let uri = params.text_document.uri; |
150 | let path = uri.to_file_path() | 163 | let path = uri.to_file_path() |
151 | .map_err(|()| format_err!("invalid uri: {}", uri))?; | 164 | .map_err(|()| format_err!("invalid uri: {}", uri))?; |
152 | state.add_mem_file(path, params.text_document.text); | 165 | let file_id = state.add_mem_file(path, params.text_document.text); |
153 | update_file_notifications_on_threadpool( | 166 | subs.add_sub(file_id); |
154 | pool, | ||
155 | state.snapshot(), | ||
156 | sender.clone(), | ||
157 | uri, | ||
158 | ); | ||
159 | Ok(()) | 167 | Ok(()) |
160 | })?; | 168 | })?; |
161 | dispatch::handle_notification::<req::DidChangeTextDocument, _>(&mut not, |mut params| { | 169 | dispatch::handle_notification::<req::DidChangeTextDocument, _>(&mut not, |mut params| { |
@@ -166,23 +174,15 @@ fn on_notification( | |||
166 | .ok_or_else(|| format_err!("empty changes"))? | 174 | .ok_or_else(|| format_err!("empty changes"))? |
167 | .text; | 175 | .text; |
168 | state.change_mem_file(path.as_path(), text)?; | 176 | state.change_mem_file(path.as_path(), text)?; |
169 | update_file_notifications_on_threadpool( | ||
170 | pool, | ||
171 | state.snapshot(), | ||
172 | sender.clone(), | ||
173 | uri, | ||
174 | ); | ||
175 | Ok(()) | 177 | Ok(()) |
176 | })?; | 178 | })?; |
177 | dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| { | 179 | dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| { |
178 | let uri = params.text_document.uri; | 180 | let uri = params.text_document.uri; |
179 | let path = uri.to_file_path() | 181 | let path = uri.to_file_path() |
180 | .map_err(|()| format_err!("invalid uri: {}", uri))?; | 182 | .map_err(|()| format_err!("invalid uri: {}", uri))?; |
181 | state.remove_mem_file(path.as_path())?; | 183 | let file_id = state.remove_mem_file(path.as_path())?; |
182 | let not = req::PublishDiagnosticsParams { | 184 | subs.remove_sub(file_id); |
183 | uri, | 185 | let not = req::PublishDiagnosticsParams { uri, diagnostics: Vec::new() }; |
184 | diagnostics: Vec::new(), | ||
185 | }; | ||
186 | let not = dispatch::send_notification::<req::PublishDiagnostics>(not); | 186 | let not = dispatch::send_notification::<req::PublishDiagnostics>(not); |
187 | io.send(RawMsg::Notification(not)); | 187 | io.send(RawMsg::Notification(not)); |
188 | Ok(()) | 188 | Ok(()) |
@@ -227,25 +227,27 @@ fn update_file_notifications_on_threadpool( | |||
227 | pool: &ThreadPool, | 227 | pool: &ThreadPool, |
228 | world: ServerWorld, | 228 | world: ServerWorld, |
229 | sender: Sender<Task>, | 229 | sender: Sender<Task>, |
230 | uri: Url, | 230 | subscriptions: Vec<FileId>, |
231 | ) { | 231 | ) { |
232 | pool.execute(move || { | 232 | pool.execute(move || { |
233 | match handlers::publish_diagnostics(world.clone(), uri.clone()) { | 233 | for file_id in subscriptions { |
234 | Err(e) => { | 234 | match handlers::publish_diagnostics(world.clone(), file_id) { |
235 | error!("failed to compute diagnostics: {:?}", e) | 235 | Err(e) => { |
236 | } | 236 | error!("failed to compute diagnostics: {:?}", e) |
237 | Ok(params) => { | 237 | } |
238 | let not = dispatch::send_notification::<req::PublishDiagnostics>(params); | 238 | Ok(params) => { |
239 | sender.send(Task::Notify(not)); | 239 | let not = dispatch::send_notification::<req::PublishDiagnostics>(params); |
240 | } | 240 | sender.send(Task::Notify(not)); |
241 | } | 241 | } |
242 | match handlers::publish_decorations(world, uri) { | ||
243 | Err(e) => { | ||
244 | error!("failed to compute decorations: {:?}", e) | ||
245 | } | 242 | } |
246 | Ok(params) => { | 243 | match handlers::publish_decorations(world.clone(), file_id) { |
247 | let not = dispatch::send_notification::<req::PublishDecorations>(params); | 244 | Err(e) => { |
248 | sender.send(Task::Notify(not)) | 245 | error!("failed to compute decorations: {:?}", e) |
246 | } | ||
247 | Ok(params) => { | ||
248 | let not = dispatch::send_notification::<req::PublishDecorations>(params); | ||
249 | sender.send(Task::Notify(not)) | ||
250 | } | ||
249 | } | 251 | } |
250 | } | 252 | } |
251 | }); | 253 | }); |
diff --git a/crates/server/src/main_loop/subscriptions.rs b/crates/server/src/main_loop/subscriptions.rs new file mode 100644 index 000000000..963096aef --- /dev/null +++ b/crates/server/src/main_loop/subscriptions.rs | |||
@@ -0,0 +1,21 @@ | |||
1 | use std::collections::HashSet; | ||
2 | use libanalysis::FileId; | ||
3 | |||
4 | pub struct Subscriptions { | ||
5 | subs: HashSet<FileId>, | ||
6 | } | ||
7 | |||
8 | impl Subscriptions { | ||
9 | pub fn new() -> Subscriptions { | ||
10 | Subscriptions { subs: HashSet::new() } | ||
11 | } | ||
12 | pub fn add_sub(&mut self, file_id: FileId) { | ||
13 | self.subs.insert(file_id); | ||
14 | } | ||
15 | pub fn remove_sub(&mut self, file_id: FileId) { | ||
16 | self.subs.remove(&file_id); | ||
17 | } | ||
18 | pub fn subscriptions(&self) -> Vec<FileId> { | ||
19 | self.subs.iter().cloned().collect() | ||
20 | } | ||
21 | } | ||
diff --git a/crates/server/src/server_world.rs b/crates/server/src/server_world.rs index 9ba7df0b8..d99ef661e 100644 --- a/crates/server/src/server_world.rs +++ b/crates/server/src/server_world.rs | |||
@@ -61,10 +61,11 @@ impl ServerWorldState { | |||
61 | self.analysis_host.change_files(changes); | 61 | self.analysis_host.change_files(changes); |
62 | } | 62 | } |
63 | 63 | ||
64 | pub fn add_mem_file(&mut self, path: PathBuf, text: String) { | 64 | pub fn add_mem_file(&mut self, path: PathBuf, text: String) -> FileId { |
65 | let file_id = self.path_map.get_or_insert(path); | 65 | let file_id = self.path_map.get_or_insert(path); |
66 | self.mem_map.insert(file_id, None); | 66 | self.mem_map.insert(file_id, None); |
67 | self.analysis_host.change_file(file_id, Some(text)); | 67 | self.analysis_host.change_file(file_id, Some(text)); |
68 | file_id | ||
68 | } | 69 | } |
69 | 70 | ||
70 | pub fn change_mem_file(&mut self, path: &Path, text: String) -> Result<()> { | 71 | pub fn change_mem_file(&mut self, path: &Path, text: String) -> Result<()> { |
@@ -75,7 +76,7 @@ impl ServerWorldState { | |||
75 | Ok(()) | 76 | Ok(()) |
76 | } | 77 | } |
77 | 78 | ||
78 | pub fn remove_mem_file(&mut self, path: &Path) -> Result<()> { | 79 | pub fn remove_mem_file(&mut self, path: &Path) -> Result<FileId> { |
79 | let file_id = self.path_map.get_id(path).ok_or_else(|| { | 80 | let file_id = self.path_map.get_id(path).ok_or_else(|| { |
80 | format_err!("change to unknown file: {}", path.display()) | 81 | format_err!("change to unknown file: {}", path.display()) |
81 | })?; | 82 | })?; |
@@ -86,7 +87,7 @@ impl ServerWorldState { | |||
86 | // Do this via file watcher ideally. | 87 | // Do this via file watcher ideally. |
87 | let text = fs::read_to_string(path).ok(); | 88 | let text = fs::read_to_string(path).ok(); |
88 | self.analysis_host.change_file(file_id, text); | 89 | self.analysis_host.change_file(file_id, text); |
89 | Ok(()) | 90 | Ok(file_id) |
90 | } | 91 | } |
91 | 92 | ||
92 | pub fn snapshot(&self) -> ServerWorld { | 93 | pub fn snapshot(&self) -> ServerWorld { |