Skip to content
This repository was archived by the owner on Dec 29, 2022. It is now read-only.

Commit dab46fa

Browse files
authored
Merge pull request #856 from alexheretic/pre-initialize-request
Handle pre-initialize requests
2 parents f25e416 + a2c28bf commit dab46fa

File tree

4 files changed

+142
-55
lines changed

4 files changed

+142
-55
lines changed

src/server/dispatch.rs

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
use super::requests::*;
1212
use actions::work_pool;
1313
use actions::work_pool::WorkDescription;
14-
use jsonrpc_core as jsonrpc;
14+
use actions::InitActionContext;
1515
use jsonrpc_core::types::ErrorCode;
16+
use lsp_data::LSPRequest;
1617
use server;
17-
use server::{Request, Response};
1818
use server::io::Output;
19-
use actions::InitActionContext;
20-
use lsp_data::LSPRequest;
19+
use server::message::ResponseError;
20+
use server::{Request, Response};
2121
use std::sync::mpsc;
2222
use std::thread;
2323
use std::time::Duration;
@@ -183,18 +183,3 @@ pub trait RequestAction: LSPRequest {
183183
params: Self::Params,
184184
) -> Result<Self::Response, ResponseError>;
185185
}
186-
187-
/// Wrapper for a response error
188-
#[derive(Debug)]
189-
pub enum ResponseError {
190-
/// Error with no special response to the client
191-
Empty,
192-
/// Error with a response to the client
193-
Message(jsonrpc::ErrorCode, String),
194-
}
195-
196-
impl From<()> for ResponseError {
197-
fn from(_: ()) -> Self {
198-
ResponseError::Empty
199-
}
200-
}

src/server/message.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,21 @@ impl<R: ::serde::Serialize + fmt::Debug> Response for R {
4949
}
5050
}
5151

52+
/// Wrapper for a response error
53+
#[derive(Debug)]
54+
pub enum ResponseError {
55+
/// Error with no special response to the client
56+
Empty,
57+
/// Error with a response to the client
58+
Message(jsonrpc::ErrorCode, String),
59+
}
60+
61+
impl From<()> for ResponseError {
62+
fn from(_: ()) -> Self {
63+
ResponseError::Empty
64+
}
65+
}
66+
5267
/// An action taken in response to some notification from the client.
5368
/// Blocks stdin whilst being handled.
5469
pub trait BlockingNotificationAction: LSPNotification {
@@ -60,13 +75,12 @@ pub trait BlockingNotificationAction: LSPNotification {
6075
pub trait BlockingRequestAction: LSPRequest {
6176
type Response: Response + fmt::Debug;
6277

63-
/// Handle request and send its response back along the given output.
78+
/// Handle request and return its response. Output is also provided for additional messaging.
6479
fn handle<O: Output>(
65-
id: usize,
6680
params: Self::Params,
6781
ctx: &mut ActionContext,
6882
out: O,
69-
) -> Result<Self::Response, ()>;
83+
) -> Result<Self::Response, ResponseError>;
7084
}
7185

7286
/// A request that gets JSON serialized in the language server protocol.
@@ -166,10 +180,8 @@ impl<A: BlockingRequestAction> Request<A> {
166180
self,
167181
ctx: &mut ActionContext,
168182
out: &O,
169-
) -> Result<A::Response, ()> {
170-
let result = A::handle(self.id, self.params, ctx, out.clone())?;
171-
result.send(self.id, out);
172-
Ok(result)
183+
) -> Result<A::Response, ResponseError> {
184+
A::handle(self.params, ctx, out.clone())
173185
}
174186
}
175187

src/server/mod.rs

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use actions::{notifications, requests, ActionContext};
1616
use analysis::AnalysisHost;
1717
use config::Config;
18-
use jsonrpc_core::{self as jsonrpc, Id};
18+
use jsonrpc_core::{self as jsonrpc, Id, types::error::ErrorCode};
1919
pub use ls_types::notification::Exit as ExitNotification;
2020
pub use ls_types::request::Initialize as InitializeRequest;
2121
pub use ls_types::request::Shutdown as ShutdownRequest;
@@ -27,13 +27,13 @@ use lsp_data;
2727
use lsp_data::{InitializationOptions, LSPNotification, LSPRequest};
2828
use serde_json;
2929
use server::dispatch::Dispatcher;
30-
pub use server::dispatch::{RequestAction, ResponseError, DEFAULT_REQUEST_TIMEOUT};
30+
pub use server::dispatch::{RequestAction, DEFAULT_REQUEST_TIMEOUT};
3131
pub use server::io::{MessageReader, Output};
3232
use server::io::{StdioMsgReader, StdioOutput};
3333
use server::message::RawMessage;
3434
pub use server::message::{
3535
Ack, BlockingNotificationAction, BlockingRequestAction, NoResponse, Notification, Request,
36-
Response,
36+
Response, ResponseError
3737
};
3838
use std::path::PathBuf;
3939
use std::sync::atomic::Ordering;
@@ -45,6 +45,8 @@ mod dispatch;
4545
mod io;
4646
mod message;
4747

48+
const NOT_INITIALIZED_CODE: ErrorCode = ErrorCode::ServerError(-32002);
49+
4850
/// Run the Rust Language Server.
4951
pub fn run_server(analysis: Arc<AnalysisHost>, vfs: Arc<Vfs>) {
5052
debug!("Language Server starting up. Version: {}", version());
@@ -63,18 +65,21 @@ impl BlockingRequestAction for ShutdownRequest {
6365
type Response = Ack;
6466

6567
fn handle<O: Output>(
66-
_id: usize,
6768
_params: Self::Params,
6869
ctx: &mut ActionContext,
6970
_out: O,
70-
) -> Result<Self::Response, ()> {
71-
// Currently we don't perform an explicit cleanup, other than storing state
72-
ctx.inited()
73-
.expect("initialized context: todo -32002 response")
74-
.shut_down
75-
.store(true, Ordering::SeqCst);
76-
77-
Ok(Ack)
71+
) -> Result<Self::Response, ResponseError> {
72+
if let Ok(ctx) = ctx.inited() {
73+
// Currently we don't perform an explicit cleanup, other than storing state
74+
ctx.shut_down.store(true, Ordering::SeqCst);
75+
Ok(Ack)
76+
}
77+
else {
78+
Err(ResponseError::Message(
79+
NOT_INITIALIZED_CODE,
80+
"not yet received `initialize` request".to_owned(),
81+
))
82+
}
7883
}
7984
}
8085

@@ -87,14 +92,13 @@ fn handle_exit_notification(ctx: &mut ActionContext) -> ! {
8792
}
8893

8994
impl BlockingRequestAction for InitializeRequest {
90-
type Response = NoResponse;
95+
type Response = InitializeResult;
9196

9297
fn handle<O: Output>(
93-
id: usize,
9498
params: Self::Params,
9599
ctx: &mut ActionContext,
96100
out: O,
97-
) -> Result<NoResponse, ()> {
101+
) -> Result<InitializeResult, ResponseError> {
98102
let init_options: InitializationOptions = params
99103
.initialization_options
100104
.as_ref()
@@ -106,13 +110,16 @@ impl BlockingRequestAction for InitializeRequest {
106110
let result = InitializeResult {
107111
capabilities: server_caps(),
108112
};
109-
out.success(id, &result);
110113

111114
let capabilities = lsp_data::ClientCapabilities::new(&params);
112-
ctx.init(get_root_path(&params), &init_options, capabilities, &out)
113-
.expect("context already initialized");
114-
115-
Ok(NoResponse)
115+
match ctx.init(get_root_path(&params), &init_options, capabilities, &out) {
116+
Ok(_) => Ok(result),
117+
Err(_) => Err(ResponseError::Message(
118+
// No code in the spec, just use some number
119+
ErrorCode::ServerError(123),
120+
"Already received an initialize request".to_owned(),
121+
)),
122+
}
116123
}
117124
}
118125

@@ -169,7 +176,7 @@ impl<O: Output> LsService<O> {
169176
}
170177
else {
171178
warn!(
172-
"Server has not yet received an `initialize` request, ignoring {}", <$n_action as LSPNotification>::METHOD,
179+
"Server has not yet received an `initialize` request, ignoring {}", $method,
173180
);
174181
}
175182
}
@@ -182,20 +189,41 @@ impl<O: Output> LsService<O> {
182189
// block until all nonblocking requests have been handled ensuring ordering
183190
self.dispatcher.await_all_dispatched();
184191

185-
if request.blocking_dispatch(&mut self.ctx, &self.output).is_err() {
186-
debug!("Error handling request: {:?}", msg);
192+
let req_id = request.id;
193+
match request.blocking_dispatch(&mut self.ctx, &self.output) {
194+
Ok(res) => res.send(req_id, &self.output),
195+
Err(ResponseError::Empty) => {
196+
debug!("error handling {}", $method);
197+
self.output.failure_message(
198+
req_id,
199+
ErrorCode::InternalError,
200+
"An unknown error occurred"
201+
)
202+
}
203+
Err(ResponseError::Message(code, msg)) => {
204+
debug!("error handling {}: {}", $method, msg);
205+
self.output.failure_message(req_id, code, msg)
206+
}
187207
}
188208
}
189209
)*
190210

191211
$(
192212
<$request as LSPRequest>::METHOD => {
193213
let request: Request<$request> = msg.parse_as_request()?;
194-
let request = (
195-
request,
196-
self.ctx.inited().expect("initialized context: todo -32002 response"),
197-
);
198-
self.dispatcher.dispatch(request);
214+
if let Ok(ctx) = self.ctx.inited() {
215+
self.dispatcher.dispatch((request, ctx));
216+
}
217+
else {
218+
warn!(
219+
"Server has not yet received an `initialize` request, cannot handle {}", $method,
220+
);
221+
self.output.failure_message(
222+
request.id,
223+
NOT_INITIALIZED_CODE,
224+
"not yet received `initialize` request".to_owned(),
225+
);
226+
}
199227
}
200228
)*
201229
// exit notification can uniquely handle pre `initialize` request state

src/test/mod.rs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1569,7 +1569,8 @@ fn test_all_targets() {
15691569
);
15701570
}
15711571

1572-
/// Test hover continues to work after the source has moved line
1572+
/// Handle receiving a notification before the `initialize` request by ignoring and
1573+
/// continuing to run
15731574
#[test]
15741575
fn ignore_uninitialized_notification() {
15751576
let mut env = Environment::new("common");
@@ -1626,3 +1627,64 @@ fn ignore_uninitialized_notification() {
16261627
],
16271628
);
16281629
}
1630+
1631+
/// Handle receiving requests before the `initialize` request by returning an error response
1632+
/// and continuing to run
1633+
#[test]
1634+
fn fail_uninitialized_request() {
1635+
let mut env = Environment::new("common");
1636+
1637+
let source_file_path = Path::new("src").join("main.rs");
1638+
let root_path = env.cache.abs_path(Path::new("."));
1639+
let url = Url::from_file_path(env.cache.abs_path(&source_file_path))
1640+
.expect("couldn't convert file path to URL");
1641+
1642+
let messages = vec![
1643+
request::<requests::Definition>(
1644+
0,
1645+
TextDocumentPositionParams {
1646+
text_document: TextDocumentIdentifier::new(url),
1647+
position: env.cache
1648+
.mk_ls_position(src(&source_file_path, 22, "world")),
1649+
},
1650+
).to_string(),
1651+
initialize(1, root_path.as_os_str().to_str().map(|x| x.to_owned())).to_string(),
1652+
];
1653+
1654+
let (mut server, results) = env.mock_server(messages);
1655+
1656+
// Return error response to pre `initialize` request, keep running.
1657+
assert_eq!(
1658+
ls_server::LsService::handle_message(&mut server),
1659+
ls_server::ServerStateChange::Continue
1660+
);
1661+
{
1662+
wait_for_n_results!(1, results);
1663+
let response = json::parse(&results.lock().unwrap().remove(0)).unwrap();
1664+
assert_eq!(response["id"], 0);
1665+
assert_eq!(response["error"]["code"], -32002);
1666+
let message = response["error"]["message"].as_str().unwrap();
1667+
assert!(
1668+
message.to_lowercase().contains("initialize"),
1669+
"Unexpected error.message `{}`",
1670+
message,
1671+
);
1672+
}
1673+
1674+
// Initialize and build.
1675+
assert_eq!(
1676+
ls_server::LsService::handle_message(&mut server),
1677+
ls_server::ServerStateChange::Continue
1678+
);
1679+
expect_messages(
1680+
results.clone(),
1681+
&[
1682+
ExpectedMessage::new(Some(1)).expect_contains("capabilities"),
1683+
ExpectedMessage::new(None).expect_contains("progress").expect_contains(r#"title":"Building""#),
1684+
ExpectedMessage::new(None).expect_contains("progress").expect_contains("completion"),
1685+
ExpectedMessage::new(None).expect_contains("progress").expect_contains(r#""done":true"#),
1686+
ExpectedMessage::new(None).expect_contains("progress").expect_contains(r#"title":"Indexing""#),
1687+
ExpectedMessage::new(None).expect_contains("progress").expect_contains(r#""done":true"#),
1688+
],
1689+
);
1690+
}

0 commit comments

Comments
 (0)