diff options
author | Aleksey Kladov <[email protected]> | 2018-08-10 19:13:39 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-10 19:13:39 +0100 |
commit | 120789804d5483f14c9682b2b777adf6d2992547 (patch) | |
tree | bccbe4a72e648516f838499b91e5158484b5cbcd /codeless/server | |
parent | 1be7af26a83b79863efb0d66a77b1fb7c0235bd2 (diff) |
Add line index
Diffstat (limited to 'codeless/server')
-rw-r--r-- | codeless/server/src/caps.rs | 17 | ||||
-rw-r--r-- | codeless/server/src/dispatch.rs | 41 | ||||
-rw-r--r-- | codeless/server/src/handlers.rs | 13 | ||||
-rw-r--r-- | codeless/server/src/main.rs | 75 | ||||
-rw-r--r-- | codeless/server/src/req.rs | 25 |
5 files changed, 147 insertions, 24 deletions
diff --git a/codeless/server/src/caps.rs b/codeless/server/src/caps.rs index b2fad6732..3d89c64a9 100644 --- a/codeless/server/src/caps.rs +++ b/codeless/server/src/caps.rs | |||
@@ -1,7 +1,20 @@ | |||
1 | use languageserver_types::ServerCapabilities; | 1 | use languageserver_types::{ |
2 | ServerCapabilities, | ||
3 | TextDocumentSyncCapability, | ||
4 | TextDocumentSyncOptions, | ||
5 | TextDocumentSyncKind, | ||
6 | }; | ||
2 | 7 | ||
3 | pub const SERVER_CAPABILITIES: ServerCapabilities = ServerCapabilities { | 8 | pub const SERVER_CAPABILITIES: ServerCapabilities = ServerCapabilities { |
4 | text_document_sync: None, | 9 | text_document_sync: Some(TextDocumentSyncCapability::Options( |
10 | TextDocumentSyncOptions { | ||
11 | open_close: Some(true), | ||
12 | change: Some(TextDocumentSyncKind::Full), | ||
13 | will_save: None, | ||
14 | will_save_wait_until: None, | ||
15 | save: None, | ||
16 | } | ||
17 | )), | ||
5 | hover_provider: None, | 18 | hover_provider: None, |
6 | completion_provider: None, | 19 | completion_provider: None, |
7 | signature_help_provider: None, | 20 | signature_help_provider: None, |
diff --git a/codeless/server/src/dispatch.rs b/codeless/server/src/dispatch.rs index ee87fa6c3..41437b62a 100644 --- a/codeless/server/src/dispatch.rs +++ b/codeless/server/src/dispatch.rs | |||
@@ -9,8 +9,8 @@ use drop_bomb::DropBomb; | |||
9 | 9 | ||
10 | use ::{ | 10 | use ::{ |
11 | Result, | 11 | Result, |
12 | req::Request, | 12 | req::{Request, Notification}, |
13 | io::{Io, RawMsg, RawResponse, RawRequest}, | 13 | io::{Io, RawMsg, RawResponse, RawRequest, RawNotification}, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | pub struct Responder<R: Request> { | 16 | pub struct Responder<R: Request> { |
@@ -52,7 +52,7 @@ impl<R: Request> Responder<R> | |||
52 | } | 52 | } |
53 | 53 | ||
54 | 54 | ||
55 | pub fn parse_as<R>(raw: RawRequest) -> Result<::std::result::Result<(R::Params, Responder<R>), RawRequest>> | 55 | pub fn parse_request_as<R>(raw: RawRequest) -> Result<::std::result::Result<(R::Params, Responder<R>), RawRequest>> |
56 | where | 56 | where |
57 | R: Request, | 57 | R: Request, |
58 | R::Params: DeserializeOwned, | 58 | R::Params: DeserializeOwned, |
@@ -71,13 +71,13 @@ pub fn parse_as<R>(raw: RawRequest) -> Result<::std::result::Result<(R::Params, | |||
71 | Ok(Ok((params, responder))) | 71 | Ok(Ok((params, responder))) |
72 | } | 72 | } |
73 | 73 | ||
74 | pub fn expect<R>(io: &mut Io, raw: RawRequest) -> Result<Option<(R::Params, Responder<R>)>> | 74 | pub fn expect_request<R>(io: &mut Io, raw: RawRequest) -> Result<Option<(R::Params, Responder<R>)>> |
75 | where | 75 | where |
76 | R: Request, | 76 | R: Request, |
77 | R::Params: DeserializeOwned, | 77 | R::Params: DeserializeOwned, |
78 | R::Result: Serialize, | 78 | R::Result: Serialize, |
79 | { | 79 | { |
80 | let ret = match parse_as::<R>(raw)? { | 80 | let ret = match parse_request_as::<R>(raw)? { |
81 | Ok(x) => Some(x), | 81 | Ok(x) => Some(x), |
82 | Err(raw) => { | 82 | Err(raw) => { |
83 | unknown_method(io, raw)?; | 83 | unknown_method(io, raw)?; |
@@ -87,6 +87,37 @@ pub fn expect<R>(io: &mut Io, raw: RawRequest) -> Result<Option<(R::Params, Resp | |||
87 | Ok(ret) | 87 | Ok(ret) |
88 | } | 88 | } |
89 | 89 | ||
90 | pub fn parse_notification_as<N>(raw: RawNotification) -> Result<::std::result::Result<N::Params, RawNotification>> | ||
91 | where | ||
92 | N: Notification, | ||
93 | N::Params: DeserializeOwned, | ||
94 | { | ||
95 | if raw.method != N::METHOD { | ||
96 | return Ok(Err(raw)); | ||
97 | } | ||
98 | let params: N::Params = serde_json::from_value(raw.params)?; | ||
99 | Ok(Ok(params)) | ||
100 | } | ||
101 | |||
102 | pub fn handle_notification<N, F>(not: &mut Option<RawNotification>, f: F) -> Result<()> | ||
103 | where | ||
104 | N: Notification, | ||
105 | N::Params: DeserializeOwned, | ||
106 | F: FnOnce(N::Params) -> Result<()> | ||
107 | { | ||
108 | match not.take() { | ||
109 | None => Ok(()), | ||
110 | Some(n) => match parse_notification_as::<N>(n)? { | ||
111 | Ok(params) => f(params), | ||
112 | Err(n) => { | ||
113 | *not = Some(n); | ||
114 | Ok(()) | ||
115 | }, | ||
116 | } | ||
117 | } | ||
118 | } | ||
119 | |||
120 | |||
90 | pub fn unknown_method(io: &mut Io, raw: RawRequest) -> Result<()> { | 121 | pub fn unknown_method(io: &mut Io, raw: RawRequest) -> Result<()> { |
91 | error(io, raw.id, ErrorCode::MethodNotFound, "unknown method") | 122 | error(io, raw.id, ErrorCode::MethodNotFound, "unknown method") |
92 | } | 123 | } |
diff --git a/codeless/server/src/handlers.rs b/codeless/server/src/handlers.rs new file mode 100644 index 000000000..3f257941a --- /dev/null +++ b/codeless/server/src/handlers.rs | |||
@@ -0,0 +1,13 @@ | |||
1 | use libanalysis::World; | ||
2 | use libeditor; | ||
3 | use {req, Result}; | ||
4 | |||
5 | pub fn handle_syntax_tree( | ||
6 | world: World, | ||
7 | params: req::SyntaxTreeParams | ||
8 | ) -> Result<String> { | ||
9 | let path = params.text_document.uri.to_file_path() | ||
10 | .map_err(|()| format_err!("invalid path"))?; | ||
11 | let file = world.file_syntax(&path)?; | ||
12 | Ok(libeditor::syntax_tree(&file)) | ||
13 | } | ||
diff --git a/codeless/server/src/main.rs b/codeless/server/src/main.rs index fdb2fe2d5..287d650fa 100644 --- a/codeless/server/src/main.rs +++ b/codeless/server/src/main.rs | |||
@@ -19,20 +19,25 @@ mod io; | |||
19 | mod caps; | 19 | mod caps; |
20 | mod req; | 20 | mod req; |
21 | mod dispatch; | 21 | mod dispatch; |
22 | mod handlers; | ||
23 | |||
24 | use std::path::PathBuf; | ||
22 | 25 | ||
23 | use threadpool::ThreadPool; | 26 | use threadpool::ThreadPool; |
24 | use crossbeam_channel::{bounded, Sender, Receiver}; | 27 | use crossbeam_channel::{bounded, Sender, Receiver}; |
25 | use flexi_logger::Logger; | 28 | use flexi_logger::Logger; |
26 | use libanalysis::WorldState; | 29 | use libanalysis::WorldState; |
30 | use languageserver_types::{TextDocumentItem, VersionedTextDocumentIdentifier, TextDocumentIdentifier}; | ||
27 | 31 | ||
28 | use ::{ | 32 | use ::{ |
29 | io::{Io, RawMsg}, | 33 | io::{Io, RawMsg}, |
34 | handlers::handle_syntax_tree, | ||
30 | }; | 35 | }; |
31 | 36 | ||
32 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | 37 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; |
33 | 38 | ||
34 | fn main() -> Result<()> { | 39 | fn main() -> Result<()> { |
35 | Logger::with_env_or_str("m=trace") | 40 | Logger::with_env_or_str("m=trace, libanalysis=trace") |
36 | .log_to_file() | 41 | .log_to_file() |
37 | .directory("log") | 42 | .directory("log") |
38 | .start()?; | 43 | .start()?; |
@@ -70,7 +75,7 @@ fn initialize(io: &mut Io) -> Result<()> { | |||
70 | loop { | 75 | loop { |
71 | match io.recv()? { | 76 | match io.recv()? { |
72 | RawMsg::Request(req) => { | 77 | RawMsg::Request(req) => { |
73 | if let Some((_params, resp)) = dispatch::expect::<req::Initialize>(io, req)? { | 78 | if let Some((_params, resp)) = dispatch::expect_request::<req::Initialize>(io, req)? { |
74 | resp.result(io, req::InitializeResult { | 79 | resp.result(io, req::InitializeResult { |
75 | capabilities: caps::SERVER_CAPABILITIES | 80 | capabilities: caps::SERVER_CAPABILITIES |
76 | })?; | 81 | })?; |
@@ -148,18 +153,12 @@ fn main_loop( | |||
148 | 153 | ||
149 | match msg { | 154 | match msg { |
150 | RawMsg::Request(req) => { | 155 | RawMsg::Request(req) => { |
151 | let req = match dispatch::parse_as::<req::SyntaxTree>(req)? { | 156 | let req = match dispatch::parse_request_as::<req::SyntaxTree>(req)? { |
152 | Ok((params, resp)) => { | 157 | Ok((params, resp)) => { |
153 | let world = world.snapshot(); | 158 | let world = world.snapshot(); |
154 | let sender = sender.clone(); | 159 | let sender = sender.clone(); |
155 | pool.execute(move || { | 160 | pool.execute(move || { |
156 | let res: Result<String> = (|| { | 161 | let res: Result<String> = handle_syntax_tree(world, params); |
157 | let path = params.text_document.uri.to_file_path() | ||
158 | .map_err(|()| format_err!("invalid path"))?; | ||
159 | let file = world.file_syntax(&path)?; | ||
160 | Ok(libeditor::syntax_tree(&file)) | ||
161 | })(); | ||
162 | |||
163 | sender.send(Box::new(|io: &mut Io| resp.response(io, res))) | 162 | sender.send(Box::new(|io: &mut Io| resp.response(io, res))) |
164 | }); | 163 | }); |
165 | continue; | 164 | continue; |
@@ -167,12 +166,38 @@ fn main_loop( | |||
167 | Err(req) => req, | 166 | Err(req) => req, |
168 | }; | 167 | }; |
169 | 168 | ||
170 | if let Some(((), resp)) = dispatch::expect::<req::Shutdown>(io, req)? { | 169 | if let Some(((), resp)) = dispatch::expect_request::<req::Shutdown>(io, req)? { |
171 | info!("shutdown request"); | 170 | info!("clean shutdown started"); |
172 | resp.result(io, ())?; | 171 | resp.result(io, ())?; |
173 | return Ok(()); | 172 | return Ok(()); |
174 | } | 173 | } |
175 | } | 174 | } |
175 | RawMsg::Notification(not) => { | ||
176 | use dispatch::handle_notification as h; | ||
177 | let mut not = Some(not); | ||
178 | h::<req::DidOpenTextDocument, _>(&mut not, |params| { | ||
179 | let path = params.text_document.file_path()?; | ||
180 | world.change_overlay(path, Some(params.text_document.text)); | ||
181 | Ok(()) | ||
182 | })?; | ||
183 | h::<req::DidChangeTextDocument, _>(&mut not, |mut params| { | ||
184 | let path = params.text_document.file_path()?; | ||
185 | let text = params.content_changes.pop() | ||
186 | .ok_or_else(|| format_err!("empty changes"))? | ||
187 | .text; | ||
188 | world.change_overlay(path, Some(text)); | ||
189 | Ok(()) | ||
190 | })?; | ||
191 | h::<req::DidCloseTextDocument, _>(&mut not, |params| { | ||
192 | let path = params.text_document.file_path()?; | ||
193 | world.change_overlay(path, None); | ||
194 | Ok(()) | ||
195 | })?; | ||
196 | |||
197 | if let Some(not) = not { | ||
198 | error!("unhandled notification: {:?}", not) | ||
199 | } | ||
200 | } | ||
176 | msg => { | 201 | msg => { |
177 | eprintln!("msg = {:?}", msg); | 202 | eprintln!("msg = {:?}", msg); |
178 | } | 203 | } |
@@ -180,7 +205,6 @@ fn main_loop( | |||
180 | } | 205 | } |
181 | } | 206 | } |
182 | 207 | ||
183 | |||
184 | trait FnBox<A, R>: Send { | 208 | trait FnBox<A, R>: Send { |
185 | fn call_box(self: Box<Self>, a: A) -> R; | 209 | fn call_box(self: Box<Self>, a: A) -> R; |
186 | } | 210 | } |
@@ -190,3 +214,28 @@ impl<A, R, F: FnOnce(A) -> R + Send> FnBox<A, R> for F { | |||
190 | (*self)(a) | 214 | (*self)(a) |
191 | } | 215 | } |
192 | } | 216 | } |
217 | |||
218 | trait FilePath { | ||
219 | fn file_path(&self) -> Result<PathBuf>; | ||
220 | } | ||
221 | |||
222 | impl FilePath for TextDocumentItem { | ||
223 | fn file_path(&self) -> Result<PathBuf> { | ||
224 | self.uri.to_file_path() | ||
225 | .map_err(|()| format_err!("invalid uri: {}", self.uri)) | ||
226 | } | ||
227 | } | ||
228 | |||
229 | impl FilePath for VersionedTextDocumentIdentifier { | ||
230 | fn file_path(&self) -> Result<PathBuf> { | ||
231 | self.uri.to_file_path() | ||
232 | .map_err(|()| format_err!("invalid uri: {}", self.uri)) | ||
233 | } | ||
234 | } | ||
235 | |||
236 | impl FilePath for TextDocumentIdentifier { | ||
237 | fn file_path(&self) -> Result<PathBuf> { | ||
238 | self.uri.to_file_path() | ||
239 | .map_err(|()| format_err!("invalid uri: {}", self.uri)) | ||
240 | } | ||
241 | } | ||
diff --git a/codeless/server/src/req.rs b/codeless/server/src/req.rs index 35a20a229..ee4a786c7 100644 --- a/codeless/server/src/req.rs +++ b/codeless/server/src/req.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | use languageserver_types::TextDocumentIdentifier; | 1 | use languageserver_types::{TextDocumentIdentifier, Range}; |
2 | pub use languageserver_types::request::*; | 2 | |
3 | pub use languageserver_types::{InitializeResult}; | 3 | pub use languageserver_types::{ |
4 | request::*, notification::*, | ||
5 | InitializeResult, | ||
6 | }; | ||
4 | 7 | ||
5 | pub enum SyntaxTree {} | 8 | pub enum SyntaxTree {} |
6 | 9 | ||
@@ -11,7 +14,21 @@ impl Request for SyntaxTree { | |||
11 | } | 14 | } |
12 | 15 | ||
13 | #[derive(Deserialize, Debug)] | 16 | #[derive(Deserialize, Debug)] |
14 | #[serde(rename_all="camelCase")] | 17 | #[serde(rename_all = "camelCase")] |
15 | pub struct SyntaxTreeParams { | 18 | pub struct SyntaxTreeParams { |
16 | pub text_document: TextDocumentIdentifier | 19 | pub text_document: TextDocumentIdentifier |
17 | } | 20 | } |
21 | |||
22 | pub enum ExtendSelection {} | ||
23 | |||
24 | #[derive(Deserialize, Debug)] | ||
25 | #[serde(rename_all = "camelCase")] | ||
26 | pub struct ExtendSelectionParams { | ||
27 | pub text_document: TextDocumentIdentifier, | ||
28 | pub selections: Vec<Range>, | ||
29 | } | ||
30 | |||
31 | |||
32 | pub struct ExtendSelectionResult { | ||
33 | pub selections: Vec<Range>, | ||
34 | } | ||