From 2b956fd3a83313cee37ff179eae843bc88dd572a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 9 Oct 2018 16:00:20 +0300 Subject: Add on-enter handler Now, typing doc comments is much more pleasant --- crates/ra_editor/src/lib.rs | 2 +- crates/ra_editor/src/typing.rs | 101 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 3 deletions(-) (limited to 'crates/ra_editor/src') diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index 2a801f7da..fe0045378 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs @@ -35,7 +35,7 @@ pub use self::{ flip_comma, add_derive, add_impl, introduce_variable, }, - typing::{join_lines, on_eq_typed}, + typing::{join_lines, on_eq_typed, on_enter}, completion::{scope_completion, CompletionItem}, folding_ranges::{Fold, FoldKind, folding_ranges} }; diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs index 512076941..3384389d1 100644 --- a/crates/ra_editor/src/typing.rs +++ b/crates/ra_editor/src/typing.rs @@ -4,7 +4,7 @@ use ra_syntax::{ TextUnit, TextRange, SyntaxNodeRef, File, AstNode, SyntaxKind, ast, algo::{ - find_covering_node, + find_covering_node, find_leaf_at_offset, LeafAtOffset, }, text_utils::{intersect, contains_offset_nonstrict}, SyntaxKind::*, @@ -56,6 +56,58 @@ pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { } } +pub fn on_enter(file: &File, offset: TextUnit) -> Option { + let comment = find_leaf_at_offset(file.syntax(), offset).left_biased().filter(|it| it.kind() == COMMENT)?; + let prefix = comment_preffix(comment)?; + if offset < comment.range().start() + TextUnit::of_str(prefix) { + return None; + } + + let indent = node_indent(file, comment)?; + let inserted = format!("\n{}{}", indent, prefix); + let cursor_position = offset + TextUnit::of_str(&inserted); + let mut edit = EditBuilder::new(); + edit.insert(offset, inserted); + Some(LocalEdit { + edit: edit.finish(), + cursor_position: Some(cursor_position), + }) +} + +fn comment_preffix(comment: SyntaxNodeRef) -> Option<&'static str> { + let text = comment.leaf_text().unwrap(); + let res = if text.starts_with("///") { + "/// " + } else if text.starts_with("//!") { + "//! " + } else if text.starts_with("//") { + "// " + } else { + return None; + }; + Some(res) +} + +fn node_indent<'a>(file: &'a File, node: SyntaxNodeRef) -> Option<&'a str> { + let ws = match find_leaf_at_offset(file.syntax(), node.range().start()) { + LeafAtOffset::Between(l, r) => { + assert!(r == node); + l + } + LeafAtOffset::Single(n) => { + assert!(n == node); + return Some("") + } + LeafAtOffset::None => unreachable!(), + }; + if ws.kind() != WHITESPACE { + return None; + } + let text = ws.leaf_text().unwrap(); + let pos = text.as_str().rfind('\n').map(|it| it + 1).unwrap_or(0); + Some(&text[pos..]) +} + pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option { let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; if let_stmt.has_semi() { @@ -187,7 +239,7 @@ fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str { #[cfg(test)] mod tests { use super::*; - use test_utils::{check_action, extract_range, extract_offset}; + use test_utils::{check_action, extract_range, extract_offset, add_cursor}; fn check_join_lines(before: &str, after: &str) { check_action(before, after, |file, offset| { @@ -344,4 +396,49 @@ fn foo() { // } // "); } + + #[test] + fn test_on_enter() { + fn apply_on_enter(before: &str) -> Option { + let (offset, before) = extract_offset(before); + let file = File::parse(&before); + let result = on_enter(&file, offset)?; + let actual = result.edit.apply(&before); + let actual = add_cursor(&actual, result.cursor_position.unwrap()); + Some(actual) + } + + fn do_check(before: &str, after: &str) { + let actual = apply_on_enter(before).unwrap(); + assert_eq_text!(after, &actual); + } + + fn do_check_noop(text: &str) { + assert!(apply_on_enter(text).is_none()) + } + + do_check(r" +/// Some docs<|> +fn foo() { +} +", r" +/// Some docs +/// <|> +fn foo() { +} +"); + do_check(r" +impl S { + /// Some<|> docs. + fn foo() {} +} +", r" +impl S { + /// Some + /// <|> docs. + fn foo() {} +} +"); + do_check_noop(r"<|>//! docz"); + } } -- cgit v1.2.3