diff options
-rw-r--r-- | crates/ra_ide/src/lib.rs | 9 | ||||
-rw-r--r-- | crates/ra_text_edit/src/lib.rs | 33 | ||||
-rw-r--r-- | crates/rust-analyzer/src/caps.rs | 9 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_ext.rs | 6 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 30 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 7 | ||||
-rw-r--r-- | docs/dev/lsp-extensions.md | 66 | ||||
-rw-r--r-- | editors/code/src/commands/join_lines.ts | 12 | ||||
-rw-r--r-- | editors/code/src/rust-analyzer-api.ts | 4 |
9 files changed, 129 insertions, 47 deletions
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index d0aeb3ba7..97ff67ee8 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -89,6 +89,7 @@ pub use ra_ide_db::{ | |||
89 | symbol_index::Query, | 89 | symbol_index::Query, |
90 | RootDatabase, | 90 | RootDatabase, |
91 | }; | 91 | }; |
92 | pub use ra_text_edit::{Indel, TextEdit}; | ||
92 | 93 | ||
93 | pub type Cancelable<T> = Result<T, Canceled>; | 94 | pub type Cancelable<T> = Result<T, Canceled>; |
94 | 95 | ||
@@ -285,14 +286,10 @@ impl Analysis { | |||
285 | 286 | ||
286 | /// Returns an edit to remove all newlines in the range, cleaning up minor | 287 | /// Returns an edit to remove all newlines in the range, cleaning up minor |
287 | /// stuff like trailing commas. | 288 | /// stuff like trailing commas. |
288 | pub fn join_lines(&self, frange: FileRange) -> Cancelable<SourceChange> { | 289 | pub fn join_lines(&self, frange: FileRange) -> Cancelable<TextEdit> { |
289 | self.with_db(|db| { | 290 | self.with_db(|db| { |
290 | let parse = db.parse(frange.file_id); | 291 | let parse = db.parse(frange.file_id); |
291 | let file_edit = SourceFileEdit { | 292 | join_lines::join_lines(&parse.tree(), frange.range) |
292 | file_id: frange.file_id, | ||
293 | edit: join_lines::join_lines(&parse.tree(), frange.range), | ||
294 | }; | ||
295 | SourceChange::source_file_edit("Join lines", file_edit) | ||
296 | }) | 293 | }) |
297 | } | 294 | } |
298 | 295 | ||
diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs index 199fd1096..25554f583 100644 --- a/crates/ra_text_edit/src/lib.rs +++ b/crates/ra_text_edit/src/lib.rs | |||
@@ -17,7 +17,7 @@ pub struct Indel { | |||
17 | pub delete: TextRange, | 17 | pub delete: TextRange, |
18 | } | 18 | } |
19 | 19 | ||
20 | #[derive(Debug, Clone)] | 20 | #[derive(Default, Debug, Clone)] |
21 | pub struct TextEdit { | 21 | pub struct TextEdit { |
22 | indels: Vec<Indel>, | 22 | indels: Vec<Indel>, |
23 | } | 23 | } |
@@ -64,14 +64,6 @@ impl TextEdit { | |||
64 | builder.finish() | 64 | builder.finish() |
65 | } | 65 | } |
66 | 66 | ||
67 | pub(crate) fn from_indels(mut indels: Vec<Indel>) -> TextEdit { | ||
68 | indels.sort_by_key(|a| (a.delete.start(), a.delete.end())); | ||
69 | for (a1, a2) in indels.iter().zip(indels.iter().skip(1)) { | ||
70 | assert!(a1.delete.end() <= a2.delete.start()) | ||
71 | } | ||
72 | TextEdit { indels } | ||
73 | } | ||
74 | |||
75 | pub fn len(&self) -> usize { | 67 | pub fn len(&self) -> usize { |
76 | self.indels.len() | 68 | self.indels.len() |
77 | } | 69 | } |
@@ -122,6 +114,17 @@ impl TextEdit { | |||
122 | *text = buf | 114 | *text = buf |
123 | } | 115 | } |
124 | 116 | ||
117 | pub fn union(&mut self, other: TextEdit) -> Result<(), TextEdit> { | ||
118 | // FIXME: can be done without allocating intermediate vector | ||
119 | let mut all = self.iter().chain(other.iter()).collect::<Vec<_>>(); | ||
120 | if !check_disjoint(&mut all) { | ||
121 | return Err(other); | ||
122 | } | ||
123 | self.indels.extend(other.indels); | ||
124 | assert!(check_disjoint(&mut self.indels)); | ||
125 | Ok(()) | ||
126 | } | ||
127 | |||
125 | pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> { | 128 | pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> { |
126 | let mut res = offset; | 129 | let mut res = offset; |
127 | for indel in self.indels.iter() { | 130 | for indel in self.indels.iter() { |
@@ -149,9 +152,19 @@ impl TextEditBuilder { | |||
149 | self.indels.push(Indel::insert(offset, text)) | 152 | self.indels.push(Indel::insert(offset, text)) |
150 | } | 153 | } |
151 | pub fn finish(self) -> TextEdit { | 154 | pub fn finish(self) -> TextEdit { |
152 | TextEdit::from_indels(self.indels) | 155 | let mut indels = self.indels; |
156 | assert!(check_disjoint(&mut indels)); | ||
157 | TextEdit { indels } | ||
153 | } | 158 | } |
154 | pub fn invalidates_offset(&self, offset: TextSize) -> bool { | 159 | pub fn invalidates_offset(&self, offset: TextSize) -> bool { |
155 | self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) | 160 | self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) |
156 | } | 161 | } |
157 | } | 162 | } |
163 | |||
164 | fn check_disjoint(indels: &mut [impl std::borrow::Borrow<Indel>]) -> bool { | ||
165 | indels.sort_by_key(|indel| (indel.borrow().delete.start(), indel.borrow().delete.end())); | ||
166 | indels | ||
167 | .iter() | ||
168 | .zip(indels.iter().skip(1)) | ||
169 | .all(|(l, r)| l.borrow().delete.end() <= r.borrow().delete.start()) | ||
170 | } | ||
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 110c9a442..4c417c270 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -1,8 +1,6 @@ | |||
1 | //! Advertizes the capabilities of the LSP Server. | 1 | //! Advertizes the capabilities of the LSP Server. |
2 | use std::env; | 2 | use std::env; |
3 | 3 | ||
4 | use crate::semantic_tokens; | ||
5 | |||
6 | use lsp_types::{ | 4 | use lsp_types::{ |
7 | CallHierarchyServerCapability, CodeActionOptions, CodeActionProviderCapability, | 5 | CallHierarchyServerCapability, CodeActionOptions, CodeActionProviderCapability, |
8 | CodeLensOptions, CompletionOptions, DocumentOnTypeFormattingOptions, | 6 | CodeLensOptions, CompletionOptions, DocumentOnTypeFormattingOptions, |
@@ -12,6 +10,9 @@ use lsp_types::{ | |||
12 | ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, | 10 | ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, |
13 | TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions, | 11 | TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions, |
14 | }; | 12 | }; |
13 | use serde_json::json; | ||
14 | |||
15 | use crate::semantic_tokens; | ||
15 | 16 | ||
16 | pub fn server_capabilities() -> ServerCapabilities { | 17 | pub fn server_capabilities() -> ServerCapabilities { |
17 | ServerCapabilities { | 18 | ServerCapabilities { |
@@ -91,6 +92,8 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
91 | } | 92 | } |
92 | .into(), | 93 | .into(), |
93 | ), | 94 | ), |
94 | experimental: Default::default(), | 95 | experimental: Some(json!({ |
96 | "joinLines": true, | ||
97 | })), | ||
95 | } | 98 | } |
96 | } | 99 | } |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 3c7bd609d..1bb1b02ab 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -87,15 +87,15 @@ pub enum JoinLines {} | |||
87 | 87 | ||
88 | impl Request for JoinLines { | 88 | impl Request for JoinLines { |
89 | type Params = JoinLinesParams; | 89 | type Params = JoinLinesParams; |
90 | type Result = SourceChange; | 90 | type Result = Vec<lsp_types::TextEdit>; |
91 | const METHOD: &'static str = "rust-analyzer/joinLines"; | 91 | const METHOD: &'static str = "experimental/joinLines"; |
92 | } | 92 | } |
93 | 93 | ||
94 | #[derive(Deserialize, Serialize, Debug)] | 94 | #[derive(Deserialize, Serialize, Debug)] |
95 | #[serde(rename_all = "camelCase")] | 95 | #[serde(rename_all = "camelCase")] |
96 | pub struct JoinLinesParams { | 96 | pub struct JoinLinesParams { |
97 | pub text_document: TextDocumentIdentifier, | 97 | pub text_document: TextDocumentIdentifier, |
98 | pub range: Range, | 98 | pub ranges: Vec<Range>, |
99 | } | 99 | } |
100 | 100 | ||
101 | pub enum OnEnter {} | 101 | pub enum OnEnter {} |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index fcf08cd79..121964718 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -15,10 +15,11 @@ use lsp_types::{ | |||
15 | DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, | 15 | DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, |
16 | MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, | 16 | MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, |
17 | SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, | 17 | SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, |
18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit, | 18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, |
19 | }; | 19 | }; |
20 | use ra_ide::{ | 20 | use ra_ide::{ |
21 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, | 21 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, |
22 | TextEdit, | ||
22 | }; | 23 | }; |
23 | use ra_prof::profile; | 24 | use ra_prof::profile; |
24 | use ra_project_model::TargetKind; | 25 | use ra_project_model::TargetKind; |
@@ -149,11 +150,24 @@ pub fn handle_find_matching_brace( | |||
149 | pub fn handle_join_lines( | 150 | pub fn handle_join_lines( |
150 | world: WorldSnapshot, | 151 | world: WorldSnapshot, |
151 | params: lsp_ext::JoinLinesParams, | 152 | params: lsp_ext::JoinLinesParams, |
152 | ) -> Result<lsp_ext::SourceChange> { | 153 | ) -> Result<Vec<lsp_types::TextEdit>> { |
153 | let _p = profile("handle_join_lines"); | 154 | let _p = profile("handle_join_lines"); |
154 | let frange = from_proto::file_range(&world, params.text_document, params.range)?; | 155 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
155 | let source_change = world.analysis().join_lines(frange)?; | 156 | let line_index = world.analysis().file_line_index(file_id)?; |
156 | to_proto::source_change(&world, source_change) | 157 | let line_endings = world.file_line_endings(file_id); |
158 | let mut res = TextEdit::default(); | ||
159 | for range in params.ranges { | ||
160 | let range = from_proto::text_range(&line_index, range); | ||
161 | let edit = world.analysis().join_lines(FileRange { file_id, range })?; | ||
162 | match res.union(edit) { | ||
163 | Ok(()) => (), | ||
164 | Err(_edit) => { | ||
165 | // just ignore overlapping edits | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | let res = to_proto::text_edit_vec(&line_index, line_endings, res); | ||
170 | Ok(res) | ||
157 | } | 171 | } |
158 | 172 | ||
159 | pub fn handle_on_enter( | 173 | pub fn handle_on_enter( |
@@ -172,7 +186,7 @@ pub fn handle_on_enter( | |||
172 | pub fn handle_on_type_formatting( | 186 | pub fn handle_on_type_formatting( |
173 | world: WorldSnapshot, | 187 | world: WorldSnapshot, |
174 | params: lsp_types::DocumentOnTypeFormattingParams, | 188 | params: lsp_types::DocumentOnTypeFormattingParams, |
175 | ) -> Result<Option<Vec<TextEdit>>> { | 189 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { |
176 | let _p = profile("handle_on_type_formatting"); | 190 | let _p = profile("handle_on_type_formatting"); |
177 | let mut position = from_proto::file_position(&world, params.text_document_position)?; | 191 | let mut position = from_proto::file_position(&world, params.text_document_position)?; |
178 | let line_index = world.analysis().file_line_index(position.file_id)?; | 192 | let line_index = world.analysis().file_line_index(position.file_id)?; |
@@ -618,7 +632,7 @@ pub fn handle_references( | |||
618 | pub fn handle_formatting( | 632 | pub fn handle_formatting( |
619 | world: WorldSnapshot, | 633 | world: WorldSnapshot, |
620 | params: DocumentFormattingParams, | 634 | params: DocumentFormattingParams, |
621 | ) -> Result<Option<Vec<TextEdit>>> { | 635 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { |
622 | let _p = profile("handle_formatting"); | 636 | let _p = profile("handle_formatting"); |
623 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 637 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
624 | let file = world.analysis().file_text(file_id)?; | 638 | let file = world.analysis().file_text(file_id)?; |
@@ -685,7 +699,7 @@ pub fn handle_formatting( | |||
685 | } | 699 | } |
686 | } | 700 | } |
687 | 701 | ||
688 | Ok(Some(vec![TextEdit { | 702 | Ok(Some(vec![lsp_types::TextEdit { |
689 | range: Range::new(Position::new(0, 0), end_position), | 703 | range: Range::new(Position::new(0, 0), end_position), |
690 | new_text: captured_stdout, | 704 | new_text: captured_stdout, |
691 | }])) | 705 | }])) |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 617197963..f6f4bb134 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -2,12 +2,11 @@ | |||
2 | use ra_db::{FileId, FileRange}; | 2 | use ra_db::{FileId, FileRange}; |
3 | use ra_ide::{ | 3 | use ra_ide::{ |
4 | Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, | 4 | Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, |
5 | FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, InlayHint, | 5 | FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, |
6 | InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, | 6 | InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, |
7 | SourceChange, SourceFileEdit, | 7 | SourceChange, SourceFileEdit, TextEdit, |
8 | }; | 8 | }; |
9 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; | 9 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; |
10 | use ra_text_edit::{Indel, TextEdit}; | ||
11 | use ra_vfs::LineEndings; | 10 | use ra_vfs::LineEndings; |
12 | 11 | ||
13 | use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; | 12 | use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; |
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index d2ec6c021..0e3a0af1c 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -7,13 +7,7 @@ All capabilities are enabled via `experimental` field of `ClientCapabilities`. | |||
7 | 7 | ||
8 | ## `SnippetTextEdit` | 8 | ## `SnippetTextEdit` |
9 | 9 | ||
10 | **Capability** | 10 | **Client Capability:** `{ "snippetTextEdit": boolean }` |
11 | |||
12 | ```typescript | ||
13 | { | ||
14 | "snippetTextEdit": boolean | ||
15 | } | ||
16 | ``` | ||
17 | 11 | ||
18 | If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: | 12 | If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: |
19 | 13 | ||
@@ -32,3 +26,61 @@ export interface TextDocumentEdit { | |||
32 | 26 | ||
33 | When applying such code action, the editor should insert snippet, with tab stops and placeholder. | 27 | When applying such code action, the editor should insert snippet, with tab stops and placeholder. |
34 | At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`. | 28 | At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`. |
29 | |||
30 | ### Example | ||
31 | |||
32 | "Add `derive`" code action transforms `struct S;` into `#[derive($0)] struct S;` | ||
33 | |||
34 | ### Unresolved Questions | ||
35 | |||
36 | * Where exactly are `SnippetTextEdit`s allowed (only in code actions at the moment)? | ||
37 | * Can snippets span multiple files (so far, no)? | ||
38 | |||
39 | ## `joinLines` | ||
40 | |||
41 | **Server Capability:** `{ "joinLines": boolean }` | ||
42 | |||
43 | This request is send from client to server to handle "Join Lines" editor action. | ||
44 | |||
45 | **Method:** `experimental/JoinLines` | ||
46 | |||
47 | **Request:** | ||
48 | |||
49 | ```typescript | ||
50 | interface JoinLinesParams { | ||
51 | textDocument: TextDocumentIdentifier, | ||
52 | /// Currently active selections/cursor offsets. | ||
53 | /// This is an array to support multiple cursors. | ||
54 | ranges: Range[], | ||
55 | } | ||
56 | ``` | ||
57 | |||
58 | **Response:** | ||
59 | |||
60 | ```typescript | ||
61 | TextEdit[] | ||
62 | ``` | ||
63 | |||
64 | ### Example | ||
65 | |||
66 | ```rust | ||
67 | fn main() { | ||
68 | /*cursor here*/let x = { | ||
69 | 92 | ||
70 | }; | ||
71 | } | ||
72 | ``` | ||
73 | |||
74 | `experimental/joinLines` yields (curly braces are automagiacally removed) | ||
75 | |||
76 | ```rust | ||
77 | fn main() { | ||
78 | let x = 92; | ||
79 | } | ||
80 | ``` | ||
81 | |||
82 | ### Unresolved Question | ||
83 | |||
84 | * What is the position of the cursor after `joinLines`? | ||
85 | Currently this is left to editor's discretion, but it might be useful to specify on the server via snippets. | ||
86 | However, it then becomes unclear how it works with multi cursor. | ||
diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts index de0614653..0bf1ee6e6 100644 --- a/editors/code/src/commands/join_lines.ts +++ b/editors/code/src/commands/join_lines.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as ra from '../rust-analyzer-api'; | 1 | import * as ra from '../rust-analyzer-api'; |
2 | import * as lc from 'vscode-languageclient'; | ||
2 | 3 | ||
3 | import { Ctx, Cmd } from '../ctx'; | 4 | import { Ctx, Cmd } from '../ctx'; |
4 | import { applySourceChange } from '../source_change'; | ||
5 | 5 | ||
6 | export function joinLines(ctx: Ctx): Cmd { | 6 | export function joinLines(ctx: Ctx): Cmd { |
7 | return async () => { | 7 | return async () => { |
@@ -9,10 +9,14 @@ export function joinLines(ctx: Ctx): Cmd { | |||
9 | const client = ctx.client; | 9 | const client = ctx.client; |
10 | if (!editor || !client) return; | 10 | if (!editor || !client) return; |
11 | 11 | ||
12 | const change = await client.sendRequest(ra.joinLines, { | 12 | const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, { |
13 | range: client.code2ProtocolConverter.asRange(editor.selection), | 13 | ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)), |
14 | textDocument: { uri: editor.document.uri.toString() }, | 14 | textDocument: { uri: editor.document.uri.toString() }, |
15 | }); | 15 | }); |
16 | await applySourceChange(ctx, change); | 16 | editor.edit((builder) => { |
17 | client.protocol2CodeConverter.asTextEdits(items).forEach((edit) => { | ||
18 | builder.replace(edit.range, edit.newText); | ||
19 | }); | ||
20 | }); | ||
17 | }; | 21 | }; |
18 | } | 22 | } |
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts index 3b83b10e3..8ed56c173 100644 --- a/editors/code/src/rust-analyzer-api.ts +++ b/editors/code/src/rust-analyzer-api.ts | |||
@@ -64,9 +64,9 @@ export const parentModule = request<lc.TextDocumentPositionParams, Vec<lc.Locati | |||
64 | 64 | ||
65 | export interface JoinLinesParams { | 65 | export interface JoinLinesParams { |
66 | textDocument: lc.TextDocumentIdentifier; | 66 | textDocument: lc.TextDocumentIdentifier; |
67 | range: lc.Range; | 67 | ranges: lc.Range[]; |
68 | } | 68 | } |
69 | export const joinLines = request<JoinLinesParams, SourceChange>("joinLines"); | 69 | export const joinLines = new lc.RequestType<JoinLinesParams, lc.TextEdit[], unknown>('experimental/joinLines'); |
70 | 70 | ||
71 | 71 | ||
72 | export const onEnter = request<lc.TextDocumentPositionParams, Option<lc.WorkspaceEdit>>("onEnter"); | 72 | export const onEnter = request<lc.TextDocumentPositionParams, Option<lc.WorkspaceEdit>>("onEnter"); |