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

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

[nongnu] elpa/bash-completion f2ea2b65df 141/313: Detect directories whe


From: ELPA Syncer
Subject: [nongnu] elpa/bash-completion f2ea2b65df 141/313: Detect directories when expanding commands and add a slash instead of a
Date: Sat, 3 Dec 2022 10:59:24 -0500 (EST)

branch: elpa/bash-completion
commit f2ea2b65df5e6c7fd1c0aa61f6d80f444692f9ea
Author: Stephane Zermatten <szermatt@gmx.net>
Commit: Stephane Zermatten <szermatt@gmx.net>

    Detect directories when expanding commands and add a slash instead of a
    space. fixes #11
    
    Before this commit, the parameter -S ' ' was passed to compgen when
    expanding a command, which means that it would always append a space.
    Adding a space is useful when expanding a command, but annoying when
    expanding a directory.
    
    With this commit, the space is added by bash-completion.el instead of
    bash commands, allows checking for matching directory.
---
 bash-completion-test.el | 107 +++++++++++++++++++-------------
 bash-completion.el      | 160 ++++++++++++++++++++++++------------------------
 2 files changed, 146 insertions(+), 121 deletions(-)

diff --git a/bash-completion-test.el b/bash-completion-test.el
index 95dd55c80d..9454793fa4 100644
--- a/bash-completion-test.el
+++ b/bash-completion-test.el
@@ -411,36 +411,6 @@ garbage
           (let ((default-directory "~/x"))
             (bash-completion-cd-command-prefix)))))
 
-(ert-deftest bash-completion-addsuffix-test ()
-  (cl-letf (((symbol-function 'file-accessible-directory-p)
-            (lambda (a) (error "unexpected"))))
-    (should (equal "hello/" (bash-completion-addsuffix nil "hello/")))
-    ;; ends with space"
-    (should (equal "hello " (bash-completion-addsuffix nil "hello ")))
-    ;; ends with separator"
-    (should (equal "hello:" (bash-completion-addsuffix nil "hello:"))))
-  ;; check directory"
-  (cl-letf (((symbol-function 'file-accessible-directory-p)
-            (lambda (a) (equal a "/tmp/hello")))
-           (default-directory "/tmp"))
-    (should (equal "hello/" (bash-completion-addsuffix nil "hello"))))
-  (cl-letf (((symbol-function 'file-accessible-directory-p)
-            (lambda (a) (equal a "/tmp/hello world")))
-           (default-directory "/tmp"))
-    (should (equal "hello\\ world/" (bash-completion-addsuffix nil "hello\\ 
world"))))
-  (cl-letf (((symbol-function 'file-accessible-directory-p)
-            (lambda (a) (equal a "/tmp/hello \"world\"")))
-           (default-directory "/tmp"))
-    (should (equal "hello \\\"world\\\"/" (bash-completion-addsuffix ?\" 
"hello \\\"world\\\""))))
-  (cl-letf (((symbol-function 'file-accessible-directory-p)
-            (lambda (a) (equal a "/tmp/d'uh")))
-           (default-directory "/tmp"))
-    (should (equal "d'\\''uh/" (bash-completion-addsuffix ?' "d'\\''uh"))))
-  (cl-letf (((symbol-function 'file-accessible-directory-p)
-            (lambda (a) (equal a (concat (expand-file-name "y" "~/x")))))
-           (default-directory "~/x"))
-    (should (equal "y/" (bash-completion-addsuffix nil "y")))))
-
 (ert-deftest bash-completion-starts-with-test ()
   (should (equal nil (bash-completion-starts-with "" "hello ")))
   (should (equal t (bash-completion-starts-with "hello world" "hello ")))
@@ -487,38 +457,62 @@ garbage
 (ert-deftest bash-completion-fix-test ()
   ;; escape rest
   (should (equal "a\\ bc\\ d\\ e"
-                (bash-completion-fix "a\\ bc d e" "a\\ b" "a\\ b")))
+                (bash-completion-fix "a\\ bc d e" "a\\ b" "a\\ b" nil nil)))
 
   ;; recover original escaping
   (should (equal "a' 'bc\\ d\\ e"
-                (bash-completion-fix "a\\ bc d e" "a\\ b" "a' 'b")))
+                (bash-completion-fix "a\\ bc d e" "a\\ b" "a' 'b" nil nil)))
 
   ;; do not escape final space
   (should (equal "ab "
                 (let ((bash-completion-nospace nil))
-                  (bash-completion-fix "ab " "a" "a"))))
+                  (bash-completion-fix "ab " "a" "a" nil nil))))
   
   ;; remove final space
   (should (equal "ab"
                 (let ((bash-completion-nospace t))
-                  (bash-completion-fix "ab " "a" "a"))))
+                  (bash-completion-fix "ab " "a" "a" nil nil))))
 
   ;; unexpand home and escape
   (should (equal "~/a/hello\\ world"
                 (bash-completion-fix (expand-file-name "~/a/hello world")
-                                     "~/a/he" "~/a/he")))
+                                     "~/a/he" "~/a/he" nil nil)))
 
   ;; match after wordbreak and escape
   (should (equal "a:b:c:hello\\ world"
-                (bash-completion-fix "hello world" "a:b:c:he" "a:b:c:he")))
+                (bash-completion-fix "hello world" "a:b:c:he" "a:b:c:he" nil 
nil)))
 
   ;; just append
   (should (equal "hello\\ world"
-                (bash-completion-fix " world" "hello" "hello")))
+                (bash-completion-fix " world" "hello" "hello" nil nil)))
+
+  ;; append / for home
+  (should (equal "~/"
+                 (bash-completion-fix (expand-file-name "~")  "~" "~" nil 
nil)))
+  
+  (cl-letf (((symbol-function 'file-accessible-directory-p)
+             (lambda (d)
+               (message "check: %s" d)
+               (equal d "/tmp/somedir"))))
+    (let ((default-directory "/tmp/"))
+      ;; append / for directory
+      (should (equal "somedir/"
+                     (bash-completion-fix "somedir" "some" "some" nil nil)))
+      ;; append / for initial command that is a directory
+      (should (equal "somedir/"
+                     (bash-completion-fix "somedir" "some" "some" nil t)))))
+
+  ;; append a space for initial command that is not a directory
+  (should (let ((bash-completion-nospace nil))
+            (equal "somecmd " (bash-completion-fix "somecmd" "some" "some" nil 
t))))
+  
+  ;; ... but not if nospace is t.
+  (should (let ((bash-completion-nospace t))
+            (equal "somecmd" (bash-completion-fix "somecmd" "some" "some" nil 
t))))
 
   ;; subset of the prefix"
   (should (equal "Dexter"
-                (bash-completion-fix "Dexter" "Dexter'" "Dexter'"))))
+                (bash-completion-fix "Dexter" "Dexter'" "Dexter'" nil nil))))
 
 (ert-deftest bash-completion-extract-candidates-test ()
   (should (equal 
@@ -528,7 +522,7 @@ garbage
            (cl-letf (((symbol-function 'bash-completion-buffer)
                       (lambda () (current-buffer)))
                      (bash-completion-nospace nil))
-             (bash-completion-extract-candidates "hello" "hello" nil))))))
+             (bash-completion-extract-candidates "hello" "hello" nil nil))))))
 
 (ert-deftest bash-completion-nonsep-test ()
   (should (equal "^ \t\n\r;&|'\"#"
@@ -643,7 +637,8 @@ garbage
                  (let ((comint-dynamic-complete-functions 
'(bash-completion-dynamic-complete)))
                    (completion-at-point))
                (bash-completion-dynamic-complete))
-             (buffer-substring (comint-line-beginning-position) (point))))
+             (buffer-substring-no-properties
+               (comint-line-beginning-position) (point))))
        ;; finally
        (when (and shell-buffer (buffer-live-p shell-buffer))
          (kill-process (get-buffer-process shell-buffer))
@@ -726,6 +721,24 @@ before calling `bash-completion-dynamic-complete-nocomint'.
    (should (equal "cd >/dev/null 2>&1 /tmp/test ; compgen -o default he 
2>/dev/null"
                   (pop --captured-commands)))))
 
+(ert-deftest bash-completion-trailing ()
+  (--with-fake-bash-completion-send
+   (push "without space\nwith space \nwith slash/\n" --send-results)
+   (insert "$ ls with")
+   (let ((bash-completion-nospace nil))
+     (should (equal
+              '("without\\ space" "with\\ space " "with\\ slash/")
+              (nth 2 (bash-completion-dynamic-complete-nocomint 3 
(point))))))))
+
+(ert-deftest bash-completion-trailing-nospace ()
+  (--with-fake-bash-completion-send
+   (push "without space\nwith space \nwith slash/\n" --send-results)
+   (insert "$ ls with")
+   (let ((bash-completion-nospace t))
+     (should (equal
+              '("without\\ space" "with\\ space" "with\\ slash/")
+              (nth 2 (bash-completion-dynamic-complete-nocomint 3 
(point))))))))
+
 (ert-deftest bash-completion-complete-dir-with-spaces-test ()
   (--with-fake-bash-completion-send
    (push "/tmp/test/Documents" --directories)
@@ -740,7 +753,7 @@ before calling `bash-completion-dynamic-complete-nocomint'.
    (push "Documents/Modes d'emplois\n" --send-results)
    (should (equal
             '("Documents/Modes\\ d\\'emplois/")
-            (nth 2(bash-completion-dynamic-complete-nocomint 3 (point)))))
+            (nth 2 (bash-completion-dynamic-complete-nocomint 3 (point)))))
    (insert "Modes\\ d\\'emplois/")
    (push "Documents/Modes d'emplois/KAR 1.pdf\nDocuments/Modes d'emplois/KAR 
2.pdf\n"
          --send-results)
@@ -772,4 +785,16 @@ before calling `bash-completion-dynamic-complete-nocomint'.
               "Documents/Modes d'\\''emplois/KAR 2.pdf")
             (nth 2 (bash-completion-dynamic-complete-nocomint 3 (point)))))))
 
+(ert-deftest bash-completion-complete-command-with-dir ()
+  (--with-fake-bash-completion-send
+   (push "/tmp/test/bin" --directories)
+   (push "bin\nbind\n" --send-results)
+   (insert "$ b")
+   (should (equal
+            '("bin/" "bind ")
+            (nth 2 (bash-completion-dynamic-complete-nocomint 3 (point)))))
+   (should (equal (concat "cd >/dev/null 2>&1 /tmp/test ; "
+                          "compgen -b -c -a -A function b 2>/dev/null")
+                  (pop --captured-commands)))))
+
 ;;; bash-completion_test.el ends here
diff --git a/bash-completion.el b/bash-completion.el
index ea2f173c23..a0e8850ded 100644
--- a/bash-completion.el
+++ b/bash-completion.el
@@ -198,9 +198,6 @@ to remove the extra space bash adds after a completion."
 
 (defvar bash-completion-process nil
   "Bash process object.")
-(defvar bash-completion-unparsed-prefix "" "")
-(defvar bash-completion-parsed-prefix "" "")
-(defvar bash-completion-open-quote nil "")
 (defvar bash-completion-alist nil
   "Maps from command name to the 'complete' arguments.
 
@@ -392,7 +389,7 @@ This function is not meant to be called outside of
                             (bash-completion-quote after-wordbreak)))
       (let ((completions
             (bash-completion-extract-candidates
-              after-wordbreak unparsed-after-wordbreak open-quote)))
+              after-wordbreak unparsed-after-wordbreak open-quote nil)))
        (list (+ stub-start separator-pos-in-unparsed)
               pos
              completions)))))
@@ -701,9 +698,9 @@ The result is a list of candidates, which might be empty."
             (bash-completion-generate-line line pos words cword nil))))
     (when (eq 0 completion-status)
       (bash-completion-extract-candidates
-       (nth cword words) unparsed-stub open-quote))))
+       (nth cword words) unparsed-stub open-quote (eq cword 0)))))
 
-(defun bash-completion-extract-candidates (parsed-stub unparsed-stub 
open-quote)
+(defun bash-completion-extract-candidates (parsed-stub unparsed-stub 
open-quote is-command)
   "Extract the completion candidates from the process buffer for PARSED-STUB.
 
 This command takes the content of the completion process buffer,
@@ -716,17 +713,20 @@ for directory name detection to work.
 If PARSED-STUB is quoted, the quote character, ' or \", should be
 passed in OPEN-QUOTE.
 
+If IS-COMMAND is t, it is passed down to `bash-completion-suffix'
+
 Post-processing includes escaping special characters, adding a /
 to directory names, replacing STUB with UNPARSED-STUB in the
 result. See `bash-completion-fix' for more details."
-  (let ((bash-completion-parsed-prefix parsed-stub)
-        (bash-completion-unparsed-prefix unparsed-stub)
-       (bash-completion-open-quote open-quote))
-    (mapcar 'bash-completion-fix
-           (with-current-buffer (bash-completion-buffer)
-             (split-string (buffer-string) "\n" t)))))
-
-(defun bash-completion-fix (str &optional parsed-prefix unparsed-prefix 
open-quote)
+  (let ((result (list)))
+    (dolist (completion (with-current-buffer (bash-completion-buffer)
+                          (split-string (buffer-string) "\n" t)))
+      (push (bash-completion-fix
+             completion parsed-stub unparsed-stub open-quote is-command)
+            result))
+    (nreverse result)))
+
+(defun bash-completion-fix (str parsed-prefix unparsed-prefix open-quote 
is-command)
   "Fix completion candidate in STR if PREFIX is the current prefix.
 
 STR is the completion candidate to modify.
@@ -743,6 +743,9 @@ character (' or \") or nil.  If it is nil, the value of
 `bash-completion-open-quote' is used.  This allows
 calling this function from `mapcar'.
 
+If IS-COMMAND is t and `bash-completion-nospace' isn't set,
+guarantee the result return with a known suffix or space.
+
 Return a modified version of the completion candidate.
 
 Modification include:
@@ -755,42 +758,62 @@ It should be invoked with the comint buffer as the 
current buffer
 for directory name detection to work."
   (let ((parsed-prefix (or parsed-prefix bash-completion-parsed-prefix))
         (unparsed-prefix (or unparsed-prefix bash-completion-unparsed-prefix))
-       (open-quote (or open-quote (and bash-completion-open-quote)))
-       (suffix ""))
-    (bash-completion-addsuffix
-     open-quote
-     (let* (rebuilt
-           (rest (cond
-                  ((bash-completion-starts-with str parsed-prefix)
-                   (substring str (length parsed-prefix)))
-                  ;; unexpand the home directory expanded by bash automatically
-                  ((and (bash-completion-starts-with parsed-prefix "~")
-                        (bash-completion-starts-with str (expand-file-name 
"~")))
-                   (substring (concat "~" (substring str (length 
(expand-file-name "~"))))
-                              (length parsed-prefix)))
-                  ((bash-completion-starts-with parsed-prefix str)
-                   ;; completion is a substring of prefix something's gone
-                   ;; wrong. Treat it as one (useless) candidate.
-                    (setq unparsed-prefix "")
-                    str)
-                  ;; completion sometimes only applies to the last word, as
-                  ;; defined by COMP_WORDBREAKS. This detects and works around
-                  ;; this feature.
-                  ((bash-completion-starts-with
-                    (setq rebuilt (concat 
(bash-completion-before-last-wordbreak parsed-prefix) str))
-                    parsed-prefix)
-                   (substring rebuilt (length parsed-prefix)))
-                  ;; there is no meaningful link between the prefix and
-                  ;; the string. just append the string to the prefix and
-                  ;; hope for the best.
-                  (t str))))
-       (when (bash-completion-ends-with rest " ")
-        (setq rest (substring rest 0 -1))
-        (unless bash-completion-nospace
-          (setq suffix " ")))
-       (concat unparsed-prefix
-               (bash-completion-escape-candidate rest open-quote)
-               suffix)))))
+        (open-quote (or open-quote (and bash-completion-open-quote)))
+        (suffix "")
+        (rest) ; the part between the prefix and the suffix
+        (rebuilt))
+
+    ;; build rest by removing parsed-prefix from str
+    (cond
+     ((bash-completion-starts-with str parsed-prefix)
+      (setq rest (substring str (length parsed-prefix))))
+
+     ;; unexpand the home directory expanded by bash automatically
+     ((and (bash-completion-starts-with parsed-prefix "~")
+           (bash-completion-starts-with str (expand-file-name "~")))
+      (setq rest (substring (concat "~" (substring str (length 
(expand-file-name "~"))))
+                            (length parsed-prefix))))
+
+     ((bash-completion-starts-with parsed-prefix str)
+      ;; completion is a substring of prefix something's gone
+      ;; wrong. Treat it as one (useless) candidate.
+      (setq unparsed-prefix "")
+      (setq rest str))
+
+     ;; completion sometimes only applies to the last word, as
+     ;; defined by COMP_WORDBREAKS. This detects and works around
+     ;; this feature.
+     ((bash-completion-starts-with
+       (setq rebuilt (concat (bash-completion-before-last-wordbreak 
parsed-prefix) str))
+       parsed-prefix)
+      (setq rest (substring rebuilt (length parsed-prefix))))
+
+     ;; there is no meaningful link between the prefix and
+     ;; the string. just append the string to the prefix and
+     ;; hope for the best.
+     (t (setq rest str)))
+
+    ;; build suffix
+    (let ((last-char (bash-completion-last-char rest)))
+      (cond
+       ((eq ?\  last-char)
+        (setq rest (substring rest 0 -1))
+        (setq suffix (if bash-completion-nospace "" " ")))
+       ((or (memq last-char bash-completion-wordbreaks)
+            (eq ?/ last-char))
+        (setq suffix ""))
+       ((file-accessible-directory-p
+         (expand-file-name (bash-completion-unescape
+                            open-quote (concat parsed-prefix rest))
+                           default-directory))
+        (setq suffix "/"))
+       (is-command
+        (setq suffix (if bash-completion-nospace "" " ")))))
+
+    ;; put everything back together
+    (concat unparsed-prefix
+            (bash-completion-escape-candidate rest open-quote)
+            suffix)))
 
 (defun bash-completion-escape-candidate (completion-candidate open-quote)
   "Escapes COMPLETION-CANDIDATE.
@@ -828,24 +851,6 @@ Return a possibly escaped version of COMPLETION-CANDIDATE."
       (replace-regexp-in-string "'\\\\''" "'" string)
     (replace-regexp-in-string "\\(\\\\\\)\\(.\\)" "\\2" string)))
 
-(defconst bash-completion-known-suffixes-regexp
-  (concat "[" (regexp-quote bash-completion-wordbreaks-str) "/ ]$")
-  "Regexp matching known suffixes for `bash-completion-addsuffix'.")
-
-(defun bash-completion-addsuffix (open-quote str)
-  "Add a directory suffix to OPEN-QUOTE'd STR if it looks like a directory.
-
-This function looks for a directory called STR relative to the
-buffer-local variable default-directory. If it exists, it returns
-\(concat STR \"/\"). Otherwise it retruns STR."
-  (if (and (null (string-match bash-completion-known-suffixes-regexp str))
-          (file-accessible-directory-p
-            (expand-file-name
-             (bash-completion-unescape open-quote str)
-             default-directory)))
-       (concat str "/")
-    str))
-
 (defun bash-completion-before-last-wordbreak (str)
   "Return the part of STR that comes after the last wordbreak character.
 The return value does not include the worbreak character itself.
@@ -891,15 +896,11 @@ Return a CONS containing (before . after)."
        (setq end (1- end))))
       (list "" str ?\0)))
 
-(defun bash-completion-ends-with (str suffix)
-  "Return t if STR ends with SUFFIX."
-  (let ((suffix-len (length suffix))
-       (str-len (length str)))
-    (or
-     (= 0 suffix-len)
-     (and
-      (>= str-len suffix-len)
-      (string= (substring str (- suffix-len)) suffix)))))
+(defun bash-completion-last-char (str)
+  "Returns the last char of STR or nil."
+  (let ((str-len (length str)))
+    (and (>= str-len 1)
+         (aref str (1- str-len)))))
 
 (defun bash-completion-starts-with (str prefix)
   "Return t if STR starts with PREFIX."
@@ -1078,9 +1079,8 @@ candidates."
           (stub (nth cword words)) )
      (cond
       ((= cword 0)
-       ;; a command. let emacs expand executable, let bash
-       ;; expand builtins, aliases and functions
-       (concat "compgen -S ' ' -b -c -a -A function " stub))
+       ;; a command. let bash expand builtins, aliases and functions
+       (concat "compgen -b -c -a -A function " stub))
 
       ((not compgen-args)
        ;; no completion configured for this command



reply via email to

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