Function: completion-table-with-quoting

completion-table-with-quoting is a byte-compiled function defined in minibuffer.el.gz.

Signature

(completion-table-with-quoting TABLE UNQUOTE REQUOTE)

Documentation

Return a new completion table operating on quoted text.

TABLE operates on the unquoted text. UNQUOTE is a function that takes a string and returns a new unquoted string. REQUOTE is a function of 2 args (UPOS QSTR) where
  QSTR is a string entered by the user (and hence indicating
  the user's preferred form of quoting); and
  UPOS is a position within the unquoted form of QSTR.
REQUOTE should return a pair (QPOS . QFUN) such that QPOS is the position corresponding to UPOS but in QSTR, and QFUN is a function of one argument (a string) which returns that argument appropriately quoted for use at QPOS.

View in manual

Probably introduced at or before Emacs version 24.3.

Source Code

;; Defined in /usr/src/emacs/lisp/minibuffer.el.gz
(defun completion-table-with-quoting (table unquote requote)
  ;; A difficult part of completion-with-quoting is to map positions in the
  ;; quoted string to equivalent positions in the unquoted string and
  ;; vice-versa.  There is no efficient and reliable algorithm that works for
  ;; arbitrary quote and unquote functions.
  ;; So to map from quoted positions to unquoted positions, we simply assume
  ;; that `concat' and `unquote' commute (which tends to be the case).
  ;; And we ask `requote' to do the work of mapping from unquoted positions
  ;; back to quoted positions.
  ;; FIXME: For some forms of "quoting" such as the truncation behavior of
  ;; substitute-in-file-name, it would be desirable not to requote completely.
  "Return a new completion table operating on quoted text.
TABLE operates on the unquoted text.
UNQUOTE is a function that takes a string and returns a new unquoted string.
REQUOTE is a function of 2 args (UPOS QSTR) where
  QSTR is a string entered by the user (and hence indicating
  the user's preferred form of quoting); and
  UPOS is a position within the unquoted form of QSTR.
REQUOTE should return a pair (QPOS . QFUN) such that QPOS is the
position corresponding to UPOS but in QSTR, and QFUN is a function
of one argument (a string) which returns that argument appropriately quoted
for use at QPOS."
  ;; FIXME: One problem with the current setup is that `qfun' doesn't know if
  ;; its argument is "the end of the completion", so if the quoting used double
  ;; quotes (for example), we end up completing "fo" to "foobar and throwing
  ;; away the closing double quote.
  (lambda (string pred action)
    (cond
     ((eq action 'metadata)
      (append (completion-metadata string table pred)
              '((completion--unquote-requote . t))))

     ((eq action 'lambda) ;;test-completion
      (let ((ustring (funcall unquote string)))
        (test-completion ustring table pred)))

     ((eq (car-safe action) 'boundaries)
      (let* ((ustring (funcall unquote string))
             (qsuffix (cdr action))
             (ufull (if (zerop (length qsuffix)) ustring
                      (funcall unquote (concat string qsuffix))))
             ;; If (not (string-prefix-p ustring ufull)) we have a problem:
             ;; unquoting the qfull gives something "unrelated" to ustring.
             ;; E.g. "~/" and "/" where "~//" gets unquoted to just "/" (see
             ;; bug#47678).
             ;; In that case we can't even tell if we're right before the
             ;; "/" or right after it (aka if this "/" is from qstring or
             ;; from qsuffix), thus which usuffix to use is very unclear.
             (usuffix (if (string-prefix-p ustring ufull)
                          (substring ufull (length ustring))
                        ;; FIXME: Maybe "" is preferable/safer?
                        qsuffix))
             (boundaries (completion-boundaries ustring table pred usuffix))
             (qlboundary (car (funcall requote (car boundaries) string)))
             (qrboundary (if (zerop (cdr boundaries)) 0 ;Common case.
                           (let* ((urfullboundary
                                   (+ (cdr boundaries) (length ustring))))
                             (- (car (funcall requote urfullboundary
                                              (concat string qsuffix)))
                                (length string))))))
        `(boundaries ,qlboundary . ,qrboundary)))

     ;; In "normal" use a c-t-with-quoting completion table should never be
     ;; called with action in (t nil) because `completion--unquote' should have
     ;; been called before and would have returned a different completion table
     ;; to apply to the unquoted text.  But there's still a lot of code around
     ;; that likes to use all/try-completions directly, so we do our best to
     ;; handle those calls as well as we can.

     ((eq action nil) ;;try-completion
      (let* ((ustring (funcall unquote string))
             (completion (try-completion ustring table pred)))
        ;; Most forms of quoting allow several ways to quote the same string.
        ;; So here we could simply requote `completion' in a kind of
        ;; "canonical" quoted form without paying attention to the way
        ;; `string' was quoted.  But since we have to solve the more complex
        ;; problems of "pay attention to the original quoting" for
        ;; all-completions, we may as well use it here, since it provides
        ;; a nicer behavior.
        (if (not (stringp completion)) completion
          (car (completion--twq-try
                string ustring completion 0 unquote requote)))))

     ((eq action t) ;;all-completions
      ;; When all-completions is used for completion-try/all-completions
      ;; (e.g. for `pcm' style), we can't do the job properly here because
      ;; the caller will match our output against some pattern derived from
      ;; the user's (quoted) input, and we don't have access to that
      ;; pattern, so we can't know how to requote our output so that it
      ;; matches the quoting used in the pattern.  It is to fix this
      ;; fundamental problem that we have to introduce the new
      ;; unquote-requote method so that completion-try/all-completions can
      ;; pass the unquoted string to the style functions.
      (pcase-let*
          ((ustring (funcall unquote string))
           (completions (all-completions ustring table pred))
           (boundary (car (completion-boundaries ustring table pred "")))
           (completions
            (completion--twq-all
             string ustring completions boundary unquote requote))
           (last (last completions)))
        (when (consp last) (setcdr last nil))
        completions))

     ((eq action 'completion--unquote)
      ;; PRED is really a POINT in STRING.
      ;; We should return a new set (STRING TABLE POINT REQUOTE)
      ;; where STRING is a new (unquoted) STRING to match against the new TABLE
      ;; using a new POINT inside it, and REQUOTE is a requoting function which
      ;; should reverse the unquoting, (i.e. it receives the completion result
      ;; of using the new TABLE and should turn it into the corresponding
      ;; quoted result).
      (let* ((qpos pred)
	     (ustring (funcall unquote string))
	     (uprefix (funcall unquote (substring string 0 qpos)))
	     ;; FIXME: we really should pass `qpos' to `unquote' and have that
	     ;; function give us the corresponding `uqpos'.  But for now we
	     ;; presume (more or less) that `concat' and `unquote' commute.
	     (uqpos (if (string-prefix-p uprefix ustring)
			;; Yay!!  They do seem to commute!
			(length uprefix)
		      ;; They don't commute this time!  :-(
		      ;; Maybe qpos is in some text that disappears in the
		      ;; ustring (bug#17239).  Let's try a second chance guess.
		      (let ((usuffix (funcall unquote (substring string qpos))))
			(if (string-suffix-p usuffix ustring)
			    ;; Yay!!  They still "commute" in a sense!
			    (- (length ustring) (length usuffix))
			  ;; Still no luck!  Let's just choose *some* position
			  ;; within ustring.
			  (/ (+ (min (length uprefix) (length ustring))
				(max (- (length ustring) (length usuffix)) 0))
			     2))))))
        (list ustring table uqpos
              (lambda (unquoted-result op)
                (pcase op
                  (1 ;;try
                   (if (not (stringp (car-safe unquoted-result)))
                       unquoted-result
                     (completion--twq-try
                      string ustring
                      (car unquoted-result) (cdr unquoted-result)
                      unquote requote)))
                  (2 ;;all
                   (let* ((last (last unquoted-result))
                          (base (or (cdr last) 0)))
                     (when last
                       (setcdr last nil)
                       (completion--twq-all string ustring
                                            unquoted-result base
                                            unquote requote))))))))))))