Running a custom command with Vim - vim

I want to be create an artisan command in Vim, so I can do something like:
:artisan make:migration create_blah_table --create=blah
And have it run the following:
ssh -t vagrant "cd /var/www && php artisan make:migration create_blah_table --create=blah"
Is this possible?
Update
The make:migration create_blah_table --create=blah bit could be anything.

See :help user-commands:
It is possible to define your own Ex commands. A user-defined command can act
just like a built-in command (it can have a range or arguments, arguments can
be completed as filenames or buffer names, etc), except that when the command
is executed, it is transformed into a normal Ex command and then executed.
For starters: See section |40.2| in the user manual.
*E183* *E841* *user-cmd-ambiguous*
All user defined commands must start with an uppercase letter, to avoid
confusion with builtin commands.
You want :command
:com[mand][!] [{attr}...] {cmd} {rep}
Define a user command. The name of the command is
{cmd} and its replacement text is {rep}. The command's
attributes (see below) are {attr}. If the command
already exists, an error is reported, unless a ! is
specified, in which case the command is redefined.
You can put this in your ~/.vimrc:
command -nargs=* Artisan !ssh -t vagrant "cd /var/www && php artisan <args>"
And then use the :Artisan make:migration create_blah_table --create=blah command in ex-mode.

Related

How to redirect vim's output to terminal?

I'm currently writing a small bash script to create a pre-filled file at a specific location. To do so, my bash script call vim and execute a vim script.
In this vim script, I want to ask to the user if the location where the created file is going to be saved is the correct one. The user then enter yes or no.
To do so, I'm using this set of command:
call inputsave()
let name = input('Is it the correct location ? y/n')
call inputrestore()
this input function works fine when I'm in gvim or vim. But from a script, nothing is displayed is the terminal where I launched my bash script.
Is there a way to redirect outputs to my terminal ?
I found :redir > {file} but this is obviously redirecting all vim outputs to a file which not interactive.
I also managed to echo something int the terminal by using this:
let tmp = "!echo what I want to display"
execute tmp
unlet tmp
but again this is only to display something. I won't be able to enter an input
my bash script:
#!/bin/bash
touch tmp.txt #file used to pass argument to vim
echo "$1" >> tmp.txt #append argument to the end of the txt file
vim -e tmp.txt <create_new_file.vim #launch vim and execute the scrip inside create_new_file.vim
rm tmp.txt
echo "new file created"
the create_new_file.vim basically just call my function CreateFile(_name) located in my .vimrc. This is this function who call inputsave(), input() and inputrestore().
You're feeding commands directly into Vim via standard input; this way, Vim has no way to interact with you (the user), and this use of Vim is atypical and therefore discouraged.
Alternatives
Unless you really need special Vim capabilities, you're probably better off using non-interactive tools like sed, awk, or Perl / Python / Ruby / your favorite scripting language here.
That said, you can use Vim non-interactively:
Full Automation
For more advanced processing involving multiple windows, and real automation of Vim (where you might interact with the user or leave Vim running to let the user take over), use:
vim -N -u NONE -n -c "set nomore" -S "create_new_file.vim" tmp.txt
This should allow you to interact with Vim while following a script of commands.
Here's a summary of the used (or useful) arguments:
-N -u NONE Do not load vimrc and plugins, alternatively:
--noplugin Do not load plugins.
-n No swapfile.
-i NONE Ignore the |viminfo| file (to avoid disturbing the
user's settings).
-es Ex mode + silent batch mode -s-ex
Attention: Must be given in that order!
-S ... Source script.
-c 'set nomore' Suppress the more-prompt when the screen is filled
with messages or output to avoid blocking.

Sublimetext integration with Windows Subsystem for Linux (WSL, bash)

Is there a way of executing bash commands from sublimetext?
In many projects it's just better developing with linux tools via the Windows Subsystem for Linux (WSL, bash) as most tools are built for linux.
In windows, you can run bash commands in a windows-console like this:
bash -c "echo \"my bash command here\""
Let's say I want to run a very specific build script like this:
bash -c "prettier mypath/ && eslint mypath/ --fix"
or like this:
bash -c "my_very_specific_build_script.sh"
or even better, having a hook executing a linter via a hook with:
https://packagecontrol.io/packages/Hooks
bash -c "my_linting_script.sh"
and then call the script on every "save" like this:
"on_post_save_user":
[
{
"command": "bash_execute",
"args": {
"command": "my_linting_script.sh",
}
}
],
This would be a game changer for all of us developers without a mac
MY ADVANCE SO FAR
In Keybindings this works (creates a log when ctrl+alt+b pressed)
{
"keys": ["ctrl+alt+b"],
"command": "exec",
"args": {
"cmd": "bash -c \"cd ~ && echo ok >> log.txt\"",
}
}
And this works in preferences triggered on every "save" using the "hooks" plugin:
"on_post_save_user": [
{
"command": "exec",
"args": {
"cmd": "bash -c \"cd ~ && echo ok >> log.txt\"",
},
"scope": "window"
}
],
Is this the best way?
An attempt at creating a plugin
I made a simple plugin that successfully runs "top" with bash in windows with WSL.
https://github.com/eduardoarandah/sublime-bash-execute
Just copy BashCommand.py in Packages/User and run in console:
view.run_command('bash_exec')
I've never made a sublime plugin, any help would be credited, appreciated and so on.
The internal exec command can run any arbitrary command that you choose to throw at it, so it's possible to use it directly to do this using that command if so desired.
Something to note is that the exec command can be given either cmd as a ["list", "of", "strings"] or shell_cmd as "a single string". When using cmd, the first item in the list is invoked directly and everything else is passed as arguments, while in the second case the string is passed directly to the shell as is.
Generally for items such as you're mentioning here, you want to use shell_cmd; since it's passed directly to the shell to execute, it can contain things such as redirection and chained commands.
As noted in your examples above, you can use shell_cmd and prefix any command that you want to execute with bash -c, wrapping the rest of the command in double quotes, and the exec command will run bash and pass it the command that you provided.
It's also possible to create a simple command in Sublime that behaves like exec does, while at the same time automatically injecting the appropriate item in there to get bash to execute the command for you.
Such a plugin might look like the following:
import sublime
import sublime_plugin
from Default.exec import ExecCommand
class BashExecCommand(ExecCommand):
"""
A drop in replacement for the internal exec command that will run the
given command directly in bash. Use of "shell_cmd" is recommended, but
some care is taken to convert "cmd" to the appropriate "shell_cmd"
equivalent.
For use in key bindings, replace `exec` with `bash_exec` and you're good
to go. For use in a build system, include the following lines to your
build to tell Sublime to use this command to execute the build as well as
how to kill it:
"target": "bash_exec",
"cancel": {"kill": True}
"""
def run(self, **kwargs):
# If we're being told to kill a running build, short circuit everything
# and just do it directly. Not really needed, but it's a nice reminder
# that custom builds need to handle this sort of thing in general.
if kwargs.get("kill", False):
return super().run(kill=True)
# If there is a "cmd" argument, grab it out and convert it into a
# string. Items in cmd that contain spaces are double quoted so that
# they're considered a single argument, and all items are joined
# together.
cmd = kwargs.pop("cmd", None)
if cmd is not None:
cmd = " ".join(["\"%s\"" % s if " " in s else s for s in cmd])
# If there is a "shell_cmd" argument, then modify it to include the
# correct prefix to run the command via bash as well as to quote all
# double quote characters so the shell sees them.
#
# This will fall back to the string we just gathered for "cmd" if no
# shell_cmd is given.
shell_cmd = kwargs.pop("shell_cmd", cmd)
if shell_cmd is not None:
kwargs["shell_cmd"] = "bash -c \"{0}\"".format(
shell_cmd.replace("\"", "\\\""))
# Defer to the built in exec command to run the command
super().run(**kwargs)
If you redact away the comments that explain what this is doing, the change is ~20 lines or so. What this is doing is leveraging the internal exec command to do all of the heavy lifting for us. Thus we can just modify the arguments to the command automatically and let Sublime take care of the details.
As seen in the code, if a cmd argument is provided, it's converted into a string by joining all of the arguments together with space characters. Along the way any argument that contains a space is automatically double quoted so that it conveys properly to the shell.
If shell_cmd is provided, it's similarly extracted and converted into a string with a bash -c prefix; along the way any double quotes that appear in the command are quoted so that they convey properly to the shell.
Additionally, the converted value of cmd is used as the default if no shell_cmd is provided; thus if you specify shell_cmd that's always what is executed, and if you use cmd it's converted into the appropriate shell_cmd argument directly.
In use the command that this implements is bash_exec, and should be a drop in replacement for uses of exec in places where you're calling it directly, such as in key bindings, menu entries, command palette entries, via other commands, etc.
In addition, you can add the following two lines directly to a sublime-build file to use this command to execute builds undertaken that way; the first line tells Sublime to use this command instead of exec to execute the build, while the second tells it what arguments to pass to the command to get it to cancel a running build.
"target": "bash_exec",
"cancel": {"kill": True},
Caveats
This is really only of use on Windows; on Linux and MacOS Sublime already uses bash to execute shell_cmd without your having to do anything special. Using sublime.platform(), the command could determine if it's being executed on Windows and only rewrite commands on that OS and otherwise leave things alone, if desired.
Note also that I don't personally use WSL on my Windows machines, so this is untested in that environment (but it performs as expected on my Linux box); as such there may still be some tweaks to be made here, such as if bash isn't directly on the path or you want it to be configurable.
The last thing to mention is that the code to convert shell_cmd and cmd into a bash command line may require some more intelligence or subtle tweaks depending on what you're trying to pull off. If things don't seem to do what you expect, the Sublime console displays what command exec is running, which may shed some light on things.

Setting bindkey for "cd ../"

In tcsh, how to set bind-key for general command like cd or ps ?
I was trying bindkey "^G" "cd ../../" I get error Bad Command name: cd
I have looked at few online example. Its working when I want to do something, history search forward or end of line. But I want to do it for some other common command which I execute thousands of time in given day. e.g I use CD or ps command very often. I wanted to set bindkey for such commands ?
You can use the -c flag when setting bindkeys for external commands. For instance: bindkey -c "^]^d" "ls /" will bind Ctrl+]+d to spit out the contents of the root directory.
In your case:
bindkey -c "^G" 'cd ../../'
One caveat here is that your shell prompt may not update upon execution via the bindkey. So you will have traversed your two parent directories, but your shell prompt may still indicated that you are sitting in the directory from which you used the bindkey, even though you aren't.

Can make shell run interactively along with --command option

I'm using GNU bash that is installed as git bash. On startup I need to change directory, so I'm doing it like this:
"C:\Program Files\Git\bin\sh.exe" --rcfile "./cd.sh"
Where cd.sh just contains cd /d/ command. Everything works fine here. Now I'm trying to get rid of cd.sh file and pass command to the shell as a parameter yet I want it to remain interactive, so I'm doing like this:
"C:\Program Files\Git\bin\sh.exe" -ic "cd /d"
It executes the command (tested with echo command) but then exits. Why doesn't it stay interactive?
From man bash:
An interactive shell is one started without non-option arguments and without the -c option ...
From man dash:
If no args are present and if the standard input of the shell is connected to a terminal (or if the -i flag is set), and the -c option is not present, the shell is considered an interactive shell.

Reuse last externally executed command in VIM

In VIM, I want to execute a command (like :!mkdir src/main/scala/xxx)
Then, I want to also make a subdirectory of the just created directory.
Can I have VIM retype the last used command and then I append the sub directory name to it
(So I can have :!mkdir scr/main/scala/xxx/yyy without retyping the whole stuff).
Can't you just hit : then the up arrow to go through your command history?
:!mkdir test
:!!/test2
will do what you want, see :h :!. Citation from there:
Any '!' in {cmd} is replaced with the previous external command (see also 'cpoptions').
// Why don't you use -p switch to mkdir? mkdir -p test/test2/test3/... will create directory with all its parents if they do not exist.

Resources