Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1378 lines
55 KiB

  1. ;;; powershell.el --- Mode for editing Powershell scripts -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2009, 2010 Frédéric Perrin
  3. ;; Copyright (C) 2012 Richard Bielawski rbielaws-at-i1-dot-net
  4. ;; http://www.emacswiki.org/emacs/Rick_Bielawski
  5. ;; Author: Frédéric Perrin <frederic (dot) perrin (arobas) resel (dot) fr>
  6. ;; URL: http://github.com/jschaf/powershell.el
  7. ;; Package-Version: 0.1
  8. ;; Package-Commit: 7316f44d0b528552f5a0692f778e5f0efd964299
  9. ;; Version: 0.3
  10. ;; Package-Requires: ((emacs "24))
  11. ;; Keywords: powershell, languages
  12. ;; This file is NOT part of GNU Emacs.
  13. ;; This file is free software: you can redistribute it and/or modify
  14. ;; it under the terms of the GNU General Public License as published
  15. ;; by the Free Software Foundation, either version 3 of the License,
  16. ;; or (at your option) any later version.
  17. ;; GNU Emacs is distributed in the hope that it will be useful, but
  18. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20. ;; General Public License for more details.
  21. ;; You should have received a copy of the GNU General Public License
  22. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  23. ;;; Installation:
  24. ;; Place powershell.el on your `load-path' by adding the following
  25. ;; code to your `user-init-file', which is usually ~/.emacs.d/init.el
  26. ;; or ~/.emacs.
  27. ;;
  28. ;; (add-to-list 'load-path "~/path/to/powershell")
  29. ;;
  30. ;;; Commentary:
  31. ;; Powershell.el is a combination of powershell.el by Dino Chiesa
  32. ;; <dpchiesa@hotmail.com> and powershell-mode.el by Frédéric Perrin
  33. ;; and Richard Bielawski. Joe Schafer combined the work into a single
  34. ;; file.
  35. ;;; Frédéric Perrin Comments:
  36. ;;
  37. ;; The original powershell-mode.el was written from scratch, without
  38. ;; using Vivek Sharma's code: it had issues I wanted to correct, but
  39. ;; unfortunately there were no licence indication, and Vivek didn't
  40. ;; answered my mails.
  41. ;;
  42. ;;; Rick Bielawski Comments 2012/09/28:
  43. ;;
  44. ;; On March 31, 2012 Frédéric gave me permission to take over support
  45. ;; for powershell-mode.el. I've added support for multi-line comments
  46. ;; and here-strings as well as enhancement/features such as: Functions
  47. ;; to quote, unquote and escape a selection, and one to wrap a
  48. ;; selection in $(). Meanwhile I hope I didn't break anything.
  49. ;;
  50. ;; Joe Schafer Comments 2013-06-06:
  51. ;;
  52. ;; I combined powershell.el and powershell-mode.el. Since
  53. ;; powershell.el was licensed with the new BSD license I combined the
  54. ;; two files using the more restrictive license, the GPL. I also
  55. ;; cleaned up the documentation and reorganized some of the code.
  56. ;;; Updates:
  57. ;; 2012/10/01 Fixed several bugs in highlighting variables and types.
  58. ;; Renamed some variables to be more descriptive.
  59. ;; 2012/10/02 Enhanced PowerShell-mode indenting & syntax table.
  60. ;; Fixed dangling parens and re-indented the elisp itself.
  61. ;; 2012/10/05 Added eldoc support. Fixed bug where indent could loop.
  62. ;; See comment below on how to generate powershell-eldoc.el
  63. ;; 2013/06/06 Merged powershell.el and powershell-mode.el
  64. ;;; Code:
  65. (eval-when-compile (require 'thingatpt))
  66. (require 'shell)
  67. (require 'compile)
  68. ;;;###autoload
  69. (add-to-list 'auto-mode-alist (cons (purecopy "\\.ps1\\'") 'powershell-mode))
  70. ;; User Variables
  71. (defgroup powershell nil
  72. "Customization of PowerShell mode."
  73. :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
  74. :group 'languages)
  75. (defcustom powershell-indent 4
  76. "Amount of horizontal space to indent.
  77. After, for instance, an opening brace"
  78. :type 'integer
  79. :group 'powershell)
  80. (defcustom powershell-continuation-indent 2
  81. "Amount of horizontal space to indent a continuation line."
  82. :type 'integer
  83. :group 'powershell)
  84. (defcustom powershell-continued-regexp ".*\\(|[\\t ]*\\|`\\)$"
  85. "Regexp matching a continued line.
  86. Ending either with an explicit backtick, or with a pipe."
  87. :type 'integer
  88. :group 'powershell)
  89. ;; Note: There are no explicit references to the variable
  90. ;; `explicit-powershell.exe-args'. It is used implicitly by M-x shell
  91. ;; when the shell is `powershell.exe'. See
  92. ;; http://blogs.msdn.com/b/dotnetinterop/archive/2008/04/10/run-powershell-as-a-shell-within-emacs.aspx
  93. ;; for details.
  94. (defcustom explicit-powershell.exe-args '("-Command" "-" )
  95. "Args passed to inferior shell by \\[shell], if the shell is powershell.exe.
  96. Value is a list of strings, which may be nil."
  97. :type '(repeat (string :tag "Argument"))
  98. :group 'powershell)
  99. (defun powershell-continuation-line-p ()
  100. "Return t is the current line is a continuation line.
  101. The current line is a continued line when the previous line ends
  102. with a backtick or a pipe"
  103. (interactive)
  104. (save-excursion
  105. (forward-line -1)
  106. (looking-at powershell-continued-regexp)))
  107. ;; Rick added significant complexity to Frédéric's original version
  108. (defun powershell-indent-line-amount ()
  109. "Return the column to which the current line ought to be indented."
  110. (interactive)
  111. (save-excursion
  112. (beginning-of-line)
  113. (if (powershell-continuation-line-p)
  114. ;; on a continuation line (i.e. prior line ends with backtick
  115. ;; or pipe), indent relative to the continued line.
  116. (progn
  117. (while (and (not (bobp))(powershell-continuation-line-p))
  118. (forward-line -1))
  119. (+ (current-indentation) powershell-continuation-indent))
  120. ;; otherwise, indent relative to the block's opening char ([{
  121. (let ((closing-paren (looking-at "\\s-*\\s)"))
  122. new-indent
  123. block-open-line)
  124. (condition-case nil
  125. (progn
  126. (backward-up-list) ;when at top level, throw to no-indent
  127. (setq block-open-line (line-number-at-pos))
  128. ;; We're in a block, calculate/return indent amount.
  129. (if (not (looking-at "\\s(\\s-*\\(#.*\\)?$"))
  130. ;; code (not comments) follow the block open so
  131. ;; vertically align the block with the code.
  132. (if closing-paren
  133. ;; closing indent = open
  134. (setq new-indent (current-column))
  135. ;; block indent = first line of code
  136. (forward-char)
  137. (skip-syntax-forward " ")
  138. (setq new-indent (current-column)))
  139. ;; otherwise block open is at eol so indent is relative to
  140. ;; bol or another block open on the same line.
  141. (if closing-paren ; this sets the default indent
  142. (setq new-indent (current-indentation))
  143. (setq new-indent (+ powershell-indent (current-indentation))))
  144. ;; now see if the block is nested on the same line
  145. (when (condition-case nil
  146. (progn
  147. (backward-up-list)
  148. (= block-open-line (line-number-at-pos)))
  149. (scan-error nil))
  150. (forward-char)
  151. (skip-syntax-forward " ")
  152. (if closing-paren
  153. (setq new-indent (current-column))
  154. (setq new-indent (+ powershell-indent (current-column))))))
  155. new-indent)
  156. (scan-error ;; most likely, we are at the top-level
  157. 0))))))
  158. (defun powershell-indent-line ()
  159. "Indent the current line of powershell mode.
  160. Leave the point in place if it is inside the meat of the line"
  161. (interactive)
  162. (let ((savep (> (current-column) (current-indentation)))
  163. (amount (powershell-indent-line-amount)))
  164. (if savep
  165. (save-excursion (indent-line-to amount))
  166. (indent-line-to amount))))
  167. (defun powershell-quote-selection (beg end)
  168. "Quotes the selection between BEG and END.
  169. Quotes with single quotes and doubles embedded single quotes."
  170. (interactive `(,(region-beginning) ,(region-end)))
  171. (if (not mark-active)
  172. (error "Command requires a marked region"))
  173. (goto-char beg)
  174. (while (re-search-forward "'" end t)
  175. (replace-match "''")(setq end (1+ end)))
  176. (goto-char beg)
  177. (insert "'")
  178. (setq end (1+ end))
  179. (goto-char end)
  180. (insert "'"))
  181. (defun powershell-unquote-selection (beg end)
  182. "Unquotes the selected text between BEG and END.
  183. Remove doubled single quotes as we go."
  184. (interactive `(,(region-beginning) ,(region-end)))
  185. (if (not mark-active)
  186. (error "Command requires a marked region"))
  187. (goto-char beg)
  188. (cond ((looking-at "'")
  189. (goto-char end)
  190. (when (looking-back "'" nil)
  191. (delete-char -1)
  192. (setq end (1- end))
  193. (goto-char beg)
  194. (delete-char 1)
  195. (setq end (1- end))
  196. (while (search-forward "'" end t)
  197. (delete-char -1)
  198. (forward-char)
  199. (setq end (1- end)))))
  200. ((looking-at "\"")
  201. (goto-char end)
  202. (when (looking-back "\"" nil)
  203. (delete-char -1)
  204. (setq end (1- end))
  205. (goto-char beg)
  206. (delete-char 1)
  207. (setq end (1- end))
  208. (while (search-forward "\"" end t)
  209. (delete-char -1)
  210. (forward-char)
  211. (setq end (1- end)))
  212. (while (search-forward "`" end t)
  213. (delete-char -1)
  214. (forward-char)
  215. (setq end (1- end)))))
  216. (t (error "Must select quoted text exactly"))))
  217. (defun powershell-escape-selection (beg end)
  218. "Escape variables between BEG and END.
  219. Also extend existing escapes."
  220. (interactive `(,(region-beginning) ,(region-end)))
  221. (if (not mark-active)
  222. (error "Command requires a marked region"))
  223. (goto-char beg)
  224. (while (re-search-forward "`" end t)
  225. (replace-match "```")(setq end (+ end 2)))
  226. (goto-char beg)
  227. (while (re-search-forward "\\(?:\\=\\|[^`]\\)[$]" end t)
  228. (goto-char (car (cdr (match-data))))
  229. (backward-char)
  230. (insert "`")
  231. (forward-char)
  232. (setq end (1+ end))))
  233. (defun powershell-doublequote-selection (beg end)
  234. "Quotes the text between BEG and END with double quotes.
  235. Embedded quotes are doubled."
  236. (interactive `(,(region-beginning) ,(region-end)))
  237. (if (not mark-active)
  238. (error "Command requires a marked region"))
  239. (goto-char beg)
  240. (while (re-search-forward "\"" end t)
  241. (replace-match "\"\"")(setq end (1+ end)))
  242. (goto-char beg)
  243. (while (re-search-forward "`'" end t)
  244. (replace-match "```")(setq end (+ 2 end)))
  245. (goto-char beg)
  246. (insert "\"")
  247. (setq end (1+ end))
  248. (goto-char end)
  249. (insert "\""))
  250. (defun powershell-dollarparen-selection (beg end)
  251. "Wraps the text between BEG and END with $().
  252. The point is moved to the closing paren."
  253. (interactive `(,(region-beginning) ,(region-end)))
  254. (if (not mark-active)
  255. (error "Command requires a marked region"))
  256. (save-excursion
  257. (goto-char end)
  258. (insert ")")
  259. (goto-char beg)
  260. (insert "$("))
  261. (forward-char))
  262. (defun powershell-regexp-to-regex (beg end)
  263. "Turn the text between BEG and END into a regex.
  264. The text is assumed to be `regexp-opt' output."
  265. (interactive `(,(region-beginning) ,(region-end)))
  266. (if (not mark-active)
  267. (error "Command requires a marked region"))
  268. (save-restriction
  269. (narrow-to-region beg end)
  270. (goto-char (point-min))
  271. (while (re-search-forward "\\\\(" nil t)
  272. (replace-match "("))
  273. (goto-char (point-min))
  274. (while (re-search-forward "\\\\)" nil t)
  275. (replace-match ")"))
  276. (goto-char (point-min))
  277. (while (re-search-forward "\\\\|" nil t)
  278. (replace-match "|"))))
  279. ;; Taken from About_Keywords
  280. (defvar powershell-keywords
  281. (concat "\\_<"
  282. (regexp-opt
  283. '("begin" "break" "catch" "class" "continue" "data" "do" "default"
  284. "dynamicparam" "else" "elseif" "end" "enum" "exit" "filter" "finally"
  285. "for" "foreach" "from" "function" "if" "in" "param" "process"
  286. "return" "switch" "throw" "trap" "try" "until" "where" "while")
  287. t)
  288. "\\_>")
  289. "Powershell keywords.")
  290. ;; Taken from About_Comparison_Operators and some questionable sources :-)
  291. (defvar powershell-operators
  292. (concat "\\_<"
  293. (regexp-opt
  294. '("-eq" "-ne" "-gt" "-ge" "-lt" "-le"
  295. ;; case sensitive versions
  296. "-ceq" "-cne" "-cgt" "-cge" "-clt" "-cle"
  297. ;; explicitly case insensitive
  298. "-ieq" "-ine" "-igt" "-ige" "-ilt" "-ile"
  299. "-band" "-bor" "-bxor"
  300. "-and" "-or" "-xor"
  301. "-like" "-notlike" "-clike" "-cnotlike" "-ilike" "-inotlike"
  302. "-match" "-notmatch" "-cmatch" "-cnotmatch" "-imatch" "-inotmatch"
  303. "-contains" "-notcontains" "-ccontains" "-cnotcontains"
  304. "-icontains" "-inotcontains"
  305. "-replace" "-creplace" "-ireplace"
  306. "-is" "-as" "-f"
  307. ;; Questionable --> specific to certain contexts
  308. "-casesensitive" "-wildcard" "-regex" "-exact" ;specific to case
  309. "-begin" "-process" "-end" ;specific to scriptblock
  310. ) t)
  311. "\\_>")
  312. "Powershell operators.")
  313. (defvar powershell-scope-names
  314. '("global" "local" "private" "script" )
  315. "Names of scopes in Powershell mode.")
  316. (defvar powershell-variable-drive-names
  317. (append '("env" "function" "variable" "alias") powershell-scope-names)
  318. "Names of scopes in Powershell mode.")
  319. (defconst powershell-variables-regexp
  320. ;; There are 2 syntaxes detected: ${[scope:]name} and $[scope:]name
  321. ;; Match 0 is the entire variable name.
  322. ;; Match 1 is scope when the former syntax is found.
  323. ;; Match 2 is scope when the latter syntax is found.
  324. (concat
  325. "\\_<$\\(?:{\\(?:" (regexp-opt powershell-variable-drive-names t)
  326. ":\\)?[^}]+}\\|"
  327. "\\(?:" (regexp-opt powershell-variable-drive-names t)
  328. ":\\)?[a-zA-Z0-9_]+\\_>\\)")
  329. "Identifies legal powershell variable names.")
  330. (defconst powershell-function-names-regex
  331. ;; Syntax detected is [scope:]verb-noun
  332. ;; Match 0 is the entire name.
  333. ;; Match 1 is the scope if any.
  334. ;; Match 2 is the function name (which must exist)
  335. (concat
  336. "\\_<\\(?:" (regexp-opt powershell-scope-names t) ":\\)?"
  337. "\\([A-Z][a-zA-Z0-9]*-[A-Z0-9][a-zA-Z0-9]*\\)\\_>")
  338. "Identifies legal function & filter names.")
  339. (defconst powershell-object-types-regexp
  340. ;; Syntax is \[name[.name]\] (where the escaped []s are literal)
  341. ;; Only Match 0 is returned.
  342. "\\[\\(?:[a-zA-Z_][a-zA-Z0-9]*\\)\\(?:\\.[a-zA-Z_][a-zA-Z0-9]*\\)*\\]"
  343. "Identifies object type references. I.E. [object.data.type] syntax.")
  344. (defconst powershell-function-switch-names-regexp
  345. ;; Only Match 0 is returned.
  346. "\\_<-[a-zA-Z][a-zA-Z0-9]*\\_>"
  347. "Identifies function parameter names of the form -xxxx.")
  348. ;; Taken from Get-Variable on a fresh shell, merged with man
  349. ;; about_automatic_variables
  350. (defvar powershell-builtin-variables-regexp
  351. (regexp-opt
  352. '("$" "?"
  353. "^" "_"
  354. "args" "ConsoleFileName"
  355. "Error" "Event"
  356. "EventSubscriber" "ExecutionContext"
  357. "false" "Foreach"
  358. "HOME" "Host"
  359. "input" "LASTEXITCODE"
  360. "Matches" "MyInvocation"
  361. "NestedPromptLevel" "null"
  362. "PID" "PROFILE"
  363. "PSBoundParameters" "PSCmdlet"
  364. "PSCulture" "PSDebugContext"
  365. "PSHOME" "PSScriptRoot"
  366. "PSUICulture" "PSVersionTable"
  367. "PWD" "ReportErrorShowExceptionClass"
  368. "ReportErrorShowInnerException" "ReportErrorShowSource"
  369. "ReportErrorShowStackTrace" "Sender"
  370. "ShellId" "SourceArgs"
  371. "SourceEventArgs" "StackTrace"
  372. "this" "true" ) t)
  373. "The names of the built-in Powershell variables.
  374. They are highlighted differently from the other variables.")
  375. (defvar powershell-config-variables-regexp
  376. (regexp-opt
  377. '("ConfirmPreference" "DebugPreference"
  378. "ErrorActionPreference" "ErrorView"
  379. "FormatEnumerationLimit" "LogCommandHealthEvent"
  380. "LogCommandLifecycleEvent" "LogEngineHealthEvent"
  381. "LogEngineLifecycleEvent" "LogProviderHealthEvent"
  382. "LogProviderLifecycleEvent" "MaximumAliasCount"
  383. "MaximumDriveCount" "MaximumErrorCount"
  384. "MaximumFunctionCount" "MaximumHistoryCount"
  385. "MaximumVariableCount" "OFS"
  386. "OutputEncoding" "ProgressPreference"
  387. "PSEmailServer" "PSSessionApplicationName"
  388. "PSSessionConfigurationName" "PSSessionOption"
  389. "VerbosePreference" "WarningPreference"
  390. "WhatIfPreference" ) t)
  391. "Names of variables that configure powershell features.")
  392. (defun powershell-find-syntactic-comments (limit)
  393. "Find PowerShell comment begin and comment end characters.
  394. Returns match 1 and match 2 for <# #> comment sequences respectively.
  395. Returns match 3 and optionally match 4 for #/eol comments.
  396. Match 4 is returned only if eol is found before LIMIT"
  397. (when (search-forward "#" limit t)
  398. (cond
  399. ((looking-back "<#" nil)
  400. (set-match-data (list (match-beginning 0) (1+ (match-beginning 0))
  401. (match-beginning 0) (1+ (match-beginning 0)))))
  402. ((looking-at ">")
  403. (set-match-data (list (match-beginning 0) (match-end 0)
  404. nil nil
  405. (match-beginning 0) (match-end 0)))
  406. (forward-char))
  407. (t
  408. (let ((start (point)))
  409. (if (search-forward "\n" limit t)
  410. (set-match-data (list (1- start) (match-end 0)
  411. nil nil nil nil
  412. (1- start) start
  413. (match-beginning 0) (match-end 0)))
  414. (set-match-data (list start (match-end 0)
  415. nil nil nil nil
  416. (1- start) start))))))
  417. t))
  418. (defun powershell-find-syntactic-quotes (limit)
  419. "Find PowerShell hear string begin and end sequences upto LIMIT.
  420. Returns match 1 and match 2 for @' '@ sequences respectively.
  421. Returns match 3 and match 4 for @\" \"@ sequences respectively."
  422. (when (search-forward "@" limit t)
  423. (cond
  424. ((looking-at "'$")
  425. (set-match-data (list (match-beginning 0) (1+ (match-beginning 0))
  426. (match-beginning 0) (1+ (match-beginning 0))))
  427. (forward-char))
  428. ((looking-back "^'@" nil)
  429. (set-match-data (list (1- (match-end 0)) (match-end 0)
  430. nil nil
  431. (1- (match-end 0)) (match-end 0))))
  432. ((looking-at "\"$")
  433. (set-match-data (list (match-beginning 0) (1+ (match-beginning 0))
  434. nil nil
  435. nil nil
  436. (match-beginning 0) (1+ (match-beginning 0))))
  437. (forward-char))
  438. ((looking-back "^\"@" nil)
  439. (set-match-data (list (1- (match-end 0)) (match-end 0)
  440. nil nil
  441. nil nil
  442. nil nil
  443. (1- (match-end 0)) (match-end 0)))))
  444. t))
  445. (defvar powershell-font-lock-syntactic-keywords
  446. `((powershell-find-syntactic-comments (1 "!" t t) (2 "!" t t)
  447. (3 "<" t t) (4 ">" t t))
  448. (powershell-find-syntactic-quotes (1 "|" t t) (2 "|" t t)
  449. (3 "|" t t) (4 "|" t t)))
  450. "A list of regexp's or functions.
  451. Used to add `syntax-table' properties to
  452. characters that can't be set by the `syntax-table' alone.")
  453. (defvar powershell-font-lock-keywords-1
  454. `( ;; Type annotations
  455. (,powershell-object-types-regexp . font-lock-type-face)
  456. ;; syntaxic keywords
  457. (,powershell-keywords . font-lock-keyword-face)
  458. ;; operators
  459. (,powershell-operators . font-lock-builtin-face)
  460. ;; the REQUIRES mark
  461. ("^#\\(REQUIRES\\)" 1 font-lock-warning-face t))
  462. "Keywords for the first level of font-locking in Powershell mode.")
  463. (defvar powershell-font-lock-keywords-2
  464. (append
  465. powershell-font-lock-keywords-1
  466. `( ;; Built-in variables
  467. (,(concat "\\$\\(" powershell-builtin-variables-regexp "\\)\\>")
  468. 0 font-lock-builtin-face t)
  469. (,(concat "\\$\\(" powershell-config-variables-regexp "\\)\\>")
  470. 0 font-lock-builtin-face t)))
  471. "Keywords for the second level of font-locking in Powershell mode.")
  472. (defvar powershell-font-lock-keywords-3
  473. (append
  474. powershell-font-lock-keywords-2
  475. `( ;; user variables
  476. (,powershell-variables-regexp
  477. (0 font-lock-variable-name-face)
  478. (1 (cons font-lock-type-face '(underline)) t t)
  479. (2 (cons font-lock-type-face '(underline)) t t))
  480. ;; function argument names
  481. (,powershell-function-switch-names-regexp
  482. (0 font-lock-reference-face)
  483. (1 (cons font-lock-type-face '(underline)) t t)
  484. (2 (cons font-lock-type-face '(underline)) t t))
  485. ;; function names
  486. (,powershell-function-names-regex
  487. (0 font-lock-function-name-face)
  488. (1 (cons font-lock-type-face '(underline)) t t))))
  489. "Keywords for the maximum level of font-locking in Powershell mode.")
  490. (defun powershell-setup-font-lock ()
  491. "Set up the buffer local value for `font-lock-defaults'."
  492. ;; I use font-lock-syntactic-keywords to set some properties and I
  493. ;; don't want them ignored.
  494. (set (make-local-variable 'parse-sexp-lookup-properties) t)
  495. ;; This is where all the font-lock stuff actually gets set up. Once
  496. ;; font-lock-defaults has its value, setting font-lock-mode true should
  497. ;; cause all your syntax highlighting dreams to come true.
  498. (setq font-lock-defaults
  499. ;; The first value is all the keyword expressions.
  500. '((powershell-font-lock-keywords-1
  501. powershell-font-lock-keywords-2
  502. powershell-font-lock-keywords-3)
  503. ;; keywords-only means no strings or comments get fontified
  504. nil
  505. ;; case-fold (t ignores case)
  506. t
  507. ;; syntax-alist nothing special here
  508. nil
  509. ;; syntax-begin - no function defined to move outside syntactic block
  510. nil
  511. ;; font-lock-syntactic-keywords
  512. ;; takes (matcher (match syntax override lexmatch) ...)...
  513. (font-lock-syntactic-keywords
  514. . powershell-font-lock-syntactic-keywords))))
  515. (defvar powershell-mode-syntax-table
  516. (let ((powershell-mode-syntax-table (make-syntax-table)))
  517. (modify-syntax-entry ?$ "_" powershell-mode-syntax-table)
  518. (modify-syntax-entry ?: "_" powershell-mode-syntax-table)
  519. (modify-syntax-entry ?- "_" powershell-mode-syntax-table)
  520. (modify-syntax-entry ?^ "_" powershell-mode-syntax-table)
  521. (modify-syntax-entry ?\\ "_" powershell-mode-syntax-table)
  522. (modify-syntax-entry ?\{ "(}" powershell-mode-syntax-table)
  523. (modify-syntax-entry ?\} "){" powershell-mode-syntax-table)
  524. (modify-syntax-entry ?\[ "(]" powershell-mode-syntax-table)
  525. (modify-syntax-entry ?\] ")[" powershell-mode-syntax-table)
  526. (modify-syntax-entry ?\( "()" powershell-mode-syntax-table)
  527. (modify-syntax-entry ?\) ")(" powershell-mode-syntax-table)
  528. (modify-syntax-entry ?` "\\" powershell-mode-syntax-table)
  529. (modify-syntax-entry ?_ "w" powershell-mode-syntax-table)
  530. (modify-syntax-entry ?= "." powershell-mode-syntax-table)
  531. (modify-syntax-entry ?| "." powershell-mode-syntax-table)
  532. (modify-syntax-entry ?+ "." powershell-mode-syntax-table)
  533. (modify-syntax-entry ?* "." powershell-mode-syntax-table)
  534. (modify-syntax-entry ?/ "." powershell-mode-syntax-table)
  535. (modify-syntax-entry ?' "\"" powershell-mode-syntax-table)
  536. (modify-syntax-entry ?# "<" powershell-mode-syntax-table)
  537. powershell-mode-syntax-table)
  538. "Syntax for PowerShell major mode.")
  539. (defvar powershell-mode-map
  540. (let ((powershell-mode-map (make-keymap)))
  541. ;; (define-key powershell-mode-map "\r" 'powershell-indent-line)
  542. (define-key powershell-mode-map "\t" 'powershell-indent-line)
  543. (define-key powershell-mode-map (kbd "M-\"")
  544. 'powershell-doublequote-selection)
  545. (define-key powershell-mode-map (kbd "M-'") 'powershell-quote-selection)
  546. (define-key powershell-mode-map (kbd "C-'") 'powershell-unquote-selection)
  547. (define-key powershell-mode-map (kbd "C-\"") 'powershell-unquote-selection)
  548. (define-key powershell-mode-map (kbd "M-`") 'powershell-escape-selection)
  549. (define-key powershell-mode-map (kbd "C-$")
  550. 'powershell-dollarparen-selection)
  551. powershell-mode-map)
  552. "Keymap for PS major mode.")
  553. (defun powershell-setup-menu ()
  554. "Add a menu of PowerShell specific functions to the menu bar."
  555. (define-key (current-local-map) [menu-bar powershell-menu]
  556. (cons "PowerShell" (make-sparse-keymap "PowerShell")))
  557. (define-key (current-local-map) [menu-bar powershell-menu doublequote]
  558. '(menu-item "DoubleQuote Selection" powershell-doublequote-selection
  559. :key-sequence(kbd "M-\"")
  560. :help
  561. "DoubleQuotes the selection escaping embedded double quotes"))
  562. (define-key (current-local-map) [menu-bar powershell-menu quote]
  563. '(menu-item "SingleQuote Selection" powershell-quote-selection
  564. :key-sequence (kbd "M-'")
  565. :help
  566. "SingleQuotes the selection escaping embedded single quotes"))
  567. (define-key (current-local-map) [menu-bar powershell-menu unquote]
  568. '(menu-item "UnQuote Selection" powershell-unquote-selection
  569. :key-sequence (kbd "C-'")
  570. :help "Un-Quotes the selection un-escaping any escaped quotes"))
  571. (define-key (current-local-map) [menu-bar powershell-menu escape]
  572. '(menu-item "Escape Selection" powershell-escape-selection
  573. :key-sequence (kbd "M-`")
  574. :help (concat "Escapes variables in the selection"
  575. " and extends existing escapes.")))
  576. (define-key (current-local-map) [menu-bar powershell-menu dollarparen]
  577. '(menu-item "DollarParen Selection" powershell-dollarparen-selection
  578. :key-sequence (kbd "C-$")
  579. :help "Wraps the selection in $()")))
  580. ;;; Eldoc support
  581. (defcustom powershell-eldoc-def-files nil
  582. "List of files containing function help strings used by function `eldoc-mode'.
  583. These are the strings function `eldoc-mode' displays as help for
  584. functions near point. The format of the file must be exactly as
  585. follows or who knows what happens.
  586. (set (intern \"<fcn-name1>\" powershell-eldoc-obarray) \"<helper string1>\")
  587. (set (intern \"<fcn-name2>\" powershell-eldoc-obarray) \"<helper string2>\")
  588. ...
  589. Where <fcn-name> is the name of the function to which <helper string> applies.
  590. <helper-string> is the string to display when point is near <fcn-name>."
  591. :type '(repeat string)
  592. :group 'powershell)
  593. (defvar powershell-eldoc-obarray ()
  594. "Array for file entries by the function `eldoc'.
  595. `powershell-eldoc-def-files' entries are added into this array.")
  596. (defun powershell-eldoc-function ()
  597. "Return a documentation string appropriate for the current context or nil."
  598. (let ((word (thing-at-point 'symbol)))
  599. (if word
  600. (eval (intern-soft word powershell-eldoc-obarray)))))
  601. (defun powershell-setup-eldoc ()
  602. "Load the function documentation for use with eldoc."
  603. (when (not (null powershell-eldoc-def-files))
  604. (set (make-local-variable 'eldoc-documentation-function)
  605. 'powershell-eldoc-function)
  606. (unless (vectorp powershell-eldoc-obarray)
  607. (setq powershell-eldoc-obarray (make-vector 41 0))
  608. (condition-case var (mapc 'load powershell-eldoc-def-files)
  609. (error (message "*** powershell-setup-eldoc ERROR *** %s" var))))))
  610. ;;; Note: You can create quite a bit of help with these commands:
  611. ;;
  612. ;; function Get-Signature ($Cmd) {
  613. ;; if ($Cmd -is [Management.Automation.PSMethod]) {
  614. ;; $List = @($Cmd)}
  615. ;; elseif ($Cmd -isnot [string]) {
  616. ;; throw ("Get-Signature {<method>|<command>}`n" +
  617. ;; "'$Cmd' is not a method or command")}
  618. ;; else {$List = @(Get-Command $Cmd -ErrorAction SilentlyContinue)}
  619. ;; if (!$List[0] ) {
  620. ;; throw "Command '$Cmd' not found"}
  621. ;; foreach ($O in $List) {
  622. ;; switch -regex ($O.GetType().Name) {
  623. ;; 'AliasInfo' {
  624. ;; Get-Signature ($O.Definition)}
  625. ;; '(Cmdlet|ExternalScript)Info' {
  626. ;; $O.Definition} # not sure what to do with ExternalScript
  627. ;; 'F(unction|ilter)Info'{
  628. ;; if ($O.Definition -match '^param *\(') {
  629. ;; $t = [Management.Automation.PSParser]::tokenize($O.Definition,
  630. ;; [ref]$null)
  631. ;; $c = 1;$i = 1
  632. ;; while($c -and $i++ -lt $t.count) {
  633. ;; switch ($t[$i].Type.ToString()) {
  634. ;; GroupStart {$c++}
  635. ;; GroupEnd {$c--}}}
  636. ;; $O.Definition.substring(0,$t[$i].start + 1)} #needs parsing
  637. ;; else {$O.Name}}
  638. ;; 'PSMethod' {
  639. ;; foreach ($t in @($O.OverloadDefinitions)) {
  640. ;; while (($b=$t.IndexOf('`1[[')) -ge 0) {
  641. ;; $t=$t.remove($b,$t.IndexOf(']]')-$b+2)}
  642. ;; $t}}}}}
  643. ;; get-command|
  644. ;; ?{$_.CommandType -ne 'Alias' -and $_.Name -notlike '*:'}|
  645. ;; %{$_.Name}|
  646. ;; sort|
  647. ;; %{("(set (intern ""$($_.Replace('\','\\'))"" powershell-eldoc-obarray)" +
  648. ;; " ""$(Get-Signature $_|%{$_.Replace('\','\\').Replace('"','\"')})"")"
  649. ;; ).Replace("`r`n"")",""")")} > .\powershell-eldoc.el
  650. (defvar powershell-imenu-expression
  651. `(("Functions" ,(concat "function " powershell-function-names-regex) 2)
  652. ("Filters" ,(concat "filter " powershell-function-names-regex) 2)
  653. ("Top variables"
  654. , (concat "^\\(" powershell-object-types-regexp "\\)?\\("
  655. powershell-variables-regexp "\\)\\s-*=")
  656. 2))
  657. "List of regexps matching important expressions, for speebar & imenu.")
  658. (defun powershell-setup-imenu ()
  659. "Install `powershell-imenu-expression'."
  660. (when (require 'imenu nil t)
  661. ;; imenu doc says these are buffer-local by default
  662. (setq imenu-generic-expression powershell-imenu-expression)
  663. (setq imenu-case-fold-search nil)
  664. (imenu-add-menubar-index)
  665. (when (require 'which-func nil t)
  666. (which-function-mode t))))
  667. (when (require 'speedbar nil t)
  668. (declare-function speedbar-add-supported-extension "speedbar")
  669. (speedbar-add-supported-extension ".ps1?"))
  670. ;; A better command would be something like "powershell.exe -NoLogo
  671. ;; -NonInteractive -Command & (buffer-file-name)". But it will just
  672. ;; sit there waiting... The following will only work when .ps1 files
  673. ;; are associated with powershell.exe. And if they don't contain spaces.
  674. (defvar powershell-compile-command
  675. '(buffer-file-name)
  676. "Default command used to invoke a powershell script.")
  677. ;; The column number will be off whenever tabs are used. Since this is
  678. ;; the default in this mode, we will not capture the column number.
  679. (setq compilation-error-regexp-alist
  680. (cons '("At \\(.*\\):\\([0-9]+\\) char:\\([0-9]+\\)" 1 2)
  681. compilation-error-regexp-alist))
  682. (add-hook 'powershell-mode-hook #'imenu-add-menubar-index)
  683. ;;;###autoload
  684. (define-derived-mode powershell-mode prog-mode "PS"
  685. "Major mode for editing PowerShell scripts.
  686. \\{powershell-mode-map}
  687. Entry to this mode calls the value of `powershell-mode-hook' if
  688. that value is non-nil."
  689. (powershell-setup-font-lock)
  690. (set (make-local-variable 'indent-line-function) 'powershell-indent-line)
  691. (set (make-local-variable 'compile-command) powershell-compile-command)
  692. (set (make-local-variable 'comment-start) "#")
  693. (set (make-local-variable 'comment-start-skip) "#+\\s*")
  694. (set (make-local-variable 'parse-sexp-ignore-comments) t)
  695. (powershell-setup-imenu)
  696. (powershell-setup-menu)
  697. (powershell-setup-eldoc))
  698. ;;; Powershell inferior mode
  699. ;; TODO: set this programmatically, relying on %WINDIR%
  700. ;;; Code:
  701. (defcustom powershell-location-of-exe
  702. "c:\\windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe"
  703. "A string, providing the location of the Powershell.exe."
  704. :group 'powershell)
  705. (defcustom powershell-log-level 3
  706. "The current log level for powershell internal operations.
  707. 0 = NONE, 1 = Info, 2 = VERBOSE, 3 = DEBUG."
  708. :group 'powershell)
  709. (defcustom powershell-squish-results-of-silent-commands t
  710. "The function `powershell-invoke-command-silently' returns the results
  711. of a command in a string. PowerShell by default, inserts newlines when
  712. the output exceeds the configured width of the powershell virtual
  713. window. In some cases callers might want to get the results with the
  714. newlines and formatting removed. Set this to true, to do that."
  715. :group 'powershell)
  716. (defvar powershell-prompt-regex "PS [^#$%>]+> "
  717. "Regexp to match the powershell prompt.
  718. Powershell.el uses this regex to determine when a command has
  719. completed. Therefore, you need to set this appropriately if you
  720. explicitly change the prompt function in powershell. Any value
  721. should include a trailing space, if the powershell prompt uses a
  722. trailing space, but should not include a trailing newline.
  723. The default value will match the default PowerShell prompt.")
  724. (defvar powershell-command-reply nil
  725. "The reply of powershell commands.
  726. This is retained for housekeeping purposes.")
  727. (defvar powershell--max-window-width 0
  728. "The maximum width of a powershell window.
  729. You shouldn't need to ever set this. It gets set automatically,
  730. once, when the powershell starts up.")
  731. (defvar powershell-command-timeout-seconds 12
  732. "The timeout for a powershell command.
  733. Powershell.el will wait this long before giving up.")
  734. (defvar powershell--need-rawui-resize t
  735. "No need to fuss with this. It's intended for internal use
  736. only. It gets set when powershell needs to be informed that
  737. emacs has resized its window.")
  738. (defconst powershell--find-max-window-width-command
  739. (concat
  740. "function _Emacs_GetMaxPhsWindowSize \n"
  741. "{\n"
  742. " $rawui = (Get-Host).UI.RawUI\n"
  743. " $mpws_exists = ($rawui | Get-Member | ? "
  744. "{$_.Name -eq \"MaxPhysicalWindowSize\"})\n"
  745. " if ($mpws_exists -eq $null) {\n"
  746. " \"210\" | Out-Host\n"
  747. " } else {\n"
  748. " $rawui.MaxPhysicalWindowSize.Width | Out-Host\n"
  749. " }\n"
  750. "}\n"
  751. "_Emacs_GetMaxPhsWindowSize\n")
  752. "The powershell logic to determine the max physical window width.")
  753. (defconst powershell--set-window-width-fn-name "_Emacs_SetWindowWidth"
  754. "The name of the function this mode defines in PowerShell to
  755. set the window width. Intended for internal use only.")
  756. (defconst powershell--text-of-set-window-width-ps-function
  757. ;; see
  758. ;; http://blogs.msdn.com/lior/archive/2009/05/27/ResizePowerShellConsoleWindow.aspx
  759. ;;
  760. ;; When making the console window narrower, you mus set the window
  761. ;; size first. When making the console window wider, you must set the
  762. ;; buffer size first.
  763. (concat "function " powershell--set-window-width-fn-name
  764. "([string] $pswidth)\n"
  765. "{\n"
  766. ;;" \"resetting window width to $pswidth\n\" | Out-Host\n"
  767. " $rawui = (Get-Host).UI.RawUI\n"
  768. " # retrieve the values\n"
  769. " $bufsize = $rawui.BufferSize\n"
  770. " $winsize = $rawui.WindowSize\n"
  771. " $cwidth = $winsize.Width\n"
  772. " $winsize.Width = $pswidth \n"
  773. " $bufsize.Width = $pswidth\n"
  774. " if ($cwidth -lt $pswidth) {\n"
  775. " # increase the width\n"
  776. " $rawui.BufferSize = $bufsize\n"
  777. " $rawui.WindowSize = $winsize\n"
  778. " }\n"
  779. " elseif ($cwidth -gt $pswidth) {\n"
  780. " # decrease the width\n"
  781. " $rawui.WindowSize = $winsize\n"
  782. " $rawui.BufferSize = $bufsize\n"
  783. " }\n"
  784. " # destroy variables\n"
  785. " Set-Variable -name rawui -value $null\n"
  786. " Set-Variable -name winsize -value $null\n"
  787. " Set-Variable -name bufsize -value $null\n"
  788. " Set-Variable -name cwidth -value $null\n"
  789. "}\n\n")
  790. "The text of the powershell function that will be used at runtime to
  791. set the width of the virtual Window in PowerShell, as the Emacs window
  792. gets resized.")
  793. (defun powershell-log (level text &rest args)
  794. "Log a message at level LEVEL.
  795. If LEVEL is higher than `powershell-log-level', the message is
  796. ignored. Otherwise, it is printed using `message'.
  797. TEXT is a format control string, and the remaining arguments ARGS
  798. are the string substitutions (see `format')."
  799. (if (<= level powershell-log-level)
  800. (let* ((msg (apply 'format text args)))
  801. (message "%s" msg))))
  802. ;; (defun dino-powershell-complete (arg)
  803. ;; "do powershell completion on the given STRING. Pop up a buffer
  804. ;; with the completion list."
  805. ;; (interactive
  806. ;; (list (read-no-blanks-input "\
  807. ;; Stub to complete: ")))
  808. ;; (let ((proc
  809. ;; (get-buffer-process (current-buffer))))
  810. ;; (comint-proc-query proc (concat "Get-Command " arg "*\n"))
  811. ;; )
  812. ;; )
  813. ;; (defun dino-powershell-cmd-complete ()
  814. ;; "try to get powershell completion to work."
  815. ;; (interactive)
  816. ;; (let ((proc
  817. ;; (get-buffer-process (current-buffer))))
  818. ;; ;; (comint-proc-query proc "Get-a\t")
  819. ;; ;; (comint-simple-send proc "Get-a\t")
  820. ;; (comint-send-string proc "Get-a\t\n")
  821. ;; ;; (process-send-eof)
  822. ;; )
  823. ;; )
  824. (defun powershell--define-set-window-width-function (proc)
  825. "Sends a function definition to the PowerShell instance
  826. identified by PROC. The function sets the window width of the
  827. PowerShell virtual window. Later, the function will be called
  828. when the width of the emacs window changes."
  829. (if proc
  830. (progn
  831. ;;process-send-string
  832. (comint-simple-send
  833. proc
  834. powershell--text-of-set-window-width-ps-function))))
  835. (defun powershell--get-max-window-width (buffer-name)
  836. "Gets the maximum width of the virtual window for PowerShell running
  837. in the buffer with name BUFFER-NAME.
  838. In PowerShell 1.0, the maximum WindowSize.Width for
  839. PowerShell is 210, hardcoded, I believe. In PowerShell 2.0, the max
  840. windowsize.Width is provided in the RawUI.MaxPhysicalWindowSize
  841. property.
  842. This function does the right thing, and sets the buffer-local
  843. `powershell--max-window-width' variable with the correct value."
  844. (let ((proc (get-buffer-process buffer-name)))
  845. (if proc
  846. (with-current-buffer buffer-name
  847. (powershell-invoke-command-silently
  848. proc
  849. powershell--find-max-window-width-command
  850. 0.90)
  851. ;; store the retrieved width
  852. (setq powershell--max-window-width
  853. (if (and (not (null powershell-command-reply))
  854. (string-match
  855. "\\([1-9][0-9]*\\)[ \t\f\v\n]+"
  856. powershell-command-reply))
  857. (string-to-number (match-string 1 powershell-command-reply))
  858. 200)))))) ;; could go to 210, but let's use 200 to be safe
  859. (defun powershell--set-window-width (proc)
  860. "Run the PowerShell function that sets the RawUI width
  861. appropriately for a PowerShell shell.
  862. This is necessary to get powershell to do the right thing, as far
  863. as text formatting, when the emacs window gets resized.
  864. The function gets defined in powershell upon powershell startup."
  865. (let ((ps-width
  866. (number-to-string (min powershell--max-window-width (window-width)))))
  867. (progn
  868. ;;(process-send-string
  869. (comint-simple-send
  870. proc
  871. (concat powershell--set-window-width-fn-name
  872. "('" ps-width "')")))))
  873. ;;;###autoload
  874. (defun powershell (&optional buffer prompt-string)
  875. "Run an inferior PowerShell.
  876. If BUFFER is non-nil, use it to hold the powershell
  877. process. Defaults to *PowerShell*.
  878. Interactively, a prefix arg means to prompt for BUFFER.
  879. If BUFFER exists but the shell process is not running, it makes a
  880. new shell.
  881. If BUFFER exists and the shell process is running, just switch to
  882. BUFFER.
  883. If PROMPT-STRING is non-nil, sets the prompt to the given value.
  884. See the help for `shell' for more details. \(Type
  885. \\[describe-mode] in the shell buffer for a list of commands.)"
  886. (interactive
  887. (list
  888. (and current-prefix-arg
  889. (read-buffer "Shell buffer: "
  890. (generate-new-buffer-name "*PowerShell*")))))
  891. (setq buffer (get-buffer-create (or buffer "*PowerShell*")))
  892. (powershell-log 1 "powershell starting up...in buffer %s" (buffer-name buffer))
  893. (let ((tmp-shellfile explicit-shell-file-name))
  894. ;; set arguments for the powershell exe.
  895. ;; Does this need to be tunable?
  896. (setq explicit-shell-file-name powershell-location-of-exe)
  897. (shell buffer)
  898. (setq explicit-shell-file-name tmp-shellfile))
  899. ;; (powershell--get-max-window-width "*PowerShell*")
  900. ;; (powershell-invoke-command-silently (get-buffer-process "*csdeshell*")
  901. ;; "[Ionic.Csde.Utilities]::Version()" 2.9)
  902. ;; (comint-simple-send (get-buffer-process "*csdeshell*") "prompt\n")
  903. (let ((proc (get-buffer-process buffer)))
  904. (make-local-variable 'powershell-prompt-regex)
  905. (make-local-variable 'powershell-command-reply)
  906. (make-local-variable 'powershell--max-window-width)
  907. (make-local-variable 'powershell-command-timeout-seconds)
  908. (make-local-variable 'powershell-squish-results-of-silent-commands)
  909. (make-local-variable 'powershell--need-rawui-resize)
  910. (make-local-variable 'comint-prompt-read-only)
  911. ;; disallow backspace over the prompt:
  912. (setq comint-prompt-read-only t)
  913. ;; We need to tell powershell how wide the emacs window is, because
  914. ;; powershell pads its output to the width it thinks its window is.
  915. ;;
  916. ;; The way it's done: every time the width of the emacs window changes, we
  917. ;; set a flag. Then, before sending a powershell command that is
  918. ;; typed into the buffer, to the actual powershell process, we check
  919. ;; that flag. If it is set, we resize the powershell window appropriately,
  920. ;; before sending the command.
  921. ;; If we didn't do this, powershell output would get wrapped at a
  922. ;; column width that would be different than the emacs buffer width,
  923. ;; and everything would look ugly.
  924. ;; get the maximum width for powershell - can't go beyond this
  925. (powershell--get-max-window-width buffer)
  926. ;; define the function for use within powershell to resize the window
  927. (powershell--define-set-window-width-function proc)
  928. ;; add the hook that sets the flag
  929. (add-hook 'window-size-change-functions
  930. '(lambda (&optional x)
  931. (setq powershell--need-rawui-resize t)))
  932. ;; set the flag so we resize properly the first time.
  933. (setq powershell--need-rawui-resize t)
  934. (if prompt-string
  935. (progn
  936. ;; This sets up a prompt for the PowerShell. The prompt is
  937. ;; important because later, after sending a command to the
  938. ;; shell, the scanning logic that grabs the output looks for
  939. ;; the prompt string to determine that the output is complete.
  940. (comint-simple-send
  941. proc
  942. (concat "function prompt { '" prompt-string "' }"))
  943. (setq powershell-prompt-regex prompt-string)))
  944. ;; hook the kill-buffer action so we can kill the inferior process?
  945. (add-hook 'kill-buffer-hook 'powershell-delete-process)
  946. ;; wrap the comint-input-sender with a PS version
  947. ;; must do this after launching the shell!
  948. (make-local-variable 'comint-input-sender)
  949. (setq comint-input-sender 'powershell-simple-send)
  950. ;; set a preoutput filter for powershell. This will trim newlines
  951. ;; after the prompt.
  952. (add-hook 'comint-preoutput-filter-functions
  953. 'powershell-preoutput-filter-for-prompt)
  954. ;; send a carriage-return (get the prompt)
  955. (comint-send-input)
  956. (accept-process-output proc))
  957. ;; The launch hooks for powershell has not (yet?) been implemented
  958. ;;(run-hooks 'powershell-launch-hook)
  959. ;; return the buffer created
  960. buffer)
  961. ;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
  962. ;; Using powershell on emacs23, I get an error:
  963. ;;
  964. ;; ansi-color-process-output: Marker does not point anywhere
  965. ;;
  966. ;; Here's what's happening.
  967. ;;
  968. ;; In order to be able to read the output from powershell, this shell
  969. ;; starts powershell.exe in "interactive mode", using the -i
  970. ;; option. This which has the curious side-effect of turning off the
  971. ;; prompt in powershell. Normally powershell will return its results,
  972. ;; then emit a prompt to indicate that it is ready for more input. In
  973. ;; interactive mode it doesn't emit the prompt. To work around this,
  974. ;; this code (powershell.el) sends an explicit `prompt` command after
  975. ;; sending any user-entered command to powershell. This tells powershell
  976. ;; to explicitly return the prompt, after the results of the prior
  977. ;; command. The prompt then shows up in the powershell buffer. Lovely.
  978. ;;
  979. ;; But, `ansi-color-apply-on-region` gets called after every command
  980. ;; gets sent to powershell. It gets called with args `(begin end)`,
  981. ;; which are both markers. Turns out the very first time this fn is
  982. ;; called, the position for the begin marker is nil.
  983. ;;
  984. ;; `ansi-color-apply-on-region` calls `(goto-char begin)` (effectively),
  985. ;; and when the position on the marker is nil, the call errors with
  986. ;; "Marker does not point anywhere."
  987. ;;
  988. ;; The following advice suppresses the call to
  989. ;; `ansi-color-apply-on-region` when the begin marker points
  990. ;; nowhere.
  991. (defadvice ansi-color-apply-on-region (around
  992. powershell-throttle-ansi-colorizing
  993. (begin end)
  994. compile)
  995. (progn
  996. (let ((start-pos (marker-position begin)))
  997. (cond
  998. (start-pos
  999. (progn
  1000. ad-do-it))))))
  1001. (defun powershell--silent-cmd-filter (process result)
  1002. "A process filter that captures output from a shell and stores it
  1003. to `powershell-command-reply', rather than allowing the output to
  1004. be displayed in the shell buffer.
  1005. This function is intended for internal use only."
  1006. (let ((end-of-result
  1007. (string-match (concat ".*\n\\(" powershell-prompt-regex "\\)[ \n]*\\'")
  1008. result)))
  1009. (if (and end-of-result (numberp end-of-result))
  1010. (progn
  1011. ;; Store everything except the follow-on prompt.
  1012. ;; The result probably includes a final newline!
  1013. (setq result (substring result 0 (match-beginning 1)))
  1014. (if powershell-squish-results-of-silent-commands
  1015. (setq result
  1016. (replace-regexp-in-string "\n" "" result)))
  1017. (setq powershell-command-reply
  1018. (concat powershell-command-reply result)))
  1019. (progn
  1020. (if powershell-squish-results-of-silent-commands
  1021. (setq result
  1022. (replace-regexp-in-string "\n" "" result)))
  1023. (setq powershell-command-reply
  1024. (concat powershell-command-reply result))
  1025. ;; recurse. For very very long output, the recursion can
  1026. ;; cause stack overflow. Careful!
  1027. (accept-process-output process powershell-command-timeout-seconds)))))
  1028. (defun powershell-invoke-command-silently (proc command
  1029. &optional timeout-seconds)
  1030. "In the PowerShell instance PROC, invoke COMMAND silently.
  1031. Neither the COMMAND is echoed nor the results to the associated
  1032. buffer. Use TIMEOUT-SECONDS as the timeout, waiting for a
  1033. response. The COMMAND should be a string, and need not be
  1034. terminated with a newline.
  1035. This is helpful when, for example, doing setup work. Or other sneaky
  1036. stuff, such as resetting the size of the PowerShell virtual window.
  1037. Returns the result of the command, a string, without the follow-on
  1038. command prompt. The result will probably end in a newline. This result
  1039. is also stored in the buffer-local variable `powershell-command-reply'.
  1040. In some cases the result can be prepended with the command prompt, as
  1041. when, for example, several commands have been send in succession and the
  1042. results of the prior command were not fully processed by the application.
  1043. If a PowerShell buffer is not the current buffer, this function
  1044. should be invoked within a call to `with-current-buffer' or
  1045. similar in order to insure that the buffer-local values of
  1046. `powershell-command-reply', `powershell-prompt-regex', and
  1047. `powershell-command-timeout-seconds' are used.
  1048. Example:
  1049. (with-current-buffer powershell-buffer-name
  1050. (powershell-invoke-command-silently
  1051. proc
  1052. command-string
  1053. 1.90))"
  1054. (let ((old-timeout powershell-command-timeout-seconds)
  1055. (original-filter (process-filter proc)))
  1056. (setq powershell-command-reply nil)
  1057. (if timeout-seconds
  1058. (setq powershell-command-timeout-seconds timeout-seconds))
  1059. (set-process-filter proc 'powershell--silent-cmd-filter)
  1060. ;; Send the command plus the "prompt" command. The filter
  1061. ;; will know the command is finished when it sees the command
  1062. ;; prompt.
  1063. ;;
  1064. (process-send-string proc (concat command "\nprompt\n"))
  1065. (accept-process-output proc powershell-command-timeout-seconds)
  1066. ;; output of the command is now available in powershell-command-reply
  1067. ;; Trim prompt from the beginning of the output.
  1068. ;; this can happen for the first command through
  1069. ;; the shell. I think there's a race condition.
  1070. (if (and powershell-command-reply
  1071. (string-match (concat "^" powershell-prompt-regex "\\(.*\\)\\'")
  1072. powershell-command-reply))
  1073. (setq powershell-command-reply
  1074. (substring powershell-command-reply
  1075. (match-beginning 1)
  1076. (match-end 1))))
  1077. ;; restore the original filter
  1078. (set-process-filter proc original-filter)
  1079. ;; restore the original timeout
  1080. (if timeout-seconds
  1081. (setq powershell-command-timeout-seconds old-timeout))
  1082. ;; the result:
  1083. powershell-command-reply))
  1084. (defun powershell-delete-process (&optional proc)
  1085. "Delete the current buffer process or PROC."
  1086. (or proc
  1087. (setq proc (get-buffer-process (current-buffer))))
  1088. (and (processp proc)
  1089. (delete-process proc)))
  1090. (defun powershell-preoutput-filter-for-prompt (string)
  1091. "Trim the newline from STRING, the prompt that we get back from
  1092. powershell. This fn is set into the preoutput filters, so the
  1093. newline is trimmed before being put into the output buffer."
  1094. (if (string-match (concat powershell-prompt-regex "\n\\'") string)
  1095. (substring string 0 -1) ;; remove newline
  1096. string))
  1097. (defun powershell-simple-send (proc string)
  1098. "Override of the comint-simple-send function, with logic
  1099. specifically designed for powershell. This just sends STRING,
  1100. plus the prompt command.
  1101. When running as an inferior shell with stdin/stdout redirected,
  1102. powershell is in noninteractive mode. This means no prompts get
  1103. emitted when a PS command completes. This makes it difficult for
  1104. a comint mode to determine when the command has completed.
  1105. Therefore, we send an explicit request for the prompt, after
  1106. sending the actual (primary) command. When the primary command
  1107. completes, Powershell then responds to the \"prompt\" command,
  1108. and emits the prompt.
  1109. This insures we get and display the prompt."
  1110. ;; Tell PowerShell to resize its virtual window, if necessary. We do
  1111. ;; this by calling a resize function in the PowerShell, before sending
  1112. ;; the user-entered command to the shell.
  1113. ;;
  1114. ;; Powershell keeps track of its \"console\", and formats its output
  1115. ;; according to the width it thinks it is using. This is true even when
  1116. ;; powershell is invoked with the - argument, which tells it to use
  1117. ;; stdin as input.
  1118. ;; Therefore, if the user has resized the emacs window since the last
  1119. ;; PowerShell command, we need to tell PowerShell to change the size
  1120. ;; of its virtual window. Calling that function does not change the
  1121. ;; size of a window that is visible on screen - it only changes the
  1122. ;; size of the virtual window that PowerShell thinks it is using. We
  1123. ;; do that by invoking the PowerShell function that this module
  1124. ;; defined for that purpose.
  1125. ;;
  1126. (if powershell--need-rawui-resize
  1127. (progn
  1128. (powershell--set-window-width proc)
  1129. (setq powershell--need-rawui-resize nil)))
  1130. (comint-simple-send proc (concat string "\n"))
  1131. (comint-simple-send proc "prompt\n"))
  1132. ;; Notes on TAB for completion.
  1133. ;; -------------------------------------------------------
  1134. ;; Emacs calls comint-dynamic-complete when the TAB key is pressed in a shell.
  1135. ;; This is set up in shell-mode-map.
  1136. ;;
  1137. ;; comint-dynamic-complete calls the functions in
  1138. ;; comint-dynamic-complete-functions, until one of them returns
  1139. ;; non-nil.
  1140. ;;
  1141. ;; comint-dynamic-complete-functions is a good thing to set in the mode hook.
  1142. ;;
  1143. ;; The default value for that var in a powershell shell is:
  1144. ;; (comint-replace-by-expanded-history
  1145. ;; shell-dynamic-complete-environment-variable
  1146. ;; shell-dynamic-complete-command
  1147. ;; shell-replace-by-expanded-directory
  1148. ;; comint-dynamic-complete-filename)
  1149. ;; (defun powershell-dynamic-complete-command ()
  1150. ;; "Dynamically complete the command at point. This function is
  1151. ;; similar to `comint-dynamic-complete-filename', except that it
  1152. ;; searches the commands from powershell and then the `exec-path'
  1153. ;; (minus the trailing Emacs library path) for completion candidates.
  1154. ;; Completion is dependent on the value of
  1155. ;; `shell-completion-execonly', plus those that effect file
  1156. ;; completion. See `powershell-dynamic-complete-as-command'.
  1157. ;; Returns t if successful."
  1158. ;; (interactive)
  1159. ;; (let ((filename (comint-match-partial-filename)))
  1160. ;; (if (and filename
  1161. ;; (save-match-data (not (string-match "[~/]" filename)))
  1162. ;; (eq (match-beginning 0)
  1163. ;; (save-excursion (shell-backward-command 1) (point))))
  1164. ;; (prog2 (message "Completing command name...")
  1165. ;; (powershell-dynamic-complete-as-command)))))
  1166. ;; (defun powershell-dynamic-complete-as-command ()
  1167. ;; "Dynamically complete at point as a command.
  1168. ;; See `shell-dynamic-complete-filename'. Returns t if successful."
  1169. ;; (let* ((filename (or (comint-match-partial-filename) ""))
  1170. ;; (filenondir (file-name-nondirectory filename))
  1171. ;; (path-dirs (cdr (reverse exec-path)))
  1172. ;; (cwd (file-name-as-directory (expand-file-name default-directory)))
  1173. ;; (ignored-extensions
  1174. ;; (and comint-completion-fignore
  1175. ;; (mapconcat (function (lambda (x) (concat (regexp-quote x) "$")))
  1176. ;; comint-completion-fignore "\\|")))
  1177. ;; (dir "") (comps-in-dir ())
  1178. ;; (file "") (abs-file-name "") (completions ()))
  1179. ;; ;; Go thru each cmd in powershell's lexicon, finding completions.
  1180. ;; ;; Go thru each dir in the search path, finding completions.
  1181. ;; (while path-dirs
  1182. ;; (setq dir (file-name-as-directory (comint-directory (or (car path-dirs) ".")))
  1183. ;; comps-in-dir (and (file-accessible-directory-p dir)
  1184. ;; (file-name-all-completions filenondir dir)))
  1185. ;; ;; Go thru each completion found, to see whether it should be used.
  1186. ;; (while comps-in-dir
  1187. ;; (setq file (car comps-in-dir)
  1188. ;; abs-file-name (concat dir file))
  1189. ;; (if (and (not (member file completions))
  1190. ;; (not (and ignored-extensions
  1191. ;; (string-match ignored-extensions file)))
  1192. ;; (or (string-equal dir cwd)
  1193. ;; (not (file-directory-p abs-file-name)))
  1194. ;; (or (null shell-completion-execonly)
  1195. ;; (file-executable-p abs-file-name)))
  1196. ;; (setq completions (cons file completions)))
  1197. ;; (setq comps-in-dir (cdr comps-in-dir)))
  1198. ;; (setq path-dirs (cdr path-dirs)))
  1199. ;; ;; OK, we've got a list of completions.
  1200. ;; (let ((success (let ((comint-completion-addsuffix nil))
  1201. ;; (comint-dynamic-simple-complete filenondir completions))))
  1202. ;; (if (and (memq success '(sole shortest)) comint-completion-addsuffix
  1203. ;; (not (file-directory-p (comint-match-partial-filename))))
  1204. ;; (insert " "))
  1205. ;; success)))
  1206. (provide 'powershell)
  1207. ;;; powershell.el ends here