diff options
author | Aleksey Kladov <[email protected]> | 2018-08-12 19:02:56 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-12 19:02:56 +0100 |
commit | 23c06db9c236db0f9c8ef5117fb1adefd6616c43 (patch) | |
tree | 8e265a79b7f8f62761c6ea2dd4fa0d6bc0c74204 /crates/server/src | |
parent | 66be735aa98c32fb062d1c756fa9303ff2d13002 (diff) |
Half of code-actions
Diffstat (limited to 'crates/server/src')
-rw-r--r-- | crates/server/src/caps.rs | 2 | ||||
-rw-r--r-- | crates/server/src/conv.rs | 81 | ||||
-rw-r--r-- | crates/server/src/handlers.rs | 103 | ||||
-rw-r--r-- | crates/server/src/main.rs | 7 | ||||
-rw-r--r-- | crates/server/src/req.rs | 3 |
5 files changed, 139 insertions, 57 deletions
diff --git a/crates/server/src/caps.rs b/crates/server/src/caps.rs index dca07ebc9..b502b0865 100644 --- a/crates/server/src/caps.rs +++ b/crates/server/src/caps.rs | |||
@@ -25,7 +25,7 @@ pub const SERVER_CAPABILITIES: ServerCapabilities = ServerCapabilities { | |||
25 | document_highlight_provider: None, | 25 | document_highlight_provider: None, |
26 | document_symbol_provider: Some(true), | 26 | document_symbol_provider: Some(true), |
27 | workspace_symbol_provider: None, | 27 | workspace_symbol_provider: None, |
28 | code_action_provider: None, | 28 | code_action_provider: Some(true), |
29 | code_lens_provider: None, | 29 | code_lens_provider: None, |
30 | document_formatting_provider: None, | 30 | document_formatting_provider: None, |
31 | document_range_formatting_provider: None, | 31 | document_range_formatting_provider: None, |
diff --git a/crates/server/src/conv.rs b/crates/server/src/conv.rs new file mode 100644 index 000000000..c7bea15df --- /dev/null +++ b/crates/server/src/conv.rs | |||
@@ -0,0 +1,81 @@ | |||
1 | use languageserver_types::{Range, SymbolKind, Position}; | ||
2 | use libeditor::{LineIndex, LineCol}; | ||
3 | use libsyntax2::{SyntaxKind, TextUnit, TextRange}; | ||
4 | |||
5 | pub trait Conv { | ||
6 | type Output; | ||
7 | fn conv(&self) -> Self::Output; | ||
8 | } | ||
9 | |||
10 | pub trait ConvWith { | ||
11 | type Ctx; | ||
12 | type Output; | ||
13 | fn conv_with(&self, ctx: &Self::Ctx) -> Self::Output; | ||
14 | } | ||
15 | |||
16 | impl Conv for SyntaxKind { | ||
17 | type Output = SymbolKind; | ||
18 | |||
19 | fn conv(&self) -> <Self as Conv>::Output { | ||
20 | match *self { | ||
21 | SyntaxKind::FUNCTION => SymbolKind::Function, | ||
22 | SyntaxKind::STRUCT => SymbolKind::Struct, | ||
23 | SyntaxKind::ENUM => SymbolKind::Enum, | ||
24 | SyntaxKind::TRAIT => SymbolKind::Interface, | ||
25 | SyntaxKind::MODULE => SymbolKind::Module, | ||
26 | SyntaxKind::TYPE_ITEM => SymbolKind::TypeParameter, | ||
27 | SyntaxKind::STATIC_ITEM => SymbolKind::Constant, | ||
28 | SyntaxKind::CONST_ITEM => SymbolKind::Constant, | ||
29 | _ => SymbolKind::Variable, | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | |||
34 | impl ConvWith for Position { | ||
35 | type Ctx = LineIndex; | ||
36 | type Output = TextUnit; | ||
37 | |||
38 | fn conv_with(&self, line_index: &LineIndex) -> TextUnit { | ||
39 | // TODO: UTF-16 | ||
40 | let line_col = LineCol { | ||
41 | line: self.line as u32, | ||
42 | col: (self.character as u32).into(), | ||
43 | }; | ||
44 | line_index.offset(line_col) | ||
45 | } | ||
46 | } | ||
47 | |||
48 | impl ConvWith for TextUnit { | ||
49 | type Ctx = LineIndex; | ||
50 | type Output = Position; | ||
51 | |||
52 | fn conv_with(&self, line_index: &LineIndex) -> Position { | ||
53 | let line_col = line_index.line_col(*self); | ||
54 | // TODO: UTF-16 | ||
55 | Position::new(line_col.line as u64, u32::from(line_col.col) as u64) | ||
56 | } | ||
57 | } | ||
58 | |||
59 | impl ConvWith for TextRange { | ||
60 | type Ctx = LineIndex; | ||
61 | type Output = Range; | ||
62 | |||
63 | fn conv_with(&self, line_index: &LineIndex) -> Range { | ||
64 | Range::new( | ||
65 | self.start().conv_with(line_index), | ||
66 | self.end().conv_with(line_index), | ||
67 | ) | ||
68 | } | ||
69 | } | ||
70 | |||
71 | impl ConvWith for Range { | ||
72 | type Ctx = LineIndex; | ||
73 | type Output = TextRange; | ||
74 | |||
75 | fn conv_with(&self, line_index: &LineIndex) -> TextRange { | ||
76 | TextRange::from_to( | ||
77 | self.start.conv_with(line_index), | ||
78 | self.end.conv_with(line_index), | ||
79 | ) | ||
80 | } | ||
81 | } | ||
diff --git a/crates/server/src/handlers.rs b/crates/server/src/handlers.rs index f6f960d22..c6db22289 100644 --- a/crates/server/src/handlers.rs +++ b/crates/server/src/handlers.rs | |||
@@ -1,11 +1,15 @@ | |||
1 | use languageserver_types::{Range, Position, Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, SymbolKind}; | 1 | use languageserver_types::{ |
2 | use libsyntax2::SyntaxKind; | 2 | Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, |
3 | Command | ||
4 | }; | ||
3 | use libanalysis::World; | 5 | use libanalysis::World; |
4 | use libeditor::{self, LineIndex, LineCol, TextRange, TextUnit}; | 6 | use libeditor; |
7 | use serde_json::to_value; | ||
5 | 8 | ||
6 | use ::{ | 9 | use ::{ |
7 | req::{self, Decoration}, Result, | 10 | req::{self, Decoration}, Result, |
8 | util::FilePath, | 11 | util::FilePath, |
12 | conv::{Conv, ConvWith}, | ||
9 | }; | 13 | }; |
10 | 14 | ||
11 | pub fn handle_syntax_tree( | 15 | pub fn handle_syntax_tree( |
@@ -25,11 +29,9 @@ pub fn handle_extend_selection( | |||
25 | let file = world.file_syntax(&path)?; | 29 | let file = world.file_syntax(&path)?; |
26 | let line_index = world.file_line_index(&path)?; | 30 | let line_index = world.file_line_index(&path)?; |
27 | let selections = params.selections.into_iter() | 31 | let selections = params.selections.into_iter() |
28 | .map(|r| { | 32 | .map(|r| r.conv_with(&line_index)) |
29 | let r = to_text_range(&line_index, r); | 33 | .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r)) |
30 | let r = libeditor::extend_selection(&file, r).unwrap_or(r); | 34 | .map(|r| r.conv_with(&line_index)) |
31 | to_vs_range(&line_index, r) | ||
32 | }) | ||
33 | .collect(); | 35 | .collect(); |
34 | Ok(req::ExtendSelectionResult { selections }) | 36 | Ok(req::ExtendSelectionResult { selections }) |
35 | } | 37 | } |
@@ -48,10 +50,10 @@ pub fn handle_document_symbol( | |||
48 | let doc_symbol = DocumentSymbol { | 50 | let doc_symbol = DocumentSymbol { |
49 | name: symbol.name.clone(), | 51 | name: symbol.name.clone(), |
50 | detail: Some(symbol.name), | 52 | detail: Some(symbol.name), |
51 | kind: to_symbol_kind(symbol.kind), | 53 | kind: symbol.kind.conv(), |
52 | deprecated: None, | 54 | deprecated: None, |
53 | range: to_vs_range(&line_index, symbol.node_range), | 55 | range: symbol.node_range.conv_with(&line_index), |
54 | selection_range: to_vs_range(&line_index, symbol.name_range), | 56 | selection_range: symbol.name_range.conv_with(&line_index), |
55 | children: None, | 57 | children: None, |
56 | }; | 58 | }; |
57 | if let Some(idx) = symbol.parent { | 59 | if let Some(idx) = symbol.parent { |
@@ -67,17 +69,40 @@ pub fn handle_document_symbol( | |||
67 | Ok(Some(req::DocumentSymbolResponse::Nested(res))) | 69 | Ok(Some(req::DocumentSymbolResponse::Nested(res))) |
68 | } | 70 | } |
69 | 71 | ||
70 | fn to_symbol_kind(kind: SyntaxKind) -> SymbolKind { | 72 | pub fn handle_code_action( |
71 | match kind { | 73 | world: World, |
72 | SyntaxKind::FUNCTION => SymbolKind::Function, | 74 | params: req::CodeActionParams, |
73 | SyntaxKind::STRUCT => SymbolKind::Struct, | 75 | ) -> Result<Option<Vec<Command>>> { |
74 | SyntaxKind::ENUM => SymbolKind::Enum, | 76 | let path = params.text_document.file_path()?; |
75 | SyntaxKind::TRAIT => SymbolKind::Interface, | 77 | let file = world.file_syntax(&path)?; |
76 | SyntaxKind::MODULE => SymbolKind::Module, | 78 | let line_index = world.file_line_index(&path)?; |
77 | SyntaxKind::TYPE_ITEM => SymbolKind::TypeParameter, | 79 | let offset = params.range.conv_with(&line_index).start(); |
78 | SyntaxKind::STATIC_ITEM => SymbolKind::Constant, | 80 | let ret = if libeditor::flip_comma(&file, offset).is_some() { |
79 | SyntaxKind::CONST_ITEM => SymbolKind::Constant, | 81 | Some(vec![apply_code_action_cmd(ActionId::FlipComma)]) |
80 | _ => SymbolKind::Variable, | 82 | } else { |
83 | None | ||
84 | }; | ||
85 | Ok(ret) | ||
86 | } | ||
87 | |||
88 | fn apply_code_action_cmd(id: ActionId) -> Command { | ||
89 | Command { | ||
90 | title: id.title().to_string(), | ||
91 | command: "apply_code_action".to_string(), | ||
92 | arguments: Some(vec![to_value(id).unwrap()]), | ||
93 | } | ||
94 | } | ||
95 | |||
96 | #[derive(Serialize, Deserialize, Clone, Copy)] | ||
97 | enum ActionId { | ||
98 | FlipComma | ||
99 | } | ||
100 | |||
101 | impl ActionId { | ||
102 | fn title(&self) -> &'static str { | ||
103 | match *self { | ||
104 | ActionId::FlipComma => "Flip `,`", | ||
105 | } | ||
81 | } | 106 | } |
82 | } | 107 | } |
83 | 108 | ||
@@ -88,7 +113,7 @@ pub fn publish_diagnostics(world: World, uri: Url) -> Result<req::PublishDiagnos | |||
88 | let diagnostics = libeditor::diagnostics(&file) | 113 | let diagnostics = libeditor::diagnostics(&file) |
89 | .into_iter() | 114 | .into_iter() |
90 | .map(|d| Diagnostic { | 115 | .map(|d| Diagnostic { |
91 | range: to_vs_range(&line_index, d.range), | 116 | range: d.range.conv_with(&line_index), |
92 | severity: Some(DiagnosticSeverity::Error), | 117 | severity: Some(DiagnosticSeverity::Error), |
93 | code: None, | 118 | code: None, |
94 | source: Some("libsyntax2".to_string()), | 119 | source: Some("libsyntax2".to_string()), |
@@ -105,38 +130,8 @@ pub fn publish_decorations(world: World, uri: Url) -> Result<req::PublishDecorat | |||
105 | let decorations = libeditor::highlight(&file) | 130 | let decorations = libeditor::highlight(&file) |
106 | .into_iter() | 131 | .into_iter() |
107 | .map(|h| Decoration { | 132 | .map(|h| Decoration { |
108 | range: to_vs_range(&line_index, h.range), | 133 | range: h.range.conv_with(&line_index), |
109 | tag: h.tag, | 134 | tag: h.tag, |
110 | }).collect(); | 135 | }).collect(); |
111 | Ok(req::PublishDecorationsParams { uri, decorations }) | 136 | Ok(req::PublishDecorationsParams { uri, decorations }) |
112 | } | 137 | } |
113 | |||
114 | fn to_text_range(line_index: &LineIndex, range: Range) -> TextRange { | ||
115 | TextRange::from_to( | ||
116 | to_text_unit(line_index, range.start), | ||
117 | to_text_unit(line_index, range.end), | ||
118 | ) | ||
119 | } | ||
120 | |||
121 | fn to_text_unit(line_index: &LineIndex, position: Position) -> TextUnit { | ||
122 | // TODO: UTF-16 | ||
123 | let line_col = LineCol { | ||
124 | line: position.line as u32, | ||
125 | col: (position.character as u32).into(), | ||
126 | }; | ||
127 | line_index.offset(line_col) | ||
128 | } | ||
129 | |||
130 | |||
131 | fn to_vs_range(line_index: &LineIndex, range: TextRange) -> Range { | ||
132 | Range::new( | ||
133 | to_vs_position(line_index, range.start()), | ||
134 | to_vs_position(line_index, range.end()), | ||
135 | ) | ||
136 | } | ||
137 | |||
138 | fn to_vs_position(line_index: &LineIndex, offset: TextUnit) -> Position { | ||
139 | let line_col = line_index.line_col(offset); | ||
140 | // TODO: UTF-16 | ||
141 | Position::new(line_col.line as u64, u32::from(line_col.col) as u64) | ||
142 | } | ||
diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index 916638d49..3ff300e3d 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs | |||
@@ -23,6 +23,7 @@ mod req; | |||
23 | mod dispatch; | 23 | mod dispatch; |
24 | mod handlers; | 24 | mod handlers; |
25 | mod util; | 25 | mod util; |
26 | mod conv; | ||
26 | 27 | ||
27 | use threadpool::ThreadPool; | 28 | use threadpool::ThreadPool; |
28 | use crossbeam_channel::{bounded, Sender, Receiver}; | 29 | use crossbeam_channel::{bounded, Sender, Receiver}; |
@@ -33,7 +34,7 @@ use libanalysis::{WorldState, World}; | |||
33 | use ::{ | 34 | use ::{ |
34 | io::{Io, RawMsg, RawRequest}, | 35 | io::{Io, RawMsg, RawRequest}, |
35 | handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics, publish_decorations, | 36 | handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics, publish_decorations, |
36 | handle_document_symbol}, | 37 | handle_document_symbol, handle_code_action}, |
37 | util::{FilePath, FnBox} | 38 | util::{FilePath, FnBox} |
38 | }; | 39 | }; |
39 | 40 | ||
@@ -182,6 +183,10 @@ fn main_loop( | |||
182 | handle_request_on_threadpool::<req::DocumentSymbolRequest>( | 183 | handle_request_on_threadpool::<req::DocumentSymbolRequest>( |
183 | &mut req, pool, world, &sender, handle_document_symbol | 184 | &mut req, pool, world, &sender, handle_document_symbol |
184 | )?; | 185 | )?; |
186 | handle_request_on_threadpool::<req::CodeActionRequest>( | ||
187 | &mut req, pool, world, &sender, handle_code_action | ||
188 | )?; | ||
189 | |||
185 | let mut shutdown = false; | 190 | let mut shutdown = false; |
186 | dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { | 191 | dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { |
187 | resp.result(io, ())?; | 192 | resp.result(io, ())?; |
diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs index 6a0926084..e3b1cdf50 100644 --- a/crates/server/src/req.rs +++ b/crates/server/src/req.rs | |||
@@ -5,7 +5,8 @@ use url_serde; | |||
5 | pub use languageserver_types::{ | 5 | pub use languageserver_types::{ |
6 | request::*, notification::*, | 6 | request::*, notification::*, |
7 | InitializeResult, PublishDiagnosticsParams, | 7 | InitializeResult, PublishDiagnosticsParams, |
8 | DocumentSymbolParams, DocumentSymbolResponse | 8 | DocumentSymbolParams, DocumentSymbolResponse, |
9 | CodeActionParams, | ||
9 | }; | 10 | }; |
10 | 11 | ||
11 | 12 | ||