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")
+)


Reply via email to