summaryrefslogtreecommitdiff
path: root/misc/vim/ftplugin/go/import.vim
blob: 1d969e4602b863a87ec4769b3f009f35da9f8a94 (plain)
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
" Copyright 2011 The Go Authors. All rights reserved.
" Use of this source code is governed by a BSD-style
" license that can be found in the LICENSE file.
"
" import.vim: Vim commands to import/drop Go packages.
"
" This filetype plugin adds three new commands for go buffers:
"
"   :Import {path}
"
"       Import ensures that the provided package {path} is imported
"       in the current Go buffer, using proper style and ordering.
"       If {path} is already being imported, an error will be
"       displayed and the buffer will be untouched.
"
"   :ImportAs {localname} {path}
"
"       Same as Import, but uses a custom local name for the package.
"
"   :Drop {path}
"
"       Remove the import line for the provided package {path}, if
"       present in the current Go buffer.  If {path} is not being
"       imported, an error will be displayed and the buffer will be
"       untouched.
"
" In addition to these commands, there are also two shortcuts mapped:
"
"   \f  -  Runs :Import fmt
"   \F  -  Runs :Drop fmt
"
" The backslash is the default maplocalleader, so it is possible that
" your vim is set to use a different character (:help maplocalleader).
"
if exists("b:did_ftplugin_go_import")
    finish
endif

command! -buffer -nargs=? -complete=customlist,go#complete#Package Drop call s:SwitchImport(0, '', <f-args>)
command! -buffer -nargs=1 -complete=customlist,go#complete#Package Import call s:SwitchImport(1, '', <f-args>)
command! -buffer -nargs=* -complete=customlist,go#complete#Package ImportAs call s:SwitchImport(1, <f-args>)
map <buffer> <LocalLeader>f :Import fmt<CR>
map <buffer> <LocalLeader>F :Drop fmt<CR>

function! s:SwitchImport(enabled, localname, path)
    let view = winsaveview()
    let path = a:path

    " Quotes are not necessary, so remove them if provided.
    if path[0] == '"'
        let path = strpart(path, 1)
    endif
    if path[len(path)-1] == '"'
        let path = strpart(path, 0, len(path) - 1)
    endif
    if path == ''
        call s:Error('Import path not provided')
        return
    endif

    " Extract any site prefix (e.g. github.com/).
    " If other imports with the same prefix are grouped separately,
    " we will add this new import with them.
    " Only up to and including the first slash is used.
    let siteprefix = matchstr(path, "^[^/]*/")

    let qpath = '"' . path . '"'
    if a:localname != ''
        let qlocalpath = a:localname . ' ' . qpath
    else
        let qlocalpath = qpath
    endif
    let indentstr = 0
    let packageline = -1 " Position of package name statement
    let appendline = -1  " Position to introduce new import
    let deleteline = -1  " Position of line with existing import
    let linesdelta = 0   " Lines added/removed

    " Find proper place to add/remove import.
    let line = 0
    while line <= line('$')
        let linestr = getline(line)

        if linestr =~# '^package\s'
            let packageline = line
            let appendline = line

        elseif linestr =~# '^import\s\+('
            let appendstr = qlocalpath
            let indentstr = 1
            let appendline = line
            let firstblank = -1
            let lastprefix = ""
            while line <= line("$")
                let line = line + 1
                let linestr = getline(line)
                let m = matchlist(getline(line), '^\()\|\(\s\+\)\(\S*\s*\)"\(.\+\)"\)')
                if empty(m)
                    if siteprefix == "" && a:enabled
                        " must be in the first group
                        break
                    endif
                    " record this position, but keep looking
                    if firstblank < 0
                        let firstblank = line
                    endif
                    continue
                endif
                if m[1] == ')'
                    " if there's no match, add it to the first group
                    if appendline < 0 && firstblank >= 0
                        let appendline = firstblank
                    endif
                    break
                endif
                let lastprefix = matchstr(m[4], "^[^/]*/")
                if a:localname != '' && m[3] != ''
                    let qlocalpath = printf('%-' . (len(m[3])-1) . 's %s', a:localname, qpath)
                endif
                let appendstr = m[2] . qlocalpath
                let indentstr = 0
                if m[4] == path
                    let appendline = -1
                    let deleteline = line
                    break
                elseif m[4] < path
                    " don't set candidate position if we have a site prefix,
                    " we've passed a blank line, and this doesn't share the same
                    " site prefix.
                    if siteprefix == "" || firstblank < 0 || match(m[4], "^" . siteprefix) >= 0
                        let appendline = line
                    endif
                elseif siteprefix != "" && match(m[4], "^" . siteprefix) >= 0
                    " first entry of site group
                    let appendline = line - 1
                    break
                endif
            endwhile
            break

        elseif linestr =~# '^import '
            if appendline == packageline
                let appendstr = 'import ' . qlocalpath
                let appendline = line - 1
            endif
            let m = matchlist(linestr, '^import\(\s\+\)\(\S*\s*\)"\(.\+\)"')
            if !empty(m)
                if m[3] == path
                    let appendline = -1
                    let deleteline = line
                    break
                endif
                if m[3] < path
                    let appendline = line
                endif
                if a:localname != '' && m[2] != ''
                    let qlocalpath = printf("%s %" . len(m[2])-1 . "s", a:localname, qpath)
                endif
                let appendstr = 'import' . m[1] . qlocalpath
            endif

        elseif linestr =~# '^\(var\|const\|type\|func\)\>'
            break

        endif
        let line = line + 1
    endwhile

    " Append or remove the package import, as requested.
    if a:enabled
        if deleteline != -1
            call s:Error(qpath . ' already being imported')
        elseif appendline == -1
            call s:Error('No package line found')
        else
            if appendline == packageline
                call append(appendline + 0, '')
                call append(appendline + 1, 'import (')
                call append(appendline + 2, ')')
                let appendline += 2
                let linesdelta += 3
                let appendstr = qlocalpath
                let indentstr = 1
            endif
            call append(appendline, appendstr)
            execute appendline + 1
            if indentstr
                execute 'normal >>'
            endif
            let linesdelta += 1
        endif
    else
        if deleteline == -1
            call s:Error(qpath . ' not being imported')
        else
            execute deleteline . 'd'
            let linesdelta -= 1

            if getline(deleteline-1) =~# '^import\s\+(' && getline(deleteline) =~# '^)'
                " Delete empty import block
                let deleteline -= 1
                execute deleteline . "d"
                execute deleteline . "d"
                let linesdelta -= 2
            endif

            if getline(deleteline) == '' && getline(deleteline - 1) == ''
                " Delete spacing for removed line too.
                execute deleteline . "d"
                let linesdelta -= 1
            endif
        endif
    endif

    " Adjust view for any changes.
    let view.lnum += linesdelta
    let view.topline += linesdelta
    if view.topline < 0
        let view.topline = 0
    endif

    " Put buffer back where it was.
    call winrestview(view)

endfunction

function! s:Error(s)
    echohl Error | echo a:s | echohl None
endfunction

let b:did_ftplugin_go_import = 1

" vim:ts=4:sw=4:et