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

Reply via email to