mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 13:48:50 +00:00
Extract libeditor
This commit is contained in:
parent
b9189ed2db
commit
966e9db2b8
15 changed files with 138 additions and 50 deletions
|
@ -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"
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,4 +16,4 @@ neon-build = "0.2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
neon = "0.2.0"
|
neon = "0.2.0"
|
||||||
libsyntax2 = { path = "../../" }
|
libeditor = { path = "../../libeditor" }
|
||||||
|
|
|
@ -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
9
libeditor/Cargo.toml
Normal 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
53
libeditor/src/lib.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>),
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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" },
|
||||||
|
|
|
@ -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) %}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue