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

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

[nongnu] elpa/bash-completion 4f13e00702 183/313: Execute wordbreak comp


From: ELPA Syncer
Subject: [nongnu] elpa/bash-completion 4f13e00702 183/313: Execute wordbreak completion "properly", as part of the normal
Date: Sat, 3 Dec 2022 10:59:29 -0500 (EST)

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

    Execute wordbreak completion "properly", as part of the normal
    completion, instead of as an extra completion, when nothing matches.
    
    With this commit, emacs-bash-completion's behavior gets closer to the
    one of bash 3, when it comes to wordbreaks, that is: the words
    of the lines are split according to shell rules and the result is used
    to build COMP_WORDS. After that, wordbreak completion, controlled by
    COMP_WORDBREAKS, is applied on the last word and, if a wordbreak is
    found, the stub will be the part of the last word after that
    wordbreak.
    
    Note that bash 4 behaves differently: it splits COMP_WORDS according
    to COMP_WORDBREAKS.
    
    This commit also fixes the arguments of completion functions: before
    emacs-bash-completion would send a copy of COMP_WORDS, but what bash 3
    does is send:
     the command to complete
     the stub
     the last word before COMP_CWORDS
    
    Related to issue #30
---
 bash-completion.el                       | 308 +++++++-------------
 test/bash-completion-integration-test.el |   7 +-
 test/bash-completion-test.el             | 477 ++++++++++++++++++++-----------
 3 files changed, 427 insertions(+), 365 deletions(-)

diff --git a/bash-completion.el b/bash-completion.el
index d48c808444..ae4b988748 100644
--- a/bash-completion.el
+++ b/bash-completion.el
@@ -230,17 +230,6 @@ beginning of a bash completion subprocess.")
 Mapping between remote paths as returned by `file-remote-p' and
 Bash processes")
 
-(defconst bash-completion-wordbreaks-str "@><=;|&(:"
-  "String of word break characters.
-This is the equivalent of COMP_WORDBREAKS: special characters
-that are considered word breaks in some cases when doing
-completion.  This was introduced initially to support file
-completion in colon-separated values.")
-
-(defconst bash-completion-wordbreaks
-  (append bash-completion-wordbreaks-str nil)
-  "`bash-completion-wordbreaks-str' as a list of characters.")
-
 (defconst bash-completion-special-chars "[^-0-9a-zA-Z_./\n=]"
   "Regexp of characters that must be escaped or quoted.")
 
@@ -271,16 +260,16 @@ to be included into a completion output.")
   point          ; 0-based position of the cursor in line (number)
   words          ; line split into words, unescaped (list of strings)
   cword          ; 0-based index of the word to be completed in words (number)
+  unparsed-stub  ; unparsed version of the thing we are completing,
+                 ; that is, the part of the last word after the last
+                 ; wordbreak separator.
   stub-start     ; start position of the thing we are completing
-  unparsed-stub  ; unparsed version of (nth cword words)
+  stub           ; parsed version of the stub
   open-quote     ; quote open at stub end: nil, ?' or ?\""
-  compgen-args   ; compgen arguments, if custom (list of strings)
+  compgen-args   ; compgen arguments for this command (list of strings)
+  wordbreaks     ; value of COMP_WORDBREAKS active for this completion
 )
 
-(defsubst bash-completion--stub (comp)
-  "Returns the stub being completed for COMP."
-  (nth (bash-completion--cword comp) (bash-completion--words comp)))
-
 (defun bash-completion--type (comp)
   "Returns the type of COMP.
 
@@ -305,6 +294,10 @@ See options definition in
         (bash-completion--compgen-args comp))
      nil)))
 
+(defun bash-completion--command (comp)
+  "Return the current command for the completion, if there is one."
+  (file-name-nondirectory (car (bash-completion--words comp))))
+
 ;;; ---------- Inline functions
 
 (defsubst bash-completion-tokenize-get-range (token)
@@ -394,68 +387,20 @@ Returns (list stub-start stub-end completions) with
  - completions, a possibly empty list of completion candidates or a function if
    `bash-completion-enable-caching' is non-nil"
   (when bash-completion-enabled
-    (let* ((comp (bash-completion--parse comp-start comp-pos))
-           (open-quote (bash-completion--open-quote comp))
-          (stub-start (bash-completion--stub-start comp))
-           (stub (bash-completion--stub comp))
-           (unparsed-stub (bash-completion--unparsed-stub comp)))
-      (if bash-completion-enable-caching
-          (list
-           stub-start
-           comp-pos
+    (let* ((process (bash-completion-require-process))
+           (comp (bash-completion--parse
+                  comp-start comp-pos
+                  (process-get process 'wordbreaks)))
+          (stub-start (bash-completion--stub-start comp)))
+      (bash-completion--customize comp process)
+      (list
+       stub-start
+       comp-pos
+       (if bash-completion-enable-caching
            (bash-completion--completion-table-with-cache
             (lambda (_)
-              (bash-completion--customize comp)
-              (or (bash-completion-comm comp)
-                  (pcase-let ((`(,wordbreak-start _ ,wordbreak-collection)
-                               (bash-completion--try-wordbreak-complete
-                                stub unparsed-stub stub-start comp-pos
-                                open-quote)))
-                    (if wordbreak-collection
-                        ;; prepend the part of unparsed-stub before
-                        ;; the wordbreak.
-                        (let ((before-wordbreak
-                               (substring unparsed-stub 0
-                                          (- wordbreak-start stub-start))))
-                          (mapcar (lambda (c) (concat before-wordbreak c))
-                                  wordbreak-collection))))))))
-        (bash-completion--customize comp)
-        (let ((completions (bash-completion-comm comp)))
-          (if completions
-              (list stub-start comp-pos completions)
-            (bash-completion--try-wordbreak-complete
-             stub unparsed-stub stub-start comp-pos open-quote)))))))
-
-(defun bash-completion--try-wordbreak-complete
-    (parsed-stub unparsed-stub stub-start pos open-quote)
-  "Try wordbreak completion on PARSED-STUB if the complete completion failed.
-
-Split PARSED-STUB using the wordbreak list and apply compgen
-default completion on the last part. Return non-nil if a match
-was found. The original version of the stub is UNPARSED-STUB. It
-can be found on the buffer, between STUB-START and POS.
-
-If PARSED-STUB is quoted, the quote character, ' or \", should be
-passed to the parameter OPEN-QUOTE.
-
-This function is not meant to be called outside of
-`bash-completion-dynamic-complete'."
-  (let* ((wordbreak-split (bash-completion-last-wordbreak-split parsed-stub))
-         (before-wordbreak (nth 0 wordbreak-split))
-        (after-wordbreak (nth 1 wordbreak-split))
-         (separator (nth 2 wordbreak-split))
-         (after-wordbreak-in-unparsed-pos
-          (1+ (or (bash-completion--find-last separator unparsed-stub) -1)))
-         (unparsed-after-wordbreak
-          (substring unparsed-stub
-                     after-wordbreak-in-unparsed-pos
-                     (length unparsed-stub))))
-    (when (> (length before-wordbreak) 0)
-      (list (+ stub-start after-wordbreak-in-unparsed-pos)
-            pos
-            (bash-completion--default-completion
-             after-wordbreak unparsed-after-wordbreak
-             open-quote (bash-completion--parse-options nil))))))
+              (bash-completion-comm comp process)))
+         (bash-completion-comm comp process))))))
 
 (defun bash-completion--find-last (elt array)
   "Return the position of the last intance of ELT in array or nil."
@@ -466,20 +411,6 @@ This function is not meant to be called outside of
             (throw 'bash-completion-return (- array-len index 1)))))
     nil))
 
-(defun bash-completion--default-completion
-    (stub unparsed-stub open-quote options)
-  "Do default completion on the given STUB.
-
-Return the extracted candidate, with STUB replaced with
-UNPARSED-STUB, taking OPEN-QUOTE into account."
-  (when (eq 0 (bash-completion-send (concat
-                                     (bash-completion-cd-command-prefix)
-                                     "compgen -o default -- "
-                                     (bash-completion-quote stub))))
-    (bash-completion-extract-candidates
-     stub unparsed-stub open-quote
-     options)))
-
 ;;; ---------- Functions: parsing and tokenizing
 
 (defun bash-completion-join (words)
@@ -507,24 +438,17 @@ 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)
+(defun bash-completion--parse (comp-start comp-pos wordbreaks)
   "Process a command line split into TOKENS that end at POS.
 
-If stub is quoted, the quote character should be passed as
-OPEN-QUOTE.
+WORDBREAK is the value of COMP_WORDBREAKS to use for this completion,
+usually taken from the current process.
 
 This function takes a list of tokens built by
 `bash-completion-tokenize' and returns the variables compgen
 function expect in an association list.
 
-Return an association list with the current symbol as keys:
- line - the relevant command between START and POS (string)
- point - 0-based position of the cursor in line (number)
- cword - 0-based index of the word to be completed in words (number)
- words - line split into words, unescaped (list of strings)
- stub-start - start position of the thing we are completing
- unparsed-stub - unparsed version of (nth cword words)
- open-quote - quote open at stub end: nil, ?' or ?\""
+Returns a completion struct."
   (let* ((all-tokens (bash-completion-tokenize comp-start comp-pos))
          (line-tokens (bash-completion-parse-current-command  all-tokens))
          (first-token (car line-tokens))
@@ -533,21 +457,37 @@ Return an association list with the current symbol as 
keys:
         (start (or (car (bash-completion-tokenize-get-range first-token)) 
comp-pos))
         (end (or (cdr (bash-completion-tokenize-get-range last-token)) 
comp-pos))
         (words (bash-completion-strings-from-tokens line-tokens))
-        (stub-empty (or (> comp-pos end) (= start end)))
-        (stub-start
-         (if stub-empty
-             comp-pos
-           (+ (car (bash-completion-tokenize-get-range last-token))
-              (if open-quote 1 0)))))
-    (when stub-empty (setq words (append words '(""))))
+         (stub-start) (unparsed-stub) (parsed-stub))
+    (if (or (> comp-pos end) (= start end))
+        (setq stub-start comp-pos
+              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))))))
     (bash-completion--make
      :line (buffer-substring-no-properties start comp-pos)
      :point (- comp-pos start)
      :cword (- (length words) 1)
      :words words
      :stub-start stub-start
-     :unparsed-stub (buffer-substring-no-properties stub-start comp-pos)
-     :open-quote open-quote)))
+     :unparsed-stub unparsed-stub
+     :stub parsed-stub
+     :open-quote open-quote
+     :wordbreaks wordbreaks)))
 
 (defun bash-completion-parse-current-command (tokens)
   "Extract from TOKENS the tokens forming the current command.
@@ -747,8 +687,8 @@ QUOTE should be nil, ?' or ?\"."
 
 ;;; ---------- Functions: getting candidates from bash
 
-(defun bash-completion-comm (comp)
-  "Call compgen on COMP return the result.
+(defun bash-completion-comm (comp process)
+  "Call compgen on COMP for PROCESS, return the result.
 
 COMP should be a struct returned by `bash-completion--parse'
 
@@ -757,9 +697,8 @@ up the completion environment (COMP_LINE, COMP_POINT, 
COMP_WORDS,
 COMP_CWORD) and calls compgen.
 
 The result is a list of candidates, which might be empty."
-  (let* ((process (bash-completion-require-process))
-         (completion-status)
-         (options))
+  (let* ((buffer (process-buffer process))
+         (completion-status))
     (setq completion-status (bash-completion-send 
(bash-completion-generate-line comp) process))
     (when (eq 124 completion-status)
       ;; Special 'retry-completion' exit status, typically returned by
@@ -767,20 +706,14 @@ The result is a list of candidates, which might be empty."
       ;; just setup completion for the current command and is asking
       ;; us to retry once with the new configuration.
       (bash-completion-send "complete -p" process)
-      (process-put process 'complete-p (bash-completion-build-alist 
(process-buffer process)))
-      (bash-completion--customize comp 'nodefault)
+      (process-put process 'complete-p (bash-completion-build-alist buffer))
+      (bash-completion--customize comp process 'nodefault)
       (setq completion-status (bash-completion-send 
(bash-completion-generate-line comp) process)))
-    (setq options (bash-completion--options comp))
     (when (eq 0 completion-status)
-      (bash-completion-extract-candidates
-       (bash-completion--stub comp)
-       (bash-completion--unparsed-stub comp)
-       (bash-completion--open-quote comp)
-       options))))
+      (bash-completion-extract-candidates comp buffer))))
 
-(defun bash-completion-extract-candidates
-    (parsed-stub unparsed-stub open-quote options)
-  "Extract the completion candidates from the process buffer for PARSED-STUB.
+(defun bash-completion-extract-candidates (comp buffer)
+  "Extract the completion candidates for COMP form BUFFER.
 
 This command takes the content of the completion process buffer,
 splits it by newlines, post-process the candidates and returns
@@ -789,59 +722,43 @@ them as a list of strings.
 It should be invoked with the comint buffer as the current buffer
 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 ((candidates) (result (list)))
-    (setq candidates (delete-dups (with-current-buffer (bash-completion-buffer)
-                                    (split-string (buffer-string) "\n" t))))
+    (setq candidates (delete-dups
+                      (with-current-buffer buffer
+                        (split-string (buffer-string) "\n" t))))
     (if (eq 1 (length candidates))
-        (list (bash-completion-fix
-               (car candidates) parsed-stub unparsed-stub
-               open-quote options t))
+        (list (bash-completion-fix (car candidates) comp t))
       (dolist (completion candidates)
-        (push (bash-completion-fix
-               completion parsed-stub unparsed-stub open-quote options nil)
-              result))
+        (push (bash-completion-fix completion comp nil) result))
       (delete-dups (nreverse result)))))
 
-(defun bash-completion-fix
-    (str parsed-prefix unparsed-prefix open-quote options single)
-  "Fix completion candidate in STR if PREFIX is the current prefix.
+(defun bash-completion-fix (str comp single)
+  "Fix completion candidate in STR for COMP
 
-STR is the completion candidate to modify.
+STR is the completion candidate to modify, COMP the current
+completion operation.
 
-PARSED-PREFIX should be the current string being completed. If it
-is nil, the value of `bash-completion-prefix' is used. This
-allows calling this function from `mapcar'.
+If STR is the single candidate, SINGLE is t.
 
-PARSED-PREFIX is replaced with UNPARSED-PREFIX in set fixed set
-of candidates.
-
-OPEN-QUOTE should be the quote that's still open in prefix.  A
-character (' or \") or nil.  
-
-OPTIONS configrues some behaviors:
- 'nospace to not add a space after a single completion
-
-If SINGLE is non-nil, this is the single completion candidate.
-
-Return a modified version of the completion candidate.
+Return a modified version of STR.
 
 Modification include:
  - escaping of special characters in STR
- - prepending PREFIX if STR does not contain all of it, when
+ - prepending the stub if STR does not contain all of it, when
    completion was done after a wordbreak
  - adding / to recognized directory names
 
 It should be invoked with the comint buffer as the current buffer
 for directory name detection to work."
-  (let ((suffix "")
+  (let ((parsed-prefix (bash-completion--stub comp))
+        (unparsed-prefix (bash-completion--unparsed-stub comp))
+        (open-quote (bash-completion--open-quote comp))
+        (options (bash-completion--options comp))
+        (wordbreaks (bash-completion--wordbreaks comp))
+        (suffix "")
         (rest) ; the part between the prefix and the suffix
         (rebuilt))
 
@@ -866,7 +783,7 @@ for directory name detection to work."
      ;; 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))
+       (setq rebuilt (concat (bash-completion-before-last-wordbreak 
parsed-prefix wordbreaks) str))
        parsed-prefix)
       (setq rest (substring rebuilt (length parsed-prefix))))
 
@@ -883,7 +800,7 @@ for directory name detection to work."
        ((eq ?\  last-char)
         (setq rest (substring rest 0 -1))
         (setq suffix (concat close-quote-str final-space-str)))
-       ((or (memq last-char bash-completion-wordbreaks)
+       ((or (bash-completion--find-last last-char wordbreaks)
             (eq ?/ last-char))
         (setq suffix ""))
        ((file-accessible-directory-p
@@ -934,26 +851,15 @@ Return a possibly escaped version of 
COMPLETION-CANDIDATE."
       (replace-regexp-in-string "'\\\\''" "'" string)
     (replace-regexp-in-string "\\(\\\\\\)\\(.\\)" "\\2" string)))
 
-(defun bash-completion-before-last-wordbreak (str)
-  "Return the part of STR that comes after the last wordbreak character.
+(defun bash-completion-before-last-wordbreak (str wordbreaks)
+  "Return the part of STR that comes after the last WORDBREAKS character.
 The return value does not include the worbreak character itself.
 
-If no wordbreak was found, it returns STR.
-
-Wordbreaks characters are defined in 'bash-completion-wordbreak'."
-  (nth 0 (bash-completion-last-wordbreak-split str)))
+If no wordbreak was found, it returns STR."
+  (nth 0 (bash-completion-last-wordbreak-split str wordbreaks)))
 
-(defun bash-completion-after-last-wordbreak (str)
-  "Return the part of STR that comes before the last wordbreak character.
-The return value includes the worbreak character itself.
-
-If no wordbreak was found, it returns \"\".
-
-Wordbreaks characters are defined in 'bash-completion-wordbreak'."
-  (nth 1 (bash-completion-last-wordbreak-split str)))
-
-(defun bash-completion-last-wordbreak-split (str)
-  "Split STR at the last wordbreak character.
+(defun bash-completion-last-wordbreak-split (str wordbreaks)
+  "Split STR at the last WORDBREAKS character.
 
 The part before the last wordbreak character includes the
 wordbreak character itself.  It is \"\" if no wordbreak character
@@ -963,19 +869,15 @@ The part after the last wordbreak character does not 
include the
 wordbreak character.  It is STR if no wordbreak character was
 found.
 
-Wordbreaks characters are defined in 'bash-completion-wordbreak'.
-
 Return a CONS containing (before . after)."
   (catch 'bash-completion-return
-    (let ((end (- (length str) 1))
-          (breakc))
+    (let ((end (- (length str) 1)))
       (while (>= end 0)
-        (setq breakc (memq (aref str end) bash-completion-wordbreaks))
-       (when breakc
+        (when (bash-completion--find-last (aref str end) wordbreaks)
          (throw 'bash-completion-return
                  (list (substring str 0 (1+ end))
                        (substring str (1+ end))
-                       (car breakc))))
+                       (aref str end))))
        (setq end (1- end))))
       (list "" str ?\0)))
 
@@ -1075,6 +977,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 ${COMP_WORDBREAKS}" process)
+              (process-put process 'wordbreaks
+                           (with-current-buffer (process-buffer process)
+                             (buffer-substring-no-properties
+                              (point-min) (point-max))))
               (push (cons remote process) bash-completion-processes)
               (setq cleanup nil)
               process)
@@ -1129,14 +1036,16 @@ The returned alist is a sligthly parsed version of the 
output of
                     (push (cons command options) alist)))))))))
     alist))
 
-(defun bash-completion--customize (comp &optional nodefault)
-  (let ((compgen-args-alist
-         (process-get (bash-completion-require-process) 'complete-p))
-        (command-name (file-name-nondirectory
-                       (car (bash-completion--words comp)))))
-    (setf (bash-completion--compgen-args comp)
-          (or (cdr (assoc command-name compgen-args-alist))
-              (and (not nodefault) (cdr (assoc nil compgen-args-alist)))))))
+(defun bash-completion--customize (comp process &optional nodefault)
+  (unless (eq 'command (bash-completion--type comp))
+    (let ((compgen-args-alist
+           (process-get process 'complete-p))
+          (command-name (bash-completion--command comp)))
+      ;; TODO: first lookup the full command path, then only the
+      ;; command name.
+      (setf (bash-completion--compgen-args comp)
+            (or (cdr (assoc command-name compgen-args-alist))
+                (and (not nodefault) (cdr (assoc nil compgen-args-alist))))))))
 
 (defun bash-completion-generate-line (comp)
   "Generate a command-line that calls compgen for COMP.
@@ -1177,12 +1086,17 @@ completion candidates."
          (setcar (cdr function) "__bash_complete_wrapper")
          (format "__BASH_COMPLETE_WRAPPER=%s compgen %s -- %s"
                  (bash-completion-quote
-                  (format "COMP_LINE=%s; COMP_POINT=%s; COMP_CWORD=%s; 
COMP_WORDS=( %s ); %s \"${COMP_WORDS[@]}\""
+                  (format "COMP_LINE=%s; COMP_POINT=%s; COMP_CWORD=%s; 
COMP_WORDS=( %s ); %s %s %s %s"
                           (bash-completion-quote (bash-completion--line comp))
                           (bash-completion--point comp)
                           (bash-completion--cword comp)
                           (bash-completion-join (bash-completion--words comp))
-                          (bash-completion-quote function-name)))
+                          (bash-completion-quote function-name)
+                          (bash-completion-quote (bash-completion--command 
comp))
+                          (bash-completion-quote (bash-completion--stub comp))
+                          (bash-completion-quote (or (nth (1- 
(bash-completion--cword comp))
+                                                          
(bash-completion--words comp))
+                                                     ""))))
                  (bash-completion-join args)
                  quoted-stub)))
       ((eq 'custom completion-type)
diff --git a/test/bash-completion-integration-test.el 
b/test/bash-completion-integration-test.el
index 48e1713933..e3a753b46b 100644
--- a/test/bash-completion-integration-test.el
+++ b/test/bash-completion-integration-test.el
@@ -126,7 +126,10 @@ for testing completion."
                           :point 4
                           :words '("hel")
                           :cword 0
-                          :unparsed-stub "hel"))))))
+                          :stub "hel"
+                          :unparsed-stub "hel"
+                          :wordbreaks "@><=;|&(:")
+                        (bash-completion-require-process))))))
    (bash-completion-reset)
    (should-not (bash-completion-is-running))))
 
@@ -154,7 +157,7 @@ for testing completion."
                   (bash-completion_test-complete "somefunction du")))
    ;; function returns nothing, no -o default
    (should (equal "somefunction so"
-                  (bash-completion_test-complete "somefunction so")))
+                  (bash-completion_test-complete "somefunction so"))) ;
    ;; function returns nothing, -o default, so fallback to default 
    (should (equal "someotherfunction some/"
                   (bash-completion_test-complete "someotherfunction so")))
diff --git a/test/bash-completion-test.el b/test/bash-completion-test.el
index c11cb959a6..65e5617f88 100644
--- a/test/bash-completion-test.el
+++ b/test/bash-completion-test.el
@@ -103,7 +103,7 @@ The return value is the one returned by BODY."
                  "a \"hello world\" b c"
                  (bash-completion-strings-from-tokens
                   (bash-completion-tokenize 1 (line-end-position))))))
-  
+
   ;; escaped single quotes
   (should (equal '("a" "-'hello world'-" "b" "c")
                 (bash-completion-test-with-buffer
@@ -147,31 +147,21 @@ The return value is the one returned by BODY."
                   (bash-completion-tokenize 1 (line-end-position)))))))
 
 (ert-deftest bash-completion--parse-test ()
-  ;; cursor at end of word
-  (should (equal
-           (bash-completion--make
-            :line "a hello world"
-            :point 13
-            :cword 2
-            :words '("a" "hello" "world")
-            :stub-start 9
-             :unparsed-stub "world")
+  (let ((wordbreaks "@><=;|&(:"))
+    ;; cursor at end of word
+    (should (equal
+             (bash-completion--make
+              :line "a hello world"
+              :point 13
+              :cword 2
+              :words '("a" "hello" "world")
+              :stub-start 9
+              :stub "world"
+              :unparsed-stub "world"
+              :wordbreaks wordbreaks)
           (bash-completion-test-with-buffer
            "a hello world"
-           (bash-completion--parse (point-min) 14))))
-
-  ;; just one space, cursor after it
-  (should (equal
-           (bash-completion--make
-            :line ""
-            :point 0
-            :cword 0
-            :words '("")
-            :stub-start 2
-            :unparsed-stub "")
-           (bash-completion-test-with-buffer
-            " "
-            (bash-completion--parse (point-min) 2))))
+           (bash-completion--parse (point-min) 14 wordbreaks))))
 
   ;; some words separated by spaces, cursor after the last space
   (should (equal
@@ -181,10 +171,12 @@ The return value is the one returned by BODY."
             :cword 2
             :words '("a" "hello" "")
             :stub-start 9
-            :unparsed-stub "")
+            :stub ""
+            :unparsed-stub ""
+            :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "a hello "
-            (bash-completion--parse (point-min) 9))))
+            (bash-completion--parse (point-min) 9 wordbreaks))))
 
   ;; complex multi-command line
   (should (equal
@@ -194,10 +186,12 @@ The return value is the one returned by BODY."
             :cword 1
             :words '("make" "-")
             :stub-start 27
-            :unparsed-stub "-")
+            :stub "-"
+            :unparsed-stub "-"
+            :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "cd /var/tmp ; ZORG=t make -"
-            (bash-completion--parse (point-min) 28))))
+            (bash-completion--parse (point-min) 28 wordbreaks))))
 
   ;; pipe
   (should (equal
@@ -207,10 +201,12 @@ The return value is the one returned by BODY."
             :cword 1
             :words '("sort" "-")
             :stub-start 20
-            :unparsed-stub "-")
+            :stub "-"
+            :unparsed-stub "-"
+            :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "ls /var/tmp | sort -"
-            (bash-completion--parse (point-min) 21))))
+            (bash-completion--parse (point-min) 21 wordbreaks))))
 
   ;; escaped semicolon
   (should (equal
@@ -220,10 +216,12 @@ The return value is the one returned by BODY."
             :cword 7
             :words '("find" "-name" "*.txt" "-exec" "echo" "{}" ";" "-")
             :stub-start 38
-            :unparsed-stub "-")
+            :stub "-"
+            :unparsed-stub "-"
+            :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "find -name '*.txt' -exec echo {} ';' -"
-            (bash-completion--parse (point-min) 39))))
+            (bash-completion--parse (point-min) 39 wordbreaks))))
 
   ;; at var assignment
   (should (equal
@@ -232,11 +230,28 @@ The return value is the one returned by BODY."
             :point 6
             :cword 0
             :words '("ZORG=t")
-            :stub-start 19
-            :unparsed-stub "ZORG=t")
+            :stub-start 24
+            :stub "t"
+            :unparsed-stub "t"
+            :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "cd /var/tmp ; A=f ZORG=t"
-            (bash-completion--parse (point-min) 25))))
+            (bash-completion--parse (point-min) 25 wordbreaks))))
+
+  ;; stub is a subset of last word
+  (should (equal
+           (bash-completion--make
+            :line "export PATH=/bin:/usr/bi"
+            :point 24
+            :cword 1
+            :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))))
 
   ;; with escaped quote
   (should (equal
@@ -246,10 +261,12 @@ The return value is the one returned by BODY."
             :cword 1
             :words '("cd" "/vcr/shows/Dexter's")
             :stub-start 4
-            :unparsed-stub "/vcr/shows/Dexter\\'s")
+            :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))))
+            (bash-completion--parse (point-min) 24 wordbreaks))))
 
   ;; with double quote
   (should (equal
@@ -258,12 +275,14 @@ The return value is the one returned by BODY."
             :point 23
             :cword 1
             :words '("cd" "/vcr/shows/Dexter's")
-            :stub-start 5
-            :unparsed-stub "/vcr/shows/Dexter's"
-            :open-quote ?\")
+            :stub-start 4
+            :stub "/vcr/shows/Dexter's"
+            :unparsed-stub "\"/vcr/shows/Dexter's"
+            :open-quote ?\"
+            :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "cd \"/vcr/shows/Dexter's"
-            (bash-completion--parse (point-min) 24))))
+            (bash-completion--parse (point-min) 24 wordbreaks))))
 
   ;; with single quote
   (should (equal
@@ -272,12 +291,29 @@ The return value is the one returned by BODY."
             :point 26
             :cword 1
             :words '("cd" "/vcr/shows/Dexter's")
-            :stub-start 5
-            :unparsed-stub "/vcr/shows/Dexter'\\''s"
-            :open-quote ?')
+            :stub-start 4
+            :unparsed-stub "'/vcr/shows/Dexter'\\''s"
+            :stub "/vcr/shows/Dexter's"
+            :open-quote ?'
+            :wordbreaks wordbreaks)
            (bash-completion-test-with-buffer
             "cd '/vcr/shows/Dexter'\\''s"
-            (bash-completion--parse (point-min) 27)))))
+            (bash-completion--parse (point-min) 27 wordbreaks))))
+
+  ;; just one space, cursor after it
+  (should (equal
+           (bash-completion--make
+            :line ""
+            :point 0
+            :cword 0
+            :words '("")
+            :stub-start 2
+            :stub ""
+            :unparsed-stub ""
+            :wordbreaks wordbreaks)
+           (bash-completion-test-with-buffer
+            " "
+            (bash-completion--parse (point-min) 2 wordbreaks))))))
 
 (ert-deftest bash-completion-build-alist ()
   (should (equal
@@ -313,71 +349,85 @@ garbage
 
 (ert-deftest bash-completion-generate-line-test ()
   ;; no custom completion
-  (should
-   (equal (concat "cd >/dev/null 2>&1 " (expand-file-name "~/test")
-                  " ; compgen -o default -- worl 2>/dev/null")
-         (let ((bash-completion-alist nil)
-               (default-directory "~/test"))
-           (bash-completion-generate-line
+  (let ((default-directory "/test"))
+    (should
+     (equal (concat "cd >/dev/null 2>&1 /test"
+                    " ; compgen -o default -- worl 2>/dev/null")
+            (bash-completion-generate-line
              (bash-completion--make
               :line "hello worl"
               :point 7
               :words '("hello" "worl")
-              :cword 1)))))
+              :stub "worl"
+              :unparsed-stub "worl"
+              :cword 1))))
 
-  ;; custom completion no function or command
-  (should (equal
-           "cd >/dev/null 2>&1 /test ; compgen -A -G '*.txt' -- worl 
2>/dev/null"
-           (let ((default-directory "/test"))
+    ;; custom completion no function or command
+    (should (equal
+             "cd >/dev/null 2>&1 /test ; compgen -A -G '*.txt' -- worl 
2>/dev/null"
              (bash-completion-generate-line
               (bash-completion--make
                :line "zorg worl"
                :point 7
                :words '("zorg" "worl")
+               :stub "worl"
+               :unparsed-stub "worl"
                :cword 1
-               :compgen-args '("-A" "-G" "*.txt"))))))
-
-  ;; custom completion function
-  (should (equal
-           (concat
-            "cd >/dev/null 2>&1 /test ; "
-            "__BASH_COMPLETE_WRAPPER='COMP_LINE='\\''zorg worl'\\''; "
-            "COMP_POINT=7; COMP_CWORD=1; "
-            "COMP_WORDS=( zorg worl ); "
-            "__zorg \"${COMP_WORDS[@]}\"' "
-            "compgen -F __bash_complete_wrapper -- worl 2>/dev/null")
-           (let ((default-directory "/test"))
+               :compgen-args '("-A" "-G" "*.txt")))))
+
+    ;; custom completion function
+    (should (equal
+             (concat
+              "cd >/dev/null 2>&1 /test ; "
+              "__BASH_COMPLETE_WRAPPER='COMP_LINE='\\''zorg blah worl'\\''; "
+              "COMP_POINT=12; COMP_CWORD=2; "
+              "COMP_WORDS=( zorg blah worl ); "
+              "__zorg zorg worl blah' "
+              "compgen -F __bash_complete_wrapper -- worl 2>/dev/null")
+             (bash-completion-generate-line
+              (bash-completion--make
+               :line "zorg blah worl"
+               :point 12
+               :words '("zorg" "blah" "worl")
+               :cword 2
+               :stub "worl"
+               :unparsed-stub "worl"
+               :compgen-args '("-F" "__zorg")))))
+
+    ;; custom completion command
+    (should (equal
+             (concat
+              "cd >/dev/null 2>&1 /test ; "
+              "__BASH_COMPLETE_WRAPPER='COMP_LINE='\\''zorg worl'\\''; "
+              "COMP_POINT=7; "
+              "COMP_CWORD=1; "
+              "COMP_WORDS=( zorg worl ); "
+              "__zorg zorg worl zorg' "
+              "compgen -F __bash_complete_wrapper -- worl 2>/dev/null")
              (bash-completion-generate-line
               (bash-completion--make
                :line "zorg worl"
                :point 7
                :words '("zorg" "worl")
                :cword 1
-               :compgen-args '("-F" "__zorg"))))))
+               :stub "worl"
+               :unparsed-stub "worl"
+               :compgen-args '("-C" "__zorg")))))
 
-  ;; custom completion command
-  (should (equal
-           (concat
-            "cd >/dev/null 2>&1 /test ; "
-            "__BASH_COMPLETE_WRAPPER='COMP_LINE='\\''zorg worl'\\''; "
-            "COMP_POINT=7; "
-            "COMP_CWORD=1; "
-            "COMP_WORDS=( zorg worl ); "
-            "__zorg \"${COMP_WORDS[@]}\"' "
-            "compgen -F __bash_complete_wrapper -- worl 2>/dev/null")
-           (let ((default-directory "/test"))
+    ;; command completion
+    (should (equal
+             "cd >/dev/null 2>&1 /test ; compgen -b -c -a -A function -- worl 
2>/dev/null"
              (bash-completion-generate-line
               (bash-completion--make
-               :line "zorg worl"
+               :line "worl"
                :point 7
-               :words '("zorg" "worl")
-               :cword 1
-               :compgen-args '("-C" "__zorg")))))))
+               :words '("worl")
+               :cword 0
+               :stub "worl"
+               :unparsed-stub "worl"))))))
 
 (ert-deftest bash-completion-customize-test ()
-  (cl-letf (((symbol-function 'bash-completion-require-process)
-             (lambda () 'process))
-            ((symbol-function 'process-get)
+  (cl-letf (((symbol-function 'process-get)
              (lambda (process prop)
                (if (and (eq 'process process)
                         (eq 'complete-p prop))
@@ -386,14 +436,14 @@ garbage
                  (error "unexpected call")))))
     (let ((comp (bash-completion--make :cword 1)))
       (setf (bash-completion--words comp) '("zorg" "world"))
-      (bash-completion--customize comp)
+      (bash-completion--customize comp 'process)
       (should (equal '("-F" "__zorg") (bash-completion--compgen-args comp)))
 
       (setf (bash-completion--words comp) '("notzorg" "world"))
-      (bash-completion--customize comp)
+      (bash-completion--customize comp 'process)
       (should (equal '("-F" "__default") (bash-completion--compgen-args comp)))
 
-      (bash-completion--customize comp 'nodefault)
+      (bash-completion--customize comp 'process 'nodefault)
       (should (null (bash-completion--compgen-args comp))))))
 
 (ert-deftest bash-completion--find-last-test ()
@@ -449,17 +499,17 @@ Return (const return-value new-buffer-content)"
         (buffer-string))))))
 
 (ert-deftest bash-completion-send-test ()
-  (should (equal 
+  (should (equal
           (cons 0 "line1\nline2\n")
           (bash-completion-test-send "line1\nline2\n\t0\v")))
 
   ;; command failed"
-  (should (equal 
+  (should (equal
           (cons 1 "line1\nline2\n")
           (bash-completion-test-send "line1\nline2\n\t1\v")))
 
   ;; wrapped function returned 124"
-  (should (equal 
+  (should (equal
           (cons 124 "line1\nline2\n")
           (bash-completion-test-send
            (concat "line1\nli" bash-completion-wrapped-status "ne2\n\t0\v")))))
@@ -469,7 +519,7 @@ Return (const return-value new-buffer-content)"
   (should (equal ""
                 (let ((default-directory nil))
                   (bash-completion-cd-command-prefix))))
-  
+
   ;; current dir
   (should (equal "cd >/dev/null 2>&1 /tmp/x ; "
                 (let ((default-directory "/tmp/x"))
@@ -487,109 +537,194 @@ Return (const return-value new-buffer-content)"
   (should (equal nil (bash-completion-starts-with "hello world" "hullo ")))
   (should (equal t (bash-completion-starts-with "hello" ""))))
 
-(ert-deftest bash-completion-last-wordbreak-test ()
-  (should (equal '("a:b:c:d:" "e" ?:)
-                (bash-completion-last-wordbreak-split "a:b:c:d:e")))
-  (should (equal '("hello=" "world" ?=)
-                (bash-completion-last-wordbreak-split "hello=world")))
-  (should (equal '("hello>" "world" ?>)
-                (bash-completion-last-wordbreak-split "hello>world")))
-  (should (equal '(">" "world" ?>)
-                (bash-completion-last-wordbreak-split ">world")))
-  (should (equal '("" "hello" ?\0)
-                (bash-completion-last-wordbreak-split "hello"))))
+(ert-deftest bash-completion-last-wordbreak-split-test ()
+  (let ((wordbreaks "@><=;|&(:"))
+    (should (equal '("a:b:c:d:" "e" ?:)
+                   (bash-completion-last-wordbreak-split "a:b:c:d:e" 
wordbreaks)))
+    (should (equal '("hello=" "world" ?=)
+                   (bash-completion-last-wordbreak-split "hello=world" 
wordbreaks)))
+    (should (equal '("hello>" "world" ?>)
+                   (bash-completion-last-wordbreak-split "hello>world" 
wordbreaks)))
+    (should (equal '(">" "world" ?>)
+                   (bash-completion-last-wordbreak-split ">world" wordbreaks)))
+    (should (equal '("" "hello" ?\0)
+                   (bash-completion-last-wordbreak-split "hello" 
wordbreaks)))))
 
 (ert-deftest bash-completion-before-last-wordbreak-test ()
-  (should (equal "a:b:c:d:"
-                (bash-completion-before-last-wordbreak "a:b:c:d:e")))
-  (should (equal "hello="
-                (bash-completion-before-last-wordbreak "hello=world")))
-  (should (equal "hello>"
-                (bash-completion-before-last-wordbreak "hello>world")))
-  (should (equal "" (bash-completion-before-last-wordbreak "hello"))))
-
-(ert-deftest bash-completion-after-last-wordbreak-test ()
-  (should (equal "e"
-                (bash-completion-after-last-wordbreak "a:b:c:d:e")))
-  (should (equal "world"
-                (bash-completion-after-last-wordbreak "hello=world")))
-  (should (equal "world"
-                (bash-completion-after-last-wordbreak "hello>world")))
-  (should (equal "hello"
-                (bash-completion-after-last-wordbreak "hello"))))
+  (let ((wordbreaks "@><=;|&(:"))
+    (should (equal "a:b:c:d:"
+                   (bash-completion-before-last-wordbreak "a:b:c:d:e" 
wordbreaks)))
+    (should (equal "hello="
+                   (bash-completion-before-last-wordbreak "hello=world" 
wordbreaks)))
+    (should (equal "hello>"
+                   (bash-completion-before-last-wordbreak "hello>world" 
wordbreaks)))
+    (should (equal "" (bash-completion-before-last-wordbreak "hello" 
wordbreaks)))))
 
 (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" nil nil 
nil)))
+                (bash-completion-fix
+                  "a\\ bc d e"
+                  (bash-completion--make
+                   :cword 1
+                   :stub "a\\ b"
+                   :unparsed-stub "a\\ b"
+                   :wordbreaks "")
+                  nil)))
+                  
 
   ;; recover original escaping
   (should (equal "a' 'bc\\ d\\ e"
-                (bash-completion-fix "a\\ bc d e" "a\\ b" "a' 'b" nil nil 
nil)))
+                (bash-completion-fix
+                  "a\\ bc d e"
+                  (bash-completion--make
+                   :cword 1
+                   :stub "a\\ b"
+                   :unparsed-stub "a' 'b"
+                   :wordbreaks "")
+                  nil)))
 
   ;; do not escape final space
   (should (equal "ab "
-                  (bash-completion-fix "ab " "a" "a" nil nil nil)))
-  
+                 (bash-completion-fix
+                  "ab "
+                  (bash-completion--make
+                   :cword 1
+                   :stub "a"
+                   :unparsed-stub "a"
+                   :wordbreaks "")
+                  nil)))
+
   ;; remove final space with option nospace
   (should (equal "ab"
-                 (bash-completion-fix "ab " "a" "a" nil '(nospace) nil)))
+                 (bash-completion-fix
+                  "ab "
+                  (bash-completion--make
+                   :cword 1
+                   :stub "a"
+                   :unparsed-stub "a"
+                   :wordbreaks ""
+                   :compgen-args '("-o" "nospace"))
+                  nil)))
 
   ;; unexpand home and escape
   (should (equal "~/a/hello\\ world"
-                (bash-completion-fix (expand-file-name "~/a/hello world")
-                                     "~/a/he" "~/a/he" nil nil nil)))
+                (bash-completion-fix
+                  (expand-file-name "~/a/hello world")
+                  (bash-completion--make
+                   :cword 1
+                   :stub "~/a/he"
+                   :unparsed-stub "~/a/he"
+                   :wordbreaks "")
+                  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"
-                                      nil nil nil)))
+                (bash-completion-fix
+                  "hello world"
+                  (bash-completion--make
+                   :cword 1
+                   :stub "a:b:c:he"
+                   :unparsed-stub "a:b:c:he"
+                   :wordbreaks "@><=;|&(:")
+                  nil)))
 
   ;; just append
   (should (equal "hello\\ world"
-                (bash-completion-fix " world" "hello" "hello" nil nil nil)))
+                (bash-completion-fix
+                  " world"
+                  (bash-completion--make
+                   :cword 1
+                   :stub "hello"
+                   :unparsed-stub "hello"
+                   :wordbreaks "")
+                  nil)))
 
   ;; append / for home
   (should (equal "~/"
-                 (bash-completion-fix (expand-file-name "~")
-                                      "~" "~" nil '(filenames) nil)))
+                 (bash-completion-fix
+                  (expand-file-name "~")
+                  (bash-completion--make
+                   :cword 1
+                   :stub "~"
+                   :unparsed-stub "~"
+                   :wordbreaks "")
+                  nil)))
 
   (cl-letf (((symbol-function 'file-accessible-directory-p)
              (lambda (d) (equal d "/tmp/somedir"))))
     (let ((default-directory "/tmp/"))
       ;; append / for directory
       (should (equal "somedir/"
-                     (bash-completion-fix "somedir" "some" "some"
-                                          nil '(filenames) nil)))))
+                     (bash-completion-fix
+                      "somedir"
+                      (bash-completion--make
+                       :cword 1
+                       :stub "some"
+                       :unparsed-stub "some"
+                       :wordbreaks ""
+                       :compgen-args '(filenames))
+                      nil)))))
 
   ;; 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 nil 'single))))
+                   (bash-completion-fix
+                    "somecmd"
+                    (bash-completion--make
+                     :cword 0
+                     :stub "some"
+                     :unparsed-stub "some"
+                     :wordbreaks "")
+                    'single))))
 
   ;; ... but not if nospace option is set
   (should (let ((bash-completion-nospace t))
             (equal "somecmd"
-                   (bash-completion-fix "somecmd" "some" "some"
-                                        nil '(nospace) nil))))
+                   (bash-completion-fix
+                    "somecmd"
+                    (bash-completion--make
+                     :cword 0
+                     :stub "some"
+                     :unparsed-stub "some"
+                     :wordbreaks ""
+                     :compgen-args '("-o" "nospace"))
+                    nil))))
 
   ;; append a space for a single completion
   (should (let ((bash-completion-nospace nil))
             (equal "somecmd "
-                   (bash-completion-fix "somecmd" "some" "some"
-                                        nil nil 'single))))
+                   (bash-completion-fix
+                    "somecmd"
+                    (bash-completion--make
+                     :cword 0
+                     :stub "some"
+                     :unparsed-stub "some"
+                     :wordbreaks "")
+                    'single))))
 
   ;; but only for a single completion
   (should (let ((bash-completion-nospace nil))
             (equal "somecmd"
-                   (bash-completion-fix "somecmd" "some" "some"
-                                        nil nil nil))))
+                   (bash-completion-fix
+                    "somecmd"
+                    (bash-completion--make
+                     :cword 0
+                     :stub "some"
+                     :unparsed-stub "some"
+                     :wordbreaks "")
+                    nil))))
 
   ;; subset of the prefix"
   (should (equal "Dexter"
-                (bash-completion-fix "Dexter" "Dexter'" "Dexter'"
-                                      nil nil nil))))
+                (bash-completion-fix
+                  "Dexter"
+                  (bash-completion--make
+                   :cword 1
+                   :stub "Dexter'"
+                   :unparsed-stub "Dexter'"
+                   :wordbreaks "")
+                  nil))))
 
 (ert-deftest bash-completion-extract-candidates-test ()
   (let ((bash-completion-nospace nil))
@@ -598,17 +733,23 @@ Return (const return-value new-buffer-content)"
       '("hello\\ world" "hello ")
       (bash-completion-test-with-buffer
        "hello world\nhello \n\n"
-       (cl-letf (((symbol-function 'bash-completion-buffer)
-                  (lambda () (current-buffer))))
-         (bash-completion-extract-candidates "hello" "hello" nil nil)))))
+       (bash-completion-extract-candidates
+        (bash-completion--make :stub "hello"
+                               :unparsed-stub "hello"
+                               :wordbreaks ""
+                               :cword 1)
+        (current-buffer)))))
     (should
      (equal
       '("hello" "hellish" "hellow")
       (bash-completion-test-with-buffer
        "hello\nhellish\nhello\nhellow\n"
-       (cl-letf (((symbol-function 'bash-completion-buffer)
-                  (lambda () (current-buffer))))
-         (bash-completion-extract-candidates "hell" "hell" nil nil)))))))
+       (bash-completion-extract-candidates
+        (bash-completion--make :stub "hell"
+                               :unparsed-stub "hell"
+                               :wordbreaks ""
+                               :cword 1)
+        (current-buffer)))))))
 
 (ert-deftest bash-completion-nonsep-test ()
   (should (equal "^ \t\n\r;&|'\"#"
@@ -658,7 +799,7 @@ Return (const return-value new-buffer-content)"
   ;; double quote: specials
   (should (equal "Specials in double quotes: \\$\\`\\\""
                 (bash-completion-escape-candidate "Specials in double quotes: 
$`\"" ?\")))
-  
+
   ;; double quote: escaped specials
   (should (equal "Slash-prefixed specials in double quotes: 
\\\\\\$\\\\\\`\\\\\\\""
                 (bash-completion-escape-candidate "Slash-prefixed specials in 
double quotes: \\$\\`\\\"" ?\")))
@@ -708,6 +849,7 @@ before calling `bash-completion-dynamic-complete-nocomint'.
 "
   `(let ((default-directory "/tmp/test")
          (bash-completion-alist '())
+         (wordbreaks "@><=;|&(:")
          (bash-completion-enable-caching nil))
      (lexical-let ((--process-buffer)
                    (--test-buffer)
@@ -723,12 +865,15 @@ before calling 
`bash-completion-dynamic-complete-nocomint'.
                       (lambda (process prop value)
                         (if (and (eq 'process process) (eq 'complete-p prop))
                             (setq bash-completion-alist value)
-                          (error))))
+                          (error "unexpected call"))))
                      ((symbol-function 'process-get)
                       (lambda (process prop)
-                        (if (and (eq 'process process) (eq 'complete-p prop))
-                            bash-completion-alist
-                          (error))))
+                        (cond
+                         ((and (eq 'process process) (eq 'complete-p prop))
+                          bash-completion-alist)
+                         ((and (eq 'process process) (eq 'wordbreaks prop))
+                          wordbreaks)
+                         (t (error "unexpected call")))))
                      ((symbol-function 'bash-completion-buffer) (lambda () 
--process-buffer))
                      ((symbol-function 'process-buffer) (lambda (p) 
--process-buffer))
                      ((symbol-function 'file-accessible-directory-p)
@@ -766,7 +911,7 @@ before calling `bash-completion-dynamic-complete-nocomint'.
    (push "hello\n" --send-results)
    (insert "$ cat \"he")
    (should (equal
-            '("hello\" ")
+            '("\"hello\" ")
             (nth 2 (bash-completion-dynamic-complete-nocomint 3 (point)))))))
 
 (ert-deftest bash-completion-single-completion-single-quotes ()
@@ -774,7 +919,7 @@ before calling `bash-completion-dynamic-complete-nocomint'.
    (push "hello\n" --send-results)
    (insert "$ cat 'he")
    (should (equal
-            '("hello' ")
+            '("'hello' ")
             (nth 2 (bash-completion-dynamic-complete-nocomint 3 (point)))))))
 
 (ert-deftest bash-completion-completion-with-double-quotes ()
@@ -782,7 +927,7 @@ before calling `bash-completion-dynamic-complete-nocomint'.
    (push "hell\nhello\n" --send-results)
    (insert "$ cat \"he")
    (should (equal
-            '("hell\"" "hello\"")
+            '("\"hell\"" "\"hello\"")
             (nth 2 (bash-completion-dynamic-complete-nocomint 3 (point)))))))
 
 (ert-deftest bash-completion-trailing-default-completion ()
@@ -869,19 +1014,19 @@ before calling 
`bash-completion-dynamic-complete-nocomint'.
    (push "Documents\n" --send-results)
    (insert "$ cat 'Doc")
    (should (equal
-            '(8 11 ("Documents/"))
+            '(7 11 ("'Documents/"))
             (bash-completion-dynamic-complete-nocomint 3 (point))))
    (insert "uments/")
    (push "Documents/Modes d'emplois\n" --send-results)
    (should (equal
-            '("Documents/Modes d'\\''emplois/")
+            '("'Documents/Modes d'\\''emplois/")
             (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)
    (should (equal
-            '("Documents/Modes d'\\''emplois/KAR 1.pdf'"
-              "Documents/Modes d'\\''emplois/KAR 2.pdf'")
+            '("'Documents/Modes d'\\''emplois/KAR 1.pdf'"
+              "'Documents/Modes d'\\''emplois/KAR 2.pdf'")
             (nth 2 (bash-completion-dynamic-complete-nocomint 3 (point)))))))
 
 (ert-deftest bash-completion-complete-command-with-dir ()
@@ -917,7 +1062,7 @@ before calling `bash-completion-dynamic-complete-nocomint'.
 (ert-deftest bash-completion-wordbreak-completion ()
   (--with-fake-bash-completion-send
    (push "/tmp/test/bin" --directories)
-   (setq --send-results '("" "./binary\n./bind\n./bin\n"))
+   (setq --send-results '("./binary\n./bind\n./bin\n"))
    (insert "$ export PATH=$PATH:./b")
    (should
     (equal '(21 24 ("./binary" "./bind" "./bin/"))
@@ -926,7 +1071,7 @@ before calling `bash-completion-dynamic-complete-nocomint'.
 (ert-deftest bash-completion-single-wordbreak-completion ()
   (--with-fake-bash-completion-send
    (push "/tmp/test/bin" --directories)
-   (setq --send-results '("" "./world\n"))
+   (setq --send-results '("./world\n"))
    (insert "$ set a=./hello:./w")
    (should
     (equal '(17 20 ("./world "))



reply via email to

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