From d6c7030aeb084106a3c4bae765731421e8ac1dbd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 19 Sep 2018 00:46:10 +0300 Subject: [PATCH] Add emacs function for extend shirnk selection --- crates/ra_cli/Cargo.toml | 1 + crates/ra_cli/src/main.rs | 33 +++++++++++- crates/ra_syntax/src/algo/mod.rs | 6 ++- editors/emacs/ra.el | 86 ++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 editors/emacs/ra.el diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index 17011e0636e..5e7bf3ed457 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] clap = "2.32.0" failure = "0.1.1" +join_to_string = "0.1.1" ra_syntax = { path = "../ra_syntax" } ra_editor = { path = "../ra_editor" } tools = { path = "../tools" } diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs index 96e5b718c96..11605cfd86c 100644 --- a/crates/ra_cli/src/main.rs +++ b/crates/ra_cli/src/main.rs @@ -1,6 +1,7 @@ extern crate clap; #[macro_use] extern crate failure; +extern crate join_to_string; extern crate ra_syntax; extern crate ra_editor; extern crate tools; @@ -10,9 +11,10 @@ use std::{ time::Instant }; use clap::{App, Arg, SubCommand}; +use join_to_string::join; use tools::collect_tests; -use ra_syntax::File; -use ra_editor::{syntax_tree, file_structure}; +use ra_syntax::{TextRange, File}; +use ra_editor::{syntax_tree, file_structure, extend_selection}; type Result = ::std::result::Result; @@ -39,6 +41,10 @@ fn main() -> Result<()> { .arg(Arg::with_name("no-dump").long("--no-dump")) ) .subcommand(SubCommand::with_name("symbols")) + .subcommand(SubCommand::with_name("extend-selection") + .arg(Arg::with_name("start")) + .arg(Arg::with_name("end")) + ) .get_matches(); match matches.subcommand() { ("parse", Some(matches)) => { @@ -65,6 +71,13 @@ fn main() -> Result<()> { let (test, tree) = render_test(file, line)?; println!("{}\n{}", test, tree); } + ("extend-selection", Some(matches)) => { + let start: u32 = matches.value_of("start").unwrap().parse()?; + let end: u32 = matches.value_of("end").unwrap().parse()?; + let file = file()?; + let sels = selections(&file, start, end); + println!("{}", sels) + } _ => unreachable!(), } Ok(()) @@ -95,3 +108,19 @@ fn render_test(file: &Path, line: usize) -> Result<(String, String)> { let tree = syntax_tree(&file); Ok((test.text, tree)) } + +fn selections(file: &File, start: u32, end: u32) -> String { + let mut ranges = Vec::new(); + let mut cur = Some(TextRange::from_to((start - 1).into(), (end - 1).into())); + while let Some(r) = cur { + ranges.push(r); + cur = extend_selection(&file, r); + } + let ranges = ranges.iter() + .map(|r| (1 + u32::from(r.start()), 1 + u32::from(r.end()))) + .map(|(s, e)| format!("({} {})", s, e)); + join(ranges) + .separator(" ") + .surround_with("(", ")") + .to_string() +} diff --git a/crates/ra_syntax/src/algo/mod.rs b/crates/ra_syntax/src/algo/mod.rs index 7287f5bb233..8de44c58614 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> { } pub fn find_covering_node(root: SyntaxNodeRef, range: TextRange) -> SyntaxNodeRef { - assert!(is_subrange(root.range(), range)); + assert!( + is_subrange(root.range(), range), + "node range: {:?}, target range: {:?}", + root.range(), range, + ); let (left, right) = match ( find_leaf_at_offset(root, range.start()).right_biased(), 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 00000000000..6ff2ae7426d --- /dev/null +++ b/editors/emacs/ra.el @@ -0,0 +1,86 @@ +;;; ra.el --- Rust analyzer emacs bindings -*- lexical-binding: t; -*- +;;; Commentary: +;;; Small utilities for interacting with Rust analyzer. +;;; Run +;;; cargo install --git https://github.com/matklad/rust-analyzer/ --bin ra_cli +;;; to install the binary, copy-paste the bellow code to your `.init.el` and use +;;; `ra-extend-selection` and `ra-shrink-selection` functions +;;; Code: + + +(defvar ra--selections-cache '(0 0 ())) +(defun ra--cache-tick () + "Get buffer modification count for cache." + (nth 0 ra--selections-cache)) +(defun ra--cache-sel () + "Get current selection for cache." + (nth 1 ra--selections-cache)) +(defun ra--cache-nth-sel (n) + "Get Nth selection." + (nth n (nth 2 ra--selections-cache))) +(defun ra--cache-set-nth-sel (n) + "Get Nth selection." + (setf (nth 1 ra--selections-cache) n) + (nth n (nth 2 ra--selections-cache))) + + +(defun ra-extend-selection () + "Extend START END region to contain the encompassing syntactic construct." + (interactive) + (let* ((p (point)) + (m (or (and mark-active (mark)) p)) + (start (min p m)) + (end (max p m))) + (ra--extend-selection start end))) + + +(defun ra-shrink-selection (start end) + "Shrink START END region to contain previous selection." + (interactive "r") + (ra--freshen-cache start end) + (let ((sel-id (ra--cache-sel))) + (if (not (= 0 sel-id)) + (let* ((r (ra--cache-set-nth-sel (- sel-id 1)))) + (push-mark (nth 0 r) t t) + (goto-char (nth 1 r)) + (setq deactivate-mark nil))))) + +; Add this to setup keybinding +; (require 'rust-mode) +; (define-key rust-mode-map (kbd "C-w") 'ra-extend-selection) +; (define-key rust-mode-map (kbd "C-S-w") 'ra-shrink-selection) + + + +(defun ra--extend-selection (start end) + "Extend START END region to contain the encompassing syntactic construct." + (ra--freshen-cache start end) + (let* ((next-sel-idx (+ 1 (ra--cache-sel))) + (r (ra--cache-set-nth-sel next-sel-idx))) + (push-mark (nth 0 r) t t) + (goto-char (nth 1 r)) + (setq deactivate-mark nil))) + +(defun ra--selections (start end) + "Get list of selections for START END from Rust analyzer." + (read (with-output-to-string + (call-process-region + (point-min) (point-max) + "ra_cli" nil standard-output nil + "extend-selection" + (number-to-string start) + (number-to-string end))))) + +(defun ra--freshen-cache (start end) + "Make selection cache up-to-date for current buffer state and START END." + (if (not (and + (= (buffer-modified-tick) (ra--cache-tick)) + (equal `(,start ,end) (ra--cache-nth-sel (ra--cache-sel))))) + (ra--set-cache start end))) + +(defun ra--set-cache (start end) + "Set selections cache for current buffer state and START END." + (setq ra--selections-cache `(,(buffer-modified-tick) 0 ,(ra--selections start end)))) + +(provide 'ra) +;;; ra.el ends here