3

How to add abbrevs made of more than one word?

I've been trying:

iabbrev a aquilo àquilo
iabbrev a aquele àquele
iabbrev a aquela àquela

What I want to do is have the two words "a aquilo" expand to "àquilo".

But it won't work. Also, I see in the docs for abbrev:

Examples of strings that cannot be abbreviations: "a.b", "#def", "a b", "_$r"

So I'm assuming that it can't be done officially, I wonder if is there a work around?

Cheers

filbranden
  • 28,785
  • 3
  • 26
  • 71

2 Answers2

1

Simply omit the first a and change the abbreviation as a compromise:

inoreabbrev aaquilo àquilo
inoreabbrev aaquele àquele
inoreabbrev aaquela àquela

As should be clear from :help abbrev.

The reason you can't have a aquilo -> àquilo is that vim sees a -> aquilo àquilo. The left-hand-side of the abbreviation must follow certain patterns (see :help abbreviations and then /full-id).

D. Ben Knoble
  • 26,070
  • 3
  • 29
  • 65
  • You may also be more interested in the 'spell' features of vim – D. Ben Knoble Jul 04 '19 at 13:43
  • It turns out "aquilo" (that) and "àquilo" (to that) are two different words. This is like trying to abbreviate "you are" into "you're" or "is not" into "isn't". – filbranden Jul 04 '19 at 14:52
  • @filbranden so what do you want to abbreviate? You cannot have a turn into all of those words unless you have iabbrev a aquilo àquilo aquele ... (and you dont get a choice...). Would aaquilo -> àquilo be acceptable? – D. Ben Knoble Jul 04 '19 at 14:56
  • I guess you can kind of match what the OP is asking with an :iab <expr>, looking back if there's a preceding "a" word, but not sure you can remove that "a" with an <expr> and that would be required. Yes I'd suggest "aaquilo" as a great compromise, I'd upvote an answer that explains why abbreviations have to be a single word and suggesting that compromise. – filbranden Jul 04 '19 at 15:02
  • @filbranden apologies... I mistook you for the OP /embarassed – D. Ben Knoble Jul 04 '19 at 15:28
  • @DBenKnoble That's totally fine! I just happen to speak the same language so I understood what the OP meant... – filbranden Jul 04 '19 at 15:30
  • @DBenKnoble Take a look at my other answer, I believe that implements exactly what the OP is asking for. I'd appreciate it if you could have a look, since you're clearly very experienced with Vim and Vimscript. Thanks! – filbranden Jul 04 '19 at 16:40
  • Thanks for your answers. Reason why I can't use aaquilo is that this is a clutch for my bad writing. I almost never remember the short form of 'a aquilo' and similar forms. So I wanted to automate my way of out it. – Paulo Phagula Jul 05 '19 at 07:51
  • I'm also interested in knowing why vim has this one word limit. Federal Bureau of Investigation is FBI after all. – Paulo Phagula Jul 05 '19 at 07:53
1

You can accomplish what you're trying to do by using an expression (introduced using <C-r>=, or using <expr> when defining the abbreviation) and then matching a single "a" word at the end of the text that precedes the word being abbreviated.

When you find that "a" word, then you can expand it to a <C-w>, that will delete that word, followed by the abbreviation you desire.

You can use this function to implement all the "à" (named crase) abbreviations in Portuguese:

function Crase()
    " Get the list of words until this point,
    " including the one being abbreviated.
    let words = split(getline('.')[0:col('.')-2])
    " By default, return the word itself.
    let replacement = words[-1]
    " Check if a word 'a' precedes it.
    if len(words) >=# 2 && words[-2] ==? 'a'
        " Handle capitalization.
        if words[-2] ==# 'A' || words[-1] =~# '^A'
            let replacement = substitute(replacement, '^\ca', 'À', '')
        else
            let replacement = substitute(replacement, '^\ca', 'à', '')
        endif
        " Remove the 'a' word and
        " contract the 'crase'.
        return "\<C-w>".replacement
    endif
    " Otherwise, no change.
    return replacement
endfunction

It looks at the sentence so far, then takes the two last words in it (when using iab <expr>, you'll get the word being abbreviated as well.) Then it checks whether the second last word was an "a" or an "A". If that's the case, it will then remove that word (using the "\<C-w>" at the beginning of the expansion), then return the word being abbreviated, just replacing the first character (which must be an "a", lower or uppercase!) with an "à" or an "À".

The function does proper capitalization, so "A aquele" will be expanded to "Àquele", as expected.

Use the function as follows:

inoreabbrev <expr> aquele Crase()
inoreabbrev <expr> Aquele Crase()
inoreabbrev <expr> AQUELE Crase()
inoreabbrev <expr> aquela Crase()
inoreabbrev <expr> Aquela Crase()
inoreabbrev <expr> AQUELA Crase()
inoreabbrev <expr> aquilo Crase()
inoreabbrev <expr> Aquilo Crase()
inoreabbrev <expr> AQUILO Crase()

You can even use it for "a a", turn it into "à", with:

inoreabbrev <expr> a Crase()
inoreabbrev <expr> A Crase()
filbranden
  • 28,785
  • 3
  • 26
  • 71
  • 1
    This is quite clever! Well done! The end should probably be explicitly endif, however. – D. Ben Knoble Jul 04 '19 at 16:54
  • Also, you can use expand('<cword>') to get the word under the cursor (though this may not work if the abbreviation is triggered with a space—needs testing). – D. Ben Knoble Jul 04 '19 at 16:55
  • @DBenKnoble Thanks! Fixed :endif (turns out :end is a valid abbreviation for it.) I thought of <cword> but since I also need the preceding word I decided to use getline() instead. (That's what I saw in similar examples too.) Thanks for the feedback! – filbranden Jul 04 '19 at 17:06