dotemacs

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

mc-mark-more.el (22741B)


      1 ;;; mc-mark-more.el
      2 
      3 ;; Copyright (C) 2012 Magnar Sveen
      4 
      5 ;; Author: Magnar Sveen <magnars@gmail.com>
      6 ;; Keywords: editing cursors
      7 
      8 ;; This program is free software; you can redistribute it and/or modify
      9 ;; it under the terms of the GNU General Public License as published by
     10 ;; the Free Software Foundation, either version 3 of the License, or
     11 ;; (at your option) any later version.
     12 
     13 ;; This program is distributed in the hope that it will be useful,
     14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 ;; GNU General Public License for more details.
     17 
     18 ;; You should have received a copy of the GNU General Public License
     19 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
     20 
     21 ;;; Commentary:
     22 
     23 ;; This file contains functions to mark more parts of the buffer.
     24 ;; See ./features/mark-more.feature for examples.
     25 
     26 ;; Please see multiple-cursors.el for more commentary.
     27 
     28 ;;; Code:
     29 
     30 (require 'multiple-cursors-core)
     31 (require 'thingatpt)
     32 
     33 (defun mc/cursor-end (cursor)
     34   (if (overlay-get cursor 'mark-active)
     35       (max (overlay-get cursor 'point)
     36            (overlay-get cursor 'mark))
     37     (overlay-get cursor 'point)))
     38 
     39 (defun mc/cursor-beg (cursor)
     40   (if (overlay-get cursor 'mark-active)
     41       (min (overlay-get cursor 'point)
     42            (overlay-get cursor 'mark))
     43     (overlay-get cursor 'point)))
     44 
     45 (defun mc/furthest-region-end ()
     46   (let ((end (max (mark) (point))))
     47     (mc/for-each-fake-cursor
     48      (setq end (max end (mc/cursor-end cursor))))
     49     end))
     50 
     51 (defun mc/first-region-start ()
     52   (let ((beg (min (mark) (point))))
     53     (mc/for-each-fake-cursor
     54      (setq beg (min beg (mc/cursor-beg cursor))))
     55     beg))
     56 
     57 (defun mc/furthest-cursor-before-point ()
     58   (let ((beg (if mark-active (min (mark) (point)) (point)))
     59 	furthest)
     60     (mc/for-each-fake-cursor
     61      (when (< (mc/cursor-beg cursor) beg)
     62        (setq beg (mc/cursor-beg cursor))
     63        (setq furthest cursor)))
     64     furthest))
     65 
     66 (defun mc/furthest-cursor-after-point ()
     67   (let ((end (if mark-active (max (mark) (point)) (point)))
     68 	furthest)
     69     (mc/for-each-fake-cursor
     70      (when (> (mc/cursor-end cursor) end)
     71        (setq end (mc/cursor-end cursor))
     72        (setq furthest cursor)))
     73     furthest))
     74 
     75 (defun mc/fake-cursor-at-point (&optional point)
     76   "Return the fake cursor with its point right at POINT (defaults
     77 to (point)), or nil."
     78   (setq point (or point (point)))
     79   (let ((cursors (mc/all-fake-cursors))
     80         (c nil))
     81     (catch 'found
     82       (while (setq c (pop cursors))
     83         (when (eq (marker-position (overlay-get c 'point))
     84                   point)
     85           (throw 'found c))))))
     86 
     87 (defun mc/region-strings ()
     88   (let ((strings (list (buffer-substring-no-properties (point) (mark)))))
     89     (mc/for-each-fake-cursor
     90      (add-to-list 'strings (buffer-substring-no-properties
     91                             (mc/cursor-beg cursor)
     92                             (mc/cursor-end cursor))))
     93     strings))
     94 
     95 (defvar mc/enclose-search-term nil
     96   "How should mc/mark-more-* search for more matches?
     97 
     98 Match everything: nil
     99 Match only whole words: 'words
    100 Match only whole symbols: 'symbols
    101 
    102 Use like case-fold-search, don't recommend setting it globally.")
    103 
    104 (defun mc/mark-more-like-this (skip-last direction)
    105   (let ((case-fold-search nil)
    106         (re (regexp-opt (mc/region-strings) mc/enclose-search-term))
    107         (point-out-of-order (cl-ecase direction
    108                               (forwards       (< (point) (mark)))
    109                               (backwards (not (< (point) (mark))))))
    110         (furthest-cursor (cl-ecase direction
    111                            (forwards  (mc/furthest-cursor-after-point))
    112                            (backwards (mc/furthest-cursor-before-point))))
    113         (start-char (cl-ecase direction
    114                       (forwards  (mc/furthest-region-end))
    115                       (backwards (mc/first-region-start))))
    116         (search-function (cl-ecase direction
    117                            (forwards  'search-forward-regexp)
    118                            (backwards 'search-backward-regexp)))
    119         (match-point-getter (cl-ecase direction
    120                               (forwards 'match-beginning)
    121                               (backwards 'match-end))))
    122     (if (and skip-last (not furthest-cursor))
    123         (error "No cursors to be skipped")
    124       (mc/save-excursion
    125        (goto-char start-char)
    126        (when skip-last
    127          (mc/remove-fake-cursor furthest-cursor))
    128        (if (funcall search-function re nil t)
    129            (progn
    130              (push-mark (funcall match-point-getter 0))
    131              (when point-out-of-order
    132                (exchange-point-and-mark))
    133              (mc/create-fake-cursor-at-point))
    134          (error "no more matches found."))))))
    135 
    136 ;;;###autoload
    137 (defun mc/mark-next-like-this (arg)
    138   "Find and mark the next part of the buffer matching the currently active region
    139 If no region is active add a cursor on the next line
    140 With negative ARG, delete the last one instead.
    141 With zero ARG, skip the last one and mark next."
    142   (interactive "p")
    143   (if (< arg 0)
    144       (let ((cursor (mc/furthest-cursor-after-point)))
    145 	(if cursor
    146 	    (mc/remove-fake-cursor cursor)
    147 	  (error "No cursors to be unmarked")))
    148     (if (region-active-p)
    149         (mc/mark-more-like-this (= arg 0) 'forwards)
    150       (mc/mark-lines arg 'forwards)))
    151   (mc/maybe-multiple-cursors-mode))
    152 
    153 ;;;###autoload
    154 (defun mc/mark-next-like-this-word (arg)
    155   "Find and mark the next part of the buffer matching the currently active region
    156 If no region is active, mark the word at the point and find the next match
    157 With negative ARG, delete the last one instead.
    158 With zero ARG, skip the last one and mark next."
    159   (interactive "p")
    160   (if (< arg 0)
    161       (let ((cursor (mc/furthest-cursor-after-point)))
    162 	(if cursor
    163 	    (mc/remove-fake-cursor cursor)
    164 	  (error "No cursors to be unmarked")))
    165     (if (region-active-p)
    166         (mc/mark-more-like-this (= arg 0) 'forwards)
    167       (mc--select-thing-at-point 'word)
    168       (mc/mark-more-like-this (= arg 0) 'forwards)))
    169   (mc/maybe-multiple-cursors-mode))
    170 
    171 (defun mc/mark-next-like-this-symbol (arg)
    172   "Find and mark the next part of the buffer matching the currently active region
    173 If no region is active, mark the symbol at the point and find the next match
    174 With negative ARG, delete the last one instead.
    175 With zero ARG, skip the last one and mark next."
    176   (interactive "p")
    177   (if (< arg 0)
    178       (let ((cursor (mc/furthest-cursor-after-point)))
    179 	(if cursor
    180 	    (mc/remove-fake-cursor cursor)
    181 	  (error "No cursors to be unmarked")))
    182     (if (region-active-p)
    183         (mc/mark-more-like-this (= arg 0) 'forwards)
    184       (mc--select-thing-at-point 'symbol)
    185       (mc/mark-more-like-this (= arg 0) 'forwards)))
    186   (mc/maybe-multiple-cursors-mode))
    187 
    188 
    189 ;;;###autoload
    190 (defun mc/mark-next-word-like-this (arg)
    191   (interactive "p")
    192   (let ((mc/enclose-search-term 'words))
    193     (mc/mark-next-like-this arg)))
    194 
    195 ;;;###autoload
    196 (defun mc/mark-next-symbol-like-this (arg)
    197   (interactive "p")
    198   (let ((mc/enclose-search-term 'symbols))
    199     (mc/mark-next-like-this arg)))
    200 
    201 ;;;###autoload
    202 (defun mc/mark-previous-like-this (arg)
    203   "Find and mark the previous part of the buffer matching the currently active region
    204 With negative ARG, delete the last one instead.
    205 With zero ARG, skip the last one and mark next."
    206   (interactive "p")
    207   (if (< arg 0)
    208       (let ((cursor (mc/furthest-cursor-before-point)))
    209 	(if cursor
    210 	    (mc/remove-fake-cursor cursor)
    211 	  (error "No cursors to be unmarked")))
    212     (if (region-active-p)
    213         (mc/mark-more-like-this (= arg 0) 'backwards)
    214       (mc/mark-lines arg 'backwards)))
    215   (mc/maybe-multiple-cursors-mode))
    216 
    217 ;;;###autoload
    218 (defun mc/mark-previous-word-like-this (arg)
    219   (interactive "p")
    220   (let ((mc/enclose-search-term 'words))
    221     (mc/mark-previous-like-this arg)))
    222 
    223 ;;;###autoload
    224 (defun mc/mark-previous-symbol-like-this (arg)
    225   (interactive "p")
    226   (let ((mc/enclose-search-term 'symbols))
    227     (mc/mark-previous-like-this arg)))
    228 
    229 (defun mc/mark-lines (num-lines direction)
    230   (dotimes (i (if (= num-lines 0) 1 num-lines))
    231     (mc/save-excursion
    232      (let ((furthest-cursor (cl-ecase direction
    233 			      (forwards  (mc/furthest-cursor-after-point))
    234 			      (backwards (mc/furthest-cursor-before-point)))))
    235        (when (overlayp furthest-cursor)
    236          (goto-char (overlay-get furthest-cursor 'point))
    237          (when (= num-lines 0)
    238            (mc/remove-fake-cursor furthest-cursor))))
    239      (cl-ecase direction
    240        (forwards (next-logical-line 1 nil))
    241        (backwards (previous-logical-line 1 nil)))
    242      (mc/create-fake-cursor-at-point))))
    243 
    244 ;;;###autoload
    245 (defun mc/mark-next-lines (arg)
    246   (interactive "p")
    247   (mc/mark-lines arg 'forwards)
    248   (mc/maybe-multiple-cursors-mode))
    249 
    250 ;;;###autoload
    251 (defun mc/mark-previous-lines (arg)
    252   (interactive "p")
    253   (mc/mark-lines arg 'backwards)
    254   (mc/maybe-multiple-cursors-mode))
    255 
    256 ;;;###autoload
    257 (defun mc/unmark-next-like-this ()
    258   "Deselect next part of the buffer matching the currently active region."
    259   (interactive)
    260   (mc/mark-next-like-this -1))
    261 
    262 ;;;###autoload
    263 (defun mc/unmark-previous-like-this ()
    264   "Deselect prev part of the buffer matching the currently active region."
    265   (interactive)
    266   (mc/mark-previous-like-this -1))
    267 
    268 ;;;###autoload
    269 (defun mc/skip-to-next-like-this ()
    270   "Skip the current one and select the next part of the buffer matching the currently active region."
    271   (interactive)
    272   (mc/mark-next-like-this 0))
    273 
    274 ;;;###autoload
    275 (defun mc/skip-to-previous-like-this ()
    276   "Skip the current one and select the prev part of the buffer matching the currently active region."
    277   (interactive)
    278   (mc/mark-previous-like-this 0))
    279 
    280 ;;;###autoload
    281 (defun mc/mark-all-like-this ()
    282   "Find and mark all the parts of the buffer matching the currently active region"
    283   (interactive)
    284   (unless (region-active-p)
    285     (error "Mark a region to match first."))
    286   (mc/remove-fake-cursors)
    287   (let ((master (point))
    288         (case-fold-search nil)
    289         (point-first (< (point) (mark)))
    290         (re (regexp-opt (mc/region-strings) mc/enclose-search-term)))
    291     (mc/save-excursion
    292      (goto-char 0)
    293      (while (search-forward-regexp re nil t)
    294        (push-mark (match-beginning 0))
    295        (when point-first (exchange-point-and-mark))
    296        (unless (= master (point))
    297          (mc/create-fake-cursor-at-point))
    298        (when point-first (exchange-point-and-mark)))))
    299   (if (> (mc/num-cursors) 1)
    300       (multiple-cursors-mode 1)
    301     (multiple-cursors-mode 0)))
    302 
    303 (defun mc--select-thing-at-point (thing)
    304   (let ((bound (bounds-of-thing-at-point thing)))
    305     (when bound
    306       (set-mark (car bound))
    307       (goto-char (cdr bound))
    308       bound)))
    309 
    310 (defun mc--select-thing-at-point-or-bark (thing)
    311   (unless (or (region-active-p) (mc--select-thing-at-point thing))
    312     (error "Mark a region or set cursor on a %s." thing)))
    313 
    314 ;;;###autoload
    315 (defun mc/mark-all-words-like-this ()
    316   (interactive)
    317   (mc--select-thing-at-point-or-bark 'word)
    318   (let ((mc/enclose-search-term 'words))
    319     (mc/mark-all-like-this)))
    320 
    321 ;;;###autoload
    322 (defun mc/mark-all-symbols-like-this ()
    323   (interactive)
    324   (mc--select-thing-at-point-or-bark 'symbol)
    325   (let ((mc/enclose-search-term 'symbols))
    326     (mc/mark-all-like-this)))
    327 
    328 ;;;###autoload
    329 (defun mc/mark-all-in-region (beg end &optional search)
    330   "Find and mark all the parts in the region matching the given search"
    331   (interactive "r")
    332   (let ((search (or search (read-from-minibuffer "Mark all in region: ")))
    333         (case-fold-search nil))
    334     (if (string= search "")
    335         (message "Mark aborted")
    336       (progn
    337         (mc/remove-fake-cursors)
    338         (goto-char beg)
    339         (while (search-forward search end t)
    340           (push-mark (match-beginning 0))
    341           (mc/create-fake-cursor-at-point))
    342         (let ((first (mc/furthest-cursor-before-point)))
    343           (if (not first)
    344               (error "Search failed for %S" search)
    345             (mc/pop-state-from-overlay first)))
    346         (if (> (mc/num-cursors) 1)
    347             (multiple-cursors-mode 1)
    348           (multiple-cursors-mode 0))))))
    349 
    350 ;;;###autoload
    351 (defun mc/mark-all-in-region-regexp (beg end)
    352   "Find and mark all the parts in the region matching the given regexp."
    353   (interactive "r")
    354   (let ((search (read-regexp "Mark regexp in region: "))
    355         (case-fold-search nil))
    356     (if (string= search "")
    357         (message "Mark aborted")
    358       (progn
    359         (mc/remove-fake-cursors)
    360         (goto-char beg)
    361         (let ((lastmatch))
    362           (while (and (< (point) end) ; can happen because of (forward-char)
    363                       (search-forward-regexp search end t))
    364             (push-mark (match-beginning 0))
    365             (mc/create-fake-cursor-at-point)
    366             (setq lastmatch (point))
    367             (when (= (point) (match-beginning 0))
    368               (forward-char)))
    369           (when lastmatch (goto-char lastmatch)))
    370         (when (> (mc/num-cursors) 0)
    371           (goto-char (match-end 0)))
    372         (let ((first (mc/furthest-cursor-before-point)))
    373           (if (not first)
    374               (error "Search failed for %S" search)
    375             (mc/pop-state-from-overlay first)))
    376         (if (> (mc/num-cursors) 1)
    377             (multiple-cursors-mode 1)
    378           (multiple-cursors-mode 0))))))
    379 
    380 (when (not (fboundp 'set-temporary-overlay-map))
    381   ;; Backport this function from newer emacs versions
    382   (defun set-temporary-overlay-map (map &optional keep-pred)
    383     "Set a new keymap that will only exist for a short period of time.
    384 The new keymap to use must be given in the MAP variable. When to
    385 remove the keymap depends on user input and KEEP-PRED:
    386 
    387 - if KEEP-PRED is nil (the default), the keymap disappears as
    388   soon as any key is pressed, whether or not the key is in MAP;
    389 
    390 - if KEEP-PRED is t, the keymap disappears as soon as a key *not*
    391   in MAP is pressed;
    392 
    393 - otherwise, KEEP-PRED must be a 0-arguments predicate that will
    394   decide if the keymap should be removed (if predicate returns
    395   nil) or kept (otherwise). The predicate will be called after
    396   each key sequence."
    397 
    398     (let* ((clearfunsym (make-symbol "clear-temporary-overlay-map"))
    399            (overlaysym (make-symbol "t"))
    400            (alist (list (cons overlaysym map)))
    401            (clearfun
    402             `(lambda ()
    403                (unless ,(cond ((null keep-pred) nil)
    404                               ((eq t keep-pred)
    405                                `(eq this-command
    406                                     (lookup-key ',map
    407                                                 (this-command-keys-vector))))
    408                               (t `(funcall ',keep-pred)))
    409                  (remove-hook 'pre-command-hook ',clearfunsym)
    410                  (setq emulation-mode-map-alists
    411                        (delq ',alist emulation-mode-map-alists))))))
    412       (set overlaysym overlaysym)
    413       (fset clearfunsym clearfun)
    414       (add-hook 'pre-command-hook clearfunsym)
    415 
    416       (push alist emulation-mode-map-alists))))
    417 
    418 ;;;###autoload
    419 (defun mc/mark-more-like-this-extended ()
    420   "Like mark-more-like-this, but then lets you adjust with arrows key.
    421 The adjustments work like this:
    422 
    423    <up>    Mark previous like this and set direction to 'up
    424    <down>  Mark next like this and set direction to 'down
    425 
    426 If direction is 'up:
    427 
    428    <left>  Skip past the cursor furthest up
    429    <right> Remove the cursor furthest up
    430 
    431 If direction is 'down:
    432 
    433    <left>  Remove the cursor furthest down
    434    <right> Skip past the cursor furthest down
    435 
    436 The bindings for these commands can be changed. See `mc/mark-more-like-this-extended-keymap'."
    437   (interactive)
    438   (mc/mmlte--down)
    439   (set-temporary-overlay-map mc/mark-more-like-this-extended-keymap t))
    440 
    441 (defvar mc/mark-more-like-this-extended-direction nil
    442   "When using mc/mark-more-like-this-extended are we working on the next or previous cursors?")
    443 
    444 (make-variable-buffer-local 'mc/mark-more-like-this-extended)
    445 
    446 (defun mc/mmlte--message ()
    447   (if (eq mc/mark-more-like-this-extended-direction 'up)
    448       (message "<up> to mark previous, <left> to skip, <right> to remove, <down> to mark next")
    449     (message "<down> to mark next, <right> to skip, <left> to remove, <up> to mark previous")))
    450 
    451 (defun mc/mmlte--up ()
    452   (interactive)
    453   (mc/mark-previous-like-this 1)
    454   (setq mc/mark-more-like-this-extended-direction 'up)
    455   (mc/mmlte--message))
    456 
    457 (defun mc/mmlte--down ()
    458   (interactive)
    459   (mc/mark-next-like-this 1)
    460   (setq mc/mark-more-like-this-extended-direction 'down)
    461   (mc/mmlte--message))
    462 
    463 (defun mc/mmlte--left ()
    464   (interactive)
    465   (if (eq mc/mark-more-like-this-extended-direction 'down)
    466       (mc/unmark-next-like-this)
    467     (mc/skip-to-previous-like-this))
    468   (mc/mmlte--message))
    469 
    470 (defun mc/mmlte--right ()
    471   (interactive)
    472   (if (eq mc/mark-more-like-this-extended-direction 'up)
    473       (mc/unmark-previous-like-this)
    474     (mc/skip-to-next-like-this))
    475   (mc/mmlte--message))
    476 
    477 (defvar mc/mark-more-like-this-extended-keymap (make-sparse-keymap))
    478 
    479 (define-key mc/mark-more-like-this-extended-keymap (kbd "<up>") 'mc/mmlte--up)
    480 (define-key mc/mark-more-like-this-extended-keymap (kbd "<down>") 'mc/mmlte--down)
    481 (define-key mc/mark-more-like-this-extended-keymap (kbd "<left>") 'mc/mmlte--left)
    482 (define-key mc/mark-more-like-this-extended-keymap (kbd "<right>") 'mc/mmlte--right)
    483 
    484 (defvar mc--restrict-mark-all-to-symbols nil)
    485 
    486 ;;;###autoload
    487 (defun mc/mark-all-like-this-dwim (arg)
    488   "Tries to guess what you want to mark all of.
    489 Can be pressed multiple times to increase selection.
    490 
    491 With prefix, it behaves the same as original `mc/mark-all-like-this'"
    492   (interactive "P")
    493   (if arg
    494       (mc/mark-all-like-this)
    495     (if (and (not (use-region-p))
    496              (derived-mode-p 'sgml-mode)
    497              (mc--on-tag-name-p))
    498         (mc/mark-sgml-tag-pair)
    499       (let ((before (mc/num-cursors)))
    500         (unless (eq last-command 'mc/mark-all-like-this-dwim)
    501           (setq mc--restrict-mark-all-to-symbols nil))
    502         (unless (use-region-p)
    503           (mc--mark-symbol-at-point)
    504           (setq mc--restrict-mark-all-to-symbols t))
    505         (if mc--restrict-mark-all-to-symbols
    506             (mc/mark-all-symbols-like-this-in-defun)
    507           (mc/mark-all-like-this-in-defun))
    508         (when (<= (mc/num-cursors) before)
    509           (if mc--restrict-mark-all-to-symbols
    510               (mc/mark-all-symbols-like-this)
    511             (mc/mark-all-like-this)))
    512         (when (<= (mc/num-cursors) before)
    513           (mc/mark-all-like-this))))))
    514 
    515 ;;;###autoload
    516 (defun mc/mark-all-dwim (arg)
    517   "Tries even harder to guess what you want to mark all of.
    518 
    519 If the region is active and spans multiple lines, it will behave
    520 as if `mc/mark-all-in-region'. With the prefix ARG, it will call
    521 `mc/edit-lines' instead.
    522 
    523 If the region is inactive or on a single line, it will behave like
    524 `mc/mark-all-like-this-dwim'."
    525   (interactive "P")
    526   (if (and (use-region-p)
    527            (not (> (mc/num-cursors) 1))
    528            (not (= (line-number-at-pos (region-beginning))
    529                    (line-number-at-pos (region-end)))))
    530       (if arg
    531           (call-interactively 'mc/edit-lines)
    532        (call-interactively 'mc/mark-all-in-region))
    533     (progn
    534       (setq this-command 'mc/mark-all-like-this-dwim)
    535       (mc/mark-all-like-this-dwim arg))))
    536 
    537 (defun mc--in-defun ()
    538   (bounds-of-thing-at-point 'defun))
    539 
    540 ;;;###autoload
    541 (defun mc/mark-all-like-this-in-defun ()
    542   "Mark all like this in defun."
    543   (interactive)
    544   (if (mc--in-defun)
    545       (save-restriction
    546         (widen)
    547         (narrow-to-defun)
    548         (mc/mark-all-like-this))
    549     (mc/mark-all-like-this)))
    550 
    551 ;;;###autoload
    552 (defun mc/mark-all-words-like-this-in-defun ()
    553   "Mark all words like this in defun."
    554   (interactive)
    555   (mc--select-thing-at-point-or-bark 'word)
    556   (if (mc--in-defun)
    557       (save-restriction
    558         (widen)
    559         (narrow-to-defun)
    560         (mc/mark-all-words-like-this))
    561     (mc/mark-all-words-like-this)))
    562 
    563 ;;;###autoload
    564 (defun mc/mark-all-symbols-like-this-in-defun ()
    565   "Mark all symbols like this in defun."
    566   (interactive)
    567   (mc--select-thing-at-point-or-bark 'symbol)
    568   (if (mc--in-defun)
    569       (save-restriction
    570         (widen)
    571         (narrow-to-defun)
    572         (mc/mark-all-symbols-like-this))
    573     (mc/mark-all-symbols-like-this)))
    574 
    575 (defun mc--mark-symbol-at-point ()
    576   "Select the symbol under cursor"
    577   (interactive)
    578   (when (not (use-region-p))
    579     (let ((b (bounds-of-thing-at-point 'symbol)))
    580       (goto-char (car b))
    581       (set-mark (cdr b)))))
    582 
    583 (defun mc--get-nice-sgml-context ()
    584   (car
    585    (last
    586     (progn
    587       (when (looking-at "<") (forward-char 1))
    588       (when (looking-back ">") (forward-char -1))
    589       (sgml-get-context)))))
    590 
    591 (defun mc--on-tag-name-p ()
    592   (let* ((context (save-excursion (mc--get-nice-sgml-context)))
    593          (tag-name-len (length (aref context 4)))
    594          (beg (aref context 2))
    595          (end (+ beg tag-name-len (if (eq 'open (aref context 1)) 1 3))))
    596     (and context
    597          (>= (point) beg)
    598          (<= (point) end))))
    599 
    600 ;;;###autoload
    601 (defun mc/toggle-cursor-on-click (event)
    602   "Add a cursor where you click, or remove a fake cursor that is
    603 already there."
    604   (interactive "e")
    605   (mouse-minibuffer-check event)
    606   ;; Use event-end in case called from mouse-drag-region.
    607   ;; If EVENT is a click, event-end and event-start give same value.
    608   (let ((position (event-end event)))
    609     (if (not (windowp (posn-window position)))
    610         (error "Position not in text area of window"))
    611     (select-window (posn-window position))
    612     (let ((pt (posn-point position)))
    613       (if (numberp pt)
    614           ;; is there a fake cursor with the actual *point* right where we are?
    615           (let ((existing (mc/fake-cursor-at-point pt)))
    616             (if existing
    617                 (mc/remove-fake-cursor existing)
    618               (save-excursion
    619                 (goto-char pt)
    620                 (mc/create-fake-cursor-at-point))))))
    621     (mc/maybe-multiple-cursors-mode)))
    622 
    623 ;;;###autoload
    624 (defalias 'mc/add-cursor-on-click 'mc/toggle-cursor-on-click)
    625 
    626 ;;;###autoload
    627 (defun mc/mark-sgml-tag-pair ()
    628   "Mark the tag we're in and its pair for renaming."
    629   (interactive)
    630   (when (not (mc--inside-tag-p))
    631     (error "Place point inside tag to rename."))
    632   (let ((context (mc--get-nice-sgml-context)))
    633     (if (looking-at "</")
    634         (setq context (car (last (sgml-get-context)))))
    635     (goto-char (aref context 2))
    636     (let* ((tag-name (aref context 4))
    637            (num-chars (length tag-name))
    638            (master-start (1+ (point)))
    639            (mirror-end (save-excursion
    640                          (sgml-skip-tag-forward 1)
    641                          (1- (point)))))
    642       (goto-char (- mirror-end num-chars))
    643       (set-mark mirror-end)
    644       (mc/create-fake-cursor-at-point)
    645       (goto-char master-start)
    646       (set-mark (+ (point) num-chars))))
    647   (mc/maybe-multiple-cursors-mode))
    648 
    649 (defun mc--inside-tag-p ()
    650   (save-excursion
    651     (not (null (sgml-get-context)))))
    652 
    653 (provide 'mc-mark-more)
    654 
    655 ;;; mc-mark-more.el ends here