aboutsummaryrefslogtreecommitdiff
path: root/codeless/server
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-08-10 13:07:43 +0100
committerAleksey Kladov <[email protected]>2018-08-10 13:07:43 +0100
commitd7c5a6f3081c2e7266620779d3c32067f947b959 (patch)
tree2aefb594e062c8fa7cc7879e5df6883f3bc5d015 /codeless/server
parent4a900fd6815d3ea722b5e664aee9eac8bb9cb14f (diff)
Start lang server
Diffstat (limited to 'codeless/server')
-rw-r--r--codeless/server/.gitignore1
-rw-r--r--codeless/server/Cargo.toml16
-rw-r--r--codeless/server/src/caps.rs23
-rw-r--r--codeless/server/src/dispatch.rs124
-rw-r--r--codeless/server/src/io.rs201
-rw-r--r--codeless/server/src/main.rs84
-rw-r--r--codeless/server/src/req.rs16
-rw-r--r--codeless/server/target/.rustc_info.json1
-rw-r--r--codeless/server/target/debug/.cargo-lock0
-rw-r--r--codeless/server/target/debug/libm.d1
-rw-r--r--codeless/server/target/debug/libm.rmeta0
11 files changed, 467 insertions, 0 deletions
diff --git a/codeless/server/.gitignore b/codeless/server/.gitignore
new file mode 100644
index 000000000..5a50b7f98
--- /dev/null
+++ b/codeless/server/.gitignore
@@ -0,0 +1 @@
/target/*
diff --git a/codeless/server/Cargo.toml b/codeless/server/Cargo.toml
new file mode 100644
index 000000000..4c3dd345c
--- /dev/null
+++ b/codeless/server/Cargo.toml
@@ -0,0 +1,16 @@
1[package]
2name = "m"
3version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"]
5[workspace]
6
7[dependencies]
8failure = "0.1.2"
9languageserver-types = "0.48.0"
10serde_json = "1.0.24"
11serde = "1.0.71"
12serde_derive = "1.0.71"
13drop_bomb = "0.1.0"
14crossbeam-channel = "0.2.4"
15libeditor = { path = "../../libeditor" }
16libanalysis = { path = "../../libanalysis" }
diff --git a/codeless/server/src/caps.rs b/codeless/server/src/caps.rs
new file mode 100644
index 000000000..b2fad6732
--- /dev/null
+++ b/codeless/server/src/caps.rs
@@ -0,0 +1,23 @@
1use languageserver_types::ServerCapabilities;
2
3pub const SERVER_CAPABILITIES: ServerCapabilities = ServerCapabilities {
4 text_document_sync: None,
5 hover_provider: None,
6 completion_provider: None,
7 signature_help_provider: None,
8 definition_provider: None,
9 type_definition_provider: None,
10 implementation_provider: None,
11 references_provider: None,
12 document_highlight_provider: None,
13 document_symbol_provider: None,
14 workspace_symbol_provider: None,
15 code_action_provider: None,
16 code_lens_provider: None,
17 document_formatting_provider: None,
18 document_range_formatting_provider: None,
19 document_on_type_formatting_provider: None,
20 rename_provider: None,
21 color_provider: None,
22 execute_command_provider: None,
23};
diff --git a/codeless/server/src/dispatch.rs b/codeless/server/src/dispatch.rs
new file mode 100644
index 000000000..a9476acde
--- /dev/null
+++ b/codeless/server/src/dispatch.rs
@@ -0,0 +1,124 @@
1use std::marker::PhantomData;
2
3use serde::{
4 ser::Serialize,
5 de::DeserializeOwned,
6};
7use serde_json;
8use drop_bomb::DropBomb;
9
10use ::{
11 Result,
12 req::Request,
13 io::{Io, RawMsg, RawResponse, RawRequest},
14};
15
16pub struct Responder<R: Request> {
17 id: u64,
18 bomb: DropBomb,
19 ph: PhantomData<R>,
20}
21
22impl<R: Request> Responder<R>
23 where
24 R::Params: DeserializeOwned,
25 R::Result: Serialize,
26{
27 pub fn respond_with(self, io: &mut Io, f: impl FnOnce() -> Result<R::Result>) -> Result<()> {
28 match f() {
29 Ok(res) => self.result(io, res)?,
30 Err(e) => {
31 self.error(io)?;
32 return Err(e);
33 }
34 }
35 Ok(())
36 }
37
38 pub fn result(mut self, io: &mut Io, result: R::Result) -> Result<()> {
39 self.bomb.defuse();
40 io.send(RawMsg::Response(RawResponse {
41 id: Some(self.id),
42 result: serde_json::to_value(result)?,
43 error: serde_json::Value::Null,
44 }));
45 Ok(())
46 }
47
48 pub fn error(mut self, io: &mut Io) -> Result<()> {
49 self.bomb.defuse();
50 error(io, self.id, ErrorCode::InternalError, "internal error")
51 }
52}
53
54
55pub fn parse_as<R>(raw: RawRequest) -> Result<::std::result::Result<(R::Params, Responder<R>), RawRequest>>
56 where
57 R: Request,
58 R::Params: DeserializeOwned,
59 R::Result: Serialize,
60{
61 if raw.method != R::METHOD {
62 return Ok(Err(raw));
63 }
64
65 let params: R::Params = serde_json::from_value(raw.params)?;
66 let responder = Responder {
67 id: raw.id,
68 bomb: DropBomb::new("dropped request"),
69 ph: PhantomData,
70 };
71 Ok(Ok((params, responder)))
72}
73
74pub fn expect<R>(io: &mut Io, raw: RawRequest) -> Result<Option<(R::Params, Responder<R>)>>
75 where
76 R: Request,
77 R::Params: DeserializeOwned,
78 R::Result: Serialize,
79{
80 let ret = match parse_as::<R>(raw)? {
81 Ok(x) => Some(x),
82 Err(raw) => {
83 unknown_method(io, raw)?;
84 None
85 }
86 };
87 Ok(ret)
88}
89
90pub fn unknown_method(io: &mut Io, raw: RawRequest) -> Result<()> {
91 error(io, raw.id, ErrorCode::MethodNotFound, "unknown method")
92}
93
94fn error(io: &mut Io, id: u64, code: ErrorCode, message: &'static str) -> Result<()> {
95 #[derive(Serialize)]
96 struct Error {
97 code: i32,
98 message: &'static str,
99 }
100 io.send(RawMsg::Response(RawResponse {
101 id: Some(id),
102 result: serde_json::Value::Null,
103 error: serde_json::to_value(Error {
104 code: code as i32,
105 message,
106 })?,
107 }));
108 Ok(())
109}
110
111
112#[allow(unused)]
113enum ErrorCode {
114 ParseError = -32700,
115 InvalidRequest = -32600,
116 MethodNotFound = -32601,
117 InvalidParams = -32602,
118 InternalError = -32603,
119 ServerErrorStart = -32099,
120 ServerErrorEnd = -32000,
121 ServerNotInitialized = -32002,
122 UnknownErrorCode = -32001,
123 RequestCancelled = -32800,
124}
diff --git a/codeless/server/src/io.rs b/codeless/server/src/io.rs
new file mode 100644
index 000000000..b84103d65
--- /dev/null
+++ b/codeless/server/src/io.rs
@@ -0,0 +1,201 @@
1use std::{
2 thread,
3 io::{
4 stdout, stdin,
5 BufRead, Write,
6 },
7};
8use serde_json::{Value, from_str, to_string};
9use crossbeam_channel::{Receiver, Sender, bounded};
10
11use Result;
12
13
14#[derive(Debug, Serialize, Deserialize)]
15#[serde(untagged)]
16pub enum RawMsg {
17 Request(RawRequest),
18 Notification(RawNotification),
19 Response(RawResponse),
20}
21
22#[derive(Debug, Serialize, Deserialize)]
23pub struct RawRequest {
24 pub id: u64,
25 pub method: String,
26 pub params: Value,
27}
28
29#[derive(Debug, Serialize, Deserialize)]
30pub struct RawNotification {
31 pub method: String,
32 pub params: Value,
33}
34
35#[derive(Debug, Serialize, Deserialize)]
36pub struct RawResponse {
37 pub id: Option<u64>,
38 pub result: Value,
39 pub error: Value,
40}
41
42struct MsgReceiver {
43 chan: Receiver<RawMsg>,
44 thread: Option<thread::JoinHandle<Result<()>>>,
45}
46
47impl MsgReceiver {
48 fn recv(&mut self) -> Result<RawMsg> {
49 match self.chan.recv() {
50 Some(msg) => Ok(msg),
51 None => {
52 self.thread
53 .take()
54 .ok_or_else(|| format_err!("MsgReceiver thread panicked"))?
55 .join()
56 .map_err(|_| format_err!("MsgReceiver thread panicked"))??;
57 bail!("client disconnected")
58 }
59 }
60 }
61
62 fn stop(self) -> Result<()> {
63 // Can't really self.thread.join() here, b/c it might be
64 // blocking on read
65 Ok(())
66 }
67}
68
69struct MsgSender {
70 chan: Sender<RawMsg>,
71 thread: Option<thread::JoinHandle<Result<()>>>,
72}
73
74impl MsgSender {
75 fn send(&mut self, msg: RawMsg) {
76 self.chan.send(msg)
77 }
78
79 fn stop(mut self) -> Result<()> {
80 if let Some(thread) = self.thread.take() {
81 thread.join()
82 .map_err(|_| format_err!("MsgSender thread panicked"))??
83 }
84 Ok(())
85 }
86}
87
88impl Drop for MsgSender {
89 fn drop(&mut self) {
90 if let Some(thread) = self.thread.take() {
91 let res = thread.join();
92 if thread::panicking() {
93 drop(res)
94 } else {
95 res.unwrap().unwrap()
96 }
97 }
98 }
99}
100
101pub struct Io {
102 receiver: MsgReceiver,
103 sender: MsgSender,
104}
105
106impl Io {
107 pub fn from_stdio() -> Io {
108 let sender = {
109 let (tx, rx) = bounded(16);
110 MsgSender {
111 chan: tx,
112 thread: Some(thread::spawn(move || {
113 let stdout = stdout();
114 let mut stdout = stdout.lock();
115 for msg in rx {
116 #[derive(Serialize)]
117 struct JsonRpc {
118 jsonrpc: &'static str,
119 #[serde(flatten)]
120 msg: RawMsg,
121 }
122 let text = to_string(&JsonRpc {
123 jsonrpc: "2.0",
124 msg,
125 })?;
126 write_msg_text(&mut stdout, &text)?;
127 }
128 Ok(())
129 })),
130 }
131 };
132 let receiver = {
133 let (tx, rx) = bounded(16);
134 MsgReceiver {
135 chan: rx,
136 thread: Some(thread::spawn(move || {
137 let stdin = stdin();
138 let mut stdin = stdin.lock();
139 while let Some(text) = read_msg_text(&mut stdin)? {
140 let msg: RawMsg = from_str(&text)?;
141 tx.send(msg);
142 }
143 Ok(())
144 })),
145 }
146 };
147 Io { receiver, sender }
148 }
149
150 pub fn send(&mut self, msg: RawMsg) {
151 self.sender.send(msg)
152 }
153
154 pub fn recv(&mut self) -> Result<RawMsg> {
155 self.receiver.recv()
156 }
157
158 pub fn stop(self) -> Result<()> {
159 self.receiver.stop()?;
160 self.sender.stop()?;
161 Ok(())
162 }
163}
164
165
166fn read_msg_text(inp: &mut impl BufRead) -> Result<Option<String>> {
167 let mut size = None;
168 let mut buf = String::new();
169 loop {
170 buf.clear();
171 if inp.read_line(&mut buf)? == 0 {
172 return Ok(None);
173 }
174 if !buf.ends_with("\r\n") {
175 bail!("malformed header: {:?}", buf);
176 }
177 let buf = &buf[..buf.len() - 2];
178 if buf.is_empty() {
179 break;
180 }
181 let mut parts = buf.splitn(2, ": ");
182 let header_name = parts.next().unwrap();
183 let header_value = parts.next().ok_or_else(|| format_err!("malformed header: {:?}", buf))?;
184 if header_name == "Content-Length" {
185 size = Some(header_value.parse::<usize>()?);
186 }
187 }
188 let size = size.ok_or_else(|| format_err!("no Content-Length"))?;
189 let mut buf = buf.into_bytes();
190 buf.resize(size, 0);
191 inp.read_exact(&mut buf)?;
192 let buf = String::from_utf8(buf)?;
193 Ok(Some(buf))
194}
195
196fn write_msg_text(out: &mut impl Write, msg: &str) -> Result<()> {
197 write!(out, "Content-Length: {}\r\n\r\n", msg.len())?;
198 out.write_all(msg.as_bytes())?;
199 out.flush()?;
200 Ok(())
201}
diff --git a/codeless/server/src/main.rs b/codeless/server/src/main.rs
new file mode 100644
index 000000000..11b6b7067
--- /dev/null
+++ b/codeless/server/src/main.rs
@@ -0,0 +1,84 @@
1#[macro_use]
2extern crate failure;
3#[macro_use]
4extern crate serde_derive;
5extern crate serde;
6extern crate serde_json;
7extern crate languageserver_types;
8extern crate drop_bomb;
9extern crate crossbeam_channel;
10extern crate libeditor;
11extern crate libanalysis;
12
13mod io;
14mod caps;
15mod req;
16mod dispatch;
17
18use languageserver_types::InitializeResult;
19use libanalysis::WorldState;
20use self::io::{Io, RawMsg};
21
22pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
23
24fn main() -> Result<()> {
25 let mut io = Io::from_stdio();
26 initialize(&mut io)?;
27 io.stop()?;
28 Ok(())
29}
30
31fn initialize(io: &mut Io) -> Result<()> {
32 loop {
33 match io.recv()? {
34 RawMsg::Request(req) => {
35 if let Some((_params, resp)) = dispatch::expect::<req::Initialize>(io, req)? {
36 resp.result(io, InitializeResult {
37 capabilities: caps::SERVER_CAPABILITIES
38 })?;
39 match io.recv()? {
40 RawMsg::Notification(n) => {
41 if n.method != "initialized" {
42 bail!("expected initialized notification");
43 }
44 }
45 _ => {
46 bail!("expected initialized notification");
47 }
48 }
49 return initialized(io);
50 }
51 }
52 RawMsg::Notification(n) => {
53 bail!("expected initialize request, got {:?}", n)
54 }
55 RawMsg::Response(res) => {
56 bail!("expected initialize request, got {:?}", res)
57 }
58 }
59 }
60}
61
62fn initialized(io: &mut Io) -> Result<()> {
63 eprintln!("initialized");
64 let world = WorldState::new();
65 loop {
66 match io.recv()? {
67 RawMsg::Request(req) => {
68 let world = world.snapshot();
69 if let Some((params, resp)) = dispatch::expect::<req::SyntaxTree>(io, req)? {
70 resp.respond_with(io, || {
71 let path = params.text_document.uri.to_file_path()
72 .map_err(|()| format_err!("invalid path"))?;
73 let file = world.file_syntax(&path)?;
74 Ok(libeditor::syntax_tree(&file))
75 })?
76 }
77 }
78 msg => {
79 eprintln!("msg = {:?}", msg);
80 }
81 }
82 }
83}
84
diff --git a/codeless/server/src/req.rs b/codeless/server/src/req.rs
new file mode 100644
index 000000000..bc54c1d33
--- /dev/null
+++ b/codeless/server/src/req.rs
@@ -0,0 +1,16 @@
1use languageserver_types::TextDocumentIdentifier;
2pub use languageserver_types::request::*;
3
4pub enum SyntaxTree {}
5
6impl Request for SyntaxTree {
7 type Params = SyntaxTreeParams;
8 type Result = String;
9 const METHOD: &'static str = "m/syntaxTree";
10}
11
12#[derive(Deserialize, Debug)]
13#[serde(rename_all="camelCase")]
14pub struct SyntaxTreeParams {
15 pub text_document: TextDocumentIdentifier
16}
diff --git a/codeless/server/target/.rustc_info.json b/codeless/server/target/.rustc_info.json
new file mode 100644
index 000000000..a37ac2011
--- /dev/null
+++ b/codeless/server/target/.rustc_info.json
@@ -0,0 +1 @@
{"rustc_fingerprint":11898242945176772229,"outputs":{"15337506775154344876":["___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/matklad/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\ndebug_assertions\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\nunix\n",""],"1617349019360157463":["___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/matklad/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\ndebug_assertions\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\nunix\n",""],"1164083562126845933":["rustc 1.28.0 (9634041f0 2018-07-30)\nbinary: rustc\ncommit-hash: 9634041f0e8c0f3191d2867311276f19d0a42564\ncommit-date: 2018-07-30\nhost: x86_64-unknown-linux-gnu\nrelease: 1.28.0\nLLVM version: 6.0\n",""]}} \ No newline at end of file
diff --git a/codeless/server/target/debug/.cargo-lock b/codeless/server/target/debug/.cargo-lock
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/codeless/server/target/debug/.cargo-lock
diff --git a/codeless/server/target/debug/libm.d b/codeless/server/target/debug/libm.d
new file mode 100644
index 000000000..04d8bb9ed
--- /dev/null
+++ b/codeless/server/target/debug/libm.d
@@ -0,0 +1 @@
/home/matklad/projects/libsyntax2/codeless/server/target/debug/libm.rmeta: /home/matklad/projects/libsyntax2/codeless/server/src/caps.rs /home/matklad/projects/libsyntax2/codeless/server/src/dispatch.rs /home/matklad/projects/libsyntax2/codeless/server/src/io.rs /home/matklad/projects/libsyntax2/codeless/server/src/main.rs /home/matklad/projects/libsyntax2/codeless/server/src/req.rs /home/matklad/projects/libsyntax2/libanalysis/src/lib.rs /home/matklad/projects/libsyntax2/libeditor/src/extend_selection.rs /home/matklad/projects/libsyntax2/libeditor/src/lib.rs /home/matklad/projects/libsyntax2/src/algo/mod.rs /home/matklad/projects/libsyntax2/src/algo/walk.rs /home/matklad/projects/libsyntax2/src/ast/generated.rs /home/matklad/projects/libsyntax2/src/ast/mod.rs /home/matklad/projects/libsyntax2/src/grammar/attributes.rs /home/matklad/projects/libsyntax2/src/grammar/expressions/atom.rs /home/matklad/projects/libsyntax2/src/grammar/expressions/mod.rs /home/matklad/projects/libsyntax2/src/grammar/items/consts.rs /home/matklad/projects/libsyntax2/src/grammar/items/mod.rs /home/matklad/projects/libsyntax2/src/grammar/items/structs.rs /home/matklad/projects/libsyntax2/src/grammar/items/traits.rs /home/matklad/projects/libsyntax2/src/grammar/items/use_item.rs /home/matklad/projects/libsyntax2/src/grammar/mod.rs /home/matklad/projects/libsyntax2/src/grammar/params.rs /home/matklad/projects/libsyntax2/src/grammar/paths.rs /home/matklad/projects/libsyntax2/src/grammar/patterns.rs /home/matklad/projects/libsyntax2/src/grammar/type_args.rs /home/matklad/projects/libsyntax2/src/grammar/type_params.rs /home/matklad/projects/libsyntax2/src/grammar/types.rs /home/matklad/projects/libsyntax2/src/lexer/classes.rs /home/matklad/projects/libsyntax2/src/lexer/comments.rs /home/matklad/projects/libsyntax2/src/lexer/mod.rs /home/matklad/projects/libsyntax2/src/lexer/numbers.rs /home/matklad/projects/libsyntax2/src/lexer/ptr.rs /home/matklad/projects/libsyntax2/src/lexer/strings.rs /home/matklad/projects/libsyntax2/src/lib.rs /home/matklad/projects/libsyntax2/src/parser_api.rs /home/matklad/projects/libsyntax2/src/parser_impl/event.rs /home/matklad/projects/libsyntax2/src/parser_impl/input.rs /home/matklad/projects/libsyntax2/src/parser_impl/mod.rs /home/matklad/projects/libsyntax2/src/smol_str.rs /home/matklad/projects/libsyntax2/src/syntax_kinds/generated.rs /home/matklad/projects/libsyntax2/src/syntax_kinds/mod.rs /home/matklad/projects/libsyntax2/src/utils.rs /home/matklad/projects/libsyntax2/src/yellow/builder.rs /home/matklad/projects/libsyntax2/src/yellow/green.rs /home/matklad/projects/libsyntax2/src/yellow/mod.rs /home/matklad/projects/libsyntax2/src/yellow/red.rs /home/matklad/projects/libsyntax2/src/yellow/syntax.rs
diff --git a/codeless/server/target/debug/libm.rmeta b/codeless/server/target/debug/libm.rmeta
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/codeless/server/target/debug/libm.rmeta