From 08b1c963d817b251ecb939d1989d8231442d4b90 Mon Sep 17 00:00:00 2001 From: John Doty Date: Mon, 30 Mar 2015 16:44:31 -0700 Subject: [PATCH] Powershell Mode Missing Add the files we need for local powershell mode --- .emacs.d/core.el | 2 - site-lisp/powershell-mode.el | 707 +++++++++++++++++++++++++++++++++++ 2 files changed, 707 insertions(+), 2 deletions(-) create mode 100644 site-lisp/powershell-mode.el diff --git a/.emacs.d/core.el b/.emacs.d/core.el index 4956c62..f141c8a 100644 --- a/.emacs.d/core.el +++ b/.emacs.d/core.el @@ -40,7 +40,6 @@ ;; ================================================================= ;; Various bits of path setup (init-dir is defined by init.el) ;; ================================================================= -(add-to-list 'load-path init-dir) (setq autoload-file (concat init-dir "loaddefs.el")) (setq package-user-dir (concat init-dir "elpa")) (setq custom-file (concat init-dir "custom.el")) @@ -79,7 +78,6 @@ 'color-theme-solarized ; ...Solarized 'csharp-mode ; C# mode 'js2-mode ; Improved JS mode - 'powershell-mode ; Powershell mode 'lua-mode ; LUA 'go-mode ; Go programming language mode 'flyspell ; Spell-checking diff --git a/site-lisp/powershell-mode.el b/site-lisp/powershell-mode.el new file mode 100644 index 0000000..a014f7c --- /dev/null +++ b/site-lisp/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 +;; 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 . + +;;; 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 (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 \"\" powershell-eldoc-obarray) \"\") + (set (intern \"\" powershell-eldoc-obarray) \"\") +... + +Where is the name of the function to which applies. + is the string to display when point is near ." + :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 {|}`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 +