From a450142aca947b9364e498897f522f854f19781d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 26 Aug 2018 09:12:18 +0300 Subject: [PATCH] fix stray curly --- crates/libanalysis/src/lib.rs | 6 +-- crates/libeditor/src/code_actions.rs | 18 ++----- crates/libeditor/src/completion.rs | 31 ++++++++++++ crates/libeditor/src/lib.rs | 21 +++++++-- crates/libeditor/tests/test.rs | 22 ++++++++- crates/libsyntax2/src/grammar/items/mod.rs | 7 ++- crates/libsyntax2/src/lib.rs | 4 +- crates/libsyntax2/src/parser_api.rs | 4 +- .../parser/err/0007_stray_curly_in_file.txt | 6 +-- .../data/parser/err/0015_curly_in_params.txt | 2 +- .../parser/err/0017_incomplete_binexpr.rs | 4 ++ .../parser/err/0017_incomplete_binexpr.txt | 47 +++++++++++++++++++ crates/libsyntax2/tests/test/main.rs | 1 + 13 files changed, 144 insertions(+), 29 deletions(-) create mode 100644 crates/libeditor/src/completion.rs create mode 100644 crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.rs create mode 100644 crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index c84ab6077d..a3f721cc86 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs @@ -32,7 +32,7 @@ use libsyntax2::{ ast::{self, AstNode, NameOwner}, SyntaxKind::*, }; -use libeditor::{LineIndex, FileSymbol, find_node}; +use libeditor::{LineIndex, FileSymbol, find_node_at_offset}; use self::{ symbol_index::FileSymbols, @@ -183,10 +183,10 @@ impl World { ) -> Result> { let file = self.file_syntax(id)?; let syntax = file.syntax(); - if let Some(name_ref) = find_node::(syntax, offset) { + if let Some(name_ref) = find_node_at_offset::(syntax, offset) { return Ok(self.index_resolve(name_ref)); } - if let Some(name) = find_node::(syntax, offset) { + if let Some(name) = find_node_at_offset::(syntax, offset) { if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { if module.has_semi() { let file_ids = self.resolve_module(id, module); diff --git a/crates/libeditor/src/code_actions.rs b/crates/libeditor/src/code_actions.rs index b3305be2ae..f53a8f9c65 100644 --- a/crates/libeditor/src/code_actions.rs +++ b/crates/libeditor/src/code_actions.rs @@ -3,7 +3,7 @@ use std::{ }; use libsyntax2::{ - File, + File, TextUnit, ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner}, SyntaxKind::COMMA, SyntaxNodeRef, @@ -13,7 +13,7 @@ use libsyntax2::{ }, }; -use {TextUnit, EditBuilder, Edit}; +use {EditBuilder, Edit, find_node_at_offset}; #[derive(Debug)] pub struct ActionResult { @@ -39,7 +39,7 @@ pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option(file: &'a File, offset: TextUnit) -> Option ActionResult + 'a> { - let nominal = find_node::(file.syntax(), offset)?; + let nominal = find_node_at_offset::(file.syntax(), offset)?; Some(move || { let derive_attr = nominal .attrs() @@ -66,7 +66,7 @@ pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option(file: &'a File, offset: TextUnit) -> Option ActionResult + 'a> { - let nominal = find_node::(file.syntax(), offset)?; + let nominal = find_node_at_offset::(file.syntax(), offset)?; let name = nominal.name()?; Some(move || { @@ -105,16 +105,6 @@ fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option>(syntax: SyntaxNodeRef<'a>, offset: TextUnit) -> Option { - 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) { buf.push_str(bra); let mut first = true; diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs new file mode 100644 index 0000000000..cf61ec784d --- /dev/null +++ b/crates/libeditor/src/completion.rs @@ -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> { + // 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::(file.syntax(), offset)?; + Some(complete(name_ref)) +} + +fn complete(name_ref: ast::NameRef) -> Vec { + vec![CompletionItem { + name: "foo".to_string() + }] +} diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs index 55302265f5..60489f7e3b 100644 --- a/crates/libeditor/src/lib.rs +++ b/crates/libeditor/src/lib.rs @@ -8,11 +8,12 @@ mod line_index; mod edit; mod code_actions; mod typing; +mod completion; use libsyntax2::{ - File, TextUnit, TextRange, + File, TextUnit, TextRange, SyntaxNodeRef, ast::{AstNode, NameOwner}, - algo::{walk, find_leaf_at_offset}, + algo::{walk, find_leaf_at_offset, ancestors}, SyntaxKind::{self, *}, }; pub use libsyntax2::AtomEdit; @@ -22,10 +23,11 @@ pub use self::{ symbols::{StructureNode, file_structure, FileSymbol, file_symbols}, edit::{EditBuilder, Edit}, code_actions::{ - ActionResult, find_node, + ActionResult, flip_comma, add_derive, add_impl, }, typing::join_lines, + completion::scope_completion, }; #[derive(Debug)] @@ -138,3 +140,16 @@ pub fn runnables(file: &File) -> Vec { }) .collect() } + +pub fn find_node_at_offset<'a, N: AstNode<'a>>( + syntax: SyntaxNodeRef<'a>, + offset: TextUnit, +) -> Option { + 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() +} diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs index c8882c94d2..9d59f4cdfc 100644 --- a/crates/libeditor/tests/test.rs +++ b/crates/libeditor/tests/test.rs @@ -9,7 +9,7 @@ use libeditor::{ ActionResult, highlight, runnables, extend_selection, file_structure, flip_comma, add_derive, add_impl, matching_brace, - join_lines, + join_lines, scope_completion, }; #[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 { File::parse(text) } diff --git a/crates/libsyntax2/src/grammar/items/mod.rs b/crates/libsyntax2/src/grammar/items/mod.rs index 32d0778c48..a285892df2 100644 --- a/crates/libsyntax2/src/grammar/items/mod.rs +++ b/crates/libsyntax2/src/grammar/items/mod.rs @@ -43,7 +43,12 @@ pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemF m.abandon(p); if p.at(L_CURLY) { 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"); } else { p.error("expected an item"); diff --git a/crates/libsyntax2/src/lib.rs b/crates/libsyntax2/src/lib.rs index 9ba9970c9f..93057dd6a3 100644 --- a/crates/libsyntax2/src/lib.rs +++ b/crates/libsyntax2/src/lib.rs @@ -127,12 +127,12 @@ fn validate_block_structure(root: SyntaxNodeRef) { assert_eq!( node.parent(), pair.parent(), - "unpaired curleys:\n{}", + "\nunpaired curleys:\n{}", utils::dump_tree(root), ); assert!( 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, root.text(), node.text(), diff --git a/crates/libsyntax2/src/parser_api.rs b/crates/libsyntax2/src/parser_api.rs index bb34fe9732..0a3b29b70f 100644 --- a/crates/libsyntax2/src/parser_api.rs +++ b/crates/libsyntax2/src/parser_api.rs @@ -141,7 +141,9 @@ impl<'t> Parser<'t> { pub(crate) fn err_and_bump(&mut self, message: &str) { let m = self.start(); self.error(message); - self.bump(); + if !self.at(SyntaxKind::L_CURLY) && !self.at(SyntaxKind::R_CURLY) { + self.bump(); + } m.complete(self, ERROR); } } diff --git a/crates/libsyntax2/tests/data/parser/err/0007_stray_curly_in_file.txt b/crates/libsyntax2/tests/data/parser/err/0007_stray_curly_in_file.txt index 81e82f7e20..802c69b316 100644 --- a/crates/libsyntax2/tests/data/parser/err/0007_stray_curly_in_file.txt +++ b/crates/libsyntax2/tests/data/parser/err/0007_stray_curly_in_file.txt @@ -1,7 +1,7 @@ ROOT@[0; 31) ERROR@[0; 1) R_CURLY@[0; 1) - err: `expected an item` + err: `unmatched `}`` WHITESPACE@[1; 3) STRUCT_DEF@[3; 12) STRUCT_KW@[3; 9) @@ -10,7 +10,7 @@ ROOT@[0; 31) IDENT@[10; 11) "S" SEMI@[11; 12) WHITESPACE@[12; 14) - err: `expected an item` + err: `unmatched `}`` ERROR@[14; 15) R_CURLY@[14; 15) WHITESPACE@[15; 17) @@ -26,7 +26,7 @@ ROOT@[0; 31) L_CURLY@[25; 26) R_CURLY@[26; 27) WHITESPACE@[27; 29) - err: `expected an item` + err: `unmatched `}`` ERROR@[29; 30) R_CURLY@[29; 30) WHITESPACE@[30; 31) diff --git a/crates/libsyntax2/tests/data/parser/err/0015_curly_in_params.txt b/crates/libsyntax2/tests/data/parser/err/0015_curly_in_params.txt index dbc19abea5..5f736a9784 100644 --- a/crates/libsyntax2/tests/data/parser/err/0015_curly_in_params.txt +++ b/crates/libsyntax2/tests/data/parser/err/0015_curly_in_params.txt @@ -9,7 +9,7 @@ ROOT@[0; 14) err: `expected value parameter` err: `expected R_PAREN` err: `expected a block` - err: `expected an item` + err: `unmatched `}`` ERROR@[7; 8) R_CURLY@[7; 8) err: `expected an item` diff --git a/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.rs b/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.rs new file mode 100644 index 0000000000..17bd497777 --- /dev/null +++ b/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.rs @@ -0,0 +1,4 @@ +fn foo(foo: i32) { + let bar = 92; + 1 + +} diff --git a/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt b/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt new file mode 100644 index 0000000000..db9a2f1751 --- /dev/null +++ b/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt @@ -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) diff --git a/crates/libsyntax2/tests/test/main.rs b/crates/libsyntax2/tests/test/main.rs index 596f322164..9958c7ecee 100644 --- a/crates/libsyntax2/tests/test/main.rs +++ b/crates/libsyntax2/tests/test/main.rs @@ -26,6 +26,7 @@ fn lexer_tests() { #[test] fn parser_tests() { dir_tests(&["parser/inline", "parser/ok", "parser/err"], |text| { + eprintln!("\n{}\n", text); let file = File::parse(text); dump_tree(file.syntax()) })