1

Follow up to this question.

The Question

I have code for a custom key mapping with conditional logic. My understanding is that in vimscript, that means:

  1. it has to be all on one line
    (since it's a key mapping), and
  2. I'm restricted to using nested ternary operators
    (since it's all on one line).

The code works, and is reproduced below. (I know there are extraneous parentheses, but it helps me see the code more clearly since it's already such a jumbled mess.)

nnoremap <expr> <Leader>o (getline(line(".")) =~ '\v^' . strpart(&commentstring,0,1) . '?$') ? ( (getline(line(".")-1) =~ '\v^' . strpart(&commentstring,0,1) . '?$') ? ( (getline(line(".")+1) =~ '\v^' . strpart(&commentstring,0,1) . '?$') ? ("i") : ("O") ) : ( (getline(line(".")+1) =~ '\v^' . strpart(&commentstring,0,1) . '?$') ? ("o") : ( strlen(getline(line("."))) == 0 ? ("O\<CR>") : ("O\<CR>\<Space>") ) ) ) : ( (getline(line(".")+1) =~ '\v^' . strpart(&commentstring,0,1) . '?$') ? ("o\<CR>") : ( strlen(getline(line("."))) == 0 ? ("o\<CR>\<C-o>O") : ("o\<CR>\<C-o>O\<Space>") ) )

How can I (/what facilities does vimscript offer to) simplify, refactor, or otherwise clean up this code?

Pseudocode for clarity

 1 if (current line is empty, or contains only a single comment marker)
 2   if (previous line is empty, or contains only a single comment marker) 
 3     if (next line is empty, or contains only a single comment marker) 
 4       ("i") 
 5     else
 6       ("O") 
 7   else
 8     if (next line is empty, or contains only a single comment marker) 
 9       ("o") 
10     else
11       if (current line does not contain a comment marker)
12         ("O\<CR>")  
13       else
14         ("O\<CR>\<Space>")
15 else
16   if (previous line is empty, or contains only a single comment marker) 
17     ("o\<CR>") 
18   else
19     if (current line does not contain a comment marker)
20       ("o\<CR>\<C-o>O") 
21     else
22       ("o\<CR>\<C-o>O\<Space>")

The Background

The mapping above facilitates creating new paragraphs by inserting the necessary carriage returns between two lines of text and placing the cursor halfway inbetween. That is to say that when executed here,

CURSOR (NORMAL MODE) Morbi tincidunt elementum facilisis. Sed convallis, mi et vulputate vestibulum, mauris nulla maximus risus, sed sagittis augue arcu a risus.

Mauris porta mauris nisi, sed facilisis tortor volutpat vel.

the mapping does o<CR> to produce this:

Morbi tincidunt elementum facilisis. Sed convallis, mi et vulputate vestibulum, mauris nulla maximus risus, sed sagittis augue arcu a risus.

CURSOR (INSERT MODE)

Mauris porta mauris nisi, sed facilisis tortor volutpat vel.

It's also context-sensitive, which means it creates the appropriate spacing depending on whether the current, previous, and next line are blank. Thus, it will create the exact same end state shown above even for the following scenarios:

CURSOR Morbi tincidunt elementum facilisis. Sed convallis, mi et vulputate vestibulum, mauris nulla maximus risus, sed sagittis augue arcu a risus.
Mauris porta mauris nisi, sed facilisis tortor volutpat vel.

(via o<CR><C-o>O),

Morbi tincidunt elementum facilisis. Sed convallis, mi et vulputate vestibulum, mauris nulla maximus risus, sed sagittis augue arcu a risus.
CURSOR
Mauris porta mauris nisi, sed facilisis tortor volutpat vel.

(via O<CR>),

Morbi tincidunt elementum facilisis. Sed convallis, mi et vulputate vestibulum, mauris nulla maximus risus, sed sagittis augue arcu a risus.
CURSOR

Mauris porta mauris nisi, sed facilisis tortor volutpat vel.

(via O), etc.

There are, on lines 11 and 19 of the pseudocode, conditions for auto-indenting comments. If anyone knows of a better way of doing this, I'd be glad to hear it, too.

1 Answer 1

3

Nested ternaries are indeed very hard to write and maintain, in vimscript or any language.

Here is your jumbled mess turned into a proper function:

function! O()
    if getline(line(".")) =~ "\v^" . strpart(&commentstring,0,1) . "?$"
        if getline(line(".")-1)) =~ '\v^' . strpart(&commentstring,0,1) . '?$'
            if getline(line(".")+1)) =~ '\v^' . strpart(&commentstring,0,1) . '?$'
                return "i"
            else
                return "O"
            endif
        else
            if getline(line(".")+1)) =~ '\v^' . strpart(&commentstring,0,1) . '?$'
                return "o"
            else
                if strlen(getline(line("."))) == 0
                    return "O\<CR>"
                else
                    return "O\<CR>\<Space>"
                endif
            endif
        endif
    else
        if getline(line(".")+1) =~ '\v^' . strpart(&commentstring,0,1) . '?$'
            return "o\<CR>"
        else
            if strlen(getline(line("."))) == 0
                return "o\<CR>\<C-o>O"
            else
                return "o\<CR>\<C-o>O\<Space>"
            endif
        endif
    endif
endfunction

nnoremap <expr> <F5> O()
2
  • 1
    to the rescue again. Damnit I should just learn vimscript I guess, huh?
    – Ryan Lue
    Oct 8, 2016 at 9:56
  • 1
    Well, that's what you are doing.
    – romainl
    Oct 8, 2016 at 10:48

Your Answer

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

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