]> git.madduck.net Git - etc/vim.git/blob - .vim/bundle/ale/ale_linters/ruby/steep.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Merge commit '76265755a1add77121c8f9dabb3e9bb70fe9a972' as '.vim/bundle/ale'
[etc/vim.git] / .vim / bundle / ale / ale_linters / ruby / steep.vim
1 call ale#Set('ruby_steep_executable', 'steep')
2 call ale#Set('ruby_steep_options', '')
3
4 " Find the nearest dir containing a Steepfile
5 function! ale_linters#ruby#steep#FindRoot(buffer) abort
6     for l:name in ['Steepfile']
7         let l:dir = fnamemodify(
8         \   ale#path#FindNearestFile(a:buffer, l:name),
9         \   ':h'
10         \)
11
12         if l:dir isnot# '.' && isdirectory(l:dir)
13             return l:dir
14         endif
15     endfor
16
17     return ''
18 endfunction
19
20 " Rename path relative to root
21 function! ale_linters#ruby#steep#RelativeToRoot(buffer, path) abort
22     let l:separator = has('win32') ? '\' : '/'
23     let l:steep_root = ale_linters#ruby#steep#FindRoot(a:buffer)
24
25     " path isn't under root
26     if l:steep_root is# ''
27         return ''
28     endif
29
30     let l:steep_root_prefix = l:steep_root . l:separator
31
32     " win32 path separators get interpreted by substitute, escape them
33     if has('win32')
34         let l:steep_root_pat = substitute(l:steep_root_prefix, '\\', '\\\\', 'g')
35     else
36         let l:steep_root_pat = l:steep_root_prefix
37     endif
38
39     return substitute(a:path, l:steep_root_pat, '', '')
40 endfunction
41
42 function! ale_linters#ruby#steep#GetCommand(buffer) abort
43     let l:executable = ale#Var(a:buffer, 'ruby_steep_executable')
44
45     " steep check needs to apply some config from the file path so:
46     " - steep check can't use stdin (no path)
47     " - steep check can't use %t (path outside of project)
48     " => we can only use %s
49
50     " somehow :ALEInfo shows that ALE still appends '< %t' to the command
51     " => luckily steep check ignores stdin
52
53     " somehow steep has a problem with absolute path to file but a path
54     " relative to Steepfile directory works:
55     " see https://github.com/soutaro/steep/pull/975
56     " => change to Steepfile directory and remove leading path
57
58     let l:buffer_filename = fnamemodify(bufname(a:buffer), ':p')
59     let l:buffer_filename = fnameescape(l:buffer_filename)
60
61     let l:relative = ale_linters#ruby#steep#RelativeToRoot(a:buffer, l:buffer_filename)
62
63     " if file is not under steep root, steep can't type check
64     if l:relative is# ''
65         " don't execute
66         return ''
67     endif
68
69     return ale#ruby#EscapeExecutable(l:executable, 'steep')
70     \   . ' check '
71     \   . ale#Var(a:buffer, 'ruby_steep_options')
72     \   . ' ' . fnameescape(l:relative)
73 endfunction
74
75 function! ale_linters#ruby#steep#GetType(severity) abort
76     if a:severity is? 'information'
77     \|| a:severity is? 'hint'
78         return 'I'
79     endif
80
81     if a:severity is? 'warning'
82         return 'W'
83     endif
84
85     return 'E'
86 endfunction
87
88 " Handle output from steep
89 function! ale_linters#ruby#steep#HandleOutput(buffer, lines) abort
90     let l:output = []
91
92     let l:in = 0
93     let l:item = {}
94
95     for l:line in a:lines
96         " Look for first line of a message block
97         " If not in-message (l:in == 0) that's expected
98         " If in-message (l:in > 0) that's less expected but let's recover
99         let l:match = matchlist(l:line, '^\([^:]*\):\([0-9]*\):\([0-9]*\): \[\([^]]*\)\] \(.*\)')
100
101         if len(l:match) > 0
102             " Something is lingering: recover by pushing what is there
103             if len(l:item) > 0
104                 call add(l:output, l:item)
105                 let l:item = {}
106             endif
107
108             let l:filename = l:match[1]
109
110             " Steep's reported column is offset by 1 (zero-indexed?)
111             let l:item = {
112             \   'lnum': l:match[2] + 0,
113             \   'col': l:match[3] + 1,
114             \   'type': ale_linters#ruby#steep#GetType(l:match[4]),
115             \   'text': l:match[5],
116             \}
117
118             " Done with this line, mark being in-message and go on with next line
119             let l:in = 1
120             continue
121         endif
122
123         " We're past the first line of a message block
124         if l:in > 0
125             " Look for code in subsequent lines of the message block
126             if l:line =~# '^│ Diagnostic ID:'
127                 let l:match = matchlist(l:line, '^│ Diagnostic ID: \(.*\)')
128
129                 if len(l:match) > 0
130                     let l:item.code = l:match[1]
131                 endif
132
133                 " Done with the line
134                 continue
135             endif
136
137             " Look for last line of the message block
138             if l:line =~# '^└'
139                 " Done with the line, mark looking for underline and go on with the next line
140                 let l:in = 2
141                 continue
142             endif
143
144             " Look for underline right after last line
145             if l:in == 2
146                 let l:match = matchlist(l:line, '\([~][~]*\)')
147
148                 if len(l:match) > 0
149                     let l:item.end_col = l:item['col'] + len(l:match[1]) - 1
150                 endif
151
152                 call add(l:output, l:item)
153
154                 " Done with the line, mark looking for first line and go on with the next line
155                 let l:in = 0
156                 let l:item = {}
157                 continue
158             endif
159         endif
160     endfor
161
162     return l:output
163 endfunction
164
165 call ale#linter#Define('ruby', {
166 \   'name': 'steep',
167 \   'executable': {b -> ale#Var(b, 'ruby_steep_executable')},
168 \   'language': 'ruby',
169 \   'command': function('ale_linters#ruby#steep#GetCommand'),
170 \   'project_root': function('ale_linters#ruby#steep#FindRoot'),
171 \   'callback': 'ale_linters#ruby#steep#HandleOutput',
172 \})