aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/libanalysis/src/lib.rs10
-rw-r--r--crates/server/Cargo.toml1
-rw-r--r--crates/server/src/conv.rs72
-rw-r--r--crates/server/src/main.rs6
-rw-r--r--crates/server/src/main_loop/handlers.rs91
-rw-r--r--crates/server/src/main_loop/mod.rs78
-rw-r--r--crates/server/src/path_map.rs47
-rw-r--r--crates/server/src/util.rs33
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>;
37const INDEXING_THRESHOLD: usize = 128; 37const INDEXING_THRESHOLD: usize = 128;
38 38
39pub struct WorldState { 39pub 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)]
50pub struct FileId(u32); 49pub struct FileId(pub u32);
51 50
52impl WorldState { 51impl 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"
16url_serde = "0.2.0" 16url_serde = "0.2.0"
17languageserver-types = "0.49.0" 17languageserver-types = "0.49.0"
18walkdir = "2.2.0" 18walkdir = "2.2.0"
19im = { version = "11.0.1", features = ["arc"] }
19text_unit = { version = "0.1.2", features = ["serde"] } 20text_unit = { version = "0.1.2", features = ["serde"] }
20 21
21libsyntax2 = { path = "../libsyntax2" } 22libsyntax2 = { 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 @@
1use std::path::Path; 1use languageserver_types::{
2 2 Range, SymbolKind, Position, TextEdit, Location, Url,
3use languageserver_types::{Range, SymbolKind, Position, TextEdit, Location, Url}; 3 TextDocumentIdentifier, VersionedTextDocumentIdentifier, TextDocumentItem,
4};
4use libeditor::{LineIndex, LineCol, Edit, AtomEdit}; 5use libeditor::{LineIndex, LineCol, Edit, AtomEdit};
5use libsyntax2::{SyntaxKind, TextUnit, TextRange}; 6use libsyntax2::{SyntaxKind, TextUnit, TextRange};
7use libanalysis::FileId;
6 8
7use Result; 9use {Result, PathMap};
8 10
9pub trait Conv { 11pub trait Conv {
10 type Output; 12 type Output;
@@ -115,21 +117,65 @@ impl ConvWith for AtomEdit {
115 } 117 }
116} 118}
117 119
118impl<'a> TryConvWith for (&'a Path, TextRange) { 120impl<'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
130impl 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
141impl<'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> { 149impl<'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
157impl<'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
165pub 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
133pub trait MapConvWith<'a>: Sized { 179pub 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;
17extern crate libeditor; 17extern crate libeditor;
18extern crate libanalysis; 18extern crate libanalysis;
19extern crate libsyntax2; 19extern crate libsyntax2;
20extern crate im;
20 21
21mod io; 22mod io;
22mod caps; 23mod caps;
23mod req; 24mod req;
24mod dispatch; 25mod dispatch;
25mod util;
26mod conv; 26mod conv;
27mod main_loop; 27mod main_loop;
28mod vfs; 28mod vfs;
29mod path_map;
29 30
30use threadpool::ThreadPool; 31use threadpool::ThreadPool;
31use crossbeam_channel::bounded; 32use crossbeam_channel::bounded;
@@ -33,7 +34,8 @@ use flexi_logger::{Logger, Duplicate};
33use libanalysis::WorldState; 34use libanalysis::WorldState;
34 35
35use ::{ 36use ::{
36 io::{Io, RawMsg, RawResponse, RawRequest, RawNotification} 37 io::{Io, RawMsg, RawResponse, RawRequest, RawNotification},
38 path_map::PathMap,
37}; 39};
38 40
39pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 41pub 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;
11use serde_json::{to_value, from_value}; 11use serde_json::{to_value, from_value};
12 12
13use ::{ 13use ::{
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
19pub fn handle_syntax_tree( 19pub 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
28pub fn handle_extend_selection( 29pub 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
43pub fn handle_document_symbol( 45pub 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
82pub fn handle_code_action( 85pub 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
106pub fn handle_workspace_symbol( 110pub 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
147pub fn handle_goto_definition( 155pub 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
163pub fn handle_execute_command( 175pub 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
234pub fn publish_diagnostics(world: World, uri: Url) -> Result<req::PublishDiagnosticsParams> { 247pub 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
251pub fn publish_decorations(world: World, uri: Url) -> Result<req::PublishDecorationsParams> { 268pub 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 @@
1mod handlers; 1mod handlers;
2 2
3use std::{ 3use std::{
4 path::PathBuf,
5 collections::{HashSet, HashMap}, 4 collections::{HashSet, HashMap},
6}; 5};
7 6
8use threadpool::ThreadPool; 7use threadpool::ThreadPool;
9use crossbeam_channel::{Sender, Receiver}; 8use crossbeam_channel::{Sender, Receiver};
10use languageserver_types::Url; 9use languageserver_types::Url;
11use libanalysis::{World, WorldState}; 10use libanalysis::{World, WorldState, FileId};
12use serde_json::to_value; 11use serde_json::to_value;
13 12
14use { 13use {
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(
133fn on_request( 136fn 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(
202fn on_notification( 207fn 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(
253fn handle_request_on_threadpool<R: req::ClientRequest>( 262fn 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>(
276fn update_file_notifications_on_threadpool( 287fn 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 @@
1use std::path::{PathBuf, Path};
2use im;
3use libanalysis::{FileId};
4
5#[derive(Debug, Default, Clone)]
6pub struct PathMap {
7 next_id: u32,
8 path2id: im::HashMap<PathBuf, FileId>,
9 id2path: im::HashMap<FileId, PathBuf>,
10}
11
12impl 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 @@
1use std::path::PathBuf;
2use languageserver_types::{TextDocumentItem, VersionedTextDocumentIdentifier,
3 TextDocumentIdentifier, Url};
4use ::{Result};
5
6pub trait FilePath {
7 fn file_path(&self) -> Result<PathBuf>;
8}
9
10impl FilePath for TextDocumentItem {
11 fn file_path(&self) -> Result<PathBuf> {
12 self.uri.file_path()
13 }
14}
15
16impl FilePath for VersionedTextDocumentIdentifier {
17 fn file_path(&self) -> Result<PathBuf> {
18 self.uri.file_path()
19 }
20}
21
22impl FilePath for TextDocumentIdentifier {
23 fn file_path(&self) -> Result<PathBuf> {
24 self.uri.file_path()
25 }
26}
27
28impl 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}