-
Notifications
You must be signed in to change notification settings - Fork 1
/
powershell-mode.el
240 lines (206 loc) · 9.82 KB
/
powershell-mode.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
;;; powershell-mode.el --- Mode for editing Powershell scripts
;; Copyright (C) 2009, 2010 Frédéric Perrin
;; 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/>.
;;; Comment:
;; This is still WIP.
;;
;; 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.
(setq debug-on-error t)
(defvar powershell-indent 8
"Amount of horizontal space to indent after, for instance, an
opening brace")
(defvar powershell-continuation-indent 4
"Amount of horizontal space to indent a continuation line")
(defvar powershell-continued-regexp ".*\\(|[\\t ]*\\|`\\)$"
"Regexp matching a continued line (ending either with an
explicit backtick, or with a pipe).")
(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)))
(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.
(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)))))
(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))))
(if savep
(save-excursion (indent-line-to amount))
(indent-line-to amount))))
;; 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...)
(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"))
"Powershell keywords")
(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"))
"Powershell operators")
(defvar powershell-scope-names
(regexp-opt
'("env" "function" "global" "local" "private" "script" "variable"))
"Names of scopes in Powershell mode.")
;; Taken from Get-Variable on a fresh shell, merged with man
;; about_automatic_variables
(defvar powershell-builtin-variables
(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" ))
"Names of the built-in Powershell variables. They are hilighted
differently from the other variables.")
(defvar powershell-font-lock-keywords-1
`(;; Type annotations
("\\[\\([[:word:].]+\\(\\[\\]\\)?\\)\\]" 1 font-lock-type-face)
;; syntaxic keywords
(,(concat "\\<" powershell-keywords "\\>") . font-lock-keyword-face)
;; operators
(,(concat "\\<-" 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 "\\)\\>")
1 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)))
"Keywords for the maximum level of font-locking in Powershell mode.")
(defvar powershell-mode-syntax-table (make-syntax-table)
"Syntax table for Powershell mode")
(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-imenu-expression
`(("Functions" "function \\(\\w+\\)" 1)
("Top variables" ,(concat "^\\$\\(" powershell-scope-names "\\)?:?"
"\\([[:alnum:]_]+\\)")
2))
"List of regexps matching important expressions, for speebar & imenu.")
(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.")
(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)
(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))
(provide 'powershell-mode)