Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Better *shell* integration

 - bash completion
 - tab completion with shell-command
 - kill dead shell with C-d
 - open shell with C-z (who need suspend?)
 - zap-to-char is back to M-z
    - repeat command is back to C-x z
  • Loading branch information...
commit 4fed54ca0d4dc759ea31ff9f78d44ae3b9a14a5c 1 parent 956205a
@magnars authored
View
3  .gitmodules
@@ -52,3 +52,6 @@
[submodule "site-lisp/vimgolf"]
path = site-lisp/vimgolf
url = https://github.com/timvisher/vimgolf.git
+[submodule "site-lisp/bash-completion"]
+ path = site-lisp/bash-completion
+ url = https://github.com/szermatt/emacs-bash-completion.git
View
1  init.el
@@ -39,6 +39,7 @@
(require 'setup-hippie)
(require 'setup-ace-jump-mode)
(require 'setup-perspective)
+(require 'setup-shell)
;(require 'setup-autopair) -- could this be the culprit in delete-selection-mode failures?
;; Map files to modes
View
7 key-bindings.el
@@ -20,9 +20,6 @@
(define-key global-map (kbd "C-c SPC") 'ace-jump-mode)
(define-key global-map (kbd "C-ø") 'ace-jump-mode)
-;; Repeat last command - too cumbersome with C-x z
-(global-set-key (kbd "M-z") 'repeat)
-
;; Perform general cleanup.
(global-set-key (kbd "C-c n") 'cleanup-buffer)
(global-set-key (kbd "C-c C-<return>") 'delete-blank-lines)
@@ -48,8 +45,8 @@
(global-set-key (kbd "M-w") 'save-region-or-current-line)
(global-set-key (kbd "M-W") '(lambda () (interactive) (save-region-or-current-line 1)))
-;; Make zap-to-char more convenient, and suspend-frame less
-(global-set-key (kbd "C-z") 'zap-to-char)
+;; Make shell more convenient, and suspend-frame less
+(global-set-key (kbd "C-z") 'shell)
(global-set-key (kbd "C-x C-z") 'suspend-frame)
;; iy-go-to-char - like f in Vim
View
33 setup-shell.el
@@ -0,0 +1,33 @@
+;; Setup shell
+
+;; Note: Emacs runs .bashrc in *shell*
+;; So mac users should use that instead of .profile
+
+;; bash-completion
+
+(autoload 'bash-completion-dynamic-complete
+ "bash-completion"
+ "BASH completion hook")
+(add-hook 'shell-dynamic-complete-functions
+ 'bash-completion-dynamic-complete)
+(add-hook 'shell-command-complete-functions
+ 'bash-completion-dynamic-complete)
+
+;; tab-completion for shell-command
+
+(require 'shell-command)
+(shell-command-completion-mode)
+
+;; C-d to kill buffer if process is dead.
+
+(defun comint-delchar-or-eof-or-kill-buffer (arg)
+ (interactive "p")
+ (if (null (get-buffer-process (current-buffer)))
+ (kill-buffer)
+ (comint-delchar-or-maybe-eof arg)))
+
+(add-hook 'shell-mode-hook
+ (lambda ()
+ (define-key shell-mode-map (kbd "C-d") 'comint-delchar-or-eof-or-kill-buffer)))
+
+(provide 'setup-shell)
View
1  site-lisp/bash-completion/.gitignore
@@ -0,0 +1 @@
+*.elc
View
117 site-lisp/bash-completion/README.md
@@ -0,0 +1,117 @@
+bash-completion.el defines dynamic completion hooks for shell-mode and
+shell-command prompts that are based on bash completion.
+
+You will need shell-command.el to get tab completion in the
+minibuffer. See [http://www.namazu.org/~tsuchiya/elisp/shell-command.el](http://www.namazu.org/~tsuchiya/elisp/shell-command.el)
+
+Bash completion for emacs:
+
+- is aware of bash builtins, aliases and functions
+- does file expansion inside of colon-separated variables
+ and after redirections (> or <)
+- escapes special characters when expanding file names
+- is configurable through programmable bash completion
+
+When the first completion is requested in shell model or a shell
+command, bash-completion.el starts a separate bash
+process. Bash-completion.el then uses this process to do the actual
+completion and includes it into Emacs completion suggestions.
+
+A simpler and more complete alternative to bash-completion.el is to
+run a bash shell in a buffer in term mode(M-x `ansi-term').
+Unfortunately, many Emacs editing features are not available when
+running in term mode. Also, term mode is not available in
+shell-command prompts.
+
+## INSTALLATION
+
+1. copy bash-completion.el into a directory that's on Emacs load-path
+2. add this into your .emacs file:
+
+ (autoload 'bash-completion-dynamic-complete
+ "bash-completion"
+ "BASH completion hook")
+ (add-hook 'shell-dynamic-complete-functions
+ 'bash-completion-dynamic-complete)
+ (add-hook 'shell-command-complete-functions
+ 'bash-completion-dynamic-complete)
+
+ or simpler, but forces you to load this file at startup:
+
+ (require 'bash-completion)
+ (bash-completion-setup)
+
+3. reload your .emacs (M-x `eval-buffer') or restart
+
+Once this is done, use <TAB> as usual to do dynamic completion from
+shell mode or a shell command minibuffer, such as the one started
+for M-x `compile'. Note that the first completion is slow, as emacs
+launches a new bash process.
+
+You'll get better results if you turn on programmable bash completion.
+On Ubuntu, this means running:
+
+ sudo apt-get install bash-completion
+
+and then adding this to your .bashrc:
+
+ . /etc/bash_completion
+
+Right after enabling programmable bash completion, and whenever you
+make changes to you .bashrc, call `bash-completion-reset' to make
+sure bash completion takes your new settings into account.
+
+Loading /etc/bash_completion often takes time, and is not necessary
+in shell mode, since completion is done by a separate process, not
+the process shell-mode process.
+
+To turn off bash completion when running from emacs but keep it on
+for processes started by bash-completion.el, add this to your .bashrc:
+
+ if [[ ( -z "$INSIDE_EMACS" || "$EMACS_BASH_COMPLETE" = "t" ) &&\
+ -f /etc/bash_completion ]]; then
+ . /etc/bash_completion
+ fi
+
+Emacs sets the environment variable INSIDE_EMACS to the processes
+started from it. Processes started by bash-completion.el have
+the environment variable EMACS_BASH_COMPLETE set to t.
+
+## CAVEATS
+
+Using a separate process for doing the completion has several
+important disadvantages:
+
+- bash completion is slower than standard emacs completion
+- the first completion can take a long time, since a new bash process
+ needs to be started and initialized
+- the separate process is not aware of any changes made to bash
+ in the current buffer.
+ In a standard terminal, you could do:
+
+ $ alias myalias=ls
+ $ myal<TAB>
+
+ and bash would propose the new alias.
+ Bash-completion.el cannot do that, as it is not aware of anything
+ configured in the current shell. To make bash-completion.el aware
+ of a new alias, you need to add it to .bashrc and restart the
+ completion process using `bash-completion-reset'.
+
+## COMPATIBILITY
+
+bash-completion.el is quite sensitive to the OS and BASH version.
+This package is known to work on the following environment:
+
+- GNU Emacs 22.3.1 (Aquamacs 1.7)
+- GNU Emacs 22.1.1 (OSX 10.5)
+- GNU Emacs 22.1.1 (Ubuntu 8.04)
+- GNU Emacs 23.0.94.1 (Ubuntu 8.10)
+
+and using the following bash versions:
+
+- BASH 3.2.17
+- BASH 3.2.32
+- BASH 3.2.39
+
+bash-completion.el does not works on XEmacs.
View
1,046 site-lisp/bash-completion/bash-completion.el
@@ -0,0 +1,1046 @@
+;;; bash-completion.el --- BASH completion for the shell buffer
+
+;; Copyright (C) 2009 Stephane Zermatten
+
+;; Author: Stephane Zermatten <szermatt@gmx.net>
+
+;; This program is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; either version 2 of the
+;; License, or (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see
+;; `http://www.gnu.org/licenses/'.
+
+;;; Commentary:
+;;
+;; This file defines dynamic completion hooks for shell-mode and
+;; shell-command prompts that are based on bash completion.
+;;
+;; You will need shell-command.el to get tab completion in the
+;; minibuffer. See http://www.namazu.org/~tsuchiya/elisp/shell-command.el
+;;
+;; Bash completion for emacs:
+;; - is aware of bash builtins, aliases and functions
+;; - does file expansion inside of colon-separated variables
+;; and after redirections (> or <)
+;; - escapes special characters when expanding file names
+;; - is configurable through programmable bash completion
+;;
+;; When the first completion is requested in shell model or a shell
+;; command, bash-completion.el starts a separate bash
+;; process. Bash-completion.el then uses this process to do the actual
+;; completion and includes it into Emacs completion suggestions.
+;;
+;; A simpler and more complete alternative to bash-completion.el is to
+;; run a bash shell in a buffer in term mode(M-x `ansi-term').
+;; Unfortunately, many Emacs editing features are not available when
+;; running in term mode. Also, term mode is not available in
+;; shell-command prompts.
+;;
+;; INSTALLATION
+;;
+;; 1. copy bash-completion.el into a directory that's on Emacs load-path
+;; 2. add this into your .emacs file:
+;; (autoload 'bash-completion-dynamic-complete \"bash-completion\"
+;; \"BASH completion hook\")
+;; (add-hook 'shell-dynamic-complete-functions
+;; 'bash-completion-dynamic-complete)
+;; (add-hook 'shell-command-complete-functions
+;; 'bash-completion-dynamic-complete)
+;;
+;; or simpler, but forces you to load this file at startup:
+;;
+;; (require 'bash-completion)
+;; (bash-completion-setup)
+;;
+;; 3. reload your .emacs (M-x `eval-buffer') or restart
+;;
+;; Once this is done, use <TAB> as usual to do dynamic completion from
+;; shell mode or a shell command minibuffer, such as the one started
+;; for M-x `compile'. Note that the first completion is slow, as emacs
+;; launches a new bash process.
+;;
+;; You'll get better results if you turn on programmable bash completion.
+;; On Ubuntu, this means running:
+;; sudo apt-get install bash-completion
+;; and then adding this to your .bashrc:
+;; . /etc/bash_completion
+;;
+;; Right after enabling programmable bash completion, and whenever you
+;; make changes to you .bashrc, call `bash-completion-reset' to make
+;; sure bash completion takes your new settings into account.
+;;
+;; Loading /etc/bash_completion often takes time, and is not necessary
+;; in shell mode, since completion is done by a separate process, not
+;; the process shell-mode process.
+;;
+;; To turn off bash completion when running from emacs but keep it on
+;; for processes started by bash-completion.el, add this to your .bashrc:
+;; if [[ ( -z "$INSIDE_EMACS" || "$EMACS_BASH_COMPLETE" = "t" ) &&\
+;; -f /etc/bash_completion ]]; then
+;; . /etc/bash_completion
+;; fi
+;;
+;; Emacs sets the environment variable INSIDE_EMACS to the processes
+;; started from it. Processes started by bash-completion.el have
+;; the environment variable EMACS_BASH_COMPLETE set to t.
+;;
+;; CAVEATS
+;;
+;; Using a separate process for doing the completion has several
+;; important disadvantages:
+;; - bash completion is slower than standard emacs completion
+;; - the first completion can take a long time, since a new bash process
+;; needs to be started and initialized
+;; - the separate process is not aware of any changes made to bash
+;; in the current buffer.
+;; In a standard terminal, you could do:
+;; $ alias myalias=ls
+;; $ myal<TAB>
+;; and bash would propose the new alias.
+;; Bash-completion.el cannot do that, as it is not aware of anything
+;; configured in the current shell. To make bash-completion.el aware
+;; of a new alias, you need to add it to .bashrc and restart the
+;; completion process using `bash-completion-reset'.
+;;
+;; COMPATIBILITY
+;;
+;; bash-completion.el is quite sensitive to the OS and BASH version.
+;; This package is known to work on the following environment:
+;; GNU Emacs 22.3.1 (Aquamacs 1.7)
+;; GNU Emacs 22.1.1 (OSX 10.5)
+;; GNU Emacs 22.1.1 (Ubuntu 8.04)
+;; GNU Emacs 23.0.94.1 (Ubuntu 8.10)
+;;
+;; and using the following bash versions:
+;; BASH 3.2.17
+;; BASH 3.2.32
+;; BASH 3.2.39
+;;
+;; bash-completion.el does not works on XEmacs.
+;;
+
+;;; History:
+;;
+;; 2009-11-25 Stephane Zermatten <szermatt@gmail.com>
+;;
+;; * bash-completion-require-process: set MAILCHECK to -1
+;; to disable mail check message.
+;;
+;; 2009-08-01 Stephane Zermatten <szermatt@gmail.com>
+;;
+;; * bash-completion-generate-line: add missing compgen
+;; option to complete commands (duh!).
+;;
+;; Current version:
+;; $Id$
+;;
+
+(require 'comint)
+
+;;; Code:
+
+;;; ---------- Customization
+(defgroup bash-completion nil
+ "BASH configurable command-line completion "
+ :group 'shell
+ :group 'shell-command)
+
+(defcustom bash-completion-enabled t
+ "Enable/Disable BASH configurable command-line completion globally.
+
+This flag is useful for temporarily disabling bash completion
+once it's been installed.
+
+Setting this variable to t is NOT enough to enable BASH completion.
+BASH completion is only available in the environment for which
+`bash-completion-dynamic-complete' has been registered. See
+`bash-completion-setup' for that."
+ :type '(boolean)
+ :group 'bash-completion)
+
+(defcustom bash-completion-prog "/bin/bash"
+ "Name or path of the BASH executable to run for command-line completion.
+This should be either an absolute path to the BASH executable or
+the name of the bash command if it is on Emacs' PATH. This
+should point to a recent version of BASH (BASH 3) with support
+for command-line completion."
+ :type '(file :must-match t)
+ :group 'bash-completion)
+
+(defcustom bash-completion-process-timeout 2.5
+ "Timeout value to apply when waiting from an answer from bash.
+If bash takes longer than that to answer, the answer will be
+ignored."
+ :type '(float)
+ :group 'bash-completion)
+
+(defcustom bash-completion-initial-timeout 30
+ "Timeout value to apply when talking to bash for the first time.
+The first thing bash is supposed to do is process /etc/bash_complete,
+which typically takes a long time."
+ :type '(float)
+ :group 'bash-completion)
+
+(defcustom bash-completion-nospace nil
+ "Never let bash add a final space at the end of a completion.
+
+When there is only one completion candidate, bash sometimes adds
+a space at the end of the completion to move the cursor at the
+appropriate position to add more command-line arguments. This
+feature doesn't always work perfectly with programmable completion.
+
+Enable this option if you find yourself having to often backtrack
+to remove the extra space bash adds after a completion."
+ :type '(boolean)
+ :group 'bash-completion)
+
+;;; ---------- Internal variables and constants
+
+(defvar bash-completion-process nil
+ "Bash process object.")
+(defvar bash-completion-alist nil
+ "Maps from command name to the 'complete' arguments.
+
+For example if the following completion is defined in bash:
+ complete -F _cdargs_aliases cdb
+the following entry is added to `bash-completion-alist':
+ (\"cdb\" . (\"-F\" \"_cdargs\"))
+
+See `bash-completion-add-to-alist'.")
+
+(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.")
+
+;;; ---------- Functions: completion
+
+;;;###autoload
+(defun bash-completion-setup ()
+ "Register bash completion for the shell buffer and shell command line.
+
+This function adds `bash-completion-dynamic-complete' to the completion
+function list of shell mode, `shell-dynamic-complete-functions' and to the
+completion function list of shell-command, `shell-command-complete-functions'.
+
+This function is convenient, but it might not be the best way of enabling
+bash completion in your .emacs file because it forces you to load the module
+before it is needed. For an autoload version, add:
+
+ (autoload 'bash-completion-dynamic-complete \"bash-completion\"
+ \"BASH completion hook\")
+ (add-hook 'shell-dynamic-complete-functions
+ 'bash-completion-dynamic-complete)
+ (add-hook 'shell-command-complete-functions
+ 'bash-completion-dynamic-complete))
+"
+ (add-hook 'shell-dynamic-complete-functions
+ 'bash-completion-dynamic-complete)
+ (add-hook 'shell-command-complete-functions
+ 'bash-completion-dynamic-complete))
+
+;;;###autoload
+(defun bash-completion-dynamic-complete ()
+ "Complete word at cursor using BASH completion.
+
+This function is meant to be added into
+`shell-dynamic-complete-functions' or
+`shell-command-complete-functions'. It uses `comint' to figure
+out what the current command is and calls
+`comint-dynamic-simple-complete' to do the completion.
+
+If a match was found, it is displayed as is usual for comint
+completion. Return nil if no match was found."
+ (when bash-completion-enabled
+ (when (not (window-minibuffer-p))
+ (message "Bash completion..."))
+ (let* ( (start (comint-line-beginning-position))
+ (pos (point))
+ (tokens (bash-completion-tokenize start pos))
+ (open-quote (bash-completion-tokenize-open-quote tokens))
+ (parsed (bash-completion-process-tokens tokens pos))
+ (line (cdr (assq 'line parsed)))
+ (point (cdr (assq 'point parsed)))
+ (cword (cdr (assq 'cword parsed)))
+ (words (cdr (assq 'words parsed)))
+ (stub (nth cword words))
+ (completions (bash-completion-comm line point words cword open-quote))
+ ;; Override configuration for comint-dynamic-simple-complete.
+ ;; Bash adds a space suffix automatically.
+ (comint-completion-addsuffix nil) )
+ (if completions
+ (comint-dynamic-simple-complete stub completions)
+ ;; no standard completion
+ ;; try default (file) completion after a wordbreak
+ (bash-completion-dynamic-try-wordbreak-complete stub open-quote)))))
+
+(defun bash-completion-dynamic-try-wordbreak-complete (stub open-quote)
+ "Try wordbreak completion on STUB if the complete completion failed.
+
+Split STUB using the wordbreak list and apply compgen default
+completion on the last part. Return non-nil if a match was found.
+
+If 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 stub))
+ (before-wordbreak (car wordbreak-split))
+ (after-wordbreak (cdr wordbreak-split)))
+ (when (car wordbreak-split)
+ (bash-completion-send (concat
+ (bash-completion-cd-command-prefix)
+ "compgen -o default -- "
+ (bash-completion-quote after-wordbreak)))
+ (comint-dynamic-simple-complete
+ after-wordbreak
+ (bash-completion-extract-candidates after-wordbreak open-quote)))))
+
+;;; ---------- Functions: parsing and tokenizing
+
+(defun bash-completion-join (words)
+ "Join WORDS into a shell command line.
+
+All words that contain even mildly suspicious characters are
+quoted using single quotes to avoid the shell interpreting them
+when it shouldn't.
+
+Return one string containing WORDS."
+ (if words
+ (mapconcat
+ 'bash-completion-quote
+ words " ")
+ ""))
+
+(defun bash-completion-quote (word)
+ "Put single quotes around WORD unless it's crearly unnecessary.
+
+If WORD contains characters that aren't known to be harmless, this
+functions adds single quotes around it and return the result."
+ (if (string-match "^[a-zA-Z0-9_./-]*$" word)
+ word
+ (concat "'"
+ (replace-regexp-in-string "'" "'\\''" word :literal t)
+ "'")))
+
+(defun bash-completion-parse-line (start pos)
+ "Split a command line in the current buffer between START and POS.
+
+This function combines `bash-completion-tokenize' and
+`bash-completion-process-tokens'. It takes the same arguments as
+`bash-completion-tokenize' and returns the same value as
+`bash-completion-process-tokens'."
+ (bash-completion-process-tokens
+ (bash-completion-tokenize start pos) pos))
+
+(defun bash-completion-process-tokens (tokens pos)
+ "Process a command line split into TOKENS that end at POS.
+
+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 - 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)"
+ (bash-completion-parse-line-postprocess
+ (bash-completion-parse-current-command tokens) pos))
+
+(defun bash-completion-parse-line-postprocess (tokens pos)
+ "Extract from TOKENS the data needed by compgen functions.
+
+This function takes a list of TOKENS created by `bash-completion-tokenize'
+for the current buffer and generate the data needed by compgen functions
+as returned by `bash-completion-parse-line' given the cursor position POS."
+ (let* ((first-token (car tokens))
+ (last-token (car (last tokens)))
+ (start (or (car (bash-completion-tokenize-get-range first-token)) pos))
+ (end (or (cdr (bash-completion-tokenize-get-range last-token)) pos))
+ (words (bash-completion-strings-from-tokens tokens)))
+ (when (or (> pos end) (= start end))
+ (setq words (append words '(""))))
+ (list
+ (cons 'line (buffer-substring-no-properties start pos))
+ (cons 'point (- pos start))
+ (cons 'cword (- (length words) 1))
+ (cons 'words words))))
+
+(defun bash-completion-parse-current-command (tokens)
+ "Extract from TOKENS the tokens forming the current command.
+
+This function takes a list of TOKENS created by
+`bash-completion-tokenize' for the current buffer and select the
+tokens on this list that form the current command given that the
+word to be completed is the last token.
+
+For example, given this stream of tokens:
+ cd /var/tmp && ls -l
+if the last token is -l, it will select:
+ ls -l
+if the last token is /var/tmp, it will select:
+ cd /var/tmp
+
+Return a sublist of TOKENS."
+ (nreverse
+ (catch 'bash-completion-return
+ (let ((command nil) (state 'initial))
+ (dolist (token tokens)
+ (let* (
+ (string (bash-completion-tokenize-get-str token))
+ (is-terminal
+ (and (member string '(";" "&" "|" "&&" "||"))
+ (let ((range (bash-completion-tokenize-get-range token)))
+ (= (- (cdr range) (car range))
+ (length string))))))
+ (cond
+ (is-terminal
+ (setq state 'initial)
+ (setq command nil))
+
+ ((and (eq state 'initial)
+ (null (string-match "=" string)))
+ (setq state 'args)
+ (push token command))
+
+ ((eq state 'args)
+ (push token command)))))
+ (or command (last tokens))))))
+
+(defun bash-completion-strings-from-tokens (tokens)
+ "Extract the strings from TOKENS.
+
+This function takes all strings from TOKENS and retrun it as a
+list of strings.
+
+TOKENS should be in the format returned by `bash-completion-tokenize'."
+ (mapcar 'bash-completion-tokenize-get-str tokens))
+
+(defsubst bash-completion-tokenize-get-range (token)
+ "Return the TOKEN range as a cons: (start . end)."
+ (cdr (assq 'range token)))
+
+(defsubst bash-completion-tokenize-set-end (token)
+ "Set the end position of TOKEN to the cursor position."
+ (setcdr (bash-completion-tokenize-get-range token) (point)))
+
+(defsubst bash-completion-tokenize-append-str (token str)
+ "Append to TOKEN the string STR."
+ (let ((str-cons (assq 'str token)))
+ (setcdr str-cons (concat (cdr str-cons) str))))
+
+(defsubst bash-completion-tokenize-get-str (token)
+ "Return the TOKEN string."
+ (cdr (assq 'str token)))
+
+(defsubst bash-completion-tokenize-open-quote (tokens)
+ "Return the quote character that was still open in the last token.
+
+TOKENS is a list of token as returned by
+`bash-completion-tokenize'."
+ (cdr (assq 'quote (car (last tokens)))))
+
+(defun bash-completion-tokenize (start end)
+ "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 &&.
+
+This method returns a list of tokens found between START and END,
+ordered by position. Tokens contain a string and a range.
+
+The string in a token is an unescaped version of the token. For
+example, if the token is 'hello world', the string contains
+\"hello world\", without the quotes. It can be accessed using
+`bash-completion-tokenize-get-str'. It can be modified using
+`bash-completion-tokenize-append-str'.
+
+The range is a cons containing the start and end position of the
+token (start . end). Start is the position of the first character
+that belongs to the token. End is the position of the first
+character that doesn't belong to the token. For example in the
+string \" hello world \", the first token range is (2 . 7) and
+the second token range (9 . 14). It can be accessed using
+`bash-completion-tokenize-get-range'. The end position can be
+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))))
+
+(defun bash-completion-tokenize-new-element (end tokens)
+ "Tokenize the rest of the line until END and complete TOKENS.
+
+This function is meant to be called exclusively from
+`bash-completion-tokenize' and `bash-completion-tokenize-0'.
+
+This function expect the point to be at the start of a new
+element to be added to the list of tokens.
+
+Return TOKENS with new tokens found betwen the current point and
+END prepended to it."
+ (skip-chars-forward " \t\n\r" end)
+ (if (< (point) end)
+ (bash-completion-tokenize-0 end tokens
+ (list
+ (cons 'str "")
+ (cons 'range (cons (point) nil))))
+ tokens))
+
+(defun bash-completion-tokenize-0 (end tokens token)
+ "Tokenize the rest of the token until END and add it into TOKENS.
+
+This function is meant to be called exclusively from
+`bash-completion-tokenize-new-element'.
+
+This function expect the point to be at the start of a new token
+section, either at the start of the token or just after a quote
+has been closed in the token. It detects new opening quotes and
+calls `bash-completion-tokenize-1'.
+
+END specifies the point at which tokenization should stop.
+
+TOKENS is the list of tokens built so farin reverse order.
+
+TOKEN is the token currently being built.
+
+Return TOKENS with new tokens prepended to it."
+ (let ( (char-start (char-after))
+ (quote nil) )
+ (when (and char-start (or (= char-start ?') (= char-start ?\")))
+ (forward-char)
+ (setq quote char-start))
+ (bash-completion-tokenize-1 end quote tokens token)))
+
+(defun bash-completion-tokenize-1 (end quote tokens token)
+ "Tokenize the rest of the token.
+
+This function is meant to be called exclusively from
+`bash-completion-tokenize-0'.
+
+This function tokenize the rest of the token and either call
+itself and `bash-completion-tokenize-0' recursively or append the
+token to the list of token and call
+`bash-completion-tokenize-new-element' to look for the next
+token.
+
+END specifies the point at which tokenization should stop.
+
+QUOTE specifies the current quote. It should be nil ?' or ?\"
+
+TOKENS is the list of tokens built so farin reverse order.
+
+TOKEN is the token currently being built.
+
+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))
+ (bash-completion-tokenize-append-str
+ token
+ (buffer-substring-no-properties local-start (point))))
+ (cond
+ ;; an escaped char, skip, whatever it is
+ ((and (char-before) (= ?\\ (char-before)))
+ (forward-char)
+ (let ((str (bash-completion-tokenize-get-str token)))
+ (aset str (1- (length str)) (char-before)))
+ (bash-completion-tokenize-1 end quote tokens token))
+ ;; opening quote
+ ((and (not quote) (char-after) (or (= ?' (char-after)) (= ?\" (char-after))))
+ (bash-completion-tokenize-0 end tokens token))
+ ;; closing quote
+ ((and quote (char-after) (= quote (char-after)))
+ (forward-char)
+ (bash-completion-tokenize-0 end tokens token))
+ ;; space inside a quote
+ ((and quote (char-after) (not (= quote (char-after))))
+ (forward-char)
+ (bash-completion-tokenize-append-str token (char-to-string (char-before)))
+ (bash-completion-tokenize-1 end quote tokens token))
+ ;; word end
+ (t
+ (bash-completion-tokenize-set-end token)
+ (when quote
+ (push (cons 'quote quote) token))
+ (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)
+ "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)))
+
+;;; ---------- Functions: getting candidates from bash
+
+(defun bash-completion-comm (line pos words cword open-quote)
+ "Set LINE, POS, WORDS and CWORD, call compgen, return the result.
+
+This function starts a separate bash process if necessary, sets
+up the completion environment (COMP_LINE, COMP_POINT, COMP_WORDS,
+COMP_CWORD) and calls compgen.
+
+OPEN-QUOTE should be the quote, a character, that's still open in
+the last word or nil.
+
+The result is a list of candidates, which might be empty."
+ ;; start process now, to make sure bash-completion-alist is
+ ;; set before we run bash-completion-generate-line
+ (bash-completion-require-process)
+ (bash-completion-send
+ (concat
+ (bash-completion-generate-line line pos words cword)
+ " 2>/dev/null"))
+ (bash-completion-extract-candidates (nth cword words) open-quote))
+
+(defun bash-completion-extract-candidates (stub open-quote)
+ "Extract the completion candidates from the process buffer for STUB.
+
+This command takes the content of the completion process buffer, split
+it by newlines, post-process the candidates and returns 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 STUB is quoted, the quote character, ' or \", should be passed
+in OPEN-QUOTE.
+
+Post-processing includes escaping special characters, adding a /
+to directory names, merging STUB with the result. See `bash-completion-fix'
+for more details."
+ (let ((bash-completion-prefix 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 prefix open-quote)
+ "Fix completion candidate in STR if PREFIX is the current prefix.
+
+STR is the completion candidate to modify.
+
+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'.
+
+OPEN-QUOTE should be the quote that's still open in prefix. A
+character (' or \") or nil. If it is nil, the value of
+`bash-completion-open-quote' is used. This allows
+calling this function from `mapcar'.
+
+Return a modified version of the completion candidate.
+
+Modification include:
+ - escaping of special characters in STR
+ - prepending PREFIX 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 ((prefix (or prefix bash-completion-prefix))
+ (open-quote (or open-quote (and (boundp 'bash-completion-open-quote) bash-completion-open-quote)))
+ (suffix ""))
+ (bash-completion-addsuffix
+ (let* ((rebuilt)
+ (rest (cond
+ ((bash-completion-starts-with str prefix)
+ (substring str (length prefix)))
+ ;; bash expands the home directory automatically. This is confusing
+ ;; for comint-dynamic-simple-complete
+ ((and (bash-completion-starts-with prefix "~")
+ (bash-completion-starts-with str (expand-file-name "~")))
+ (substring (concat "~" (substring str (length (expand-file-name "~"))))
+ (length prefix)))
+ ((bash-completion-starts-with prefix str)
+ ;; completion is a substring of prefix something's
+ ;; gone wrong. Treat it as one (useless)
+ ;; candidate.
+ (setq 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 prefix) str))
+ prefix)
+ (substring rebuilt (length 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 prefix (bash-completion-escape-candidate rest open-quote) suffix)))))
+
+(defun bash-completion-escape-candidate (completion-candidate open-quote)
+ "Escapes COMPLETION-CANDIDATE.
+
+This function escapes all special characters in the result of
+bash completion. It does nothing if COMPLETION-CANDIDATE looks
+like a quoted string.
+
+It uses escape characters appropriate for the quote defined in
+OPEN-QUOTE, either nil, ' or \".
+
+Return a possibly escaped version of COMPLETION-CANDIDATE."
+ (cond
+ ((and (null open-quote)
+ (null (string-match "^['\"]" completion-candidate)))
+ (replace-regexp-in-string "\\([ '\"#]\\)" "\\\\\\1" completion-candidate))
+ ((eq ?' open-quote)
+ (replace-regexp-in-string "'" "'\\''" completion-candidate :literal t))
+ ((eq ?\" open-quote)
+ (replace-regexp-in-string "\"" "\\\"" completion-candidate :literal t))
+ (t
+ completion-candidate)))
+
+(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 (str)
+ "Add a directory suffix to 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 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.
+
+If no wordbreak was found, it returns STR.
+
+Wordbreaks characters are defined in 'bash-completion-wordbreak'."
+ (car (bash-completion-last-wordbreak-split str)))
+
+(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'."
+ (cdr (bash-completion-last-wordbreak-split str)))
+
+(defun bash-completion-last-wordbreak-split (str)
+ "Split STR at the last wordbreak character.
+
+The part before the last wordbreak character includes the
+wordbreak character itself. It is \"\" if no wordbreak character
+was found.
+
+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)))
+ (while (>= end 0)
+ (when (memq (aref str end) bash-completion-wordbreaks)
+ (throw 'bash-completion-return (cons (substring str 0 (1+ end)) (substring str (1+ end)))))
+ (setq end (1- end))))
+ (cons "" str)))
+
+(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)
+ (equal (substring str (- suffix-len)) suffix)))))
+
+(defun bash-completion-starts-with (str prefix)
+ "Return t if STR starts with PREFIX."
+ (let ((prefix-len (length prefix))
+ (str-len (length str)))
+ (and
+ (>= str-len prefix-len)
+ (equal (substring str 0 prefix-len) prefix))))
+
+;;; ---------- Functions: bash subprocess
+
+(defun bash-completion-require-process ()
+ "Return the bash completion process or start it.
+
+If a bash completion process is already running, return it.
+
+Otherwise, create a bash completion process and return the
+result. This can take a since bash needs to start completely
+before this function returns to be sure everything has been
+initialized correctly.
+
+The process uses `bash-completion-prog' to figure out the path to
+bash on the current system.
+
+To get an environment consistent with shells started with `shell',
+the first file found in the following list are sourced if they exist:
+ ~/.emacs_bash.sh
+ ~/.emacs.d/init_bash.sh
+Or, to be more exact, ~/.emacs_$(basename `bash-completion-prog').sh)
+and ~/.emacs.d/init_$(basename `bash-completion-prog').sh)
+
+To allow scripts to tell the difference between shells launched
+by bash-completion, the environment variable EMACS_BASH_COMPLETE
+is set to t."
+ (if (bash-completion-is-running)
+ bash-completion-process
+ ;; start process
+ (let ((process) (oldterm (getenv "TERM")))
+ (unwind-protect
+ (progn
+ (setenv "EMACS_BASH_COMPLETE" "t")
+ (setenv "TERM" "dumb")
+ (setq process
+ (start-process
+ "*bash-completion*"
+ (generate-new-buffer-name " bash-completion")
+ bash-completion-prog
+ "--noediting"))
+ (set-process-query-on-exit-flag process nil)
+ (let* ((shell-name (file-name-nondirectory bash-completion-prog))
+ (startfile1 (concat "~/.emacs_" shell-name ".sh"))
+ (startfile2 (concat "~/.emacs.d/init_" shell-name ".sh")))
+ (cond
+ ((file-exists-p startfile1)
+ (process-send-string process (concat ". " startfile1 "\n")))
+ ((file-exists-p startfile2)
+ (process-send-string process (concat ". " startfile2 "\n")))))
+ (bash-completion-send "PS1='\v'" process bash-completion-initial-timeout)
+ (bash-completion-send "function __bash_complete_wrapper { eval $__BASH_COMPLETE_WRAPPER; }" process)
+ ;; attempt to turn off unexpected status messages from bash
+ ;; if the current version of bash does not support these options,
+ ;; the commands will fail silently and be ignored.
+ (bash-completion-send "shopt -u checkjobs" process)
+ (bash-completion-send "shopt -u mailwarn" process)
+ (bash-completion-send "export MAILCHECK=-1" process)
+ (bash-completion-send "export -n MAIL" process)
+ (bash-completion-send "export -n MAILPATH" process)
+ ;; some bash completion functions use quote_readline to double-quote
+ ;; strings - which compgen understands but only in some environment.
+ ;; disable this dreadful business to get a saner way of handling
+ ;; spaces. Noticed in bash_completion v1.872.
+ (bash-completion-send "function quote_readline { echo \"$1\"; }" process)
+ (bash-completion-send "complete -p" process)
+ (bash-completion-build-alist (process-buffer process))
+ (setq bash-completion-process process)
+ (setq process nil)
+ bash-completion-process)
+ ;; finally
+ (progn
+ (setenv "EMACS_BASH_COMPLETE" nil)
+ (setenv "TERM" oldterm)
+ (when process
+ (condition-case err
+ (bash-completion-kill process)
+ (error nil))))))))
+
+(defun bash-completion-cd-command-prefix ()
+ "Build a command-line that CD to default-directory.
+
+Return a bash command-line for going to default-directory or \"\"."
+ (if default-directory
+ (concat "cd >/dev/null 2>&1 "
+ (bash-completion-quote (expand-file-name default-directory))
+ " ; ")
+ ""))
+
+(defun bash-completion-build-alist (buffer)
+ "Build `bash-completion-alist' with the content of BUFFER.
+
+BUFFER should contains the output of:
+ complete -p
+
+Return `bash-completion-alist', which is slightly parsed version
+of the output of \"complete -p\"."
+ (with-current-buffer buffer
+ (save-excursion
+ (setq bash-completion-alist nil)
+ (goto-char (point-max))
+ (while (= 0 (forward-line -1))
+ (bash-completion-add-to-alist
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize
+ (line-beginning-position)
+ (line-end-position)))))))
+ bash-completion-alist)
+
+(defun bash-completion-add-to-alist (words)
+ "Add split 'complete' line WORDS to `bash-completion-add-to-alist'.
+
+This parses the complete command-line arguments as output by
+ complete -p
+
+This does not work on arbitrary 'complete' calls.
+
+Lines that do not start with the word complete are skipped.
+
+Return `bash-completion-alist'."
+ (when (string= "complete" (car words))
+ (let* ( (reverse-wordsrest (nreverse (cdr words)))
+ (command (car reverse-wordsrest))
+ (options (nreverse (cdr reverse-wordsrest))) )
+ (when (and command options)
+ (push (cons command options) bash-completion-alist))))
+ bash-completion-alist)
+
+(defun bash-completion-generate-line (line pos words cword)
+ "Generate a command-line that calls compgen.
+
+This function looks into `bash-completion-alist' for a matching compgen
+argument set. If it finds one, it executes it. Otherwise, it executes the
+default bash completion (compgen -o default)
+
+LINE is the command-line to complete.
+POS is the position of the cursor on LINE
+WORDS is the content of LINE split by words and unescaped
+CWORD is the word 0-based index of the word to complete in WORDS
+
+If the compgen argument set specifies a custom function or command, the
+arguments will be passed to this function or command as:
+ COMP_LINE, taken from LINE
+ COMP_POINT, taken from POS
+ COMP_WORDS, taken from WORDS (a bash array)
+ COMP_CWORD, taken for CWORD
+
+Return a bash command-line that calls compgen to get the completion
+candidates."
+ (concat
+ (bash-completion-cd-command-prefix)
+ (let* ( (command-name (file-name-nondirectory (car words)))
+ (compgen-args (cdr (assoc command-name bash-completion-alist)))
+ (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))
+
+ ((not compgen-args)
+ ;; no completion configured for this command
+ (bash-completion-join (list "compgen" "-o" "default" stub)))
+
+ ((or (member "-F" compgen-args) (member "-C" compgen-args))
+ ;; custom completion with a function of command
+ (let* ( (args (copy-tree compgen-args))
+ (function (or (member "-F" args) (member "-C" args)))
+ (function-name (car (cdr function))) )
+ (setcar function "-F")
+ (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[@]}\""
+ (bash-completion-quote line)
+ pos
+ cword
+ (bash-completion-join words)
+ (bash-completion-quote function-name)))
+ (bash-completion-join args)
+ (bash-completion-quote stub))))
+ (t
+ ;; simple custom completion
+ (format "compgen %s -- %s" (bash-completion-join compgen-args) stub))))))
+
+;;;###autoload
+(defun bash-completion-reset ()
+ "Force the next completion command to start with a fresh BASH process.
+
+This function kills any existing BASH completion process. This way, the
+next time BASH completion is requested, a new process will be created with
+the latest configuration.
+
+Call this method if you have updated your .bashrc or any bash init scripts
+and would like bash completion in Emacs to take these changes into account."
+ (interactive)
+ (bash-completion-kill bash-completion-process)
+ (setq bash-completion-process nil))
+
+(defun bash-completion-kill (process)
+ "Kill PROCESS and its buffer."
+ (when process
+ (when (eq 'run (process-status process))
+ (kill-process process))
+ (let ((buffer (process-buffer process)))
+ (when (buffer-live-p buffer)
+ (kill-buffer buffer)))))
+
+(defun bash-completion-buffer ()
+ "Return the buffer of the BASH process, create the BASH process if necessary."
+ (process-buffer (bash-completion-require-process)))
+
+(defun bash-completion-is-running ()
+ "Check whether the bash completion process is running."
+ (and bash-completion-process (eq 'run (process-status bash-completion-process))))
+
+(defun bash-completion-send (commandline &optional process timeout)
+ "Send a command to the bash completion process.
+
+COMMANDLINE should be a bash command, without the final newline.
+
+PROCESS should be the bash process, if nil this function calls
+`bash-completion-require-process' which might start a new process.
+
+TIMEOUT is the timeout value for this operation, if nil the value of
+`bash-completion-process-timeout' is used.
+
+Once this command has run without errors, you will find the result
+of the command in the bash completion process buffer."
+ ;;(message commandline)
+ (let ((process (or process (bash-completion-require-process)))
+ (timeout (or timeout bash-completion-process-timeout)))
+ (with-current-buffer (process-buffer process)
+ (erase-buffer)
+ (process-send-string process (concat commandline "\n"))
+ (while (not (progn (goto-char 1) (search-forward "\v" nil t)))
+ (unless (accept-process-output process timeout)
+ (error "Timeout while waiting for an answer from bash-completion process")))
+ (goto-char (point-max))
+ (delete-backward-char 1))))
+
+(provide 'bash-completion)
+;;; bash-completion.el ends here
View
639 site-lisp/bash-completion/bash-completion_test.el
@@ -0,0 +1,639 @@
+;;; bash-completion_test.el --- Tests bash-completion.el
+
+;; Copyright (C) 2009 Stephane Zermatten
+
+;; Author: Stephane Zermatten <szermatt@gmx.net>
+
+;; This program is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; either version 2 of the
+;; License, or (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see
+;; `http://www.gnu.org/licenses/'.
+
+
+:;;; Commentary:
+;;
+;; This file defines `bash-completion-regress' and run the
+;; regression tests if and only if regress is already imported.
+;;
+
+;;; History:
+;;
+
+;;; Code:
+(eval-when-compile
+ (require 'sz-testutils)
+ (require 'cl)
+
+ (defvar bash-completion-run-integration-tests nil
+ "Run integration tests. Integration start subprocess (bash
+shells) and as a result are too slow to be run in many
+cases. That's why they need to be enabled manually.")
+
+ ;; This code will not appear in the compiled (.elc) file
+
+ ;; ---------- unit tests
+ (put 'bash-completion-regress 'regression-suite t)
+ (setq bash-completion-regress
+ '("bash-completion-regress"
+ ;; Each test in the suite is of the form:
+ ;; ([description] probe grader)
+ ;; DESCRIPTION - string
+ ;; PROBE - a sexp which runs the actual test
+ ;; GRADER - the desired result or a sexp which determines
+ ;; how we did
+
+ ("bash-completion-join simple"
+ (bash-completion-join '("a" "hello" "world" "b" "c"))
+ "a hello world b c")
+
+ ("bash-completion-join escape quote"
+ (bash-completion-join '("a" "hel'lo" "world" "b" "c"))
+ "a 'hel'\\''lo' world b c")
+
+ ("bash-completion-join escape space"
+ (bash-completion-join '("a" "hello world" "b" "c"))
+ "a 'hello world' b c")
+
+ ("bash-completion-tokenize simple"
+ (sz-testutils-with-buffer
+ '("a hello world b c")
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position))))
+ '("a" "hello" "world" "b" "c"))
+
+ ("bash-completion-tokenize simple extra spaces"
+ (sz-testutils-with-buffer
+ '(" a hello \n world \t b \r c ")
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position 2))))
+ '("a" "hello" "world" "b" "c"))
+
+ ("bash-completion-tokenize escaped space"
+ (sz-testutils-with-buffer
+ '("a hello\\ world b c")
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position))))
+ '("a" "hello world" "b" "c"))
+
+ ("bash-completion-tokenize escaped #"
+ (sz-testutils-with-buffer
+ '("a hello \\#world\\# b")
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position))))
+ '("a" "hello" "#world#" "b"))
+
+ ("bash-completion-tokenize double quotes"
+ (sz-testutils-with-buffer
+ '("a \"hello world\" b c")
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position))))
+ '("a" "hello world" "b" "c"))
+
+ ("bash-completion-tokenize double quotes escaped"
+ (sz-testutils-with-buffer
+ '("a \"-\\\"hello world\\\"-\" b c")
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position))))
+ '("a" "-\"hello world\"-" "b" "c"))
+
+ ("bash-completion-tokenize single quotes"
+ (sz-testutils-with-buffer
+ '("a \"hello world\" b c")
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position))))
+ '("a" "hello world" "b" "c"))
+
+ ("bash-completion-tokenize single quotes escaped"
+ (sz-testutils-with-buffer
+ '("a '-\\'hello world\\'-' b c")
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position))))
+ '("a" "-'hello world'-" "b" "c"))
+
+ ("bash-completion-tokenize complex quote mix"
+ (sz-testutils-with-buffer
+ '("a hel\"lo w\"o'rld b'c d")
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position))))
+ '("a" "hello world bc" "d"))
+
+ ("bash-completion-tokenize unescaped semicolon"
+ (sz-testutils-with-buffer
+ "to infinity;and\\ beyond"
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position))))
+ '("to" "infinity" ";" "and beyond"))
+
+ ("bash-completion-tokenize unescaped &&"
+ (sz-testutils-with-buffer
+ "to infinity&&and\\ beyond"
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position))))
+ '("to" "infinity" "&&" "and beyond"))
+
+ ("bash-completion-tokenize unescaped ||"
+ (sz-testutils-with-buffer
+ "to infinity||and\\ beyond"
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position))))
+ '("to" "infinity" "||" "and beyond"))
+
+ ("bash-completion-tokenize quoted ;&|"
+ (sz-testutils-with-buffer
+ "to \"infinity;&|and\" beyond"
+ (bash-completion-strings-from-tokens
+ (bash-completion-tokenize 1 (line-end-position))))
+ '("to" "infinity;&|and" "beyond"))
+
+ ("bash-completion-parse-line cursor at end of word"
+ (sz-testutils-with-buffer
+ "a hello world"
+ (bash-completion-parse-line 1 (line-end-position)))
+ '((line . "a hello world")
+ (point . 13)
+ (cword . 2)
+ (words . ("a" "hello" "world"))))
+
+ ("bash-completion-parse-line cursor in the middle of a word"
+ (sz-testutils-with-buffer
+ "a hello wo"
+ (bash-completion-parse-line 1 (line-end-position)))
+ '((line . "a hello wo")
+ (point . 10)
+ (cword . 2)
+ (words . ("a" "hello" "wo"))))
+
+ ("bash-completion-parse-line cursor at the beginning"
+ (sz-testutils-with-buffer
+ " "
+ (bash-completion-parse-line 1 (line-end-position)))
+ '((line . "")
+ (point . 0)
+ (cword . 0)
+ (words . (""))))
+
+ ("bash-completion-parse-line cursor in the middle"
+ (sz-testutils-with-buffer
+ "a hello "
+ (bash-completion-parse-line 1 (line-end-position)))
+ '((line . "a hello ")
+ (point . 8)
+ (cword . 2)
+ (words . ("a" "hello" ""))))
+
+ ("bash-completion-parse-line cursor at end"
+ (sz-testutils-with-buffer
+ "a hello world b c"
+ (bash-completion-parse-line 1 (line-end-position)))
+ '((line . "a hello world b c")
+ (point . 17)
+ (cword . 4)
+ (words . ("a" "hello" "world" "b" "c"))))
+
+ ("bash-completion-parse-line complex multi-command line"
+ (sz-testutils-with-buffer
+ "cd /var/tmp ; ZORG=t make -"
+ (bash-completion-parse-line 1 (line-end-position)))
+ '((line . "make -")
+ (point . 6)
+ (cword . 1)
+ (words . ("make" "-"))))
+
+
+ ("bash-completion-parse-line pipe"
+ (sz-testutils-with-buffer
+ "ls /var/tmp | sort -"
+ (bash-completion-parse-line 1 (line-end-position)))
+ '((line . "sort -")
+ (point . 6)
+ (cword . 1)
+ (words . ("sort" "-"))))
+
+ ("bash-completion-parse-line escaped semicolon"
+ (sz-testutils-with-buffer
+ "find -name '*.txt' -exec echo {} ';' -"
+ (bash-completion-parse-line 1 (line-end-position)))
+ '((line . "find -name '*.txt' -exec echo {} ';' -")
+ (point . 38)
+ (cword . 7)
+ (words . ("find" "-name" "*.txt" "-exec" "echo" "{}" ";" "-"))))
+
+ ("bash-completion-parse-line at var assignment"
+ (sz-testutils-with-buffer
+ "cd /var/tmp ; A=f ZORG=t"
+ (bash-completion-parse-line 1 (line-end-position)))
+ '((line . "ZORG=t")
+ (point . 6)
+ (cword . 0)
+ (words . ("ZORG=t"))))
+
+ ("bash-completion-parse-line cursor after end"
+ (sz-testutils-with-buffer
+ "a hello world b c "
+ (bash-completion-parse-line 1 (line-end-position)))
+ '((line . "a hello world b c ")
+ (point . 18)
+ (cword . 5)
+ (words . ("a" "hello" "world" "b" "c" ""))))
+
+ ("bash-completion-parse-line with escaped quote"
+ (sz-testutils-with-buffer
+ "cd /vcr/shows/Dexter\\'s"
+ (bash-completion-parse-line 1 (line-end-position)))
+ '((line . "cd /vcr/shows/Dexter\\'s")
+ (point . 23)
+ (cword . 1)
+ (words . ("cd" "/vcr/shows/Dexter's"))))
+
+ ("bash-completion-add-to-alist garbage"
+ (let ((bash-completion-alist nil))
+ (bash-completion-add-to-alist '("just" "some" "garbage")))
+ nil)
+
+ ("bash-completion-add-to-alist empty"
+ (let ((bash-completion-alist nil))
+ (bash-completion-add-to-alist '()))
+ nil)
+
+ ("bash-completion-add-to-alist empty string"
+ (let ((bash-completion-alist nil))
+ (bash-completion-add-to-alist '("")))
+ nil)
+
+ ("bash-completion-add-to-alist empty complete"
+ (let ((bash-completion-alist nil))
+ (bash-completion-add-to-alist '("complete")))
+ nil)
+
+ ("bash-completion-add-to-alist one command"
+ (let ((bash-completion-alist nil))
+ (bash-completion-add-to-alist '("complete" "-e" "-F" "_cdargs_aliases" "cdb")))
+ '(("cdb" . ("-e" "-F" "_cdargs_aliases"))))
+
+ ("bash-completion-build-alist"
+ (sz-testutils-with-buffer
+ "
+complete -F _cdargs_aliases cdb
+complete -F complete_projects project
+complete -F complete_projects pro
+complete -F _cdargs_aliases cv
+complete -F _cdargs_aliases cb
+garbage
+"
+ (let ((bash-completion-alist '(garbage)))
+ (bash-completion-build-alist (current-buffer))))
+ '(("cdb" "-F" "_cdargs_aliases")
+ ("project" "-F" "complete_projects")
+ ("pro" "-F" "complete_projects")
+ ("cv" "-F" "_cdargs_aliases")
+ ("cb" "-F" "_cdargs_aliases")))
+
+ ("bash-completion-quote not necessary"
+ (bash-completion-quote "hello")
+ "hello")
+
+ ("bash-completion-quote space"
+ (bash-completion-quote "hello world")
+ "'hello world'")
+
+ ("bash-completion-quote quote"
+ (bash-completion-quote "hell'o")
+ "'hell'\\''o'")
+
+ ("bash-completion-generate-line no custom completion"
+ (let ((bash-completion-alist nil)
+ (default-directory "~/test"))
+ (bash-completion-generate-line "hello worl" 7 '("hello" "worl") 1))
+ (concat "cd >/dev/null 2>&1 " (expand-file-name "~/test") " ; compgen -o default worl"))
+
+ ("bash-completion-generate-line custom completion no function or command"
+ (let ((bash-completion-alist '(("zorg" . ("-A" "-G" "*.txt"))))
+ (default-directory "/test"))
+ (bash-completion-generate-line "zorg worl" 7 '("zorg" "worl") 1))
+ "cd >/dev/null 2>&1 /test ; compgen -A -G '*.txt' -- worl")
+
+ ("bash-completion-generate-line custom completion function"
+ (let ((bash-completion-alist '(("zorg" . ("-F" "__zorg"))))
+ (default-directory "/test"))
+ (bash-completion-generate-line "zorg worl" 7 '("zorg" "worl") 1))
+ "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")
+
+ ("bash-completion-generate-line custom completion command"
+ (let ((bash-completion-alist '(("zorg" . ("-C" "__zorg"))))
+ (default-directory "/test"))
+ (bash-completion-generate-line "zorg worl" 7 '("zorg" "worl") 1))
+ "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")
+
+
+ ("bash-completion-starts-with empty str"
+ (bash-completion-starts-with "" "prefix")
+ nil)
+
+ ("bash-completion-starts-with starts with"
+ (bash-completion-starts-with "blah-blah" "blah-")
+ t)
+
+ ("bash-completion-starts-with does not starts with"
+ (bash-completion-starts-with "blah-blah" "blih-")
+ nil)
+
+ ("bash-completion-starts-with same"
+ (bash-completion-starts-with "blah-" "blah-")
+ t)
+
+ ("bash-completion-send"
+ (let ((process 'proces))
+ (flet ((process-buffer
+ (process)
+ (unless (eq process 'process)
+ (error "unexpected: %s" process))
+ (current-buffer))
+ (process-send-string
+ (process command)
+ (unless (eq process 'process)
+ (error "unexpected process: %s" process))
+ (unless (equal "cmd\n" command)
+ (error "unexpected command: %s" command)))
+ (accept-process-output
+ (process timeout)
+ (unless (eq process 'process)
+ (error "unexpected process: %s" process))
+ (unless (= timeout 3.14)
+ (error "unexpected timeout: %s" timeout))
+ (insert "line1\nline2\n\v")
+ t))
+ (sz-testutils-with-buffer-content
+ ""
+ (bash-completion-send "cmd" 'process 3.14))))
+ "line1\nline2\n")
+
+ ("bash-completion-cd-command-prefix no current dir"
+ (let ((default-directory nil))
+ (bash-completion-cd-command-prefix))
+ "")
+
+ ("bash-completion-cd-command-prefix current dir"
+ (let ((default-directory "/tmp/x"))
+ (bash-completion-cd-command-prefix))
+ "cd >/dev/null 2>&1 /tmp/x ; ")
+
+ ("bash-completion-cd-command-prefix expand tilde"
+ (let ((default-directory "~/x"))
+ (bash-completion-cd-command-prefix))
+ (concat "cd >/dev/null 2>&1 " (expand-file-name "~/x") " ; "))
+
+ ("bash-completion-addsuffix ends with /"
+ (flet ((file-accessible-directory-p (a) (error "unexpected")))
+ (bash-completion-addsuffix "hello/"))
+ "hello/")
+
+ ("bash-completion-addsuffix ends with space"
+ (flet ((file-accessible-directory-p (a) (error "unexpected")))
+ (bash-completion-addsuffix "hello "))
+ "hello ")
+
+ ("bash-completion-addsuffix ends with separator"
+ (flet ((file-accessible-directory-p (a) (error "unexpected")))
+ (bash-completion-addsuffix "hello:"))
+ "hello:")
+
+ ("bash-completion-addsuffix check directory"
+ (flet ((file-accessible-directory-p (a) (equal a "/tmp/hello")))
+ (let ((default-directory "/tmp"))
+ (bash-completion-addsuffix "hello")))
+ "hello/")
+
+ ("bash-completion-addsuffix check directory, expand tilde"
+ (flet ((file-accessible-directory-p (a) (equal a (concat (expand-file-name "y" "~/x")))))
+ (let ((default-directory "~/x"))
+ (bash-completion-addsuffix "y")))
+ "y/")
+
+ ("bash-completion-starts-with"
+ (list
+ (bash-completion-starts-with "" "hello ")
+ (bash-completion-starts-with "hello world" "hello ")
+ (bash-completion-starts-with "hello world" "hullo ")
+ (bash-completion-starts-with "hello" ""))
+ '(nil t nil t))
+
+ ("bash-completion-ends-with"
+ (list
+ (bash-completion-ends-with "" "world")
+ (bash-completion-ends-with "hello world" "world")
+ (bash-completion-ends-with "hello world" "wurld")
+ (bash-completion-ends-with "hello" ""))
+ '(nil t nil t))
+
+ ("bash-completion-last-wordbreak-split"
+ (list
+ (bash-completion-last-wordbreak-split "a:b:c:d:e")
+ (bash-completion-last-wordbreak-split "hello=world")
+ (bash-completion-last-wordbreak-split "hello>world")
+ (bash-completion-last-wordbreak-split ">world")
+ (bash-completion-last-wordbreak-split "hello"))
+ '(("a:b:c:d:" . "e")
+ ("hello=" . "world")
+ ("hello>" . "world")
+ (">" . "world")
+ ("" . "hello")))
+
+
+ ("bash-completion-before-last-wordbreak"
+ (list
+ (bash-completion-before-last-wordbreak "a:b:c:d:e")
+ (bash-completion-before-last-wordbreak "hello=world")
+ (bash-completion-before-last-wordbreak "hello>world")
+ (bash-completion-before-last-wordbreak "hello"))
+ '("a:b:c:d:" "hello=" "hello>" ""))
+
+ ("bash-completion-after-last-wordbreak"
+ (list
+ (bash-completion-after-last-wordbreak "a:b:c:d:e")
+ (bash-completion-after-last-wordbreak "hello=world")
+ (bash-completion-after-last-wordbreak "hello>world")
+ (bash-completion-after-last-wordbreak "hello"))
+ '("e" "world" "world" "hello"))
+
+ ("bash-completion-fix escape rest"
+ (bash-completion-fix "a\\ bc d e" "a\\ b")
+ "a\\ bc\\ d\\ e")
+
+ ("bash-completion-fix do not escape final space"
+ (let ((bash-completion-nospace nil))
+ (bash-completion-fix "ab " "a"))
+ "ab ")
+
+ ("bash-completion-fix remove final space"
+ (let ((bash-completion-nospace t))
+ (bash-completion-fix "ab " "a"))
+ "ab")
+
+ ("bash-completion-fix unexpand home and escape"
+ (bash-completion-fix (expand-file-name "~/a/hello world") "~/a/he")
+ "~/a/hello\\ world")
+
+ ("bash-completion-fix match after wordbreak and escape"
+ (bash-completion-fix "hello world" "a:b:c:he")
+ "a:b:c:hello\\ world")
+
+ ("bash-completion-fix just append"
+ (bash-completion-fix " world" "hello")
+ "hello\\ world")
+
+ ("bash-completion-fix subset of the prefix"
+ (bash-completion-fix "Dexter" "Dexter'")
+ "Dexter")
+
+ ("bash-completion-extract-candidates"
+ (let ((bash-completion-nospace nil))
+ (flet ((bash-completion-buffer () (current-buffer)))
+ (sz-testutils-with-buffer
+ "hello world\nhello \n\n"
+ (bash-completion-extract-candidates "hello" nil))))
+ '("hello\\ world" "hello "))
+
+ ("bash-completion-nonsep"
+ (list
+ (bash-completion-nonsep nil)
+ (bash-completion-nonsep ?')
+ (bash-completion-nonsep ?\"))
+ '("^ \t\n\r;&|'\"#" "^ \t\n\r'" "^ \t\n\r\""))
+
+
+ ("bash-completion-escape-candidate no quote"
+ (bash-completion-escape-candidate "He said: \"hello, 'you'\"" nil)
+ "He\\ said:\\ \\\"hello,\\ \\'you\\'\\\"")
+
+ ("bash-completion-escape-candidate no quote"
+ (bash-completion-escape-candidate "#hello#" nil)
+ "\\#hello\\#")
+
+ ("bash-completion-escape-candidate single quote"
+ (bash-completion-escape-candidate "He said: \"hello, 'you'\"" ?')
+ "He said: \"hello, '\\''you'\\''\"")
+
+ ("bash-completion-escape-candidate double quote"
+ (bash-completion-escape-candidate "He said: \"hello, 'you'\"" ?\")
+ "He said: \\\"hello, 'you'\\\"")
+
+ ("bash-completion-escape-candidate no quote not if double quoted"
+ (bash-completion-escape-candidate "\"hello, you" nil)
+ "\"hello, you")
+
+ ("bash-completion-escape-candidate no quote not if single quoted"
+ (bash-completion-escape-candidate "'hello, you" nil)
+ "'hello, you")
+
+ ("bash-completion-quote allowed"
+ (bash-completion-quote "abc_ABC/1-2.3")
+ "abc_ABC/1-2.3")
+
+ ("bash-completion-quote quoted"
+ (bash-completion-quote "a$b")
+ "'a$b'")
+
+ ("bash-completion-quote quoted single quote"
+ (bash-completion-quote "a'b")
+ "'a'\\''b'")
+
+ ("bash-completion-join"
+ (bash-completion-join '("ls" "-l" "/a/b" "/a/b c" "/a/b'c" "$help/d"))
+ "ls -l /a/b '/a/b c' '/a/b'\\''c' '$help/d'")
+
+ ))
+
+ ;; ---------- integration tests
+
+ (defmacro bash-completion_test-harness (&rest body)
+ `(let ((bash-completion-process nil) (bash-completion-alist nil))
+ (unwind-protect
+ (progn ,@body)
+ ;; tearDown
+ (condition-case err
+ (when bash-completion-process
+ (let ((buffer (process-buffer bash-completion-process)))
+ (kill-process bash-completion-process)
+ (kill-buffer buffer)))
+ (error (message "error in bash-completion_test tearDown: %s" err))))))
+
+ (defmacro bash-completion_test-with-shell (&rest body)
+ `(bash-completion_test-harness
+ (let ((shell-buffer nil)
+ (explicit-shell-file-name bash-completion-prog))
+ (unwind-protect
+ (progn
+ (setq shell-buffer (shell (generate-new-buffer-name "*bash-completion_test-with-shell*")))
+ ;; accept process output until there's nothing left
+ (while (accept-process-output nil 0.6))
+ ;; do a completion and return the result
+ (with-current-buffer shell-buffer
+ (let ((start (point)))
+ (progn ,@body)
+ (buffer-substring-no-properties start (point-max)))))
+ ;; finally
+ (when (and shell-buffer (buffer-live-p shell-buffer))
+ (with-current-buffer shell-buffer
+ (insert "\nexit\n"))
+ (kill-buffer shell-buffer))))))
+
+ (put 'bash-completion-regress-integration 'regression-suite t)
+ (setq bash-completion-regress-integration '(
+ ("bash-completion interaction"
+ (bash-completion_test-harness
+ (list
+ (bash-completion-is-running)
+ (buffer-live-p (bash-completion-buffer))
+ (bash-completion-is-running)
+ (bash-completion-comm "hel" 4 '("hel") 0 nil)
+ (progn
+ (bash-completion-send "echo $EMACS_BASH_COMPLETE")
+ (with-current-buffer (bash-completion-buffer)
+ (buffer-string)))
+ (bash-completion-reset)
+ (bash-completion-is-running)))
+ '(nil t t ("help ") "t\n" nil nil))
+
+ ("bash-completion setenv"
+ (bash-completion_test-harness
+ (bash-completion-send "echo $EMACS_BASH_COMPLETE")
+ (with-current-buffer (bash-completion-buffer)
+ (buffer-string)))
+ "t\n")
+
+ ("bash-completion execute one completion"
+ (bash-completion_test-with-shell
+ (let ((start (point)))
+ (insert "__bash_complete_")
+ (bash-completion-dynamic-complete)))
+ "__bash_complete_wrapper ")
+
+ ("bash-completion execute wordbreak completion"
+ (bash-completion_test-with-shell
+ (let ((start (point)))
+ (insert "export PATH=/sbin:/bi")
+ (bash-completion-dynamic-complete)))
+ "export PATH=/sbin:/bin/")
+
+ )))
+
+;; Run diagnostics when this module is evaluated or compiled
+;; if and only if the "regress" package is already loaded.
+;; This code will not appear in the compiled (.elc) file
+(eval-when-compile
+ (autoload 'regress "regress" "run regression test suites" t)
+ (when (featurep 'regress)
+ (regress bash-completion-regress)
+ (when bash-completion-run-integration-tests
+ (regress bash-completion-regress-integration))))
+
+;;; bash-completion_test.el ends here
View
339 site-lisp/bash-completion/license
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
View
170 site-lisp/bash-completion/sz-testutils.el
@@ -0,0 +1,170 @@
+;;; sz-testutils.el -- test utilities
+
+;; Copyright (C) 2009 Stephane Zermatten
+
+;; Author: Stephane Zermatten <szermatt@gmx.net>
+
+;; This program is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; either version 2 of the
+;; License, or (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see
+;; `http://www.gnu.org/licenses/'.
+
+(defmacro sz-testutils-with-buffer (content &rest body)
+ "Create a temporary buffer with CONTENT and execute BODY.
+
+CONTENT can be a string or a list of strings with one
+symbol 'cursor in it. The point will be put where 'cursor
+is: '(\"Here >>\" cursor \"<<.\")
+
+The return value is the one returned by BODY."
+ `(with-temp-buffer
+ (sz-testutils-insert ,content)
+ (progn ,@body)))
+
+(defmacro sz-testutils-with-buffer-content (content &rest body)
+ "Create a temporary buffer with CONTENT and execute BODY.
+
+CONTENT can be a string or a list of strings with one
+symbol 'cursor in it. The point will be put where 'cursor
+is: '(\"Here >>\" cursor \"<<.\")
+
+The return value is the content of the buffer after BODY
+has run."
+ `(with-temp-buffer
+ (sz-testutils-insert ,content)
+ (save-excursion
+ ,@body)
+ (buffer-string)))
+
+(defmacro sz-testutils-with-buffer-region (content &rest body)
+ "Create a temporary buffer with CONTENT and execute BODY.
+
+CONTENT can be a string or a list of strings with one
+symbol 'cursor in it. The point will be put where 'cursor
+is: '(\"Here >>\" cursor \"<<.\")
+
+The return value is the content of the regionBODY
+has run. The mark must be set."
+ `(with-temp-buffer
+ (sz-testutils-insert ,content)
+ (save-excursion
+ ,@body
+ (buffer-substring-no-properties (point) (mark)))))
+
+(defmacro sz-testutils-with-buffer-ret-and-content (content &rest body)
+ "Create a temporary buffer with CONTENT and execute BODY.
+
+CONTENT can be a string or a list of strings with one
+symbol 'cursor in it. The point will be put where 'cursor
+is: '(\"Here >>\" cursor \"<<.\")
+
+The return value is a cons containing the return value of
+BODY and the content of the buffer after BODY has run."
+ `(with-temp-buffer
+ (sz-testutils-insert ,content)
+ (cons
+ (save-excursion
+ ,@body)
+ (buffer-string))))
+
+(defun sz-testutils-insert (content)
+"Insert CONTENT to the current buffer.
+
+CONTENT can be a string or a list of strings with one
+symbol 'cursor in it. The point will be put where 'cursor
+is: '(\"Here >>\" cursor \"<<.\")"
+ (cond
+ ((stringp content)
+ (insert content)
+ (goto-char (point-min)))
+
+ ((listp content)
+ (let ( (cursor (point-min)) )
+ (dolist (element content)
+ (cond
+ ((eq 'cursor element)
+ (setq cursor (point)))
+ ((eq 'mark element)
+ (set-mark (point))
+ (setq mark-active t))
+ ((and (listp element) (eq 'is (car element)))
+ (set (car (cdr element)) (point)))
+ (t
+ (insert element))))
+ (goto-char cursor)))
+
+ (t
+ (error (format "Neither a string nor a list of strings: %s" content)))))
+
+(eval-when-compile
+ ;; This code will not appear in the compiled (.elc) file
+ (put 'sz-testutils-regress 'regression-suite t)
+ (setq sz-testutils-regress
+ '("sz-testutils-regress"
+ ;; Each test in the suite is of the form:
+ ;; ([description] probe grader)
+ ;; DESCRIPTION - string
+ ;; PROBE - a sexp which runs the actual test
+ ;; GRADER - the desired result or a sexp which determines
+ ;; how we did
+ ("sz-testutils-with-buffer execute body with buffer"
+ (sz-testutils-with-buffer "hello" (buffer-string))
+ "hello")
+
+ ("sz-testutils-with-buffer return body "
+ (sz-testutils-with-buffer "hello" "bobo")
+ "bobo")
+
+ ("sz-testutils-with-buffer-content execute body with buffer"
+ (sz-testutils-with-buffer-content "hello" (insert (buffer-string)))
+ "hellohello")
+
+ ("sz-testutils-with-buffer set cursor position"
+ (sz-testutils-with-buffer '("hel" cursor "lo") (point))
+ 4)
+
+ ("sz-testutils-with-buffer-content set cursor position"
+ (sz-testutils-with-buffer-content '("hel" cursor "lo") (insert "X"))
+ "helXlo")
+
+ ("sz-testutils-with-buffer store position"
+ (let ((p1) (p2))
+ (sz-testutils-with-buffer '("hel" (is p1) "lo" (is p2))
+ (list p1 p2)))
+ '(4 6))
+
+ ("sz-testutils-with-buffer set mark"
+ (sz-testutils-with-buffer '("hel" mark "lo" cursor)
+ (list mark-active (mark) (point)))
+ '(t 4 6))
+
+ ("sz-testutils-with-buffer-region set mark"
+ (sz-testutils-with-buffer-region "hello world"
+ (goto-char 4) (push-mark 9))
+ "lo wo")
+
+ ("sz-testutils-with-buffer-ret-and-content"
+ (sz-testutils-with-buffer-ret-and-content '("hel" cursor "lo") (insert "X") 2)
+ '(2 . "helXlo"))
+
+ )))
+
+
+;; Run diagnostics when this module is evaluated or compiled
+;; if and only if the "regress" package is already loaded.
+;; This code will not appear in the compiled (.elc) file
+(eval-when-compile
+ (autoload 'regress "regress" "run regression test suites" t)
+ (if (featurep 'regress)
+ (regress sz-testutils-regress)))
+
+(provide 'sz-testutils)
View
405 site-lisp/shell-command.el
@@ -0,0 +1,405 @@
+;;; shell-command.el --- enables tab-completion for `shell-command'
+
+;; Copyright (C) 1998-2007 TSUCHIYA Masatoshi <tsuchiya@namazu.org>
+
+;; Author: TSUCHIYA Masatoshi <tsuchiya@namazu.org>
+;; Keywords: shell
+;; Version: $Revision: 1.38 $
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, you can either send email to this
+;; program's maintainer or write to: The Free Software Foundation,
+;; Inc.; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; This is an enhancement of shell-command, shell-command-on-region,
+;; grep, grep-find, and compile, that enables tab-completion of
+;; commands and dir/filenames within their input contexts.
+
+;; The latest version of this program can be downloaded from
+;; http://namazu.org/~tsuchiya/elisp/shell-command.el.
+
+;;; Install:
+
+;; Install this file to appropriate directory, and put these lines
+;; into your ~/.emacs.
+
+;; (require 'shell-command)
+;; (shell-command-completion-mode)
+
+;;; Code:
+(eval-when-compile
+ (require 'shell)
+ (require 'comint))
+
+(eval-and-compile
+ ;; Stuffs to keep compatibility between Emacsen.
+ (if (locate-library "custom")
+ (require 'custom)
+ (or (fboundp 'defgroup)
+ (defmacro defgroup (symbol members doc &rest args) nil))
+ (or (fboundp 'defcustom)
+ (defmacro defcustom (symbol value doc &rest args)
+ (list 'defvar symbol value doc))))
+ ;; These macros, such as `when' and `unless' are imported from
+ ;; subr.el of Emacs-21.2.
+ (or (fboundp 'when)