dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

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)