- Vim Script 71%
- Ruby 25.6%
- Python 3.4%
| autoload | Fix :Enum bug with multiple for-loops | |
| data | Fix smart quotes | |
| doc | Open external documentation in a browser | |
| ftplugin/python | Open documentation on stdlib imports | |
| plugin | Open external documentation in a browser | |
| scripts | Fix smart quotes | |
| spec | Fix :Enum bug with multiple for-loops | |
| .gitignore | Set up basic tests | |
| _project.vim | Initial commit | |
| CODE_OF_CONDUCT.md | Documentation | |
| CONTRIBUTING.md | README and CONTRIBUTING file | |
| Gemfile | Set up basic tests | |
| LICENSE | Documentation | |
| Rakefile | Update archive task | |
| README.md | README | |
Usage
This plugin automatically looks up imported symbols in a Python file and highlights them by underscoring them. You can then use the built-in gf mapping to follow the imported symbol to its definition. You can also use CTRL-W_f, CTRL-W_gf and other functions in the same family.
Here's a demo video of the plugin (and its extension, flask_tools) in action: https://www.youtube.com/watch?v=nth9-NxhILk.
This should work out of the box, provided the Python file you're editing is in a project that includes one of several different files or directories described below.
Updating imports
Imports are recalculated when the buffer gets saved. You can decide to recalculate them more often by scheduling a different autocommand with the function that triggers the refresh. For instance, to update imports on exiting normal mode, you can place this in an ftplugin/python.vim in your config:
autocmd InsertLeave <buffer> call python_tools#imports#Parse()
autocmd InsertLeave <buffer> call python_tools#imports#Highlight()
You can try out different autocommands like InsertLeave, CursorHold, or TextChanged/TextChangedI. Personally, I don't need immediate as-you-type highlighting, so I'm happy to leave the default behaviour to update on write.
You can change the highlighting style itself by defining a pythonToolsImport highlight group, for example:
highlight pythonToolsImport ctermbg=lightgray guibg=lightgray
This line can be placed in the ftplugin/python.vim file to give imports a light gray background in the terminal or in the GUI (or in a terminal with 'termguicolors' on).
You can disable highlighting altogether by setting g:python_tools_highlight_imports to v:false.
Project root
The plugin needs to find the root of the project in order to resolve imports. By default, it looks for certain files defined in a configuration variable:
let g:python_tools_root_filenames = [
\ '.python_tools.json',
\ 'pyproject.toml',
\ 'requirements.txt',
\ '.git',
\ ]
The first one, .python_tools.json, is a plugin-specific file that you could gitignore globally and just place wherever you need to specify the root of your project or subproject. It takes priority, because it indicates a deliberate choice. Its contents currently don't matter, though they might allow some project-specific configuration in the future, similar to what vim-projectionist does.
The second file pyproject.toml, seems to be common for Python projects, though not universal. The third one, requirements.txt, should be indicative of a pip-managed project. As a final fallback, a .git directory is considered the project root.
Either way, you can redefine this variable in your .vimrc to decide on the relative priority between these files and/or add additional filenames like .flake8, a conda/mamba environment file, or anything else. If you have recommendations for default paths to look for, please open an issue on the project's repository.
To get similar functionality for jinja2 templates in a flask project (and a few more goodies), you can augment this plugin with flask_tools.vim.
Extra commands
A few additional buffer-local commands are available to perform certain editing tasks. You can disable them by setting g:python_tools_commands to v:false.
:Doc [{module.path}]
This command lets you open the documentation from a module in the standard library. For instance, :Doc re will attempt to open "https://docs.python.org/3/library/re.html" in a web browser. If no term is given, the symbol under the cursor will be resolved into a full module path and looked up in the stdlib module list.
The function tab-completes stdlib modules from a hardcoded list. The list is scraped from "https://docs.python.org/3/library/index.html", which may or may not be accurate for the version of python you're using.
If the symbol you're querying is not a stdlib import, but it's a valid external library import in the current file, a browser page will be opened with a search query for the full module path. If you'd like to configure your search engine, you can modify g:python_tools_search_command.
:Init
The :Init command find the closest class constructor upwards and initializes its properties with the arguments given to the constructor function. Example:
class Example():
def __init__(self, one, two, three=None):
pass
When the :Init command is executed, this gets turned into:
class Example():
def __init__(self, one, two, three=None):
self.one = one
self.two = two
self.three = three
The plugin won't touch definitions that have already been set, so if you modify e.g. self.three = three to self.three = three or [], this should not cause problems if you add more arguments and call the command again.
:Enum[!] [{variable name}]
Calling :Enum with the cursor inside of a for loop will wrap the iterable in an enumerate() call and add an index variable. Example:
for k, v in dictionary.items():
print(f"{k}: {v}")
With the cursor on the print call, :Enum will change the code as follows:
for i, (k, v) in enumerate(dictionary.items()):
print(f"{k}: {v}")
A single optional argument to the command is the variable name to use for the index. It's "i" by default, but if you call it as :Enum index, the for loops will be for index, (...) in enumerate(...).
To remove an enumerate() wrapping, call the command with a bang, :Enum!. In that case, any given variable name will be ignored.
Function text object
The plugin implements a text object that operates on a function definition. By default, it uses the "m" prefix for "method", so that "dam" would delete a method definition, and "cim" would delete the body of the function and leave you in insert mode inside of it.
You can set the keybinding by changing g:python_tools_function_textobj, and setting it to the empty string will disable the mapping.
Contributing
Pull requests are welcome, as long as they did not involve LLM usage. Take a look at CONTRIBUTING.md for details. Be sure to abide by the CODE_OF_CONDUCT.md as well.