2015-11-09 15:25:20 +09:00
|
|
|
;;; boon-arguments.el --- An Ergonomic Command Mode -*- lexical-binding: t -*-
|
2014-10-19 14:17:46 +02:00
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
2014-10-23 09:12:59 +02:00
|
|
|
|
2016-08-20 20:39:08 +02:00
|
|
|
;; A region list has either of the following forms:
|
|
|
|
;; ('region (begining . end) (begining . end) ...)
|
|
|
|
|
2014-10-19 14:17:46 +02:00
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
(require 'boon-core)
|
2015-10-16 14:29:28 +02:00
|
|
|
(require 'boon-regs)
|
2015-10-24 21:33:38 +02:00
|
|
|
(require 'multiple-cursors)
|
2014-10-19 14:17:46 +02:00
|
|
|
|
2015-03-19 10:50:55 +01:00
|
|
|
(defcustom boon-enclosures
|
2014-10-19 14:17:46 +02:00
|
|
|
'(
|
|
|
|
(?A . ("⟨" "⟩"))
|
|
|
|
(?a . ("<" ">"))
|
|
|
|
(?b . ("[" "]"))
|
|
|
|
(?c . ("{-" "-}"))
|
2015-03-19 10:50:55 +01:00
|
|
|
(?d . ("\"" "\"")) ;; double quotes
|
2015-10-16 09:59:31 +02:00
|
|
|
(?D . ("``" "''")) ;; Double quotes
|
2014-10-19 14:17:46 +02:00
|
|
|
(?f . ("«" "»")) ;; french quotes
|
|
|
|
(?h . ("#" "#")) ;; hash
|
|
|
|
(?m . ("`" "'"))
|
|
|
|
(?p . ("(" ")"))
|
|
|
|
(?q . ("'" "'"))
|
|
|
|
(?r . ("{" "}"))
|
|
|
|
(?o . ("⟦" "⟧")) ;; oxford brackets
|
|
|
|
(?t . ("~" "~")) ;; tilda
|
2015-03-19 10:50:55 +01:00
|
|
|
)
|
2015-10-15 23:02:57 +02:00
|
|
|
"Enclosures to use with the boon-enclose command."
|
2015-03-19 10:50:55 +01:00
|
|
|
:type '(alist :key-type character :value-type (list string))
|
|
|
|
:group 'boon
|
|
|
|
)
|
2014-10-19 14:17:46 +02:00
|
|
|
|
|
|
|
(defun boon-spec-enclosure ()
|
2015-10-14 13:59:53 +02:00
|
|
|
"Specify an enclosure style. To be used as an argument to interactive."
|
2015-10-16 14:31:03 +02:00
|
|
|
(let* ((c (boon-read-char "Specify the enclosure"))
|
2015-10-15 22:08:59 +02:00
|
|
|
(s (make-string 1 c))
|
|
|
|
(choice (assoc c boon-enclosures)))
|
|
|
|
(if choice (cdr choice) (list s s))))
|
2014-10-19 14:17:46 +02:00
|
|
|
|
2014-10-19 13:59:12 +02:00
|
|
|
(defun boon-select-thing-at-point (thing)
|
2014-10-23 09:12:59 +02:00
|
|
|
"Return a region list with a single item pointing to the THING at point."
|
2016-08-21 22:33:29 +02:00
|
|
|
(lambda ()(cons 'bounds (bounds-of-thing-at-point thing))))
|
2014-10-19 13:59:12 +02:00
|
|
|
|
|
|
|
(defun boon-select-from-region (select-fun)
|
2014-10-23 09:12:59 +02:00
|
|
|
"Return a region list with a single item: the region selected after calling SELECT-FUN (interactively)."
|
2014-10-19 13:59:12 +02:00
|
|
|
(interactive)
|
|
|
|
(save-excursion
|
2015-11-23 22:44:11 +01:00
|
|
|
;; FIXME: deactivate mark
|
2014-10-19 13:59:12 +02:00
|
|
|
(call-interactively select-fun)
|
2016-08-21 22:33:29 +02:00
|
|
|
(lambda ()(cons 'bounds (cons (region-beginning) (region-end))))))
|
2014-10-19 13:59:12 +02:00
|
|
|
|
|
|
|
(defun boon-select-wim () ;; what i mean
|
2014-10-23 09:12:59 +02:00
|
|
|
"Return a region list with a single item: either the symbol at point, or, if this fails, the sexp at point."
|
2014-10-19 13:59:12 +02:00
|
|
|
(interactive)
|
2016-08-21 22:33:29 +02:00
|
|
|
(lambda ()(let ((bounds (or (bounds-of-thing-at-point 'symbol)
|
|
|
|
(bounds-of-thing-at-point 'sexp))))
|
|
|
|
(cons 'bounds bounds))))
|
2015-10-16 09:59:31 +02:00
|
|
|
|
2015-11-09 20:29:16 +01:00
|
|
|
(defun boon-jump-over-blanks-forward ()
|
2014-10-23 09:12:59 +02:00
|
|
|
"Jump over blanks, forward."
|
2014-10-19 14:17:46 +02:00
|
|
|
(interactive)
|
|
|
|
(skip-chars-forward "\n\t "))
|
|
|
|
|
|
|
|
(defun boon-jump-over-blanks-backward ()
|
2014-10-23 09:12:59 +02:00
|
|
|
"Jump over blanks, backward."
|
2014-10-19 14:17:46 +02:00
|
|
|
(interactive)
|
|
|
|
(skip-chars-backward "\n\t "))
|
|
|
|
|
2015-11-23 22:44:11 +01:00
|
|
|
(defun boon-select-org-table-cell ()
|
2016-05-02 15:06:13 +02:00
|
|
|
"Return the region between pipes (|)."
|
2015-11-23 22:44:11 +01:00
|
|
|
(interactive)
|
2016-08-21 22:33:29 +02:00
|
|
|
(lambda ()(cons 'bounds
|
2015-11-23 22:44:11 +01:00
|
|
|
(cons (save-excursion
|
|
|
|
(skip-chars-backward "^|") (point))
|
|
|
|
(save-excursion
|
2016-08-21 22:33:29 +02:00
|
|
|
(skip-chars-forward "^|") (point))))))
|
2016-01-28 07:48:40 +01:00
|
|
|
|
2016-05-02 15:06:13 +02:00
|
|
|
(defun boon-select-justline ()
|
|
|
|
"Return the region of the current line, without any newline."
|
2016-08-20 20:39:08 +02:00
|
|
|
(interactive) (cons 'bounds (cons (line-beginning-position) (line-end-position))))
|
2016-01-28 07:48:40 +01:00
|
|
|
|
|
|
|
(defun boon-select-line (count)
|
|
|
|
"Return a region of COUNT visual lines."
|
|
|
|
(interactive "p")
|
|
|
|
(setq temporary-goal-column 0)
|
|
|
|
(boon-select-n count 'beginning-of-visual-line 'line-move-visual))
|
|
|
|
|
|
|
|
(defun boon-select-n (count goto-beginning forward-n)
|
|
|
|
"Return a region of COUNT objects defined by GOTO-BEGINNING and FORWARD-N."
|
2016-08-21 22:33:29 +02:00
|
|
|
(lambda()(save-excursion
|
2016-01-28 07:48:40 +01:00
|
|
|
(funcall goto-beginning)
|
2016-08-21 22:33:29 +02:00
|
|
|
(cons 'bounds (cons (point) (progn (funcall forward-n count) (point)))))))
|
2016-01-28 07:48:40 +01:00
|
|
|
|
|
|
|
(defun boon-select-paragraph (count) (interactive "p") (boon-select-n count 'start-of-paragraph-text 'forward-paragraph))
|
2016-08-21 22:33:29 +02:00
|
|
|
(defun boon-select-document () (interactive) (cons 'bounds (cons (point-min) (point-max))))
|
2014-10-19 13:59:12 +02:00
|
|
|
(defun boon-select-word () (interactive) (boon-select-thing-at-point 'word))
|
|
|
|
(defun boon-select-sentence () (interactive) (boon-select-thing-at-point 'sentence))
|
|
|
|
(defun boon-select-symbol () (interactive) (boon-select-thing-at-point 'symbol))
|
|
|
|
(defun boon-select-list () (interactive) (boon-select-thing-at-point 'list))
|
|
|
|
(defun boon-select-sexp () (interactive) (boon-select-thing-at-point 'sexp))
|
|
|
|
(defun boon-select-outside-pairs () (interactive) (boon-select-from-region 'er/mark-outside-pairs))
|
2015-08-19 11:30:39 +02:00
|
|
|
(defun boon-select-comment () (interactive) (boon-select-from-region 'er/mark-comment))
|
2014-10-19 13:59:12 +02:00
|
|
|
(defun boon-select-inside-pairs () (interactive) (boon-select-from-region 'er/mark-inside-pairs))
|
|
|
|
(defun boon-select-outside-quotes () (interactive) (boon-select-from-region 'er/mark-outside-quotes))
|
|
|
|
(defun boon-select-whitespace () (interactive) (boon-select-thing-at-point 'whitespace))
|
|
|
|
(defun boon-select-blanks ()
|
|
|
|
(interactive)
|
2016-08-20 20:39:08 +02:00
|
|
|
(cons 'bounds (cons
|
2014-10-19 13:59:12 +02:00
|
|
|
(save-excursion
|
|
|
|
(boon-jump-over-blanks-backward)
|
|
|
|
(point))
|
|
|
|
(save-excursion
|
2015-11-09 20:29:16 +01:00
|
|
|
(boon-jump-over-blanks-forward)
|
2014-10-19 13:59:12 +02:00
|
|
|
(point)))))
|
|
|
|
|
2016-01-27 16:36:49 +01:00
|
|
|
(defun boon-spec-string (prompt)
|
|
|
|
"Read a string using the region selection functionality.
|
|
|
|
Intented to be used as an argument to interactive.
|
|
|
|
Display PROMPT in the echo area."
|
2016-05-02 15:06:13 +02:00
|
|
|
(let ((head (read-event)))
|
|
|
|
(if (equal head ? ) (read-string (concat prompt ": "))
|
|
|
|
; if space, read a literal string, otherwise use the region specifier.
|
|
|
|
(setq unread-command-events
|
|
|
|
(cons head unread-command-events))
|
|
|
|
(let* ((regs (boon-spec-region prompt))
|
|
|
|
(reg (car regs)))
|
|
|
|
(buffer-substring-no-properties (boon-reg-begin reg) (boon-reg-end reg))))))
|
2016-01-27 16:36:49 +01:00
|
|
|
|
|
|
|
(defun boon-select-occurences (what where)
|
|
|
|
"Return the occurences of WHAT as sub-regions of WHERE."
|
|
|
|
(interactive (list (boon-spec-string "occurences of what?") (boon-spec-region "where?")))
|
|
|
|
(let ((result nil))
|
|
|
|
(save-excursion
|
|
|
|
(dolist (reg where)
|
|
|
|
(goto-char (boon-reg-begin reg))
|
|
|
|
(while (search-forward what (boon-reg-end reg) t)
|
|
|
|
(setq result (cons (boon-mk-reg (match-beginning 0)
|
2016-08-21 22:33:29 +02:00
|
|
|
(match-end 0)
|
|
|
|
(boon-reg-cursor reg))
|
2016-01-27 16:36:49 +01:00
|
|
|
result))))
|
|
|
|
(cons 'region result))))
|
2014-10-19 13:59:12 +02:00
|
|
|
|
|
|
|
(defun boon-select-borders (how-much regs)
|
2014-10-23 09:12:59 +02:00
|
|
|
"Return the bordering (of size HOW-MUCH) of a region list REGS.
|
|
|
|
This function is meant to be called interactively."
|
2016-08-21 22:33:29 +02:00
|
|
|
(interactive (list (prefix-numeric-value current-prefix-arg) (boon-spec-region-lazy "select contents")))
|
|
|
|
(lambda ()(cons 'region (apply 'append (mapcar (lambda (reg) (boon-borders reg how-much)) (mapcar 'boon-normalize-reg (funcall regs)))))))
|
2014-10-19 13:59:12 +02:00
|
|
|
|
2015-11-09 21:01:47 +01:00
|
|
|
(defun boon-select-with-spaces (regs)
|
|
|
|
"Return the regions REGS, including some surrounding spaces.
|
|
|
|
This function is meant to be called interactively."
|
2016-08-21 22:33:29 +02:00
|
|
|
(interactive (list (boon-spec-region-lazy "select with spaces")))
|
2016-08-22 21:24:33 +02:00
|
|
|
(lambda ()(cons 'region (mapcar (lambda (reg) (boon-include-surround-spaces reg)) (mapcar 'boon-normalize-reg (funcall regs))))))
|
2015-11-09 21:01:47 +01:00
|
|
|
|
2014-10-19 13:59:12 +02:00
|
|
|
(defun boon-select-content (regs)
|
2014-10-23 09:12:59 +02:00
|
|
|
"Return the contents (of size HOW-MUCH) of a region list REGS.
|
|
|
|
This function is meant to be called interactively."
|
2016-08-21 22:33:29 +02:00
|
|
|
(interactive (list (boon-spec-region-lazy "select borders")))
|
|
|
|
(lambda ()(cons 'region (mapcar 'boon-content (mapcar 'boon-normalize-reg (funcall regs))))))
|
2014-10-19 13:59:12 +02:00
|
|
|
|
2015-10-15 23:02:57 +02:00
|
|
|
(defun boon-bypass-mc ()
|
2015-10-24 21:33:38 +02:00
|
|
|
"Should we bypass multiple cursors when gathering regions?"
|
2015-10-15 23:02:57 +02:00
|
|
|
(and (bound-and-true-p multiple-cursors-mode)
|
|
|
|
(memq this-command mc/cmds-to-run-once)))
|
2016-08-21 22:33:29 +02:00
|
|
|
|
2015-10-15 22:00:16 +02:00
|
|
|
(defun boon-multiple-cursor-regs ()
|
2015-10-24 21:33:38 +02:00
|
|
|
"Return all regions defined by multiple-cursors-mode, and outside."
|
2016-08-20 20:39:08 +02:00
|
|
|
(cons (boon-mk-reg (mark) (point) nil)
|
2015-10-15 23:02:57 +02:00
|
|
|
(if (boon-bypass-mc)
|
2015-10-16 14:29:28 +02:00
|
|
|
;; TODO: is marker-position really necessary here?
|
2016-08-20 20:39:08 +02:00
|
|
|
(mapcar (lambda (o) (boon-mk-reg (marker-position (overlay-get o 'mark)) (marker-position (overlay-get o 'point)) o))
|
2016-08-21 22:33:29 +02:00
|
|
|
(mc/all-fake-cursors)))))
|
2015-10-15 22:00:16 +02:00
|
|
|
|
2015-10-16 09:59:31 +02:00
|
|
|
(defun boon-read-char (&optional prompt inherit-input-method seconds)
|
|
|
|
"Read a character, bypassing multiple cursors defadvice if applicable."
|
|
|
|
;; do this so that mc's read-char defadvice does not kick in; so we can actually read characters here.
|
|
|
|
;; a hack for now: as read-event doesn't do the same thing as read char.
|
|
|
|
(if (boon-bypass-mc)
|
|
|
|
(read-event prompt inherit-input-method seconds)
|
|
|
|
(read-char prompt inherit-input-method seconds)))
|
|
|
|
|
2014-10-19 13:59:12 +02:00
|
|
|
(defun boon-spec-region (msg)
|
2014-10-19 14:17:46 +02:00
|
|
|
"Specify a region concisely using the keyboard.
|
2016-08-21 22:33:29 +02:00
|
|
|
The prompt (as MSG) is displayed. This function returns a list
|
|
|
|
of regions (See boon-regs.el). If multiple-cursors are enabled
|
|
|
|
BUT the command is executed just once (not once per cursor), you
|
|
|
|
get a region for each cursor.
|
|
|
|
"
|
2015-10-15 23:02:57 +02:00
|
|
|
(let ((orig-regs (boon-multiple-cursor-regs)))
|
2016-08-21 22:33:29 +02:00
|
|
|
(if (use-region-p) orig-regs
|
|
|
|
(let ((selector (boon-spec-region-lazy msg)))
|
|
|
|
(apply 'append
|
|
|
|
(mapcar (lambda (in-reg)
|
|
|
|
(save-excursion
|
|
|
|
(goto-char (boon-reg-point in-reg))
|
|
|
|
(mapcar (lambda (r) (boon-mk-reg (boon-reg-mark r)
|
|
|
|
(boon-reg-point r)
|
|
|
|
(boon-reg-cursor in-reg)))
|
|
|
|
(funcall selector))))
|
|
|
|
orig-regs))))))
|
|
|
|
|
|
|
|
(defun boon-spec-region-lazy (msg)
|
|
|
|
"Specify a region selector concisely using the keyboard.
|
|
|
|
The prompt (as MSG) is displayed. This function returns a
|
|
|
|
non-interactive function which, when run, will return bounds.
|
|
|
|
The bounds have either of the following forms: ('bounds . begin
|
|
|
|
. end) OR a list of regions (see boon-regs.el) OR something else,
|
|
|
|
in which case the region is defined by the movement of the point.
|
|
|
|
"
|
|
|
|
(let ((my-prefix-arg 0)
|
|
|
|
(kmv boon-moves-map)
|
|
|
|
(kms boon-select-map)
|
|
|
|
last-char)
|
|
|
|
;; We read a move or selection, in both keymaps in parallel. First command found wins.
|
|
|
|
(while (and (or kmv kms) (not (commandp kms)) (not (commandp kmv)))
|
|
|
|
(let ((last-char (boon-read-char (format "%s %s" msg my-prefix-arg))))
|
|
|
|
(if (and (>= last-char ?0) (<= last-char ?9))
|
|
|
|
(setq my-prefix-arg (+ (- last-char ?0) (* 10 my-prefix-arg )))
|
|
|
|
(if kms (setq kms (lookup-key kms (vector last-char))))
|
|
|
|
(if kmv (setq kmv (lookup-key kmv (vector last-char)))))))
|
|
|
|
(when (eq my-prefix-arg 0) (setq my-prefix-arg nil))
|
2015-10-16 09:59:31 +02:00
|
|
|
;; The command is ready; we now execute it (once per cursor if applicable).
|
2016-08-21 22:33:29 +02:00
|
|
|
(if (or kms kmv)
|
|
|
|
(if (commandp kms)
|
|
|
|
;; we have a 'selection'. These commands may take prefix
|
|
|
|
;; args, which they parse right away, and return a
|
|
|
|
;; continuation constructing the region.
|
|
|
|
(let ((action (let ((current-prefix-arg my-prefix-arg))
|
|
|
|
(call-interactively kms)))) ;; must be called first so that the interactive arguments are read
|
|
|
|
(lambda () ;; FIXME: massage the return value into common format.
|
|
|
|
(let ((regs (funcall action)))
|
2016-08-22 21:24:33 +02:00
|
|
|
(cond ((eq (car regs) 'bounds)
|
|
|
|
(mapcar 'boon-reg-from-bounds (list (cdr regs))))
|
|
|
|
((eq (car regs) 'region)
|
|
|
|
(message "")
|
|
|
|
(cdr regs))
|
|
|
|
(t (error "unknown regs format"))))))
|
2016-08-21 22:33:29 +02:00
|
|
|
;; we have a 'move'. These commands do not take non-universal arguments. So just run it.
|
|
|
|
(lambda ()
|
|
|
|
(save-excursion
|
|
|
|
(let ((orig (point)))
|
|
|
|
(let ((current-prefix-arg my-prefix-arg)) ;; dynamic bindig so env remains clean
|
|
|
|
(call-interactively kmv))
|
|
|
|
(list (boon-mk-reg orig (point) nil))))))
|
|
|
|
(error "Unknown region specifier"))))
|
2014-10-19 14:17:46 +02:00
|
|
|
|
|
|
|
(provide 'boon-arguments)
|
|
|
|
;;; boon-arguments.el ends here
|