aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/libanalysis/src/imp.rs20
-rw-r--r--crates/libanalysis/src/lib.rs8
-rw-r--r--crates/server/src/main_loop/handlers.rs10
-rw-r--r--crates/server/src/main_loop/mod.rs80
-rw-r--r--crates/server/src/main_loop/subscriptions.rs21
-rw-r--r--crates/server/src/server_world.rs7
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
79pub(crate) struct AnalysisImpl { 79pub(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;
12mod module_map; 12mod module_map;
13mod imp; 13mod imp;
14 14
15use std::sync::Arc;
16
15use relative_path::{RelativePath, RelativePathBuf}; 17use relative_path::{RelativePath, RelativePathBuf};
16use libsyntax2::{File, TextRange, TextUnit, AtomEdit}; 18use libsyntax2::{File, TextRange, TextUnit, AtomEdit};
17use imp::{AnalysisImpl, AnalysisHostImpl}; 19use imp::{AnalysisImpl, AnalysisHostImpl};
@@ -31,7 +33,7 @@ pub trait FileResolver: Send + Sync + 'static {
31 33
32#[derive(Debug)] 34#[derive(Debug)]
33pub struct AnalysisHost { 35pub struct AnalysisHost {
34 pub(crate) imp: AnalysisHostImpl 36 imp: AnalysisHostImpl
35} 37}
36 38
37impl AnalysisHost { 39impl 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)]
123pub struct Analysis { 125pub struct Analysis {
124 pub(crate) imp: AnalysisImpl 126 imp: AnalysisImpl
125} 127}
126 128
127impl Analysis { 129impl 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 @@
1use std::collections::HashMap; 1use std::collections::HashMap;
2 2
3use languageserver_types::{ 3use 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
326pub fn publish_diagnostics( 326pub 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
345pub fn publish_decorations( 345pub 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 @@
1mod handlers; 1mod handlers;
2mod subscriptions;
2 3
3use std::{ 4use std::{
4 collections::{HashSet}, 5 collections::{HashSet},
@@ -6,7 +7,7 @@ use std::{
6 7
7use threadpool::ThreadPool; 8use threadpool::ThreadPool;
8use crossbeam_channel::{Sender, Receiver}; 9use crossbeam_channel::{Sender, Receiver};
9use languageserver_types::Url; 10use libanalysis::FileId;
10 11
11use { 12use {
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
19pub(super) fn main_loop( 21pub(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(
140fn on_notification( 154fn 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 @@
1use std::collections::HashSet;
2use libanalysis::FileId;
3
4pub struct Subscriptions {
5 subs: HashSet<FileId>,
6}
7
8impl 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 {