diff options
-rw-r--r-- | crates/libanalysis/src/lib.rs | 10 | ||||
-rw-r--r-- | crates/server/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/server/src/conv.rs | 72 | ||||
-rw-r--r-- | crates/server/src/main.rs | 6 | ||||
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 91 | ||||
-rw-r--r-- | crates/server/src/main_loop/mod.rs | 78 | ||||
-rw-r--r-- | crates/server/src/path_map.rs | 47 | ||||
-rw-r--r-- | crates/server/src/util.rs | 33 |
8 files changed, 213 insertions, 125 deletions
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index fc0b9ee9a..7c52080cb 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs | |||
@@ -37,7 +37,6 @@ pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | |||
37 | const INDEXING_THRESHOLD: usize = 128; | 37 | const INDEXING_THRESHOLD: usize = 128; |
38 | 38 | ||
39 | pub struct WorldState { | 39 | pub struct WorldState { |
40 | next_file_id: u32, | ||
41 | data: Arc<WorldData> | 40 | data: Arc<WorldData> |
42 | } | 41 | } |
43 | 42 | ||
@@ -47,12 +46,11 @@ pub struct World { | |||
47 | } | 46 | } |
48 | 47 | ||
49 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 48 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
50 | pub struct FileId(u32); | 49 | pub struct FileId(pub u32); |
51 | 50 | ||
52 | impl WorldState { | 51 | impl WorldState { |
53 | pub fn new() -> WorldState { | 52 | pub fn new() -> WorldState { |
54 | WorldState { | 53 | WorldState { |
55 | next_file_id: 0, | ||
56 | data: Arc::new(WorldData::default()) | 54 | data: Arc::new(WorldData::default()) |
57 | } | 55 | } |
58 | } | 56 | } |
@@ -61,12 +59,6 @@ impl WorldState { | |||
61 | World { data: self.data.clone() } | 59 | World { data: self.data.clone() } |
62 | } | 60 | } |
63 | 61 | ||
64 | pub fn new_file_id(&mut self) -> FileId { | ||
65 | let id = FileId(self.next_file_id); | ||
66 | self.next_file_id += 1; | ||
67 | id | ||
68 | } | ||
69 | |||
70 | pub fn change_file(&mut self, file_id: FileId, text: Option<String>) { | 62 | pub fn change_file(&mut self, file_id: FileId, text: Option<String>) { |
71 | self.change_files(::std::iter::once((file_id, text))); | 63 | self.change_files(::std::iter::once((file_id, text))); |
72 | } | 64 | } |
diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index 0ad193b8a..058bf36d2 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml | |||
@@ -16,6 +16,7 @@ log = "0.4.3" | |||
16 | url_serde = "0.2.0" | 16 | url_serde = "0.2.0" |
17 | languageserver-types = "0.49.0" | 17 | languageserver-types = "0.49.0" |
18 | walkdir = "2.2.0" | 18 | walkdir = "2.2.0" |
19 | im = { version = "11.0.1", features = ["arc"] } | ||
19 | text_unit = { version = "0.1.2", features = ["serde"] } | 20 | text_unit = { version = "0.1.2", features = ["serde"] } |
20 | 21 | ||
21 | libsyntax2 = { path = "../libsyntax2" } | 22 | libsyntax2 = { path = "../libsyntax2" } |
diff --git a/crates/server/src/conv.rs b/crates/server/src/conv.rs index 3ddce5fb3..bbe512ece 100644 --- a/crates/server/src/conv.rs +++ b/crates/server/src/conv.rs | |||
@@ -1,10 +1,12 @@ | |||
1 | use std::path::Path; | 1 | use languageserver_types::{ |
2 | 2 | Range, SymbolKind, Position, TextEdit, Location, Url, | |
3 | use languageserver_types::{Range, SymbolKind, Position, TextEdit, Location, Url}; | 3 | TextDocumentIdentifier, VersionedTextDocumentIdentifier, TextDocumentItem, |
4 | }; | ||
4 | use libeditor::{LineIndex, LineCol, Edit, AtomEdit}; | 5 | use libeditor::{LineIndex, LineCol, Edit, AtomEdit}; |
5 | use libsyntax2::{SyntaxKind, TextUnit, TextRange}; | 6 | use libsyntax2::{SyntaxKind, TextUnit, TextRange}; |
7 | use libanalysis::FileId; | ||
6 | 8 | ||
7 | use Result; | 9 | use {Result, PathMap}; |
8 | 10 | ||
9 | pub trait Conv { | 11 | pub trait Conv { |
10 | type Output; | 12 | type Output; |
@@ -115,21 +117,65 @@ impl ConvWith for AtomEdit { | |||
115 | } | 117 | } |
116 | } | 118 | } |
117 | 119 | ||
118 | impl<'a> TryConvWith for (&'a Path, TextRange) { | 120 | impl<'a> TryConvWith for &'a Url { |
119 | type Ctx = LineIndex; | 121 | type Ctx = PathMap; |
120 | type Output = Location; | 122 | type Output = FileId; |
123 | fn try_conv_with(self, path_map: &PathMap) -> Result<FileId> { | ||
124 | let path = self.to_file_path() | ||
125 | .map_err(|()| format_err!("invalid uri: {}", self))?; | ||
126 | path_map.get_id(&path).ok_or_else(|| format_err!("unknown file: {}", path.display())) | ||
127 | } | ||
128 | } | ||
129 | |||
130 | impl TryConvWith for FileId { | ||
131 | type Ctx = PathMap; | ||
132 | type Output = Url; | ||
133 | fn try_conv_with(self, path_map: &PathMap) -> Result<Url> { | ||
134 | let path = path_map.get_path(self); | ||
135 | let url = Url::from_file_path(path) | ||
136 | .map_err(|()| format_err!("can't convert path to url: {}", path.display()))?; | ||
137 | Ok(url) | ||
138 | } | ||
139 | } | ||
140 | |||
141 | impl<'a> TryConvWith for &'a TextDocumentItem { | ||
142 | type Ctx = PathMap; | ||
143 | type Output = FileId; | ||
144 | fn try_conv_with(self, path_map: &PathMap) -> Result<FileId> { | ||
145 | self.uri.try_conv_with(path_map) | ||
146 | } | ||
147 | } | ||
121 | 148 | ||
122 | fn try_conv_with(self, line_index: &LineIndex) -> Result<Location> { | 149 | impl<'a> TryConvWith for &'a VersionedTextDocumentIdentifier { |
150 | type Ctx = PathMap; | ||
151 | type Output = FileId; | ||
152 | fn try_conv_with(self, path_map: &PathMap) -> Result<FileId> { | ||
153 | self.uri.try_conv_with(path_map) | ||
154 | } | ||
155 | } | ||
156 | |||
157 | impl<'a> TryConvWith for &'a TextDocumentIdentifier { | ||
158 | type Ctx = PathMap; | ||
159 | type Output = FileId; | ||
160 | fn try_conv_with(self, path_map: &PathMap) -> Result<FileId> { | ||
161 | self.uri.try_conv_with(path_map) | ||
162 | } | ||
163 | } | ||
164 | |||
165 | pub fn to_location( | ||
166 | file_id: FileId, | ||
167 | range: TextRange, | ||
168 | path_map: &PathMap, | ||
169 | line_index: &LineIndex, | ||
170 | ) -> Result<Location> { | ||
171 | let url = file_id.try_conv_with(path_map)?; | ||
123 | let loc = Location::new( | 172 | let loc = Location::new( |
124 | Url::from_file_path(self.0) | 173 | url, |
125 | .map_err(|()| format_err!("can't convert path to url: {}", self.0.display()))?, | 174 | range.conv_with(line_index), |
126 | self.1.conv_with(line_index), | ||
127 | ); | 175 | ); |
128 | Ok(loc) | 176 | Ok(loc) |
129 | } | ||
130 | } | 177 | } |
131 | 178 | ||
132 | |||
133 | pub trait MapConvWith<'a>: Sized { | 179 | pub trait MapConvWith<'a>: Sized { |
134 | type Ctx; | 180 | type Ctx; |
135 | type Output; | 181 | type Output; |
diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index 9c044d5a9..71d53199c 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs | |||
@@ -17,15 +17,16 @@ extern crate walkdir; | |||
17 | extern crate libeditor; | 17 | extern crate libeditor; |
18 | extern crate libanalysis; | 18 | extern crate libanalysis; |
19 | extern crate libsyntax2; | 19 | extern crate libsyntax2; |
20 | extern crate im; | ||
20 | 21 | ||
21 | mod io; | 22 | mod io; |
22 | mod caps; | 23 | mod caps; |
23 | mod req; | 24 | mod req; |
24 | mod dispatch; | 25 | mod dispatch; |
25 | mod util; | ||
26 | mod conv; | 26 | mod conv; |
27 | mod main_loop; | 27 | mod main_loop; |
28 | mod vfs; | 28 | mod vfs; |
29 | mod path_map; | ||
29 | 30 | ||
30 | use threadpool::ThreadPool; | 31 | use threadpool::ThreadPool; |
31 | use crossbeam_channel::bounded; | 32 | use crossbeam_channel::bounded; |
@@ -33,7 +34,8 @@ use flexi_logger::{Logger, Duplicate}; | |||
33 | use libanalysis::WorldState; | 34 | use libanalysis::WorldState; |
34 | 35 | ||
35 | use ::{ | 36 | use ::{ |
36 | io::{Io, RawMsg, RawResponse, RawRequest, RawNotification} | 37 | io::{Io, RawMsg, RawResponse, RawRequest, RawNotification}, |
38 | path_map::PathMap, | ||
37 | }; | 39 | }; |
38 | 40 | ||
39 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | 41 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; |
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index 9de6f480b..637bf1b24 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs | |||
@@ -11,27 +11,29 @@ use libsyntax2::TextUnit; | |||
11 | use serde_json::{to_value, from_value}; | 11 | use serde_json::{to_value, from_value}; |
12 | 12 | ||
13 | use ::{ | 13 | use ::{ |
14 | PathMap, | ||
14 | req::{self, Decoration}, Result, | 15 | req::{self, Decoration}, Result, |
15 | util::FilePath, | 16 | conv::{Conv, ConvWith, TryConvWith, MapConvWith, to_location}, |
16 | conv::{Conv, ConvWith, TryConvWith, MapConvWith}, | ||
17 | }; | 17 | }; |
18 | 18 | ||
19 | pub fn handle_syntax_tree( | 19 | pub fn handle_syntax_tree( |
20 | world: World, | 20 | world: World, |
21 | path_map: PathMap, | ||
21 | params: req::SyntaxTreeParams, | 22 | params: req::SyntaxTreeParams, |
22 | ) -> Result<String> { | 23 | ) -> Result<String> { |
23 | let path = params.text_document.file_path()?; | 24 | let id = params.text_document.try_conv_with(&path_map)?; |
24 | let file = world.file_syntax(&path)?; | 25 | let file = world.file_syntax(id)?; |
25 | Ok(libeditor::syntax_tree(&file)) | 26 | Ok(libeditor::syntax_tree(&file)) |
26 | } | 27 | } |
27 | 28 | ||
28 | pub fn handle_extend_selection( | 29 | pub fn handle_extend_selection( |
29 | world: World, | 30 | world: World, |
31 | path_map: PathMap, | ||
30 | params: req::ExtendSelectionParams, | 32 | params: req::ExtendSelectionParams, |
31 | ) -> Result<req::ExtendSelectionResult> { | 33 | ) -> Result<req::ExtendSelectionResult> { |
32 | let path = params.text_document.file_path()?; | 34 | let file_id = params.text_document.try_conv_with(&path_map)?; |
33 | let file = world.file_syntax(&path)?; | 35 | let file = world.file_syntax(file_id)?; |
34 | let line_index = world.file_line_index(&path)?; | 36 | let line_index = world.file_line_index(file_id)?; |
35 | let selections = params.selections.into_iter() | 37 | let selections = params.selections.into_iter() |
36 | .map_conv_with(&line_index) | 38 | .map_conv_with(&line_index) |
37 | .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r)) | 39 | .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r)) |
@@ -42,11 +44,12 @@ pub fn handle_extend_selection( | |||
42 | 44 | ||
43 | pub fn handle_document_symbol( | 45 | pub fn handle_document_symbol( |
44 | world: World, | 46 | world: World, |
47 | path_map: PathMap, | ||
45 | params: req::DocumentSymbolParams, | 48 | params: req::DocumentSymbolParams, |
46 | ) -> Result<Option<req::DocumentSymbolResponse>> { | 49 | ) -> Result<Option<req::DocumentSymbolResponse>> { |
47 | let path = params.text_document.file_path()?; | 50 | let file_id = params.text_document.try_conv_with(&path_map)?; |
48 | let file = world.file_syntax(&path)?; | 51 | let file = world.file_syntax(file_id)?; |
49 | let line_index = world.file_line_index(&path)?; | 52 | let line_index = world.file_line_index(file_id)?; |
50 | 53 | ||
51 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); | 54 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); |
52 | 55 | ||
@@ -81,11 +84,12 @@ pub fn handle_document_symbol( | |||
81 | 84 | ||
82 | pub fn handle_code_action( | 85 | pub fn handle_code_action( |
83 | world: World, | 86 | world: World, |
87 | path_map: PathMap, | ||
84 | params: req::CodeActionParams, | 88 | params: req::CodeActionParams, |
85 | ) -> Result<Option<Vec<Command>>> { | 89 | ) -> Result<Option<Vec<Command>>> { |
86 | let path = params.text_document.file_path()?; | 90 | let file_id = params.text_document.try_conv_with(&path_map)?; |
87 | let file = world.file_syntax(&path)?; | 91 | let file = world.file_syntax(file_id)?; |
88 | let line_index = world.file_line_index(&path)?; | 92 | let line_index = world.file_line_index(file_id)?; |
89 | let offset = params.range.conv_with(&line_index).start(); | 93 | let offset = params.range.conv_with(&line_index).start(); |
90 | let mut ret = Vec::new(); | 94 | let mut ret = Vec::new(); |
91 | 95 | ||
@@ -105,6 +109,7 @@ pub fn handle_code_action( | |||
105 | 109 | ||
106 | pub fn handle_workspace_symbol( | 110 | pub fn handle_workspace_symbol( |
107 | world: World, | 111 | world: World, |
112 | path_map: PathMap, | ||
108 | params: req::WorkspaceSymbolParams, | 113 | params: req::WorkspaceSymbolParams, |
109 | ) -> Result<Option<Vec<SymbolInformation>>> { | 114 | ) -> Result<Option<Vec<SymbolInformation>>> { |
110 | let all_symbols = params.query.contains("#"); | 115 | let all_symbols = params.query.contains("#"); |
@@ -119,23 +124,26 @@ pub fn handle_workspace_symbol( | |||
119 | q.limit(128); | 124 | q.limit(128); |
120 | q | 125 | q |
121 | }; | 126 | }; |
122 | let mut res = exec_query(&world, query)?; | 127 | let mut res = exec_query(&world, &path_map, query)?; |
123 | if res.is_empty() && !all_symbols { | 128 | if res.is_empty() && !all_symbols { |
124 | let mut query = Query::new(params.query); | 129 | let mut query = Query::new(params.query); |
125 | query.limit(128); | 130 | query.limit(128); |
126 | res = exec_query(&world, query)?; | 131 | res = exec_query(&world, &path_map, query)?; |
127 | } | 132 | } |
128 | 133 | ||
129 | return Ok(Some(res)); | 134 | return Ok(Some(res)); |
130 | 135 | ||
131 | fn exec_query(world: &World, query: Query) -> Result<Vec<SymbolInformation>> { | 136 | fn exec_query(world: &World, path_map: &PathMap, query: Query) -> Result<Vec<SymbolInformation>> { |
132 | let mut res = Vec::new(); | 137 | let mut res = Vec::new(); |
133 | for (path, symbol) in world.world_symbols(query) { | 138 | for (file_id, symbol) in world.world_symbols(query) { |
134 | let line_index = world.file_line_index(path)?; | 139 | let line_index = world.file_line_index(file_id)?; |
135 | let info = SymbolInformation { | 140 | let info = SymbolInformation { |
136 | name: symbol.name.to_string(), | 141 | name: symbol.name.to_string(), |
137 | kind: symbol.kind.conv(), | 142 | kind: symbol.kind.conv(), |
138 | location: (path, symbol.node_range).try_conv_with(&line_index)?, | 143 | location: to_location( |
144 | file_id, symbol.node_range, | ||
145 | path_map, &line_index | ||
146 | )?, | ||
139 | container_name: None, | 147 | container_name: None, |
140 | }; | 148 | }; |
141 | res.push(info); | 149 | res.push(info); |
@@ -146,15 +154,19 @@ pub fn handle_workspace_symbol( | |||
146 | 154 | ||
147 | pub fn handle_goto_definition( | 155 | pub fn handle_goto_definition( |
148 | world: World, | 156 | world: World, |
157 | path_map: PathMap, | ||
149 | params: req::TextDocumentPositionParams, | 158 | params: req::TextDocumentPositionParams, |
150 | ) -> Result<Option<req::GotoDefinitionResponse>> { | 159 | ) -> Result<Option<req::GotoDefinitionResponse>> { |
151 | let path = params.text_document.file_path()?; | 160 | let file_id = params.text_document.try_conv_with(&path_map)?; |
152 | let line_index = world.file_line_index(&path)?; | 161 | let line_index = world.file_line_index(file_id)?; |
153 | let offset = params.position.conv_with(&line_index); | 162 | let offset = params.position.conv_with(&line_index); |
154 | let mut res = Vec::new(); | 163 | let mut res = Vec::new(); |
155 | for (path, symbol) in world.approximately_resolve_symbol(&path, offset)? { | 164 | for (file_id, symbol) in world.approximately_resolve_symbol(file_id, offset)? { |
156 | let line_index = world.file_line_index(path)?; | 165 | let line_index = world.file_line_index(file_id)?; |
157 | let location = (path, symbol.node_range).try_conv_with(&line_index)?; | 166 | let location = to_location( |
167 | file_id, symbol.node_range, | ||
168 | &path_map, &line_index, | ||
169 | )?; | ||
158 | res.push(location) | 170 | res.push(location) |
159 | } | 171 | } |
160 | Ok(Some(req::GotoDefinitionResponse::Array(res))) | 172 | Ok(Some(req::GotoDefinitionResponse::Array(res))) |
@@ -162,6 +174,7 @@ pub fn handle_goto_definition( | |||
162 | 174 | ||
163 | pub fn handle_execute_command( | 175 | pub fn handle_execute_command( |
164 | world: World, | 176 | world: World, |
177 | path_map: PathMap, | ||
165 | mut params: req::ExecuteCommandParams, | 178 | mut params: req::ExecuteCommandParams, |
166 | ) -> Result<req::ApplyWorkspaceEditParams> { | 179 | ) -> Result<req::ApplyWorkspaceEditParams> { |
167 | if params.command.as_str() != "apply_code_action" { | 180 | if params.command.as_str() != "apply_code_action" { |
@@ -172,8 +185,8 @@ pub fn handle_execute_command( | |||
172 | } | 185 | } |
173 | let arg = params.arguments.pop().unwrap(); | 186 | let arg = params.arguments.pop().unwrap(); |
174 | let arg: ActionRequest = from_value(arg)?; | 187 | let arg: ActionRequest = from_value(arg)?; |
175 | let path = arg.text_document.file_path()?; | 188 | let file_id = arg.text_document.try_conv_with(&path_map)?; |
176 | let file = world.file_syntax(&path)?; | 189 | let file = world.file_syntax(file_id)?; |
177 | let edit = match arg.id { | 190 | let edit = match arg.id { |
178 | ActionId::FlipComma => libeditor::flip_comma(&file, arg.offset).map(|edit| edit()), | 191 | ActionId::FlipComma => libeditor::flip_comma(&file, arg.offset).map(|edit| edit()), |
179 | ActionId::AddDerive => libeditor::add_derive(&file, arg.offset).map(|edit| edit()), | 192 | ActionId::AddDerive => libeditor::add_derive(&file, arg.offset).map(|edit| edit()), |
@@ -182,7 +195,7 @@ pub fn handle_execute_command( | |||
182 | Some(edit) => edit, | 195 | Some(edit) => edit, |
183 | None => bail!("command not applicable"), | 196 | None => bail!("command not applicable"), |
184 | }; | 197 | }; |
185 | let line_index = world.file_line_index(&path)?; | 198 | let line_index = world.file_line_index(file_id)?; |
186 | let mut changes = HashMap::new(); | 199 | let mut changes = HashMap::new(); |
187 | changes.insert( | 200 | changes.insert( |
188 | arg.text_document.uri, | 201 | arg.text_document.uri, |
@@ -231,10 +244,14 @@ impl ActionId { | |||
231 | } | 244 | } |
232 | } | 245 | } |
233 | 246 | ||
234 | pub fn publish_diagnostics(world: World, uri: Url) -> Result<req::PublishDiagnosticsParams> { | 247 | pub fn publish_diagnostics( |
235 | let path = uri.file_path()?; | 248 | world: World, |
236 | let file = world.file_syntax(&path)?; | 249 | path_map: PathMap, |
237 | let line_index = world.file_line_index(&path)?; | 250 | uri: Url |
251 | ) -> Result<req::PublishDiagnosticsParams> { | ||
252 | let file_id = uri.try_conv_with(&path_map)?; | ||
253 | let file = world.file_syntax(file_id)?; | ||
254 | let line_index = world.file_line_index(file_id)?; | ||
238 | let diagnostics = libeditor::diagnostics(&file) | 255 | let diagnostics = libeditor::diagnostics(&file) |
239 | .into_iter() | 256 | .into_iter() |
240 | .map(|d| Diagnostic { | 257 | .map(|d| Diagnostic { |
@@ -248,10 +265,14 @@ pub fn publish_diagnostics(world: World, uri: Url) -> Result<req::PublishDiagnos | |||
248 | Ok(req::PublishDiagnosticsParams { uri, diagnostics }) | 265 | Ok(req::PublishDiagnosticsParams { uri, diagnostics }) |
249 | } | 266 | } |
250 | 267 | ||
251 | pub fn publish_decorations(world: World, uri: Url) -> Result<req::PublishDecorationsParams> { | 268 | pub fn publish_decorations( |
252 | let path = uri.file_path()?; | 269 | world: World, |
253 | let file = world.file_syntax(&path)?; | 270 | path_map: PathMap, |
254 | let line_index = world.file_line_index(&path)?; | 271 | uri: Url |
272 | ) -> Result<req::PublishDecorationsParams> { | ||
273 | let file_id = uri.try_conv_with(&path_map)?; | ||
274 | let file = world.file_syntax(file_id)?; | ||
275 | let line_index = world.file_line_index(file_id)?; | ||
255 | let decorations = libeditor::highlight(&file) | 276 | let decorations = libeditor::highlight(&file) |
256 | .into_iter() | 277 | .into_iter() |
257 | .map(|h| Decoration { | 278 | .map(|h| Decoration { |
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index bc898c17b..2a31297be 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs | |||
@@ -1,22 +1,21 @@ | |||
1 | mod handlers; | 1 | mod handlers; |
2 | 2 | ||
3 | use std::{ | 3 | use std::{ |
4 | path::PathBuf, | ||
5 | collections::{HashSet, HashMap}, | 4 | collections::{HashSet, HashMap}, |
6 | }; | 5 | }; |
7 | 6 | ||
8 | use threadpool::ThreadPool; | 7 | use threadpool::ThreadPool; |
9 | use crossbeam_channel::{Sender, Receiver}; | 8 | use crossbeam_channel::{Sender, Receiver}; |
10 | use languageserver_types::Url; | 9 | use languageserver_types::Url; |
11 | use libanalysis::{World, WorldState}; | 10 | use libanalysis::{World, WorldState, FileId}; |
12 | use serde_json::to_value; | 11 | use serde_json::to_value; |
13 | 12 | ||
14 | use { | 13 | use { |
15 | req, dispatch, | 14 | req, dispatch, |
16 | Task, Result, | 15 | Task, Result, PathMap, |
17 | io::{Io, RawMsg, RawRequest, RawNotification}, | 16 | io::{Io, RawMsg, RawRequest, RawNotification}, |
18 | util::FilePath, | ||
19 | vfs::{FileEvent, FileEventKind}, | 17 | vfs::{FileEvent, FileEventKind}, |
18 | conv::TryConvWith, | ||
20 | main_loop::handlers::{ | 19 | main_loop::handlers::{ |
21 | handle_syntax_tree, | 20 | handle_syntax_tree, |
22 | handle_extend_selection, | 21 | handle_extend_selection, |
@@ -41,7 +40,8 @@ pub(super) fn main_loop( | |||
41 | info!("server initialized, serving requests"); | 40 | info!("server initialized, serving requests"); |
42 | let mut next_request_id = 0; | 41 | let mut next_request_id = 0; |
43 | let mut pending_requests: HashSet<u64> = HashSet::new(); | 42 | let mut pending_requests: HashSet<u64> = HashSet::new(); |
44 | let mut mem_map: HashMap<PathBuf, Option<String>> = HashMap::new(); | 43 | let mut path_map = PathMap::new(); |
44 | let mut mem_map: HashMap<FileId, Option<String>> = HashMap::new(); | ||
45 | let mut fs_events_receiver = Some(&fs_events_receiver); | 45 | let mut fs_events_receiver = Some(&fs_events_receiver); |
46 | loop { | 46 | loop { |
47 | enum Event { | 47 | enum Event { |
@@ -98,12 +98,15 @@ pub(super) fn main_loop( | |||
98 | }; | 98 | }; |
99 | (event.path, text) | 99 | (event.path, text) |
100 | }) | 100 | }) |
101 | .filter_map(|(path, text)| { | 101 | .map(|(path, text)| { |
102 | if mem_map.contains_key(path.as_path()) { | 102 | (path_map.get_or_insert(path), text) |
103 | mem_map.insert(path, text); | 103 | }) |
104 | .filter_map(|(id, text)| { | ||
105 | if mem_map.contains_key(&id) { | ||
106 | mem_map.insert(id, text); | ||
104 | None | 107 | None |
105 | } else { | 108 | } else { |
106 | Some((path, text)) | 109 | Some((id, text)) |
107 | } | 110 | } |
108 | }); | 111 | }); |
109 | 112 | ||
@@ -112,12 +115,12 @@ pub(super) fn main_loop( | |||
112 | Event::Msg(msg) => { | 115 | Event::Msg(msg) => { |
113 | match msg { | 116 | match msg { |
114 | RawMsg::Request(req) => { | 117 | RawMsg::Request(req) => { |
115 | if !on_request(io, world, pool, &task_sender, req)? { | 118 | if !on_request(io, world, &path_map, pool, &task_sender, req)? { |
116 | return Ok(()); | 119 | return Ok(()); |
117 | } | 120 | } |
118 | } | 121 | } |
119 | RawMsg::Notification(not) => { | 122 | RawMsg::Notification(not) => { |
120 | on_notification(io, world, pool, &task_sender, not, &mut mem_map)? | 123 | on_notification(io, world, &mut path_map, pool, &task_sender, not, &mut mem_map)? |
121 | } | 124 | } |
122 | RawMsg::Response(resp) => { | 125 | RawMsg::Response(resp) => { |
123 | if !pending_requests.remove(&resp.id) { | 126 | if !pending_requests.remove(&resp.id) { |
@@ -133,36 +136,38 @@ pub(super) fn main_loop( | |||
133 | fn on_request( | 136 | fn on_request( |
134 | io: &mut Io, | 137 | io: &mut Io, |
135 | world: &WorldState, | 138 | world: &WorldState, |
139 | path_map: &PathMap, | ||
136 | pool: &ThreadPool, | 140 | pool: &ThreadPool, |
137 | sender: &Sender<Task>, | 141 | sender: &Sender<Task>, |
138 | req: RawRequest, | 142 | req: RawRequest, |
139 | ) -> Result<bool> { | 143 | ) -> Result<bool> { |
140 | let mut req = Some(req); | 144 | let mut req = Some(req); |
141 | handle_request_on_threadpool::<req::SyntaxTree>( | 145 | handle_request_on_threadpool::<req::SyntaxTree>( |
142 | &mut req, pool, world, sender, handle_syntax_tree, | 146 | &mut req, pool, path_map, world, sender, handle_syntax_tree, |
143 | )?; | 147 | )?; |
144 | handle_request_on_threadpool::<req::ExtendSelection>( | 148 | handle_request_on_threadpool::<req::ExtendSelection>( |
145 | &mut req, pool, world, sender, handle_extend_selection, | 149 | &mut req, pool, path_map, world, sender, handle_extend_selection, |
146 | )?; | 150 | )?; |
147 | handle_request_on_threadpool::<req::DocumentSymbolRequest>( | 151 | handle_request_on_threadpool::<req::DocumentSymbolRequest>( |
148 | &mut req, pool, world, sender, handle_document_symbol, | 152 | &mut req, pool, path_map, world, sender, handle_document_symbol, |
149 | )?; | 153 | )?; |
150 | handle_request_on_threadpool::<req::CodeActionRequest>( | 154 | handle_request_on_threadpool::<req::CodeActionRequest>( |
151 | &mut req, pool, world, sender, handle_code_action, | 155 | &mut req, pool, path_map, world, sender, handle_code_action, |
152 | )?; | 156 | )?; |
153 | handle_request_on_threadpool::<req::WorkspaceSymbol>( | 157 | handle_request_on_threadpool::<req::WorkspaceSymbol>( |
154 | &mut req, pool, world, sender, handle_workspace_symbol, | 158 | &mut req, pool, path_map, world, sender, handle_workspace_symbol, |
155 | )?; | 159 | )?; |
156 | handle_request_on_threadpool::<req::GotoDefinition>( | 160 | handle_request_on_threadpool::<req::GotoDefinition>( |
157 | &mut req, pool, world, sender, handle_goto_definition, | 161 | &mut req, pool, path_map, world, sender, handle_goto_definition, |
158 | )?; | 162 | )?; |
159 | dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| { | 163 | dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| { |
160 | io.send(RawMsg::Response(resp.into_response(Ok(None))?)); | 164 | io.send(RawMsg::Response(resp.into_response(Ok(None))?)); |
161 | 165 | ||
162 | let world = world.snapshot(); | 166 | let world = world.snapshot(); |
167 | let path_map = path_map.clone(); | ||
163 | let sender = sender.clone(); | 168 | let sender = sender.clone(); |
164 | pool.execute(move || { | 169 | pool.execute(move || { |
165 | let task = match handle_execute_command(world, params) { | 170 | let task = match handle_execute_command(world, path_map, params) { |
166 | Ok(req) => match to_value(req) { | 171 | Ok(req) => match to_value(req) { |
167 | Err(e) => Task::Die(e.into()), | 172 | Err(e) => Task::Die(e.into()), |
168 | Ok(params) => { | 173 | Ok(params) => { |
@@ -202,39 +207,43 @@ fn on_request( | |||
202 | fn on_notification( | 207 | fn on_notification( |
203 | io: &mut Io, | 208 | io: &mut Io, |
204 | world: &mut WorldState, | 209 | world: &mut WorldState, |
210 | path_map: &mut PathMap, | ||
205 | pool: &ThreadPool, | 211 | pool: &ThreadPool, |
206 | sender: &Sender<Task>, | 212 | sender: &Sender<Task>, |
207 | not: RawNotification, | 213 | not: RawNotification, |
208 | mem_map: &mut HashMap<PathBuf, Option<String>>, | 214 | mem_map: &mut HashMap<FileId, Option<String>>, |
209 | ) -> Result<()> { | 215 | ) -> Result<()> { |
210 | let mut not = Some(not); | 216 | let mut not = Some(not); |
211 | dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| { | 217 | dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| { |
212 | let path = params.text_document.file_path()?; | 218 | let uri = params.text_document.uri; |
213 | mem_map.insert(path.clone(), None); | 219 | let path = uri.to_file_path() |
214 | world.change_file(path, Some(params.text_document.text)); | 220 | .map_err(|()| format_err!("invalid uri: {}", uri))?; |
221 | let file_id = path_map.get_or_insert(path); | ||
222 | mem_map.insert(file_id, None); | ||
223 | world.change_file(file_id, Some(params.text_document.text)); | ||
215 | update_file_notifications_on_threadpool( | 224 | update_file_notifications_on_threadpool( |
216 | pool, world.snapshot(), sender.clone(), params.text_document.uri, | 225 | pool, world.snapshot(), path_map.clone(), sender.clone(), uri, |
217 | ); | 226 | ); |
218 | Ok(()) | 227 | Ok(()) |
219 | })?; | 228 | })?; |
220 | dispatch::handle_notification::<req::DidChangeTextDocument, _>(&mut not, |mut params| { | 229 | dispatch::handle_notification::<req::DidChangeTextDocument, _>(&mut not, |mut params| { |
221 | let path = params.text_document.file_path()?; | 230 | let file_id = params.text_document.try_conv_with(path_map)?; |
222 | let text = params.content_changes.pop() | 231 | let text = params.content_changes.pop() |
223 | .ok_or_else(|| format_err!("empty changes"))? | 232 | .ok_or_else(|| format_err!("empty changes"))? |
224 | .text; | 233 | .text; |
225 | world.change_file(path, Some(text)); | 234 | world.change_file(file_id, Some(text)); |
226 | update_file_notifications_on_threadpool( | 235 | update_file_notifications_on_threadpool( |
227 | pool, world.snapshot(), sender.clone(), params.text_document.uri, | 236 | pool, world.snapshot(), path_map.clone(), sender.clone(), params.text_document.uri, |
228 | ); | 237 | ); |
229 | Ok(()) | 238 | Ok(()) |
230 | })?; | 239 | })?; |
231 | dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| { | 240 | dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| { |
232 | let path = params.text_document.file_path()?; | 241 | let file_id = params.text_document.try_conv_with(path_map)?; |
233 | let text = match mem_map.remove(&path) { | 242 | let text = match mem_map.remove(&file_id) { |
234 | Some(text) => text, | 243 | Some(text) => text, |
235 | None => bail!("unmatched close notification"), | 244 | None => bail!("unmatched close notification"), |
236 | }; | 245 | }; |
237 | world.change_file(path, text); | 246 | world.change_file(file_id, text); |
238 | let not = req::PublishDiagnosticsParams { | 247 | let not = req::PublishDiagnosticsParams { |
239 | uri: params.text_document.uri, | 248 | uri: params.text_document.uri, |
240 | diagnostics: Vec::new(), | 249 | diagnostics: Vec::new(), |
@@ -253,16 +262,18 @@ fn on_notification( | |||
253 | fn handle_request_on_threadpool<R: req::ClientRequest>( | 262 | fn handle_request_on_threadpool<R: req::ClientRequest>( |
254 | req: &mut Option<RawRequest>, | 263 | req: &mut Option<RawRequest>, |
255 | pool: &ThreadPool, | 264 | pool: &ThreadPool, |
265 | path_map: &PathMap, | ||
256 | world: &WorldState, | 266 | world: &WorldState, |
257 | sender: &Sender<Task>, | 267 | sender: &Sender<Task>, |
258 | f: fn(World, R::Params) -> Result<R::Result>, | 268 | f: fn(World, PathMap, R::Params) -> Result<R::Result>, |
259 | ) -> Result<()> | 269 | ) -> Result<()> |
260 | { | 270 | { |
261 | dispatch::handle_request::<R, _>(req, |params, resp| { | 271 | dispatch::handle_request::<R, _>(req, |params, resp| { |
262 | let world = world.snapshot(); | 272 | let world = world.snapshot(); |
273 | let path_map = path_map.clone(); | ||
263 | let sender = sender.clone(); | 274 | let sender = sender.clone(); |
264 | pool.execute(move || { | 275 | pool.execute(move || { |
265 | let res = f(world, params); | 276 | let res = f(world, path_map, params); |
266 | let task = match resp.into_response(res) { | 277 | let task = match resp.into_response(res) { |
267 | Ok(resp) => Task::Respond(resp), | 278 | Ok(resp) => Task::Respond(resp), |
268 | Err(e) => Task::Die(e), | 279 | Err(e) => Task::Die(e), |
@@ -276,11 +287,12 @@ fn handle_request_on_threadpool<R: req::ClientRequest>( | |||
276 | fn update_file_notifications_on_threadpool( | 287 | fn update_file_notifications_on_threadpool( |
277 | pool: &ThreadPool, | 288 | pool: &ThreadPool, |
278 | world: World, | 289 | world: World, |
290 | path_map: PathMap, | ||
279 | sender: Sender<Task>, | 291 | sender: Sender<Task>, |
280 | uri: Url, | 292 | uri: Url, |
281 | ) { | 293 | ) { |
282 | pool.execute(move || { | 294 | pool.execute(move || { |
283 | match publish_diagnostics(world.clone(), uri.clone()) { | 295 | match publish_diagnostics(world.clone(), path_map.clone(), uri.clone()) { |
284 | Err(e) => { | 296 | Err(e) => { |
285 | error!("failed to compute diagnostics: {:?}", e) | 297 | error!("failed to compute diagnostics: {:?}", e) |
286 | } | 298 | } |
@@ -289,7 +301,7 @@ fn update_file_notifications_on_threadpool( | |||
289 | sender.send(Task::Notify(not)); | 301 | sender.send(Task::Notify(not)); |
290 | } | 302 | } |
291 | } | 303 | } |
292 | match publish_decorations(world, uri) { | 304 | match publish_decorations(world, path_map.clone(), uri) { |
293 | Err(e) => { | 305 | Err(e) => { |
294 | error!("failed to compute decorations: {:?}", e) | 306 | error!("failed to compute decorations: {:?}", e) |
295 | } | 307 | } |
diff --git a/crates/server/src/path_map.rs b/crates/server/src/path_map.rs new file mode 100644 index 000000000..2454ba05f --- /dev/null +++ b/crates/server/src/path_map.rs | |||
@@ -0,0 +1,47 @@ | |||
1 | use std::path::{PathBuf, Path}; | ||
2 | use im; | ||
3 | use libanalysis::{FileId}; | ||
4 | |||
5 | #[derive(Debug, Default, Clone)] | ||
6 | pub struct PathMap { | ||
7 | next_id: u32, | ||
8 | path2id: im::HashMap<PathBuf, FileId>, | ||
9 | id2path: im::HashMap<FileId, PathBuf>, | ||
10 | } | ||
11 | |||
12 | impl PathMap { | ||
13 | pub fn new() -> PathMap { | ||
14 | Default::default() | ||
15 | } | ||
16 | |||
17 | pub fn get_or_insert(&mut self, path: PathBuf) -> FileId { | ||
18 | self.path2id.get(path.as_path()) | ||
19 | .map(|&id| id) | ||
20 | .unwrap_or_else(|| { | ||
21 | let id = self.new_file_id(); | ||
22 | self.insert(path, id); | ||
23 | id | ||
24 | }) | ||
25 | } | ||
26 | |||
27 | pub fn get_id(&self, path: &Path) -> Option<FileId> { | ||
28 | self.path2id.get(path).map(|&id| id) | ||
29 | } | ||
30 | |||
31 | pub fn get_path(&self, id: FileId) -> &Path { | ||
32 | self.id2path.get(&id) | ||
33 | .unwrap() | ||
34 | .as_path() | ||
35 | } | ||
36 | |||
37 | fn insert(&mut self, path: PathBuf, id: FileId) { | ||
38 | self.path2id.insert(path.clone(), id); | ||
39 | self.id2path.insert(id, path.clone()); | ||
40 | } | ||
41 | |||
42 | fn new_file_id(&mut self) -> FileId { | ||
43 | let id = FileId(self.next_id); | ||
44 | self.next_id += 1; | ||
45 | id | ||
46 | } | ||
47 | } | ||
diff --git a/crates/server/src/util.rs b/crates/server/src/util.rs deleted file mode 100644 index 6747c20a8..000000000 --- a/crates/server/src/util.rs +++ /dev/null | |||
@@ -1,33 +0,0 @@ | |||
1 | use std::path::PathBuf; | ||
2 | use languageserver_types::{TextDocumentItem, VersionedTextDocumentIdentifier, | ||
3 | TextDocumentIdentifier, Url}; | ||
4 | use ::{Result}; | ||
5 | |||
6 | pub trait FilePath { | ||
7 | fn file_path(&self) -> Result<PathBuf>; | ||
8 | } | ||
9 | |||
10 | impl FilePath for TextDocumentItem { | ||
11 | fn file_path(&self) -> Result<PathBuf> { | ||
12 | self.uri.file_path() | ||
13 | } | ||
14 | } | ||
15 | |||
16 | impl FilePath for VersionedTextDocumentIdentifier { | ||
17 | fn file_path(&self) -> Result<PathBuf> { | ||
18 | self.uri.file_path() | ||
19 | } | ||
20 | } | ||
21 | |||
22 | impl FilePath for TextDocumentIdentifier { | ||
23 | fn file_path(&self) -> Result<PathBuf> { | ||
24 | self.uri.file_path() | ||
25 | } | ||
26 | } | ||
27 | |||
28 | impl FilePath for Url { | ||
29 | fn file_path(&self) -> Result<PathBuf> { | ||
30 | self.to_file_path() | ||
31 | .map_err(|()| format_err!("invalid uri: {}", self)) | ||
32 | } | ||
33 | } | ||