ruby-mode-expansions.el (7165B)
1 ;;; ruby-mode-expansions.el --- ruby-specific expansions for expand-region -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2011-2023 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-lib)) 40 (require 'expand-region-core) 41 (require 'er-basic-expansions) 42 (require 'ruby-mode) 43 44 (defvar er/ruby-block-end-re 45 (concat ruby-block-end-re "\\|}") 46 "like ruby-mode's but also for '}'") 47 48 (defun er/ruby-skip-past-block-end () 49 "If line is blockend, move point to next line." 50 (when (looking-at er/ruby-block-end-re) 51 (forward-line 1))) 52 53 (defun er/ruby-end-of-block (&optional arg) 54 "By default `ruby-end-of-block' goes to BOL of line containing end-re. 55 56 This moves point to the next line to include the end of the block" 57 (interactive "p") 58 ;; Workaround for `ruby-end-of-block' in Emacs 23. 59 (when (re-search-forward (concat "\\<\\(" ruby-block-beg-re "\\)\\>") 60 (line-end-position) t) 61 (goto-char (match-beginning 0))) 62 (ruby-end-of-block (or arg 1)) 63 (er/ruby-skip-past-block-end)) 64 65 (defun er/point-at-indentation () 66 "Return the point where current line's indentation ends." 67 (save-excursion 68 (back-to-indentation) 69 (point))) 70 71 (defun er/ruby-backward-up () 72 "a la `paredit-backward-up'" 73 (interactive) 74 ;; if our current line ends a block, we back a line, otherwise we 75 (when (save-excursion 76 (back-to-indentation) 77 (looking-at-p ruby-block-end-re)) 78 (forward-line -1)) 79 (let ((orig-point (point)) 80 progress-beg 81 progress-end) 82 83 ;; cover the case when point is in the line of beginning of block 84 (unless (progn (ruby-end-of-block) 85 (ruby-beginning-of-block) 86 ;; "Block beginning" is often not at indentation in Emacs 24. 87 (< (er/point-at-indentation) orig-point)) 88 (cl-loop 89 (ruby-beginning-of-block) 90 (setq progress-beg (point)) 91 (when (= (point) (point-min)) 92 (cl-return)) 93 (ruby-end-of-block) 94 (setq progress-end (line-beginning-position 95 (if (looking-at-p er/ruby-block-end-re) 0 1))) 96 (goto-char progress-beg) 97 (when (> progress-end orig-point) 98 (cl-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 (cons (progn 114 (er/ruby-backward-up) 115 (er/point-at-indentation)) 116 (progn 117 (er/ruby-end-of-block) 118 (point))))) 119 120 (defun er/mark-ruby-block-up-1 () 121 (er/ruby-backward-up) 122 (set-mark (er/point-at-indentation)) 123 (er/ruby-end-of-block) 124 (exchange-point-and-mark)) 125 126 (defun er/mark-ruby-block-up (&optional no-recurse) 127 "mark the next level up." 128 (interactive) 129 (if (use-region-p) 130 (let* ((orig-end (region-end)) 131 (orig-beg (region-beginning)) 132 (orig-len (- orig-end orig-beg)) 133 (prev-block-point 134 (or (save-excursion 135 (goto-char orig-end) 136 (forward-line 0) 137 (back-to-indentation) 138 (cond ((looking-at-p er/ruby-block-end-re) 139 (line-beginning-position 0)) 140 ((re-search-forward 141 (concat "\\<\\(" ruby-block-beg-re "\\)\\>") 142 (line-end-position) 143 t) 144 (line-beginning-position 2))) ) 145 (point))) 146 (prev-block-info (er/get-ruby-block prev-block-point)) 147 (prev-block-beg (car prev-block-info)) 148 (prev-block-end (cdr prev-block-info)) 149 (prev-block-len (- prev-block-end prev-block-beg))) 150 (if (and (>= orig-beg prev-block-beg) 151 (<= orig-end prev-block-end) 152 (< orig-len prev-block-len)) 153 ;; expand to previous block if it contains and grows current 154 ;; region 155 (progn 156 (deactivate-mark) 157 (goto-char prev-block-point) 158 (or no-recurse 159 (er/mark-ruby-block-up 'no-recurse))) 160 (er/mark-ruby-block-up-1))) 161 (er/mark-ruby-block-up-1))) 162 163 (defun er/mark-ruby-instance-variable () 164 "Marks instance variables in ruby. 165 Assumes that point is at the @ - if it is inside the word, that will 166 be marked first anyway." 167 (when (looking-at "@") 168 (forward-char 1)) 169 (when (er/looking-back-exact "@") 170 (er/mark-symbol) 171 (forward-char -1))) 172 173 (defun er/mark-ruby-heredoc () 174 "Marks a heredoc, since `er/mark-inside-quotes' assumes single quote chars." 175 (let ((ppss (syntax-ppss))) 176 (when (elt ppss 3) 177 (let ((s-start (elt ppss 8))) 178 (goto-char s-start) 179 (when (save-excursion 180 (beginning-of-line) 181 (re-search-forward "<<\\(-?\\)['\"]?\\([a-zA-Z0-9_]+\\)" s-start nil)) 182 (let ((allow-indent (string= "-" (match-string 1))) 183 (terminator (match-string 2)) 184 (heredoc-start (save-excursion 185 (forward-line) 186 (point)))) 187 (forward-sexp 1) 188 (forward-line -1) 189 (when (looking-at (concat "^" (if allow-indent "[ \t]*" "") terminator "$")) 190 (set-mark heredoc-start) 191 (exchange-point-and-mark)))))))) 192 193 (defun er/add-ruby-mode-expansions () 194 "Adds Ruby-specific expansions for buffers in ruby-mode" 195 (set (make-local-variable 'er/try-expand-list) 196 (remove 'er/mark-defun 197 (append 198 (default-value 'er/try-expand-list) 199 '(er/mark-ruby-instance-variable 200 er/mark-ruby-block-up 201 er/mark-ruby-heredoc))))) 202 203 (er/enable-mode-expansions 'ruby-mode #'er/add-ruby-mode-expansions) 204 (provide 'ruby-mode-expansions)