mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 12:54:58 +00:00
better recovery for exprs
This commit is contained in:
parent
13110f48e9
commit
2fa90e736b
16 changed files with 263 additions and 27 deletions
|
@ -27,7 +27,7 @@ pub use self::{
|
||||||
ActionResult,
|
ActionResult,
|
||||||
flip_comma, add_derive, add_impl,
|
flip_comma, add_derive, add_impl,
|
||||||
},
|
},
|
||||||
typing::join_lines,
|
typing::{join_lines, on_eq_typed},
|
||||||
completion::scope_completion,
|
completion::scope_completion,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,11 @@ use libsyntax2::{
|
||||||
walk::preorder,
|
walk::preorder,
|
||||||
find_covering_node,
|
find_covering_node,
|
||||||
},
|
},
|
||||||
text_utils::intersect,
|
text_utils::{intersect, contains_offset_nonstrict},
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use {ActionResult, EditBuilder};
|
use {ActionResult, EditBuilder, find_node_at_offset};
|
||||||
|
|
||||||
pub fn join_lines(file: &File, range: TextRange) -> ActionResult {
|
pub fn join_lines(file: &File, range: TextRange) -> ActionResult {
|
||||||
let range = if range.is_empty() {
|
let range = if range.is_empty() {
|
||||||
|
@ -56,6 +56,26 @@ pub fn join_lines(file: &File, range: TextRange) -> ActionResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option<ActionResult> {
|
||||||
|
let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
|
||||||
|
if let_stmt.has_semi() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if let Some(expr) = let_stmt.initializer() {
|
||||||
|
let expr_range = expr.syntax().range();
|
||||||
|
if contains_offset_nonstrict(expr_range, offset) && offset != expr_range.start() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let offset = let_stmt.syntax().range().end();
|
||||||
|
let mut edit = EditBuilder::new();
|
||||||
|
edit.insert(offset, ";".to_string());
|
||||||
|
Some(ActionResult {
|
||||||
|
edit: edit.finish(),
|
||||||
|
cursor_position: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn remove_newline(
|
fn remove_newline(
|
||||||
edit: &mut EditBuilder,
|
edit: &mut EditBuilder,
|
||||||
node: SyntaxNodeRef,
|
node: SyntaxNodeRef,
|
||||||
|
|
|
@ -9,7 +9,7 @@ use libeditor::{
|
||||||
ActionResult,
|
ActionResult,
|
||||||
highlight, runnables, extend_selection, file_structure,
|
highlight, runnables, extend_selection, file_structure,
|
||||||
flip_comma, add_derive, add_impl, matching_brace,
|
flip_comma, add_derive, add_impl, matching_brace,
|
||||||
join_lines, scope_completion,
|
join_lines, on_eq_typed, scope_completion,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -227,7 +227,7 @@ pub fn reparse(&self, edit: &AtomEdit) -> File {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_join_lines_selection() {
|
fn test_join_lines_selection() {
|
||||||
fn do_check(before: &str, after: &str) {
|
fn do_check(before: &str, after: &str) {
|
||||||
let (sel, before) = extract_range(&before);
|
let (sel, before) = extract_range(before);
|
||||||
let file = file(&before);
|
let file = file(&before);
|
||||||
let result = join_lines(&file, sel);
|
let result = join_lines(&file, sel);
|
||||||
let actual = result.edit.apply(&before);
|
let actual = result.edit.apply(&before);
|
||||||
|
@ -256,6 +256,48 @@ struct Foo { f: u32 }
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_on_eq_typed() {
|
||||||
|
fn do_check(before: &str, after: &str) {
|
||||||
|
let (offset, before) = extract_offset(before);
|
||||||
|
let file = file(&before);
|
||||||
|
let result = on_eq_typed(&file, offset).unwrap();
|
||||||
|
let actual = result.edit.apply(&before);
|
||||||
|
assert_eq_text!(after, &actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_check(r"
|
||||||
|
fn foo() {
|
||||||
|
let foo =<|>
|
||||||
|
}
|
||||||
|
", r"
|
||||||
|
fn foo() {
|
||||||
|
let foo =;
|
||||||
|
}
|
||||||
|
");
|
||||||
|
do_check(r"
|
||||||
|
fn foo() {
|
||||||
|
let foo =<|> 1 + 1
|
||||||
|
}
|
||||||
|
", r"
|
||||||
|
fn foo() {
|
||||||
|
let foo = 1 + 1;
|
||||||
|
}
|
||||||
|
");
|
||||||
|
// do_check(r"
|
||||||
|
// fn foo() {
|
||||||
|
// let foo =<|>
|
||||||
|
// let bar = 1;
|
||||||
|
// }
|
||||||
|
// ", r"
|
||||||
|
// fn foo() {
|
||||||
|
// let foo =;
|
||||||
|
// let bar = 1;
|
||||||
|
// }
|
||||||
|
// ");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_completion() {
|
fn test_completion() {
|
||||||
fn do_check(code: &str, expected_completions: &str) {
|
fn do_check(code: &str, expected_completions: &str) {
|
||||||
|
|
|
@ -439,6 +439,24 @@ impl<'a> ExprStmt<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExternCrateItem
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ExternCrateItem<'a> {
|
||||||
|
syntax: SyntaxNodeRef<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AstNode<'a> for ExternCrateItem<'a> {
|
||||||
|
fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
|
||||||
|
match syntax.kind() {
|
||||||
|
EXTERN_CRATE_ITEM => Some(ExternCrateItem { syntax }),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ExternCrateItem<'a> {}
|
||||||
|
|
||||||
// FieldExpr
|
// FieldExpr
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct FieldExpr<'a> {
|
pub struct FieldExpr<'a> {
|
||||||
|
@ -839,11 +857,51 @@ impl<'a> AstNode<'a> for Module<'a> {
|
||||||
impl<'a> ast::NameOwner<'a> for Module<'a> {}
|
impl<'a> ast::NameOwner<'a> for Module<'a> {}
|
||||||
impl<'a> ast::AttrsOwner<'a> for Module<'a> {}
|
impl<'a> ast::AttrsOwner<'a> for Module<'a> {}
|
||||||
impl<'a> Module<'a> {
|
impl<'a> Module<'a> {
|
||||||
pub fn modules(self) -> impl Iterator<Item = Module<'a>> + 'a {
|
pub fn items(self) -> impl Iterator<Item = ModuleItem<'a>> + 'a {
|
||||||
super::children(self)
|
super::children(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ModuleItem
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ModuleItem<'a> {
|
||||||
|
StructDef(StructDef<'a>),
|
||||||
|
EnumDef(EnumDef<'a>),
|
||||||
|
FnDef(FnDef<'a>),
|
||||||
|
TraitDef(TraitDef<'a>),
|
||||||
|
ImplItem(ImplItem<'a>),
|
||||||
|
UseItem(UseItem<'a>),
|
||||||
|
ExternCrateItem(ExternCrateItem<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AstNode<'a> for ModuleItem<'a> {
|
||||||
|
fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
|
||||||
|
match syntax.kind() {
|
||||||
|
STRUCT_DEF => Some(ModuleItem::StructDef(StructDef { syntax })),
|
||||||
|
ENUM_DEF => Some(ModuleItem::EnumDef(EnumDef { syntax })),
|
||||||
|
FN_DEF => Some(ModuleItem::FnDef(FnDef { syntax })),
|
||||||
|
TRAIT_DEF => Some(ModuleItem::TraitDef(TraitDef { syntax })),
|
||||||
|
IMPL_ITEM => Some(ModuleItem::ImplItem(ImplItem { syntax })),
|
||||||
|
USE_ITEM => Some(ModuleItem::UseItem(UseItem { syntax })),
|
||||||
|
EXTERN_CRATE_ITEM => Some(ModuleItem::ExternCrateItem(ExternCrateItem { syntax })),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn syntax(self) -> SyntaxNodeRef<'a> {
|
||||||
|
match self {
|
||||||
|
ModuleItem::StructDef(inner) => inner.syntax(),
|
||||||
|
ModuleItem::EnumDef(inner) => inner.syntax(),
|
||||||
|
ModuleItem::FnDef(inner) => inner.syntax(),
|
||||||
|
ModuleItem::TraitDef(inner) => inner.syntax(),
|
||||||
|
ModuleItem::ImplItem(inner) => inner.syntax(),
|
||||||
|
ModuleItem::UseItem(inner) => inner.syntax(),
|
||||||
|
ModuleItem::ExternCrateItem(inner) => inner.syntax(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ModuleItem<'a> {}
|
||||||
|
|
||||||
// Name
|
// Name
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Name<'a> {
|
pub struct Name<'a> {
|
||||||
|
@ -1762,6 +1820,24 @@ impl<'a> AstNode<'a> for TypeRef<'a> {
|
||||||
|
|
||||||
impl<'a> TypeRef<'a> {}
|
impl<'a> TypeRef<'a> {}
|
||||||
|
|
||||||
|
// UseItem
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct UseItem<'a> {
|
||||||
|
syntax: SyntaxNodeRef<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AstNode<'a> for UseItem<'a> {
|
||||||
|
fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
|
||||||
|
match syntax.kind() {
|
||||||
|
USE_ITEM => Some(UseItem { syntax }),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> UseItem<'a> {}
|
||||||
|
|
||||||
// WhereClause
|
// WhereClause
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct WhereClause<'a> {
|
pub struct WhereClause<'a> {
|
||||||
|
|
|
@ -115,6 +115,15 @@ impl<'a> Module<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> LetStmt<'a> {
|
||||||
|
pub fn has_semi(self) -> bool {
|
||||||
|
match self.syntax().last_child() {
|
||||||
|
None => false,
|
||||||
|
Some(node) => node.kind() == SEMI,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> IfExpr<'a> {
|
impl<'a> IfExpr<'a> {
|
||||||
pub fn then_branch(self) -> Option<Block<'a>> {
|
pub fn then_branch(self) -> Option<Block<'a>> {
|
||||||
self.blocks().nth(0)
|
self.blocks().nth(0)
|
||||||
|
|
|
@ -273,7 +273,7 @@ Grammar(
|
||||||
"Module": (
|
"Module": (
|
||||||
traits: ["NameOwner", "AttrsOwner"],
|
traits: ["NameOwner", "AttrsOwner"],
|
||||||
collections: [
|
collections: [
|
||||||
["modules", "Module"]
|
["items", "ModuleItem"]
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
"ConstDef": ( traits: [
|
"ConstDef": ( traits: [
|
||||||
|
@ -331,6 +331,10 @@ Grammar(
|
||||||
"AttrsOwner"
|
"AttrsOwner"
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
"ModuleItem": (
|
||||||
|
enum: ["StructDef", "EnumDef", "FnDef", "TraitDef", "ImplItem",
|
||||||
|
"UseItem", "ExternCrateItem" ]
|
||||||
|
),
|
||||||
|
|
||||||
"TupleExpr": (),
|
"TupleExpr": (),
|
||||||
"ArrayExpr": (),
|
"ArrayExpr": (),
|
||||||
|
@ -479,6 +483,8 @@ Grammar(
|
||||||
),
|
),
|
||||||
"Param": (
|
"Param": (
|
||||||
options: [["pat", "Pat"]],
|
options: [["pat", "Pat"]],
|
||||||
)
|
),
|
||||||
|
"UseItem": (),
|
||||||
|
"ExternCrateItem": (),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,6 +33,9 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet =
|
||||||
RETURN_KW, IDENT, SELF_KW, SUPER_KW, COLONCOLON, BREAK_KW, CONTINUE_KW, LIFETIME ],
|
RETURN_KW, IDENT, SELF_KW, SUPER_KW, COLONCOLON, BREAK_KW, CONTINUE_KW, LIFETIME ],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const EXPR_RECOVERY_SET: TokenSet =
|
||||||
|
token_set![LET_KW];
|
||||||
|
|
||||||
pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> {
|
pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> {
|
||||||
match literal(p) {
|
match literal(p) {
|
||||||
Some(m) => return Some(m),
|
Some(m) => return Some(m),
|
||||||
|
@ -73,7 +76,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<CompletedMark
|
||||||
CONTINUE_KW => continue_expr(p),
|
CONTINUE_KW => continue_expr(p),
|
||||||
BREAK_KW => break_expr(p),
|
BREAK_KW => break_expr(p),
|
||||||
_ => {
|
_ => {
|
||||||
p.err_and_bump("expected expression");
|
p.err_recover("expected expression", EXPR_RECOVERY_SET);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,6 +12,8 @@ fn mask(kind: SyntaxKind) -> u128 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenSet {
|
impl TokenSet {
|
||||||
|
const EMPTY: TokenSet = TokenSet(0);
|
||||||
|
|
||||||
pub fn contains(&self, kind: SyntaxKind) -> bool {
|
pub fn contains(&self, kind: SyntaxKind) -> bool {
|
||||||
self.0 & mask(kind) != 0
|
self.0 & mask(kind) != 0
|
||||||
}
|
}
|
||||||
|
@ -139,13 +141,22 @@ impl<'t> Parser<'t> {
|
||||||
|
|
||||||
/// Create an error node and consume the next token.
|
/// Create an error node and consume the next token.
|
||||||
pub(crate) fn err_and_bump(&mut self, message: &str) {
|
pub(crate) fn err_and_bump(&mut self, message: &str) {
|
||||||
|
self.err_recover(message, TokenSet::EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an error node and consume the next token.
|
||||||
|
pub(crate) fn err_recover(&mut self, message: &str, recovery_set: TokenSet) {
|
||||||
|
if self.at(SyntaxKind::L_CURLY)
|
||||||
|
|| self.at(SyntaxKind::R_CURLY)
|
||||||
|
|| recovery_set.contains(self.current()) {
|
||||||
|
self.error(message);
|
||||||
|
} else {
|
||||||
let m = self.start();
|
let m = self.start();
|
||||||
self.error(message);
|
self.error(message);
|
||||||
if !self.at(SyntaxKind::L_CURLY) && !self.at(SyntaxKind::R_CURLY) {
|
|
||||||
self.bump();
|
self.bump();
|
||||||
}
|
|
||||||
m.complete(self, ERROR);
|
m.complete(self, ERROR);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See `Parser::start`.
|
/// See `Parser::start`.
|
||||||
|
|
|
@ -35,13 +35,12 @@ ROOT@[0; 47)
|
||||||
INT_NUMBER@[33; 35) "92"
|
INT_NUMBER@[33; 35) "92"
|
||||||
SEMI@[35; 36)
|
SEMI@[35; 36)
|
||||||
WHITESPACE@[36; 41)
|
WHITESPACE@[36; 41)
|
||||||
BIN_EXPR@[41; 45)
|
BIN_EXPR@[41; 44)
|
||||||
LITERAL@[41; 42)
|
LITERAL@[41; 42)
|
||||||
INT_NUMBER@[41; 42) "1"
|
INT_NUMBER@[41; 42) "1"
|
||||||
WHITESPACE@[42; 43)
|
WHITESPACE@[42; 43)
|
||||||
PLUS@[43; 44)
|
PLUS@[43; 44)
|
||||||
WHITESPACE@[44; 45)
|
|
||||||
err: `expected expression`
|
err: `expected expression`
|
||||||
ERROR@[45; 45)
|
WHITESPACE@[44; 45)
|
||||||
R_CURLY@[45; 46)
|
R_CURLY@[45; 46)
|
||||||
WHITESPACE@[46; 47)
|
WHITESPACE@[46; 47)
|
||||||
|
|
|
@ -11,12 +11,12 @@ ROOT@[0; 183)
|
||||||
ITEM_LIST@[14; 182)
|
ITEM_LIST@[14; 182)
|
||||||
L_CURLY@[14; 15)
|
L_CURLY@[14; 15)
|
||||||
WHITESPACE@[15; 20)
|
WHITESPACE@[15; 20)
|
||||||
FN_DEF@[20; 181)
|
FN_DEF@[20; 180)
|
||||||
FN_KW@[20; 22)
|
FN_KW@[20; 22)
|
||||||
WHITESPACE@[22; 23)
|
WHITESPACE@[22; 23)
|
||||||
NAME@[23; 32)
|
NAME@[23; 32)
|
||||||
IDENT@[23; 32) "new_scope"
|
IDENT@[23; 32) "new_scope"
|
||||||
PARAM_LIST@[32; 181)
|
PARAM_LIST@[32; 180)
|
||||||
L_PAREN@[32; 33)
|
L_PAREN@[32; 33)
|
||||||
PARAM@[33; 38)
|
PARAM@[33; 38)
|
||||||
REF_PAT@[33; 35)
|
REF_PAT@[33; 35)
|
||||||
|
@ -163,17 +163,16 @@ ROOT@[0; 183)
|
||||||
err: `expected parameters`
|
err: `expected parameters`
|
||||||
err: `expected COMMA`
|
err: `expected COMMA`
|
||||||
WHITESPACE@[169; 170)
|
WHITESPACE@[169; 170)
|
||||||
PARAM@[170; 181)
|
PARAM@[170; 180)
|
||||||
BIND_PAT@[170; 180)
|
BIND_PAT@[170; 180)
|
||||||
NAME@[170; 180)
|
NAME@[170; 180)
|
||||||
IDENT@[170; 180) "set_parent"
|
IDENT@[170; 180) "set_parent"
|
||||||
err: `expected COLON`
|
err: `expected COLON`
|
||||||
WHITESPACE@[180; 181)
|
|
||||||
err: `expected type`
|
err: `expected type`
|
||||||
err: `expected COMMA`
|
err: `expected COMMA`
|
||||||
err: `expected value parameter`
|
err: `expected value parameter`
|
||||||
err: `expected R_PAREN`
|
err: `expected R_PAREN`
|
||||||
err: `expected a block`
|
err: `expected a block`
|
||||||
ERROR@[181; 181)
|
WHITESPACE@[180; 181)
|
||||||
R_CURLY@[181; 182)
|
R_CURLY@[181; 182)
|
||||||
WHITESPACE@[182; 183)
|
WHITESPACE@[182; 183)
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
fn foo() {
|
||||||
|
let foo =
|
||||||
|
let bar = 1;
|
||||||
|
}
|
39
crates/libsyntax2/tests/data/parser/err/0019_let_recover.txt
Normal file
39
crates/libsyntax2/tests/data/parser/err/0019_let_recover.txt
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
ROOT@[0; 44)
|
||||||
|
FN_DEF@[0; 43)
|
||||||
|
FN_KW@[0; 2)
|
||||||
|
WHITESPACE@[2; 3)
|
||||||
|
NAME@[3; 6)
|
||||||
|
IDENT@[3; 6) "foo"
|
||||||
|
PARAM_LIST@[6; 8)
|
||||||
|
L_PAREN@[6; 7)
|
||||||
|
R_PAREN@[7; 8)
|
||||||
|
WHITESPACE@[8; 9)
|
||||||
|
BLOCK@[9; 43)
|
||||||
|
L_CURLY@[9; 10)
|
||||||
|
WHITESPACE@[10; 15)
|
||||||
|
LET_STMT@[15; 24)
|
||||||
|
LET_KW@[15; 18)
|
||||||
|
WHITESPACE@[18; 19)
|
||||||
|
BIND_PAT@[19; 22)
|
||||||
|
NAME@[19; 22)
|
||||||
|
IDENT@[19; 22) "foo"
|
||||||
|
WHITESPACE@[22; 23)
|
||||||
|
EQ@[23; 24)
|
||||||
|
err: `expected expression`
|
||||||
|
err: `expected SEMI`
|
||||||
|
WHITESPACE@[24; 29)
|
||||||
|
LET_STMT@[29; 41)
|
||||||
|
LET_KW@[29; 32)
|
||||||
|
WHITESPACE@[32; 33)
|
||||||
|
BIND_PAT@[33; 36)
|
||||||
|
NAME@[33; 36)
|
||||||
|
IDENT@[33; 36) "bar"
|
||||||
|
WHITESPACE@[36; 37)
|
||||||
|
EQ@[37; 38)
|
||||||
|
WHITESPACE@[38; 39)
|
||||||
|
LITERAL@[39; 40)
|
||||||
|
INT_NUMBER@[39; 40) "1"
|
||||||
|
SEMI@[40; 41)
|
||||||
|
WHITESPACE@[41; 42)
|
||||||
|
R_CURLY@[42; 43)
|
||||||
|
WHITESPACE@[43; 44)
|
|
@ -5,6 +5,7 @@ use languageserver_types::{
|
||||||
TextDocumentSyncKind,
|
TextDocumentSyncKind,
|
||||||
ExecuteCommandOptions,
|
ExecuteCommandOptions,
|
||||||
CompletionOptions,
|
CompletionOptions,
|
||||||
|
DocumentOnTypeFormattingOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn server_capabilities() -> ServerCapabilities {
|
pub fn server_capabilities() -> ServerCapabilities {
|
||||||
|
@ -35,7 +36,10 @@ pub fn server_capabilities() -> ServerCapabilities {
|
||||||
code_lens_provider: None,
|
code_lens_provider: None,
|
||||||
document_formatting_provider: None,
|
document_formatting_provider: None,
|
||||||
document_range_formatting_provider: None,
|
document_range_formatting_provider: None,
|
||||||
document_on_type_formatting_provider: None,
|
document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
|
||||||
|
first_trigger_character: "=".to_string(),
|
||||||
|
more_trigger_character: None,
|
||||||
|
}),
|
||||||
rename_provider: None,
|
rename_provider: None,
|
||||||
color_provider: None,
|
color_provider: None,
|
||||||
execute_command_provider: Some(ExecuteCommandOptions {
|
execute_command_provider: Some(ExecuteCommandOptions {
|
||||||
|
|
|
@ -314,6 +314,25 @@ pub fn handle_completion(
|
||||||
Ok(Some(req::CompletionResponse::Array(items)))
|
Ok(Some(req::CompletionResponse::Array(items)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_on_type_formatting(
|
||||||
|
world: ServerWorld,
|
||||||
|
params: req::DocumentOnTypeFormattingParams,
|
||||||
|
) -> Result<Option<Vec<TextEdit>>> {
|
||||||
|
if params.ch != "=" {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_id = params.text_document.try_conv_with(&world)?;
|
||||||
|
let line_index = world.analysis().file_line_index(file_id)?;
|
||||||
|
let offset = params.position.conv_with(&line_index);
|
||||||
|
let file = world.analysis().file_syntax(file_id)?;
|
||||||
|
let action = match libeditor::on_eq_typed(&file, offset) {
|
||||||
|
None => return Ok(None),
|
||||||
|
Some(action) => action,
|
||||||
|
};
|
||||||
|
Ok(Some(action.edit.conv_with(&line_index)))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_execute_command(
|
pub fn handle_execute_command(
|
||||||
world: ServerWorld,
|
world: ServerWorld,
|
||||||
mut params: req::ExecuteCommandParams,
|
mut params: req::ExecuteCommandParams,
|
||||||
|
|
|
@ -31,6 +31,7 @@ use {
|
||||||
handle_completion,
|
handle_completion,
|
||||||
handle_runnables,
|
handle_runnables,
|
||||||
handle_decorations,
|
handle_decorations,
|
||||||
|
handle_on_type_formatting,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -161,6 +162,9 @@ fn on_request(
|
||||||
handle_request_on_threadpool::<req::DecorationsRequest>(
|
handle_request_on_threadpool::<req::DecorationsRequest>(
|
||||||
&mut req, pool, world, sender, handle_decorations,
|
&mut req, pool, world, sender, handle_decorations,
|
||||||
)?;
|
)?;
|
||||||
|
handle_request_on_threadpool::<req::OnTypeFormatting>(
|
||||||
|
&mut req, pool, world, sender, handle_on_type_formatting,
|
||||||
|
)?;
|
||||||
dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| {
|
dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| {
|
||||||
io.send(RawMsg::Response(resp.into_response(Ok(None))?));
|
io.send(RawMsg::Response(resp.into_response(Ok(None))?));
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub use languageserver_types::{
|
||||||
TextDocumentPositionParams,
|
TextDocumentPositionParams,
|
||||||
TextEdit,
|
TextEdit,
|
||||||
CompletionParams, CompletionResponse,
|
CompletionParams, CompletionResponse,
|
||||||
|
DocumentOnTypeFormattingParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue