I started using VIM as my editor around six months back and I enjoy it very much. However, there are a few work related scripts that I'd like to implement to make my life easier. If there is anyone who can help me I would be grateful.
This is my question. I have some tests written in python and I wrote a key mapping to run those tests using vim terminal. It works perfectly. However, now I want to use VimScript and some vim functions to make it look better. I'm a beginner in VimScript and therefore, I'm not sure whether this is doable.
My folder structure looks like,
.
├── my_test.py
└── test
└── testRunner.py
1 directory, 2 files
My test code looks something like,
my_test.py:
#!/bin/python
class MyTest1:
def Run():
# Test body
class MyTest2:
def Run():
# Test body
test/testRunner.py:
#!/bin/python
print "Running the test"
My current key-mapping in .vimrc looks like:
nnoremap <leader>t mZ/class<CR>Nwyiw:noh<CR>:terminal<CR>cd test<CR>python testRunner.py <C-W>"0<CR><C-W><C-W>'Z
What this does is,
Find the test name (the test that I'm currently editing)
Copy the name and run that test name in a vim-terminal
What I want it to be something which looks like:
nnoremap <leader>t :call RunThisTest()<CR>
function! RunThisTest()
RememberEditContext()
FindAndCopyTestName()
RunTestInTestDirectory()
ReturnToEditContext()
endfunction
Can someone help me in developing these functions?
Thank you in advance!
One option is to use the :normal! command directly, which allows you to run a sequence of keystrokes directly as you'd have used them in a mapping.
But it turns out we can do better, much better, so let's get to it!
Searching and Matching
You can use the search() function to look for the class you're in. You can pass it flags, such as bcnW, to have it search backwards, possibly match at the cursor position, do not move the cursor and do not wrap around the file. Putting it all together:
let line = search('^class \w', 'bcnW')
This will return a line number if there was a positive match, or 0 if there wasn't one. If there was a match, we can use getline() to get its contents and then matchlist() to capture the name of the class.
let [_, classname; _] = matchlist(getline(line), '^class \(\w\+\)')
As you can see, using Vimscript we were able to get the classname without moving the cursor and without touching the search register. So we didn't need to set any marks and we won't need to worry about recovering the current position and view!
Running a command
Now it's time to run a command on the terminal. We can simplify the process by passing it a command directly. (Note that there's a difference here, in that the terminal will run just that command, it won't leave the shell around after finished. Depending on your use case, you might prefer to do something more akin to what you're doing now.)
We can run the command in a terminal with:
:terminal ++shell cd test && python testRunner.py MyTest1
But, of course, we need to actually pass it the class name we got, not a fixed value here. We can use the :execute command for this purpose. It takes a string and runs it as a Vimscript command. We can use this to assemble the string dynamically.
execute "terminal ++shell cd test && python testRunner.py ".shellescape(classname)
Finally, to go back to the original window, we can use the :wincmd command, more specifically wincmd p.
Putting it together
The resulting function is:
function! RunThisTest() abort
let line = search('^class \w', 'bcnW')
if line == 0
echoerr "Not inside a test class!"
return
endif
let [_, classname; _] = matchlist(getline(line), '^class \(\w\+\)')
execute "terminal ++shell cd test && python testRunner.py ".shellescape(classname)
wincmd p
endfunction
nnoremap <silent> <leader>t :call RunThisTest()<CR>
There's definitely room for improvement, but this should get you started!
Saving and restoring context
We didn't go into saving and restoring context, since this case actually didn't need any of that!
If you were to develop functions that use commands that affect global context, you can use Vimscript to save and restore it.
For example, if you're going to search, you can save the #/ register and restore it after the search:
let saved_search = #/
/class
let #/ = saved_search
If you're going to yank into a register, you can save and restore it too. For example, #" for the default register. You should also save the register type, which records whether the contents were taken in a character-wise, linewise or blockwise context.
let saved_register = getreg('"')
let saved_regtype = getregtype('"')
normal! y3W
let words = getreg('"')
call setreg('"', saved_register, saved_regtype)
You can also save the current view, which includes the position your cursor is in, but also the other parameters of the window, such as what the first displayed line and column are, such that you can fully restore that context. See the winsaveview() and winrestview() functions for details on that.
Managing Terminals
There are functions to control the terminal that go way beyond what :terminal can do.
For instance, the much richer term_start() allows running a command as a list and passing options such as 'cwd' to run the command on a different directory.
So we could simplify our test execution with:
call term_start(['python', 'testRunner.py', classname], {'cwd': 'test'})
There's also term_sendkeys() which you can use to send keystrokes to the terminal. For example, if you prefer to start a shell and call the Python script through the shell:
let termbuf = term_start(&shell, {'cwd': 'test'})
call term_sendkeys(termbuf, "python testRunner.py ".shellescape(classname)."\r")
You can also use term_getline(termbuf, '.') to get the contents of the line where the cursor currently is. For instance, you could use that to detect whether the terminal is on a shell prompt (line ending in $ and whitespace) or still on an execution of a test runner.
Finally, you can even have the command running inside the terminal call Vim commands! Through special escape sequences, it can call exported functions or ask Vim to open files for editing. See :help terminal-api for details.
Learning More
This is all very neat... But how can I learn more?
My first strong recommendation would be to read the excellent "Learn Vimscript the Hard Way", by Steve Losh. It covers the basics of the language, how to interface with the editor (mappings, auto-commands, indentation expressions, filetypes) and basics of how to put together Vim plug-ins. It also covers common pitfalls of Vimscript and best practices for writing reliable code. That's a must if you want to get serious about scripting Vim.
Second suggestion is read the excellent documentation that's available through :help! Few applications are as well documented as Vim is, so knowing your way around the help system can really help a lot.
Third is using StackExchange. In particular, the Vi & Vim SE which is dedicated to the subject. Not only you'll find great answers there and you'll be able to ask great questions, you will also have the opportunity of seeing great questions, wonder how to solve them and possibly take a stab at writing an answer. (Personally, since I started using the Vi & Vim SE, my Vim-foo has greatly improved, to the point I can consider myself almost an expert.) I strongly recommend that.
Finally, practice. It typically takes a few attempts to get something really right. But the fact that the environment is fairly dynamic and flexible allows for experimentation. You can type and experiment with the commands in the editor itself, so it's usually quick to test your code and get it right as you're writing it.
I wondered if anyone could help me out.
In sublime text, when I want to shuffle some lines (for example if I had a list of colour names and wanted them in random order). I've been using Ctrl+Shift+P, and then writing shuffle to get the "Permute lines: Shuffle" command. This is fairly quick but I'd love to have a shortcut for it as I use it very often. I know there's a file I can change but I don't know how to write the command.
Many thanks in advance!
Items that appear in the command palette are stored in sublime-commands files. If you use the View Package File command from the command palette and enter sublime-commands as the filter text, the list of all files in all packages that add commands to the command palette will be displayed.
The first part of the filename shows you what package is contributing the command, and commands that are part of core Sublime are in the Default/ package, so choosing the file Default/Default.sublime-commands will show you the commands Sublime ships with (note that some packages include a file named Default.sublime-commands, so make sure you're picking the Default/ version).
If you look in that file and search for the command that you see in the command palette, you'll find this (reformatted here to not be all one line):
{
"caption": "Permute Lines: Shuffle",
"command": "permute_lines",
"args": {"operation": "shuffle"}
},
This shows you the command and args you need to apply in a key binding.
For commands that also appear in the menu (or are bound to other keys and you want to remap them) you can also open the Sublime console with View > Show Console in the menu and enter sublime.log_commands(True). Now when you choose a menu item or press a key, the command that is being executed will be logged for you. The logging remains in effect until you enter sublime.log_commands(False) in the console or restart Sublime.
In this case doing that and then choosing Edit > Permute Lines > Shuffle will log this in the console:
command: permute_lines {"operation": "shuffle"}
This shows the same command and arguments that are required (if any).
Background: I am working on an SML file on SublimeText3 with build system setup.
After a build, i can successfully jump to the first error using the F4 key. I want to add another key mapping for the same "Next Result" command eg:Cmd+N in Vintage mode.
What should i add as in my keybindings file to achieve this?
What documentation,file did you refer/look around to find the proper answer for question 1? What was your thought process in brief to figure it out ?
edit: changed the required keybinding from <leader>cn to Cmd+N to make things easier
For future reference, you can type sublime.log_commands(True) into the Sublime console and then the command name of every action you do will get printed to the console. Once you get the command name you need, you can stop the command printing with sublime.log_commands(False)
You can find the command being called on a default key-binding in the default key bingings file. This file is opened using the menu:
Preferences > Key Bindings - Default
Inside this file you must find the entry corresponding to the keys you are looking for, in this case f4 key is mapped to next_result command as you can see in the key binding:
{ "keys": ["f4"], "command": "next_result" }
i am working with Sublime Text2 in vintage mode. I disabled the arrow keys, so i don't use them to move the cursor in insert mode. Now is was wondering, if it is possible, to map the up/down keys, so that they will move a line of code up and down. In vim it is easy possible by just mapping the keys to do a sequence like "dd k P" to delete the line move cursor up and past it above.
The syntax for the key mapping in Sublime still is quite complicated to me as a beginner.
Thanks
Insert the following into your user key bindings.
[
{ "keys": ["up"], "command": "swap_line_up" },
{ "keys": ["down"], "command": "swap_line_down" }
]
The key mapping file is just JSON. There are 4 keys.
keysis a list of key entries. An entry will generally be something like ["<modifier> + <character>"]. You can define multi level keybindings by creating additional entries in the array. An example of this is to show and hide the side bar. The entry for this is ["ctrl+k", "ctrl+b"]. The available keys are described here.
command is a string specifying the command to run. To see what command is running with a particular action, you can enter sublime.log_commands(True) in the ST console.
args are the arguments passed to the command. This is a dictionary object. The keys for this correspond to the parameter name for a given command.
context is a list of dictionary entries to conditionally execute a given command. These can be somewhat complicated. There is a reference for context here.
I think the best way to familiarize yourself with the key bindings is to just try things out. I used the default keys as a reference.
You might want to keep this as a reference.
You can run a series of commands by creating macros. These are just lists of commands and arguments and are further described here.
I just installed a plugin called CodeSniffer (http://soulbroken.co.uk/code/sublimephpcs), and I want to link one of it's commands from the command palette to a keyboard shortcut because I use it so often.
Is there any easy way to do this? Or will I just need to ask the developer what the name of the command is (in the command palette it is 'PHP CodeSniffer: Clear sniffer marks')?
Thanks
It's actually very easy to find the name of a command but it requires a few steps.
Open Sublime Text's built-in console (control+`)
Type in sublime.log_commands(True)
Trigger the command from the command palette
The name of the command will be logged to the console. Then open your user keybindings and create a new keybinding like this:
{ "keys": ["YOUR_SEQUENCE"], "command": "YOUR_COMMAND" }
I provided a similar answer here: Keymap Sublime Text 2 File Type?
Another way is to crack open the .sublime-commands files.
Let's say you've installed Sublime Package Control (which you really want to do!) and then open it up in the command palette (⌘⇧p on os x) and install the Search Stack Overflow package. You'll now have two new commands in the command palette, the "Stackoverflow: Search Selection" and "Stackoverflow: Search from Input" commands.
OK, open the .sublime-commands file for the package. You need to find it first. If you're hardcore you do View > Show Console, and enter print(sublime.packages_path())
Otherwise it should be here
Windows: %APPDATA%\Sublime Text 2\Packages
OS X: ~/Library/Application Support/Sublime Text 2/Packages
Linux: ~/.Sublime Text 2/Packages
Portable Installation: Sublime Text 2/Data/Packages
and then "Search Stack Overflow/Default.sublime-commands"
This is the file that make the commands show up in the command palette in the first place.
It's another JSON file with entries like these
{
"caption": "Stackoverflow: Search from Input",
"command": "stackoverflow_search_from_input"
}
see, that's the command name right there: stackoverflow_search_from_input
Now just open the user key bindings JSON file and add the key binding like #BoundinCode said.