Skip to content
This repository has been archived by the owner on Sep 20, 2023. It is now read-only.

AlexJS Syntax Checker #1557

Closed
iamnewton opened this issue Sep 20, 2015 · 3 comments
Closed

AlexJS Syntax Checker #1557

iamnewton opened this issue Sep 20, 2015 · 3 comments

Comments

@iamnewton
Copy link

This is not an issue as it is more a question of help!!!

I'm trying to write a text checker for AlexJS, which checks for insensitive writing. Using the docs and the existing checkers I'm just not getting it to work. I'm a noob when it comes to writing ViM, so any help would be awesome. If this is not the appropriate place to post this, please direct me to it and I'll gladly move it over there.

"============================================================================
"File:        alex.vim
"Description: Syntax checking plugin for syntastic.vim
"Maintainer:  Newton Koumantzelis <chrisohpedia at gmail dot com>
"License:     This program is free software. It comes without any warranty,
"             to the extent permitted by applicable law. You can redistribute
"             it and/or modify it under the terms of the Do What The Fuck You
"             Want To Public License, Version 2, as published by Sam Hocevar.
"             See http://sam.zoy.org/wtfpl/COPYING for more details.
"
"============================================================================

if exists('g:loaded_syntastic_text_alex_checker')
    finish
endif
let g:loaded_syntastic_text_alex_checker = 1

let s:save_cpo = &cpo
set cpo&vim

" @TODO: figure out to highlight the word in question
" function! SyntaxCheckers_text_alex_GetHighlightRegex(item)
"     let term = matchstr(a:item['text'], '\m "\zs[^"]\+\ze"\($\| | suggestions:\)')
"     if term !=# ''
"         let col = get(a:item, 'col', 0)
"         let term = (col != 0 ? '\%' . col . 'c' : '') . '\V' . escape(term, '\')
"     endif
"     return term
" endfunction

function! SyntaxCheckers_text_alex_GetLocList() dict
    if !exists('s:alex_new')
        let s:alex_new = syntastic#util#versionIsAtLeast(self.getVersion(), [1])
    endif

    "<stdin>
    "   1:5-1:14  warning  `boogeyman` may be insensitive, use `boogey` instead
    "  1:42-1:48  warning  `master` / `slaves` may be insensitive, use `primary` / `replica` instead
    "  2:52-2:54  warning  `he` may be insensitive, use `they`, `it` instead
    "  2:61-2:68  warning  `cripple` may be insensitive, use `person with a limp` instead
    "
    "⚠ 4 warnings
    let errorformat =
        \ '%W<stdin>' .
        \ '%+C%p^%l:%c-%l:%c  warning  %m' .
        \ '%>%Z'

    let loclist = SyntasticMake({
        \ 'errorformat': errorformat,
        \ 'defaults': {'bufnr': bufnr('')} })

    for e in loclist
        let e['text'] = substitute(e['text'], '\m\n\s\+', ' | ', 'g')
    endfor

    return loclist
endfunction

call g:SyntasticRegistry.CreateAndRegisterChecker({
    \ 'filetype': 'text',
    \ 'name': 'alex'})

let &cpo = s:save_cpo
unlet s:save_cpo

" vim: set sw=4 sts=4 et fdm=marker:
@lcd047
Copy link
Collaborator

lcd047 commented Sep 21, 2015

If this is your first attempt at writing VimL code, you chose a pretty bad place to start. The checker itself is relatively easy to get to work. It's pretty nasty to get it to work properly. Either way, you'll have to actually understand what you're doing. If you expect to copy bits and pieces from other checkers and put them together without knowing exactly what each piece does, you won't get anywhere. I can answer questions related to syntastic API. You'll have to look elsewhere for help related to VimL.

Back to your checker, there are a number of problems with your code, and a number of problems with alex.

First, alex is really meant to run against markdown files. It's supposed to be able to check plain text files too, but you'll run into problems pretty soon if you try to check text files. Take f.i. a line indented with a tab character: alex will happily ignore it, presumably because tabs are supposed to start formatted code, or some such. Consequently, you probably want to make this a markdown checker. You can add a redirect to it for text files once you get it working for markdown, but as I said, checking plain text won't work too well.

Then, s:alex_new doesn't seem to serve any useful purpose. In other checkers something like that is used to work around differences in behaviour between checker versions. There is no need for such thing here (at least not for now). Perhaps get rid of it?

Then, you're missing a makeprg. That's the command line for the checker. It won't work without one. Now, normally something like this should do:

let makeprg = self.makeprgBuild({})

However, alex tries to be smart about stdin, and this won't work from within Vim:

$ alex example.md </dev/null

<stdin>: no issues found

You can trick it to work, sort of (on UNIX at least; I imagine Windows is different):

let makeprg = self.makeprgBuild({ 'tail': '</dev/tty' })

But this is really a bug in alex.

Next, the errorformat. If you run alex as above, the output is something like this:

$ alex example.md 

example.md
  1:43-1:49  warning  `master` / `slaves` may be insensitive, use `primary` / `replica` instead
  2:52-2:54  warning  `he` may be insensitive, use `they`, `it` instead
  2:61-2:68  warning  `cripple` may be insensitive, use `person with a limp` instead

⚠ 3 warnings

It doesn't make any sense to match this in multi-line mode. You can match the filename with %P and %Q (see :help errorformat-separate-filename). That's pretty straightforward: %P%f, and %-Q.

Ignoring the second pair of line/column, you can match the error lines like this:

'%*[ ]%l:%c-%*\d:%*\d%*[ ]%tarning%*[ ]%m'

Here %*[ ] skips spaces, %l:%c reads the line and column of the error, %*\d skips digits, %t matches the w in warning and sets the error type (otherwise you can still set that with a default, but it's shorter to do it like this), and %m reads the error message.

Finally, you need to ignore the rest of the junk. This is done with %-G%.%#. Putting it together:

let errorformat =
    \ '%P%f,' .
    \ '%-Q,' .
    \ '%*[ ]%l:%c-%*\d:%*\d%*[ ]%tarning%*[ ]%m,' .
    \ '%-G%.%#'

If you do it like this, the error text doesn't contain either spurious spaces or embedded newlines, so you can drop the substitute().

Next you need to add everything to SyntasticMake():

return SyntasticMake({
    \ 'makeprg': makeprg,
    \ 'errorformat': errorformat,
    \ 'subtype': 'Style',
    \ 'returns': [0, 1] })

The subtype thing is just for decoration, alex checks style, rather than syntax (at least until political correctness in newspeak gets enforced into law). And returns checks the exit code, alex is supposed to return 0 or 1 (which I found reading the sources).

So, a crude checker might look like this.

Now, to get it to work properly.

First, alex is not an unique name, and you'd need to distinguish it from the (probably far more popular) Haskell lexer generator, and possibly from other alexes. So you need to write an IsAvailable() function for it. Sadly, neither alex --version, nor alex --help mentions an official name, so this is going to be mildly annoying.

Then you'll probably want syntax highlighting. You can in principle add that using the column numbers (see :help /\%<c). However, there are two problems with this: first, highlighting can't span multiple lines (not without a lot of VimL gymnastics that is), and second, errorformat doesn't have enough %-formats to pass around all relevant information. In order to get around this, you could either write a preprocess function, or you could simply assume no errors span multiple lines, and abuse %n to get the second column number. You could then write a GetHighlightRegex() function, or just set the hl field in error items. Either way, you need to clear nr (which corresponds to %n in errorformat). This is how the flow checker handles highlighting. The modified checker (without IsAvailable()) might look like this.

Finally, if you insist on checking plain text files, you could add a redirect from text/alex to markdown/alex. And you'll probably also need to handle a few other quirks, such as parsing messages when alex fails.

@lcd047
Copy link
Collaborator

lcd047 commented Sep 28, 2015

As it turns out, a checker for alex won't be included in syntastic after all. Please see this thread for more information. Guidelines for maintaining external checkers can be found in the wiki.

@lcd047 lcd047 closed this as completed Sep 28, 2015
@iamnewton
Copy link
Author

Thanks for all of this @lcd047 I've been following the discussion.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants