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

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

[nongnu] elpa/bash-completion 0aed19067b 188/313: When using Bash 4, sp


From: ELPA Syncer
Subject: [nongnu] elpa/bash-completion 0aed19067b 188/313: When using Bash 4, split words as Bash 4 does.
Date: Sat, 3 Dec 2022 10:59:29 -0500 (EST)

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

    When using Bash 4, split words as Bash 4 does.
    
    Bash 3 doesn't apply COMP_WORDBREAKS to the words, just to the stub.
    Bash 4 applies it to both.
    
    This commit makes emacs-bash-completion behave in a way that's
    compatible with the current bash major version. This fixes wordbreak
    completion when on bash 4, when using the _minimal function.
    
    Issue #32
---
 bash-completion.el           |  80 ++++++++++++++++++----------------
 test/bash-completion-test.el | 100 +++++++++++++++++++++++++++++--------------
 2 files changed, 112 insertions(+), 68 deletions(-)

diff --git a/bash-completion.el b/bash-completion.el
index bce01221f7..1d9652eb37 100644
--- a/bash-completion.el
+++ b/bash-completion.el
@@ -202,6 +202,9 @@ to remove the extra space bash adds after a completion."
   "Shell files that, if they exist, will be sourced at the
 beginning of a bash completion subprocess.")
 
+(defvar bash-completion-wordbreaks ""
+  "Extra wordbreaks to use when tokenizing, in `bash-completion-tokenize'")
+
 ;;; ---------- Internal variables and constants
 
 (defvar bash-completion-processes nil
@@ -378,7 +381,8 @@ Returns (list stub-start stub-end completions) with
     (let* ((process (bash-completion-require-process))
            (comp (bash-completion--parse
                   comp-start comp-pos
-                  (process-get process 'wordbreaks)))
+                  (process-get process 'wordbreaks)
+                  (process-get process 'bash-major-version)))
           (stub-start (bash-completion--stub-start comp)))
       (bash-completion--customize comp process)
       (list
@@ -426,7 +430,7 @@ functions adds single quotes around it and return the 
result."
            (replace-regexp-in-string "'" "'\\''" word nil t)
            "'")))
 
-(defun bash-completion--parse (comp-start comp-pos wordbreaks)
+(defun bash-completion--parse (comp-start comp-pos wordbreaks 
bash-major-version)
   "Process a command line split into TOKENS that end at POS.
 
 WORDBREAK is the value of COMP_WORDBREAKS to use for this completion,
@@ -437,7 +441,9 @@ This function takes a list of tokens built by
 function expect in an association list.
 
 Returns a completion struct."
-  (let* ((all-tokens (bash-completion-tokenize comp-start comp-pos))
+  (let* ((all-tokens (bash-completion-tokenize
+                      comp-start comp-pos (if (>= bash-major-version 4)
+                                              wordbreaks "")))
          (line-tokens (bash-completion-parse-current-command  all-tokens))
          (first-token (car line-tokens))
         (last-token (car (last line-tokens)))
@@ -451,21 +457,12 @@ Returns a completion struct."
               unparsed-stub ""
               parsed-stub ""
               words (append words '("")))
-      (let* ((last-word-start (car (bash-completion-tokenize-get-range 
last-token)))
-             (last-word (bash-completion-tokenize-get-str last-token))
-             (last-word-unparsed (buffer-substring-no-properties 
last-word-start comp-pos))
-             (last-word-unparsed-split (bash-completion-last-wordbreak-split
-                                        last-word-unparsed wordbreaks))
-             (last-word-unparsed-separator (nth 2 last-word-unparsed-split))
-             (last-word-unparsed-before (if (zerop 
last-word-unparsed-separator)
-                                            ""
-                                          (nth 0 last-word-unparsed-split))))
-        (setq stub-start (+ last-word-start (length last-word-unparsed-before))
-              unparsed-stub (buffer-substring-no-properties stub-start 
comp-pos)
-              parsed-stub (substring last-word
-                                     (1+ (or (bash-completion--find-last
-                                              last-word-unparsed-separator 
last-word)
-                                             -1))))))
+      (if (< bash-major-version 4)
+          (setq last-token (car (last (bash-completion-tokenize
+                                       comp-start comp-pos wordbreaks)))))
+      (setq stub-start (car (bash-completion-tokenize-get-range last-token))
+            parsed-stub (bash-completion-tokenize-get-str last-token)
+            unparsed-stub (buffer-substring-no-properties stub-start 
comp-pos)))
     (bash-completion--make
      :line (buffer-substring-no-properties start comp-pos)
      :point (- comp-pos start)
@@ -526,12 +523,13 @@ list of strings.
 TOKENS should be in the format returned by `bash-completion-tokenize'."
   (mapcar 'bash-completion-tokenize-get-str tokens))
 
-(defun bash-completion-tokenize (start end)
+(defun bash-completion-tokenize (start end &optional wordbreaks)
   "Tokenize the portion of the current buffer between START and END.
 
 This function splits a BASH command line into tokens.  It knows
 about quotes, escape characters and special command separators such
-as ;, | and &&.
+as ;, | and &&. If specified WORDBREAKS contains extra word breaks,
+usually taken from COMP_WORDBREAKS, to apply while tokenizing.
 
 This method returns a list of tokens found between START and END,
 ordered by position.  Tokens contain a string and a range.
@@ -554,9 +552,16 @@ set using `bash-completion-tokenize-set-end'.
 Tokens should always be accessed using the functions specified above,
 never directly as they're likely to change as this code evolves.
 The current format of a token is '(string . (start . end))."
-  (save-excursion
-    (goto-char start)
-    (nreverse (bash-completion-tokenize-new-element end nil))))
+  (let ((bash-completion-wordbreaks
+         (mapconcat 'char-to-string
+                    (delq nil (mapcar
+                               (lambda (c)
+                                 (if (memq c '(?\; ?& ?| ?' ?\")) nil c))
+                               (or wordbreaks "")))
+                    "")))
+    (save-excursion
+      (goto-char start)
+      (nreverse (bash-completion-tokenize-new-element end nil)))))
 
 (defun bash-completion-tokenize-new-element (end tokens)
   "Tokenize the rest of the line until END and complete TOKENS.
@@ -626,8 +631,12 @@ Return TOKENS with new tokens prepended to it."
   ;; parse the token elements at the current position and
   ;; append them
   (let ((local-start (point)))
-    (when (= (skip-chars-forward "[;&|]" end) 0)
-      (skip-chars-forward (bash-completion-nonsep quote) end))
+    (when (= (skip-chars-forward
+              (concat "[;&|" bash-completion-wordbreaks "]")
+              end)
+             0)
+      (skip-chars-forward
+       (bash-completion-nonsep quote bash-completion-wordbreaks) end))
     (bash-completion-tokenize-append-str
      token
      (buffer-substring-no-properties local-start (point))))
@@ -658,20 +667,14 @@ Return TOKENS with new tokens prepended to it."
     (push token tokens)
     (bash-completion-tokenize-new-element end tokens))))
 
-(defconst bash-completion-nonsep-alist
-  '((nil . "^ \t\n\r;&|'\"#")
-    (?' . "^ \t\n\r'")
-    (?\" . "^ \t\n\r\""))
-  "Alist of sets of non-breaking characters.
-Keeps a regexp specifying the set of non-breaking characters for
-all quoting environment (no quote, single quote and double
-quote).  Get it using `bash-completion-nonsep'.")
-
-(defun bash-completion-nonsep (quote)
+(defun bash-completion-nonsep (quote wordbreaks)
   "Return the set of non-breaking characters when QUOTE is the current quote.
 
 QUOTE should be nil, ?' or ?\"."
-  (cdr (assq quote bash-completion-nonsep-alist)))
+  (concat
+   "^ \t\n\r"
+   (if (null quote) (concat ";&|'\"" wordbreaks)
+     (char-to-string quote))))
 
 ;;; ---------- Functions: getting candidates from bash
 
@@ -965,6 +968,11 @@ is set to t."
               (bash-completion-send "complete -p" process)
               (process-put process 'complete-p
                            (bash-completion-build-alist (process-buffer 
process)))
+              (bash-completion-send "echo -n ${BASH_VERSINFO[0]}" process)
+              (process-put process 'bash-major-version
+                           (with-current-buffer (process-buffer process)
+                             (string-to-number (buffer-substring-no-properties
+                                                (point-min) (point-max)))))
               (bash-completion-send "echo -n ${COMP_WORDBREAKS}" process)
               (process-put process 'wordbreaks
                            (with-current-buffer (process-buffer process)
diff --git a/test/bash-completion-test.el b/test/bash-completion-test.el
index 7472d75edc..c3f3b78974 100644
--- a/test/bash-completion-test.el
+++ b/test/bash-completion-test.el
@@ -76,13 +76,6 @@ The return value is the one returned by BODY."
                  (bash-completion-strings-from-tokens
                   (bash-completion-tokenize 1 (line-end-position))))))
 
-  ;; escaped #
-  (should (equal '("a" "hello" "#world#" "b")
-                (bash-completion-test-with-buffer
-                 "a hello \\#world\\# b"
-                 (bash-completion-strings-from-tokens
-                  (bash-completion-tokenize 1 (line-end-position))))))
-
   ;; double quotes
   (should (equal '("a" "hello world" "b" "c")
                 (bash-completion-test-with-buffer
@@ -147,7 +140,7 @@ The return value is the one returned by BODY."
                   (bash-completion-tokenize 1 (line-end-position)))))))
 
 (ert-deftest bash-completion--parse-test ()
-  (let ((wordbreaks "@><=;|&(:"))
+  (let ((wordbreaks "@><=;|&(:'\""))
     ;; cursor at end of word
     (should (equal
              (bash-completion--make
@@ -161,7 +154,7 @@ The return value is the one returned by BODY."
               :wordbreaks wordbreaks)
           (bash-completion-test-with-buffer
            "a hello world"
-           (bash-completion--parse (point-min) 14 wordbreaks))))
+           (bash-completion--parse (point-min) 14 wordbreaks 3))))
 
   ;; some words separated by spaces, cursor after the last space
   (should (equal
@@ -176,7 +169,7 @@ The return value is the one returned by BODY."
             :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "a hello "
-            (bash-completion--parse (point-min) 9 wordbreaks))))
+            (bash-completion--parse (point-min) 9 wordbreaks 3))))
 
   ;; complex multi-command line
   (should (equal
@@ -191,7 +184,7 @@ The return value is the one returned by BODY."
             :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "cd /var/tmp ; ZORG=t make -"
-            (bash-completion--parse (point-min) 28 wordbreaks))))
+            (bash-completion--parse (point-min) 28 wordbreaks 3))))
 
   ;; pipe
   (should (equal
@@ -206,7 +199,7 @@ The return value is the one returned by BODY."
             :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "ls /var/tmp | sort -"
-            (bash-completion--parse (point-min) 21 wordbreaks))))
+            (bash-completion--parse (point-min) 21 wordbreaks 3))))
 
   ;; escaped semicolon
   (should (equal
@@ -221,7 +214,7 @@ The return value is the one returned by BODY."
             :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "find -name '*.txt' -exec echo {} ';' -"
-            (bash-completion--parse (point-min) 39 wordbreaks))))
+            (bash-completion--parse (point-min) 39 wordbreaks 3))))
 
   ;; at var assignment
   (should (equal
@@ -236,9 +229,9 @@ The return value is the one returned by BODY."
             :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "cd /var/tmp ; A=f ZORG=t"
-            (bash-completion--parse (point-min) 25 wordbreaks))))
+            (bash-completion--parse (point-min) 25 wordbreaks 3))))
 
-  ;; stub is a subset of last word
+  ;; stub is a subset of last word (bash 3)
   (should (equal
            (bash-completion--make
             :line "export PATH=/bin:/usr/bi"
@@ -251,7 +244,22 @@ The return value is the one returned by BODY."
             :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "export PATH=/bin:/usr/bi"
-            (bash-completion--parse (point-min) (point-max) wordbreaks))))
+            (bash-completion--parse (point-min) (point-max) wordbreaks 3))))
+
+  ;; last word is split according to COMP_WORDBREAKS (bash 4)
+  (should (equal
+           (bash-completion--make
+            :line "export PATH=/bin:/usr/bi"
+            :point 24
+            :cword 5
+            :words '("export" "PATH" "=" "/bin" ":" "/usr/bi")
+            :stub-start 18
+            :stub "/usr/bi"
+            :unparsed-stub "/usr/bi"
+            :wordbreaks wordbreaks)
+           (bash-completion-test-with-buffer
+            "export PATH=/bin:/usr/bi"
+            (bash-completion--parse (point-min) (point-max) wordbreaks 4))))
 
   ;; with escaped quote
   (should (equal
@@ -266,7 +274,22 @@ The return value is the one returned by BODY."
             :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "cd /vcr/shows/Dexter\\'s"
-            (bash-completion--parse (point-min) 24 wordbreaks))))
+            (bash-completion--parse (point-min) 24 wordbreaks 3))))
+
+  ;; with escaped quote, bash 4
+  (should (equal
+           (bash-completion--make
+            :line "cd /vcr/shows/Dexter\\'s"
+            :point 23
+            :cword 1
+            :words '("cd" "/vcr/shows/Dexter's")
+            :stub-start 4
+            :stub "/vcr/shows/Dexter's"
+            :unparsed-stub "/vcr/shows/Dexter\\'s"
+            :wordbreaks wordbreaks)
+           (bash-completion-test-with-buffer
+            "cd /vcr/shows/Dexter\\'s"
+            (bash-completion--parse (point-min) 24 wordbreaks 4))))
 
   ;; with double quote
   (should (equal
@@ -282,7 +305,7 @@ The return value is the one returned by BODY."
             :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "cd \"/vcr/shows/Dexter's"
-            (bash-completion--parse (point-min) 24 wordbreaks))))
+            (bash-completion--parse (point-min) 24 wordbreaks 3))))
 
   ;; with single quote
   (should (equal
@@ -298,7 +321,7 @@ The return value is the one returned by BODY."
             :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "cd '/vcr/shows/Dexter'\\''s"
-            (bash-completion--parse (point-min) 27 wordbreaks))))
+            (bash-completion--parse (point-min) 27 wordbreaks 3))))
 
   ;; just one space, cursor after it
   (should (equal
@@ -313,7 +336,7 @@ The return value is the one returned by BODY."
             :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             " "
-            (bash-completion--parse (point-min) 2 wordbreaks))))))
+            (bash-completion--parse (point-min) 2 wordbreaks 3))))))
 
 (ert-deftest bash-completion-build-alist ()
   (should (equal
@@ -429,11 +452,17 @@ garbage
 (ert-deftest bash-completion-customize-test ()
   (cl-letf (((symbol-function 'process-get)
              (lambda (process prop)
-               (if (and (eq 'process process)
-                        (eq 'complete-p prop))
-                   '((nil "-F" "__default")
-                     ("zorg" "-F" "__zorg"))
-                 (error "unexpected call")))))
+               (cond
+                ((and (eq 'process process)
+                      (eq 'complete-p prop))
+                 '((nil "-F" "__default")
+                   ("zorg" "-F" "__zorg")))
+                ((and (eq 'process process)
+                      (eq 'bash-major-version prop)) 3)
+                ((and (eq 'process process)
+                      (eq 'wordbreaks prop)) "\"'@><=;|&(:")
+                (t (error "unexpected: (process-get %s %s)"
+                          process prop))))))
     (let ((comp (bash-completion--make :cword 1)))
       (setf (bash-completion--words comp) '("zorg" "world"))
       (bash-completion--customize comp 'process)
@@ -765,12 +794,14 @@ Return (const return-value new-buffer-content)"
         (current-buffer)))))))
 
 (ert-deftest bash-completion-nonsep-test ()
-  (should (equal "^ \t\n\r;&|'\"#"
-                (bash-completion-nonsep nil)))
+  (should (equal "^ \t\n\r;&|'\""
+                (bash-completion-nonsep nil "")))
+  (should (equal "^ \t\n\r;&|'\":="
+                (bash-completion-nonsep nil ":=")))
   (should (equal "^ \t\n\r'"
-                (bash-completion-nonsep ?')))
+                (bash-completion-nonsep ?' "")))
   (should (equal "^ \t\n\r\""
-                (bash-completion-nonsep ?\"))))
+                (bash-completion-nonsep ?\" ""))))
 
 (ert-deftest bash-completion-escape-candidate-test ()
   ;; empty string - no quote
@@ -863,6 +894,7 @@ before calling `bash-completion-dynamic-complete-nocomint'.
   `(let ((default-directory "/tmp/test")
          (bash-completion-alist '())
          (wordbreaks "@><=;|&(:")
+         (bash-major-version 3)
          (bash-completion-nospace nil))
      (lexical-let ((--process-buffer)
                    (--test-buffer)
@@ -876,9 +908,11 @@ before calling `bash-completion-dynamic-complete-nocomint'.
            (cl-letf (((symbol-function 'bash-completion-require-process) 
(lambda () 'process))
                      ((symbol-function 'process-put)
                       (lambda (process prop value)
-                        (if (and (eq 'process process) (eq 'complete-p prop))
-                            (setq bash-completion-alist value)
-                          (error "unexpected call"))))
+                        (cond ((and (eq 'process process) (eq 'complete-p 
prop))
+                               (setq bash-completion-alist value))
+                              ((and (eq 'process process) (eq 
'bash-major-version prop))
+                               (setq bash-major-version value))
+                              (t (error "unexpected: (process-put %s %s)" 
process prop)))))
                      ((symbol-function 'process-get)
                       (lambda (process prop)
                         (cond
@@ -886,6 +920,8 @@ before calling `bash-completion-dynamic-complete-nocomint'.
                           bash-completion-alist)
                          ((and (eq 'process process) (eq 'wordbreaks prop))
                           wordbreaks)
+                         ((and (eq 'process process) (eq 'bash-major-version 
prop))
+                          bash-major-version)
                          (t (error "unexpected call")))))
                      ((symbol-function 'bash-completion-buffer) (lambda () 
--process-buffer))
                      ((symbol-function 'process-buffer) (lambda (p) 
--process-buffer))



reply via email to

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