aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/libanalysis/src/lib.rs6
-rw-r--r--crates/libeditor/src/code_actions.rs18
-rw-r--r--crates/libeditor/src/completion.rs31
-rw-r--r--crates/libeditor/src/lib.rs21
-rw-r--r--crates/libeditor/tests/test.rs22
-rw-r--r--crates/libsyntax2/src/grammar/items/mod.rs7
-rw-r--r--crates/libsyntax2/src/lib.rs4
-rw-r--r--crates/libsyntax2/src/parser_api.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0007_stray_curly_in_file.txt6
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0015_curly_in_params.txt2
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.rs4
-rw-r--r--crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt47
-rw-r--r--crates/libsyntax2/tests/test/main.rs1
13 files changed, 144 insertions, 29 deletions
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs
index c84ab6077..a3f721cc8 100644
--- a/crates/libanalysis/src/lib.rs
+++ b/crates/libanalysis/src/lib.rs
@@ -32,7 +32,7 @@ use libsyntax2::{
32 ast::{self, AstNode, NameOwner}, 32 ast::{self, AstNode, NameOwner},
33 SyntaxKind::*, 33 SyntaxKind::*,
34}; 34};
35use libeditor::{LineIndex, FileSymbol, find_node}; 35use libeditor::{LineIndex, FileSymbol, find_node_at_offset};
36 36
37use self::{ 37use self::{
38 symbol_index::FileSymbols, 38 symbol_index::FileSymbols,
@@ -183,10 +183,10 @@ impl World {
183 ) -> Result<Vec<(FileId, FileSymbol)>> { 183 ) -> Result<Vec<(FileId, FileSymbol)>> {
184 let file = self.file_syntax(id)?; 184 let file = self.file_syntax(id)?;
185 let syntax = file.syntax(); 185 let syntax = file.syntax();
186 if let Some(name_ref) = find_node::<ast::NameRef>(syntax, offset) { 186 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) {
187 return Ok(self.index_resolve(name_ref)); 187 return Ok(self.index_resolve(name_ref));
188 } 188 }
189 if let Some(name) = find_node::<ast::Name>(syntax, offset) { 189 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, offset) {
190 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { 190 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
191 if module.has_semi() { 191 if module.has_semi() {
192 let file_ids = self.resolve_module(id, module); 192 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 b3305be2a..f53a8f9c6 100644
--- a/crates/libeditor/src/code_actions.rs
+++ b/crates/libeditor/src/code_actions.rs
@@ -3,7 +3,7 @@ use std::{
3}; 3};
4 4
5use libsyntax2::{ 5use libsyntax2::{
6 File, 6 File, TextUnit,
7 ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner}, 7 ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner},
8 SyntaxKind::COMMA, 8 SyntaxKind::COMMA,
9 SyntaxNodeRef, 9 SyntaxNodeRef,
@@ -13,7 +13,7 @@ use libsyntax2::{
13 }, 13 },
14}; 14};
15 15
16use {TextUnit, EditBuilder, Edit}; 16use {EditBuilder, Edit, find_node_at_offset};
17 17
18#[derive(Debug)] 18#[derive(Debug)]
19pub struct ActionResult { 19pub struct ActionResult {
@@ -39,7 +39,7 @@ pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce()
39} 39}
40 40
41pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> ActionResult + 'a> { 41pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> ActionResult + 'a> {
42 let nominal = find_node::<ast::NominalDef>(file.syntax(), offset)?; 42 let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
43 Some(move || { 43 Some(move || {
44 let derive_attr = nominal 44 let derive_attr = nominal
45 .attrs() 45 .attrs()
@@ -66,7 +66,7 @@ pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce()
66} 66}
67 67
68pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> ActionResult + 'a> { 68pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> ActionResult + 'a> {
69 let nominal = find_node::<ast::NominalDef>(file.syntax(), offset)?; 69 let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
70 let name = nominal.name()?; 70 let name = nominal.name()?;
71 71
72 Some(move || { 72 Some(move || {
@@ -105,16 +105,6 @@ fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<Synta
105 .find(|node| !node.kind().is_trivia()) 105 .find(|node| !node.kind().is_trivia())
106} 106}
107 107
108pub fn find_node<'a, N: AstNode<'a>>(syntax: SyntaxNodeRef<'a>, offset: TextUnit) -> Option<N> {
109 let leaves = find_leaf_at_offset(syntax, offset);
110 let leaf = leaves.clone()
111 .find(|leaf| !leaf.kind().is_trivia())
112 .or_else(|| leaves.right_biased())?;
113 ancestors(leaf)
114 .filter_map(N::cast)
115 .next()
116}
117
118fn comma_list(buf: &mut String, bra: &str, ket: &str, items: impl Iterator<Item=impl fmt::Display>) { 108fn comma_list(buf: &mut String, bra: &str, ket: &str, items: impl Iterator<Item=impl fmt::Display>) {
119 buf.push_str(bra); 109 buf.push_str(bra);
120 let mut first = true; 110 let mut first = true;
diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs
new file mode 100644
index 000000000..cf61ec784
--- /dev/null
+++ b/crates/libeditor/src/completion.rs
@@ -0,0 +1,31 @@
1use libsyntax2::{
2 File, TextUnit,
3 ast,
4 algo::find_leaf_at_offset,
5};
6
7use {
8 AtomEdit, find_node_at_offset,
9};
10
11#[derive(Debug)]
12pub struct CompletionItem {
13 name: String,
14}
15
16pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionItem>> {
17 // Insert a fake ident to get a valid parse tree
18 let file = {
19 let edit = AtomEdit::insert(offset, "intellijRulezz".to_string());
20 // Don't bother with completion if incremental reparse fails
21 file.incremental_reparse(&edit)?
22 };
23 let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), offset)?;
24 Some(complete(name_ref))
25}
26
27fn complete(name_ref: ast::NameRef) -> Vec<CompletionItem> {
28 vec![CompletionItem {
29 name: "foo".to_string()
30 }]
31}
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs
index 55302265f..60489f7e3 100644
--- a/crates/libeditor/src/lib.rs
+++ b/crates/libeditor/src/lib.rs
@@ -8,11 +8,12 @@ mod line_index;
8mod edit; 8mod edit;
9mod code_actions; 9mod code_actions;
10mod typing; 10mod typing;
11mod completion;
11 12
12use libsyntax2::{ 13use libsyntax2::{
13 File, TextUnit, TextRange, 14 File, TextUnit, TextRange, SyntaxNodeRef,
14 ast::{AstNode, NameOwner}, 15 ast::{AstNode, NameOwner},
15 algo::{walk, find_leaf_at_offset}, 16 algo::{walk, find_leaf_at_offset, ancestors},
16 SyntaxKind::{self, *}, 17 SyntaxKind::{self, *},
17}; 18};
18pub use libsyntax2::AtomEdit; 19pub use libsyntax2::AtomEdit;
@@ -22,10 +23,11 @@ pub use self::{
22 symbols::{StructureNode, file_structure, FileSymbol, file_symbols}, 23 symbols::{StructureNode, file_structure, FileSymbol, file_symbols},
23 edit::{EditBuilder, Edit}, 24 edit::{EditBuilder, Edit},
24 code_actions::{ 25 code_actions::{
25 ActionResult, find_node, 26 ActionResult,
26 flip_comma, add_derive, add_impl, 27 flip_comma, add_derive, add_impl,
27 }, 28 },
28 typing::join_lines, 29 typing::join_lines,
30 completion::scope_completion,
29}; 31};
30 32
31#[derive(Debug)] 33#[derive(Debug)]
@@ -138,3 +140,16 @@ pub fn runnables(file: &File) -> Vec<Runnable> {
138 }) 140 })
139 .collect() 141 .collect()
140} 142}
143
144pub fn find_node_at_offset<'a, N: AstNode<'a>>(
145 syntax: SyntaxNodeRef<'a>,
146 offset: TextUnit,
147) -> Option<N> {
148 let leaves = find_leaf_at_offset(syntax, offset);
149 let leaf = leaves.clone()
150 .find(|leaf| !leaf.kind().is_trivia())
151 .or_else(|| leaves.right_biased())?;
152 ancestors(leaf)
153 .filter_map(N::cast)
154 .next()
155}
diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs
index c8882c94d..9d59f4cdf 100644
--- a/crates/libeditor/tests/test.rs
+++ b/crates/libeditor/tests/test.rs
@@ -9,7 +9,7 @@ use libeditor::{
9 ActionResult, 9 ActionResult,
10 highlight, runnables, extend_selection, file_structure, 10 highlight, runnables, extend_selection, file_structure,
11 flip_comma, add_derive, add_impl, matching_brace, 11 flip_comma, add_derive, add_impl, matching_brace,
12 join_lines, 12 join_lines, scope_completion,
13}; 13};
14 14
15#[test] 15#[test]
@@ -244,6 +244,26 @@ struct Foo { f: u32 }
244"); 244");
245} 245}
246 246
247// #[test]
248// fn test_completion() {
249// fn do_check(code: &str, expected_completions: &str) {
250// let (off, code) = extract_offset(&code);
251// let file = file(&code);
252// let completions = scope_completion(&file, off).unwrap();
253// assert_eq_dbg(expected_completions, &completions);
254// }
255
256// do_check(r"
257// fn foo(foo: i32) {
258// let bar = 92;
259// 1 + <|>
260// }
261// ", r#"
262// CompletionItem { name: "bar" },
263// CompletionItem { name: "foo" },
264// "#);
265// }
266
247fn file(text: &str) -> File { 267fn file(text: &str) -> File {
248 File::parse(text) 268 File::parse(text)
249} 269}
diff --git a/crates/libsyntax2/src/grammar/items/mod.rs b/crates/libsyntax2/src/grammar/items/mod.rs
index 32d0778c4..a285892df 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
43 m.abandon(p); 43 m.abandon(p);
44 if p.at(L_CURLY) { 44 if p.at(L_CURLY) {
45 error_block(p, "expected an item"); 45 error_block(p, "expected an item");
46 } else if !p.at(EOF) && !(stop_on_r_curly && p.at(R_CURLY)) { 46 } else if p.at(R_CURLY) && !stop_on_r_curly {
47 let e = p.start();
48 p.error("unmatched `}`");
49 p.bump();
50 e.complete(p, ERROR);
51 } else if !p.at(EOF) && !p.at(R_CURLY) {
47 p.err_and_bump("expected an item"); 52 p.err_and_bump("expected an item");
48 } else { 53 } else {
49 p.error("expected an item"); 54 p.error("expected an item");
diff --git a/crates/libsyntax2/src/lib.rs b/crates/libsyntax2/src/lib.rs
index 9ba9970c9..93057dd6a 100644
--- a/crates/libsyntax2/src/lib.rs
+++ b/crates/libsyntax2/src/lib.rs
@@ -127,12 +127,12 @@ fn validate_block_structure(root: SyntaxNodeRef) {
127 assert_eq!( 127 assert_eq!(
128 node.parent(), 128 node.parent(),
129 pair.parent(), 129 pair.parent(),
130 "unpaired curleys:\n{}", 130 "\nunpaired curleys:\n{}",
131 utils::dump_tree(root), 131 utils::dump_tree(root),
132 ); 132 );
133 assert!( 133 assert!(
134 node.next_sibling().is_none() && pair.prev_sibling().is_none(), 134 node.next_sibling().is_none() && pair.prev_sibling().is_none(),
135 "floating curlys at {:?}\nfile:\n{}\nerror:\n{}\n", 135 "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
136 node, 136 node,
137 root.text(), 137 root.text(),
138 node.text(), 138 node.text(),
diff --git a/crates/libsyntax2/src/parser_api.rs b/crates/libsyntax2/src/parser_api.rs
index bb34fe973..0a3b29b70 100644
--- a/crates/libsyntax2/src/parser_api.rs
+++ b/crates/libsyntax2/src/parser_api.rs
@@ -141,7 +141,9 @@ impl<'t> Parser<'t> {
141 pub(crate) fn err_and_bump(&mut self, message: &str) { 141 pub(crate) fn err_and_bump(&mut self, message: &str) {
142 let m = self.start(); 142 let m = self.start();
143 self.error(message); 143 self.error(message);
144 self.bump(); 144 if !self.at(SyntaxKind::L_CURLY) && !self.at(SyntaxKind::R_CURLY) {
145 self.bump();
146 }
145 m.complete(self, ERROR); 147 m.complete(self, ERROR);
146 } 148 }
147} 149}
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 81e82f7e2..802c69b31 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 @@
1ROOT@[0; 31) 1ROOT@[0; 31)
2 ERROR@[0; 1) 2 ERROR@[0; 1)
3 R_CURLY@[0; 1) 3 R_CURLY@[0; 1)
4 err: `expected an item` 4 err: `unmatched `}``
5 WHITESPACE@[1; 3) 5 WHITESPACE@[1; 3)
6 STRUCT_DEF@[3; 12) 6 STRUCT_DEF@[3; 12)
7 STRUCT_KW@[3; 9) 7 STRUCT_KW@[3; 9)
@@ -10,7 +10,7 @@ ROOT@[0; 31)
10 IDENT@[10; 11) "S" 10 IDENT@[10; 11) "S"
11 SEMI@[11; 12) 11 SEMI@[11; 12)
12 WHITESPACE@[12; 14) 12 WHITESPACE@[12; 14)
13 err: `expected an item` 13 err: `unmatched `}``
14 ERROR@[14; 15) 14 ERROR@[14; 15)
15 R_CURLY@[14; 15) 15 R_CURLY@[14; 15)
16 WHITESPACE@[15; 17) 16 WHITESPACE@[15; 17)
@@ -26,7 +26,7 @@ ROOT@[0; 31)
26 L_CURLY@[25; 26) 26 L_CURLY@[25; 26)
27 R_CURLY@[26; 27) 27 R_CURLY@[26; 27)
28 WHITESPACE@[27; 29) 28 WHITESPACE@[27; 29)
29 err: `expected an item` 29 err: `unmatched `}``
30 ERROR@[29; 30) 30 ERROR@[29; 30)
31 R_CURLY@[29; 30) 31 R_CURLY@[29; 30)
32 WHITESPACE@[30; 31) 32 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 dbc19abea..5f736a978 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)
9 err: `expected value parameter` 9 err: `expected value parameter`
10 err: `expected R_PAREN` 10 err: `expected R_PAREN`
11 err: `expected a block` 11 err: `expected a block`
12 err: `expected an item` 12 err: `unmatched `}``
13 ERROR@[7; 8) 13 ERROR@[7; 8)
14 R_CURLY@[7; 8) 14 R_CURLY@[7; 8)
15 err: `expected an item` 15 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 000000000..17bd49777
--- /dev/null
+++ b/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.rs
@@ -0,0 +1,4 @@
1fn foo(foo: i32) {
2 let bar = 92;
3 1 +
4}
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 000000000..db9a2f175
--- /dev/null
+++ b/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt
@@ -0,0 +1,47 @@
1ROOT@[0; 47)
2 FN_DEF@[0; 46)
3 FN_KW@[0; 2)
4 WHITESPACE@[2; 3)
5 NAME@[3; 6)
6 IDENT@[3; 6) "foo"
7 PARAM_LIST@[6; 16)
8 L_PAREN@[6; 7)
9 PARAM@[7; 15)
10 BIND_PAT@[7; 10)
11 NAME@[7; 10)
12 IDENT@[7; 10) "foo"
13 COLON@[10; 11)
14 WHITESPACE@[11; 12)
15 PATH_TYPE@[12; 15)
16 PATH@[12; 15)
17 PATH_SEGMENT@[12; 15)
18 NAME_REF@[12; 15)
19 IDENT@[12; 15) "i32"
20 R_PAREN@[15; 16)
21 WHITESPACE@[16; 17)
22 BLOCK@[17; 46)
23 L_CURLY@[17; 18)
24 WHITESPACE@[18; 23)
25 LET_STMT@[23; 36)
26 LET_KW@[23; 26)
27 WHITESPACE@[26; 27)
28 BIND_PAT@[27; 30)
29 NAME@[27; 30)
30 IDENT@[27; 30) "bar"
31 WHITESPACE@[30; 31)
32 EQ@[31; 32)
33 WHITESPACE@[32; 33)
34 LITERAL@[33; 35)
35 INT_NUMBER@[33; 35) "92"
36 SEMI@[35; 36)
37 WHITESPACE@[36; 41)
38 BIN_EXPR@[41; 45)
39 LITERAL@[41; 42)
40 INT_NUMBER@[41; 42) "1"
41 WHITESPACE@[42; 43)
42 PLUS@[43; 44)
43 WHITESPACE@[44; 45)
44 err: `expected expression`
45 ERROR@[45; 45)
46 R_CURLY@[45; 46)
47 WHITESPACE@[46; 47)
diff --git a/crates/libsyntax2/tests/test/main.rs b/crates/libsyntax2/tests/test/main.rs
index 596f32216..9958c7ece 100644
--- a/crates/libsyntax2/tests/test/main.rs
+++ b/crates/libsyntax2/tests/test/main.rs
@@ -26,6 +26,7 @@ fn lexer_tests() {
26#[test] 26#[test]
27fn parser_tests() { 27fn parser_tests() {
28 dir_tests(&["parser/inline", "parser/ok", "parser/err"], |text| { 28 dir_tests(&["parser/inline", "parser/ok", "parser/err"], |text| {
29 eprintln!("\n{}\n", text);
29 let file = File::parse(text); 30 let file = File::parse(text);
30 dump_tree(file.syntax()) 31 dump_tree(file.syntax())
31 }) 32 })