4
\$\begingroup\$

So I have a list of files which names are something like this

long_boring_filename_del_1.tex
long_boring_filename_del_2.tex
long_boring_filename_del_3.sty
long_boring_filename_del_4.tex

I want to cycle through these filenames using some mapping defined in vim. Meaning if I have long_boring_filename_del_2.tex open and hit the mapping it should take me to the next file long_boring_filename_del_3.sty. It should also loop, meaning long_boring_filename_del_4.tex should take me back to long_boring_filename_del_4.tex.

In addition I want to be able to cycle thorugh the files in both directions.

After obtaining a bit of help over at vi and vim I was able to come up with the following solution (which works).

function! s:next_del()

    " do nothing if current filename doesn't match "
    if @% !~# 'del-\v[1234]\.'
        return
    endif

    " open next file "
    let fnameA = substitute(@%, '\v[1234]\ze\.', '\=submatch(0)%4+1', '')
    if filereadable(fnameA)
        exec 'echo'
        exec 'e ' . fnameA
    else 
        " if the .tex file does not exist check if the .sty versions does "
        let fnameB = substitute(fnameA, '\.tex', '\.sty', '')
        if filereadable(fnameB)
            exec 'e ' . fnameB
        else 
            " if the above fails, check if changing sty to tex works "
            let fnameB = substitute(fnameA, '\.sty', '\.tex', '')
            if filereadable(fnameB)
                exec 'e ' . fnameB
            endif
        endif
    endif
endfunction

" Same as s:next_del() except this cycles in the opposite direction "
function! s:prev_del()

    " do nothing if current filename doesn't match "
    if @% !~# 'del-\v[1234]\.'
        return
    endif

    " open previous file "
    let fnameA = substitute(@%, '\v[1234]\ze\.', '\=submatch(0)-1', '')
    let fnameA = substitute(fnameA, '\v[0]\ze\.', '\=submatch(0)+4', '')
    if filereadable(fnameA)
        exec 'e ' . fnameA
    else 
        " if the .tex file does not exist check if the .sty versions does "
        let fnameB = substitute(fnameA, '\.tex', '\.sty', '')
        if filereadable(fnameB)
            exec 'e ' . fnameB
        else
            " if the above fails, check if changing sty to tex works "
            let fnameB = substitute(fnameA, '\.sty', '\.tex', '')
            if filereadable(fnameB)
                exec 'e ' . fnameB
            endif
        endif
    endif
endfunction

nnoremap <silent> <localleader>w :call <sid>next_del()<CR>
nnoremap <silent> <localleader>q :call <sid>prev_del()<CR>

While it work I would love to get some pointers on how to improve the code as I am very much a beginner when it come to vimscript. Here are the major points in the code that needs improvement:

  • Can the two functions s:next_del() and s:prev_del() be combined into one?
  • My method of cycling backwards is very barbaric.

    let fnameA = substitute(@%, '\v[1234]\ze\.', '\=submatch(0)-1', '')
    let fnameA = substitute(fnameA, '\v[0]\ze\.', '\=submatch(0)+4', '')
    

    I did it in this fashion because apparently you can't separate the leading \ and = , as otherwise it's a pure string. I would like to get some pointers on how to improve it.

  • Lastly my method of "checking" whether I have a sty file or tex file is strange at best. I do not really check, just change sty to tex and see if it works. I tried to get some regex going by using (tex|sty) but for some reason vimscript did not accept it. I would like to learn a better method of alternating between the sty and tex files.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Your code can be improved in many aspects, so it is easier to write my own solution rather to rewrite your.

Differences from your:

  1. I suggest to use glob() function to find all files, which are matching to a pattern.
  2. Singular Move(move_direction) function is enough, a duplication is not needed.
  3. Not only new files are opened, but switching between already opened buffers are supported.

function! s:MyCompare(i1, i2)
    let l:f = str2nr(matchstr(a:i1, '[0-9]\+'))
    let l:s = str2nr(matchstr(a:i2, '[0-9]\+'))
    return l:f == l:s ? 0 : l:f > l:s ? 1 : -1
endfunc

function! s:Move(step)
    let l:current_filename = bufname('%')
    let l:common_part = matchstr(l:current_filename, '[^0-9]*')

    "" sort() is needed for numerical order
    let l:matched_file_list = sort(glob(l:common_part . '*', 0, 1), 's:MyCompare') 

    let l:list_size = len(l:matched_file_list)
    let l:cur_idx = index(l:matched_file_list, l:current_filename)

    let l:next_idx = (l:cur_idx + a:step) % l:list_size
    let l:next_buf_name = l:matched_file_list[l:next_idx]

    if !bufloaded(l:next_buf_name)
        execute 'e' l:next_buf_name
    endif

    execute 'buffer' l:next_buf_name
endfunction

command! Fwrd call s:Move(1)
command! Bwrd call s:Move(-1)

noremap <Leader>c :Fwrd<CR>
noremap <Leader>d :Bwrd<CR>

Directory content

ls -1

long_boring_filename_del_101.tex
long_boring_filename_del_1.tex
long_boring_filename_del_202.tex
long_boring_filename_del_2.tex
long_boring_filename_del_3.sty
long_boring_filename_del_4.tex
long_boring_filename_del_5.tex

Demonstration

, is a Leader key.

enter image description here

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.