matching brace

This commit is contained in:
Aleksey Kladov 2018-08-16 00:23:22 +03:00
parent aa0d344581
commit c631b585a7
9 changed files with 135 additions and 14 deletions

View file

@ -1,3 +1,5 @@
[workspace]
members = [ "crates/*" ]
exclude = [ "crates/indxr" ]
[profile.release]
debug = true

View file

@ -36,6 +36,11 @@
{
"command": "libsyntax-rust.extendSelection",
"title": "Rust Extend Selection"
},
{
"command": "libsyntax-rust.matchingBrace",
"key": "ctrl+shift+m",
"title": "Rust Matching Brace"
}
],
"keybindings": [

View file

@ -35,6 +35,22 @@ export function activate(context: vscode.ExtensionContext) {
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(
'libsyntax-rust',
@ -184,6 +200,11 @@ interface ExtendSelectionResult {
selections: lc.Range[];
}
interface FindMatchingBraceParams {
textDocument: lc.TextDocumentIdentifier;
offsets: lc.Position[];
}
interface PublishDecorationsParams {
uri: string,
decorations: Decoration[],

View file

@ -12,8 +12,8 @@ mod code_actions;
use libsyntax2::{
ast::{self, NameOwner},
AstNode,
algo::walk,
SyntaxKind::*,
algo::{walk, find_leaf_at_offset},
SyntaxKind::{self, *},
};
pub use libsyntax2::{File, TextRange, TextUnit};
pub use self::{
@ -52,6 +52,28 @@ pub fn parse(text: &str) -> ast::File {
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> {
let syntax = file.syntax();
let mut res = Vec::new();

View file

@ -9,7 +9,7 @@ use itertools::Itertools;
use libeditor::{
File, TextUnit, TextRange, ActionResult, CursorPosition,
highlight, runnables, extend_selection, file_structure,
flip_comma, add_derive,
flip_comma, add_derive, matching_brace,
};
#[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 {
File::parse(text)
}
@ -138,16 +157,12 @@ fn check_action<F: Fn(&File, TextUnit) -> Option<ActionResult>>(
let file = file(&before);
let result = f(&file, before_cursor_pos).expect("code action is not applicable");
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::Offset(off) => off,
}.into();
let actual_cursor_pos = actual_cursor_pos as usize;
let mut actual_with_cursor = String::new();
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);
};
let actual = add_cursor(&actual, actual_cursor_pos);
assert_eq_text!(after, &actual);
}
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);
(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
}

View file

@ -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 {
type Ctx = PathMap;
type Output = FileId;

View file

@ -3,7 +3,7 @@ use std::collections::HashMap;
use languageserver_types::{
Diagnostic, DiagnosticSeverity, Url, DocumentSymbol,
Command, TextDocumentIdentifier, WorkspaceEdit,
SymbolInformation,
SymbolInformation, Position,
};
use libanalysis::{World, Query};
use libeditor;
@ -42,6 +42,25 @@ pub fn handle_extend_selection(
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(
world: World,
path_map: PathMap,

View file

@ -26,6 +26,7 @@ use {
handle_execute_command,
handle_workspace_symbol,
handle_goto_definition,
handle_find_matching_brace,
},
};
@ -148,6 +149,9 @@ fn on_request(
handle_request_on_threadpool::<req::ExtendSelection>(
&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>(
&mut req, pool, path_map, world, sender, handle_document_symbol,
)?;

View file

@ -1,5 +1,5 @@
use serde::{ser::Serialize, de::DeserializeOwned};
use languageserver_types::{TextDocumentIdentifier, Range, Url};
use languageserver_types::{TextDocumentIdentifier, Range, Url, Position};
use url_serde;
pub use languageserver_types::{
@ -65,6 +65,21 @@ pub struct ExtendSelectionResult {
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 {}
impl Notification for PublishDecorations {