Half of code-actions

This commit is contained in:
Aleksey Kladov 2018-08-12 21:02:56 +03:00
parent 66be735aa9
commit 23c06db9c2
5 changed files with 139 additions and 57 deletions

View file

@ -25,7 +25,7 @@ pub const SERVER_CAPABILITIES: ServerCapabilities = ServerCapabilities {
document_highlight_provider: None, document_highlight_provider: None,
document_symbol_provider: Some(true), document_symbol_provider: Some(true),
workspace_symbol_provider: None, workspace_symbol_provider: None,
code_action_provider: None, code_action_provider: Some(true),
code_lens_provider: None, code_lens_provider: None,
document_formatting_provider: None, document_formatting_provider: None,
document_range_formatting_provider: None, document_range_formatting_provider: None,

81
crates/server/src/conv.rs Normal file
View file

@ -0,0 +1,81 @@
use languageserver_types::{Range, SymbolKind, Position};
use libeditor::{LineIndex, LineCol};
use libsyntax2::{SyntaxKind, TextUnit, TextRange};
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;
}
impl Conv for SyntaxKind {
type Output = SymbolKind;
fn conv(&self) -> <Self as Conv>::Output {
match *self {
SyntaxKind::FUNCTION => SymbolKind::Function,
SyntaxKind::STRUCT => SymbolKind::Struct,
SyntaxKind::ENUM => SymbolKind::Enum,
SyntaxKind::TRAIT => SymbolKind::Interface,
SyntaxKind::MODULE => SymbolKind::Module,
SyntaxKind::TYPE_ITEM => SymbolKind::TypeParameter,
SyntaxKind::STATIC_ITEM => SymbolKind::Constant,
SyntaxKind::CONST_ITEM => SymbolKind::Constant,
_ => 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),
)
}
}

View file

@ -1,11 +1,15 @@
use languageserver_types::{Range, Position, Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, SymbolKind}; use languageserver_types::{
use libsyntax2::SyntaxKind; Diagnostic, DiagnosticSeverity, Url, DocumentSymbol,
Command
};
use libanalysis::World; use libanalysis::World;
use libeditor::{self, LineIndex, LineCol, TextRange, TextUnit}; use libeditor;
use serde_json::to_value;
use ::{ use ::{
req::{self, Decoration}, Result, req::{self, Decoration}, Result,
util::FilePath, util::FilePath,
conv::{Conv, ConvWith},
}; };
pub fn handle_syntax_tree( pub fn handle_syntax_tree(
@ -25,11 +29,9 @@ pub fn handle_extend_selection(
let file = world.file_syntax(&path)?; let file = world.file_syntax(&path)?;
let line_index = world.file_line_index(&path)?; let line_index = world.file_line_index(&path)?;
let selections = params.selections.into_iter() let selections = params.selections.into_iter()
.map(|r| { .map(|r| r.conv_with(&line_index))
let r = to_text_range(&line_index, r); .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r))
let r = libeditor::extend_selection(&file, r).unwrap_or(r); .map(|r| r.conv_with(&line_index))
to_vs_range(&line_index, r)
})
.collect(); .collect();
Ok(req::ExtendSelectionResult { selections }) Ok(req::ExtendSelectionResult { selections })
} }
@ -48,10 +50,10 @@ pub fn handle_document_symbol(
let doc_symbol = DocumentSymbol { let doc_symbol = DocumentSymbol {
name: symbol.name.clone(), name: symbol.name.clone(),
detail: Some(symbol.name), detail: Some(symbol.name),
kind: to_symbol_kind(symbol.kind), kind: symbol.kind.conv(),
deprecated: None, deprecated: None,
range: to_vs_range(&line_index, symbol.node_range), range: symbol.node_range.conv_with(&line_index),
selection_range: to_vs_range(&line_index, symbol.name_range), selection_range: symbol.name_range.conv_with(&line_index),
children: None, children: None,
}; };
if let Some(idx) = symbol.parent { if let Some(idx) = symbol.parent {
@ -67,17 +69,40 @@ pub fn handle_document_symbol(
Ok(Some(req::DocumentSymbolResponse::Nested(res))) Ok(Some(req::DocumentSymbolResponse::Nested(res)))
} }
fn to_symbol_kind(kind: SyntaxKind) -> SymbolKind { pub fn handle_code_action(
match kind { world: World,
SyntaxKind::FUNCTION => SymbolKind::Function, params: req::CodeActionParams,
SyntaxKind::STRUCT => SymbolKind::Struct, ) -> Result<Option<Vec<Command>>> {
SyntaxKind::ENUM => SymbolKind::Enum, let path = params.text_document.file_path()?;
SyntaxKind::TRAIT => SymbolKind::Interface, let file = world.file_syntax(&path)?;
SyntaxKind::MODULE => SymbolKind::Module, let line_index = world.file_line_index(&path)?;
SyntaxKind::TYPE_ITEM => SymbolKind::TypeParameter, let offset = params.range.conv_with(&line_index).start();
SyntaxKind::STATIC_ITEM => SymbolKind::Constant, let ret = if libeditor::flip_comma(&file, offset).is_some() {
SyntaxKind::CONST_ITEM => SymbolKind::Constant, Some(vec![apply_code_action_cmd(ActionId::FlipComma)])
_ => SymbolKind::Variable, } 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 `,`",
}
} }
} }
@ -88,7 +113,7 @@ pub fn publish_diagnostics(world: World, uri: Url) -> Result<req::PublishDiagnos
let diagnostics = libeditor::diagnostics(&file) let diagnostics = libeditor::diagnostics(&file)
.into_iter() .into_iter()
.map(|d| Diagnostic { .map(|d| Diagnostic {
range: to_vs_range(&line_index, d.range), range: d.range.conv_with(&line_index),
severity: Some(DiagnosticSeverity::Error), severity: Some(DiagnosticSeverity::Error),
code: None, code: None,
source: Some("libsyntax2".to_string()), source: Some("libsyntax2".to_string()),
@ -105,38 +130,8 @@ pub fn publish_decorations(world: World, uri: Url) -> Result<req::PublishDecorat
let decorations = libeditor::highlight(&file) let decorations = libeditor::highlight(&file)
.into_iter() .into_iter()
.map(|h| Decoration { .map(|h| Decoration {
range: to_vs_range(&line_index, h.range), range: h.range.conv_with(&line_index),
tag: h.tag, tag: h.tag,
}).collect(); }).collect();
Ok(req::PublishDecorationsParams { uri, decorations }) Ok(req::PublishDecorationsParams { uri, decorations })
} }
fn to_text_range(line_index: &LineIndex, range: Range) -> TextRange {
TextRange::from_to(
to_text_unit(line_index, range.start),
to_text_unit(line_index, range.end),
)
}
fn to_text_unit(line_index: &LineIndex, position: Position) -> TextUnit {
// TODO: UTF-16
let line_col = LineCol {
line: position.line as u32,
col: (position.character as u32).into(),
};
line_index.offset(line_col)
}
fn to_vs_range(line_index: &LineIndex, range: TextRange) -> Range {
Range::new(
to_vs_position(line_index, range.start()),
to_vs_position(line_index, range.end()),
)
}
fn to_vs_position(line_index: &LineIndex, offset: TextUnit) -> Position {
let line_col = line_index.line_col(offset);
// TODO: UTF-16
Position::new(line_col.line as u64, u32::from(line_col.col) as u64)
}

View file

@ -23,6 +23,7 @@ mod req;
mod dispatch; mod dispatch;
mod handlers; mod handlers;
mod util; mod util;
mod conv;
use threadpool::ThreadPool; use threadpool::ThreadPool;
use crossbeam_channel::{bounded, Sender, Receiver}; use crossbeam_channel::{bounded, Sender, Receiver};
@ -33,7 +34,7 @@ use libanalysis::{WorldState, World};
use ::{ use ::{
io::{Io, RawMsg, RawRequest}, io::{Io, RawMsg, RawRequest},
handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics, publish_decorations, handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics, publish_decorations,
handle_document_symbol}, handle_document_symbol, handle_code_action},
util::{FilePath, FnBox} util::{FilePath, FnBox}
}; };
@ -182,6 +183,10 @@ fn main_loop(
handle_request_on_threadpool::<req::DocumentSymbolRequest>( handle_request_on_threadpool::<req::DocumentSymbolRequest>(
&mut req, pool, world, &sender, handle_document_symbol &mut req, pool, world, &sender, handle_document_symbol
)?; )?;
handle_request_on_threadpool::<req::CodeActionRequest>(
&mut req, pool, world, &sender, handle_code_action
)?;
let mut shutdown = false; let mut shutdown = false;
dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| {
resp.result(io, ())?; resp.result(io, ())?;

View file

@ -5,7 +5,8 @@ use url_serde;
pub use languageserver_types::{ pub use languageserver_types::{
request::*, notification::*, request::*, notification::*,
InitializeResult, PublishDiagnosticsParams, InitializeResult, PublishDiagnosticsParams,
DocumentSymbolParams, DocumentSymbolResponse DocumentSymbolParams, DocumentSymbolResponse,
CodeActionParams,
}; };