ruby-mode-expansions.el (7186B)
1 ;;; ruby-mode-expansions.el --- ruby-specific expansions for expand-region 2 3 ;; Copyright (C) 2011-2020 Free Software Foundation, Inc 4 5 ;; Author: Matt Briggs 6 ;; Based on js-mode-expansions by: Magnar Sveen <magnars@gmail.com> 7 ;; Keywords: marking region 8 9 ;; This program is free software; you can redistribute it and/or modify 10 ;; it under the terms of the GNU General Public License as published by 11 ;; the Free Software Foundation, either version 3 of the License, or 12 ;; (at your option) any later version. 13 14 ;; This program is distributed in the hope that it will be useful, 15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 ;; GNU General Public License for more details. 18 19 ;; You should have received a copy of the GNU General Public License 20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 21 22 ;;; Commentary: 23 24 25 ;; LeWang: 26 ;; 27 ;; I think `er/ruby-backward-up' and `er/ruby-forward-up' are nifty 28 ;; functions in their own right. 29 ;; 30 ;; I would bind them to C-M-u and C-M-d respectively. 31 32 ;; Expansions: 33 ;; 34 ;; 35 ;; er/mark-ruby-block-up 36 ;; 37 38 ;;; Code: 39 (eval-when-compile (require 'cl)) 40 (require 'expand-region-core) 41 (require 'ruby-mode) 42 43 (defvar er/ruby-block-end-re 44 (concat ruby-block-end-re "\\|}") 45 "like ruby-mode's but also for '}'") 46 47 (defun er/ruby-skip-past-block-end () 48 "If line is blockend, move point to next line." 49 (when (looking-at er/ruby-block-end-re) 50 (forward-line 1))) 51 52 (defun er/ruby-end-of-block (&optional arg) 53 "By default `ruby-end-of-block' goes to BOL of line containing end-re. 54 55 This moves point to the next line to include the end of the block" 56 (interactive "p") 57 ;; Workaround for `ruby-end-of-block' in Emacs 23. 58 (when (re-search-forward (concat "\\<\\(" ruby-block-beg-re "\\)\\>") 59 (point-at-eol) t) 60 (goto-char (match-beginning 0))) 61 (ruby-end-of-block (or arg 1)) 62 (er/ruby-skip-past-block-end)) 63 64 (defun er/point-at-indentation () 65 "Return the point where current line's indentation ends." 66 (save-excursion 67 (back-to-indentation) 68 (point))) 69 70 (defun er/ruby-backward-up () 71 "a la `paredit-backward-up'" 72 (interactive) 73 ;; if our current line ends a block, we back a line, otherwise we 74 (when (save-excursion 75 (back-to-indentation) 76 (looking-at-p ruby-block-end-re)) 77 (forward-line -1)) 78 (let ((orig-point (point)) 79 progress-beg 80 progress-end) 81 82 ;; cover the case when point is in the line of beginning of block 83 (unless (progn (ruby-end-of-block) 84 (ruby-beginning-of-block) 85 ;; "Block beginning" is often not at indentation in Emacs 24. 86 (< (er/point-at-indentation) orig-point)) 87 (loop do 88 (ruby-beginning-of-block) 89 (setq progress-beg (point)) 90 (when (= (point) (point-min)) 91 (return)) 92 (ruby-end-of-block) 93 (setq progress-end (if (looking-at-p er/ruby-block-end-re) 94 (point-at-bol 0) 95 (point-at-bol 1))) 96 (goto-char progress-beg) 97 (when (> progress-end orig-point) 98 (return)))))) 99 100 ;;; This command isn't used here explicitly, but it's symmetrical with 101 ;;; `er/ruby-backward-up', and nifty for interactive use. 102 (defun er/ruby-forward-up () 103 "a la `paredit-forward-up'" 104 (interactive) 105 (er/ruby-backward-up) 106 (er/ruby-end-of-block)) 107 108 (defun er/get-ruby-block (&optional pos) 109 "return (beg . end) of current block" 110 (setq pos (or pos (point))) 111 (save-excursion 112 (goto-char pos) 113 (let (beg end) 114 (cons (progn 115 (er/ruby-backward-up) 116 (er/point-at-indentation)) 117 (progn 118 (er/ruby-end-of-block) 119 (point)))))) 120 121 (defun er/mark-ruby-block-up-1 () 122 (er/ruby-backward-up) 123 (set-mark (er/point-at-indentation)) 124 (er/ruby-end-of-block) 125 (exchange-point-and-mark)) 126 127 (defun er/mark-ruby-block-up (&optional no-recurse) 128 "mark the next level up." 129 (interactive) 130 (if (use-region-p) 131 (let* ((orig-end (region-end)) 132 (orig-beg (region-beginning)) 133 (orig-len (- orig-end orig-beg)) 134 (prev-block-point 135 (or (save-excursion 136 (goto-char orig-end) 137 (forward-line 0) 138 (back-to-indentation) 139 (cond ((looking-at-p er/ruby-block-end-re) 140 (point-at-bol 0)) 141 ((re-search-forward 142 (concat "\\<\\(" ruby-block-beg-re "\\)\\>") 143 (point-at-eol) 144 t) 145 (point-at-bol 2))) ) 146 (point))) 147 (prev-block-info (er/get-ruby-block prev-block-point)) 148 (prev-block-beg (car prev-block-info)) 149 (prev-block-end (cdr prev-block-info)) 150 (prev-block-len (- prev-block-end prev-block-beg))) 151 (if (and (>= orig-beg prev-block-beg) 152 (<= orig-end prev-block-end) 153 (< orig-len prev-block-len)) 154 ;; expand to previous block if it contains and grows current 155 ;; region 156 (progn 157 (deactivate-mark) 158 (goto-char prev-block-point) 159 (or no-recurse 160 (er/mark-ruby-block-up 'no-recurse))) 161 (er/mark-ruby-block-up-1))) 162 (er/mark-ruby-block-up-1))) 163 164 (defun er/mark-ruby-instance-variable () 165 "Marks instance variables in ruby. 166 Assumes that point is at the @ - if it is inside the word, that will 167 be marked first anyway." 168 (when (looking-at "@") 169 (forward-char 1)) 170 (when (er/looking-back-exact "@") 171 (er/mark-symbol) 172 (forward-char -1))) 173 174 (defun er/mark-ruby-heredoc () 175 "Marks a heredoc, since `er/mark-inside-quotes' assumes single quote chars." 176 (let ((ppss (syntax-ppss))) 177 (when (elt ppss 3) 178 (let ((s-start (elt ppss 8))) 179 (goto-char s-start) 180 (when (save-excursion 181 (beginning-of-line) 182 (re-search-forward "<<\\(-?\\)['\"]?\\([a-zA-Z0-9_]+\\)" s-start nil)) 183 (let ((allow-indent (string= "-" (match-string 1))) 184 (terminator (match-string 2)) 185 (heredoc-start (save-excursion 186 (forward-line) 187 (point)))) 188 (forward-sexp 1) 189 (forward-line -1) 190 (when (looking-at (concat "^" (if allow-indent "[ \t]*" "") terminator "$")) 191 (set-mark heredoc-start) 192 (exchange-point-and-mark)))))))) 193 194 (defun er/add-ruby-mode-expansions () 195 "Adds Ruby-specific expansions for buffers in ruby-mode" 196 (set (make-local-variable 'er/try-expand-list) 197 (remove 'er/mark-defun 198 (append 199 (default-value 'er/try-expand-list) 200 '(er/mark-ruby-instance-variable 201 er/mark-ruby-block-up 202 er/mark-ruby-heredoc))))) 203 204 (er/enable-mode-expansions 'ruby-mode 'er/add-ruby-mode-expansions) 205 (provide 'ruby-mode-expansions)