I want to customize vi mode in bash. Two things I want to do very badly.
Map Esc to CAPS_LOCK and CAPS_LOCK to SHIFT+CAPS_LOCK
Use 'm' to mark current directory to a character 'a-z' and use ' to cd to that directory.
In general, is there a way to extend vi mode in bash?
Bash uses GNU readline to provide a usable command line prompt. Readline supports vi mode that provides a basic set of keys and a modal interface for it.
Mappings of caps lock and others is not bash's or readline's job. If you are willing to make those bindings global, you can use Xmodmap to achieve satisfactory results.
As for the second question: Unfortunately, the configurability of readline is very limited. But you could achieve something like that by writing functions that you init via a loop.
The following kind of works:
Set_Ma () {
DIR_a=`pwd`
}
Go_Ma (){
cd "$DIR_a"
}
set -o vi
bind -m vi-command -x '"ma":"Set_Ma"'
bind -m vi-command -x '"'"'"'a":"Go_Ma"'
You won't see any effect immediately after typing 'a because it doesn't redraw the prompt to match new CWD. You could also use an associative array for storing the marks but I won't go there.
May I suggest jumping in the ZSH bandwagon. Zsh doesn't use readline. Instead they wrote a more flexible library for line editing that can be properly scripted by ordinary zsh functions.
You can change options from within vi by using the ex command :set. In addition, whenever vi is started up, it reads a file in your home directory called .exrc for further operating instructions. By placing :set commands in this file, you can modify the way vi acts whenever you use it.
You can also set up .exrc files in local directories to initialize various options that you want to use in different environments. For example, you might define one set of options for editing English text, but another set for editing source programs. The .exrc file in your home directory will be executed first, then the one in your current directory.
Finally, any commands stored in the shell variable EXINIT will be executed by vi on startup. If there is a conflict between settings made in .exrc and EXINIT, those in .exrc take precedence.
Hope this might help you
Thanks
Related
As an example, say that I type part of a name:
$ vi partn <= here I press <tab><tab>
partname partnook partnum <= these are the results
This is standard behaviour in bash and that is fine, but I've been working a lot on PowerShell lately where the default behaviour is that if you press tab, it cycles through the names, completing each name and then pressing tab again will go to the next file, so if I pressed tab one time, I see vi ./partname, then press tab again, I see vi ./partnook, then tab again I see vi ./partnum. I tend to prefer this behaviour as it's less typing (you just hit tab to cycle through until you get the filename or command that you want).
Everything is customisable in Linux of course, so can someone advise how I might change the default bash behaviour to follow the above by default?
To persistently make tab-completion not require two Tab keypresses whenever there are multiple matching completions and to instead cycle through them, in-line:
Option A: If you either already have:
an /etc/inputrc file (applies system-wide, modification requires sudo)
and/or a ~/.inputrc file (user-specific)
and/or
you're planning to customize the Readline library extensively and/or want to make customizations effective for scripts too when they call read -e:
Add line:
"\C-i": menu-complete
to either file, depending on whether you want the setting to be effective for all users or the current user (create the file, if necessary).
Note: Alternatively, you can simply turn off the need to press Tab twice and exhibit the standard behavior instantly, which means showing a multi-column list of all possible completions, and then redisplaying the input line, to allow you to type additional characters to either fully complete the argument or to type enough of it so that the next Tab keypress unambiguously completes:
set show-all-if-ambiguous on
Option B: Alternatively, you may add Readline commands to your user-specific initialization file (~/.bash_profile on macOS, ~/.bashrc on Linux and other Unix-like platforms), by passing them as a single argument to the bind builtin:
bind '"\C-i": menu-complete'
If you prefer the show-all-matches behavior:
bind 'set show-all-if-ambiguous on'
Note that bind commands in ~/.bash_profile / ~/.bashrc take precedence over equivalent commands in either /etc/inputrc or ~/.inputrc.
As implied above, configuring Readline this way will not take effect in scripts that call read -e in order to activate Readline support for reading user input.
I noticed when in vi mode in bash (i.e. the mode enabled with "set -o vi"), that some commands, such as "diw", that work in vim but not in vi, don't work on the bash command line. Is there an easy way to configure bash so that its keybindings will support vim commands? I would like to be able to enter vim commands on the command line without having to actually start the vim program, as described in this question.
The best way of doing this that I know of would be to use athame.
It can be a surprisingly powerful experience in some cases. I particularly like it for interacting with a repl.
Athame patches your shell to add full Vim support by routing your keystrokes through an actual Vim process. Athame can currently be used to patch readline (used by bash, gdb, python, etc) and/or zsh (which doesn't use readline).
Alternatively I find spacemacs with the eshell to be a reasonably functional if strange solution.
Teach vi-command-mode diw to any software that uses readline (such as bash) by adding this to your ~/.inputrc:
set keymap vi-command
"diw": "bde"
First, the "vi mode" you get with set -o vi is not vi itself. It's an incomplete approximation of vi's behavior built into readline, the command-line editing library used by bash under the hood.
Second, because it is incomplete there's no reason whatsoever to expect every vi command to work.
Third, no, there's no "vim mode" so even less reason to expect any vim commands to work.
Fourth, if you absolutely want to edit the current command-line with Vim-like commands, why don't you go all the way and… actually use Vim:
<C-x><C-e>
That said, $ man readline tells you everything you need to customize its behavior and add bindings.
You can use ble.sh. Which is a command line editor written in pure Bash which replaces the default GNU Readline. I like to call it "zsh for bash".
Appart from vim-style navigation it gives you:
Enhanced completion
Syntax highlighting
Other interesting features
After installation don't forget to add (recommended) to your .bashrc:
# at the start of your .bashrc file
[[ $- == *i* ]] && source /usr/share/blesh/ble.sh --noattach
...
#for vim-style mode
set -o vi
...
#at the end of your .bashrc file
[[ ${BLE_VERSION-} ]] && ble-attach
or you can just:
# at the start of your .bashrc file
source /usr/share/blesh/ble.sh
but this may not work as expected - read this.
I have added several aliases to my .zshrc file and they ONLY work if I restart terminal or use the source ~/.zshrc If I just open terminal, then type the alias, it will not recognize it, until I call source ~/.zshrc
So I know it's not a problem with the alias I created, I just have to load up the .zshrc file every time I want to use them.
What is going on? How can I fix this?
Well, you don't expect that you only have to edit a file and then, by magic, all your current zsh instances somehow ingest the changes, do you?
From the zsh man page, section STARTUP/SHUTDOWN FILES :
if the shell is interactive, commands are read from /etc/zshrc and then $ZDOTDIR/.zshrc
($ZDOTDIR defaults to your $HOME). Hence, if you are in your terminal, you have three choices. Two of them you already found out (restart the terminal, source .zshrc manually). The third choice would be to just open a zsh subshell (by typing zsh).
Actually, there is a trick to do some "magic" in reading the file automatically: Zsh allows you to define a so-called precmd hook, which allows you to establish an arbitrary command to be executed just before a command prompt will be displayed. You could use it to source any file you like. If you want to use this feature, I strongly recommend against sourcing all of .zshrc. Sooner or later, you will have stuff in .zshrc that you don't want to be executed every time.
Instead, put your alias definitions into a separate file, say $HOME/.aliases, and in Zsh define the hook
function precmd {
source $HOME/.aliases
}
If you later change the .aliases file, you would still have to type a Carriage Return in your shell, in order to provoke a new prompt to be written and the precmd to be executed, but this is less cumbersome than sourcing the file manually.
i.e. I want to use shift+arrows to select, ctrl+c/v to copy/paste. I'm also open to using another shell that makes this easier
bash uses the readline library to handle input. By default it uses emacs-style notation for commands. See this cheatsheet to get a list of commands on how to manipulate the command line with emacs-style notation.
If, instead, you would like bash/readline to use vi-style notation, then run set -o vi in your ~/.bash_profile
I'm not sure where "everywhere else" is, but both emacs and vi are pervasive in the *nix world. If those two styles are not to your liking, you'll most liking have to look to another shell.
Often I find my self navigating the filesystem from a Conque shell in Vim and want to open a specific file inside my existing MacVim session. Is this possible ? - I was hoping for something like:
shell> open some/file.txt
and then have file.txt pop up inside my existing Vim window (preferably in a new tab).
Note: I am using #wycats vim dot files (not sure this matters).
Type from ConqueShell
mvim --remote-tab-silent filename
This will open the file in a new tab in MacVim
You could also write a Bash alias to shorten the command (assuming you are using bash).
Put in your ~/.profile
alias vim='mvim --remote-tab-silent'
this would enable you to type
vim filename
from ConqueShell or bash, and have it open in a new MacVim tab, rather than terminal vim. It of course does disable your ability to run standard vim (although you could still use the vi command), so maybe you would want to name the alias differently.
Just to add, this will work only if you placed the mvim executable on your path E.G. /usr/bin/mvim. It comes with the MacVim.app
Often I find my self navigating the filesystem from a Conque shell
The beauty of running a shell from inside vim is you have all of vim and the shell at your disposal.
gf is your friend. Once you get the file you want displayed on the screen in some way, you can enter normal mode, move the cursor to the file you want to edit, then use the gf command to navigate to the file. There are many ways to use this. Any program or command that outputs file names is great for this (ll, git status, etc). You could also type the filename into the shell, just to make it visible on the screen without actually running any terminal commands (tab completion is handy here).
It is possible, you can start vim as server and then add as many files as you want, but I'm not very familiar with this, so I can't give you just a direction.