├── .gitignore ├── LICENSE ├── README.md ├── autoload ├── rec.vim └── reccomplete.vim ├── compiler └── rec.vim ├── doc └── rec.txt ├── ftdetect └── rec.vim ├── ftplugin └── rec.vim └── syntax └── rec.vim /.gitignore: -------------------------------------------------------------------------------- 1 | doc/tags 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | VIM LICENSE 2 | 3 | I) There are no restrictions on distributing unmodified copies of vim-rec 4 | except that they must include this license text. You can also distribute 5 | unmodified parts of vim-rec, likewise unrestricted except that they must 6 | include this license text. You are also allowed to include executables 7 | that you made from the unmodified vim-rec sources, plus your own usage 8 | examples and Vim scripts. 9 | 10 | II) It is allowed to distribute a modified (or extended) version of vim-rec, 11 | including executables and/or source code, when the following four 12 | conditions are met: 13 | 1) This license text must be included unmodified. 14 | 2) The modified vim-rec must be distributed in one of the following five 15 | ways: 16 | a) If you make changes to vim-rec yourself, you must clearly describe 17 | in the distribution how to contact you. When the maintainer asks 18 | you (in any way) for a copy of the modified vim-rec you 19 | distributed, you must make your changes, including source code, 20 | available to the maintainer without fee. The maintainer reserves 21 | the right to include your changes in the official version of 22 | vim-rec. What the maintainer will do with your changes and under 23 | what license they will be distributed is negotiable. If there has 24 | been no negotiation then this license, or a later version, also 25 | applies to your changes. The current maintainer is Bram Moolenaar 26 | . If this changes it will be announced in appropriate 27 | places (most likely vim.sf.net, www.vim.org and/or comp.editors). 28 | When it is completely impossible to contact the maintainer, the 29 | obligation to send him your changes ceases. Once the maintainer has 30 | confirmed that he has received your changes they will not have to be 31 | sent again. 32 | b) If you have received a modified vim-rec that was distributed as 33 | mentioned under a) you are allowed to further distribute it 34 | unmodified, as mentioned at I). If you make additional changes the 35 | text under a) applies to those changes. 36 | c) Provide all the changes, including source code, with every copy of 37 | the modified vim-rec you distribute. This may be done in the form 38 | of a context diff. You can choose what license to use for new code 39 | you add. The changes and their license must not restrict others 40 | from making their own changes to the official version of vim-rec. 41 | d) When you have a modified vim-rec which includes changes as 42 | mentioned under c), you can distribute it without the source code 43 | for the changes if the following three conditions are met: 44 | - The license that applies to the changes permits you to distribute 45 | the changes to the Vim maintainer without fee or restriction, and 46 | permits the Vim maintainer to include the changes in the official 47 | version of vim-rec without fee or restriction. 48 | - You keep the changes for at least three years after last 49 | distributing the corresponding modified vim-rec. When the 50 | maintainer or someone who you distributed the modified vim-rec 51 | to asks you (in any way) for the changes within this period, you 52 | must make them available to him. 53 | - You clearly describe in the distribution how to contact you. This 54 | contact information must remain valid for at least three years 55 | after last distributing the corresponding modified vim-rec, or 56 | as long as possible. 57 | e) When the GNU General Public License (GPL) applies to the changes, 58 | you can distribute the modified vim-rec under the GNU GPL version 59 | 2 or any later version. 60 | 3) A message must be added, at least in the output of the ":version" 61 | command and in the intro screen, such that the user of the modified 62 | vim-rec is able to see that it was modified. When distributing as 63 | mentioned under 2)e) adding the message is only required for as far as 64 | this does not conflict with the license used for the changes. 65 | 4) The contact information as required under 2)a) and 2)d) must not be 66 | removed or changed, except that the person himself can make 67 | corrections. 68 | 69 | III) If you distribute a modified version of vim-rec, you are encouraged to 70 | use the Vim license for your changes and make them available to the 71 | maintainer, including the source code. The preferred way to do this is 72 | by e-mail or by uploading the files to a server and e-mailing the URL. If 73 | the number of changes is small (e.g., a modified Makefile) e-mailing a 74 | context diff will do. The e-mail address to be used is 75 | 76 | 77 | IV) It is not allowed to remove this license from the distribution of the 78 | vim-rec sources, parts of it or from a modified version. You may use 79 | this license for previous vim-rec releases instead of the license that 80 | they came with, at your option. 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GNU Recutils syntax support for Vim 2 | 3 | This plugin provides syntax highlighting and runtime support for [GNU Recutils](https://www.gnu.org/software/recutils/)' .rec files. 4 | 5 | https://user-images.githubusercontent.com/39915/148492614-d88d21cf-a6a2-4a18-a50d-06c6935b9080.mov 6 | 7 | ## Installation 8 | 9 | * If you're on Vim 8.x then you can use the built-in package support by running: 10 | ``` 11 | mkdir -p ~/.vim/pack/default/start/vim-rec 12 | git clone git@github.com:zaid/vim-rec.git ~/.vim/pack/default/start/vim-rec 13 | ``` 14 | * If you're using vim-plug then you can install it by adding the following to your `.vimrc`: 15 | 16 | `Plug 'zaid/vim-rec'` 17 | 18 | * If you're using minpac then you can install it by adding the following to your `.vimrc`: 19 | 20 | `call minpac#add('zaid/vim-rec')` 21 | 22 | ## Features 23 | 24 | * Syntax highlighting. 25 | * Folding for records. (can be disabled by setting the `g:recutils_no_folding` variable to `1`). 26 | * Command wrappers for `recsel`, `recfix`, `recinf` and `rec2csv`. 27 | * Auto-completion of record set properties. 28 | * Record navigation commands and maps. 29 | * Preview record descriptor in a popup/floating/preview window. 30 | 31 | See `help :recutils` for more information. 32 | 33 | ## Examples 34 | 35 | ### Recfix 36 | To find any syntax errors in the file, you can run `:Recfix` in any `.rec` buffer 37 | and the syntax errors will be loaded in a local `location-list` where you'll be 38 | able to navigate the list and jump straight to the line with the error. 39 | 40 | *Note* The `location-list` will not open if there are no errors in the file. 41 | 42 | ### Recsel 43 | To query the `Title` of any book with a rating over `4`, you can execute the following: 44 | `:Recsel -t Book -p Title -e Rating>4` 45 | 46 | ### Recinf 47 | To query the descriptors of records stored in a specifc `.rec` buffer, you can execute the following: 48 | `:Recinf -d` 49 | 50 | ### Rec2csv 51 | To convert Rec data into CSV format and populate it in a split buffer, you can execute the following: 52 | `:Rec2csv -tBook` 53 | 54 | ## Notes 55 | 56 | Whitespace might need to be escaped in certain scenarios. For example, the following line: 57 | `:Recsel -t Book -p Title -e Rating>4` 58 | can be rewritten as: 59 | `:Recsel -t Book -p Title -e Rating\ >\ 4` 60 | 61 | To close the floating window in Neovim, you can use any of the `q`, `Esc`, ``, `Ctrl-C` or `` keys. 62 | 63 | ## Command maps 64 | 65 | There are 6 command maps defined (which can be disabled by setting the `g:recutils_no_maps` variable to `1`): 66 | * `rf` Invokes `:Recfix --check` on the current buffer. 67 | * `rn` populates the Vim command line with `:Recinf`. 68 | * `rs` populates the Vim command line with `:Recsel`. 69 | * `rv` populates the Vim command line with `:Rec2csv`. 70 | * `r]` jumps to the next record descriptor. 71 | * `r[` jumps to the previous record descriptor. 72 | * `r?` show the current record descriptor in a popup/floating/preview window. 73 | 74 | ## Auto-completion 75 | 76 | Basic support for auto-completing record set properties is available (by using `C-x C-o` when in insert mode). 77 | It can be disabled by setting the `g:recutils_no_autocompletion` variable to `1`. 78 | 79 | ## TODO 80 | 81 | * Keymaps for formatting text. 82 | 83 | ## License 84 | 85 | Copyright Zaid Al-Jarrah. Distributed under the same terms as Vim itself. See `:help license`. 86 | -------------------------------------------------------------------------------- /autoload/rec.vim: -------------------------------------------------------------------------------- 1 | " Maintainer: Zaid Al-Jarrah 2 | 3 | if exists('g:autoloaded_rec') 4 | finish 5 | endif 6 | let g:autoloaded_rec = 1 7 | 8 | " Execute recsel command with arguments. 9 | function! rec#Recsel(...) abort 10 | call s:ExecuteCommand('recsel', s:GetLocationListCallbackFunctions(), a:000) 11 | endfunction 12 | 13 | " Execute recinf command with arguments. 14 | function! rec#Recinf(...) abort 15 | call s:ExecuteCommand('recinf', s:GetLocationListCallbackFunctions(), a:000) 16 | endfunction 17 | 18 | " Execute recfix command with arguments. 19 | function! rec#Recfix(...) abort 20 | call s:ExecuteCommand('recfix', s:GetLocationListCallbackFunctions(), a:000) 21 | endfunction 22 | 23 | " Execute rec2csv command with arguments. 24 | function! rec#Rec2csv(...) abort 25 | call s:ExecuteCommand('rec2csv', s:GetBufferCallbackFunctions(a:000), a:000) 26 | endfunction 27 | 28 | " Find the previous record descriptor. 29 | function! rec#RecPreviousDescriptor() abort 30 | call search('\v^\%rec:', 'bW') 31 | endfunction 32 | 33 | " Find the next record descriptor. 34 | function! rec#RecNextDescriptor() abort 35 | call search('\v^\%rec:', 'W') 36 | endfunction 37 | 38 | " Show the current record descriptor block in a popup/preview window. 39 | function! rec#RecPreviewDescriptor() abort 40 | let previousWindowView = winsaveview() 41 | let previousDescriptorStart = search('\v^\%rec:', 'bW') 42 | let previousDescriptorEnd = l:previousDescriptorStart > 0 ? search('\v^\n', 'W') : 0 43 | let descriptorBlock = getline(l:previousDescriptorStart, l:previousDescriptorEnd - 1) 44 | 45 | call winrestview(l:previousWindowView) 46 | 47 | if s:SupportsPopups() && !empty(l:descriptorBlock) 48 | call s:ShowPopupWindow(l:descriptorBlock) 49 | else 50 | call s:ShowPreviewWindow(l:descriptorBlock) 51 | endif 52 | endfunction 53 | 54 | " Execute a command with arguments (either synchronously or asynchronously). 55 | function! s:ExecuteCommand(command, callbackFunctions, arguments) abort 56 | let commandWithArguments = [a:command] 57 | let filename = fnameescape(s:GetFilenameFromArgumentsList(a:arguments, expand('%@'))) 58 | let arguments = s:GetCommandArgumentsFromArgumentsList(a:arguments) 59 | 60 | call extend(l:commandWithArguments, l:arguments + [l:filename]) 61 | call s:PrepareLocationWindow(join(l:commandWithArguments)) 62 | 63 | if s:SupportsAsyncJobs() 64 | call s:ExecuteAsyncCommand(a:callbackFunctions, l:commandWithArguments) 65 | else 66 | call s:ExecuteSyncCommand(a:callbackFunctions, l:commandWithArguments) 67 | endif 68 | endfunction 69 | 70 | " Check if Vim/Neovim was compiled with job control support. 71 | function! s:SupportsAsyncJobs() abort 72 | return exists('*job_start') || exists('*jobstart') 73 | endfunction 74 | 75 | " Execute a command asynchronously and populate the location list with the 76 | " results. 77 | function! s:ExecuteAsyncCommand(callbackFunctions, commandWithArguments) abort 78 | let command = a:commandWithArguments[0] 79 | 80 | if exists('*job_start') 81 | let s:job = job_start(a:commandWithArguments, a:callbackFunctions) 82 | elseif exists('*jobstart') 83 | let s:job = jobstart(a:commandWithArguments, a:callbackFunctions) 84 | else 85 | throw 'No supported job control mechanism found.' 86 | endif 87 | endfunction 88 | 89 | " Execute a command synchronously and populate the location list with the 90 | " results. 91 | function! s:ExecuteSyncCommand(callbackFunctions, commandWithArguments) abort 92 | let command = a:commandWithArguments[0] 93 | let output = system(join(a:commandWithArguments)) 94 | let OutputCallbackFunction = get(a:callbackFunctions, 'out_cb', get(a:callbackFunctions, 'on_stdout')) 95 | 96 | call function(l:OutputCallbackFunction)('', l:output) 97 | endfunction 98 | 99 | " Prepare the location list by clearing it's content, setting the title to the command 100 | " that we're going to execute (plus it's arguments) then closing the window 101 | " (in case it was open from a previous run). 102 | function! s:PrepareLocationWindow(command) abort 103 | call setloclist(0, [], 'r', {'title': a:command, 'lines': []}) 104 | lclose 105 | endfunction 106 | 107 | " The job execution callback which appends the output to the location list. 108 | function! s:LocationListJobCallback(channel, msg, ...) abort 109 | let output = type(a:msg) == type([]) ? join(a:msg, "\n") : a:msg 110 | 111 | if strlen(l:output) > 0 112 | call setloclist(0, [], 'a', {'lines': split(l:output, "\n", 1)}) 113 | lopen 114 | endif 115 | endfunction 116 | 117 | " The job execution callback which appends the output to a named buffer. 118 | function! s:BufferJobCallback(bufferNumber, channel, msg, ...) abort 119 | call appendbufline(a:bufferNumber, '$', a:msg) 120 | endfunction 121 | 122 | " The job exit callback which splits the window and loads the specified 123 | " buffer. 124 | function! s:BufferExitJobCallback(bufferNumber, ...) abort 125 | if strlen(join(getbufline(a:bufferNumber, 1))) == 0 126 | call deletebufline(a:bufferNumber, 1) 127 | endif 128 | 129 | if bufwinid(a:bufferNumber) == -1 130 | execute 'sbuffer' . a:bufferNumber 131 | endif 132 | endfunction 133 | 134 | " Parse the command arguments and return the filename. 135 | function! s:GetFilenameFromArgumentsList(arguments, defaultValue) abort 136 | if empty(a:arguments) 137 | return a:defaultValue 138 | endif 139 | 140 | let filename = filter(copy(a:arguments), {idx, entry -> match(expand(entry), '\v[[:alnum:]]+\.rec$') != -1}) 141 | return empty(filename) ? a:defaultValue : expand(get(filename, 0)) 142 | endfunction 143 | 144 | " Parse the command arguments and return a list without the filename. 145 | function! s:GetCommandArgumentsFromArgumentsList(arguments) abort 146 | return filter(copy(a:arguments), { idx, entry -> match(expand(entry), '\v[[:alnum:]]+\.rec$') == -1 || empty(entry) }) 147 | endfunction 148 | 149 | " Return a dictionary with the callback function names for location-list 150 | " output. 151 | 152 | function! s:GetLocationListCallbackFunctions() abort 153 | let callbacks = {} 154 | 155 | if exists('*job_start') 156 | let l:callbacks['out_cb'] = function('s:LocationListJobCallback') 157 | let l:callbacks['err_cb'] = function('s:LocationListJobCallback') 158 | elseif exists('*jobstart') 159 | let l:callbacks['on_stdout'] = function('s:LocationListJobCallback') 160 | let l:callbacks['on_stderr'] = function('s:LocationListJobCallback') 161 | endif 162 | 163 | return l:callbacks 164 | endfunction 165 | 166 | " Return a dictionary with the callback function names for location-list 167 | " output. 168 | function! s:GetBufferCallbackFunctions(arguments) abort 169 | let callbacks = {} 170 | let filename = s:GetFilenameFromArgumentsList(a:arguments, expand('%@')) 171 | let bufferNumber = s:AddCsvBuffer(l:filename) 172 | 173 | if exists('*job_start') 174 | let l:callbacks['out_cb'] = function('s:BufferJobCallback', [l:bufferNumber]) 175 | let l:callbacks['err_cb'] = function('s:LocationListJobCallback') 176 | let l:callbacks['exit_cb'] = function('s:BufferExitJobCallback', [l:bufferNumber]) 177 | elseif exists('*jobstart') 178 | let l:callbacks['on_stdout'] = function('s:BufferJobCallback', [l:bufferNumber]) 179 | let l:callbacks['on_stderr'] = function('s:LocationListJobCallback') 180 | let l:callbacks['on_exit'] = function('s:BufferExitJobCallback', [l:bufferNumber]) 181 | end 182 | 183 | return l:callbacks 184 | endfunction 185 | 186 | function! s:AddCsvBuffer(filename) abort 187 | let csvFilename = substitute(a:filename, '\.rec', '.csv', '') 188 | let csvBuffer = bufadd(l:csvFilename) 189 | call bufload(l:csvBuffer) 190 | call setbufvar(l:csvBuffer, '&buflisted', 1) 191 | call deletebufline(l:csvBuffer, 1, '$') 192 | 193 | return l:csvBuffer 194 | endfunction 195 | 196 | " Check if Vim/Neovim was compiled with popup/floating window support. 197 | function! s:SupportsPopups() abort 198 | return has('popupwin') || has('nvim') 199 | endfunction 200 | 201 | " Return record descriptor name from descriptor block. 202 | function! s:GetRecordDescriptorName(descriptor) abort 203 | let recordType = substitute(a:descriptor[0], '%rec: ', '', '') 204 | 205 | return l:recordType . ' descriptor' 206 | endfunction 207 | 208 | " Show the popup window and populate it with the record descriptor block. 209 | function! s:ShowPopupWindow(descriptor) abort 210 | let title = s:GetRecordDescriptorName(a:descriptor) 211 | let linePosition = len(a:descriptor) 212 | 213 | if has('popupwin') 214 | call s:ShowVimPopupWindow(l:title, a:descriptor, l:linePosition) 215 | elseif has('nvim') 216 | call s:ShowNeovimFloatingWindow(l:title, a:descriptor, l:linePosition) 217 | endif 218 | endfunction 219 | 220 | " Calls the underlying Vim functions to create the popup window. 221 | function! s:ShowVimPopupWindow(title, descriptor, linePosition) abort 222 | let windowId = popup_create(a:descriptor, s:VimPopupWindowOptions(a:linePosition, a:title)) 223 | call win_execute(windowId, 'setlocal filetype=rec') 224 | endfunction 225 | 226 | " Returns options specific to Vim's popup windows. 227 | function! s:VimPopupWindowOptions(linePosition, title) abort 228 | let options = #{ 229 | \ title: a:title, pos: 'botleft', line: 'cursor-'.a:linePosition, moved: 'any', 230 | \ border: [], padding: [] 231 | \ } 232 | 233 | return l:options 234 | endfunction 235 | 236 | " Calls the underlying Neovim functions to create the floating window. 237 | function! s:ShowNeovimFloatingWindow(title, descriptor, linePosition) abort 238 | let descriptorBuffer = nvim_create_buf(v:false, v:true) 239 | 240 | call nvim_buf_set_lines(l:descriptorBuffer, 0, -1, v:false, a:descriptor) 241 | call nvim_buf_set_option(l:descriptorBuffer, 'filetype', 'rec') 242 | 243 | for key in ['q', '', '', '', ''] 244 | call nvim_buf_set_keymap(l:descriptorBuffer, 'n', key, ':close', #{silent: v:true, noremap: v:true, nowait: v:true}) 245 | endfor 246 | 247 | let windowId = nvim_open_win(descriptorBuffer, 1, s:NeovimPopupWindowOptions(a:title, a:descriptor, a:linePosition)) 248 | call win_execute(windowId, 'setlocal readonly') 249 | endfunction 250 | 251 | " Returns options specific to Neovim's popup windows. 252 | function! s:NeovimPopupWindowOptions(title, descriptor, linePosition) abort 253 | let uiOptions = nvim_list_uis()[0] 254 | let width = max(map(a:descriptor, 'len(v:val)')) 255 | 256 | let options = #{ 257 | \ anchor: 'SE', row: -a:linePosition, style: 'minimal', col: l:uiOptions.width/2 + l:width/2, 258 | \ border: 'single', relative: 'cursor', height: len(a:descriptor), width: l:width, 259 | \ focusable: v:false, zindex: 50 260 | \ } 261 | 262 | return l:options 263 | endfunction 264 | 265 | " Show the preview window and populate it with the record descriptor block. 266 | function! s:ShowPreviewWindow(descriptor) abort 267 | let filename = s:GetRecordDescriptorName(a:descriptor) 268 | let previewBuffer = bufadd(l:filename) 269 | 270 | call bufload(l:previewBuffer) 271 | call deletebufline(l:previewBuffer, 1, '$') 272 | call appendbufline(l:previewBuffer, 1, a:descriptor) 273 | 274 | let windowId = bufwinid(l:previewBuffer) 275 | 276 | if l:windowId < 0 277 | execute 'pedit! ' . l:filename 278 | 279 | let l:windowId = bufwinid(l:previewBuffer) 280 | call win_execute(windowId, 'setlocal nonumber noswapfile nobuflisted buftype=nofile filetype=rec') 281 | endif 282 | endfunction 283 | -------------------------------------------------------------------------------- /autoload/reccomplete.vim: -------------------------------------------------------------------------------- 1 | " Maintainer: Zaid Al-Jarrah 2 | 3 | if exists('g:autoloaded_rec_autocomplete') 4 | finish 5 | endif 6 | let g:autoloaded_rec_autocomplete = 1 7 | 8 | let s:record_set_descriptors = { 9 | \ '%allowed': 'List of allowed fields', 10 | \ '%auto': 'List of fields with auto-generated values', 11 | \ '%confidential': 'List of encrypted field names', 12 | \ '%constraint': 'Arbitrary constraint for a given field', 13 | \ '%doc': 'Description for the record set', 14 | \ '%key': 'Primary key for the record set (implied %unique and %mandatory)', 15 | \ '%mandatory': 'List of required fields', 16 | \ '%prohibit': 'List of prohibited fields', 17 | \ '%rec': 'Record set type', 18 | \ '%size': 'Constraint for the total number of records allowed for a record set', 19 | \ '%sort': 'List of fields to use when querying the records', 20 | \ '%type': 'Specify the type of a specific field', 21 | \ '%typedef': 'Declare a new type which can be used in a %type declaration', 22 | \ '%unique': 'List of unique fields', 23 | \ } 24 | 25 | " Auto-completion function for record sets. 26 | function! reccomplete#Complete(findstart, base) abort 27 | if a:findstart 28 | let line = getline('.') 29 | let start = col('.') - 1 30 | 31 | if s:LineSupportsAutocompletion(l:line) 32 | while l:start > 0 && l:line[l:start - 1] =~ '\<' 33 | let l:start -= 1 34 | endwhile 35 | 36 | if l:start > 1 37 | let l:start = -3 38 | endif 39 | else 40 | let l:start = -3 41 | endif 42 | 43 | return l:start 44 | else 45 | let res = [] 46 | for [field, description] in s:RecordSetFields() 47 | if field =~ '^' . a:base 48 | call add(res, { 'word': field . ':', 'menu': description }) 49 | endif 50 | endfor 51 | return res 52 | endif 53 | endfunction 54 | 55 | " Check if a line supports autocompletion (if it starts with a % or is an 56 | " empty line between folds). 57 | function! s:LineSupportsAutocompletion(line) abort 58 | let [bufferNumber, lineNumber, columnNumber, off] = getpos('.') 59 | let previousLine = getline(l:lineNumber - 1) 60 | let previousLineFolded = foldclosed(l:lineNumber - 1) != -1 61 | let nextLine = getline(l:lineNumber + 1) 62 | let nextLineFolded = foldclosed(l:lineNumber + 1) != -1 63 | 64 | return a:line =~ '^%' || 65 | \ ( 66 | \ strlen(trim(a:line)) == 0 && 67 | \ (l:previousLineFolded || l:previousLine !~ '^\w') && 68 | \ (l:nextLineFolded || l:nextLine !~ '^\w') 69 | \ ) 70 | endfunction 71 | 72 | " Return the list of the record set descriptors. 73 | function! s:RecordSetFields() abort 74 | return items(s:record_set_descriptors) 75 | endfunction 76 | -------------------------------------------------------------------------------- /compiler/rec.vim: -------------------------------------------------------------------------------- 1 | " Vim Compiler File 2 | " Compiler: rec 3 | 4 | if exists("current_compiler") 5 | finish 6 | endif 7 | let current_compiler = "rec" 8 | 9 | if exists(":CompilerSet") != 2 10 | command -nargs=* CompilerSet setlocal 11 | endif 12 | 13 | CompilerSet errorformat=%f:%l:\ %trror:\ %m 14 | CompilerSet errorformat+=%f:\ %l:\ %trror:\ %m 15 | exe 'CompilerSet makeprg=recfix\ '.substitute(shellescape(expand('%')), ' ', '\\ ', 'g') 16 | -------------------------------------------------------------------------------- /doc/rec.txt: -------------------------------------------------------------------------------- 1 | *recutils.txt* GNU Recutils support for Vim 2 | 3 | Author: Zaid Al-Jarrah 4 | License: Same terms as Vim itself (see |license|) 5 | 6 | INTRODUCTION *recutils* 7 | 8 | The set of commands below will be available whenever you edit a .rec file. 9 | 10 | COMMANDS *recutils-commands* 11 | 12 | These commands operate on buffers so you don't have to pass them a filepath as an 13 | argument (unless you want to run the command on a different file). 14 | 15 | :Recsel {args} Run recsel with the supplied arguments and display 16 | the output in a |location-list| in the current 17 | window. 18 | 19 | :Recinf {args} Run recinf with the supplied arguments and display 20 | the output in a |location-list| in the current 21 | window. 22 | 23 | :Recfix {args} Run recfix with the supplied arguments and display 24 | the output in a |location-list| in the current 25 | window. 26 | If there are no syntax errors in the file then the 27 | |location-list| will not open, otherwise you will be 28 | able to navigate the list and jump to the syntax 29 | errors directly. 30 | 31 | :Rec2csv {args} Run rec2csv with the supplied arguments and 32 | display the output in a split horizontal buffer 33 | in the current window. 34 | 35 | :RecNextDescriptor Jump to the next record descriptor. 36 | 37 | :RecPreviousDescriptor Jump to the previous record descriptor. 38 | 39 | :RecPreviewDescriptor Show the current record descriptor in a 40 | |popup-window| / floating-window / |preview-window|. 41 | 42 | MAPS *recutils-maps* 43 | 44 | These |recutils-commands| maps are available in the |recutils| buffers only. 45 | 46 | rf Invokes `:Recfix --check` on the current buffer. 47 | rn Populates the command line with :Recinf. 48 | rs Populates the command line with :Recsel. 49 | rv Populates the command line with :Rec2csv 50 | r] Jump to the next record descriptor. 51 | r[ Jump to the previous record descriptor. 52 | r? Show the current record descriptor in a 53 | popup/floating/preview window. 54 | 55 | The above maps can be disabled by setting the g:recutils_no_maps option. 56 | > 57 | let g:recutils_no_maps = 1 58 | < 59 | 60 | AUTO COMPLETION *recutils-autocompletion* 61 | 62 | Basic support for auto-completing record set properties is available (by using 63 | C-x C-o while in insert mode). 64 | 65 | It can be disabled by setting the g:recutils_no_autocompletion option. 66 | > 67 | let g:recutils_no_autocompletion = 1 68 | < 69 | " vim:tw=78:ft=help 70 | -------------------------------------------------------------------------------- /ftdetect/rec.vim: -------------------------------------------------------------------------------- 1 | autocmd BufNewFile,BufRead *.rec setfiletype rec 2 | -------------------------------------------------------------------------------- /ftplugin/rec.vim: -------------------------------------------------------------------------------- 1 | " Only do this when not done yet for this buffer 2 | if exists('b:did_ftplugin') 3 | finish 4 | endif 5 | 6 | " Don't load another plugin for this buffer 7 | let b:did_ftplugin = 1 8 | 9 | let s:cpo_save = &cpo 10 | set cpo&vim 11 | 12 | setlocal commentstring=#\ %s 13 | setlocal iskeyword+=% 14 | setlocal tabstop=2 15 | setlocal softtabstop=2 16 | setlocal shiftwidth=2 17 | 18 | let b:undo_ftplugin = 'setlocal commentstring< iskeyword< tabstop< softtabstop< shiftwidth<' 19 | 20 | " Return fold level for a given line 21 | function! GetRecFold(lnum) abort 22 | let currentline = getline(a:lnum) 23 | let previousline = getline(a:lnum - 1) 24 | 25 | if s:IsRecBlankLine(previousline) && s:IsRecFieldLine(currentline) 26 | return '1' 27 | elseif (s:IsRecFieldLine(previousline) || s:IsRecMultilineValue(previousline)) && s:IsRecFieldLine(currentline) 28 | return '2' 29 | elseif (s:IsRecFieldLine(previousline) || s:IsRecMultilineValue(previousline)) && s:IsRecMultilineValue(currentline) 30 | return '3' 31 | endif 32 | 33 | return '0' 34 | endfunction 35 | 36 | " Check if the line starts with a field label 37 | function! s:IsRecFieldLine(line) abort 38 | return a:line =~? '\v^\w+' 39 | endfunction 40 | 41 | " Check if the line starts with a multiline character 42 | function! s:IsRecMultilineValue(line) abort 43 | return a:line =~? '\v^\+\s*' 44 | endfunction 45 | 46 | " Check if the line is a blank line 47 | function! s:IsRecBlankLine(line) abort 48 | return a:line =~? '\v^\s*$' 49 | endfunction 50 | 51 | " Enable folding if Vim was compiled with +folding support 52 | if has('folding') && !get(g:, 'recutils_no_folding') 53 | setlocal foldmethod=expr 54 | setlocal foldexpr=GetRecFold(v:lnum) 55 | let b:undo_ftplugin .= ' | setlocal foldmethod< foldexpr<' 56 | endif 57 | 58 | " Define commands wrappers for GNU Recutils 59 | command! -nargs=* Recsel call rec#Recsel() 60 | command! -nargs=* Recinf call rec#Recinf() 61 | command! -nargs=* Recfix call rec#Recfix() 62 | command! -nargs=* Rec2csv call rec#Rec2csv() 63 | command! RecPreviousDescriptor call rec#RecPreviousDescriptor() 64 | command! RecNextDescriptor call rec#RecNextDescriptor() 65 | command! RecPreviewDescriptor call rec#RecPreviewDescriptor() 66 | 67 | " Define command maps 68 | if !get(g:, 'recutils_no_maps') 69 | nnoremap rf :Recfix --check 70 | nnoremap rn :Recinf 71 | nnoremap rs :Recsel 72 | nnoremap rv :Rec2csv 73 | noremap r] :RecNextDescriptor 74 | noremap r[ :RecPreviousDescriptor 75 | noremap r? :RecPreviewDescriptor 76 | let b:undo_ftplugin .= " | silent! execute 'nunmap rf'" . 77 | \ " | silent! execute 'nunmap rn'" . 78 | \ " | silent! execute 'nunmap rs'" . 79 | \ " | silent! execute 'nunmap rv'" . 80 | \ " | silent! execute 'unmap r]'" . 81 | \ " | silent! execute 'unmap r['" . 82 | \ " | silent! execute 'unmap r?'" 83 | endif 84 | 85 | "" Enable auto-completion for record sets 86 | if has('eval') && !get(g:, 'recutils_no_autocompletion') 87 | setlocal omnifunc=reccomplete#Complete 88 | let b:undo_ftplugin .= ' | setlocal omnifunc<' 89 | endif 90 | 91 | let &cpo = s:cpo_save 92 | unlet s:cpo_save 93 | -------------------------------------------------------------------------------- /syntax/rec.vim: -------------------------------------------------------------------------------- 1 | " Vim syntax file 2 | " Language: GNU's Recutils 3 | " Maintainer: Zaid Al-Jarrah 4 | " Filenames: *.rec 5 | 6 | if exists("b:current_syntax") 7 | finish 8 | endif 9 | 10 | syntax keyword recKeyword %allowed %auto %confidential %doc %key 11 | syntax keyword recKeyword %mandatory %prohibit %rec %sort %unique 12 | syntax keyword recKeyword %unique 13 | 14 | syntax match recComment "\v^#.*$" 15 | syntax match recField "\v^\w+" 16 | syntax match recComparisonOperator "\v\>?\