Some local changes
This commit is contained in:
parent
85d3c38b00
commit
2a4c6969ce
3 changed files with 605 additions and 129 deletions
|
|
@ -1,8 +1,10 @@
|
|||
;;; powershell-mode.el --- Mode for editing Powershell scripts
|
||||
|
||||
;; Copyright (C) 2009, 2010 Frédéric Perrin
|
||||
;; 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>
|
||||
;; 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
|
||||
|
|
@ -18,29 +20,48 @@
|
|||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Comment:
|
||||
;; This is still WIP.
|
||||
;;
|
||||
;;; 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.
|
||||
;;
|
||||
;; This is still pretty basic: there is indentation, syntax
|
||||
;; hilighting, speedbar/imenu support. The indentation is pretty naïve
|
||||
;; but robust, and sufficient for my current needs.
|
||||
;;; 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
|
||||
|
||||
(setq debug-on-error t)
|
||||
;; 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 )
|
||||
|
||||
(defvar powershell-indent 4
|
||||
(defcustom powershell-indent 4
|
||||
"Amount of horizontal space to indent after, for instance, an
|
||||
opening brace")
|
||||
opening brace"
|
||||
:type 'integer
|
||||
:group 'powershell)
|
||||
|
||||
(defvar powershell-continuation-indent 2
|
||||
"Amount of horizontal space to indent a continuation line")
|
||||
(defcustom powershell-continuation-indent 2
|
||||
"Amount of horizontal space to indent a continuation line"
|
||||
:type 'integer
|
||||
:group 'powershell)
|
||||
|
||||
(defvar powershell-continued-regexp ".*\\(|[\\t ]*\\|`\\)$"
|
||||
(defcustom powershell-continued-regexp ".*\\(|[\\t ]*\\|`\\)$"
|
||||
"Regexp matching a continued line (ending either with an
|
||||
explicit backtick, or with a pipe).")
|
||||
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
|
||||
|
|
@ -50,103 +71,375 @@ previous line is a continued line, ending with a backtick or a pipe"
|
|||
(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)
|
||||
(beginning-of-line)
|
||||
(let ((closing-paren (looking-at "[\t ]*[])}]")))
|
||||
;; a very simple indentation method: if on a continuation line (i.e. the
|
||||
;; previous line ends with a trailing backtick or pipe), we indent relative
|
||||
;; to the continued line; otherwise, we indent relative to the ([{ that
|
||||
;; opened the current block.
|
||||
(save-excursion
|
||||
(beginning-of-line)
|
||||
(if (powershell-continuation-line-p)
|
||||
(progn
|
||||
(while (powershell-continuation-line-p)
|
||||
(forward-line -1))
|
||||
(+ (current-indentation) powershell-continuation-indent))
|
||||
(condition-case nil
|
||||
(progn
|
||||
(backward-up-list)
|
||||
;; indentation relative to the opening paren: if there is text (no
|
||||
;; comment) after the opening paren, vertically align the block
|
||||
;; with this text; if we were looking at the closing paren, reset
|
||||
;; the indentation; otherwise, indent the block by powershell-indent.
|
||||
(cond ((not (looking-at ".[\t ]*\\(#.*\\)?$"))
|
||||
(forward-char)
|
||||
(skip-chars-forward " \t")
|
||||
(current-column))
|
||||
(closing-paren
|
||||
(current-indentation))
|
||||
(t
|
||||
(+ (current-indentation) powershell-indent))))
|
||||
(scan-error ;; most likely, we are at the top-level
|
||||
0)))))
|
||||
;; 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 (save-excursion (powershell-indent-line-amount))))
|
||||
(amount (powershell-indent-line-amount)))
|
||||
(if savep
|
||||
(save-excursion (indent-line-to amount))
|
||||
(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 "'"))
|
||||
|
||||
|
||||
;; Taken from <http://www.manning.com/payette/AppCexcerpt.pdf> which seems the
|
||||
;; closest to a grammar definition for powershell. It is not complete, and
|
||||
;; contains some inaccuracies (e.g. it says that variables match \$[:alnum:]+,
|
||||
;; so $_ is not a variable it seems...)
|
||||
(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
|
||||
(regexp-opt '("begin" "break" "catch" "continue" "data" "do" "dynamicparam"
|
||||
"else" "elseif" "end" "exit" "filter" "finally" "for" "foreach"
|
||||
"from" "function" "if" "in" "param" "process" "return"
|
||||
"switch" "throw" "trap" "try" "until" "while"))
|
||||
(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
|
||||
(regexp-opt '("and" "as" "band" "bnot" "bor" "bxor" "casesensitive"
|
||||
"ccontains" "ceq" "cge" "cgt" "cle" "clike" "clt" "cmatch"
|
||||
"cne" "cnotcontains" "cnotlike" "cnotmatch" "contains"
|
||||
"creplace" "eq" "exact" "f" "file" "ge" "gt" "icontains"
|
||||
"ieq" "ige" "igt" "ile" "ilike" "ilt" "imatch" "ine"
|
||||
"inotcontains" "inotlike" "inotmatch" "ireplace" "is"
|
||||
"isnot" "le" "like" "lt" "match" "ne" "not" "notcontains"
|
||||
"notlike" "notmatch" "or" "replace" "wildcard"))
|
||||
(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
|
||||
(regexp-opt
|
||||
'("env" "function" "global" "local" "private" "script" "variable"))
|
||||
'("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
|
||||
(defvar powershell-builtin-variables-regexp
|
||||
(regexp-opt
|
||||
'("^" "_" "$" "?" "Args" "ConfirmPreference" "ConsoleFileName"
|
||||
"DebugPreference" "Error" "ErrorActionPreference" "ErrorView"
|
||||
"ExecutionContext" "foreach" "FormatEnumerationLimit" "HOME" "Host"
|
||||
"Input" "LASTEXITCODE" "MaximumAliasCount" "MaximumDriveCount"
|
||||
"MaximumErrorCount" "MaximumFunctionCount" "MaximumHistoryCount"
|
||||
"MaximumVariableCount" "MyInvocation" "NestedPromptLevel" "OFS"
|
||||
"OutputEncoding" "PID" "PROFILE" "PSHOME" "PWD" "ProgressPreference"
|
||||
"ReportErrorShowExceptionClass" "ReportErrorShowInnerException"
|
||||
"ReportErrorShowSource" "ReportErrorShowStackTrace" "ShellId"
|
||||
"ShouldProcessPreference" "ShouldProcessReturnPreference" "StackTrace"
|
||||
"VerbosePreference" "WarningPreference" "WhatIfPreference" "false"
|
||||
"input" "lastWord" "line" "null" "true" ))
|
||||
'("$" "?"
|
||||
"^" "_"
|
||||
"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
|
||||
("\\[\\([[:word:].]+\\(\\[\\]\\)?\\)\\]" 1 font-lock-type-face)
|
||||
`( ;; Type annotations
|
||||
(,powershell-object-types-regexp . font-lock-type-face)
|
||||
;; syntaxic keywords
|
||||
(,(concat "\\<" powershell-keywords "\\>") . font-lock-keyword-face)
|
||||
(,powershell-keywords . font-lock-keyword-face)
|
||||
;; operators
|
||||
(,(concat "\\<-" powershell-operators "\\>") . font-lock-builtin-face)
|
||||
(,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.")
|
||||
|
|
@ -154,48 +447,219 @@ differently from the other variables.")
|
|||
(defvar powershell-font-lock-keywords-2
|
||||
(append
|
||||
powershell-font-lock-keywords-1
|
||||
`(;; Built-in variables
|
||||
(,(concat "\\$\\(" powershell-builtin-variables "\\)\\>")
|
||||
1 font-lock-builtin-face t)))
|
||||
`( ;; 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
|
||||
`(;; Variables in curly brackets
|
||||
("\\${\\([^}]+\\)}" 1 font-lock-variable-name-face)
|
||||
;; Variables, with a scope
|
||||
(,(concat "\\$\\(" powershell-scope-names "\\):"
|
||||
"\\([[:alnum:]_]+\\)")
|
||||
(1 (cons font-lock-type-face '(underline)))
|
||||
(2 font-lock-variable-name-face))
|
||||
;; Variables, without a scope. XXX: unify this with the
|
||||
;; previous rule?
|
||||
("\\$\\([[:alnum:]_]+\\)" 1 font-lock-variable-name-face)
|
||||
;; hilight properties, but not the methods (personnal preference)
|
||||
("\\.\\([[:alnum:]_.]+\\)\\>\\s *[^(]" 1 font-lock-variable-name-face)))
|
||||
`( ;; 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.")
|
||||
|
||||
|
||||
(defvar powershell-mode-syntax-table (make-syntax-table)
|
||||
"Syntax table for 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))))
|
||||
|
||||
(modify-syntax-entry ?# "<" powershell-mode-syntax-table)
|
||||
(modify-syntax-entry ?\n ">" powershell-mode-syntax-table)
|
||||
;; Powershell uses a backtick as its escape character.
|
||||
(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)
|
||||
(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" "function \\(\\w+\\)" 1)
|
||||
("Top variables" ,(concat "^\\$\\(" powershell-scope-names "\\)?:?"
|
||||
"\\([[:alnum:]_]+\\)")
|
||||
`(("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?"))
|
||||
|
||||
|
|
@ -212,29 +676,32 @@ differently from the other variables.")
|
|||
;; 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))
|
||||
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.")
|
||||
|
||||
(define-derived-mode powershell-mode fundamental-mode "PS"
|
||||
"A major mode for editing Powershell script files."
|
||||
(set (make-local-variable 'indent-line-function) 'powershell-indent-line)
|
||||
(set (make-local-variable 'font-lock-defaults)
|
||||
'((powershell-font-lock-keywords-1
|
||||
powershell-font-lock-keywords-2
|
||||
powershell-font-lock-keywords-3)
|
||||
nil t))
|
||||
(set (make-local-variable 'comment-start) "# ")
|
||||
(set (make-local-variable 'comment-start-skip) "#+\\s*")
|
||||
;; not sure why this is not the default
|
||||
(set (make-local-variable 'parse-sexp-ignore-comments) t)
|
||||
(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)
|
||||
(set (make-local-variable 'imenu-generic-expression)
|
||||
powershell-imenu-expression)
|
||||
(set (make-local-variable 'imenu-case-fold-search) nil)
|
||||
(set (make-local-variable 'compile-command) powershell-compile-command))
|
||||
(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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue