mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-12 21:28:51 +00:00
matching brace
This commit is contained in:
parent
aa0d344581
commit
c631b585a7
9 changed files with 135 additions and 14 deletions
|
@ -1,3 +1,5 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [ "crates/*" ]
|
members = [ "crates/*" ]
|
||||||
exclude = [ "crates/indxr" ]
|
|
||||||
|
[profile.release]
|
||||||
|
debug = true
|
||||||
|
|
|
@ -36,6 +36,11 @@
|
||||||
{
|
{
|
||||||
"command": "libsyntax-rust.extendSelection",
|
"command": "libsyntax-rust.extendSelection",
|
||||||
"title": "Rust Extend Selection"
|
"title": "Rust Extend Selection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "libsyntax-rust.matchingBrace",
|
||||||
|
"key": "ctrl+shift+m",
|
||||||
|
"title": "Rust Matching Brace"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"keybindings": [
|
"keybindings": [
|
||||||
|
|
|
@ -35,6 +35,22 @@ export function activate(context: vscode.ExtensionContext) {
|
||||||
return new vscode.Selection(r.start, r.end)
|
return new vscode.Selection(r.start, r.end)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
registerCommand('libsyntax-rust.matchingBrace', async () => {
|
||||||
|
let editor = vscode.window.activeTextEditor
|
||||||
|
if (editor == null || editor.document.languageId != "rust") return
|
||||||
|
let request: FindMatchingBraceParams = {
|
||||||
|
textDocument: { uri: editor.document.uri.toString() },
|
||||||
|
offsets: editor.selections.map((s) => {
|
||||||
|
return client.code2ProtocolConverter.asPosition(s.active)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
let response = await client.sendRequest<lc.Position[]>("m/findMatchingBrace", request)
|
||||||
|
editor.selections = editor.selections.map((sel, idx) => {
|
||||||
|
let active = client.protocol2CodeConverter.asPosition(response[idx])
|
||||||
|
let anchor = sel.isEmpty ? active : sel.anchor
|
||||||
|
return new vscode.Selection(anchor, active)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
dispose(vscode.workspace.registerTextDocumentContentProvider(
|
dispose(vscode.workspace.registerTextDocumentContentProvider(
|
||||||
'libsyntax-rust',
|
'libsyntax-rust',
|
||||||
|
@ -184,6 +200,11 @@ interface ExtendSelectionResult {
|
||||||
selections: lc.Range[];
|
selections: lc.Range[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FindMatchingBraceParams {
|
||||||
|
textDocument: lc.TextDocumentIdentifier;
|
||||||
|
offsets: lc.Position[];
|
||||||
|
}
|
||||||
|
|
||||||
interface PublishDecorationsParams {
|
interface PublishDecorationsParams {
|
||||||
uri: string,
|
uri: string,
|
||||||
decorations: Decoration[],
|
decorations: Decoration[],
|
||||||
|
|
|
@ -12,8 +12,8 @@ mod code_actions;
|
||||||
use libsyntax2::{
|
use libsyntax2::{
|
||||||
ast::{self, NameOwner},
|
ast::{self, NameOwner},
|
||||||
AstNode,
|
AstNode,
|
||||||
algo::walk,
|
algo::{walk, find_leaf_at_offset},
|
||||||
SyntaxKind::*,
|
SyntaxKind::{self, *},
|
||||||
};
|
};
|
||||||
pub use libsyntax2::{File, TextRange, TextUnit};
|
pub use libsyntax2::{File, TextRange, TextUnit};
|
||||||
pub use self::{
|
pub use self::{
|
||||||
|
@ -52,6 +52,28 @@ pub fn parse(text: &str) -> ast::File {
|
||||||
ast::File::parse(text)
|
ast::File::parse(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn matching_brace(file: &ast::File, offset: TextUnit) -> Option<TextUnit> {
|
||||||
|
const BRACES: &[SyntaxKind] = &[
|
||||||
|
L_CURLY, R_CURLY,
|
||||||
|
L_BRACK, R_BRACK,
|
||||||
|
L_PAREN, R_PAREN,
|
||||||
|
L_ANGLE, R_ANGLE,
|
||||||
|
];
|
||||||
|
let syntax = file.syntax();
|
||||||
|
let syntax = syntax.as_ref();
|
||||||
|
let (brace_node, brace_idx) = find_leaf_at_offset(syntax, offset)
|
||||||
|
.filter_map(|node| {
|
||||||
|
let idx = BRACES.iter().position(|&brace| brace == node.kind())?;
|
||||||
|
Some((node, idx))
|
||||||
|
})
|
||||||
|
.next()?;
|
||||||
|
let parent = brace_node.parent()?;
|
||||||
|
let matching_kind = BRACES[brace_idx ^ 1];
|
||||||
|
let matching_node = parent.children()
|
||||||
|
.find(|node| node.kind() == matching_kind)?;
|
||||||
|
Some(matching_node.range().start())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn highlight(file: &ast::File) -> Vec<HighlightedRange> {
|
pub fn highlight(file: &ast::File) -> Vec<HighlightedRange> {
|
||||||
let syntax = file.syntax();
|
let syntax = file.syntax();
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
|
|
|
@ -9,7 +9,7 @@ use itertools::Itertools;
|
||||||
use libeditor::{
|
use libeditor::{
|
||||||
File, TextUnit, TextRange, ActionResult, CursorPosition,
|
File, TextUnit, TextRange, ActionResult, CursorPosition,
|
||||||
highlight, runnables, extend_selection, file_structure,
|
highlight, runnables, extend_selection, file_structure,
|
||||||
flip_comma, add_derive,
|
flip_comma, add_derive, matching_brace,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -119,6 +119,25 @@ fn test_add_derive() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_matching_brace() {
|
||||||
|
fn do_check(before: &str, after: &str) {
|
||||||
|
let (pos, before) = extract_cursor(before);
|
||||||
|
let file = file(&before);
|
||||||
|
let new_pos = match matching_brace(&file, pos) {
|
||||||
|
None => pos,
|
||||||
|
Some(pos) => pos,
|
||||||
|
};
|
||||||
|
let actual = add_cursor(&before, new_pos);
|
||||||
|
assert_eq_text!(after, &actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_check(
|
||||||
|
"struct Foo { a: i32, }<|>",
|
||||||
|
"struct Foo <|>{ a: i32, }",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn file(text: &str) -> File {
|
fn file(text: &str) -> File {
|
||||||
File::parse(text)
|
File::parse(text)
|
||||||
}
|
}
|
||||||
|
@ -138,16 +157,12 @@ fn check_action<F: Fn(&File, TextUnit) -> Option<ActionResult>>(
|
||||||
let file = file(&before);
|
let file = file(&before);
|
||||||
let result = f(&file, before_cursor_pos).expect("code action is not applicable");
|
let result = f(&file, before_cursor_pos).expect("code action is not applicable");
|
||||||
let actual = result.edit.apply(&before);
|
let actual = result.edit.apply(&before);
|
||||||
let actual_cursor_pos: u32 = match result.cursor_position {
|
let actual_cursor_pos = match result.cursor_position {
|
||||||
CursorPosition::Same => result.edit.apply_to_offset(before_cursor_pos).unwrap(),
|
CursorPosition::Same => result.edit.apply_to_offset(before_cursor_pos).unwrap(),
|
||||||
CursorPosition::Offset(off) => off,
|
CursorPosition::Offset(off) => off,
|
||||||
}.into();
|
};
|
||||||
let actual_cursor_pos = actual_cursor_pos as usize;
|
let actual = add_cursor(&actual, actual_cursor_pos);
|
||||||
let mut actual_with_cursor = String::new();
|
assert_eq_text!(after, &actual);
|
||||||
actual_with_cursor.push_str(&actual[..actual_cursor_pos]);
|
|
||||||
actual_with_cursor.push_str("<|>");
|
|
||||||
actual_with_cursor.push_str(&actual[actual_cursor_pos..]);
|
|
||||||
assert_eq_text!(after, &actual_with_cursor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_cursor(text: &str) -> (TextUnit, String) {
|
fn extract_cursor(text: &str) -> (TextUnit, String) {
|
||||||
|
@ -162,3 +177,13 @@ fn extract_cursor(text: &str) -> (TextUnit, String) {
|
||||||
let cursor_pos = TextUnit::from(cursor_pos as u32);
|
let cursor_pos = TextUnit::from(cursor_pos as u32);
|
||||||
(cursor_pos, new_text)
|
(cursor_pos, new_text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_cursor(text: &str, offset: TextUnit) -> String {
|
||||||
|
let offset: u32 = offset.into();
|
||||||
|
let offset: usize = offset as usize;
|
||||||
|
let mut res = String::new();
|
||||||
|
res.push_str(&text[..offset]);
|
||||||
|
res.push_str("<|>");
|
||||||
|
res.push_str(&text[offset..]);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
|
@ -117,6 +117,14 @@ impl ConvWith for AtomEdit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: ConvWith> ConvWith for Option<T> {
|
||||||
|
type Ctx = <T as ConvWith>::Ctx;
|
||||||
|
type Output = Option<<T as ConvWith>::Output>;
|
||||||
|
fn conv_with(self, ctx: &Self::Ctx) -> Self::Output {
|
||||||
|
self.map(|x| ConvWith::conv_with(x, ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> TryConvWith for &'a Url {
|
impl<'a> TryConvWith for &'a Url {
|
||||||
type Ctx = PathMap;
|
type Ctx = PathMap;
|
||||||
type Output = FileId;
|
type Output = FileId;
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||||
use languageserver_types::{
|
use languageserver_types::{
|
||||||
Diagnostic, DiagnosticSeverity, Url, DocumentSymbol,
|
Diagnostic, DiagnosticSeverity, Url, DocumentSymbol,
|
||||||
Command, TextDocumentIdentifier, WorkspaceEdit,
|
Command, TextDocumentIdentifier, WorkspaceEdit,
|
||||||
SymbolInformation,
|
SymbolInformation, Position,
|
||||||
};
|
};
|
||||||
use libanalysis::{World, Query};
|
use libanalysis::{World, Query};
|
||||||
use libeditor;
|
use libeditor;
|
||||||
|
@ -42,6 +42,25 @@ pub fn handle_extend_selection(
|
||||||
Ok(req::ExtendSelectionResult { selections })
|
Ok(req::ExtendSelectionResult { selections })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_find_matching_brace(
|
||||||
|
world: World,
|
||||||
|
path_map: PathMap,
|
||||||
|
params: req::FindMatchingBraceParams,
|
||||||
|
) -> Result<Vec<Position>> {
|
||||||
|
let file_id = params.text_document.try_conv_with(&path_map)?;
|
||||||
|
let file = world.file_syntax(file_id)?;
|
||||||
|
let line_index = world.file_line_index(file_id)?;
|
||||||
|
let res = params.offsets
|
||||||
|
.into_iter()
|
||||||
|
.map_conv_with(&line_index)
|
||||||
|
.map(|offset| {
|
||||||
|
libeditor::matching_brace(&file, offset).unwrap_or(offset)
|
||||||
|
})
|
||||||
|
.map_conv_with(&line_index)
|
||||||
|
.collect();
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_document_symbol(
|
pub fn handle_document_symbol(
|
||||||
world: World,
|
world: World,
|
||||||
path_map: PathMap,
|
path_map: PathMap,
|
||||||
|
|
|
@ -26,6 +26,7 @@ use {
|
||||||
handle_execute_command,
|
handle_execute_command,
|
||||||
handle_workspace_symbol,
|
handle_workspace_symbol,
|
||||||
handle_goto_definition,
|
handle_goto_definition,
|
||||||
|
handle_find_matching_brace,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -148,6 +149,9 @@ fn on_request(
|
||||||
handle_request_on_threadpool::<req::ExtendSelection>(
|
handle_request_on_threadpool::<req::ExtendSelection>(
|
||||||
&mut req, pool, path_map, world, sender, handle_extend_selection,
|
&mut req, pool, path_map, world, sender, handle_extend_selection,
|
||||||
)?;
|
)?;
|
||||||
|
handle_request_on_threadpool::<req::FindMatchingBrace>(
|
||||||
|
&mut req, pool, path_map, world, sender, handle_find_matching_brace,
|
||||||
|
)?;
|
||||||
handle_request_on_threadpool::<req::DocumentSymbolRequest>(
|
handle_request_on_threadpool::<req::DocumentSymbolRequest>(
|
||||||
&mut req, pool, path_map, world, sender, handle_document_symbol,
|
&mut req, pool, path_map, world, sender, handle_document_symbol,
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use serde::{ser::Serialize, de::DeserializeOwned};
|
use serde::{ser::Serialize, de::DeserializeOwned};
|
||||||
use languageserver_types::{TextDocumentIdentifier, Range, Url};
|
use languageserver_types::{TextDocumentIdentifier, Range, Url, Position};
|
||||||
use url_serde;
|
use url_serde;
|
||||||
|
|
||||||
pub use languageserver_types::{
|
pub use languageserver_types::{
|
||||||
|
@ -65,6 +65,21 @@ pub struct ExtendSelectionResult {
|
||||||
pub selections: Vec<Range>,
|
pub selections: Vec<Range>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum FindMatchingBrace {}
|
||||||
|
|
||||||
|
impl Request for FindMatchingBrace {
|
||||||
|
type Params = FindMatchingBraceParams;
|
||||||
|
type Result = Vec<Position>;
|
||||||
|
const METHOD: &'static str = "m/findMatchingBrace";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct FindMatchingBraceParams {
|
||||||
|
pub text_document: TextDocumentIdentifier,
|
||||||
|
pub offsets: Vec<Position>,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum PublishDecorations {}
|
pub enum PublishDecorations {}
|
||||||
|
|
||||||
impl Notification for PublishDecorations {
|
impl Notification for PublishDecorations {
|
||||||
|
|
Loading…
Reference in a new issue