0

N.B I use the word blank here due to lack of a better word. By blank I mean the remaining part of a tab that is visualized after the starting part. Please feel free to edit this question.

I know there are questions already answered about vim's indent with tabs and align with spaces. But those questions have answers that says it's not possible or possible with supertab plugin. And moreover, my question is a little bit specific.

Take this example,

function! Format_tabs() abort
>...let s = ''
>...let s .= repeat(' ', &ts)
>...let t = '>..>...hello there>>...hey'
>...echo substitute(t, '\v[^\t]+\zs\t+\ze', s, 'g')
endfunction

where all > are start of a tab and following . are the remaining blank part throughout the tabstop.

And my tabstop settings are all set to 4.

In the above function I'm trying to convert the tabs in the string that are not at the beginning of a line with equivalent number of spaces. At the beginning I thought, I could replace each tabs, that are not at the beginning, with &tabstop number of spaces. But it turns out due to some settings, that I'm not trying to change, the tabs show different amount of blanks to figure out the alignment. So I need to figure out how many spaces I need to substitute the tabs for. But I have no idea!

Any solution?

  • You only want to convert the tabs in the string, is that correct? NOT the tabs at the beginning of lines? – D. Ben Knoble Feb 8 at 15:27
  • As a guess at a fix: you might need an expression in the substitute (something like \=repeat(' ', len(\1)), but im rusty on that syntax). – D. Ben Knoble Feb 8 at 15:29
  • @D.BenKnoble, yes, that is correct. I took a string with tabs at the beginning to simulate lines where tabs are present at the beginning of lines to indent. And the other tabs are there just because of set noet. I want to change those other tabs – Enan Ajmain Feb 8 at 15:50
  • substitute(t, '\v[^\t]+\zs\t+\ze', repeat(' ', len(\1)), 'g')? doesn't work! – Enan Ajmain Feb 8 at 15:58
  • 1
    Ill take a look this afternoon or evening. – D. Ben Knoble Feb 8 at 16:01
0

Based on the docs for submatch(), I would use something like (untested)

function! Format_tabs() abort
  let t = '>..>...hello there>>...hey'
  echo substitute(t, '\v[^\t]+\zs\t+\ze', '\=repeat(" ", len(submatch(1)) * &tabstop)', 'g')
endfunction

For reference, the example in the docs is

:echo substitute(text, '\d\+', '\=submatch(0) + 1', '')
  • I already tried that approach, and that should be \=repeat(" ", len(submatch(0)) * &ts). But it doesn't work, because my main problem was, tabs, in case of alignment, i.e. used NOT at the beginning, would not always take up &ts amount of space. I think a simple substitution would not suffice, I need to check at what column the tab is and insert (col+&ts)%&ts amount of spaces. I'm working on this, but this would need to check every single tab of the file one by one, and this amount of overhead feels stupid to me. – Enan Ajmain Feb 10 at 3:39
  • Yes realizing i missed a few pieces. Ill leave the answer here, but its a bummer it doesnt work. – D. Ben Knoble Feb 10 at 4:10
0

This seems to work:

function! ReplaceMiddleTabs() abort
    let s:linenum = line('.')
    let s:current_line = getline(s:linenum)
    let s:temp = Format_tabs(s:current_line)
    call setline(s:linenum, s:temp)
endfunction

function! Format_tabs(line) abort
    let s:tablen = &ts
    let s:start = match(a:line, '^\s\+\zs.\ze')

    let s:temp = a:line
    let s:control = 1
    while s:control > 0
        let s:next = match(s:temp, '\t', s:start + 1)
        if s:next > 0
            let s:mod = (s:next % s:tablen)
            if s:mod == 0
                let s:mod = 4
            endif
            let s:substlen = s:tablen - s:mod + 1

            let s:temp = s:temp[0:s:next - 1] . repeat(' ', s:substlen) . s:temp[s:next + 1:]
        else
            let s:control = 0
        endif
    endwhile

    return s:temp
endfunction

The trick here is to calculate how many visual spaces each tab fills. To test it, I've used this:

|...|...|...|...|...|...|...|...|...|...|...|
>...|...>...|...|...|...|...|...|...|...|...|
>...|...|>..|...|...|...|...|...|...|...|...|
>...|...|.>.|...|...|...|...|...|...|...|...|
>...|...|..>|...|...|...|...|...|...|...|...|
>...|...|...>...|...|...|...|...|...|...|...|
>...|...>...|...|>..>...>...>...>...|...|...|
>...|...|>..|...|.>.|.>.|.>.|.>.|...|...|...|
>...|...|.>.|...|..>|..>|..>|..>|...|...|...|
>...|...|..>|...|...>...>...|...>...|...|...|
>...|...|...>...|...|...>...|..>|.>.|>..|...|
|...|...|...|...|...|...|...|...|...|...|...|

:%s/>\.*/\t/g
:call ReplaceMiddleTabs()
:%s/\t/>###/g

|...|...|...|...|...|...|...|...|...|...|...|
>###|...    |...|...|...|...|...|...|...|...|
>###|...|   |...|...|...|...|...|...|...|...|
>###|...|.  |...|...|...|...|...|...|...|...|
>###|...|.. |...|...|...|...|...|...|...|...|
>###|...|...    |...|...|...|...|...|...|...|
>###|...    |...|                   |...|...|
>###|...|   |...|.  |.  |.  |.  |...|...|...|
>###|...|.  |...|.. |.. |.. |.. |...|...|...|
>###|...|.. |...|...        |...    |...|...|
>###|...|...    |...|...    |.. |.  |   |...|
|...|...|...|...|...|...|...|...|...|...|...|

But this is my first vimscript function, so look at it with a grain of salt.

Edited: use % (thanks, @Enan Ajmain)

  • First of all, remainder(a, b) is equivalent to a%b. – Enan Ajmain Feb 9 at 13:38
  • Thanks! I've looked for that but couldn't find it... Need newer glasses, I think :) – João A. Toledo Feb 11 at 10:55

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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