├── .editorconfig ├── README.md ├── autoload └── devtools.vim ├── ftplugin ├── r.vim ├── rmd.vim └── rnoweb.vim └── plugin └── devtools.vim /.editorconfig: -------------------------------------------------------------------------------- 1 | # See http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | trim_trailing_whitespace = true 10 | 11 | [*.{vim}] 12 | indent_size = 4 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim-devtools-plugin 2 | 3 | Extension for the vim plugins [Vim-R-Plugin](https://github.com/jcfaria/Vim-R-plugin) and [Nvim-r](https://github.com/jalvesaq/Nvim-R) to support the [devtools R package](https://github.com/hadley/devtools). 4 | 5 | ## Installation 6 | Use your favourite bundle manager to install this script, e.g.: 7 | ```vim 8 | " Vundle 9 | Plugin 'mllg/vim-devtools-plugin' 10 | 11 | " Neobundle with lazy load 12 | NeoBundleLazy 'mllg/vim-devtools-plugin', 13 | \ {'autoload' : {'filetypes' : ['r','rmd','rnoweb']}} 14 | 15 | " dein with lazy load 16 | call dein#add('mllg/vim-devtools-plugin', {'on_ft' : ['r', 'rmd', 'rdoc', 'rnoweb']}) 17 | 18 | " Vim-Plug with lazy load 19 | Plug 'mllg/vim-devtools-plugin', { 'for': ['r', 'rmd', 'rnoweb']} 20 | 21 | " packer 22 | use { 'mllg/vim-devtools-plugin', ft = {'r', 'rmd', 'rnoweb'}} 23 | ``` 24 | 25 | ## Available commands 26 | 27 | * `RInstallPackage `: Runs `devtools::install`. 28 | * `RLoadPackage `: Runs `devtools::load_all`. 29 | * `RUnloadPackage `: Runs `devtools::unload`. 30 | * `RBuildPackage `: Runs `devtools::build`. 31 | * `RCheckPackage `: Runs `devtools::check`. 32 | * `RClean `: Runs `devtools::clean_dll` 33 | * `RTestPackage `: Runs `devtools::test` using specified filter (default `''`). 34 | * `RDocumentPackage `: Runs `devtools::document` 35 | * `RMake `: Runs `devtools::document`, then `devtools::install`. 36 | * `RSetupTest `: Loads "testthat" and invisibly sources all files matching pattern `^helper` in the test directory. 37 | * `RSourceFile `: Sources the given file. If argument is omitted, either the master file (if set, see below) or the current buffer gets sourced. 38 | * `RBuildPackgeTags `: Builds a tag file for the package and stores it in `g:devtools_rtags_dir` (default is "~/.rtags"). All tag files in this directory will automatically added to &tags for file types `r`, `rnoweb` and `rmd`. 39 | * `RUsage `: Loads the package and calls `codetools::checkUsagePackage()`. Reported problems are send to the quickfix window. 40 | * `RSetMaster `: Declare as the master file. Used in `:RSourceFile`. If is omitted, the current buffer is declared as master. 41 | Note that this choice is not persistent between vim sessions. 42 | 43 | The DESCRIPTION file is searched in `` and all its parents. 44 | Default for `` is the directory of the current buffer. 45 | 46 | The command `RTestFile` has been removed to simplify command completion with the much more frequently used `RTestFile`. 47 | If you liked it, you can restore it by defining the command in your `.vimrc`: 48 | ```vim 49 | command! -nargs=0 RTestFile :call devtools#test_file() 50 | ``` 51 | 52 | If you do not want any of these commands to be defined, set the option `devtools_commands`: 53 | ```vim 54 | let g:devtools_commands = 0 55 | ``` 56 | 57 | 58 | ## Support for FZF 59 | 60 | If you are a user of [FZF](https://github.com/junegunn/fzf.vim), you can grep your R history file with the following setup: 61 | 62 | ### Make R history persistent across sessions 63 | 64 | Put the following code in your `.Rprofile`, preferably inside the `.First` function: 65 | 66 | ```r 67 | if (interactive()) { 68 | history_file = normalizePath("~/.Rhistory", mustWork = FALSE) 69 | ok = try(utils::loadhistory(history_file)) 70 | if (inherits(ok, "try-error")) { 71 | message("History could not be loaded: ", history_file) 72 | } else { 73 | message("Loaded history: ", history_file) 74 | .Last <<- function() try(utils::savehistory(history_file)) 75 | } 76 | } 77 | ``` 78 | 79 | ### Create new command 80 | ```vim 81 | function! s:fzf_r_history() 82 | let l:history_file = expand('~/.Rhistory') 83 | call g:devtools#send_cmd('utils::savehistory("' . l:history_file . '")') 84 | call fzf#run({ 85 | \ 'source': 'cat ' . l:history_file . ' | grep -v "# \\[history skip\\]$" | uniq', 86 | \ 'sink' : g:SendCmdToR, 87 | \ 'options': '--no-sort --tac', 88 | \ 'down' : '40%' }) 89 | endfunction 90 | 91 | command! RHistory call s:fzf_r_history() 92 | ``` 93 | Map to a key as you find appropiate. 94 | -------------------------------------------------------------------------------- /autoload/devtools.vim: -------------------------------------------------------------------------------- 1 | function! devtools#escape(path) 2 | return substitute(a:path, '\', '/', 'g') 3 | endfunction 4 | 5 | function! devtools#send_cmd(line) 6 | call g:SendCmdToR(a:line . ' # [history skip]') 7 | endfunction 8 | 9 | function! devtools#find_description(path) 10 | if len(a:path) == 0 11 | let l:desc = findfile('DESCRIPTION', '.;') 12 | else 13 | let l:desc = findfile('DESCRIPTION', expand(a:path[0]) . ';') 14 | endif 15 | 16 | if l:desc == '' 17 | call RWarningMsg('DESCRIPTION file not found.') 18 | return "" 19 | endif 20 | 21 | let l:path = fnamemodify(l:desc, ':p:h') 22 | echo 'Using package DESCRIPTION in "' . l:path . '".' 23 | return devtools#escape(l:path) 24 | endfunction 25 | 26 | 27 | function devtools#simple_cmd(cmd, ...) 28 | let l:desc = devtools#find_description(a:000) 29 | if (l:desc != '') 30 | call devtools#send_cmd('devtools::' . a:cmd . '("' . l:desc . '")') 31 | endif 32 | endfunction 33 | 34 | 35 | function! devtools#test(...) 36 | if a:0 == 2 37 | let l:filter = a:2 38 | let l:desc = devtools#find_description([a:1]) 39 | else 40 | if a:0 == 1 41 | let l:filter = '"' . substitute(a:1, '"', '', 'g') . '"' 42 | else 43 | let l:filter = 'NULL' 44 | endif 45 | let l:desc = devtools#find_description([]) 46 | endif 47 | if (l:desc != '') 48 | if filereadable(l:desc . '/tests/tinytest.R') 49 | let l:line = 'tinytest::run_test_dir(file.path("' . l:desc . '", "inst", "tinytest"))' 50 | else 51 | let l:line = ' testthat::test_local("' . l:desc . '", filter = ' . l:filter . ')' 52 | endif 53 | call devtools#send_cmd(l:line) 54 | endif 55 | endfunction 56 | 57 | 58 | function! devtools#test_file() 59 | let l:filter = '"' . substitute(expand('%:t:r'), '^test[-_]', '', '') . '"' 60 | call devtools#test(l:filter) 61 | endfunction 62 | 63 | 64 | function! devtools#make(...) 65 | let l:desc = devtools#find_description(a:000) 66 | if (l:desc != '') 67 | let l:line = 'devtools::document("' . l:desc . '")' 68 | let l:line .= '; devtools::install("' . l:desc . '")' 69 | call devtools#send_cmd(l:line) 70 | endif 71 | call devtools#build_tags(l:desc) 72 | call devtools#use_r_tags() 73 | endfunction 74 | 75 | 76 | function! devtools#setup_test(...) 77 | let l:desc = devtools#find_description(a:000) 78 | if (l:desc != '') 79 | let l:line = 'library("testthat"); devtools::load_all("' . l:desc . '")' 80 | let l:line .= '; invisible(lapply(list.files(file.path("' . l:desc . '", "tests", "testthat"),' 81 | let l:line .= ' pattern="^helper", full.names=TRUE), source, chdir=TRUE, verbose=FALSE))' 82 | call devtools#send_cmd(l:line) 83 | endif 84 | endfunction 85 | 86 | 87 | function! devtools#build_tags(...) 88 | let l:rtags = fnamemodify(g:devtools_rtags_dir, ':p') 89 | if !isdirectory(l:rtags) 90 | call mkdir(l:rtags) 91 | endif 92 | let l:desc = devtools#find_description(a:000) 93 | if (l:desc != '') 94 | let l:line = 'local({ ' 95 | let l:line .= printf('etagsfile = tempfile(); utils::rtags(path=file.path("%s", "R"), ofile= etagsfile)', l:desc) 96 | let l:line .= printf('; etags2ctags(etagsfile, file.path("%s", sprintf("%%s.ctags", devtools::as.package("%s")$package)))', devtools#escape(l:rtags), l:desc) 97 | let l:line .= ' })' 98 | call devtools#send_cmd(l:line) 99 | call devtools#use_r_tags() 100 | endif 101 | endfunction 102 | 103 | function! devtools#source_file(...) 104 | if a:0 == 0 105 | if exists("s:devtools_master_file") 106 | let l:file = s:devtools_master_file 107 | else 108 | let l:file = devtools#escape(expand('%:p')) 109 | endif 110 | else 111 | let l:file = a:1 112 | endif 113 | call devtools#send_cmd(printf('source("%s")', l:file)) 114 | endfunction 115 | 116 | 117 | function! devtools#usage(...) 118 | let l:desc = devtools#find_description(a:000) 119 | if (l:desc != '') 120 | let l:tmp = tempname() 121 | let l:line = 'devtools::load_all("' . l:desc . '")' 122 | let l:line .= '; local({ tmp = capture.output(codetools::checkUsagePackage(devtools::as.package("' . l:desc . '")$package)); writeLines(tmp, "' . devtools#escape(l:tmp) . '")})' 123 | call devtools#send_cmd(l:line) 124 | setlocal efm+=%m\ (%f:%l%.%#) 125 | 126 | while !filereadable(l:tmp) 127 | sleep 100m 128 | endwhile 129 | sleep 50m 130 | execute ":cfile " . l:tmp 131 | call delete(l:tmp) 132 | endif 133 | endfunction 134 | 135 | function! devtools#set_master(...) 136 | if a:0 == 0 137 | let s:devtools_master_file = devtools#escape(expand('%:p')) 138 | else 139 | let s:devtools_master_file = a:1 140 | endif 141 | endfunction 142 | 143 | function! devtools#use_r_tags() 144 | let l:rtags = fnamemodify(g:devtools_rtags_dir, ':p') 145 | if isdirectory(l:rtags) 146 | let l:tags = extend(split(&tags, ','), split(glob(l:rtags . '*.ctags'), '\n')) 147 | let &tags = join(filter(l:tags, 'count(l:tags, v:val) == 1'), ',') 148 | endif 149 | endfunction 150 | -------------------------------------------------------------------------------- /ftplugin/r.vim: -------------------------------------------------------------------------------- 1 | call devtools#use_r_tags() 2 | -------------------------------------------------------------------------------- /ftplugin/rmd.vim: -------------------------------------------------------------------------------- 1 | call devtools#use_r_tags() 2 | -------------------------------------------------------------------------------- /ftplugin/rnoweb.vim: -------------------------------------------------------------------------------- 1 | call devtools#use_r_tags() 2 | -------------------------------------------------------------------------------- /plugin/devtools.vim: -------------------------------------------------------------------------------- 1 | if exists("g:loaded_vim_devtools_plugin") || &cpo 2 | finish 3 | endif 4 | 5 | let g:loaded_vim_devtools_plugin=1 6 | let s:keepcpo=&cpo 7 | set cpo&vim 8 | 9 | if !exists('g:devtools_commands') || g:devtools_commands 10 | command! -complete=dir -nargs=? RInstallPackage :call devtools#simple_cmd('install', ) 11 | command! -complete=dir -nargs=? RLoadPackage :call devtools#simple_cmd('load_all', ) 12 | command! -complete=dir -nargs=? RUnloadPackage :call devtools#simple_cmd('unload', ) 13 | command! -complete=dir -nargs=? RBuildPackage :call devtools#simple_cmd('build', ) 14 | command! -complete=dir -nargs=? RCheckPackage :call devtools#simple_cmd('check', ) 15 | command! -complete=dir -nargs=? RDocumentPackage :call devtools#simple_cmd('document', ) 16 | command! -complete=dir -nargs=? RClean :call devtools#simple_cmd('clean_dll', ) 17 | command! -complete=dir -nargs=* RTestPackage :call devtools#test() 18 | command! -complete=dir -nargs=? RMake :call devtools#make() 19 | command! -complete=dir -nargs=? RSetupTest :call devtools#setup_test() 20 | command! -complete=dir -nargs=? RBuildPackageTags :call devtools#build_tags() 21 | command! -complete=file -nargs=? RSourceFile :call devtools#source_file() 22 | command! -complete=file -nargs=? RSetMaster :call devtools#set_master() 23 | command! -complete=file -nargs=? RUsage :call devtools#usage() 24 | endif 25 | 26 | let g:devtools_rtags_dir = get(g:, 'devtools_rtags_dir', expand('~/.rtags')) 27 | 28 | let &cpo=s:keepcpo 29 | unlet s:keepcpo 30 | --------------------------------------------------------------------------------