From b5021411a84822cb3f1e3aeffad9550dd15bdeb6 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 16 Sep 2018 12:54:24 +0300 Subject: rename all things --- crates/server/src/caps.rs | 49 --- crates/server/src/conv.rs | 296 ------------------ crates/server/src/lib.rs | 37 --- crates/server/src/main.rs | 52 ---- crates/server/src/main_loop/handlers.rs | 436 --------------------------- crates/server/src/main_loop/mod.rs | 419 ------------------------- crates/server/src/main_loop/subscriptions.rs | 21 -- crates/server/src/path_map.rs | 110 ------- crates/server/src/project_model.rs | 175 ----------- crates/server/src/req.rs | 176 ----------- crates/server/src/server_world.rs | 167 ---------- crates/server/src/thread_watcher.rs | 70 ----- crates/server/src/vfs.rs | 71 ----- 13 files changed, 2079 deletions(-) delete mode 100644 crates/server/src/caps.rs delete mode 100644 crates/server/src/conv.rs delete mode 100644 crates/server/src/lib.rs delete mode 100644 crates/server/src/main.rs delete mode 100644 crates/server/src/main_loop/handlers.rs delete mode 100644 crates/server/src/main_loop/mod.rs delete mode 100644 crates/server/src/main_loop/subscriptions.rs delete mode 100644 crates/server/src/path_map.rs delete mode 100644 crates/server/src/project_model.rs delete mode 100644 crates/server/src/req.rs delete mode 100644 crates/server/src/server_world.rs delete mode 100644 crates/server/src/thread_watcher.rs delete mode 100644 crates/server/src/vfs.rs (limited to 'crates/server/src') diff --git a/crates/server/src/caps.rs b/crates/server/src/caps.rs deleted file mode 100644 index 7456aea8a..000000000 --- a/crates/server/src/caps.rs +++ /dev/null @@ -1,49 +0,0 @@ -use languageserver_types::{ - ServerCapabilities, - TextDocumentSyncCapability, - TextDocumentSyncOptions, - TextDocumentSyncKind, - ExecuteCommandOptions, - CompletionOptions, - DocumentOnTypeFormattingOptions, -}; - -pub fn server_capabilities() -> ServerCapabilities { - ServerCapabilities { - text_document_sync: Some(TextDocumentSyncCapability::Options( - TextDocumentSyncOptions { - open_close: Some(true), - change: Some(TextDocumentSyncKind::Full), - will_save: None, - will_save_wait_until: None, - save: None, - } - )), - hover_provider: None, - completion_provider: Some(CompletionOptions { - resolve_provider: None, - trigger_characters: None, - }), - signature_help_provider: None, - definition_provider: Some(true), - type_definition_provider: None, - implementation_provider: None, - references_provider: None, - document_highlight_provider: None, - document_symbol_provider: Some(true), - workspace_symbol_provider: Some(true), - code_action_provider: Some(true), - code_lens_provider: None, - document_formatting_provider: None, - document_range_formatting_provider: None, - document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { - first_trigger_character: "=".to_string(), - more_trigger_character: None, - }), - rename_provider: None, - color_provider: None, - execute_command_provider: Some(ExecuteCommandOptions { - commands: vec!["apply_code_action".to_string()], - }), - } -} diff --git a/crates/server/src/conv.rs b/crates/server/src/conv.rs deleted file mode 100644 index a59308c3f..000000000 --- a/crates/server/src/conv.rs +++ /dev/null @@ -1,296 +0,0 @@ -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, SourceChange, SourceFileEdit, FileSystemEdit}; - -use { - Result, - server_world::ServerWorld, - req, -}; - -pub trait Conv { - type Output; - fn conv(self) -> Self::Output; -} - -pub trait ConvWith { - type Ctx; - type Output; - fn conv_with(self, ctx: &Self::Ctx) -> Self::Output; -} - -pub trait TryConvWith { - type Ctx; - type Output; - fn try_conv_with(self, ctx: &Self::Ctx) -> Result; -} - -impl Conv for SyntaxKind { - type Output = SymbolKind; - - fn conv(self) -> ::Output { - match self { - SyntaxKind::FN_DEF => SymbolKind::Function, - SyntaxKind::STRUCT_DEF => SymbolKind::Struct, - SyntaxKind::ENUM_DEF => SymbolKind::Enum, - SyntaxKind::TRAIT_DEF => SymbolKind::Interface, - SyntaxKind::MODULE => SymbolKind::Module, - SyntaxKind::TYPE_DEF => SymbolKind::TypeParameter, - SyntaxKind::STATIC_DEF => SymbolKind::Constant, - SyntaxKind::CONST_DEF => SymbolKind::Constant, - SyntaxKind::IMPL_ITEM => SymbolKind::Object, - _ => SymbolKind::Variable, - } - } -} - -impl ConvWith for Position { - type Ctx = LineIndex; - type Output = TextUnit; - - fn conv_with(self, line_index: &LineIndex) -> TextUnit { - // TODO: UTF-16 - let line_col = LineCol { - line: self.line as u32, - col: (self.character as u32).into(), - }; - line_index.offset(line_col) - } -} - -impl ConvWith for TextUnit { - type Ctx = LineIndex; - type Output = Position; - - fn conv_with(self, line_index: &LineIndex) -> Position { - let line_col = line_index.line_col(self); - // TODO: UTF-16 - Position::new(line_col.line as u64, u32::from(line_col.col) as u64) - } -} - -impl ConvWith for TextRange { - type Ctx = LineIndex; - type Output = Range; - - fn conv_with(self, line_index: &LineIndex) -> Range { - Range::new( - self.start().conv_with(line_index), - self.end().conv_with(line_index), - ) - } -} - -impl ConvWith for Range { - type Ctx = LineIndex; - type Output = TextRange; - - fn conv_with(self, line_index: &LineIndex) -> TextRange { - TextRange::from_to( - self.start.conv_with(line_index), - self.end.conv_with(line_index), - ) - } -} - -impl ConvWith for Edit { - type Ctx = LineIndex; - type Output = Vec; - - fn conv_with(self, line_index: &LineIndex) -> Vec { - self.into_atoms() - .into_iter() - .map_conv_with(line_index) - .collect() - } -} - -impl ConvWith for AtomEdit { - type Ctx = LineIndex; - type Output = TextEdit; - - fn conv_with(self, line_index: &LineIndex) -> TextEdit { - TextEdit { - range: self.delete.conv_with(line_index), - new_text: self.insert, - } - } -} - -impl ConvWith for Option { - type Ctx = ::Ctx; - type Output = Option<::Output>; - fn conv_with(self, ctx: &Self::Ctx) -> Self::Output { - self.map(|x| ConvWith::conv_with(x, ctx)) - } -} - -impl<'a> TryConvWith for &'a Url { - type Ctx = ServerWorld; - type Output = FileId; - fn try_conv_with(self, world: &ServerWorld) -> Result { - world.uri_to_file_id(self) - } -} - -impl TryConvWith for FileId { - type Ctx = ServerWorld; - type Output = Url; - fn try_conv_with(self, world: &ServerWorld) -> Result { - world.file_id_to_uri(self) - } -} - -impl<'a> TryConvWith for &'a TextDocumentItem { - type Ctx = ServerWorld; - type Output = FileId; - fn try_conv_with(self, world: &ServerWorld) -> Result { - self.uri.try_conv_with(world) - } -} - -impl<'a> TryConvWith for &'a VersionedTextDocumentIdentifier { - type Ctx = ServerWorld; - type Output = FileId; - fn try_conv_with(self, world: &ServerWorld) -> Result { - self.uri.try_conv_with(world) - } -} - -impl<'a> TryConvWith for &'a TextDocumentIdentifier { - type Ctx = ServerWorld; - type Output = FileId; - fn try_conv_with(self, world: &ServerWorld) -> Result { - world.uri_to_file_id(&self.uri) - } -} - -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, - world: &ServerWorld, - line_index: &LineIndex, -) -> Result { - let url = file_id.try_conv_with(world)?; - let loc = Location::new( - url, - range.conv_with(line_index), - ); - Ok(loc) -} - -pub trait MapConvWith<'a>: Sized { - type Ctx; - type Output; - - fn map_conv_with(self, ctx: &'a Self::Ctx) -> ConvWithIter<'a, Self, Self::Ctx> { - ConvWithIter { iter: self, ctx } - } -} - -impl<'a, I> MapConvWith<'a> for I - where I: Iterator, - I::Item: ConvWith -{ - type Ctx = ::Ctx; - type Output = ::Output; -} - -pub struct ConvWithIter<'a, I, Ctx: 'a> { - iter: I, - ctx: &'a Ctx, -} - -impl<'a, I, Ctx> Iterator for ConvWithIter<'a, I, Ctx> - where - I: Iterator, - I::Item: ConvWith, -{ - type Item = ::Output; - - fn next(&mut self) -> Option { - self.iter.next().map(|item| item.conv_with(self.ctx)) - } -} - diff --git a/crates/server/src/lib.rs b/crates/server/src/lib.rs deleted file mode 100644 index c8aebc59c..000000000 --- a/crates/server/src/lib.rs +++ /dev/null @@ -1,37 +0,0 @@ -#[macro_use] -extern crate failure; -#[macro_use] -extern crate serde_derive; -extern crate serde; -extern crate serde_json; -extern crate languageserver_types; -#[macro_use] -extern crate crossbeam_channel; -extern crate rayon; -#[macro_use] -extern crate log; -extern crate drop_bomb; -extern crate url_serde; -extern crate walkdir; -extern crate im; -extern crate relative_path; -extern crate cargo_metadata; - -extern crate gen_lsp_server; -extern crate libeditor; -extern crate libanalysis; -extern crate libsyntax2; - -mod caps; -pub mod req; -mod conv; -mod main_loop; -mod vfs; -mod path_map; -mod server_world; -mod project_model; -pub mod thread_watcher; - -pub type Result = ::std::result::Result; -pub use caps::server_capabilities; -pub use main_loop::main_loop; diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs deleted file mode 100644 index 968b3b87a..000000000 --- a/crates/server/src/main.rs +++ /dev/null @@ -1,52 +0,0 @@ -#[macro_use] -extern crate log; -#[macro_use] -extern crate failure; -extern crate flexi_logger; -extern crate gen_lsp_server; -extern crate m; - -use flexi_logger::{Logger, Duplicate}; -use gen_lsp_server::{run_server, stdio_transport}; -use m::Result; - -fn main() -> Result<()> { - ::std::env::set_var("RUST_BACKTRACE", "short"); - Logger::with_env_or_str("error") - .duplicate_to_stderr(Duplicate::All) - .log_to_file() - .directory("log") - .start()?; - info!("lifecycle: server started"); - match ::std::panic::catch_unwind(|| main_inner()) { - Ok(res) => { - info!("lifecycle: terminating process with {:?}", res); - res - } - Err(_) => { - error!("server panicked"); - bail!("server panicked") - } - } -} - -fn main_inner() -> Result<()> { - let (receiver, sender, threads) = stdio_transport(); - let cwd = ::std::env::current_dir()?; - run_server( - m::server_capabilities(), - |params, r, s| { - let root = params.root_uri - .and_then(|it| it.to_file_path().ok()) - .unwrap_or(cwd); - m::main_loop(false, root, r, s) - }, - receiver, - sender, - )?; - info!("shutting down IO..."); - threads.join()?; - info!("... IO is down"); - Ok(()) -} - diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs deleted file mode 100644 index 3e02227d5..000000000 --- a/crates/server/src/main_loop/handlers.rs +++ /dev/null @@ -1,436 +0,0 @@ -use std::collections::HashMap; - -use languageserver_types::{ - Diagnostic, DiagnosticSeverity, DocumentSymbol, - Command, TextDocumentIdentifier, - SymbolInformation, Position, Location, TextEdit, - CompletionItem, InsertTextFormat, CompletionItemKind, -}; -use serde_json::to_value; -use libanalysis::{Query, FileId, RunnableKind, JobToken}; -use libsyntax2::{ - text_utils::contains_offset_nonstrict, -}; - -use ::{ - req::{self, Decoration}, Result, - conv::{Conv, ConvWith, TryConvWith, MapConvWith, to_location}, - server_world::ServerWorld, - project_model::TargetKind, -}; - -pub fn handle_syntax_tree( - world: ServerWorld, - params: req::SyntaxTreeParams, - _token: JobToken, -) -> Result { - let id = params.text_document.try_conv_with(&world)?; - let res = world.analysis().syntax_tree(id); - Ok(res) -} - -pub fn handle_extend_selection( - world: ServerWorld, - params: req::ExtendSelectionParams, - _token: JobToken, -) -> 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 selections = params.selections.into_iter() - .map_conv_with(&line_index) - .map(|r| world.analysis().extend_selection(&file, r)) - .map_conv_with(&line_index) - .collect(); - Ok(req::ExtendSelectionResult { selections }) -} - -pub fn handle_find_matching_brace( - world: ServerWorld, - params: req::FindMatchingBraceParams, - _token: JobToken, -) -> 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) -} - -pub fn handle_join_lines( - world: ServerWorld, - params: req::JoinLinesParams, - _token: JobToken, -) -> Result { - let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id); - let range = params.range.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, - _token: JobToken, -) -> 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( - world: ServerWorld, - params: req::DocumentSymbolParams, - _token: JobToken, -) -> Result> { - let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id); - - let mut parents: Vec<(DocumentSymbol, Option)> = Vec::new(); - - for symbol in world.analysis().file_structure(file_id) { - let doc_symbol = DocumentSymbol { - name: symbol.label, - detail: Some("".to_string()), - kind: symbol.kind.conv(), - deprecated: None, - range: symbol.node_range.conv_with(&line_index), - selection_range: symbol.navigation_range.conv_with(&line_index), - children: None, - }; - parents.push((doc_symbol, symbol.parent)); - } - let mut res = Vec::new(); - while let Some((node, parent)) = parents.pop() { - match parent { - None => res.push(node), - Some(i) => { - let children = &mut parents[i].0.children; - if children.is_none() { - *children = Some(Vec::new()); - } - children.as_mut().unwrap().push(node); - } - } - } - - Ok(Some(req::DocumentSymbolResponse::Nested(res))) -} - -pub fn handle_workspace_symbol( - world: ServerWorld, - params: req::WorkspaceSymbolParams, - token: JobToken, -) -> Result>> { - let all_symbols = params.query.contains("#"); - let libs = params.query.contains("*"); - let query = { - let query: String = params.query.chars() - .filter(|&c| c != '#' && c != '*') - .collect(); - let mut q = Query::new(query); - if !all_symbols { - q.only_types(); - } - if libs { - q.libs(); - } - q.limit(128); - q - }; - let mut res = exec_query(&world, query, &token)?; - if res.is_empty() && !all_symbols { - let mut query = Query::new(params.query); - query.limit(128); - res = exec_query(&world, query, &token)?; - } - - return Ok(Some(res)); - - fn exec_query(world: &ServerWorld, query: Query, token: &JobToken) -> Result> { - let mut res = Vec::new(); - for (file_id, symbol) in world.analysis().symbol_search(query, token) { - let line_index = world.analysis().file_line_index(file_id); - let info = SymbolInformation { - name: symbol.name.to_string(), - kind: symbol.kind.conv(), - location: to_location( - file_id, symbol.node_range, - world, &line_index - )?, - container_name: None, - }; - res.push(info); - }; - Ok(res) - } -} - -pub fn handle_goto_definition( - world: ServerWorld, - params: req::TextDocumentPositionParams, - token: JobToken, -) -> 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 mut res = Vec::new(); - for (file_id, symbol) in world.analysis().approximately_resolve_symbol(file_id, offset, &token) { - let line_index = world.analysis().file_line_index(file_id); - let location = to_location( - file_id, symbol.node_range, - &world, &line_index, - )?; - res.push(location) - } - Ok(Some(req::GotoDefinitionResponse::Array(res))) -} - -pub fn handle_parent_module( - world: ServerWorld, - params: TextDocumentIdentifier, - _token: JobToken, -) -> Result> { - 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 location = to_location( - file_id, symbol.node_range, - &world, &line_index - )?; - res.push(location); - } - Ok(res) -} - -pub fn handle_runnables( - world: ServerWorld, - params: req::RunnablesParams, - _token: JobToken, -) -> 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 args = runnable_args(&world, file_id, &runnable.kind); - - 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, - env: { - let mut m = HashMap::new(); - m.insert( - "RUST_BACKTRACE".to_string(), - "short".to_string(), - ); - m - } - }; - res.push(r); - } - return Ok(res); - - fn runnable_args(world: &ServerWorld, file_id: FileId, kind: &RunnableKind) -> Vec { - let spec = if let Some(&crate_id) = world.analysis().crate_for(file_id).first() { - let file_id = world.analysis().crate_root(crate_id); - let path = world.path_map.get_path(file_id); - world.workspaces.iter() - .filter_map(|ws| { - let tgt = ws.target_by_root(path)?; - Some((tgt.package(ws).name(ws).clone(), tgt.name(ws).clone(), tgt.kind(ws))) - }) - .next() - } else { - None - }; - let mut res = Vec::new(); - match kind { - RunnableKind::Test { name } => { - res.push("test".to_string()); - if let Some((pkg_name, tgt_name, tgt_kind)) = spec { - spec_args(pkg_name, tgt_name, tgt_kind, &mut res); - } - res.push("--".to_string()); - res.push(name.to_string()); - res.push("--nocapture".to_string()); - } - RunnableKind::Bin => { - res.push("run".to_string()); - if let Some((pkg_name, tgt_name, tgt_kind)) = spec { - spec_args(pkg_name, tgt_name, tgt_kind, &mut res); - } - } - } - res - } - - fn spec_args(pkg_name: &str, tgt_name: &str, tgt_kind: TargetKind, buf: &mut Vec) { - buf.push("--package".to_string()); - buf.push(pkg_name.to_string()); - match tgt_kind { - TargetKind::Bin => { - buf.push("--bin".to_string()); - buf.push(tgt_name.to_string()); - } - TargetKind::Test => { - buf.push("--test".to_string()); - buf.push(tgt_name.to_string()); - } - TargetKind::Bench => { - buf.push("--bench".to_string()); - buf.push(tgt_name.to_string()); - } - TargetKind::Example => { - buf.push("--example".to_string()); - buf.push(tgt_name.to_string()); - } - TargetKind::Lib => { - buf.push("--lib".to_string()); - } - TargetKind::Other => (), - } - } -} - -pub fn handle_decorations( - world: ServerWorld, - params: TextDocumentIdentifier, - _token: JobToken, -) -> Result> { - let file_id = params.try_conv_with(&world)?; - Ok(highlight(&world, file_id)) -} - -pub fn handle_completion( - world: ServerWorld, - params: req::CompletionParams, - _token: JobToken, -) -> 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 items = match world.analysis().completions(file_id, offset) { - None => return Ok(None), - Some(items) => items, - }; - let items = items.into_iter() - .map(|item| { - let mut res = CompletionItem { - label: item.label, - filter_text: item.lookup, - .. Default::default() - }; - if let Some(snip) = item.snippet { - res.insert_text = Some(snip); - res.insert_text_format = Some(InsertTextFormat::Snippet); - res.kind = Some(CompletionItemKind::Keyword); - }; - res - }) - .collect(); - - Ok(Some(req::CompletionResponse::Array(items))) -} - -pub fn handle_code_action( - world: ServerWorld, - params: req::CodeActionParams, - _token: JobToken, -) -> Result>> { - let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id); - let range = params.range.conv_with(&line_index); - - let assists = world.analysis().assists(file_id, range).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, range.start())) - .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); - } - - Ok(Some(res)) -} - -pub fn publish_diagnostics( - world: ServerWorld, - file_id: FileId, -) -> Result { - let uri = world.file_id_to_uri(file_id)?; - let line_index = world.analysis().file_line_index(file_id); - let diagnostics = world.analysis().diagnostics(file_id) - .into_iter() - .map(|d| Diagnostic { - range: d.range.conv_with(&line_index), - severity: Some(DiagnosticSeverity::Error), - code: None, - source: Some("libsyntax2".to_string()), - message: d.message, - related_information: None, - }).collect(); - Ok(req::PublishDiagnosticsParams { uri, diagnostics }) -} - -pub fn publish_decorations( - world: ServerWorld, - file_id: FileId, -) -> Result { - let uri = world.file_id_to_uri(file_id)?; - Ok(req::PublishDecorationsParams { - uri, - decorations: highlight(&world, file_id), - }) -} - -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() -} diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs deleted file mode 100644 index f3b2744bf..000000000 --- a/crates/server/src/main_loop/mod.rs +++ /dev/null @@ -1,419 +0,0 @@ -mod handlers; -mod subscriptions; - -use std::{ - path::PathBuf, - collections::{HashMap}, -}; - -use serde::{Serialize, de::DeserializeOwned}; -use crossbeam_channel::{unbounded, Sender, Receiver}; -use rayon::{self, ThreadPool}; -use languageserver_types::{NumberOrString}; -use libanalysis::{FileId, JobHandle, JobToken, LibraryData}; -use gen_lsp_server::{ - RawRequest, RawNotification, RawMessage, RawResponse, ErrorCode, - handle_shutdown, -}; - -use { - req, - Result, - vfs::{self, FileEvent}, - server_world::{ServerWorldState, ServerWorld}, - main_loop::subscriptions::{Subscriptions}, - project_model::{CargoWorkspace, workspace_loader}, - thread_watcher::Worker, -}; - -#[derive(Debug)] -enum Task { - Respond(RawResponse), - Notify(RawNotification), -} - -pub fn main_loop( - internal_mode: bool, - root: PathBuf, - msg_receriver: &mut Receiver, - msg_sender: &mut Sender, -) -> Result<()> { - let pool = rayon::ThreadPoolBuilder::new() - .num_threads(4) - .panic_handler(|_| error!("thread panicked :(")) - .build() - .unwrap(); - let (task_sender, task_receiver) = unbounded::(); - let (fs_worker, fs_watcher) = vfs::roots_loader(); - let (ws_worker, ws_watcher) = workspace_loader(); - - info!("server initialized, serving requests"); - let mut state = ServerWorldState::new(); - - let mut pending_requests = HashMap::new(); - let mut subs = Subscriptions::new(); - let main_res = main_loop_inner( - internal_mode, - root, - &pool, - msg_sender, - msg_receriver, - task_sender, - task_receiver.clone(), - fs_worker, - ws_worker, - &mut state, - &mut pending_requests, - &mut subs, - ); - - info!("waiting for tasks to finish..."); - task_receiver.for_each(|task| on_task(task, msg_sender, &mut pending_requests)); - info!("...tasks have finished"); - info!("joining threadpool..."); - drop(pool); - info!("...threadpool has finished"); - - let fs_res = fs_watcher.stop(); - let ws_res = ws_watcher.stop(); - - main_res?; - fs_res?; - ws_res?; - - Ok(()) -} - -fn main_loop_inner( - internal_mode: bool, - ws_root: PathBuf, - pool: &ThreadPool, - msg_sender: &mut Sender, - msg_receiver: &mut Receiver, - task_sender: Sender, - task_receiver: Receiver, - fs_worker: Worker)>, - ws_worker: Worker>, - state: &mut ServerWorldState, - pending_requests: &mut HashMap, - subs: &mut Subscriptions, -) -> Result<()> { - let (libdata_sender, libdata_receiver) = unbounded(); - ws_worker.send(ws_root.clone()); - fs_worker.send(ws_root.clone()); - loop { - #[derive(Debug)] - enum Event { - Msg(RawMessage), - Task(Task), - Fs(PathBuf, Vec), - Ws(Result), - Lib(LibraryData), - } - trace!("selecting"); - let event = select! { - recv(msg_receiver, msg) => match msg { - Some(msg) => Event::Msg(msg), - None => bail!("client exited without shutdown"), - }, - recv(task_receiver, task) => Event::Task(task.unwrap()), - recv(fs_worker.out, events) => match events { - None => bail!("roots watcher died"), - Some((pb, events)) => Event::Fs(pb, events), - } - recv(ws_worker.out, ws) => match ws { - None => bail!("workspace watcher died"), - Some(ws) => Event::Ws(ws), - } - recv(libdata_receiver, data) => Event::Lib(data.unwrap()) - }; - let mut state_changed = false; - match event { - Event::Task(task) => on_task(task, msg_sender, pending_requests), - Event::Fs(root, events) => { - info!("fs change, {}, {} events", root.display(), events.len()); - if root == ws_root { - state.apply_fs_changes(events); - } else { - let (files, resolver) = state.events_to_files(events); - let sender = libdata_sender.clone(); - pool.spawn(move || { - let start = ::std::time::Instant::now(); - info!("indexing {} ... ", root.display()); - let data = LibraryData::prepare(files, resolver); - info!("indexed {:?} {}", start.elapsed(), root.display()); - sender.send(data); - }); - } - state_changed = true; - } - Event::Ws(ws) => { - match ws { - Ok(ws) => { - let workspaces = vec![ws]; - feedback(internal_mode, "workspace loaded", msg_sender); - for ws in workspaces.iter() { - for pkg in ws.packages().filter(|pkg| !pkg.is_member(ws)) { - debug!("sending root, {}", pkg.root(ws).to_path_buf().display()); - fs_worker.send(pkg.root(ws).to_path_buf()); - } - } - state.set_workspaces(workspaces); - state_changed = true; - } - Err(e) => warn!("loading workspace failed: {}", e), - } - } - Event::Lib(lib) => { - feedback(internal_mode, "library loaded", msg_sender); - state.add_lib(lib); - } - Event::Msg(msg) => { - match msg { - RawMessage::Request(req) => { - let req = match handle_shutdown(req, msg_sender) { - Some(req) => req, - None => return Ok(()), - }; - match on_request(state, pending_requests, pool, &task_sender, req)? { - None => (), - Some(req) => { - error!("unknown request: {:?}", req); - let resp = RawResponse::err( - req.id, - ErrorCode::MethodNotFound as i32, - "unknown request".to_string(), - ); - msg_sender.send(RawMessage::Response(resp)) - } - } - } - RawMessage::Notification(not) => { - on_notification(msg_sender, state, pending_requests, subs, not)?; - state_changed = true; - } - RawMessage::Response(resp) => { - error!("unexpected response: {:?}", resp) - } - } - } - }; - - if state_changed { - update_file_notifications_on_threadpool( - pool, - state.snapshot(), - task_sender.clone(), - subs.subscriptions(), - ) - } - } -} - -fn on_task( - task: Task, - msg_sender: &mut Sender, - pending_requests: &mut HashMap, -) { - match task { - Task::Respond(response) => { - if let Some(handle) = pending_requests.remove(&response.id) { - assert!(handle.has_completed()); - } - msg_sender.send(RawMessage::Response(response)) - } - Task::Notify(n) => - msg_sender.send(RawMessage::Notification(n)), - } -} - -fn on_request( - world: &mut ServerWorldState, - pending_requests: &mut HashMap, - pool: &ThreadPool, - sender: &Sender, - req: RawRequest, -) -> Result> { - let mut pool_dispatcher = PoolDispatcher { - req: Some(req), - res: None, - pool, world, sender - }; - let req = 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)? - .finish(); - match req { - Ok((id, handle)) => { - let inserted = pending_requests.insert(id, handle).is_none(); - assert!(inserted, "duplicate request: {}", id); - Ok(None) - }, - Err(req) => Ok(Some(req)), - } -} - -fn on_notification( - msg_sender: &mut Sender, - state: &mut ServerWorldState, - pending_requests: &mut HashMap, - subs: &mut Subscriptions, - not: RawNotification, -) -> Result<()> { - let not = match not.cast::() { - Ok(params) => { - let id = match params.id { - NumberOrString::Number(id) => id, - NumberOrString::String(id) => { - panic!("string id's not supported: {:?}", id); - } - }; - if let Some(handle) = pending_requests.remove(&id) { - handle.cancel(); - } - return Ok(()) - } - Err(not) => not, - }; - let not = match not.cast::() { - Ok(params) => { - let uri = params.text_document.uri; - let path = uri.to_file_path() - .map_err(|()| format_err!("invalid uri: {}", uri))?; - let file_id = state.add_mem_file(path, params.text_document.text); - subs.add_sub(file_id); - return Ok(()) - } - Err(not) => not, - }; - let not = match not.cast::() { - Ok(mut params) => { - let uri = params.text_document.uri; - let path = uri.to_file_path() - .map_err(|()| format_err!("invalid uri: {}", uri))?; - let text = params.content_changes.pop() - .ok_or_else(|| format_err!("empty changes"))? - .text; - state.change_mem_file(path.as_path(), text)?; - return Ok(()) - } - Err(not) => not, - }; - let not = match not.cast::() { - Ok(params) => { - let uri = params.text_document.uri; - let path = uri.to_file_path() - .map_err(|()| format_err!("invalid uri: {}", uri))?; - let file_id = state.remove_mem_file(path.as_path())?; - subs.remove_sub(file_id); - let params = req::PublishDiagnosticsParams { uri, diagnostics: Vec::new() }; - let not = RawNotification::new::(¶ms); - msg_sender.send(RawMessage::Notification(not)); - return Ok(()) - } - Err(not) => not, - }; - error!("unhandled notification: {:?}", not); - Ok(()) -} - -struct PoolDispatcher<'a> { - req: Option, - res: Option<(u64, JobHandle)>, - pool: &'a ThreadPool, - world: &'a ServerWorldState, - sender: &'a Sender, -} - -impl<'a> PoolDispatcher<'a> { - fn on<'b, R>( - &'b mut self, - f: fn(ServerWorld, R::Params, JobToken) -> Result - ) -> Result<&'b mut Self> - where R: req::Request, - R::Params: DeserializeOwned + Send + 'static, - R::Result: Serialize + 'static, - { - let req = match self.req.take() { - None => return Ok(self), - Some(req) => req, - }; - match req.cast::() { - Ok((id, params)) => { - let (handle, token) = JobHandle::new(); - let world = self.world.snapshot(); - let sender = self.sender.clone(); - self.pool.spawn(move || { - let resp = match f(world, params, token) { - Ok(resp) => RawResponse::ok::(id, &resp), - Err(e) => RawResponse::err(id, ErrorCode::InternalError as i32, e.to_string()), - }; - let task = Task::Respond(resp); - sender.send(task); - }); - self.res = Some((id, handle)); - } - Err(req) => { - self.req = Some(req) - } - } - Ok(self) - } - - fn finish(&mut self) -> ::std::result::Result<(u64, JobHandle), RawRequest> { - match (self.res.take(), self.req.take()) { - (Some(res), None) => Ok(res), - (None, Some(req)) => Err(req), - _ => unreachable!(), - } - } -} - -fn update_file_notifications_on_threadpool( - pool: &ThreadPool, - world: ServerWorld, - sender: Sender, - subscriptions: Vec, -) { - pool.spawn(move || { - for file_id in subscriptions { - match handlers::publish_diagnostics(world.clone(), file_id) { - Err(e) => { - error!("failed to compute diagnostics: {:?}", e) - } - Ok(params) => { - let not = RawNotification::new::(¶ms); - sender.send(Task::Notify(not)); - } - } - match handlers::publish_decorations(world.clone(), file_id) { - Err(e) => { - error!("failed to compute decorations: {:?}", e) - } - Ok(params) => { - let not = RawNotification::new::(¶ms); - sender.send(Task::Notify(not)) - } - } - } - }); -} - -fn feedback(intrnal_mode: bool, msg: &str, sender: &Sender) { - if !intrnal_mode { - return; - } - let not = RawNotification::new::(&msg.to_string()); - sender.send(RawMessage::Notification(not)); -} diff --git a/crates/server/src/main_loop/subscriptions.rs b/crates/server/src/main_loop/subscriptions.rs deleted file mode 100644 index 963096aef..000000000 --- a/crates/server/src/main_loop/subscriptions.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::collections::HashSet; -use libanalysis::FileId; - -pub struct Subscriptions { - subs: HashSet, -} - -impl Subscriptions { - pub fn new() -> Subscriptions { - Subscriptions { subs: HashSet::new() } - } - pub fn add_sub(&mut self, file_id: FileId) { - self.subs.insert(file_id); - } - pub fn remove_sub(&mut self, file_id: FileId) { - self.subs.remove(&file_id); - } - pub fn subscriptions(&self) -> Vec { - self.subs.iter().cloned().collect() - } -} diff --git a/crates/server/src/path_map.rs b/crates/server/src/path_map.rs deleted file mode 100644 index 282a03271..000000000 --- a/crates/server/src/path_map.rs +++ /dev/null @@ -1,110 +0,0 @@ -use std::path::{PathBuf, Path, Component}; -use im; -use relative_path::RelativePath; -use libanalysis::{FileId, FileResolver}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Root { - Workspace, Lib -} - -#[derive(Debug, Default, Clone)] -pub struct PathMap { - next_id: u32, - path2id: im::HashMap, - id2path: im::HashMap, - id2root: im::HashMap, -} - -impl PathMap { - pub fn new() -> PathMap { - Default::default() - } - pub fn get_or_insert(&mut self, path: PathBuf, root: Root) -> FileId { - self.path2id.get(path.as_path()) - .map(|&id| id) - .unwrap_or_else(|| { - let id = self.new_file_id(); - self.insert(path, id, root); - id - }) - } - pub fn get_id(&self, path: &Path) -> Option { - self.path2id.get(path).map(|&id| id) - } - pub fn get_path(&self, file_id: FileId) -> &Path { - self.id2path.get(&file_id) - .unwrap() - .as_path() - } - pub fn get_root(&self, file_id: FileId) -> Root { - self.id2root[&file_id] - } - fn insert(&mut self, path: PathBuf, file_id: FileId, root: Root) { - self.path2id.insert(path.clone(), file_id); - self.id2path.insert(file_id, path.clone()); - self.id2root.insert(file_id, root); - } - - fn new_file_id(&mut self) -> FileId { - let id = FileId(self.next_id); - self.next_id += 1; - id - } -} - -impl FileResolver for PathMap { - fn file_stem(&self, file_id: FileId) -> String { - self.get_path(file_id).file_stem().unwrap().to_str().unwrap().to_string() - } - - fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option { - let path = path.to_path(&self.get_path(file_id)); - let path = normalize(&path); - self.get_id(&path) - } -} - -fn normalize(path: &Path) -> PathBuf { - let mut components = path.components().peekable(); - let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { - components.next(); - PathBuf::from(c.as_os_str()) - } else { - PathBuf::new() - }; - - for component in components { - match component { - Component::Prefix(..) => unreachable!(), - Component::RootDir => { - ret.push(component.as_os_str()); - } - Component::CurDir => {} - Component::ParentDir => { - ret.pop(); - } - Component::Normal(c) => { - ret.push(c); - } - } - } - ret -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_resolve() { - let mut m = PathMap::new(); - let id1 = m.get_or_insert(PathBuf::from("/foo"), Root::Workspace); - let id2 = m.get_or_insert(PathBuf::from("/foo/bar.rs"), Root::Workspace); - assert_eq!( - m.resolve(id1, &RelativePath::new("bar.rs")), - Some(id2), - ) - } -} - diff --git a/crates/server/src/project_model.rs b/crates/server/src/project_model.rs deleted file mode 100644 index 359cf787d..000000000 --- a/crates/server/src/project_model.rs +++ /dev/null @@ -1,175 +0,0 @@ -use std::{ - collections::{HashMap, HashSet}, - path::{Path, PathBuf}, -}; -use cargo_metadata::{metadata_run, CargoOpt}; -use libsyntax2::SmolStr; - -use { - Result, - thread_watcher::{Worker, ThreadWatcher}, -}; - -#[derive(Debug, Clone)] -pub struct CargoWorkspace { - packages: Vec, - targets: Vec, -} - -#[derive(Clone, Copy, Debug, Serialize)] -pub struct Package(usize); -#[derive(Clone, Copy, Debug, Serialize)] -pub struct Target(usize); - -#[derive(Debug, Clone)] -struct PackageData { - name: SmolStr, - manifest: PathBuf, - targets: Vec, - is_member: bool, -} - -#[derive(Debug, Clone)] -struct TargetData { - pkg: Package, - name: SmolStr, - root: PathBuf, - kind: TargetKind, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TargetKind { - Bin, Lib, Example, Test, Bench, Other, -} - -impl Package { - pub fn name(self, ws: &CargoWorkspace) -> &str { - ws.pkg(self).name.as_str() - } - pub fn root(self, ws: &CargoWorkspace) -> &Path { - ws.pkg(self).manifest.parent().unwrap() - } - pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator + 'a { - ws.pkg(self).targets.iter().cloned() - } - pub fn is_member(self, ws: &CargoWorkspace) -> bool { - ws.pkg(self).is_member - } -} - -impl Target { - pub fn package(self, ws: &CargoWorkspace) -> Package { - ws.tgt(self).pkg - } - pub fn name(self, ws: &CargoWorkspace) -> &str { - ws.tgt(self).name.as_str() - } - pub fn root(self, ws: &CargoWorkspace) -> &Path { - ws.tgt(self).root.as_path() - } - pub fn kind(self, ws: &CargoWorkspace) -> TargetKind { - ws.tgt(self).kind - } -} - -impl CargoWorkspace { - pub fn from_cargo_metadata(path: &Path) -> Result { - let cargo_toml = find_cargo_toml(path)?; - let meta = metadata_run( - Some(cargo_toml.as_path()), - true, - Some(CargoOpt::AllFeatures) - ).map_err(|e| format_err!("cargo metadata failed: {}", e))?; - let mut pkg_by_id = HashMap::new(); - let mut packages = Vec::new(); - let mut targets = Vec::new(); - - let ws_members: HashSet = meta.workspace_members - .into_iter() - .map(|it| it.raw) - .collect(); - - for meta_pkg in meta.packages { - let pkg = Package(packages.len()); - let is_member = ws_members.contains(&meta_pkg.id); - pkg_by_id.insert(meta_pkg.id.clone(), pkg); - let mut pkg_data = PackageData { - name: meta_pkg.name.into(), - manifest: PathBuf::from(meta_pkg.manifest_path), - targets: Vec::new(), - is_member, - }; - for meta_tgt in meta_pkg.targets { - let tgt = Target(targets.len()); - targets.push(TargetData { - pkg, - name: meta_tgt.name.into(), - root: PathBuf::from(meta_tgt.src_path), - kind: TargetKind::new(meta_tgt.kind.as_slice()), - }); - pkg_data.targets.push(tgt); - } - packages.push(pkg_data) - } - - Ok(CargoWorkspace { packages, targets }) - } - pub fn packages<'a>(&'a self) -> impl Iterator + 'a { - (0..self.packages.len()).map(Package) - } - pub fn target_by_root(&self, root: &Path) -> Option { - self.packages() - .filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)) - .next() - } - fn pkg(&self, pkg: Package) -> &PackageData { - &self.packages[pkg.0] - } - fn tgt(&self, tgt: Target) -> &TargetData { - &self.targets[tgt.0] - } -} - -fn find_cargo_toml(path: &Path) -> Result { - if path.ends_with("Cargo.toml") { - return Ok(path.to_path_buf()); - } - let mut curr = Some(path); - while let Some(path) = curr { - let candidate = path.join("Cargo.toml"); - if candidate.exists() { - return Ok(candidate); - } - curr = path.parent(); - } - bail!("can't find Cargo.toml at {}", path.display()) -} - -impl TargetKind { - fn new(kinds: &[String]) -> TargetKind { - for kind in kinds { - return match kind.as_str() { - "bin" => TargetKind::Bin, - "test" => TargetKind::Test, - "bench" => TargetKind::Bench, - "example" => TargetKind::Example, - _ if kind.contains("lib") => TargetKind::Lib, - _ => continue, - } - } - TargetKind::Other - } -} - -pub fn workspace_loader() -> (Worker>, ThreadWatcher) { - Worker::>::spawn( - "workspace loader", - 1, - |input_receiver, output_sender| { - input_receiver - .into_iter() - .map(|path| CargoWorkspace::from_cargo_metadata(path.as_path())) - .for_each(|it| output_sender.send(it)) - } - ) -} diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs deleted file mode 100644 index 4af61dbbd..000000000 --- a/crates/server/src/req.rs +++ /dev/null @@ -1,176 +0,0 @@ -use std::collections::HashMap; -use languageserver_types::{TextDocumentIdentifier, Range, Url, Position, Location}; -use url_serde; - -pub use languageserver_types::{ - request::*, notification::*, - InitializeResult, PublishDiagnosticsParams, - DocumentSymbolParams, DocumentSymbolResponse, - CodeActionParams, ApplyWorkspaceEditParams, - ExecuteCommandParams, - WorkspaceSymbolParams, - TextDocumentPositionParams, - TextEdit, - CompletionParams, CompletionResponse, - DocumentOnTypeFormattingParams, - TextDocumentEdit, -}; - -pub enum SyntaxTree {} - -impl Request for SyntaxTree { - type Params = SyntaxTreeParams; - type Result = String; - const METHOD: &'static str = "m/syntaxTree"; -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct SyntaxTreeParams { - pub text_document: TextDocumentIdentifier -} - -pub enum ExtendSelection {} - -impl Request for ExtendSelection { - type Params = ExtendSelectionParams; - type Result = ExtendSelectionResult; - const METHOD: &'static str = "m/extendSelection"; -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct ExtendSelectionParams { - pub text_document: TextDocumentIdentifier, - pub selections: Vec, -} - -#[derive(Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct ExtendSelectionResult { - pub selections: Vec, -} - -pub enum FindMatchingBrace {} - -impl Request for FindMatchingBrace { - type Params = FindMatchingBraceParams; - type Result = Vec; - const METHOD: &'static str = "m/findMatchingBrace"; -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct FindMatchingBraceParams { - pub text_document: TextDocumentIdentifier, - pub offsets: Vec, -} - -pub enum DecorationsRequest {} - -impl Request for DecorationsRequest { - type Params = TextDocumentIdentifier; - type Result = Vec; - const METHOD: &'static str = "m/decorationsRequest"; -} - -pub enum PublishDecorations {} - -impl Notification for PublishDecorations { - type Params = PublishDecorationsParams; - const METHOD: &'static str = "m/publishDecorations"; -} - -#[derive(Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct PublishDecorationsParams { - #[serde(with = "url_serde")] - pub uri: Url, - pub decorations: Vec, -} - -#[derive(Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Decoration { - pub range: Range, - pub tag: &'static str -} - -pub enum ParentModule {} - -impl Request for ParentModule { - type Params = TextDocumentIdentifier; - type Result = Vec; - const METHOD: &'static str = "m/parentModule"; -} - -pub enum JoinLines {} - -impl Request for JoinLines { - type Params = JoinLinesParams; - type Result = SourceChange; - const METHOD: &'static str = "m/joinLines"; -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct JoinLinesParams { - pub text_document: TextDocumentIdentifier, - pub range: Range, -} - -pub enum Runnables {} - -impl Request for Runnables { - type Params = RunnablesParams; - type Result = Vec; - const METHOD: &'static str = "m/runnables"; -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct RunnablesParams { - pub text_document: TextDocumentIdentifier, - pub position: Option, -} - -#[derive(Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Runnable { - pub range: Range, - pub label: String, - pub bin: String, - 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, - } -} - -pub enum InternalFeedback {} - -impl Notification for InternalFeedback { - const METHOD: &'static str = "internalFeedback"; - type Params = String; -} diff --git a/crates/server/src/server_world.rs b/crates/server/src/server_world.rs deleted file mode 100644 index 6853efc00..000000000 --- a/crates/server/src/server_world.rs +++ /dev/null @@ -1,167 +0,0 @@ -use std::{ - fs, - path::{PathBuf, Path}, - collections::HashMap, - sync::Arc, -}; - -use languageserver_types::Url; -use libanalysis::{FileId, AnalysisHost, Analysis, CrateGraph, CrateId, LibraryData, FileResolver}; - -use { - Result, - path_map::{PathMap, Root}, - vfs::{FileEvent, FileEventKind}, - project_model::CargoWorkspace, -}; - -#[derive(Debug)] -pub struct ServerWorldState { - pub workspaces: Arc>, - pub analysis_host: AnalysisHost, - pub path_map: PathMap, - pub mem_map: HashMap>, -} - -#[derive(Clone)] -pub struct ServerWorld { - pub workspaces: Arc>, - pub analysis: Analysis, - pub path_map: PathMap, -} - -impl ServerWorldState { - pub fn new() -> ServerWorldState { - ServerWorldState { - workspaces: Arc::new(Vec::new()), - analysis_host: AnalysisHost::new(), - path_map: PathMap::new(), - mem_map: HashMap::new(), - } - } - pub fn apply_fs_changes(&mut self, events: Vec) { - { - let pm = &mut self.path_map; - let mm = &mut self.mem_map; - let changes = events.into_iter() - .map(|event| { - let text = match event.kind { - FileEventKind::Add(text) => Some(text), - }; - (event.path, text) - }) - .map(|(path, text)| { - (pm.get_or_insert(path, Root::Workspace), text) - }) - .filter_map(|(id, text)| { - if mm.contains_key(&id) { - mm.insert(id, text); - None - } else { - Some((id, text)) - } - }); - self.analysis_host.change_files(changes); - } - self.analysis_host.set_file_resolver(Arc::new(self.path_map.clone())); - } - pub fn events_to_files(&mut self, events: Vec) -> (Vec<(FileId, String)>, Arc) { - let files = { - let pm = &mut self.path_map; - events.into_iter() - .map(|event| { - let text = match event.kind { - FileEventKind::Add(text) => text, - }; - (event.path, text) - }) - .map(|(path, text)| (pm.get_or_insert(path, Root::Lib), text)) - .collect() - }; - let resolver = Arc::new(self.path_map.clone()); - (files, resolver) - } - pub fn add_lib(&mut self, data: LibraryData) { - self.analysis_host.add_library(data); - } - - pub fn add_mem_file(&mut self, path: PathBuf, text: String) -> FileId { - let file_id = self.path_map.get_or_insert(path, Root::Workspace); - self.analysis_host.set_file_resolver(Arc::new(self.path_map.clone())); - self.mem_map.insert(file_id, None); - if self.path_map.get_root(file_id) != Root::Lib { - self.analysis_host.change_file(file_id, Some(text)); - } - file_id - } - - pub fn change_mem_file(&mut self, path: &Path, text: String) -> Result<()> { - let file_id = self.path_map.get_id(path).ok_or_else(|| { - format_err!("change to unknown file: {}", path.display()) - })?; - if self.path_map.get_root(file_id) != Root::Lib { - self.analysis_host.change_file(file_id, Some(text)); - } - Ok(()) - } - - pub fn remove_mem_file(&mut self, path: &Path) -> Result { - let file_id = self.path_map.get_id(path).ok_or_else(|| { - format_err!("change to unknown file: {}", path.display()) - })?; - match self.mem_map.remove(&file_id) { - Some(_) => (), - None => bail!("unmatched close notification"), - }; - // Do this via file watcher ideally. - let text = fs::read_to_string(path).ok(); - if self.path_map.get_root(file_id) != Root::Lib { - self.analysis_host.change_file(file_id, text); - } - Ok(file_id) - } - pub fn set_workspaces(&mut self, ws: Vec) { - let mut crate_roots = HashMap::new(); - ws.iter() - .flat_map(|ws| { - ws.packages() - .flat_map(move |pkg| pkg.targets(ws)) - .map(move |tgt| tgt.root(ws)) - }) - .for_each(|root| { - if let Some(file_id) = self.path_map.get_id(root) { - let crate_id = CrateId(crate_roots.len() as u32); - crate_roots.insert(crate_id, file_id); - } - }); - let crate_graph = CrateGraph { crate_roots }; - self.workspaces = Arc::new(ws); - self.analysis_host.set_crate_graph(crate_graph); - } - pub fn snapshot(&self) -> ServerWorld { - ServerWorld { - workspaces: Arc::clone(&self.workspaces), - analysis: self.analysis_host.analysis(), - path_map: self.path_map.clone() - } - } -} - -impl ServerWorld { - pub fn analysis(&self) -> &Analysis { - &self.analysis - } - - pub fn uri_to_file_id(&self, uri: &Url) -> Result { - let path = uri.to_file_path() - .map_err(|()| format_err!("invalid uri: {}", uri))?; - self.path_map.get_id(&path).ok_or_else(|| format_err!("unknown file: {}", path.display())) - } - - pub fn file_id_to_uri(&self, id: FileId) -> Result { - let path = self.path_map.get_path(id); - let url = Url::from_file_path(path) - .map_err(|()| format_err!("can't convert path to url: {}", path.display()))?; - Ok(url) - } -} diff --git a/crates/server/src/thread_watcher.rs b/crates/server/src/thread_watcher.rs deleted file mode 100644 index 86a3a91e0..000000000 --- a/crates/server/src/thread_watcher.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::thread; -use crossbeam_channel::{bounded, unbounded, Sender, Receiver}; -use drop_bomb::DropBomb; -use Result; - -pub struct Worker { - pub inp: Sender, - pub out: Receiver, -} - -impl Worker { - pub fn spawn(name: &'static str, buf: usize, f: F) -> (Self, ThreadWatcher) - where - F: FnOnce(Receiver, Sender) + Send + 'static, - I: Send + 'static, - O: Send + 'static, - { - let ((inp, out), inp_r, out_s) = worker_chan(buf); - let worker = Worker { inp, out }; - let watcher = ThreadWatcher::spawn(name, move || f(inp_r, out_s)); - (worker, watcher) - } - - pub fn stop(self) -> Receiver { - self.out - } - - pub fn send(&self, item: I) { - self.inp.send(item) - } -} - -pub struct ThreadWatcher { - name: &'static str, - thread: thread::JoinHandle<()>, - bomb: DropBomb, -} - -impl ThreadWatcher { - fn spawn(name: &'static str, f: impl FnOnce() + Send + 'static) -> ThreadWatcher { - let thread = thread::spawn(f); - ThreadWatcher { - name, - thread, - bomb: DropBomb::new(format!("ThreadWatcher {} was not stopped", name)), - } - } - - pub fn stop(mut self) -> Result<()> { - info!("waiting for {} to finish ...", self.name); - let name = self.name; - self.bomb.defuse(); - let res = self.thread.join() - .map_err(|_| format_err!("ThreadWatcher {} died", name)); - match &res { - Ok(()) => info!("... {} terminated with ok", name), - Err(_) => error!("... {} terminated with err", name) - } - res - } -} - -/// Sets up worker channels in a deadlock-avoind way. -/// If one sets both input and output buffers to a fixed size, -/// a worker might get stuck. -fn worker_chan(buf: usize) -> ((Sender, Receiver), Receiver, Sender) { - let (input_sender, input_receiver) = bounded::(buf); - let (output_sender, output_receiver) = unbounded::(); - ((input_sender, output_receiver), input_receiver, output_sender) -} diff --git a/crates/server/src/vfs.rs b/crates/server/src/vfs.rs deleted file mode 100644 index a1c1783f2..000000000 --- a/crates/server/src/vfs.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::{ - path::{PathBuf, Path}, - fs, -}; - -use walkdir::WalkDir; - -use { - thread_watcher::{Worker, ThreadWatcher}, -}; - - -#[derive(Debug)] -pub struct FileEvent { - pub path: PathBuf, - pub kind: FileEventKind, -} - -#[derive(Debug)] -pub enum FileEventKind { - Add(String), -} - -pub fn roots_loader() -> (Worker)>, ThreadWatcher) { - Worker::)>::spawn( - "roots loader", - 128, |input_receiver, output_sender| { - input_receiver - .into_iter() - .map(|path| { - debug!("loading {} ...", path.as_path().display()); - let events = load_root(path.as_path()); - debug!("... loaded {}", path.as_path().display()); - (path, events) - }) - .for_each(|it| output_sender.send(it)) - } - ) -} - -fn load_root(path: &Path) -> Vec { - let mut res = Vec::new(); - for entry in WalkDir::new(path) { - let entry = match entry { - Ok(entry) => entry, - Err(e) => { - warn!("watcher error: {}", e); - continue; - } - }; - if !entry.file_type().is_file() { - continue; - } - let path = entry.path(); - if path.extension().and_then(|os| os.to_str()) != Some("rs") { - continue; - } - let text = match fs::read_to_string(path) { - Ok(text) => text, - Err(e) => { - warn!("watcher error: {}", e); - continue; - } - }; - res.push(FileEvent { - path: path.to_owned(), - kind: FileEventKind::Add(text), - }) - } - res -} -- cgit v1.2.3