aboutsummaryrefslogtreecommitdiff
path: root/crates/libanalysis
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libanalysis')
-rw-r--r--crates/libanalysis/src/api.rs136
-rw-r--r--crates/libanalysis/src/lib.rs91
2 files changed, 195 insertions, 32 deletions
diff --git a/crates/libanalysis/src/api.rs b/crates/libanalysis/src/api.rs
new file mode 100644
index 000000000..bb4fee398
--- /dev/null
+++ b/crates/libanalysis/src/api.rs
@@ -0,0 +1,136 @@
1use relative_path::RelativePathBuf;
2use libsyntax2::{File, TextRange, TextUnit, AtomEdit};
3use libeditor;
4use {World, FileId, Query};
5
6pub use libeditor::{
7 LocalEdit, StructureNode, LineIndex, FileSymbol,
8 Runnable, RunnableKind, HighlightedRange, CompletionItem
9};
10
11#[derive(Clone, Debug)]
12pub struct Analysis {
13 pub(crate) imp: World
14}
15
16impl Analysis {
17 pub fn file_syntax(&self, file_id: FileId) -> File {
18 self.imp.file_syntax(file_id)
19 .unwrap()
20 }
21 pub fn file_line_index(&self, file_id: FileId) -> LineIndex {
22 self.imp.file_line_index(file_id)
23 .unwrap()
24 }
25 pub fn extend_selection(&self, file: &File, range: TextRange) -> TextRange {
26 libeditor::extend_selection(file, range).unwrap_or(range)
27 }
28 pub fn matching_brace(&self, file: &File, offset: TextUnit) -> Option<TextUnit> {
29 libeditor::matching_brace(file, offset)
30 }
31 pub fn syntax_tree(&self, file_id: FileId) -> String {
32 let file = self.file_syntax(file_id);
33 libeditor::syntax_tree(&file)
34 }
35 pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange {
36 let file = self.file_syntax(file_id);
37 SourceChange::from_local_edit(
38 file_id, "join lines",
39 libeditor::join_lines(&file, range),
40 )
41 }
42 pub fn on_eq_typed(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> {
43 let file = self.file_syntax(file_id);
44 Some(SourceChange::from_local_edit(
45 file_id, "add semicolon",
46 libeditor::on_eq_typed(&file, offset)?,
47 ))
48 }
49 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
50 let file = self.file_syntax(file_id);
51 libeditor::file_structure(&file)
52 }
53 pub fn symbol_search(&self, query: Query) -> Vec<(FileId, FileSymbol)> {
54 self.imp.world_symbols(query)
55 }
56 pub fn approximately_resolve_symbol(&self, file_id: FileId, offset: TextUnit) -> Vec<(FileId, FileSymbol)> {
57 self.imp.approximately_resolve_symbol(file_id, offset)
58 .unwrap()
59 }
60 pub fn parent_module(&self, file_id: FileId) -> Vec<(FileId, FileSymbol)> {
61 self.imp.parent_module(file_id)
62 }
63 pub fn runnables(&self, file_id: FileId) -> Vec<Runnable> {
64 let file = self.file_syntax(file_id);
65 libeditor::runnables(&file)
66 }
67 pub fn highlight(&self, file_id: FileId) -> Vec<HighlightedRange> {
68 let file = self.file_syntax(file_id);
69 libeditor::highlight(&file)
70 }
71 pub fn completions(&self, file_id: FileId, offset: TextUnit) -> Option<Vec<CompletionItem>> {
72 let file = self.file_syntax(file_id);
73 libeditor::scope_completion(&file, offset)
74 }
75 pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec<SourceChange> {
76 self.imp.assists(file_id, offset)
77 }
78 pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> {
79 self.imp.diagnostics(file_id)
80 }
81}
82
83#[derive(Debug)]
84pub struct SourceChange {
85 pub label: String,
86 pub source_file_edits: Vec<SourceFileEdit>,
87 pub file_system_edits: Vec<FileSystemEdit>,
88 pub cursor_position: Option<Position>,
89}
90
91#[derive(Debug)]
92pub struct Position {
93 pub file_id: FileId,
94 pub offset: TextUnit,
95}
96
97#[derive(Debug)]
98pub struct SourceFileEdit {
99 pub file_id: FileId,
100 pub edits: Vec<AtomEdit>,
101}
102
103#[derive(Debug)]
104pub enum FileSystemEdit {
105 CreateFile {
106 anchor: FileId,
107 path: RelativePathBuf,
108 },
109 MoveFile {
110 file: FileId,
111 path: RelativePathBuf,
112 }
113}
114
115#[derive(Debug)]
116pub struct Diagnostic {
117 pub message: String,
118 pub range: TextRange,
119 pub fix: Option<SourceChange>,
120}
121
122impl SourceChange {
123 pub(crate) fn from_local_edit(file_id: FileId, label: &str, edit: LocalEdit) -> SourceChange {
124 let file_edit = SourceFileEdit {
125 file_id,
126 edits: edit.edit.into_atoms(),
127 };
128 SourceChange {
129 label: label.to_string(),
130 source_file_edits: vec![file_edit],
131 file_system_edits: vec![],
132 cursor_position: edit.cursor_position
133 .map(|offset| Position { offset, file_id })
134 }
135 }
136}
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs
index 96d10a087..ec20d106f 100644
--- a/crates/libanalysis/src/lib.rs
+++ b/crates/libanalysis/src/lib.rs
@@ -12,6 +12,7 @@ extern crate relative_path;
12 12
13mod symbol_index; 13mod symbol_index;
14mod module_map; 14mod module_map;
15mod api;
15 16
16use std::{ 17use std::{
17 fmt, 18 fmt,
@@ -34,13 +35,14 @@ use libsyntax2::{
34 ast::{self, AstNode, NameOwner}, 35 ast::{self, AstNode, NameOwner},
35 SyntaxKind::*, 36 SyntaxKind::*,
36}; 37};
37use libeditor::{Diagnostic, LineIndex, FileSymbol, find_node_at_offset}; 38use libeditor::{LineIndex, FileSymbol, find_node_at_offset};
38 39
39use self::{ 40use self::{
40 symbol_index::FileSymbols, 41 symbol_index::FileSymbols,
41 module_map::{ModuleMap, ChangeKind, Problem}, 42 module_map::{ModuleMap, ChangeKind, Problem},
42}; 43};
43pub use self::symbol_index::Query; 44pub use self::symbol_index::Query;
45pub use self::api::*;
44 46
45pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 47pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
46 48
@@ -97,6 +99,13 @@ impl WorldState {
97 } 99 }
98 } 100 }
99 101
102 pub fn analysis(
103 &self,
104 file_resolver: impl FileResolver,
105 ) -> Analysis {
106 Analysis { imp: self.snapshot(file_resolver) }
107 }
108
100 pub fn change_file(&mut self, file_id: FileId, text: Option<String>) { 109 pub fn change_file(&mut self, file_id: FileId, text: Option<String>) {
101 self.change_files(::std::iter::once((file_id, text))); 110 self.change_files(::std::iter::once((file_id, text)));
102 } 111 }
@@ -231,11 +240,11 @@ impl World {
231 Ok(vec![]) 240 Ok(vec![])
232 } 241 }
233 242
234 pub fn diagnostics(&self, file_id: FileId) -> Result<Vec<(Diagnostic, Option<QuickFix>)>> { 243 pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> {
235 let syntax = self.file_syntax(file_id)?; 244 let syntax = self.file_syntax(file_id).unwrap();
236 let mut res = libeditor::diagnostics(&syntax) 245 let mut res = libeditor::diagnostics(&syntax)
237 .into_iter() 246 .into_iter()
238 .map(|d| (d, None)) 247 .map(|d| Diagnostic { range: d.range, message: d.msg, fix: None })
239 .collect::<Vec<_>>(); 248 .collect::<Vec<_>>();
240 249
241 self.data.module_map.problems( 250 self.data.module_map.problems(
@@ -243,44 +252,62 @@ impl World {
243 &*self.file_resolver, 252 &*self.file_resolver,
244 &|file_id| self.file_syntax(file_id).unwrap(), 253 &|file_id| self.file_syntax(file_id).unwrap(),
245 |name_node, problem| { 254 |name_node, problem| {
246 let (diag, fix) = match problem { 255 let diag = match problem {
247 Problem::UnresolvedModule { candidate } => { 256 Problem::UnresolvedModule { candidate } => {
248 let diag = Diagnostic { 257 let create_file = FileSystemEdit::CreateFile {
249 range: name_node.syntax().range(), 258 anchor: file_id,
250 msg: "unresolved module".to_string(), 259 path: candidate.clone(),
251 }; 260 };
252 let fix = QuickFix { 261 let fix = SourceChange {
253 fs_ops: vec![FsOp::CreateFile { 262 label: "create module".to_string(),
254 anchor: file_id, 263 source_file_edits: Vec::new(),
255 path: candidate.clone(), 264 file_system_edits: vec![create_file],
256 }] 265 cursor_position: None,
257 }; 266 };
258 (diag, fix) 267 Diagnostic {
268 range: name_node.syntax().range(),
269 message: "unresolved module".to_string(),
270 fix: Some(fix),
271 }
259 } 272 }
260 Problem::NotDirOwner { move_to, candidate } => { 273 Problem::NotDirOwner { move_to, candidate } => {
261 let diag = Diagnostic { 274 let move_file = FileSystemEdit::MoveFile { file: file_id, path: move_to.clone() };
262 range: name_node.syntax().range(), 275 let create_file = FileSystemEdit::CreateFile { anchor: file_id, path: move_to.join(candidate) };
263 msg: "can't declare module at this location".to_string(), 276 let fix = SourceChange {
277 label: "move file and create module".to_string(),
278 source_file_edits: Vec::new(),
279 file_system_edits: vec![move_file, create_file],
280 cursor_position: None,
264 }; 281 };
265 let fix = QuickFix { 282 Diagnostic {
266 fs_ops: vec![ 283 range: name_node.syntax().range(),
267 FsOp::MoveFile { 284 message: "can't declare module at this location".to_string(),
268 file: file_id, 285 fix: Some(fix),
269 path: move_to.clone(), 286 }
270 },
271 FsOp::CreateFile {
272 anchor: file_id,
273 path: move_to.join(candidate),
274 }
275 ],
276 };
277 (diag, fix)
278 } 287 }
279 }; 288 };
280 res.push((diag, Some(fix))) 289 res.push(diag)
281 } 290 }
282 ); 291 );
283 Ok(res) 292 res
293 }
294
295 pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec<SourceChange> {
296 let file = self.file_syntax(file_id).unwrap();
297 let actions = vec![
298 ("flip comma", libeditor::flip_comma(&file, offset).map(|f| f())),
299 ("add `#[derive]`", libeditor::add_derive(&file, offset).map(|f| f())),
300 ("add impl", libeditor::add_impl(&file, offset).map(|f| f())),
301 ];
302 let mut res = Vec::new();
303 for (name, local_edit) in actions {
304 if let Some(local_edit) = local_edit {
305 res.push(SourceChange::from_local_edit(
306 file_id, name, local_edit
307 ))
308 }
309 }
310 res
284 } 311 }
285 312
286 fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> { 313 fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> {