# Editing text using $EDITOR from program

**URL:** <https://www.ruby-forum.com/t/editing-text-using-editor-from-program/190852>\
**Category:** Ruby\
**Created:** [June 29, 2010, 7:31am UTC](https://www.ruby-forum.com/t/editing-text-using-editor-from-program/190852 "2010-06-29T07:31:15Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![sentinel](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/fbc32d/32.png) [@sentinel](https://www.ruby-forum.com/u/sentinel)\
**Post date:** [June 29, 2010, 7:31am UTC](https://www.ruby-forum.com/t/editing-text-using-editor-from-program/190852/1 "2010-06-29T07:31:15Z")

</div>

Does Highline or Thor/Facets/ActiveSupport etc have a method for when  
you want user to edit multiline text using the $EDITOR value?

Commander has but it does not work with vim. So i wrote a method, if  
anyone has time please give it a look and give me feedback. Is it  
correct to pass back a nil if user does not edit, or should I pass a  
blank String.

Thanks. (File attached).  
[http://gist.github.com/456809](http://gist.github.com/456809)

Also, following is the method “commander” has. It is supposed to work  
with TextMate but does not with Vim. It does open TextEdit in the  
background, but does not seem to put the text in it.

def ask\_editor input = nil, editor = ENV[‘EDITOR’] || ‘mate’  
IO.popen(editor.to\_s, ‘w+’) do |pipe|  
pipe.puts input.to\_s unless input.nil?  
pipe.close\_write  
pipe.read  
end  
end

I’d like to know why the developer of commander used this instead of a  
“system” command. In what way is this superior ? Why does it fail with  
vim.

---

<div class="post-metadata">

**Author:** ![sentinel](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/fbc32d/32.png) [@sentinel](https://www.ruby-forum.com/u/sentinel)\
**Post date:** [July 5, 2010, 11:39pm UTC](https://www.ruby-forum.com/t/editing-text-using-editor-from-program/190852/2 "2010-07-05T23:39:56Z")

</div>

On 6/28/10, R… Kumar [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Does Highline or Thor/Facets/ActiveSupport etc have a method for when  
> you want user to edit multiline text using the $EDITOR value?

Ok, this was a week ago now, but no one else has responded.

> Commander has but it does not work with vim.  
> [snip]  
> end  
> This code is wrong. EDITOR is supposed to be set to a text-mode  
> editor, which runs inside a text terminal environment. If the user has  
> a graphical editor he wants to use, such as gvim, gnotepad, or  
> textmate, he should set the VISUAL environment variable instead.  
> ask\_editor or equivalent code should look for VISUAL first and then  
> EDITOR only if VISUAL is not set.

A gui editor needs to be called in a different way than a text-mode  
editor is called. A gui editor needs no controlling text-mode  
terminal, whereas a text-mode editor requires one. A gui editor should  
be called asynchronously whereas a text-mode editor must be called  
synchronously. A gui editor doesn’t (really) use stdin/stdout/stderr  
whereas a text-mode editor must use them.

The code above is designed to be used with a gui editor, even tho it  
consults the EDITOR environment variable. It starts the editor process  
asynchronously. It captures the subprocess’s stdin and uses it for a  
different purpose, which prevents vim from ever getting any input from  
the user.  
These factors sccount for the problems you observed, I believe.  
ENV[‘EDITOR’] in that method should be replaced with ENV[‘VISUAL’].  
(And it should be expanded to use EDITOR if that is set as well…)

> So i wrote a method, if  
> anyone has time please give it a look and give me feedback. Is it  
> correct to pass back a nil if user does not edit, or should I pass a  
> blank String.
> 
> Thanks. (File attached).  
> [http://gist.github.com/456809](http://gist.github.com/456809)

> I’d like to know why the developer of commander used this instead of a  
> “system” command. In what way is this superior ? Why does it fail with  
> vim.
> 
> Attachments:  
> [http://www.ruby-forum.com/attachment/4822/edit.rb](http://www.ruby-forum.com/attachment/4822/edit.rb)

Your code looks pretty good to me. You really ought to check for  
VISUAL as well, as I outlined above. If VISUAL is set, do something  
like what the ask\_editor code you posted above is doing. Except I  
don’t think you should assume you can use stdin and stdout to  
communicate with a gui editor; that sounds too textmate specific to  
me. Working with temp files like you have for the EDITOR case is the  
most portable and general plan.

Some other minor nits:

1. quote all words on the command line, to handle spaces in filenames  
better. Better yet, break the command line up into words, or rather  
don’t join them together in one line. So, replace  
system(“#{ed} #{temp.path}”)  
with  
system(ed, temp.path)

2. I’d just read the whole file in one big slirp instead of line by  
line. (Unless you think you’re user is really going to write a whole  
lot of stuff… in which case you need to rewrite anyway.)

---

<div class="post-metadata">

**Author:** ![sentinel](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/fbc32d/32.png) [@sentinel](https://www.ruby-forum.com/u/sentinel)\
**Post date:** [July 6, 2010, 7:20am UTC](https://www.ruby-forum.com/t/editing-text-using-editor-from-program/190852/3 "2010-07-06T07:20:22Z")

</div>

Caleb C. wrote:

> On 6/28/10, R… Kumar [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:
> 
> > Does Highline or Thor/Facets/ActiveSupport etc have a method for when  
> > you want user to edit multiline text using the $EDITOR value?

> system(“#{ed} #{temp.path}”)  
> with  
> system(ed, temp.path)
> 
> 1. I’d just read the whole file in one big slirp instead of line by  
> line. (Unless you think you’re user is really going to write a whole  
> lot of stuff… in which case you need to rewrite anyway.)

Thanks a lot for your feedback. Could you give me some on this other  
issue.

I am mailing some data to the user using the system “cat file | mail -s  
“title” removed\_email\_address@domain.invalid” format.  
I’ve used the system() for this and it works fine. I am doing a “cat”  
rather than “echo” since the data can have single or double quotes. Is  
it okay to use the system command this way, with a pipe inside it ?  
Looks clumsy to me!

I looked back some old code of mine where i used “sendmail” and there I  
do:

def pipe\_output (str)  
pipeto = ‘/usr/sbin/sendmail -t’  
if pipeto != nil # i was taking pipeto from a hash, so checking  
proc = IO.popen(pipeto, “w+”)  
proc.puts str  
proc.close\_write  
#@main.log.info(proc.gets)  
end  
end

This works fine, too.

However, since “mail -s” requires both input, as well as some command  
line parameters (title and emailid), i was not clear whether this method  
would work.

thx.

---

<div class="post-metadata">

**Author:** ![sentinel](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/fbc32d/32.png) [@sentinel](https://www.ruby-forum.com/u/sentinel)\
**Post date:** [July 6, 2010, 3:30pm UTC](https://www.ruby-forum.com/t/editing-text-using-editor-from-program/190852/4 "2010-07-06T15:30:41Z")

</div>

On 7/6/10, R… Kumar 1.9.1 OSX [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> str = “this is some data to go in mail”  
> pipe\_output str
> 
> Not sure which is cleaner and more reliable though, this way or:
> 
> cmd = %q{cat file.txt | mail -s “title” email}  
> system(cmd)

Either way should work fine, but I would go with popen, personally. I  
think it’s a little cleaner semantically; might be a tad more portable  
too.

---

<div class="post-metadata">

**Author:** ![sentinel](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/fbc32d/32.png) [@sentinel](https://www.ruby-forum.com/u/sentinel)\
**Post date:** [July 6, 2010, 9:46am UTC](https://www.ruby-forum.com/t/editing-text-using-editor-from-program/190852/5 "2010-07-06T09:46:20Z")

</div>

R… Kumar 1.9.1 OSX wrote:

> However, since “mail -s” requires both input, as well as some command  
> line parameters (title and emailid), i was not clear whether this method  
> would work.
> 
> thx.

This works:

def pipe\_output (str)

```
pipeto = %q{mail -s "a title" myemail }
if pipeto != nil # i was taking pipeto from a hash, so checking
  proc = IO.popen(pipeto, "w+")
  proc.puts str
  proc.close_write
  #puts proc.gets
end

```

end

str = “this is some data to go in mail”  
pipe\_output str

Not sure which is cleaner and more reliable though, this way or:

cmd = %q{cat file.txt | mail -s “title” email}  
system(cmd)
