From 8abf5363433e977c5393bb569e2a5d559cb0a602 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 29 Aug 2018 18:03:14 +0300 Subject: [PATCH] Grand refactoring --- code/src/extension.ts | 91 +++--- crates/libanalysis/src/api.rs | 136 +++++++++ crates/libanalysis/src/lib.rs | 93 ++++-- crates/libeditor/src/code_actions.rs | 15 +- crates/libeditor/src/completion.rs | 3 +- crates/libeditor/src/lib.rs | 4 +- crates/libeditor/src/test_utils.rs | 4 +- crates/libeditor/src/typing.rs | 48 ++- crates/server/src/conv.rs | 80 ++++- crates/server/src/main.rs | 3 +- crates/server/src/main_loop/handlers.rs | 386 ++++++++---------------- crates/server/src/main_loop/mod.rs | 169 +++-------- crates/server/src/req.rs | 35 ++- crates/server/src/server_world.rs | 8 +- 14 files changed, 591 insertions(+), 484 deletions(-) create mode 100644 crates/libanalysis/src/api.rs diff --git a/code/src/extension.ts b/code/src/extension.ts index 3b24b73b64..f2589ef2f8 100644 --- a/code/src/extension.ts +++ b/code/src/extension.ts @@ -60,11 +60,8 @@ export function activate(context: vscode.ExtensionContext) { textDocument: { uri: editor.document.uri.toString() }, range: client.code2ProtocolConverter.asRange(editor.selection), } - let response = await client.sendRequest("m/joinLines", request) - let edits = client.protocol2CodeConverter.asTextEdits(response) - let wsEdit = new vscode.WorkspaceEdit() - wsEdit.set(editor.document.uri, edits) - return vscode.workspace.applyEdit(wsEdit) + let change = await client.sendRequest("m/joinLines", request) + await applySourceChange(change) }) registerCommand('libsyntax-rust.parentModule', async () => { let editor = vscode.window.activeTextEditor @@ -113,28 +110,7 @@ export function activate(context: vscode.ExtensionContext) { return await vscode.tasks.executeTask(task) } }) - registerCommand('libsyntax-rust.fsEdit', async (ops: FsOp[]) => { - let edit = new vscode.WorkspaceEdit() - let created; - let moved; - for (let op of ops) { - if (op.type == "createFile") { - let uri = vscode.Uri.parse(op.uri!) - edit.createFile(uri) - created = uri - } else if (op.type == "moveFile") { - let src = vscode.Uri.parse(op.src!) - let dst = vscode.Uri.parse(op.dst!) - edit.renameFile(src, dst) - moved = dst - } else { - console.error(`unknown op: ${JSON.stringify(op)}`) - } - } - await vscode.workspace.applyEdit(edit) - let doc = await vscode.workspace.openTextDocument((created || moved)!) - await vscode.window.showTextDocument(doc) - }) + registerCommand('libsyntax-rust.applySourceChange', applySourceChange) dispose(vscode.workspace.registerTextDocumentContentProvider( 'libsyntax-rust', @@ -207,18 +183,6 @@ function startServer() { ) } ) - client.onRequest( - new lc.RequestType("m/moveCursor"), - (params: lc.Position, token: lc.CancellationToken) => { - let editor = vscode.window.activeTextEditor; - if (!editor) return - if (!editor.selection.isEmpty) return - let position = client.protocol2CodeConverter.asPosition(params) - afterLs(() => { - editor!.selection = new vscode.Selection(position, position) - }) - } - ) }) client.start(); } @@ -383,9 +347,56 @@ function createTask(spec: Runnable): vscode.Task { return t; } -interface FsOp { +interface FileSystemEdit { type: string; uri?: string; src?: string; dst?: string; } + +interface SourceChange { + label: string, + sourceFileEdits: lc.TextDocumentEdit[], + fileSystemEdits: FileSystemEdit[], + cursorPosition?: lc.TextDocumentPositionParams, +} + +async function applySourceChange(change: SourceChange) { + console.log(`applySOurceChange ${JSON.stringify(change)}`) + let wsEdit = new vscode.WorkspaceEdit() + for (let sourceEdit of change.sourceFileEdits) { + let uri = client.protocol2CodeConverter.asUri(sourceEdit.textDocument.uri) + let edits = client.protocol2CodeConverter.asTextEdits(sourceEdit.edits) + wsEdit.set(uri, edits) + } + let created; + let moved; + for (let fsEdit of change.fileSystemEdits) { + if (fsEdit.type == "createFile") { + let uri = vscode.Uri.parse(fsEdit.uri!) + wsEdit.createFile(uri) + created = uri + } else if (fsEdit.type == "moveFile") { + let src = vscode.Uri.parse(fsEdit.src!) + let dst = vscode.Uri.parse(fsEdit.dst!) + wsEdit.renameFile(src, dst) + moved = dst + } else { + console.error(`unknown op: ${JSON.stringify(fsEdit)}`) + } + } + let toOpen = created || moved + let toReveal = change.cursorPosition + await vscode.workspace.applyEdit(wsEdit) + if (toOpen) { + let doc = await vscode.workspace.openTextDocument(toOpen) + await vscode.window.showTextDocument(doc) + } else if (toReveal) { + let uri = client.protocol2CodeConverter.asUri(toReveal.textDocument.uri) + let position = client.protocol2CodeConverter.asPosition(toReveal.position) + let editor = vscode.window.activeTextEditor; + if (!editor || editor.document.uri != uri) return + if (!editor.selection.isEmpty) return + editor!.selection = new vscode.Selection(position, position) + } +} diff --git a/crates/libanalysis/src/api.rs b/crates/libanalysis/src/api.rs new file mode 100644 index 0000000000..bb4fee398d --- /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 96d10a087c..ec20d106f6 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 { + let create_file = FileSystemEdit::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, + }; + Diagnostic { range: name_node.syntax().range(), - msg: "unresolved module".to_string(), - }; - let fix = QuickFix { - fs_ops: vec![FsOp::CreateFile { - anchor: file_id, - path: candidate.clone(), - }] - }; - (diag, fix) + message: "unresolved module".to_string(), + fix: Some(fix), + } } Problem::NotDirOwner { move_to, candidate } => { - let diag = Diagnostic { + 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, + }; + Diagnostic { range: name_node.syntax().range(), - msg: "can't declare module at this location".to_string(), - }; - 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) + 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)> { diff --git a/crates/libeditor/src/code_actions.rs b/crates/libeditor/src/code_actions.rs index 08a85f6e26..dadbd63ab9 100644 --- a/crates/libeditor/src/code_actions.rs +++ b/crates/libeditor/src/code_actions.rs @@ -13,13 +13,14 @@ use libsyntax2::{ use {EditBuilder, Edit, find_node_at_offset}; +// TODO: rename to FileEdit #[derive(Debug)] -pub struct ActionResult { +pub struct LocalEdit { pub edit: Edit, pub cursor_position: Option, } -pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option ActionResult + 'a> { +pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option LocalEdit + 'a> { let syntax = file.syntax(); let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?; @@ -29,14 +30,14 @@ pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option(file: &'a File, offset: TextUnit) -> Option ActionResult + 'a> { +pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option LocalEdit + 'a> { let nominal = find_node_at_offset::(file.syntax(), offset)?; Some(move || { let derive_attr = nominal @@ -56,14 +57,14 @@ pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option(file: &'a File, offset: TextUnit) -> Option ActionResult + 'a> { +pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option LocalEdit + 'a> { let nominal = find_node_at_offset::(file.syntax(), offset)?; let name = nominal.name()?; @@ -90,7 +91,7 @@ pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option let offset = start_offset + TextUnit::of_str(&buf); buf.push_str("\n}"); edit.insert(start_offset, buf); - ActionResult { + LocalEdit { edit: edit.finish(), cursor_position: Some(offset), } diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs index fe4c9b2170..d95c407738 100644 --- a/crates/libeditor/src/completion.rs +++ b/crates/libeditor/src/completion.rs @@ -12,8 +12,7 @@ use { }; #[derive(Debug)] -pub struct - CompletionItem { +pub struct CompletionItem { pub name: String, pub snippet: Option } diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs index 4895f6fa99..4700ef3288 100644 --- a/crates/libeditor/src/lib.rs +++ b/crates/libeditor/src/lib.rs @@ -30,11 +30,11 @@ pub use self::{ symbols::{StructureNode, file_structure, FileSymbol, file_symbols}, edit::{EditBuilder, Edit}, code_actions::{ - ActionResult, + LocalEdit, flip_comma, add_derive, add_impl, }, typing::{join_lines, on_eq_typed}, - completion::scope_completion, + completion::{scope_completion, CompletionItem}, }; #[derive(Debug)] diff --git a/crates/libeditor/src/test_utils.rs b/crates/libeditor/src/test_utils.rs index 475f560fa9..037319cd00 100644 --- a/crates/libeditor/src/test_utils.rs +++ b/crates/libeditor/src/test_utils.rs @@ -1,8 +1,8 @@ use libsyntax2::{File, TextUnit}; pub use _test_utils::*; -use ActionResult; +use LocalEdit; -pub fn check_action Option> ( +pub fn check_action Option> ( before: &str, after: &str, f: F, diff --git a/crates/libeditor/src/typing.rs b/crates/libeditor/src/typing.rs index 5008b8d497..f888f3240a 100644 --- a/crates/libeditor/src/typing.rs +++ b/crates/libeditor/src/typing.rs @@ -11,14 +11,14 @@ use libsyntax2::{ SyntaxKind::*, }; -use {ActionResult, EditBuilder, find_node_at_offset}; +use {LocalEdit, EditBuilder, find_node_at_offset}; -pub fn join_lines(file: &File, range: TextRange) -> ActionResult { +pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { let range = if range.is_empty() { let syntax = file.syntax(); let text = syntax.text().slice(range.start()..); let pos = match text.find('\n') { - None => return ActionResult { + None => return LocalEdit { edit: EditBuilder::new().finish(), cursor_position: None }, @@ -50,13 +50,13 @@ pub fn join_lines(file: &File, range: TextRange) -> ActionResult { } eprintln!("{:?}", edit); - ActionResult { + LocalEdit { edit: edit.finish(), cursor_position: None, } } -pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option { +pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option { let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; if let_stmt.has_semi() { return None; @@ -75,7 +75,7 @@ pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option { let offset = let_stmt.syntax().range().end(); let mut edit = EditBuilder::new(); edit.insert(offset, ";".to_string()); - Some(ActionResult { + Some(LocalEdit { edit: edit.finish(), cursor_position: None, }) @@ -277,7 +277,41 @@ fn foo() { }", r" fn foo() { join(type_params.type_params().filter_map(|it| it.name()).map(|it| it.text())) -}") +}"); + + do_check(r" +pub fn handle_find_matching_brace( + world: ServerWorld, + params: req::FindMatchingBraceParams, +) -> Result> { + let file_id = params.text_document.try_conv_with(&world)?; + let file = world.analysis().file_syntax(file_id); + let line_index = world.analysis().file_line_index(file_id); + let res = params.offsets + .into_iter() + .map_conv_with(&line_index) + .map(|offset| <|>{ + world.analysis().matching_brace(&file, offset).unwrap_or(offset) + }<|>) + .map_conv_with(&line_index) + .collect(); + Ok(res) +}", r" +pub fn handle_find_matching_brace( + world: ServerWorld, + params: req::FindMatchingBraceParams, +) -> Result> { + let file_id = params.text_document.try_conv_with(&world)?; + let file = world.analysis().file_syntax(file_id); + let line_index = world.analysis().file_line_index(file_id); + let res = params.offsets + .into_iter() + .map_conv_with(&line_index) + .map(|offset| world.analysis().matching_brace(&file, offset).unwrap_or(offset)) + .map_conv_with(&line_index) + .collect(); + Ok(res) +}"); } #[test] diff --git a/crates/server/src/conv.rs b/crates/server/src/conv.rs index fc90569141..a59308c3fb 100644 --- a/crates/server/src/conv.rs +++ b/crates/server/src/conv.rs @@ -1,14 +1,16 @@ use languageserver_types::{ Range, SymbolKind, Position, TextEdit, Location, Url, TextDocumentIdentifier, VersionedTextDocumentIdentifier, TextDocumentItem, + TextDocumentPositionParams, TextDocumentEdit, }; use libeditor::{LineIndex, LineCol, Edit, AtomEdit}; use libsyntax2::{SyntaxKind, TextUnit, TextRange}; -use libanalysis::FileId; +use libanalysis::{FileId, SourceChange, SourceFileEdit, FileSystemEdit}; use { Result, server_world::ServerWorld, + req, }; pub trait Conv { @@ -168,6 +170,82 @@ impl<'a> TryConvWith for &'a TextDocumentIdentifier { } } +impl TryConvWith for Vec { + type Ctx = ::Ctx; + type Output = Vec<::Output>; + fn try_conv_with(self, ctx: &Self::Ctx) -> Result { + let mut res = Vec::with_capacity(self.len()); + for item in self { + res.push(item.try_conv_with(ctx)?); + } + Ok(res) + } +} + +impl TryConvWith for SourceChange { + type Ctx = ServerWorld; + type Output = req::SourceChange; + fn try_conv_with(self, world: &ServerWorld) -> Result { + let cursor_position = match self.cursor_position { + None => None, + Some(pos) => { + let line_index = world.analysis().file_line_index(pos.file_id); + Some(TextDocumentPositionParams { + text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?), + position: pos.offset.conv_with(&line_index), + }) + } + }; + let source_file_edits = self.source_file_edits.try_conv_with(world)?; + let file_system_edits = self.file_system_edits.try_conv_with(world)?; + Ok(req::SourceChange { + label: self.label, + source_file_edits, + file_system_edits, + cursor_position, + }) + } +} + +impl TryConvWith for SourceFileEdit { + type Ctx = ServerWorld; + type Output = TextDocumentEdit; + fn try_conv_with(self, world: &ServerWorld) -> Result { + let text_document = VersionedTextDocumentIdentifier { + uri: self.file_id.try_conv_with(world)?, + version: None, + }; + let line_index = world.analysis().file_line_index(self.file_id); + let edits = self.edits + .into_iter() + .map_conv_with(&line_index) + .collect(); + Ok(TextDocumentEdit { text_document, edits }) + } +} + +impl TryConvWith for FileSystemEdit { + type Ctx = ServerWorld; + type Output = req::FileSystemEdit; + fn try_conv_with(self, world: &ServerWorld) -> Result { + let res = match self { + FileSystemEdit::CreateFile { anchor, path } => { + let uri = world.file_id_to_uri(anchor)?; + let path = &path.as_str()[3..]; // strip `../` b/c url is weird + let uri = uri.join(path)?; + req::FileSystemEdit::CreateFile { uri } + }, + FileSystemEdit::MoveFile { file, path } => { + let src = world.file_id_to_uri(file)?; + let path = &path.as_str()[3..]; // strip `../` b/c url is weird + let dst = src.join(path)?; + req::FileSystemEdit::MoveFile { src, dst } + }, + }; + Ok(res) + } +} + pub fn to_location( file_id: FileId, range: TextRange, diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index 1a93af65b7..6af8bf81b2 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs @@ -35,7 +35,7 @@ use crossbeam_channel::bounded; use flexi_logger::{Logger, Duplicate}; use ::{ - io::{Io, RawMsg, RawResponse, RawRequest, RawNotification}, + io::{Io, RawMsg, RawResponse, RawNotification}, }; pub type Result = ::std::result::Result; @@ -109,7 +109,6 @@ fn initialize(io: &mut Io) -> Result<()> { enum Task { Respond(RawResponse), - Request(RawRequest), Notify(RawNotification), Die(::failure::Error), } diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index 3ee0873f4d..45083b084e 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs @@ -2,16 +2,13 @@ use std::collections::HashMap; use languageserver_types::{ Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, - Command, TextDocumentIdentifier, WorkspaceEdit, + Command, TextDocumentIdentifier, SymbolInformation, Position, Location, TextEdit, CompletionItem, InsertTextFormat, CompletionItemKind, }; -use serde_json::{to_value, from_value}; -use url_serde; -use libanalysis::{self, Query, FileId}; -use libeditor; +use serde_json::to_value; +use libanalysis::{Query, FileId, RunnableKind}; use libsyntax2::{ - TextUnit, text_utils::contains_offset_nonstrict, }; @@ -26,8 +23,8 @@ pub fn handle_syntax_tree( params: req::SyntaxTreeParams, ) -> Result { let id = params.text_document.try_conv_with(&world)?; - let file = world.analysis().file_syntax(id)?; - Ok(libeditor::syntax_tree(&file)) + let res = world.analysis().syntax_tree(id); + Ok(res) } pub fn handle_extend_selection( @@ -35,11 +32,11 @@ pub fn handle_extend_selection( params: req::ExtendSelectionParams, ) -> Result { let file_id = params.text_document.try_conv_with(&world)?; - let file = world.analysis().file_syntax(file_id)?; - let line_index = world.analysis().file_line_index(file_id)?; + let file = world.analysis().file_syntax(file_id); + let line_index = world.analysis().file_line_index(file_id); let selections = params.selections.into_iter() .map_conv_with(&line_index) - .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r)) + .map(|r| world.analysis().extend_selection(&file, r)) .map_conv_with(&line_index) .collect(); Ok(req::ExtendSelectionResult { selections }) @@ -50,13 +47,13 @@ pub fn handle_find_matching_brace( params: req::FindMatchingBraceParams, ) -> Result> { let file_id = params.text_document.try_conv_with(&world)?; - let file = world.analysis().file_syntax(file_id)?; - let line_index = world.analysis().file_line_index(file_id)?; + let file = world.analysis().file_syntax(file_id); + let line_index = world.analysis().file_line_index(file_id); let res = params.offsets .into_iter() .map_conv_with(&line_index) .map(|offset| { - libeditor::matching_brace(&file, offset).unwrap_or(offset) + world.analysis().matching_brace(&file, offset).unwrap_or(offset) }) .map_conv_with(&line_index) .collect(); @@ -66,13 +63,31 @@ pub fn handle_find_matching_brace( pub fn handle_join_lines( world: ServerWorld, params: req::JoinLinesParams, -) -> Result> { +) -> Result { let file_id = params.text_document.try_conv_with(&world)?; - let file = world.analysis().file_syntax(file_id)?; - let line_index = world.analysis().file_line_index(file_id)?; + let line_index = world.analysis().file_line_index(file_id); let range = params.range.conv_with(&line_index); - let res = libeditor::join_lines(&file, range); - Ok(res.edit.conv_with(&line_index)) + world.analysis().join_lines(file_id, range) + .try_conv_with(&world) +} + +pub fn handle_on_type_formatting( + world: ServerWorld, + params: req::DocumentOnTypeFormattingParams, +) -> Result>> { + if params.ch != "=" { + return Ok(None); + } + + let file_id = params.text_document.try_conv_with(&world)?; + let line_index = world.analysis().file_line_index(file_id); + let offset = params.position.conv_with(&line_index); + let edits = match world.analysis().on_eq_typed(file_id, offset) { + None => return Ok(None), + Some(mut action) => action.source_file_edits.pop().unwrap().edits, + }; + let edits = edits.into_iter().map_conv_with(&line_index).collect(); + Ok(Some(edits)) } pub fn handle_document_symbol( @@ -80,12 +95,11 @@ pub fn handle_document_symbol( params: req::DocumentSymbolParams, ) -> Result> { let file_id = params.text_document.try_conv_with(&world)?; - let file = world.analysis().file_syntax(file_id)?; - let line_index = world.analysis().file_line_index(file_id)?; + let line_index = world.analysis().file_line_index(file_id); let mut parents: Vec<(DocumentSymbol, Option)> = Vec::new(); - for symbol in libeditor::file_structure(&file) { + for symbol in world.analysis().file_structure(file_id) { let doc_symbol = DocumentSymbol { name: symbol.label, detail: Some("".to_string()), @@ -114,130 +128,6 @@ pub fn handle_document_symbol( Ok(Some(req::DocumentSymbolResponse::Nested(res))) } -pub fn handle_code_action( - world: ServerWorld, - params: req::CodeActionParams, -) -> Result>> { - let file_id = params.text_document.try_conv_with(&world)?; - let file = world.analysis().file_syntax(file_id)?; - let line_index = world.analysis().file_line_index(file_id)?; - let offset = params.range.conv_with(&line_index).start(); - let mut res = Vec::new(); - - let actions = &[ - (ActionId::FlipComma, libeditor::flip_comma(&file, offset).is_some()), - (ActionId::AddDerive, libeditor::add_derive(&file, offset).is_some()), - (ActionId::AddImpl, libeditor::add_impl(&file, offset).is_some()), - ]; - - for (id, edit) in actions { - if *edit { - let cmd = apply_code_action_cmd(*id, params.text_document.clone(), offset); - res.push(cmd); - } - } - - for (diag, quick_fix) in world.analysis().diagnostics(file_id)? { - let quick_fix = match quick_fix { - Some(quick_fix) => quick_fix, - None => continue, - }; - if !contains_offset_nonstrict(diag.range, offset) { - continue; - } - let mut ops = Vec::new(); - for op in quick_fix.fs_ops { - let op = match op { - libanalysis::FsOp::CreateFile { anchor, path } => { - let uri = world.file_id_to_uri(anchor)?; - let path = &path.as_str()[3..]; // strip `../` b/c url is weird - let uri = uri.join(path)?; - FsOp::CreateFile { uri } - }, - libanalysis::FsOp::MoveFile { file, path } => { - let src = world.file_id_to_uri(file)?; - let path = &path.as_str()[3..]; // strip `../` b/c url is weird - let dst = src.join(path)?; - FsOp::MoveFile { src, dst } - }, - }; - ops.push(op) - } - let cmd = Command { - title: "Create module".to_string(), - command: "libsyntax-rust.fsEdit".to_string(), - arguments: Some(vec![to_value(ops).unwrap()]), - }; - res.push(cmd) - } - return Ok(Some(res)); -} - -#[derive(Serialize)] -#[serde(tag = "type", rename_all = "camelCase")] -enum FsOp { - CreateFile { - #[serde(with = "url_serde")] - uri: Url - }, - MoveFile { - #[serde(with = "url_serde")] - src: Url, - #[serde(with = "url_serde")] - dst: Url, - } -} - -pub fn handle_runnables( - world: ServerWorld, - params: req::RunnablesParams, -) -> Result> { - let file_id = params.text_document.try_conv_with(&world)?; - let file = world.analysis().file_syntax(file_id)?; - let line_index = world.analysis().file_line_index(file_id)?; - let offset = params.position.map(|it| it.conv_with(&line_index)); - let mut res = Vec::new(); - for runnable in libeditor::runnables(&file) { - if let Some(offset) = offset { - if !contains_offset_nonstrict(runnable.range, offset) { - continue; - } - } - - let r = req::Runnable { - range: runnable.range.conv_with(&line_index), - label: match &runnable.kind { - libeditor::RunnableKind::Test { name } => - format!("test {}", name), - libeditor::RunnableKind::Bin => - "run binary".to_string(), - }, - bin: "cargo".to_string(), - args: match runnable.kind { - libeditor::RunnableKind::Test { name } => { - vec![ - "test".to_string(), - "--".to_string(), - name, - "--nocapture".to_string(), - ] - } - libeditor::RunnableKind::Bin => vec!["run".to_string()] - }, - env: { - let mut m = HashMap::new(); - m.insert( - "RUST_BACKTRACE".to_string(), - "short".to_string(), - ); - m - } - }; - res.push(r); - } - return Ok(res); -} - pub fn handle_workspace_symbol( world: ServerWorld, params: req::WorkspaceSymbolParams, @@ -265,8 +155,8 @@ pub fn handle_workspace_symbol( fn exec_query(world: &ServerWorld, query: Query) -> Result> { let mut res = Vec::new(); - for (file_id, symbol) in world.analysis().world_symbols(query) { - let line_index = world.analysis().file_line_index(file_id)?; + for (file_id, symbol) in world.analysis().symbol_search(query) { + let line_index = world.analysis().file_line_index(file_id); let info = SymbolInformation { name: symbol.name.to_string(), kind: symbol.kind.conv(), @@ -287,11 +177,11 @@ pub fn handle_goto_definition( params: req::TextDocumentPositionParams, ) -> Result> { let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id)?; + let line_index = world.analysis().file_line_index(file_id); let offset = params.position.conv_with(&line_index); let mut res = Vec::new(); - for (file_id, symbol) in world.analysis().approximately_resolve_symbol(file_id, offset)? { - let line_index = world.analysis().file_line_index(file_id)?; + for (file_id, symbol) in world.analysis().approximately_resolve_symbol(file_id, offset) { + let line_index = world.analysis().file_line_index(file_id); let location = to_location( file_id, symbol.node_range, &world, &line_index, @@ -308,7 +198,7 @@ pub fn handle_parent_module( let file_id = params.try_conv_with(&world)?; let mut res = Vec::new(); for (file_id, symbol) in world.analysis().parent_module(file_id) { - let line_index = world.analysis().file_line_index(file_id)?; + let line_index = world.analysis().file_line_index(file_id); let location = to_location( file_id, symbol.node_range, &world, &line_index @@ -318,15 +208,71 @@ pub fn handle_parent_module( Ok(res) } +pub fn handle_runnables( + world: ServerWorld, + params: req::RunnablesParams, +) -> Result> { + let file_id = params.text_document.try_conv_with(&world)?; + let line_index = world.analysis().file_line_index(file_id); + let offset = params.position.map(|it| it.conv_with(&line_index)); + let mut res = Vec::new(); + for runnable in world.analysis().runnables(file_id) { + if let Some(offset) = offset { + if !contains_offset_nonstrict(runnable.range, offset) { + continue; + } + } + + let r = req::Runnable { + range: runnable.range.conv_with(&line_index), + label: match &runnable.kind { + RunnableKind::Test { name } => + format!("test {}", name), + RunnableKind::Bin => + "run binary".to_string(), + }, + bin: "cargo".to_string(), + args: match runnable.kind { + RunnableKind::Test { name } => { + vec![ + "test".to_string(), + "--".to_string(), + name, + "--nocapture".to_string(), + ] + } + RunnableKind::Bin => vec!["run".to_string()] + }, + env: { + let mut m = HashMap::new(); + m.insert( + "RUST_BACKTRACE".to_string(), + "short".to_string(), + ); + m + } + }; + res.push(r); + } + return Ok(res); +} + +pub fn handle_decorations( + world: ServerWorld, + params: TextDocumentIdentifier, +) -> Result> { + let file_id = params.try_conv_with(&world)?; + Ok(highlight(&world, file_id)) +} + pub fn handle_completion( world: ServerWorld, params: req::CompletionParams, ) -> Result> { let file_id = params.text_document.try_conv_with(&world)?; - let file = world.analysis().file_syntax(file_id)?; - let line_index = world.analysis().file_line_index(file_id)?; + let line_index = world.analysis().file_line_index(file_id); let offset = params.position.conv_with(&line_index); - let items = match libeditor::scope_completion(&file, offset) { + let items = match world.analysis().completions(file_id, offset) { None => return Ok(None), Some(items) => items, }; @@ -348,91 +294,33 @@ pub fn handle_completion( Ok(Some(req::CompletionResponse::Array(items))) } -pub fn handle_on_type_formatting( +pub fn handle_code_action( world: ServerWorld, - params: req::DocumentOnTypeFormattingParams, -) -> Result>> { - if params.ch != "=" { - return Ok(None); - } - + params: req::CodeActionParams, +) -> Result>> { let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id)?; - let offset = params.position.conv_with(&line_index); - let file = world.analysis().file_syntax(file_id)?; - let action = match libeditor::on_eq_typed(&file, offset) { - None => return Ok(None), - Some(action) => action, - }; - Ok(Some(action.edit.conv_with(&line_index))) -} + let line_index = world.analysis().file_line_index(file_id); + let offset = params.range.conv_with(&line_index).start(); -pub fn handle_execute_command( - world: ServerWorld, - mut params: req::ExecuteCommandParams, -) -> Result<(req::ApplyWorkspaceEditParams, Option)> { - if params.command.as_str() != "apply_code_action" { - bail!("unknown cmd: {:?}", params.command); + let assists = world.analysis().assists(file_id, offset).into_iter(); + let fixes = world.analysis().diagnostics(file_id).into_iter() + .filter_map(|d| Some((d.range, d.fix?))) + .filter(|(range, _fix)| contains_offset_nonstrict(*range, offset)) + .map(|(_range, fix)| fix); + + let mut res = Vec::new(); + for source_edit in assists.chain(fixes) { + let title = source_edit.label.clone(); + let edit = source_edit.try_conv_with(&world)?; + let cmd = Command { + title, + command: "libsyntax-rust.applySourceChange".to_string(), + arguments: Some(vec![to_value(edit).unwrap()]), + }; + res.push(cmd); } - if params.arguments.len() != 1 { - bail!("expected single arg, got {}", params.arguments.len()); - } - let arg = params.arguments.pop().unwrap(); - let arg: ActionRequest = from_value(arg)?; - let file_id = arg.text_document.try_conv_with(&world)?; - let file = world.analysis().file_syntax(file_id)?; - let action_result = match arg.id { - ActionId::FlipComma => libeditor::flip_comma(&file, arg.offset).map(|f| f()), - ActionId::AddDerive => libeditor::add_derive(&file, arg.offset).map(|f| f()), - ActionId::AddImpl => libeditor::add_impl(&file, arg.offset).map(|f| f()), - }.ok_or_else(|| format_err!("command not applicable"))?; - let line_index = world.analysis().file_line_index(file_id)?; - let mut changes = HashMap::new(); - changes.insert( - arg.text_document.uri, - action_result.edit.conv_with(&line_index), - ); - let edit = WorkspaceEdit { - changes: Some(changes), - document_changes: None, - }; - let edit = req::ApplyWorkspaceEditParams { edit }; - let cursor_pos = action_result.cursor_position - .map(|off| off.conv_with(&line_index)); - Ok((edit, cursor_pos)) -} -#[derive(Serialize, Deserialize)] -struct ActionRequest { - id: ActionId, - text_document: TextDocumentIdentifier, - offset: TextUnit, -} - -fn apply_code_action_cmd(id: ActionId, doc: TextDocumentIdentifier, offset: TextUnit) -> Command { - let action_request = ActionRequest { id, text_document: doc, offset }; - Command { - title: id.title().to_string(), - command: "apply_code_action".to_string(), - arguments: Some(vec![to_value(action_request).unwrap()]), - } -} - -#[derive(Serialize, Deserialize, Clone, Copy)] -enum ActionId { - FlipComma, - AddDerive, - AddImpl, -} - -impl ActionId { - fn title(&self) -> &'static str { - match *self { - ActionId::FlipComma => "Flip `,`", - ActionId::AddDerive => "Add `#[derive]`", - ActionId::AddImpl => "Add impl", - } - } + Ok(Some(res)) } pub fn publish_diagnostics( @@ -440,28 +328,20 @@ pub fn publish_diagnostics( uri: Url ) -> Result { let file_id = world.uri_to_file_id(&uri)?; - let line_index = world.analysis().file_line_index(file_id)?; - let diagnostics = world.analysis().diagnostics(file_id)? + let line_index = world.analysis().file_line_index(file_id); + let diagnostics = world.analysis().diagnostics(file_id) .into_iter() - .map(|(d, _quick_fix)| Diagnostic { + .map(|d| Diagnostic { range: d.range.conv_with(&line_index), severity: Some(DiagnosticSeverity::Error), code: None, source: Some("libsyntax2".to_string()), - message: d.msg, + message: d.message, related_information: None, }).collect(); Ok(req::PublishDiagnosticsParams { uri, diagnostics }) } -pub fn handle_decorations( - world: ServerWorld, - params: TextDocumentIdentifier, -) -> Result> { - let file_id = params.try_conv_with(&world)?; - highlight(&world, file_id) -} - pub fn publish_decorations( world: ServerWorld, uri: Url @@ -469,18 +349,16 @@ pub fn publish_decorations( let file_id = world.uri_to_file_id(&uri)?; Ok(req::PublishDecorationsParams { uri, - decorations: highlight(&world, file_id)? + decorations: highlight(&world, file_id), }) } -fn highlight(world: &ServerWorld, file_id: FileId) -> Result> { - let file = world.analysis().file_syntax(file_id)?; - let line_index = world.analysis().file_line_index(file_id)?; - let res = libeditor::highlight(&file) +fn highlight(world: &ServerWorld, file_id: FileId) -> Vec { + let line_index = world.analysis().file_line_index(file_id); + world.analysis().highlight(file_id) .into_iter() .map(|h| Decoration { range: h.range.conv_with(&line_index), tag: h.tag, - }).collect(); - Ok(res) + }).collect() } diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index accb138784..0f66248a5a 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs @@ -7,7 +7,6 @@ use std::{ use threadpool::ThreadPool; use crossbeam_channel::{Sender, Receiver}; use languageserver_types::Url; -use serde_json::to_value; use { req, dispatch, @@ -15,24 +14,6 @@ use { io::{Io, RawMsg, RawRequest, RawNotification}, vfs::FileEvent, server_world::{ServerWorldState, ServerWorld}, - main_loop::handlers::{ - handle_syntax_tree, - handle_extend_selection, - publish_diagnostics, - publish_decorations, - handle_document_symbol, - handle_code_action, - handle_execute_command, - handle_workspace_symbol, - handle_goto_definition, - handle_find_matching_brace, - handle_parent_module, - handle_join_lines, - handle_completion, - handle_runnables, - handle_decorations, - handle_on_type_formatting, - }, }; pub(super) fn main_loop( @@ -45,7 +26,6 @@ pub(super) fn main_loop( info!("server initialized, serving requests"); let mut state = ServerWorldState::new(); - let mut next_request_id = 0; let mut pending_requests: HashSet = HashSet::new(); let mut fs_events_receiver = Some(&fs_events_receiver); loop { @@ -78,12 +58,6 @@ pub(super) fn main_loop( } Event::Task(task) => { match task { - Task::Request(mut request) => { - request.id = next_request_id; - pending_requests.insert(next_request_id); - next_request_id += 1; - io.send(RawMsg::Request(request)); - } Task::Respond(response) => io.send(RawMsg::Response(response)), Task::Notify(n) => @@ -125,79 +99,26 @@ fn on_request( sender: &Sender, req: RawRequest, ) -> Result { - let mut req = Some(req); - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_syntax_tree, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_extend_selection, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_find_matching_brace, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_document_symbol, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_code_action, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_runnables, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_workspace_symbol, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_goto_definition, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_completion, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_parent_module, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_join_lines, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_decorations, - )?; - handle_request_on_threadpool::( - &mut req, pool, world, sender, handle_on_type_formatting, - )?; - dispatch::handle_request::(&mut req, |params, resp| { - io.send(RawMsg::Response(resp.into_response(Ok(None))?)); - - let world = world.snapshot(); - let sender = sender.clone(); - pool.execute(move || { - let (edit, cursor) = match handle_execute_command(world, params) { - Ok(res) => res, - Err(e) => return sender.send(Task::Die(e)), - }; - match to_value(edit) { - Err(e) => return sender.send(Task::Die(e.into())), - Ok(params) => { - let request = RawRequest { - id: 0, - method: ::METHOD.to_string(), - params, - }; - sender.send(Task::Request(request)) - } - } - if let Some(cursor) = cursor { - let request = RawRequest { - id: 0, - method: ::METHOD.to_string(), - params: to_value(cursor).unwrap(), - }; - sender.send(Task::Request(request)) - } - }); - Ok(()) - })?; + let mut pool_dispatcher = PoolDispatcher { + req: Some(req), + pool, world, sender + }; + pool_dispatcher + .on::(handlers::handle_syntax_tree)? + .on::(handlers::handle_extend_selection)? + .on::(handlers::handle_find_matching_brace)? + .on::(handlers::handle_join_lines)? + .on::(handlers::handle_on_type_formatting)? + .on::(handlers::handle_document_symbol)? + .on::(handlers::handle_workspace_symbol)? + .on::(handlers::handle_goto_definition)? + .on::(handlers::handle_parent_module)? + .on::(handlers::handle_runnables)? + .on::(handlers::handle_decorations)? + .on::(handlers::handle_completion)? + .on::(handlers::handle_code_action)?; + let mut req = pool_dispatcher.req; let mut shutdown = false; dispatch::handle_request::(&mut req, |(), resp| { let resp = resp.into_response(Ok(()))?; @@ -273,27 +194,33 @@ fn on_notification( Ok(()) } -fn handle_request_on_threadpool( - req: &mut Option, - pool: &ThreadPool, - world: &ServerWorldState, - sender: &Sender, - f: fn(ServerWorld, R::Params) -> Result, -) -> Result<()> -{ - dispatch::handle_request::(req, |params, resp| { - let world = world.snapshot(); - let sender = sender.clone(); - pool.execute(move || { - let res = f(world, params); - let task = match resp.into_response(res) { - Ok(resp) => Task::Respond(resp), - Err(e) => Task::Die(e), - }; - sender.send(task); - }); - Ok(()) - }) +struct PoolDispatcher<'a> { + req: Option, + pool: &'a ThreadPool, + world: &'a ServerWorldState, + sender: &'a Sender, +} + +impl<'a> PoolDispatcher<'a> { + fn on<'b, R: req::ClientRequest>(&'b mut self, f: fn(ServerWorld, R::Params) -> Result) -> Result<&'b mut Self> { + let world = self.world; + let sender = self.sender; + let pool = self.pool; + dispatch::handle_request::(&mut self.req, |params, resp| { + let world = world.snapshot(); + let sender = sender.clone(); + pool.execute(move || { + let res = f(world, params); + let task = match resp.into_response(res) { + Ok(resp) => Task::Respond(resp), + Err(e) => Task::Die(e), + }; + sender.send(task); + }); + Ok(()) + })?; + Ok(self) + } } fn update_file_notifications_on_threadpool( @@ -303,7 +230,7 @@ fn update_file_notifications_on_threadpool( uri: Url, ) { pool.execute(move || { - match publish_diagnostics(world.clone(), uri.clone()) { + match handlers::publish_diagnostics(world.clone(), uri.clone()) { Err(e) => { error!("failed to compute diagnostics: {:?}", e) } @@ -312,7 +239,7 @@ fn update_file_notifications_on_threadpool( sender.send(Task::Notify(not)); } } - match publish_decorations(world, uri) { + match handlers::publish_decorations(world, uri) { Err(e) => { error!("failed to compute decorations: {:?}", e) } diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs index 881069b1f0..c6d2f2efbc 100644 --- a/crates/server/src/req.rs +++ b/crates/server/src/req.rs @@ -15,6 +15,7 @@ pub use languageserver_types::{ TextEdit, CompletionParams, CompletionResponse, DocumentOnTypeFormattingParams, + TextDocumentEdit, }; @@ -115,14 +116,6 @@ pub struct Decoration { pub tag: &'static str } -pub enum MoveCursor {} - -impl Request for MoveCursor { - type Params = Position; - type Result = (); - const METHOD: &'static str = "m/moveCursor"; -} - pub enum ParentModule {} impl Request for ParentModule { @@ -135,7 +128,7 @@ pub enum JoinLines {} impl Request for JoinLines { type Params = JoinLinesParams; - type Result = Vec; + type Result = SourceChange; const METHOD: &'static str = "m/joinLines"; } @@ -170,3 +163,27 @@ pub struct Runnable { pub args: Vec, pub env: HashMap, } + +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct SourceChange { + pub label: String, + pub source_file_edits: Vec, + pub file_system_edits: Vec, + pub cursor_position: Option, +} + +#[derive(Serialize, Debug)] +#[serde(tag = "type", rename_all = "camelCase")] +pub enum FileSystemEdit { + CreateFile { + #[serde(with = "url_serde")] + uri: Url + }, + MoveFile { + #[serde(with = "url_serde")] + src: Url, + #[serde(with = "url_serde")] + dst: Url, + } +} diff --git a/crates/server/src/server_world.rs b/crates/server/src/server_world.rs index 1982e727f6..6c85914ba3 100644 --- a/crates/server/src/server_world.rs +++ b/crates/server/src/server_world.rs @@ -5,7 +5,7 @@ use std::{ }; use languageserver_types::Url; -use libanalysis::{FileId, WorldState, World}; +use libanalysis::{FileId, WorldState, Analysis}; use { Result, @@ -22,7 +22,7 @@ pub struct ServerWorldState { #[derive(Clone)] pub struct ServerWorld { - pub analysis: World, + pub analysis: Analysis, pub path_map: PathMap, } @@ -91,14 +91,14 @@ impl ServerWorldState { pub fn snapshot(&self) -> ServerWorld { ServerWorld { - analysis: self.analysis.snapshot(self.path_map.clone()), + analysis: self.analysis.analysis(self.path_map.clone()), path_map: self.path_map.clone() } } } impl ServerWorld { - pub fn analysis(&self) -> &World { + pub fn analysis(&self) -> &Analysis { &self.analysis }