mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Apply code actions
This commit is contained in:
parent
25aebb5225
commit
be742a5877
12 changed files with 247 additions and 118 deletions
|
@ -10,7 +10,8 @@
|
||||||
"vscode": "^1.25.0"
|
"vscode": "^1.25.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"compile": "tsc -p ./",
|
"vscode:prepublish": "tsc -p ./",
|
||||||
|
"compile": "tsc -watch -p ./",
|
||||||
"postinstall": "node ./node_modules/vscode/bin/install"
|
"postinstall": "node ./node_modules/vscode/bin/install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use {TextRange, TextUnit};
|
use {TextRange, TextUnit};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Edit {
|
pub struct Edit {
|
||||||
pub atoms: Vec<AtomEdit>,
|
atoms: Vec<AtomEdit>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AtomEdit {
|
pub struct AtomEdit {
|
||||||
pub delete: TextRange,
|
pub delete: TextRange,
|
||||||
pub insert: String,
|
pub insert: String,
|
||||||
|
@ -22,7 +22,6 @@ impl EditBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace(&mut self, range: TextRange, replacement: String) {
|
pub fn replace(&mut self, range: TextRange, replacement: String) {
|
||||||
let range = self.translate(range);
|
|
||||||
self.atoms.push(AtomEdit { delete: range, insert: replacement })
|
self.atoms.push(AtomEdit { delete: range, insert: replacement })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,59 +34,47 @@ impl EditBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(self) -> Edit {
|
pub fn finish(self) -> Edit {
|
||||||
Edit { atoms: self.atoms }
|
let mut atoms = self.atoms;
|
||||||
|
atoms.sort_by_key(|a| a.delete.start());
|
||||||
|
for (a1, a2) in atoms.iter().zip(atoms.iter().skip(1)) {
|
||||||
|
assert!(a1.end() <= a2.start())
|
||||||
}
|
}
|
||||||
|
Edit { atoms }
|
||||||
fn translate(&self, range: TextRange) -> TextRange {
|
|
||||||
let mut range = range;
|
|
||||||
for atom in self.atoms.iter() {
|
|
||||||
range = atom.apply_to_range(range)
|
|
||||||
.expect("conflicting edits");
|
|
||||||
}
|
|
||||||
range
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Edit {
|
impl Edit {
|
||||||
pub fn apply(&self, text: &str) -> String {
|
pub fn into_atoms(self) -> Vec<AtomEdit> {
|
||||||
let mut text = text.to_owned();
|
self.atoms
|
||||||
for atom in self.atoms.iter() {
|
|
||||||
text = atom.apply(&text);
|
|
||||||
}
|
}
|
||||||
text
|
|
||||||
|
pub fn apply(&self, text: &str) -> String {
|
||||||
|
let mut total_len = text.len();
|
||||||
|
for atom in self.atoms.iter() {
|
||||||
|
total_len += atom.insert.len();
|
||||||
|
total_len -= atom.end() - atom.start();
|
||||||
|
}
|
||||||
|
let mut buf = String::with_capacity(total_len);
|
||||||
|
let mut prev = 0;
|
||||||
|
for atom in self.atoms.iter() {
|
||||||
|
if atom.start() > prev {
|
||||||
|
buf.push_str(&text[prev..atom.start()]);
|
||||||
|
}
|
||||||
|
buf.push_str(&atom.insert);
|
||||||
|
prev = atom.end();
|
||||||
|
}
|
||||||
|
buf.push_str(&text[prev..text.len()]);
|
||||||
|
assert_eq!(buf.len(), total_len);
|
||||||
|
buf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AtomEdit {
|
impl AtomEdit {
|
||||||
fn apply(&self, text: &str) -> String {
|
fn start(&self) -> usize {
|
||||||
let prefix = &text[
|
u32::from(self.delete.start()) as usize
|
||||||
TextRange::from_to(0.into(), self.delete.start())
|
|
||||||
];
|
|
||||||
let suffix = &text[
|
|
||||||
TextRange::from_to(self.delete.end(), TextUnit::of_str(text))
|
|
||||||
];
|
|
||||||
let mut res = String::with_capacity(prefix.len() + self.insert.len() + suffix.len());
|
|
||||||
res.push_str(prefix);
|
|
||||||
res.push_str(&self.insert);
|
|
||||||
res.push_str(suffix);
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_to_position(&self, pos: TextUnit) -> Option<TextUnit> {
|
fn end(&self) -> usize {
|
||||||
if pos <= self.delete.start() {
|
u32::from(self.delete.end()) as usize
|
||||||
return Some(pos);
|
|
||||||
}
|
|
||||||
if pos < self.delete.end() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(pos - self.delete.len() + TextUnit::of_str(&self.insert))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_to_range(&self, range: TextRange) -> Option<TextRange> {
|
|
||||||
Some(TextRange::from_to(
|
|
||||||
self.apply_to_position(range.start())?,
|
|
||||||
self.apply_to_position(range.end())?,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub use self::{
|
||||||
line_index::{LineIndex, LineCol},
|
line_index::{LineIndex, LineCol},
|
||||||
extend_selection::extend_selection,
|
extend_selection::extend_selection,
|
||||||
symbols::{FileSymbol, file_symbols},
|
symbols::{FileSymbol, file_symbols},
|
||||||
edit::{EditBuilder, Edit},
|
edit::{EditBuilder, Edit, AtomEdit},
|
||||||
code_actions::{flip_comma},
|
code_actions::{flip_comma},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,11 @@ serde_derive = "1.0.71"
|
||||||
drop_bomb = "0.1.0"
|
drop_bomb = "0.1.0"
|
||||||
crossbeam-channel = "0.2.4"
|
crossbeam-channel = "0.2.4"
|
||||||
threadpool = "1.7.1"
|
threadpool = "1.7.1"
|
||||||
flexi_logger = "0.9.0"
|
flexi_logger = "0.9.1"
|
||||||
log = "0.4.3"
|
log = "0.4.3"
|
||||||
url_serde = "0.2.0"
|
url_serde = "0.2.0"
|
||||||
languageserver-types = "0.49.0"
|
languageserver-types = "0.49.0"
|
||||||
|
text_unit = { version = "0.1.2", features = ["serde"] }
|
||||||
|
|
||||||
libsyntax2 = { path = "../libsyntax2" }
|
libsyntax2 = { path = "../libsyntax2" }
|
||||||
libeditor = { path = "../libeditor" }
|
libeditor = { path = "../libeditor" }
|
||||||
|
|
|
@ -3,9 +3,11 @@ use languageserver_types::{
|
||||||
TextDocumentSyncCapability,
|
TextDocumentSyncCapability,
|
||||||
TextDocumentSyncOptions,
|
TextDocumentSyncOptions,
|
||||||
TextDocumentSyncKind,
|
TextDocumentSyncKind,
|
||||||
|
ExecuteCommandOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const SERVER_CAPABILITIES: ServerCapabilities = ServerCapabilities {
|
pub fn server_capabilities() -> ServerCapabilities {
|
||||||
|
ServerCapabilities {
|
||||||
text_document_sync: Some(TextDocumentSyncCapability::Options(
|
text_document_sync: Some(TextDocumentSyncCapability::Options(
|
||||||
TextDocumentSyncOptions {
|
TextDocumentSyncOptions {
|
||||||
open_close: Some(true),
|
open_close: Some(true),
|
||||||
|
@ -32,5 +34,8 @@ pub const SERVER_CAPABILITIES: ServerCapabilities = ServerCapabilities {
|
||||||
document_on_type_formatting_provider: None,
|
document_on_type_formatting_provider: None,
|
||||||
rename_provider: None,
|
rename_provider: None,
|
||||||
color_provider: None,
|
color_provider: None,
|
||||||
execute_command_provider: None,
|
execute_command_provider: Some(ExecuteCommandOptions {
|
||||||
};
|
commands: vec!["apply_code_action".to_string()],
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
use languageserver_types::{Range, SymbolKind, Position};
|
use languageserver_types::{Range, SymbolKind, Position, TextEdit};
|
||||||
use libeditor::{LineIndex, LineCol};
|
use libeditor::{LineIndex, LineCol, Edit, AtomEdit};
|
||||||
use libsyntax2::{SyntaxKind, TextUnit, TextRange};
|
use libsyntax2::{SyntaxKind, TextUnit, TextRange};
|
||||||
|
|
||||||
pub trait Conv {
|
pub trait Conv {
|
||||||
type Output;
|
type Output;
|
||||||
fn conv(&self) -> Self::Output;
|
fn conv(self) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ConvWith {
|
pub trait ConvWith {
|
||||||
type Ctx;
|
type Ctx;
|
||||||
type Output;
|
type Output;
|
||||||
fn conv_with(&self, ctx: &Self::Ctx) -> Self::Output;
|
fn conv_with(self, ctx: &Self::Ctx) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Conv for SyntaxKind {
|
impl Conv for SyntaxKind {
|
||||||
type Output = SymbolKind;
|
type Output = SymbolKind;
|
||||||
|
|
||||||
fn conv(&self) -> <Self as Conv>::Output {
|
fn conv(self) -> <Self as Conv>::Output {
|
||||||
match *self {
|
match self {
|
||||||
SyntaxKind::FUNCTION => SymbolKind::Function,
|
SyntaxKind::FUNCTION => SymbolKind::Function,
|
||||||
SyntaxKind::STRUCT => SymbolKind::Struct,
|
SyntaxKind::STRUCT => SymbolKind::Struct,
|
||||||
SyntaxKind::ENUM => SymbolKind::Enum,
|
SyntaxKind::ENUM => SymbolKind::Enum,
|
||||||
|
@ -35,7 +35,7 @@ impl ConvWith for Position {
|
||||||
type Ctx = LineIndex;
|
type Ctx = LineIndex;
|
||||||
type Output = TextUnit;
|
type Output = TextUnit;
|
||||||
|
|
||||||
fn conv_with(&self, line_index: &LineIndex) -> TextUnit {
|
fn conv_with(self, line_index: &LineIndex) -> TextUnit {
|
||||||
// TODO: UTF-16
|
// TODO: UTF-16
|
||||||
let line_col = LineCol {
|
let line_col = LineCol {
|
||||||
line: self.line as u32,
|
line: self.line as u32,
|
||||||
|
@ -49,8 +49,8 @@ impl ConvWith for TextUnit {
|
||||||
type Ctx = LineIndex;
|
type Ctx = LineIndex;
|
||||||
type Output = Position;
|
type Output = Position;
|
||||||
|
|
||||||
fn conv_with(&self, line_index: &LineIndex) -> Position {
|
fn conv_with(self, line_index: &LineIndex) -> Position {
|
||||||
let line_col = line_index.line_col(*self);
|
let line_col = line_index.line_col(self);
|
||||||
// TODO: UTF-16
|
// TODO: UTF-16
|
||||||
Position::new(line_col.line as u64, u32::from(line_col.col) as u64)
|
Position::new(line_col.line as u64, u32::from(line_col.col) as u64)
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ impl ConvWith for TextRange {
|
||||||
type Ctx = LineIndex;
|
type Ctx = LineIndex;
|
||||||
type Output = Range;
|
type Output = Range;
|
||||||
|
|
||||||
fn conv_with(&self, line_index: &LineIndex) -> Range {
|
fn conv_with(self, line_index: &LineIndex) -> Range {
|
||||||
Range::new(
|
Range::new(
|
||||||
self.start().conv_with(line_index),
|
self.start().conv_with(line_index),
|
||||||
self.end().conv_with(line_index),
|
self.end().conv_with(line_index),
|
||||||
|
@ -72,10 +72,70 @@ impl ConvWith for Range {
|
||||||
type Ctx = LineIndex;
|
type Ctx = LineIndex;
|
||||||
type Output = TextRange;
|
type Output = TextRange;
|
||||||
|
|
||||||
fn conv_with(&self, line_index: &LineIndex) -> TextRange {
|
fn conv_with(self, line_index: &LineIndex) -> TextRange {
|
||||||
TextRange::from_to(
|
TextRange::from_to(
|
||||||
self.start.conv_with(line_index),
|
self.start.conv_with(line_index),
|
||||||
self.end.conv_with(line_index),
|
self.end.conv_with(line_index),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ConvWith for Edit {
|
||||||
|
type Ctx = LineIndex;
|
||||||
|
type Output = Vec<TextEdit>;
|
||||||
|
|
||||||
|
fn conv_with(self, line_index: &LineIndex) -> Vec<TextEdit> {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 = <I::Item as ConvWith>::Ctx;
|
||||||
|
type Output = <I::Item as ConvWith>::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<Ctx=Ctx>,
|
||||||
|
{
|
||||||
|
type Item = <I::Item as ConvWith>::Output;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.iter.next().map(|item| item.conv_with(self.ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ impl<R: ClientRequest> Responder<R> {
|
||||||
let res = match result {
|
let res = match result {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
RawResponse {
|
RawResponse {
|
||||||
id: Some(self.id),
|
id: self.id,
|
||||||
result: serde_json::to_value(result)?,
|
result: serde_json::to_value(result)?,
|
||||||
error: serde_json::Value::Null,
|
error: serde_json::Value::Null,
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ fn error_response(id: u64, code: ErrorCode, message: &'static str) -> Result<Raw
|
||||||
message: &'static str,
|
message: &'static str,
|
||||||
}
|
}
|
||||||
let resp = RawResponse {
|
let resp = RawResponse {
|
||||||
id: Some(id),
|
id,
|
||||||
result: serde_json::Value::Null,
|
result: serde_json::Value::Null,
|
||||||
error: serde_json::to_value(Error {
|
error: serde_json::to_value(Error {
|
||||||
code: code as i32,
|
code: code as i32,
|
||||||
|
|
|
@ -34,8 +34,13 @@ pub struct RawNotification {
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct RawResponse {
|
pub struct RawResponse {
|
||||||
pub id: Option<u64>,
|
// JSON RPC allows this to be null if it was impossible
|
||||||
|
// to decode the request's id. Ignore this special case
|
||||||
|
// and just die horribly.
|
||||||
|
pub id: u64,
|
||||||
|
#[serde(default)]
|
||||||
pub result: Value,
|
pub result: Value,
|
||||||
|
#[serde(default)]
|
||||||
pub error: Value,
|
pub error: Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ mod main_loop;
|
||||||
|
|
||||||
use threadpool::ThreadPool;
|
use threadpool::ThreadPool;
|
||||||
use crossbeam_channel::bounded;
|
use crossbeam_channel::bounded;
|
||||||
use flexi_logger::Logger;
|
use flexi_logger::{Logger, Duplicate};
|
||||||
use libanalysis::WorldState;
|
use libanalysis::WorldState;
|
||||||
|
|
||||||
use ::{
|
use ::{
|
||||||
|
@ -38,6 +38,7 @@ pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
Logger::with_env()
|
Logger::with_env()
|
||||||
|
.duplicate_to_stderr(Duplicate::All)
|
||||||
.log_to_file()
|
.log_to_file()
|
||||||
.directory("log")
|
.directory("log")
|
||||||
.start()?;
|
.start()?;
|
||||||
|
@ -81,7 +82,7 @@ fn initialize(io: &mut Io) -> Result<()> {
|
||||||
RawMsg::Request(req) => {
|
RawMsg::Request(req) => {
|
||||||
let mut req = Some(req);
|
let mut req = Some(req);
|
||||||
dispatch::handle_request::<req::Initialize, _>(&mut req, |_params, resp| {
|
dispatch::handle_request::<req::Initialize, _>(&mut req, |_params, resp| {
|
||||||
let res = req::InitializeResult { capabilities: caps::SERVER_CAPABILITIES };
|
let res = req::InitializeResult { capabilities: caps::server_capabilities() };
|
||||||
let resp = resp.into_response(Ok(res))?;
|
let resp = resp.into_response(Ok(res))?;
|
||||||
io.send(RawMsg::Response(resp));
|
io.send(RawMsg::Response(resp));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use languageserver_types::{
|
use languageserver_types::{
|
||||||
Diagnostic, DiagnosticSeverity, Url, DocumentSymbol,
|
Diagnostic, DiagnosticSeverity, Url, DocumentSymbol,
|
||||||
Command
|
Command, TextDocumentIdentifier, WorkspaceEdit
|
||||||
};
|
};
|
||||||
use libanalysis::World;
|
use libanalysis::World;
|
||||||
use libeditor;
|
use libeditor;
|
||||||
use serde_json::to_value;
|
use libsyntax2::TextUnit;
|
||||||
|
use serde_json::{to_value, from_value};
|
||||||
|
|
||||||
use ::{
|
use ::{
|
||||||
req::{self, Decoration}, Result,
|
req::{self, Decoration}, Result,
|
||||||
util::FilePath,
|
util::FilePath,
|
||||||
conv::{Conv, ConvWith},
|
conv::{Conv, ConvWith, MapConvWith},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn handle_syntax_tree(
|
pub fn handle_syntax_tree(
|
||||||
|
@ -29,9 +32,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| r.conv_with(&line_index))
|
.map_conv_with(&line_index)
|
||||||
.map(|r| libeditor::extend_selection(&file, r).unwrap_or(r))
|
.map(|r| libeditor::extend_selection(&file, r).unwrap_or(r))
|
||||||
.map(|r| r.conv_with(&line_index))
|
.map_conv_with(&line_index)
|
||||||
.collect();
|
.collect();
|
||||||
Ok(req::ExtendSelectionResult { selections })
|
Ok(req::ExtendSelectionResult { selections })
|
||||||
}
|
}
|
||||||
|
@ -78,18 +81,71 @@ pub fn handle_code_action(
|
||||||
let line_index = world.file_line_index(&path)?;
|
let line_index = world.file_line_index(&path)?;
|
||||||
let offset = params.range.conv_with(&line_index).start();
|
let offset = params.range.conv_with(&line_index).start();
|
||||||
let ret = if libeditor::flip_comma(&file, offset).is_some() {
|
let ret = if libeditor::flip_comma(&file, offset).is_some() {
|
||||||
Some(vec![apply_code_action_cmd(ActionId::FlipComma)])
|
let cmd = apply_code_action_cmd(
|
||||||
|
ActionId::FlipComma,
|
||||||
|
params.text_document,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
|
Some(vec![cmd])
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_code_action_cmd(id: ActionId) -> Command {
|
pub fn handle_execute_command(
|
||||||
|
world: World,
|
||||||
|
mut params: req::ExecuteCommandParams,
|
||||||
|
) -> Result<req::ApplyWorkspaceEditParams> {
|
||||||
|
if params.command.as_str() != "apply_code_action" {
|
||||||
|
bail!("unknown cmd: {:?}", params.command);
|
||||||
|
}
|
||||||
|
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)?;
|
||||||
|
match arg.id {
|
||||||
|
ActionId::FlipComma => {
|
||||||
|
let path = arg.text_document.file_path()?;
|
||||||
|
let file = world.file_syntax(&path)?;
|
||||||
|
let line_index = world.file_line_index(&path)?;
|
||||||
|
let edit = match libeditor::flip_comma(&file, arg.offset) {
|
||||||
|
Some(edit) => edit(),
|
||||||
|
None => bail!("command not applicable"),
|
||||||
|
};
|
||||||
|
let mut changes = HashMap::new();
|
||||||
|
changes.insert(
|
||||||
|
arg.text_document.uri,
|
||||||
|
edit.conv_with(&line_index),
|
||||||
|
);
|
||||||
|
let edit = WorkspaceEdit {
|
||||||
|
changes: Some(changes),
|
||||||
|
document_changes: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(req::ApplyWorkspaceEditParams { edit })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 {
|
Command {
|
||||||
title: id.title().to_string(),
|
title: id.title().to_string(),
|
||||||
command: "apply_code_action".to_string(),
|
command: "apply_code_action".to_string(),
|
||||||
arguments: Some(vec![to_value(id).unwrap()]),
|
arguments: Some(vec![to_value(action_request).unwrap()]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ use threadpool::ThreadPool;
|
||||||
use crossbeam_channel::{Sender, Receiver};
|
use crossbeam_channel::{Sender, Receiver};
|
||||||
use languageserver_types::Url;
|
use languageserver_types::Url;
|
||||||
use libanalysis::{World, WorldState};
|
use libanalysis::{World, WorldState};
|
||||||
|
use serde_json::to_value;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
req, dispatch,
|
req, dispatch,
|
||||||
|
@ -19,6 +20,7 @@ use {
|
||||||
publish_decorations,
|
publish_decorations,
|
||||||
handle_document_symbol,
|
handle_document_symbol,
|
||||||
handle_code_action,
|
handle_code_action,
|
||||||
|
handle_execute_command,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -79,10 +81,12 @@ pub(super) fn main_loop(
|
||||||
on_notification(io, world, pool, &sender, not)?
|
on_notification(io, world, pool, &sender, not)?
|
||||||
}
|
}
|
||||||
RawMsg::Response(resp) => {
|
RawMsg::Response(resp) => {
|
||||||
|
if !pending_requests.remove(&resp.id) {
|
||||||
error!("unexpected response: {:?}", resp)
|
error!("unexpected response: {:?}", resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,22 +111,30 @@ fn on_request(
|
||||||
handle_request_on_threadpool::<req::CodeActionRequest>(
|
handle_request_on_threadpool::<req::CodeActionRequest>(
|
||||||
&mut req, pool, world, sender, handle_code_action,
|
&mut req, pool, world, sender, handle_code_action,
|
||||||
)?;
|
)?;
|
||||||
// dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |(), resp| {
|
dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| {
|
||||||
// let world = world.snapshot();
|
io.send(RawMsg::Response(resp.into_response(Ok(None))?));
|
||||||
// let sender = sender.clone();
|
|
||||||
// pool.execute(move || {
|
let world = world.snapshot();
|
||||||
// let task = match handle_execute_command(world, params) {
|
let sender = sender.clone();
|
||||||
// Ok(req) => Task::Request(req),
|
pool.execute(move || {
|
||||||
// Err(e) => Task::Die(e),
|
let task = match handle_execute_command(world, params) {
|
||||||
// };
|
Ok(req) => match to_value(req) {
|
||||||
// sender.send(task)
|
Err(e) => Task::Die(e.into()),
|
||||||
// });
|
Ok(params) => {
|
||||||
//
|
let request = RawRequest {
|
||||||
// let resp = resp.into_response(Ok(()))?;
|
id: 0,
|
||||||
// io.send(RawMsg::Response(resp));
|
method: <req::ApplyWorkspaceEdit as req::ClientRequest>::METHOD.to_string(),
|
||||||
// shutdown = true;
|
params,
|
||||||
// Ok(())
|
};
|
||||||
// })?;
|
Task::Request(request)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => Task::Die(e),
|
||||||
|
};
|
||||||
|
sender.send(task)
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
let mut shutdown = false;
|
let mut shutdown = false;
|
||||||
dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| {
|
dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| {
|
||||||
|
|
|
@ -6,7 +6,8 @@ pub use languageserver_types::{
|
||||||
request::*, notification::*,
|
request::*, notification::*,
|
||||||
InitializeResult, PublishDiagnosticsParams,
|
InitializeResult, PublishDiagnosticsParams,
|
||||||
DocumentSymbolParams, DocumentSymbolResponse,
|
DocumentSymbolParams, DocumentSymbolResponse,
|
||||||
CodeActionParams,
|
CodeActionParams, ApplyWorkspaceEditParams,
|
||||||
|
ExecuteCommandParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue