1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
" Author: w0rp <devw0rp@gmail.com>
" Description: "dmd for D files"
function! s:GetDUBCommand(buffer) abort
" If we can't run dub, then skip this command.
if executable('dub')
" Returning an empty string skips to the DMD command.
let l:config = ale#d#FindDUBConfig(a:buffer)
" To support older dub versions, we just change the directory to the
" directory where we found the dub config, and then run `dub describe`
" from that directory.
if !empty(l:config)
return [fnamemodify(l:config, ':h'), 'dub describe --data-list
\ --data=import-paths
\ --data=string-import-paths
\ --data=versions
\ --data=debug-versions
\']
endif
endif
return ['', '']
endfunction
function! ale_linters#d#dmd#RunDUBCommand(buffer) abort
let [l:cwd, l:command] = s:GetDUBCommand(a:buffer)
if empty(l:command)
" If we can't run DUB, just run DMD.
return ale_linters#d#dmd#DMDCommand(a:buffer, [], {})
endif
return ale#command#Run(
\ a:buffer,
\ l:command,
\ function('ale_linters#d#dmd#DMDCommand'),
\ {'cwd': l:cwd},
\)
endfunction
function! ale_linters#d#dmd#DMDCommand(buffer, dub_output, meta) abort
let l:import_list = []
let l:str_import_list = []
let l:versions_list = []
let l:deb_versions_list = []
let l:list_ind = 1
let l:seen_line = 0
" Build a list of options generated from DUB, if available.
" DUB output each path or version on a single line.
" Each list is separated by a blank line.
" Empty list are represented by a blank line (followed and/or
" preceded by a separation blank line)
for l:line in a:dub_output
" line still has end of line char on windows
let l:line = substitute(l:line, '[\r\n]*$', '', '')
if !empty(l:line)
if l:list_ind == 1
call add(l:import_list, '-I' . ale#Escape(l:line))
elseif l:list_ind == 2
call add(l:str_import_list, '-J' . ale#Escape(l:line))
elseif l:list_ind == 3
call add(l:versions_list, '-version=' . ale#Escape(l:line))
elseif l:list_ind == 4
call add(l:deb_versions_list, '-debug=' . ale#Escape(l:line))
endif
let l:seen_line = 1
elseif !l:seen_line
" if list is empty must skip one empty line
let l:seen_line = 1
else
let l:seen_line = 0
let l:list_ind += 1
endif
endfor
return 'dmd ' . join(l:import_list) . ' ' .
\ join(l:str_import_list) . ' ' .
\ join(l:versions_list) . ' ' .
\ join(l:deb_versions_list) . ' -o- -wi -vcolumns -c %t'
endfunction
function! ale_linters#d#dmd#Handle(buffer, lines) abort
" Matches patterns lines like the following:
" /tmp/tmp.qclsa7qLP7/file.d(1): Error: function declaration without return type. (Note that constructors are always named 'this')
" /tmp/tmp.G1L5xIizvB.d(8,8): Error: module weak_reference is in file 'dstruct/weak_reference.d' which cannot be read
let l:pattern = '\v^(\f+)\((\d+)(,(\d+))?\): (\w+): (.+)$'
let l:output = []
let l:dir = expand('#' . a:buffer . ':p:h')
for l:match in ale#util#GetMatches(a:lines, l:pattern)
" If dmd was invoked with relative path, match[1] is relative, otherwise it is absolute.
" As we invoke dmd with the buffer path (in /tmp), this will generally be absolute already
let l:fname = ale#path#GetAbsPath(l:dir, l:match[1])
call add(l:output, {
\ 'filename': l:fname,
\ 'lnum': l:match[2],
\ 'col': l:match[4],
\ 'type': l:match[5] is# 'Warning' || l:match[5] is# 'Deprecation' ? 'W' : 'E',
\ 'text': l:match[6],
\})
endfor
return l:output
endfunction
call ale#linter#Define('d', {
\ 'name': 'dmd',
\ 'executable': 'dmd',
\ 'command': function('ale_linters#d#dmd#RunDUBCommand'),
\ 'callback': 'ale_linters#d#dmd#Handle',
\ 'output_stream': 'stderr',
\})
|