Merge branch 'master' of gitlab.com:marcuskammer/emacs.d

This commit is contained in:
Marcus Kammer 2020-02-04 18:30:37 +01:00
commit 8025d663a7
59 changed files with 20436 additions and 2 deletions

View file

@ -84,8 +84,8 @@
'(package-enable-at-startup t)
'(package-selected-packages
(quote
(elm-mode dashboard pickle poet-theme flymake-eslint json-mode elpy darkroom dockerfile-mode ein spacemacs-theme flucui-themes leuven-theme htmlize scss-mode berrys-theme web-mode python-docstring sphinx-doc sphinx-frontend sphinx-mode ox-nikola racket-mode slime gherkin-mode powershell typescript-mode ob-http ob-ipython ob-restclient nord-theme restclient request restclient-test yaml-mode magit)))
'(python-shell-interpreter "python3" t)
(ag xref-js2 js2-refactor js2-mode elm-mode dashboard pickle poet-theme flymake-eslint json-mode elpy darkroom dockerfile-mode ein spacemacs-theme flucui-themes leuven-theme htmlize scss-mode berrys-theme web-mode python-docstring sphinx-doc sphinx-frontend sphinx-mode ox-nikola racket-mode slime gherkin-mode powershell typescript-mode ob-http ob-ipython ob-restclient nord-theme restclient request restclient-test yaml-mode magit)))
'(python-shell-interpreter "python3")
'(register-preview-delay 2)
'(register-separator 43)
'(request-log-level (quote debug))

View file

@ -0,0 +1,128 @@
;;; ag-autoloads.el --- automatically extracted autoloads
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "ag" "ag.el" (0 0 0 0))
;;; Generated autoloads from ag.el
(autoload 'ag "ag" "\
Search using ag in a given DIRECTORY for a given literal search STRING,
with STRING defaulting to the symbol under point.
If called with a prefix, prompts for flags to pass to ag.
\(fn STRING DIRECTORY)" t nil)
(autoload 'ag-files "ag" "\
Search using ag in a given DIRECTORY for a given literal search STRING,
limited to files that match FILE-TYPE. STRING defaults to the
symbol under point.
If called with a prefix, prompts for flags to pass to ag.
\(fn STRING FILE-TYPE DIRECTORY)" t nil)
(autoload 'ag-regexp "ag" "\
Search using ag in a given directory for a given regexp.
The regexp should be in PCRE syntax, not Emacs regexp syntax.
If called with a prefix, prompts for flags to pass to ag.
\(fn STRING DIRECTORY)" t nil)
(autoload 'ag-project "ag" "\
Guess the root of the current project and search it with ag
for the given literal search STRING.
If called with a prefix, prompts for flags to pass to ag.
\(fn STRING)" t nil)
(autoload 'ag-project-files "ag" "\
Search using ag for a given literal search STRING,
limited to files that match FILE-TYPE. STRING defaults to the
symbol under point.
If called with a prefix, prompts for flags to pass to ag.
\(fn STRING FILE-TYPE)" t nil)
(autoload 'ag-project-regexp "ag" "\
Guess the root of the current project and search it with ag
for the given regexp. The regexp should be in PCRE syntax, not
Emacs regexp syntax.
If called with a prefix, prompts for flags to pass to ag.
\(fn REGEXP)" t nil)
(defalias 'ag-project-at-point 'ag-project)
(defalias 'ag-regexp-project-at-point 'ag-project-regexp)
(autoload 'ag-dired "ag" "\
Recursively find files in DIR matching literal search STRING.
The PATTERN is matched against the full path to the file, not
only against the file name.
The results are presented as a `dired-mode' buffer with
`default-directory' being DIR.
See also `ag-dired-regexp'.
\(fn DIR STRING)" t nil)
(autoload 'ag-dired-regexp "ag" "\
Recursively find files in DIR matching REGEXP.
REGEXP should be in PCRE syntax, not Emacs regexp syntax.
The REGEXP is matched against the full path to the file, not
only against the file name.
Results are presented as a `dired-mode' buffer with
`default-directory' being DIR.
See also `find-dired'.
\(fn DIR REGEXP)" t nil)
(autoload 'ag-project-dired "ag" "\
Recursively find files in current project matching PATTERN.
See also `ag-dired'.
\(fn PATTERN)" t nil)
(autoload 'ag-project-dired-regexp "ag" "\
Recursively find files in current project matching REGEXP.
See also `ag-dired-regexp'.
\(fn REGEXP)" t nil)
(autoload 'ag-kill-buffers "ag" "\
Kill all `ag-mode' buffers.
\(fn)" t nil)
(autoload 'ag-kill-other-buffers "ag" "\
Kill all `ag-mode' buffers other than the current buffer.
\(fn)" t nil)
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "ag" '("ag-" "ag/")))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; ag-autoloads.el ends here

View file

@ -0,0 +1,2 @@
;;; -*- no-byte-compile: t -*-
(define-package "ag" "20190726.9" "A front-end for ag ('the silver searcher'), the C ack replacement." '((dash "2.8.0") (s "1.9.0") (cl-lib "0.5")) :commit "bd81d68466e44301505629454dfc689b6c17d94b" :authors '(("Wilfred Hughes" . "me@wilfred.me.uk")) :maintainer '("Wilfred Hughes" . "me@wilfred.me.uk"))

689
elpa/ag-20190726.9/ag.el Normal file
View file

@ -0,0 +1,689 @@
;;; ag.el --- A front-end for ag ('the silver searcher'), the C ack replacement.
;; Copyright (C) 2013-2014 Wilfred Hughes <me@wilfred.me.uk>
;;
;; Author: Wilfred Hughes <me@wilfred.me.uk>
;; Created: 11 January 2013
;; Version: 0.48
;; Package-Version: 20190726.9
;; Package-Requires: ((dash "2.8.0") (s "1.9.0") (cl-lib "0.5"))
;;; Commentary:
;; Please see README.md for documentation, or read it online at
;; https://github.com/Wilfred/ag.el/#agel
;;; License:
;; This file is not part of GNU Emacs.
;; However, it is distributed under the same license.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Code:
(require 'cl-lib) ;; cl-letf, cl-defun
(require 'dired) ;; dired-sort-inhibit
(require 'dash)
(require 's)
(require 'find-dired) ;; find-dired-filter
(defgroup ag nil
"A front-end for ag - The Silver Searcher."
:group 'tools
:group 'matching)
(defcustom ag-executable
"ag"
"Name of the ag executable to use."
:type 'string
:group 'ag)
(defcustom ag-arguments
(list "--smart-case" "--stats")
"Additional arguments passed to ag.
Ag.el internally uses --column, --line-number and --color
options (with specific colors) to match groups, so options
specified here should not conflict.
--line-number is required on Windows, as otherwise ag will not
print line numbers when the input is a stream."
:type '(repeat (string))
:group 'ag)
(defcustom ag-dired-arguments
(list "--nocolor" "-S")
"Additional arguments passed to ag-dired."
:type '(repeat (string))
:group 'ag)
(defcustom ag-context-lines nil
"Number of context lines to include before and after a matching line."
:type 'integer
:group 'ag)
(defcustom ag-group-matches t
"Group matches in the same file together.
If nil, the file name is repeated at the beginning of every match line."
:type 'boolean
:group 'ag)
(defcustom ag-highlight-search nil
"Non-nil means we highlight the current search term in results.
This requires the ag command to support --color-match, which is only in v0.14+"
:type 'boolean
:group 'ag)
(defcustom ag-reuse-buffers nil
"Non-nil means we reuse the existing search results buffer or
dired results buffer, rather than creating one buffer per unique
search."
:type 'boolean
:group 'ag)
(defcustom ag-reuse-window nil
"Non-nil means we open search results in the same window,
hiding the results buffer."
:type 'boolean
:group 'ag)
(defcustom ag-project-root-function nil
"A function to determine the project root for `ag-project'.
If set to a function, call this function with the name of the
file or directory for which to determine the project root
directory.
If set to nil, fall back to finding VCS root directories."
:type '(choice (const :tag "Default (VCS root)" nil)
(function :tag "Function"))
:group 'ag)
(defcustom ag-ignore-list nil
"A list of patterns for files/directories to ignore when searching."
:type '(repeat (string))
:group 'ag)
(make-variable-buffer-local 'ag-ignore-list)
(put 'ag-ignore-list 'safe-local-variable #'listp)
(require 'compile)
;; Although ag results aren't exactly errors, we treat them as errors
;; so `next-error' and `previous-error' work. However, we ensure our
;; face inherits from `compilation-info-face' so the results are
;; styled appropriately.
(defface ag-hit-face '((t :inherit compilation-info))
"Face name to use for ag matches."
:group 'ag)
(defface ag-match-face '((t :inherit match))
"Face name to use for ag matches."
:group 'ag)
(defvar ag-search-finished-hook nil
"Hook run when ag completes a search in a buffer.")
(defun ag/run-finished-hook (buffer how-finished)
"Run the ag hook to signal that the search has completed."
(with-current-buffer buffer
(run-hooks 'ag-search-finished-hook)))
(defmacro ag/with-patch-function (fun-name fun-args fun-body &rest body)
"Temporarily override the definition of FUN-NAME whilst BODY is executed.
Assumes FUNCTION is already defined (see http://emacs.stackexchange.com/a/3452/304)."
`(cl-letf (((symbol-function ,fun-name)
(lambda ,fun-args ,fun-body)))
,@body))
(defun ag/next-error-function (n &optional reset)
"Open the search result at point in the current window or a
different window, according to `ag-reuse-window'."
(if ag-reuse-window
;; prevent changing the window
(ag/with-patch-function
'pop-to-buffer (buffer &rest args) (switch-to-buffer buffer)
(compilation-next-error-function n reset))
;; just navigate to the results as normal
(compilation-next-error-function n reset)))
;; Note that we want to use as tight a regexp as we can to try and
;; handle weird file names (with colons in them) as well as possible.
;; E.g. we use [1-9][0-9]* rather than [0-9]+ so as to accept ":034:"
;; in file names.
(defvar ag/file-column-pattern-nogroup
"^\\(.+?\\):\\([1-9][0-9]*\\):\\([1-9][0-9]*\\):"
"A regexp pattern that groups output into filename, line number and column number.")
(defvar ag/file-column-pattern-group
"^\\([[:digit:]]+\\):\\([[:digit:]]+\\):"
"A regexp pattern to match line number and column number with grouped output.")
(defun ag/compilation-match-grouped-filename ()
"Match filename backwards when a line/column match is found in grouped output mode."
(save-match-data
(save-excursion
(when (re-search-backward "^File: \\(.*\\)$" (point-min) t)
(list (match-string 1))))))
(define-compilation-mode ag-mode "Ag"
"Ag results compilation mode"
(set (make-local-variable 'compilation-error-regexp-alist)
'(compilation-ag-nogroup compilation-ag-group))
(set (make-local-variable 'compilation-error-regexp-alist-alist)
(list (cons 'compilation-ag-nogroup (list ag/file-column-pattern-nogroup 1 2 3))
(cons 'compilation-ag-group (list ag/file-column-pattern-group
'ag/compilation-match-grouped-filename 1 2))))
(set (make-local-variable 'compilation-error-face) 'ag-hit-face)
(set (make-local-variable 'next-error-function) #'ag/next-error-function)
(set (make-local-variable 'compilation-finish-functions)
#'ag/run-finished-hook)
(add-hook 'compilation-filter-hook 'ag-filter nil t))
(define-key ag-mode-map (kbd "p") #'compilation-previous-error)
(define-key ag-mode-map (kbd "n") #'compilation-next-error)
(define-key ag-mode-map (kbd "k") '(lambda () (interactive)
(let (kill-buffer-query-functions) (kill-buffer))))
(defun ag/buffer-name (search-string directory regexp)
"Return a buffer name formatted according to ag.el conventions."
(cond
(ag-reuse-buffers "*ag search*")
(regexp (format "*ag search regexp:%s dir:%s*" search-string directory))
(:else (format "*ag search text:%s dir:%s*" search-string directory))))
(defun ag/format-ignore (ignores)
"Prepend '--ignore' to every item in IGNORES."
(apply #'append
(mapcar (lambda (item) (list "--ignore" item)) ignores)))
(cl-defun ag/search (string directory
&key (regexp nil) (file-regex nil) (file-type nil) (files '(".")))
"Run ag searching for the STRING given in DIRECTORY.
If `files` is passed, tell ag to look only on those files.
If REGEXP is non-nil, treat STRING as a regular expression."
(let ((default-directory (file-name-as-directory directory))
(arguments ag-arguments)
(shell-command-switch "-c"))
;; Add double dashes at the end of command line if not specified in
;; ag-arguments.
(unless (equal (car (last arguments)) "--")
(setq arguments (append arguments '("--"))))
(setq arguments
(append '("--line-number" "--column" "--color" "--color-match" "30;43"
"--color-path" "1;32")
arguments))
(if ag-group-matches
(setq arguments (cons "--group" arguments))
(setq arguments (cons "--nogroup" arguments)))
(unless regexp
(setq arguments (cons "--literal" arguments)))
(when (or (eq system-type 'windows-nt) (eq system-type 'cygwin))
;; Use --vimgrep to work around issue #97 on Windows.
(setq arguments (cons "--vimgrep" arguments)))
(when (char-or-string-p file-regex)
(setq arguments (append `("--file-search-regex" ,file-regex) arguments)))
(when file-type
(setq arguments (cons (format "--%s" file-type) arguments)))
(if (integerp current-prefix-arg)
(setq arguments (cons (format "--context=%d" (abs current-prefix-arg)) arguments))
(when ag-context-lines
(setq arguments (cons (format "--context=%d" ag-context-lines) arguments))))
(when ag-ignore-list
(setq arguments (append (ag/format-ignore ag-ignore-list) arguments)))
(unless (file-exists-p default-directory)
(error "No such directory %s" default-directory))
(let ((command-string
(mapconcat #'shell-quote-argument
(append (list ag-executable) arguments (append `(,string) files))
" ")))
;; If we're called with a prefix, let the user modify the command before
;; running it. Typically this means they want to pass additional arguments.
;; The numeric value is used for context lines: positive is just context
;; number (no modification), negative allows further modification.
(when (and current-prefix-arg (not (and (integerp current-prefix-arg) (> current-prefix-arg 0))))
;; Make a space in the command-string for the user to enter more arguments.
(setq command-string (ag/replace-first command-string " -- " " -- "))
;; Prompt for the command.
(let ((adjusted-point (- (length command-string) (length string) 5)))
(setq command-string
(read-from-minibuffer "ag command: "
(cons command-string adjusted-point)))))
;; Call ag.
(compilation-start
command-string
#'ag-mode
`(lambda (mode-name) ,(ag/buffer-name string directory regexp))))))
(defun ag/dwim-at-point ()
"If there's an active selection, return that.
Otherwise, get the symbol at point, as a string."
(cond ((use-region-p)
(buffer-substring-no-properties (region-beginning) (region-end)))
((symbol-at-point)
(substring-no-properties
(symbol-name (symbol-at-point))))))
(defun ag/buffer-extension-regex ()
"If the current buffer has an extension, return
a PCRE pattern that matches files with that extension.
Returns an empty string otherwise."
(let ((file-name (buffer-file-name)))
(if (stringp file-name)
(format "\\.%s$" (ag/escape-pcre (file-name-extension file-name)))
"")))
(defun ag/longest-string (&rest strings)
"Given a list of strings and nils, return the longest string."
(let ((longest-string nil))
(dolist (string (-non-nil strings))
(when (< (length longest-string)
(length string))
(setq longest-string string)))
longest-string))
(defun ag/replace-first (string before after)
"Replace the first occurrence of BEFORE in STRING with AFTER."
(replace-regexp-in-string
(concat "\\(" (regexp-quote before) "\\)" ".*\\'")
after string
nil nil 1))
(autoload 'vc-git-root "vc-git")
(require 'vc-svn)
;; Emacs 23.4 doesn't provide vc-svn-root.
(unless (functionp 'vc-svn-root)
(defun vc-svn-root (file)
(vc-find-root file vc-svn-admin-directory)))
(autoload 'vc-hg-root "vc-hg")
(autoload 'vc-bzr-root "vc-bzr")
(defun ag/project-root (file-path)
"Guess the project root of the given FILE-PATH.
Use `ag-project-root-function' if set, or fall back to VCS
roots."
(if ag-project-root-function
(funcall ag-project-root-function file-path)
(or (ag/longest-string
(vc-git-root file-path)
(vc-svn-root file-path)
(vc-hg-root file-path)
(vc-bzr-root file-path))
file-path)))
(defun ag/dired-align-size-column ()
(beginning-of-line)
(when (looking-at "^ ")
(forward-char 2)
(search-forward " " nil t 4)
(let* ((size-start (point))
(size-end (search-forward " " nil t))
(width (and size-end (- size-end size-start))))
(when (and size-end
(< width 12)
(> width 1))
(goto-char size-start)
(insert (make-string (- 12 width) ? ))))))
(defun ag/dired-filter (proc string)
"Filter the output of ag to make it suitable for `dired-mode'."
(let ((buf (process-buffer proc))
(inhibit-read-only t))
(if (buffer-name buf)
(with-current-buffer buf
(save-excursion
(save-restriction
(widen)
(let ((beg (point-max)))
(goto-char beg)
(insert string)
(goto-char beg)
(or (looking-at "^")
(progn
(ag/dired-align-size-column)
(forward-line 1)))
(while (looking-at "^")
(insert " ")
(ag/dired-align-size-column)
(forward-line 1))
(goto-char beg)
(beginning-of-line)
;; Remove occurrences of default-directory.
(while (search-forward (concat " " default-directory) nil t)
(replace-match " " nil t))
(goto-char (point-max))
(if (search-backward "\n" (process-mark proc) t)
(progn
(dired-insert-set-properties (process-mark proc)
(1+ (point)))
(move-marker (process-mark proc) (1+ (point)))))))))
(delete-process proc))))
(defun ag/dired-sentinel (proc state)
"Update the status/modeline after the process finishes."
(let ((buf (process-buffer proc))
(inhibit-read-only t))
(if (buffer-name buf)
(with-current-buffer buf
(let ((buffer-read-only nil))
(save-excursion
(goto-char (point-max))
(insert "\n ag " state)
(forward-char -1) ;Back up before \n at end of STATE.
(insert " at " (substring (current-time-string) 0 19))
(forward-char 1)
(setq mode-line-process
(concat ":" (symbol-name (process-status proc))))
;; Since the buffer and mode line will show that the
;; process is dead, we can delete it now. Otherwise it
;; will stay around until M-x list-processes.
(delete-process proc)
(force-mode-line-update)))
(run-hooks 'dired-after-readin-hook)
(message "%s finished." (current-buffer))))))
(defun ag/kill-process ()
"Kill the `ag' process running in the current buffer."
(interactive)
(let ((ag (get-buffer-process (current-buffer))))
(and ag (eq (process-status ag) 'run)
(eq (process-filter ag) (function find-dired-filter))
(condition-case nil
(delete-process ag)
(error nil)))))
(defun ag/escape-pcre (regexp)
"Escape the PCRE-special characters in REGEXP so that it is
matched literally."
(let ((alphanum "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))
(apply #'concat
(mapcar
(lambda (c)
(cond
((not (string-match-p (regexp-quote c) alphanum))
(concat "\\" c))
(t c)))
(mapcar #'char-to-string (string-to-list regexp))))))
;;;###autoload
(defun ag (string directory)
"Search using ag in a given DIRECTORY for a given literal search STRING,
with STRING defaulting to the symbol under point.
If called with a prefix, prompts for flags to pass to ag."
(interactive (list (ag/read-from-minibuffer "Search string")
(read-directory-name "Directory: ")))
(ag/search string directory))
;;;###autoload
(defun ag-files (string file-type directory)
"Search using ag in a given DIRECTORY for a given literal search STRING,
limited to files that match FILE-TYPE. STRING defaults to the
symbol under point.
If called with a prefix, prompts for flags to pass to ag."
(interactive (list (ag/read-from-minibuffer "Search string")
(ag/read-file-type)
(read-directory-name "Directory: ")))
(apply #'ag/search string directory file-type))
;;;###autoload
(defun ag-regexp (string directory)
"Search using ag in a given directory for a given regexp.
The regexp should be in PCRE syntax, not Emacs regexp syntax.
If called with a prefix, prompts for flags to pass to ag."
(interactive "sSearch regexp: \nDDirectory: ")
(ag/search string directory :regexp t))
;;;###autoload
(defun ag-project (string)
"Guess the root of the current project and search it with ag
for the given literal search STRING.
If called with a prefix, prompts for flags to pass to ag."
(interactive (list (ag/read-from-minibuffer "Search string")))
(ag/search string (ag/project-root default-directory)))
;;;###autoload
(defun ag-project-files (string file-type)
"Search using ag for a given literal search STRING,
limited to files that match FILE-TYPE. STRING defaults to the
symbol under point.
If called with a prefix, prompts for flags to pass to ag."
(interactive (list (ag/read-from-minibuffer "Search string")
(ag/read-file-type)))
(apply 'ag/search string (ag/project-root default-directory) file-type))
(defun ag/read-from-minibuffer (prompt)
"Read a value from the minibuffer with PROMPT.
If there's a string at point, offer that as a default."
(let* ((suggested (ag/dwim-at-point))
(final-prompt
(if suggested
(format "%s (default %s): " prompt suggested)
(format "%s: " prompt)))
;; Ask the user for input, but add `suggested' to the history
;; so they can use M-n if they want to modify it.
(user-input (read-from-minibuffer
final-prompt
nil nil nil nil suggested)))
;; Return the input provided by the user, or use `suggested' if
;; the input was empty.
(if (> (length user-input) 0)
user-input
suggested)))
;;;###autoload
(defun ag-project-regexp (regexp)
"Guess the root of the current project and search it with ag
for the given regexp. The regexp should be in PCRE syntax, not
Emacs regexp syntax.
If called with a prefix, prompts for flags to pass to ag."
(interactive (list (ag/read-from-minibuffer "Search regexp")))
(ag/search regexp (ag/project-root default-directory) :regexp t))
(autoload 'symbol-at-point "thingatpt")
;;;###autoload
(defalias 'ag-project-at-point 'ag-project)
(make-obsolete 'ag-project-at-point 'ag-project "0.19")
;;;###autoload
(defalias 'ag-regexp-project-at-point 'ag-project-regexp)
(make-obsolete 'ag-regexp-project-at-point 'ag-project-regexp "0.46")
;;;###autoload
(defun ag-dired (dir string)
"Recursively find files in DIR matching literal search STRING.
The PATTERN is matched against the full path to the file, not
only against the file name.
The results are presented as a `dired-mode' buffer with
`default-directory' being DIR.
See also `ag-dired-regexp'."
(interactive "DDirectory: \nsFile pattern: ")
(ag-dired-regexp dir (ag/escape-pcre string)))
;;;###autoload
(defun ag-dired-regexp (dir regexp)
"Recursively find files in DIR matching REGEXP.
REGEXP should be in PCRE syntax, not Emacs regexp syntax.
The REGEXP is matched against the full path to the file, not
only against the file name.
Results are presented as a `dired-mode' buffer with
`default-directory' being DIR.
See also `find-dired'."
(interactive "DDirectory: \nsFile regexp: ")
(let* ((dired-buffers dired-buffers) ;; do not mess with regular dired buffers
(orig-dir dir)
(dir (file-name-as-directory (expand-file-name dir)))
(buffer-name (if ag-reuse-buffers
"*ag dired*"
(format "*ag dired pattern:%s dir:%s*" regexp dir)))
(cmd (if (string= system-type "windows-nt")
(concat ag-executable " " (combine-and-quote-strings ag-dired-arguments " ") " -g \"" regexp "\" "
(shell-quote-argument dir)
" | grep -v \"^$\" | sed \"s/'/\\\\\\\\'/g\" | xargs -I '{}' "
insert-directory-program " "
dired-listing-switches " '{}' &")
(concat ag-executable " " (combine-and-quote-strings ag-dired-arguments " ") " -g '" regexp "' "
(shell-quote-argument dir)
" | grep -v '^$' | sed s/\\'/\\\\\\\\\\'/g | xargs -I '{}' "
insert-directory-program " "
dired-listing-switches " '{}' &"))))
(with-current-buffer (get-buffer-create buffer-name)
(switch-to-buffer (current-buffer))
(widen)
(kill-all-local-variables)
(if (fboundp 'read-only-mode)
(read-only-mode -1)
(setq buffer-read-only nil))
(let ((inhibit-read-only t)) (erase-buffer))
(setq default-directory dir)
(run-hooks 'dired-before-readin-hook)
(shell-command cmd (current-buffer))
(insert " " dir ":\n")
(insert " " cmd "\n")
(dired-mode dir)
(let ((map (make-sparse-keymap)))
(set-keymap-parent map (current-local-map))
(define-key map "\C-c\C-k" 'ag/kill-process)
(use-local-map map))
(set (make-local-variable 'dired-sort-inhibit) t)
(set (make-local-variable 'revert-buffer-function)
`(lambda (ignore-auto noconfirm)
(ag-dired-regexp ,orig-dir ,regexp)))
(if (fboundp 'dired-simple-subdir-alist)
(dired-simple-subdir-alist)
(set (make-local-variable 'dired-subdir-alist)
(list (cons default-directory (point-min-marker)))))
(let ((proc (get-buffer-process (current-buffer))))
(set-process-filter proc #'ag/dired-filter)
(set-process-sentinel proc #'ag/dired-sentinel)
;; Initialize the process marker; it is used by the filter.
(move-marker (process-mark proc) 1 (current-buffer)))
(setq mode-line-process '(":%s")))))
;;;###autoload
(defun ag-project-dired (pattern)
"Recursively find files in current project matching PATTERN.
See also `ag-dired'."
(interactive "sFile pattern: ")
(ag-dired-regexp (ag/project-root default-directory) (ag/escape-pcre pattern)))
;;;###autoload
(defun ag-project-dired-regexp (regexp)
"Recursively find files in current project matching REGEXP.
See also `ag-dired-regexp'."
(interactive "sFile regexp: ")
(ag-dired-regexp (ag/project-root default-directory) regexp))
;;;###autoload
(defun ag-kill-buffers ()
"Kill all `ag-mode' buffers."
(interactive)
(dolist (buffer (buffer-list))
(when (eq (buffer-local-value 'major-mode buffer) 'ag-mode)
(kill-buffer buffer))))
;;;###autoload
(defun ag-kill-other-buffers ()
"Kill all `ag-mode' buffers other than the current buffer."
(interactive)
(let ((current-buffer (current-buffer)))
(dolist (buffer (buffer-list))
(when (and
(eq (buffer-local-value 'major-mode buffer) 'ag-mode)
(not (eq buffer current-buffer)))
(kill-buffer buffer)))))
;; Based on grep-filter.
(defun ag-filter ()
"Handle escape sequences inserted by the ag process.
This function is called from `compilation-filter-hook'."
(save-excursion
(forward-line 0)
(let ((end (point)) beg)
(goto-char compilation-filter-start)
(forward-line 0)
(setq beg (point))
;; Only operate on whole lines so we don't get caught with part of an
;; escape sequence in one chunk and the rest in another.
(when (< (point) end)
(setq end (copy-marker end))
(when ag-highlight-search
;; Highlight ag matches and delete marking sequences.
(while (re-search-forward "\033\\[30;43m\\(.*?\\)\033\\[0m\033\\[K" end 1)
(replace-match (propertize (match-string 1)
'face nil 'font-lock-face 'ag-match-face)
t t)))
;; Add marker at start of line for files. This is used by the match
;; in `compilation-error-regexp-alist' to extract the file name.
(when ag-group-matches
(goto-char beg)
(while (re-search-forward "\033\\[1;32m\\(.*\\)\033\\[0m\033\\[K" end 1)
(replace-match
(concat "File: " (propertize (match-string 1) 'face nil 'font-lock-face
'compilation-info))
t t)))
;; Delete all remaining escape sequences
(goto-char beg)
(while (re-search-forward "\033\\[[0-9;]*[mK]" end 1)
(replace-match "" t t))))))
(defun ag/get-supported-types ()
"Query the ag executable for which file types it recognises."
(let* ((ag-output (shell-command-to-string (format "%s --list-file-types" ag-executable)))
(lines (-map #'s-trim (s-lines ag-output)))
(types (--keep (when (s-starts-with? "--" it) (s-chop-prefix "--" it )) lines))
(extensions (--map (s-split " " it) (--filter (s-starts-with? "." it) lines))))
(-zip types extensions)))
(defun ag/read-file-type ()
"Prompt the user for a known file type, or let them specify a PCRE regex."
(let* ((all-types-with-extensions (ag/get-supported-types))
(all-types (mapcar 'car all-types-with-extensions))
(file-type
(completing-read "Select file type: "
(append '("custom (provide a PCRE regex)") all-types)))
(file-type-extensions
(cdr (assoc file-type all-types-with-extensions))))
(if file-type-extensions
(list :file-type file-type)
(list :file-regex
(read-from-minibuffer "Filenames which match PCRE: "
(ag/buffer-extension-regex))))))
(provide 'ag)
;;; ag.el ends here

BIN
elpa/ag-20190726.9/ag.elc Normal file

Binary file not shown.

View file

@ -0,0 +1,349 @@
;;; js2-imenu-extras.el --- Imenu support for additional constructs
;; Copyright (C) 2012-2014 Free Software Foundation, Inc.
;; Author: Dmitry Gutov <dgutov@yandex.ru>
;; Keywords: languages, javascript, imenu
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This package adds Imenu support for additional framework constructs and
;; structural patterns to `js2-mode'.
;; Usage:
;; (add-hook 'js2-mode-hook 'js2-imenu-extras-mode)
;; To customize how it works:
;; M-x customize-group RET js2-imenu RET
(eval-when-compile
(require 'cl))
(require 'js2-mode)
(defvar js2-imenu-extension-styles
`((:framework jquery
:call-re "\\_<\\(?:jQuery\\|\\$\\|_\\)\\.extend\\s-*("
:recorder js2-imenu-record-jquery-extend)
(:framework jquery-ui
:call-re "^\\s-*\\(?:jQuery\\|\\$\\)\\.widget\\s-*("
:recorder js2-imenu-record-string-declare)
(:framework dojo
:call-re "^\\s-*dojo.declare\\s-*("
:recorder js2-imenu-record-string-declare)
(:framework backbone
:call-re ,(concat "\\_<" js2-mode-identifier-re "\\.extend\\s-*(")
:recorder js2-imenu-record-backbone-extend)
(:framework enyo
:call-re "\\_<enyo\\.kind\\s-*("
:recorder js2-imenu-record-enyo-kind)
(:framework react
:call-re "\\_<React\\.createClass\\s-*("
:recorder js2-imenu-record-react-class)
(:framework sencha
:call-re "^\\s-*Ext\\.define\\s-*("
:recorder js2-imenu-record-sencha-class))
"List of JavaScript class definition or extension styles.
:framework is a valid value in `js2-imenu-enabled-frameworks'.
:call-re is a regular expression that has no capturing groups.
:recorder is a function name that will be called when the regular
expression matches some text in the buffer. When it's called, point will be
at the end of the match. The function must keep the point position.")
(defconst js2-imenu-available-frameworks
(mapcar (lambda (style) (plist-get style :framework)) js2-imenu-extension-styles)
"List of available JavaScript framework symbols.")
(defcustom js2-imenu-enabled-frameworks js2-imenu-available-frameworks
"Frameworks to be recognized by `js2-mode'."
:type (cons 'set (mapcar (lambda (x) (list 'const x))
js2-imenu-available-frameworks))
:group 'js2-imenu)
(defcustom js2-imenu-show-other-functions t
"Non-nil to show functions not recognized by other mechanisms,
in a shared namespace."
:type 'boolean
:group 'js2-imenu)
(defcustom js2-imenu-other-functions-ns "?"
"Namespace name to use for other functions."
:type 'string
:group 'js2-imenu)
(defcustom js2-imenu-show-module-pattern t
"Non-nil to recognize the module pattern:
var foobs = (function(a) {
return {fib: function() {}, fub: function() {}};
})(b);
We record the returned hash as belonging to the named module, and
prefix any functions defined inside the IIFE with the module name."
:type 'boolean
:group 'js2-imenu)
(defcustom js2-imenu-split-string-identifiers t
"When non-nil, split string identifiers on dots.
Currently used for jQuery widgets, Dojo and Enyo declarations."
:type 'boolean
:group 'js2-imenu)
;;;###autoload
(defun js2-imenu-extras-setup ()
(when js2-imenu-enabled-frameworks
(add-hook 'js2-build-imenu-callbacks 'js2-imenu-record-declarations t t))
(when (or js2-imenu-show-other-functions js2-imenu-show-module-pattern)
(add-hook 'js2-build-imenu-callbacks 'js2-imenu-walk-ast t t)))
(defun js2-imenu-extras-remove ()
(remove-hook 'js2-build-imenu-callbacks 'js2-imenu-record-declarations t)
(remove-hook 'js2-build-imenu-callbacks 'js2-imenu-walk-ast t))
(defun js2-imenu-record-declarations ()
(let* ((styles (loop for style in js2-imenu-extension-styles
when (memq (plist-get style :framework)
js2-imenu-enabled-frameworks)
collect style))
(re (mapconcat (lambda (style)
(concat "\\(" (plist-get style :call-re) "\\)"))
styles "\\|")))
(goto-char (point-min))
(while (js2-re-search-forward re nil t)
(loop for i from 0 to (1- (length styles))
when (match-beginning (1+ i))
return (funcall (plist-get (nth i styles) :recorder))))))
(defun js2-imenu-record-jquery-extend ()
(let ((pred (lambda (subject)
(and
(js2-prop-get-node-p subject)
(string= (js2-name-node-name (js2-prop-get-node-right subject))
"prototype")))))
(js2-imenu-record-extend-first-arg (1- (point)) pred
'js2-compute-nested-prop-get)))
(defun js2-imenu-record-string-declare ()
(js2-imenu-record-extend-first-arg
(1- (point)) 'js2-string-node-p
(lambda (node)
(if js2-imenu-split-string-identifiers
(split-string (js2-string-node-value node) "\\." t)
(list (js2-string-node-value node))))))
(defun js2-imenu-record-extend-first-arg (point pred qname-fn)
(let* ((node (js2-node-at-point point))
(args (js2-call-node-args node))
(subject (first args)))
(when (funcall pred subject)
(loop for arg in (cdr args)
when (js2-object-node-p arg)
do (js2-record-object-literal
arg (funcall qname-fn subject) (js2-node-abs-pos arg))))))
(defun js2-imenu-record-backbone-or-react ()
(let* ((node (js2-node-at-point (1- (point))))
(args (js2-call-node-args node))
(methods (first args))
(parent (js2-node-parent node)))
(when (js2-object-node-p methods)
(let ((subject (cond ((js2-var-init-node-p parent)
(js2-var-init-node-target parent))
((js2-assign-node-p parent)
(js2-assign-node-left parent)))))
(when subject
(js2-record-object-literal methods
(js2-compute-nested-prop-get subject)
(js2-node-abs-pos methods)))))))
(defalias 'js2-imenu-record-backbone-extend 'js2-imenu-record-backbone-or-react)
(defalias 'js2-imenu-record-react-class 'js2-imenu-record-backbone-or-react)
(defun js2-imenu-record-enyo-kind ()
(let* ((node (js2-node-at-point (1- (point))))
(args (js2-call-node-args node))
(options (first args)))
(when (js2-object-node-p options)
(let ((name-value
(loop for elem in (js2-object-node-elems options)
thereis
(let ((key (js2-object-prop-node-left elem))
(value (js2-object-prop-node-right elem)))
(when (and (equal
(cond ((js2-name-node-p key)
(js2-name-node-name key))
((js2-string-node-p key)
(js2-string-node-value key)))
"name")
(js2-string-node-p value))
(js2-string-node-value value))))))
(when name-value
(js2-record-object-literal options
(if js2-imenu-split-string-identifiers
(split-string name-value "\\.")
(list name-value))
(js2-node-abs-pos options)))))))
(defun js2-imenu-record-sencha-class ()
(let* ((node (js2-node-at-point (1- (point))))
(args (js2-call-node-args node))
(name (first args))
(methods (second args)))
(when (and (js2-string-node-p name) (js2-object-node-p methods))
(let ((name-value (js2-string-node-value name)))
(js2-record-object-literal methods
(if js2-imenu-split-string-identifiers
(split-string name-value "\\." t)
(list name-value))
(js2-node-abs-pos methods))))))
(defun js2-imenu-walk-ast ()
(js2-visit-ast
js2-mode-ast
(lambda (node end-p)
(unless end-p
(cond
((and js2-imenu-show-other-functions
(js2-object-prop-node-p node))
(js2-imenu-record-orphan-prop-node-function node))
((js2-assign-node-p node)
(cond
((and js2-imenu-show-other-functions
(js2-function-node-p
(js2-assign-node-right node)))
(js2-imenu-record-orphan-assign-node-function
(js2-assign-node-left node)
(js2-assign-node-right node)))
((and js2-imenu-show-module-pattern
(js2-call-node-p
(js2-assign-node-right node)))
(js2-imenu-record-module-pattern
(js2-assign-node-left node)
(js2-assign-node-right node)))))
((js2-var-init-node-p node)
(cond
((and js2-imenu-show-other-functions
(js2-function-node-p
(js2-var-init-node-initializer node)))
(js2-imenu-record-orphan-assign-node-function
(js2-var-init-node-target node)
(js2-var-init-node-initializer node)))
((and js2-imenu-show-module-pattern
(js2-call-node-p
(js2-var-init-node-initializer node)))
(js2-imenu-record-module-pattern
(js2-var-init-node-target node)
(js2-var-init-node-initializer node))))))
t))))
(defun js2-imenu-parent-key-names (node)
"Get the list of parent key names of NODE.
For example, for code
{rules: {password: {required: function() {}}}}
when NODE is the inner `js2-object-prop-mode',
it returns `(\"rules\" \"password\")'."
(let (rlt (n node))
(while (setq n (js2-imenu-parent-prop-node n))
(push (js2-prop-node-name (js2-object-prop-node-left n)) rlt))
rlt))
(defun js2-imenu-parent-prop-node (node)
"When the parent of NODE is `js2-object-node',
and the grandparent is `js2-object-prop-node',
return the grandparent."
;; Suppose the code is:
;; {parent-key: {required: function() {}}}
;; NODE is `required: function() {}'.
(let (p2 p3)
;; Parent is `{required: function() {}}'.
(setq p2 (js2-node-parent node))
;; GP is `parent-key: {required: function() {}}'.
(when (and p2 (js2-object-node-p p2))
(setq p3 (js2-node-parent p2))
(if (and p3 (js2-object-prop-node-p p3)) p3))))
(defun js2-imenu-record-orphan-prop-node-function (node)
"Record orphan function when it's the value of NODE.
NODE must be `js2-object-prop-node'."
(when (js2-function-node-p (js2-object-prop-node-right node))
(let ((fn-node (js2-object-prop-node-right node)))
(unless (and js2-imenu-function-map
(gethash fn-node js2-imenu-function-map))
(let ((key-node (js2-object-prop-node-left node))
(parent-prop-node (js2-imenu-parent-prop-node node))
chain)
(setq chain (nconc (js2-imenu-parent-key-names node)
(list (js2-prop-node-name key-node))))
(push js2-imenu-other-functions-ns chain)
(js2-record-imenu-entry fn-node chain
(js2-node-abs-pos key-node)))))))
(defun js2-imenu-record-orphan-assign-node-function (target-node fn-node)
"Record orphan function FN-NODE assigned to node TARGET."
(when (or (not js2-imenu-function-map)
(eq 'skip
(gethash fn-node js2-imenu-function-map 'skip)))
(let ((chain (js2-compute-nested-prop-get target-node)))
(when chain
(push js2-imenu-other-functions-ns chain)
(js2-record-imenu-entry fn-node chain (js2-node-abs-pos fn-node))))))
(defun js2-imenu-record-module-pattern (target init)
"Recognize and record module pattern use instance.
INIT must be `js2-call-node'."
(let ((callt (js2-call-node-target init)))
;; Just basic call form: (function() {...})();
;; TODO: Handle variations without duplicating `js2-wrapper-function-p'?
(when (and (js2-paren-node-p callt)
(js2-function-node-p (js2-paren-node-expr callt)))
(let* ((fn (js2-paren-node-expr callt))
(blk (js2-function-node-body fn))
(ret (car (last (js2-block-node-kids blk)))))
(when (and (js2-return-node-p ret)
(js2-object-node-p (js2-return-node-retval ret)))
;; TODO: Map function names when revealing module pattern is used.
(let ((retval (js2-return-node-retval ret))
(target-qname (js2-compute-nested-prop-get target)))
(js2-record-object-literal retval target-qname
(js2-node-abs-pos retval))
(js2-record-imenu-entry fn target-qname
(js2-node-abs-pos target))))))))
;;;###autoload
(define-minor-mode js2-imenu-extras-mode
"Toggle Imenu support for frameworks and structural patterns."
:lighter ""
(if js2-imenu-extras-mode
(js2-imenu-extras-setup)
(js2-imenu-extras-remove)))
(provide 'js2-imenu-extras)

Binary file not shown.

View file

@ -0,0 +1,85 @@
;;; js2-mode-autoloads.el --- automatically extracted autoloads
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "js2-imenu-extras" "js2-imenu-extras.el" (0
;;;;;; 0 0 0))
;;; Generated autoloads from js2-imenu-extras.el
(autoload 'js2-imenu-extras-setup "js2-imenu-extras" "\
\(fn)" nil nil)
(autoload 'js2-imenu-extras-mode "js2-imenu-extras" "\
Toggle Imenu support for frameworks and structural patterns.
\(fn &optional ARG)" t nil)
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2-imenu-extras" '("js2-imenu-")))
;;;***
;;;### (autoloads nil "js2-mode" "js2-mode.el" (0 0 0 0))
;;; Generated autoloads from js2-mode.el
(autoload 'js2-highlight-unused-variables-mode "js2-mode" "\
Toggle highlight of unused variables.
\(fn &optional ARG)" t nil)
(autoload 'js2-minor-mode "js2-mode" "\
Minor mode for running js2 as a background linter.
This allows you to use a different major mode for JavaScript editing,
such as `js-mode', while retaining the asynchronous error/warning
highlighting features of `js2-mode'.
\(fn &optional ARG)" t nil)
(autoload 'js2-mode "js2-mode" "\
Major mode for editing JavaScript code.
\(fn)" t nil)
(autoload 'js2-jsx-mode "js2-mode" "\
Major mode for editing JSX code in Emacs 26 and earlier.
To edit JSX code in Emacs 27, use `js-mode' as your major mode
with `js2-minor-mode' enabled.
To customize the indentation for this mode, set the SGML offset
variables (`sgml-basic-offset' et al) locally, like so:
(defun set-jsx-indentation ()
(setq-local sgml-basic-offset js2-basic-offset))
(add-hook \\='js2-jsx-mode-hook #\\='set-jsx-indentation)
\(fn)" t nil)
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2-mode" '("js2-")))
;;;***
;;;### (autoloads nil "js2-old-indent" "js2-old-indent.el" (0 0 0
;;;;;; 0))
;;; Generated autoloads from js2-old-indent.el
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2-old-indent" '("js2-")))
;;;***
;;;### (autoloads nil nil ("js2-mode-pkg.el") (0 0 0 0))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; js2-mode-autoloads.el ends here

View file

@ -0,0 +1,15 @@
(define-package "js2-mode" "20190815.1327" "Improved JavaScript editing mode"
'((emacs "24.1")
(cl-lib "0.5"))
:keywords
'("languages" "javascript")
:authors
'(("Steve Yegge" . "steve.yegge@gmail.com")
("mooz" . "stillpedant@gmail.com")
("Dmitry Gutov" . "dgutov@yandex.ru"))
:maintainer
'("Steve Yegge" . "steve.yegge@gmail.com")
:url "https://github.com/mooz/js2-mode/")
;; Local Variables:
;; no-byte-compile: t
;; End:

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -0,0 +1,712 @@
;;; js2-old-indent.el --- Indentation code kept for compatibility
;; Copyright (C) 2015 Free Software Foundation, Inc.
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; All features of this indentation code have been ported to Emacs's
;; built-in `js-mode' by now, so we derive from it. An older
;; commentary follows.
;; This code is kept for Emacs 24.5 and ealier.
;; This indenter is based on Karl Landström's "javascript.el" indenter.
;; Karl cleverly deduces that the desired indentation level is often a
;; function of paren/bracket/brace nesting depth, which can be determined
;; quickly via the built-in `parse-partial-sexp' function. His indenter
;; then does some equally clever checks to see if we're in the context of a
;; substatement of a possibly braceless statement keyword such as if, while,
;; or finally. This approach yields pretty good results.
;; The indenter is often "wrong", however, and needs to be overridden.
;; The right long-term solution is probably to emulate (or integrate
;; with) cc-engine, but it's a nontrivial amount of coding. Even when a
;; parse tree from `js2-parse' is present, which is not true at the
;; moment the user is typing, computing indentation is still thousands
;; of lines of code to handle every possible syntactic edge case.
;; In the meantime, the compromise solution is that we offer a "bounce
;; indenter", configured with `js2-bounce-indent-p', which cycles the
;; current line indent among various likely guess points. This approach
;; is far from perfect, but should at least make it slightly easier to
;; move the line towards its desired indentation when manually
;; overriding Karl's heuristic nesting guesser.
;; I've made miscellaneous tweaks to Karl's code to handle some Ecma
;; extensions such as `let' and Array comprehensions. Major kudos to
;; Karl for coming up with the initial approach, which packs a lot of
;; punch for so little code. -- Steve
;;; Code:
(require 'sgml-mode)
(defvar js2-language-version)
(declare-function js2-backward-sws "js2-mode")
(declare-function js2-forward-sws "js2-mode")
(declare-function js2-same-line "js2-mode")
(defcustom js2-basic-offset (if (and (boundp 'c-basic-offset)
(numberp c-basic-offset))
c-basic-offset
4)
"Number of spaces to indent nested statements.
Similar to `c-basic-offset'."
:group 'js2-mode
:safe 'integerp
:type 'integer)
(defcustom js2-pretty-multiline-declarations t
"Non-nil to line up multiline declarations vertically:
var a = 10,
b = 20,
c = 30;
If the value is t, and the first assigned value in the
declaration is a function/array/object literal spanning several
lines, it won't be indented additionally:
var o = { var bar = 2,
foo: 3 vs. o = {
}, foo: 3
bar = 2; };
If the value is `all', it will always be indented additionally:
var o = {
foo: 3
};
var o = {
foo: 3
},
bar = 2;
If the value is `dynamic', it will be indented additionally only
if the declaration contains more than one variable:
var o = {
foo: 3
};
var o = {
foo: 3
},
bar = 2;"
:group 'js2-mode
:safe 'symbolp
:type 'symbol)
(defcustom js2-indent-switch-body nil
"When nil, case labels are indented on the same level as the
containing switch statement. Otherwise, all lines inside
switch statement body are indented one additional level."
:type 'boolean
:safe 'booleanp
:group 'js2-mode)
(defconst js2-possibly-braceless-keywords-re
(concat "else[ \t]+if\\|for[ \t]+each\\|"
(regexp-opt '("catch" "do" "else" "finally" "for" "if"
"try" "while" "with" "let")))
"Regular expression matching keywords that are optionally
followed by an opening brace.")
(defconst js2-indent-operator-re
(concat "[-+*/%<>&^|?:.]\\([^-+*/.]\\|$\\)\\|!?=\\|"
(regexp-opt '("in" "instanceof") 'symbols))
"Regular expression matching operators that affect indentation
of continued expressions.")
(defconst js2-declaration-keyword-re
(regexp-opt '("var" "let" "const") 'symbols)
"Regular expression matching variable declaration keywords.")
(defun js2-re-search-forward-inner (regexp &optional bound count)
"Auxiliary function for `js2-re-search-forward'."
(let (parse saved-point)
(while (> count 0)
(re-search-forward regexp bound)
(setq parse (if saved-point
(parse-partial-sexp saved-point (point))
(syntax-ppss (point))))
(cond ((nth 3 parse)
(re-search-forward
(concat "\\(\\=\\|[^\\]\\|^\\)" (string (nth 3 parse)))
(save-excursion (end-of-line) (point)) t))
((nth 7 parse)
(forward-line))
((or (nth 4 parse)
(and (eq (char-before) ?\/) (eq (char-after) ?\*)))
(re-search-forward "\\*/"))
(t
(setq count (1- count))))
(setq saved-point (point))))
(point))
(defun js2-re-search-forward (regexp &optional bound noerror count)
"Search forward but ignore strings and comments.
Invokes `re-search-forward' but treats the buffer as if strings
and comments have been removed."
(let ((saved-point (point)))
(condition-case err
(cond ((null count)
(js2-re-search-forward-inner regexp bound 1))
((< count 0)
(js2-re-search-backward-inner regexp bound (- count)))
((> count 0)
(js2-re-search-forward-inner regexp bound count)))
(search-failed
(goto-char saved-point)
(unless noerror
(error (error-message-string err)))))))
(defun js2-re-search-backward-inner (regexp &optional bound count)
"Auxiliary function for `js2-re-search-backward'."
(let (parse)
(while (> count 0)
(re-search-backward regexp bound)
(setq parse (syntax-ppss (point)))
(cond ((nth 3 parse)
(re-search-backward
(concat "\\([^\\]\\|^\\)" (string (nth 3 parse)))
(line-beginning-position) t))
((nth 7 parse)
(goto-char (nth 8 parse)))
((or (nth 4 parse)
(and (eq (char-before) ?/) (eq (char-after) ?*)))
(re-search-backward "/\\*"))
(t
(setq count (1- count))))))
(point))
(defun js2-re-search-backward (regexp &optional bound noerror count)
"Search backward but ignore strings and comments.
Invokes `re-search-backward' but treats the buffer as if strings
and comments have been removed."
(let ((saved-point (point)))
(condition-case err
(cond ((null count)
(js2-re-search-backward-inner regexp bound 1))
((< count 0)
(js2-re-search-forward-inner regexp bound (- count)))
((> count 0)
(js2-re-search-backward-inner regexp bound count)))
(search-failed
(goto-char saved-point)
(unless noerror
(error (error-message-string err)))))))
(defun js2-looking-at-operator-p ()
"Return non-nil if text after point is a non-comma operator."
(defvar js2-mode-identifier-re)
(and (looking-at js2-indent-operator-re)
(or (not (eq (char-after) ?:))
(save-excursion
(and (js2-re-search-backward "[?:{]\\|\\_<case\\_>" nil t)
(eq (char-after) ??))))
(not (and
(eq (char-after) ?/)
(save-excursion
(eq (nth 3 (syntax-ppss)) ?/))))
(not (and
(eq (char-after) ?*)
;; Generator method (possibly using computed property).
(looking-at (concat "\\* *\\(?:\\[\\|"
js2-mode-identifier-re
" *(\\)"))
(save-excursion
(js2-backward-sws)
;; We might misindent some expressions that would
;; return NaN anyway. Shouldn't be a problem.
(memq (char-before) '(?, ?} ?{)))))))
(defun js2-continued-expression-p ()
"Return non-nil if the current line continues an expression."
(save-excursion
(back-to-indentation)
(if (js2-looking-at-operator-p)
(or (not (memq (char-after) '(?- ?+)))
(progn
(forward-comment (- (point)))
(not (memq (char-before) '(?, ?\[ ?\()))))
(forward-comment (- (point)))
(or (bobp) (backward-char))
(when (js2-looking-at-operator-p)
(backward-char)
(not (looking-at "\\*\\|\\+\\+\\|--\\|/[/*]"))))))
(defun js2-end-of-do-while-loop-p ()
"Return non-nil if word after point is `while' of a do-while
statement, else returns nil. A braceless do-while statement
spanning several lines requires that the start of the loop is
indented to the same column as the current line."
(interactive)
(save-excursion
(when (looking-at "\\s-*\\_<while\\_>")
(if (save-excursion
(skip-chars-backward "[ \t\n]*}")
(looking-at "[ \t\n]*}"))
(save-excursion
(backward-list) (backward-word 1) (looking-at "\\_<do\\_>"))
(js2-re-search-backward "\\_<do\\_>" (point-at-bol) t)
(or (looking-at "\\_<do\\_>")
(let ((saved-indent (current-indentation)))
(while (and (js2-re-search-backward "^[ \t]*\\_<" nil t)
(/= (current-indentation) saved-indent)))
(and (looking-at "[ \t]*\\_<do\\_>")
(not (js2-re-search-forward
"\\_<while\\_>" (point-at-eol) t))
(= (current-indentation) saved-indent))))))))
(defun js2-multiline-decl-indentation ()
"Return the declaration indentation column if the current line belongs
to a multiline declaration statement. See `js2-pretty-multiline-declarations'."
(let (forward-sexp-function ; use Lisp version
at-opening-bracket)
(save-excursion
(back-to-indentation)
(when (not (looking-at js2-declaration-keyword-re))
(when (looking-at js2-indent-operator-re)
(goto-char (match-end 0))) ; continued expressions are ok
(while (and (not at-opening-bracket)
(not (bobp))
(let ((pos (point)))
(save-excursion
(js2-backward-sws)
(or (eq (char-before) ?,)
(and (not (eq (char-before) ?\;))
(prog2 (skip-syntax-backward ".")
(looking-at js2-indent-operator-re)
(js2-backward-sws))
(not (eq (char-before) ?\;)))
(js2-same-line pos)))))
(condition-case _
(backward-sexp)
(scan-error (setq at-opening-bracket t))))
(when (looking-at js2-declaration-keyword-re)
(goto-char (match-end 0))
(1+ (current-column)))))))
(defun js2-ctrl-statement-indentation ()
"Return the proper indentation of current line if it is a control statement.
Returns an indentation if this line starts the body of a control
statement without braces, else returns nil."
(let (forward-sexp-function)
(save-excursion
(back-to-indentation)
(when (and (not (js2-same-line (point-min)))
(not (looking-at "{"))
(js2-re-search-backward "[[:graph:]]" nil t)
(not (looking-at "[{([]"))
(progn
(forward-char)
(when (= (char-before) ?\))
;; scan-sexps sometimes throws an error
(ignore-errors (backward-sexp))
(skip-chars-backward " \t" (point-at-bol)))
(let ((pt (point)))
(back-to-indentation)
(when (looking-at "}[ \t]*")
(goto-char (match-end 0)))
(and (looking-at js2-possibly-braceless-keywords-re)
(= (match-end 0) pt)
(not (js2-end-of-do-while-loop-p))))))
(+ (current-indentation) js2-basic-offset)))))
(defun js2-indent-in-array-comp (parse-status)
"Return non-nil if we think we're in an array comprehension.
In particular, return the buffer position of the first `for' kwd."
(let ((bracket (nth 1 parse-status))
(end (point)))
(when bracket
(save-excursion
(goto-char bracket)
(when (looking-at "\\[")
(forward-char 1)
(js2-forward-sws)
(if (looking-at "[[{]")
(let (forward-sexp-function) ; use Lisp version
(forward-sexp) ; skip destructuring form
(js2-forward-sws)
(if (and (/= (char-after) ?,) ; regular array
(looking-at "for"))
(match-beginning 0)))
;; to skip arbitrary expressions we need the parser,
;; so we'll just guess at it.
(if (and (> end (point)) ; not empty literal
(re-search-forward "[^,]]* \\(for\\) " end t)
;; not inside comment or string literal
(let ((state (parse-partial-sexp bracket (point))))
(and (= 1 (car state))
(not (nth 8 state)))))
(match-beginning 1))))))))
(defun js2-array-comp-indentation (parse-status for-kwd)
(if (js2-same-line for-kwd)
;; first continuation line
(save-excursion
(goto-char (nth 1 parse-status))
(forward-char 1)
(skip-chars-forward " \t")
(current-column))
(save-excursion
(goto-char for-kwd)
(current-column))))
(defun js2-maybe-goto-declaration-keyword-end (bracket)
"Helper function for `js2-proper-indentation'.
Depending on the value of `js2-pretty-multiline-declarations',
move point to the end of a variable declaration keyword so that
indentation is aligned to that column."
(cond
((eq js2-pretty-multiline-declarations 'all)
(when (looking-at js2-declaration-keyword-re)
(goto-char (1+ (match-end 0)))))
((eq js2-pretty-multiline-declarations 'dynamic)
(let (declaration-keyword-end
at-closing-bracket-p
comma-p)
(when (looking-at js2-declaration-keyword-re)
;; Preserve the match data lest it somehow be overridden.
(setq declaration-keyword-end (match-end 0))
(save-excursion
(goto-char bracket)
(setq at-closing-bracket-p
;; Handle scan errors gracefully.
(condition-case nil
(progn
;; Use the regular `forward-sexp-function' because the
;; normal one for this mode uses the AST.
(let (forward-sexp-function)
(forward-sexp))
t)
(error nil)))
(when at-closing-bracket-p
(js2-forward-sws)
(setq comma-p (looking-at-p ","))))
(when comma-p
(goto-char (1+ declaration-keyword-end))))))))
(cl-defun js2-proper-indentation (parse-status)
"Return the proper indentation for the current line."
(save-excursion
(back-to-indentation)
(when (nth 4 parse-status)
(cl-return-from js2-proper-indentation (js2--comment-indent parse-status)))
(let* ((at-closing-bracket (looking-at "[]})]"))
(same-indent-p (or at-closing-bracket
(looking-at "\\_<case\\_>[^:]")
(and (looking-at "\\_<default:")
(save-excursion
(js2-backward-sws)
(not (memq (char-before) '(?, ?{)))))))
(continued-expr-p (js2-continued-expression-p))
(declaration-indent (and js2-pretty-multiline-declarations
(js2-multiline-decl-indentation)))
(bracket (nth 1 parse-status))
beg indent)
(cond
;; indent array comprehension continuation lines specially
((and bracket
(>= js2-language-version 170)
(not (js2-same-line bracket))
(setq beg (js2-indent-in-array-comp parse-status))
(>= (point) (save-excursion
(goto-char beg)
(point-at-bol)))) ; at or after first loop?
(js2-array-comp-indentation parse-status beg))
((js2-ctrl-statement-indentation))
((and declaration-indent continued-expr-p)
(+ declaration-indent js2-basic-offset))
(declaration-indent)
(bracket
(goto-char bracket)
(cond
((looking-at "[({[][ \t]*\\(/[/*]\\|$\\)")
(when (save-excursion (skip-chars-backward " \t\n)")
(looking-at ")"))
(backward-list))
(back-to-indentation)
(js2-maybe-goto-declaration-keyword-end bracket)
(setq indent
(cond (same-indent-p
(current-column))
(continued-expr-p
(+ (current-column) (* 2 js2-basic-offset)))
(t
(+ (current-column) js2-basic-offset))))
(if (and js2-indent-switch-body
(not at-closing-bracket)
(looking-at "\\_<switch\\_>"))
(+ indent js2-basic-offset)
indent))
(t
(unless same-indent-p
(forward-char)
(skip-chars-forward " \t"))
(current-column))))
(continued-expr-p js2-basic-offset)
(t 0)))))
(defun js2--comment-indent (parse-status)
"Indentation inside a multi-line block comment continuation line."
(save-excursion
(goto-char (nth 8 parse-status))
(if (looking-at "/\\*")
(+ 1 (current-column))
0)))
(defun js2-indent-line (&optional bounce-backwards)
"Indent the current line as JavaScript source text."
(interactive)
(let (parse-status offset
;; Don't whine about errors/warnings when we're indenting.
;; This has to be set before calling parse-partial-sexp below.
(inhibit-point-motion-hooks t))
(setq parse-status (save-excursion
(syntax-ppss (point-at-bol)))
offset (- (point) (save-excursion
(back-to-indentation)
(point))))
;; Don't touch multiline strings.
(unless (nth 3 parse-status)
(indent-line-to (js2-proper-indentation parse-status))
(when (cl-plusp offset)
(forward-char offset)))))
;;; JSX Indentation
;; The following JSX indentation code is copied basically verbatim from js.el at
;; 958da7f, except that the prefixes on the functions/variables are changed.
(defsubst js2--jsx-find-before-tag ()
"Find where JSX starts.
Assume JSX appears in the following instances:
- Inside parentheses, when returned or as the first argument
to a function, and after a newline
- When assigned to variables or object properties, but only
on a single line
- As the N+1th argument to a function
This is an optimized version of (re-search-backward \"[(,]\n\"
nil t), except set point to the end of the match. This logic
executes up to the number of lines in the file, so it should be
really fast to reduce that impact."
(let (pos)
(while (and (> (point) (point-min))
(not (progn
(end-of-line 0)
(when (or (eq (char-before) 40) ; (
(eq (char-before) 44)) ; ,
(setq pos (1- (point))))))))
pos))
(defconst js2--jsx-end-tag-re
(concat "</" sgml-name-re ">\\|/>")
"Find the end of a JSX element.")
(defconst js2--jsx-after-tag-re "[),]"
"Find where JSX ends.
This complements the assumption of where JSX appears from
`js--jsx-before-tag-re', which see.")
(defun js2--jsx-indented-element-p ()
"Determine if/how the current line should be indented as JSX.
Return `first' for the first JSXElement on its own line.
Return `nth' for subsequent lines of the first JSXElement.
Return `expression' for an embedded JS expression.
Return `after' for anything after the last JSXElement.
Return nil for non-JSX lines.
Currently, JSX indentation supports the following styles:
- Single-line elements (indented like normal JS):
var element = <div></div>;
- Multi-line elements (enclosed in parentheses):
function () {
return (
<div>
<div></div>
</div>
);
}
- Function arguments:
React.render(
<div></div>,
document.querySelector('.root')
);"
(let ((current-pos (point))
(current-line (line-number-at-pos))
last-pos
before-tag-pos before-tag-line
tag-start-pos tag-start-line
tag-end-pos tag-end-line
after-tag-line
parens paren type)
(save-excursion
(and
;; Determine if we're inside a jsx element
(progn
(end-of-line)
(while (and (not tag-start-pos)
(setq last-pos (js2--jsx-find-before-tag)))
(while (forward-comment 1))
(when (= (char-after) 60) ; <
(setq before-tag-pos last-pos
tag-start-pos (point)))
(goto-char last-pos))
tag-start-pos)
(progn
(setq before-tag-line (line-number-at-pos before-tag-pos)
tag-start-line (line-number-at-pos tag-start-pos))
(and
;; A "before" line which also starts an element begins with js, so
;; indent it like js
(> current-line before-tag-line)
;; Only indent the jsx lines like jsx
(>= current-line tag-start-line)))
(cond
;; Analyze bounds if there are any
((progn
(while (and (not tag-end-pos)
(setq last-pos (re-search-forward js2--jsx-end-tag-re nil t)))
(while (forward-comment 1))
(when (looking-at js2--jsx-after-tag-re)
(setq tag-end-pos last-pos)))
tag-end-pos)
(setq tag-end-line (line-number-at-pos tag-end-pos)
after-tag-line (line-number-at-pos after-tag-line))
(or (and
;; Ensure we're actually within the bounds of the jsx
(<= current-line tag-end-line)
;; An "after" line which does not end an element begins with
;; js, so indent it like js
(<= current-line after-tag-line))
(and
;; Handle another case where there could be e.g. comments after
;; the element
(> current-line tag-end-line)
(< current-line after-tag-line)
(setq type 'after))))
;; They may not be any bounds (yet)
(t))
;; Check if we're inside an embedded multi-line js expression
(cond
((not type)
(goto-char current-pos)
(end-of-line)
(setq parens (nth 9 (syntax-ppss)))
(while (and parens (not type))
(setq paren (car parens))
(cond
((and (>= paren tag-start-pos)
;; Curly bracket indicates the start of an embedded expression
(= (char-after paren) 123) ; {
;; The first line of the expression is indented like sgml
(> current-line (line-number-at-pos paren))
;; Check if within a closing curly bracket (if any)
;; (exclusive, as the closing bracket is indented like sgml)
(cond
((progn
(goto-char paren)
(ignore-errors (let (forward-sexp-function)
(forward-sexp))))
(< current-line (line-number-at-pos)))
(t)))
;; Indicate this guy will be indented specially
(setq type 'expression))
(t (setq parens (cdr parens)))))
t)
(t))
(cond
(type)
;; Indent the first jsx thing like js so we can indent future jsx things
;; like sgml relative to the first thing
((= current-line tag-start-line) 'first)
('nth))))))
(defmacro js2--as-sgml (&rest body)
"Execute BODY as if in sgml-mode."
`(with-syntax-table sgml-mode-syntax-table
(let (forward-sexp-function
parse-sexp-lookup-properties)
,@body)))
(defun js2--expression-in-sgml-indent-line ()
"Indent the current line as JavaScript or SGML (whichever is farther)."
(let* (indent-col
(savep (point))
;; Don't whine about errors/warnings when we're indenting.
;; This has to be set before calling parse-partial-sexp below.
(inhibit-point-motion-hooks t)
(parse-status (save-excursion
(syntax-ppss (point-at-bol)))))
;; Don't touch multiline strings.
(unless (nth 3 parse-status)
(setq indent-col (save-excursion
(back-to-indentation)
(if (>= (point) savep) (setq savep nil))
(js2--as-sgml (sgml-calculate-indent))))
(if (null indent-col)
'noindent
;; Use whichever indentation column is greater, such that the sgml
;; column is effectively a minimum
(setq indent-col (max (js2-proper-indentation parse-status)
(+ indent-col js2-basic-offset)))
(if savep
(save-excursion (indent-line-to indent-col))
(indent-line-to indent-col))))))
(defun js2-jsx-indent-line ()
"Indent the current line as JSX (with SGML offsets).
i.e., customize JSX element indentation with `sgml-basic-offset'
et al."
(interactive)
(let ((indentation-type (js2--jsx-indented-element-p)))
(cond
((eq indentation-type 'expression)
(js2--expression-in-sgml-indent-line))
((or (eq indentation-type 'first)
(eq indentation-type 'after))
;; Don't treat this first thing as a continued expression (often a "<" or
;; ">" causes this misinterpretation)
(cl-letf (((symbol-function #'js2-continued-expression-p) 'ignore))
(js2-indent-line)))
((eq indentation-type 'nth)
(js2--as-sgml (sgml-indent-line)))
(t (js2-indent-line)))))
(provide 'js2-old-indent)
;;; js2-old-indent.el ends here

Binary file not shown.

View file

@ -0,0 +1,123 @@
;;; js2-refactor-autoloads.el --- automatically extracted autoloads
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "js2-refactor" "js2-refactor.el" (0 0 0 0))
;;; Generated autoloads from js2-refactor.el
(autoload 'js2-refactor-mode "js2-refactor" "\
Minor mode providing JavaScript refactorings.
\(fn &optional ARG)" t nil)
(autoload 'js2r-add-keybindings-with-prefix "js2-refactor" "\
Add js2r keybindings using the prefix PREFIX.
\(fn PREFIX)" nil nil)
(autoload 'js2r-add-keybindings-with-modifier "js2-refactor" "\
Add js2r keybindings using the modifier MODIFIER.
\(fn MODIFIER)" nil nil)
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2-refactor" '("js2")))
;;;***
;;;### (autoloads nil "js2r-conditionals" "js2r-conditionals.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from js2r-conditionals.el
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2r-conditionals" '("js2r-ternary-to-if")))
;;;***
;;;### (autoloads nil "js2r-conveniences" "js2r-conveniences.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from js2r-conveniences.el
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2r-conveniences" '("js2r-" "move-line-")))
;;;***
;;;### (autoloads nil "js2r-formatting" "js2r-formatting.el" (0 0
;;;;;; 0 0))
;;; Generated autoloads from js2r-formatting.el
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2r-formatting" '("js2r-")))
;;;***
;;;### (autoloads nil "js2r-functions" "js2r-functions.el" (0 0 0
;;;;;; 0))
;;; Generated autoloads from js2r-functions.el
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2r-functions" '("js2r-")))
;;;***
;;;### (autoloads nil "js2r-helpers" "js2r-helpers.el" (0 0 0 0))
;;; Generated autoloads from js2r-helpers.el
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2r-helpers" '("js2r--")))
;;;***
;;;### (autoloads nil "js2r-iife" "js2r-iife.el" (0 0 0 0))
;;; Generated autoloads from js2r-iife.el
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2r-iife" '("js2r-")))
;;;***
;;;### (autoloads nil "js2r-paredit" "js2r-paredit.el" (0 0 0 0))
;;; Generated autoloads from js2r-paredit.el
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2r-paredit" '("js2r-")))
;;;***
;;;### (autoloads nil "js2r-vars" "js2r-vars.el" (0 0 0 0))
;;; Generated autoloads from js2r-vars.el
(autoload 'js2r-extract-var "js2r-vars" "\
\(fn)" t nil)
(autoload 'js2r-extract-let "js2r-vars" "\
\(fn)" t nil)
(autoload 'js2r-extract-const "js2r-vars" "\
\(fn)" t nil)
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2r-vars" '("js2r-" "current-line-contents")))
;;;***
;;;### (autoloads nil "js2r-wrapping" "js2r-wrapping.el" (0 0 0 0))
;;; Generated autoloads from js2r-wrapping.el
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "js2r-wrapping" '("js2r-")))
;;;***
;;;### (autoloads nil nil ("js2-refactor-pkg.el") (0 0 0 0))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; js2-refactor-autoloads.el ends here

View file

@ -0,0 +1,10 @@
(define-package "js2-refactor" "20190630.2108" "A JavaScript refactoring library for emacs."
'((js2-mode "20101228")
(s "1.9.0")
(multiple-cursors "1.0.0")
(dash "1.0.0")
(s "1.0.0")
(yasnippet "0.9.0.1")))
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -0,0 +1,238 @@
;;; js2-refactor.el --- The beginnings of a JavaScript refactoring library in emacs. -*- lexical-binding: t; -*-
;; Copyright (C) 2012-2014 Magnar Sveen
;; Copyright (C) 2015-2016 Magnar Sveen and Nicolas Petton
;; Author: Magnar Sveen <magnars@gmail.com>,
;; Nicolas Petton <nicolas@petton.fr>
;; Keywords: conveniences
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This is a collection of small refactoring functions to further the idea of a
;; JavaScript IDE in Emacs that started with js2-mode.
;; ## Installation
;; Start by installing the dependencies:
;; * js2-mode https://github.com/mooz/js2-mode/
;; * dash https://github.com/magnars/dash.el
;; * multiple-cursors https://github.com/magnars/multiple-cursors.el
;; It is also recommended to get
;; [expand-region](https://github.com/magnars/expand-region.el) to more easily mark
;; vars, method calls and functions for refactorings.
;; Then add this to your Emacs settings:
;; (require 'js2-refactor)
;; (add-hook 'js2-mode-hook #'js2-refactor-mode)
;; (js2r-add-keybindings-with-prefix "C-c C-m")
;; Note: I am working on a smoother installation path through package.el,
;; but I haven't had the time to whip this project into that sort of
;; structure - yet.
;; ## Usage
;; All refactorings start with `C-c C-m` and then a two-letter mnemonic shortcut.
;; * `ee` is `expand-node-at-point`: Expand bracketed list according to node type at point (array, object, function, call args).
;; * `cc` is `contract-node-at-point`: Contract bracketed list according to node type at point (array, object, function, call args).
;; * `ef` is `extract-function`: Extracts the marked expressions out into a new named function.
;; * `em` is `extract-method`: Extracts the marked expressions out into a new named method in an object literal.
;; * `tf` is `toggle-function-expression-and-declaration`: Toggle between function name() {} and var name = function ();
;; * `ta` is `toggle-arrow-function-and-expression`: Toggle between function expression to arrow function.
;; * `ts` is `toggle-function-async`: Toggle between an async and a regular function.
;; * `ip` is `introduce-parameter`: Changes the marked expression to a parameter in a local function.
;; * `lp` is `localize-parameter`: Changes a parameter to a local var in a local function.
;; * `wi` is `wrap-buffer-in-iife`: Wraps the entire buffer in an immediately invoked function expression
;; * `ig` is `inject-global-in-iife`: Creates a shortcut for a marked global by injecting it in the wrapping immediately invoked function expression
;; * `ag` is `add-to-globals-annotation`: Creates a `/*global */` annotation if it is missing, and adds the var at point to it.
;; * `ev` is `extract-var`: Takes a marked expression and replaces it with a var.
;; * `el` is `extract-var`: Takes a marked expression and replaces it with a let.
;; * `ec` is `extract-var`: Takes a marked expression and replaces it with a const.
;; * `iv` is `inline-var`: Replaces all instances of a variable with its initial value.
;; * `rv` is `rename-var`: Renames the variable on point and all occurrences in its lexical scope.
;; * `vt` is `var-to-this`: Changes local `var a` to be `this.a` instead.
;; * `ao` is `arguments-to-object`: Replaces arguments to a function call with an object literal of named arguments.
;; * `3i` is `ternary-to-if`: Converts ternary operator to if-statement.
;; * `sv` is `split-var-declaration`: Splits a `var` with multiple vars declared, into several `var` statements.
;; * `ss` is `split-string`: Splits a `string`.
;; * `st` is `string-to-template`: Converts a `string` into a template string.
;; * `uw` is `unwrap`: Replaces the parent statement with the selected region.
;; * `lt` is `log-this`: Adds a console.log() statement for what is at point (or region). With a prefix argument, use JSON pretty-printing.
;; * `dt` is `debug-this`: Adds a debug() statement for what is at point (or region).
;; * `sl` is `forward-slurp`: Moves the next statement into current function, if-statement, for-loop or while-loop.
;; * `ba` is `forward-barf`: Moves the last child out of current function, if-statement, for-loop or while-loop.
;; * `k` is `kill`: Kills to the end of the line, but does not cross semantic boundaries.
;; There are also some minor conveniences bundled:
;; * `C-S-down` and `C-S-up` moves the current line up or down. If the line is an
;; element in an object or array literal, it makes sure that the commas are
;; still correctly placed.
;; * `k` `kill-line`: Like `kill-line` but respecting the AST.
;; ## Todo
;; A list of some wanted improvements for the current refactorings.
;; * expand- and contract-array: should work recursively with nested object literals and nested arrays.
;; * expand- and contract-function: should deal better with nested object literals, array declarations, and statements terminated only by EOLs (without semicolons).
;; * wrap-buffer-in-iife: should skip comments and namespace initializations at buffer start.
;; * extract-variable: could end with a query-replace of the expression in its scope.
;; ## Contributions
;; * [Matt Briggs](https://github.com/mbriggs) contributed `js2r-add-to-globals-annotation`
;; * [Alex Chamberlain](https://github.com/apchamberlain) contributed contracting and expanding arrays and functions.
;; * [Nicolas Petton](https://github.com/NicolasPetton) contributed `js2r-kill`
;; Thanks!
;; ## Contribute
;; This project is still in its infancy, and everything isn't quite sorted out
;; yet. If you're eager to contribute, please add an issue here on github and we
;; can discuss your changes a little before diving into the elisp :-).
;; To fetch the test dependencies:
;; $ cd /path/to/multiple-cursors
;; $ git submodule init
;; $ git submodule update
;; Run the tests with:
;; $ ./util/ecukes/ecukes features
;;; Code:
(require 'js2-mode)
(require 'js2r-helpers)
(require 'js2r-formatting)
(require 'js2r-iife)
(require 'js2r-vars)
(require 'js2r-functions)
(require 'js2r-wrapping)
(require 'js2r-conditionals)
(require 'js2r-conveniences)
(require 'js2r-paredit)
(defvar js2-refactor-mode-map
(make-sparse-keymap)
"Keymap for js2-refactor.")
(defvar js2-refactor-keybinding-prefix
nil
"Store keybinding prefix used by js2-refactor.")
;;;###autoload
(define-minor-mode js2-refactor-mode
"Minor mode providing JavaScript refactorings."
:lighter " js2r"
:keymap js2-refactor-mode-map
(when js2-refactor-mode
(yas-minor-mode-on)))
;;; Settings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defgroup js2-refactor nil
"Minor mode providing JavaScript refactorings."
:group 'tools
:prefix "js2r-"
:link '(url-link :tag "Repository" "https://github.com/magnars/js2-refactor.el"))
(defcustom js2r-use-strict nil
"When non-nil, js2r inserts strict declarations in IIFEs."
:group 'js2-refactor
:type 'boolean)
(defcustom js2r-prefered-quote-type 1
"The prefered quote style for strings."
:group 'js2-refactor
:type '(choice (const :tag "Double" 1)
(const :tag "Single" 2)))
(defcustom js2r-always-insert-parens-around-arrow-function-params nil
"When non-nil, js2r always inserts parenthesis around arrow function params.
This only affects arrow functions with one parameter."
:group 'js2-refactor
:type 'boolean)
(defcustom js2r-prefer-let-over-var nil
"When non-nil, js2r uses let constructs over var when performing refactorings."
:group 'js2-refactor
:type 'boolean)
(defcustom js2r-log-before-point nil
"When non-nil, js2r inserts logging and debug statements before point.
When nil, logging and debug statements are inserted after point,
unless point is in a return statement."
:group 'js2-refactor
:type 'boolean)
;;; Keybindings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun js2r--add-keybindings (key-fn)
"Add js2r refactoring keybindings to `js2-mode-map' using KEY-FN to create each keybinding."
(define-key js2-refactor-mode-map (funcall key-fn "ee") #'js2r-expand-node-at-point)
(define-key js2-refactor-mode-map (funcall key-fn "cc") #'js2r-contract-node-at-point)
(define-key js2-refactor-mode-map (funcall key-fn "wi") #'js2r-wrap-buffer-in-iife)
(define-key js2-refactor-mode-map (funcall key-fn "ig") #'js2r-inject-global-in-iife)
(define-key js2-refactor-mode-map (funcall key-fn "ev") #'js2r-extract-var)
(define-key js2-refactor-mode-map (funcall key-fn "el") #'js2r-extract-let)
(define-key js2-refactor-mode-map (funcall key-fn "ec") #'js2r-extract-const)
(define-key js2-refactor-mode-map (funcall key-fn "iv") #'js2r-inline-var)
(define-key js2-refactor-mode-map (funcall key-fn "rv") #'js2r-rename-var)
(define-key js2-refactor-mode-map (funcall key-fn "vt") #'js2r-var-to-this)
(define-key js2-refactor-mode-map (funcall key-fn "ag") #'js2r-add-to-globals-annotation)
(define-key js2-refactor-mode-map (funcall key-fn "sv") #'js2r-split-var-declaration)
(define-key js2-refactor-mode-map (funcall key-fn "ss") #'js2r-split-string)
(define-key js2-refactor-mode-map (funcall key-fn "st") #'js2r-string-to-template)
(define-key js2-refactor-mode-map (funcall key-fn "ef") #'js2r-extract-function)
(define-key js2-refactor-mode-map (funcall key-fn "em") #'js2r-extract-method)
(define-key js2-refactor-mode-map (funcall key-fn "ip") #'js2r-introduce-parameter)
(define-key js2-refactor-mode-map (funcall key-fn "lp") #'js2r-localize-parameter)
(define-key js2-refactor-mode-map (funcall key-fn "tf") #'js2r-toggle-function-expression-and-declaration)
(define-key js2-refactor-mode-map (funcall key-fn "ta") #'js2r-toggle-arrow-function-and-expression)
(define-key js2-refactor-mode-map (funcall key-fn "ts") #'js2r-toggle-function-async)
(define-key js2-refactor-mode-map (funcall key-fn "ao") #'js2r-arguments-to-object)
(define-key js2-refactor-mode-map (funcall key-fn "uw") #'js2r-unwrap)
(define-key js2-refactor-mode-map (funcall key-fn "wl") #'js2r-wrap-in-for-loop)
(define-key js2-refactor-mode-map (funcall key-fn "3i") #'js2r-ternary-to-if)
(define-key js2-refactor-mode-map (funcall key-fn "lt") #'js2r-log-this)
(define-key js2-refactor-mode-map (funcall key-fn "dt") #'js2r-debug-this)
(define-key js2-refactor-mode-map (funcall key-fn "sl") #'js2r-forward-slurp)
(define-key js2-refactor-mode-map (funcall key-fn "ba") #'js2r-forward-barf)
(define-key js2-refactor-mode-map (funcall key-fn "k") #'js2r-kill)
(define-key js2-refactor-mode-map (kbd "<C-S-down>") #'js2r-move-line-down)
(define-key js2-refactor-mode-map (kbd "<C-S-up>") #'js2r-move-line-up))
;;;###autoload
(defun js2r-add-keybindings-with-prefix (prefix)
"Add js2r keybindings using the prefix PREFIX."
(setq js2-refactor-keybinding-prefix (read-kbd-macro prefix))
(js2r--add-keybindings (-partial #'js2r--key-pairs-with-prefix prefix)))
;;;###autoload
(defun js2r-add-keybindings-with-modifier (modifier)
"Add js2r keybindings using the modifier MODIFIER."
(js2r--add-keybindings (-partial #'js2r--key-pairs-with-modifier modifier)))
(provide 'js2-refactor)
;;; js2-refactor.el ends here

Binary file not shown.

View file

@ -0,0 +1,57 @@
;;; js2r-conditionals.el --- Conditional refactoring functions for js2-refactor -*- lexical-binding: t; -*-
;; Copyright (C) 2012-2014 Magnar Sveen
;; Copyright (C) 2015-2016 Magnar Sveen and Nicolas Petton
;; Author: Magnar Sveen <magnars@gmail.com>,
;; Nicolas Petton <nicolas@petton.fr>
;; Keywords: conveniences
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Code:
(require 's)
(require 'js2r-helpers)
(defun js2r-ternary-to-if ()
"Convert a ternary operator to an if-statement."
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(save-excursion
(let* ((ternary (js2r--closest 'js2-cond-node-p))
(test-expr (js2-node-string (js2-cond-node-test-expr ternary)))
(true-expr (js2-node-string (js2-cond-node-true-expr ternary)))
(false-expr (js2-node-string (js2-cond-node-false-expr ternary)))
(stmt (js2-node-parent-stmt ternary))
(stmt-pre (buffer-substring (js2-node-abs-pos stmt) (js2-node-abs-pos ternary)))
(stmt-post (s-trim (buffer-substring (js2-node-abs-end ternary) (js2-node-abs-end stmt))))
(beg (js2-node-abs-pos stmt)))
(goto-char beg)
(delete-char (js2-node-len stmt))
(insert "if (" test-expr ") {")
(newline)
(insert stmt-pre true-expr stmt-post)
(newline)
(insert "} else {")
(newline)
(insert stmt-pre false-expr stmt-post)
(newline)
(insert "}")
(indent-region beg (point))))))
(provide 'js2r-conditionals)
;;; js2r-conditionals ends here

Binary file not shown.

View file

@ -0,0 +1,242 @@
;;; js2r-conveniences.el --- Convenience functions for js2-refactor -*- lexical-binding: t; -*-
;; Copyright (C) 2012-2014 Magnar Sveen
;; Copyright (C) 2015-2016 Magnar Sveen and Nicolas Petton
;; Author: Magnar Sveen <magnars@gmail.com>,
;; Nicolas Petton <nicolas@petton.fr>
;; Keywords: conveniences
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Convenience functions for logging statements by inserting
;; `console.log' statements and moving lines respecting object
;; literal syntax.
;;; Code:
(require 'js2r-helpers)
(defun js2r-log-this (arg)
"Log of the node at point, adding a 'console.log()' statement.
With a prefix argument ARG, use JSON pretty-printing for logging."
(interactive "P")
(js2r--guard)
(js2r--wait-for-parse
(let* ((log-info (js2r--figure-out-what-to-log-where))
(stmt (car log-info))
(pos (cdr log-info)))
(save-excursion
(goto-char pos)
(when (looking-at "[;{]")
(forward-char 1))
(newline-and-indent)
(if arg
(progn (insert "console.log(\"" stmt " = \");")
(newline-and-indent)
(insert "console.dir(" stmt ", { depth:null, colors: true });"))
(insert "console.log(\"" stmt " = \", " stmt ");"))))))
(defun js2r-debug-this ()
"Debug the node at point, adding a 'debug()' statement."
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(let* ((log-info (js2r--figure-out-what-to-log-where))
(stmt (car log-info))
(pos (cdr log-info)))
(save-excursion
(goto-char pos)
(when (looking-at "[;{]")
(forward-char 1))
(newline-and-indent)
(insert "debug(" (js2r--wrap-text stmt " = %s") ", " stmt ");")))))
(defun js2r--figure-out-what-to-log-where ()
"Return a dotted pair containing the statement to log and the
position where the log should be inserted."
(let ((parent-stmt (js2-node-parent-stmt (js2-node-at-point))))
(if (use-region-p)
(cons (buffer-substring (region-beginning) (region-end))
(js2r--find-suitable-log-position-around parent-stmt))
(let* ((node (js2r--name-node-at-point))
(parent (js2-node-parent node)))
(cond
((js2-function-node-p parent)
(cons (js2-name-node-name node)
(js2-node-abs-pos (js2-function-node-body parent))))
((js2-prop-get-node-p parent)
(cons (buffer-substring (js2-node-abs-pos parent) (js2-node-abs-end parent))
(js2r--find-suitable-log-position-around parent-stmt)))
(:else
(cons (js2-name-node-name node)
(js2r--find-suitable-log-position-around parent-stmt))))))))
(defun js2r--find-suitable-log-position-around (parent-stmt)
"Return the position close to PARENT-STMT where the log statement should be inserted."
(if (or js2r-log-before-point (js2-return-node-p parent-stmt))
(save-excursion
(goto-char (js2-node-abs-pos parent-stmt))
(skip-chars-backward " \t\n\r") ; Can't use skip-syntax-backward since \n is end-comment
(point))
(js2-node-abs-end parent-stmt)))
(defun js2r-split-string ()
"Split the string node at point. If the string is already split, join it instead."
(interactive)
(when (js2r--point-inside-string-p)
(let ((delimiter (js2r--string-delimiter (js2-node-at-point))))
(if (looking-back " \"")
(progn
(forward-char -2)
(insert " +")
(forward-char -2))
(if (looking-at (regexp-quote (format "%s + %s" delimiter delimiter)))
(delete-char 5)
(insert (format "%s + %s" delimiter delimiter)))))))
(defun js2r-string-to-template ()
"Convert the string at point into a template string."
(interactive)
(let ((node (js2-node-at-point)))
(when (js2-string-node-p node)
(let* ((start (js2-node-abs-pos node))
(end (+ start (js2-node-len node))))
(when (memq (char-after start) '(?' ?\"))
(save-excursion
(goto-char end) (delete-char -1) (insert "`")
(goto-char start) (delete-char 1) (insert "`")
(perform-replace "`" "\\`" nil nil nil nil nil (1+ start) (1- end))))))))
(defun js2r--string-delimiter (node)
"Return the delimiter character of the string node NODE.
It can be a single or double quote."
(save-excursion
(goto-char (js2-node-abs-pos node))
(char-to-string (following-char))))
(defun move-line-down ()
"Move the current line down one line."
(interactive)
(let ((col (current-column)))
(save-excursion
(forward-line)
(transpose-lines 1))
(forward-line)
(move-to-column col)))
(defun move-line-up ()
"Move the current line up one line."
(interactive)
(let ((col (current-column)))
(transpose-lines 1)
(forward-line -2)
(move-to-column col)))
(defun js2r-move-line-down ()
"Move the current line down one line.
Make sure commas are placed correctly when moving a line up or
down in an object or array literal."
(interactive)
(if (and (js2r--current-line-is-a-list-item)
(js2r--next-line-is-a-list-item))
(js2r--move-line-down-as-list-item)
(move-line-down))
(funcall indent-line-function))
(defun js2r-move-line-up ()
"Move the current line up one line.
Make sure commas are placed correctly when moving a line up or
down in an object or array literal."
(interactive)
(if (and (js2r--current-line-is-a-list-item)
(js2r--previous-line-is-a-list-item))
(js2r--move-line-up-as-list-item)
(move-line-up))
(funcall indent-line-function))
(defun js2r--current-line-is-prefixed-with-list-item-start ()
"Return whether the current line is prefixed with '{' or '['."
(save-excursion
(back-to-indentation)
(looking-back "\\({\\|\\[\\|,\\)\\(\s\\|\n\\|\t\\)*")))
(defun js2r--current-line-is-postfixed-with-list-item-end ()
"Return whether the current line is postfixed with '{' or '['."
(save-excursion
(end-of-line)
(or (looking-back ",\s*") ; line ends in comma
(looking-at "\\(\s\\|\n\\|\t\\)*\\(\\]\\|}\\)"))))
(defun js2r--current-line-is-a-list-item ()
"Return whether the current line contain an array or object literal."
(and (js2r--current-line-is-prefixed-with-list-item-start)
(js2r--current-line-is-postfixed-with-list-item-end)))
(defun js2r--next-line-is-a-list-item ()
"Return whether the current line contain an array or object literal."
(save-excursion
(forward-line)
(js2r--current-line-is-a-list-item)))
(defun js2r--previous-line-is-a-list-item ()
"Return whether the previous line contain an array or object literal, and only that."
(save-excursion
(forward-line -1)
(js2r--current-line-is-a-list-item)))
(defun js2r--current-line-has-comma ()
"Return whether the current line ends with a comma."
(save-excursion
(end-of-line)
(looking-back ",\s*")))
(defun js2r--previous-line-has-comma ()
"Return whether the previous line ends with a comma."
(save-excursion
(forward-line -1)
(js2r--current-line-has-comma)))
(defun js2r--move-line-down-as-list-item ()
"Move the current line containing a list literal down one line, and also move the comma."
(move-line-down)
(if (not (js2r--previous-line-has-comma))
(save-excursion
(end-of-line)
(delete-char -1)
(forward-line -1)
(end-of-line)
(insert ","))))
(defun js2r--move-line-up-as-list-item ()
"Move the current line containing a list literal up one line, and also move the comma."
(move-line-up)
(if (not (js2r--current-line-has-comma))
(save-excursion
(end-of-line)
(insert ",")
(forward-line)
(end-of-line)
(delete-char -1))))
(provide 'js2r-conveniences)
;;; js2r-conveniences.el ends here

Binary file not shown.

View file

@ -0,0 +1,251 @@
;;; js2r-formatting.el --- Private helper functions for formatting -*- lexical-binding: t; -*-
;; Copyright (C) 2012-2014 Magnar Sveen
;; Copyright (C) 2015-2016 Magnar Sveen and Nicolas Petton
;; Author: Magnar Sveen <magnars@gmail.com>,
;; Nicolas Petton <nicolas@petton.fr>
;; Keywords: conveniences
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Code:
(defun js2r--ensure-newline ()
(if (and (not (looking-at "\s*\n"))
(not (looking-back "\n\s*")))
(newline-and-indent)))
(defun js2r--ensure-just-one-space ()
(interactive)
(while (or (looking-at "\s*\n")
(looking-back "\n\s*"))
(when (looking-at "\n")
(delete-char 1))
(when (looking-back "\n\s")
(backward-char)
(delete-char -1))
(just-one-space))
(just-one-space))
(defmacro js2r--create-bracketed-whitespace-traverser
(name ws-fix-func looking-at-start-func
goto-closest-start-func subexpr-str)
"Build a function to expand or contract a given type of
bracketed expression, i.e., function body, object literal, or
array (any of which may be nested).
Parameters:
name: name of the function to be built
ws-fix-func: function to adjust whitespace at point
looking-at-start-func: returns t if point is at
the start of the bracketed
thing we want to act on
goto-closest-start-func: moves point if necessary
until looking-at-start-func
is true
subexpr-str: literal delimiter of parts of the
thing to be expanded or contracted"
`(defun ,name ()
(interactive)
(save-excursion
(if (not ,looking-at-start-func)
,goto-closest-start-func)
(let ((end (make-marker)))
(set-marker end (save-excursion
(forward-list)
(point)))
(forward-char)
,ws-fix-func
(while (< (point) end)
(while (js2r--point-inside-string-p)
(forward-char))
(when (looking-at ,subexpr-str)
(forward-char)
(unless (js2-comment-node-p (js2-node-at-point))
,ws-fix-func))
(if (looking-at "\\s(")
(forward-list)
(forward-char)))
(backward-char)
,ws-fix-func))))
(defun js2r--looking-at-object-start ()
(and (looking-at "{")
(not (looking-back ")[\s\n]*"))))
(defun js2r--goto-closest-object-start ()
(while (not (js2r--looking-at-object-start))
(if (eq (car (syntax-ppss)) 0)
(error "Cursor is not on an object")
(goto-char (nth 1 (syntax-ppss))))))
(js2r--create-bracketed-whitespace-traverser js2r-expand-object
(js2r--ensure-newline)
(js2r--looking-at-object-start)
(js2r--goto-closest-object-start)
",")
(js2r--create-bracketed-whitespace-traverser js2r-contract-object
(js2r--ensure-just-one-space)
(js2r--looking-at-object-start)
(js2r--goto-closest-object-start)
",")
(defun js2r--looking-at-array-start ()
(looking-at "\\["))
(defun js2r--goto-closest-array-start ()
(while (not (js2r--looking-at-array-start))
(if (eq (car (syntax-ppss)) 0)
(error "Cursor is not on an array")
(goto-char (nth 1 (syntax-ppss))))))
(js2r--create-bracketed-whitespace-traverser js2r-expand-array
(js2r--ensure-newline)
(js2r--looking-at-array-start)
(js2r--goto-closest-array-start)
",")
(js2r--create-bracketed-whitespace-traverser js2r-contract-array
(js2r--ensure-just-one-space)
(js2r--looking-at-array-start)
(js2r--goto-closest-array-start)
",")
;; (defun js2r--looking-at-function-start ()
;; (or
;; (and (looking-at "{")
;; (looking-back
;; ;; This horrible-looking regexp is actually pretty simple. It
;; ;; matches "function <optional_name> (<optional_parameters,...>)"
;; ;; allowing for whitespace. TODO: support Unicode in function and
;; ;; parameter names.
;; (concat "function[\s\n]*"
;; "\\\([a-zA-Z_$][a-zA-Z_$0-9]*[\s\n]*\\\)?"
;; "\(\\\([a-zA-Z_$][a-zA-Z_$0-9]*"
;; "[\s\n]*,[\s\n]*\\\)*[\s\n]*"
;; "\\\([a-zA-Z_$][a-zA-Z_$0-9]*[\s\n]*\\\)*"
;; "[\s\n]*\)[\s\n]*")))
;; ;; arrow functions
;; (and (looking-at "{")
;; (looking-back "=>[\s\n]*")
;; (not (js2r--point-inside-string-p)))))
(defun js2r--looking-at-function-start ()
"Return non-nil if the point is at the start of a function body."
(let* ((node (js2-node-at-point))
(parent (js2-node-parent node)))
(and (looking-at "{")
(js2-block-node-p node)
(js2-function-node-p parent))))
(defun js2r--goto-closest-function-start ()
(while (not (js2r--looking-at-function-start))
(if (eq (car (syntax-ppss)) 0)
(error "Cursor is not on a function body")
(goto-char (nth 1 (syntax-ppss))))))
(js2r--create-bracketed-whitespace-traverser js2r-expand-function
(js2r--ensure-newline)
(js2r--looking-at-function-start)
(js2r--goto-closest-function-start)
";")
;; TODO: It'd be great if js2r-contract-function could recognize
;; newlines that are implied statement terminators and insert
;; semicolons correctly, but that would probably mean not using the
;; same macro as the other "contract" function definitions.
(js2r--create-bracketed-whitespace-traverser js2r-contract-function
(js2r--ensure-just-one-space)
(js2r--looking-at-function-start)
(js2r--goto-closest-function-start)
";")
(defun js2r--looking-at-call-start ()
(looking-at "("))
(defun js2r--goto-closest-call-start ()
(while (not (js2r--looking-at-call-start))
(if (eq (car (syntax-ppss)) 0)
(error "Cursor is not on a call")
(goto-char (nth 1 (syntax-ppss))))))
(js2r--create-bracketed-whitespace-traverser js2r-expand-call-args
(js2r--ensure-newline)
(js2r--looking-at-call-start)
(js2r--goto-closest-call-start)
",")
(js2r--create-bracketed-whitespace-traverser js2r-contract-call-args
(js2r--ensure-just-one-space)
(js2r--looking-at-call-start)
(js2r--goto-closest-call-start)
",")
(defun js2r--expand-contract-node-at-point (&optional is-contract)
"Expand or contract bracketed list according to node type in point.
Currently working on array, object, function and call args node types.
With argument, contract closest expression, otherwise expand."
(let ((pos (point))
(array-start (point-max))
(object-start (point-max))
(function-start (point-max))
(call-start (point-max)))
(save-excursion
(ignore-errors
(js2r--goto-closest-array-start)
(setq array-start (- pos (point)))))
(save-excursion
(ignore-errors
(js2r--goto-closest-object-start)
(setq object-start (- pos (point)))))
(save-excursion
(ignore-errors
(js2r--goto-closest-function-start)
(setq function-start (- pos (point)))))
(save-excursion
(ignore-errors
(js2r--goto-closest-call-start)
(setq call-start (- pos (point)))))
(setq pos (-min (list array-start object-start function-start call-start)))
(when (= pos array-start)
(if is-contract
(js2r-contract-array)
(js2r-expand-array)))
(when (= pos object-start)
(if is-contract
(js2r-contract-object)
(js2r-expand-object)))
(when (= pos function-start)
(if is-contract
(js2r-contract-function)
(js2r-expand-function)))
(when (= pos call-start)
(if is-contract
(js2r-contract-call-args)
(js2r-expand-call-args)))))
(defun js2r-expand-node-at-point ()
"Expand bracketed list according to node type at point."
(interactive)
(js2r--expand-contract-node-at-point))
(defun js2r-contract-node-at-point ()
"Contract bracketed list according to node type at point."
(interactive)
(js2r--expand-contract-node-at-point t))
(provide 'js2r-formatting)
;;; js2-formatting.el ends here

Binary file not shown.

View file

@ -0,0 +1,537 @@
;;; js2r-functions.el --- Function manipulation functions for js2-refactor -*- lexical-binding: t; -*-
;; Copyright (C) 2012-2014 Magnar Sveen
;; Copyright (C) 2015-2016 Magnar Sveen and Nicolas Petton
;; Author: Magnar Sveen <magnars@gmail.com>,
;; Nicolas Petton <nicolas@petton.fr>
;; Keywords: conveniences
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Code:
(require 'dash)
(require 'yasnippet)
(require 'js2r-helpers)
(defun js2r-localize-parameter ()
"Turn parameter into local var in local function."
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(if (js2-name-node-p (js2-node-at-point))
(js2r--localize-parameter-pull)
(js2r--localize-parameter-push))))
(defun js2r--localize-parameter-push ()
(let* ((node (js2-node-at-point))
(arg-node (or (js2r--closest-node-where 'js2r--parent-is-call-node node)
(error "Place cursor on argument to localize")))
(call-node (js2-node-parent arg-node))
(value (js2-node-string arg-node))
(target (js2-call-node-target call-node))
(fn (if (js2-name-node-p target)
(js2r--local-fn-from-name-node target)
(error "Can only localize parameter for local functions")))
(usages (js2r--function-usages fn))
(index (car (--keep (when (eq arg-node it) it-index)
(js2-call-node-args call-node))))
(name (js2-name-node-name (nth index (js2-function-node-params fn)))))
(js2r--localize-parameter fn usages index name value)))
(defun js2r--localize-parameter-pull ()
(let* ((name-node (js2-node-at-point))
(name (if (js2-name-node-p name-node)
(js2-name-node-name name-node)
(error "Place cursor on parameter to localize")))
(fn (or (js2r--closest-node-where #'js2r--is-local-function name-node)
(error "Can only localize parameter in local functions")))
(index (or (js2r--param-index-for name fn)
(error "%S isn't a parameter to this function" name)))
(usages (js2r--function-usages fn))
(examples (-distinct (--map (js2r--argument index it) usages)))
(value (js2r--choose-one "Value: " examples)))
(js2r--localize-parameter fn usages index name value)))
(defun js2r--localize-parameter (fn usages index name value)
(save-excursion
(js2r--goto-fn-body-beg fn)
(save-excursion
(--each usages (js2r--remove-argument-at-index index it)))
(newline-and-indent)
(insert "var " name " = " value ";")
(js2r--remove-parameter-at-index index fn)))
(defun js2r--parent-is-call-node (node)
(js2-call-node-p (js2-node-parent node)))
(defun js2r--local-fn-from-name-node (name-node)
(->> name-node
(js2r--local-usages-of-name-node)
(-map #'js2-node-parent)
(-first #'js2-function-node-p)))
(defun js2r--param-index-for (name fn)
(car (--keep (when (equal name (js2-name-node-name it)) it-index)
(js2-function-node-params fn))))
(defun js2r--argument (index call-node)
(js2-node-string (nth index (js2-call-node-args call-node))))
(defun js2r--remove-parameter-at-index (index fn)
(js2r--delete-node-in-params (nth index (js2-function-node-params fn))))
(defun js2r--remove-argument-at-index (index call-node)
(js2r--delete-node-in-params (nth index (js2-call-node-args call-node))))
(defun js2r--delete-node-in-params (node)
(goto-char (js2-node-abs-pos node))
(delete-char (js2-node-len node))
(if (and (looking-back "(")
(looking-at ", "))
(delete-char 2)
(when (looking-back ", ")
(delete-char -2))))
(defun js2r--choose-one (prompt options)
(when options
(if (cdr options)
(completing-read prompt options)
(car options))))
(defun js2r-introduce-parameter ()
"Introduce a parameter in a local function."
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(if (use-region-p)
(js2r--introduce-parameter-between (region-beginning) (region-end))
(let ((node (js2r--closest-extractable-node)))
(js2r--introduce-parameter-between (js2-node-abs-pos node)
(js2-node-abs-end node))))))
(defun js2r--introduce-parameter-between (beg end)
(unless (js2r--single-complete-expression-between-p beg end)
(error "Can only introduce single, complete expressions as parameter"))
(let ((fn (js2r--closest-node-where #'js2r--is-local-function (js2-node-at-point))))
(unless fn
(error "Can only introduce parameter in local functions"))
(save-excursion
(let ((name (read-string "Parameter name: "))
(val (buffer-substring beg end))
(usages (js2r--function-usages fn)))
(goto-char beg)
(save-excursion
(-each usages (-partial #'js2r--add-parameter val)))
(delete-char (- end beg))
(insert name)
(js2r--add-parameter name fn)
(query-replace val name nil (js2-node-abs-pos fn) (js2r--fn-body-end fn))))))
(defun js2r--function-usages (fn)
(-map #'js2-node-parent (js2r--function-usages-name-nodes fn)))
(defun js2r--function-usages-name-nodes (fn)
(let ((name-node (or (js2-function-node-name fn)
(js2-var-init-node-target (js2-node-parent fn)))))
(remove name-node (js2r--local-usages-of-name-node name-node))))
(defun js2r--add-parameter (name node)
(save-excursion
(js2r--goto-closing-paren node)
(unless (looking-back "(")
(insert ", "))
(insert name)))
(defun js2r--goto-closing-paren (node)
(goto-char (js2-node-abs-pos node))
(search-forward "(")
(forward-char -1)
(forward-list)
(forward-char -1))
(defun js2r--goto-fn-body-beg (fn)
(goto-char (js2-node-abs-pos fn))
(search-forward "{"))
(defun js2r--fn-body-end (fn)
(save-excursion
(js2r--goto-fn-body-beg fn)
(forward-char -1)
(forward-list)
(point)))
(defun js2r--is-local-function (node)
(or (js2r--is-var-function-expression node)
(js2r--is-function-declaration node)))
(defun js2r--is-method (node)
(and (js2-function-node-p node)
(js2-object-prop-node-p (js2-node-parent node))))
(defun js2r--is-var-function-expression (node)
(and (js2-function-node-p node)
(js2-var-init-node-p (js2-node-parent node))))
(defun js2r--is-assigned-function-expression (node)
(and (js2-function-node-p node)
(js2-assign-node-p (js2-node-parent node))))
(defun js2r--is-function-declaration (node)
(let ((parent (js2-node-parent node)))
(and (js2-function-node-p node)
(not (js2-assign-node-p parent))
(not (js2-var-init-node-p parent))
(not (js2-object-prop-node-p parent)))))
(defun js2r-arguments-to-object ()
"Change from a list of arguments to a parameter object."
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(let ((node (js2-node-at-point)))
(unless (and (looking-at "(")
(or (js2-function-node-p node)
(js2-call-node-p node)
(js2-new-node-p node)))
(error "Place point right before the opening paren in the call or function"))
(-when-let* ((target (js2r--node-target node))
(fn (and (js2-name-node-p target)
(js2r--local-fn-from-name-node target))))
(setq node fn))
(if (js2-function-node-p node)
(js2r--arguments-to-object-for-function node)
(js2r--arguments-to-object-for-args-with-unknown-function (js2r--node-args node))))))
(defun js2r--arguments-to-object-for-function (function-node)
(let ((params (js2-function-node-params function-node)))
(when (null params)
(error "No params to convert"))
(save-excursion
(js2r--execute-changes
(-concat
;; change parameter list to just (params)
(list
(list :beg (+ (js2-node-abs-pos function-node) (js2-function-node-lp function-node))
:end (+ (js2-node-abs-pos function-node) (js2-function-node-rp function-node) 1)
:contents "(params)"))
;; add params. in front of function local param usages
(let* ((local-param-name-nodes (--mapcat (-> it
(js2-node-abs-pos)
(js2r--local-name-node-at-point)
(js2r--local-usages-of-name-node))
params))
(local-param-name-usages (--remove (js2-function-node-p (js2-node-parent it))
local-param-name-nodes))
(local-param-name-positions (-map #'js2-node-abs-pos local-param-name-usages)))
(--map
(list :beg it :end it :contents "params.")
local-param-name-positions))
;; update usages of function
(let ((names (-map #'js2-name-node-name params))
(usages (js2r--function-usages function-node)))
(--map
(js2r--changes/arguments-to-object it names)
usages)))))))
(defun js2r--changes/arguments-to-object (node names)
(let ((args (js2r--node-args node)))
(list :beg (+ (js2-node-abs-pos node) (js2r--node-lp node))
:end (+ (js2-node-abs-pos node) (js2r--node-rp node) 1)
:contents (js2r--create-object-with-arguments names args))))
(defun js2r--arguments-to-object-for-args-with-unknown-function (args)
(when (null args)
(error "No arguments to convert"))
(let ((names (--map-indexed
(format "${%d:%s}"
(1+ it-index)
(if (js2-name-node-p it)
(js2-name-node-name it)
"key"))
args)))
(yas-expand-snippet (js2r--create-object-with-arguments names args)
(point)
(save-excursion (forward-list) (point)))))
(defun js2r--create-object-with-arguments (names args)
(let (arg key result)
(--dotimes (length args)
(setq arg (nth it args))
(setq key (nth it names))
(setq result
(concat result
(format " %s: %s,\n"
key
(buffer-substring (js2-node-abs-pos arg)
(js2-node-abs-end arg))))))
(concat "({\n" (substring result 0 -2) "\n})")))
(defun js2r-extract-function (name)
"Extract a function from the closest statement expression from the point."
(interactive "sName of new function: ")
(js2r--extract-fn
name
(lambda ()
(unless (js2r--looking-at-function-declaration)
(goto-char (js2-node-abs-pos (js2r--closest #'js2-expr-stmt-node-p)))))
"%s(%s);"
"function %s(%s) {\n%s\n}\n\n"))
(defun js2r-extract-method (name)
"Extract a method from the closest statement expression from the point."
(interactive "sName of new method: ")
(let ((class-node (js2r--closest #'js2-class-node-p)))
(js2r--extract-fn
name
(unless class-node
(lambda ()
(goto-char (js2-node-abs-pos (js2r--closest #'js2-object-prop-node-p)))))
"this.%s(%s);"
(if class-node
"%s(%s) {\n%s\n}\n\n"
"%s: function (%s) {\n%s\n},\n\n"))))
(defun js2r--extract-fn (name goto-position call-template function-template)
(js2r--guard)
(js2r--wait-for-parse
(unless (use-region-p)
(error "Mark the expressions to extract first"))
(save-excursion
(let* ((parent (js2r--first-common-ancestor-in-region (region-beginning) (region-end)))
(block (js2r--closest-node-where #'js2-block-node-p parent))
(fn (js2r--closest-node-where #'js2-function-node-p block))
(exprs (js2r--marked-expressions-in-block block))
(vars (-mapcat #'js2r--name-node-decendants exprs))
(local (--filter (js2r--local-to-fn-p fn it) vars))
(names (-distinct (-map 'js2-name-node-name local)))
(declared-in-exprs (-map #'js2r--var-init-node-target-name (-mapcat #'js2r--var-init-node-decendants exprs)))
(outside-exprs (-difference (js2-block-node-kids block) exprs))
(outside-var-uses (-map #'js2-name-node-name (-mapcat #'js2r--name-node-decendants outside-exprs)))
(declared-in-but-used-outside (-intersection declared-in-exprs outside-var-uses))
(export-var (car declared-in-but-used-outside))
(params (-difference names declared-in-exprs))
(params-string (mapconcat #'identity (reverse params) ", "))
(first (car exprs))
(last (car (last exprs)))
(beg (js2-node-abs-pos (car exprs)))
(end (js2-node-abs-end last))
(contents (buffer-substring beg end)))
(goto-char beg)
(delete-region beg end)
(when (js2-return-node-p last)
(insert "return "))
(when export-var
(setq contents (concat contents "\nreturn " export-var ";"))
(insert "var " export-var " = "))
(insert (format call-template name params-string))
(goto-char (js2-node-abs-pos fn))
(when goto-position (funcall goto-position))
(let ((start (point)))
(insert (format function-template name params-string contents))
(indent-region start (1+ (point))))))))
(defun js2r--var-init-node-target-name (node)
(js2-name-node-name
(js2-var-init-node-target node)))
(defun js2r--function-around-region ()
(or
(js2r--closest-node-where #'js2-function-node-p
(js2r--first-common-ancestor-in-region
(region-beginning)
(region-end)))
(error "This only works when you mark stuff inside a function")))
(defun js2r--marked-expressions-in-block (fn)
(-select #'js2r--node-is-marked (js2-block-node-kids fn)))
(defun js2r--node-is-marked (node)
(and
(<= (region-beginning) (js2-node-abs-end node))
(>= (region-end) (js2-node-abs-pos node))))
(defun js2r--name-node-decendants (node)
(-select #'js2-name-node-p (js2r--decendants node)))
(defun js2r--var-init-node-decendants (node)
(-select #'js2-var-init-node-p (js2r--decendants node)))
(defun js2r--decendants (node)
(let (vars)
(js2-visit-ast node
(lambda (node end-p)
(unless end-p
(setq vars (cons node vars)))))
vars))
(defun js2r--local-to-fn-p (fn name-node)
(let* ((name (js2-name-node-name name-node))
(scope (js2-node-get-enclosing-scope name-node))
(scope (js2-get-defining-scope scope name)))
(eq fn scope)))
(defun js2r-toggle-arrow-function-and-expression ()
"Toggle between function expression to arrow function."
(interactive)
(save-excursion
(js2r--find-closest-function)
(cond ((js2r--arrow-function-p)
(js2r--transform-arrow-function-to-expression))
((and (js2r--function-start-p) (not (js2r--looking-at-function-declaration)))
(js2r--transform-function-expression-to-arrow))
(t (error "Can only toggle between function expressions and arrow function")))))
;; Toggle between function name() {} and var name = function ();
(defun js2r-toggle-function-expression-and-declaration ()
(interactive)
(save-excursion
(js2r--find-closest-function)
(cond
((js2r--looking-at-var-function-expression)
(when (js2r--arrow-function-p) (js2r--transform-arrow-function-to-expression))
(js2r--transform-function-expression-to-declaration))
((js2r--looking-at-function-declaration)
(js2r--transform-function-declaration-to-expression))
(t (error "Can only toggle between function declarations and free standing function expressions")))))
(defun js2r--arrow-function-p ()
(interactive)
(save-excursion
(ignore-errors
(js2r--find-closest-function)
(and (looking-at "(?[,[:space:][:word:]]*)?[[:space:]]*=>")
(not (js2r--point-inside-string-p))))))
(defun js2r--transform-arrow-function-to-expression ()
(when (js2r--arrow-function-p)
(let (has-parenthesis)
(save-excursion
(js2r--find-closest-function)
(let ((end (make-marker)))
(save-excursion
(search-forward "=>")
(set-marker end (js2-node-abs-end (js2-node-at-point))))
(setq has-parenthesis (looking-at "\\s-*("))
(insert "function ")
(if has-parenthesis
(forward-list)
(insert "("))
(search-forward "=>")
(delete-char -2)
(js2r--ensure-just-one-space)
(unless has-parenthesis
(backward-char 1)
(insert ")"))
(unless (looking-at "\\s-*{")
(js2r--ensure-just-one-space)
(insert "{ return ")
(js2r--ensure-just-one-space)
(goto-char (marker-position end))
(insert "; }")))))))
(defun js2r--transform-function-expression-to-arrow ()
(when (not (js2r--arrow-function-p))
(save-excursion
(js2r--find-closest-function)
(let ((pos (point))
(params
(js2-function-node-params (js2-node-at-point)))
parenthesis-start
parenthesis-end)
(when (js2r--looking-at-function-declaration)
(error "Can not convert function declarations to arrow function"))
(search-forward "(")
(backward-char 1)
(delete-region pos (point))
(setq parenthesis-start (point))
(forward-list)
(setq parenthesis-end (point))
(insert " => ")
(js2r--ensure-just-one-space)
(when (and (= 1 (length params))
(not js2r-always-insert-parens-around-arrow-function-params))
(goto-char parenthesis-end)
(backward-delete-char 1)
(goto-char parenthesis-start)
(delete-char 1))))))
(defun js2r--function-start-p()
(let* ((fn (js2r--closest #'js2-function-node-p)))
(and fn
(= (js2-node-abs-pos fn) (point)))))
(defun js2r--find-closest-function ()
(when (not (js2r--function-start-p))
(let* ((fn (js2r--closest #'js2-function-node-p)))
(goto-char (js2-node-abs-pos fn)))))
(defun js2r--looking-at-method ()
(and (js2r--function-start-p)
(looking-back ": ?")))
(defun js2r--looking-at-function-declaration ()
(and (js2r--function-start-p)
(looking-back "^ *")))
(defun js2r--looking-at-var-function-expression ()
(and (js2r--function-start-p)
(looking-back "^ *var[\s\n]*[a-z_$]+[\s\n]*=[\s\n]*")))
(defun js2r--transform-function-expression-to-declaration ()
(when (js2r--looking-at-var-function-expression)
(delete-char 9)
(forward-list)
(forward-list)
(delete-char 1)
(backward-list)
(backward-list)
(delete-backward-char 3)
(back-to-indentation)
(delete-char 4)
(insert "function ")))
(defun js2r--transform-function-declaration-to-expression ()
(when (js2r--looking-at-function-declaration)
(delete-char 9)
(insert "var ")
(search-forward "(")
(backward-char 1)
(insert " = function ")
(forward-list)
(forward-list)
(insert ";")))
(defun js2r-toggle-function-async ()
"Toggle the innermost function from sync to async."
(interactive)
(save-excursion
(js2r--find-closest-function)
(if (looking-back "async[[:space:]\n]+")
(delete-region (match-beginning 0) (match-end 0))
(insert "async "))))
(provide 'js2r-functions)
;;; js2-functions.el ends here

Binary file not shown.

View file

@ -0,0 +1,220 @@
;;; js2r-helpers.el --- Private helper functions for js2-refactor -*- lexical-binding: t; -*-
;; Copyright (C) 2012-2014 Magnar Sveen
;; Copyright (C) 2015-2016 Magnar Sveen and Nicolas Petton
;; Author: Magnar Sveen <magnars@gmail.com>,
;; Nicolas Petton <nicolas@petton.fr>
;; Keywords: conveniences
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Code:
(require 'dash)
(require 's)
(require 'js2-mode)
(defmacro js2r--wait-for-parse (&rest body)
"Evaluate BODY once the current buffer has been parsed."
`(js2-mode-wait-for-parse (lambda () ,@body)))
(defun js2r--wrap-text (&rest text)
"Wrap TEXT with the prefered quotes. The prefered quotes is set with `js2r-prefered-quote-type'."
(let ((prefered-quotes "\""))
(when (= 2 js2r-prefered-quote-type)
(setq prefered-quotes "'"))
(concat prefered-quotes (apply 'concat text) prefered-quotes)))
(defun js2r--fix-special-modifier-combinations (key)
(case key
("C-s-i" "s-TAB")
("C-s-m" "s-RET")
(otherwise key)))
(defun js2r--key-pairs-with-modifier (modifier keys)
(->> (string-to-list keys)
(--map (js2r--fix-special-modifier-combinations
(concat modifier (char-to-string it))))
(s-join " ")
(read-kbd-macro)))
(defun js2r--key-pairs-with-prefix (prefix keys)
(read-kbd-macro (concat prefix " " keys)))
(defun js2r--guard ()
(when js2-parsed-errors
(error "Can't refactor while buffer has parse errors")))
(defun js2r--current-quotes-char ()
"The char that is the current quote delimiter"
(nth 3 (syntax-ppss)))
(defalias 'js2r--point-inside-string-p 'js2r--current-quotes-char)
(defun js2r--closest-node-where (p node)
(if (or (null node)
(apply p node nil))
node
(js2r--closest-node-where p (js2-node-parent node))))
(defun js2r--closest (p)
(save-excursion
(cond
((bolp) (back-to-indentation))
((looking-at ";") (forward-char -1))
((looking-back ";") (forward-char -2))
((looking-back "}") (forward-char -1)))
(js2r--closest-node-where p (js2-node-at-point))))
(defun js2r--goto-and-delete-node (node)
(goto-char (js2-node-abs-pos node))
(delete-char (js2-node-len node)))
(defun js2r--path-to-root (node)
(when node
(cons node (js2r--path-to-root (js2-node-parent node)))))
(defun js2r--first-common-ancestor (node1 node2)
(if (eq node1 node2)
node1
(let ((path1 (reverse (js2r--path-to-root node1)))
(path2 (reverse (js2r--path-to-root node2)))
(last-common nil))
(while (eq (car path1) (car path2))
(setq last-common (car path1))
(setq path1 (cdr path1))
(setq path2 (cdr path2)))
last-common)))
(defun js2r--first-common-ancestor-in-region (beg end)
(js2r--first-common-ancestor (js2-node-at-point beg)
(js2-node-at-point end)))
;; abstract away node type on some common property getters
(defun js2r--node-target (node)
(cond
((js2-call-node-p node) (js2-call-node-target node))
((js2-new-node-p node) (js2-new-node-target node))
(:else nil)))
(defun js2r--node-args (node)
(cond
((js2-call-node-p node) (js2-call-node-args node))
((js2-new-node-p node) (js2-new-node-args node))
(:else nil)))
(defun js2r--node-lp (node)
(cond
((js2-call-node-p node) (js2-call-node-lp node))
((js2-new-node-p node) (js2-new-node-lp node))
(:else nil)))
(defun js2r--node-rp (node)
(cond
((js2-call-node-p node) (js2-call-node-rp node))
((js2-new-node-p node) (js2-new-node-rp node))
(:else nil)))
(defun js2r--node-kids (node)
(cond
((js2-function-node-p node) (js2-block-node-kids (js2-function-node-body node)))
((js2-if-node-p node) (js2-scope-kids (js2-if-node-then-part node)))
((js2-for-node-p node) (js2-block-node-kids (js2-for-node-body node)))
((js2-while-node-p node) (js2-block-node-kids (js2-while-node-body node)))))
;; finding expressions and arguments
(defun js2r--closest-extractable-node ()
"Return the most appropriate node the be extracted into a variable or paramter.
Lookup the closest expression node from the point, or the closest literal node instead.
If no node is found, signal an error."
(or (or (js2r--closest #'js2r--expression-p)
(js2r--closest #'js2r--literal-node-p))
(error "Cannot perform refactoring: Nothing to extract at point")))
(defun js2r--closest-stmt-node ()
"Return the closest standalone statement node.
Special care is taken for if branch nodes: if a statement node is
part of an if branch node (like 'else if' nodes), return the
parent node."
(let* ((node (js2-node-parent-stmt (js2-node-at-point)))
(parent (js2-node-parent node)))
(if (and (js2-if-node-p node)
(js2-if-node-p parent))
parent
node)))
(defun js2r--argument-p (node)
(let ((parent (js2-node-parent node)))
(and (js2-call-node-p parent)
(member node (js2-call-node-args parent)))))
(defun js2r--expression-p (node)
(or (js2-call-node-p node)
(js2r--argument-p node)
(and (js2-prop-get-node-p node)
(not (js2-call-node-p (js2-node-parent node))))))
(defun js2r--literal-node-p (node)
(or (js2-object-node-p node)
(js2-string-node-p node)
(js2-number-node-p node)
(js2r--boolean-node-p node)))
(defun js2r--boolean-node-p (node)
(let* ((beg (js2-node-abs-pos node))
(end (js2-node-abs-end node))
(content (buffer-substring beg end)))
(and (js2-keyword-node-p node)
(member content '("true" "false")))))
(defun js2r--single-complete-expression-between-p (beg end)
(let ((ancestor (js2r--first-common-ancestor-in-region beg (- end 1))))
(and (= beg (js2-node-abs-pos ancestor))
(= end (js2-node-abs-end ancestor)))))
;; executing a list of changes
;; ensures changes are executed from last to first
(defun js2r--by-end-descending (change1 change2)
(> (plist-get change1 :end)
(plist-get change2 :end)))
(defun js2r--any-overlapping-changes (sorted-changes)
(--any?
(let ((one (car it))
(two (cadr it)))
(< (plist-get one :beg)
(plist-get two :end)))
(-partition-in-steps 2 1 sorted-changes)))
(defun js2r--execute-changes (changes)
(when changes
(let ((sorted-changes (sort changes 'js2r--by-end-descending)))
(when (js2r--any-overlapping-changes sorted-changes)
(error "These changes overlap, cannot execute properly."))
(let ((abs-end (set-marker (make-marker) (1+ (plist-get (car sorted-changes) :end))))
(abs-beg (plist-get (car (last sorted-changes)) :beg)))
(--each sorted-changes
(goto-char (plist-get it :beg))
(delete-char (- (plist-get it :end) (plist-get it :beg)))
(insert (plist-get it :contents)))
(indent-region abs-beg abs-end)
(set-marker abs-end nil)))))
(provide 'js2r-helpers)
;;; js2-helpers.el ends here

Binary file not shown.

View file

@ -0,0 +1,92 @@
;;; js2r-iife.el --- IIFE wrapping functions for js2-refactor -*- lexical-binding: t; -*-
;; Copyright (C) 2012-2014 Magnar Sveen
;; Copyright (C) 2015-2016 Magnar Sveen and Nicolas Petton
;; Author: Magnar Sveen <magnars@gmail.com>,
;; Nicolas Petton <nicolas@petton.fr>
;; Keywords: conveniences
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Code:
(require 'js2r-helpers)
(defvar js2r--iife-regexp "^(function (")
(defun js2r-wrap-buffer-in-iife ()
"Wrap the entire buffer in an immediately invoked function expression"
(interactive)
(save-excursion
(when (ignore-errors (search-backward-regexp js2r--iife-regexp))
(error "Buffer already contains an immediately invoked function expression."))
(goto-char (point-min))
(insert "(function () {\n")
(when js2r-use-strict (insert "\"use strict\";\n"))
(insert "\n")
(goto-char (point-max))
(insert "\n")
(delete-blank-lines)
(insert "\n}());")
(indent-region (point-min) (point-max))))
(defun js2r--selected-name-positions ()
"Returns the (beginning . end) of the name at cursor, or active region."
(let ((current-node (js2-node-at-point))
beg end)
(unless (js2-name-node-p current-node)
(setq current-node (js2-node-at-point (- (point) 1))))
(if (not (and current-node (js2-name-node-p current-node)))
(error "Point is not on an identifier."))
(if (use-region-p)
(cons (region-beginning) (region-end))
(progn
(setq end (+ (js2-node-abs-pos current-node)
(js2-node-len current-node)))
(skip-syntax-backward ".w_")
(cons (point) end)))))
(defun js2r-inject-global-in-iife ()
"Create shortcut for marked global by injecting it in the wrapping IIFE"
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(save-excursion
(let* ((name-pos (js2r--selected-name-positions))
(name-beg (car name-pos))
(name-end (cdr name-pos))
(name (buffer-substring-no-properties name-beg name-end))
(short (buster--global-shortcut name))
beg end)
(unless (search-backward-regexp js2r--iife-regexp)
(error "No immediately invoked function expression found."))
(deactivate-mark)
(forward-char 11)
(insert short)
(unless (looking-at ")")
(insert ", "))
(search-forward "{")
(setq beg (point))
(backward-char)
(forward-list)
(forward-char)
(setq end (point))
(insert name)
(unless (looking-at ")")
(insert ", "))
(replace-string name short t beg end)))))
(provide 'js2r-iife)
;;; js2-iife.el ends here

Binary file not shown.

View file

@ -0,0 +1,227 @@
;;; js2r-paredit.el --- Paredit-like extensions for js2-refactor -*- lexical-binding: t; -*-
;; Copyright (C) 2012-2014 Magnar Sveen
;; Copyright (C) 2015-2016 Magnar Sveen and Nicolas Petton
;; Author: Magnar Sveen <magnars@gmail.com>,
;; Nicolas Petton <nicolas@petton.fr>
;; Keywords: conveniences
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Code:
(require 'dash)
(require 'js2r-helpers)
(defun js2r--nesting-node-p (node)
(or (js2-function-node-p node)
(js2-if-node-p node)
(js2-for-node-p node)
(js2-while-node-p node)))
(defun js2r--standalone-node-p (node)
(or (js2-stmt-node-p node)
(and (js2-function-node-p node)
(eq 'FUNCTION_STATEMENT (js2-function-node-form node)))))
(defun js2r-kill ()
"Kill a line like `kill-line' but tries to respect node boundaries.
Falls back to `kill-line' if the buffer has parse errors.
if(|foo) {bar();} -> if() {bar();}
function foo() {|2 + 3} -> function foo() {}
// some |comment -> // some
'this is a| string' -> 'this is a'
"
(interactive)
(if js2-parsed-errors
(progn
(message "Buffer has parse errors. Killing the line")
(kill-line))
(condition-case error
(js2r--kill-line)
(progn
(message "Error occured while trying to kill AST node. Killing the line.")
(kill-line)))))
(defun js2r--kill-line ()
"Kill a line, but respecting node boundaries."
(let ((node (js2r--next-node)))
(cond
((js2-comment-node-p node) (kill-line))
((js2-string-node-p node) (js2r--kill-line-in-string))
(t (js2r--kill-line-in-sexp)))))
(defun js2r--next-node ()
"Return the node at point, or the node after the point if the
point is at the exact end of a node."
(save-excursion
(when (= (js2-node-abs-end (js2-node-at-point))
(point))
(forward-char 1))
(js2-node-at-point)))
(defun js2r--kill-line-in-sexp ()
"Kill a line, but only kills until the closest outer sexp on
the current line, delimited with \")}]\". If no sexp is found
on the current line, falls back to
`js2r--kill-line-with-inner-sexp'."
(condition-case error
(let* ((beg (point))
(end (save-excursion
(up-list)
(forward-char -1)
(point))))
(if (js2-same-line end)
(kill-region beg end)
(js2r--kill-line-with-inner-sexp)))
(scan-error
(js2r--kill-line-with-inner-sexp))))
(defun js2r--kill-line-with-inner-sexp ()
"Kill a line, but respecting inner killed sexps, ensuring that
we kill up to the end to the next inner sexp if it starts in
the current line.
If the parentheses are unbalanced, fallback to `kill-line' and
warn the user."
(condition-case error
(let* ((beg (point))
(end (save-excursion
(forward-visible-line 1)
(point)))
(beg-of-sexp (save-excursion
(js2r--goto-last-sexp-on-line)
(point)))
(end-of-sexp (save-excursion
(goto-char beg-of-sexp)
(forward-list)
;; Kill all remaining semi-colons as well
(while (looking-at ";")
(forward-char))
(point))))
(if (js2-same-line beg-of-sexp)
(kill-region beg (max end end-of-sexp))
(kill-line)))
(scan-error
(message "Unbalanced parentheses. Killing the line.")
(kill-line))))
(defun js2r--goto-last-sexp-on-line ()
"Move the cursor to the opening of the last sexp on the current
line, or to the end of the line if no sexp is found."
(let ((pos (point)))
(down-list)
(backward-char 1)
(forward-list)
(if (js2-same-line pos)
(js2r--goto-last-sexp-on-line)
(backward-list))))
(defun js2r--kill-line-in-string ()
"Kill a line in a string node, respecting the node boundaries.
When at the beginning of the node, kill from outside of it."
(let* ((node (js2-node-at-point))
(beg (point))
(node-start (js2-node-abs-pos node))
(node-end (js2-node-abs-end node)))
(if (= beg node-start)
(js2r--kill-line-in-sexp)
(kill-region beg (1- node-end)))))
(defun js2r-forward-slurp (&optional arg)
"Add the expression following the current function into it.
The addition is performed by moving the closing brace of the
function down.
When called with a prefix argument ARG, slurp ARG expressions
following the current function."
(interactive "p")
(js2r--guard)
(js2r--wait-for-parse
(let* ((nesting (js2r--closest 'js2r--nesting-node-p))
(standalone (if (js2r--standalone-node-p nesting)
nesting
(js2-node-parent-stmt nesting)))
(next-sibling (js2-node-next-sibling standalone))
(beg (js2-node-abs-pos next-sibling))
(last-sibling (if (wholenump arg)
(let ((num arg)
(iter-sibling next-sibling))
(while (> num 1) ;; Do next-sibling arg nbr of times
(setq iter-sibling (js2-node-next-sibling iter-sibling))
(setq num (1- num)))
iter-sibling)
next-sibling)) ;; No optional arg. Just use next-sibling
(end (js2-node-abs-end last-sibling))
(text (buffer-substring beg end)))
(save-excursion
(delete-region beg end)
;; Delete newline character if the deleted AST node was at the end of the line
(goto-char beg)
(when (and (eolp)
(not (eobp)))
(delete-char 1))
(goto-char (js2-node-abs-end nesting))
(forward-char -1)
(when (looking-back "{ *")
(newline))
(setq beg (point))
(insert text)
(when (looking-at " *}")
(newline))
(setq end (point))
(indent-region beg end)))))
(defun js2r-forward-barf (&optional arg)
(interactive "p")
(js2r--guard)
(js2r--wait-for-parse
(let* ((nesting (js2r--closest 'js2r--nesting-node-p))
(standalone (if (js2r--standalone-node-p nesting)
nesting
(js2-node-parent-stmt nesting)))
(standalone-end (js2-node-abs-end standalone))
(last-child (car (last (if (js2-if-node-p nesting)
(js2-scope-kids (js2r--closest 'js2-scope-p))
(js2r--node-kids nesting)))))
(first-barf-child (if (wholenump arg)
(let ((num arg)
(iter-child last-child))
(while (> num 1) ;; Do prev-sibling arg nbr of times
(setq iter-child (js2-node-prev-sibling iter-child))
(setq num (1- num)))
iter-child)
last-child)) ; No optional arg. Just use last-child
(last-child-beg (save-excursion
(goto-char (js2-node-abs-pos first-barf-child))
(skip-syntax-backward " ")
(while (looking-back "\n") (backward-char))
(point)))
(last-child-end (js2-node-abs-end last-child))
(text (buffer-substring last-child-beg last-child-end)))
(save-excursion
(js2r--execute-changes
(list
(list :beg last-child-beg :end last-child-end :contents "")
(list :beg standalone-end :end standalone-end :contents text)))))))
(provide 'js2r-paredit)
;;; js2r-paredit.el ends here

Binary file not shown.

View file

@ -0,0 +1,375 @@
;;; js2r-vars.el --- Variable declaration manipulation functions for js2-refactor -*- lexical-binding: t; -*-
;; Copyright (C) 2012-2014 Magnar Sveen
;; Copyright (C) 2015-2016 Magnar Sveen and Nicolas Petton
;; Author: Magnar Sveen <magnars@gmail.com>,
;; Nicolas Petton <nicolas@petton.fr>
;; Keywords: conveniences
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Code:
(require 'multiple-cursors-core)
(require 'dash)
(require 'js2r-helpers)
;; Helpers
(defun js2r--name-node-at-point (&optional pos)
(setq pos (or pos (point)))
(let ((current-node (js2-node-at-point pos)))
(unless (js2-name-node-p current-node)
(setq current-node (js2-node-at-point (- (point) 1))))
(if (not (and current-node (js2-name-node-p current-node)))
(error "Point is not on an identifier.")
current-node)))
(defun js2r--local-name-node-at-point (&optional pos)
(setq pos (or pos (point)))
(let ((current-node (js2r--name-node-at-point pos)))
(unless (js2r--local-name-node-p current-node)
(error "Point is not on a local identifier"))
current-node))
(defun js2r--local-name-node-p (node)
(let ((parent (js2-node-parent node)))
(and parent (js2-name-node-p node)
;; is not foo in { foo: 1 }
(not (and (js2-object-prop-node-p parent)
(eq node (js2-object-prop-node-left parent))
;; could be { foo } though, in which case the node
;; is parent's both left and right.
(not (eq node (js2-object-prop-node-right parent)))))
;; is not foo in x.foo
(not (and (js2-prop-get-node-p parent)
(eq node (js2-prop-get-node-right parent))))
;; is not foo in import { foo as bar } from ...
;; could be bar, though.
(not (and (js2-export-binding-node-p parent)
(eq node (js2-export-binding-node-extern-name parent))
(not (eq node (js2-export-binding-node-local-name parent))))))))
(defun js2r--name-node-defining-scope (name-node)
(unless (js2r--local-name-node-p name-node)
(error "Node is not on a local identifier"))
(js2-get-defining-scope
(js2-node-get-enclosing-scope name-node)
(js2-name-node-name name-node)))
(defun js2r--local-usages-of-name-node (name-node)
(unless (js2r--local-name-node-p name-node)
(error "Node is not on a local identifier"))
(let* ((name (js2-name-node-name name-node))
(scope (js2r--name-node-defining-scope name-node))
(result nil))
(js2-visit-ast
scope
(lambda (node end-p)
(when (and (not end-p)
(js2r--local-name-node-p node)
(string= name (js2-name-node-name node))
(eq scope (js2r--name-node-defining-scope node)))
(add-to-list 'result node))
t))
result))
(defun js2r--local-var-positions (name-node)
(-map 'js2-node-abs-pos (js2r--local-usages-of-name-node name-node)))
(defun js2r--var-defining-node (var-node)
(unless (js2r--local-name-node-p var-node)
(error "Node is not on a local identifier"))
(let* ((name (js2-name-node-name var-node))
(scope (js2r--name-node-defining-scope var-node)))
(js2-symbol-ast-node
(js2-scope-get-symbol scope name))))
;; Add to jslint globals annotation
(defun current-line-contents ()
"Find the contents of the current line, minus indentation."
(buffer-substring (save-excursion (back-to-indentation) (point))
(save-excursion (end-of-line) (point))))
(require 'thingatpt)
(defun js2r-add-to-globals-annotation ()
(interactive)
(let ((var (thing-at-point 'symbol)))
(save-excursion
(beginning-of-buffer)
(when (not (string-match "^/\\* *global " (current-line-contents)))
(newline)
(forward-line -1)
(insert "/* global */")
(newline)
(forward-line -1))
(while (not (string-match "*/" (current-line-contents)))
(forward-line))
(end-of-line)
(delete-char -2)
(unless (looking-back "global ")
(while (looking-back " ")
(delete-char -1))
(insert ", "))
(insert (concat var " */")))))
;; Rename variable
(defun js2r-rename-var ()
"Renames the variable on point and all occurrences in its lexical scope."
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(let* ((current-node (js2r--local-name-node-at-point))
(len (js2-node-len current-node))
(current-start (js2-node-abs-pos current-node))
(current-end (+ current-start len)))
(save-excursion
(mapc (lambda (beg)
(when (not (= beg current-start))
(goto-char beg)
(set-mark (+ beg len))
(mc/create-fake-cursor-at-point)))
(js2r--local-var-positions current-node)))
(push-mark current-end)
(goto-char current-start)
(activate-mark))
(mc/maybe-multiple-cursors-mode)))
(add-to-list 'mc--default-cmds-to-run-once 'js2r-rename-var)
;; Change local variable to use this. instead
(defun js2r-var-to-this ()
"Changes the variable on point to use this.var instead."
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(save-excursion
(let ((node (js2-node-at-point)))
(when (js2-var-decl-node-p node)
(let ((kids (js2-var-decl-node-kids node)))
(when (cdr kids)
(error "Currently does not support converting multivar statements."))
(goto-char (js2-node-abs-pos (car kids))))))
(--each (js2r--local-var-positions (js2r--local-name-node-at-point))
(goto-char it)
(cond ((looking-back "var ") (delete-char -4))
((looking-back "let ") (delete-char -4))
((looking-back "const ") (delete-char -6)))
(insert "this.")))))
;; Inline var
(defun js2r-inline-var ()
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(save-excursion
(let* ((current-node (js2r--local-name-node-at-point))
(definer (js2r--var-defining-node current-node))
(definer-start (js2-node-abs-pos definer))
(var-init (js2-node-parent definer))
(initializer (js2-var-init-node-initializer
var-init)))
(unless initializer
(error "Var is not initialized when defined."))
(let* ((var-len (js2-node-len current-node))
(init-beg (js2-node-abs-pos initializer))
(init-end (+ init-beg (js2-node-len initializer)))
(var-init-beg (copy-marker (js2-node-abs-pos var-init)))
(var-init-end (copy-marker (+ var-init-beg (js2-node-len var-init))))
(contents (buffer-substring init-beg init-end)))
(mapc (lambda (beg)
(when (not (= beg definer-start))
(goto-char beg)
(delete-char var-len)
(insert contents)))
(js2r--local-var-positions current-node))
(js2r--delete-var-init var-init-beg var-init-end))))))
(defun js2r--was-single-var ()
(save-excursion
(goto-char (point-at-bol))
(or (looking-at "^[[:space:]]*\\(var\\|const\\|let\\)[[:space:]]?;?$")
(looking-at "^[[:space:]]*,[[:space:]]*$"))))
(defun js2r--was-starting-var ()
(or (looking-back "var ")
(looking-back "const ")
(looking-back "let ")))
(defun js2r--was-ending-var ()
(looking-at ";"))
(defun js2r--delete-var-init (beg end)
(goto-char beg)
(delete-char (- end beg))
(cond
((js2r--was-single-var)
(delete-region (point-at-bol) (point-at-eol))
(delete-blank-lines))
((js2r--was-starting-var)
(delete-char 1)
(if (looking-at " ")
(delete-char 1)
(join-line -1)))
((js2r--was-ending-var)
(if (looking-back ", ")
(delete-char -1)
(join-line)
(delete-char 1))
(delete-char -1))
(t (delete-char 2))))
;; two cases
;; - it's the only var -> remove the line
;; - there are several vars -> remove the node then clean up commas
;; Extract variable
(defun js2r--start-of-parent-stmt ()
(js2-node-abs-pos (js2r--closest-stmt-node)))
(defun js2r--object-literal-key-behind (pos)
(save-excursion
(goto-char pos)
(when (looking-back "\\sw: ?")
(backward-char 2)
(js2-name-node-name (js2r--name-node-at-point)))))
(defun js2r--line-above-is-blank ()
(save-excursion
(forward-line -1)
(string= "" (current-line-contents))))
;;;###autoload
(defun js2r-extract-var ()
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(let ((keyword (if js2r-prefer-let-over-var "let" "var")))
(if (use-region-p)
(js2r--extract-var-between (region-beginning) (region-end) keyword)
(let ((node (js2r--closest-extractable-node)))
(js2r--extract-var-between (js2-node-abs-pos node)
(js2-node-abs-end node) keyword))))))
;;;###autoload
(defun js2r-extract-let ()
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(if (use-region-p)
(js2r--extract-var-between (region-beginning) (region-end) "let")
(let ((node (js2r--closest-extractable-node)))
(js2r--extract-var-between (js2-node-abs-pos node)
(js2-node-abs-end node) "let")))))
;;;###autoload
(defun js2r-extract-const ()
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(if (use-region-p)
(js2r--extract-var-between (region-beginning) (region-end) "const")
(let ((node (js2r--closest-extractable-node)))
(js2r--extract-var-between (js2-node-abs-pos node)
(js2-node-abs-end node) "const")))))
(add-to-list 'mc--default-cmds-to-run-once 'js2r-extract-var)
(add-to-list 'mc--default-cmds-to-run-once 'js2r-extract-let)
(add-to-list 'mc--default-cmds-to-run-once 'js2r-extract-const)
(defun js2r--extract-var-between (beg end keyword)
(interactive "r")
(unless (js2r--single-complete-expression-between-p beg end)
(error "Can only extract single, complete expressions to var"))
(let ((deactivate-mark nil)
(expression (buffer-substring beg end))
(orig-var-end (make-marker))
(new-var-end (make-marker))
(name (or (js2r--object-literal-key-behind beg) "name")))
(delete-region beg end)
(insert name)
(set-marker orig-var-end (point))
(goto-char (js2r--start-of-parent-stmt))
(js2r--insert-var name keyword)
(set-marker new-var-end (point))
(insert " = " expression ";")
(when (or (js2r--line-above-is-blank)
(string-match-p "^function " expression))
(newline))
(newline)
(indent-region new-var-end orig-var-end)
(save-excursion
(goto-char new-var-end)
(set-mark (- (point) (length name)))
(mc/create-fake-cursor-at-point))
(goto-char orig-var-end)
(set-mark (- (point) (length name)))
(set-marker orig-var-end nil)
(set-marker new-var-end nil))
(mc/maybe-multiple-cursors-mode))
(defun js2r--insert-var (name keyword)
"Insert a var definition for NAME."
(insert (format "%s %s" keyword name)))
(defun js2r-split-var-declaration ()
"Split a variable declaration into separate variable
declarations for each declared variable."
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(save-excursion
(let* ((declaration (or (js2r--closest #'js2-var-decl-node-p) (error "No var declaration at point.")))
(kids (js2-var-decl-node-kids declaration))
(stmt (js2-node-parent-stmt declaration))
(tt (js2-var-decl-node-decl-type declaration))
(keyword (cond
((= tt js2-VAR) "var")
((= tt js2-LET) "let")
((= tt js2-CONST) "const"))))
(goto-char (js2-node-abs-end stmt))
(mapc (lambda (kid)
(js2r--insert-var (js2-node-string kid) keyword)
(insert ";")
(newline)
(if (save-excursion
(goto-char (js2-node-abs-end kid))
(looking-at ", *\n *\n"))
(newline)))
kids)
(delete-char -1) ;; delete final newline
(let ((end (point)))
(js2r--goto-and-delete-node stmt)
(indent-region (point) end))))))
(provide 'js2r-vars)
;;; js2-vars.el ends here

Binary file not shown.

View file

@ -0,0 +1,76 @@
;;; js2r-wrapping.el --- Helper functions for wrapping/unwrapping -*- lexical-binding: t; -*-
;; Copyright (C) 2017 Nicolas Petton
;; Author: Nicolas Petton
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
(require 'yasnippet)
(require 'js2r-helpers)
(defvar js2r--space-str " \t\n")
(defun js2r--skip-region-whitespace ()
(let ((p-first (< (point) (mark))))
(unless p-first
(exchange-point-and-mark))
(skip-chars-forward js2r--space-str)
(exchange-point-and-mark)
(skip-chars-backward js2r--space-str)
(when p-first
(exchange-point-and-mark))))
(defun js2r-unwrap ()
(interactive)
(js2r--guard)
(js2r--wait-for-parse
(let (beg end)
(if (use-region-p)
(progn
(js2r--skip-region-whitespace)
(setq beg (min (point) (mark)))
(setq end (max (point) (mark))))
(let ((stmt (js2-node-parent-stmt (js2-node-at-point))))
(setq beg (js2-node-abs-pos stmt))
(setq end (js2-node-abs-end stmt))))
(let* ((ancestor (js2-node-parent-stmt
(js2r--first-common-ancestor-in-region beg end)))
(abeg (js2-node-abs-pos ancestor))
(aend (js2-node-abs-end ancestor)))
(save-excursion
(goto-char end)
(delete-char (- aend end))
(goto-char abeg)
(delete-char (- beg abeg)))
(indent-region (point-min) (point-max))))))
(defun js2r-wrap-in-for-loop (beg end)
(interactive "r")
(js2r--skip-region-whitespace)
(setq beg (min (point) (mark)))
(setq end (max (point) (mark)))
(let ((yas-wrap-around-region t))
(yas-expand-snippet "for (var i = 0, l = ${1:length}; i < l; i++) {\n$0\n}"
beg end)))
(provide 'js2r-wrapping)
;;; js2r-wrapping.el ends here

Binary file not shown.

View file

@ -0,0 +1,119 @@
;;; mc-cycle-cursors.el
;; Copyright (C) 2012-2016 Magnar Sveen
;; Author: Magnar Sveen <magnars@gmail.com>
;; Keywords: editing cursors
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This scrolls the buffer to center each cursor in turn.
;; Scroll down with C-v, scroll up with M-v
;; This is nice when you have cursors that's outside of your view.
;;; Code:
(require 'multiple-cursors-core)
(defun mc/next-fake-cursor-after-point ()
(let ((pos (point))
(next-pos (1+ (point-max)))
next)
(mc/for-each-fake-cursor
(let ((cursor-pos (overlay-get cursor 'point)))
(when (and (< pos cursor-pos)
(< cursor-pos next-pos))
(setq next-pos cursor-pos)
(setq next cursor))))
next))
(defun mc/prev-fake-cursor-before-point ()
(let ((pos (point))
(prev-pos (1- (point-min)))
prev)
(mc/for-each-fake-cursor
(let ((cursor-pos (overlay-get cursor 'point)))
(when (and (> pos cursor-pos)
(> cursor-pos prev-pos))
(setq prev-pos cursor-pos)
(setq prev cursor))))
prev))
(defcustom mc/cycle-looping-behaviour 'continue
"What to do if asked to cycle beyond the last cursor or before the first cursor."
:type '(radio (const :tag "Loop around to beginning/end of document." continue)
(const :tag "Warn and then loop around." warn)
(const :tag "Signal an error." error)
(const :tag "Don't loop." stop))
:group 'multiple-cursors)
(defun mc/handle-loop-condition (error-message)
(cl-ecase mc/cycle-looping-behaviour
(error (error error-message))
(warn (message error-message))
(continue 'continue)
(stop 'stop)))
(defun mc/first-fake-cursor-after (point)
"Very similar to mc/furthest-cursor-before-point, but ignores (mark) and (point)."
(let* ((cursors (mc/all-fake-cursors))
(cursors-after-point (cl-remove-if (lambda (cursor)
(< (mc/cursor-beg cursor) point))
cursors))
(cursors-in-order (cl-sort cursors-after-point '< :key 'mc/cursor-beg)))
(car cursors-in-order)))
(defun mc/last-fake-cursor-before (point)
"Very similar to mc/furthest-cursor-before-point, but ignores (mark) and (point)."
(let* ((cursors (mc/all-fake-cursors))
(cursors-before-point (cl-remove-if (lambda (cursor)
(> (mc/cursor-end cursor) point))
cursors))
(cursors-in-order (cl-sort cursors-before-point '> :key 'mc/cursor-end)))
(car cursors-in-order)))
(cl-defun mc/cycle (next-cursor fallback-cursor loop-message)
(when (null next-cursor)
(when (eql 'stop (mc/handle-loop-condition loop-message))
(return-from mc/cycle nil))
(setf next-cursor fallback-cursor))
(mc/create-fake-cursor-at-point)
(mc/pop-state-from-overlay next-cursor)
(recenter))
(defun mc/cycle-forward ()
(interactive)
(mc/cycle (mc/next-fake-cursor-after-point)
(mc/first-fake-cursor-after (point-min))
"We're already at the last cursor."))
(defun mc/cycle-backward ()
(interactive)
(mc/cycle (mc/prev-fake-cursor-before-point)
(mc/last-fake-cursor-before (point-max))
"We're already at the last cursor"))
(define-key mc/keymap (kbd "C-v") 'mc/cycle-forward)
(define-key mc/keymap (kbd "M-v") 'mc/cycle-backward)
(provide 'mc-cycle-cursors)
;; Local Variables:
;; coding: utf-8
;; End:
;;; mc-cycle-cursors.el ends here

View file

@ -0,0 +1,110 @@
;;; mc-edit-lines.el
;; Copyright (C) 2012-2016 Magnar Sveen
;; Author: Magnar Sveen <magnars@gmail.com>
;; Keywords: editing cursors
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This file contains functions to add multiple cursors to consecutive lines
;; given an active region.
;; Please see multiple-cursors.el for more commentary.
;;; Code:
(require 'multiple-cursors-core)
(defcustom mc/edit-lines-empty-lines nil
"What should be done by `mc/edit-lines' when a line is not long enough."
:type '(radio (const :tag "Pad the line with spaces." pad)
(const :tag "Ignore the line." ignore)
(const :tag "Signal an error." error)
(const :tag "Nothing. Cursor is at end of line." nil))
:group 'multiple-cursors)
;;;###autoload
(defun mc/edit-lines (&optional arg)
"Add one cursor to each line of the active region.
Starts from mark and moves in straight down or up towards the
line point is on.
What is done with lines which are not long enough is governed by
`mc/edit-lines-empty-lines'. The prefix argument ARG can be used
to override this. If ARG is a symbol (when called from Lisp),
that symbol is used instead of `mc/edit-lines-empty-lines'.
Otherwise, if ARG negative, short lines will be ignored. Any
other non-nil value will cause short lines to be padded."
(interactive "P")
(when (not (and mark-active (/= (point) (mark))))
(error "Mark a set of lines first"))
(mc/remove-fake-cursors)
(let* ((col (current-column))
(point-line (mc/line-number-at-pos))
(mark-line (progn (exchange-point-and-mark) (mc/line-number-at-pos)))
(direction (if (< point-line mark-line) :up :down))
(style (cond
;; called from lisp
((and arg (symbolp arg))
arg)
;; negative argument
((< (prefix-numeric-value arg) 0)
'ignore)
(arg 'pad)
(t mc/edit-lines-empty-lines))))
(deactivate-mark)
(when (and (eq direction :up) (bolp))
(previous-logical-line 1 nil)
(move-to-column col))
;; Add the cursors
(while (not (eq (mc/line-number-at-pos) point-line))
;; Pad the line
(when (eq style 'pad)
(while (< (current-column) col)
(insert " ")))
;; Error
(when (and (eq style 'error)
(not (equal col (current-column))))
(error "Short line encountered in `mc/edit-lines'"))
;; create the cursor
(unless (and (eq style 'ignore)
(not (equal col (current-column))))
(mc/create-fake-cursor-at-point))
;; proceed to next
(if (eq direction :up)
(previous-logical-line 1 nil)
(next-logical-line 1 nil))
(move-to-column col))
(multiple-cursors-mode)))
;;;###autoload
(defun mc/edit-ends-of-lines ()
"Add one cursor to the end of each line in the active region."
(interactive)
(mc/edit-lines)
(mc/execute-command-for-all-cursors 'end-of-line))
;;;###autoload
(defun mc/edit-beginnings-of-lines ()
"Add one cursor to the beginning of each line in the active region."
(interactive)
(mc/edit-lines)
(mc/execute-command-for-all-cursors 'beginning-of-line))
(provide 'mc-edit-lines)
;;; mc-edit-lines.el ends here

Binary file not shown.

View file

@ -0,0 +1,107 @@
;;; mc-hide-unmatched-lines.el
;; Copyright (C) 2014 Aleksey Fedotov
;; Author: Aleksey Fedotov <lexa@cfotr.com>
;; Keywords: editing cursors
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This minor mode when enabled hides all lines where no cursors (and
;; also hum/lines-to-expand below and above) To make use of this mode
;; press "C-'" while multiple-cursor-mode is active. You can still
;; edit lines while you are in mc-hide-unmatched-lines mode. To leave
;; this mode press "<return>" or "C-g"
;;
;;; Code:
(require 'multiple-cursors-core)
(require 'mc-mark-more)
(defvar hum/hide-unmatched-lines-mode-map (make-sparse-keymap)
"Keymap for hide unmatched lines is mainly for rebinding C-g")
(define-key hum/hide-unmatched-lines-mode-map (kbd "C-g") 'hum/keyboard-quit)
(define-key hum/hide-unmatched-lines-mode-map (kbd "<return>") 'hum/keyboard-quit)
(defun hum/keyboard-quit ()
"Leave hide-unmatched-lines mode"
(interactive)
(mc-hide-unmatched-lines-mode 0))
;; used only in in multiple-cursors-mode-disabled-hook
(defun hum/disable-hum-mode ()
(mc-hide-unmatched-lines-mode 0))
;;;###autoload
(define-minor-mode mc-hide-unmatched-lines-mode
"Minor mode when enabled hides all lines where no cursors (and
also hum/lines-to-expand below and above) To make use of this
mode press \"C-'\" while multiple-cursor-mode is active. You can
still edit lines while you are in mc-hide-unmatched-lines
mode. To leave this mode press <return> or \"C-g\""
nil " hu"
hum/hide-unmatched-lines-mode-map
(if mc-hide-unmatched-lines-mode
;;just in case if mc mode will be disabled while hide-unmatched-lines is active
(progn
(hum/hide-unmatched-lines)
(add-hook 'multiple-cursors-mode-disabled-hook 'hum/disable-hum-mode t t))
(progn
(hum/unhide-unmatched-lines)
(remove-hook 'multiple-cursors-mode-disabled-hook 'hum/disable-hum-mode))))
(defconst hum/invisible-overlay-name 'hum/invisible-overlay-name)
(defcustom hum/lines-to-expand 2
"How many lines below and above cursor to show"
:type '(integer)
:group 'multiple-cursors)
(defcustom hum/placeholder "..."
"Placeholder which will be placed instead of hidden text"
:type '(string)
:group 'multiple-cursors)
(defun hum/add-invisible-overlay (begin end)
(let ((overlay (make-overlay begin
end
(current-buffer)
t
nil
)))
(overlay-put overlay hum/invisible-overlay-name t)
(overlay-put overlay 'invisible t)
(overlay-put overlay 'intangible t)
(overlay-put overlay 'evaporate t)
(overlay-put overlay 'after-string hum/placeholder)))
(defun hum/hide-unmatched-lines ()
(let ((begin (point-min)))
(mc/for-each-cursor-ordered
(save-excursion
(goto-char (mc/cursor-beg cursor))
(if (< begin (line-beginning-position (- hum/lines-to-expand)))
(hum/add-invisible-overlay begin (line-end-position (- hum/lines-to-expand))))
(setq begin (line-beginning-position (+ 2 hum/lines-to-expand)))))
(hum/add-invisible-overlay begin (point-max))))
(defun hum/unhide-unmatched-lines ()
(remove-overlays nil nil hum/invisible-overlay-name t))
(provide 'mc-hide-unmatched-lines-mode)
(define-key mc/keymap (kbd "C-'") 'mc-hide-unmatched-lines-mode)

View file

@ -0,0 +1,709 @@
;;; mc-mark-more.el
;; Copyright (C) 2012-2016 Magnar Sveen
;; Author: Magnar Sveen <magnars@gmail.com>
;; Keywords: editing cursors
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This file contains functions to mark more parts of the buffer.
;; See ./features/mark-more.feature for examples.
;; Please see multiple-cursors.el for more commentary.
;;; Code:
(require 'multiple-cursors-core)
(require 'thingatpt)
(defun mc/cursor-end (cursor)
(if (overlay-get cursor 'mark-active)
(max (overlay-get cursor 'point)
(overlay-get cursor 'mark))
(overlay-get cursor 'point)))
(defun mc/cursor-beg (cursor)
(if (overlay-get cursor 'mark-active)
(min (overlay-get cursor 'point)
(overlay-get cursor 'mark))
(overlay-get cursor 'point)))
(defun mc/furthest-region-end ()
(let ((end (max (mark) (point))))
(mc/for-each-fake-cursor
(setq end (max end (mc/cursor-end cursor))))
end))
(defun mc/first-region-start ()
(let ((beg (min (mark) (point))))
(mc/for-each-fake-cursor
(setq beg (min beg (mc/cursor-beg cursor))))
beg))
(defun mc/furthest-cursor-before-point ()
(let ((beg (if mark-active (min (mark) (point)) (point)))
furthest)
(mc/for-each-fake-cursor
(when (< (mc/cursor-beg cursor) beg)
(setq beg (mc/cursor-beg cursor))
(setq furthest cursor)))
furthest))
(defun mc/furthest-cursor-after-point ()
(let ((end (if mark-active (max (mark) (point)) (point)))
furthest)
(mc/for-each-fake-cursor
(when (> (mc/cursor-end cursor) end)
(setq end (mc/cursor-end cursor))
(setq furthest cursor)))
furthest))
(defun mc/fake-cursor-at-point (&optional point)
"Return the fake cursor with its point right at POINT (defaults
to (point)), or nil."
(setq point (or point (point)))
(let ((cursors (mc/all-fake-cursors))
(c nil))
(catch 'found
(while (setq c (pop cursors))
(when (eq (marker-position (overlay-get c 'point))
point)
(throw 'found c))))))
(defun mc/region-strings ()
(let ((strings (list (buffer-substring-no-properties (point) (mark)))))
(mc/for-each-fake-cursor
(add-to-list 'strings (buffer-substring-no-properties
(mc/cursor-beg cursor)
(mc/cursor-end cursor))))
strings))
(defvar mc/enclose-search-term nil
"How should mc/mark-more-* search for more matches?
Match everything: nil
Match only whole words: 'words
Match only whole symbols: 'symbols
Use like case-fold-search, don't recommend setting it globally.")
(defun mc/mark-more-like-this (skip-last direction)
(let ((case-fold-search nil)
(re (regexp-opt (mc/region-strings) mc/enclose-search-term))
(point-out-of-order (cl-ecase direction
(forwards (< (point) (mark)))
(backwards (not (< (point) (mark))))))
(furthest-cursor (cl-ecase direction
(forwards (mc/furthest-cursor-after-point))
(backwards (mc/furthest-cursor-before-point))))
(start-char (cl-ecase direction
(forwards (mc/furthest-region-end))
(backwards (mc/first-region-start))))
(search-function (cl-ecase direction
(forwards 'search-forward-regexp)
(backwards 'search-backward-regexp)))
(match-point-getter (cl-ecase direction
(forwards 'match-beginning)
(backwards 'match-end))))
(if (and skip-last (not furthest-cursor))
(error "No cursors to be skipped")
(mc/save-excursion
(goto-char start-char)
(when skip-last
(mc/remove-fake-cursor furthest-cursor))
(if (funcall search-function re nil t)
(progn
(push-mark (funcall match-point-getter 0))
(when point-out-of-order
(exchange-point-and-mark))
(mc/create-fake-cursor-at-point))
(user-error "no more matches found."))))))
;;;###autoload
(defun mc/mark-next-like-this (arg)
"Find and mark the next part of the buffer matching the currently active region
If no region is active add a cursor on the next line
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next."
(interactive "p")
(if (< arg 0)
(let ((cursor (mc/furthest-cursor-after-point)))
(if cursor
(mc/remove-fake-cursor cursor)
(error "No cursors to be unmarked")))
(if (region-active-p)
(mc/mark-more-like-this (= arg 0) 'forwards)
(mc/mark-lines arg 'forwards)))
(mc/maybe-multiple-cursors-mode))
;;;###autoload
(defun mc/mark-next-like-this-word (arg)
"Find and mark the next part of the buffer matching the currently active region
If no region is active, mark the word at the point and find the next match
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next."
(interactive "p")
(if (< arg 0)
(let ((cursor (mc/furthest-cursor-after-point)))
(if cursor
(mc/remove-fake-cursor cursor)
(error "No cursors to be unmarked")))
(if (region-active-p)
(mc/mark-more-like-this (= arg 0) 'forwards)
(mc--select-thing-at-point 'word)
(mc/mark-more-like-this (= arg 0) 'forwards)))
(mc/maybe-multiple-cursors-mode))
(defun mc/mark-next-like-this-symbol (arg)
"Find and mark the next part of the buffer matching the currently active region
If no region is active, mark the symbol at the point and find the next match
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next."
(interactive "p")
(if (< arg 0)
(let ((cursor (mc/furthest-cursor-after-point)))
(if cursor
(mc/remove-fake-cursor cursor)
(error "No cursors to be unmarked")))
(if (region-active-p)
(mc/mark-more-like-this (= arg 0) 'forwards)
(mc--select-thing-at-point 'symbol)
(mc/mark-more-like-this (= arg 0) 'forwards)))
(mc/maybe-multiple-cursors-mode))
;;;###autoload
(defun mc/mark-next-word-like-this (arg)
"Find and mark the next word of the buffer matching the currently active region
The matching region must be a whole word to be a match
If no region is active, mark the symbol at the point and find the next match
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next."
(interactive "p")
(let ((mc/enclose-search-term 'words))
(mc/mark-next-like-this arg)))
;;;###autoload
(defun mc/mark-next-symbol-like-this (arg)
"Find and mark the next symbol of the buffer matching the currently active region
The matching region must be a whole symbol to be a match
If no region is active, mark the symbol at the point and find the next match
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next."
(interactive "p")
(let ((mc/enclose-search-term 'symbols))
(mc/mark-next-like-this arg)))
;;;###autoload
(defun mc/mark-previous-like-this (arg)
"Find and mark the previous part of the buffer matching the currently active region
If no region is active add a cursor on the previous line
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next."
(interactive "p")
(if (< arg 0)
(let ((cursor (mc/furthest-cursor-before-point)))
(if cursor
(mc/remove-fake-cursor cursor)
(error "No cursors to be unmarked")))
(if (region-active-p)
(mc/mark-more-like-this (= arg 0) 'backwards)
(mc/mark-lines arg 'backwards)))
(mc/maybe-multiple-cursors-mode))
;;;###autoload
(defun mc/mark-previous-like-this-word (arg)
"Find and mark the previous part of the buffer matching the currently active region
If no region is active, mark the word at the point and find the previous match
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark previous."
(interactive "p")
(if (< arg 0)
(let ((cursor (mc/furthest-cursor-after-point)))
(if cursor
(mc/remove-fake-cursor cursor)
(error "No cursors to be unmarked")))
(if (region-active-p)
(mc/mark-more-like-this (= arg 0) 'backwards)
(mc--select-thing-at-point 'word)
(mc/mark-more-like-this (= arg 0) 'backwards)))
(mc/maybe-multiple-cursors-mode))
(defun mc/mark-previous-like-this-symbol (arg)
"Find and mark the previous part of the buffer matching the currently active region
If no region is active, mark the symbol at the point and find the previous match
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark previous."
(interactive "p")
(if (< arg 0)
(let ((cursor (mc/furthest-cursor-after-point)))
(if cursor
(mc/remove-fake-cursor cursor)
(error "No cursors to be unmarked")))
(if (region-active-p)
(mc/mark-more-like-this (= arg 0) 'backwards)
(mc--select-thing-at-point 'symbol)
(mc/mark-more-like-this (= arg 0) 'backwards)))
(mc/maybe-multiple-cursors-mode))
;;;###autoload
(defun mc/mark-previous-word-like-this (arg)
"Find and mark the previous part of the buffer matching the currently active region
The matching region must be a whole word to be a match
If no region is active add a cursor on the previous line
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next."
(interactive "p")
(let ((mc/enclose-search-term 'words))
(mc/mark-previous-like-this arg)))
;;;###autoload
(defun mc/mark-previous-symbol-like-this (arg)
"Find and mark the previous part of the buffer matching the currently active region
The matching region must be a whole symbol to be a match
If no region is active add a cursor on the previous line
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next."
(interactive "p")
(let ((mc/enclose-search-term 'symbols))
(mc/mark-previous-like-this arg)))
(defun mc/mark-lines (num-lines direction)
(dotimes (i (if (= num-lines 0) 1 num-lines))
(mc/save-excursion
(let ((furthest-cursor (cl-ecase direction
(forwards (mc/furthest-cursor-after-point))
(backwards (mc/furthest-cursor-before-point)))))
(when (overlayp furthest-cursor)
(goto-char (overlay-get furthest-cursor 'point))
(when (= num-lines 0)
(mc/remove-fake-cursor furthest-cursor))))
(cl-ecase direction
(forwards (next-logical-line 1 nil))
(backwards (previous-logical-line 1 nil)))
(mc/create-fake-cursor-at-point))))
;;;###autoload
(defun mc/mark-next-lines (arg)
(interactive "p")
(mc/mark-lines arg 'forwards)
(mc/maybe-multiple-cursors-mode))
;;;###autoload
(defun mc/mark-previous-lines (arg)
(interactive "p")
(mc/mark-lines arg 'backwards)
(mc/maybe-multiple-cursors-mode))
;;;###autoload
(defun mc/unmark-next-like-this ()
"Deselect next part of the buffer matching the currently active region."
(interactive)
(mc/mark-next-like-this -1))
;;;###autoload
(defun mc/unmark-previous-like-this ()
"Deselect prev part of the buffer matching the currently active region."
(interactive)
(mc/mark-previous-like-this -1))
;;;###autoload
(defun mc/skip-to-next-like-this ()
"Skip the current one and select the next part of the buffer matching the currently active region."
(interactive)
(mc/mark-next-like-this 0))
;;;###autoload
(defun mc/skip-to-previous-like-this ()
"Skip the current one and select the prev part of the buffer matching the currently active region."
(interactive)
(mc/mark-previous-like-this 0))
;;;###autoload
(defun mc/mark-all-like-this ()
"Find and mark all the parts of the buffer matching the currently active region"
(interactive)
(unless (region-active-p)
(error "Mark a region to match first."))
(mc/remove-fake-cursors)
(let ((master (point))
(case-fold-search nil)
(point-first (< (point) (mark)))
(re (regexp-opt (mc/region-strings) mc/enclose-search-term)))
(mc/save-excursion
(goto-char 0)
(while (search-forward-regexp re nil t)
(push-mark (match-beginning 0))
(when point-first (exchange-point-and-mark))
(unless (= master (point))
(mc/create-fake-cursor-at-point))
(when point-first (exchange-point-and-mark)))))
(if (> (mc/num-cursors) 1)
(multiple-cursors-mode 1)
(multiple-cursors-mode 0)))
(defun mc--select-thing-at-point (thing)
(let ((bound (bounds-of-thing-at-point thing)))
(when bound
(set-mark (car bound))
(goto-char (cdr bound))
bound)))
(defun mc--select-thing-at-point-or-bark (thing)
(unless (or (region-active-p) (mc--select-thing-at-point thing))
(error "Mark a region or set cursor on a %s." thing)))
;;;###autoload
(defun mc/mark-all-words-like-this ()
(interactive)
(mc--select-thing-at-point-or-bark 'word)
(let ((mc/enclose-search-term 'words))
(mc/mark-all-like-this)))
;;;###autoload
(defun mc/mark-all-symbols-like-this ()
(interactive)
(mc--select-thing-at-point-or-bark 'symbol)
(let ((mc/enclose-search-term 'symbols))
(mc/mark-all-like-this)))
;;;###autoload
(defun mc/mark-all-in-region (beg end &optional search)
"Find and mark all the parts in the region matching the given search"
(interactive "r")
(let ((search (or search (read-from-minibuffer "Mark all in region: ")))
(case-fold-search nil))
(if (string= search "")
(message "Mark aborted")
(progn
(mc/remove-fake-cursors)
(goto-char beg)
(while (search-forward search end t)
(push-mark (match-beginning 0))
(mc/create-fake-cursor-at-point))
(let ((first (mc/furthest-cursor-before-point)))
(if (not first)
(error "Search failed for %S" search)
(mc/pop-state-from-overlay first)))
(if (> (mc/num-cursors) 1)
(multiple-cursors-mode 1)
(multiple-cursors-mode 0))))))
;;;###autoload
(defun mc/mark-all-in-region-regexp (beg end)
"Find and mark all the parts in the region matching the given regexp."
(interactive "r")
(let ((search (read-regexp "Mark regexp in region: "))
(case-fold-search nil))
(if (string= search "")
(message "Mark aborted")
(progn
(mc/remove-fake-cursors)
(goto-char beg)
(let ((lastmatch))
(while (and (< (point) end) ; can happen because of (forward-char)
(search-forward-regexp search end t))
(push-mark (match-beginning 0))
(mc/create-fake-cursor-at-point)
(setq lastmatch (point))
(when (= (point) (match-beginning 0))
(forward-char)))
(unless lastmatch
(error "Search failed for %S" search)))
(goto-char (match-end 0))
(if (< (mc/num-cursors) 3)
(multiple-cursors-mode 0)
(mc/pop-state-from-overlay (mc/furthest-cursor-before-point))
(multiple-cursors-mode 1))))))
(when (not (fboundp 'set-temporary-overlay-map))
;; Backport this function from newer emacs versions
(defun set-temporary-overlay-map (map &optional keep-pred)
"Set a new keymap that will only exist for a short period of time.
The new keymap to use must be given in the MAP variable. When to
remove the keymap depends on user input and KEEP-PRED:
- if KEEP-PRED is nil (the default), the keymap disappears as
soon as any key is pressed, whether or not the key is in MAP;
- if KEEP-PRED is t, the keymap disappears as soon as a key *not*
in MAP is pressed;
- otherwise, KEEP-PRED must be a 0-arguments predicate that will
decide if the keymap should be removed (if predicate returns
nil) or kept (otherwise). The predicate will be called after
each key sequence."
(let* ((clearfunsym (make-symbol "clear-temporary-overlay-map"))
(overlaysym (make-symbol "t"))
(alist (list (cons overlaysym map)))
(clearfun
`(lambda ()
(unless ,(cond ((null keep-pred) nil)
((eq t keep-pred)
`(eq this-command
(lookup-key ',map
(this-command-keys-vector))))
(t `(funcall ',keep-pred)))
(remove-hook 'pre-command-hook ',clearfunsym)
(setq emulation-mode-map-alists
(delq ',alist emulation-mode-map-alists))))))
(set overlaysym overlaysym)
(fset clearfunsym clearfun)
(add-hook 'pre-command-hook clearfunsym)
(push alist emulation-mode-map-alists))))
;;;###autoload
(defun mc/mark-more-like-this-extended ()
"Like mark-more-like-this, but then lets you adjust with arrows key.
The adjustments work like this:
<up> Mark previous like this and set direction to 'up
<down> Mark next like this and set direction to 'down
If direction is 'up:
<left> Skip past the cursor furthest up
<right> Remove the cursor furthest up
If direction is 'down:
<left> Remove the cursor furthest down
<right> Skip past the cursor furthest down
The bindings for these commands can be changed. See `mc/mark-more-like-this-extended-keymap'."
(interactive)
(mc/mmlte--down)
(set-temporary-overlay-map mc/mark-more-like-this-extended-keymap t))
(defvar mc/mark-more-like-this-extended-direction nil
"When using mc/mark-more-like-this-extended are we working on the next or previous cursors?")
(make-variable-buffer-local 'mc/mark-more-like-this-extended)
(defun mc/mmlte--message ()
(if (eq mc/mark-more-like-this-extended-direction 'up)
(message "<up> to mark previous, <left> to skip, <right> to remove, <down> to mark next")
(message "<down> to mark next, <right> to skip, <left> to remove, <up> to mark previous")))
(defun mc/mmlte--up ()
(interactive)
(mc/mark-previous-like-this 1)
(setq mc/mark-more-like-this-extended-direction 'up)
(mc/mmlte--message))
(defun mc/mmlte--down ()
(interactive)
(mc/mark-next-like-this 1)
(setq mc/mark-more-like-this-extended-direction 'down)
(mc/mmlte--message))
(defun mc/mmlte--left ()
(interactive)
(if (eq mc/mark-more-like-this-extended-direction 'down)
(mc/unmark-next-like-this)
(mc/skip-to-previous-like-this))
(mc/mmlte--message))
(defun mc/mmlte--right ()
(interactive)
(if (eq mc/mark-more-like-this-extended-direction 'up)
(mc/unmark-previous-like-this)
(mc/skip-to-next-like-this))
(mc/mmlte--message))
(defvar mc/mark-more-like-this-extended-keymap (make-sparse-keymap))
(define-key mc/mark-more-like-this-extended-keymap (kbd "<up>") 'mc/mmlte--up)
(define-key mc/mark-more-like-this-extended-keymap (kbd "<down>") 'mc/mmlte--down)
(define-key mc/mark-more-like-this-extended-keymap (kbd "<left>") 'mc/mmlte--left)
(define-key mc/mark-more-like-this-extended-keymap (kbd "<right>") 'mc/mmlte--right)
(defvar mc--restrict-mark-all-to-symbols nil)
;;;###autoload
(defun mc/mark-all-like-this-dwim (arg)
"Tries to guess what you want to mark all of.
Can be pressed multiple times to increase selection.
With prefix, it behaves the same as original `mc/mark-all-like-this'"
(interactive "P")
(if arg
(mc/mark-all-like-this)
(if (and (not (use-region-p))
(derived-mode-p 'sgml-mode)
(mc--on-tag-name-p))
(mc/mark-sgml-tag-pair)
(let ((before (mc/num-cursors)))
(unless (eq last-command 'mc/mark-all-like-this-dwim)
(setq mc--restrict-mark-all-to-symbols nil))
(unless (use-region-p)
(mc--mark-symbol-at-point)
(setq mc--restrict-mark-all-to-symbols t))
(if mc--restrict-mark-all-to-symbols
(mc/mark-all-symbols-like-this-in-defun)
(mc/mark-all-like-this-in-defun))
(when (<= (mc/num-cursors) before)
(if mc--restrict-mark-all-to-symbols
(mc/mark-all-symbols-like-this)
(mc/mark-all-like-this)))
(when (<= (mc/num-cursors) before)
(mc/mark-all-like-this))))))
;;;###autoload
(defun mc/mark-all-dwim (arg)
"Tries even harder to guess what you want to mark all of.
If the region is active and spans multiple lines, it will behave
as if `mc/mark-all-in-region'. With the prefix ARG, it will call
`mc/edit-lines' instead.
If the region is inactive or on a single line, it will behave like
`mc/mark-all-like-this-dwim'."
(interactive "P")
(if (and (use-region-p)
(not (> (mc/num-cursors) 1))
(not (= (mc/line-number-at-pos (region-beginning))
(mc/line-number-at-pos (region-end)))))
(if arg
(call-interactively 'mc/edit-lines)
(call-interactively 'mc/mark-all-in-region))
(progn
(setq this-command 'mc/mark-all-like-this-dwim)
(mc/mark-all-like-this-dwim arg))))
(defun mc--in-defun ()
(bounds-of-thing-at-point 'defun))
;;;###autoload
(defun mc/mark-all-like-this-in-defun ()
"Mark all like this in defun."
(interactive)
(if (mc--in-defun)
(save-restriction
(widen)
(narrow-to-defun)
(mc/mark-all-like-this))
(mc/mark-all-like-this)))
;;;###autoload
(defun mc/mark-all-words-like-this-in-defun ()
"Mark all words like this in defun."
(interactive)
(mc--select-thing-at-point-or-bark 'word)
(if (mc--in-defun)
(save-restriction
(widen)
(narrow-to-defun)
(mc/mark-all-words-like-this))
(mc/mark-all-words-like-this)))
;;;###autoload
(defun mc/mark-all-symbols-like-this-in-defun ()
"Mark all symbols like this in defun."
(interactive)
(mc--select-thing-at-point-or-bark 'symbol)
(if (mc--in-defun)
(save-restriction
(widen)
(narrow-to-defun)
(mc/mark-all-symbols-like-this))
(mc/mark-all-symbols-like-this)))
(defun mc--mark-symbol-at-point ()
"Select the symbol under cursor"
(interactive)
(when (not (use-region-p))
(let ((b (bounds-of-thing-at-point 'symbol)))
(goto-char (car b))
(set-mark (cdr b)))))
(defun mc--get-nice-sgml-context ()
(car
(last
(progn
(when (looking-at "<") (forward-char 1))
(when (looking-back ">") (forward-char -1))
(sgml-get-context)))))
(defun mc--on-tag-name-p ()
(let* ((context (save-excursion (mc--get-nice-sgml-context)))
(tag-name-len (length (aref context 4)))
(beg (aref context 2))
(end (+ beg tag-name-len (if (eq 'open (aref context 1)) 1 3))))
(and context
(>= (point) beg)
(<= (point) end))))
;;;###autoload
(defun mc/toggle-cursor-on-click (event)
"Add a cursor where you click, or remove a fake cursor that is
already there."
(interactive "e")
(mouse-minibuffer-check event)
;; Use event-end in case called from mouse-drag-region.
;; If EVENT is a click, event-end and event-start give same value.
(let ((position (event-end event)))
(if (not (windowp (posn-window position)))
(error "Position not in text area of window"))
(select-window (posn-window position))
(let ((pt (posn-point position)))
(if (numberp pt)
;; is there a fake cursor with the actual *point* right where we are?
(let ((existing (mc/fake-cursor-at-point pt)))
(if existing
(mc/remove-fake-cursor existing)
(save-excursion
(goto-char pt)
(mc/create-fake-cursor-at-point))))))
(mc/maybe-multiple-cursors-mode)))
;;;###autoload
(defalias 'mc/add-cursor-on-click 'mc/toggle-cursor-on-click)
;;;###autoload
(defun mc/mark-sgml-tag-pair ()
"Mark the tag we're in and its pair for renaming."
(interactive)
(when (not (mc--inside-tag-p))
(error "Place point inside tag to rename."))
(let ((context (mc--get-nice-sgml-context)))
(if (looking-at "</")
(setq context (car (last (sgml-get-context)))))
(goto-char (aref context 2))
(let* ((tag-name (aref context 4))
(num-chars (length tag-name))
(master-start (1+ (point)))
(mirror-end (save-excursion
(sgml-skip-tag-forward 1)
(1- (point)))))
(goto-char (- mirror-end num-chars))
(set-mark mirror-end)
(mc/create-fake-cursor-at-point)
(goto-char master-start)
(set-mark (+ (point) num-chars))))
(mc/maybe-multiple-cursors-mode))
(defun mc--inside-tag-p ()
(save-excursion
(not (null (sgml-get-context)))))
(provide 'mc-mark-more)
;;; mc-mark-more.el ends here

Binary file not shown.

View file

@ -0,0 +1,22 @@
;;; mc-mark-pop.el --- Pop cursors off of the mark stack
(require 'multiple-cursors-core)
;;;###autoload
(defun mc/mark-pop ()
"Add a cursor at the current point, pop off mark ring and jump
to the popped mark."
(interactive)
;; If the mark happens to be at the current point, just pop that one off.
(while (eql (mark) (point))
(pop-mark))
(mc/create-fake-cursor-at-point)
(exchange-point-and-mark)
(pop-mark)
(mc/maybe-multiple-cursors-mode))
;; A good key binding for this feature is perhaps "C-S-p" ('p' for pop).
(provide 'mc-mark-pop)
;;; mc-mark-pop.el ends here

Binary file not shown.

View file

@ -0,0 +1,150 @@
;;; mc-separate-operations.el - functions that work differently on each cursor
;; Copyright (C) 2012-2016 Magnar Sveen
;; Author: Magnar Sveen <magnars@gmail.com>
;; Keywords: editing cursors
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This file contains functions that work differently on each cursor,
;; instead of treating all of them the same.
;; Please see multiple-cursors.el for more commentary.
;;; Code:
(require 'multiple-cursors-core)
(defcustom mc/insert-numbers-default 0
"The default number at which to start counting for
`mc/insert-numbers'"
:type 'integer
:group 'multiple-cursors)
(defvar mc--insert-numbers-number 0)
;;;###autoload
(defun mc/insert-numbers (arg)
"Insert increasing numbers for each cursor, starting at
`mc/insert-numbers-default' or ARG."
(interactive "P")
(setq mc--insert-numbers-number (or (and arg (prefix-numeric-value arg))
mc/insert-numbers-default))
(mc/for-each-cursor-ordered
(mc/execute-command-for-fake-cursor 'mc--insert-number-and-increase cursor)))
(defun mc--insert-number-and-increase ()
(interactive)
(insert (number-to-string mc--insert-numbers-number))
(setq mc--insert-numbers-number (1+ mc--insert-numbers-number)))
(defun mc--ordered-region-strings ()
(let (strings)
(save-excursion
(mc/for-each-cursor-ordered
(setq strings (cons (buffer-substring-no-properties
(mc/cursor-beg cursor)
(mc/cursor-end cursor)) strings))))
(nreverse strings)))
(defvar mc--insert-letters-number 0)
;;;###autoload
(defun mc/insert-letters (arg)
"Insert increasing letters for each cursor, starting at 0 or ARG.
Where letter[0]=a letter[2]=c letter[26]=aa"
(interactive "P")
(setq mc--insert-letters-number (or (and arg (prefix-numeric-value arg))
0))
(mc/for-each-cursor-ordered
(mc/execute-command-for-fake-cursor 'mc--insert-letter-and-increase cursor)))
(defun mc--number-to-letters (number)
(let ((letter
(char-to-string
(+ (mod number 26) ?a)))
(number2 (/ number 26)))
(if (> number2 0)
(concat (mc--number-to-letters (- number2 1)) letter)
letter)))
(defun mc--insert-letter-and-increase ()
(interactive)
(insert (mc--number-to-letters mc--insert-letters-number))
(setq mc--insert-letters-number (1+ mc--insert-letters-number)))
(defvar mc--strings-to-replace nil)
(defun mc--replace-region-strings-1 ()
(interactive)
(delete-region (region-beginning) (region-end))
(save-excursion (insert (car mc--strings-to-replace)))
(setq mc--strings-to-replace (cdr mc--strings-to-replace)))
(defun mc--replace-region-strings ()
(mc/for-each-cursor-ordered
(mc/execute-command-for-fake-cursor 'mc--replace-region-strings-1 cursor)))
;;;###autoload
(defun mc/reverse-regions ()
(interactive)
(if (not multiple-cursors-mode)
(progn
(mc/mark-next-lines 1)
(mc/reverse-regions)
(multiple-cursors-mode 0))
(unless (use-region-p)
(mc/execute-command-for-all-cursors 'mark-sexp))
(setq mc--strings-to-replace (nreverse (mc--ordered-region-strings)))
(mc--replace-region-strings)))
;;;###autoload
(defun mc/sort-regions ()
(interactive)
(unless (use-region-p)
(mc/execute-command-for-all-cursors 'mark-sexp))
(setq mc--strings-to-replace (sort (mc--ordered-region-strings) 'string<))
(mc--replace-region-strings))
;;;###autoload
(defun mc/vertical-align (character)
"Aligns all cursors vertically with a given CHARACTER to the one with the
highest column number (the rightest).
Might not behave as intended if more than one cursors are on the same line."
(interactive "c")
(let ((rightest-column (current-column)))
(mc/execute-command-for-all-cursors
(lambda () "get the rightest cursor"
(interactive)
(setq rightest-column (max (current-column) rightest-column))
))
(mc/execute-command-for-all-cursors
(lambda ()
(interactive)
(let ((missing-spaces (- rightest-column (current-column))))
(save-excursion (insert (make-string missing-spaces character)))
(forward-char missing-spaces))))))
;;;###autoload
(defun mc/vertical-align-with-space ()
"Aligns all cursors with whitespace like `mc/vertical-align' does"
(interactive)
(mc/vertical-align 32))
(provide 'mc-separate-operations)
;;; mc-separate-operations.el ends here

View file

@ -0,0 +1,362 @@
;;; multiple-cursors-autoloads.el --- automatically extracted autoloads
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "mc-cycle-cursors" "mc-cycle-cursors.el" (0
;;;;;; 0 0 0))
;;; Generated autoloads from mc-cycle-cursors.el
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "mc-cycle-cursors" '("mc/")))
;;;***
;;;### (autoloads nil "mc-edit-lines" "mc-edit-lines.el" (0 0 0 0))
;;; Generated autoloads from mc-edit-lines.el
(autoload 'mc/edit-lines "mc-edit-lines" "\
Add one cursor to each line of the active region.
Starts from mark and moves in straight down or up towards the
line point is on.
What is done with lines which are not long enough is governed by
`mc/edit-lines-empty-lines'. The prefix argument ARG can be used
to override this. If ARG is a symbol (when called from Lisp),
that symbol is used instead of `mc/edit-lines-empty-lines'.
Otherwise, if ARG negative, short lines will be ignored. Any
other non-nil value will cause short lines to be padded.
\(fn &optional ARG)" t nil)
(autoload 'mc/edit-ends-of-lines "mc-edit-lines" "\
Add one cursor to the end of each line in the active region.
\(fn)" t nil)
(autoload 'mc/edit-beginnings-of-lines "mc-edit-lines" "\
Add one cursor to the beginning of each line in the active region.
\(fn)" t nil)
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "mc-edit-lines" '("mc/edit-lines-empty-lines")))
;;;***
;;;### (autoloads nil "mc-hide-unmatched-lines-mode" "mc-hide-unmatched-lines-mode.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from mc-hide-unmatched-lines-mode.el
(autoload 'mc-hide-unmatched-lines-mode "mc-hide-unmatched-lines-mode" "\
Minor mode when enabled hides all lines where no cursors (and
also hum/lines-to-expand below and above) To make use of this
mode press \"C-'\" while multiple-cursor-mode is active. You can
still edit lines while you are in mc-hide-unmatched-lines
mode. To leave this mode press <return> or \"C-g\"
\(fn &optional ARG)" t nil)
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "mc-hide-unmatched-lines-mode" '("hum/")))
;;;***
;;;### (autoloads nil "mc-mark-more" "mc-mark-more.el" (0 0 0 0))
;;; Generated autoloads from mc-mark-more.el
(autoload 'mc/mark-next-like-this "mc-mark-more" "\
Find and mark the next part of the buffer matching the currently active region
If no region is active add a cursor on the next line
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next.
\(fn ARG)" t nil)
(autoload 'mc/mark-next-like-this-word "mc-mark-more" "\
Find and mark the next part of the buffer matching the currently active region
If no region is active, mark the word at the point and find the next match
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next.
\(fn ARG)" t nil)
(autoload 'mc/mark-next-word-like-this "mc-mark-more" "\
Find and mark the next word of the buffer matching the currently active region
The matching region must be a whole word to be a match
If no region is active, mark the symbol at the point and find the next match
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next.
\(fn ARG)" t nil)
(autoload 'mc/mark-next-symbol-like-this "mc-mark-more" "\
Find and mark the next symbol of the buffer matching the currently active region
The matching region must be a whole symbol to be a match
If no region is active, mark the symbol at the point and find the next match
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next.
\(fn ARG)" t nil)
(autoload 'mc/mark-previous-like-this "mc-mark-more" "\
Find and mark the previous part of the buffer matching the currently active region
If no region is active add a cursor on the previous line
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next.
\(fn ARG)" t nil)
(autoload 'mc/mark-previous-like-this-word "mc-mark-more" "\
Find and mark the previous part of the buffer matching the currently active region
If no region is active, mark the word at the point and find the previous match
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark previous.
\(fn ARG)" t nil)
(autoload 'mc/mark-previous-word-like-this "mc-mark-more" "\
Find and mark the previous part of the buffer matching the currently active region
The matching region must be a whole word to be a match
If no region is active add a cursor on the previous line
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next.
\(fn ARG)" t nil)
(autoload 'mc/mark-previous-symbol-like-this "mc-mark-more" "\
Find and mark the previous part of the buffer matching the currently active region
The matching region must be a whole symbol to be a match
If no region is active add a cursor on the previous line
With negative ARG, delete the last one instead.
With zero ARG, skip the last one and mark next.
\(fn ARG)" t nil)
(autoload 'mc/mark-next-lines "mc-mark-more" "\
\(fn ARG)" t nil)
(autoload 'mc/mark-previous-lines "mc-mark-more" "\
\(fn ARG)" t nil)
(autoload 'mc/unmark-next-like-this "mc-mark-more" "\
Deselect next part of the buffer matching the currently active region.
\(fn)" t nil)
(autoload 'mc/unmark-previous-like-this "mc-mark-more" "\
Deselect prev part of the buffer matching the currently active region.
\(fn)" t nil)
(autoload 'mc/skip-to-next-like-this "mc-mark-more" "\
Skip the current one and select the next part of the buffer matching the currently active region.
\(fn)" t nil)
(autoload 'mc/skip-to-previous-like-this "mc-mark-more" "\
Skip the current one and select the prev part of the buffer matching the currently active region.
\(fn)" t nil)
(autoload 'mc/mark-all-like-this "mc-mark-more" "\
Find and mark all the parts of the buffer matching the currently active region
\(fn)" t nil)
(autoload 'mc/mark-all-words-like-this "mc-mark-more" "\
\(fn)" t nil)
(autoload 'mc/mark-all-symbols-like-this "mc-mark-more" "\
\(fn)" t nil)
(autoload 'mc/mark-all-in-region "mc-mark-more" "\
Find and mark all the parts in the region matching the given search
\(fn BEG END &optional SEARCH)" t nil)
(autoload 'mc/mark-all-in-region-regexp "mc-mark-more" "\
Find and mark all the parts in the region matching the given regexp.
\(fn BEG END)" t nil)
(autoload 'mc/mark-more-like-this-extended "mc-mark-more" "\
Like mark-more-like-this, but then lets you adjust with arrows key.
The adjustments work like this:
<up> Mark previous like this and set direction to 'up
<down> Mark next like this and set direction to 'down
If direction is 'up:
<left> Skip past the cursor furthest up
<right> Remove the cursor furthest up
If direction is 'down:
<left> Remove the cursor furthest down
<right> Skip past the cursor furthest down
The bindings for these commands can be changed. See `mc/mark-more-like-this-extended-keymap'.
\(fn)" t nil)
(autoload 'mc/mark-all-like-this-dwim "mc-mark-more" "\
Tries to guess what you want to mark all of.
Can be pressed multiple times to increase selection.
With prefix, it behaves the same as original `mc/mark-all-like-this'
\(fn ARG)" t nil)
(autoload 'mc/mark-all-dwim "mc-mark-more" "\
Tries even harder to guess what you want to mark all of.
If the region is active and spans multiple lines, it will behave
as if `mc/mark-all-in-region'. With the prefix ARG, it will call
`mc/edit-lines' instead.
If the region is inactive or on a single line, it will behave like
`mc/mark-all-like-this-dwim'.
\(fn ARG)" t nil)
(autoload 'mc/mark-all-like-this-in-defun "mc-mark-more" "\
Mark all like this in defun.
\(fn)" t nil)
(autoload 'mc/mark-all-words-like-this-in-defun "mc-mark-more" "\
Mark all words like this in defun.
\(fn)" t nil)
(autoload 'mc/mark-all-symbols-like-this-in-defun "mc-mark-more" "\
Mark all symbols like this in defun.
\(fn)" t nil)
(autoload 'mc/toggle-cursor-on-click "mc-mark-more" "\
Add a cursor where you click, or remove a fake cursor that is
already there.
\(fn EVENT)" t nil)
(defalias 'mc/add-cursor-on-click 'mc/toggle-cursor-on-click)
(autoload 'mc/mark-sgml-tag-pair "mc-mark-more" "\
Mark the tag we're in and its pair for renaming.
\(fn)" t nil)
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "mc-mark-more" '("mc/" "mc--")))
;;;***
;;;### (autoloads nil "mc-mark-pop" "mc-mark-pop.el" (0 0 0 0))
;;; Generated autoloads from mc-mark-pop.el
(autoload 'mc/mark-pop "mc-mark-pop" "\
Add a cursor at the current point, pop off mark ring and jump
to the popped mark.
\(fn)" t nil)
;;;***
;;;### (autoloads nil "mc-separate-operations" "mc-separate-operations.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from mc-separate-operations.el
(autoload 'mc/insert-numbers "mc-separate-operations" "\
Insert increasing numbers for each cursor, starting at
`mc/insert-numbers-default' or ARG.
\(fn ARG)" t nil)
(autoload 'mc/insert-letters "mc-separate-operations" "\
Insert increasing letters for each cursor, starting at 0 or ARG.
Where letter[0]=a letter[2]=c letter[26]=aa
\(fn ARG)" t nil)
(autoload 'mc/reverse-regions "mc-separate-operations" "\
\(fn)" t nil)
(autoload 'mc/sort-regions "mc-separate-operations" "\
\(fn)" t nil)
(autoload 'mc/vertical-align "mc-separate-operations" "\
Aligns all cursors vertically with a given CHARACTER to the one with the
highest column number (the rightest).
Might not behave as intended if more than one cursors are on the same line.
\(fn CHARACTER)" t nil)
(autoload 'mc/vertical-align-with-space "mc-separate-operations" "\
Aligns all cursors with whitespace like `mc/vertical-align' does
\(fn)" t nil)
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "mc-separate-operations" '("mc/insert-numbers-default" "mc--")))
;;;***
;;;### (autoloads nil "multiple-cursors-core" "multiple-cursors-core.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from multiple-cursors-core.el
(autoload 'multiple-cursors-mode "multiple-cursors-core" "\
Mode while multiple cursors are active.
\(fn &optional ARG)" t nil)
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "multiple-cursors-core" '("multiple-cursors-mode" "unsupported-cmd" "deactivate-cursor-after-undo" "activate-cursor-for-undo")))
;;;***
;;;### (autoloads nil "rectangular-region-mode" "rectangular-region-mode.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from rectangular-region-mode.el
(autoload 'set-rectangular-region-anchor "rectangular-region-mode" "\
Anchors the rectangular region at point.
Think of this one as `set-mark' except you're marking a rectangular region. It is
an exceedingly quick way of adding multiple cursors to multiple lines.
\(fn)" t nil)
(autoload 'rectangular-region-mode "rectangular-region-mode" "\
A mode for creating a rectangular region to edit
\(fn &optional ARG)" t nil)
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "rectangular-region-mode" '("rectangular-region-mode" "rrm/")))
;;;***
;;;### (autoloads nil nil ("multiple-cursors-pkg.el" "multiple-cursors.el")
;;;;;; (0 0 0 0))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; multiple-cursors-autoloads.el ends here

View file

@ -0,0 +1,852 @@
;;; multiple-cursors-core.el --- An experiment in multiple cursors for emacs.
;; Copyright (C) 2012-2016 Magnar Sveen
;; Author: Magnar Sveen <magnars@gmail.com>
;; Keywords: editing cursors
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This file contains the core functionality of multiple-cursors.
;; Please see multiple-cursors.el for more commentary.
;;; Code:
(require 'cl-lib)
(require 'rect)
(defvar mc--read-char)
(defface mc/cursor-face
'((t (:inverse-video t)))
"The face used for fake cursors"
:group 'multiple-cursors)
(defface mc/cursor-bar-face
`((t (:height 1 :background ,(face-attribute 'cursor :background))))
"The face used for fake cursors if the cursor-type is bar"
:group 'multiple-cursors)
(defface mc/region-face
'((t :inherit region))
"The face used for fake regions"
:group 'multiple-cursors)
(defmacro mc/add-fake-cursor-to-undo-list (&rest forms)
"Make sure point is in the right place when undoing"
(let ((uc (make-symbol "undo-cleaner")))
`(let ((,uc (cons 'apply (cons 'deactivate-cursor-after-undo (list id)))))
(setq buffer-undo-list (cons ,uc buffer-undo-list))
,@forms
(if (eq ,uc (car buffer-undo-list)) ;; if nothing has been added to the undo-list
(setq buffer-undo-list (cdr buffer-undo-list)) ;; then pop the cleaner right off again
(setq buffer-undo-list ;; otherwise add a function to activate this cursor
(cons (cons 'apply (cons 'activate-cursor-for-undo (list id))) buffer-undo-list))))))
(defun mc/all-fake-cursors (&optional start end)
(cl-remove-if-not 'mc/fake-cursor-p
(overlays-in (or start (point-min))
(or end (point-max)))))
(defmacro mc/for-each-fake-cursor (&rest forms)
"Runs the body for each fake cursor, bound to the name cursor"
`(mapc #'(lambda (cursor) ,@forms)
(mc/all-fake-cursors)))
(defmacro mc/save-excursion (&rest forms)
"Saves and restores all the state that multiple-cursors cares about."
(let ((cs (make-symbol "current-state")))
`(let ((,cs (mc/store-current-state-in-overlay
(make-overlay (point) (point) nil nil t))))
(overlay-put ,cs 'type 'original-cursor)
(save-excursion ,@forms)
(mc/pop-state-from-overlay ,cs))))
(defun mc--compare-by-overlay-start (o1 o2)
(< (overlay-start o1) (overlay-start o2)))
(defmacro mc/for-each-cursor-ordered (&rest forms)
"Runs the body for each cursor, fake and real, bound to the name cursor"
(let ((rci (make-symbol "real-cursor-id")))
`(let ((,rci (overlay-get (mc/create-fake-cursor-at-point) 'mc-id)))
(mapc #'(lambda (cursor)
(when (mc/fake-cursor-p cursor)
,@forms))
(sort (overlays-in (point-min) (point-max)) 'mc--compare-by-overlay-start))
(mc/pop-state-from-overlay (mc/cursor-with-id ,rci)))))
(defmacro mc/save-window-scroll (&rest forms)
"Saves and restores the window scroll position"
(let ((p (make-symbol "p"))
(s (make-symbol "start"))
(h (make-symbol "hscroll")))
`(let ((,p (set-marker (make-marker) (point)))
(,s (set-marker (make-marker) (window-start)))
(,h (window-hscroll)))
,@forms
(goto-char ,p)
(set-window-start nil ,s t)
(set-window-hscroll nil ,h)
(set-marker ,p nil)
(set-marker ,s nil))))
(defun mc/cursor-is-bar ()
"Return non-nil if the cursor is a bar."
(or (eq cursor-type 'bar)
(and (listp cursor-type)
(eq (car cursor-type) 'bar))))
(defun mc/line-number-at-pos (&optional pos absolute)
"Faster implementation of `line-number-at-pos'."
(if pos
(save-excursion
(if absolute
(save-restriction
(widen)
(goto-char pos)
(string-to-number (format-mode-line "%l")))
(goto-char pos)
(string-to-number (format-mode-line "%l"))))
(string-to-number (format-mode-line "%l"))))
(defun mc/make-cursor-overlay-at-eol (pos)
"Create overlay to look like cursor at end of line."
(let ((overlay (make-overlay pos pos nil nil nil)))
(if (mc/cursor-is-bar)
(overlay-put overlay 'before-string (propertize "|" 'face 'mc/cursor-bar-face))
(overlay-put overlay 'after-string (propertize " " 'face 'mc/cursor-face)))
overlay))
(defun mc/make-cursor-overlay-inline (pos)
"Create overlay to look like cursor inside text."
(let ((overlay (make-overlay pos (1+ pos) nil nil nil)))
(if (mc/cursor-is-bar)
(overlay-put overlay 'before-string (propertize "|" 'face 'mc/cursor-bar-face))
(overlay-put overlay 'face 'mc/cursor-face))
overlay))
(defun mc/make-cursor-overlay-at-point ()
"Create overlay to look like cursor.
Special case for end of line, because overlay over a newline
highlights the entire width of the window."
(if (eolp)
(mc/make-cursor-overlay-at-eol (point))
(mc/make-cursor-overlay-inline (point))))
(defun mc/make-region-overlay-between-point-and-mark ()
"Create overlay to look like active region."
(let ((overlay (make-overlay (mark) (point) nil nil t)))
(overlay-put overlay 'face 'mc/region-face)
(overlay-put overlay 'type 'additional-region)
overlay))
(defvar mc/cursor-specific-vars '(transient-mark-mode
kill-ring
kill-ring-yank-pointer
mark-ring
mark-active
yank-undo-function
autopair-action
autopair-wrap-action
temporary-goal-column
er/history
dabbrev--abbrev-char-regexp
dabbrev--check-other-buffers
dabbrev--friend-buffer-list
dabbrev--last-abbrev-location
dabbrev--last-abbreviation
dabbrev--last-buffer
dabbrev--last-buffer-found
dabbrev--last-direction
dabbrev--last-expansion
dabbrev--last-expansion-location
dabbrev--last-table)
"A list of vars that need to be tracked on a per-cursor basis.")
(defun mc/store-current-state-in-overlay (o)
"Store relevant info about point and mark in the given overlay."
(overlay-put o 'point (set-marker (make-marker) (point)))
(overlay-put o 'mark (set-marker (make-marker)
(let ((mark-even-if-inactive t))
(mark))))
(dolist (var mc/cursor-specific-vars)
(when (boundp var) (overlay-put o var (symbol-value var))))
o)
(defun mc/restore-state-from-overlay (o)
"Restore point and mark from stored info in the given overlay."
(goto-char (overlay-get o 'point))
(set-marker (mark-marker) (overlay-get o 'mark))
(dolist (var mc/cursor-specific-vars)
(when (boundp var) (set var (overlay-get o var)))))
(defun mc/remove-fake-cursor (o)
"Delete overlay with state, including dependent overlays and markers."
(set-marker (overlay-get o 'point) nil)
(set-marker (overlay-get o 'mark) nil)
(mc/delete-region-overlay o)
(delete-overlay o))
(defun mc/pop-state-from-overlay (o)
"Restore the state stored in given overlay and then remove the overlay."
(mc/restore-state-from-overlay o)
(mc/remove-fake-cursor o))
(defun mc/delete-region-overlay (o)
"Remove the dependent region overlay for a given cursor overlay."
(ignore-errors
(delete-overlay (overlay-get o 'region-overlay))))
(defvar mc--current-cursor-id 0
"Var to store increasing id of fake cursors, used to keep track of them for undo.")
(defun mc/create-cursor-id ()
"Returns a unique cursor id"
(cl-incf mc--current-cursor-id))
(defvar mc--max-cursors-original nil
"This variable maintains the original maximum number of cursors.
When `mc/create-fake-cursor-at-point' is called and
`mc/max-cursors' is overridden, this value serves as a backup so
that `mc/max-cursors' can take on a new value. When
`mc/remove-fake-cursors' is called, the values are reset.")
(defcustom mc/max-cursors nil
"Safety ceiling for the number of active cursors.
If your emacs slows down or freezes when using too many cursors,
customize this value appropriately.
Cursors will be added until this value is reached, at which point
you can either temporarily override the value or abort the
operation entirely.
If this value is nil, there is no ceiling."
:type '(integer)
:group 'multiple-cursors)
(defun mc/create-fake-cursor-at-point (&optional id)
"Add a fake cursor and possibly a fake active region overlay based on point and mark.
Saves the current state in the overlay to be restored later."
(unless mc--max-cursors-original
(setq mc--max-cursors-original mc/max-cursors))
(when mc/max-cursors
(unless (< (mc/num-cursors) mc/max-cursors)
(if (yes-or-no-p (format "%d active cursors. Continue? " (mc/num-cursors)))
(setq mc/max-cursors (read-number "Enter a new, temporary maximum: "))
(mc/remove-fake-cursors)
(error "Aborted: too many cursors"))))
(let ((overlay (mc/make-cursor-overlay-at-point)))
(overlay-put overlay 'mc-id (or id (mc/create-cursor-id)))
(overlay-put overlay 'type 'fake-cursor)
(overlay-put overlay 'priority 100)
(mc/store-current-state-in-overlay overlay)
(when (use-region-p)
(overlay-put overlay 'region-overlay
(mc/make-region-overlay-between-point-and-mark)))
overlay))
(defun mc/execute-command (cmd)
"Run command, simulating the parts of the command loop that makes sense for fake cursors."
(setq this-command cmd)
(run-hooks 'pre-command-hook)
(unless (eq this-command 'ignore)
(call-interactively cmd))
(run-hooks 'post-command-hook)
(when deactivate-mark (deactivate-mark)))
(defvar mc--executing-command-for-fake-cursor nil)
(defun mc/execute-command-for-fake-cursor (cmd cursor)
(let ((mc--executing-command-for-fake-cursor t)
(id (overlay-get cursor 'mc-id))
(annoying-arrows-mode nil)
(smooth-scroll-margin 0))
(mc/add-fake-cursor-to-undo-list
(mc/pop-state-from-overlay cursor)
(ignore-errors
(mc/execute-command cmd)
(mc/create-fake-cursor-at-point id)))))
(defun mc/execute-command-for-all-fake-cursors (cmd)
"Calls CMD interactively for each cursor.
It works by moving point to the fake cursor, setting
up the proper environment, and then removing the cursor.
After executing the command, it sets up a new fake
cursor with updated info."
(mc/save-excursion
(mc/save-window-scroll
(mc/for-each-fake-cursor
(save-excursion
(mc/execute-command-for-fake-cursor cmd cursor)))))
(mc--reset-read-prompts))
(defun mc/execute-command-for-all-cursors (cmd)
"Calls CMD interactively for the real cursor and all fakes."
(call-interactively cmd)
(mc/execute-command-for-all-fake-cursors cmd))
;; Intercept some reading commands so you won't have to
;; answer them for every single cursor
(defvar mc--read-char nil)
(defvar multiple-cursors-mode nil)
(defadvice read-char (around mc-support activate)
(if (not multiple-cursors-mode)
ad-do-it
(unless mc--read-char
(setq mc--read-char ad-do-it))
(setq ad-return-value mc--read-char)))
(defvar mc--read-quoted-char nil)
(defadvice read-quoted-char (around mc-support activate)
(if (not multiple-cursors-mode)
ad-do-it
(unless mc--read-quoted-char
(setq mc--read-quoted-char ad-do-it))
(setq ad-return-value mc--read-quoted-char)))
(defun mc--reset-read-prompts ()
(setq mc--read-char nil)
(setq mc--read-quoted-char nil))
(mc--reset-read-prompts)
(defun mc/fake-cursor-p (o)
"Predicate to check if an overlay is a fake cursor"
(eq (overlay-get o 'type) 'fake-cursor))
(defun mc/cursor-with-id (id)
"Find the first cursor with the given id, or nil"
(cl-find-if #'(lambda (o) (and (mc/fake-cursor-p o)
(= id (overlay-get o 'mc-id))))
(overlays-in (point-min) (point-max))))
(defvar mc--stored-state-for-undo nil
"Variable to keep the state of the real cursor while undoing a fake one")
(defun activate-cursor-for-undo (id)
"Called when undoing to temporarily activate the fake cursor which action is being undone."
(let ((cursor (mc/cursor-with-id id)))
(when cursor
(setq mc--stored-state-for-undo (mc/store-current-state-in-overlay
(make-overlay (point) (point) nil nil t)))
(mc/pop-state-from-overlay cursor))))
(defun deactivate-cursor-after-undo (id)
"Called when undoing to reinstate the real cursor after undoing a fake one."
(when mc--stored-state-for-undo
(mc/create-fake-cursor-at-point id)
(mc/pop-state-from-overlay mc--stored-state-for-undo)
(setq mc--stored-state-for-undo nil)))
(defcustom mc/always-run-for-all nil
"Disables whitelisting and always executes commands for every fake cursor."
:type '(boolean)
:group 'multiple-cursors)
(defcustom mc/always-repeat-command nil
"Disables confirmation for `mc/repeat-command' command."
:type '(boolean)
:group 'multiple-cursors)
(defun mc/prompt-for-inclusion-in-whitelist (original-command)
"Asks the user, then adds the command either to the once-list or the all-list."
(let ((all-p (y-or-n-p (format "Do %S for all cursors?" original-command))))
(if all-p
(add-to-list 'mc/cmds-to-run-for-all original-command)
(add-to-list 'mc/cmds-to-run-once original-command))
(mc/save-lists)
all-p))
(defun mc/num-cursors ()
"The number of cursors (real and fake) in the buffer."
(1+ (cl-count-if 'mc/fake-cursor-p
(overlays-in (point-min) (point-max)))))
(defvar mc--this-command nil
"Used to store the original command being run.")
(make-variable-buffer-local 'mc--this-command)
(defun mc/make-a-note-of-the-command-being-run ()
"Used with pre-command-hook to store the original command being run.
Since that cannot be reliably determined in the post-command-hook.
Specifically, this-original-command isn't always right, because it could have
been remapped. And certain modes (cua comes to mind) will change their
remapping based on state. So a command that changes the state will afterwards
not be recognized through the command-remapping lookup."
(unless mc--executing-command-for-fake-cursor
(let ((cmd (or (command-remapping this-original-command)
this-original-command)))
(setq mc--this-command (and (not (eq cmd 'god-mode-self-insert))
cmd)))))
(defun mc/execute-this-command-for-all-cursors ()
"Wrap around `mc/execute-this-command-for-all-cursors-1' to protect hook."
(condition-case error
(mc/execute-this-command-for-all-cursors-1)
(error
(message "[mc] problem in `mc/execute-this-command-for-all-cursors': %s"
(error-message-string error)))))
;; execute-kbd-macro should never be run for fake cursors. The real cursor will
;; execute the keyboard macro, resulting in new commands in the command loop,
;; and the fake cursors can pick up on those instead.
(defadvice execute-kbd-macro (around skip-fake-cursors activate)
(unless mc--executing-command-for-fake-cursor
ad-do-it))
(defun mc/execute-this-command-for-all-cursors-1 ()
"Used with post-command-hook to execute supported commands for all cursors.
It uses two lists of commands to know what to do: the run-once
list and the run-for-all list. If a command is in neither of these lists,
it will prompt for the proper action and then save that preference.
Some commands are so unsupported that they are even prevented for
the original cursor, to inform about the lack of support."
(unless mc--executing-command-for-fake-cursor
(if (eq 1 (mc/num-cursors)) ;; no fake cursors? disable mc-mode
(multiple-cursors-mode 0)
(when this-original-command
(let ((original-command (or mc--this-command
(command-remapping this-original-command)
this-original-command)))
;; skip keyboard macros, since they will generate actual commands that are
;; also run in the command loop - we'll handle those later instead.
(when (functionp original-command)
;; if it's a lambda, we can't know if it's supported or not
;; - so go ahead and assume it's ok, because we're just optimistic like that
(if (or (not (symbolp original-command))
;; lambda registered by smartrep
(string-prefix-p "(" (symbol-name original-command)))
(mc/execute-command-for-all-fake-cursors original-command)
;; smartrep `intern's commands into own obarray to help
;; `describe-bindings'. So, let's re-`intern' here to
;; make the command comparable by `eq'.
(setq original-command (intern (symbol-name original-command)))
;; otherwise it's a symbol, and we can be more thorough
(if (get original-command 'mc--unsupported)
(message "%S is not supported with multiple cursors%s"
original-command
(get original-command 'mc--unsupported))
;; lazy-load the user's list file
(mc/load-lists)
(when (and original-command
(not (memq original-command mc--default-cmds-to-run-once))
(not (memq original-command mc/cmds-to-run-once))
(or mc/always-run-for-all
(memq original-command mc--default-cmds-to-run-for-all)
(memq original-command mc/cmds-to-run-for-all)
(mc/prompt-for-inclusion-in-whitelist original-command)))
(mc/execute-command-for-all-fake-cursors original-command))))))))))
(defun mc/remove-fake-cursors ()
"Remove all fake cursors.
Do not use to conclude editing with multiple cursors. For that
you should disable multiple-cursors-mode."
(mc/for-each-fake-cursor
(mc/remove-fake-cursor cursor))
(when mc--max-cursors-original
(setq mc/max-cursors mc--max-cursors-original))
(setq mc--max-cursors-original nil))
(defun mc/keyboard-quit ()
"Deactivate mark if there are any active, otherwise exit multiple-cursors-mode."
(interactive)
(if (not (use-region-p))
(multiple-cursors-mode 0)
(deactivate-mark)))
(defun mc/repeat-command ()
"Run last command from `command-history' for every fake cursor."
(interactive)
(when (or mc/always-repeat-command
(y-or-n-p (format "[mc] repeat complex command: %s? " (caar command-history))))
(mc/execute-command-for-all-fake-cursors
(lambda () (interactive)
(cl-letf (((symbol-function 'read-from-minibuffer)
(lambda (p &optional i k r h d m) (read i))))
(repeat-complex-command 0))))))
(defvar mc/keymap nil
"Keymap while multiple cursors are active.
Main goal of the keymap is to rebind C-g and <return> to conclude
multiple cursors editing.")
(unless mc/keymap
(setq mc/keymap (make-sparse-keymap))
(define-key mc/keymap (kbd "C-g") 'mc/keyboard-quit)
(define-key mc/keymap (kbd "<return>") 'multiple-cursors-mode)
(define-key mc/keymap (kbd "C-:") 'mc/repeat-command)
(when (fboundp 'phi-search)
(define-key mc/keymap (kbd "C-s") 'phi-search))
(when (fboundp 'phi-search-backward)
(define-key mc/keymap (kbd "C-r") 'phi-search-backward)))
(defun mc--all-equal (list)
"Are all the items in LIST equal?"
(let ((first (car list))
(all-equal t))
(while (and all-equal list)
(setq all-equal (equal first (car list)))
(setq list (cdr list)))
all-equal))
(defun mc--kill-ring-entries ()
"Return the latest kill-ring entry for each cursor.
The entries are returned in the order they are found in the buffer."
(let (entries)
(mc/for-each-cursor-ordered
(setq entries (cons (car (overlay-get cursor 'kill-ring)) entries)))
(reverse entries)))
(defun mc--maybe-set-killed-rectangle ()
"Add the latest kill-ring entry for each cursor to killed-rectangle.
So you can paste it in later with `yank-rectangle'."
(let ((entries (let (mc/max-cursors) (mc--kill-ring-entries))))
(unless (mc--all-equal entries)
(setq killed-rectangle entries))))
(defvar mc/unsupported-minor-modes '(company-mode auto-complete-mode flyspell-mode jedi-mode)
"List of minor-modes that does not play well with multiple-cursors.
They are temporarily disabled when multiple-cursors are active.")
(defvar mc/temporarily-disabled-minor-modes nil
"The list of temporarily disabled minor-modes.")
(make-variable-buffer-local 'mc/temporarily-disabled-minor-modes)
(defun mc/temporarily-disable-minor-mode (mode)
"If MODE is available and turned on, remember that and turn it off."
(when (and (boundp mode) (eval mode))
(add-to-list 'mc/temporarily-disabled-minor-modes mode)
(funcall mode -1)))
(defun mc/temporarily-disable-unsupported-minor-modes ()
(mapc 'mc/temporarily-disable-minor-mode mc/unsupported-minor-modes))
(defun mc/enable-minor-mode (mode)
(funcall mode 1))
(defun mc/enable-temporarily-disabled-minor-modes ()
(mapc 'mc/enable-minor-mode mc/temporarily-disabled-minor-modes)
(setq mc/temporarily-disabled-minor-modes nil))
(defcustom mc/mode-line
`(" mc:" (:eval (format ,(propertize "%d" 'face 'font-lock-warning-face)
(mc/num-cursors))))
"What to display in the mode line while multiple-cursors-mode is active."
:group 'multiple-cursors)
(put 'mc/mode-line 'risky-local-variable t)
;;;###autoload
(define-minor-mode multiple-cursors-mode
"Mode while multiple cursors are active."
nil mc/mode-line mc/keymap
(if multiple-cursors-mode
(progn
(mc/temporarily-disable-unsupported-minor-modes)
(add-hook 'pre-command-hook 'mc/make-a-note-of-the-command-being-run nil t)
(add-hook 'post-command-hook 'mc/execute-this-command-for-all-cursors t t)
(run-hooks 'multiple-cursors-mode-enabled-hook))
(remove-hook 'post-command-hook 'mc/execute-this-command-for-all-cursors t)
(remove-hook 'pre-command-hook 'mc/make-a-note-of-the-command-being-run t)
(setq mc--this-command nil)
(mc--maybe-set-killed-rectangle)
(mc/remove-fake-cursors)
(mc/enable-temporarily-disabled-minor-modes)
(run-hooks 'multiple-cursors-mode-disabled-hook)))
(add-hook 'after-revert-hook #'(lambda () (multiple-cursors-mode 0)))
(defun mc/maybe-multiple-cursors-mode ()
"Enable multiple-cursors-mode if there is more than one currently active cursor."
(if (> (mc/num-cursors) 1)
(multiple-cursors-mode 1)
(multiple-cursors-mode 0)))
(defmacro unsupported-cmd (cmd msg)
"Adds command to list of unsupported commands and prevents it
from being executed if in multiple-cursors-mode."
`(progn
(put (quote ,cmd) 'mc--unsupported ,msg)
(defadvice ,cmd (around unsupported-advice activate)
"command isn't supported with multiple cursors"
(unless (and multiple-cursors-mode (called-interactively-p 'any))
ad-do-it))))
;; Commands that does not work with multiple-cursors
(unsupported-cmd isearch-forward ". Feel free to add a compatible version.")
(unsupported-cmd isearch-backward ". Feel free to add a compatible version.")
;; Make sure pastes from other programs are added to all kill-rings when yanking
(defadvice current-kill (before interprogram-paste-for-all-cursors
(n &optional do-not-move) activate)
(let ((interprogram-paste (and (= n 0)
interprogram-paste-function
(funcall interprogram-paste-function))))
(when interprogram-paste
;; Add interprogram-paste to normal kill ring, just
;; like current-kill usually does for itself.
;; We have to do the work for it though, since the funcall only returns
;; something once. It is not a pure function.
(let ((interprogram-cut-function nil))
(if (listp interprogram-paste)
(mapc 'kill-new (nreverse interprogram-paste))
(kill-new interprogram-paste))
;; And then add interprogram-paste to the kill-rings
;; of all the other cursors too.
(mc/for-each-fake-cursor
(let ((kill-ring (overlay-get cursor 'kill-ring))
(kill-ring-yank-pointer (overlay-get cursor 'kill-ring-yank-pointer)))
(if (listp interprogram-paste)
(mapc 'kill-new (nreverse interprogram-paste))
(kill-new interprogram-paste))
(overlay-put cursor 'kill-ring kill-ring)
(overlay-put cursor 'kill-ring-yank-pointer kill-ring-yank-pointer)))))))
(defcustom mc/list-file (locate-user-emacs-file ".mc-lists.el")
"The position of the file that keeps track of your preferences
for running commands with multiple cursors."
:type 'file
:group 'multiple-cursors)
(defvar mc--list-file-loaded nil
"Whether the list file has already been loaded.")
(defun mc/load-lists ()
"Loads preferences for running commands with multiple cursors from `mc/list-file'"
(unless mc--list-file-loaded
(load mc/list-file 'noerror 'nomessage)
(setq mc--list-file-loaded t)))
(defun mc/dump-list (list-symbol)
"Insert (setq 'LIST-SYMBOL LIST-VALUE) to current buffer."
(cl-symbol-macrolet ((value (symbol-value list-symbol)))
(insert "(setq " (symbol-name list-symbol) "\n"
" '(")
(newline-and-indent)
(set list-symbol
(sort value (lambda (x y) (string-lessp (symbol-name x)
(symbol-name y)))))
(mapc #'(lambda (cmd) (insert (format "%S" cmd)) (newline-and-indent))
value)
(insert "))")
(newline)))
(defun mc/save-lists ()
"Saves preferences for running commands with multiple cursors to `mc/list-file'"
(with-temp-file mc/list-file
(emacs-lisp-mode)
(insert ";; This file is automatically generated by the multiple-cursors extension.")
(newline)
(insert ";; It keeps track of your preferences for running commands with multiple cursors.")
(newline)
(newline)
(mc/dump-list 'mc/cmds-to-run-for-all)
(newline)
(mc/dump-list 'mc/cmds-to-run-once)))
(defvar mc/cmds-to-run-once nil
"Commands to run only once in multiple-cursors-mode.")
(defvar mc--default-cmds-to-run-once nil
"Default set of commands to run only once in multiple-cursors-mode.")
(setq mc--default-cmds-to-run-once '(mc/edit-lines
mc/edit-ends-of-lines
mc/edit-beginnings-of-lines
mc/mark-next-like-this
mc/mark-next-like-this-word
mc/mark-next-like-this-symbol
mc/mark-next-word-like-this
mc/mark-next-symbol-like-this
mc/mark-previous-like-this
mc/mark-previous-like-this-word
mc/mark-previous-like-this-symbol
mc/mark-previous-word-like-this
mc/mark-previous-symbol-like-this
mc/mark-all-like-this
mc/mark-all-words-like-this
mc/mark-all-symbols-like-this
mc/mark-more-like-this-extended
mc/mark-all-like-this-in-defun
mc/mark-all-words-like-this-in-defun
mc/mark-all-symbols-like-this-in-defun
mc/mark-all-like-this-dwim
mc/mark-all-dwim
mc/mark-sgml-tag-pair
mc/insert-numbers
mc/insert-letters
mc/sort-regions
mc/reverse-regions
mc/cycle-forward
mc/cycle-backward
mc/add-cursor-on-click
mc/mark-pop
mc/add-cursors-to-all-matches
mc/mmlte--left
mc/mmlte--right
mc/mmlte--up
mc/mmlte--down
mc/unmark-next-like-this
mc/unmark-previous-like-this
mc/skip-to-next-like-this
mc/skip-to-previous-like-this
rrm/switch-to-multiple-cursors
mc-hide-unmatched-lines-mode
mc/repeat-command
hum/keyboard-quit
hum/unhide-invisible-overlays
save-buffer
ido-exit-minibuffer
ivy-done
exit-minibuffer
minibuffer-complete-and-exit
execute-extended-command
eval-expression
undo
redo
undo-tree-undo
undo-tree-redo
universal-argument
universal-argument-more
universal-argument-other-key
negative-argument
digit-argument
top-level
recenter-top-bottom
describe-mode
describe-key-1
describe-function
describe-bindings
describe-prefix-bindings
view-echo-area-messages
other-window
kill-buffer-and-window
split-window-right
split-window-below
delete-other-windows
toggle-window-split
mwheel-scroll
scroll-up-command
scroll-down-command
mouse-set-point
mouse-drag-region
quit-window
toggle-read-only
windmove-left
windmove-right
windmove-up
windmove-down
repeat-complex-command))
(defvar mc--default-cmds-to-run-for-all nil
"Default set of commands that should be mirrored by all cursors")
(setq mc--default-cmds-to-run-for-all '(mc/keyboard-quit
self-insert-command
quoted-insert
previous-line
next-line
newline
newline-and-indent
open-line
delete-blank-lines
transpose-chars
transpose-lines
transpose-paragraphs
transpose-regions
join-line
right-char
right-word
forward-char
forward-word
left-char
left-word
backward-char
backward-word
forward-paragraph
backward-paragraph
upcase-word
downcase-word
capitalize-word
forward-list
backward-list
hippie-expand
hippie-expand-lines
yank
yank-pop
append-next-kill
kill-word
kill-line
kill-whole-line
backward-kill-word
backward-delete-char-untabify
delete-char delete-forward-char
delete-backward-char
py-electric-backspace
c-electric-backspace
org-delete-backward-char
cperl-electric-backspace
python-indent-dedent-line-backspace
paredit-backward-delete
autopair-backspace
just-one-space
zap-to-char
end-of-line
set-mark-command
exchange-point-and-mark
cua-set-mark
cua-replace-region
cua-delete-region
move-end-of-line
beginning-of-line
move-beginning-of-line
kill-ring-save
back-to-indentation
subword-forward
subword-backward
subword-mark
subword-kill
subword-backward-kill
subword-transpose
subword-capitalize
subword-upcase
subword-downcase
er/expand-region
er/contract-region
smart-forward
smart-backward
smart-up
smart-down))
(defvar mc/cmds-to-run-for-all nil
"Commands to run for all cursors in multiple-cursors-mode")
(provide 'multiple-cursors-core)
;; Local Variables:
;; coding: utf-8
;; End:
;;; multiple-cursors-core.el ends here

View file

@ -0,0 +1,5 @@
(define-package "multiple-cursors" "20191210.1759" "Multiple cursors for Emacs."
'((cl-lib "0.5")))
;; Local Variables:
;; no-byte-compile: t
;; End:

View file

@ -0,0 +1,203 @@
;;; multiple-cursors.el --- Multiple cursors for emacs.
;; Copyright (C) 2012-2016 Magnar Sveen
;; Author: Magnar Sveen <magnars@gmail.com>
;; Version: 1.4.0
;; Keywords: editing cursors
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Multiple cursors for Emacs. This is some pretty crazy functionality, so yes,
;; there are kinks. Don't be afraid though, I've been using it since 2011 with
;; great success and much merriment.
;; ## Basic usage
;; Start out with:
;; (require 'multiple-cursors)
;; Then you have to set up your keybindings - multiple-cursors doesn't presume to
;; know how you'd like them laid out. Here are some examples:
;; When you have an active region that spans multiple lines, the following will
;; add a cursor to each line:
;; (global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
;; When you want to add multiple cursors not based on continuous lines, but based on
;; keywords in the buffer, use:
;; (global-set-key (kbd "C->") 'mc/mark-next-like-this)
;; (global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
;; (global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this)
;; First mark the word, then add more cursors.
;; To get out of multiple-cursors-mode, press `<return>` or `C-g`. The latter will
;; first disable multiple regions before disabling multiple cursors. If you want to
;; insert a newline in multiple-cursors-mode, use `C-j`.
;; ## Video
;; You can [watch an intro to multiple-cursors at Emacs Rocks](http://emacsrocks.com/e13.html).
;; ## Command overview
;; ### Mark one more occurrence
;; - `mc/mark-next-like-this`: Adds a cursor and region at the next part of the buffer forwards that matches the current region.
;; - `mc/mark-next-like-this-word`: Adds a cursor and region at the next part of the buffer forwards that matches the current region, if no region is selected it selects the word at the point.
;; - `mc/mark-next-like-this-symbol`: Adds a cursor and region at the next part of the buffer forwards that matches the current region, if no region is selected it selects the symbol at the point.
;; - `mc/mark-next-word-like-this`: Like `mc/mark-next-like-this` but only for whole words.
;; - `mc/mark-next-symbol-like-this`: Like `mc/mark-next-like-this` but only for whole symbols.
;; - `mc/mark-previous-like-this`: Adds a cursor and region at the next part of the buffer backwards that matches the current region.
;; - `mc/mark-previous-word-like-this`: Like `mc/mark-previous-like-this` but only for whole words.
;; - `mc/mark-previous-symbol-like-this`: Like `mc/mark-previous-like-this` but only for whole symbols.
;; - `mc/mark-more-like-this-extended`: Use arrow keys to quickly mark/skip next/previous occurrences.
;; - `mc/add-cursor-on-click`: Bind to a mouse event to add cursors by clicking. See tips-section.
;; ### Mark many occurrences
;; - `mc/mark-all-like-this`: Marks all parts of the buffer that matches the current region.
;; - `mc/mark-all-words-like-this`: Like `mc/mark-all-like-this` but only for whole words.
;; - `mc/mark-all-symbols-like-this`: Like `mc/mark-all-like-this` but only for whole symbols.
;; - `mc/mark-all-in-region`: Prompts for a string to match in the region, adding cursors to all of them.
;; - `mc/mark-all-like-this-in-defun`: Marks all parts of the current defun that matches the current region.
;; - `mc/mark-all-words-like-this-in-defun`: Like `mc/mark-all-like-this-in-defun` but only for whole words.
;; - `mc/mark-all-symbols-like-this-in-defun`: Like `mc/mark-all-like-this-in-defun` but only for whole symbols.
;; - `mc/mark-all-like-this-dwim`: Tries to be smart about marking everything you want. Can be pressed multiple times.
;; ### Special
;; - `set-rectangular-region-anchor`: Think of this one as `set-mark` except you're marking a rectangular region.
;; - `mc/mark-sgml-tag-pair`: Mark the current opening and closing tag.
;; - `mc/insert-numbers`: Insert increasing numbers for each cursor, top to bottom.
;; - `mc/insert-letters`: Insert increasing letters for each cursor, top to bottom.
;; - `mc/sort-regions`: Sort the marked regions alphabetically.
;; - `mc/reverse-regions`: Reverse the order of the marked regions.
;; ## Tips and tricks
;; - To get out of multiple-cursors-mode, press `<return>` or `C-g`. The latter will
;; first disable multiple regions before disabling multiple cursors. If you want to
;; insert a newline in multiple-cursors-mode, use `C-j`.
;;
;; - Sometimes you end up with cursors outside of your view. You can
;; scroll the screen to center on each cursor with `C-v` and `M-v`.
;;
;; - Try pressing `mc/mark-next-like-this` with no region selected. It will just add a cursor
;; on the next line.
;;
;; - Try pressing `mc/mark-next-like-this-word` or
;; `mc/mark-next-like-this-symbol` with no region selected. It will
;; mark the symbol and add a cursor at the next occurrence
;;
;; - Try pressing `mc/mark-all-like-this-dwim` on a tagname in html-mode.
;;
;; - Notice that the number of cursors active can be seen in the modeline.
;;
;; - If you get out of multiple-cursors-mode and yank - it will yank only
;; from the kill-ring of main cursor. To yank from the kill-rings of
;; every cursor use yank-rectangle, normally found at C-x r y.
;;
;; - You can use `mc/reverse-regions` with nothing selected and just one cursor.
;; It will then flip the sexp at point and the one below it.
;;
;; - If you would like to keep the global bindings clean, and get custom keybindings
;; when the region is active, you can try [region-bindings-mode](https://github.com/fgallina/region-bindings-mode).
;;
;; BTW, I highly recommend adding `mc/mark-next-like-this` to a key binding that's
;; right next to the key for `er/expand-region`.
;; ### Binding mouse events
;; To override a mouse event, you will likely have to also unbind the
;; `down-mouse` part of the event. Like this:
;;
;; (global-unset-key (kbd "M-<down-mouse-1>"))
;; (global-set-key (kbd "M-<mouse-1>") 'mc/add-cursor-on-click)
;;
;; Or you can do like me and find an unused, but less convenient, binding:
;;
;; (global-set-key (kbd "C-S-<mouse-1>") 'mc/add-cursor-on-click)
;; ## Unknown commands
;; Multiple-cursors uses two lists of commands to know what to do: the run-once list
;; and the run-for-all list. It comes with a set of defaults, but it would be beyond silly
;; to try and include all the known Emacs commands.
;; So that's why multiple-cursors occasionally asks what to do about a command. It will
;; then remember your choice by saving it in `~/.emacs.d/.mc-lists.el`. You can change
;; the location with:
;; (setq mc/list-file "/my/preferred/file")
;; ## Known limitations
;; * isearch-forward and isearch-backward aren't supported with multiple cursors.
;; You should feel free to add a simplified version that can work with it.
;; * Commands run with `M-x` won't be repeated for all cursors.
;; * All key bindings that refer to lambdas are always run for all cursors. If you
;; need to limit it, you will have to give it a name.
;; * Redo might screw with your cursors. Undo works very well.
;; ## Contribute
;; Yes, please do. There's a suite of tests, so remember to add tests for your
;; specific feature, or I might break it later.
;; You'll find the repo at:
;; https://github.com/magnars/multiple-cursors.el
;; To fetch the test dependencies:
;; $ cd /path/to/multiple-cursors
;; $ git submodule update --init
;; Run the tests with:
;; $ ./util/ecukes/ecukes --graphical
;; ## Contributors
;; * [Takafumi Arakaki](https://github.com/tkf) made .mc-lists.el diff friendly
;; * [Marco Baringer](https://github.com/segv) contributed looping to mc/cycle and adding cursors without region for mark-more.
;; * [Ivan Andrus](https://github.com/gvol) added showing number of cursors in mode-line
;; * [Fuco](https://github.com/Fuco1) added the first version of `mc/mark-all-like-this-dwim`
;; Thanks!
;;; Code:
(defgroup multiple-cursors nil
"Multiple cursors for emacs."
:group 'editing)
(require 'mc-edit-lines)
(require 'mc-cycle-cursors)
(require 'mc-mark-more)
(require 'mc-mark-pop)
(require 'rectangular-region-mode)
(require 'mc-separate-operations)
(require 'mc-hide-unmatched-lines-mode)
(provide 'multiple-cursors)
;;; multiple-cursors.el ends here

View file

@ -0,0 +1,125 @@
;;; rectangular-region-mode.el
;; Copyright (C) 2012-2016 Magnar Sveen
;; Author: Magnar Sveen <magnars@gmail.com>
;; Keywords: editing cursors
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; (global-set-key (kbd "H-SPC") 'set-rectangular-region-anchor)
;; Think of this one as `set-mark` except you're marking a rectangular region. It is
;; an exceedingly quick way of adding multiple cursors to multiple lines.
;;; Code:
(require 'multiple-cursors-core)
(defvar rrm/anchor (make-marker)
"The position in the buffer that anchors the rectangular region.")
(defvar rectangular-region-mode-map (make-sparse-keymap)
"Keymap for rectangular region is mainly for rebinding C-g")
(define-key rectangular-region-mode-map (kbd "C-g") 'rrm/keyboard-quit)
(define-key rectangular-region-mode-map (kbd "<return>") 'rrm/switch-to-multiple-cursors)
(defvar rectangular-region-mode nil)
(defun rrm/keyboard-quit ()
"Exit rectangular-region-mode."
(interactive)
(rectangular-region-mode 0)
(rrm/remove-rectangular-region-overlays)
(deactivate-mark))
;; Bind this to a key (for instance H-SPC) to start rectangular-region-mode
;;;###autoload
(defun set-rectangular-region-anchor ()
"Anchors the rectangular region at point.
Think of this one as `set-mark' except you're marking a rectangular region. It is
an exceedingly quick way of adding multiple cursors to multiple lines."
(interactive)
(set-marker rrm/anchor (point))
(push-mark (point))
(rectangular-region-mode 1))
(defun rrm/remove-rectangular-region-overlays ()
"Remove all rectangular-region overlays."
(mc/remove-fake-cursors)
(mapc #'(lambda (o)
(when (eq (overlay-get o 'type) 'additional-region)
(delete-overlay o)))
(overlays-in (point-min) (point-max))))
(defun rrm/repaint ()
"Start from the anchor and draw a rectangle between it and point."
(if (not rectangular-region-mode)
(remove-hook 'post-command-hook 'rrm/repaint t)
;; else
(rrm/remove-rectangular-region-overlays)
(let* ((annoying-arrows-mode nil)
(point-column (current-column))
(point-line (mc/line-number-at-pos))
(anchor-column (save-excursion (goto-char rrm/anchor) (current-column)))
(anchor-line (save-excursion (goto-char rrm/anchor) (mc/line-number-at-pos)))
(left-column (if (< point-column anchor-column) point-column anchor-column))
(right-column (if (> point-column anchor-column) point-column anchor-column))
(navigation-step (if (< point-line anchor-line) 1 -1)))
(move-to-column anchor-column)
(set-mark (point))
(move-to-column point-column)
(mc/save-excursion
(while (not (= anchor-line (mc/line-number-at-pos)))
(forward-line navigation-step)
(move-to-column anchor-column)
(when (= anchor-column (current-column))
(set-mark (point))
(move-to-column point-column)
(when (= point-column (current-column))
(mc/create-fake-cursor-at-point))))))))
(defun rrm/switch-to-multiple-cursors (&rest forms)
"Switch from rectangular-region-mode to multiple-cursors-mode."
(interactive)
(rectangular-region-mode 0)
(multiple-cursors-mode 1))
(defadvice er/expand-region (before switch-from-rrm-to-mc activate)
(when rectangular-region-mode
(rrm/switch-to-multiple-cursors)))
(defadvice kill-ring-save (before switch-from-rrm-to-mc activate)
(when rectangular-region-mode
(rrm/switch-to-multiple-cursors)))
;;;###autoload
(define-minor-mode rectangular-region-mode
"A mode for creating a rectangular region to edit"
nil " rr" rectangular-region-mode-map
(if rectangular-region-mode
(progn
(add-hook 'after-change-functions 'rrm/switch-to-multiple-cursors t t)
(add-hook 'post-command-hook 'rrm/repaint t t))
(remove-hook 'after-change-functions 'rrm/switch-to-multiple-cursors t)
(remove-hook 'post-command-hook 'rrm/repaint t)
(set-marker rrm/anchor nil)))
(provide 'rectangular-region-mode)
;;; rectangular-region-mode.el ends here

View file

@ -0,0 +1,27 @@
;;; xref-js2-autoloads.el --- automatically extracted autoloads
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "xref-js2" "xref-js2.el" (0 0 0 0))
;;; Generated autoloads from xref-js2.el
(autoload 'xref-js2-xref-backend "xref-js2" "\
Xref-Js2 backend for Xref.
\(fn)" nil nil)
(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "xref-js2" '("xref-js2-")))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; xref-js2-autoloads.el ends here

View file

@ -0,0 +1,2 @@
;;; -*- no-byte-compile: t -*-
(define-package "xref-js2" "20190915.2032" "Jump to references/definitions using ag & js2-mode's AST" '((emacs "25") (js2-mode "20150909")) :commit "6f1ed5dae0c2485416196a51f2fa92f32e4b8262" :keywords '("javascript" "convenience" "tools") :authors '(("Nicolas Petton" . "nicolas@petton.fr")) :maintainer '("Nicolas Petton" . "nicolas@petton.fr") :url "https://github.com/NicolasPetton/xref-js2")

View file

@ -0,0 +1,273 @@
;;; xref-js2.el --- Jump to references/definitions using ag & js2-mode's AST -*- lexical-binding: t; -*-
;; Copyright (C) 2016 Nicolas Petton
;; Author: Nicolas Petton <nicolas@petton.fr>
;; URL: https://github.com/NicolasPetton/xref-js2
;; Package-Version: 20190915.2032
;; Keywords: javascript, convenience, tools
;; Version: 1.0
;; Package: xref-js2
;; Package-Requires: ((emacs "25") (js2-mode "20150909"))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; xref-js2 adds an xref backend for JavaScript files.
;;
;; Instead of using a tag system, it relies on `ag' to query the codebase of a
;; project. This might sound crazy at first, but it turns out that `ag' is so
;; fast that jumping using xref-js2 is most of the time instantaneous, even on
;; fairly large JavaScript codebase (it successfully works with 50k lines of JS
;; code).
;;
;; Because line by line regexp search has its downside, xref-js2 does a second
;; pass on result candidates and eliminates possible false positives using
;; `js2-mode''s AST, thus giving very accurate results.
;;; Code:
(require 'subr-x)
(require 'xref)
(require 'seq)
(require 'map)
(require 'js2-mode)
(require 'vc)
(defcustom xref-js2-search-program 'ag
"The backend program used for searching."
:type 'symbol
:group 'xref-js2
:options '(ag rg))
(defcustom xref-js2-ag-arguments '("--js" "--noheading" "--nocolor")
"Default arguments passed to ag."
:type 'list
:group 'xref-js2)
(defcustom xref-js2-js-extensions '("js" "mjs" "jsx" "ts" "tsx")
"Extensions for file types xref-js2 is expected to search.
warning, this is currently only supported by ripgrep, not ag.
if an empty-list/nil no filtering based on file extension will
take place."
:type 'list
:group 'xref-js2)
(defcustom xref-js2-rg-arguments '("--no-heading"
"--line-number" ; not activated by default on comint
"--pcre2" ; provides regexp backtracking
"--ignore-case" ; ag is case insensitive by default
"--color" "never")
"Default arguments passed to ripgrep."
:type 'list
:group 'xref-js2)
(defcustom xref-js2-ignored-dirs '("bower_components"
"node_modules"
"build"
"lib")
"List of directories to be ignored when performing a search."
:type 'list
:group 'xref-js2)
(defcustom xref-js2-ignored-files '("*.min.js")
"List of files to be ignored when performing a search."
:type 'list
:group 'xref-js2)
(defcustom xref-js2-definitions-regexps '("\\b%s\\b[\\s]*[:=][^=]"
"function[\\s]+\\b%s\\b"
"class[\\s]+\\b%s\\b"
"(?<!new)[^.]%s[\\s]*\\(")
"List of regular expressions that match definitions of a symbol.
In each regexp string, '%s' is expanded with the searched symbol."
:type 'list
:group 'xref-js2)
(defcustom xref-js2-references-regexps '("\\b%s\\b(?!\\s*[:=][^=])")
"List of regular expressions that match references to a symbol.
In each regexp string, '%s' is expanded with the searched symbol."
:type 'list
:group 'xref-js2)
;;;###autoload
(defun xref-js2-xref-backend ()
"Xref-Js2 backend for Xref."
'xref-js2)
(cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-js2)))
(symbol-name (symbol-at-point)))
(cl-defmethod xref-backend-definitions ((_backend (eql xref-js2)) symbol)
(xref-js2--xref-find-definitions symbol))
(cl-defmethod xref-backend-references ((_backend (eql xref-js2)) symbol)
(xref-js2--xref-find-references symbol))
(defun xref-js2--xref-find-definitions (symbol)
"Return a list of candidates matching SYMBOL."
(seq-map (lambda (candidate)
(xref-js2--make-xref candidate))
(xref-js2--find-definitions symbol)))
(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-js2)))
"Return a list of terms for completions taken from the symbols in the current buffer.
The current implementation returns all the words in the buffer,
which is really sub optimal."
(let (words)
(save-excursion
(save-restriction
(widen)
(goto-char (point-min))
(while (re-search-forward "\\w+" nil t)
(add-to-list 'words (match-string-no-properties 0)))
(seq-uniq words)))))
(defun xref-js2--xref-find-references (symbol)
"Return a list of reference candidates matching SYMBOL."
(seq-map (lambda (candidate)
(xref-js2--make-xref candidate))
(xref-js2--find-references symbol)))
(defun xref-js2--make-xref (candidate)
"Return a new Xref object built from CANDIDATE."
(xref-make (map-elt candidate 'match)
(xref-make-file-location (map-elt candidate 'file)
(map-elt candidate 'line)
0)))
(defun xref-js2--find-definitions (symbol)
"Return a list of definitions for SYMBOL from an ag search."
(xref-js2--find-candidates
symbol
(xref-js2--make-regexp symbol xref-js2-definitions-regexps)))
(defun xref-js2--find-references (symbol)
"Return a list of references for SYMBOL from an ag search."
(xref-js2--find-candidates
symbol
(xref-js2--make-regexp symbol xref-js2-references-regexps)))
(defun xref-js2--make-regexp (symbol regexps)
"Return a regular expression to search for SYMBOL using REGEXPS.
REGEXPS must be a list of regular expressions, which are
concatenated together into one regexp, expanding occurrences of
'%s' with SYMBOL."
(mapconcat #'identity
(mapcar (lambda (str)
(format str symbol))
regexps) "|"))
(defun xref-js2--find-candidates (symbol regexp)
(let ((default-directory (xref-js2--root-dir))
matches)
(with-temp-buffer
(let* ((search-tuple (cond ;; => (prog-name . function-to-get-args)
((eq xref-js2-search-program 'rg)
'("rg" . xref-js2--search-rg-get-args))
(t ;; (eq xref-js2-search-program 'ag)
'("ag" . xref-js2--search-ag-get-args))))
(search-program (car search-tuple))
(search-args (remove nil ;; rm in case no search args given
(funcall (cdr search-tuple) regexp))))
(apply #'process-file (executable-find search-program) nil t nil search-args))
(goto-char (point-max)) ;; NOTE maybe redundant
(while (re-search-backward "^\\(.+\\)$" nil t)
(push (match-string-no-properties 1) matches)))
(seq-remove #'xref-js2--false-positive
(seq-map (lambda (match)
(xref-js2--candidate symbol match))
matches))))
(defun xref-js2--search-ag-get-args (regexp)
"Aggregate command line arguments to search for REGEXP using ag."
`(,@xref-js2-ag-arguments
,@(seq-mapcat (lambda (dir)
(list "--ignore-dir" dir))
xref-js2-ignored-dirs)
,@(seq-mapcat (lambda (file)
(list "--ignore" file))
xref-js2-ignored-files)
,regexp))
(defun xref-js2--search-rg-get-args (regexp)
"Aggregate command line arguments to search for REGEXP using ripgrep."
`(,@xref-js2-rg-arguments
,@(if (not xref-js2-js-extensions)
nil ;; no filtering based on extension
(seq-mapcat (lambda (ext)
(list "-g" (concat "*." ext)))
xref-js2-js-extensions))
,@(seq-mapcat (lambda (dir)
(list "-g" (concat "!" ; exclude not include
dir ; directory string
(unless (string-suffix-p "/" dir) ; pattern for a directory
"/")))) ; must end with a slash
xref-js2-ignored-dirs)
,@(seq-mapcat (lambda (pattern)
(list "-g" (concat "!" pattern)))
xref-js2-ignored-files)
,regexp))
(defun xref-js2--false-positive (candidate)
"Return non-nil if CANDIDATE is a false positive.
Filtering is done using the AST from js2-mode."
(let* ((file (map-elt candidate 'file))
(buffer-open (get-file-buffer file)))
(prog1
(with-current-buffer (find-file-noselect file t)
(save-excursion
(save-restriction
(widen)
(unless (or (eq major-mode 'js2-mode)
(seq-contains (map-keys minor-mode-alist) 'js2-minor-mode))
(js2-minor-mode 1))
(goto-char (point-min))
(forward-line (1- (map-elt candidate 'line)))
(search-forward (map-elt candidate 'symbol) nil t)
;; js2-mode fails to parse the AST for some minified files
(ignore-errors
(let ((node (js2-node-at-point)))
(or (js2-string-node-p node)
(js2-comment-node-p node))))))))))
(defun xref-js2--root-dir ()
"Return the root directory of the project."
(or (ignore-errors
(projectile-project-root))
(ignore-errors
(vc-root-dir))
(user-error "You are not in a project")))
(defun xref-js2--candidate (symbol match)
"Return a candidate alist built from SYMBOL and a raw MATCH result.
The MATCH is one output result from the ag search."
(let* ((attrs (split-string match ":" t))
(match (string-trim (mapconcat #'identity (cddr attrs) ":"))))
;; Some minified JS files might match a search. To avoid cluttering the
;; search result, we trim the output.
(when (> (seq-length match) 100)
(setq match (concat (seq-take match 100) "...")))
(list (cons 'file (expand-file-name (car attrs) (xref-js2--root-dir)))
(cons 'line (string-to-number (cadr attrs)))
(cons 'symbol symbol)
(cons 'match match))))
(provide 'xref-js2)
;;; xref-js2.el ends here

Binary file not shown.