Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 1 | " Description: Helper functions for Rust commands/mappings |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 2 | " Last Modified: 2023-09-11 |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 3 | " For bugs, patches and license go to https://github.com/rust-lang/rust.vim |
| 4 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 5 | function! rust#Load() |
| 6 | " Utility call to get this script loaded, for debugging |
| 7 | endfunction |
| 8 | |
| 9 | function! rust#GetConfigVar(name, default) |
| 10 | " Local buffer variable with same name takes predeence over global |
| 11 | if has_key(b:, a:name) |
| 12 | return get(b:, a:name) |
| 13 | endif |
| 14 | if has_key(g:, a:name) |
| 15 | return get(g:, a:name) |
| 16 | endif |
| 17 | return a:default |
| 18 | endfunction |
| 19 | |
| 20 | " Include expression {{{1 |
| 21 | |
| 22 | function! rust#IncludeExpr(fname) abort |
| 23 | " Remove leading 'crate::' to deal with 2018 edition style 'use' |
| 24 | " statements |
| 25 | let l:fname = substitute(a:fname, '^crate::', '', '') |
| 26 | |
| 27 | " Remove trailing colons arising from lines like |
| 28 | " |
| 29 | " use foo::{Bar, Baz}; |
| 30 | let l:fname = substitute(l:fname, ':\+$', '', '') |
| 31 | |
| 32 | " Replace '::' with '/' |
| 33 | let l:fname = substitute(l:fname, '::', '/', 'g') |
| 34 | |
| 35 | " When we have |
| 36 | " |
| 37 | " use foo::bar::baz; |
| 38 | " |
| 39 | " we can't tell whether baz is a module or a function; and we can't tell |
| 40 | " which modules correspond to files. |
| 41 | " |
| 42 | " So we work our way up, trying |
| 43 | " |
| 44 | " foo/bar/baz.rs |
| 45 | " foo/bar.rs |
| 46 | " foo.rs |
| 47 | while l:fname !=# '.' |
| 48 | let l:path = findfile(l:fname) |
| 49 | if !empty(l:path) |
| 50 | return l:fname |
| 51 | endif |
| 52 | let l:fname = fnamemodify(l:fname, ':h') |
| 53 | endwhile |
| 54 | return l:fname |
| 55 | endfunction |
| 56 | |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 57 | " Jump {{{1 |
| 58 | |
| 59 | function! rust#Jump(mode, function) range |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 60 | let cnt = v:count1 |
| 61 | normal! m' |
| 62 | if a:mode ==# 'v' |
| 63 | norm! gv |
| 64 | endif |
| 65 | let foldenable = &foldenable |
| 66 | set nofoldenable |
| 67 | while cnt > 0 |
| 68 | execute "call <SID>Jump_" . a:function . "()" |
| 69 | let cnt = cnt - 1 |
| 70 | endwhile |
| 71 | let &foldenable = foldenable |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 72 | endfunction |
| 73 | |
| 74 | function! s:Jump_Back() |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 75 | call search('{', 'b') |
| 76 | keepjumps normal! w99[{ |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 77 | endfunction |
| 78 | |
| 79 | function! s:Jump_Forward() |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 80 | normal! j0 |
| 81 | call search('{', 'b') |
| 82 | keepjumps normal! w99[{% |
| 83 | call search('{') |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 84 | endfunction |
| 85 | |
| 86 | " Run {{{1 |
| 87 | |
| 88 | function! rust#Run(bang, args) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 89 | let args = s:ShellTokenize(a:args) |
| 90 | if a:bang |
| 91 | let idx = index(l:args, '--') |
| 92 | if idx != -1 |
| 93 | let rustc_args = idx == 0 ? [] : l:args[:idx-1] |
| 94 | let args = l:args[idx+1:] |
| 95 | else |
| 96 | let rustc_args = l:args |
| 97 | let args = [] |
| 98 | endif |
| 99 | else |
| 100 | let rustc_args = [] |
| 101 | endif |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 102 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 103 | let b:rust_last_rustc_args = l:rustc_args |
| 104 | let b:rust_last_args = l:args |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 105 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 106 | call s:WithPath(function("s:Run"), rustc_args, args) |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 107 | endfunction |
| 108 | |
| 109 | function! s:Run(dict, rustc_args, args) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 110 | let exepath = a:dict.tmpdir.'/'.fnamemodify(a:dict.path, ':t:r') |
| 111 | if has('win32') |
| 112 | let exepath .= '.exe' |
| 113 | endif |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 114 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 115 | let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) |
| 116 | let rustc_args = [relpath, '-o', exepath] + a:rustc_args |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 117 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 118 | let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 119 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 120 | let pwd = a:dict.istemp ? a:dict.tmpdir : '' |
| 121 | let output = s:system(pwd, shellescape(rustc) . " " . join(map(rustc_args, 'shellescape(v:val)'))) |
| 122 | if output !=# '' |
| 123 | echohl WarningMsg |
| 124 | echo output |
| 125 | echohl None |
| 126 | endif |
| 127 | if !v:shell_error |
| 128 | exe '!' . shellescape(exepath) . " " . join(map(a:args, 'shellescape(v:val)')) |
| 129 | endif |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 130 | endfunction |
| 131 | |
| 132 | " Expand {{{1 |
| 133 | |
| 134 | function! rust#Expand(bang, args) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 135 | let args = s:ShellTokenize(a:args) |
| 136 | if a:bang && !empty(l:args) |
| 137 | let pretty = remove(l:args, 0) |
| 138 | else |
| 139 | let pretty = "expanded" |
| 140 | endif |
| 141 | call s:WithPath(function("s:Expand"), pretty, args) |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 142 | endfunction |
| 143 | |
| 144 | function! s:Expand(dict, pretty, args) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 145 | try |
| 146 | let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 147 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 148 | if a:pretty =~? '^\%(everybody_loops$\|flowgraph=\)' |
| 149 | let flag = '--xpretty' |
| 150 | else |
| 151 | let flag = '--pretty' |
| 152 | endif |
| 153 | let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) |
| 154 | let args = [relpath, '-Z', 'unstable-options', l:flag, a:pretty] + a:args |
| 155 | let pwd = a:dict.istemp ? a:dict.tmpdir : '' |
| 156 | let output = s:system(pwd, shellescape(rustc) . " " . join(map(args, 'shellescape(v:val)'))) |
| 157 | if v:shell_error |
| 158 | echohl WarningMsg |
| 159 | echo output |
| 160 | echohl None |
| 161 | else |
| 162 | new |
| 163 | silent put =output |
| 164 | 1 |
| 165 | d |
| 166 | setl filetype=rust |
| 167 | setl buftype=nofile |
| 168 | setl bufhidden=hide |
| 169 | setl noswapfile |
| 170 | " give the buffer a nice name |
| 171 | let suffix = 1 |
| 172 | let basename = fnamemodify(a:dict.path, ':t:r') |
| 173 | while 1 |
| 174 | let bufname = basename |
| 175 | if suffix > 1 | let bufname .= ' ('.suffix.')' | endif |
| 176 | let bufname .= '.pretty.rs' |
| 177 | if bufexists(bufname) |
| 178 | let suffix += 1 |
| 179 | continue |
| 180 | endif |
| 181 | exe 'silent noautocmd keepalt file' fnameescape(bufname) |
| 182 | break |
| 183 | endwhile |
| 184 | endif |
| 185 | endtry |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 186 | endfunction |
| 187 | |
| 188 | function! rust#CompleteExpand(lead, line, pos) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 189 | if a:line[: a:pos-1] =~# '^RustExpand!\s*\S*$' |
| 190 | " first argument and it has a ! |
| 191 | let list = ["normal", "expanded", "typed", "expanded,identified", "flowgraph=", "everybody_loops"] |
| 192 | if !empty(a:lead) |
| 193 | call filter(list, "v:val[:len(a:lead)-1] == a:lead") |
| 194 | endif |
| 195 | return list |
| 196 | endif |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 197 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 198 | return glob(escape(a:lead, "*?[") . '*', 0, 1) |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 199 | endfunction |
| 200 | |
| 201 | " Emit {{{1 |
| 202 | |
| 203 | function! rust#Emit(type, args) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 204 | let args = s:ShellTokenize(a:args) |
| 205 | call s:WithPath(function("s:Emit"), a:type, args) |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 206 | endfunction |
| 207 | |
| 208 | function! s:Emit(dict, type, args) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 209 | try |
| 210 | let output_path = a:dict.tmpdir.'/output' |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 211 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 212 | let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 213 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 214 | let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) |
| 215 | let args = [relpath, '--emit', a:type, '-o', output_path] + a:args |
| 216 | let pwd = a:dict.istemp ? a:dict.tmpdir : '' |
| 217 | let output = s:system(pwd, shellescape(rustc) . " " . join(map(args, 'shellescape(v:val)'))) |
| 218 | if output !=# '' |
| 219 | echohl WarningMsg |
| 220 | echo output |
| 221 | echohl None |
| 222 | endif |
| 223 | if !v:shell_error |
| 224 | new |
| 225 | exe 'silent keepalt read' fnameescape(output_path) |
| 226 | 1 |
| 227 | d |
| 228 | if a:type ==# "llvm-ir" |
| 229 | setl filetype=llvm |
| 230 | let extension = 'll' |
| 231 | elseif a:type ==# "asm" |
| 232 | setl filetype=asm |
| 233 | let extension = 's' |
| 234 | endif |
| 235 | setl buftype=nofile |
| 236 | setl bufhidden=hide |
| 237 | setl noswapfile |
| 238 | if exists('l:extension') |
| 239 | " give the buffer a nice name |
| 240 | let suffix = 1 |
| 241 | let basename = fnamemodify(a:dict.path, ':t:r') |
| 242 | while 1 |
| 243 | let bufname = basename |
| 244 | if suffix > 1 | let bufname .= ' ('.suffix.')' | endif |
| 245 | let bufname .= '.'.extension |
| 246 | if bufexists(bufname) |
| 247 | let suffix += 1 |
| 248 | continue |
| 249 | endif |
| 250 | exe 'silent noautocmd keepalt file' fnameescape(bufname) |
| 251 | break |
| 252 | endwhile |
| 253 | endif |
| 254 | endif |
| 255 | endtry |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 256 | endfunction |
| 257 | |
| 258 | " Utility functions {{{1 |
| 259 | |
| 260 | " Invokes func(dict, ...) |
| 261 | " Where {dict} is a dictionary with the following keys: |
| 262 | " 'path' - The path to the file |
| 263 | " 'tmpdir' - The path to a temporary directory that will be deleted when the |
| 264 | " function returns. |
| 265 | " 'istemp' - 1 if the path is a file inside of {dict.tmpdir} or 0 otherwise. |
| 266 | " If {istemp} is 1 then an additional key is provided: |
| 267 | " 'tmpdir_relpath' - The {path} relative to the {tmpdir}. |
| 268 | " |
| 269 | " {dict.path} may be a path to a file inside of {dict.tmpdir} or it may be the |
| 270 | " existing path of the current buffer. If the path is inside of {dict.tmpdir} |
| 271 | " then it is guaranteed to have a '.rs' extension. |
| 272 | function! s:WithPath(func, ...) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 273 | let buf = bufnr('') |
| 274 | let saved = {} |
| 275 | let dict = {} |
| 276 | try |
| 277 | let saved.write = &write |
| 278 | set write |
| 279 | let dict.path = expand('%') |
| 280 | let pathisempty = empty(dict.path) |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 281 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 282 | " Always create a tmpdir in case the wrapped command wants it |
| 283 | let dict.tmpdir = tempname() |
| 284 | call mkdir(dict.tmpdir) |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 285 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 286 | if pathisempty || !saved.write |
| 287 | let dict.istemp = 1 |
| 288 | " if we're doing this because of nowrite, preserve the filename |
| 289 | if !pathisempty |
| 290 | let filename = expand('%:t:r').".rs" |
| 291 | else |
| 292 | let filename = 'unnamed.rs' |
| 293 | endif |
| 294 | let dict.tmpdir_relpath = filename |
| 295 | let dict.path = dict.tmpdir.'/'.filename |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 296 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 297 | let saved.mod = &modified |
| 298 | set nomodified |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 299 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 300 | silent exe 'keepalt write! ' . fnameescape(dict.path) |
| 301 | if pathisempty |
| 302 | silent keepalt 0file |
| 303 | endif |
| 304 | else |
| 305 | let dict.istemp = 0 |
| 306 | update |
| 307 | endif |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 308 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 309 | call call(a:func, [dict] + a:000) |
| 310 | finally |
| 311 | if bufexists(buf) |
| 312 | for [opt, value] in items(saved) |
| 313 | silent call setbufvar(buf, '&'.opt, value) |
| 314 | unlet value " avoid variable type mismatches |
| 315 | endfor |
| 316 | endif |
| 317 | if has_key(dict, 'tmpdir') | silent call s:RmDir(dict.tmpdir) | endif |
| 318 | endtry |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 319 | endfunction |
| 320 | |
| 321 | function! rust#AppendCmdLine(text) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 322 | call setcmdpos(getcmdpos()) |
| 323 | let cmd = getcmdline() . a:text |
| 324 | return cmd |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 325 | endfunction |
| 326 | |
| 327 | " Tokenize the string according to sh parsing rules |
| 328 | function! s:ShellTokenize(text) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 329 | " states: |
| 330 | " 0: start of word |
| 331 | " 1: unquoted |
| 332 | " 2: unquoted backslash |
| 333 | " 3: double-quote |
| 334 | " 4: double-quoted backslash |
| 335 | " 5: single-quote |
| 336 | let l:state = 0 |
| 337 | let l:current = '' |
| 338 | let l:args = [] |
| 339 | for c in split(a:text, '\zs') |
| 340 | if l:state == 0 || l:state == 1 " unquoted |
| 341 | if l:c ==# ' ' |
| 342 | if l:state == 0 | continue | endif |
| 343 | call add(l:args, l:current) |
| 344 | let l:current = '' |
| 345 | let l:state = 0 |
| 346 | elseif l:c ==# '\' |
| 347 | let l:state = 2 |
| 348 | elseif l:c ==# '"' |
| 349 | let l:state = 3 |
| 350 | elseif l:c ==# "'" |
| 351 | let l:state = 5 |
| 352 | else |
| 353 | let l:current .= l:c |
| 354 | let l:state = 1 |
| 355 | endif |
| 356 | elseif l:state == 2 " unquoted backslash |
| 357 | if l:c !=# "\n" " can it even be \n? |
| 358 | let l:current .= l:c |
| 359 | endif |
| 360 | let l:state = 1 |
| 361 | elseif l:state == 3 " double-quote |
| 362 | if l:c ==# '\' |
| 363 | let l:state = 4 |
| 364 | elseif l:c ==# '"' |
| 365 | let l:state = 1 |
| 366 | else |
| 367 | let l:current .= l:c |
| 368 | endif |
| 369 | elseif l:state == 4 " double-quoted backslash |
| 370 | if stridx('$`"\', l:c) >= 0 |
| 371 | let l:current .= l:c |
| 372 | elseif l:c ==# "\n" " is this even possible? |
| 373 | " skip it |
| 374 | else |
| 375 | let l:current .= '\'.l:c |
| 376 | endif |
| 377 | let l:state = 3 |
| 378 | elseif l:state == 5 " single-quoted |
| 379 | if l:c ==# "'" |
| 380 | let l:state = 1 |
| 381 | else |
| 382 | let l:current .= l:c |
| 383 | endif |
| 384 | endif |
| 385 | endfor |
| 386 | if l:state != 0 |
| 387 | call add(l:args, l:current) |
| 388 | endif |
| 389 | return l:args |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 390 | endfunction |
| 391 | |
| 392 | function! s:RmDir(path) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 393 | " sanity check; make sure it's not empty, /, or $HOME |
| 394 | if empty(a:path) |
| 395 | echoerr 'Attempted to delete empty path' |
| 396 | return 0 |
| 397 | elseif a:path ==# '/' || a:path ==# $HOME |
| 398 | let l:path = expand(a:path) |
| 399 | if l:path ==# '/' || l:path ==# $HOME |
| 400 | echoerr 'Attempted to delete protected path: ' . a:path |
| 401 | return 0 |
| 402 | endif |
| 403 | endif |
| 404 | |
| 405 | if !isdirectory(a:path) |
| 406 | return 0 |
| 407 | endif |
| 408 | |
| 409 | " delete() returns 0 when removing file successfully |
| 410 | return delete(a:path, 'rf') == 0 |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 411 | endfunction |
| 412 | |
| 413 | " Executes {cmd} with the cwd set to {pwd}, without changing Vim's cwd. |
| 414 | " If {pwd} is the empty string then it doesn't change the cwd. |
| 415 | function! s:system(pwd, cmd) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 416 | let cmd = a:cmd |
| 417 | if !empty(a:pwd) |
| 418 | let cmd = 'cd ' . shellescape(a:pwd) . ' && ' . cmd |
| 419 | endif |
| 420 | return system(cmd) |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 421 | endfunction |
| 422 | |
| 423 | " Playpen Support {{{1 |
| 424 | " Parts of gist.vim by Yasuhiro Matsumoto <mattn.jp@gmail.com> reused |
| 425 | " gist.vim available under the BSD license, available at |
| 426 | " http://github.com/mattn/gist-vim |
| 427 | function! s:has_webapi() |
| 428 | if !exists("*webapi#http#post") |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 429 | try |
| 430 | call webapi#http#post() |
| 431 | catch |
| 432 | endtry |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 433 | endif |
| 434 | return exists("*webapi#http#post") |
| 435 | endfunction |
| 436 | |
| 437 | function! rust#Play(count, line1, line2, ...) abort |
| 438 | redraw |
| 439 | |
| 440 | let l:rust_playpen_url = get(g:, 'rust_playpen_url', 'https://play.rust-lang.org/') |
| 441 | let l:rust_shortener_url = get(g:, 'rust_shortener_url', 'https://is.gd/') |
| 442 | |
| 443 | if !s:has_webapi() |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 444 | echohl ErrorMsg | echomsg ':RustPlay depends on webapi.vim (https://github.com/mattn/webapi-vim)' | echohl None |
| 445 | return |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 446 | endif |
| 447 | |
| 448 | let bufname = bufname('%') |
| 449 | if a:count < 1 |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 450 | let content = join(getline(a:line1, a:line2), "\n") |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 451 | else |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 452 | let save_regcont = @" |
| 453 | let save_regtype = getregtype('"') |
| 454 | silent! normal! gvy |
| 455 | let content = @" |
| 456 | call setreg('"', save_regcont, save_regtype) |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 457 | endif |
| 458 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 459 | let url = l:rust_playpen_url."?code=".webapi#http#encodeURI(content) |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 460 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 461 | if strlen(url) > 5000 |
| 462 | echohl ErrorMsg | echomsg 'Buffer too large, max 5000 encoded characters ('.strlen(url).')' | echohl None |
| 463 | return |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 464 | endif |
| 465 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 466 | let payload = "format=simple&url=".webapi#http#encodeURI(url) |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 467 | let res = webapi#http#post(l:rust_shortener_url.'create.php', payload, {}) |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 468 | if res.status[0] ==# '2' |
| 469 | let url = res.content |
| 470 | endif |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 471 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 472 | let footer = '' |
| 473 | if exists('g:rust_clip_command') |
| 474 | call system(g:rust_clip_command, url) |
| 475 | if !v:shell_error |
| 476 | let footer = ' (copied to clipboard)' |
| 477 | endif |
| 478 | endif |
| 479 | redraw | echomsg 'Done: '.url.footer |
| 480 | endfunction |
| 481 | |
| 482 | " Run a test under the cursor or all tests {{{1 |
| 483 | |
| 484 | " Finds a test function name under the cursor. Returns empty string when a |
| 485 | " test function is not found. |
| 486 | function! s:SearchTestFunctionNameUnderCursor() abort |
| 487 | let cursor_line = line('.') |
| 488 | |
| 489 | " Find #[test] attribute |
| 490 | if search('\m\C#\[test\]', 'bcW') is 0 |
| 491 | return '' |
| 492 | endif |
| 493 | |
| 494 | " Move to an opening brace of the test function |
| 495 | let test_func_line = search('\m\C^\s*fn\s\+\h\w*\s*(.\+{$', 'eW') |
| 496 | if test_func_line is 0 |
| 497 | return '' |
| 498 | endif |
| 499 | |
| 500 | " Search the end of test function (closing brace) to ensure that the |
| 501 | " cursor position is within function definition |
| 502 | if maparg('<Plug>(MatchitNormalForward)') ==# '' |
| 503 | keepjumps normal! % |
| 504 | else |
| 505 | " Prefer matchit.vim official plugin to native % since the plugin |
| 506 | " provides better behavior than original % (#391) |
| 507 | " To load the plugin, run: |
| 508 | " :packadd matchit |
| 509 | execute 'keepjumps' 'normal' "\<Plug>(MatchitNormalForward)" |
| 510 | endif |
| 511 | if line('.') < cursor_line |
| 512 | return '' |
| 513 | endif |
| 514 | |
| 515 | return matchstr(getline(test_func_line), '\m\C^\s*fn\s\+\zs\h\w*') |
| 516 | endfunction |
| 517 | |
| 518 | function! rust#Test(mods, winsize, all, options) abort |
| 519 | let manifest = findfile('Cargo.toml', expand('%:p:h') . ';') |
| 520 | if manifest ==# '' |
| 521 | return rust#Run(1, '--test ' . a:options) |
| 522 | endif |
| 523 | |
| 524 | " <count> defaults to 0, but we prefer an empty string |
| 525 | let winsize = a:winsize ? a:winsize : '' |
| 526 | |
| 527 | if has('terminal') |
| 528 | if has('patch-8.0.910') |
| 529 | let cmd = printf('%s noautocmd %snew | terminal ++curwin ', a:mods, winsize) |
| 530 | else |
| 531 | let cmd = printf('%s terminal ', a:mods) |
| 532 | endif |
| 533 | elseif has('nvim') |
| 534 | let cmd = printf('%s noautocmd %snew | terminal ', a:mods, winsize) |
| 535 | else |
| 536 | let cmd = '!' |
| 537 | let manifest = shellescape(manifest) |
| 538 | endif |
| 539 | |
| 540 | if a:all |
| 541 | if a:options ==# '' |
| 542 | execute cmd . 'cargo test --manifest-path' manifest |
| 543 | else |
| 544 | execute cmd . 'cargo test --manifest-path' manifest a:options |
| 545 | endif |
| 546 | return |
| 547 | endif |
| 548 | |
| 549 | let saved = getpos('.') |
| 550 | try |
| 551 | let func_name = s:SearchTestFunctionNameUnderCursor() |
| 552 | finally |
| 553 | call setpos('.', saved) |
| 554 | endtry |
| 555 | if func_name ==# '' |
| 556 | echohl ErrorMsg |
| 557 | echomsg 'No test function was found under the cursor. Please add ! to command if you want to run all tests' |
| 558 | echohl None |
| 559 | return |
| 560 | endif |
| 561 | if a:options ==# '' |
| 562 | execute cmd . 'cargo test --manifest-path' manifest func_name |
| 563 | else |
| 564 | execute cmd . 'cargo test --manifest-path' manifest func_name a:options |
| 565 | endif |
Bram Moolenaar | 3c2881d | 2017-03-21 19:18:29 +0100 | [diff] [blame] | 566 | endfunction |
| 567 | |
| 568 | " }}}1 |
| 569 | |
Gregory Anders | fc93594 | 2023-09-12 13:23:38 -0500 | [diff] [blame] | 570 | " vim: set et sw=4 sts=4 ts=8: |