Extract runnables

This commit is contained in:
Aleksey Kladov 2018-08-09 16:03:21 +03:00
parent e847a1b7a6
commit 36bd28633b
3 changed files with 130 additions and 1 deletions

View File

@ -26,6 +26,18 @@ pub struct Symbol {
pub range: TextRange,
}
#[derive(Debug)]
pub struct Runnable {
pub range: TextRange,
pub kind: RunnableKind,
}
#[derive(Debug)]
pub enum RunnableKind {
Test { name: String },
Bin,
}
impl File {
pub fn new(text: &str) -> File {
File {
@ -78,6 +90,28 @@ impl File {
let syntax = self.inner.syntax();
extend_selection::extend_selection(syntax.as_ref(), range)
}
pub fn runnables(&self) -> Vec<Runnable> {
self.inner
.functions()
.filter_map(|f| {
let name = f.name()?.text();
let kind = if name == "main" {
RunnableKind::Bin
} else if f.has_atom_attr("test") {
RunnableKind::Test {
name: name.to_string()
}
} else {
return None;
};
Some(Runnable {
range: f.syntax().range(),
kind,
})
})
.collect()
}
}

View File

@ -37,11 +37,33 @@ fn main() {}
);
}
#[test]
fn test_runnables() {
let file = file(r#"
fn main() {}
#[test]
fn test_foo() {}
#[test]
#[ignore]
fn test_foo() {}
"#);
let runnables = file.runnables();
dbg_eq(
&runnables,
r#"[Runnable { range: [1; 13), kind: Bin },
Runnable { range: [15; 39), kind: Test { name: "test_foo" } },
Runnable { range: [41; 75), kind: Test { name: "test_foo" } }]"#,
)
}
fn file(text: &str) -> File {
File::new(text)
}
fn dbg_eq(actual: &impl fmt::Debug, expected: &str) {
let actual = format!("{:?}", actual);
let expected = expected.lines().map(|l| l.trim()).join(" ");
assert_eq!(actual, expected);
}

View File

@ -1,11 +1,25 @@
use std::sync::Arc;
use {SyntaxNode, SyntaxRoot, TreeRoot};
use {
SyntaxNode, SyntaxRoot, TreeRoot,
SyntaxKind::*,
};
#[derive(Debug)]
pub struct File<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
}
#[derive(Debug)]
pub struct Function<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
}
#[derive(Debug)]
pub struct Name<R: TreeRoot = Arc<SyntaxRoot>> {
syntax: SyntaxNode<R>,
}
impl File<Arc<SyntaxRoot>> {
pub fn parse(text: &str) -> Self {
File {
@ -14,6 +28,65 @@ impl File<Arc<SyntaxRoot>> {
}
}
impl<R: TreeRoot> File<R> {
pub fn functions<'a>(&'a self) -> impl Iterator<Item = Function<R>> + 'a {
self.syntax
.children()
.filter(|node| node.kind() == FN_ITEM)
.map(|node| Function { syntax: node })
}
}
impl<R: TreeRoot> Function<R> {
pub fn syntax(&self) -> SyntaxNode<R> {
self.syntax.clone()
}
pub fn name(&self) -> Option<Name<R>> {
self.syntax
.children()
.filter(|node| node.kind() == NAME)
.map(|node| Name { syntax: node })
.next()
}
pub fn has_atom_attr(&self, atom: &str) -> bool {
self.syntax
.children()
.filter(|node| node.kind() == ATTR)
.any(|attr| {
let mut metas = attr.children().filter(|node| node.kind() == META_ITEM);
let meta = match metas.next() {
None => return false,
Some(meta) => {
if metas.next().is_some() {
return false;
}
meta
}
};
let mut children = meta.children();
match children.next() {
None => false,
Some(child) => {
if children.next().is_some() {
return false;
}
child.kind() == IDENT && child.text() == atom
}
}
})
}
}
impl<R: TreeRoot> Name<R> {
pub fn text(&self) -> String {
self.syntax.text()
}
}
impl<R: TreeRoot> File<R> {
pub fn syntax(&self) -> SyntaxNode<R> {
self.syntax.clone()