boon/boon-arguments.el

228 lines
9.8 KiB
EmacsLisp
Raw Normal View History

;;; 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
;; A region list has the following form: ('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."
2014-10-19 13:59:12 +02:00
(list 'region (bounds-of-thing-at-point thing)))
(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)
(list 'region (cons (region-beginning) (region-end)))))
(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)
(let ((bounds (or (bounds-of-thing-at-point 'symbol)
(bounds-of-thing-at-point 'sexp))))
(list 'region 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)
(list 'region
(cons (save-excursion
(skip-chars-backward "^|") (point))
(save-excursion
(skip-chars-forward "^|") (point)))))
2016-05-02 15:06:13 +02:00
(defun boon-select-justline ()
"Return the region of the current line, without any newline."
(interactive) (list 'region (cons (line-beginning-position) (line-end-position))))
(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."
(save-excursion
(funcall goto-beginning)
(list 'region (cons (point) (progn (funcall forward-n count) (point))))))
(defun boon-select-paragraph (count) (interactive "p") (boon-select-n count 'start-of-paragraph-text 'forward-paragraph))
2014-10-19 13:59:12 +02:00
(defun boon-select-document () (interactive)
(list 'region (cons (point-min) (point-max))))
(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)
(list 'region (cons
(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)))))
(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))))))
(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)
(match-end 0))
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."
(interactive (list (prefix-numeric-value current-prefix-arg) (boon-spec-region "select contents")))
2014-10-19 13:59:12 +02:00
(cons 'region (apply 'append (mapcar (lambda (reg) (boon-borders reg how-much)) (mapcar 'boon-normalize-reg regs)))))
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."
2015-12-30 21:37:28 +01:00
(interactive (list (boon-spec-region "select with spaces")))
2015-11-09 21:01:47 +01:00
(cons 'region (mapcar (lambda (reg) (boon-include-surround-spaces reg)) (mapcar 'boon-normalize-reg regs))))
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."
(interactive (list (boon-spec-region "select borders")))
2014-10-19 13:59:12 +02:00
(cons 'region (mapcar 'boon-content (mapcar 'boon-normalize-reg regs))))
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)))
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."
2015-10-15 22:00:16 +02:00
(cons (cons (mark) (point))
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?
2015-10-15 22:00:16 +02:00
(mapcar (lambda (o) (cons (marker-position (overlay-get o 'mark)) (marker-position (overlay-get o 'point))))
(mc/all-fake-cursors))
nil)))
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.
The prompt (as MSG) is displayed. This function actually returns
2015-10-16 09:59:31 +02:00
a list of regions, in the form ((beginning . end) ...). 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)))
2015-10-15 22:00:16 +02:00
(if (use-region-p)
2015-10-15 23:02:57 +02:00
orig-regs
2015-10-15 22:00:16 +02:00
(let (current-prefix-arg
;; this code fiddles with the prefix arg; but if we do not
;; hide our fiddling, the next command will use the prefix
;; arg that we have set. So we dynamically bind another
;; current-prefix-arg here.
(km boon-select-map))
2015-10-16 09:59:31 +02:00
;; We read an entry in the appropriate keymap. This is done "by hand."
2015-10-15 22:00:16 +02:00
(setq current-prefix-arg 0)
(while (and km (keymapp km))
2015-10-16 09:59:31 +02:00
(let ((last-char (boon-read-char (format "%s %s" msg current-prefix-arg))))
2015-10-15 22:00:16 +02:00
(if (and (>= last-char ?0) (<= last-char ?9))
(setq current-prefix-arg (+ (- last-char ?0) (* 10 current-prefix-arg )))
(setq km (lookup-key km (vector last-char))))))
(when (eq current-prefix-arg 0)
(setq current-prefix-arg nil))
2015-10-16 09:59:31 +02:00
;; The command is ready; we now execute it (once per cursor if applicable).
2015-10-15 22:00:16 +02:00
(if km (apply 'append (mapcar (lambda (in-reg)
(let (regs final (orig (cdr in-reg)))
(save-excursion
(goto-char orig)
(setq regs (call-interactively km))
(setq final (point)))
2015-10-15 23:02:57 +02:00
;; (message "in-reg=%s regs=%s orig=%s final=%s" in-reg regs orig final)
2015-10-15 22:00:16 +02:00
(if (and regs
(listp regs)
(eq (car regs) 'region))
(cdr regs)
(list (cons orig final)))))
2015-10-15 23:02:57 +02:00
orig-regs))
(error "Unknown region specifier"))))))
2014-10-19 14:17:46 +02:00
(provide 'boon-arguments)
;;; boon-arguments.el ends here