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.
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))))))))))))