Banner of automating-code-transformations-in-vim-with-runscriptsonselect-.jpeg

Automating Code Transformations in Vim with RunScriptsOnSelect


Category: vim

📅 November 23, 2025   |   👁️ Views: 1

Author:   mosaid

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.


← Improving TeX Editing in Vim with a Smart 'Select Inside Any Pair' Function Boosting LaTeX Editing with Custom Vim Mappings →