branch: elpa/powershell
commit e41abe820ff7d625429ef5d70b2e3dd3a4cb55db
Author: Joe Schafer <[email protected]>
Commit: Joe Schafer <[email protected]>
Update to newer version on Emacs wiki.
---
powershell.el | 790 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 684 insertions(+), 106 deletions(-)
diff --git a/powershell.el b/powershell.el
index ed89a2d08ed..589bae275ab 100644
--- a/powershell.el
+++ b/powershell.el
@@ -1,156 +1,734 @@
-;; powershell.el, version 0.1
+;;;; powershell.el --- run powershell as an inferior shell in emacs
;;
-;; Author: Dino Chiesa
-;; Thu, 10 Apr 2008 11:10
+;; Author : Dino Chiesa <[email protected]>
+;; Created : 10 Apr 2008
+;; Modified : May 2010
+;; Version : 0.2.4
+;; Keywords : powershell shell ms-windows
+;; X-URL : http://www.emacswiki.org/emacs/PowerShell#toc3
+;; Last-saved : <2011-February-17 12:10:59>
;;
-;; Run Windows PowerShell v1.0 as an inferior shell within emacs. Tested with
emacs v22.2.
+
+
+;;; Commentary:
+;;
+;; Run Windows PowerShell v1.0 or v2.0 as an inferior shell within
+;; emacs. Tested with emacs v22.2 and v23.2.
+;;
+;; To use it, M-x powershell .
+;;
+;; ==============
;;
;; TODO:
-;; test what happens when you expand the window size beyond the
maxWindowWidth for the RawUI
-;; make everything configurable (Powershell exe, initial args, powershell
prompt regexp)
-;; implement powershell launch hooks
-;; prevent backspace from deleting the powershell prompt? (do other shells do
this?)
+;;
+;; - get TAB to do proper completion for powershell commands, filenames,
+;; etc.
+;;
+;;
+
+;;; Versions:
+;;
+;; 0.1.0 - Initial release.
+;; 0.2.4 - make powershell fn an autoload.
+;; - fixed problem where running a single shell, caused all
+;; future shells to be powershell. This meant reverting to
+;; the original value of explicit-shell-file-name after
+;; invoking `shell'.
+;; - make location of powershell specifiable, via defcustom
+;; `powershell-location-of-exe'. Also turn a few other defvar
+;; into defcustom.
+;; - fix "Marker does not point anywhere" problem in
+;; `ansi-color-apply-on-region'.
;;
+
+;;; License:
+;;
+;; This code is distributed under the New BSD License.
+;;
+;; Copyright (c) 2008-2010, Dino Chiesa
+;; All rights reserved.
+;;
+;; Redistribution and use in source and binary forms, with or without
+;; modification, are permitted provided that the following conditions
+;; are met:
+;;
+;; Redistributions of source code must retain the above copyright
+;; notice, this list of conditions and the following disclaimer.
+;;
+;; Redistributions in binary form must reproduce the above copyright
+;; notice, this list of conditions and the following disclaimer in the
+;; documentation and/or other materials provided with the distribution.
+;;
+;; Neither the name of the author or any contributors, nor the names of
+;; any organizations they belong to, may be used to endorse or promote
+;; products derived from this software without specific prior written
+;; permission.
+;;
+;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+;; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+;; HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+;; BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+;; OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+;; AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+;; WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+;; POSSIBILITY OF SUCH DAMAGE.
+;;
+;;
+
+
+
(require 'shell)
+;; TODO: set this programmatically, relying on %WINDIR%
+(defcustom powershell-location-of-exe
+ "c:\\windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe"
+ "A string, providing the location of the Powershell.exe"
+ :group 'powershell)
-(defun powershell-gen-window-width-string ()
- (concat "$a = (Get-Host).UI.RawUI\n"
- "$b = $a.WindowSize\n"
- "$b.Width = " (number-to-string (window-width)) "\n"
- "$a.BufferSize = $b\n"
- "$a.WindowSize = $b")
- )
+(defcustom powershell-log-level 3
+ "The current log level for powershell internal operations.
+0 = NONE, 1 = Info, 2 = VERBOSE, 3 = DEBUG. "
+ :group 'powershell)
+
+(defcustom powershell-squish-results-of-silent-commands t
+"The function `powershell-invoke-command-silently' returns the results
+of a command in a string. PowerShell by default, inserts newlines when
+the output exceeds the configured width of the powershell virtual
+window. In some cases callers might want to get the results with the
+newlines and formatting removed. Set this to true, to do that."
+
+:group 'powershell)
+
+
+
+(defvar powershell-prompt-regex "PS [^#$%>]+> "
+ "Regexp for powershell prompt.
+
+Powershell.el uses this regex to determine when a command has completed.
+
+Therefore, you need to set this appropriately if you explicitly
+change the prompt function in powershell. Any value should
+include a trailing space, if the powershell prompt uses a
+trailing space, but should not include a trailing newline.
+The default value will match the default PowerShell prompt.
+")
-(defvar powershell-prompt-pattern "PS [^#$%>]+>"
- "Regexp for powershell prompt. This isn't really used, because I couldn't
figure out how to get it to work."
+(defvar powershell-command-reply nil
+ "For internal use only. It holds the reply of powershell commands sent for
housekeeping purposes.")
+
+
+(defvar powershell--max-window-width 0
+ "The maximum width of a powershell window. You shouldn't need to ever
+set this. It gets set automatically, once, when the powershell starts up. "
)
-(defgroup powershell nil
- "Running shell from within Emacs buffers."
- :group 'processes
+(defvar powershell-command-timeout-seconds 12
+ "The timeout for a powershell command. Powershell.el
+will wait this long before giving up.")
+
+
+(defvar powershell--need-rawui-resize t
+ "No need to fuss with this. It's intended for internal use
+only. It gets set when powershell needs to be informed that
+emacs has resized its window. ")
+
+
+(defconst powershell--find-max-window-width-command
+ (concat
+ "function _Emacs_GetMaxPhsWindowSize \n"
+"{\n"
+" $rawui = (Get-Host).UI.RawUI\n"
+" $mpws_exists = ($rawui | Get-Member | ? {$_.Name -eq
\"MaxPhysicalWindowSize\"})\n"
+" if ($mpws_exists -eq $null) {\n"
+" \"210\" | Out-Host\n"
+" } else {\n"
+" $rawui.MaxPhysicalWindowSize.Width | Out-Host\n"
+" }\n"
+"}\n"
+"_Emacs_GetMaxPhsWindowSize\n"
+)
+ "The powershell logic to determine the max physical window width."
)
-(defcustom powershell-need-rawui-resize t
- "set when powershell needs to be resized"
- :group 'powershell
-)
+(defconst powershell--set-window-width-fn-name "_Emacs_SetWindowWidth"
+ "The name of the function this mode defines in PowerShell to set the window
width. Intended for internal use only. ")
+
+
+(defconst powershell--text-of-set-window-width-ps-function
+ ;; see
http://blogs.msdn.com/lior/archive/2009/05/27/ResizePowerShellConsoleWindow.aspx
+ ;;
+ ;; When making the console window narrower, you mus set the window
+ ;; size first. When making the console window wider, you must set the
+ ;; buffer size first.
+
+ (concat "function " powershell--set-window-width-fn-name "([string]
$pswidth)\n"
+ "{\n"
+ ;;" \"resetting window width to $pswidth\n\" | Out-Host\n"
+ " $rawui = (Get-Host).UI.RawUI\n"
+ " # retrieve the values\n"
+ " $bufsize = $rawui.BufferSize\n"
+ " $winsize = $rawui.WindowSize\n"
+ " $cwidth = $winsize.Width\n"
+ " $winsize.Width = $pswidth \n"
+ " $bufsize.Width = $pswidth\n"
+ " if ($cwidth -lt $pswidth) {\n"
+ " # increase the width\n"
+ " $rawui.BufferSize = $bufsize\n"
+ " $rawui.WindowSize = $winsize\n"
+ " }\n"
+ " elseif ($cwidth -gt $pswidth) {\n"
+ " # decrease the width\n"
+ " $rawui.WindowSize = $winsize\n"
+ " $rawui.BufferSize = $bufsize\n"
+ " }\n"
+ " # destroy variables\n"
+ " Set-Variable -name rawui -value $null\n"
+ " Set-Variable -name winsize -value $null\n"
+ " Set-Variable -name bufsize -value $null\n"
+ " Set-Variable -name cwidth -value $null\n"
+ "}\n\n")
+
+ "The text of the powershell function that will be used at runtime to
+set the width of the virtual Window in PowerShell, as the Emacs window
+gets resized.
+")
+
+
+
+(defun powershell-log (level text &rest args)
+ "Log a message at level LEVEL.
+If LEVEL is higher than `powershell-log-level', the message is
+ignored. Otherwise, it is printed using `message'.
+TEXT is a format control string, and the remaining arguments ARGS
+are the string substitutions (see `format')."
+ (if (<= level powershell-log-level)
+ (let* ((msg (apply 'format text args)))
+ (message "%s" msg)
+ )))
+
+
+;; (defun dino-powershell-complete (arg)
+;; "do powershell completion on the given STRING. Pop up a buffer with the
completion list."
+;; (interactive
+;; (list (read-no-blanks-input "\
+;; Stub to complete: ")))
+
+;; (let ((proc
+;; (get-buffer-process (current-buffer))))
+;; (comint-proc-query proc (concat "Get-Command " arg "*\n"))
+;; )
+;; )
+
+;; (defun dino-powershell-cmd-complete ()
+;; "try to get powershell completion to work."
+;; (interactive)
+;; (let ((proc
+;; (get-buffer-process (current-buffer))))
+;; ;; (comint-proc-query proc "Get-a\t")
+;; ;; (comint-simple-send proc "Get-a\t")
+;; (comint-send-string proc "Get-a\t\n")
+;; ;; (process-send-eof)
+;; )
+;; )
+
+
+
+(defun powershell--define-set-window-width-function (proc)
+ "Sends a function definition to the PowerShell instance
+identified by PROC. The function sets the window width of the
+PowerShell virtual window. Later, the function will be called
+when the width of the emacs window changes.
+"
+ (if proc
+ (progn
+ ;;process-send-string
+ (comint-simple-send
+ proc
+ powershell--text-of-set-window-width-ps-function))))
+
+
+
+
+(defun powershell--get-max-window-width (buffer-name)
+ "Gets the maximum width of the virtual window for PowerShell running
+in the buffer with name BUFFER-NAME.
+
+In PowerShell 1.0, the maximum WindowSize.Width for
+PowerShell is 210, hardcoded, I believe. In PowerShell 2.0, the max
+windowsize.Width is provided in the RawUI.MaxPhysicalWindowSize
+property.
+
+This function does the right thing, and sets the buffer-local
+`powershell--max-window-width' variable with the correct value.
+
+"
+ (let ((proc (get-buffer-process buffer-name)))
+
+ (if proc
+ (save-excursion
+ (set-buffer buffer-name) ;; to get buffer-local variables
+
+ (powershell-invoke-command-silently
+ proc
+ powershell--find-max-window-width-command
+ 0.90)
+
+ ;; store the retrieved width
+ (setq powershell--max-window-width
+ (if (and (not (null powershell-command-reply))
+ (string-match
+ "\\([1-9][0-9]*\\)[ \t\f\v\n]+"
+ powershell-command-reply))
+ (string-to-number (match-string 1
powershell-command-reply))
+ 200)))))) ;; could go to 210, but let's use 200 to be safe
+
+
+
-;;;###autoload
-(defun powershell (&optional buffer)
- "Run an inferior powershell, by invoking the shell function. See the help
for shell for more details.
-\(Type \\[describe-mode] in the shell buffer for a list of commands.)"
+(defun powershell--set-window-width (proc)
+ "Run the PowerShell function that sets the RawUI width
+appropriately for a PowerShell shell.
+
+This is necessary to get powershell to do the right thing, as far
+as text formatting, when the emacs window gets resized.
+
+The function gets defined in powershell upon powershell startup.
+"
+ (let ((ps-width
+ (number-to-string (min powershell--max-window-width (window-width)))))
+ (progn
+ ;;(process-send-string
+ (comint-simple-send
+ proc
+ (concat powershell--set-window-width-fn-name
+ "('" ps-width "')")))))
+
+
+
+
+(defun powershell (&optional buffer prompt-string)
+
+ "Run an inferior PowerShell, with I/O through tne named
+BUFFER (which defaults to `*PowerShell*').
+
+Interactively, a prefix arg means to prompt for BUFFER.
+
+If BUFFER exists but the shell process is not running, it makes a new shell.
+
+If BUFFER exists and the shell process is running, just switch to BUFFER.
+
+If PROMPT-STRING is non-nil, sets the prompt to the given value.
+
+See the help for `shell' for more details. \(Type
+\\[describe-mode] in the shell buffer for a list of commands.)
+
+"
(interactive
(list
(and current-prefix-arg
(read-buffer "Shell buffer: "
(generate-new-buffer-name "*PowerShell*")))))
- ; get a name for the buffer
- (setq buffer (get-buffer-create (or buffer "*PowerShell*")))
- (let (
- (tmp-shellfile explicit-shell-file-name)
- )
- ; set arguments for the powershell exe.
- ; This needs to be tunable.
- (setq explicit-shell-file-name
"c:\\windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe")
- (setq explicit-powershell.exe-args '("-Command" "-" )) ; interactive, but
no command prompt
+ (setq buffer (get-buffer-create (or buffer "*PowerShell*")))
+ (powershell-log 1 "powershell starting up...in buffer %s" (buffer-name
buffer))
+ (let ((tmp-shellfile explicit-shell-file-name))
+ ;; set arguments for the powershell exe.
+ ;; Does this need to be tunable?
- ; launch the shell
+ (setq explicit-shell-file-name powershell-location-of-exe)
+ (setq explicit-powershell.exe-args '("-Command" "-" ))
(shell buffer)
+ (setq explicit-shell-file-name tmp-shellfile))
- ; restore the original shell
- (if explicit-shell-file-name
- (setq explicit-shell-file-name tmp-shellfile)
- )
- )
+ ;; (powershell--get-max-window-width "*PowerShell*")
+ ;; (powershell-invoke-command-silently (get-buffer-process "*csdeshell*")
"[Ionic.Csde.Utilities]::Version()" 2.9)
- (let (
- (proc (get-buffer-process buffer))
- )
+ ;; (comint-simple-send (get-buffer-process "*csdeshell*") "prompt\n")
- ; This sets up the powershell RawUI screen width. By default,
- ; the powershell v1.0 assumes terminal width of 80 chars.
- ;This means input gets wrapped at the 80th column. We reset the
- ; width of the PS terminal to the window width.
- (add-hook 'window-size-change-functions 'powershell-window-size-changed)
- (powershell-window-size-changed)
+ (let ((proc (get-buffer-process buffer)))
- ; ask for initial prompt
- (comint-simple-send proc "prompt")
- )
+ (make-local-variable 'powershell-prompt-regex)
+ (make-local-variable 'powershell-command-reply)
+ (make-local-variable 'powershell--max-window-width)
+ (make-local-variable 'powershell-command-timeout-seconds)
+ (make-local-variable 'powershell-squish-results-of-silent-commands)
+ (make-local-variable 'powershell--need-rawui-resize)
+ (make-local-variable 'comint-prompt-read-only)
- ; hook the kill-buffer action so we can kill the inferior process?
- (add-hook 'kill-buffer-hook 'powershell-delete-process)
+ ;; disallow backspace over the prompt:
+ (setq comint-prompt-read-only t)
- ; wrap the comint-input-sender with a PS version
- ; must do this after launching the shell!
- (make-local-variable 'comint-input-sender)
- (setq comint-input-sender 'powershell-simple-send)
+ ;; We need to tell powershell how wide the emacs window is, because
+ ;; powershell pads its output to the width it thinks its window is.
+ ;;
+ ;; The way it's done: every time the width of the emacs window changes, we
+ ;; set a flag. Then, before sending a powershell command that is
+ ;; typed into the buffer, to the actual powershell process, we check
+ ;; that flag. If it is set, we resize the powershell window
appropriately,
+ ;; before sending the command.
- ; set a preoutput filter for powershell. This will trim newlines after the
prompt.
- (add-hook 'comint-preoutput-filter-functions
'powershell-preoutput-filter-for-prompt)
+ ;; If we didn't do this, powershell output would get wrapped at a
+ ;; column width that would be different than the emacs buffer width,
+ ;; and everything would look ugly.
- ;(run-hooks 'powershell-launch-hook)
+ ;; get the maximum width for powershell - can't go beyond this
+ (powershell--get-max-window-width buffer)
- ; return the buffer created
- buffer
-)
+ ;; define the function for use within powershell to resize the window
+ (powershell--define-set-window-width-function proc)
+ ;; add the hook that sets the flag
+ (add-hook 'window-size-change-functions
+ '(lambda (&optional x)
+ (setq powershell--need-rawui-resize t)))
-(defun powershell-window-size-changed (&optional frame)
- ; do not actually resize here. instead just set a flag.
- (setq powershell-need-rawui-resize t)
-)
+ ;; set the flag so we resize properly the first time.
+ (setq powershell--need-rawui-resize t)
+ (if prompt-string
+ (progn
+ ;; This sets up a prompt for the PowerShell. The prompt is
+ ;; important because later, after sending a command to the
+ ;; shell, the scanning logic that grabs the output looks for
+ ;; the prompt string to determine that the output is complete.
+ (comint-simple-send
+ proc
+ (concat "function prompt { '" prompt-string "' }"))
+ (setq powershell-prompt-regex prompt-string)))
-(defun powershell-delete-process (&optional proc)
- (or proc
- (setq proc (get-buffer-process (current-buffer))))
- (and (processp proc)
- (delete-process proc))
- )
+ ;; hook the kill-buffer action so we can kill the inferior process?
+ (add-hook 'kill-buffer-hook 'powershell-delete-process)
+ ;; wrap the comint-input-sender with a PS version
+ ;; must do this after launching the shell!
+ (make-local-variable 'comint-input-sender)
+ (setq comint-input-sender 'powershell-simple-send)
+
+ ;; set a preoutput filter for powershell. This will trim newlines after
the prompt.
+ (add-hook 'comint-preoutput-filter-functions
'powershell-preoutput-filter-for-prompt)
+
+ ;; send a carriage-return (get the prompt)
+ (comint-send-input)
+ (accept-process-output proc))
+
+ ;; The launch hooks for powershell has not (yet?) been implemented
+ ;;(run-hooks 'powershell-launch-hook)
+
+ ;; return the buffer created
+ buffer)
+
+
+;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+;; Using powershell on emacs23, I get an error:
+;;
+;; ansi-color-process-output: Marker does not point anywhere
+;;
+;; Here's what's happening.
+;;
+;; In order to be able to read the output from powershell, this shell
+;; starts powershell.exe in "interactive mode", using the -i
+;; option. This which has the curious side-effect of turning off the
+;; prompt in powershell. Normally powershell will return its results,
+;; then emit a prompt to indicate that it is ready for more input. In
+;; interactive mode it doesn't emit the prompt. To work around this,
+;; this code (powershell.el) sends an explicit `prompt` command after
+;; sending any user-entered command to powershell. This tells powershell
+;; to explicitly return the prompt, after the results of the prior
+;; command. The prompt then shows up in the powershell buffer. Lovely.
+;;
+;; But, `ansi-color-apply-on-region` gets called after every command
+;; gets sent to powershell. It gets called with args `(begin end)`,
+;; which are both markers. Turns out the very first time this fn is
+;; called, the position for the begin marker is nil.
+;;
+;; `ansi-color-apply-on-region` calls `(goto-char begin)` (effectively),
+;; and when the position on the marker is nil, the call errors with
+;; "Marker does not point anywhere."
+;;
+;; The following advice suppresses the call to
+;; `ansi-color-apply-on-region` when the begin marker points
+;; nowhere.
+(defadvice ansi-color-apply-on-region (around
+ powershell-throttle-ansi-colorizing
+ (begin end)
+ activate compile)
+ (progn
+ (let ((start-pos (marker-position begin)))
+ (cond
+ (start-pos
+ (progn
+ ad-do-it))))))
+
+
+
+
+(defun powershell--silent-cmd-filter (process result)
+"A process filter that captures output from a shell and stores it
+to `powershell-command-reply', rather than allowing the output to
+be displayed in the shell buffer.
+
+This function is intended for internal use only.
+"
+ (let ((end-of-result
+ (string-match (concat ".*\n\\(" powershell-prompt-regex "\\)[
\n]*\\'")
+ result)))
+ (if (and end-of-result (numberp end-of-result))
+
+ (progn
+ ;; Store everything except the follow-on prompt.
+ ;; The result probably includes a final newline!
+ (setq result (substring result 0 (match-beginning 1)))
+
+ (if powershell-squish-results-of-silent-commands
+ (setq result
+ (replace-regexp-in-string "\n" "" result)))
+
+ (setq powershell-command-reply
+ (concat powershell-command-reply result)))
+
+ (progn
+ (if powershell-squish-results-of-silent-commands
+ (setq result
+ (replace-regexp-in-string "\n" "" result)))
+
+ (setq powershell-command-reply
+ (concat powershell-command-reply result))
+
+ ;; recurse. For very very long output, the recursion can
+ ;; cause stack overflow. Careful!
+ (accept-process-output process powershell-command-timeout-seconds)))))
-;; This function trims the newline from the prompt that we
-;; get back from powershell. It is set into the preoutput
-;; filters, so the newline is trimmed before being put into
-;; the output buffer.
-(defun powershell-preoutput-filter-for-prompt (string)
- (if
- ; not sure why, but I have not succeeded in using a variable here???
- ;(string-match powershell-prompt-pattern string)
- (string-match "PS [^#$%>]+>" string)
- (substring string 0 -1)
+(defun powershell-invoke-command-silently (proc command &optional
timeout-seconds)
+ "Invoke COMMAND in the PowerShell instance PROC, silently, without
+echoing the command or the results to the associated buffer. Use
+TIMEOUT-SECONDS as the timeout, waiting for a response. The COMMAND
+should be a string, and need not be terminated with a newline.
- string
+This is helpful when, for example, doing setup work. Or other sneaky
+stuff, such as resetting the size of the PowerShell virtual window.
- )
- )
+Returns the result of the command, a string, without the follow-on
+command prompt. The result will probably end in a newline. This result
+is also stored in the buffer-local variable `powershell-command-reply'.
+In some cases the result can be prepended with the command prompt, as
+when, for example, several commands have been send in succession and the
+results of the prior command were not fully processed by the application.
+
+If a PowerShell buffer is not the current buffer, this function
+should be invoked within a call to `with-current-buffer' or
+similar in order to insure that the buffer-local values of
+`powershell-command-reply', `powershell-prompt-regex', and
+`powershell-command-timeout-seconds' are used.
+
+Example:
+
+ (with-current-buffer powershell-buffer-name
+ (powershell-invoke-command-silently
+ proc
+ command-string
+ 1.90))
+
+"
+
+ (let ((old-timeout powershell-command-timeout-seconds)
+ (original-filter (process-filter proc)))
+
+ (setq powershell-command-reply nil)
+
+ (if timeout-seconds
+ (setq powershell-command-timeout-seconds timeout-seconds))
+
+ (set-process-filter proc 'powershell--silent-cmd-filter)
+
+ ;; Send the command plus the "prompt" command. The filter
+ ;; will know the command is finished when it sees the command
+ ;; prompt.
+ ;;
+ (process-send-string proc (concat command "\nprompt\n"))
+
+ (accept-process-output proc powershell-command-timeout-seconds)
+
+ ;; output of the command is now available in powershell-command-reply
+
+ ;; Trim prompt from the beginning of the output.
+ ;; this can happen for the first command through
+ ;; the shell. I think there's a race condition.
+ (if (and powershell-command-reply
+ (string-match (concat "^" powershell-prompt-regex "\\(.*\\)\\'")
+ powershell-command-reply))
+ (setq powershell-command-reply
+ (substring powershell-command-reply
+ (match-beginning 1)
+ (match-end 1))))
+
+ ;; restore the original filter
+ (set-process-filter proc original-filter)
+
+ ;; restore the original timeout
+ (if timeout-seconds
+ (setq powershell-command-timeout-seconds old-timeout))
+
+ ;; the result:
+ powershell-command-reply))
+
+
+
+
+(defun powershell-delete-process (&optional proc)
+ (or proc
+ (setq proc (get-buffer-process (current-buffer))))
+ (and (processp proc)
+ (delete-process proc)))
+
+
+(defun powershell-preoutput-filter-for-prompt (string)
+ "Trim the newline from STRING, the prompt that we get back from
+powershell. This fn is set into the preoutput filters, so the
+newline is trimmed before being put into the output buffer.
+"
+ (if (string-match (concat powershell-prompt-regex "\n\\'") string)
+ (substring string 0 -1) ;; remove newline
+ string))
(defun powershell-simple-send (proc string)
- "Override of the comint-simple-send function, specific for powershell.
-This just sends STRING, plus the prompt command. Normally powershell is in
-noninteractive model when run as an inferior shell with stdin/stdout
-redirected, which is the case when running as a shell within emacs.
-This function insures we get and display the prompt. "
- ; resize if necessary. We do this by sending a resize string to the shell,
- ; before sending the actual command to the shell.
- (if powershell-need-rawui-resize
- (and
- (comint-simple-send proc (powershell-gen-window-width-string))
- (setq powershell-need-rawui-resize nil)
- )
- )
- (comint-simple-send proc string)
- (comint-simple-send proc "prompt")
-)
+ "Override of the comint-simple-send function, with logic
+specifically designed for powershell. This just sends STRING,
+plus the prompt command.
+
+When running as an inferior shell with stdin/stdout redirected,
+powershell is in noninteractive mode. This means no prompts get
+emitted when a PS command completes. This makes it difficult for
+a comint mode to determine when the command has completed.
+Therefore, we send an explicit request for the prompt, after
+sending the actual (primary) command. When the primary command
+completes, Powershell then responds to the \"prompt\" command,
+and emits the prompt.
+
+This insures we get and display the prompt.
+"
+ ;; Tell PowerShell to resize its virtual window, if necessary. We do
+ ;; this by calling a resize function in the PowerShell, before sending
+ ;; the user-entered command to the shell.
+ ;;
+ ;; Powershell keeps track of its \"console\", and formats its output
+ ;; according to the width it thinks it is using. This is true even when
+ ;; powershell is invoked with the - argument, which tells it to use
+ ;; stdin as input.
+
+ ;; Therefore, if the user has resized the emacs window since the last
+ ;; PowerShell command, we need to tell PowerShell to change the size
+ ;; of its virtual window. Calling that function does not change the
+ ;; size of a window that is visible on screen - it only changes the
+ ;; size of the virtual window that PowerShell thinks it is using. We
+ ;; do that by invoking the PowerShell function that this module
+ ;; defined for that purpose.
+ ;;
+ (if powershell--need-rawui-resize
+ (progn
+ (powershell--set-window-width proc)
+ (setq powershell--need-rawui-resize nil)))
+ (comint-simple-send proc (concat string "\n"))
+ (comint-simple-send proc "prompt\n"))
+
+
+
+;; Notes on TAB for completion.
+;; -------------------------------------------------------
+;; Emacs calls comint-dynamic-complete when the TAB key is pressed in a shell.
+;; This is set up in shell-mode-map.
+;;
+;; comint-dynamic-complete calls the functions in
comint-dynamic-complete-functions,
+;; until one of them returns non-nil.
+;;
+;; comint-dynamic-complete-functions is a good thing to set in the mode hook.
+;;
+;; The default value for that var in a powershell shell is:
+;; (comint-replace-by-expanded-history
+;; shell-dynamic-complete-environment-variable
+;; shell-dynamic-complete-command
+;; shell-replace-by-expanded-directory
+;; comint-dynamic-complete-filename)
+
+
+
+;; (defun powershell-dynamic-complete-command ()
+;; "Dynamically complete the command at point.
+;; This function is similar to `comint-dynamic-complete-filename', except that
it
+;; searches the commands from powershell and then the `exec-path' (minus the
+;; trailing Emacs library path) for completion
+;; candidates.
+
+;; Completion is dependent on the value of `shell-completion-execonly', plus
+;; those that effect file completion. See
`powershell-dynamic-complete-as-command'.
+
+;; Returns t if successful."
+;; (interactive)
+;; (let ((filename (comint-match-partial-filename)))
+;; (if (and filename
+;; (save-match-data (not (string-match "[~/]" filename)))
+;; (eq (match-beginning 0)
+;; (save-excursion (shell-backward-command 1) (point))))
+;; (prog2 (message "Completing command name...")
+;; (powershell-dynamic-complete-as-command)))))
+
+
+;; (defun powershell-dynamic-complete-as-command ()
+;; "Dynamically complete at point as a command.
+;; See `shell-dynamic-complete-filename'. Returns t if successful."
+;; (let* ((filename (or (comint-match-partial-filename) ""))
+;; (filenondir (file-name-nondirectory filename))
+;; (path-dirs (cdr (reverse exec-path)))
+;; (cwd (file-name-as-directory (expand-file-name default-directory)))
+;; (ignored-extensions
+;; (and comint-completion-fignore
+;; (mapconcat (function (lambda (x) (concat (regexp-quote x)
"$")))
+;; comint-completion-fignore "\\|")))
+;; (dir "") (comps-in-dir ())
+;; (file "") (abs-file-name "") (completions ()))
+
+;; ;; Go thru each cmd in powershell's lexicon, finding completions.
+
+;; ;; Go thru each dir in the search path, finding completions.
+;; (while path-dirs
+;; (setq dir (file-name-as-directory (comint-directory (or (car
path-dirs) ".")))
+;; comps-in-dir (and (file-accessible-directory-p dir)
+;; (file-name-all-completions filenondir dir)))
+;; ;; Go thru each completion found, to see whether it should be used.
+;; (while comps-in-dir
+;; (setq file (car comps-in-dir)
+;; abs-file-name (concat dir file))
+;; (if (and (not (member file completions))
+;; (not (and ignored-extensions
+;; (string-match ignored-extensions file)))
+;; (or (string-equal dir cwd)
+;; (not (file-directory-p abs-file-name)))
+;; (or (null shell-completion-execonly)
+;; (file-executable-p abs-file-name)))
+;; (setq completions (cons file completions)))
+;; (setq comps-in-dir (cdr comps-in-dir)))
+;; (setq path-dirs (cdr path-dirs)))
+;; ;; OK, we've got a list of completions.
+;; (let ((success (let ((comint-completion-addsuffix nil))
+;; (comint-dynamic-simple-complete filenondir
completions))))
+;; (if (and (memq success '(sole shortest)) comint-completion-addsuffix
+;; (not (file-directory-p (comint-match-partial-filename))))
+;; (insert " "))
+;; success)))
+
+
+(provide 'powershell)
+
+;;; powershell.el ends here