This commit is contained in:
Aleksey Kladov 2018-08-10 17:49:45 +03:00
parent d7c5a6f308
commit 2e165ae82e
18 changed files with 233 additions and 115 deletions

View file

@ -5,7 +5,7 @@ authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
[workspace] [workspace]
members = [ "tools", "cli", "libeditor", "libanalysis" ] members = [ "tools", "cli", "libeditor", "libanalysis", "codeless/server" ]
[dependencies] [dependencies]
unicode-xid = "0.1.0" unicode-xid = "0.1.0"

View file

@ -10,7 +10,7 @@ use std::{
}; };
use clap::{App, Arg, SubCommand}; use clap::{App, Arg, SubCommand};
use tools::collect_tests; use tools::collect_tests;
use libeditor::File; use libeditor::{ast, syntax_tree, symbols};
type Result<T> = ::std::result::Result<T, failure::Error>; type Result<T> = ::std::result::Result<T, failure::Error>;
@ -44,14 +44,14 @@ fn main() -> Result<()> {
let file = file()?; let file = file()?;
let elapsed = start.elapsed(); let elapsed = start.elapsed();
if !matches.is_present("no-dump") { if !matches.is_present("no-dump") {
println!("{}", file.syntax_tree()); println!("{}", syntax_tree(&file));
} }
eprintln!("parsing: {:?}", elapsed); eprintln!("parsing: {:?}", elapsed);
::std::mem::forget(file); ::std::mem::forget(file);
} }
("symbols", _) => { ("symbols", _) => {
let file = file()?; let file = file()?;
for s in file.symbols() { for s in symbols(&file) {
println!("{:?}", s); println!("{:?}", s);
} }
} }
@ -68,9 +68,9 @@ fn main() -> Result<()> {
Ok(()) Ok(())
} }
fn file() -> Result<File> { fn file() -> Result<ast::File> {
let text = read_stdin()?; let text = read_stdin()?;
Ok(File::new(&text)) Ok(ast::File::parse(&text))
} }
fn read_stdin() -> Result<String> { fn read_stdin() -> Result<String> {
@ -89,7 +89,7 @@ fn render_test(file: &Path, line: usize) -> Result<(String, String)> {
None => bail!("No test found at line {} at {}", line, file.display()), None => bail!("No test found at line {} at {}", line, file.display()),
Some((_start_line, test)) => test, Some((_start_line, test)) => test,
}; };
let file = File::new(&test.text); let file = ast::File::parse(&test.text);
let tree = file.syntax_tree(); let tree = syntax_tree(&file);
Ok((test.text, tree)) Ok((test.text, tree))
} }

View file

@ -1 +0,0 @@
/target/*

View file

@ -2,7 +2,6 @@
name = "m" name = "m"
version = "0.1.0" version = "0.1.0"
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"] authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
[workspace]
[dependencies] [dependencies]
failure = "0.1.2" failure = "0.1.2"
@ -12,5 +11,8 @@ serde = "1.0.71"
serde_derive = "1.0.71" serde_derive = "1.0.71"
drop_bomb = "0.1.0" drop_bomb = "0.1.0"
crossbeam-channel = "0.2.4" crossbeam-channel = "0.2.4"
threadpool = "1.7.1"
flexi_logger = "0.9.0"
log = "0.4.3"
libeditor = { path = "../../libeditor" } libeditor = { path = "../../libeditor" }
libanalysis = { path = "../../libanalysis" } libanalysis = { path = "../../libanalysis" }

View file

@ -24,8 +24,8 @@ impl<R: Request> Responder<R>
R::Params: DeserializeOwned, R::Params: DeserializeOwned,
R::Result: Serialize, R::Result: Serialize,
{ {
pub fn respond_with(self, io: &mut Io, f: impl FnOnce() -> Result<R::Result>) -> Result<()> { pub fn response(self, io: &mut Io, resp: Result<R::Result>) -> Result<()> {
match f() { match resp {
Ok(res) => self.result(io, res)?, Ok(res) => self.result(io, res)?,
Err(e) => { Err(e) => {
self.error(io)?; self.error(io)?;

View file

@ -49,6 +49,13 @@ impl MsgReceiver {
match self.chan.recv() { match self.chan.recv() {
Some(msg) => Ok(msg), Some(msg) => Ok(msg),
None => { None => {
self.cleanup()?;
unreachable!()
}
}
}
fn cleanup(&mut self) -> Result<()> {
self.thread self.thread
.take() .take()
.ok_or_else(|| format_err!("MsgReceiver thread panicked"))? .ok_or_else(|| format_err!("MsgReceiver thread panicked"))?
@ -56,8 +63,6 @@ impl MsgReceiver {
.map_err(|_| format_err!("MsgReceiver thread panicked"))??; .map_err(|_| format_err!("MsgReceiver thread panicked"))??;
bail!("client disconnected") bail!("client disconnected")
} }
}
}
fn stop(self) -> Result<()> { fn stop(self) -> Result<()> {
// Can't really self.thread.join() here, b/c it might be // Can't really self.thread.join() here, b/c it might be
@ -68,7 +73,7 @@ impl MsgReceiver {
struct MsgSender { struct MsgSender {
chan: Sender<RawMsg>, chan: Sender<RawMsg>,
thread: Option<thread::JoinHandle<Result<()>>>, thread: thread::JoinHandle<Result<()>>,
} }
impl MsgSender { impl MsgSender {
@ -76,28 +81,14 @@ impl MsgSender {
self.chan.send(msg) self.chan.send(msg)
} }
fn stop(mut self) -> Result<()> { fn stop(self) -> Result<()> {
if let Some(thread) = self.thread.take() { drop(self.chan);
thread.join() self.thread.join()
.map_err(|_| format_err!("MsgSender thread panicked"))?? .map_err(|_| format_err!("MsgSender thread panicked"))??;
}
Ok(()) Ok(())
} }
} }
impl Drop for MsgSender {
fn drop(&mut self) {
if let Some(thread) = self.thread.take() {
let res = thread.join();
if thread::panicking() {
drop(res)
} else {
res.unwrap().unwrap()
}
}
}
}
pub struct Io { pub struct Io {
receiver: MsgReceiver, receiver: MsgReceiver,
sender: MsgSender, sender: MsgSender,
@ -109,7 +100,7 @@ impl Io {
let (tx, rx) = bounded(16); let (tx, rx) = bounded(16);
MsgSender { MsgSender {
chan: tx, chan: tx,
thread: Some(thread::spawn(move || { thread: thread::spawn(move || {
let stdout = stdout(); let stdout = stdout();
let mut stdout = stdout.lock(); let mut stdout = stdout.lock();
for msg in rx { for msg in rx {
@ -126,7 +117,7 @@ impl Io {
write_msg_text(&mut stdout, &text)?; write_msg_text(&mut stdout, &text)?;
} }
Ok(()) Ok(())
})), }),
} }
}; };
let receiver = { let receiver = {
@ -155,6 +146,14 @@ impl Io {
self.receiver.recv() self.receiver.recv()
} }
pub fn receiver(&mut self) -> &mut Receiver<RawMsg> {
&mut self.receiver.chan
}
pub fn cleanup_receiver(&mut self) -> Result<()> {
self.receiver.cleanup()
}
pub fn stop(self) -> Result<()> { pub fn stop(self) -> Result<()> {
self.receiver.stop()?; self.receiver.stop()?;
self.sender.stop()?; self.sender.stop()?;
@ -190,10 +189,12 @@ fn read_msg_text(inp: &mut impl BufRead) -> Result<Option<String>> {
buf.resize(size, 0); buf.resize(size, 0);
inp.read_exact(&mut buf)?; inp.read_exact(&mut buf)?;
let buf = String::from_utf8(buf)?; let buf = String::from_utf8(buf)?;
debug!("< {}", buf);
Ok(Some(buf)) Ok(Some(buf))
} }
fn write_msg_text(out: &mut impl Write, msg: &str) -> Result<()> { fn write_msg_text(out: &mut impl Write, msg: &str) -> Result<()> {
debug!("> {}", msg);
write!(out, "Content-Length: {}\r\n\r\n", msg.len())?; write!(out, "Content-Length: {}\r\n\r\n", msg.len())?;
out.write_all(msg.as_bytes())?; out.write_all(msg.as_bytes())?;
out.flush()?; out.flush()?;

View file

@ -6,7 +6,12 @@ extern crate serde;
extern crate serde_json; extern crate serde_json;
extern crate languageserver_types; extern crate languageserver_types;
extern crate drop_bomb; extern crate drop_bomb;
#[macro_use]
extern crate crossbeam_channel; extern crate crossbeam_channel;
extern crate threadpool;
#[macro_use]
extern crate log;
extern crate flexi_logger;
extern crate libeditor; extern crate libeditor;
extern crate libanalysis; extern crate libanalysis;
@ -16,16 +21,50 @@ mod req;
mod dispatch; mod dispatch;
use languageserver_types::InitializeResult; use languageserver_types::InitializeResult;
use threadpool::ThreadPool;
use crossbeam_channel::{bounded, Sender, Receiver};
use flexi_logger::Logger;
use libanalysis::WorldState; use libanalysis::WorldState;
use self::io::{Io, RawMsg};
use ::{
io::{Io, RawMsg},
};
pub type Result<T> = ::std::result::Result<T, ::failure::Error>; pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
fn main() -> Result<()> { fn main() -> Result<()> {
Logger::with_env_or_str("m=trace")
.log_to_file()
.directory("log")
.start()?;
info!("starting server");
match ::std::panic::catch_unwind(|| main_inner()) {
Ok(res) => {
info!("shutting down: {:?}", res);
res
}
Err(_) => {
error!("server panicked");
bail!("server panicked")
},
}
}
fn main_inner() -> Result<()> {
let mut io = Io::from_stdio(); let mut io = Io::from_stdio();
initialize(&mut io)?; let res = initialize(&mut io);
io.stop()?; info!("shutting down IO...");
Ok(()) let io_res = io.stop();
info!("... IO is down");
match (res, io_res) {
(Ok(()), Ok(())) => Ok(()),
(res, Ok(())) => res,
(Ok(()), io_res) => io_res,
(res, Err(io_err)) => {
error!("shutdown error: {:?}", io_err);
res
}
}
} }
fn initialize(io: &mut Io) -> Result<()> { fn initialize(io: &mut Io) -> Result<()> {
@ -59,20 +98,69 @@ fn initialize(io: &mut Io) -> Result<()> {
} }
} }
type Thunk = Box<for<'a> FnBox<&'a mut Io, Result<()>>>;
fn initialized(io: &mut Io) -> Result<()> { fn initialized(io: &mut Io) -> Result<()> {
eprintln!("initialized"); let mut world = WorldState::new();
let world = WorldState::new(); let mut pool = ThreadPool::new(4);
let (sender, receiver) = bounded::<Thunk>(16);
let res = main_loop(io, &mut world, &mut pool, sender, receiver.clone());
info!("waiting for background jobs to finish...");
receiver.for_each(drop);
info!("...background jobs have finished");
res
}
fn main_loop(
io: &mut Io,
world: &mut WorldState,
pool: &mut ThreadPool,
sender: Sender<Thunk>,
receiver: Receiver<Thunk>,
) -> Result<()> {
info!("server initialized, serving requests");
loop { loop {
match io.recv()? { enum Event {
Msg(RawMsg),
Thunk(Thunk),
ReceiverDead,
}
let event = select! {
recv(io.receiver(), msg) => match msg {
Some(msg) => Event::Msg(msg),
None => Event::ReceiverDead,
},
recv(receiver, thunk) => Event::Thunk(thunk.unwrap()),
};
let msg = match event {
Event::ReceiverDead => {
io.cleanup_receiver()?;
unreachable!();
}
Event::Thunk(thunk) => {
thunk.call_box(io)?;
continue;
}
Event::Msg(msg) => msg,
};
match msg {
RawMsg::Request(req) => { RawMsg::Request(req) => {
let world = world.snapshot();
if let Some((params, resp)) = dispatch::expect::<req::SyntaxTree>(io, req)? { if let Some((params, resp)) = dispatch::expect::<req::SyntaxTree>(io, req)? {
resp.respond_with(io, || { let world = world.snapshot();
let sender = sender.clone();
pool.execute(move || {
let res: Result<String> = (|| {
let path = params.text_document.uri.to_file_path() let path = params.text_document.uri.to_file_path()
.map_err(|()| format_err!("invalid path"))?; .map_err(|()| format_err!("invalid path"))?;
let file = world.file_syntax(&path)?; let file = world.file_syntax(&path)?;
Ok(libeditor::syntax_tree(&file)) Ok(libeditor::syntax_tree(&file))
})? })();
sender.send(Box::new(|io: &mut Io| resp.response(io, res)))
});
} }
} }
msg => { msg => {
@ -82,3 +170,13 @@ fn initialized(io: &mut Io) -> Result<()> {
} }
} }
trait FnBox<A, R>: Send {
fn call_box(self: Box<Self>, a: A) -> R;
}
impl<A, R, F: FnOnce(A) -> R + Send> FnBox<A, R> for F {
fn call_box(self: Box<F>, a: A) -> R {
(*self)(a)
}
}

View file

@ -1 +0,0 @@
{"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",""]}}

View file

@ -1 +0,0 @@
/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

View file

@ -43,10 +43,8 @@ export function deactivate(): Thenable<void> {
function startServer() { function startServer() {
let run: Executable = { let run: Executable = {
command: "cargo", command: "cargo",
args: ["run"], args: ["run", "--package", "m"],
options: { options: { cwd: "." }
cwd: "./server"
}
} }
let serverOptions: ServerOptions = { let serverOptions: ServerOptions = {
run, run,

View file

@ -6,9 +6,8 @@ use libsyntax2::{
SyntaxNodeRef, AstNode, SyntaxNodeRef, AstNode,
algo::walk, algo::walk,
SyntaxKind::*, SyntaxKind::*,
ast,
}; };
pub use libsyntax2::{TextRange, TextUnit}; pub use libsyntax2::{TextRange, TextUnit, ast};
#[derive(Debug)] #[derive(Debug)]
pub struct HighlightedRange { pub struct HighlightedRange {

View file

@ -3,7 +3,7 @@ extern crate itertools;
use std::fmt; use std::fmt;
use itertools::Itertools; use itertools::Itertools;
use libeditor::{File, TextRange}; use libeditor::{ast, highlight, runnables, extend_selection, TextRange};
#[test] #[test]
fn test_extend_selection() { fn test_extend_selection() {
@ -12,9 +12,9 @@ fn test_extend_selection() {
} }
"#); "#);
let range = TextRange::offset_len(18.into(), 0.into()); let range = TextRange::offset_len(18.into(), 0.into());
let range = file.extend_selection(range).unwrap(); let range = extend_selection(&file, range).unwrap();
assert_eq!(range, TextRange::from_to(17.into(), 18.into())); assert_eq!(range, TextRange::from_to(17.into(), 18.into()));
let range = file.extend_selection(range).unwrap(); let range = extend_selection(&file, range).unwrap();
assert_eq!(range, TextRange::from_to(15.into(), 20.into())); assert_eq!(range, TextRange::from_to(15.into(), 20.into()));
} }
@ -25,7 +25,7 @@ fn test_highlighting() {
fn main() {} fn main() {}
println!("Hello, {}!", 92); println!("Hello, {}!", 92);
"#); "#);
let hls = file.highlight(); let hls = highlight(&file);
dbg_eq( dbg_eq(
&hls, &hls,
r#"[HighlightedRange { range: [1; 11), tag: "comment" }, r#"[HighlightedRange { range: [1; 11), tag: "comment" },
@ -49,7 +49,7 @@ fn test_foo() {}
#[ignore] #[ignore]
fn test_foo() {} fn test_foo() {}
"#); "#);
let runnables = file.runnables(); let runnables = runnables(&file);
dbg_eq( dbg_eq(
&runnables, &runnables,
r#"[Runnable { range: [1; 13), kind: Bin }, r#"[Runnable { range: [1; 13), kind: Bin },
@ -58,8 +58,8 @@ fn test_foo() {}
) )
} }
fn file(text: &str) -> File { fn file(text: &str) -> ast::File {
File::new(text) ast::File::parse(text)
} }
fn dbg_eq(actual: &impl fmt::Debug, expected: &str) { fn dbg_eq(actual: &impl fmt::Debug, expected: &str) {

View file

@ -56,12 +56,6 @@ impl GreenNode {
} }
} }
#[test]
fn assert_send_sync() {
fn f<T: Send + Sync>() {}
f::<GreenNode>();
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct GreenBranch { pub(crate) struct GreenBranch {
text_len: TextUnit, text_len: TextUnit,

View file

@ -3,9 +3,60 @@ mod green;
mod red; mod red;
mod syntax; mod syntax;
pub use self::syntax::{SyntaxNode, SyntaxNodeRef, SyntaxRoot, TreeRoot, SyntaxError}; use std::{
ops::Deref,
sync::Arc,
ptr,
};
pub use self::syntax::{SyntaxNode, SyntaxNodeRef, SyntaxError};
pub(crate) use self::{ pub(crate) use self::{
builder::GreenBuilder, builder::GreenBuilder,
green::GreenNode, green::GreenNode,
red::RedNode, red::RedNode,
}; };
pub trait TreeRoot: Deref<Target=SyntaxRoot> + Clone + Send + Sync {}
#[derive(Debug)]
pub struct SyntaxRoot {
red: RedNode,
pub(crate) errors: Vec<SyntaxError>,
}
impl TreeRoot for Arc<SyntaxRoot> {}
impl<'a> TreeRoot for &'a SyntaxRoot {}
impl SyntaxRoot {
pub(crate) fn new(green: GreenNode, errors: Vec<SyntaxError>) -> SyntaxRoot {
SyntaxRoot {
red: RedNode::new_root(green),
errors,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) struct RedPtr(ptr::NonNull<RedNode>);
unsafe impl Send for RedPtr {}
unsafe impl Sync for RedPtr {}
impl RedPtr {
fn new(red: &RedNode) -> RedPtr {
RedPtr(red.into())
}
unsafe fn get<'a>(self, _root: &'a impl TreeRoot) -> &'a RedNode {
&*self.0.as_ptr()
}
}
#[test]
fn assert_send_sync() {
fn f<T: Send + Sync>() {}
f::<GreenNode>();
f::<RedNode>();
f::<SyntaxNode>();
}

View file

@ -1,7 +1,5 @@
use std::ptr;
use parking_lot::RwLock; use parking_lot::RwLock;
use {yellow::GreenNode, TextUnit}; use {yellow::{GreenNode, RedPtr}, TextUnit};
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct RedNode { pub(crate) struct RedNode {
@ -12,7 +10,7 @@ pub(crate) struct RedNode {
#[derive(Debug)] #[derive(Debug)]
struct ParentData { struct ParentData {
parent: ptr::NonNull<RedNode>, parent: RedPtr,
start_offset: TextUnit, start_offset: TextUnit,
index_in_parent: usize, index_in_parent: usize,
} }
@ -24,7 +22,7 @@ impl RedNode {
fn new_child( fn new_child(
green: GreenNode, green: GreenNode,
parent: ptr::NonNull<RedNode>, parent: RedPtr,
start_offset: TextUnit, start_offset: TextUnit,
index_in_parent: usize, index_in_parent: usize,
) -> RedNode { ) -> RedNode {
@ -64,12 +62,12 @@ impl RedNode {
self.green.children().len() self.green.children().len()
} }
pub(crate) fn get_child(&self, idx: usize) -> Option<ptr::NonNull<RedNode>> { pub(crate) fn get_child(&self, idx: usize) -> Option<RedPtr> {
if idx >= self.n_children() { if idx >= self.n_children() {
return None; return None;
} }
match &self.children.read()[idx] { match &self.children.read()[idx] {
Some(child) => return Some(child.into()), Some(child) => return Some(RedPtr::new(child)),
None => (), None => (),
}; };
let green_children = self.green.children(); let green_children = self.green.children();
@ -79,15 +77,15 @@ impl RedNode {
.map(|x| x.text_len()) .map(|x| x.text_len())
.sum::<TextUnit>(); .sum::<TextUnit>();
let child = let child =
RedNode::new_child(green_children[idx].clone(), self.into(), start_offset, idx); RedNode::new_child(green_children[idx].clone(), RedPtr::new(self), start_offset, idx);
let mut children = self.children.write(); let mut children = self.children.write();
if children[idx].is_none() { if children[idx].is_none() {
children[idx] = Some(child) children[idx] = Some(child)
} }
Some(children[idx].as_ref().unwrap().into()) Some(RedPtr::new(children[idx].as_ref().unwrap()))
} }
pub(crate) fn parent(&self) -> Option<ptr::NonNull<RedNode>> { pub(crate) fn parent(&self) -> Option<RedPtr> {
Some(self.parent.as_ref()?.parent) Some(self.parent.as_ref()?.parent)
} }
pub(crate) fn index_in_parent(&self) -> Option<usize> { pub(crate) fn index_in_parent(&self) -> Option<usize> {

View file

@ -1,25 +1,23 @@
use std::{fmt, ops::Deref, ptr, sync::Arc}; use std::{fmt, sync::Arc};
use { use {
yellow::{GreenNode, RedNode}, yellow::{RedNode, TreeRoot, SyntaxRoot, RedPtr},
SyntaxKind::{self, *}, SyntaxKind::{self, *},
TextRange, TextUnit, TextRange, TextUnit,
}; };
pub trait TreeRoot: Deref<Target = SyntaxRoot> + Clone {}
impl TreeRoot for Arc<SyntaxRoot> {}
impl<'a> TreeRoot for &'a SyntaxRoot {}
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct SyntaxNode<R: TreeRoot = Arc<SyntaxRoot>> { pub struct SyntaxNode<R: TreeRoot = Arc<SyntaxRoot>> {
pub(crate) root: R, pub(crate) root: R,
// Guaranteed to not dangle, because `root` holds a // Guaranteed to not dangle, because `root` holds a
// strong reference to red's ancestor // strong reference to red's ancestor
red: ptr::NonNull<RedNode>, red: RedPtr,
} }
unsafe impl<R: TreeRoot> Send for SyntaxNode<R> {}
unsafe impl<R: TreeRoot> Sync for SyntaxNode<R> {}
impl<R1: TreeRoot, R2: TreeRoot> PartialEq<SyntaxNode<R1>> for SyntaxNode<R2> { impl<R1: TreeRoot, R2: TreeRoot> PartialEq<SyntaxNode<R1>> for SyntaxNode<R2> {
fn eq(&self, other: &SyntaxNode<R1>) -> bool { fn eq(&self, other: &SyntaxNode<R1>) -> bool {
self.red == other.red self.red == other.red
@ -30,21 +28,6 @@ impl<R: TreeRoot> Eq for SyntaxNode<R> {}
pub type SyntaxNodeRef<'a> = SyntaxNode<&'a SyntaxRoot>; pub type SyntaxNodeRef<'a> = SyntaxNode<&'a SyntaxRoot>;
#[derive(Debug)]
pub struct SyntaxRoot {
red: RedNode,
pub(crate) errors: Vec<SyntaxError>,
}
impl SyntaxRoot {
pub(crate) fn new(green: GreenNode, errors: Vec<SyntaxError>) -> SyntaxRoot {
SyntaxRoot {
red: RedNode::new_root(green),
errors,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct SyntaxError { pub struct SyntaxError {
pub msg: String, pub msg: String,
@ -54,11 +37,8 @@ pub struct SyntaxError {
impl SyntaxNode<Arc<SyntaxRoot>> { impl SyntaxNode<Arc<SyntaxRoot>> {
pub(crate) fn new_owned(root: SyntaxRoot) -> Self { pub(crate) fn new_owned(root: SyntaxRoot) -> Self {
let root = Arc::new(root); let root = Arc::new(root);
let red_weak = ptr::NonNull::from(&root.red); let red = RedPtr::new(&root.red);
SyntaxNode { SyntaxNode { root, red }
root,
red: red_weak,
}
} }
} }
@ -66,7 +46,7 @@ impl<R: TreeRoot> SyntaxNode<R> {
pub fn as_ref<'a>(&'a self) -> SyntaxNode<&'a SyntaxRoot> { pub fn as_ref<'a>(&'a self) -> SyntaxNode<&'a SyntaxRoot> {
SyntaxNode { SyntaxNode {
root: &*self.root, root: &*self.root,
red: ptr::NonNull::clone(&self.red), red: self.red,
} }
} }
@ -120,7 +100,7 @@ impl<R: TreeRoot> SyntaxNode<R> {
} }
fn red(&self) -> &RedNode { fn red(&self) -> &RedNode {
unsafe { self.red.as_ref() } unsafe { self.red.get(&self.root) }
} }
} }