use languageserver_types::{ Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, Command }; use libanalysis::World; use libeditor; use serde_json::to_value; use ::{ req::{self, Decoration}, Result, util::FilePath, conv::{Conv, ConvWith}, }; pub fn handle_syntax_tree( world: World, params: req::SyntaxTreeParams, ) -> Result<String> { let path = params.text_document.file_path()?; let file = world.file_syntax(&path)?; Ok(libeditor::syntax_tree(&file)) } pub fn handle_extend_selection( world: World, params: req::ExtendSelectionParams, ) -> Result<req::ExtendSelectionResult> { let path = params.text_document.file_path()?; let file = world.file_syntax(&path)?; let line_index = world.file_line_index(&path)?; let selections = params.selections.into_iter() .map(|r| r.conv_with(&line_index)) .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r)) .map(|r| r.conv_with(&line_index)) .collect(); Ok(req::ExtendSelectionResult { selections }) } pub fn handle_document_symbol( world: World, params: req::DocumentSymbolParams, ) -> Result<Option<req::DocumentSymbolResponse>> { let path = params.text_document.file_path()?; let file = world.file_syntax(&path)?; let line_index = world.file_line_index(&path)?; let mut res: Vec<DocumentSymbol> = Vec::new(); for symbol in libeditor::file_symbols(&file) { let doc_symbol = DocumentSymbol { name: symbol.name.clone(), detail: Some(symbol.name), kind: symbol.kind.conv(), deprecated: None, range: symbol.node_range.conv_with(&line_index), selection_range: symbol.name_range.conv_with(&line_index), children: None, }; if let Some(idx) = symbol.parent { let children = &mut res[idx].children; if children.is_none() { *children = Some(Vec::new()); } children.as_mut().unwrap().push(doc_symbol); } else { res.push(doc_symbol); } } Ok(Some(req::DocumentSymbolResponse::Nested(res))) } pub fn handle_code_action( world: World, params: req::CodeActionParams, ) -> Result<Option<Vec<Command>>> { let path = params.text_document.file_path()?; let file = world.file_syntax(&path)?; let line_index = world.file_line_index(&path)?; let offset = params.range.conv_with(&line_index).start(); let ret = if libeditor::flip_comma(&file, offset).is_some() { Some(vec![apply_code_action_cmd(ActionId::FlipComma)]) } else { None }; Ok(ret) } fn apply_code_action_cmd(id: ActionId) -> Command { Command { title: id.title().to_string(), command: "apply_code_action".to_string(), arguments: Some(vec![to_value(id).unwrap()]), } } #[derive(Serialize, Deserialize, Clone, Copy)] enum ActionId { FlipComma } impl ActionId { fn title(&self) -> &'static str { match *self { ActionId::FlipComma => "Flip `,`", } } } pub fn publish_diagnostics(world: World, uri: Url) -> Result<req::PublishDiagnosticsParams> { let path = uri.file_path()?; let file = world.file_syntax(&path)?; let line_index = world.file_line_index(&path)?; let diagnostics = libeditor::diagnostics(&file) .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.msg, related_information: None, }).collect(); Ok(req::PublishDiagnosticsParams { uri, diagnostics }) } pub fn publish_decorations(world: World, uri: Url) -> Result<req::PublishDecorationsParams> { let path = uri.file_path()?; let file = world.file_syntax(&path)?; let line_index = world.file_line_index(&path)?; let decorations = libeditor::highlight(&file) .into_iter() .map(|h| Decoration { range: h.range.conv_with(&line_index), tag: h.tag, }).collect(); Ok(req::PublishDecorationsParams { uri, decorations }) }