5

I am transitioning to Emacs with evil-mode after years of Vim. For work reasons I have to use a GUI statistics program (Stata) frequently. This program does not integrate well with external editors. ess-mode doesn't support the GUI version of Stata but only a very limited terminal version. I prefer a simple solution that I can maintain myself.

To make it work with Vim, I wrote a shell script using xdotool that takes as argument a filename, opens the GUI window, pastes "do filename" in the command window, sends return and then goes back to the original editor window. It's not a sophisticated solution but works well in practice.

In my .vimrc I had some code that would write the currently selected region or the current line to a temporary file in /tmp/randomfoldername/consecutivenumber.do, then call the shell script with that file as the argument. Upon leaving vim, the folder would be deleted.

I am looking to replicate this functionality in Emacs, but don't know how or where to start. I essentially want to mimick what the ess-mode functions ess-eval-region-or-line-and-step and ess-eval-buffer are doing, simply by calling the shell script with the name of a temporary file or the name of the current buffer.

If it helps, this is the vimscript code:

 " Run current buffer fun! RunIt() w !sh "~/dotfiles/rundo.sh" "%:p" endfun " Run selection fun! RunDoLines() let selectedLines = getbufline('%', line("'<"), line("'>")) if col("'>") < strlen(getline(line("'>"))) let selectedLines[-1] = strpart(selectedLines[-1], 0, col("'>")) endif if col("'<") != 1 let selectedLines[0] = strpart(selectedLines[0], col("'<")-1) endif let temp = tempname() . ".do" call writefile(selectedLines, temp) exec "!sh ~/dotfiles/rundo.sh " . temp au VimLeave * silent exe '!del /Q "'.$TEMP.'\*.tmp.do"' endfun " Mappings au FileType stata noremap <F8> :<C-U>call RunIt()<CR><CR> au FileType stata noremap <F9> :<C-U>call RunDoLines()<CR><CR> au FileType stata noremap <C-Return> <S-v>:<C-U>call RunDoLines()<CR><CR> 
2
  • Are you running Windows? Commented May 12, 2019 at 10:27
  • No, running Linux. Commented May 13, 2019 at 0:22

1 Answer 1

5

If your ~/dotfiles/rundo.sh accepts stdin as input, like many other commands such as grep/wc/bash/python, to run a command with the region as stdin, simply run M-| ~/dotfiles/rundo.sh (M-| runs shell-command-on-region).

If the command doesn't support stdin, the following should do what you described

(defun rundo (beg end) "Wrapper of ~/dotfiles/rundo.sh." (interactive ;; 1. If the region is highlighted (if (use-region-p) ;; the region (list (region-beginning) (region-end)) ;; the line (list (line-beginning-position) (line-end-position)))) ;; 2. create a temp file (let ((tempfile (make-temp-file nil nil ".do"))) ;; 3. save text to the file (write-region beg end tempfile) ;; 4. run the command asynchronously ;; (remove '&' to run it synchronously, i.e., blocking Emacs) (shell-command (format "~/dotfiles/rundo.sh %s &" tempfile)))) 
6
  • Thanks, the function does what I wanted. For some reason running it synchronously blocks emacs and doesn't do anything. With the & it works fine. Is there a way to get rid of the empty *Async shell command* buffer that is opened every time shell-command is executed? Commented May 12, 2019 at 5:08
  • Actually, it only sends the selected reason. When nothing is selected, the tmp file is empty instead of containing the current line. Commented May 12, 2019 at 5:21
  • @ilprincipe The empty buffer tells you the command starts without error and gives no output, its mode-line also displays the status of the command (running or finished). If you don't need these, you can use start-process-shell-command, e.g., (start-process-shell-command "rundo" nil (format "~/dotfiles/rundo.sh %s" tempfile)). You don't need & in start-process-shell-command because start-process-shell-command already runs command asynchronously. Commented May 12, 2019 at 5:27
  • @ilprincipe I don't think so, read the code to see how it chooses the region. When nothing is selected, (use-region-p) returns nil. Commented May 12, 2019 at 5:30
  • Thanks, that gets rid of the buffer. You're right, the tmp file is correct and contains the line. It just doesn't get executed for some reason. I might need to add a newline character. At which point could I best add something like deactivate-mark to deselect the region and (forward-line 1) to move the cursor? Commented May 12, 2019 at 6:03

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.