aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-08-27 18:58:38 +0100
committerAleksey Kladov <[email protected]>2018-08-27 18:58:38 +0100
commitaaca7d003bd969785be53d8f312b67bfa26f6272 (patch)
tree9e3ab4d75d631fb1055fb6ab5c05f0a78153fadd
parent846114a6e95da696deb6a0f3243ad23c45074a00 (diff)
move scopes to file
-rw-r--r--code/package.json4
-rw-r--r--code/src/extension.ts12
-rw-r--r--crates/libanalysis/src/lib.rs36
-rw-r--r--crates/libanalysis/src/module_map.rs5
-rw-r--r--crates/libanalysis/tests/tests.rs13
-rw-r--r--crates/libeditor/scope.rs0
-rw-r--r--crates/libeditor/src/completion.rs187
-rw-r--r--crates/libeditor/src/lib.rs1
-rw-r--r--crates/libeditor/src/scope.rs183
-rw-r--r--crates/libsyntax2/src/grammar/expressions/atom.rs4
-rw-r--r--crates/server/src/main_loop/handlers.rs31
11 files changed, 280 insertions, 196 deletions
diff --git a/code/package.json b/code/package.json
index 00604efcc..282e533af 100644
--- a/code/package.json
+++ b/code/package.json
@@ -52,6 +52,10 @@
52 ], 52 ],
53 "commands": [ 53 "commands": [
54 { 54 {
55 "command": "libsyntax-rust.createFile",
56 "title": "Show Rust syntax tree"
57 },
58 {
55 "command": "libsyntax-rust.syntaxTree", 59 "command": "libsyntax-rust.syntaxTree",
56 "title": "Show Rust syntax tree" 60 "title": "Show Rust syntax tree"
57 }, 61 },
diff --git a/code/src/extension.ts b/code/src/extension.ts
index c25e8cb61..b9d009776 100644
--- a/code/src/extension.ts
+++ b/code/src/extension.ts
@@ -81,11 +81,21 @@ export function activate(context: vscode.ExtensionContext) {
81 let e = await vscode.window.showTextDocument(doc) 81 let e = await vscode.window.showTextDocument(doc)
82 e.revealRange(range, vscode.TextEditorRevealType.InCenter) 82 e.revealRange(range, vscode.TextEditorRevealType.InCenter)
83 }) 83 })
84 console.log("ping") 84
85 registerCommand('libsyntax-rust.run', async (cmd: ProcessSpec) => { 85 registerCommand('libsyntax-rust.run', async (cmd: ProcessSpec) => {
86 let task = createTask(cmd) 86 let task = createTask(cmd)
87 await vscode.tasks.executeTask(task) 87 await vscode.tasks.executeTask(task)
88 }) 88 })
89 registerCommand('libsyntax-rust.createFile', async (uri_: string) => {
90 console.log(`uri: ${uri_}`)
91 let uri = vscode.Uri.parse(uri_)
92 let edit = new vscode.WorkspaceEdit()
93 edit.createFile(uri)
94 await vscode.workspace.applyEdit(edit)
95 let doc = await vscode.workspace.openTextDocument(uri)
96 await vscode.window.showTextDocument(doc)
97 console.log("Done")
98 })
89 99
90 dispose(vscode.workspace.registerTextDocumentContentProvider( 100 dispose(vscode.workspace.registerTextDocumentContentProvider(
91 'libsyntax-rust', 101 'libsyntax-rust',
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs
index a3f721cc8..49c39e1f3 100644
--- a/crates/libanalysis/src/lib.rs
+++ b/crates/libanalysis/src/lib.rs
@@ -12,12 +12,9 @@ extern crate rayon;
12mod symbol_index; 12mod symbol_index;
13mod module_map; 13mod module_map;
14 14
15use once_cell::sync::OnceCell;
16use rayon::prelude::*;
17
18use std::{ 15use std::{
19 fmt, 16 fmt,
20 path::{Path}, 17 path::{Path, PathBuf},
21 sync::{ 18 sync::{
22 Arc, 19 Arc,
23 atomic::{AtomicBool, Ordering::SeqCst}, 20 atomic::{AtomicBool, Ordering::SeqCst},
@@ -26,13 +23,16 @@ use std::{
26 time::Instant, 23 time::Instant,
27}; 24};
28 25
26use once_cell::sync::OnceCell;
27use rayon::prelude::*;
28
29use libsyntax2::{ 29use libsyntax2::{
30 File, 30 File,
31 TextUnit, TextRange, SmolStr, 31 TextUnit, TextRange, SmolStr,
32 ast::{self, AstNode, NameOwner}, 32 ast::{self, AstNode, NameOwner},
33 SyntaxKind::*, 33 SyntaxKind::*,
34}; 34};
35use libeditor::{LineIndex, FileSymbol, find_node_at_offset}; 35use libeditor::{Diagnostic, LineIndex, FileSymbol, find_node_at_offset};
36 36
37use self::{ 37use self::{
38 symbol_index::FileSymbols, 38 symbol_index::FileSymbols,
@@ -130,6 +130,9 @@ impl WorldState {
130 } 130 }
131} 131}
132 132
133pub enum QuickFix {
134 CreateFile(PathBuf),
135}
133 136
134impl World { 137impl World {
135 pub fn file_syntax(&self, file_id: FileId) -> Result<File> { 138 pub fn file_syntax(&self, file_id: FileId) -> Result<File> {
@@ -210,6 +213,29 @@ impl World {
210 Ok(vec![]) 213 Ok(vec![])
211 } 214 }
212 215
216 pub fn diagnostics(&self, file_id: FileId) -> Result<Vec<(Diagnostic, Option<QuickFix>)>> {
217 let syntax = self.file_syntax(file_id)?;
218 let mut res = libeditor::diagnostics(&syntax)
219 .into_iter()
220 .map(|d| (d, None))
221 .collect::<Vec<_>>();
222 for module in syntax.ast().modules() {
223 if module.has_semi() && self.resolve_module(file_id, module).is_empty() {
224 if let Some(name) = module.name() {
225 let d = Diagnostic {
226 range: name.syntax().range(),
227 msg: "unresolved module".to_string(),
228 };
229 let quick_fix = self.data.module_map.suggested_child_mod_path(module)
230 .map(QuickFix::CreateFile);
231
232 res.push((d, quick_fix))
233 }
234 }
235 }
236 Ok(res)
237 }
238
213 fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> { 239 fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> {
214 let name = name_ref.text(); 240 let name = name_ref.text();
215 let mut query = Query::new(name.to_string()); 241 let mut query = Query::new(name.to_string());
diff --git a/crates/libanalysis/src/module_map.rs b/crates/libanalysis/src/module_map.rs
index 6a9104d84..4f480591e 100644
--- a/crates/libanalysis/src/module_map.rs
+++ b/crates/libanalysis/src/module_map.rs
@@ -93,6 +93,11 @@ impl ModuleMap {
93 res 93 res
94 } 94 }
95 95
96 pub fn suggested_child_mod_path(&self, m: ast::Module) -> Option<PathBuf> {
97 let name = m.name()?;
98 Some(PathBuf::from(format!("../{}.rs", name.text())))
99 }
100
96 fn links( 101 fn links(
97 &self, 102 &self,
98 file_resolver: &FileResolver, 103 file_resolver: &FileResolver,
diff --git a/crates/libanalysis/tests/tests.rs b/crates/libanalysis/tests/tests.rs
index 32abf9152..b3c357b02 100644
--- a/crates/libanalysis/tests/tests.rs
+++ b/crates/libanalysis/tests/tests.rs
@@ -45,6 +45,19 @@ fn test_resolve_module() {
45} 45}
46 46
47#[test] 47#[test]
48fn test_unresolved_module_diagnostic() {
49 let mut world = WorldState::new();
50 world.change_file(FileId(1), Some("mod foo;".to_string()));
51
52 let snap = world.snapshot(|_id, _path| None);
53 let diagnostics = snap.diagnostics(FileId(1)).unwrap();
54 assert_eq_dbg(
55 r#"[Diagnostic { range: [4; 7), msg: "unresolved module" }]"#,
56 &diagnostics,
57 );
58}
59
60#[test]
48fn test_resolve_parent_module() { 61fn test_resolve_parent_module() {
49 let mut world = WorldState::new(); 62 let mut world = WorldState::new();
50 world.change_file(FileId(1), Some("mod foo;".to_string())); 63 world.change_file(FileId(1), Some("mod foo;".to_string()));
diff --git a/crates/libeditor/scope.rs b/crates/libeditor/scope.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/crates/libeditor/scope.rs
diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs
index 69c039e83..242a3a434 100644
--- a/crates/libeditor/src/completion.rs
+++ b/crates/libeditor/src/completion.rs
@@ -1,20 +1,14 @@
1use std::{
2 fmt,
3 collections::HashMap,
4};
5
6use libsyntax2::{ 1use libsyntax2::{
7 File, TextUnit, AstNode, SyntaxNodeRef, SyntaxNode, SmolStr, 2 File, TextUnit, AstNode,
8 ast::{self, NameOwner}, 3 ast::self,
9 algo::{ 4 algo::{
10 ancestors, 5 ancestors,
11 walk::preorder,
12 generate,
13 }, 6 },
14}; 7};
15 8
16use { 9use {
17 AtomEdit, find_node_at_offset, 10 AtomEdit, find_node_at_offset,
11 scope::{FnScopes, compute_scopes},
18}; 12};
19 13
20#[derive(Debug)] 14#[derive(Debug)]
@@ -43,178 +37,3 @@ fn complete(name_ref: ast::NameRef, scopes: &FnScopes) -> Vec<CompletionItem> {
43 }) 37 })
44 .collect() 38 .collect()
45} 39}
46
47fn compute_scopes(fn_def: ast::FnDef) -> FnScopes {
48 let mut scopes = FnScopes::new();
49 let root = scopes.root_scope();
50 fn_def.param_list().into_iter()
51 .flat_map(|it| it.params())
52 .filter_map(|it| it.pat())
53 .for_each(|it| scopes.add_bindings(root, it));
54
55 if let Some(body) = fn_def.body() {
56 compute_block_scopes(body, &mut scopes, root)
57 }
58 scopes
59}
60
61fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
62 for stmt in block.statements() {
63 match stmt {
64 ast::Stmt::LetStmt(stmt) => {
65 scope = scopes.new_scope(scope);
66 if let Some(pat) = stmt.pat() {
67 scopes.add_bindings(scope, pat);
68 }
69 if let Some(expr) = stmt.initializer() {
70 scopes.set_scope(expr.syntax(), scope)
71 }
72 }
73 ast::Stmt::ExprStmt(expr_stmt) => {
74 if let Some(expr) = expr_stmt.expr() {
75 scopes.set_scope(expr.syntax(), scope);
76 compute_expr_scopes(expr, scopes, scope);
77 }
78 }
79 }
80 }
81 if let Some(expr) = block.expr() {
82 scopes.set_scope(expr.syntax(), scope);
83 compute_expr_scopes(expr, scopes, scope);
84 }
85}
86
87fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
88 match expr {
89 ast::Expr::IfExpr(e) => {
90 let cond_scope = e.condition().and_then(|cond| {
91 compute_cond_scopes(cond, scopes, scope)
92 });
93 if let Some(block) = e.then_branch() {
94 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
95 }
96 if let Some(block) = e.else_branch() {
97 compute_block_scopes(block, scopes, scope);
98 }
99 },
100 ast::Expr::WhileExpr(e) => {
101 let cond_scope = e.condition().and_then(|cond| {
102 compute_cond_scopes(cond, scopes, scope)
103 });
104 if let Some(block) = e.body() {
105 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
106 }
107 },
108 ast::Expr::BlockExpr(e) => {
109 if let Some(block) = e.block() {
110 compute_block_scopes(block, scopes, scope);
111 }
112 }
113 // ForExpr(e) => TODO,
114 _ => {
115 expr.syntax().children()
116 .filter_map(ast::Expr::cast)
117 .for_each(|expr| compute_expr_scopes(expr, scopes, scope))
118 }
119 };
120
121 fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option<ScopeId> {
122 if let Some(expr) = cond.expr() {
123 compute_expr_scopes(expr, scopes, scope);
124 }
125 if let Some(pat) = cond.pat() {
126 let s = scopes.new_scope(scope);
127 scopes.add_bindings(s, pat);
128 Some(s)
129 } else {
130 None
131 }
132 }
133}
134
135type ScopeId = usize;
136
137#[derive(Debug)]
138struct FnScopes {
139 scopes: Vec<ScopeData>,
140 scope_for: HashMap<SyntaxNode, ScopeId>,
141}
142
143impl FnScopes {
144 fn new() -> FnScopes {
145 FnScopes {
146 scopes: vec![],
147 scope_for: HashMap::new(),
148 }
149 }
150 fn root_scope(&mut self) -> ScopeId {
151 let res = self.scopes.len();
152 self.scopes.push(ScopeData { parent: None, entries: vec![] });
153 res
154 }
155 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
156 let res = self.scopes.len();
157 self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] });
158 res
159 }
160 fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
161 let entries = preorder(pat.syntax())
162 .filter_map(ast::BindPat::cast)
163 .filter_map(ScopeEntry::new);
164 self.scopes[scope].entries.extend(entries);
165 }
166 fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
167 self.scope_for.insert(node.owned(), scope);
168 }
169 fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
170 &self.scopes[scope].entries
171 }
172 fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
173 ancestors(node)
174 .filter_map(|it| self.scope_for.get(&it.owned()).map(|&scope| scope))
175 .next()
176 }
177 fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item=ScopeId> + 'a {
178 generate(self.scope_for(node), move |&scope| self.scopes[scope].parent)
179 }
180}
181
182#[derive(Debug)]
183struct ScopeData {
184 parent: Option<ScopeId>,
185 entries: Vec<ScopeEntry>
186}
187
188struct ScopeEntry {
189 syntax: SyntaxNode
190}
191
192impl ScopeEntry {
193 fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
194 if pat.name().is_some() {
195 Some(ScopeEntry { syntax: pat.syntax().owned() })
196 } else {
197 None
198 }
199 }
200
201 fn name(&self) -> SmolStr {
202 self.ast().name()
203 .unwrap()
204 .text()
205 }
206
207 fn ast(&self) -> ast::BindPat {
208 ast::BindPat::cast(self.syntax.borrowed())
209 .unwrap()
210 }
211}
212
213impl fmt::Debug for ScopeEntry {
214 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
215 f.debug_struct("ScopeEntry")
216 .field("name", &self.name())
217 .field("syntax", &self.syntax)
218 .finish()
219 }
220}
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs
index 60489f7e3..d39e56d81 100644
--- a/crates/libeditor/src/lib.rs
+++ b/crates/libeditor/src/lib.rs
@@ -9,6 +9,7 @@ mod edit;
9mod code_actions; 9mod code_actions;
10mod typing; 10mod typing;
11mod completion; 11mod completion;
12mod scope;
12 13
13use libsyntax2::{ 14use libsyntax2::{
14 File, TextUnit, TextRange, SyntaxNodeRef, 15 File, TextUnit, TextRange, SyntaxNodeRef,
diff --git a/crates/libeditor/src/scope.rs b/crates/libeditor/src/scope.rs
new file mode 100644
index 000000000..1fec0b24e
--- /dev/null
+++ b/crates/libeditor/src/scope.rs
@@ -0,0 +1,183 @@
1use std::{
2 fmt,
3 collections::HashMap,
4};
5
6use libsyntax2::{
7 SyntaxNodeRef, SyntaxNode, SmolStr, AstNode,
8 ast::{self, NameOwner},
9 algo::{ancestors, generate, walk::preorder}
10};
11
12pub fn compute_scopes(fn_def: ast::FnDef) -> FnScopes {
13 let mut scopes = FnScopes::new();
14 let root = scopes.root_scope();
15 fn_def.param_list().into_iter()
16 .flat_map(|it| it.params())
17 .filter_map(|it| it.pat())
18 .for_each(|it| scopes.add_bindings(root, it));
19
20 if let Some(body) = fn_def.body() {
21 compute_block_scopes(body, &mut scopes, root)
22 }
23 scopes
24}
25
26fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
27 for stmt in block.statements() {
28 match stmt {
29 ast::Stmt::LetStmt(stmt) => {
30 scope = scopes.new_scope(scope);
31 if let Some(pat) = stmt.pat() {
32 scopes.add_bindings(scope, pat);
33 }
34 if let Some(expr) = stmt.initializer() {
35 scopes.set_scope(expr.syntax(), scope)
36 }
37 }
38 ast::Stmt::ExprStmt(expr_stmt) => {
39 if let Some(expr) = expr_stmt.expr() {
40 scopes.set_scope(expr.syntax(), scope);
41 compute_expr_scopes(expr, scopes, scope);
42 }
43 }
44 }
45 }
46 if let Some(expr) = block.expr() {
47 scopes.set_scope(expr.syntax(), scope);
48 compute_expr_scopes(expr, scopes, scope);
49 }
50}
51
52fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
53 match expr {
54 ast::Expr::IfExpr(e) => {
55 let cond_scope = e.condition().and_then(|cond| {
56 compute_cond_scopes(cond, scopes, scope)
57 });
58 if let Some(block) = e.then_branch() {
59 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
60 }
61 if let Some(block) = e.else_branch() {
62 compute_block_scopes(block, scopes, scope);
63 }
64 },
65 ast::Expr::WhileExpr(e) => {
66 let cond_scope = e.condition().and_then(|cond| {
67 compute_cond_scopes(cond, scopes, scope)
68 });
69 if let Some(block) = e.body() {
70 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
71 }
72 },
73 ast::Expr::BlockExpr(e) => {
74 if let Some(block) = e.block() {
75 compute_block_scopes(block, scopes, scope);
76 }
77 }
78 // ForExpr(e) => TODO,
79 _ => {
80 expr.syntax().children()
81 .filter_map(ast::Expr::cast)
82 .for_each(|expr| compute_expr_scopes(expr, scopes, scope))
83 }
84 };
85
86 fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option<ScopeId> {
87 if let Some(expr) = cond.expr() {
88 compute_expr_scopes(expr, scopes, scope);
89 }
90 if let Some(pat) = cond.pat() {
91 let s = scopes.new_scope(scope);
92 scopes.add_bindings(s, pat);
93 Some(s)
94 } else {
95 None
96 }
97 }
98}
99
100type ScopeId = usize;
101
102#[derive(Debug)]
103pub struct FnScopes {
104 scopes: Vec<ScopeData>,
105 scope_for: HashMap<SyntaxNode, ScopeId>,
106}
107
108impl FnScopes {
109 fn new() -> FnScopes {
110 FnScopes {
111 scopes: vec![],
112 scope_for: HashMap::new(),
113 }
114 }
115 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
116 &self.scopes[scope].entries
117 }
118 pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item=ScopeId> + 'a {
119 generate(self.scope_for(node), move |&scope| self.scopes[scope].parent)
120 }
121 fn root_scope(&mut self) -> ScopeId {
122 let res = self.scopes.len();
123 self.scopes.push(ScopeData { parent: None, entries: vec![] });
124 res
125 }
126 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
127 let res = self.scopes.len();
128 self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] });
129 res
130 }
131 fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
132 let entries = preorder(pat.syntax())
133 .filter_map(ast::BindPat::cast)
134 .filter_map(ScopeEntry::new);
135 self.scopes[scope].entries.extend(entries);
136 }
137 fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
138 self.scope_for.insert(node.owned(), scope);
139 }
140 fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
141 ancestors(node)
142 .filter_map(|it| self.scope_for.get(&it.owned()).map(|&scope| scope))
143 .next()
144 }
145}
146
147#[derive(Debug)]
148struct ScopeData {
149 parent: Option<ScopeId>,
150 entries: Vec<ScopeEntry>
151}
152
153pub struct ScopeEntry {
154 syntax: SyntaxNode
155}
156
157impl ScopeEntry {
158 fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
159 if pat.name().is_some() {
160 Some(ScopeEntry { syntax: pat.syntax().owned() })
161 } else {
162 None
163 }
164 }
165 pub fn name(&self) -> SmolStr {
166 self.ast().name()
167 .unwrap()
168 .text()
169 }
170 fn ast(&self) -> ast::BindPat {
171 ast::BindPat::cast(self.syntax.borrowed())
172 .unwrap()
173 }
174}
175
176impl fmt::Debug for ScopeEntry {
177 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178 f.debug_struct("ScopeEntry")
179 .field("name", &self.name())
180 .field("syntax", &self.syntax)
181 .finish()
182 }
183}
diff --git a/crates/libsyntax2/src/grammar/expressions/atom.rs b/crates/libsyntax2/src/grammar/expressions/atom.rs
index bb5402af7..e83c82c92 100644
--- a/crates/libsyntax2/src/grammar/expressions/atom.rs
+++ b/crates/libsyntax2/src/grammar/expressions/atom.rs
@@ -29,8 +29,8 @@ pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> {
29pub(super) const ATOM_EXPR_FIRST: TokenSet = 29pub(super) const ATOM_EXPR_FIRST: TokenSet =
30 token_set_union![ 30 token_set_union![
31 LITERAL_FIRST, 31 LITERAL_FIRST,
32 token_set![L_PAREN, PIPE, MOVE_KW, IF_KW, WHILE_KW, MATCH_KW, UNSAFE_KW, L_CURLY, RETURN_KW, 32 token_set![L_CURLY, L_PAREN, L_BRACK, PIPE, MOVE_KW, IF_KW, WHILE_KW, MATCH_KW, UNSAFE_KW,
33 IDENT, SELF_KW, SUPER_KW, COLONCOLON, BREAK_KW, CONTINUE_KW, LIFETIME ], 33 RETURN_KW, IDENT, SELF_KW, SUPER_KW, COLONCOLON, BREAK_KW, CONTINUE_KW, LIFETIME ],
34 ]; 34 ];
35 35
36pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> { 36pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> {
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs
index e7ac53028..350eda7df 100644
--- a/crates/server/src/main_loop/handlers.rs
+++ b/crates/server/src/main_loop/handlers.rs
@@ -7,7 +7,7 @@ use languageserver_types::{
7 CompletionItem, 7 CompletionItem,
8}; 8};
9use serde_json::{to_value, from_value}; 9use serde_json::{to_value, from_value};
10use libanalysis::{Query}; 10use libanalysis::{Query, QuickFix};
11use libeditor; 11use libeditor;
12use libsyntax2::{ 12use libsyntax2::{
13 TextUnit, 13 TextUnit,
@@ -177,6 +177,30 @@ pub fn handle_code_action(
177 }; 177 };
178 res.push(cmd); 178 res.push(cmd);
179 } 179 }
180
181 for (diag, quick_fix) in world.analysis().diagnostics(file_id)? {
182 let quick_fix = match quick_fix {
183 Some(quick_fix) => quick_fix,
184 None => continue,
185 };
186 if !contains_offset_nonstrict(diag.range, offset) {
187 continue;
188 }
189 let cmd = match quick_fix {
190 QuickFix::CreateFile(path) => {
191 let path = &path.to_str().unwrap()[3..]; // strip `../` b/c url is weird
192 let uri = params.text_document.uri.join(path)
193 .unwrap();
194 let uri = ::url_serde::Ser::new(&uri);
195 Command {
196 title: "Create file".to_string(),
197 command: "libsyntax-rust.createFile".to_string(),
198 arguments: Some(vec![to_value(uri).unwrap()]),
199 }
200 }
201 };
202 res.push(cmd)
203 }
180 return Ok(Some(res)); 204 return Ok(Some(res));
181} 205}
182 206
@@ -355,11 +379,10 @@ pub fn publish_diagnostics(
355 uri: Url 379 uri: Url
356) -> Result<req::PublishDiagnosticsParams> { 380) -> Result<req::PublishDiagnosticsParams> {
357 let file_id = world.uri_to_file_id(&uri)?; 381 let file_id = world.uri_to_file_id(&uri)?;
358 let file = world.analysis().file_syntax(file_id)?;
359 let line_index = world.analysis().file_line_index(file_id)?; 382 let line_index = world.analysis().file_line_index(file_id)?;
360 let diagnostics = libeditor::diagnostics(&file) 383 let diagnostics = world.analysis().diagnostics(file_id)?
361 .into_iter() 384 .into_iter()
362 .map(|d| Diagnostic { 385 .map(|(d, _quick_fix)| Diagnostic {
363 range: d.range.conv_with(&line_index), 386 range: d.range.conv_with(&line_index),
364 severity: Some(DiagnosticSeverity::Error), 387 severity: Some(DiagnosticSeverity::Error),
365 code: None, 388 code: None,