This commit is contained in:
Aleksey Kladov 2018-08-11 12:28:59 +03:00
parent d5119133fc
commit 7afd84febc
11 changed files with 211 additions and 86 deletions

View file

@ -1,11 +1,16 @@
use libsyntax2::{
ast, AstNode,
TextRange, SyntaxNodeRef,
SyntaxKind::WHITESPACE,
algo::{find_leaf_at_offset, find_covering_node, ancestors},
};
pub fn extend_selection(file: &ast::File, range: TextRange) -> Option<TextRange> {
let syntax = file.syntax();
extend(syntax.as_ref(), range)
}
pub(crate) fn extend_selection(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> {
pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> {
if range.is_empty() {
let offset = range.start();
let mut leaves = find_leaf_at_offset(root, offset);

View file

@ -2,16 +2,21 @@ extern crate libsyntax2;
extern crate superslice;
mod extend_selection;
mod symbols;
mod line_index;
use libsyntax2::{
ast,
ast::{self, NameOwner},
SyntaxNodeRef, AstNode,
algo::walk,
SyntaxKind::*,
};
pub use libsyntax2::{File, TextRange, TextUnit};
pub use self::line_index::{LineIndex, LineCol};
pub use self::{
line_index::{LineIndex, LineCol},
extend_selection::extend_selection,
symbols::{FileSymbol, file_symbols}
};
#[derive(Debug)]
pub struct HighlightedRange {
@ -108,11 +113,6 @@ pub fn symbols(file: &ast::File) -> Vec<Symbol> {
res // NLL :-(
}
pub fn extend_selection(file: &ast::File, range: TextRange) -> Option<TextRange> {
let syntax = file.syntax();
extend_selection::extend_selection(syntax.as_ref(), range)
}
pub fn runnables(file: &ast::File) -> Vec<Runnable> {
file
.functions()

View file

@ -0,0 +1,67 @@
use libsyntax2::{
SyntaxKind, SyntaxNodeRef, SyntaxRoot, AstNode,
ast::{self, NameOwner},
algo::{
visit::{visitor, Visitor},
walk::{walk, WalkEvent},
},
};
use TextRange;
#[derive(Debug)]
pub struct FileSymbol {
pub parent: Option<usize>,
pub name: String,
pub name_range: TextRange,
pub node_range: TextRange,
pub kind: SyntaxKind,
}
pub fn file_symbols(file: &ast::File) -> Vec<FileSymbol> {
let mut res = Vec::new();
let mut stack = Vec::new();
let syntax = file.syntax();
for event in walk(syntax.as_ref()) {
match event {
WalkEvent::Enter(node) => {
match to_symbol(node) {
Some(mut symbol) => {
symbol.parent = stack.last().map(|&n| n);
stack.push(res.len());
res.push(symbol);
}
None => (),
}
}
WalkEvent::Exit(node) => {
if to_symbol(node).is_some() {
stack.pop().unwrap();
}
}
}
}
res
}
fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> {
fn decl<'a, N: NameOwner<&'a SyntaxRoot>>(node: N) -> Option<FileSymbol> {
let name = node.name()?;
Some(FileSymbol {
parent: None,
name: name.text(),
name_range: name.syntax().range(),
node_range: node.syntax().range(),
kind: node.syntax().kind(),
})
}
visitor()
.visit(decl::<ast::Function<_>>)
.visit(decl::<ast::Struct<_>>)
.visit(decl::<ast::Enum<_>>)
.visit(decl::<ast::Trait<_>>)
.visit(decl::<ast::Module<_>>)
.accept(node)?
}

View file

@ -3,7 +3,7 @@ extern crate itertools;
use std::fmt;
use itertools::Itertools;
use libeditor::{File, highlight, runnables, extend_selection, TextRange};
use libeditor::{File, highlight, runnables, extend_selection, TextRange, file_symbols};
#[test]
fn test_extend_selection() {
@ -58,6 +58,29 @@ fn test_foo() {}
)
}
#[test]
fn symbols() {
let file = file(r#"
struct Foo {
x: i32
}
mod m {
fn bar() {}
}
enum E { X, Y(i32) }
"#);
let symbols = file_symbols(&file);
dbg_eq(
&symbols,
r#"[FileSymbol { parent: None, name: "Foo", name_range: [8; 11), node_range: [1; 26), kind: STRUCT },
FileSymbol { parent: None, name: "m", name_range: [32; 33), node_range: [28; 53), kind: MODULE },
FileSymbol { parent: Some(1), name: "bar", name_range: [43; 46), node_range: [40; 51), kind: FUNCTION },
FileSymbol { parent: None, name: "E", name_range: [60; 61), node_range: [55; 75), kind: ENUM }]"#,
)
}
fn file(text: &str) -> File {
File::parse(text)
}

View file

@ -1,4 +1,5 @@
pub mod walk;
pub mod visit;
use {SyntaxNodeRef, TextUnit, TextRange};

View file

@ -0,0 +1,52 @@
use std::marker::PhantomData;
use {SyntaxNodeRef, AstNode, SyntaxRoot};
pub fn visitor<'a, T>() -> impl Visitor<'a, Output=T> {
EmptyVisitor { ph: PhantomData }
}
pub trait Visitor<'a>: Sized {
type Output;
fn accept(self, node: SyntaxNodeRef<'a>) -> Option<Self::Output>;
fn visit<N, F>(self, f: F) -> Vis<Self, N, F>
where N: AstNode<&'a SyntaxRoot>,
F: FnOnce(N) -> Self::Output,
{
Vis { inner: self, f, ph: PhantomData }
}
}
#[derive(Debug)]
struct EmptyVisitor<T> {
ph: PhantomData<fn() -> T>
}
impl<'a, T> Visitor<'a> for EmptyVisitor<T> {
type Output = T;
fn accept(self, _node: SyntaxNodeRef<'a>) -> Option<T> {
None
}
}
#[derive(Debug)]
pub struct Vis<V, N, F> {
inner: V,
f: F,
ph: PhantomData<fn(N)>,
}
impl<'a, V, N, F> Visitor<'a> for Vis<V, N, F>
where
V: Visitor<'a>,
N: AstNode<&'a SyntaxRoot>,
F: FnOnce(N) -> <V as Visitor<'a>>::Output,
{
type Output = <V as Visitor<'a>>::Output;
fn accept(self, node: SyntaxNodeRef<'a>) -> Option<Self::Output> {
let Vis { inner, f, .. } = self;
inner.accept(node).or_else(|| N::cast(node).map(f))
}
}

View file

@ -1,9 +1,11 @@
use std::sync::Arc;
use {
ast,
SyntaxNode, SyntaxRoot, TreeRoot, AstNode,
SyntaxKind::*,
};
// ConstItem
#[derive(Debug, Clone, Copy)]
pub struct ConstItem<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
@ -19,15 +21,10 @@ impl<R: TreeRoot> AstNode<R> for ConstItem<R> {
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}
impl<R: TreeRoot> ConstItem<R> {
pub fn name(&self) -> Option<Name<R>> {
self.syntax()
.children()
.filter_map(Name::cast)
.next()
}
}
impl<R: TreeRoot> ast::NameOwner<R> for ConstItem<R> {}
impl<R: TreeRoot> ConstItem<R> {}
// Enum
#[derive(Debug, Clone, Copy)]
pub struct Enum<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
@ -43,15 +40,10 @@ impl<R: TreeRoot> AstNode<R> for Enum<R> {
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}
impl<R: TreeRoot> Enum<R> {
pub fn name(&self) -> Option<Name<R>> {
self.syntax()
.children()
.filter_map(Name::cast)
.next()
}
}
impl<R: TreeRoot> ast::NameOwner<R> for Enum<R> {}
impl<R: TreeRoot> Enum<R> {}
// File
#[derive(Debug, Clone, Copy)]
pub struct File<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
@ -75,6 +67,7 @@ impl<R: TreeRoot> File<R> {
}
}
// Function
#[derive(Debug, Clone, Copy)]
pub struct Function<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
@ -90,15 +83,10 @@ impl<R: TreeRoot> AstNode<R> for Function<R> {
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}
impl<R: TreeRoot> Function<R> {
pub fn name(&self) -> Option<Name<R>> {
self.syntax()
.children()
.filter_map(Name::cast)
.next()
}
}
impl<R: TreeRoot> ast::NameOwner<R> for Function<R> {}
impl<R: TreeRoot> Function<R> {}
// Module
#[derive(Debug, Clone, Copy)]
pub struct Module<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
@ -114,15 +102,10 @@ impl<R: TreeRoot> AstNode<R> for Module<R> {
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}
impl<R: TreeRoot> Module<R> {
pub fn name(&self) -> Option<Name<R>> {
self.syntax()
.children()
.filter_map(Name::cast)
.next()
}
}
impl<R: TreeRoot> ast::NameOwner<R> for Module<R> {}
impl<R: TreeRoot> Module<R> {}
// Name
#[derive(Debug, Clone, Copy)]
pub struct Name<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
@ -140,6 +123,7 @@ impl<R: TreeRoot> AstNode<R> for Name<R> {
impl<R: TreeRoot> Name<R> {}
// StaticItem
#[derive(Debug, Clone, Copy)]
pub struct StaticItem<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
@ -155,15 +139,10 @@ impl<R: TreeRoot> AstNode<R> for StaticItem<R> {
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}
impl<R: TreeRoot> StaticItem<R> {
pub fn name(&self) -> Option<Name<R>> {
self.syntax()
.children()
.filter_map(Name::cast)
.next()
}
}
impl<R: TreeRoot> ast::NameOwner<R> for StaticItem<R> {}
impl<R: TreeRoot> StaticItem<R> {}
// Struct
#[derive(Debug, Clone, Copy)]
pub struct Struct<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
@ -179,15 +158,10 @@ impl<R: TreeRoot> AstNode<R> for Struct<R> {
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}
impl<R: TreeRoot> Struct<R> {
pub fn name(&self) -> Option<Name<R>> {
self.syntax()
.children()
.filter_map(Name::cast)
.next()
}
}
impl<R: TreeRoot> ast::NameOwner<R> for Struct<R> {}
impl<R: TreeRoot> Struct<R> {}
// Trait
#[derive(Debug, Clone, Copy)]
pub struct Trait<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
@ -203,12 +177,6 @@ impl<R: TreeRoot> AstNode<R> for Trait<R> {
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}
impl<R: TreeRoot> Trait<R> {
pub fn name(&self) -> Option<Name<R>> {
self.syntax()
.children()
.filter_map(Name::cast)
.next()
}
}
impl<R: TreeRoot> ast::NameOwner<R> for Trait<R> {}
impl<R: TreeRoot> Trait<R> {}

View file

@ -1,9 +1,11 @@
use std::sync::Arc;
use {
ast,
SyntaxNode, SyntaxRoot, TreeRoot, AstNode,
SyntaxKind::*,
};
{% for node, methods in ast %}
// {{ node }}
#[derive(Debug, Clone, Copy)]
pub struct {{ node }}<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
@ -19,6 +21,12 @@ impl<R: TreeRoot> AstNode<R> for {{ node }}<R> {
fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
}
{% if methods.traits -%}
{%- for t in methods.traits -%}
impl<R: TreeRoot> ast::{{ t }}<R> for {{ node }}<R> {}
{% endfor -%}
{%- endif -%}
impl<R: TreeRoot> {{ node }}<R> {
{%- if methods.collections -%}
{%- for m in methods.collections -%}

View file

@ -12,6 +12,15 @@ pub trait AstNode<R: TreeRoot>: Sized {
fn syntax(&self) -> &SyntaxNode<R>;
}
pub trait NameOwner<R: TreeRoot>: AstNode<R> {
fn name(&self) -> Option<Name<R>> {
self.syntax()
.children()
.filter_map(Name::cast)
.next()
}
}
impl File<Arc<SyntaxRoot>> {
pub fn parse(text: &str) -> Self {
File::cast(::parse(text)).unwrap()

View file

@ -220,39 +220,25 @@ Grammar(
]
),
"Function": (
options: [
["name", "Name"]
]
traits: ["NameOwner"]
),
"Struct": (
options: [
["name", "Name"]
]
traits: ["NameOwner"]
),
"Enum": (
options: [
["name", "Name"]
]
traits: ["NameOwner"]
),
"Trait": (
options: [
["name", "Name"]
]
traits: ["NameOwner"]
),
"Module": (
options: [
["name", "Name"]
]
traits: ["NameOwner"]
),
"ConstItem": (
options: [
["name", "Name"]
]
traits: ["NameOwner"]
),
"StaticItem": (
options: [
["name", "Name"]
]
traits: ["NameOwner"]
),
"Name": (),
},

View file

@ -28,6 +28,12 @@ impl<R: TreeRoot> Eq for SyntaxNode<R> {}
pub type SyntaxNodeRef<'a> = SyntaxNode<&'a SyntaxRoot>;
#[test]
fn syntax_node_ref_is_copy() {
fn assert_copy<T: Copy>(){}
assert_copy::<SyntaxNodeRef>()
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct SyntaxError {
pub msg: String,