branch: elpa/powershell
commit 691c1a2791703a023120fd413e2a4556b32b763f
Author: Joe Schafer <[email protected]>
Commit: Joe Schafer <[email protected]>
Initial Commit.
---
README.md | 7 +
powershell-mode.el | 707 +++++++++++++++++++++++++++++++++++++++++++++++++++++
powershell.el | 156 ++++++++++++
3 files changed, 870 insertions(+)
diff --git a/README.md b/README.md
new file mode 100644
index 00000000000..084ebe05487
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+PowerShell Mode
+===============
+
+
+PowerShell Mode is an Emacs major mode for editing and running
+Microsoft PowerShell files.
+
diff --git a/powershell-mode.el b/powershell-mode.el
new file mode 100644
index 00000000000..5457767f94f
--- /dev/null
+++ b/powershell-mode.el
@@ -0,0 +1,707 @@
+;;; powershell-mode.el --- Mode for editing Powershell scripts
+
+;; Copyright (C) 2009, 2010 Fr�d�ric Perrin
+;; Copyright (C) 2012 Richard Bielawski rbielaws-at-i1-dot-net
+;; http://www.emacswiki.org/emacs/Rick_Bielawski
+
+;; Author: Fr�d�ric Perrin <frederic (dot) perrin (arobas) resel (dot) fr>
+;; Keywords: Powershell, Monad, MSH
+
+;; This file 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 3 of the License,
+;; or (at your option) any later version.
+
+;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Fr�d�ric Perrin Comments:
+;; This was written from scratch, without using Vivek Sharma's code:
+;; it had issues I wanted to correct, but unfortunately there were no
+;; licence indication, and Vivek didn't answered my mails.
+;;
+;;; Rick Bielawski Comments 2012/09/28:
+;; On March 31, 2012 Fr�d�ric gave me permission to take over support.
+;; I've added support for multi-line comments and here-strings as well
+;; as enhancement/features such as:
+;; Functions to quote, unquote and escape a selection, and one to wrap
+;; a selection in $(). Meanwhile I hope I didn't break anything.
+;;
+;;; Updates
+;; 2012/10/01 Fixed several bugs in highlighting variables and types.
+;; Renamed some variables to be more descriptive.
+;; 2012/10/02 Enhanced PowerShell-mode indenting & syntax table.
+;; Fixed dangling parens and re-indented the elisp itself.
+;; 2012/10/05 Added eldoc support. Fixed bug where indent could loop.
+;; See comment below on how to generate powershell-eldoc.el
+
+;; Variables you may want to customize.
+(defgroup powershell nil
+ "Customization of PowerShell mode."
+ :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
+ :group 'languages )
+
+(defcustom powershell-indent 4
+ "Amount of horizontal space to indent after, for instance, an
+opening brace"
+ :type 'integer
+ :group 'powershell)
+
+(defcustom powershell-continuation-indent 2
+ "Amount of horizontal space to indent a continuation line"
+ :type 'integer
+ :group 'powershell)
+
+(defcustom powershell-continued-regexp ".*\\(|[\\t ]*\\|`\\)$"
+ "Regexp matching a continued line (ending either with an
+explicit backtick, or with a pipe)."
+ :type 'integer
+ :group 'powershell)
+
+(defun powershell-continuation-line-p ()
+ "Returns t is the current line is a continuation line (i.e. the
+previous line is a continued line, ending with a backtick or a pipe"
+ (interactive)
+ (save-excursion
+ (forward-line -1)
+ (looking-at powershell-continued-regexp)))
+
+;; Rick added significant complexity to Fr�d�ric's original version
+(defun powershell-indent-line-amount ()
+ "Returns the column to which the current line ought to be indented."
+ (interactive)
+ (save-excursion
+ (beginning-of-line)
+ (if (powershell-continuation-line-p)
+ ;; on a continuation line (i.e. prior line ends with backtick
+ ;; or pipe), indent relative to the continued line.
+ (progn
+ (while (and (not (bobp))(powershell-continuation-line-p))
+ (forward-line -1))
+ (+ (current-indentation) powershell-continuation-indent))
+ ;; otherwise, indent relative to the block's opening char ([{
+ (let ((closing-paren (looking-at "\\s-*\\s)"))
+ new-indent
+ block-open-line)
+ (condition-case nil
+ (progn
+ (backward-up-list) ;when at top level, throw to no-indent
+ (setq block-open-line (line-number-at-pos))
+ ;; We're in a block, calculate/return indent amount.
+ (if (not (looking-at "\\s(\\s-*\\(#.*\\)?$"))
+ ;; code (not comments) follow the block open so
+ ;; vertically align the block with the code.
+ (if closing-paren
+ ;; closing indent = open
+ (setq new-indent (current-column))
+ ;; block indent = first line of code
+ (forward-char)
+ (skip-syntax-forward " ")
+ (setq new-indent (current-column)))
+ ;; otherwise block open is at eol so indent is relative to
+ ;; bol or another block open on the same line.
+ (if closing-paren ; this sets the default indent
+ (setq new-indent (current-indentation))
+ (setq new-indent (+ powershell-indent
(current-indentation))))
+ ;; now see if the block is nested on the same line
+ (when (condition-case nil
+ (progn
+ (backward-up-list)
+ (= block-open-line (line-number-at-pos)))
+ (scan-error nil))
+ (forward-char)
+ (skip-syntax-forward " ")
+ (if closing-paren
+ (setq new-indent (current-column))
+ (setq new-indent (+ powershell-indent (current-column))))))
+ new-indent)
+ (scan-error ;; most likely, we are at the top-level
+ 0))))))
+
+(defun powershell-indent-line ()
+ "Indent the current line of powershell mode, leaving the point
+in place if it is inside the meat of the line"
+ (interactive)
+ (let ((savep (> (current-column) (current-indentation)))
+ (amount (powershell-indent-line-amount)))
+ (if savep
+ (save-excursion (indent-line-to amount))
+ (indent-line-to amount))))
+
+(defun powershell-quote-selection (beg end)
+ "Quotes the selection with single quotes and doubles embedded single quotes"
+ (interactive `(,(region-beginning) ,(region-end)))
+ (if (not mark-active)
+ (error "Command requires a marked region"))
+ (goto-char beg)
+ (while (re-search-forward "'" end t)
+ (replace-match "''")(setq end (1+ end)))
+ (goto-char beg)
+ (insert "'")
+ (setq end (1+ end))
+ (goto-char end)
+ (insert "'"))
+
+(defun powershell-unquote-selection (beg end)
+ "Unquotes the selected text removing doubles as we go"
+ (interactive `(,(region-beginning) ,(region-end)))
+ (if (not mark-active)
+ (error "Command requires a marked region"))
+ (goto-char beg)
+ (cond ((looking-at "'")
+ (goto-char end)
+ (when (looking-back "'")
+ (delete-char -1)
+ (setq end (1- end))
+ (goto-char beg)
+ (delete-char 1)
+ (setq end (1- end))
+ (while (search-forward "'" end t)
+ (delete-char -1)
+ (forward-char)
+ (setq end (1- end)))))
+ ((looking-at "\"")
+ (goto-char end)
+ (when (looking-back "\"")
+ (delete-char -1)
+ (setq end (1- end))
+ (goto-char beg)
+ (delete-char 1)
+ (setq end (1- end))
+ (while (search-forward "\"" end t)
+ (delete-char -1)
+ (forward-char)
+ (setq end (1- end)))
+ (while (search-forward "`" end t)
+ (delete-char -1)
+ (forward-char)
+ (setq end (1- end)))))
+ (t (error "Must select quoted text exactly."))))
+
+(defun powershell-escape-selection (beg end)
+ "Escapes variables in the selection and extends existing escapes."
+ (interactive `(,(region-beginning) ,(region-end)))
+ (if (not mark-active)
+ (error "Command requires a marked region"))
+ (goto-char beg)
+ (while (re-search-forward "`" end t)
+ (replace-match "```")(setq end (+ end 2)))
+ (goto-char beg)
+ (while (re-search-forward "\\(?:\\=\\|[^`]\\)[$]" end t)
+ (goto-char (car (cdr (match-data))))
+ (backward-char)
+ (insert "`")
+ (forward-char)
+ (setq end (1+ end))))
+
+(defun powershell-doublequote-selection (beg end)
+ "Quotes the selection with double quotes, doubles embedded quotes"
+ (interactive `(,(region-beginning) ,(region-end)))
+ (if (not mark-active)
+ (error "Command requires a marked region"))
+ (goto-char beg)
+ (while (re-search-forward "\"" end t)
+ (replace-match "\"\"")(setq end (1+ end)))
+ (goto-char beg)
+ (while (re-search-forward "`'" end t)
+ (replace-match "```")(setq end (+ 2 end)))
+ (goto-char beg)
+ (insert "\"")
+ (setq end (1+ end))
+ (goto-char end)
+ (insert "\""))
+
+(defun powershell-dollarparen-selection (beg end)
+ "Wraps the selected text with $() leaving point after closing paren."
+ (interactive `(,(region-beginning) ,(region-end)))
+ (if (not mark-active)
+ (error "Command requires a marked region"))
+ (save-excursion
+ (goto-char end)
+ (insert ")")
+ (goto-char beg)
+ (insert "$("))
+ (forward-char))
+
+(defun powershell-regexp-to-regex (beg end)
+ "Turns the selected string (assumed to be regexp-opt output) into a regex"
+ (interactive `(,(region-beginning) ,(region-end)))
+ (if (not mark-active)
+ (error "Command requires a marked region"))
+ (save-restriction
+ (narrow-to-region beg end)
+ (goto-char (point-min))
+ (while (re-search-forward "\\\\(" nil t)
+ (replace-match "("))
+ (goto-char (point-min))
+ (while (re-search-forward "\\\\)" nil t)
+ (replace-match ")"))
+ (goto-char (point-min))
+ (while (re-search-forward "\\\\|" nil t)
+ (replace-match "|"))))
+
+
+;; Taken from About_Keywords
+(defvar powershell-keywords
+ (concat "\\_<"
+ (regexp-opt
+ '("begin" "break" "catch"
+ "continue" "data" "do"
+ "default" "dynamicparam" "else"
+ "elseif" "end" "exit"
+ "filter" "finally" "for"
+ "foreach" "from" "function"
+ "if" "in" "param"
+ "process" "return" "switch"
+ "throw" "trap" "try"
+ "until" "where" "while" ) t)
+ "\\_>")
+ "Powershell keywords")
+
+;; Taken from About_Comparison_Operators and some questionable sources :-)
+(defvar powershell-operators
+ (concat "\\_<"
+ (regexp-opt
+ '("-eq" "-ne" "-gt" "-ge" "-lt" "-le"
+ "-ceq" "-cne" "-cgt" "-cge" "-clt" "-cle" ;; case sensitive
versions
+ "-ieq" "-ine" "-igt" "-ige" "-ilt" "-ile" ;; explicitly case
insensitive
+ "-band" "-bor" "-bxor"
+ "-and" "-or" "-xor"
+ "-like" "-notlike" "-clike" "-cnotlike" "-ilike" "-inotlike"
+ "-match" "-notmatch" "-cmatch" "-cnotmatch" "-imatch" "-inotmatch"
+ "-contains" "-notcontains" "-ccontains" "-cnotcontains"
"-icontains" "-inotcontains"
+ "-replace" "-creplace" "-ireplace"
+ "-is" "-as" "-f"
+ ;; Questionable --> specific to certain contexts
+ "-casesensitive" "-wildcard" "-regex" "-exact" ;specific to case
+ "-begin" "-process" "-end" ;specific to scriptblock
+ ) t)
+ "\\_>")
+ "Powershell operators")
+
+(defvar powershell-scope-names
+ '("global" "local" "private" "script" )
+ "Names of scopes in Powershell mode.")
+
+(defvar powershell-variable-drive-names
+ (append '("env" "function" "variable" "alias" )
powershell-scope-names)
+ "Names of scopes in Powershell mode.")
+
+(defconst powershell-variables-regexp
+ ;; There are 2 syntaxes detected: ${[scope:]name} and $[scope:]name
+ ;; Match 0 is the entire variable name.
+ ;; Match 1 is scope when the former syntax is found.
+ ;; Match 2 is scope when the latter syntax is found.
+ (concat
+ "\\_<$\\(?:{\\(?:" (regexp-opt powershell-variable-drive-names t)
":\\)?[^}]+}\\|"
+ "\\(?:" (regexp-opt powershell-variable-drive-names t)
":\\)?[a-zA-Z0-9_]+\\_>\\)")
+ "Identifies legal powershell variable names")
+
+(defconst powershell-function-names-regex
+ ;; Syntax detected is [scope:]verb-noun
+ ;; Match 0 is the entire name.
+ ;; Match 1 is the scope if any.
+ ;; Match 2 is the function name (which must exist)
+ (concat
+ "\\_<\\(?:" (regexp-opt powershell-scope-names t) ":\\)?"
+ "\\([A-Z][a-zA-Z0-9]*-[A-Z0-9][a-zA-Z0-9]*\\)\\_>")
+ "Identifies legal function & filter names")
+
+(defconst powershell-object-types-regexp
+ ;; Syntax is \[name[.name]\] (where the escaped []s are literal)
+ ;; Only Match 0 is returned.
+ "\\[\\(?:[a-zA-Z_][a-zA-Z0-9]*\\)\\(?:\\.[a-zA-Z_][a-zA-Z0-9]*\\)*\\]"
+ "Identifies object type references. I.E. [object.data.type] syntax")
+
+(defconst powershell-function-switch-names-regexp
+ ;; Only Match 0 is returned.
+ "\\_<-[a-zA-Z][a-zA-Z0-9]*\\_>"
+ "Identifies function parameter names of the form -xxxx")
+
+;; Taken from Get-Variable on a fresh shell, merged with man
+;; about_automatic_variables
+(defvar powershell-builtin-variables-regexp
+ (regexp-opt
+ '("$" "?"
+ "^" "_"
+ "args" "ConsoleFileName"
+ "Error" "Event"
+ "EventSubscriber" "ExecutionContext"
+ "false" "Foreach"
+ "HOME" "Host"
+ "input" "LASTEXITCODE"
+ "Matches" "MyInvocation"
+ "NestedPromptLevel" "null"
+ "PID" "PROFILE"
+ "PSBoundParameters" "PSCmdlet"
+ "PSCulture" "PSDebugContext"
+ "PSHOME" "PSScriptRoot"
+ "PSUICulture" "PSVersionTable"
+ "PWD" "ReportErrorShowExceptionClass"
+ "ReportErrorShowInnerException" "ReportErrorShowSource"
+ "ReportErrorShowStackTrace" "Sender"
+ "ShellId" "SourceArgs"
+ "SourceEventArgs" "StackTrace"
+ "this" "true" ) t)
+ "Names of the built-in Powershell variables. They are hilighted
+differently from the other variables.")
+
+(defvar powershell-config-variables-regexp
+ (regexp-opt
+ '("ConfirmPreference" "DebugPreference"
+ "ErrorActionPreference" "ErrorView"
+ "FormatEnumerationLimit" "LogCommandHealthEvent"
+ "LogCommandLifecycleEvent" "LogEngineHealthEvent"
+ "LogEngineLifecycleEvent" "LogProviderHealthEvent"
+ "LogProviderLifecycleEvent" "MaximumAliasCount"
+ "MaximumDriveCount" "MaximumErrorCount"
+ "MaximumFunctionCount" "MaximumHistoryCount"
+ "MaximumVariableCount" "OFS"
+ "OutputEncoding" "ProgressPreference"
+ "PSEmailServer" "PSSessionApplicationName"
+ "PSSessionConfigurationName" "PSSessionOption"
+ "VerbosePreference" "WarningPreference"
+ "WhatIfPreference" ) t)
+ "Names of variables that configure powershell features.")
+
+
+(defun powershell-find-syntactic-comments (limit)
+ "Finds PowerShell comment begin and comment end characters.
+Returns match 1 and match 2 for <# #> comment sequences respectively.
+Returns match 3 and optionally match 4 for #/eol comments.
+Match 4 is returned only if eol is found before LIMIT"
+ (when (search-forward "#" limit t)
+ (cond
+ ((looking-back "<#")
+ (set-match-data (list (match-beginning 0) (1+ (match-beginning 0))
+ (match-beginning 0) (1+ (match-beginning 0)))))
+ ((looking-at ">")
+ (set-match-data (list (match-beginning 0) (match-end 0)
+ nil nil
+ (match-beginning 0) (match-end 0)))
+ (forward-char))
+ (t
+ (let ((start (point)))
+ (if (search-forward "\n" limit t)
+ (set-match-data (list (1- start) (match-end 0)
+ nil nil nil nil
+ (1- start) start
+ (match-beginning 0) (match-end 0)))
+ (set-match-data (list start (match-end 0)
+ nil nil nil nil
+ (1- start) start))))))
+ t))
+
+(defun powershell-find-syntactic-quotes (limit)
+ "Finds PowerShell hear string begin and end sequences.
+Returns match 1 and match 2 for @' '@ sequences respectively.
+Returns match 3 and match 4 for @\" \"@ sequences respectively."
+ (when (search-forward "@" limit t)
+ (cond
+ ((looking-at "'$")
+ (set-match-data (list (match-beginning 0) (1+ (match-beginning 0))
+ (match-beginning 0) (1+ (match-beginning 0))))
+ (forward-char))
+ ((looking-back "^'@")
+ (set-match-data (list (1- (match-end 0)) (match-end 0)
+ nil nil
+ (1- (match-end 0)) (match-end 0))))
+ ((looking-at "\"$")
+ (set-match-data (list (match-beginning 0) (1+ (match-beginning 0))
+ nil nil
+ nil nil
+ (match-beginning 0) (1+ (match-beginning 0))))
+ (forward-char))
+ ((looking-back "^\"@")
+ (set-match-data (list (1- (match-end 0)) (match-end 0)
+ nil nil
+ nil nil
+ nil nil
+ (1- (match-end 0)) (match-end 0)))))
+ t))
+(defvar powershell-font-lock-syntactic-keywords
+ `((powershell-find-syntactic-comments (1 "!" t t) (2 "!" t t)
+ (3 "<" t t) (4 ">" t t))
+ (powershell-find-syntactic-quotes (1 "|" t t) (2 "|" t t)
+ (3 "|" t t) (4 "|" t t)))
+ "A list of regexp's or functions. Used to add syntax-table properties to
+characters that can't be set by the syntax-table alone.")
+
+
+(defvar powershell-font-lock-keywords-1
+ `( ;; Type annotations
+ (,powershell-object-types-regexp . font-lock-type-face)
+ ;; syntaxic keywords
+ (,powershell-keywords . font-lock-keyword-face)
+ ;; operators
+ (,powershell-operators . font-lock-builtin-face)
+ ;; the REQUIRES mark
+ ("^#\\(REQUIRES\\)" 1 font-lock-warning-face t))
+ "Keywords for the first level of font-locking in Powershell mode.")
+
+(defvar powershell-font-lock-keywords-2
+ (append
+ powershell-font-lock-keywords-1
+ `( ;; Built-in variables
+ (,(concat "\\$\\(" powershell-builtin-variables-regexp "\\)\\>")
+ 0 font-lock-builtin-face t)
+ (,(concat "\\$\\(" powershell-config-variables-regexp "\\)\\>")
+ 0 font-lock-builtin-face t)))
+ "Keywords for the second level of font-locking in Powershell mode.")
+
+(defvar powershell-font-lock-keywords-3
+ (append
+ powershell-font-lock-keywords-2
+ `( ;; user variables
+ (,powershell-variables-regexp
+ (0 font-lock-variable-name-face)
+ (1 (cons font-lock-type-face '(underline)) t t)
+ (2 (cons font-lock-type-face '(underline)) t t))
+ ;; function argument names
+ (,powershell-function-switch-names-regexp
+ (0 font-lock-reference-face)
+ (1 (cons font-lock-type-face '(underline)) t t)
+ (2 (cons font-lock-type-face '(underline)) t t))
+ ;; function names
+ (,powershell-function-names-regex
+ (0 font-lock-function-name-face)
+ (1 (cons font-lock-type-face '(underline)) t t))))
+ "Keywords for the maximum level of font-locking in Powershell mode.")
+
+
+(defun powershell-setup-font-lock ()
+ "Sets up the buffer local value for font-lock-defaults"
+ ;; I use font-lock-syntactic-keywords to set some properties and I
+ ;; don't want them ignored.
+ (set (make-local-variable 'parse-sexp-lookup-properties) t)
+ ;; This is where all the font-lock stuff actually gets set up. Once
+ ;; font-lock-defaults has its value, setting font-lock-mode true should
+ ;; cause all your syntax highlighting dreams to come true.
+ (setq font-lock-defaults
+ ;; The first value is all the keyword expressions.
+ '((powershell-font-lock-keywords-1
+ powershell-font-lock-keywords-2
+ powershell-font-lock-keywords-3)
+ ;; keywords-only means no strings or comments get fontified
+ nil
+ ;; case-fold (t ignores case)
+ t
+ ;; syntax-alist nothing special here
+ nil
+ ;; syntax-begin - no function defined to move outside syntactic block
+ nil
+ ;; font-lock-syntactic-keywords
+ ;; takes (matcher (match syntax override lexmatch) ...)...
+ (font-lock-syntactic-keywords .
powershell-font-lock-syntactic-keywords))))
+
+(defvar powershell-mode-syntax-table
+ (let ((powershell-mode-syntax-table (make-syntax-table)))
+ (modify-syntax-entry ?$ "_" powershell-mode-syntax-table)
+ (modify-syntax-entry ?: "_" powershell-mode-syntax-table)
+ (modify-syntax-entry ?- "_" powershell-mode-syntax-table)
+ (modify-syntax-entry ?^ "_" powershell-mode-syntax-table)
+ (modify-syntax-entry ?\\ "_" powershell-mode-syntax-table)
+ (modify-syntax-entry ?{ "(}" powershell-mode-syntax-table)
+ (modify-syntax-entry ?} "){" powershell-mode-syntax-table)
+ (modify-syntax-entry ?[ "(]" powershell-mode-syntax-table)
+ (modify-syntax-entry ?] ")[" powershell-mode-syntax-table)
+ (modify-syntax-entry ?( "()" powershell-mode-syntax-table)
+ (modify-syntax-entry ?) ")(" powershell-mode-syntax-table)
+ (modify-syntax-entry ?` "\\" powershell-mode-syntax-table)
+ (modify-syntax-entry ?_ "w" powershell-mode-syntax-table)
+ (modify-syntax-entry ?= "." powershell-mode-syntax-table)
+ (modify-syntax-entry ?| "." powershell-mode-syntax-table)
+ (modify-syntax-entry ?+ "." powershell-mode-syntax-table)
+ (modify-syntax-entry ?* "." powershell-mode-syntax-table)
+ (modify-syntax-entry ?/ "." powershell-mode-syntax-table)
+ (modify-syntax-entry ?' "\"" powershell-mode-syntax-table)
+ (modify-syntax-entry ?# "<" powershell-mode-syntax-table)
+ powershell-mode-syntax-table)
+ "Syntax for PowerShell major mode")
+
+(defvar powershell-mode-map
+ (let ((powershell-mode-map (make-keymap)))
+ ;; (define-key powershell-mode-map "\r" 'powershell-indent-line)
+ (define-key powershell-mode-map "\t" 'powershell-indent-line)
+ (define-key powershell-mode-map (kbd "M-\"")
'powershell-doublequote-selection)
+ (define-key powershell-mode-map (kbd "M-'") 'powershell-quote-selection)
+ (define-key powershell-mode-map (kbd "C-'") 'powershell-unquote-selection)
+ (define-key powershell-mode-map (kbd "C-\"") 'powershell-unquote-selection)
+ (define-key powershell-mode-map (kbd "M-`") 'powershell-escape-selection)
+ (define-key powershell-mode-map (kbd "C-$")
'powershell-dollarparen-selection)
+ powershell-mode-map)
+ "Keymap for PS major mode")
+
+(defun powershell-setup-menu ()
+ "Adds a menu of PowerShell specific functions to the menu bar."
+ (define-key (current-local-map) [menu-bar powershell-menu]
+ (cons "PowerShell" (make-sparse-keymap "PowerShell")))
+ (define-key (current-local-map) [menu-bar powershell-menu doublequote]
+ '(menu-item "DoubleQuote Selection" powershell-doublequote-selection
+ :key-sequence(kbd "M-\"")
+ :help "DoubleQuotes the selection escaping embedded double
quotes"))
+ (define-key (current-local-map) [menu-bar powershell-menu quote]
+ '(menu-item "SingleQuote Selection" powershell-quote-selection
+ :key-sequence (kbd "M-'")
+ :help "SingleQuotes the selection escaping embedded single
quotes"))
+ (define-key (current-local-map) [menu-bar powershell-menu unquote]
+ '(menu-item "UnQuote Selection" powershell-unquote-selection
+ :key-sequence (kbd "C-'")
+ :help "Un-Quotes the selection un-escaping any escaped
quotes"))
+ (define-key (current-local-map) [menu-bar powershell-menu escape]
+ '(menu-item "Escape Selection" powershell-escape-selection
+ :key-sequence (kbd "M-`")
+ :help "Escapes variables in the selection and extends existing
escapes."))
+ (define-key (current-local-map) [menu-bar powershell-menu dollarparen]
+ '(menu-item "DollarParen Selection" powershell-dollarparen-selection
+ :key-sequence (kbd "C-$")
+ :help "Wraps the selection in $()")))
+
+
+;;; Eldoc support
+
+(eval-when-compile (require 'thingatpt))
+(defcustom powershell-eldoc-def-files nil
+ "List of files containing function help strings used by `eldoc-mode'.
+These are the strings eldoc-mode displays as help for functions near point.
+The format of the file must be exactly as follows or who knows what happens.
+
+ (set (intern \"<fcn-name1>\" powershell-eldoc-obarray) \"<helper string1>\")
+ (set (intern \"<fcn-name2>\" powershell-eldoc-obarray) \"<helper string2>\")
+...
+
+Where <fcn-name> is the name of the function to which <helper string> applies.
+ <helper-string> is the string to display when point is near <fcn-name>."
+ :type '(repeat string)
+ :group 'powershell)
+
+(defvar powershell-eldoc-obarray ()
+ "Array into which powershell-eldoc-def-files entries are added for use by
eldoc.")
+
+(defun powershell-eldoc-function ()
+ "Returns a documentation string appropriate for the current context or nil"
+ (let ((word (thing-at-point 'symbol)))
+ (if word
+ (eval (intern-soft word powershell-eldoc-obarray)))))
+
+(defun powershell-setup-eldoc ()
+ "Loads the function documentation for use with eldoc."
+ (when (not (null powershell-eldoc-def-files))
+ (set (make-local-variable 'eldoc-documentation-function)
+ 'powershell-eldoc-function)
+ (unless (vectorp powershell-eldoc-obarray)
+ (setq powershell-eldoc-obarray (make-vector 41 0))
+ (condition-case var (mapc 'load powershell-eldoc-def-files)
+ (error (message "*** powershell-setup-eldoc ERROR *** %s" var))))))
+;;; Note: You can create quite a bit of help with these commands:
+;;
+;; function Get-Signature ($Cmd) {
+;; if ($Cmd -is [Management.Automation.PSMethod]) {
+;; $List = @($Cmd)}
+;; elseif ($Cmd -isnot [string]) {
+;; throw ("Get-Signature {<method>|<command>}`n" +
+;; "'$Cmd' is not a method or command")}
+;; else {$List = @(Get-Command $Cmd -ErrorAction SilentlyContinue)}
+;; if (!$List[0] ) {
+;; throw "Command '$Cmd' not found"}
+;; foreach ($O in $List) {
+;; switch -regex ($O.GetType().Name) {
+;; 'AliasInfo' {
+;; Get-Signature ($O.Definition)}
+;; '(Cmdlet|ExternalScript)Info' {
+;; $O.Definition} # not sure what to do with ExternalScript
+;; 'F(unction|ilter)Info'{
+;; if ($O.Definition -match '^param *\(') {
+;; $t = [Management.Automation.PSParser]::tokenize($O.Definition,
+;; [ref]$null)
+;; $c = 1;$i = 1
+;; while($c -and $i++ -lt $t.count) {
+;; switch ($t[$i].Type.ToString()) {
+;; GroupStart {$c++}
+;; GroupEnd {$c--}}}
+;; $O.Definition.substring(0,$t[$i].start + 1)} #needs parsing
+;; else {$O.Name}}
+;; 'PSMethod' {
+;; foreach ($t in @($O.OverloadDefinitions)) {
+;; while (($b=$t.IndexOf('`1[[')) -ge 0) {
+;; $t=$t.remove($b,$t.IndexOf(']]')-$b+2)}
+;; $t}}}}}
+;; get-command|
+;; ?{$_.CommandType -ne 'Alias' -and $_.Name -notlike '*:'}|
+;; %{$_.Name}|
+;; sort|
+;; %{("(set (intern ""$($_.Replace('\','\\'))"" powershell-eldoc-obarray)" +
+;; " ""$(Get-Signature $_|%{$_.Replace('\','\\').Replace('"','\"')})"")"
+;; ).Replace("`r`n"")",""")")} > .\powershell-eldoc.el
+
+
+(defvar powershell-imenu-expression
+ `(("Functions" ,(concat "function " powershell-function-names-regex) 2)
+ ("Filters" ,(concat "filter " powershell-function-names-regex) 2)
+ ("Top variables"
+ , (concat "^\\(" powershell-object-types-regexp "\\)?\\("
+ powershell-variables-regexp "\\)\\s-*=")
+ 2))
+ "List of regexps matching important expressions, for speebar & imenu.")
+
+(defun powershell-setup-imenu ()
+ "Installs powershell-imenu-expression."
+ (when (require 'imenu nil t)
+ ;; imenu doc says these are buffer-local by default
+ (setq imenu-generic-expression powershell-imenu-expression)
+ (setq imenu-case-fold-search nil)
+ (imenu-add-menubar-index)
+ (when (require 'which-func nil t)
+ (which-function-mode t))))
+
+(eval-when-compile (require 'speedbar))
+(if (require 'speedbar nil t)
+ (speedbar-add-supported-extension ".ps1?"))
+
+(require 'compile nil t)
+;; A better command would be something like "powershell.exe -NoLogo
+;; -NonInteractive -Command & (buffer-file-name)". But it will just
+;; sit there waiting... The following will only work when .ps1 files
+;; are associated with powershell.exe. And if they don't contain spaces.
+(defvar powershell-compile-command
+ '(buffer-file-name)
+ "Default command used to invoke a powershell script")
+
+;; The column number will be off whenever tabs are used. Since this is
+;; the default in this mode, we will not capture the column number.
+(setq compilation-error-regexp-alist
+ (cons '("At \\(.*\\):\\([0-9]+\\) char:\\([0-9]+\\)" 1 2)
+ compilation-error-regexp-alist))
+
+
+;; the hook is automatically run by derived-mode
+(defvar powershell-mode-hook '(imenu-add-menubar-index)
+ "Hook run after the initialization of Powershell mode.")
+
+(defun powershell-mode ()
+ "Major mode for editing PowerShell scripts."
+ (interactive)
+ (kill-all-local-variables)
+ (setq major-mode 'powershell-mode)
+ (setq mode-name "PS")
+ (set-syntax-table powershell-mode-syntax-table)
+ (use-local-map powershell-mode-map)
+ (powershell-setup-font-lock)
+ (set (make-local-variable 'indent-line-function) 'powershell-indent-line)
+ (set (make-local-variable 'compile-command) powershell-compile-command)
+ (set (make-local-variable 'comment-start) "#")
+ (set (make-local-variable 'comment-start-skip) "#+\\s*")
+ (set (make-local-variable 'parse-sexp-ignore-comments) t)
+ (powershell-setup-imenu)
+ (powershell-setup-menu)
+ (powershell-setup-eldoc)
+ (run-hooks 'powershell-mode-hook))
+
+(provide 'powershell-mode)
+
+;;; end of powershell-mode.el
diff --git a/powershell.el b/powershell.el
new file mode 100644
index 00000000000..ed89a2d08ed
--- /dev/null
+++ b/powershell.el
@@ -0,0 +1,156 @@
+;; powershell.el, version 0.1
+;;
+;; Author: Dino Chiesa
+;; Thu, 10 Apr 2008 11:10
+;;
+;; Run Windows PowerShell v1.0 as an inferior shell within emacs. Tested with
emacs v22.2.
+;;
+;; TODO:
+;; test what happens when you expand the window size beyond the
maxWindowWidth for the RawUI
+;; make everything configurable (Powershell exe, initial args, powershell
prompt regexp)
+;; implement powershell launch hooks
+;; prevent backspace from deleting the powershell prompt? (do other shells do
this?)
+;;
+
+(require 'shell)
+
+
+(defun powershell-gen-window-width-string ()
+ (concat "$a = (Get-Host).UI.RawUI\n"
+ "$b = $a.WindowSize\n"
+ "$b.Width = " (number-to-string (window-width)) "\n"
+ "$a.BufferSize = $b\n"
+ "$a.WindowSize = $b")
+ )
+
+
+(defvar powershell-prompt-pattern "PS [^#$%>]+>"
+ "Regexp for powershell prompt. This isn't really used, because I couldn't
figure out how to get it to work."
+ )
+
+(defgroup powershell nil
+ "Running shell from within Emacs buffers."
+ :group 'processes
+ )
+
+
+(defcustom powershell-need-rawui-resize t
+ "set when powershell needs to be resized"
+ :group 'powershell
+)
+
+;;;###autoload
+(defun powershell (&optional buffer)
+ "Run an inferior powershell, by invoking the shell function. See the help
for shell for more details.
+\(Type \\[describe-mode] in the shell buffer for a list of commands.)"
+ (interactive
+ (list
+ (and current-prefix-arg
+ (read-buffer "Shell buffer: "
+ (generate-new-buffer-name "*PowerShell*")))))
+ ; get a name for the buffer
+ (setq buffer (get-buffer-create (or buffer "*PowerShell*")))
+
+ (let (
+ (tmp-shellfile explicit-shell-file-name)
+ )
+ ; set arguments for the powershell exe.
+ ; This needs to be tunable.
+ (setq explicit-shell-file-name
"c:\\windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe")
+ (setq explicit-powershell.exe-args '("-Command" "-" )) ; interactive, but
no command prompt
+
+ ; launch the shell
+ (shell buffer)
+
+ ; restore the original shell
+ (if explicit-shell-file-name
+ (setq explicit-shell-file-name tmp-shellfile)
+ )
+ )
+
+ (let (
+ (proc (get-buffer-process buffer))
+ )
+
+ ; This sets up the powershell RawUI screen width. By default,
+ ; the powershell v1.0 assumes terminal width of 80 chars.
+ ;This means input gets wrapped at the 80th column. We reset the
+ ; width of the PS terminal to the window width.
+ (add-hook 'window-size-change-functions 'powershell-window-size-changed)
+
+ (powershell-window-size-changed)
+
+ ; ask for initial prompt
+ (comint-simple-send proc "prompt")
+ )
+
+ ; hook the kill-buffer action so we can kill the inferior process?
+ (add-hook 'kill-buffer-hook 'powershell-delete-process)
+
+ ; wrap the comint-input-sender with a PS version
+ ; must do this after launching the shell!
+ (make-local-variable 'comint-input-sender)
+ (setq comint-input-sender 'powershell-simple-send)
+
+ ; set a preoutput filter for powershell. This will trim newlines after the
prompt.
+ (add-hook 'comint-preoutput-filter-functions
'powershell-preoutput-filter-for-prompt)
+
+ ;(run-hooks 'powershell-launch-hook)
+
+ ; return the buffer created
+ buffer
+)
+
+
+(defun powershell-window-size-changed (&optional frame)
+ ; do not actually resize here. instead just set a flag.
+ (setq powershell-need-rawui-resize t)
+)
+
+
+
+(defun powershell-delete-process (&optional proc)
+ (or proc
+ (setq proc (get-buffer-process (current-buffer))))
+ (and (processp proc)
+ (delete-process proc))
+ )
+
+
+
+;; This function trims the newline from the prompt that we
+;; get back from powershell. It is set into the preoutput
+;; filters, so the newline is trimmed before being put into
+;; the output buffer.
+(defun powershell-preoutput-filter-for-prompt (string)
+ (if
+ ; not sure why, but I have not succeeded in using a variable here???
+ ;(string-match powershell-prompt-pattern string)
+
+ (string-match "PS [^#$%>]+>" string)
+ (substring string 0 -1)
+
+ string
+
+ )
+ )
+
+
+
+(defun powershell-simple-send (proc string)
+ "Override of the comint-simple-send function, specific for powershell.
+This just sends STRING, plus the prompt command. Normally powershell is in
+noninteractive model when run as an inferior shell with stdin/stdout
+redirected, which is the case when running as a shell within emacs.
+This function insures we get and display the prompt. "
+ ; resize if necessary. We do this by sending a resize string to the shell,
+ ; before sending the actual command to the shell.
+ (if powershell-need-rawui-resize
+ (and
+ (comint-simple-send proc (powershell-gen-window-width-string))
+ (setq powershell-need-rawui-resize nil)
+ )
+ )
+ (comint-simple-send proc string)
+ (comint-simple-send proc "prompt")
+)