From 8abf5363433e977c5393bb569e2a5d559cb0a602 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 29 Aug 2018 18:03:14 +0300 Subject: Grand refactoring --- crates/libanalysis/src/api.rs | 136 ++++++++++++++++++++++++++++++++++++++++++ crates/libanalysis/src/lib.rs | 91 ++++++++++++++++++---------- 2 files changed, 195 insertions(+), 32 deletions(-) create mode 100644 crates/libanalysis/src/api.rs (limited to 'crates/libanalysis/src') 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 @@ +use relative_path::RelativePathBuf; +use libsyntax2::{File, TextRange, TextUnit, AtomEdit}; +use libeditor; +use {World, FileId, Query}; + +pub use libeditor::{ + LocalEdit, StructureNode, LineIndex, FileSymbol, + Runnable, RunnableKind, HighlightedRange, CompletionItem +}; + +#[derive(Clone, Debug)] +pub struct Analysis { + pub(crate) imp: World +} + +impl Analysis { + pub fn file_syntax(&self, file_id: FileId) -> File { + self.imp.file_syntax(file_id) + .unwrap() + } + pub fn file_line_index(&self, file_id: FileId) -> LineIndex { + self.imp.file_line_index(file_id) + .unwrap() + } + pub fn extend_selection(&self, file: &File, range: TextRange) -> TextRange { + libeditor::extend_selection(file, range).unwrap_or(range) + } + pub fn matching_brace(&self, file: &File, offset: TextUnit) -> Option { + libeditor::matching_brace(file, offset) + } + pub fn syntax_tree(&self, file_id: FileId) -> String { + let file = self.file_syntax(file_id); + libeditor::syntax_tree(&file) + } + pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange { + let file = self.file_syntax(file_id); + SourceChange::from_local_edit( + file_id, "join lines", + libeditor::join_lines(&file, range), + ) + } + pub fn on_eq_typed(&self, file_id: FileId, offset: TextUnit) -> Option { + let file = self.file_syntax(file_id); + Some(SourceChange::from_local_edit( + file_id, "add semicolon", + libeditor::on_eq_typed(&file, offset)?, + )) + } + pub fn file_structure(&self, file_id: FileId) -> Vec { + let file = self.file_syntax(file_id); + libeditor::file_structure(&file) + } + pub fn symbol_search(&self, query: Query) -> Vec<(FileId, FileSymbol)> { + self.imp.world_symbols(query) + } + pub fn approximately_resolve_symbol(&self, file_id: FileId, offset: TextUnit) -> Vec<(FileId, FileSymbol)> { + self.imp.approximately_resolve_symbol(file_id, offset) + .unwrap() + } + pub fn parent_module(&self, file_id: FileId) -> Vec<(FileId, FileSymbol)> { + self.imp.parent_module(file_id) + } + pub fn runnables(&self, file_id: FileId) -> Vec { + let file = self.file_syntax(file_id); + libeditor::runnables(&file) + } + pub fn highlight(&self, file_id: FileId) -> Vec { + let file = self.file_syntax(file_id); + libeditor::highlight(&file) + } + pub fn completions(&self, file_id: FileId, offset: TextUnit) -> Option> { + let file = self.file_syntax(file_id); + libeditor::scope_completion(&file, offset) + } + pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec { + self.imp.assists(file_id, offset) + } + pub fn diagnostics(&self, file_id: FileId) -> Vec { + self.imp.diagnostics(file_id) + } +} + +#[derive(Debug)] +pub struct SourceChange { + pub label: String, + pub source_file_edits: Vec, + pub file_system_edits: Vec, + pub cursor_position: Option, +} + +#[derive(Debug)] +pub struct Position { + pub file_id: FileId, + pub offset: TextUnit, +} + +#[derive(Debug)] +pub struct SourceFileEdit { + pub file_id: FileId, + pub edits: Vec, +} + +#[derive(Debug)] +pub enum FileSystemEdit { + CreateFile { + anchor: FileId, + path: RelativePathBuf, + }, + MoveFile { + file: FileId, + path: RelativePathBuf, + } +} + +#[derive(Debug)] +pub struct Diagnostic { + pub message: String, + pub range: TextRange, + pub fix: Option, +} + +impl SourceChange { + pub(crate) fn from_local_edit(file_id: FileId, label: &str, edit: LocalEdit) -> SourceChange { + let file_edit = SourceFileEdit { + file_id, + edits: edit.edit.into_atoms(), + }; + SourceChange { + label: label.to_string(), + source_file_edits: vec![file_edit], + file_system_edits: vec![], + cursor_position: edit.cursor_position + .map(|offset| Position { offset, file_id }) + } + } +} 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; mod symbol_index; mod module_map; +mod api; use std::{ fmt, @@ -34,13 +35,14 @@ use libsyntax2::{ ast::{self, AstNode, NameOwner}, SyntaxKind::*, }; -use libeditor::{Diagnostic, LineIndex, FileSymbol, find_node_at_offset}; +use libeditor::{LineIndex, FileSymbol, find_node_at_offset}; use self::{ symbol_index::FileSymbols, module_map::{ModuleMap, ChangeKind, Problem}, }; pub use self::symbol_index::Query; +pub use self::api::*; pub type Result = ::std::result::Result; @@ -97,6 +99,13 @@ impl WorldState { } } + pub fn analysis( + &self, + file_resolver: impl FileResolver, + ) -> Analysis { + Analysis { imp: self.snapshot(file_resolver) } + } + pub fn change_file(&mut self, file_id: FileId, text: Option) { self.change_files(::std::iter::once((file_id, text))); } @@ -231,11 +240,11 @@ impl World { Ok(vec![]) } - pub fn diagnostics(&self, file_id: FileId) -> Result)>> { - let syntax = self.file_syntax(file_id)?; + pub fn diagnostics(&self, file_id: FileId) -> Vec { + let syntax = self.file_syntax(file_id).unwrap(); let mut res = libeditor::diagnostics(&syntax) .into_iter() - .map(|d| (d, None)) + .map(|d| Diagnostic { range: d.range, message: d.msg, fix: None }) .collect::>(); self.data.module_map.problems( @@ -243,44 +252,62 @@ impl World { &*self.file_resolver, &|file_id| self.file_syntax(file_id).unwrap(), |name_node, problem| { - let (diag, fix) = match problem { + let diag = match problem { Problem::UnresolvedModule { candidate } => { - let diag = Diagnostic { - range: name_node.syntax().range(), - msg: "unresolved module".to_string(), + let create_file = FileSystemEdit::CreateFile { + anchor: file_id, + path: candidate.clone(), }; - let fix = QuickFix { - fs_ops: vec![FsOp::CreateFile { - anchor: file_id, - path: candidate.clone(), - }] + let fix = SourceChange { + label: "create module".to_string(), + source_file_edits: Vec::new(), + file_system_edits: vec![create_file], + cursor_position: None, }; - (diag, fix) + Diagnostic { + range: name_node.syntax().range(), + message: "unresolved module".to_string(), + fix: Some(fix), + } } Problem::NotDirOwner { move_to, candidate } => { - let diag = Diagnostic { - range: name_node.syntax().range(), - msg: "can't declare module at this location".to_string(), + let move_file = FileSystemEdit::MoveFile { file: file_id, path: move_to.clone() }; + let create_file = FileSystemEdit::CreateFile { anchor: file_id, path: move_to.join(candidate) }; + let fix = SourceChange { + label: "move file and create module".to_string(), + source_file_edits: Vec::new(), + file_system_edits: vec![move_file, create_file], + cursor_position: None, }; - let fix = QuickFix { - fs_ops: vec![ - FsOp::MoveFile { - file: file_id, - path: move_to.clone(), - }, - FsOp::CreateFile { - anchor: file_id, - path: move_to.join(candidate), - } - ], - }; - (diag, fix) + Diagnostic { + range: name_node.syntax().range(), + message: "can't declare module at this location".to_string(), + fix: Some(fix), + } } }; - res.push((diag, Some(fix))) + res.push(diag) } ); - Ok(res) + res + } + + pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec { + let file = self.file_syntax(file_id).unwrap(); + let actions = vec![ + ("flip comma", libeditor::flip_comma(&file, offset).map(|f| f())), + ("add `#[derive]`", libeditor::add_derive(&file, offset).map(|f| f())), + ("add impl", libeditor::add_impl(&file, offset).map(|f| f())), + ]; + let mut res = Vec::new(); + for (name, local_edit) in actions { + if let Some(local_edit) = local_edit { + res.push(SourceChange::from_local_edit( + file_id, name, local_edit + )) + } + } + res } fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> { -- cgit v1.2.3