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_symbol_provider: Some(true),
workspace_symbol_provider: None,
code_action_provider: None,
code_action_provider: Some(true),
code_lens_provider: None,
document_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 libsyntax2::SyntaxKind;
use languageserver_types::{
Diagnostic, DiagnosticSeverity, Url, DocumentSymbol,
Command
};
use libanalysis::World;
use libeditor::{self, LineIndex, LineCol, TextRange, TextUnit};
use libeditor;
use serde_json::to_value;
use ::{
req::{self, Decoration}, Result,
util::FilePath,
conv::{Conv, ConvWith},
};
pub fn handle_syntax_tree(
@ -25,11 +29,9 @@ pub fn handle_extend_selection(
let file = world.file_syntax(&path)?;
let line_index = world.file_line_index(&path)?;
let selections = params.selections.into_iter()
.map(|r| {
let r = to_text_range(&line_index, r);
let r = libeditor::extend_selection(&file, r).unwrap_or(r);
to_vs_range(&line_index, r)
})
.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 })
}
@ -48,10 +50,10 @@ pub fn handle_document_symbol(
let doc_symbol = DocumentSymbol {
name: symbol.name.clone(),
detail: Some(symbol.name),
kind: to_symbol_kind(symbol.kind),
kind: symbol.kind.conv(),
deprecated: None,
range: to_vs_range(&line_index, symbol.node_range),
selection_range: to_vs_range(&line_index, symbol.name_range),
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 {
@ -67,17 +69,40 @@ pub fn handle_document_symbol(
Ok(Some(req::DocumentSymbolResponse::Nested(res)))
}
fn to_symbol_kind(kind: SyntaxKind) -> SymbolKind {
match kind {
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,
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 `,`",
}
}
}
@ -88,7 +113,7 @@ pub fn publish_diagnostics(world: World, uri: Url) -> Result<req::PublishDiagnos
let diagnostics = libeditor::diagnostics(&file)
.into_iter()
.map(|d| Diagnostic {
range: to_vs_range(&line_index, d.range),
range: d.range.conv_with(&line_index),
severity: Some(DiagnosticSeverity::Error),
code: None,
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)
.into_iter()
.map(|h| Decoration {
range: to_vs_range(&line_index, h.range),
range: h.range.conv_with(&line_index),
tag: h.tag,
}).collect();
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 handlers;
mod util;
mod conv;
use threadpool::ThreadPool;
use crossbeam_channel::{bounded, Sender, Receiver};
@ -33,7 +34,7 @@ use libanalysis::{WorldState, World};
use ::{
io::{Io, RawMsg, RawRequest},
handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics, publish_decorations,
handle_document_symbol},
handle_document_symbol, handle_code_action},
util::{FilePath, FnBox}
};
@ -182,6 +183,10 @@ fn main_loop(
handle_request_on_threadpool::<req::DocumentSymbolRequest>(
&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;
dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| {
resp.result(io, ())?;

View File

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