fix stray curly

This commit is contained in:
Aleksey Kladov 2018-08-26 09:12:18 +03:00
parent a48964c64d
commit a450142aca
13 changed files with 144 additions and 29 deletions

View file

@ -32,7 +32,7 @@ use libsyntax2::{
ast::{self, AstNode, NameOwner}, ast::{self, AstNode, NameOwner},
SyntaxKind::*, SyntaxKind::*,
}; };
use libeditor::{LineIndex, FileSymbol, find_node}; use libeditor::{LineIndex, FileSymbol, find_node_at_offset};
use self::{ use self::{
symbol_index::FileSymbols, symbol_index::FileSymbols,
@ -183,10 +183,10 @@ impl World {
) -> Result<Vec<(FileId, FileSymbol)>> { ) -> Result<Vec<(FileId, FileSymbol)>> {
let file = self.file_syntax(id)?; let file = self.file_syntax(id)?;
let syntax = file.syntax(); let syntax = file.syntax();
if let Some(name_ref) = find_node::<ast::NameRef>(syntax, offset) { if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) {
return Ok(self.index_resolve(name_ref)); return Ok(self.index_resolve(name_ref));
} }
if let Some(name) = find_node::<ast::Name>(syntax, offset) { if let Some(name) = find_node_at_offset::<ast::Name>(syntax, offset) {
if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
if module.has_semi() { if module.has_semi() {
let file_ids = self.resolve_module(id, module); let file_ids = self.resolve_module(id, module);

View file

@ -3,7 +3,7 @@ use std::{
}; };
use libsyntax2::{ use libsyntax2::{
File, File, TextUnit,
ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner}, ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner},
SyntaxKind::COMMA, SyntaxKind::COMMA,
SyntaxNodeRef, SyntaxNodeRef,
@ -13,7 +13,7 @@ use libsyntax2::{
}, },
}; };
use {TextUnit, EditBuilder, Edit}; use {EditBuilder, Edit, find_node_at_offset};
#[derive(Debug)] #[derive(Debug)]
pub struct ActionResult { pub struct ActionResult {
@ -39,7 +39,7 @@ pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce()
} }
pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> ActionResult + 'a> { pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> ActionResult + 'a> {
let nominal = find_node::<ast::NominalDef>(file.syntax(), offset)?; let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
Some(move || { Some(move || {
let derive_attr = nominal let derive_attr = nominal
.attrs() .attrs()
@ -66,7 +66,7 @@ pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce()
} }
pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> ActionResult + 'a> { pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> ActionResult + 'a> {
let nominal = find_node::<ast::NominalDef>(file.syntax(), offset)?; let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
let name = nominal.name()?; let name = nominal.name()?;
Some(move || { Some(move || {
@ -105,16 +105,6 @@ fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<Synta
.find(|node| !node.kind().is_trivia()) .find(|node| !node.kind().is_trivia())
} }
pub fn find_node<'a, N: AstNode<'a>>(syntax: SyntaxNodeRef<'a>, offset: TextUnit) -> Option<N> {
let leaves = find_leaf_at_offset(syntax, offset);
let leaf = leaves.clone()
.find(|leaf| !leaf.kind().is_trivia())
.or_else(|| leaves.right_biased())?;
ancestors(leaf)
.filter_map(N::cast)
.next()
}
fn comma_list(buf: &mut String, bra: &str, ket: &str, items: impl Iterator<Item=impl fmt::Display>) { fn comma_list(buf: &mut String, bra: &str, ket: &str, items: impl Iterator<Item=impl fmt::Display>) {
buf.push_str(bra); buf.push_str(bra);
let mut first = true; let mut first = true;

View file

@ -0,0 +1,31 @@
use libsyntax2::{
File, TextUnit,
ast,
algo::find_leaf_at_offset,
};
use {
AtomEdit, find_node_at_offset,
};
#[derive(Debug)]
pub struct CompletionItem {
name: String,
}
pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionItem>> {
// Insert a fake ident to get a valid parse tree
let file = {
let edit = AtomEdit::insert(offset, "intellijRulezz".to_string());
// Don't bother with completion if incremental reparse fails
file.incremental_reparse(&edit)?
};
let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), offset)?;
Some(complete(name_ref))
}
fn complete(name_ref: ast::NameRef) -> Vec<CompletionItem> {
vec![CompletionItem {
name: "foo".to_string()
}]
}

View file

@ -8,11 +8,12 @@ mod line_index;
mod edit; mod edit;
mod code_actions; mod code_actions;
mod typing; mod typing;
mod completion;
use libsyntax2::{ use libsyntax2::{
File, TextUnit, TextRange, File, TextUnit, TextRange, SyntaxNodeRef,
ast::{AstNode, NameOwner}, ast::{AstNode, NameOwner},
algo::{walk, find_leaf_at_offset}, algo::{walk, find_leaf_at_offset, ancestors},
SyntaxKind::{self, *}, SyntaxKind::{self, *},
}; };
pub use libsyntax2::AtomEdit; pub use libsyntax2::AtomEdit;
@ -22,10 +23,11 @@ pub use self::{
symbols::{StructureNode, file_structure, FileSymbol, file_symbols}, symbols::{StructureNode, file_structure, FileSymbol, file_symbols},
edit::{EditBuilder, Edit}, edit::{EditBuilder, Edit},
code_actions::{ code_actions::{
ActionResult, find_node, ActionResult,
flip_comma, add_derive, add_impl, flip_comma, add_derive, add_impl,
}, },
typing::join_lines, typing::join_lines,
completion::scope_completion,
}; };
#[derive(Debug)] #[derive(Debug)]
@ -138,3 +140,16 @@ pub fn runnables(file: &File) -> Vec<Runnable> {
}) })
.collect() .collect()
} }
pub fn find_node_at_offset<'a, N: AstNode<'a>>(
syntax: SyntaxNodeRef<'a>,
offset: TextUnit,
) -> Option<N> {
let leaves = find_leaf_at_offset(syntax, offset);
let leaf = leaves.clone()
.find(|leaf| !leaf.kind().is_trivia())
.or_else(|| leaves.right_biased())?;
ancestors(leaf)
.filter_map(N::cast)
.next()
}

View file

@ -9,7 +9,7 @@ use libeditor::{
ActionResult, ActionResult,
highlight, runnables, extend_selection, file_structure, highlight, runnables, extend_selection, file_structure,
flip_comma, add_derive, add_impl, matching_brace, flip_comma, add_derive, add_impl, matching_brace,
join_lines, join_lines, scope_completion,
}; };
#[test] #[test]
@ -244,6 +244,26 @@ struct Foo { f: u32 }
"); ");
} }
// #[test]
// fn test_completion() {
// fn do_check(code: &str, expected_completions: &str) {
// let (off, code) = extract_offset(&code);
// let file = file(&code);
// let completions = scope_completion(&file, off).unwrap();
// assert_eq_dbg(expected_completions, &completions);
// }
// do_check(r"
// fn foo(foo: i32) {
// let bar = 92;
// 1 + <|>
// }
// ", r#"
// CompletionItem { name: "bar" },
// CompletionItem { name: "foo" },
// "#);
// }
fn file(text: &str) -> File { fn file(text: &str) -> File {
File::parse(text) File::parse(text)
} }

View file

@ -43,7 +43,12 @@ pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemF
m.abandon(p); m.abandon(p);
if p.at(L_CURLY) { if p.at(L_CURLY) {
error_block(p, "expected an item"); error_block(p, "expected an item");
} else if !p.at(EOF) && !(stop_on_r_curly && p.at(R_CURLY)) { } else if p.at(R_CURLY) && !stop_on_r_curly {
let e = p.start();
p.error("unmatched `}`");
p.bump();
e.complete(p, ERROR);
} else if !p.at(EOF) && !p.at(R_CURLY) {
p.err_and_bump("expected an item"); p.err_and_bump("expected an item");
} else { } else {
p.error("expected an item"); p.error("expected an item");

View file

@ -127,12 +127,12 @@ fn validate_block_structure(root: SyntaxNodeRef) {
assert_eq!( assert_eq!(
node.parent(), node.parent(),
pair.parent(), pair.parent(),
"unpaired curleys:\n{}", "\nunpaired curleys:\n{}",
utils::dump_tree(root), utils::dump_tree(root),
); );
assert!( assert!(
node.next_sibling().is_none() && pair.prev_sibling().is_none(), node.next_sibling().is_none() && pair.prev_sibling().is_none(),
"floating curlys at {:?}\nfile:\n{}\nerror:\n{}\n", "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
node, node,
root.text(), root.text(),
node.text(), node.text(),

View file

@ -141,7 +141,9 @@ impl<'t> Parser<'t> {
pub(crate) fn err_and_bump(&mut self, message: &str) { pub(crate) fn err_and_bump(&mut self, message: &str) {
let m = self.start(); let m = self.start();
self.error(message); self.error(message);
self.bump(); if !self.at(SyntaxKind::L_CURLY) && !self.at(SyntaxKind::R_CURLY) {
self.bump();
}
m.complete(self, ERROR); m.complete(self, ERROR);
} }
} }

View file

@ -1,7 +1,7 @@
ROOT@[0; 31) ROOT@[0; 31)
ERROR@[0; 1) ERROR@[0; 1)
R_CURLY@[0; 1) R_CURLY@[0; 1)
err: `expected an item` err: `unmatched `}``
WHITESPACE@[1; 3) WHITESPACE@[1; 3)
STRUCT_DEF@[3; 12) STRUCT_DEF@[3; 12)
STRUCT_KW@[3; 9) STRUCT_KW@[3; 9)
@ -10,7 +10,7 @@ ROOT@[0; 31)
IDENT@[10; 11) "S" IDENT@[10; 11) "S"
SEMI@[11; 12) SEMI@[11; 12)
WHITESPACE@[12; 14) WHITESPACE@[12; 14)
err: `expected an item` err: `unmatched `}``
ERROR@[14; 15) ERROR@[14; 15)
R_CURLY@[14; 15) R_CURLY@[14; 15)
WHITESPACE@[15; 17) WHITESPACE@[15; 17)
@ -26,7 +26,7 @@ ROOT@[0; 31)
L_CURLY@[25; 26) L_CURLY@[25; 26)
R_CURLY@[26; 27) R_CURLY@[26; 27)
WHITESPACE@[27; 29) WHITESPACE@[27; 29)
err: `expected an item` err: `unmatched `}``
ERROR@[29; 30) ERROR@[29; 30)
R_CURLY@[29; 30) R_CURLY@[29; 30)
WHITESPACE@[30; 31) WHITESPACE@[30; 31)

View file

@ -9,7 +9,7 @@ ROOT@[0; 14)
err: `expected value parameter` err: `expected value parameter`
err: `expected R_PAREN` err: `expected R_PAREN`
err: `expected a block` err: `expected a block`
err: `expected an item` err: `unmatched `}``
ERROR@[7; 8) ERROR@[7; 8)
R_CURLY@[7; 8) R_CURLY@[7; 8)
err: `expected an item` err: `expected an item`

View file

@ -0,0 +1,4 @@
fn foo(foo: i32) {
let bar = 92;
1 +
}

View file

@ -0,0 +1,47 @@
ROOT@[0; 47)
FN_DEF@[0; 46)
FN_KW@[0; 2)
WHITESPACE@[2; 3)
NAME@[3; 6)
IDENT@[3; 6) "foo"
PARAM_LIST@[6; 16)
L_PAREN@[6; 7)
PARAM@[7; 15)
BIND_PAT@[7; 10)
NAME@[7; 10)
IDENT@[7; 10) "foo"
COLON@[10; 11)
WHITESPACE@[11; 12)
PATH_TYPE@[12; 15)
PATH@[12; 15)
PATH_SEGMENT@[12; 15)
NAME_REF@[12; 15)
IDENT@[12; 15) "i32"
R_PAREN@[15; 16)
WHITESPACE@[16; 17)
BLOCK@[17; 46)
L_CURLY@[17; 18)
WHITESPACE@[18; 23)
LET_STMT@[23; 36)
LET_KW@[23; 26)
WHITESPACE@[26; 27)
BIND_PAT@[27; 30)
NAME@[27; 30)
IDENT@[27; 30) "bar"
WHITESPACE@[30; 31)
EQ@[31; 32)
WHITESPACE@[32; 33)
LITERAL@[33; 35)
INT_NUMBER@[33; 35) "92"
SEMI@[35; 36)
WHITESPACE@[36; 41)
BIN_EXPR@[41; 45)
LITERAL@[41; 42)
INT_NUMBER@[41; 42) "1"
WHITESPACE@[42; 43)
PLUS@[43; 44)
WHITESPACE@[44; 45)
err: `expected expression`
ERROR@[45; 45)
R_CURLY@[45; 46)
WHITESPACE@[46; 47)

View file

@ -26,6 +26,7 @@ fn lexer_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| {
eprintln!("\n{}\n", text);
let file = File::parse(text); let file = File::parse(text);
dump_tree(file.syntax()) dump_tree(file.syntax())
}) })