;;; sh-script.el --- shell-script editing commands for Emacs
-;; Copyright (C) 1993-1997, 1999, 2001-2012 Free Software Foundation, Inc.
+;; Copyright (C) 1993-1997, 1999, 2001-2013 Free Software Foundation, Inc.
;; Author: Daniel Pfeiffer <occitan@esperanto.org>
;; Version: 2.0f
. ((nil
;; function FOO
;; function FOO()
- "^\\s-*function\\s-+\\\([[:alpha:]_][[:alnum:]_]+\\)\\s-*\\(?:()\\)?"
+ "^\\s-*function\\s-+\\\([[:alpha:]_][[:alnum:]_]*\\)\\s-*\\(?:()\\)?"
1)
;; FOO()
(nil
- "^\\s-*\\([[:alpha:]_][[:alnum:]_]+\\)\\s-*()"
+ "^\\s-*\\([[:alpha:]_][[:alnum:]_]*\\)\\s-*()"
1)
)))
"Alist of regular expressions for recognizing shell function definitions.
:group 'sh-script
:version "20.4")
+(defun sh-current-defun-name ()
+ "Find the name of function or variable at point.
+For use in `add-log-current-defun-function'."
+ (save-excursion
+ (end-of-line)
+ (when (re-search-backward
+ (concat "\\(?:"
+ ;; function FOO
+ ;; function FOO()
+ "^\\s-*function\\s-+\\\([[:alpha:]_][[:alnum:]_]*\\)\\s-*\\(?:()\\)?"
+ "\\)\\|\\(?:"
+ ;; FOO()
+ "^\\s-*\\([[:alpha:]_][[:alnum:]_]*\\)\\s-*()"
+ "\\)\\|\\(?:"
+ ;; FOO=
+ "^\\([[:alpha:]_][[:alnum:]_]*\\)="
+ "\\)")
+ nil t)
+ (or (match-string-no-properties 1)
+ (match-string-no-properties 2)
+ (match-string-no-properties 3)))))
+
(defvar sh-shell-variables nil
"Alist of shell variable names that should be included in completion.
These are used for completion in addition to all the variables named
(define-key map "\C-c+" 'sh-add)
(define-key map "\C-\M-x" 'sh-execute-region)
(define-key map "\C-c\C-x" 'executable-interpret)
+ (define-key map "\C-c\C-n" 'sh-send-line-or-region-and-step)
+ (define-key map "\C-c\C-d" 'sh-cd-here)
+ (define-key map "\C-c\C-z" 'sh-show-shell)
(define-key map [remap delete-backward-char]
'backward-delete-char-untabify)
"." "alias" "bg" "bind" "builtin" "caller" "compgen" "complete"
"declare" "dirs" "disown" "enable" "fc" "fg" "help" "history"
"jobs" "kill" "let" "local" "popd" "printf" "pushd" "shopt"
- "source" "suspend" "typeset" "unalias")
+ "source" "suspend" "typeset" "unalias"
+ ;; bash4
+ "mapfile" "readarray")
;; The next entry is only used for defining the others
(bourne sh-append shell
:type '(repeat (cons (symbol :tag "Shell")
(choice (repeat string)
(sexp :format "Evaluate: %v"))))
+ :version "24.4" ; bash4 additions
:group 'sh-script)
The default is t because I assume that in one Emacs session one is
frequently editing existing scripts with different styles.")
+\f
+;; inferior shell interaction
+;; TODO: support multiple interactive shells
+(defvar sh-shell-process nil
+ "The inferior shell process for interaction.")
+(make-variable-buffer-local 'sh-shell-process)
+(defun sh-shell-process (force)
+ "Get a shell process for interaction.
+If FORCE is non-nil and no process found, create one."
+ (if (and sh-shell-process (process-live-p sh-shell-process))
+ sh-shell-process
+ (setq sh-shell-process
+ (let ((found nil) proc
+ (procs (process-list)))
+ (while (and (not found) procs
+ (process-live-p (setq proc (pop procs)))
+ (process-command proc))
+ (when (string-equal sh-shell (file-name-nondirectory
+ (car (process-command proc))))
+ (setq found proc)))
+ (or found
+ (and force
+ (get-buffer-process
+ (let ((explicit-shell-file-name sh-shell-file))
+ (shell)))))))))
+
+(defun sh-show-shell ()
+ "Pop the shell interaction buffer."
+ (interactive)
+ (pop-to-buffer (process-buffer (sh-shell-process t))))
+
+(defun sh-send-text (text)
+ "Send the text to the `sh-shell-process'."
+ (comint-send-string (sh-shell-process t) (concat text "\n")))
+
+(defun sh-cd-here ()
+ "Change directory in the current interaction shell to the current one."
+ (interactive)
+ (sh-send-text (concat "cd " default-directory)))
+
+(defun sh-send-line-or-region-and-step ()
+ "Send the current line to the inferior shell and step to the next line.
+When the region is active, send the region instead."
+ (interactive)
+ (let (from to end)
+ (if (use-region-p)
+ (setq from (region-beginning)
+ to (region-end)
+ end to)
+ (setq from (line-beginning-position)
+ to (line-end-position)
+ end (1+ to)))
+ (sh-send-text (buffer-substring-no-properties from to))
+ (goto-char end)))
+
\f
;; mode-command and utility functions
(setq-local skeleton-newline-indent-rigidly t)
(setq-local defun-prompt-regexp
(concat "^\\(function[ \t]\\|[[:alnum:]]+[ \t]+()[ \t]+\\)"))
+ (setq-local add-log-current-defun-function #'sh-current-defun-name)
;; Parse or insert magic number for exec, and set all variables depending
;; on the shell thus determined.
(sh-set-shell
;; (defconst sh-smie-csh-grammar
;; (smie-prec2->grammar
;; (smie-bnf->prec2
-;; '((exp) ;A constant, or a $var, or a sequence of them…
+;; '((exp) ;A constant, or a $var, or a sequence of them...
;; (elseifcmd (cmd)
;; (cmd "else" "else-if" exp "then" elseifcmd))
;; (cmd ("switch" branches "endsw")
controls whether to query about making the visited file executable.
Calls the value of `sh-set-shell-hook' if set."
- (interactive (list (completing-read (format "Shell \(default %s\): "
- sh-shell-file)
- interpreter-mode-alist
- (lambda (x) (eq (cdr x) 'sh-mode))
- nil nil nil sh-shell-file)
+ (interactive (list (completing-read
+ (format "Shell \(default %s\): "
+ sh-shell-file)
+ ;; This used to use interpreter-mode-alist, but that is
+ ;; no longer appropriate now that uses regexps.
+ ;; Maybe there could be a separate variable that lists
+ ;; the shells, used here and to construct i-mode-alist.
+ ;; But the following is probably good enough:
+ (append (mapcar (lambda (e) (symbol-name (car e)))
+ sh-ancestor-alist)
+ '("csh" "rc" "sh"))
+ nil nil nil nil sh-shell-file)
(eq executable-query 'function)
t))
(if (string-match "\\.exe\\'" shell)
(setq font-lock-set-defaults nil)
(font-lock-set-defaults)
(font-lock-fontify-buffer))
+ (setq sh-shell-process nil)
(run-hooks 'sh-set-shell-hook))
(defun sh-read-variable (var)
"Read a new value for indentation variable VAR."
- (interactive "*variable? ") ;; to test
(let ((minibuffer-help-form `(sh-help-string-for-variable
(quote ,var)))
val)