emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/consult c21302c038 1/2: Internal code reorganization


From: ELPA Syncer
Subject: [elpa] externals/consult c21302c038 1/2: Internal code reorganization
Date: Tue, 31 Jan 2023 05:57:24 -0500 (EST)

branch: externals/consult
commit c21302c03881aeb940343c7fec4fcb819d5fc381
Author: Daniel Mendler <mail@daniel-mendler.de>
Commit: Daniel Mendler <mail@daniel-mendler.de>

    Internal code reorganization
---
 consult-register.el |   2 +-
 consult-xref.el     |   4 +-
 consult.el          | 800 ++++++++++++++++++++++++++--------------------------
 3 files changed, 408 insertions(+), 398 deletions(-)

diff --git a/consult-register.el b/consult-register.el
index ef49faae72..72f18670c8 100644
--- a/consult-register.el
+++ b/consult-register.el
@@ -73,7 +73,7 @@ Each element of the list must have the form (char . name).")
         (let* ((line (line-number-at-pos))
                (str (propertize (consult--line-with-cursor val)
                                 'consult-location (cons val line))))
-          (list (consult--format-location (buffer-name) line str)
+          (list (consult--format-file-line-match (buffer-name) line str)
                 'multi-category `(consult-location . ,str)
                 'consult--type ?p))))))
 
diff --git a/consult-xref.el b/consult-xref.el
index 44017dc99a..306990f63c 100644
--- a/consult-xref.el
+++ b/consult-xref.el
@@ -45,7 +45,7 @@ The fetch is stored globally such that it can be accessed by
                                 (xref--group-name-for-display
                                  (xref-location-group loc) root)
                               (xref-location-group loc)))
-                     (cand (consult--format-location
+                     (cand (consult--format-file-line-match
                             group
                             (or (xref-location-line loc) 0)
                             (xref-item-summary xref))))
@@ -71,7 +71,7 @@ The fetch is stored globally such that it can be accessed by
                        ('xref-buffer-location
                         (xref-location-marker loc))
                        ((or 'xref-file-location 'xref-etags-location)
-                        (consult--position-marker
+                        (consult--marker-from-line-column
                          (funcall open
                                   ;; xref-location-group returns the file name
                                   (let ((xref-file-name-display 'abs))
diff --git a/consult.el b/consult.el
index 35872ddbcd..42b770ac1a 100644
--- a/consult.el
+++ b/consult.el
@@ -539,41 +539,7 @@ We use invalid characters outside the Unicode range.")
 (defvar-local consult--org-fold-regions nil
   "Stored regions for the org-fold API.")
 
-;;;; Customization helper
-
-(defun consult--customize-put (cmds prop form)
-  "Set property PROP to FORM of commands CMDS."
-  (dolist (cmd cmds)
-    (cond
-     ((and (boundp cmd) (consp (symbol-value cmd)))
-      (setf (plist-get (symbol-value cmd) prop) (eval form 'lexical)))
-     ((functionp cmd)
-      (setf (plist-get (alist-get cmd consult--customize-alist) prop) form))
-     (t (user-error "%s is neither a Command command nor a source" cmd))))
-  nil)
-
-(defmacro consult-customize (&rest args)
-  "Set properties of commands or sources.
-ARGS is a list of commands or sources followed by the list of
-keyword-value pairs.  For `consult-customize' to succeed, the
-customized sources and commands must exist.  When a command is
-invoked, the value of `this-command' is used to lookup the
-corresponding customization options."
-  (let (setter)
-    (while args
-      (let ((cmds (seq-take-while (lambda (x) (not (keywordp x))) args)))
-        (setq args (seq-drop-while (lambda (x) (not (keywordp x))) args))
-        (while (keywordp (car args))
-          (push `(consult--customize-put ',cmds ,(car args) ',(cadr args)) 
setter)
-          (setq args (cddr args)))))
-    (macroexp-progn setter)))
-
-(defun consult--customize-get (&optional cmd)
-  "Get configuration from `consult--customize-alist' for CMD."
-  (mapcar (lambda (x) (eval x 'lexical))
-          (alist-get (or cmd this-command) consult--customize-alist)))
-
-;;;; Helper functions and macros
+;;;; Miscellaneous helper functions
 
 (defun consult--in-buffer (fun &optional buffer)
   "Ensure that FUN is executed inside BUFFER."
@@ -619,171 +585,6 @@ Turn ARG into a list, and for each element either:
       ;; split-string-and-unquote fails if the quotes are invalid.  Ignore it.
       (cons str (and opts (ignore-errors (split-string-and-unquote opts)))))))
 
-(defun consult--find-highlights (str start &rest ignored-faces)
-  "Find highlighted regions in STR from position START.
-Highlighted regions have a non-nil face property.
-IGNORED-FACES are ignored when searching for matches."
-  (let (highlights
-        (end (length str))
-        (beg start))
-    (while (< beg end)
-      (let ((next (next-single-property-change beg 'face str end))
-            (val (get-text-property beg 'face str)))
-        (when (and val
-                   (not (memq val ignored-faces))
-                   (not (and (consp val)
-                             (seq-some (lambda (x) (memq x ignored-faces)) 
val))))
-          (push (cons (- beg start) (- next start)) highlights))
-        (setq beg next)))
-    (nreverse highlights)))
-
-(defun consult--point-placement (str start &rest ignored-faces)
-  "Compute point placement from STR with START offset.
-IGNORED-FACES are ignored when searching for matches.
-Return cons of point position and a list of match begin/end pairs."
-  (let* ((matches (apply #'consult--find-highlights str start ignored-faces))
-         (pos (pcase-exhaustive consult-point-placement
-                ('match-beginning (or (caar matches) 0))
-                ('match-end (or (cdar (last matches)) 0))
-                ('line-beginning 0))))
-    (dolist (match matches)
-      (cl-decf (car match) pos)
-      (cl-decf (cdr match) pos))
-    (cons pos matches)))
-
-(defun consult--highlight-regexps (regexps ignore-case str)
-  "Highlight REGEXPS in STR.
-If a regular expression contains capturing groups, only these are highlighted.
-If no capturing groups are used highlight the whole match.  Case is ignored
-if IGNORE-CASE is non-nil."
-  (dolist (re regexps)
-    (let ((i 0))
-      (while (and (let ((case-fold-search ignore-case))
-                    (string-match re str i))
-                  ;; Ensure that regexp search made progress (edge case for .*)
-                  (> (match-end 0) i))
-        ;; Unfortunately there is no way to avoid the allocation of the match
-        ;; data, since the number of capturing groups is unknown.
-        (let ((m (match-data)))
-          (setq i (cadr m) m (or (cddr m) m))
-          (while m
-            (when (car m)
-              (add-face-text-property (car m) (cadr m)
-                                      'consult-highlight-match nil str))
-            (setq m (cddr m)))))))
-  str)
-
-(defconst consult--convert-regexp-table
-  (append
-   ;; For simplicity, treat word beginning/end as word boundaries,
-   ;; since PCRE does not make this distinction.  Usually the
-   ;; context determines if \b is the beginning or the end.
-   '(("\\<" . "\\b") ("\\>" . "\\b")
-     ("\\_<" . "\\b") ("\\_>" . "\\b"))
-   ;; Treat \` and \' as beginning and end of line.  This is more
-   ;; widely supported and makes sense for line-based commands.
-   '(("\\`" . "^") ("\\'" . "$"))
-   ;; Historical: Unescaped *, +, ? are supported at the beginning
-   (mapcan (lambda (x)
-             (mapcar (lambda (y)
-                       (cons (concat x y)
-                             (concat (string-remove-prefix "\\" x) "\\" y)))
-                     '("*" "+" "?")))
-           '("" "\\(" "\\(?:" "\\|" "^"))
-   ;; Different escaping
-   (mapcan (lambda (x) `(,x (,(cdr x) . ,(car x))))
-           '(("\\|" . "|")
-             ("\\(" . "(") ("\\)" . ")")
-             ("\\{" . "{") ("\\}" . "}"))))
-  "Regexp conversion table.")
-
-(defun consult--convert-regexp (regexp type)
-  "Convert Emacs REGEXP to regexp syntax TYPE."
-  (if (memq type '(emacs basic))
-      regexp
-    ;; Support for Emacs regular expressions is fairly complete for basic
-    ;; usage.  There are a few unsupported Emacs regexp features:
-    ;; - \= point matching
-    ;; - Syntax classes \sx \Sx
-    ;; - Character classes \cx \Cx
-    ;; - Explicitly numbered groups (?3:group)
-    (replace-regexp-in-string
-     (rx (or "\\\\" "\\^"                         ;; Pass through
-             (seq (or "\\(?:" "\\|") (any "*+?")) ;; Historical: \|+ or \(?:* 
etc
-             (seq "\\(" (any "*+"))               ;; Historical: \(* or \(+
-             (seq (or bos "^") (any "*+?"))       ;; Historical: + or * at the 
beginning
-             (seq (opt "\\") (any "(){|}"))       ;; Escape parens/braces/pipe
-             (seq "\\" (any "'<>`"))              ;; Special escapes
-             (seq "\\_" (any "<>"))))             ;; Beginning or end of symbol
-     (lambda (x) (or (cdr (assoc x consult--convert-regexp-table)) x))
-     regexp 'fixedcase 'literal)))
-
-(defun consult--default-regexp-compiler (input type ignore-case)
-  "Compile the INPUT string to a list of regular expressions.
-The function should return a pair, the list of regular expressions and a
-highlight function.  The highlight function should take a single
-argument, the string to highlight given the INPUT.  TYPE is the desired
-type of regular expression, which can be `basic', `extended', `emacs' or
-`pcre'.  If IGNORE-CASE is non-nil return a highlight function which
-matches case insensitively."
-  (setq input (consult--split-escaped input))
-  (cons (mapcar (lambda (x) (consult--convert-regexp x type)) input)
-        (when-let (regexps (seq-filter #'consult--valid-regexp-p input))
-          (apply-partially #'consult--highlight-regexps regexps ignore-case))))
-
-(defun consult--split-escaped (str)
-  "Split STR at spaces, which can be escaped with backslash."
-  (mapcar
-   (lambda (x) (string-replace "\0" " " x))
-   (split-string (replace-regexp-in-string
-                  "\\\\\\\\\\|\\\\ "
-                  (lambda (x) (if (equal x "\\ ") "\0" x))
-                  str 'fixedcase 'literal)
-                 " +" t)))
-
-(defun consult--join-regexps (regexps type)
-  "Join REGEXPS of TYPE."
-  ;; Add lookahead wrapper only if there is more than one regular expression
-  (cond
-   ((and (eq type 'pcre) (cdr regexps))
-    (concat "^" (mapconcat (lambda (x) (format "(?=.*%s)" x))
-                           regexps "")))
-   ((eq type 'basic)
-    (string-join regexps ".*"))
-   (t
-    (when (length> regexps 3)
-      (message "Too many regexps, %S ignored. Use post-filtering!"
-               (string-join (seq-drop regexps 3) " "))
-      (setq regexps (seq-take regexps 3)))
-    (consult--regexp-join-permutations regexps
-                                       (and (memq type '(basic emacs)) 
"\\")))))
-
-(defun consult--regexp-join-permutations (regexps esc)
-  "Join all permutations of REGEXPS.
-ESC is the escaping string for choice and groups."
-  (pcase regexps
-    ('nil "")
-    (`(,r) r)
-    (`(,r1 ,r2) (concat r1 ".*" r2 esc "|" r2 ".*" r1))
-    (_ (mapconcat
-        (lambda (r)
-          (concat r ".*" esc "("
-                  (consult--regexp-join-permutations (remove r regexps) esc)
-                  esc ")"))
-        regexps (concat esc "|")))))
-
-(defun consult--valid-regexp-p (re)
-  "Return t if regexp RE is valid."
-  (condition-case nil
-      (progn (string-match-p re "") t)
-    (invalid-regexp nil)))
-
-(defun consult--regexp-filter (regexps)
-  "Create filter regexp from REGEXPS."
-  (if (stringp regexps)
-      regexps
-    (mapconcat (lambda (x) (concat "\\(?:" x "\\)")) regexps "\\|")))
-
 (defmacro consult--keep! (list form)
   "Evaluate FORM for every element of LIST and keep the non-nil results."
   (declare (indent 1))
@@ -973,14 +774,17 @@ When no project is found and MAY-PROMPT is non-nil ask 
the user."
       (match-string 1 dir)
     dir))
 
-(defun consult--format-location (file line &optional str)
-  "Format location string FILE:LINE:STR."
+(defun consult--format-file-line-match (file line &optional match)
+  "Format string FILE:LINE:MATCH with faces."
   (setq line (number-to-string line)
-        str (concat file ":" line (and str ":") str)
+        match (concat file ":" line (and match ":") match)
         file (length file))
-  (put-text-property 0 file 'face 'consult-file str)
-  (put-text-property (1+ file) (+ 1 file (length line)) 'face 
'consult-line-number str)
-  str)
+  (put-text-property 0 file 'face 'consult-file match)
+  (put-text-property (1+ file) (+ 1 file (length line)) 'face 
'consult-line-number match)
+  match)
+
+(define-obsolete-function-alias
+  'consult--format-location 'consult--format-file-line-match "0.31")
 
 (defmacro consult--overlay (beg end &rest props)
   "Make consult overlay between BEG and END with PROPS."
@@ -1001,55 +805,301 @@ When no project is found and MAY-PROMPT is non-nil ask 
the user."
   "Return t if position POS lies in range `point-min' to `point-max'."
   (<= (point-min) pos (point-max)))
 
-(defun consult--prefix-group (cand transform)
-  "Return title for CAND or TRANSFORM the candidate.
-The candidate must have a `consult--prefix-group' property."
-  (if transform
-      (substring cand (1+ (length (get-text-property 0 'consult--prefix-group 
cand))))
-    (get-text-property 0 'consult--prefix-group cand)))
-
-(defun consult--type-group (types)
-  "Return group function for TYPES."
-  (lambda (cand transform)
-    (if transform cand
-      (alist-get (get-text-property 0 'consult--type cand) types))))
-
-(defun consult--type-narrow (types)
-  "Return narrowing configuration from TYPES."
-  (list :predicate
-        (lambda (cand) (eq (get-text-property 0 'consult--type cand) 
consult--narrow))
-        :keys types))
-
 (defun consult--completion-window-p ()
   "Return non-nil if the selected window belongs to the completion UI."
   (or (eq (selected-window) (active-minibuffer-window))
       (eq #'completion-list-mode (buffer-local-value 'major-mode 
(window-buffer)))))
 
-(defun consult--location-state (candidates)
-  "Location state function.
-The cheap location markers from CANDIDATES are upgraded on window
-selection change to full Emacs markers."
-  (let ((jump (consult--jump-state))
-        (hook (make-symbol "consult--location-upgrade")))
-    (fset hook
-          (lambda (_)
-            (unless (consult--completion-window-p)
-              (remove-hook 'window-selection-change-functions hook)
-              (mapc #'consult--get-location
-                    (if (functionp candidates) (funcall candidates) 
candidates)))))
-    (lambda (action cand)
-      (pcase action
-        ('setup (add-hook 'window-selection-change-functions hook))
-        ('exit (remove-hook 'window-selection-change-functions hook)))
-      (funcall jump action cand))))
+(defun consult--forbid-minibuffer ()
+  "Raise an error if executed from the minibuffer."
+  (when (minibufferp)
+    (user-error "`%s' called inside the minibuffer" this-command)))
+
+(defun consult--require-minibuffer ()
+  "Raise an error if executed outside the minibuffer."
+  (unless (minibufferp)
+    (user-error "`%s' must be called inside the minibuffer" this-command)))
+
+(defun consult--fontify-all ()
+  "Ensure that the whole buffer is fontified."
+  ;; Font-locking is lazy, i.e., if a line has not been looked at yet, the line
+  ;; is not font-locked.  We would observe this if consulting an unfontified
+  ;; line.  Therefore we have to enforce font-locking now, which is slow.  In
+  ;; order to prevent is hang-up we check the buffer size against
+  ;; `consult-fontify-max-size'.
+  (when (and consult-fontify-preserve jit-lock-mode
+             (< (buffer-size) consult-fontify-max-size))
+    (jit-lock-fontify-now)))
+
+(defun consult--fontify-region (start end)
+  "Ensure that region between START and END is fontified."
+  (when (and consult-fontify-preserve jit-lock-mode)
+    (jit-lock-fontify-now start end)))
+
+(defmacro consult--with-increased-gc (&rest body)
+  "Temporarily increase the gc limit in BODY to optimize for throughput."
+  (let ((overwrite (make-symbol "overwrite")))
+    `(let* ((,overwrite (> consult--gc-threshold gc-cons-threshold))
+            (gc-cons-threshold (if ,overwrite consult--gc-threshold 
gc-cons-threshold))
+            (gc-cons-percentage (if ,overwrite consult--gc-percentage 
gc-cons-percentage)))
+       ,@body)))
+
+(defun consult--count-lines (pos)
+  "Move to position POS and return number of lines."
+  (let ((line 1))
+    (while (< (point) pos)
+      (forward-line)
+      (when (<= (point) pos)
+        (cl-incf line)))
+    (goto-char pos)
+    line))
+
+(defun consult--marker-from-line-column (buffer line column)
+  "Get marker in BUFFER from LINE and COLUMN."
+  (when (buffer-live-p buffer)
+    (with-current-buffer buffer
+      (save-restriction
+        (save-excursion
+          (widen)
+          (goto-char (point-min))
+          ;; Location data might be invalid by now!
+          (ignore-errors
+            (forward-line (1- line))
+            (forward-char column))
+          (point-marker))))))
+
+(defun consult--line-prefix (&optional curr-line)
+  "Annotate `consult-location' candidates with line numbers.
+CURR-LINE is the current line number."
+  (setq curr-line (or curr-line -1))
+  (let* ((width (length (number-to-string (line-number-at-pos
+                                           (point-max)
+                                           consult-line-numbers-widen))))
+         (before (format #("%%%dd " 0 6 (face consult-line-number-wrapped)) 
width))
+         (after (format #("%%%dd " 0 6 (face consult-line-number-prefix)) 
width)))
+    (lambda (cand)
+      (let ((line (cdr (get-text-property 0 'consult-location cand))))
+        (list cand (format (if (< line curr-line) before after) line) "")))))
+
+(defsubst consult--location-candidate (cand marker line tofu &rest props)
+  "Add MARKER and LINE as `consult-location' text property to CAND.
+Furthermore add the additional text properties PROPS, and append
+TOFU suffix for disambiguation."
+  (setq cand (concat cand (consult--tofu-encode tofu)))
+  (add-text-properties 0 1 `(consult-location (,marker . ,line) ,@props) cand)
+  cand)
+
+;; There is a similar variable `yank-excluded-properties'.  Unfortunately
+;; we cannot use it here since it excludes too much (e.g., invisible)
+;; and at the same time not enough (e.g., cursor-sensor-functions).
+(defconst consult--remove-text-properties
+  '(category cursor cursor-intangible cursor-sensor-functions field follow-link
+    fontified front-sticky help-echo insert-behind-hooks insert-in-front-hooks
+    intangible keymap local-map modification-hooks mouse-face pointer read-only
+    rear-nonsticky yank-handler)
+  "List of text properties to remove from buffer strings.")
+
+(defsubst consult--buffer-substring (beg end &optional fontify)
+  "Return buffer substring between BEG and END.
+If FONTIFY and `consult-fontify-preserve' are non-nil, first ensure that the
+region has been fontified."
+  (if consult-fontify-preserve
+      (let (str)
+        (when fontify (consult--fontify-region beg end))
+        (setq str (buffer-substring beg end))
+        ;; TODO Propose the upstream addition of a function
+        ;; `preserve-list-of-text-properties', which should be as efficient as
+        ;; `remove-list-of-text-properties'.
+        (remove-list-of-text-properties
+         0 (- end beg) consult--remove-text-properties str)
+        str)
+    (buffer-substring-no-properties beg end)))
+
+(defun consult--region-with-cursor (beg end marker)
+  "Return region string with a marking at the cursor position.
+
+BEG is the begin position.
+END is the end position.
+MARKER is the cursor position."
+  (let ((str (consult--buffer-substring beg end 'fontify)))
+    (if (>= marker end)
+        (concat str #(" " 0 1 (face consult-preview-cursor)))
+      (put-text-property (- marker beg) (- (1+ marker) beg)
+                         'face 'consult-preview-cursor str)
+      str)))
+
+(defun consult--line-with-cursor (marker)
+  "Return current line where the cursor MARKER is highlighted."
+  (consult--region-with-cursor (pos-bol) (pos-eol) marker))
+
+;;;; Regexp utilities
+
+(defun consult--find-highlights (str start &rest ignored-faces)
+  "Find highlighted regions in STR from position START.
+Highlighted regions have a non-nil face property.
+IGNORED-FACES are ignored when searching for matches."
+  (let (highlights
+        (end (length str))
+        (beg start))
+    (while (< beg end)
+      (let ((next (next-single-property-change beg 'face str end))
+            (val (get-text-property beg 'face str)))
+        (when (and val
+                   (not (memq val ignored-faces))
+                   (not (and (consp val)
+                             (seq-some (lambda (x) (memq x ignored-faces)) 
val))))
+          (push (cons (- beg start) (- next start)) highlights))
+        (setq beg next)))
+    (nreverse highlights)))
+
+(defun consult--point-placement (str start &rest ignored-faces)
+  "Compute point placement from STR with START offset.
+IGNORED-FACES are ignored when searching for matches.
+Return cons of point position and a list of match begin/end pairs."
+  (let* ((matches (apply #'consult--find-highlights str start ignored-faces))
+         (pos (pcase-exhaustive consult-point-placement
+                ('match-beginning (or (caar matches) 0))
+                ('match-end (or (cdar (last matches)) 0))
+                ('line-beginning 0))))
+    (dolist (match matches)
+      (cl-decf (car match) pos)
+      (cl-decf (cdr match) pos))
+    (cons pos matches)))
+
+(defun consult--highlight-regexps (regexps ignore-case str)
+  "Highlight REGEXPS in STR.
+If a regular expression contains capturing groups, only these are highlighted.
+If no capturing groups are used highlight the whole match.  Case is ignored
+if IGNORE-CASE is non-nil."
+  (dolist (re regexps)
+    (let ((i 0))
+      (while (and (let ((case-fold-search ignore-case))
+                    (string-match re str i))
+                  ;; Ensure that regexp search made progress (edge case for .*)
+                  (> (match-end 0) i))
+        ;; Unfortunately there is no way to avoid the allocation of the match
+        ;; data, since the number of capturing groups is unknown.
+        (let ((m (match-data)))
+          (setq i (cadr m) m (or (cddr m) m))
+          (while m
+            (when (car m)
+              (add-face-text-property (car m) (cadr m)
+                                      'consult-highlight-match nil str))
+            (setq m (cddr m)))))))
+  str)
+
+(defconst consult--convert-regexp-table
+  (append
+   ;; For simplicity, treat word beginning/end as word boundaries,
+   ;; since PCRE does not make this distinction.  Usually the
+   ;; context determines if \b is the beginning or the end.
+   '(("\\<" . "\\b") ("\\>" . "\\b")
+     ("\\_<" . "\\b") ("\\_>" . "\\b"))
+   ;; Treat \` and \' as beginning and end of line.  This is more
+   ;; widely supported and makes sense for line-based commands.
+   '(("\\`" . "^") ("\\'" . "$"))
+   ;; Historical: Unescaped *, +, ? are supported at the beginning
+   (mapcan (lambda (x)
+             (mapcar (lambda (y)
+                       (cons (concat x y)
+                             (concat (string-remove-prefix "\\" x) "\\" y)))
+                     '("*" "+" "?")))
+           '("" "\\(" "\\(?:" "\\|" "^"))
+   ;; Different escaping
+   (mapcan (lambda (x) `(,x (,(cdr x) . ,(car x))))
+           '(("\\|" . "|")
+             ("\\(" . "(") ("\\)" . ")")
+             ("\\{" . "{") ("\\}" . "}"))))
+  "Regexp conversion table.")
+
+(defun consult--convert-regexp (regexp type)
+  "Convert Emacs REGEXP to regexp syntax TYPE."
+  (if (memq type '(emacs basic))
+      regexp
+    ;; Support for Emacs regular expressions is fairly complete for basic
+    ;; usage.  There are a few unsupported Emacs regexp features:
+    ;; - \= point matching
+    ;; - Syntax classes \sx \Sx
+    ;; - Character classes \cx \Cx
+    ;; - Explicitly numbered groups (?3:group)
+    (replace-regexp-in-string
+     (rx (or "\\\\" "\\^"                         ;; Pass through
+             (seq (or "\\(?:" "\\|") (any "*+?")) ;; Historical: \|+ or \(?:* 
etc
+             (seq "\\(" (any "*+"))               ;; Historical: \(* or \(+
+             (seq (or bos "^") (any "*+?"))       ;; Historical: + or * at the 
beginning
+             (seq (opt "\\") (any "(){|}"))       ;; Escape parens/braces/pipe
+             (seq "\\" (any "'<>`"))              ;; Special escapes
+             (seq "\\_" (any "<>"))))             ;; Beginning or end of symbol
+     (lambda (x) (or (cdr (assoc x consult--convert-regexp-table)) x))
+     regexp 'fixedcase 'literal)))
+
+(defun consult--default-regexp-compiler (input type ignore-case)
+  "Compile the INPUT string to a list of regular expressions.
+The function should return a pair, the list of regular expressions and a
+highlight function.  The highlight function should take a single
+argument, the string to highlight given the INPUT.  TYPE is the desired
+type of regular expression, which can be `basic', `extended', `emacs' or
+`pcre'.  If IGNORE-CASE is non-nil return a highlight function which
+matches case insensitively."
+  (setq input (consult--split-escaped input))
+  (cons (mapcar (lambda (x) (consult--convert-regexp x type)) input)
+        (when-let (regexps (seq-filter #'consult--valid-regexp-p input))
+          (apply-partially #'consult--highlight-regexps regexps ignore-case))))
+
+(defun consult--split-escaped (str)
+  "Split STR at spaces, which can be escaped with backslash."
+  (mapcar
+   (lambda (x) (string-replace "\0" " " x))
+   (split-string (replace-regexp-in-string
+                  "\\\\\\\\\\|\\\\ "
+                  (lambda (x) (if (equal x "\\ ") "\0" x))
+                  str 'fixedcase 'literal)
+                 " +" t)))
+
+(defun consult--join-regexps (regexps type)
+  "Join REGEXPS of TYPE."
+  ;; Add lookahead wrapper only if there is more than one regular expression
+  (cond
+   ((and (eq type 'pcre) (cdr regexps))
+    (concat "^" (mapconcat (lambda (x) (format "(?=.*%s)" x))
+                           regexps "")))
+   ((eq type 'basic)
+    (string-join regexps ".*"))
+   (t
+    (when (length> regexps 3)
+      (message "Too many regexps, %S ignored. Use post-filtering!"
+               (string-join (seq-drop regexps 3) " "))
+      (setq regexps (seq-take regexps 3)))
+    (consult--regexp-join-permutations regexps
+                                       (and (memq type '(basic emacs)) 
"\\")))))
+
+(defun consult--regexp-join-permutations (regexps esc)
+  "Join all permutations of REGEXPS.
+ESC is the escaping string for choice and groups."
+  (pcase regexps
+    ('nil "")
+    (`(,r) r)
+    (`(,r1 ,r2) (concat r1 ".*" r2 esc "|" r2 ".*" r1))
+    (_ (mapconcat
+        (lambda (r)
+          (concat r ".*" esc "("
+                  (consult--regexp-join-permutations (remove r regexps) esc)
+                  esc ")"))
+        regexps (concat esc "|")))))
 
-(defun consult--get-location (cand)
-  "Return location from CAND."
-  (let ((loc (get-text-property 0 'consult-location cand)))
-    (when (consp (car loc))
-      ;; Transform cheap marker to real marker
-      (setcar loc (set-marker (make-marker) (cdar loc) (caar loc))))
-    loc))
+(defun consult--valid-regexp-p (re)
+  "Return t if regexp RE is valid."
+  (condition-case nil
+      (progn (string-match-p re "") t)
+    (invalid-regexp nil)))
+
+(defun consult--regexp-filter (regexps)
+  "Create filter regexp from REGEXPS."
+  (if (stringp regexps)
+      regexps
+    (mapconcat (lambda (x) (concat "\\(?:" x "\\)")) regexps "\\|")))
+
+;;;; Lookup functions
 
 (defun consult--lookup-member (selected candidates &rest _)
   "Lookup SELECTED in CANDIDATES list, return original element."
@@ -1080,128 +1130,6 @@ Return the location marker."
   "Lookup SELECTED in CANDIDATES list and return property 
`consult--candidate'."
   (consult--lookup-prop 'consult--candidate selected candidates))
 
-(defun consult--forbid-minibuffer ()
-  "Raise an error if executed from the minibuffer."
-  (when (minibufferp)
-    (user-error "`%s' called inside the minibuffer" this-command)))
-
-(defun consult--require-minibuffer ()
-  "Raise an error if executed outside the minibuffer."
-  (unless (minibufferp)
-    (user-error "`%s' must be called inside the minibuffer" this-command)))
-
-(defun consult--fontify-all ()
-  "Ensure that the whole buffer is fontified."
-  ;; Font-locking is lazy, i.e., if a line has not been looked at yet, the line
-  ;; is not font-locked.  We would observe this if consulting an unfontified
-  ;; line.  Therefore we have to enforce font-locking now, which is slow.  In
-  ;; order to prevent is hang-up we check the buffer size against
-  ;; `consult-fontify-max-size'.
-  (when (and consult-fontify-preserve jit-lock-mode
-             (< (buffer-size) consult-fontify-max-size))
-    (jit-lock-fontify-now)))
-
-(defun consult--fontify-region (start end)
-  "Ensure that region between START and END is fontified."
-  (when (and consult-fontify-preserve jit-lock-mode)
-    (jit-lock-fontify-now start end)))
-
-(defmacro consult--with-increased-gc (&rest body)
-  "Temporarily increase the gc limit in BODY to optimize for throughput."
-  (let ((overwrite (make-symbol "overwrite")))
-    `(let* ((,overwrite (> consult--gc-threshold gc-cons-threshold))
-            (gc-cons-threshold (if ,overwrite consult--gc-threshold 
gc-cons-threshold))
-            (gc-cons-percentage (if ,overwrite consult--gc-percentage 
gc-cons-percentage)))
-       ,@body)))
-
-(defun consult--count-lines (pos)
-  "Move to position POS and return number of lines."
-  (let ((line 1))
-    (while (< (point) pos)
-      (forward-line)
-      (when (<= (point) pos)
-        (cl-incf line)))
-    (goto-char pos)
-    line))
-
-(defun consult--position-marker (buffer line column)
-  "Get marker in BUFFER from LINE and COLUMN."
-  (when (buffer-live-p buffer)
-    (with-current-buffer buffer
-      (save-restriction
-        (save-excursion
-          (widen)
-          (goto-char (point-min))
-          ;; Location data might be invalid by now!
-          (ignore-errors
-            (forward-line (1- line))
-            (forward-char column))
-          (point-marker))))))
-
-(defun consult--line-prefix (&optional curr-line)
-  "Annotate `consult-location' candidates with line numbers.
-CURR-LINE is the current line number."
-  (setq curr-line (or curr-line -1))
-  (let* ((width (length (number-to-string (line-number-at-pos
-                                           (point-max)
-                                           consult-line-numbers-widen))))
-         (before (format #("%%%dd " 0 6 (face consult-line-number-wrapped)) 
width))
-         (after (format #("%%%dd " 0 6 (face consult-line-number-prefix)) 
width)))
-    (lambda (cand)
-      (let ((line (cdr (get-text-property 0 'consult-location cand))))
-        (list cand (format (if (< line curr-line) before after) line) "")))))
-
-(defsubst consult--location-candidate (cand marker line tofu &rest props)
-  "Add MARKER and LINE as `consult-location' text property to CAND.
-Furthermore add the additional text properties PROPS, and append
-TOFU suffix for disambiguation."
-  (setq cand (concat cand (consult--tofu-encode tofu)))
-  (add-text-properties 0 1 `(consult-location (,marker . ,line) ,@props) cand)
-  cand)
-
-;; There is a similar variable `yank-excluded-properties'.  Unfortunately
-;; we cannot use it here since it excludes too much (e.g., invisible)
-;; and at the same time not enough (e.g., cursor-sensor-functions).
-(defconst consult--remove-text-properties
-  '(category cursor cursor-intangible cursor-sensor-functions field follow-link
-    fontified front-sticky help-echo insert-behind-hooks insert-in-front-hooks
-    intangible keymap local-map modification-hooks mouse-face pointer read-only
-    rear-nonsticky yank-handler)
-  "List of text properties to remove from buffer strings.")
-
-(defsubst consult--buffer-substring (beg end &optional fontify)
-  "Return buffer substring between BEG and END.
-If FONTIFY and `consult-fontify-preserve' are non-nil, first ensure that the
-region has been fontified."
-  (if consult-fontify-preserve
-      (let (str)
-        (when fontify (consult--fontify-region beg end))
-        (setq str (buffer-substring beg end))
-        ;; TODO Propose the upstream addition of a function
-        ;; `preserve-list-of-text-properties', which should be as efficient as
-        ;; `remove-list-of-text-properties'.
-        (remove-list-of-text-properties
-         0 (- end beg) consult--remove-text-properties str)
-        str)
-    (buffer-substring-no-properties beg end)))
-
-(defun consult--region-with-cursor (beg end marker)
-  "Return region string with a marking at the cursor position.
-
-BEG is the begin position.
-END is the end position.
-MARKER is the cursor position."
-  (let ((str (consult--buffer-substring beg end 'fontify)))
-    (if (>= marker end)
-        (concat str #(" " 0 1 (face consult-preview-cursor)))
-      (put-text-property (- marker beg) (- (1+ marker) beg)
-                         'face 'consult-preview-cursor str)
-      str)))
-
-(defun consult--line-with-cursor (marker)
-  "Return current line where the cursor MARKER is highlighted."
-  (consult--region-with-cursor (pos-bol) (pos-eol) marker))
-
 ;;;; Preview support
 
 (defun consult--filter-find-file-hook (orig &rest hooks)
@@ -1494,6 +1422,32 @@ The function can be used as the `:state' argument of 
`consult--read'."
   "The state function used if selecting from a list of candidate positions."
   (consult--state-with-return (consult--jump-preview) #'consult--jump))
 
+(defun consult--get-location (cand)
+  "Return location from CAND."
+  (let ((loc (get-text-property 0 'consult-location cand)))
+    (when (consp (car loc))
+      ;; Transform cheap marker to real marker
+      (setcar loc (set-marker (make-marker) (cdar loc) (caar loc))))
+    loc))
+
+(defun consult--location-state (candidates)
+  "Location state function.
+The cheap location markers from CANDIDATES are upgraded on window
+selection change to full Emacs markers."
+  (let ((jump (consult--jump-state))
+        (hook (make-symbol "consult--location-upgrade")))
+    (fset hook
+          (lambda (_)
+            (unless (consult--completion-window-p)
+              (remove-hook 'window-selection-change-functions hook)
+              (mapc #'consult--get-location
+                    (if (functionp candidates) (funcall candidates) 
candidates)))))
+    (lambda (action cand)
+      (pcase action
+        ('setup (add-hook 'window-selection-change-functions hook))
+        ('exit (remove-hook 'window-selection-change-functions hook)))
+      (funcall jump action cand))))
+
 (defun consult--state-with-return (state return)
   "Compose STATE function with RETURN function."
   (lambda (action cand)
@@ -1694,7 +1648,26 @@ invoked, the state function will also be called with 
`exit' and
   (declare (indent 4))
   `(consult--with-preview-1 ,preview-key ,state ,transform ,candidate (lambda 
() ,@body)))
 
-;;;; Narrowing support
+;;;; Narrowing and grouping
+
+(defun consult--prefix-group (cand transform)
+  "Return title for CAND or TRANSFORM the candidate.
+The candidate must have a `consult--prefix-group' property."
+  (if transform
+      (substring cand (1+ (length (get-text-property 0 'consult--prefix-group 
cand))))
+    (get-text-property 0 'consult--prefix-group cand)))
+
+(defun consult--type-group (types)
+  "Return group function for TYPES."
+  (lambda (cand transform)
+    (if transform cand
+      (alist-get (get-text-property 0 'consult--type cand) types))))
+
+(defun consult--type-narrow (types)
+  "Return narrowing configuration from TYPES."
+  (list :predicate
+        (lambda (cand) (eq (get-text-property 0 'consult--type cand) 
consult--narrow))
+        :keys types))
 
 (defun consult--widen-key ()
   "Return widening key, if `consult-widen-key' is not set.
@@ -1869,7 +1842,7 @@ PLIST is the splitter configuration, including the 
separator."
                 completion-category-defaults nil
                 completion-category-overrides nil)))
 
-;;;; Async support
+;;;; Asynchronous filtering functions
 
 (defmacro consult--with-async (bind &rest body)
   "Setup asynchronous completion in BODY.
@@ -2208,6 +2181,24 @@ The refresh happens after a DELAY, defaulting to 
`consult-async-refresh-delay'."
                               (funcall async 'refresh)))))))
           ('destroy (when timer (cancel-timer timer))))))))
 
+(defmacro consult--async-command (builder &rest args)
+  "Asynchronous command pipeline.
+ARGS is a list of `make-process' properties and transforms.
+BUILDER is the command line builder function, which takes the
+input string and must either return a list of command line
+arguments or a pair of the command line argument list and a
+highlighting function."
+  (declare (indent 1))
+  `(thread-first
+     (consult--async-sink)
+     (consult--async-refresh-timer)
+     ,@(seq-take-while (lambda (x) (not (keywordp x))) args)
+     (consult--async-process
+      ,builder
+      ,@(seq-drop-while (lambda (x) (not (keywordp x))) args))
+     (consult--async-throttle)
+     (consult--async-split)))
+
 (defmacro consult--async-transform (async &rest transform)
   "Use FUN to TRANSFORM candidates of ASYNC."
   (let ((async-var (make-symbol "async"))
@@ -2224,6 +2215,8 @@ The refresh happens after a DELAY, defaulting to 
`consult-async-refresh-delay'."
   "Filter candidates of ASYNC by FUN."
   (consult--async-transform async seq-filter fun))
 
+;;;; Dynamic collections based
+
 (defun consult--dynamic-compute (async fun &optional debounce)
   "Dynamic computation of candidates.
 ASYNC is the sink.
@@ -2276,24 +2269,6 @@ FUN computes the candidates given the input."
     (consult--async-throttle)
     (consult--async-split)))
 
-(defmacro consult--async-command (builder &rest args)
-  "Asynchronous command pipeline.
-ARGS is a list of `make-process' properties and transforms.
-BUILDER is the command line builder function, which takes the
-input string and must either return a list of command line
-arguments or a pair of the command line argument list and a
-highlighting function."
-  (declare (indent 1))
-  `(thread-first
-     (consult--async-sink)
-     (consult--async-refresh-timer)
-     ,@(seq-take-while (lambda (x) (not (keywordp x))) args)
-     (consult--async-process
-      ,builder
-      ,@(seq-drop-while (lambda (x) (not (keywordp x))) args))
-     (consult--async-throttle)
-     (consult--async-split)))
-
 ;;;; Special keymaps
 
 (defvar-keymap consult-async-map
@@ -2813,6 +2788,40 @@ KEYMAP is a command-specific keymap."
                 :preview-key consult-preview-key
                 :transform #'identity))))
 
+;;;; Customization macro
+
+(defun consult--customize-put (cmds prop form)
+  "Set property PROP to FORM of commands CMDS."
+  (dolist (cmd cmds)
+    (cond
+     ((and (boundp cmd) (consp (symbol-value cmd)))
+      (setf (plist-get (symbol-value cmd) prop) (eval form 'lexical)))
+     ((functionp cmd)
+      (setf (plist-get (alist-get cmd consult--customize-alist) prop) form))
+     (t (user-error "%s is neither a Command command nor a source" cmd))))
+  nil)
+
+(defmacro consult-customize (&rest args)
+  "Set properties of commands or sources.
+ARGS is a list of commands or sources followed by the list of
+keyword-value pairs.  For `consult-customize' to succeed, the
+customized sources and commands must exist.  When a command is
+invoked, the value of `this-command' is used to lookup the
+corresponding customization options."
+  (let (setter)
+    (while args
+      (let ((cmds (seq-take-while (lambda (x) (not (keywordp x))) args)))
+        (setq args (seq-drop-while (lambda (x) (not (keywordp x))) args))
+        (while (keywordp (car args))
+          (push `(consult--customize-put ',cmds ,(car args) ',(cadr args)) 
setter)
+          (setq args (cddr args)))))
+    (macroexp-progn setter)))
+
+(defun consult--customize-get (&optional cmd)
+  "Get configuration from `consult--customize-alist' for CMD."
+  (mapcar (lambda (x) (eval x 'lexical))
+          (alist-get (or cmd this-command) consult--customize-alist)))
+
 ;;;; Commands
 
 ;;;;; Command: consult-completion-in-region
@@ -3102,9 +3111,10 @@ The symbol at point is added to the future history."
                 ;; `line-number-at-pos' is slow, see comment in 
`consult--mark-candidates'.
                 (let ((line (line-number-at-pos pos 
consult-line-numbers-widen)))
                   (push (concat
-                         (propertize (consult--format-location (buffer-name 
buf) line "")
-                                     'consult-location (cons marker line)
-                                     'consult-strip t)
+                         (propertize
+                          (consult--format-file-line-match (buffer-name buf) 
line "")
+                          'consult-location (cons marker line)
+                          'consult-strip t)
                          (consult--line-with-cursor marker)
                          (consult--tofu-encode marker))
                         candidates))))))))
@@ -4631,7 +4641,7 @@ FIND-FILE is the file open function, defaulting to 
`find-file'."
            (matches (consult--point-placement cand (1+ line-end) 
'consult-grep-context))
            (file (substring-no-properties cand 0 file-end))
            (line (string-to-number (substring-no-properties cand (+ 1 
file-end) line-end))))
-      (when-let (pos (consult--position-marker
+      (when-let (pos (consult--marker-from-line-column
                       (funcall (or find-file #'find-file) file)
                       line (or (car matches) 0)))
         (cons pos (cdr matches))))))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]