Automating Code Transformations in Vim with RunScriptsOnSelect
Streamlining Code Processing in Vim
When I work with multiple programming languages, I often need to process code snippets through external tools. Manually handling this became tedious, so I created a Vim function that automates the entire workflow. The RunScriptsOnSelect function detects code type from visual selections and routes them to appropriate Python processors automatically.
How RunScriptsOnSelect Works
The function operates in visual mode and follows this intelligent workflow:
•Selection Capture: It captures the visually selected text range including line numbers
•Type Detection: Uses the first line as a type indicator for processing
•Validation: Checks against allowed processors to prevent errors
•Routing: Sends content to the appropriate Python script
•Replacement: Cleanly replaces selection with processed output
The Complete Implementation
Here is the complete Vim function that makes this automation possible:
function! RunScriptsOnSelect()
" Get selection range
let l:start_line = line("'<")
let l:end_line = line("'>")
" Get selected lines
let l:lines = getline(l:start_line, l:end_line)
" First line = type
let l:type_line = tolower(trim(l:lines[0]))
" Allowed types
let l:allowed_types = ['tex', 'html', 'py', 'vim']
" Validate first line
if index(l:allowed_types, l:type_line) == -1
let l:reminder = ["Make the first line of your selection as tex, html, py or md"]
call setline(l:start_line, l:reminder)
" Delete the rest of selection
if l:end_line > l:start_line
call deletebufline('%', l:start_line + 1, l:end_line)
endif
return
endif
" Actual content to process = selection except first line
let l:selected_text = join(l:lines[1:], "\n")
" Script locations
let l:script_base_dir = '~/bin/python'
let l:script_map = {
\ 'tex': l:script_base_dir . '/change-tex.py',
\ 'vim': l:script_base_dir . '/change-vim.py',
\ 'py': l:script_base_dir . '/change-python.py',
\ 'html': l:script_base_dir . '/change-html.py',
\ }
" Run script
let l:output = system('python3 ' . l:script_map[l:type_line], l:selected_text)
let l:output_lines = split(l:output, "\n")
" ---- SIMPLE & SAFE REPLACEMENT ----
" 1) Delete entire selection
call deletebufline('%', l:start_line, l:end_line)
" 2) Insert type line + script output
call append(l:start_line - 1, [l:type_line] + l:output_lines)
endfunction
vnoremap <leader>rs :<C-U>call RunScriptsOnSelect()<CR>
Robust Type Validation
By checking against the allowed_types list, we ensure only supported processors are used. If someone tries an unsupported type, they get immediate feedback with clear instructions.
Flexible Script Mapping
The script_map dictionary makes it incredibly easy to extend functionality. When I want to add support for a new language, I simply add another entry to the mapping - no need to modify the core function logic.
Clean Output Replacement
This was the trickiest part to get right. The function handles cases where processed output might be longer or shorter than the original selection, ensuring clean replacement without leaving orphaned lines or missing content.
Example Processor Script
Here is a sample Python script that demonstrates how to process Vim script content. This particular example escapes HTML and wraps Vim code in proper pre and code tags:
import sys
import html
def process_text(content):
"""Escape LaTeX and wrap in <pre><code>."""
escaped_content = html.escape(content)
wrapped_content = f"""<pre class="language-vim">
<code class="language-vim">
{escaped_content}
</code>
</pre>
"""
return wrapped_content
if __name__ == "__main__":
if len(sys.argv) > 1:
# Read content from file if a path is provided
file_path = sys.argv[1]
try:
with open(file_path, 'r') as f:
content = f.read()
except Exception as e:
print(f"Error processing file: {e}")
sys.exit(1)
else:
# Otherwise, read content from stdin
content = sys.stdin.read()
print(process_text(content))
To use this function in your daily workflow, follow these steps:
1. Enter visual mode and select your code block
2. Make sure the first line indicates the processor type
3. Press your leader key followed by rs
For example, to process Vim script, your selection would look like this:
vim
function! ExampleFunction()
echo "This will be processed as Vim script"
endfunction
Customization Opportunities
•Extended Language Support: Add more types to the allowed_types list for additional languages
•Custom Script Locations: Modify the script_base_dir to point to your preferred script locations
•Error Handling: Add more robust error handling for missing script files or processing failures
•Preview Functionality: Implement a preview mode that shows changes before applying them
•Multiple Processors: Allow chaining multiple processors for complex transformations
This Vim function demonstrates how we can combine Vim's powerful extensibility with Python's processing capabilities. It creates a seamless workflow that adapts to multiple programming languages while maintaining the efficiency and speed that makes Vim such a powerful editor.
The beauty of this approach is its simplicity and extensibility. Once you have the core function working, adding support for new languages or processors becomes trivial. I have found this particularly useful when working with documentation, code samples, and automated formatting tasks.