aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml2
-rw-r--r--cli/src/main.rs14
-rw-r--r--codeless/server/.gitignore1
-rw-r--r--codeless/server/Cargo.toml4
-rw-r--r--codeless/server/src/dispatch.rs4
-rw-r--r--codeless/server/src/io.rs55
-rw-r--r--codeless/server/src/main.rs126
-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
-rw-r--r--codeless/src/extension.ts6
-rw-r--r--libeditor/src/lib.rs3
-rw-r--r--libeditor/tests/test.rs14
-rw-r--r--src/yellow/green.rs6
-rw-r--r--src/yellow/mod.rs53
-rw-r--r--src/yellow/red.rs18
-rw-r--r--src/yellow/syntax.rs40
18 files changed, 233 insertions, 115 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 77f9c4a8d..55dd9165c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,7 @@ authors = ["Aleksey Kladov <[email protected]>"]
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[workspace] 7[workspace]
8members = [ "tools", "cli", "libeditor", "libanalysis" ] 8members = [ "tools", "cli", "libeditor", "libanalysis", "codeless/server" ]
9 9
10[dependencies] 10[dependencies]
11unicode-xid = "0.1.0" 11unicode-xid = "0.1.0"
diff --git a/cli/src/main.rs b/cli/src/main.rs
index f6c66743f..45e0a1e4f 100644
--- a/cli/src/main.rs
+++ b/cli/src/main.rs
@@ -10,7 +10,7 @@ use std::{
10}; 10};
11use clap::{App, Arg, SubCommand}; 11use clap::{App, Arg, SubCommand};
12use tools::collect_tests; 12use tools::collect_tests;
13use libeditor::File; 13use libeditor::{ast, syntax_tree, symbols};
14 14
15type Result<T> = ::std::result::Result<T, failure::Error>; 15type Result<T> = ::std::result::Result<T, failure::Error>;
16 16
@@ -44,14 +44,14 @@ fn main() -> Result<()> {
44 let file = file()?; 44 let file = file()?;
45 let elapsed = start.elapsed(); 45 let elapsed = start.elapsed();
46 if !matches.is_present("no-dump") { 46 if !matches.is_present("no-dump") {
47 println!("{}", file.syntax_tree()); 47 println!("{}", syntax_tree(&file));
48 } 48 }
49 eprintln!("parsing: {:?}", elapsed); 49 eprintln!("parsing: {:?}", elapsed);
50 ::std::mem::forget(file); 50 ::std::mem::forget(file);
51 } 51 }
52 ("symbols", _) => { 52 ("symbols", _) => {
53 let file = file()?; 53 let file = file()?;
54 for s in file.symbols() { 54 for s in symbols(&file) {
55 println!("{:?}", s); 55 println!("{:?}", s);
56 } 56 }
57 } 57 }
@@ -68,9 +68,9 @@ fn main() -> Result<()> {
68 Ok(()) 68 Ok(())
69} 69}
70 70
71fn file() -> Result<File> { 71fn file() -> Result<ast::File> {
72 let text = read_stdin()?; 72 let text = read_stdin()?;
73 Ok(File::new(&text)) 73 Ok(ast::File::parse(&text))
74} 74}
75 75
76fn read_stdin() -> Result<String> { 76fn read_stdin() -> Result<String> {
@@ -89,7 +89,7 @@ fn render_test(file: &Path, line: usize) -> Result<(String, String)> {
89 None => bail!("No test found at line {} at {}", line, file.display()), 89 None => bail!("No test found at line {} at {}", line, file.display()),
90 Some((_start_line, test)) => test, 90 Some((_start_line, test)) => test,
91 }; 91 };
92 let file = File::new(&test.text); 92 let file = ast::File::parse(&test.text);
93 let tree = file.syntax_tree(); 93 let tree = syntax_tree(&file);
94 Ok((test.text, tree)) 94 Ok((test.text, tree))
95} 95}
diff --git a/codeless/server/.gitignore b/codeless/server/.gitignore
deleted file mode 100644
index 5a50b7f98..000000000
--- a/codeless/server/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
1/target/*
diff --git a/codeless/server/Cargo.toml b/codeless/server/Cargo.toml
index 4c3dd345c..f5c32b878 100644
--- a/codeless/server/Cargo.toml
+++ b/codeless/server/Cargo.toml
@@ -2,7 +2,6 @@
2name = "m" 2name = "m"
3version = "0.1.0" 3version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"] 4authors = ["Aleksey Kladov <[email protected]>"]
5[workspace]
6 5
7[dependencies] 6[dependencies]
8failure = "0.1.2" 7failure = "0.1.2"
@@ -12,5 +11,8 @@ serde = "1.0.71"
12serde_derive = "1.0.71" 11serde_derive = "1.0.71"
13drop_bomb = "0.1.0" 12drop_bomb = "0.1.0"
14crossbeam-channel = "0.2.4" 13crossbeam-channel = "0.2.4"
14threadpool = "1.7.1"
15flexi_logger = "0.9.0"
16log = "0.4.3"
15libeditor = { path = "../../libeditor" } 17libeditor = { path = "../../libeditor" }
16libanalysis = { path = "../../libanalysis" } 18libanalysis = { path = "../../libanalysis" }
diff --git a/codeless/server/src/dispatch.rs b/codeless/server/src/dispatch.rs
index a9476acde..ee87fa6c3 100644
--- a/codeless/server/src/dispatch.rs
+++ b/codeless/server/src/dispatch.rs
@@ -24,8 +24,8 @@ impl<R: Request> Responder<R>
24 R::Params: DeserializeOwned, 24 R::Params: DeserializeOwned,
25 R::Result: Serialize, 25 R::Result: Serialize,
26{ 26{
27 pub fn respond_with(self, io: &mut Io, f: impl FnOnce() -> Result<R::Result>) -> Result<()> { 27 pub fn response(self, io: &mut Io, resp: Result<R::Result>) -> Result<()> {
28 match f() { 28 match resp {
29 Ok(res) => self.result(io, res)?, 29 Ok(res) => self.result(io, res)?,
30 Err(e) => { 30 Err(e) => {
31 self.error(io)?; 31 self.error(io)?;
diff --git a/codeless/server/src/io.rs b/codeless/server/src/io.rs
index b84103d65..5eafc6942 100644
--- a/codeless/server/src/io.rs
+++ b/codeless/server/src/io.rs
@@ -49,16 +49,21 @@ impl MsgReceiver {
49 match self.chan.recv() { 49 match self.chan.recv() {
50 Some(msg) => Ok(msg), 50 Some(msg) => Ok(msg),
51 None => { 51 None => {
52 self.thread 52 self.cleanup()?;
53 .take() 53 unreachable!()
54 .ok_or_else(|| format_err!("MsgReceiver thread panicked"))?
55 .join()
56 .map_err(|_| format_err!("MsgReceiver thread panicked"))??;
57 bail!("client disconnected")
58 } 54 }
59 } 55 }
60 } 56 }
61 57
58 fn cleanup(&mut self) -> Result<()> {
59 self.thread
60 .take()
61 .ok_or_else(|| format_err!("MsgReceiver thread panicked"))?
62 .join()
63 .map_err(|_| format_err!("MsgReceiver thread panicked"))??;
64 bail!("client disconnected")
65 }
66
62 fn stop(self) -> Result<()> { 67 fn stop(self) -> Result<()> {
63 // Can't really self.thread.join() here, b/c it might be 68 // Can't really self.thread.join() here, b/c it might be
64 // blocking on read 69 // blocking on read
@@ -68,7 +73,7 @@ impl MsgReceiver {
68 73
69struct MsgSender { 74struct MsgSender {
70 chan: Sender<RawMsg>, 75 chan: Sender<RawMsg>,
71 thread: Option<thread::JoinHandle<Result<()>>>, 76 thread: thread::JoinHandle<Result<()>>,
72} 77}
73 78
74impl MsgSender { 79impl MsgSender {
@@ -76,28 +81,14 @@ impl MsgSender {
76 self.chan.send(msg) 81 self.chan.send(msg)
77 } 82 }
78 83
79 fn stop(mut self) -> Result<()> { 84 fn stop(self) -> Result<()> {
80 if let Some(thread) = self.thread.take() { 85 drop(self.chan);
81 thread.join() 86 self.thread.join()
82 .map_err(|_| format_err!("MsgSender thread panicked"))?? 87 .map_err(|_| format_err!("MsgSender thread panicked"))??;
83 }
84 Ok(()) 88 Ok(())
85 } 89 }
86} 90}
87 91
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 { 92pub struct Io {
102 receiver: MsgReceiver, 93 receiver: MsgReceiver,
103 sender: MsgSender, 94 sender: MsgSender,
@@ -109,7 +100,7 @@ impl Io {
109 let (tx, rx) = bounded(16); 100 let (tx, rx) = bounded(16);
110 MsgSender { 101 MsgSender {
111 chan: tx, 102 chan: tx,
112 thread: Some(thread::spawn(move || { 103 thread: thread::spawn(move || {
113 let stdout = stdout(); 104 let stdout = stdout();
114 let mut stdout = stdout.lock(); 105 let mut stdout = stdout.lock();
115 for msg in rx { 106 for msg in rx {
@@ -126,7 +117,7 @@ impl Io {
126 write_msg_text(&mut stdout, &text)?; 117 write_msg_text(&mut stdout, &text)?;
127 } 118 }
128 Ok(()) 119 Ok(())
129 })), 120 }),
130 } 121 }
131 }; 122 };
132 let receiver = { 123 let receiver = {
@@ -155,6 +146,14 @@ impl Io {
155 self.receiver.recv() 146 self.receiver.recv()
156 } 147 }
157 148
149 pub fn receiver(&mut self) -> &mut Receiver<RawMsg> {
150 &mut self.receiver.chan
151 }
152
153 pub fn cleanup_receiver(&mut self) -> Result<()> {
154 self.receiver.cleanup()
155 }
156
158 pub fn stop(self) -> Result<()> { 157 pub fn stop(self) -> Result<()> {
159 self.receiver.stop()?; 158 self.receiver.stop()?;
160 self.sender.stop()?; 159 self.sender.stop()?;
@@ -190,10 +189,12 @@ fn read_msg_text(inp: &mut impl BufRead) -> Result<Option<String>> {
190 buf.resize(size, 0); 189 buf.resize(size, 0);
191 inp.read_exact(&mut buf)?; 190 inp.read_exact(&mut buf)?;
192 let buf = String::from_utf8(buf)?; 191 let buf = String::from_utf8(buf)?;
192 debug!("< {}", buf);
193 Ok(Some(buf)) 193 Ok(Some(buf))
194} 194}
195 195
196fn write_msg_text(out: &mut impl Write, msg: &str) -> Result<()> { 196fn write_msg_text(out: &mut impl Write, msg: &str) -> Result<()> {
197 debug!("> {}", msg);
197 write!(out, "Content-Length: {}\r\n\r\n", msg.len())?; 198 write!(out, "Content-Length: {}\r\n\r\n", msg.len())?;
198 out.write_all(msg.as_bytes())?; 199 out.write_all(msg.as_bytes())?;
199 out.flush()?; 200 out.flush()?;
diff --git a/codeless/server/src/main.rs b/codeless/server/src/main.rs
index 11b6b7067..92f6a400c 100644
--- a/codeless/server/src/main.rs
+++ b/codeless/server/src/main.rs
@@ -6,7 +6,12 @@ extern crate serde;
6extern crate serde_json; 6extern crate serde_json;
7extern crate languageserver_types; 7extern crate languageserver_types;
8extern crate drop_bomb; 8extern crate drop_bomb;
9#[macro_use]
9extern crate crossbeam_channel; 10extern crate crossbeam_channel;
11extern crate threadpool;
12#[macro_use]
13extern crate log;
14extern crate flexi_logger;
10extern crate libeditor; 15extern crate libeditor;
11extern crate libanalysis; 16extern crate libanalysis;
12 17
@@ -16,16 +21,50 @@ mod req;
16mod dispatch; 21mod dispatch;
17 22
18use languageserver_types::InitializeResult; 23use languageserver_types::InitializeResult;
24use threadpool::ThreadPool;
25use crossbeam_channel::{bounded, Sender, Receiver};
26use flexi_logger::Logger;
19use libanalysis::WorldState; 27use libanalysis::WorldState;
20use self::io::{Io, RawMsg}; 28
29use ::{
30 io::{Io, RawMsg},
31};
21 32
22pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 33pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
23 34
24fn main() -> Result<()> { 35fn main() -> Result<()> {
36 Logger::with_env_or_str("m=trace")
37 .log_to_file()
38 .directory("log")
39 .start()?;
40 info!("starting server");
41 match ::std::panic::catch_unwind(|| main_inner()) {
42 Ok(res) => {
43 info!("shutting down: {:?}", res);
44 res
45 }
46 Err(_) => {
47 error!("server panicked");
48 bail!("server panicked")
49 },
50 }
51}
52
53fn main_inner() -> Result<()> {
25 let mut io = Io::from_stdio(); 54 let mut io = Io::from_stdio();
26 initialize(&mut io)?; 55 let res = initialize(&mut io);
27 io.stop()?; 56 info!("shutting down IO...");
28 Ok(()) 57 let io_res = io.stop();
58 info!("... IO is down");
59 match (res, io_res) {
60 (Ok(()), Ok(())) => Ok(()),
61 (res, Ok(())) => res,
62 (Ok(()), io_res) => io_res,
63 (res, Err(io_err)) => {
64 error!("shutdown error: {:?}", io_err);
65 res
66 }
67 }
29} 68}
30 69
31fn initialize(io: &mut Io) -> Result<()> { 70fn initialize(io: &mut Io) -> Result<()> {
@@ -59,20 +98,69 @@ fn initialize(io: &mut Io) -> Result<()> {
59 } 98 }
60} 99}
61 100
101type Thunk = Box<for<'a> FnBox<&'a mut Io, Result<()>>>;
102
62fn initialized(io: &mut Io) -> Result<()> { 103fn initialized(io: &mut Io) -> Result<()> {
63 eprintln!("initialized"); 104 let mut world = WorldState::new();
64 let world = WorldState::new(); 105 let mut pool = ThreadPool::new(4);
106 let (sender, receiver) = bounded::<Thunk>(16);
107 let res = main_loop(io, &mut world, &mut pool, sender, receiver.clone());
108 info!("waiting for background jobs to finish...");
109 receiver.for_each(drop);
110 info!("...background jobs have finished");
111 res
112}
113
114fn main_loop(
115 io: &mut Io,
116 world: &mut WorldState,
117 pool: &mut ThreadPool,
118 sender: Sender<Thunk>,
119 receiver: Receiver<Thunk>,
120) -> Result<()> {
121 info!("server initialized, serving requests");
65 loop { 122 loop {
66 match io.recv()? { 123 enum Event {
124 Msg(RawMsg),
125 Thunk(Thunk),
126 ReceiverDead,
127 }
128
129 let event = select! {
130 recv(io.receiver(), msg) => match msg {
131 Some(msg) => Event::Msg(msg),
132 None => Event::ReceiverDead,
133 },
134 recv(receiver, thunk) => Event::Thunk(thunk.unwrap()),
135 };
136
137 let msg = match event {
138 Event::ReceiverDead => {
139 io.cleanup_receiver()?;
140 unreachable!();
141 }
142 Event::Thunk(thunk) => {
143 thunk.call_box(io)?;
144 continue;
145 }
146 Event::Msg(msg) => msg,
147 };
148
149 match msg {
67 RawMsg::Request(req) => { 150 RawMsg::Request(req) => {
68 let world = world.snapshot();
69 if let Some((params, resp)) = dispatch::expect::<req::SyntaxTree>(io, req)? { 151 if let Some((params, resp)) = dispatch::expect::<req::SyntaxTree>(io, req)? {
70 resp.respond_with(io, || { 152 let world = world.snapshot();
71 let path = params.text_document.uri.to_file_path() 153 let sender = sender.clone();
72 .map_err(|()| format_err!("invalid path"))?; 154 pool.execute(move || {
73 let file = world.file_syntax(&path)?; 155 let res: Result<String> = (|| {
74 Ok(libeditor::syntax_tree(&file)) 156 let path = params.text_document.uri.to_file_path()
75 })? 157 .map_err(|()| format_err!("invalid path"))?;
158 let file = world.file_syntax(&path)?;
159 Ok(libeditor::syntax_tree(&file))
160 })();
161
162 sender.send(Box::new(|io: &mut Io| resp.response(io, res)))
163 });
76 } 164 }
77 } 165 }
78 msg => { 166 msg => {
@@ -82,3 +170,13 @@ fn initialized(io: &mut Io) -> Result<()> {
82 } 170 }
83} 171}
84 172
173
174trait FnBox<A, R>: Send {
175 fn call_box(self: Box<Self>, a: A) -> R;
176}
177
178impl<A, R, F: FnOnce(A) -> R + Send> FnBox<A, R> for F {
179 fn call_box(self: Box<F>, a: A) -> R {
180 (*self)(a)
181 }
182}
diff --git a/codeless/server/target/.rustc_info.json b/codeless/server/target/.rustc_info.json
deleted file mode 100644
index a37ac2011..000000000
--- a/codeless/server/target/.rustc_info.json
+++ /dev/null
@@ -1 +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
deleted file mode 100644
index e69de29bb..000000000
--- a/codeless/server/target/debug/.cargo-lock
+++ /dev/null
diff --git a/codeless/server/target/debug/libm.d b/codeless/server/target/debug/libm.d
deleted file mode 100644
index 04d8bb9ed..000000000
--- a/codeless/server/target/debug/libm.d
+++ /dev/null
@@ -1 +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
deleted file mode 100644
index e69de29bb..000000000
--- a/codeless/server/target/debug/libm.rmeta
+++ /dev/null
diff --git a/codeless/src/extension.ts b/codeless/src/extension.ts
index 712d93e04..c64065e6b 100644
--- a/codeless/src/extension.ts
+++ b/codeless/src/extension.ts
@@ -43,10 +43,8 @@ export function deactivate(): Thenable<void> {
43function startServer() { 43function startServer() {
44 let run: Executable = { 44 let run: Executable = {
45 command: "cargo", 45 command: "cargo",
46 args: ["run"], 46 args: ["run", "--package", "m"],
47 options: { 47 options: { cwd: "." }
48 cwd: "./server"
49 }
50 } 48 }
51 let serverOptions: ServerOptions = { 49 let serverOptions: ServerOptions = {
52 run, 50 run,
diff --git a/libeditor/src/lib.rs b/libeditor/src/lib.rs
index 817a2d15b..4e9631a8b 100644
--- a/libeditor/src/lib.rs
+++ b/libeditor/src/lib.rs
@@ -6,9 +6,8 @@ use libsyntax2::{
6 SyntaxNodeRef, AstNode, 6 SyntaxNodeRef, AstNode,
7 algo::walk, 7 algo::walk,
8 SyntaxKind::*, 8 SyntaxKind::*,
9 ast,
10}; 9};
11pub use libsyntax2::{TextRange, TextUnit}; 10pub use libsyntax2::{TextRange, TextUnit, ast};
12 11
13#[derive(Debug)] 12#[derive(Debug)]
14pub struct HighlightedRange { 13pub struct HighlightedRange {
diff --git a/libeditor/tests/test.rs b/libeditor/tests/test.rs
index ab8254d16..2a84c5080 100644
--- a/libeditor/tests/test.rs
+++ b/libeditor/tests/test.rs
@@ -3,7 +3,7 @@ extern crate itertools;
3 3
4use std::fmt; 4use std::fmt;
5use itertools::Itertools; 5use itertools::Itertools;
6use libeditor::{File, TextRange}; 6use libeditor::{ast, highlight, runnables, extend_selection, TextRange};
7 7
8#[test] 8#[test]
9fn test_extend_selection() { 9fn test_extend_selection() {
@@ -12,9 +12,9 @@ fn test_extend_selection() {
12} 12}
13"#); 13"#);
14 let range = TextRange::offset_len(18.into(), 0.into()); 14 let range = TextRange::offset_len(18.into(), 0.into());
15 let range = file.extend_selection(range).unwrap(); 15 let range = extend_selection(&file, range).unwrap();
16 assert_eq!(range, TextRange::from_to(17.into(), 18.into())); 16 assert_eq!(range, TextRange::from_to(17.into(), 18.into()));
17 let range = file.extend_selection(range).unwrap(); 17 let range = extend_selection(&file, range).unwrap();
18 assert_eq!(range, TextRange::from_to(15.into(), 20.into())); 18 assert_eq!(range, TextRange::from_to(15.into(), 20.into()));
19} 19}
20 20
@@ -25,7 +25,7 @@ fn test_highlighting() {
25fn main() {} 25fn main() {}
26 println!("Hello, {}!", 92); 26 println!("Hello, {}!", 92);
27"#); 27"#);
28 let hls = file.highlight(); 28 let hls = highlight(&file);
29 dbg_eq( 29 dbg_eq(
30 &hls, 30 &hls,
31 r#"[HighlightedRange { range: [1; 11), tag: "comment" }, 31 r#"[HighlightedRange { range: [1; 11), tag: "comment" },
@@ -49,7 +49,7 @@ fn test_foo() {}
49#[ignore] 49#[ignore]
50fn test_foo() {} 50fn test_foo() {}
51"#); 51"#);
52 let runnables = file.runnables(); 52 let runnables = runnables(&file);
53 dbg_eq( 53 dbg_eq(
54 &runnables, 54 &runnables,
55 r#"[Runnable { range: [1; 13), kind: Bin }, 55 r#"[Runnable { range: [1; 13), kind: Bin },
@@ -58,8 +58,8 @@ fn test_foo() {}
58 ) 58 )
59} 59}
60 60
61fn file(text: &str) -> File { 61fn file(text: &str) -> ast::File {
62 File::new(text) 62 ast::File::parse(text)
63} 63}
64 64
65fn dbg_eq(actual: &impl fmt::Debug, expected: &str) { 65fn dbg_eq(actual: &impl fmt::Debug, expected: &str) {
diff --git a/src/yellow/green.rs b/src/yellow/green.rs
index 787968363..f505b26d7 100644
--- a/src/yellow/green.rs
+++ b/src/yellow/green.rs
@@ -56,12 +56,6 @@ impl GreenNode {
56 } 56 }
57} 57}
58 58
59#[test]
60fn assert_send_sync() {
61 fn f<T: Send + Sync>() {}
62 f::<GreenNode>();
63}
64
65#[derive(Clone, Debug)] 59#[derive(Clone, Debug)]
66pub(crate) struct GreenBranch { 60pub(crate) struct GreenBranch {
67 text_len: TextUnit, 61 text_len: TextUnit,
diff --git a/src/yellow/mod.rs b/src/yellow/mod.rs
index 6129ecb99..ff3bb221b 100644
--- a/src/yellow/mod.rs
+++ b/src/yellow/mod.rs
@@ -3,9 +3,60 @@ mod green;
3mod red; 3mod red;
4mod syntax; 4mod syntax;
5 5
6pub use self::syntax::{SyntaxNode, SyntaxNodeRef, SyntaxRoot, TreeRoot, SyntaxError}; 6use std::{
7 ops::Deref,
8 sync::Arc,
9 ptr,
10};
11pub use self::syntax::{SyntaxNode, SyntaxNodeRef, SyntaxError};
7pub(crate) use self::{ 12pub(crate) use self::{
8 builder::GreenBuilder, 13 builder::GreenBuilder,
9 green::GreenNode, 14 green::GreenNode,
10 red::RedNode, 15 red::RedNode,
11}; 16};
17
18pub trait TreeRoot: Deref<Target=SyntaxRoot> + Clone + Send + Sync {}
19
20#[derive(Debug)]
21pub struct SyntaxRoot {
22 red: RedNode,
23 pub(crate) errors: Vec<SyntaxError>,
24}
25
26impl TreeRoot for Arc<SyntaxRoot> {}
27
28impl<'a> TreeRoot for &'a SyntaxRoot {}
29
30impl SyntaxRoot {
31 pub(crate) fn new(green: GreenNode, errors: Vec<SyntaxError>) -> SyntaxRoot {
32 SyntaxRoot {
33 red: RedNode::new_root(green),
34 errors,
35 }
36 }
37}
38
39#[derive(Clone, Copy, PartialEq, Eq, Debug)]
40pub(crate) struct RedPtr(ptr::NonNull<RedNode>);
41
42unsafe impl Send for RedPtr {}
43
44unsafe impl Sync for RedPtr {}
45
46impl RedPtr {
47 fn new(red: &RedNode) -> RedPtr {
48 RedPtr(red.into())
49 }
50
51 unsafe fn get<'a>(self, _root: &'a impl TreeRoot) -> &'a RedNode {
52 &*self.0.as_ptr()
53 }
54}
55
56#[test]
57fn assert_send_sync() {
58 fn f<T: Send + Sync>() {}
59 f::<GreenNode>();
60 f::<RedNode>();
61 f::<SyntaxNode>();
62}
diff --git a/src/yellow/red.rs b/src/yellow/red.rs
index f57c4a9b7..13ad44c65 100644
--- a/src/yellow/red.rs
+++ b/src/yellow/red.rs
@@ -1,7 +1,5 @@
1use std::ptr;
2
3use parking_lot::RwLock; 1use parking_lot::RwLock;
4use {yellow::GreenNode, TextUnit}; 2use {yellow::{GreenNode, RedPtr}, TextUnit};
5 3
6#[derive(Debug)] 4#[derive(Debug)]
7pub(crate) struct RedNode { 5pub(crate) struct RedNode {
@@ -12,7 +10,7 @@ pub(crate) struct RedNode {
12 10
13#[derive(Debug)] 11#[derive(Debug)]
14struct ParentData { 12struct ParentData {
15 parent: ptr::NonNull<RedNode>, 13 parent: RedPtr,
16 start_offset: TextUnit, 14 start_offset: TextUnit,
17 index_in_parent: usize, 15 index_in_parent: usize,
18} 16}
@@ -24,7 +22,7 @@ impl RedNode {
24 22
25 fn new_child( 23 fn new_child(
26 green: GreenNode, 24 green: GreenNode,
27 parent: ptr::NonNull<RedNode>, 25 parent: RedPtr,
28 start_offset: TextUnit, 26 start_offset: TextUnit,
29 index_in_parent: usize, 27 index_in_parent: usize,
30 ) -> RedNode { 28 ) -> RedNode {
@@ -64,12 +62,12 @@ impl RedNode {
64 self.green.children().len() 62 self.green.children().len()
65 } 63 }
66 64
67 pub(crate) fn get_child(&self, idx: usize) -> Option<ptr::NonNull<RedNode>> { 65 pub(crate) fn get_child(&self, idx: usize) -> Option<RedPtr> {
68 if idx >= self.n_children() { 66 if idx >= self.n_children() {
69 return None; 67 return None;
70 } 68 }
71 match &self.children.read()[idx] { 69 match &self.children.read()[idx] {
72 Some(child) => return Some(child.into()), 70 Some(child) => return Some(RedPtr::new(child)),
73 None => (), 71 None => (),
74 }; 72 };
75 let green_children = self.green.children(); 73 let green_children = self.green.children();
@@ -79,15 +77,15 @@ impl RedNode {
79 .map(|x| x.text_len()) 77 .map(|x| x.text_len())
80 .sum::<TextUnit>(); 78 .sum::<TextUnit>();
81 let child = 79 let child =
82 RedNode::new_child(green_children[idx].clone(), self.into(), start_offset, idx); 80 RedNode::new_child(green_children[idx].clone(), RedPtr::new(self), start_offset, idx);
83 let mut children = self.children.write(); 81 let mut children = self.children.write();
84 if children[idx].is_none() { 82 if children[idx].is_none() {
85 children[idx] = Some(child) 83 children[idx] = Some(child)
86 } 84 }
87 Some(children[idx].as_ref().unwrap().into()) 85 Some(RedPtr::new(children[idx].as_ref().unwrap()))
88 } 86 }
89 87
90 pub(crate) fn parent(&self) -> Option<ptr::NonNull<RedNode>> { 88 pub(crate) fn parent(&self) -> Option<RedPtr> {
91 Some(self.parent.as_ref()?.parent) 89 Some(self.parent.as_ref()?.parent)
92 } 90 }
93 pub(crate) fn index_in_parent(&self) -> Option<usize> { 91 pub(crate) fn index_in_parent(&self) -> Option<usize> {
diff --git a/src/yellow/syntax.rs b/src/yellow/syntax.rs
index 2ba9281fc..6e33310f1 100644
--- a/src/yellow/syntax.rs
+++ b/src/yellow/syntax.rs
@@ -1,25 +1,23 @@
1use std::{fmt, ops::Deref, ptr, sync::Arc}; 1use std::{fmt, sync::Arc};
2 2
3use { 3use {
4 yellow::{GreenNode, RedNode}, 4 yellow::{RedNode, TreeRoot, SyntaxRoot, RedPtr},
5 SyntaxKind::{self, *}, 5 SyntaxKind::{self, *},
6 TextRange, TextUnit, 6 TextRange, TextUnit,
7}; 7};
8 8
9pub trait TreeRoot: Deref<Target = SyntaxRoot> + Clone {}
10
11impl TreeRoot for Arc<SyntaxRoot> {}
12
13impl<'a> TreeRoot for &'a SyntaxRoot {}
14 9
15#[derive(Clone, Copy)] 10#[derive(Clone, Copy)]
16pub struct SyntaxNode<R: TreeRoot = Arc<SyntaxRoot>> { 11pub struct SyntaxNode<R: TreeRoot = Arc<SyntaxRoot>> {
17 pub(crate) root: R, 12 pub(crate) root: R,
18 // Guaranteed to not dangle, because `root` holds a 13 // Guaranteed to not dangle, because `root` holds a
19 // strong reference to red's ancestor 14 // strong reference to red's ancestor
20 red: ptr::NonNull<RedNode>, 15 red: RedPtr,
21} 16}
22 17
18unsafe impl<R: TreeRoot> Send for SyntaxNode<R> {}
19unsafe impl<R: TreeRoot> Sync for SyntaxNode<R> {}
20
23impl<R1: TreeRoot, R2: TreeRoot> PartialEq<SyntaxNode<R1>> for SyntaxNode<R2> { 21impl<R1: TreeRoot, R2: TreeRoot> PartialEq<SyntaxNode<R1>> for SyntaxNode<R2> {
24 fn eq(&self, other: &SyntaxNode<R1>) -> bool { 22 fn eq(&self, other: &SyntaxNode<R1>) -> bool {
25 self.red == other.red 23 self.red == other.red
@@ -30,21 +28,6 @@ impl<R: TreeRoot> Eq for SyntaxNode<R> {}
30 28
31pub type SyntaxNodeRef<'a> = SyntaxNode<&'a SyntaxRoot>; 29pub type SyntaxNodeRef<'a> = SyntaxNode<&'a SyntaxRoot>;
32 30
33#[derive(Debug)]
34pub struct SyntaxRoot {
35 red: RedNode,
36 pub(crate) errors: Vec<SyntaxError>,
37}
38
39impl SyntaxRoot {
40 pub(crate) fn new(green: GreenNode, errors: Vec<SyntaxError>) -> SyntaxRoot {
41 SyntaxRoot {
42 red: RedNode::new_root(green),
43 errors,
44 }
45 }
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] 31#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
49pub struct SyntaxError { 32pub struct SyntaxError {
50 pub msg: String, 33 pub msg: String,
@@ -54,11 +37,8 @@ pub struct SyntaxError {
54impl SyntaxNode<Arc<SyntaxRoot>> { 37impl SyntaxNode<Arc<SyntaxRoot>> {
55 pub(crate) fn new_owned(root: SyntaxRoot) -> Self { 38 pub(crate) fn new_owned(root: SyntaxRoot) -> Self {
56 let root = Arc::new(root); 39 let root = Arc::new(root);
57 let red_weak = ptr::NonNull::from(&root.red); 40 let red = RedPtr::new(&root.red);
58 SyntaxNode { 41 SyntaxNode { root, red }
59 root,
60 red: red_weak,
61 }
62 } 42 }
63} 43}
64 44
@@ -66,7 +46,7 @@ impl<R: TreeRoot> SyntaxNode<R> {
66 pub fn as_ref<'a>(&'a self) -> SyntaxNode<&'a SyntaxRoot> { 46 pub fn as_ref<'a>(&'a self) -> SyntaxNode<&'a SyntaxRoot> {
67 SyntaxNode { 47 SyntaxNode {
68 root: &*self.root, 48 root: &*self.root,
69 red: ptr::NonNull::clone(&self.red), 49 red: self.red,
70 } 50 }
71 } 51 }
72 52
@@ -120,7 +100,7 @@ impl<R: TreeRoot> SyntaxNode<R> {
120 } 100 }
121 101
122 fn red(&self) -> &RedNode { 102 fn red(&self) -> &RedNode {
123 unsafe { self.red.as_ref() } 103 unsafe { self.red.get(&self.root) }
124 } 104 }
125} 105}
126 106