-
Notifications
You must be signed in to change notification settings - Fork 3
Examples
okamsn edited this page Aug 21, 2021
·
4 revisions
NOTE: As Loopy develops, these examples and results might become out of date. For the most up-to-date information, refer to this package's documentation, which is installed with the package as the Info file loopy.
Below are some examples on how one could use loopy. Feel free to add you own.
Table of Contents
Selectrum is a completion framework that aims to work better with Emacs's existing completion features. Completion usually involves working with a list of candidates, and Loopy is a good tool for producing candidates.
The below commands need the following to run:
(require 'loopy)
(require 'selectrum)
(defvar selectrum-should-sort)
(defvar selectrum-swiper-history)
(defvar selectrum-outline-history)
(declare-function selectrum-read "ext:selectrum")
(autoload 'selectrum-read "ext:selectrum")Other versions of these commands can be found on the Selectrum wiki.
(defun loopy-examples-selectrum-swiper ()
"Search for a matching line and jump to the beginning of its text.
The default candidate is the line closest to the current one.
Obeys narrowing."
(interactive)
(loopy (with (current-line-number (line-number-at-pos (point) t))
(buffer-text-lines (split-string (buffer-string) "\n"))
(number-format
(format "L%%0%dd: "
(length (number-to-string
(line-number-at-pos (point-max) t))))))
;; Loop through lines in the buffer.
(list line-text buffer-text-lines)
(expr line-num (line-number-at-pos (point-min) t) (1+ line-num))
(unless (string-empty-p line-text)
;; Once the distance between candidates and the current line starts
;; to increase, select the previously formatted candidate (i.e., the
;; candidate closest to the current line) as the default.
;;
;; There are a few different ways that you could express this idea.
(when (null default-candidate)
(expr prev-dist +1.0e+INF dist-to-default-cand)
(expr dist-to-default-cand (abs (- current-line-number
line-num)))
(when (> dist-to-default-cand prev-dist)
;; Note that we don't need to declare `default-candidate'.
(expr default-candidate formatted-candidate)))
;; Format the current line.
(expr formatted-candidate
(propertize
line-text
'selectrum-candidate-display-prefix
(propertize (format number-format line-num)
'face 'completions-annotations)
'line-num line-num))
;; Create a list of formatted candidates.
(collect formatted-candidate))
(finally-do
(let* ((selectrum-should-sort nil)
(chosen-line-number
(get-text-property
0 'line-num
(selectrum-read "Jump to matching line: "
loopy-result
:default-candidate default-candidate
:history 'selectrum-swiper-history
:require-match t
:no-move-default-candidate t))))
(push-mark (point) t)
(forward-line (- chosen-line-number current-line-number))
(beginning-of-line-text 1)))))(defcustom selectrum-outline-formats
;; Groups: (1) level determinant, (2) heading text.
;; The top level is 0, for a zero-length determinant.
'((emacs-lisp-mode
. "^;;;\\(?1:;*\\)[[:blank:]]*\\(?2:[[:alnum:]][^z-a]*\\)\\'")
(lisp-mode
. "^;;;\\(?1:;*\\)[[:blank:]]*\\(?2:[[:alnum:]][^z-a]*\\)\\'")
(gfm-mode ; Github Flavored Markdown
. "^#\\(?1:#*\\)[[:blank:]]*\\(?2:[[:alnum:]][^z-a]*\\)\\'")
(markdown-mode
. "^#\\(?1:#*\\)[[:blank:]]*\\(?2:[[:alnum:]][^z-a]*\\)\\'")
(outline-mode
. "^\\*\\(?1:\\**\\)[[:blank:]]*\\(?2:[[:alnum:]][^z-a]*\\)\\'")
;; For Org, see also ‘org-goto’.
(org-mode
. "^\\*\\(?1:\\**\\)[[:blank:]]*\\(?2:[[:alnum:]][^z-a]*\\)\\'")
(python-mode
. "^##\\(?1:\\**\\|#*\\)[[:blank:]]*\\(?2:[[:alnum:]][^z-a]*\\)\\'"))
"Alist of regexps used for identifying outline headings in each major mode.
The ‘car’ of an item in the list should be a symbol of the major mode.
The ‘cdr’ should be a regular expression with two required match groups:
1. Match group 1, whose length determines the outline level of that heading.
For best formatting, the top level should be level 0 for zero length.
2. Match group 2, which is the actual heading text.
A heading is assumed to be on only one line."
:group 'selectrum
:type '(alist
:key-type (symbol :tag "Major mode symbol")
:value-type (string :tag "Regexp")))
(defun loopy-examples-selectrum-outline ()
"Jump to a heading. Regexps are pre-defined. Obeys narrowing."
(interactive)
(let ((heading-regexp (alist-get major-mode selectrum-outline-formats)))
;; Signal a `user-error' if we don't have a regexp for this major mode. We
;; could also check this in a `before-do' macro argument to `loopy', but
;; that would happen /after/ the `with' variables are defined, which is
;; inefficient.
(unless heading-regexp
(user-error "Selectrum-outline: No heading regexp for mode %s" major-mode))
(save-match-data
(loopy
(with (default-heading)
(buffer-lines (split-string (buffer-string) "\n"))
(line-number-format
(concat "L%0"
(number-to-string
(length (number-to-string (length buffer-lines))))
"d: "))
(current-line-number (line-number-at-pos (point))))
;; Iterate through a list of lines.
(list text-line buffer-lines)
;; Keep track of the line number of the candidate.
(expr line-number 1 (1+ line-number))
(when (string-match heading-regexp text-line)
(expr prev-heading-text nil heading-text)
(expr prev-heading-level nil heading-level)
(expr heading-text (match-string-no-properties 2 text-line))
(expr heading-level (length (match-string 1 text-line)))
;; Decide whether to update the prefix list and the previous
;; heading level.
(cond
;; If we've moved to a greater level (further down the tree),
;; add the previous heading to the heading prefix list so
;; that we can prepend it to the current heading when
;; formatting.
((> heading-level (or prev-heading-level
heading-level))
(push-into backwards-prefix-list
prev-heading-text))
((< heading-level (or prev-heading-level
heading-level))
;; Otherwise, if we've moved to a lower level (higher up the
;; tree), and need to remove the most recently added prefix
;; from the list (i.e., go from '(c b a) back to '(b a)).
(loop (repeat (- prev-heading-level heading-level))
(do (pop backwards-prefix-list)))))
;; Finally, add to list of formatted headings.
;; Create heading of form "L#: a/b/c" as:
;; - having a text property holding the line number
;; - prepended with a formatted line number,
;; with the face ‘completions-annotations’.
(expr formatted-heading
(propertize
(concat (string-join (reverse backwards-prefix-list) "/")
(and backwards-prefix-list "/")
heading-text)
'line-number line-number
'selectrum-candidate-display-prefix
(propertize
(format line-number-format line-number)
'face 'completions-annotations)))
;; If needed, set default candidate.
(when (and (null default-heading)
(> line-number current-line-number))
(expr default-heading formatted-heading))
;; Collect formatted heading into `loopy-result'.
(collect formatted-heading))
(finally-do
(let* ((selectrum-should-sort nil)
(chosen-heading
(selectrum-read "Jump to heading: "
loopy-result
:default-candidate default-heading
:history 'selectrum-outline-history
:require-match t
:no-move-default-candidate t)))
;; Push mark, in case we want to return to current location. This
;; needs to happen /after/ the user has made it clear that they
;; want to go somewhere.
(push-mark (point) t)
;; Move to beginning of chosen line.
(forward-line (- (get-text-property 0 'line-number chosen-heading)
current-line-number))
(beginning-of-line-text 1)))))))