aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/comint.el
diff options
context:
space:
mode:
authorStefan Monnier2012-04-25 14:53:57 -0400
committerStefan Monnier2012-04-25 14:53:57 -0400
commitb4ff4f1fcb552dab77d4312f9adb9f290782fa98 (patch)
treeed23ce73595490f9587bf179bad6b8797b13529a /lisp/comint.el
parent79c4eeb45046eca02bd4a5daad1b673eb48377a1 (diff)
downloademacs-b4ff4f1fcb552dab77d4312f9adb9f290782fa98.tar.gz
emacs-b4ff4f1fcb552dab77d4312f9adb9f290782fa98.zip
Use completion-table-with-quoting for comint and pcomplete.
* lisp/comint.el (comint--unquote&requote-argument) (comint--unquote-argument, comint--requote-argument): New functions. (comint--unquote&expand-filename, comint-unquote-filename): Obsolete. (comint-quote-filename): Use regexp-opt-charset. (comint--common-suffix, comint--common-quoted-suffix) (comint--table-subvert): Remove. (comint-unquote-function, comint-requote-function): New vars. (comint--complete-file-name-data): Use them with completion-table-with-quoting. * lisp/pcmpl-unix.el (pcomplete/scp): Use completion-table-subvert. * lisp/pcomplete.el (pcomplete-arg-quote-list) (pcomplete-quote-arg-hook, pcomplete-quote-argument): Obsolete. (pcomplete-unquote-argument-function): Default to non-nil. (pcomplete-unquote-argument): Simplify. (pcomplete--common-quoted-suffix): Remove. (pcomplete-requote-argument-function): New var. (pcomplete--common-suffix): New function. (pcomplete-completions-at-point): Use completion-table-with-quoting and completion-table-subvert.
Diffstat (limited to 'lisp/comint.el')
-rw-r--r--lisp/comint.el160
1 files changed, 63 insertions, 97 deletions
diff --git a/lisp/comint.el b/lisp/comint.el
index 10981675971..2f8d7bd850c 100644
--- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -104,6 +104,7 @@
104(eval-when-compile (require 'cl)) 104(eval-when-compile (require 'cl))
105(require 'ring) 105(require 'ring)
106(require 'ansi-color) 106(require 'ansi-color)
107(require 'regexp-opt) ;For regexp-opt-charset.
107 108
108;; Buffer Local Variables: 109;; Buffer Local Variables:
109;;============================================================================ 110;;============================================================================
@@ -3000,26 +3001,62 @@ interpreter (e.g., the percent notation of cmd.exe on Windows)."
3000See `comint-word'." 3001See `comint-word'."
3001 (comint-word comint-file-name-chars)) 3002 (comint-word comint-file-name-chars))
3002 3003
3003(defun comint--unquote&expand-filename (filename) 3004(defun comint--unquote&requote-argument (qstr &optional upos)
3004 ;; FIXME: The code below does unquote-then-expand which means that "\\$HOME" 3005 (unless upos (setq upos 0))
3005 ;; gets expanded to the same as "$HOME" 3006 (let* ((qpos 0)
3006 (comint-substitute-in-file-name 3007 (dquotes nil)
3007 (comint-unquote-filename filename))) 3008 (ustrs '())
3009 (re (concat
3010 "[\"']\\|\\\\\\(.\\)"
3011 "\\|\\$\\(?:\\([[:alpha:]][[:alnum:]]*\\)"
3012 "\\|{\\(?2:[^{}]+\\)}\\)"
3013 (when (memq system-type '(ms-dos windows-nt))
3014 "\\|%\\(?2:[^\\\\/]*\\)%")))
3015 (qupos nil)
3016 (push (lambda (str end)
3017 (push str ustrs)
3018 (setq upos (- upos (length str)))
3019 (unless (or qupos (> upos 0))
3020 (setq qupos (if (< end 0) (- end) (+ upos end))))))
3021 match)
3022 (while (setq match (string-match re qstr qpos))
3023 (funcall push (substring qstr qpos match) match)
3024 (cond
3025 ((match-beginning 1) (funcall push (match-string 1 qstr) (match-end 0)))
3026 ((match-beginning 2) (funcall push (getenv (match-string 2 qstr))
3027 (- (match-end 0))))
3028 ((eq (aref qstr match) ?\") (setq dquotes (not dquotes)))
3029 ((eq (aref qstr match) ?\')
3030 (cond
3031 (dquotes (funcall push "'" (match-end 0)))
3032 ((< match (1+ (length qstr)))
3033 (let ((end (string-match "'" qstr (1+ match))))
3034 (funcall push (substring qstr (1+ match) end)
3035 (or end (length qstr)))))
3036 (t nil)))
3037 (t (error "Unexpected case in comint--unquote&requote-argument!")))
3038 (setq qpos (match-end 0)))
3039 (funcall push (substring qstr qpos) (length qstr))
3040 (list (mapconcat #'identity (nreverse ustrs) "")
3041 qupos #'comint-quote-filename)))
3042
3043(defun comint--unquote-argument (str)
3044 (car (comint--unquote&requote-argument str)))
3045(define-obsolete-function-alias 'comint--unquote&expand-filename
3046 #'comint--unquote-argument "24.2")
3008 3047
3009(defun comint-match-partial-filename () 3048(defun comint-match-partial-filename ()
3010 "Return the unquoted&expanded filename at point, or nil if none is found. 3049 "Return the unquoted&expanded filename at point, or nil if none is found.
3011Environment variables are substituted. See `comint-word'." 3050Environment variables are substituted. See `comint-word'."
3012 (let ((filename (comint--match-partial-filename))) 3051 (let ((filename (comint--match-partial-filename)))
3013 (and filename (comint--unquote&expand-filename filename)))) 3052 (and filename (comint--unquote-argument filename))))
3014 3053
3015(defun comint-quote-filename (filename) 3054(defun comint-quote-filename (filename)
3016 "Return FILENAME with magic characters quoted. 3055 "Return FILENAME with magic characters quoted.
3017Magic characters are those in `comint-file-name-quote-list'." 3056Magic characters are those in `comint-file-name-quote-list'."
3018 (if (null comint-file-name-quote-list) 3057 (if (null comint-file-name-quote-list)
3019 filename 3058 filename
3020 (let ((regexp 3059 (let ((regexp (regexp-opt-charset comint-file-name-quote-list)))
3021 (format "[%s]"
3022 (mapconcat 'char-to-string comint-file-name-quote-list ""))))
3023 (save-match-data 3060 (save-match-data
3024 (let ((i 0)) 3061 (let ((i 0))
3025 (while (string-match regexp filename i) 3062 (while (string-match regexp filename i)
@@ -3033,6 +3070,12 @@ Magic characters are those in `comint-file-name-quote-list'."
3033 filename 3070 filename
3034 (save-match-data 3071 (save-match-data
3035 (replace-regexp-in-string "\\\\\\(.\\)" "\\1" filename t)))) 3072 (replace-regexp-in-string "\\\\\\(.\\)" "\\1" filename t))))
3073(make-obsolete 'comint-unquote-filename nil "24.2")
3074
3075(defun comint--requote-argument (upos qstr)
3076 ;; See `completion-table-with-quoting'.
3077 (let ((res (comint--unquote&requote-argument qstr upos)))
3078 (cons (nth 1 res) (nth 2 res))))
3036 3079
3037(defun comint-completion-at-point () 3080(defun comint-completion-at-point ()
3038 (run-hook-with-args-until-success 'comint-dynamic-complete-functions)) 3081 (run-hook-with-args-until-success 'comint-dynamic-complete-functions))
@@ -3066,87 +3109,6 @@ Returns t if successful."
3066 (when (comint--match-partial-filename) 3109 (when (comint--match-partial-filename)
3067 (comint--complete-file-name-data))) 3110 (comint--complete-file-name-data)))
3068 3111
3069;; FIXME: comint--common-suffix, comint--common-quoted-suffix, and
3070;; comint--table-subvert don't fully solve the problem, since
3071;; selecting a file from *Completions* won't quote it, among several
3072;; other problems.
3073
3074(defun comint--common-suffix (s1 s2)
3075 (assert (not (or (string-match "\n" s1) (string-match "\n" s2))))
3076 ;; Since S2 is expected to be the "unquoted/expanded" version of S1,
3077 ;; there shouldn't be any case difference, even if the completion is
3078 ;; case-insensitive.
3079 (let ((case-fold-search nil))
3080 (string-match ".*?\\(.*\\)\n.*\\1\\'" (concat s1 "\n" s2))
3081 (- (match-end 1) (match-beginning 1))))
3082
3083(defun comint--common-quoted-suffix (s1 s2)
3084 ;; FIXME: Copied in pcomplete.el.
3085 "Find the common suffix between S1 and S2 where S1 is the expanded S2.
3086S1 is expected to be the unquoted and expanded version of S2.
3087Returns (PS1 . PS2), i.e. the shortest prefixes of S1 and S2, such that
3088S1 = (concat PS1 SS1) and S2 = (concat PS2 SS2) and
3089SS1 = (unquote SS2)."
3090 (let* ((cs (comint--common-suffix s1 s2))
3091 (ss1 (substring s1 (- (length s1) cs)))
3092 (qss1 (comint-quote-filename ss1))
3093 qc s2b)
3094 (if (and (not (equal ss1 qss1))
3095 (setq qc (comint-quote-filename (substring ss1 0 1)))
3096 (setq s2b (- (length s2) cs (length qc) -1))
3097 (>= s2b 0) ;bug#11158.
3098 (eq t (compare-strings s2 s2b (- (length s2) cs -1)
3099 qc nil nil)))
3100 ;; The difference found is just that one char is quoted in S2
3101 ;; but not in S1, keep looking before this difference.
3102 (comint--common-quoted-suffix
3103 (substring s1 0 (- (length s1) cs))
3104 (substring s2 0 s2b))
3105 (cons (substring s1 0 (- (length s1) cs))
3106 (substring s2 0 (- (length s2) cs))))))
3107
3108(defun comint--table-subvert (table s1 s2 &optional quote-fun unquote-fun)
3109 "Completion table that replaces the prefix S1 with S2 in STRING.
3110The result is a completion table which completes strings of the
3111form (concat S1 S) in the same way as TABLE completes strings of
3112the form (concat S2 S)."
3113 (lambda (string pred action)
3114 (let* ((str (if (eq t (compare-strings string 0 (length s1) s1 nil nil
3115 completion-ignore-case))
3116 (let ((rest (substring string (length s1))))
3117 (concat s2 (if unquote-fun
3118 (funcall unquote-fun rest) rest)))))
3119 (res (if str (complete-with-action action table str pred))))
3120 (when res
3121 (cond
3122 ((and (eq (car-safe action) 'boundaries))
3123 (let ((beg (or (and (eq (car-safe res) 'boundaries) (cadr res)) 0)))
3124 (list* 'boundaries
3125 (max (length s1)
3126 ;; FIXME: Adjust because of quoting/unquoting.
3127 (+ beg (- (length s1) (length s2))))
3128 (and (eq (car-safe res) 'boundaries) (cddr res)))))
3129 ((stringp res)
3130 (if (eq t (compare-strings res 0 (length s2) s2 nil nil
3131 completion-ignore-case))
3132 (let ((rest (substring res (length s2))))
3133 (concat s1 (if quote-fun (funcall quote-fun rest) rest)))))
3134 ((eq action t)
3135 (let ((bounds (completion-boundaries str table pred "")))
3136 (if (>= (car bounds) (length s2))
3137 (if quote-fun (mapcar quote-fun res) res)
3138 (let ((re (concat "\\`"
3139 (regexp-quote (substring s2 (car bounds))))))
3140 (delq nil
3141 (mapcar (lambda (c)
3142 (if (string-match re c)
3143 (let ((str (substring c (match-end 0))))
3144 (if quote-fun
3145 (funcall quote-fun str) str))))
3146 res))))))
3147 ;; E.g. action=nil and it's the only completion.
3148 (res))))))
3149
3150(defun comint-completion-file-name-table (string pred action) 3112(defun comint-completion-file-name-table (string pred action)
3151 (if (not (file-name-absolute-p string)) 3113 (if (not (file-name-absolute-p string))
3152 (completion-file-name-table string pred action) 3114 (completion-file-name-table string pred action)
@@ -3165,6 +3127,13 @@ the form (concat S2 S)."
3165 res))) 3127 res)))
3166 (t (completion-file-name-table string pred action))))) 3128 (t (completion-file-name-table string pred action)))))
3167 3129
3130(defvar comint-unquote-function #'comint--unquote-argument
3131 "Function to use for completion of quoted data.
3132See `completion-table-with-quoting' and `comint-requote-function'.")
3133(defvar comint-requote-function #'comint--requote-argument
3134 "Function to use for completion of quoted data.
3135See `completion-table-with-quoting' and `comint-requote-function'.")
3136
3168(defun comint--complete-file-name-data () 3137(defun comint--complete-file-name-data ()
3169 "Return the completion data for file name at point." 3138 "Return the completion data for file name at point."
3170 (let* ((filesuffix (cond ((not comint-completion-addsuffix) "") 3139 (let* ((filesuffix (cond ((not comint-completion-addsuffix) "")
@@ -3175,14 +3144,11 @@ the form (concat S2 S)."
3175 (filename (comint--match-partial-filename)) 3144 (filename (comint--match-partial-filename))
3176 (filename-beg (if filename (match-beginning 0) (point))) 3145 (filename-beg (if filename (match-beginning 0) (point)))
3177 (filename-end (if filename (match-end 0) (point))) 3146 (filename-end (if filename (match-end 0) (point)))
3178 (unquoted (if filename (comint--unquote&expand-filename filename) ""))
3179 (table 3147 (table
3180 (let ((prefixes (comint--common-quoted-suffix 3148 (completion-table-with-quoting
3181 unquoted filename))) 3149 #'comint-completion-file-name-table
3182 (comint--table-subvert 3150 comint-unquote-function
3183 #'comint-completion-file-name-table 3151 comint-requote-function)))
3184 (cdr prefixes) (car prefixes)
3185 #'comint-quote-filename #'comint-unquote-filename))))
3186 (nconc 3152 (nconc
3187 (list 3153 (list
3188 filename-beg filename-end 3154 filename-beg filename-end