12

Is there a way to have vim not flag HTTP URLs and email addresses as spelling errors? Or, more generally, a way to list regexes of valid spellings?

16

You can add custom syntax rules, and give them the @nospell keyword, this will tell Vim not to apply spell checking to this syntax match. For example:

:syn match UrlNoSpell "\w\+:\/\/[^[:space:]]\+" contains=@NoSpell

The above will work for text files, and some filetypes (such as markdown), but not for all filetypes.

Note that I used a fairly simple regex here; see How do I parse URL's from the text for some alternatives.


For other filetypes, you need to do a bit more work. For example, for python files, comments are contained in a pythonComment group, from /usr/share/vim/vim74/syntax/python.vim:

syn match   pythonComment   "#.*$" contains=pythonTodo,@Spell

To override this, we need to do:

:syn match UrlNoSpellComment "\w\+:\/\/[^[:space:]]\+" contains=@NoSpell containedin=pythonComment
:highlight def link UrlNoSpellComment Comment

The trick is to add a list of previously matched syntax matches in which our custom syntax match may be contained with containedin= This tells Vim to look for the UrlNoSpell regex within the pythonComment matches.

We also need to use highlight to set the correct colours, since these aren't inherited.

You need to do this in a number of places, for example for Python strings:

:syn match UrlNoSpellString "\w\+:\/\/[^[:space:]]\+" contains=@NoSpell containedin=pythonString
:highlight def link UrlNoSpellString String

We need 2 different syntax match groups so we can apply the correct syntax highlighting.

Of course, for other filestypes you need to use other containedin= syntax matches... There is, AFAIK, no "universal" solution, but looking up the correct ones in /usr/share/vim/vim74/syntax/*.vim should not be too difficult.


Note that all of the above commands must be executed after the syntax files; there are 2 ways to do this:

  • From a command or key mapping, this must be invoked manually every time. e.g.

    fun! NoUrlSpell()
        if &filetype == 'python'
            :syn match UrlNoSpellComment "\w\+:\/\/[^[:space:]]\+" contains=@NoSpell containedin=pythonComment
            :highlight def link UrlNoSpellComment Comment
            :syn match UrlNoSpellString "\w\+:\/\/[^[:space:]]\+" contains=@NoSpell containedin=pythonString
            :highlight def link UrlNoSpellString String
        elseif &filetype == 'ruby'
            " ...
        else
            syn match   pythonComment   "#.*$" contains=pythonTodo,@Spell
        endif
    endfun
    command NoUrlSpell :call NoUrlSpell()
    
  • Put the commands in in the ~/.vim/after/syntax/[filetype].vim. Vim will pick up these files and execute them after the default syntax files (see: :help after-directory).

| improve this answer | |
3
+150

Martin Tournoij's otherwise excellent answer fails to behave as expected for me – probably due to my leveraging diraol's phenomenal python-mode plugin rather than Vim's default syntax file for Python.

To avoid highlighting URIs in Python comments, strings, or docstrings under python-mode, add the following concise one-liner to your user-specific ~/.vim/after/syntax/python.vim file:

syntax match NoSpellUriPython '\w\+:\/\/[^[:space:]]\+' transparent contained containedin=pythonComment,python.*String contains=@NoSpell

That's it. Crucially, note that this compresses what would have been twelve separate lines in Martin's answer into a single line. How? Elementary, my dear Vim-based Watson. We add:

  • The transparent keyword, instructing Vim to inherit the highlight properties for this child syntax from its parent syntax (e.g., comments, strings). This enables us to avoid explicitly restating highlight def link for each child syntax group.
  • The contained keyword, preventing this child syntax from extending past the boundaries of its parent syntax (e.g., EOL for comments, string delimiters for strings).
  • All parent syntax groups delimited by commas to the containedin keyword. The .* regex operator enables us to cleverly match all Python string syntax groups (i.e., pythonString, pythonUniString, pythonRawString, pythonUniRawString, pythonDocstring) with minimal pain and maximal forward compatibility.

Although technically valid, the vimscript embedded in Martin's answer violates the DRY (Don't Repeat Yourself) principle. See also this similar answer for further details.

But wait... there's more.

Unasked-for Improvements for Great Glory

I'm sufficiently annoyed by both Vim's overly naïve default spell checking and third-party plugins (e.g., Spelunker, which unconditionally spell checks the entire buffer rather than merely code comments and strings) that I've decided... to actually do something about that. </gasp>

The well-tested Vim snippet below intelligently avoids spell checking all of the following within Python comments and strings:

  • URIs (as above).
  • CamelCase identifiers.
  • snake_case identifiers.
  • UPPERCASE identifiers.
  • @-prefixed identifiers (e.g., @muhdecorator).
  • "-delimited filetyped filenames (e.g., "muh_module.py").
  • :-delimited substrings (e.g., the :func: in :func:`re.sub`).
  • `-delimited substrings (e.g., the re.sub in :func:`re.sub`).

Add some or all of the following to your user-specific ~/.vim/after/syntax/python.vim file and gasp in delight as Vim actually spell checks the RightStuff™ for once:

" Avoid spell checking URIs.
syntax match NoSpellPythonUri /\v\w+:\/\/[^[:space:]]+/ transparent contained containedin=pythonComment,python.*String contains=@NoSpell

" Avoid spell checking both CamelCase-formatted identifiers and uppercase
" identifiers. Since most languages (excluding Raku) prohibit Unicode in
" identifiers, these matches are intentionally confined to ASCII codepoints
" (e.g., "[A-Z]" rather than "[[:upper:]]").
syntax match NoSpellPythonCaps /\v<[A-Z]([A-Z0-9]{-1,}|[a-z0-9]+[A-Z0-9].{-})>/ transparent contained containedin=pythonComment,python.*String contains=@NoSpell

"FIXME: For unknown reasons, enabling this and *ONLY* this "syntax" statement
"causes subtle (but horrible) failures across "python-mode" indentation and
"syntax highlighting. While lamentable, we need "python-mode" more than we need
"to avoid spell checking snake_case-formatted identifiers. See also this
"currently unresolved upstream issue:
"    https://github.com/python-mode/python-mode/issues/1083
"
" " Avoid spell checking snake_case-formatted identifiers.
" syntax match NoSpellPythonSnake
"   \ /\v<\w+_.{-1,}>/ transparent
"   \ contained containedin=pythonComment,python.*String contains=@NoSpell

" Avoid spell checking "@"-prefixed identifiers.
syntax match NoSpellPythonDecorator /\v\@[a-zA-Z].{-}>/ transparent contained containedin=pythonComment,python.*String contains=@NoSpell

" Avoid spell checking ":"-delimited substrings.
syntax match NoSpellPythonColons /\v:[^:]+:/ transparent contained containedin=pythonComment,python.*String contains=@NoSpell

" Avoid spell checking "`"-delimited substrings.
syntax match NoSpellPythonTicks /\v`[^`]+`/ transparent contained containedin=pythonComment,python.*String contains=@NoSpell

" Avoid spell checking '"'-delimited filetyped filenames matched as a
" double-quoted substring containing a filename prefix, a period, and one to
" four characters comprising a filetype.
syntax match NoSpellPythonPath /\v"[^"]+.[^"]{1,4}"/ transparent contained containedin=pythonComment,python.*String contains=@NoSpell

Of course, all of the above could (and possibly should) be reduced to a single one-liner matching one Godzilla-like regular expression that no one including myself would ever be able to maintain or even read. For everyone's sanity, I didn't do that.

If someone who is not me would like to make a GitHub-hosted Vim plugin expanding the above to other popular languages, that'd be just great. Vim's default spell checking implementation is almost there, folks; it just needs a helping hand from the open-source community.

Until then, may the StackOverflow be with you always!

| improve this answer | |
  • 2
    I kinda figured out this whole Vim syntax highlighting thing while writing answers here. I didn't always know what I was doing to be honest, so especially some of the older answers may be less-than-optimal 😅 – Martin Tournoij Apr 11 at 9:13
  • You're too modest, Martin. Without your "breaking the ice" answer, I and many others wouldn't have even known where to begin. Thanks for getting this editor party started! Rome may not have been built in a day, but at least we can spell check it in one now. – Cecil Curry Apr 14 at 3:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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