diff options
author | Aleksey Kladov <[email protected]> | 2018-09-18 22:46:10 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-09-18 22:46:10 +0100 |
commit | d6c7030aeb084106a3c4bae765731421e8ac1dbd (patch) | |
tree | ca4448b02f4ba654243addba4785ff82d225a824 | |
parent | 79293d2593ba658243d0f2edf18cd283fa40447a (diff) |
Add emacs function for extend shirnk selection
-rw-r--r-- | crates/ra_cli/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_cli/src/main.rs | 33 | ||||
-rw-r--r-- | crates/ra_syntax/src/algo/mod.rs | 6 | ||||
-rw-r--r-- | editors/emacs/ra.el | 86 |
4 files changed, 123 insertions, 3 deletions
diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index 17011e063..5e7bf3ed4 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml | |||
@@ -7,6 +7,7 @@ publish = false | |||
7 | [dependencies] | 7 | [dependencies] |
8 | clap = "2.32.0" | 8 | clap = "2.32.0" |
9 | failure = "0.1.1" | 9 | failure = "0.1.1" |
10 | join_to_string = "0.1.1" | ||
10 | ra_syntax = { path = "../ra_syntax" } | 11 | ra_syntax = { path = "../ra_syntax" } |
11 | ra_editor = { path = "../ra_editor" } | 12 | ra_editor = { path = "../ra_editor" } |
12 | tools = { path = "../tools" } | 13 | tools = { path = "../tools" } |
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs index 96e5b718c..11605cfd8 100644 --- a/crates/ra_cli/src/main.rs +++ b/crates/ra_cli/src/main.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | extern crate clap; | 1 | extern crate clap; |
2 | #[macro_use] | 2 | #[macro_use] |
3 | extern crate failure; | 3 | extern crate failure; |
4 | extern crate join_to_string; | ||
4 | extern crate ra_syntax; | 5 | extern crate ra_syntax; |
5 | extern crate ra_editor; | 6 | extern crate ra_editor; |
6 | extern crate tools; | 7 | extern crate tools; |
@@ -10,9 +11,10 @@ use std::{ | |||
10 | time::Instant | 11 | time::Instant |
11 | }; | 12 | }; |
12 | use clap::{App, Arg, SubCommand}; | 13 | use clap::{App, Arg, SubCommand}; |
14 | use join_to_string::join; | ||
13 | use tools::collect_tests; | 15 | use tools::collect_tests; |
14 | use ra_syntax::File; | 16 | use ra_syntax::{TextRange, File}; |
15 | use ra_editor::{syntax_tree, file_structure}; | 17 | use ra_editor::{syntax_tree, file_structure, extend_selection}; |
16 | 18 | ||
17 | type Result<T> = ::std::result::Result<T, failure::Error>; | 19 | type Result<T> = ::std::result::Result<T, failure::Error>; |
18 | 20 | ||
@@ -39,6 +41,10 @@ fn main() -> Result<()> { | |||
39 | .arg(Arg::with_name("no-dump").long("--no-dump")) | 41 | .arg(Arg::with_name("no-dump").long("--no-dump")) |
40 | ) | 42 | ) |
41 | .subcommand(SubCommand::with_name("symbols")) | 43 | .subcommand(SubCommand::with_name("symbols")) |
44 | .subcommand(SubCommand::with_name("extend-selection") | ||
45 | .arg(Arg::with_name("start")) | ||
46 | .arg(Arg::with_name("end")) | ||
47 | ) | ||
42 | .get_matches(); | 48 | .get_matches(); |
43 | match matches.subcommand() { | 49 | match matches.subcommand() { |
44 | ("parse", Some(matches)) => { | 50 | ("parse", Some(matches)) => { |
@@ -65,6 +71,13 @@ fn main() -> Result<()> { | |||
65 | let (test, tree) = render_test(file, line)?; | 71 | let (test, tree) = render_test(file, line)?; |
66 | println!("{}\n{}", test, tree); | 72 | println!("{}\n{}", test, tree); |
67 | } | 73 | } |
74 | ("extend-selection", Some(matches)) => { | ||
75 | let start: u32 = matches.value_of("start").unwrap().parse()?; | ||
76 | let end: u32 = matches.value_of("end").unwrap().parse()?; | ||
77 | let file = file()?; | ||
78 | let sels = selections(&file, start, end); | ||
79 | println!("{}", sels) | ||
80 | } | ||
68 | _ => unreachable!(), | 81 | _ => unreachable!(), |
69 | } | 82 | } |
70 | Ok(()) | 83 | Ok(()) |
@@ -95,3 +108,19 @@ fn render_test(file: &Path, line: usize) -> Result<(String, String)> { | |||
95 | let tree = syntax_tree(&file); | 108 | let tree = syntax_tree(&file); |
96 | Ok((test.text, tree)) | 109 | Ok((test.text, tree)) |
97 | } | 110 | } |
111 | |||
112 | fn selections(file: &File, start: u32, end: u32) -> String { | ||
113 | let mut ranges = Vec::new(); | ||
114 | let mut cur = Some(TextRange::from_to((start - 1).into(), (end - 1).into())); | ||
115 | while let Some(r) = cur { | ||
116 | ranges.push(r); | ||
117 | cur = extend_selection(&file, r); | ||
118 | } | ||
119 | let ranges = ranges.iter() | ||
120 | .map(|r| (1 + u32::from(r.start()), 1 + u32::from(r.end()))) | ||
121 | .map(|(s, e)| format!("({} {})", s, e)); | ||
122 | join(ranges) | ||
123 | .separator(" ") | ||
124 | .surround_with("(", ")") | ||
125 | .to_string() | ||
126 | } | ||
diff --git a/crates/ra_syntax/src/algo/mod.rs b/crates/ra_syntax/src/algo/mod.rs index 7287f5bb2..8de44c586 100644 --- a/crates/ra_syntax/src/algo/mod.rs +++ b/crates/ra_syntax/src/algo/mod.rs | |||
@@ -78,7 +78,11 @@ impl<'f> Iterator for LeafAtOffset<'f> { | |||
78 | } | 78 | } |
79 | 79 | ||
80 | pub fn find_covering_node(root: SyntaxNodeRef, range: TextRange) -> SyntaxNodeRef { | 80 | pub fn find_covering_node(root: SyntaxNodeRef, range: TextRange) -> SyntaxNodeRef { |
81 | assert!(is_subrange(root.range(), range)); | 81 | assert!( |
82 | is_subrange(root.range(), range), | ||
83 | "node range: {:?}, target range: {:?}", | ||
84 | root.range(), range, | ||
85 | ); | ||
82 | let (left, right) = match ( | 86 | let (left, right) = match ( |
83 | find_leaf_at_offset(root, range.start()).right_biased(), | 87 | find_leaf_at_offset(root, range.start()).right_biased(), |
84 | find_leaf_at_offset(root, range.end()).left_biased() | 88 | find_leaf_at_offset(root, range.end()).left_biased() |
diff --git a/editors/emacs/ra.el b/editors/emacs/ra.el new file mode 100644 index 000000000..6ff2ae742 --- /dev/null +++ b/editors/emacs/ra.el | |||
@@ -0,0 +1,86 @@ | |||
1 | ;;; ra.el --- Rust analyzer emacs bindings -*- lexical-binding: t; -*- | ||
2 | ;;; Commentary: | ||
3 | ;;; Small utilities for interacting with Rust analyzer. | ||
4 | ;;; Run | ||
5 | ;;; cargo install --git https://github.com/matklad/rust-analyzer/ --bin ra_cli | ||
6 | ;;; to install the binary, copy-paste the bellow code to your `.init.el` and use | ||
7 | ;;; `ra-extend-selection` and `ra-shrink-selection` functions | ||
8 | ;;; Code: | ||
9 | |||
10 | |||
11 | (defvar ra--selections-cache '(0 0 ())) | ||
12 | (defun ra--cache-tick () | ||
13 | "Get buffer modification count for cache." | ||
14 | (nth 0 ra--selections-cache)) | ||
15 | (defun ra--cache-sel () | ||
16 | "Get current selection for cache." | ||
17 | (nth 1 ra--selections-cache)) | ||
18 | (defun ra--cache-nth-sel (n) | ||
19 | "Get Nth selection." | ||
20 | (nth n (nth 2 ra--selections-cache))) | ||
21 | (defun ra--cache-set-nth-sel (n) | ||
22 | "Get Nth selection." | ||
23 | (setf (nth 1 ra--selections-cache) n) | ||
24 | (nth n (nth 2 ra--selections-cache))) | ||
25 | |||
26 | |||
27 | (defun ra-extend-selection () | ||
28 | "Extend START END region to contain the encompassing syntactic construct." | ||
29 | (interactive) | ||
30 | (let* ((p (point)) | ||
31 | (m (or (and mark-active (mark)) p)) | ||
32 | (start (min p m)) | ||
33 | (end (max p m))) | ||
34 | (ra--extend-selection start end))) | ||
35 | |||
36 | |||
37 | (defun ra-shrink-selection (start end) | ||
38 | "Shrink START END region to contain previous selection." | ||
39 | (interactive "r") | ||
40 | (ra--freshen-cache start end) | ||
41 | (let ((sel-id (ra--cache-sel))) | ||
42 | (if (not (= 0 sel-id)) | ||
43 | (let* ((r (ra--cache-set-nth-sel (- sel-id 1)))) | ||
44 | (push-mark (nth 0 r) t t) | ||
45 | (goto-char (nth 1 r)) | ||
46 | (setq deactivate-mark nil))))) | ||
47 | |||
48 | ; Add this to setup keybinding | ||
49 | ; (require 'rust-mode) | ||
50 | ; (define-key rust-mode-map (kbd "C-w") 'ra-extend-selection) | ||
51 | ; (define-key rust-mode-map (kbd "C-S-w") 'ra-shrink-selection) | ||
52 | |||
53 | |||
54 | |||
55 | (defun ra--extend-selection (start end) | ||
56 | "Extend START END region to contain the encompassing syntactic construct." | ||
57 | (ra--freshen-cache start end) | ||
58 | (let* ((next-sel-idx (+ 1 (ra--cache-sel))) | ||
59 | (r (ra--cache-set-nth-sel next-sel-idx))) | ||
60 | (push-mark (nth 0 r) t t) | ||
61 | (goto-char (nth 1 r)) | ||
62 | (setq deactivate-mark nil))) | ||
63 | |||
64 | (defun ra--selections (start end) | ||
65 | "Get list of selections for START END from Rust analyzer." | ||
66 | (read (with-output-to-string | ||
67 | (call-process-region | ||
68 | (point-min) (point-max) | ||
69 | "ra_cli" nil standard-output nil | ||
70 | "extend-selection" | ||
71 | (number-to-string start) | ||
72 | (number-to-string end))))) | ||
73 | |||
74 | (defun ra--freshen-cache (start end) | ||
75 | "Make selection cache up-to-date for current buffer state and START END." | ||
76 | (if (not (and | ||
77 | (= (buffer-modified-tick) (ra--cache-tick)) | ||
78 | (equal `(,start ,end) (ra--cache-nth-sel (ra--cache-sel))))) | ||
79 | (ra--set-cache start end))) | ||
80 | |||
81 | (defun ra--set-cache (start end) | ||
82 | "Set selections cache for current buffer state and START END." | ||
83 | (setq ra--selections-cache `(,(buffer-modified-tick) 0 ,(ra--selections start end)))) | ||
84 | |||
85 | (provide 'ra) | ||
86 | ;;; ra.el ends here | ||