diff options
Diffstat (limited to '')
| -rw-r--r-- | autoload/rust.vim | 91 | ||||
| -rw-r--r-- | autoload/rust/debugging.vim | 1 | ||||
| -rw-r--r-- | autoload/rust/delimitmate.vim | 48 | ||||
| -rw-r--r-- | autoload/rustfmt.vim | 142 | 
4 files changed, 226 insertions, 56 deletions
| diff --git a/autoload/rust.vim b/autoload/rust.vim index 7e766da1..77c38b7c 100644 --- a/autoload/rust.vim +++ b/autoload/rust.vim @@ -86,7 +86,7 @@ function! s:Run(dict, rustc_args, args)      let pwd = a:dict.istemp ? a:dict.tmpdir : ''      let output = s:system(pwd, shellescape(rustc) . " " . join(map(rustc_args, 'shellescape(v:val)'))) -    if output != '' +    if output !=# ''          echohl WarningMsg          echo output          echohl None @@ -153,7 +153,7 @@ function! s:Expand(dict, pretty, args)  endfunction  function! rust#CompleteExpand(lead, line, pos) -    if a:line[: a:pos-1] =~ '^RustExpand!\s*\S*$' +    if a:line[: a:pos-1] =~# '^RustExpand!\s*\S*$'          " first argument and it has a !          let list = ["normal", "expanded", "typed", "expanded,identified", "flowgraph=", "everybody_loops"]          if !empty(a:lead) @@ -182,7 +182,7 @@ function! s:Emit(dict, type, args)          let args = [relpath, '--emit', a:type, '-o', output_path] + a:args          let pwd = a:dict.istemp ? a:dict.tmpdir : ''          let output = s:system(pwd, shellescape(rustc) . " " . join(map(args, 'shellescape(v:val)'))) -        if output != '' +        if output !=# ''              echohl WarningMsg              echo output              echohl None @@ -192,10 +192,10 @@ function! s:Emit(dict, type, args)              exe 'silent keepalt read' fnameescape(output_path)              1              d -            if a:type == "llvm-ir" +            if a:type ==# "llvm-ir"                  setl filetype=llvm                  let extension = 'll' -            elseif a:type == "asm" +            elseif a:type ==# "asm"                  setl filetype=asm                  let extension = 's'              endif @@ -261,8 +261,8 @@ function! s:WithPath(func, ...)              let dict.tmpdir_relpath = filename              let dict.path = dict.tmpdir.'/'.filename -            let saved.mod = &mod -            set nomod +            let saved.mod = &modified +            set nomodified              silent exe 'keepalt write! ' . fnameescape(dict.path)              if pathisempty @@ -343,7 +343,7 @@ function! s:ShellTokenize(text)              endif              let l:state = 3          elseif l:state == 5 " single-quoted -            if l:c == "'" +            if l:c ==# "'"                  let l:state = 1              else                  let l:current .= l:c @@ -361,7 +361,7 @@ function! s:RmDir(path)      if empty(a:path)          echoerr 'Attempted to delete empty path'          return 0 -    elseif a:path == '/' || a:path == $HOME +    elseif a:path ==# '/' || a:path ==# $HOME          echoerr 'Attempted to delete protected path: ' . a:path          return 0      endif @@ -414,22 +414,83 @@ function! rust#Play(count, line1, line2, ...) abort          call setreg('"', save_regcont, save_regtype)      endif -    let body = l:rust_playpen_url."?code=".webapi#http#encodeURI(content) +    let url = l:rust_playpen_url."?code=".webapi#http#encodeURI(content) -    if strlen(body) > 5000 -        echohl ErrorMsg | echomsg 'Buffer too large, max 5000 encoded characters ('.strlen(body).')' | echohl None +    if strlen(url) > 5000 +        echohl ErrorMsg | echomsg 'Buffer too large, max 5000 encoded characters ('.strlen(url).')' | echohl None          return      endif -    let payload = "format=simple&url=".webapi#http#encodeURI(body) +    let payload = "format=simple&url=".webapi#http#encodeURI(url)      let res = webapi#http#post(l:rust_shortener_url.'create.php', payload, {}) -    let url = res.content +    if res.status[0] ==# '2' +        let url = res.content +    endif +    let footer = ''      if exists('g:rust_clip_command')          call system(g:rust_clip_command, url) +        if !v:shell_error +            let footer = ' (copied to clipboard)' +        endif      endif +    redraw | echomsg 'Done: '.url.footer +endfunction -    redraw | echomsg 'Done: '.url +" Run a test under the cursor or all tests {{{1 + +" Finds a test function name under the cursor. Returns empty string when a +" test function is not found. +function! s:SearchTestFunctionNameUnderCursor() abort +    let cursor_line = line('.') + +    " Find #[test] attribute +    if search('#\[test]', 'bcW') is 0 +        return '' +    endif + +    " Move to an opening brace of the test function +    let test_func_line = search('^\s*fn\s\+\h\w*\s*(.\+{$', 'eW') +    if test_func_line is 0 +        return '' +    endif + +    " Search the end of test function (closing brace) to ensure that the +    " cursor position is within function definition +    normal! % +    if line('.') < cursor_line +        return '' +    endif + +    return matchstr(getline(test_func_line), '^\s*fn\s\+\zs\h\w*') +endfunction + +function! rust#Test(all, options) abort +    let pwd = expand('%:p:h') +    if findfile('Cargo.toml', pwd . ';') ==# '' +        return rust#Run(1, '--test ' . a:options) +    endif + +    let pwd = shellescape(pwd) + +    if a:all +        execute '!cd ' . pwd . ' && cargo test ' . a:options +        return +    endif + +    let saved = getpos('.') +    try +        let func_name = s:SearchTestFunctionNameUnderCursor() +        if func_name ==# '' +            echohl ErrorMsg +            echo 'No test function was found under the cursor. Please add ! to command if you want to run all tests' +            echohl None +            return +        endif +        execute '!cd ' . pwd . ' && cargo test ' . func_name . ' ' . a:options +    finally +        call setpos('.', saved) +    endtry  endfunction  " }}}1 diff --git a/autoload/rust/debugging.vim b/autoload/rust/debugging.vim index 352556d7..ff88e00c 100644 --- a/autoload/rust/debugging.vim +++ b/autoload/rust/debugging.vim @@ -18,6 +18,7 @@ let s:global_variable_list = [              \ 'rust_last_rustc_args',              \ 'rust_original_delimitMate_excluded_regions',              \ 'rust_playpen_url', +            \ 'rust_prev_delimitMate_quotes',              \ 'rust_recent_nearest_cargo_tol',              \ 'rust_recent_root_cargo_toml',              \ 'rust_recommended_style', diff --git a/autoload/rust/delimitmate.vim b/autoload/rust/delimitmate.vim new file mode 100644 index 00000000..e99cc87d --- /dev/null +++ b/autoload/rust/delimitmate.vim @@ -0,0 +1,48 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'rust') == -1 +   +let s:delimitMate_extra_excluded_regions = ',rustLifetimeCandidate,rustGenericLifetimeCandidate' + +" For this buffer, when delimitMate issues the `User delimitMate_map` +" event in the autocommand system, add the above-defined extra excluded +" regions to delimitMate's state, if they have not already been added. +function! rust#delimitmate#onMap() abort +    if &filetype !=# 'rust' +        return +    endif + +    if get(b:, "delimitMate_quotes") +        let b:rust_prev_delimitMate_quotes = b:delimitMate_quotes +    endif +    let b:delimitMate_quotes = "\" `" + +    if match(delimitMate#Get("excluded_regions"), +                \ s:delimitMate_extra_excluded_regions) == -1 +        call delimitMate#Set("excluded_regions", +                    \delimitMate#Get("excluded_regions").s:delimitMate_extra_excluded_regions) +    endif +endfunction + +" For this buffer, when delimitMate issues the `User delimitMate_unmap` +" event in the autocommand system, delete the above-defined extra excluded +" regions from delimitMate's state (the deletion being idempotent and +" having no effect if the extra excluded regions are not present in the +" targeted part of delimitMate's state). +function! rust#delimitmate#onUnmap() abort +    if &filetype !=# 'rust' +        return +    endif + +    if get(b:, "rust_prev_delimitMate_quotes") +        let b:delimitMate_quotes = b:rust_prev_delimitMate_quotes +    endif + +    call delimitMate#Set("excluded_regions", substitute( +               \ delimitMate#Get("excluded_regions"), +               \ '\C\V' . s:delimitMate_extra_excluded_regions, +               \ '', 'g')) +endfunction + +" vim: set et sw=4 sts=4 ts=8: + + +endif diff --git a/autoload/rustfmt.vim b/autoload/rustfmt.vim index c0335173..916736c0 100644 --- a/autoload/rustfmt.vim +++ b/autoload/rustfmt.vim @@ -63,6 +63,20 @@ function! s:RustfmtWriteMode()      endif  endfunction +function! s:RustfmtConfig() +    let l:rustfmt_toml = findfile('rustfmt.toml', expand('%:p:h') . ';') +    if l:rustfmt_toml !=# '' +        return '--config-path '.l:rustfmt_toml +    endif + +    let l:_rustfmt_toml = findfile('.rustfmt.toml', expand('%:p:h') . ';') +    if l:_rustfmt_toml !=# '' +        return '--config-path '.l:_rustfmt_toml +    endif + +    return '' +endfunction +  function! s:RustfmtCommandRange(filename, line1, line2)      if g:rustfmt_file_lines == 0          echo "--file-lines is not supported in the installed `rustfmt` executable" @@ -71,6 +85,7 @@ function! s:RustfmtCommandRange(filename, line1, line2)      let l:arg = {"file": shellescape(a:filename), "range": [a:line1, a:line2]}      let l:write_mode = s:RustfmtWriteMode() +    let l:rustfmt_config = s:RustfmtConfig()      " FIXME: When --file-lines gets to be stable, enhance this version range checking      " accordingly. @@ -78,34 +93,73 @@ function! s:RustfmtCommandRange(filename, line1, line2)                  \ (s:rustfmt_unstable_features && (s:rustfmt_version < '1.'))                  \ ? '--unstable-features' : '' -    let l:cmd = printf("%s %s %s %s --file-lines '[%s]' %s", g:rustfmt_command,  -                \ l:write_mode, g:rustfmt_options,  -                \ l:unstable_features, json_encode(l:arg), shellescape(a:filename)) +    let l:cmd = printf("%s %s %s %s %s --file-lines '[%s]' %s", g:rustfmt_command, +                \ l:write_mode, g:rustfmt_options, +                \ l:unstable_features, l:rustfmt_config, +                \ json_encode(l:arg), shellescape(a:filename))      return l:cmd  endfunction -function! s:RustfmtCommand(filename) -    let l:write_mode = s:RustfmtWriteMode() -    return g:rustfmt_command . " ". l:write_mode . " " . g:rustfmt_options . " " . shellescape(a:filename) +function! s:RustfmtCommand() +    if g:rustfmt_emit_files +        let l:write_mode = "--emit=stdout" +    else +        let l:write_mode = "--write-mode=display" +    endif +    " rustfmt will pick on the right config on its own due to the +    " current directory change. +    return g:rustfmt_command . " ". l:write_mode . " " . g:rustfmt_options +endfunction + +function! s:DeleteLines(start, end) abort +    silent! execute a:start . ',' . a:end . 'delete _'  endfunction  function! s:RunRustfmt(command, tmpname, fail_silently)      mkview! -    if exists("*systemlist") -        let out = systemlist(a:command) +    let l:stderr_tmpname = tempname() +    let l:command = a:command . ' 2> ' . l:stderr_tmpname + +    if a:tmpname ==# '' +        " Rustfmt in stdin/stdout mode + +        " chdir to the directory of the file +        let l:has_lcd = haslocaldir() +        let l:prev_cd = getcwd() +        execute 'lchdir! '.expand('%:h') + +        let l:buffer = getline(1, '$') +        if exists("*systemlist") +            silent let out = systemlist(l:command, l:buffer) +        else +            silent let out = split(system(l:command, l:buffer), '\r\?\n') +        endif      else -        let out = split(system(a:command), '\r\?\n') +        if exists("*systemlist") +            silent let out = systemlist(l:command) +        else +            silent let out = split(system(l:command), '\r\?\n') +        endif      endif -    if v:shell_error == 0 || v:shell_error == 3 +    let l:stderr = readfile(l:stderr_tmpname) + +    call delete(l:stderr_tmpname) + +    if v:shell_error == 0          " remove undo point caused via BufWritePre          try | silent undojoin | catch | endtry -        " take the tmpfile's content, this is better than rename -        " because it preserves file modes. -        let l:content = readfile(a:tmpname) -        1,$d _ +        if a:tmpname ==# '' +            let l:content = l:out +        else +            " take the tmpfile's content, this is better than rename +            " because it preserves file modes. +            let l:content = readfile(a:tmpname) +        endif + +        call s:DeleteLines(len(l:content), line('$'))          call setline(1, l:content)          " only clear location list if it was previously filled to prevent @@ -117,61 +171,69 @@ function! s:RunRustfmt(command, tmpname, fail_silently)          endif      elseif g:rustfmt_fail_silently == 0 && a:fail_silently == 0          " otherwise get the errors and put them in the location list -        let errors = [] +        let l:errors = [] -        let prev_line = "" -        for line in out +        let l:prev_line = "" +        for l:line in l:stderr              " error: expected one of `;` or `as`, found `extern`              "  --> src/main.rs:2:1 -            let tokens = matchlist(line, '^\s-->\s\(.\{-}\):\(\d\+\):\(\d\+\)$') +            let tokens = matchlist(l:line, '^\s\+-->\s\(.\{-}\):\(\d\+\):\(\d\+\)$')              if !empty(tokens) -                call add(errors, {"filename": @%, +                call add(l:errors, {"filename": @%,                              \"lnum":	tokens[2],                              \"col":	tokens[3], -                            \"text":	prev_line}) +                            \"text":	l:prev_line})              endif -            let prev_line = line +            let l:prev_line = l:line          endfor -        if empty(errors) -            % | " Couldn't detect rustfmt error format, output errors -        endif - -        if !empty(errors) -            call setloclist(0, errors, 'r') +        if !empty(l:errors) +            call setloclist(0, l:errors, 'r')              echohl Error | echomsg "rustfmt returned error" | echohl None +        else +            echo "rust.vim: was not able to parse rustfmt messages. Here is the raw output:" +            echo "\n" +            for l:line in l:stderr +                echo l:line +            endfor          endif          let s:got_fmt_error = 1          lwindow      endif -    call delete(a:tmpname) +    " Restore the current directory if needed +    if a:tmpname ==# '' +        if l:has_lcd +            execute 'lchdir! '.l:prev_cd +        else +            execute 'chdir! '.l:prev_cd +        endif +    endif      silent! loadview  endfunction -function! s:rustfmtSaveToTmp() +function! rustfmt#FormatRange(line1, line2)      let l:tmpname = tempname()      call writefile(getline(1, '$'), l:tmpname) -    return l:tmpname -endfunction - -function! rustfmt#FormatRange(line1, line2) -    let l:tmpname = s:rustfmtSaveToTmp()      let command = s:RustfmtCommandRange(l:tmpname, a:line1, a:line2)      call s:RunRustfmt(command, l:tmpname, 0) +    call delete(l:tmpname)  endfunction  function! rustfmt#Format() -    let l:tmpname = s:rustfmtSaveToTmp() -    let command = s:RustfmtCommand(l:tmpname) -    call s:RunRustfmt(command, l:tmpname, 0) +    call s:RunRustfmt(s:RustfmtCommand(), '', 0) +endfunction + +function! rustfmt#Cmd() +    " Mainly for debugging +    return s:RustfmtCommand()  endfunction  function! rustfmt#PreWrite()      if rust#GetConfigVar('rustfmt_autosave_if_config_present', 0) -        if findfile('rustfmt.toml', '.;') !=# ''  +        if findfile('rustfmt.toml', '.;') !=# '' || findfile('.rustfmt.toml', '.;') !=# ''              let b:rustfmt_autosave = 1              let b:rustfmt_autosave_because_of_config = 1          endif @@ -181,9 +243,7 @@ function! rustfmt#PreWrite()          return      endif -    let l:tmpname = s:rustfmtSaveToTmp() -    let command = s:RustfmtCommand(l:tmpname) -    call s:RunRustfmt(command, l:tmpname, 1) +    call s:RunRustfmt(s:RustfmtCommand(), '', 1)  endfunction | 
