├── .gitignore ├── ftdetect └── rest.vim ├── data.sample.json ├── sample_global.rest ├── autoload └── vrc │ └── opt.vim ├── sample.rest ├── CHANGELOG.md ├── indent └── rest.vim ├── syntax └── rest.vim ├── README.md ├── ftplugin └── rest.vim └── doc └── vim-rest-console.txt /.gitignore: -------------------------------------------------------------------------------- 1 | doc/tags 2 | -------------------------------------------------------------------------------- /ftdetect/rest.vim: -------------------------------------------------------------------------------- 1 | au BufNewFile,BufRead *.rest set ft=rest 2 | -------------------------------------------------------------------------------- /data.sample.json: -------------------------------------------------------------------------------- 1 | { "create": { "_type": "testtype" }} 2 | { "name": "FOO", "value": "think about BAR", "date": "2015-01-05" } 3 | { "create": { "_type": "testtype" }} 4 | { "name": "FOO", "value": "some value of FOO", "date": "2015-01-06" } 5 | { "create": { "_type": "testtype" }} 6 | { "name": "BAR", "value": "some value of BAR", "date": "2015-01-07" } 7 | { "create": { "_type": "testtype" }} 8 | { "name": "BAR", "value": "think about FOO", "date": "2015-01-08" } 9 | -------------------------------------------------------------------------------- /sample_global.rest: -------------------------------------------------------------------------------- 1 | // Global definitions of the queries 2 | http://localhost:9200 3 | Accept: application/json 4 | -- 5 | 6 | # 7 | # View nodes status 8 | # 9 | -- 10 | GET /_cat/nodes?v 11 | 12 | # 13 | # Add a new doc to the new 'testindex' and 'testtype' 14 | # 15 | -- 16 | POST /testindex/testtype 17 | { 18 | "name": "some name", 19 | "value": "some value", 20 | "date": "2015-01-01" 21 | } 22 | 23 | # 24 | # Search 'testindex' 25 | # 26 | -- 27 | GET /testindex/_search?pretty 28 | 29 | # 30 | # View mapping. 31 | # 32 | -- 33 | GET /testindex/testtype/_mapping?pretty 34 | 35 | # 36 | # Lite search from a different host. 37 | # 38 | http://localhost:9200 39 | 40 | GET /testindex/testtype/_search?pretty q=+name:FOO +value:(FOO BAR) 41 | 42 | # 43 | # Full-body search 44 | # 45 | -- 46 | POST /testindex/_search?pretty 47 | { 48 | "query": { 49 | "filtered": { 50 | "filter": { 51 | "range": { 52 | "date": { 53 | "gte": "2015-01-06", 54 | "lte": "2015-01-08" 55 | } 56 | } 57 | }, 58 | "query": { 59 | "match": { 60 | "value": "FOO" 61 | } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /autoload/vrc/opt.vim: -------------------------------------------------------------------------------- 1 | """ 2 | " Some functions from ftplugin/rest.vim will be moved here. 3 | " 4 | 5 | """ 6 | " Get the default cUrl options defined by g:vrc_curl_opts. 7 | " 8 | " @return dict 9 | " 10 | function! vrc#opt#GetDefaultCurlOpts() 11 | if !exists('g:vrc_curl_opts') 12 | return {} 13 | endif 14 | let opts = eval('g:vrc_curl_opts') 15 | if type(opts) != type({}) 16 | echoerr 'Option vrc_curl_opts should be a dictionary.' 17 | return {} 18 | endif 19 | return copy(opts) 20 | endfunction 21 | 22 | """ 23 | " Convert a dictionary of cUrl options to list. 24 | " 25 | " @param dict a:dictOpts {'opt1': 'x', 'opt2': ['y', 'z']} 26 | " @return list ['opt', 'x', 'opt2', 'y', 'opt2', 'z'] 27 | " 28 | function! vrc#opt#DictToCurlArgs(dictOpts) 29 | let opts = [] 30 | for [key, optVal] in items(a:dictOpts) 31 | if empty(optVal) 32 | call add(opts, key) 33 | continue 34 | endif 35 | 36 | " Convert optVal to list. 37 | if type(optVal) != type([]) 38 | let optVal = [optVal] 39 | endif 40 | for val in optVal 41 | call add(opts, key) 42 | call add(opts, val) 43 | endfor 44 | endfor 45 | return opts 46 | endfunction 47 | 48 | """ 49 | " Check if a dict has one of the given keys. 50 | " 51 | " @param dict a:dictOpts 52 | " @param string[] a:keys 53 | " @return boolean 54 | " 55 | function! vrc#opt#DictHasKeys(dictOpts, keys) 56 | for kk in a:keys 57 | if has_key(a:dictOpts, kk) 58 | return 1 59 | endif 60 | endfor 61 | return 0 62 | endfunction 63 | -------------------------------------------------------------------------------- /sample.rest: -------------------------------------------------------------------------------- 1 | // Examples of some ElasticSearch requests. 2 | 3 | # 4 | # View nodes status 5 | # 6 | http://localhost:9200 7 | GET /_cat/nodes?v 8 | 9 | # 10 | # Add a new doc to the new 'testindex' and 'testtype' 11 | # 12 | http://localhost:9200 13 | 14 | POST /testindex/testtype 15 | { 16 | "name": "some name", 17 | "value": "some value", 18 | "date": "2015-01-01" 19 | } 20 | 21 | # 22 | # Search 'testindex' 23 | # 24 | http://localhost:9200 25 | GET /testindex/_search?pretty 26 | 27 | # 28 | # View mapping. 29 | # 30 | http://localhost:9200 31 | GET /testindex/testtype/_mapping?pretty 32 | 33 | # 34 | # Bulk-add new docs to 'testindex' using an external data file. 35 | # 36 | http://localhost:9200 37 | 38 | POST /testindex/_bulk?pretty 39 | @data.sample.json 40 | 41 | # 42 | # Lite search 43 | # 44 | http://localhost:9200 45 | GET /testindex/testtype/_search?pretty 46 | q=+name:FOO +value:(FOO BAR) 47 | 48 | # or 49 | http://localhost:9200 50 | GET /testindex/testtype/_search 51 | pretty& 52 | q=+name:FOO +value:(FOO BAR) 53 | 54 | # 55 | # Full-body search 56 | # 57 | http://localhost:9200 58 | 59 | POST /testindex/_search?pretty 60 | { 61 | "query": { 62 | "filtered": { 63 | "filter": { 64 | "range": { 65 | "date": { 66 | "gte": "2015-01-06", 67 | "lte": "2015-01-08" 68 | } 69 | } 70 | }, 71 | "query": { 72 | "match": { 73 | "value": "FOO" 74 | } 75 | } 76 | } 77 | } 78 | } 79 | 80 | # 81 | # Delete 'testindex' 82 | # 83 | http://localhost:9200 84 | DELETE /testindex?pretty 85 | 86 | # 87 | # Check for 'testindex' existence 88 | # 89 | http://localhost:9200 90 | HEAD /testindex 91 | 92 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ========= 3 | 4 | * 3.0.2 (2017-06-13) 5 | 6 | * Fix shell escape BC with Vim 7.4. 7 | * Add CHANGELOG. 8 | * Update docs. 9 | 10 | * 3.0.1 (2017-06-09) 11 | 12 | * Fix bug `-k` auto included. 13 | * Fix response content syntax-highlighting/formatting detection. 14 | 15 | * 3.0.0 (2017-05-25) 16 | 17 | * Support raw cUrl options. 18 | * Support in-line data for `_bulk` request of ElasticSearch. 19 | * Deprecate the following VRC options in favor of cUrl options: 20 | - `vrc_connect_timeout` for `--connect-timeout` 21 | - `vrc_cookie_jar` for `-b` and `-c` 22 | - `vrc_follow_redirects` for `-L` 23 | - `vrc_include_response_header` for `-i` 24 | - `vrc_max_time` for `--max-time` 25 | - `vrc_resolve_to_ipv4` for `--ipv4` 26 | - `vrc_ssl_secure` for `-k` 27 | * Source code reformatted and refactored. 28 | 29 | * 2.6.0 (2017-01-30) 30 | 31 | * Support global variable declaration. 32 | * Support consecutive request verbs. 33 | * Bug fix: When `vrc_show_command` is set, the command is displayed in the 34 | quickfix window instead of the output view. This fixes the output 35 | formatting bug when the option is enabled. 36 | * Add option `vrc_response_default_content_type` to set the default content- 37 | type of the response. 38 | 39 | * 2.5.0 (2016-05-05) 40 | 41 | * Set `commentstring` so that lines can be commented by commenters. 42 | * Fix Content-Type to default to `application/json`. 43 | * Add option `vrc_show_command` to display the cUrl command along with output. 44 | 45 | * 2.4.0 (2016-04-11) 46 | 47 | * Support POST empty body. 48 | * Add option to horizontal-split the output buffer. 49 | * Option to transform `\uXXXX` instances to corresponding symbols. 50 | 51 | * 2.3.0 (2016-03-24) 52 | 53 | * GET request can have request body. 54 | * Request body can be specified on a line-by-line basis. 55 | 56 | * 2.2.0 (2016-02-08) 57 | 58 | * Add support for PATCH, OPTIONS, and TRACE. 59 | 60 | * 2.1.1 (2016-01-30) 61 | 62 | * Incompatibility fix. 63 | 64 | * 2.1.0 (2016-01-25) 65 | 66 | * Support default values specified in a global section. 67 | * Add options for connection and max timeout. 68 | 69 | * 2.0.0 (2015-11-24) 70 | 71 | * Support POST data from external files. 72 | * Proper use of cURL commands for HTTP verbs. 73 | * Request body is sent based on HTTP verbs. 74 | - GET, HEAD, DELETE: as GET params. 75 | - POST, PUT: as POST params. 76 | * Remove awkward syntaxes. 77 | - Option `vrc_nl_sep_post_data_patterns` removed. 78 | - GET params can be specified in request body. 79 | 80 | -------------------------------------------------------------------------------- /indent/rest.vim: -------------------------------------------------------------------------------- 1 | " Vim indent file 2 | " Language: JSON 3 | " Original Author: Rogerz Zhang http://github.com/rogerz/vim-json 4 | " Eli Parra https://github.com/elzr/vim-json 5 | " Acknowledgement: Based off of vim-javascript maintained by Darrick Wiebe 6 | " http://www.vim.org/scripts/script.php?script_id=2765 7 | 8 | " 0. Initialization {{{1 9 | " ================= 10 | 11 | " Only load this indent file when no other was loaded. 12 | if exists("b:did_indent") 13 | finish 14 | endif 15 | let b:did_indent = 1 16 | 17 | setlocal nosmartindent 18 | 19 | " Now, set up our indentation expression and keys that trigger it. 20 | setlocal indentexpr=GetJSONIndent() 21 | setlocal indentkeys=0{,0},0),0[,0],!^F,o,O,e 22 | 23 | " Only define the function once. 24 | if exists("*GetJSONIndent") 25 | finish 26 | endif 27 | 28 | let s:cpo_save = &cpo 29 | set cpo&vim 30 | 31 | " 1. Variables {{{1 32 | " ============ 33 | 34 | let s:line_term = '\s*\%(\%(\/\/\).*\)\=$' 35 | " Regex that defines blocks. 36 | let s:block_regex = '\%({\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term 37 | 38 | " 2. Auxiliary Functions {{{1 39 | " ====================== 40 | 41 | " Check if the character at lnum:col is inside a string. 42 | function s:IsInString(lnum, col) 43 | return synIDattr(synID(a:lnum, a:col, 1), 'name') == "jsonString" 44 | endfunction 45 | 46 | " Find line above 'lnum' that isn't empty, or in a string. 47 | function s:PrevNonBlankNonString(lnum) 48 | let lnum = prevnonblank(a:lnum) 49 | while lnum > 0 50 | " If the line isn't empty or in a string, end search. 51 | let line = getline(lnum) 52 | if !(s:IsInString(lnum, 1) && s:IsInString(lnum, strlen(line))) 53 | break 54 | endif 55 | let lnum = prevnonblank(lnum - 1) 56 | endwhile 57 | return lnum 58 | endfunction 59 | 60 | " Check if line 'lnum' has more opening brackets than closing ones. 61 | function s:LineHasOpeningBrackets(lnum) 62 | let open_0 = 0 63 | let open_2 = 0 64 | let open_4 = 0 65 | let line = getline(a:lnum) 66 | let pos = match(line, '[][(){}]', 0) 67 | while pos != -1 68 | let idx = stridx('(){}[]', line[pos]) 69 | if idx % 2 == 0 70 | let open_{idx} = open_{idx} + 1 71 | else 72 | let open_{idx - 1} = open_{idx - 1} - 1 73 | endif 74 | let pos = match(line, '[][(){}]', pos + 1) 75 | endwhile 76 | return (open_0 > 0) . (open_2 > 0) . (open_4 > 0) 77 | endfunction 78 | 79 | function s:Match(lnum, regex) 80 | let col = match(getline(a:lnum), a:regex) + 1 81 | return col > 0 && !s:IsInString(a:lnum, col) ? col : 0 82 | endfunction 83 | 84 | " 3. GetJSONIndent Function {{{1 85 | " ========================= 86 | 87 | function GetJSONIndent() 88 | " 3.1. Setup {{{2 89 | " ---------- 90 | 91 | " Set up variables for restoring position in file. Could use v:lnum here. 92 | let vcol = col('.') 93 | 94 | " 3.2. Work on the current line {{{2 95 | " ----------------------------- 96 | 97 | " Get the current line. 98 | let line = getline(v:lnum) 99 | let ind = -1 100 | 101 | " If we got a closing bracket on an empty line, find its match and indent 102 | " according to it. 103 | let col = matchend(line, '^\s*[]}]') 104 | 105 | if col > 0 && !s:IsInString(v:lnum, col) 106 | call cursor(v:lnum, col) 107 | let bs = strpart('{}[]', stridx('}]', line[col - 1]) * 2, 2) 108 | 109 | let pairstart = escape(bs[0], '[') 110 | let pairend = escape(bs[1], ']') 111 | let pairline = searchpair(pairstart, '', pairend, 'bW') 112 | 113 | if pairline > 0 114 | let ind = indent(pairline) 115 | else 116 | let ind = virtcol('.') - 1 117 | endif 118 | 119 | return ind 120 | endif 121 | 122 | " If we are in a multi-line string, don't do anything to it. 123 | if s:IsInString(v:lnum, matchend(line, '^\s*') + 1) 124 | return indent('.') 125 | endif 126 | 127 | " 3.3. Work on the previous line. {{{2 128 | " ------------------------------- 129 | 130 | let lnum = prevnonblank(v:lnum - 1) 131 | 132 | if lnum == 0 133 | return 0 134 | endif 135 | 136 | " Set up variables for current line. 137 | let line = getline(lnum) 138 | let ind = indent(lnum) 139 | 140 | " If the previous line ended with a block opening, add a level of indent. 141 | " if s:Match(lnum, s:block_regex) 142 | " if exists('*shiftwidth') 143 | " return indent(lnum) + shiftwidth() 144 | " else 145 | " return indent(lnum) + &sw 146 | " endif 147 | " endif 148 | 149 | " If the previous line contained an opening bracket, and we are still in it, 150 | " add indent depending on the bracket type. 151 | if line =~ '[[({]' 152 | let counts = s:LineHasOpeningBrackets(lnum) 153 | if counts[0] == '1' || counts[1] == '1' || counts[2] == '1' 154 | if exists('*shiftwidth') 155 | return ind + shiftwidth() 156 | else 157 | return ind + &sw 158 | endif 159 | else 160 | call cursor(v:lnum, vcol) 161 | end 162 | endif 163 | 164 | " }}}2 165 | 166 | return ind 167 | endfunction 168 | 169 | " }}}1 170 | 171 | let &cpo = s:cpo_save 172 | unlet s:cpo_save 173 | 174 | " vim:set sw=2 sts=2 ts=8 noet: 175 | 176 | -------------------------------------------------------------------------------- /syntax/rest.vim: -------------------------------------------------------------------------------- 1 | if exists('b:current_syntax') 2 | finish 3 | endif 4 | 5 | if !exists('b:yaml_schema') 6 | if exists('g:yaml_schema') 7 | let b:yaml_schema = g:yaml_schema 8 | else 9 | let b:yaml_schema = 'core' 10 | endif 11 | endif 12 | 13 | let s:ns_char = '\%([\n\r\uFEFF \t]\@!\p\)' 14 | let s:ns_word_char = '[[:alnum:]_\-]' 15 | let s:ns_uri_char = '\%(%\x\x\|'.s:ns_word_char.'\|[#/;?:@&=+$,.!~*''()[\]]\)' 16 | let s:ns_tag_char = '\%(%\x\x\|'.s:ns_word_char.'\|[#/;?:@&=+$.~*''()]\)' 17 | let s:c_ns_anchor_char = '\%([\n\r\uFEFF \t,[\]{}]\@!\p\)' 18 | let s:c_indicator = '[\-?:,[\]{}#&*!|>''"%@`]' 19 | let s:c_flow_indicator = '[,[\]{}]' 20 | 21 | let s:ns_char_without_c_indicator = substitute(s:ns_char, '\v\C[\zs', '\=s:c_indicator[1:-2]', '') 22 | 23 | let s:_collection = '[^\@!\(\%(\\\.\|\[^\\\]]\)\+\)]' 24 | let s:_neg_collection = '[^\(\%(\\\.\|\[^\\\]]\)\+\)]' 25 | function! s:SimplifyToAssumeAllPrintable(p) 26 | return substitute(a:p, '\V\C\\%('.s:_collection.'\\@!\\p\\)', '[^\1]', '') 27 | endfunction 28 | let s:ns_char = s:SimplifyToAssumeAllPrintable(s:ns_char) 29 | let s:ns_char_without_c_indicator = s:SimplifyToAssumeAllPrintable(s:ns_char_without_c_indicator) 30 | let s:c_ns_anchor_char = s:SimplifyToAssumeAllPrintable(s:c_ns_anchor_char) 31 | 32 | function! s:SimplifyAdjacentCollections(p) 33 | return substitute(a:p, '\V\C'.s:_collection.'\\|'.s:_collection, '[\1\2]', 'g') 34 | endfunction 35 | let s:ns_uri_char = s:SimplifyAdjacentCollections(s:ns_uri_char) 36 | let s:ns_tag_char = s:SimplifyAdjacentCollections(s:ns_tag_char) 37 | 38 | let s:c_verbatim_tag = '!<'.s:ns_uri_char.'\+>' 39 | let s:c_named_tag_handle = '!'.s:ns_word_char.'\+!' 40 | let s:c_secondary_tag_handle = '!!' 41 | let s:c_primary_tag_handle = '!' 42 | let s:c_tag_handle = '\%('.s:c_named_tag_handle. 43 | \ '\|'.s:c_secondary_tag_handle. 44 | \ '\|'.s:c_primary_tag_handle.'\)' 45 | let s:c_ns_shorthand_tag = s:c_tag_handle . s:ns_tag_char.'\+' 46 | let s:c_non_specific_tag = '!' 47 | let s:c_ns_tag_property = s:c_verbatim_tag. 48 | \ '\|'.s:c_ns_shorthand_tag. 49 | \ '\|'.s:c_non_specific_tag 50 | 51 | let s:c_ns_anchor_name = s:c_ns_anchor_char.'\+' 52 | let s:c_ns_anchor_property = '&'.s:c_ns_anchor_name 53 | let s:c_ns_alias_node = '\*'.s:c_ns_anchor_name 54 | 55 | let s:ns_directive_name = s:ns_char.'\+' 56 | 57 | let s:ns_local_tag_prefix = '!'.s:ns_uri_char.'*' 58 | let s:ns_global_tag_prefix = s:ns_tag_char.s:ns_uri_char.'*' 59 | let s:ns_tag_prefix = s:ns_local_tag_prefix. 60 | \ '\|'.s:ns_global_tag_prefix 61 | 62 | let s:ns_plain_safe_out = s:ns_char 63 | let s:ns_plain_safe_in = '\%('.s:c_flow_indicator.'\@!'.s:ns_char.'\)' 64 | 65 | let s:ns_plain_safe_in = substitute(s:ns_plain_safe_in, '\V\C\\%('.s:_collection.'\\@!'.s:_neg_collection.'\\)', '[^\1\2]', '') 66 | let s:ns_plain_safe_in_without_colhash = substitute(s:ns_plain_safe_in, '\V\C'.s:_neg_collection, '[^\1:#]', '') 67 | let s:ns_plain_safe_out_without_colhash = substitute(s:ns_plain_safe_out, '\V\C'.s:_neg_collection, '[^\1:#]', '') 68 | 69 | let s:ns_plain_first_in = '\%('.s:ns_char_without_c_indicator.'\|[?:\-]\%('.s:ns_plain_safe_in.'\)\@=\)' 70 | let s:ns_plain_first_out = '\%('.s:ns_char_without_c_indicator.'\|[?:\-]\%('.s:ns_plain_safe_out.'\)\@=\)' 71 | 72 | let s:ns_plain_char_in = '\%('.s:ns_char.'#\|:'.s:ns_plain_safe_in.'\|'.s:ns_plain_safe_in_without_colhash.'\)' 73 | let s:ns_plain_char_out = '\%('.s:ns_char.'#\|:'.s:ns_plain_safe_out.'\|'.s:ns_plain_safe_out_without_colhash.'\)' 74 | 75 | let s:ns_plain_out = s:ns_plain_first_out . s:ns_plain_char_out.'*' 76 | let s:ns_plain_in = s:ns_plain_first_in . s:ns_plain_char_in.'*' 77 | 78 | 79 | syn keyword yamlTodo contained TODO FIXME XXX NOTE 80 | 81 | syn region yamlComment display oneline start='\%\(^\|\s\)#' end='$' 82 | \ contains=yamlTodo 83 | 84 | execute 'syn region yamlDirective oneline start='.string('^\ze%'.s:ns_directive_name.'\s\+').' '. 85 | \ 'end="$" '. 86 | \ 'contains=yamlTAGDirective,'. 87 | \ 'yamlYAMLDirective,'. 88 | \ 'yamlReservedDirective '. 89 | \ 'keepend' 90 | 91 | syn match yamlTAGDirective '%TAG\s\+' contained nextgroup=yamlTagHandle 92 | execute 'syn match yamlTagHandle contained nextgroup=yamlTagPrefix '.string(s:c_tag_handle.'\s\+') 93 | execute 'syn match yamlTagPrefix contained nextgroup=yamlComment ' . string(s:ns_tag_prefix) 94 | 95 | syn match yamlYAMLDirective '%YAML\s\+' contained nextgroup=yamlYAMLVersion 96 | syn match yamlYAMLVersion '\d\+\.\d\+' contained nextgroup=yamlComment 97 | 98 | execute 'syn match yamlReservedDirective contained nextgroup=yamlComment '. 99 | \string('%\%(\%(TAG\|YAML\)\s\)\@!'.s:ns_directive_name) 100 | 101 | syn region yamlFlowString matchgroup=yamlFlowStringDelimiter start='"' skip='\\"' end='"' 102 | \ contains=yamlEscape 103 | \ nextgroup=yamlKeyValueDelimiter 104 | syn region yamlFlowString matchgroup=yamlFlowStringDelimiter start="'" skip="''" end="'" 105 | \ contains=yamlSingleEscape 106 | \ nextgroup=yamlKeyValueDelimiter 107 | syn match yamlEscape contained '\\\%([\\"abefnrtv\^0_ NLP\n]\|x\x\x\|u\x\{4}\|U\x\{8}\)' 108 | syn match yamlSingleEscape contained "''" 109 | 110 | syn match yamlBlockScalarHeader contained '\s\+\zs[|>]\%([+-]\=[1-9]\|[1-9]\=[+-]\)\=' 111 | 112 | syn cluster yamlConstant contains=yamlBool,yamlNull 113 | 114 | syn cluster yamlFlow contains=yamlFlowString,yamlFlowMapping,yamlFlowCollection 115 | syn cluster yamlFlow add=yamlFlowMappingKey,yamlFlowMappingMerge 116 | syn cluster yamlFlow add=@yamlConstant,yamlPlainScalar,yamlFloat 117 | syn cluster yamlFlow add=yamlTimestamp,yamlInteger,yamlMappingKeyStart 118 | syn cluster yamlFlow add=yamlComment 119 | syn region yamlFlowMapping matchgroup=yamlFlowIndicator start='{' end='}' contains=@yamlFlow 120 | syn region yamlFlowCollection matchgroup=yamlFlowIndicator start='\[' end='\]' contains=@yamlFlow 121 | 122 | execute 'syn match yamlPlainScalar /'.s:ns_plain_out.'/' 123 | execute 'syn match yamlPlainScalar contained /'.s:ns_plain_in.'/' 124 | 125 | syn match yamlMappingKeyStart '?\ze\s' 126 | syn match yamlMappingKeyStart '?' contained 127 | 128 | execute 'syn match yamlFlowMappingKey /\%#=1'.s:ns_plain_in.'\%(\s\+'.s:ns_plain_in.'\)*\ze\s*:/ contained '. 129 | \'nextgroup=yamlKeyValueDelimiter' 130 | syn match yamlFlowMappingMerge /<<\ze\s*:/ contained nextgroup=yamlKeyValueDelimiter 131 | 132 | syn match yamlBlockCollectionItemStart '^\s*\zs-\%(\s\+-\)*\s' nextgroup=yamlBlockMappingKey,yamlBlockMappingMerge 133 | " Use the old regexp engine, the NFA engine doesn't like all the \@ items. 134 | execute 'syn match yamlBlockMappingKey /\%#=1^\s*\zs'.s:ns_plain_out.'\%(\s\+'.s:ns_plain_out.'\)*\ze\s*:\%(\s\|$\)/ '. 135 | \'nextgroup=yamlKeyValueDelimiter' 136 | execute 'syn match yamlBlockMappingKey /\%#=1\s*\zs'.s:ns_plain_out.'\%(\s\+'.s:ns_plain_out.'\)*\ze\s*:\%(\s\|$\)/ contained '. 137 | \'nextgroup=yamlKeyValueDelimiter' 138 | syn match yamlBlockMappingMerge /^\s*\zs<<\ze:\%(\s\|$\)/ nextgroup=yamlKeyValueDelimiter 139 | syn match yamlBlockMappingMerge /<<\ze\s*:\%(\s\|$\)/ nextgroup=yamlKeyValueDelimiter contained 140 | 141 | syn match yamlKeyValueDelimiter /\s*:/ contained 142 | syn match yamlKeyValueDelimiter /\s*:/ contained 143 | 144 | syn cluster yamlScalarWithSpecials contains=yamlPlainScalar,yamlBlockMappingKey,yamlFlowMappingKey 145 | 146 | let s:_bounder = s:SimplifyToAssumeAllPrintable('\%([[\]{}, \t]\@!\p\)') 147 | if b:yaml_schema is# 'json' 148 | syn keyword yamlNull null contained containedin=@yamlScalarWithSpecials 149 | syn keyword yamlBool true false 150 | exe 'syn match yamlInteger /'.s:_bounder.'\@1` by default). 76 | * A new vertically split buffer will be shown to display the output. 77 | * Change the request block to (or add another one) 78 | 79 | ``` 80 | http://localhost:9200 81 | POST /testindex/testtype 82 | { 83 | "key": "new key", 84 | "value": "new value"| 85 | } 86 | ``` 87 | 88 | * Hit the trigger key with the cursor placed anywhere within this request block. 89 | * The display buffer will be updated with the new response. 90 | 91 | #### 4.2 Multiple VRC Buffers 92 | 93 | This example continues the previous one. 94 | 95 | * Open a new VRC buffer in a new tab 96 | 97 | ``` 98 | :tabe NewVrc.rest 99 | ``` 100 | 101 | * Since the new buffer has the extension `rest`, the VRC plug-in is active for 102 | this one. 103 | * Set `b:vrc_output_buffer_name` of this buffer to `__NEW_VRC__` 104 | 105 | ``` 106 | :let b:vrc_output_buffer_name = '__NEW_VRC__' 107 | ``` 108 | 109 | * Type in a request block such as 110 | 111 | ``` 112 | http://localhost:9200 113 | GET /testindex/_search?pretty| 114 | ``` 115 | 116 | * Hit the trigger key. 117 | * A new display buffer will be created showing the response. 118 | * Go back to the VRC buffer of the previous example (previous tab). 119 | * Try to execute an existing request block. 120 | * The corresponding display buffer will be updated. 121 | 122 | ### 5. Usage 123 | 124 | This plug-in is activated when Vim opens a buffer of type `rest`. This may be 125 | a file with the extension `.rest` or a buffer with `filetype` explicitly set to 126 | `rest` by 127 | 128 | :set ft=rest 129 | 130 | A **VRC buffer** can have one or many REST request blocks. A **request block** 131 | contains a *host*, *optional cUrl options*, *optional headers*, *query*, and an 132 | *optional request body* (usually used by POST). A block is defined as follows. 133 | 134 | # host 135 | http[s]://domain[:port] 136 | 137 | [optional cUrl options] 138 | 139 | [optional headers] 140 | 141 | # query 142 | POST /path/to/resource 143 | [optional request body] 144 | 145 | A comment starts with `#` or `//` and must be on its own line. The following 146 | is an example of a VRC buffer with multiple request blocks. 147 | 148 | # GETting from resource. 149 | http://example.com 150 | GET /path/to/resource?key=value 151 | 152 | # POSTing to an ElasticSearch service. 153 | http://example.com/elasticsearch 154 | 155 | // Specify optional headers. 156 | Content-Type: application/json; charset=utf-8 157 | 158 | POST /index/type?pretty 159 | { 160 | "key": "a key", 161 | "value": "a value" 162 | } 163 | 164 | # Submitting a form. 165 | https://example.net:8080 166 | 167 | Accept: */* 168 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== 169 | Cache-Control: no-cache 170 | Connection: keep-alive 171 | Content-Type: application/x-www-form-urlencoded 172 | Cookie: userId=ac32:dfbe:8f1a:249c; sid=cfb48e3d98fcb1 173 | User-Agent: VRC 174 | 175 | POST /form 176 | var1=value of var1& 177 | var2=value of var2 178 | 179 | When the trigger key is called (`` by default), VRC processes the request 180 | block that the cursor stays within. The response is displayed in a new 181 | vertically split buffer. This output buffer is reused if it's already present. 182 | 183 | By default, the display/output buffer is named `__REST_response__`. If there 184 | are multiple VRC buffers, they all share the same display buffer. To have a 185 | separate output display for each VRC buffer, `b:vrc_output_buffer_name` can be 186 | set in the buffer scope. 187 | 188 | #### 5.1 cUrl Options 189 | 190 | A recent addition to VRC is the ability to specify cUrl options. These may be 191 | specified by the VRC option `vrc_curl_opts` or declaring in the 192 | [global section](#52-global-definitions) of a REST buffer and request blocks. 193 | 194 | All specified cUrl options are merged together when a cUrl command is built. 195 | For the same keys (cUrl switch) specified at different scopes, the ones of the 196 | request blocks overwrite the ones in the global section then overwrite the 197 | ones defined by `vrc_curl_opts`. 198 | 199 | For the deprecated VRC options, they can be replaced by cUrl options. For 200 | example, assuming they have been defined as follows. 201 | 202 | let g:vrc_connect_timeout = 10 203 | let g:vrc_cookie_jar = '/path/to/cookie' 204 | let g:vrc_follow_redirects = 1 205 | let g:vrc_include_response_header = 1 206 | let g:vrc_max_time = 60 207 | let g:vrc_resolve_to_ipv4 = 1 208 | let g:vrc_ssl_secure = 1 209 | 210 | Using cUrl options, 211 | 212 | let g:vrc_curl_opts = { 213 | \ '--connect-timeout' : 10, 214 | \ '-b': '/path/to/cookie', 215 | \ '-c': '/path/to/cookie', 216 | \ '-L': '', 217 | \ '-i': '', 218 | \ '--max-time': 60, 219 | \ '--ipv4': '', 220 | \ '-k': '', 221 | \} 222 | 223 | #### 5.2 Global Definitions 224 | 225 | The global section is separated from the rest with two dashes `--` and may 226 | include a default host, optional default cUrl options (buffer scope) and 227 | optional default headers. These values are always included in each request. 228 | 229 | Each request block has to start with either two dashes indicating it uses the 230 | default host from the global section or any host only used by this block. If 231 | a 'local host' is given, it's used instead of the one specified in the global 232 | section. Additionally, a request block can specify extra cUrl options and 233 | headers. Local headers are merged with and overwrite global headers. 234 | 235 | # Global definitions. 236 | // Default host. 237 | https://domain[:port]/... 238 | 239 | // Default (buffer scope) cUrl options. 240 | -L 241 | --connect-timeout 10 242 | 243 | // Default headers. 244 | Accept: application/json 245 | X-Header: Custom Data 246 | -- 247 | 248 | # Request block that uses default values from the global section. 249 | -- 250 | GET /some/query 251 | 252 | # Request block that specifies its own host and extra headers. 253 | // Local host. 254 | http://example.net:9200 255 | 256 | // Local cUrl opts. 257 | -k 258 | --ipv4 259 | // This cUrl option overwrites the one in the global section. 260 | --connect-timeout 30 261 | -b /path/to/cookie 262 | -c /path/to/cookie 263 | 264 | // Extra headers. 265 | Xtra-Header: Some Extra. 266 | // This header will overwrite the one in the global section. 267 | X-Header: New Data 268 | 269 | POST /service 270 | var1=value 271 | 272 | #### 5.3 Global Variable Declaration 273 | 274 | VRC now supports variable declarations in the global scope. These variables 275 | then can be used in the query paths, headers, and the body. Notice: values 276 | are not url-encoded. Variables can contain static text, or reference an 277 | environment variable: 278 | 279 | # Global scope. 280 | http://host 281 | 282 | // Variable declarations (value passed as is). 283 | foobar = LoremIpsum 284 | city = Some%20City 285 | zip = 12345 286 | population = 42 287 | restpassword = $SECRET_ENV_VAR 288 | -- 289 | # End global scope. 290 | 291 | -- 292 | GET /city/:city 293 | 294 | -- 295 | GET /city/:city/zip/:zip 296 | 297 | -- 298 | custom-header :foobar 299 | POST /city/:city 300 | { "population": :population } 301 | 302 | #### 5.4 Line-by-line Request Body 303 | 304 | Since version 2.3.0, the request body can be specified on a line-by-line 305 | basis. It's useful for name-value pair services. Each line of the request 306 | body is passed to cURL using `--data` or `--data-urlencode` depending on 307 | the verb. 308 | 309 | To enable, 310 | 311 | let g:vrc_split_request_body = 1 312 | 313 | or 314 | 315 | let b:vrc_split_request_body = 1 316 | 317 | Then the request body can be specified as 318 | 319 | # 320 | # The following params in the request body will be 321 | # sent using `--data-urlencode` 322 | # 323 | http://localhost 324 | Content-Type: text/html; charset=UTF-8 325 | GET /service 326 | var1=value1 327 | var2=value2 328 | 329 | This option won't take effect for `GET` request if the option 330 | `vrc_allow_get_request_body` is set. 331 | 332 | #### 5.4 Consecutive Request Verbs 333 | 334 | A request block may have consecutive request verbs. The output of each request 335 | verb is appended to the output view. 336 | 337 | http://localhost:9200 338 | PUT /test 339 | GET /test 340 | DELETE /test 341 | 342 | ### 6. Configuration 343 | 344 | https://github.com/diepm/vim-rest-console/blob/master/doc/vim-rest-console.txt 345 | 346 | ### 7. Tips 'n Tricks 347 | 348 | #### 7.1 POST Data in Bulk 349 | 350 | Since v3.0, VRC supports POSTing data in bulk using in-line data or an 351 | external data file. It's helpful for such APIs as Elasticsearch's Bulk API. 352 | 353 | To use in-line data, first enable the Elasticsearch support flag. 354 | 355 | let g:vrc_elasticsearch_support = 1 356 | 357 | The request would look like this. 358 | 359 | http://localhost:9200 360 | POST /testindex/_bulk 361 | { "index": { "_index": "test", "_type": "product" } } 362 | { "sku": "SKU1", "name": "Product name 1" } 363 | { "index": { "_index": "test", "_type": "product" } } 364 | { "sku": "SKU2", "name": "Product name 2" } 365 | 366 | Using external data files doesn't need the support flag. 367 | 368 | http://localhost:9200 369 | POST /testindex/_bulk 370 | @data.sample.json 371 | 372 | You can also PUT contents of a file using the same syntax. This is 373 | equivalent to passing --data-binary flag to cURL. 374 | 375 | http://localhost:9200 376 | PUT /testindex/_bulk 377 | @data.sample.json 378 | 379 | #### 7.2 Syntax Highlighting 380 | 381 | Though VRC supports output syntax highlighting, it's based on the response 382 | Content-Type. When Content-Type is not present, the output can still be 383 | syntax-highlighted if the appropriate ftplugin is installed. To force the 384 | output highlighting based on `filetype`, place this setting in `.vimrc`: 385 | 386 | let g:vrc_output_buffer_name = '__VRC_OUTPUT.' 387 | 388 | `filetype` can also be set in the output buffer on an ad hoc basis. 389 | 390 | # vim: set ft=json 391 | 392 | ### 8. Contributors 393 | 394 | Thanks to the contributors (in alphabetical order of GitHub account) 395 | 396 | @dan-silva 397 | @dflupu 398 | @iamFIREcracker 399 | @jojoyuji 400 | @korin 401 | @minhajuddin 402 | @mjakl 403 | @nathanaelkane 404 | @p1otr 405 | @rawaludin 406 | @rlisowski 407 | @sethtrain 408 | @shanesmith 409 | @tdroxler 410 | @tonyskn 411 | @torbjornvatn 412 | 413 | ### 9. License 414 | 415 | MIT 416 | -------------------------------------------------------------------------------- /ftplugin/rest.vim: -------------------------------------------------------------------------------- 1 | setlocal commentstring=#%s 2 | 3 | let s:vrc_auto_format_response_patterns = { 4 | \ 'json': 'python -m json.tool', 5 | \ 'xml': 'xmllint --format -', 6 | \} 7 | 8 | let s:vrc_glob_delim = '\v^--\s*$' 9 | let s:vrc_comment_delim = '\c\v^\s*(#|//)' 10 | let s:vrc_block_delimiter = '\c\v^\s*HTTPS?://|^--\s*$' 11 | 12 | let s:deprecatedMessages = [] 13 | let s:deprecatedCurlOpts = { 14 | \ 'vrc_connect_timeout': '--connect-timeout', 15 | \ 'vrc_cookie_jar': '-b and -c', 16 | \ 'vrc_follow_redirects': '-L', 17 | \ 'vrc_include_response_header': '-i', 18 | \ 'vrc_max_time': '--max-time', 19 | \ 'vrc_resolve_to_ipv4': '--ipv4', 20 | \ 'vrc_ssl_secure': '-k', 21 | \} 22 | 23 | """ 24 | " Properly escape string to use in Windows and Non-Windows shells 25 | " 26 | " @param string val 27 | " @return string 28 | " 29 | function! s:Shellescape(val) 30 | if has("win32") 31 | return '"'.substitute(a:val, '["&\\]', '\\&', 'g').'"' 32 | else 33 | return shellescape(a:val) 34 | endif 35 | endfunction 36 | 37 | """ 38 | " Trim both ends of a string. 39 | " 40 | " @param string txt 41 | " @return string 42 | " 43 | function! s:StrTrim(txt) 44 | return substitute(a:txt, '\v^\s*(\S(.*\S)*)\s*$', '\1', 'g') 45 | endfunction 46 | 47 | """ 48 | " Get a VRC option. Use the given default value if option not found. 49 | " 50 | " @param string a:opt 51 | " @param mixed a:defVal 52 | " @return mixed 53 | " 54 | function! s:GetOpt(opt, defVal) 55 | " Warn if a:opt is deprecated. 56 | let curlOpt = get(s:deprecatedCurlOpts, a:opt, '') 57 | 58 | if exists('b:' . a:opt) 59 | if !empty(curlOpt) 60 | call s:DeprecateOpt(a:opt, curlOpt) 61 | endif 62 | return eval('b:' . a:opt) 63 | endif 64 | if exists('g:' . a:opt) 65 | if !empty(curlOpt) 66 | call s:DeprecateOpt(a:opt, curlOpt) 67 | endif 68 | return eval('g:' . a:opt) 69 | endif 70 | return a:defVal 71 | endfunction 72 | 73 | """ 74 | " Handle a deprecated option. 75 | " 76 | " @param string a:opt 77 | " @param string a:forOpt 78 | " 79 | function! s:DeprecateOpt(opt, forOpt) 80 | let msg = 'Option `' . a:opt . '` is deprecated and will be removed. ' 81 | \ . 'Use the cUrl option(s) ' . a:forOpt . ' instead.' 82 | 83 | echohl WarningMsg | echom msg | echohl None 84 | call add(s:deprecatedMessages, msg) 85 | endfunction 86 | 87 | """ 88 | " Get a value from a VRC dictionary option. 89 | " 90 | " @param dict a:dictName 91 | " @param string a:key 92 | " @param mixed a:defVal 93 | " @return mixed 94 | " 95 | function! s:GetDict(dictName, key, defVal) 96 | for prefix in ['b', 'g', 's'] 97 | let varName = prefix . ':' . a:dictName 98 | if exists(varName) && has_key(eval(varName), a:key) 99 | return get(eval(varName), a:key) 100 | endif 101 | endfor 102 | return a:defVal 103 | endfunction 104 | 105 | 106 | """ 107 | " Get the first and last line numbers of the 108 | " request block enclosing the cursor. 109 | " 110 | " @return list [int, int] 111 | " 112 | function! s:LineNumsRequestBlock() 113 | let curPos = getpos('.') 114 | 115 | let blockStart = 0 116 | let blockEnd = 0 117 | let lineNumGlobDelim = s:LineNumGlobSectionDelim() 118 | 119 | """ Find the start of the enclosing request block. 120 | normal! $ 121 | let blockStart = search(s:vrc_block_delimiter, 'bn') 122 | if !blockStart || blockStart > curPos[1] || blockStart <= lineNumGlobDelim 123 | call cursor(curPos[1:]) 124 | return [0, 0] 125 | endif 126 | 127 | """ Find the start of the next request block. 128 | let blockEnd = search(s:vrc_block_delimiter, 'n') - 1 129 | if blockEnd <= blockStart 130 | let blockEnd = line('$') 131 | endif 132 | call cursor(curPos[1:]) 133 | return [blockStart, blockEnd] 134 | endfunction 135 | 136 | """ 137 | " @return int The line number of the global section delimiter. 138 | " 139 | function! s:LineNumGlobSectionDelim() 140 | let curPos = getpos('.') 141 | normal! gg 142 | let lineNum = search(s:vrc_glob_delim, 'cn') 143 | call cursor(curPos[1:]) 144 | return lineNum 145 | endfunction 146 | 147 | """ 148 | " Parse host between the given line numbers (inclusive end). 149 | " 150 | " @param int a:start 151 | " @param int a:end 152 | " @return list [line num or 0, string] 153 | " 154 | function! s:ParseHost(start, end) 155 | if a:end < a:start 156 | return [0, ''] 157 | endif 158 | let curPos = getpos('.') 159 | call cursor(a:start, 1) 160 | let lineNum = search('\v\c^\s*HTTPS?://', 'cn', a:end) 161 | call cursor(curPos[1:]) 162 | if !lineNum 163 | return [lineNum, ''] 164 | endif 165 | return [lineNum, s:StrTrim(getline(lineNum))] 166 | endfunction 167 | 168 | """ 169 | " Parse the query. 170 | " 171 | " @return list [line num or 0, string] 172 | " 173 | function! s:ParseVerbQuery(start, end) 174 | let curPos = getpos('.') 175 | call cursor(a:start, 1) 176 | let lineNum = search( 177 | \ '\c\v^(GET|POST|PUT|DELETE|HEAD|PATCH|OPTIONS|TRACE)\s+', 178 | \ 'cn', 179 | \ a:end 180 | \) 181 | call cursor(curPos[1:]) 182 | if !lineNum 183 | return [lineNum, ''] 184 | endif 185 | return [lineNum, s:StrTrim(getline(lineNum))] 186 | endfunction 187 | 188 | """ 189 | " Parse header options between the given line numbers (inclusive end). 190 | " 191 | " @param int a:start 192 | " @param int a:end 193 | " @return dict {'header1': 'value1', 'header2': 'value2'} 194 | " 195 | function! s:ParseHeaders(start, end) 196 | let contentTypeOpt = s:GetOpt('vrc_header_content_type', 'application/json') 197 | let headers = {'Content-Type': contentTypeOpt} 198 | if (a:end < a:start) 199 | return headers 200 | endif 201 | 202 | let lineBuf = getline(a:start, a:end) 203 | let hasContentType = 0 204 | for line in lineBuf 205 | let line = s:StrTrim(line) 206 | if line ==? '' || line =~? s:vrc_comment_delim || line =~? '\v^--?\w+' 207 | continue 208 | endif 209 | let sepIdx = stridx(line, ':') 210 | if sepIdx > -1 211 | let key = s:StrTrim(line[0:sepIdx - 1]) 212 | let headers[key] = s:StrTrim(line[sepIdx + 1:]) 213 | endif 214 | endfor 215 | return headers 216 | endfunction 217 | 218 | """ 219 | " Parse values in global section. 220 | " 221 | " @param int a:start 222 | " @param int a:end 223 | " @return dict {'var1': 'value1', 'var2': 'value2'} 224 | " 225 | function! s:ParseVals(start, end) 226 | let vals = {} 227 | if (a:end < a:start) 228 | return vals 229 | endif 230 | 231 | let lineBuf = getline(a:start, a:end) 232 | 233 | for line in lineBuf 234 | let line = s:StrTrim(line) 235 | if line ==? '' || line =~? s:vrc_comment_delim 236 | continue 237 | endif 238 | let sepIdx = stridx(line, '=') 239 | if sepIdx > -1 240 | let key = s:StrTrim(line[0:sepIdx - 1]) 241 | let val = s:StrTrim(line[sepIdx + 1:]) 242 | if val[:0] is# "$" 243 | let vals[key] = expand(val) 244 | else 245 | let vals[key] = val 246 | endif 247 | endif 248 | endfor 249 | return vals 250 | endfunction 251 | 252 | """ 253 | " Parse the global section. 254 | " 255 | " @return dict { 'host': string, 'headers': {}, 'curlOpts': {}, vals': {} } 256 | " 257 | function! s:ParseGlobSection() 258 | let globSection = { 259 | \ 'host': '', 260 | \ 'headers': {}, 261 | \ 'curlOpts': {}, 262 | \ 'vals': {}, 263 | \} 264 | 265 | """ Search for the line of the global section delimiter. 266 | let lastLine = s:LineNumGlobSectionDelim() 267 | if !lastLine 268 | return globSection 269 | endif 270 | 271 | """ Parse global host. 272 | let [hostLine, host] = s:ParseHost(1, lastLine - 1) 273 | 274 | """ Parse global headers. 275 | let headers = s:ParseHeaders(hostLine + 1, lastLine - 1) 276 | 277 | """ Parse curl options. 278 | let curlOpts = s:ParseCurlOpts(hostLine + 1, lastLine - 1) 279 | 280 | """ Parse global vals. 281 | let vals = s:ParseVals(hostLine + 1, lastLine - 1) 282 | 283 | let globSection = { 284 | \ 'host': host, 285 | \ 'headers': headers, 286 | \ 'curlOpts': curlOpts, 287 | \ 'vals': vals, 288 | \} 289 | return globSection 290 | endfunction 291 | 292 | """ 293 | " Parse the specified cUrl options. 294 | " 295 | " @param int a:fromLine 296 | " @param int a:toLine 297 | " @return dict Dict of lists {'-a': [x, y], '-b': [z], '--opt': []} 298 | " 299 | function! s:ParseCurlOpts(fromLine, toLine) 300 | let curlOpts = {} 301 | for line in getline(a:fromLine, a:toLine) 302 | let line = s:StrTrim(line) 303 | if line !~? '\v^--?\w+' 304 | continue 305 | endif 306 | let [copt; vals] = split(line, '\v\s', 0) 307 | if !has_key(curlOpts, copt) 308 | let curlOpts[copt] = [] 309 | endif 310 | if !empty(vals) 311 | call add(curlOpts[copt], join(vals, ' ')) 312 | endif 313 | endfor 314 | return curlOpts 315 | endfunction 316 | 317 | """ 318 | " Parse the request block. 319 | " 320 | " @param int a:start 321 | " @param int a:resumeFrom (inclusive) 322 | " @param int a:end (inclusive) 323 | " @param dict a:globSection 324 | " @return dict { 325 | " 'success': boolean, 326 | " 'resumeFrom': int, 327 | " 'msg': string, 328 | " 'host': string, 329 | " 'headers': dict, 330 | " 'curlOpts': dict, 331 | " 'httpVerb': string, 332 | " 'requestPath': string, 333 | " 'dataBody': string, 334 | " } 335 | " 336 | function! s:ParseRequest(start, resumeFrom, end, globSection) 337 | """ Parse host. 338 | let [lineNumHost, host] = s:ParseHost(a:start, a:end) 339 | if !lineNumHost 340 | let host = get(a:globSection, 'host', '') 341 | let lineNumHost = a:start 342 | endif 343 | if empty(host) 344 | return { 345 | \ 'success': 0, 346 | \ 'msg': 'Missing host', 347 | \} 348 | endif 349 | 350 | """ Parse the HTTP verb query. 351 | let [lineNumVerb, restQuery] = s:ParseVerbQuery(a:resumeFrom, a:end) 352 | if !lineNumVerb 353 | return { 354 | \ 'success': 0, 355 | \ 'msg': 'Missing query', 356 | \} 357 | endif 358 | 359 | """ Parse the next HTTP verb query. 360 | let resumeFrom = lineNumVerb + 1 361 | let [lineNumNextVerb, nextRestQuery] = s:ParseVerbQuery(lineNumVerb + 1, a:end) 362 | if !lineNumNextVerb 363 | let resumeFrom = a:end + 1 364 | let lineNumNextVerb = a:end + 1 365 | endif 366 | 367 | """ Parse headers if any and merge with global headers. 368 | let localHeaders = s:ParseHeaders(lineNumHost + 1, lineNumVerb - 1) 369 | let headers = get(a:globSection, 'headers', {}) 370 | call extend(headers, localHeaders) 371 | 372 | """ Parse curl options; local opts overwrite global opts when merged. 373 | let localCurlOpts = s:ParseCurlOpts(lineNumHost + 1, lineNumVerb - 1) 374 | let curlOpts = get(a:globSection, 'curlOpts', {}) 375 | call extend(curlOpts, localCurlOpts) 376 | 377 | let vals = get(a:globSection, 'vals', {}) 378 | 379 | """ Parse http verb, query path, and data body. 380 | let [httpVerb; queryPathList] = split(restQuery) 381 | let dataBody = getline(lineNumVerb + 1, lineNumNextVerb - 1) 382 | 383 | """ Search and replace values in queryPath, dataBody, and headers 384 | let queryPath = join(queryPathList, '') 385 | for key in keys(vals) 386 | let queryPath = substitute(queryPath, ":" . key, vals[key], "") 387 | call map(dataBody, 'substitute(v:val, ":" . key, vals[key], "")') 388 | call map(headers, 'substitute(v:val, ":" . key, vals[key], "")') 389 | endfor 390 | 391 | """ Filter out comment and blank lines. 392 | call filter(dataBody, 'v:val !~ ''\v^\s*(#|//).*$|\v^\s*$''') 393 | 394 | """ Some might need leading/trailing spaces in body rows. 395 | "call map(dataBody, 's:StrTrim(v:val)') 396 | return { 397 | \ 'success': 1, 398 | \ 'resumeFrom': resumeFrom, 399 | \ 'msg': '', 400 | \ 'host': host, 401 | \ 'headers': headers, 402 | \ 'curlOpts': curlOpts, 403 | \ 'httpVerb': httpVerb, 404 | \ 'requestPath': queryPath, 405 | \ 'dataBody': dataBody 406 | \} 407 | endfunction 408 | 409 | """ 410 | " Construct the cUrl command given the request. 411 | " 412 | " @see s:ParseRequest() For a:request. 413 | " 414 | " @param dict a:request 415 | " @return list [command, dict of curl options] 416 | " 417 | function! s:GetCurlCommand(request) 418 | """ Construct curl args. 419 | let curlOpts = vrc#opt#GetDefaultCurlOpts() 420 | call extend(curlOpts, get(a:request, 'curlOpts', {})) 421 | 422 | let vrcIncludeHeader = s:GetOpt('vrc_include_response_header', 0) 423 | if vrcIncludeHeader && !vrc#opt#DictHasKeys(curlOpts, ['-i', '--include']) 424 | let curlOpts['-i'] = '' 425 | endif 426 | 427 | let vrcDebug = s:GetOpt('vrc_debug', 0) 428 | if vrcDebug && !has_key(curlOpts, '-v') 429 | let curlOpts['-v'] = '' 430 | endif 431 | 432 | " Consider to add -k only if vrc_ssl_secure is configured (secureSsl > -1). 433 | let secureSsl = s:GetOpt('vrc_ssl_secure', -1) 434 | if a:request.host =~? '\v^\s*HTTPS://' && secureSsl == 0 && !has_key(curlOpts, '-k') 435 | let curlOpts['-k'] = '' 436 | endif 437 | 438 | """ Add --ipv4 439 | let resolveToIpv4 = s:GetOpt('vrc_resolve_to_ipv4', 0) 440 | if resolveToIpv4 && !has_key(curlOpts, '--ipv4') 441 | let curlOpts['--ipv4'] = '' 442 | endif 443 | 444 | """ Add --cookie-jar 445 | let cookieJar = s:GetOpt('vrc_cookie_jar', '') 446 | if !empty(cookieJar) 447 | if !has_key(curlOpts, '-b') 448 | let curlOpts['-b'] = cookieJar 449 | endif 450 | if !has_key(curlOpts, '-c') 451 | let curlOpts['-c'] = cookieJar 452 | endif 453 | endif 454 | 455 | """ Add -L option to enable redirects 456 | let locationEnabled = s:GetOpt('vrc_follow_redirects', 0) 457 | if locationEnabled && !has_key(curlOpts, '-L') 458 | let curlOpts['-L'] = '' 459 | endif 460 | 461 | """ Add headers. 462 | let headerOpt = get(curlOpts, '-H', '') 463 | if empty(headerOpt) 464 | let curlOpts['-H'] = [] 465 | elseif type(headerOpt) != type([]) 466 | let curlOpts['-H'] = [headerOpt] 467 | endif 468 | for key in keys(a:request.headers) 469 | call add(curlOpts['-H'], key . ': ' . a:request.headers[key]) 470 | endfor 471 | 472 | """ Timeout options. 473 | let vrcConnectTimeout = s:GetOpt('vrc_connect_timeout', 0) 474 | if vrcConnectTimeout && !has_key(curlOpts, '--connect-timeout') 475 | let curlOpts['--connect-timeout'] = vrcConnectTimeout 476 | endif 477 | 478 | let vrcMaxTime = s:GetOpt('vrc_max_time', 0) 479 | if vrcMaxTime && !has_key(curlOpts, '--max-time') 480 | let curlOpts['--max-time'] = vrcMaxTime 481 | endif 482 | 483 | """ Convert cUrl options to command line arguments. 484 | let curlArgs = vrc#opt#DictToCurlArgs(curlOpts) 485 | call map(curlArgs, 's:EscapeCurlOpt(v:val)') 486 | 487 | """ Add http verb. 488 | let httpVerb = a:request.httpVerb 489 | call add(curlArgs, s:GetCurlRequestOpt(httpVerb)) 490 | 491 | """ Add data body. 492 | if !empty(a:request.dataBody) 493 | call add(curlArgs, s:GetCurlDataArgs(a:request)) 494 | endif 495 | return [ 496 | \ 'curl ' . join(curlArgs) . ' ' . s:Shellescape(a:request.host . a:request.requestPath), 497 | \ curlOpts 498 | \] 499 | endfunction 500 | 501 | """ 502 | " Helper function to shell-escape cUrl options. 503 | " 504 | " @param string a:val 505 | " 506 | function! s:EscapeCurlOpt(val) 507 | return a:val !~ '\v^-' ? s:Shellescape(a:val) : a:val 508 | endfunction 509 | 510 | """ 511 | " Get the cUrl option for request method (--get, --head, -X ...) 512 | " 513 | " @param string a:httpVerb 514 | " @return string 515 | " 516 | function! s:GetCurlRequestOpt(httpVerb) 517 | if a:httpVerb ==? 'GET' 518 | if s:GetOpt('vrc_allow_get_request_body', 0) 519 | return '-X GET' 520 | endif 521 | return '--get' 522 | elseif a:httpVerb ==? 'HEAD' 523 | return '--head' 524 | endif 525 | return '-X ' . a:httpVerb 526 | endfunction 527 | 528 | """ 529 | " Get the cUrl option to include data body (--data, --data-urlencode...) 530 | " 531 | " @see s:ParseRequest() For a:request. 532 | " 533 | " @param dict a:request 534 | " @return string 535 | " 536 | function! s:GetCurlDataArgs(request) 537 | let httpVerb = a:request.httpVerb 538 | let dataLines = a:request.dataBody 539 | 540 | let preproc = s:GetOpt('vrc_body_preprocessor', '') 541 | 542 | """ These verbs should have request body passed as POST params. 543 | if httpVerb ==? 'POST' 544 | \ || httpVerb ==? 'PUT' 545 | \ || httpVerb ==? 'PATCH' 546 | \ || httpVerb ==? 'OPTIONS' 547 | """ If data is loaded from file. 548 | if stridx(get(dataLines, 0, ''), '@') == 0 549 | return '--data-binary ' . s:Shellescape(dataLines[0]) 550 | endif 551 | 552 | """ Call body preprocessor if set. 553 | if preproc != '' 554 | let dataLines = systemlist(preproc, join(dataLines, "\r")) 555 | endif 556 | 557 | """ If request body is split line by line. 558 | if s:GetOpt('vrc_split_request_body', 0) 559 | call map(dataLines, '"--data " . s:Shellescape(v:val)') 560 | return join(dataLines) 561 | endif 562 | 563 | """ If ElasticSearch support is on and it's a _bulk request. 564 | let elasticSupport = s:GetOpt('vrc_elasticsearch_support', 0) 565 | if elasticSupport && match(a:request.requestPath, '/_bulk\|/_msearch') > -1 566 | " shellescape also escapes \n () to \\n, need to replace back. 567 | return '--data ' . 568 | \ substitute( 569 | \ s:Shellescape(join(dataLines, "\n") . "\n"), 570 | \ '\\\n', 571 | \ "\n", 572 | \ 'g' 573 | \) 574 | endif 575 | 576 | """ Otherwise, just join data using empty space. 577 | return '--data ' . s:Shellescape(join(dataLines, '')) 578 | endif 579 | 580 | """ If verb is GET and GET request body is allowed. 581 | if httpVerb ==? 'GET' && s:GetOpt('vrc_allow_get_request_body', 0) 582 | """ Call body preprocessor if set. 583 | if preproc != '' 584 | let dataLines = systemlist(preproc, join(dataLines, "\r")) 585 | endif 586 | return '--data ' . s:Shellescape(join(dataLines, '')) 587 | endif 588 | 589 | """ For other cases, request body is passed as GET params. 590 | if s:GetOpt('vrc_split_request_body', 0) 591 | """ If request body is split, url-encode each line. 592 | call map(dataLines, '"--data-urlencode " . s:Shellescape(v:val)') 593 | return join(dataLines) 594 | endif 595 | """ Otherwise, url-encode and send the request body as a whole. 596 | return '--data-urlencode ' . s:Shellescape(join(dataLines, '')) 597 | endfunction 598 | 599 | """ 600 | " Display output in the given buffer name. 601 | " 602 | " @see s:RunQuery() For a:outputInfo. 603 | " 604 | " @param string a:tmpBufName 605 | " @param dict a:outputInfo {'outputChunks': list[string], 'commands': list[string]} 606 | " @param dict a:config {'hasResponseHeader': boolean} 607 | " 608 | function! s:DisplayOutput(tmpBufName, outputInfo, config) 609 | """ Get view options before working in the view buffer. 610 | let autoFormatResponse = s:GetOpt('vrc_auto_format_response_enabled', 1) 611 | let syntaxHighlightResponse = s:GetOpt('vrc_syntax_highlight_response', 1) 612 | let includeResponseHeader = get(a:config, 'hasResponseHeader', 0) 613 | let contentType = s:GetOpt('vrc_response_default_content_type', '') 614 | 615 | """ Setup view. 616 | let origWin = winnr() 617 | let outputWin = bufwinnr(bufnr(a:tmpBufName)) 618 | if outputWin == -1 619 | let cmdSplit = 'vsplit' 620 | if s:GetOpt('vrc_horizontal_split', 0) 621 | let cmdSplit = 'split' 622 | endif 623 | 624 | if s:GetOpt('vrc_keepalt', 0) 625 | let cmdSplit = 'keepalt ' . cmdSplit 626 | endif 627 | 628 | """ Create view if not loadded or hidden. 629 | execute 'rightbelow ' . cmdSplit . ' ' . a:tmpBufName 630 | setlocal buftype=nofile 631 | else 632 | """ View already shown, switch to it. 633 | execute outputWin . 'wincmd w' 634 | endif 635 | 636 | """ Display output in view. 637 | setlocal modifiable 638 | silent! normal! ggdG 639 | let output = join(a:outputInfo['outputChunks'], "\n\n") 640 | call setline('.', split(substitute(output, '[[:return:]]', '', 'g'), '\v\n')) 641 | 642 | """ Display commands in quickfix window if any. 643 | if (!empty(a:outputInfo['commands'])) 644 | execute 'cgetexpr' string(a:outputInfo['commands']) 645 | copen 646 | execute outputWin 'wincmd w' 647 | endif 648 | 649 | """ Detect content-type based on the returned header. 650 | let emptyLineNum = 0 651 | if includeResponseHeader 652 | call cursor(1, 0) 653 | let emptyLineNum = search('\v^\s*$', 'n') 654 | let contentTypeLineNum = search('\v\c^Content-Type:', 'n', emptyLineNum) 655 | 656 | if contentTypeLineNum > 0 657 | let contentType = substitute( 658 | \ getline(contentTypeLineNum), 659 | \ '\v\c^Content-Type:\s*([^;[:blank:]]*).*$', 660 | \ '\1', 661 | \ 'g' 662 | \) 663 | endif 664 | endif 665 | 666 | """ Continue with options depending content-type. 667 | if !empty(contentType) 668 | let fileType = substitute(contentType, '\v^.*/(.*\+)?(.*)$', '\2', 'g') 669 | 670 | """ Auto-format the response. 671 | if autoFormatResponse 672 | let formatCmd = s:GetDict('vrc_auto_format_response_patterns', fileType, '') 673 | if !empty(formatCmd) 674 | """ Auto-format response body 675 | let formattedBody = system( 676 | \ formatCmd, 677 | \ getline(emptyLineNum, '$') 678 | \) 679 | if v:shell_error == 0 680 | silent! execute (emptyLineNum + 1) . ',$delete _' 681 | if s:GetOpt('vrc_auto_format_uhex', 0) 682 | let formattedBody = substitute( 683 | \ formattedBody, 684 | \ '\v\\u(\x{4})', 685 | \ '\=nr2char("0x" . submatch(1), 1)', 686 | \ 'g' 687 | \) 688 | endif 689 | call append('$', split(formattedBody, '\v\n')) 690 | elseif s:GetOpt('vrc_debug', 0) 691 | echom "VRC: auto-format error: " . v:shell_error 692 | echom formattedBody 693 | endif 694 | endif 695 | endif 696 | 697 | """ Syntax-highlight response. 698 | if syntaxHighlightResponse 699 | syntax clear 700 | try 701 | execute "syntax include @vrc_" . fileType . " syntax/" . fileType . ".vim" 702 | execute "syntax region body start=/^$/ end=/\%$/ contains=@vrc_" . fileType 703 | catch 704 | endtry 705 | endif 706 | endif 707 | 708 | """ Finalize view. 709 | setlocal nomodifiable 710 | execute origWin . 'wincmd w' 711 | endfunction 712 | 713 | """ 714 | " Run a REST request between the given lines. 715 | " 716 | " @param int a:start 717 | " @param int a:end 718 | " 719 | function! s:RunQuery(start, end) 720 | let globSection = s:ParseGlobSection() 721 | let outputInfo = { 722 | \ 'outputChunks': [], 723 | \ 'commands': [], 724 | \} 725 | 726 | " The `while loop` is to support multiple 727 | " requests using consecutive verbs. 728 | let resumeFrom = a:start 729 | let shouldShowCommand = s:GetOpt('vrc_show_command', 0) 730 | let shouldDebug = s:GetOpt('vrc_debug', 0) 731 | while resumeFrom < a:end 732 | let request = s:ParseRequest(a:start, resumeFrom, a:end, globSection) 733 | if !request.success 734 | echom request.msg 735 | return 736 | endif 737 | 738 | let [curlCmd, curlOpts] = s:GetCurlCommand(request) 739 | if shouldDebug 740 | echom '[Debug] Command: ' . curlCmd 741 | echom '[Debug] cUrl options: ' . string(curlOpts) 742 | endif 743 | silent !clear 744 | redraw! 745 | 746 | call add(outputInfo['outputChunks'], system(curlCmd)) 747 | if shouldShowCommand 748 | call add(outputInfo['commands'], curlCmd) 749 | endif 750 | let resumeFrom = request.resumeFrom 751 | endwhile 752 | 753 | call s:DisplayOutput( 754 | \ s:GetOpt('vrc_output_buffer_name', '__REST_response__'), 755 | \ outputInfo, 756 | \ { 757 | \ 'hasResponseHeader': vrc#opt#DictHasKeys(curlOpts, ['-i', '--include']) 758 | \ } 759 | \) 760 | endfunction 761 | 762 | """ 763 | " Restore the win line to the given previous line. 764 | " 765 | " @param int a:prevLine 766 | " 767 | function! s:RestoreWinLine(prevLine) 768 | let offset = winline() - a:prevLine 769 | if !offset 770 | return 771 | elseif offset > 0 772 | exec "normal! " . offset . "\" 773 | else 774 | exec "normal! " . -offset . "\" 775 | endif 776 | endfunction 777 | 778 | """ 779 | " Run a request block that encloses the cursor. 780 | " 781 | function! VrcQuery() 782 | """ We'll jump pretty much. Save the current win line to set the view as before. 783 | let curWinLine = winline() 784 | 785 | let curPos = getpos('.') 786 | if curPos[1] <= s:LineNumGlobSectionDelim() 787 | echom 'Cannot execute global section' 788 | return 789 | endif 790 | 791 | """ Determine the REST request block to process. 792 | let [blockStart, blockEnd] = s:LineNumsRequestBlock() 793 | if !blockStart 794 | call s:RestoreWinLine(curWinLine) 795 | echom 'Missing host/block start' 796 | return 797 | endif 798 | 799 | """ Parse and execute the query 800 | call s:RunQuery(blockStart, blockEnd) 801 | call s:RestoreWinLine(curWinLine) 802 | 803 | """ Display deprecated message if any. 804 | if !empty(s:deprecatedMessages) 805 | for msg in s:deprecatedMessages 806 | echohl WarningMsg | echo msg | echohl None 807 | endfor 808 | let s:deprecatedMessages = [] 809 | endif 810 | endfunction 811 | 812 | """ 813 | " Do the key map. 814 | " 815 | function! VrcMap() 816 | let triggerKey = s:GetOpt('vrc_trigger', '') 817 | execute 'vnoremap ' . triggerKey . ' :call VrcQuery()' 818 | execute 'nnoremap ' . triggerKey . ' :call VrcQuery()' 819 | execute 'inoremap ' . triggerKey . ' :call VrcQuery()' 820 | endfunction 821 | 822 | if s:GetOpt('vrc_set_default_mapping', 1) 823 | call VrcMap() 824 | endif 825 | -------------------------------------------------------------------------------- /doc/vim-rest-console.txt: -------------------------------------------------------------------------------- 1 | *vim-rest-console.txt* *vrc* A plug-in to help work with RESTful services. 2 | 3 | __ ______ ____ ~ 4 | \ \ / / _ \ / ___| ~ 5 | \ \ / /| |_) | | ~ 6 | \ V / | _ <| |___ ~ 7 | \_/ |_| \_\\____| ~ 8 | 9 | 10 | VRC creates a vim-friendly console to interact with RESTful services. 11 | 12 | ============================================================================== 13 | CONTENTS *VrcContents* 14 | 15 | 1. Introduction.......................................... |VrcIntroduction| 16 | 2. Features.................................................. |VrcFeatures| 17 | 3. Installation.......................................... |VrcInstallation| 18 | 4. Examples.................................................. |VrcExamples| 19 | 4.1 Single VRC Buffer.............................. |VrcExSingleBuffer| 20 | 4.2 Multiple VRC Buffers........................... |VrcExMultiBuffers| 21 | 5. Usage........................................................ |VrcUsage| 22 | 5.1 cUrl Options...................................... |VrcCurlOptions| 23 | 5.2 Global Definitions.......................... |VrcGlobalDefinitions| 24 | 5.3 Global Variable Declaration..................... |VrcGlobalVarDecl| 25 | 5.4 Line-by-line Request Body.................... |VrcSplitRequestBody| 26 | 5.5 Consecutive Request Verbs......................... |VrcConReqVerbs| 27 | 6. Configuration........................................ |VrcConfiguration| 28 | vrc_allow_get_request_body................ |vrc_allow_get_request_body| 29 | vrc_auto_format_response_enabled.... |vrc_auto_format_response_enabled| 30 | vrc_auto_format_response_patterns...|vrc_auto_format_response_patterns| 31 | vrc_auto_format_uhex.............................|vrc_auto_format_uhex| 32 | vrc_curl_opts.......................................... |vrc_curl_opts| 33 | vrc_debug.................................................. |vrc_debug| 34 | vrc_elasticsearch_support.................. |vrc_elasticsearch_support| 35 | vrc_header_content_type...................... |vrc_header_content_type| 36 | vrc_horizontal_split..... .................... |vrc_horizontal_split| 37 | vrc_keepalt.............................................. |vrc_keepalt| 38 | vrc_output_buffer_name........................ |vrc_output_buffer_name| 39 | vrc_response_default_content_type.. |vrc_response_default_content_type| 40 | vrc_set_default_mapping...................... |vrc_set_default_mapping| 41 | vrc_show_command.....................................|vrc_show_command| 42 | vrc_split_request_body........................ |vrc_split_request_body| 43 | vrc_syntax_highlight_response...........|vrc_syntax_highlight_response| 44 | vrc_trigger.............................................. |vrc_trigger| 45 | 7. Tips 'n Tricks................................................. |VrcTnt| 46 | 7.1 POST Data in Bulk................................. |VrcTntDataBulk| 47 | 7.2 Syntax Highlighting.................................. |VrcTntStxHi| 48 | 8. Contributors.......................................... |VrcContributors| 49 | 9. License.................................................... |VrcLicense| 50 | 51 | ============================================================================== 52 | *VrcIntroduction* 53 | 1. INTRODUCTION~ 54 | 55 | This plug-in is to help send requests to and display responses from RESTful 56 | services in Vim. It's useful for working with REST services that use JSON to 57 | exchange information between server and client such as Elasticsearch. 58 | 59 | VRC can also be used as a cURL client for simple needs such as getting a 60 | HTTP page response or posting to a form. 61 | 62 | Requirements: 63 | * cURL 64 | * Vim 7.4 (might work with the older versions) 65 | 66 | ============================================================================== 67 | *VrcFeatures* 68 | 2. FEATURES~ 69 | 70 | * Execute REST request and display the response on a separate display buffer. 71 | * Make changing/adjusting request body easy. 72 | * Can have multiple REST request blocks per VRC buffer. 73 | * Can have multiple VRC buffers where they all share the same output buffer or 74 | each can have its own output buffer. 75 | * Particularly useful for working with REST services that require the request 76 | body to be sent in JSON such as Elasticsearch. 77 | * Syntax highlighting. 78 | * Supported verbs: GET, POST, PUT, HEAD, PATCH, OPTIONS, and TRACE. 79 | 80 | ============================================================================== 81 | *VrcInstallation* 82 | 3. INSTALLATION~ 83 | 84 | To install using pathogen.vim 85 | > 86 | cd ~/.vim/bundle 87 | git clone https://github.com/diepm/vim-rest-console.git 88 | < 89 | To install using Vundle 90 | 91 | " Add this line to .vimrc > 92 | Plugin 'diepm/vim-rest-console' 93 | < 94 | Other methods should work as well. 95 | 96 | ============================================================================== 97 | *VrcExamples* 98 | 4. EXAMPLES~ 99 | 100 | For more examples, check out 101 | 102 | https://raw.githubusercontent.com/diepm/vim-rest-console/master/sample.rest 103 | 104 | The following examples assume that an Elasticsearch service is running at 105 | localhost. The pipe (|) indicates the current position of the cursor. 106 | 107 | ------------------------------------------------------------------------------ 108 | *VrcExSingleBuffer* 109 | 4.1 Single VRC Buffer~ 110 | 111 | * From the command line, run a new Vim instance. 112 | * Set the buffer `filetype` to 'rest' by 113 | > 114 | :set ft=rest 115 | < 116 | * Type in 117 | > 118 | http://localhost:9200 119 | GET /_cat/nodes?v| 120 | < 121 | * Hit the trigger key ( by default). 122 | * A new vertically split buffer will be shown to display the output. 123 | * Change the request block to (or add another one) 124 | > 125 | http://localhost:9200 126 | POST /testindex/testtype 127 | { 128 | "key": "new key", 129 | "value": "new value"| 130 | } 131 | < 132 | * Hit the trigger key with the cursor placed anywhere within this request 133 | block. 134 | * The display buffer will be updated with the new response. 135 | 136 | ------------------------------------------------------------------------------ 137 | *VrcExMultiBuffers* 138 | 4.2 Multiple VRC Buffers~ 139 | 140 | This example continues the previous one. 141 | 142 | * Open a new VRC buffer in a new tab 143 | > 144 | :tabe NewVrc.rest 145 | < 146 | * Since the new buffer has the extension 'rest', the VRC plug-in is active 147 | for this one. 148 | 149 | * Set |vrc_output_buffer_name| of this buffer to '__NEW_VRC__' 150 | > 151 | :let b:vrc_output_buffer_name = '__NEW_VRC__' 152 | < 153 | * Type in a request block such as 154 | > 155 | http://localhost:9200 156 | GET /testindex/_search?pretty| 157 | < 158 | * Hit the trigger key. 159 | * A new display buffer will be created showing the response. 160 | * Go back to the VRC buffer of the previous example (previous tab). 161 | * Try to execute an existing request block. 162 | * The corresponding display buffer will be updated. 163 | 164 | ============================================================================== 165 | *VrcUsage* 166 | 5. USAGE~ 167 | 168 | This plug-in is activated when Vim opens a buffer of type 'rest'. This may be 169 | a file with the extension 'rest' or a buffer with `filetype` explicitly set to 170 | 'rest' by 171 | > 172 | :set ft=rest 173 | < 174 | A VRC buffer can have one or many REST request blocks. A "request block" 175 | contains a "host," "optional cUrl options," "optional headers," "query," and 176 | an "optional request body" (usually used by POST). A block is defined as 177 | follows. 178 | > 179 | # host 180 | http[s]://domain[:port] 181 | 182 | [optional cUrl options] 183 | 184 | [optional headers] 185 | 186 | # query 187 | POST /path/to/resource 188 | [optional request body] 189 | < 190 | A comment starts with '#' or '//' and must be on its own line. The following 191 | is an example of a VRC buffer with multiple request blocks. 192 | > 193 | # GETting from resource. 194 | http://example.com 195 | GET /path/to/resource?key=value 196 | 197 | # POSTing to an Elasticsearch service. 198 | http://example.com/elasticsearch 199 | 200 | // Specify optional headers. 201 | Content-Type: application/json; charset=utf-8 202 | 203 | POST /index/type?pretty 204 | { 205 | "key": "a key", 206 | "value": "a value" 207 | } 208 | 209 | # Submitting a form. 210 | https://example.net:8080 211 | 212 | Accept: */* 213 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== 214 | Cache-Control: no-cache 215 | Connection: keep-alive 216 | Content-Type: application/x-www-form-urlencoded 217 | Cookie: userId=ac32:dfbe:8f1a:249c; sid=cfb48e3d98fcb1 218 | User-Agent: VRC 219 | 220 | POST /form 221 | var1=value of var1& 222 | var2=value of var2 223 | < 224 | When the trigger key is called ( by default), VRC processes the request 225 | block that the cursor stays within. The response is displayed in a new 226 | vertically split buffer. This output buffer is reused if it's already present. 227 | 228 | By default, the display/output buffer is named '__REST_response__'. If there 229 | are multiple VRC buffers, they all share the same display buffer. To have a 230 | separate output display for each VRC buffer, |vrc_output_buffer_name| can be 231 | set in the buffer scope. 232 | 233 | ------------------------------------------------------------------------------ 234 | *VrcCurlOptions* 235 | 5.1 cUrl Options~ 236 | 237 | A recent addition to VRC is the ability to specify cUrl options. These may be 238 | specified by the VRC option |vrc_curl_opts| or declaring in the global section 239 | of a REST buffer (more at |VrcGlobalDefinitions|) and request blocks. 240 | 241 | All specified cUrl options are merged together when a cUrl command is built. 242 | For the same keys (cUrl switch) specified at different scopes, the ones of the 243 | request blocks overwrite the ones in the global section then overwrite the 244 | ones defined by |vrc_curl_opts|. 245 | 246 | For the deprecated VRC options, they can be replaced by cUrl options. For 247 | example, assuming they have been defined as follows. 248 | > 249 | let g:vrc_connect_timeout = 10 250 | let g:vrc_cookie_jar = '/path/to/cookie' 251 | let g:vrc_follow_redirects = 1 252 | let g:vrc_include_response_header = 1 253 | let g:vrc_max_time = 60 254 | let g:vrc_resolve_to_ipv4 = 1 255 | let g:vrc_ssl_secure = 1 256 | < 257 | Using cUrl options, 258 | > 259 | let g:vrc_curl_opts = { 260 | \ '--connect-timeout' : 10, 261 | \ '-b': '/path/to/cookie', 262 | \ '-c': '/path/to/cookie', 263 | \ '-L': '', 264 | \ '-i': '', 265 | \ '--max-time': 60, 266 | \ '--ipv4': '', 267 | \ '-k': '', 268 | \} 269 | < 270 | ------------------------------------------------------------------------------ 271 | *VrcGlobalDefinitions* 272 | 5.2 Global Definitions~ 273 | 274 | The global section is separated from the rest with two dashes `--` and may 275 | include a default host, optional default cUrl options (buffer scope) and 276 | optional default headers. These values are always included in each request. 277 | 278 | Each request block has to start with either two dashes indicating it uses the 279 | default host from the global section or any host only used by this block. If 280 | a 'local host' is given, it's used instead of the one specified in the global 281 | section. Additionally, a request block can specify extra cUrl options and 282 | headers. Local headers are merged with and overwrite global headers. 283 | > 284 | # Global definitions. 285 | // Default host. 286 | https://domain[:port]/... 287 | 288 | // Default (buffer scope) cUrl options. 289 | -L 290 | --connect-timeout 10 291 | 292 | // Default headers. 293 | Accept: application/json 294 | X-Header: Custom Data 295 | -- 296 | 297 | # Request block that uses default values from the global section. 298 | -- 299 | GET /some/query 300 | 301 | # Request block that specifies its own host and extra headers. 302 | // Local host. 303 | https://example.net:9200 304 | 305 | // Local cUrl opts. 306 | -k 307 | --ipv4 308 | // This cUrl option overwrites the one in the global section. 309 | --connect-timeout 30 310 | -b /path/to/cookie 311 | -c /path/to/cookie 312 | 313 | // Extra headers. 314 | Xtra-Header: Some Extra. 315 | // This header will overwrite the one in the global section. 316 | X-Header: New Data 317 | 318 | POST /service 319 | var1=value 320 | < 321 | ------------------------------------------------------------------------------ 322 | *VrcGlobalVarDecl* 323 | 5.3 Global Variable Declaration~ 324 | 325 | VRC now supports variable declarations in the global scope. These variables 326 | then can be used in the query paths. Notice: values are not url-encoded. 327 | > 328 | # Global scope. 329 | http://host 330 | 331 | // Variable declarations (value passed as is). 332 | city = Some%20City 333 | zip = 12345 334 | -- 335 | # End global scope. 336 | 337 | -- 338 | GET /city/:city 339 | 340 | -- 341 | GET /city/:city/zip/:zip 342 | < 343 | ------------------------------------------------------------------------------ 344 | *VrcSplitRequestBody* 345 | 5.4 Line-by-line Request Body~ 346 | 347 | Since version 2.3.0, the request body can be specified on a line-by-line 348 | basis. It's useful for name-value pair services. Each line of the request 349 | body is passed to cURL using `--data` or `--data-urlencode` depending on 350 | the verb. 351 | 352 | To enable, 353 | > 354 | let g:vrc_split_request_body = 1 355 | < 356 | or 357 | > 358 | let b:vrc_split_request_body = 1 359 | < 360 | Then the request body can be specified as 361 | > 362 | # 363 | # The following params in the request body will be 364 | # sent using `--data-urlencode` 365 | # 366 | http://localhost 367 | Content-Type: text/html; charset=UTF-8 368 | GET /service 369 | var1=value1 370 | var2=value2 371 | < 372 | This option won't take effect for `GET` request if the option 373 | |vrc_allow_get_request_body| is set. 374 | 375 | ------------------------------------------------------------------------------ 376 | *VrcConReqVerbs* 377 | 5.5 Consecutive Request Verbs 378 | 379 | A request block may have consecutive request verbs. The output of each request 380 | verb is appended to the output view. 381 | > 382 | http://localhost:9200 383 | PUT /test 384 | GET /test 385 | DELETE /test 386 | < 387 | ============================================================================== 388 | *VrcConfiguration* 389 | 6. CONFIGURATION~ 390 | 391 | VRC supports a few configurable variables. Each of them can have a global or 392 | buffer scope (the latter takes priority). An option can be set in '.vimrc' for 393 | the global scope by 394 | > 395 | let g:option_name = value 396 | < 397 | or in Vim for the buffer scope by 398 | > 399 | let b:option_name = value 400 | < 401 | ------------------------------------------------------------------------------ 402 | *vrc_allow_get_request_body* 403 | 404 | Allow GET request to have a request body or not. Default: 0. 405 | 406 | If this option is set, `-X GET` is used and the request body is passed to 407 | cURL as a whole using `--data`. 408 | 409 | This option is useful for such services as Elasticsearch. 410 | > 411 | # 412 | # With vrc_allow_get_request_body = 1 413 | # 414 | http://localhost:9200 415 | Content-Type: application/json 416 | 417 | GET /testindex/testtype/_search 418 | { 419 | "query": { 420 | "match": { "name": "FOO" } 421 | } 422 | } 423 | < 424 | Be careful that when this option is enabled, the request body is always sent 425 | as a whole regardless of |vrc_split_request_body|. 426 | 427 | ------------------------------------------------------------------------------ 428 | *vrc_auto_format_response_enabled* 429 | 430 | This option enables the automatic formatting of the response. It's enabled by 431 | default. To disable: 432 | > 433 | let g:vrc_auto_format_response_enabled = 0 434 | < 435 | If the response header is not present (i.e., no `-i` cUrl option), this option 436 | depends on the option |vrc_response_default_content_type|. 437 | 438 | ------------------------------------------------------------------------------ 439 | *vrc_auto_format_response_patterns* 440 | 441 | This option defines which external tools to use to auto-format the response 442 | body according to the Content-Type. 443 | 444 | The defaults are: 445 | > 446 | let s:vrc_auto_format_response_patterns = { 447 | \ 'json': 'python -m json.tool', 448 | \ 'xml': 'xmllint --format -', 449 | \} 450 | > 451 | Adjust the list by defining the global or buffer variable, like so: 452 | > 453 | let g:vrc_auto_format_response_patterns = { 454 | \ 'json': '' 455 | \ 'xml': 'tidy -xml -i -' 456 | \} 457 | < 458 | If the response header isn't present (i.e., no `-i` cUrl option), this option 459 | depends on the option |vrc_response_default_content_type|. 460 | 461 | ------------------------------------------------------------------------------ 462 | *vrc_auto_format_uhex* 463 | 464 | If set, VRC will try to transform all unicode `\uXXXX` instances in the 465 | response to the corresponding symbols. It's turned of by default. 466 | 467 | ------------------------------------------------------------------------------ 468 | *vrc_body_preprocessor* 469 | 470 | An external command to run using the request body as input, the output of 471 | which will then be used as the body for the request. Default is an empty 472 | string, which will not run any preprocessing. 473 | 474 | Example using yaml2json to convert from yaml (easier to type) to JSON, 475 | and then piping into jq minify that: 476 | > 477 | let g:vrc_body_preprocessor = 'yaml2json | jq -c .' 478 | < 479 | ------------------------------------------------------------------------------ 480 | *vrc_curl_opts* 481 | 482 | A dictionary that contains the default cUrl options. Default: not exist. 483 | > 484 | let g:vrc_curl_opts = { 485 | \ '-sS': '', <-- options without value. 486 | \ '--connect-timeout: 10, <-- single option. 487 | \ '-H': ['header1: value1', 'header2: value2'], <-- multi-option. 488 | \} 489 | < 490 | ------------------------------------------------------------------------------ 491 | *vrc_debug* 492 | 493 | This option enables the debug mode by adding the '-v' option to the 'curl' 494 | command and also `echom` the command to the Vim console. It's turned off by 495 | default. 496 | 497 | ------------------------------------------------------------------------------ 498 | *vrc_elasticsearch_support* 499 | 500 | If enabled, the data of Elasticsearch's `_bulk` API can also be specified 501 | directly in the request block instead of indirectly via an external file. 502 | It's off by default. 503 | 504 | ------------------------------------------------------------------------------ 505 | *vrc_header_content_type* 506 | 507 | This option is to set the header content type of the request. It defaults to 508 | 'application/json'. To set a different default content type, 509 | > 510 | let g:vrc_header_content_type = 'application/x-www-form-urlencoded' 511 | < 512 | It can also be set in the buffer scope by 513 | > 514 | let b:vrc_header_content_type = 'application/json; charset=utf-8' 515 | < 516 | If 'Content-Type' is specified in the request block, it overrides this 517 | setting. 518 | 519 | ------------------------------------------------------------------------------ 520 | *vrc_horizontal_split* 521 | 522 | By default, the output buffer is displayed to the right of the rest buffer 523 | (vertical split). If this option is set, the output buffer is displayed 524 | below the rest buffer. 525 | 526 | ------------------------------------------------------------------------------ 527 | vrc_keepalt 528 | 529 | By default, the current alternate file name will be changed. If this 530 | option is set, it will keep the current alternate file. 531 | 532 | ------------------------------------------------------------------------------ 533 | *vrc_output_buffer_name* 534 | 535 | This option sets the name for the output/display buffer. By default, it's set 536 | to '__REST_response__'. To assign a different name, 537 | > 538 | let g:vrc_output_buffer_name = '__NEW_NAME__' 539 | < 540 | This option is useful in working with multiple VRC buffers where each one has 541 | its own output display. For this, the option can be set in the buffer scope as 542 | > 543 | let b:vrc_output_buffer_name = '__REST_1_OUTPUT__' 544 | < 545 | ------------------------------------------------------------------------------ 546 | *vrc_response_default_content_type* 547 | 548 | This option is to set the default content type of the response. It's useful 549 | when we don't want to include the response header in the output view but 550 | still want the output to be formatted or syntax-highlighted. 551 | > 552 | let b:vrc_response_default_content_type = 'application/json' 553 | < 554 | or 555 | > 556 | let g:vrc_response_default_content_type = 'text/xml' 557 | < 558 | ------------------------------------------------------------------------------ 559 | *vrc_set_default_mapping* 560 | 561 | This option is to enable/disable the trigger key mapping. It's enabled by 562 | default. To disable the mapping, 563 | > 564 | let g:vrc_set_default_mapping = 0 565 | < 566 | Once the mapping is disabled, the request block can be executed by 567 | > 568 | :call VrcQuery() 569 | < 570 | ------------------------------------------------------------------------------ 571 | *vrc_show_command* 572 | 573 | This option enables the printing of the executed curl command in the output 574 | pane. It's disabled by default. To enable: 575 | > 576 | let g:vrc_show_command = 1 577 | < 578 | ------------------------------------------------------------------------------ 579 | *vrc_split_request_body* 580 | 581 | Determine if the request body should be processed line by line. Default: 0. 582 | 583 | If this option is set, each line of the request body is passed to cURL using 584 | either `--data` or `--data-urlencode` depending on the verb. 585 | 586 | If the verb is `GET` and the option |vrc_allow_get_request_body| is enabled, 587 | this option doesn't take effect; the request body is always sent as a whole 588 | using `--data`. 589 | 590 | ------------------------------------------------------------------------------ 591 | *vrc_syntax_highlight_response* 592 | 593 | This option enables the syntax highlighting of the response body according to 594 | the Content-Type. It's enabled by default. To disable: 595 | > 596 | let g:vrc_syntax_highlight_response = 0 597 | < 598 | If the response header isn't present (i.e., no `-i` cUrl option), this option 599 | depends on the option |vrc_response_default_content_type|. 600 | 601 | ------------------------------------------------------------------------------ 602 | *vrc_trigger* 603 | 604 | This option defines the trigger key. It's by default. To remap the key, 605 | > 606 | let g:vrc_trigger = '' 607 | < 608 | ============================================================================== 609 | *VrcTnt* 610 | 7. Tips 'n Tricks~ 611 | 612 | ------------------------------------------------------------------------------ 613 | *VrcTntDataBulk* 614 | 7.1 POST Data in Bulk~ 615 | 616 | Since v3.0, VRC supports POSTing data in bulk using in-line data or an 617 | external data file. It's helpful for such APIs as Elasticsearch's Bulk API. 618 | 619 | To use in-line data, first enable the Elasticsearch support flag. 620 | > 621 | let g:vrc_elasticsearch_support = 1 622 | < 623 | The request would look like this. 624 | > 625 | http://localhost:9200 626 | POST /testindex/_bulk 627 | { "index": { "_index": "test", "_type": "product" } } 628 | { "sku": "SKU1", "name": "Product name 1" } 629 | { "index": { "_index": "test", "_type": "product" } } 630 | { "sku": "SKU2", "name": "Product name 2" } 631 | < 632 | 633 | Using external data files doesn't need the support flag. 634 | > 635 | http://localhost:9200 636 | POST /testindex/_bulk 637 | @data.sample.json 638 | < 639 | ------------------------------------------------------------------------------ 640 | *VrcTntStxHi* 641 | 7.2 Syntax Highlighting 642 | 643 | Though VRC supports output syntax highlighting, it's based on the response 644 | Content-Type. When Content-Type is not present, the output can still be 645 | syntax-highlighted if the appropriate ftplugin is installed. To force the 646 | output highlighting based on `filetype`, place this setting in '.vimrc': 647 | > 648 | let g:vrc_output_buffer_name = '__VRC_OUTPUT.' 649 | < 650 | `filetype` can also be set in the output buffer on an ad hoc basis. 651 | > 652 | # vim: set ft=json 653 | < 654 | =============================================================================== 655 | *VrcContributors* 656 | 8. Contributors~ 657 | 658 | Thanks to the contributors (in alphabetical order of GitHub account) 659 | > 660 | @dan-silva 661 | @dflupu 662 | @iamFIREcracker 663 | @jojoyuji 664 | @korin 665 | @minhajuddin 666 | @mjakl 667 | @nathanaelkane 668 | @p1otr 669 | @rawaludin 670 | @rlisowski 671 | @sethtrain 672 | @shanesmith 673 | @tdroxler 674 | @tonyskn 675 | @torbjornvatn 676 | < 677 | =============================================================================== 678 | *VrcLicense* 679 | 9. License~ 680 | 681 | MIT 682 | 683 | vim:tw=78:ts=4:ft=help:norl: 684 | --------------------------------------------------------------------------------