Open Vim from a Rakefile? - ruby

I am creating a journal application for personal notes and have the following in my Rakefile:
task :new do
entry_name = "Entries/#{Time.now.to_s.gsub(/[-\ :]+/, '.').gsub(/.0500+/,'')}.md"
`touch #{entry_name}`
`echo "# $(date)" >> #{entry_name}`
end
The last part I would like to include is the opening of the Vim text editor but I am unable to figure out how to open it as if I called it directly from the bash terminal.
I have tried:
vim #{entry_name}
but unfortunately I think both of those open it as a background process.
I have been referencing "6 Ways to Run Shell Commands in Ruby".

As in the article you referenced, `s run the command in a subshell within the current process, but the real problem is that it's trying to take the output from the command run as well, which doesn't play nice with Vim.
You can either:
Use exec to replace the current process with the new one (note that the Ruby/Rake process will end once you've called exec, and nothing after it will run).
Use system to create a subshell like `s, but avoids the problem of trying to grab Vim's stdout. Unlike exec, after Vim terminates, Ruby will continue.

you need to pass the tty as standard input for backspaces etc. to work well in vim:
exec("</dev/tty vim a b")
obviously the backtick (`) didn't work but I was having issues with system/exec from a script.
first I get Vim: Warning: Input is not from a terminal, and then I see ^? when I use backspace.

Related

How do I edit current shell command without executing it?

There seems to be quite a lot of information on how to edit and execute a command using your editor using "edit-and-execute-command (C-x C-e)", but what I would like to achieve is take the current shell command, apply certain filtering (using a script) and then return it to prompt for further approval/manual changes before execution. Is this possible with bash?
Latest update based on my experience
The part 0"+y$dd in the following mapping is really something that you should carefully think about and tailor it to your taste/workflow/experience.
For instance, very frequently I've found myself ending up with multiple lines in the buffer, where I only want to execute the one the cursor is on; in this case I can use 0"+y$dd:%d<CR> instead of 0"+y$dd.
And this is just one of the possible scenarios.
Final answer for those who like vim
Set vim as your EDITOR/VISUAL, so that when editing a command line, you will use vim to edit it.
Put au BufEnter /tmp/bash-fc.* nn <Leader>d 0"+y$dd:wq<CR> in your ~/.vimrc file to map Leaderd (which you will rarely use when editing a command) to the action "delete the current line into the + register without the trailing EOL".
you can use either the + or the * register in the mapping above; the ways to paste into the terminal will likely differ; you need the +clipboard option for these registers to be available.
When finished editing a command in the vim editor, hit EscapeLeaderd.
Paste the clipboard into the terminal (this is terminal-dependent).
Original answer
I often need to do the same, and I do it as follows. (I normally use the set -o vi in bash, so points 1 and 2 in the following are different if you use set -o emacs, the default; based on your question it looks like points 1 and 2 are unified in Ctrl+x followed by Ctrl+e, which is harder to type, imho.)
hit Escape to be in normal mode,
hit v to enter the editor to edit the command,
edit the command as I like,
(This is where you ask the question.)
hit Escape0"+y$dd:wq,
Note: 0"+y$, not simply "+yy, as the latter would copy the newline too, and this would result in executing the command upon pasting it in the command line,
paste the clipboard on the command line
how to do this depends on the terminal you are using, I guess; I hit Ctrl+Alt+v in URxvt.
proceed to approval/manual edit.
Clearly this is just a workaround, consisting in copying the edited command into the clipboard before deleting the whole command, so that nothing gets executed upon exiting the editor; however it's the best I can get for myself.
Update
As my EDITOR (and VISUAL) is equal to vim, when I edit the command, I edit it in vim.
In this respect, I have noticed that the buffer is named /tmp/bash-fc.random, where random is a 6-characters alphanumeric random string.
This gives space to a lot of possiblities, if you use vim as your editor, as you can define some mapping in your .vimrc to execute the whole sequence Escape0"+y$dd:wq. For instance, one command that you'd rarely use when editing a command line is Leaderd; therefore you can put the following mapping in your .vimrc file
au BufEnter /tmp/bash-fc.* nn <Leader>d 0"+y$dd:wq<CR>
so that step 4 in the above recipe becomes
hit EscapeLeaderd
It's not possible to do that in Bash/readline but it's possible in zsh
using edit-command-line command:
darkstar% autoload edit-command-line; zle -N edit-command-line
darkstar% bindkey "^X^E" edit-command-line
Now press Control-x Control-e to open your editor, edit line, leave the editor - you will see the updated command line but it will not be executed automatically.
Now that I think about it, maybe a variation of what #kenorb suggested in a comment is the best workaround (as it seems no solution exists), if we want to stick to bash.
What you can do is prepend a # (the comment character in bash) to the command, rather than echo. Then when you exit the editor, the command will be ineffective, and you will only have to press arrow up (or k, if you use set -o vi), remove the # and confirming.
Note that this strategy adds just a few keystrokes, so it can be fairly efficient, depending on your typing level.
These pieces might get you closer:
a) replace the the normal binding for newline newline (ctrl-M)
bind -x '"\C-M":date"
b) grab the current line from the history using !#
replace date with whatever script you want.
c) edit manually
d) if necessary, somehow tie in !!:p which prints the new command to the command line but does not execute it, thus letting you manually edit it.
e) using ctrl-J submit edited command rather than a newline
or they might not ....
There is an option in bash to modify command from history without executing it. I'm not sure it it's possible to use script for this, doesn't seem to be likely. Although, you can make modifications using history modifiers.
Enable option histverify to prevent execution of modified command
Use chain of modifiers to change last command
Use "!!" to put your result to command line for final edit
Here is how it looks:
$ shopt -s histverify
$ ls *.sh
script1.sh script2.sh script3.sh script-return.sh
$ !!:s/*/script1/:p
ls script1.sh
$ !!:s/1/2/:p
ls script2.sh
$ !!
$ ls script2.sh
script2.sh
I'd like to point you to the Composure framework for Bash (I'm not affiliated with it): https://github.com/erichs/composure
It provides draft and revise functions that sound like they could help with what you're trying to do. Here's a (long) quote from the project's readme file:
Composure helps by letting you quickly draft simple shell functions,
breaking down your long pipe filters and complex commands into
readable and reusable chunks.
Draft first, ask questions later
Once you've crafted your gem of a command, don't throw it away! Use
draft () and give it a good name. This stores your last command as a
function you can reuse later. Think of it like a rough draft.
$ cat servers.txt
bashful: up
doc: down
up-arrow
$ cat servers.txt | grep down
doc: down
$ draft finddown
$ finddown | mail -s "down server(s)" admin#here.com
Revise, revise, revise!
Now that you've got a minimal shell function, you may want to make it
better through refactoring and revision. Use the revise () command
to revise your shell function in your favorite editor.
generalize functions with input parameters
add or remove functionality
add supporting metadata for documentation
$ revise finddown
finddown ()
{
about finds servers marked 'down' in text file
group admin
cat $1 | grep down
}
$ finddown servers.txt
doc: down
It does not seem possible with a keyboard shortcut, at least:
$ bind -P | grep -e command -e edit
complete-command can be found on "\e!".
edit-and-execute-command can be found on "\C-x\C-e".
emacs-editing-mode is not bound to any keys
possible-command-completions can be found on "\C-x!".
vi-editing-mode is not bound to any keys
This can be done in native bash using readline specifically READLINE_LINE and READLINE_POINT variables. I use this functionality all the time though not through vim, you would need to get the value of $selected from your vim command and if not empty it takes your original line + your input and replaces your original line with the combination without executing. output as a variable
_main() {
selected="$(__coms_select__ "$#")"
origonal_text=$READLINE_LINE READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
}
bind -m emacs-standard -x '"\C-e": _main '
bind -m vi-command -x '"\C-e": _main '
bind -m vi-insert -x '"\C-e": _main '
Edit
Just remembered these two utilities that will let you do this as well.
Vipe allows you to run your editor in the middle of a unix pipeline and edit the data that is being piped between programs.
vp, up, vipe, Neomux (upgrade of nvim terminal) you can do some pretty neat throwing buffers between the terminal and split window.
and Athame (full vim on the command line)
https://github.com/ardagnir/athame
careful with that one though plugins work on the cli and it can get funky if you got tons of plugins

How to test external tab completion script in Ruby

So say I have an external command, fart, that supports tab completion for its subcommands. When you type fart <\tab><\tab> in the command line, it prints out something like
$ > fart █
do some things here
or other stuff there
however, if you type fart <\enter>, it prints the help text
fart is a tool for doing things and stuff
Usage: fart <command>
Commands:
init Initialize a new something or other
status Report the status of the thing
version Show the fart version information
I want to test that the tab completion works via an integration test suite in Ruby. When I tell exec to execute fart \t\t, I get the help output, not the autocomplete output. How can I get exec to not terminate the shell command with a newline so that I can assert the autocompletion output?
You shouldn't fa^M^M exec the thing. It doesn't work like this. You need to interact with a PTY. Basically spawn a shell in a PTY, then send keys like you would do interactively.
Alternatively you could write expect scripts and use the expect utility found in most linux distros.
Auto-completion is a shell function, not a program function. When you hit tab, bash or some other shell is looking for completion definitions and uses them to show you the available options. It is not executing the program in any way.

Erroneously using STDIN when pasting a script into the Terminal.app

I have the following script-snipped, which I use regularly to semi-automate one of my workflows.
I open a bash terminal and start irb and then paste the script:
require 'highline/import'
# ...
user = ask("User:")
repo = ask("Repository:")
# ...
# Do advanced fancy stuff
Now I want to be able to paste this into the terminal, but the first prompt for the user is overwritten by the following line: repo = ask("Repository:").
Is there a way to prevent this behaviour.
I know that I could write a script. But
I find it convenient to be able to just paste this into the terminal
I wonder how the STDIN can be somehow outfoxed in this case
Is there a way to prevent this behaviour.
You can prevent the echoing of pasted text with the command
stty -echo
- whether you like that is another question, since this inevitably also prevents the echoing of typed-in commands, but you can see again after entering
stty echo

Running shell commands in GVim without echoing the Vim command

When I use :! to run shell commands, like:
!echo hi
It prints both the VimScript command and it's output, so I get:
:!echo hi
hi
This is OK when I do it in command line mode, but when I run it via a .vim file I don't want to see it - I just want to see the result of the command.
Is there a way to disable the echoing of the VimScript command?
I know I can use
echo system('echo hi')
But that would prevent me from using it with interactive shell programs...
BTW, I'm using Linux - in windows this is not really a problem since shell commands run on a new console window anyways...
edit:
This is my small working example:
function! RunShellTask(cmd)
execute '!'.a:cmd
return v:shell_error
endfunction
call RunShellTask('echo hi')
I run it with :source %
You could try the :redir command:
*:redi* *:redir*
:redi[r][!] > {file} Redirect messages to file {file}. The messages which
are the output of commands are written to that file,
until redirection ends. The messages are also still
shown on the screen. When [!] is included, an
:
:
To stop the messages and commands from being echoed to
the screen, put the commands in a function and call it
with ":silent call Function()".
An alternative is to use the 'verbosefile' option,
this can be used in combination with ":redir".
I haven't tested, but you could try :redir #b, execute the shell commands from a function called with :silent call, read the output (from register b), filter out the vimscript commands, display it on the screen and then :redir end.
Another option is to try some plugins that provide similar functionality:
shellasync.vim : shellasync.vim plugin for asynchronously executing shell commands in vim
Conque Shell : Run interactive commands inside a Vim buffer
Screen (vim + gnu screen/tmux) : Simulate a split shell, using gnu screen or tmux, that you can send commands to.
Vicle : Vim - Interpreter Command Line Editor. Like Chimp or Slimv.

can the shell be told to save command output?

I'm thinking a hypothetical CMDOUTPUT would be useful:
locate -r 'regexp...' # locate finds a file: /myfile.
# Shell puts `/myfile' string into CMDOUTPUT
vim $CMDOUTPUT # No need to run locate again as with: vim `!!`
The locate command above is just an example. I want the output saved for all commands that I run so that if I need it I can access it quickly. (The output should still be printed by the command to stdout.) I don't want to do
CMDOUTPUT="$(...)"
or
command | tee /tmp/cmdoutput
or anything else that I have to do because that's more typing for me at the prompt for everything that I run: I want the shell to do it all in the background. Again, to make it clear: I am casually typing commands away and decide "Oh, I want to use the output of that last command in this command, let me just retrieve it...". Can I tell the shell to store the output somehow so that I can retrieve it.
If there's no option for it, is there some way that I can implement it that is as close to invisible as it can be, meaning exit codes from the command are not lost (...and that's all I can think of, but I'm sure there are other subtleties) etc. I'm primarily thinking of zsh, but answers for any shell would be useful.
I found a solution, not sure if this is exactly what you're looking for. But it should provide a start :)
zsh | tee log >&1

Resources