Extract libeditor

This commit is contained in:
Aleksey Kladov 2018-08-01 10:40:07 +03:00
parent b9189ed2db
commit 966e9db2b8
15 changed files with 138 additions and 50 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" ] members = [ "tools", "cli", "libeditor" ]
[dependencies] [dependencies]
unicode-xid = "0.1.0" unicode-xid = "0.1.0"

View file

@ -52,7 +52,7 @@ fn main() -> Result<()> {
fn parse() -> Result<String> { fn parse() -> Result<String> {
let text = read_stdin()?; let text = read_stdin()?;
let start = Instant::now(); let start = Instant::now();
let file = libsyntax2::parse(text); let file = libsyntax2::parse(&text);
eprintln!("elapsed {:?}", start.elapsed()); eprintln!("elapsed {:?}", start.elapsed());
let tree = libsyntax2::utils::dump_tree(&file); let tree = libsyntax2::utils::dump_tree(&file);
Ok(tree) Ok(tree)
@ -74,7 +74,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 = libsyntax2::parse(test.text.clone()); let file = libsyntax2::parse(&test.text);
let tree = libsyntax2::utils::dump_tree(&file); let tree = libsyntax2::utils::dump_tree(&file);
Ok((test.text, tree)) Ok((test.text, tree))
} }

View file

@ -16,4 +16,4 @@ neon-build = "0.2.0"
[dependencies] [dependencies]
neon = "0.2.0" neon = "0.2.0"
libsyntax2 = { path = "../../" } libeditor = { path = "../../libeditor" }

View file

@ -1,42 +1,20 @@
#[macro_use] #[macro_use]
extern crate neon; extern crate neon;
extern crate libsyntax2; extern crate libeditor;
use libsyntax2::{
TextRange,
File,
utils::dump_tree,
SyntaxKind::*,
algo,
};
use neon::prelude::*; use neon::prelude::*;
pub struct Wrapper { pub struct Wrapper {
inner: File, inner: libeditor::File,
} }
impl Wrapper {
fn highlight(&self) -> Vec<(TextRange, &'static str)> {
let mut res = Vec::new();
let syntax = self.inner.syntax();
for node in algo::walk::preorder(syntax.as_ref()) {
if node.kind() == ERROR {
res.push((node.range(), "error"))
}
}
res
}
}
declare_types! { declare_types! {
/// A class for generating greeting strings. /// A class for generating greeting strings.
pub class RustFile for Wrapper { pub class RustFile for Wrapper {
init(mut cx) { init(mut cx) {
let text = cx.argument::<JsString>(0)?.value(); let text = cx.argument::<JsString>(0)?.value();
Ok(Wrapper { Ok(Wrapper {
inner: File::parse(&text) inner: libeditor::File::new(&text)
}) })
} }
@ -45,7 +23,7 @@ declare_types! {
let tree = { let tree = {
let guard = cx.lock(); let guard = cx.lock();
let wrapper = this.borrow(&guard); let wrapper = this.borrow(&guard);
dump_tree(&wrapper.inner.syntax()) wrapper.inner.syntax_tree()
}; };
Ok(cx.string(tree.as_str()).upcast()) Ok(cx.string(tree.as_str()).upcast())
} }
@ -55,15 +33,15 @@ declare_types! {
let highlights = { let highlights = {
let guard = cx.lock(); let guard = cx.lock();
let wrapper = this.borrow(&guard); let wrapper = this.borrow(&guard);
wrapper.highlight() wrapper.inner.highlight()
}; };
let res = cx.empty_array(); let res = cx.empty_array();
for (i, (range, tag)) in highlights.into_iter().enumerate() { for (i, hl) in highlights.into_iter().enumerate() {
let start: u32 = range.start().into(); let start: u32 = hl.range.start().into();
let end: u32 = range.end().into(); let end: u32 = hl.range.end().into();
let start = cx.number(start); let start = cx.number(start);
let end = cx.number(end); let end = cx.number(end);
let tag = cx.string(tag); let tag = cx.string(hl.tag);
let hl = cx.empty_array(); let hl = cx.empty_array();
hl.set(&mut cx, 0, start)?; hl.set(&mut cx, 0, start)?;
hl.set(&mut cx, 1, end)?; hl.set(&mut cx, 1, end)?;

9
libeditor/Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "libeditor"
version = "0.1.0"
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
publish = false
[dependencies]
libsyntax2 = { path = "../" }
text_unit = "0.1.2"

53
libeditor/src/lib.rs Normal file
View file

@ -0,0 +1,53 @@
extern crate libsyntax2;
extern crate text_unit;
use libsyntax2::{
algo::walk,
SyntaxKind::*,
};
use text_unit::TextRange;
pub struct File {
inner: libsyntax2::File
}
pub struct HighlightedRange {
pub range: TextRange,
pub tag: &'static str,
}
impl File {
pub fn new(text: &str) -> File {
File {
inner: libsyntax2::File::parse(text)
}
}
pub fn highlight(&self) -> Vec<HighlightedRange> {
let syntax = self.inner.syntax();
let mut res = Vec::new();
for node in walk::preorder(syntax.as_ref()) {
let tag = match node.kind() {
ERROR => "error",
COMMENT | DOC_COMMENT => "comment",
STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string",
ATTR => "attribute",
NAME_REF => "text",
NAME => "function",
INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal",
LIFETIME => "parameter",
k if k.is_keyword() => "keyword",
_ => continue,
};
res.push(HighlightedRange {
range: node.range(),
tag
})
}
res
}
pub fn syntax_tree(&self) -> String {
::libsyntax2::utils::dump_tree(&self.inner.syntax())
}
}

View file

@ -6,7 +6,6 @@ pub fn preorder<'a>(root: SyntaxNodeRef<'a>) -> impl Iterator<Item = SyntaxNodeR
WalkEvent::Exit(_) => None, WalkEvent::Exit(_) => None,
}) })
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum WalkEvent<'a> { pub enum WalkEvent<'a> {
Enter(SyntaxNodeRef<'a>), Enter(SyntaxNodeRef<'a>),

View file

@ -9,7 +9,7 @@ pub struct File<R: TreeRoot = Arc<SyntaxRoot>> {
impl File<Arc<SyntaxRoot>> { impl File<Arc<SyntaxRoot>> {
pub fn parse(text: &str) -> Self { pub fn parse(text: &str) -> Self {
File { File {
syntax: ::parse(text.to_owned()), syntax: ::parse(text),
} }
} }
} }

View file

@ -45,7 +45,7 @@ pub use {
yellow::{SyntaxNode, SyntaxNodeRef, SyntaxRoot, TreeRoot}, yellow::{SyntaxNode, SyntaxNodeRef, SyntaxRoot, TreeRoot},
}; };
pub fn parse(text: String) -> SyntaxNode { pub fn parse(text: &str) -> SyntaxNode {
let tokens = tokenize(&text); let tokens = tokenize(&text);
parser_impl::parse::<yellow::GreenBuilder>(text, &tokens) parser_impl::parse::<yellow::GreenBuilder>(text, &tokens)
} }

View file

@ -76,7 +76,7 @@ pub(crate) enum Event {
}, },
} }
pub(super) fn process(builder: &mut impl Sink, tokens: &[Token], events: Vec<Event>) { pub(super) fn process<'a>(builder: &mut impl Sink<'a>, tokens: &[Token], events: Vec<Event>) {
let mut idx = 0; let mut idx = 0;
let mut holes = Vec::new(); let mut holes = Vec::new();

View file

@ -14,10 +14,10 @@ use {
use SyntaxKind::{self, EOF, TOMBSTONE}; use SyntaxKind::{self, EOF, TOMBSTONE};
pub(crate) trait Sink { pub(crate) trait Sink<'a> {
type Tree; type Tree;
fn new(text: String) -> Self; fn new(text: &'a str) -> Self;
fn leaf(&mut self, kind: SyntaxKind, len: TextUnit); fn leaf(&mut self, kind: SyntaxKind, len: TextUnit);
fn start_internal(&mut self, kind: SyntaxKind); fn start_internal(&mut self, kind: SyntaxKind);
@ -27,9 +27,9 @@ pub(crate) trait Sink {
} }
/// Parse a sequence of tokens into the representative node tree /// Parse a sequence of tokens into the representative node tree
pub(crate) fn parse<S: Sink>(text: String, tokens: &[Token]) -> S::Tree { pub(crate) fn parse<'a, S: Sink<'a>>(text: &'a str, tokens: &[Token]) -> S::Tree {
let events = { let events = {
let input = input::ParserInput::new(&text, tokens); let input = input::ParserInput::new(text, tokens);
let parser_impl = ParserImpl::new(&input); let parser_impl = ParserImpl::new(&input);
let mut parser_api = Parser(parser_impl); let mut parser_api = Parser(parser_impl);
grammar::file(&mut parser_api); grammar::file(&mut parser_api);

View file

@ -169,6 +169,47 @@ pub enum SyntaxKind {
use self::SyntaxKind::*; use self::SyntaxKind::*;
impl SyntaxKind { impl SyntaxKind {
pub fn is_keyword(self) -> bool {
match self {
| USE_KW
| FN_KW
| STRUCT_KW
| ENUM_KW
| TRAIT_KW
| IMPL_KW
| TRUE_KW
| FALSE_KW
| AS_KW
| EXTERN_KW
| CRATE_KW
| MOD_KW
| PUB_KW
| SELF_KW
| SUPER_KW
| IN_KW
| WHERE_KW
| FOR_KW
| LOOP_KW
| WHILE_KW
| IF_KW
| ELSE_KW
| MATCH_KW
| CONST_KW
| STATIC_KW
| MUT_KW
| UNSAFE_KW
| TYPE_KW
| REF_KW
| LET_KW
| MOVE_KW
| AUTO_KW
| DEFAULT_KW
| UNION_KW
=> true,
_ => false
}
}
pub(crate) fn info(self) -> &'static SyntaxInfo { pub(crate) fn info(self) -> &'static SyntaxInfo {
match self { match self {
SEMI => &SyntaxInfo { name: "SEMI" }, SEMI => &SyntaxInfo { name: "SEMI" },

View file

@ -24,6 +24,16 @@ pub enum SyntaxKind {
use self::SyntaxKind::*; use self::SyntaxKind::*;
impl SyntaxKind { impl SyntaxKind {
pub fn is_keyword(self) -> bool {
match self {
{%- for kw in concat(a=keywords, b=contextual_keywords) %}
| {{kw | upper}}_KW
{%- endfor %}
=> true,
_ => false
}
}
pub(crate) fn info(self) -> &'static SyntaxInfo { pub(crate) fn info(self) -> &'static SyntaxInfo {
match self { match self {
{%- for t in concat(a=single_byte_tokens, b=multi_byte_tokens) %} {%- for t in concat(a=single_byte_tokens, b=multi_byte_tokens) %}

View file

@ -4,20 +4,18 @@ use {
SyntaxKind, TextRange, TextUnit, SyntaxKind, TextRange, TextUnit,
}; };
pub(crate) struct GreenBuilder { pub(crate) struct GreenBuilder<'a> {
text: String, text: &'a str,
stack: Vec<GreenNodeBuilder>, stack: Vec<GreenNodeBuilder>,
pos: TextUnit, pos: TextUnit,
root: Option<GreenNode>, root: Option<GreenNode>,
errors: Vec<SyntaxError>, errors: Vec<SyntaxError>,
} }
impl GreenBuilder {} impl<'a> Sink<'a> for GreenBuilder<'a> {
impl Sink for GreenBuilder {
type Tree = SyntaxNode; type Tree = SyntaxNode;
fn new(text: String) -> Self { fn new(text: &'a str) -> Self {
GreenBuilder { GreenBuilder {
text, text,
stack: Vec::new(), stack: Vec::new(),

View file

@ -8,7 +8,7 @@ use testutils::dir_tests;
#[test] #[test]
fn parser_tests() { fn parser_tests() {
dir_tests(&["parser/inline", "parser/ok", "parser/err"], |text| { dir_tests(&["parser/inline", "parser/ok", "parser/err"], |text| {
let file = parse(text.to_string()); let file = parse(text);
dump_tree(&file) dump_tree(&file)
}) })
} }