Repository: nicwest/vim-http
Branch: master
Commit: 7eb78e9753c1
Files: 43
Total size: 48.8 KB
Directory structure:
gitextract_413bwcyb/
├── .circleci/
│ └── config.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── autoload/
│ ├── http.vim
│ ├── vital/
│ │ ├── _vim_http/
│ │ │ └── Data/
│ │ │ └── Base64.vim
│ │ ├── _vim_http.vim
│ │ ├── vim_http.vim
│ │ └── vim_http.vital
│ └── vital.vim
├── docker-compose.yml
├── ftdetect/
│ └── http.vim
├── plugin/
│ └── http.vim
├── syntax/
│ └── http.vim
└── test/
├── .themisrc
├── integration.vim
└── test-http-files/
├── example_in_docs.md
├── get_body_params.expected.http
├── get_body_params.http
├── get_url_params.expected.http
├── get_url_params.http
├── get_with_multiple_headers.http
├── http_1_0.expected.http
├── http_1_0.http
├── http_1_1.expected.http
├── http_1_1.http
├── http_2.http
├── patch_json.expected.http
├── patch_json.http
├── post_incomplete.http
├── post_invalid_content_length.http
├── post_json.expected.http
├── post_json.http
├── redirect.expected.http
├── redirect.http
├── redirect_follow.expected.http
├── redirect_follow.http
├── simple_get.expected.http
├── simple_get.http
└── simple_get_without_protocol.http
================================================
FILE CONTENTS
================================================
================================================
FILE: .circleci/config.yml
================================================
version: 2
jobs:
latest:
docker:
- image: thinca/vim:latest
- image: citizenstig/httpbin
steps: &steps
- run:
name: install git and curl
command: apk add --no-cache git curl
- checkout
- run:
name: test requirements
command: |
git clone https://github.com/thinca/vim-themis
git clone https://github.com/syngan/vim-vimlint /tmp/vim-vimlint
git clone https://github.com/ynkdir/vim-vimlparser /tmp/vim-vimlparser
- run:
name: test
command: |
vim-themis/bin/themis --reporter dot test
- run:
name: lint
command: |
sh /tmp/vim-vimlint/bin/vimlint.sh -l /tmp/vim-vimlint -p /tmp/vim-vimlparser -e EVL102.l:_=1 -c func_abort=1 autoload ftdetect plugin syntax
oldest:
docker:
- image: thinca/vim:v7.4.566
- image: citizenstig/httpbin
steps: *steps
workflows:
version: 2
test:
jobs:
- latest
- oldest
================================================
FILE: .gitignore
================================================
# Created by https://www.gitignore.io/api/vim
### Vim ###
# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags
vim-themis
vim-vimlint
vim-vimlparser
.vagrant
Vagrantfile
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- `g:vim_http_tempbuffer` that makes response buffers temporary
[#5](https://github.com/nicwest/vim-http/pull/5).
## [1.0.0]
### Added
- support for `PATCH` http method
- pin minimum vim version at 7.4.566, if you are using a version prior to
this you can use the version of the code with the
[pre-7.4.566](https://github.com/nicwest/vim-http/tree/pre-7.4.566) tag
## [0.0.1] - 2017-08-21
### Added
- This CHANGELOG file!
- g:vim_http_split_vertically to controll split direction
[Unreleased]: https://github.com/nicwest/vim-http/compare/1.0.0...HEAD
[1.0.0]: https://github.com/nicwest/vim-http/compare/0.0.1...1.0.0
[0.0.1]: https://github.com/nicwest/vim-http/compare/TAIL...0.0.1
================================================
FILE: CONTRIBUTING.md
================================================
Contributing
============
All issues/pull requests are welcome.
That said, things that I'm unlikely to merge:
* Deviations from the various HTTP specs
[rfc1945](https://tools.ietf.org/html/rfc1945)
[rfc7230-7235](https://tools.ietf.org/html/rfc7230)
[rfc7540](https://tools.ietf.org/html/rfc7540).
The idea is that a request should be able to be piped into a connection to a
web server (for example via telnet or openssl) and for the server to
recognise the request.
* Binding things to keyboard shortcuts by default or with some kind of global
flag. People to should be making their own decisions about where they bind
things and/or if they need to bind stuff at all. I'm happy to merge
suggestions into the README.
* New functionality without tests.
Support
-------
I aim to support versions of vim from 7.4.566
(see https://github.com/nicwest/vim-http/pull/5#issuecomment-472483160) to
whatever the latest version is.
Any changes to keep this plug-in up to date are welcome. Reasonable changes
that allow us to support older versions are also welcome but are not required.
Generally speaking I'm looking to not break things for older versions of vim
or curl, however sometimes compromises are needed. In a case where the oldest
supported version increases I'll tag the previous commit for people with older
setups.
* [<7.4.566](https://github.com/nicwest/vim-http/tree/pre-7.4.566) [#5](https://github.com/nicwest/vim-http/pull/5)
Tests
-----
The integration test use [httpbin](https://httpbin.org/), and assumes it
running locally. I use a
[docker container](https://github.com/citizen-stig/dockerhttpbin):
```
docker pull citizenstig/httpbin
docker run -d=true -p 8000:8000 citizenstig/httpbin
```
To run the tests pull the
[themis test suite](https://github.com/thinca/vim-themis)
(you don't have to install it but you can if you want). I normally just dump it
in the plugin directory.
```
git clone git@github.com:thinca/vim-themis.git
./vim-themis/bin/themis --reporter dot test
```
The test responses use a VERY hacked together parsing to match against what
can be a somewhat dynamic set of headers etc.
Basically it will match anything inside a pair of `%%`:
```
"User-Agent": "curl/%% '[123.]\+' %%"
```
Would match
```
"User-Agent": "curl/1.2.3"
```
But not
```
"User-Agent": "curl/4.5.6"
```
Or
```
"User-Agent": "curl/latest"
```
Style
-----
This project attempts to follow the
[Google Vimscript Style Guide](https://google.github.io/styleguide/vimscriptguide.xml)
================================================
FILE: LICENSE.md
================================================
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
================================================
FILE: Makefile
================================================
.PHONY: test setup httpbin
test:
vim-themis/bin/themis --reporter dot test
setup:
docker pull citizenstig/httpbin
git clone git@github.com:thinca/vim-themis.git
git clone git@github.com:syngan/vim-vimlint
git clone git@github.com:ynkdir/vim-vimlparser
httpbin:
docker run -d=true -p 8000:8000 citizenstig/httpbin
lint:
./vim-vimlint/bin/vimlint.sh -l ./vim-vimlint -p ./vim-vimlparser -e EVL102.l:_=1 -c func_abort=1 autoload ftdetect plugin syntax
================================================
FILE: README.md
================================================
[](https://circleci.com/gh/nicwest/vim-http/tree/master)
[](https://github.com/vim-jp/vital.vim)
vim-http
========
Simple wrapper over curl and http syntax highlighting.
[](https://asciinema.org/a/120707)
Usage
------
Write a raw http request
```http
GET http://httpbin.org/get HTTP/1.1
Host: httpbin.org
Accept: application/json
```
`:Http` will execute the request and display the response in a new buffer.
`:Http!` will execute the request as above and follow any redirects.
`:HttpShowCurl` displays the curl request that the plugin executes under the
hood
`:HttpShowRequest` displays the internal object representing the request
`:HttpClean` will add Host and Content-Length headers
`:HttpAuth` will prompt for authorization credentials
Configuration
-------------
`g:vim_http_clean_before_do` if set to `1` (default) will clean a request before
sending it to curl. Disable this by setting this global to `0`
`g:vim_http_additional_curl_args` can be used to provide additional arguments
to curl.
`g:vim_http_split_vertically` when set to `1` will split the window vertically
on response rather than horizontally (the default).
`g:vim_http_right_below` when set to `1` split window will be open on the
right (for vertical) or below (for horizontal).
`g:vim_http_tempbuffer` when set to `1` response buffers will overwrite each
other instead of persisting forever.
Helper Methods
--------------
`http#remove_header(header)` removes all occurances of the given header in the
current buffer.
`http#set_header(header, value)` sets the header to the given value in the
current buffer, removes duplicates
Examples for your vimrc:
```viml
function! s:set_json_header() abort
call http#set_header('Content-Type', 'application/json')
endfunction
function! s:clean_personal_stuff() abort
call http#remove_header('Cookie')
call http#remove_header('Accept')
call http#remove_header('User-Agent')
call http#remove_header('Accept-Language')
endfunction
function! s:add_compression() abort
call http#set_header('Accept-Encoding', 'deflate, gzip')
let g:vim_http_additional_curl_args = '--compressed'
endfunction
command! JSON call s:set_json_header()
command! Anon call s:clean_personal_stuff()
command! Compression call s:add_compression()
```
================================================
FILE: autoload/http.vim
================================================
let s:save_cpo = &cpo
set cpo&vim
let s:V = vital#vim_http#new()
let s:Base64 = s:V.import('Data.Base64')
function! s:new_request() abort
let l:response = {
\ 'method': 'GET',
\ 'headers': {},
\ 'uri': '',
\ 'content': '',
\ 'version': '1.1',
\ 'meta': {
\ 'follow': 0,
\ }
\ }
return l:response
endfunction
let s:pre_clean_uri_line_pattern = '^\(\a*\) \(.*\) HTTP/\([0-9.]\+\)\|$'
let s:uri_line_pattern = '^\(OPTIONS\|GET\|HEAD\|POST\|PUT\|DELETE\|TRACE\|CONNECT\|PATCH\) \(.*\) HTTP/\([0-9.]\+\)$'
let s:header_line_pattern = '^\([^:]\+\): \(.*\)$'
function! s:get_lines(buffer, count, line1, line2) abort
if a:count == -1
return getbufline(a:buffer, 0, '$')
else
return getbufline(a:buffer, a:line1, a:line2)
endif
endfunction
function! s:parse_request(lines, pattern, follow) abort
let l:request = s:new_request()
if a:follow == 1
let l:request.meta.follow = 1
end
if len(a:lines) < 0
throw 'No lines in buffer :('
endif
let l:uri_line = a:lines[0]
let l:uri_line_matches = matchlist(l:uri_line, a:pattern)
if len(l:uri_line_matches) == 0
throw 'Unable to parse first line of request'
end
let l:request.method = l:uri_line_matches[1]
let l:request.uri = l:uri_line_matches[2]
let l:request.version = l:uri_line_matches[3]
if l:request.version =~ '2.*'
let l:request.version = '2'
end
let l:in_content = 0
let l:first_content_line = 1
for l:line in a:lines[0:]
if l:in_content == 0 && l:line =~ '^\s*$'
let l:in_content = 1
end
if l:in_content == 0
let l:header_matches = matchlist(l:line, s:header_line_pattern)
if len(l:header_matches) > 0
let l:header_key = l:header_matches[1]
let l:header_value = l:header_matches[2]
if has_key(l:request.headers, l:header_key) == 0
let l:request.headers[l:header_key] = []
endif
let l:request.headers[l:header_key] += [l:header_value]
endif
else
if l:first_content_line && l:line =~ '^\s*$'
continue
endif
if l:first_content_line
let l:first_content_line = 0
else
let l:request.content .= "\r\n"
endif
let l:request.content .= l:line
endif
endfor
return l:request
endfunction
function! s:in_curl_format(request) abort
let l:curl_fmt = 'curl%s%s%s%s %s'
let l:flags = ' -si'
if a:request.meta.follow == 1
let l:flags = l:flags . 'L'
endif
let l:flags = l:flags.' '.g:vim_http_additional_curl_args
let l:flags = l:flags.' --http'.a:request.version
let l:method = printf(' -X %s', a:request.method)
let l:url = shellescape(a:request.uri)
let l:headers = ''
for [l:header_key, l:header_value_list] in items(a:request.headers)
for l:header_value in l:header_value_list
let l:headers = l:headers .
\ ' -H "' .
\ printf('%s: %s', l:header_key, l:header_value) .
\ '"'
endfor
endfor
let l:data = ''
if len(a:request.content)
let l:data = ' -d ' . substitute(shellescape(a:request.content), '\\\n', "\n", 'g')
endif
let l:curl_request = printf(l:curl_fmt, l:flags, l:method, l:headers, l:data, l:url)
return l:curl_request
endfunction
function! s:lines_with_header(header) abort
let l:linenr = 1
let l:lines = []
while l:linenr < line('$')
let l:line = getline(l:linenr)
if l:line =~ '^\s*$'
break
endif
if l:line =~ "^".a:header.":"
call add(l:lines, l:linenr)
end
let l:linenr = l:linenr + 1
endwhile
return l:lines
endfunction
function! s:remove_header(header) abort
let l:offset = 0
for l:linenr in s:lines_with_header(a:header)
let l:target = l:linenr - l:offset
exe l:target."delete _"
let l:offset = l:offset + 1
endfor
endfunction
function! s:new_response_buffer(request_buffer, response) abort
let l:request_buffer_name = bufname(a:request_buffer)
let l:buffer_name = fnamemodify(l:request_buffer_name, ":r") . '.response.' . localtime() . '.http'
if g:vim_http_tempbuffer
for l:win in range(1, winnr('$'))
if getwinvar(l:win, 'vim_http_tempbuffer')
execute l:win . 'windo close'
endif
endfor
endif
let l:rightbelow = g:vim_http_right_below ? 'rightbelow ' : ''
let l:keepalt = g:vim_http_tempbuffer ? 'keepalt ' : ''
let l:vert = g:vim_http_split_vertically ? 'vert ' : ''
execute l:keepalt . l:rightbelow . l:vert . 'new ' . l:buffer_name
set ft=http
if g:vim_http_tempbuffer
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nonumber
let w:vim_http_tempbuffer = 1
endif
let l:response_lines = split(a:response, "\\(\r\n\\|\n\\)")
call append(0, l:response_lines)
norm! G"_ddgg
endfunction
function! http#do_buffer(follow) abort
let l:bang = a:follow ? '!' : ''
call http#do(l:bang, -1, 0, 0)
endfunction
function! http#do(bang, count, line1, line2) abort
let l:follow = a:bang == '!' ? 1 : 0
if g:vim_http_clean_before_do && a:count == -1
call http#clean()
end
let l:buffer = bufnr('')
let l:lines = s:get_lines(l:buffer, a:count, a:line1, a:line2)
let l:request = s:parse_request(l:lines, s:uri_line_pattern, l:follow)
let l:curl = s:in_curl_format(l:request)
let l:response = system(l:curl)
call s:new_response_buffer(l:buffer, l:response)
endfunction
function! http#show_curl(bang, count, line1, line2) abort
let l:follow = a:bang == '!' ? 1 : 0
let l:buffer = bufnr('')
let l:lines = s:get_lines(l:buffer, a:count, a:line1, a:line2)
let l:request = s:parse_request(l:lines, s:uri_line_pattern, l:follow)
let l:curl = s:in_curl_format(l:request)
echo l:curl
endfunction
function! http#show_request(bang, count, line1, line2) abort
let l:follow = a:bang == '!' ? 1 : 0
let l:buffer = bufnr('')
let l:lines = s:get_lines(l:buffer, a:count, a:line1, a:line2)
let l:request = s:parse_request(l:lines, s:uri_line_pattern, l:follow)
echo l:request
endfunction
function! http#clean() abort
let l:buffer = bufnr('')
let l:lines = s:get_lines(l:buffer, -1, 0, 0)
let l:request = s:parse_request(l:lines, s:pre_clean_uri_line_pattern, 0)
" when the http proto is > 1.0 make sure we are adding a host header
if index(['1.1', '2'], l:request.version) != -1 && !has_key(l:request.headers, 'Host')
let l:matches = matchlist(l:request.uri, '^\([^:]\+://\)\?\([^/]\+\)')
let l:host = l:matches[2]
if len(l:host)
call append(1, 'Host: ' . l:host)
endif
endif
if l:request.version == ''
call setline('.', getline('.') . ' HTTP/1.1')
endif
let l:content_length = len(l:request.content)
" when we have a Content-Length header and it doesn't match the actual
" content length
if l:content_length && has_key(l:request.headers, 'Content-Length')
if string(l:content_length) != l:request.headers['Content-Length'][-1]
let l:correct = input("correct Content-Length header? [Y]/N:")
if len(l:correct) == 0 || tolower(l:correct) != "n"
call remove(l:request.headers, 'Content-Length')
call s:remove_header('Content-Length')
endif
endif
endif
" when we are sending content we should add a header for the content length
" curl is going to do this for us anyway, but it's good to be explicit
if l:content_length && !has_key(l:request.headers, 'Content-Length')
call append(1 + len(l:request.headers), 'Content-Length: ' . l:content_length)
endif
endfunction
function! http#auth() abort
let l:buffer = bufnr('')
let l:lines = s:get_lines(l:buffer, -1, 0, 0)
let l:request = s:parse_request(l:lines, s:uri_line_pattern, 0)
let l:method = input('method [Basic]: ')
if len(l:method) == 0
let l:method = 'Basic'
end
let l:user = input('user: ')
let l:password = input('password: ')
let l:encoded = s:Base64.encode(l:user . ':' . l:password)
let l:header = 'Authorization: ' . l:method . ' ' . l:encoded
call append(1 + len(l:request.headers), l:header)
endfunction
function! http#remove_header(header) abort
call s:remove_header(a:header)
endfunction
function! http#set_header(header, value) abort
call s:remove_header(a:header)
let l:buffer = bufnr('')
let l:lines = s:get_lines(l:buffer, -1, 0, 0)
let l:request = s:parse_request(l:lines, s:uri_line_pattern, 0)
call append(1 + len(l:request.headers), a:header.': '.a:value)
endfunction
" Teardown:{{{1
let &cpo = s:save_cpo
================================================
FILE: autoload/vital/_vim_http/Data/Base64.vim
================================================
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
if v:version > 703 || v:version == 703 && has('patch1170')
function! vital#_vim_http#Data#Base64#import() abort
return map({'decode': '', 'encodebin': '', 'encode': ''}, 'function("s:" . v:key)')
endfunction
else
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_vim_http#Data#Base64#import() abort', printf("return map({'decode': '', 'encodebin': '', 'encode': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
endif
" ___vital___
" Utilities for Base64.
let s:save_cpo = &cpo
set cpo&vim
function! s:encode(data) abort
let b64 = s:_b64encode(s:_str2bytes(a:data), s:standard_table, '=')
return join(b64, '')
endfunction
function! s:encodebin(data) abort
let b64 = s:_b64encode(s:_binstr2bytes(a:data), s:standard_table, '=')
return join(b64, '')
endfunction
function! s:decode(data) abort
let bytes = s:_b64decode(split(a:data, '\zs'), s:standard_table, '=')
return s:_bytes2str(bytes)
endfunction
let s:standard_table = [
\ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
\ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
\ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
\ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/']
let s:urlsafe_table = [
\ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
\ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
\ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
\ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','-','_']
function! s:_b64encode(bytes, table, pad) abort
let b64 = []
for i in range(0, len(a:bytes) - 1, 3)
let n = a:bytes[i] * 0x10000
\ + get(a:bytes, i + 1, 0) * 0x100
\ + get(a:bytes, i + 2, 0)
call add(b64, a:table[n / 0x40000])
call add(b64, a:table[n / 0x1000 % 0x40])
call add(b64, a:table[n / 0x40 % 0x40])
call add(b64, a:table[n % 0x40])
endfor
if len(a:bytes) % 3 == 1
let b64[-1] = a:pad
let b64[-2] = a:pad
endif
if len(a:bytes) % 3 == 2
let b64[-1] = a:pad
endif
return b64
endfunction
function! s:_b64decode(b64, table, pad) abort
let a2i = {}
for i in range(len(a:table))
let a2i[a:table[i]] = i
endfor
let bytes = []
for i in range(0, len(a:b64) - 1, 4)
let n = a2i[a:b64[i]] * 0x40000
\ + a2i[a:b64[i + 1]] * 0x1000
\ + (a:b64[i + 2] == a:pad ? 0 : a2i[a:b64[i + 2]]) * 0x40
\ + (a:b64[i + 3] == a:pad ? 0 : a2i[a:b64[i + 3]])
call add(bytes, n / 0x10000)
call add(bytes, n / 0x100 % 0x100)
call add(bytes, n % 0x100)
endfor
if a:b64[-1] == a:pad
unlet a:b64[-1]
endif
if a:b64[-2] == a:pad
unlet a:b64[-1]
endif
return bytes
endfunction
function! s:_binstr2bytes(str) abort
return map(range(len(a:str)/2), 'eval("0x".a:str[v:val*2 : v:val*2+1])')
endfunction
function! s:_str2bytes(str) abort
return map(range(len(a:str)), 'char2nr(a:str[v:val])')
endfunction
function! s:_bytes2str(bytes) abort
return eval('"' . join(map(copy(a:bytes), 'printf(''\x%02x'', v:val)'), '') . '"')
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo
" vim:set et ts=2 sts=2 sw=2 tw=0:
================================================
FILE: autoload/vital/_vim_http.vim
================================================
let s:_plugin_name = expand('<sfile>:t:r')
function! vital#{s:_plugin_name}#new() abort
return vital#{s:_plugin_name[1:]}#new()
endfunction
================================================
FILE: autoload/vital/vim_http.vim
================================================
let s:plugin_name = expand('<sfile>:t:r')
let s:vital_base_dir = expand('<sfile>:h')
let s:project_root = expand('<sfile>:h:h:h')
let s:is_vital_vim = s:plugin_name is# 'vital'
let s:loaded = {}
let s:cache_sid = {}
" function() wrapper
if v:version > 703 || v:version == 703 && has('patch1170')
function! s:_function(fstr) abort
return function(a:fstr)
endfunction
else
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
let s:_s = '<SNR>' . s:_SID() . '_'
function! s:_function(fstr) abort
return function(substitute(a:fstr, 's:', s:_s, 'g'))
endfunction
endif
function! vital#{s:plugin_name}#new() abort
return s:new(s:plugin_name)
endfunction
function! vital#{s:plugin_name}#import(...) abort
if !exists('s:V')
let s:V = s:new(s:plugin_name)
endif
return call(s:V.import, a:000, s:V)
endfunction
let s:Vital = {}
function! s:new(plugin_name) abort
let base = deepcopy(s:Vital)
let base._plugin_name = a:plugin_name
return base
endfunction
function! s:vital_files() abort
if !exists('s:vital_files')
let s:vital_files = map(
\ s:is_vital_vim ? s:_global_vital_files() : s:_self_vital_files(),
\ 'fnamemodify(v:val, ":p:gs?[\\\\/]?/?")')
endif
return copy(s:vital_files)
endfunction
let s:Vital.vital_files = s:_function('s:vital_files')
function! s:import(name, ...) abort dict
let target = {}
let functions = []
for a in a:000
if type(a) == type({})
let target = a
elseif type(a) == type([])
let functions = a
endif
unlet a
endfor
let module = self._import(a:name)
if empty(functions)
call extend(target, module, 'keep')
else
for f in functions
if has_key(module, f) && !has_key(target, f)
let target[f] = module[f]
endif
endfor
endif
return target
endfunction
let s:Vital.import = s:_function('s:import')
function! s:load(...) abort dict
for arg in a:000
let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg]
let target = split(join(as, ''), '\W\+')
let dict = self
let dict_type = type({})
while !empty(target)
let ns = remove(target, 0)
if !has_key(dict, ns)
let dict[ns] = {}
endif
if type(dict[ns]) == dict_type
let dict = dict[ns]
else
unlet dict
break
endif
endwhile
if exists('dict')
call extend(dict, self._import(name))
endif
unlet arg
endfor
return self
endfunction
let s:Vital.load = s:_function('s:load')
function! s:unload() abort dict
let s:loaded = {}
let s:cache_sid = {}
unlet! s:vital_files
endfunction
let s:Vital.unload = s:_function('s:unload')
function! s:exists(name) abort dict
if a:name !~# '\v^\u\w*%(\.\u\w*)*$'
throw 'vital: Invalid module name: ' . a:name
endif
return s:_module_path(a:name) isnot# ''
endfunction
let s:Vital.exists = s:_function('s:exists')
function! s:search(pattern) abort dict
let paths = s:_extract_files(a:pattern, self.vital_files())
let modules = sort(map(paths, 's:_file2module(v:val)'))
return s:_uniq(modules)
endfunction
let s:Vital.search = s:_function('s:search')
function! s:plugin_name() abort dict
return self._plugin_name
endfunction
let s:Vital.plugin_name = s:_function('s:plugin_name')
function! s:_self_vital_files() abort
let builtin = printf('%s/__%s__/', s:vital_base_dir, s:plugin_name)
let installed = printf('%s/_%s/', s:vital_base_dir, s:plugin_name)
let base = builtin . ',' . installed
return split(globpath(base, '**/*.vim', 1), "\n")
endfunction
function! s:_global_vital_files() abort
let pattern = 'autoload/vital/__*__/**/*.vim'
return split(globpath(&runtimepath, pattern, 1), "\n")
endfunction
function! s:_extract_files(pattern, files) abort
let tr = {'.': '/', '*': '[^/]*', '**': '.*'}
let target = substitute(a:pattern, '\.\|\*\*\?', '\=tr[submatch(0)]', 'g')
let regexp = printf('autoload/vital/[^/]\+/%s.vim$', target)
return filter(a:files, 'v:val =~# regexp')
endfunction
function! s:_file2module(file) abort
let filename = fnamemodify(a:file, ':p:gs?[\\/]?/?')
let tail = matchstr(filename, 'autoload/vital/_\w\+/\zs.*\ze\.vim$')
return join(split(tail, '[\\/]\+'), '.')
endfunction
" @param {string} name e.g. Data.List
function! s:_import(name) abort dict
if has_key(s:loaded, a:name)
return copy(s:loaded[a:name])
endif
let module = self._get_module(a:name)
if has_key(module, '_vital_created')
call module._vital_created(module)
endif
let export_module = filter(copy(module), 'v:key =~# "^\\a"')
" Cache module before calling module.vital_loaded() to avoid cyclic
" dependences but remove the cache if module._vital_loaded() fails.
" let s:loaded[a:name] = export_module
let s:loaded[a:name] = export_module
if has_key(module, '_vital_loaded')
try
call module._vital_loaded(vital#{s:plugin_name}#new())
catch
unlet s:loaded[a:name]
throw 'vital: fail to call ._vital_loaded(): ' . v:exception
endtry
endif
return copy(s:loaded[a:name])
endfunction
let s:Vital._import = s:_function('s:_import')
" s:_get_module() returns module object wihch has all script local functions.
function! s:_get_module(name) abort dict
let funcname = s:_import_func_name(self.plugin_name(), a:name)
if s:_exists_autoload_func_with_source(funcname)
return call(funcname, [])
else
return s:_get_builtin_module(a:name)
endif
endfunction
function! s:_get_builtin_module(name) abort
return s:sid2sfuncs(s:_module_sid(a:name))
endfunction
if s:is_vital_vim
" For vital.vim, we can use s:_get_builtin_module directly
let s:Vital._get_module = s:_function('s:_get_builtin_module')
else
let s:Vital._get_module = s:_function('s:_get_module')
endif
function! s:_import_func_name(plugin_name, module_name) abort
return printf('vital#_%s#%s#import', a:plugin_name, s:_dot_to_sharp(a:module_name))
endfunction
function! s:_module_sid(name) abort
let path = s:_module_path(a:name)
if !filereadable(path)
throw 'vital: module not found: ' . a:name
endif
let vital_dir = s:is_vital_vim ? '__\w\+__' : printf('_\{1,2}%s\%%(__\)\?', s:plugin_name)
let base = join([vital_dir, ''], '[/\\]\+')
let p = base . substitute('' . a:name, '\.', '[/\\\\]\\+', 'g')
let sid = s:_sid(path, p)
if !sid
call s:_source(path)
let sid = s:_sid(path, p)
if !sid
throw printf('vital: cannot get <SID> from path: %s', path)
endif
endif
return sid
endfunction
function! s:_module_path(name) abort
return get(s:_extract_files(a:name, s:vital_files()), 0, '')
endfunction
function! s:_module_sid_base_dir() abort
return s:is_vital_vim ? &rtp : s:project_root
endfunction
function! s:_dot_to_sharp(name) abort
return substitute(a:name, '\.', '#', 'g')
endfunction
" It will sources autoload file if a given func is not already defined.
function! s:_exists_autoload_func_with_source(funcname) abort
if exists('*' . a:funcname)
" Return true if a given func is already defined
return 1
endif
" source a file which may include a given func definition and try again.
let path = 'autoload/' . substitute(substitute(a:funcname, '#[^#]*$', '.vim', ''), '#', '/', 'g')
call s:_runtime(path)
return exists('*' . a:funcname)
endfunction
function! s:_runtime(path) abort
execute 'runtime' fnameescape(a:path)
endfunction
function! s:_source(path) abort
execute 'source' fnameescape(a:path)
endfunction
" @vimlint(EVL102, 1, l:_)
" @vimlint(EVL102, 1, l:__)
function! s:_sid(path, filter_pattern) abort
let unified_path = s:_unify_path(a:path)
if has_key(s:cache_sid, unified_path)
return s:cache_sid[unified_path]
endif
for line in filter(split(s:_redir(':scriptnames'), "\n"), 'v:val =~# a:filter_pattern')
let [_, sid, path; __] = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$')
if s:_unify_path(path) is# unified_path
let s:cache_sid[unified_path] = sid
return s:cache_sid[unified_path]
endif
endfor
return 0
endfunction
function! s:_redir(cmd) abort
let [save_verbose, save_verbosefile] = [&verbose, &verbosefile]
set verbose=0 verbosefile=
redir => res
silent! execute a:cmd
redir END
let [&verbose, &verbosefile] = [save_verbose, save_verbosefile]
return res
endfunction
if filereadable(expand('<sfile>:r') . '.VIM') " is case-insensitive or not
let s:_unify_path_cache = {}
" resolve() is slow, so we cache results.
" Note: On windows, vim can't expand path names from 8.3 formats.
" So if getting full path via <sfile> and $HOME was set as 8.3 format,
" vital load duplicated scripts. Below's :~ avoid this issue.
function! s:_unify_path(path) abort
if has_key(s:_unify_path_cache, a:path)
return s:_unify_path_cache[a:path]
endif
let value = tolower(fnamemodify(resolve(fnamemodify(
\ a:path, ':p')), ':~:gs?[\\/]?/?'))
let s:_unify_path_cache[a:path] = value
return value
endfunction
else
function! s:_unify_path(path) abort
return resolve(fnamemodify(a:path, ':p:gs?[\\/]?/?'))
endfunction
endif
" copied and modified from Vim.ScriptLocal
let s:SNR = join(map(range(len("\<SNR>")), '"[\\x" . printf("%0x", char2nr("\<SNR>"[v:val])) . "]"'), '')
function! s:sid2sfuncs(sid) abort
let fs = split(s:_redir(printf(':function /^%s%s_', s:SNR, a:sid)), "\n")
let r = {}
let pattern = printf('\m^function\s<SNR>%d_\zs\w\{-}\ze(', a:sid)
for fname in map(fs, 'matchstr(v:val, pattern)')
let r[fname] = function(s:_sfuncname(a:sid, fname))
endfor
return r
endfunction
"" Return funcname of script local functions with SID
function! s:_sfuncname(sid, funcname) abort
return printf('<SNR>%s_%s', a:sid, a:funcname)
endfunction
if exists('*uniq')
function! s:_uniq(list) abort
return uniq(a:list)
endfunction
else
function! s:_uniq(list) abort
let i = len(a:list) - 1
while 0 < i
if a:list[i] ==# a:list[i - 1]
call remove(a:list, i)
endif
let i -= 1
endwhile
return a:list
endfunction
endif
================================================
FILE: autoload/vital/vim_http.vital
================================================
vim_http
f07284453d294e2159424aa76b9b902a5b25d5ae
Data.Base64
================================================
FILE: autoload/vital.vim
================================================
function! vital#of(name) abort
let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital', 1)
let file = split(files, "\n")
if empty(file)
throw 'vital: version file not found: ' . a:name
endif
let ver = readfile(file[0], 'b')
if empty(ver)
throw 'vital: invalid version file: ' . a:name
endif
return vital#_{substitute(ver[0], '\W', '', 'g')}#new()
endfunction
================================================
FILE: docker-compose.yml
================================================
version: "3.4"
services:
httpbin:
image: citizenstig/httpbin
oldest:
image: thinca/vim:v7.4.566
volumes:
- .:/root/.vim
latest:
image: thinca/vim:latest
volumes:
- .:/root/.vim
================================================
FILE: ftdetect/http.vim
================================================
augroup filetypedetect
au! BufRead,BufNewFile *.http setlocal filetype=http fileformat=dos
augroup END
================================================
FILE: plugin/http.vim
================================================
if !exists('g:vim_http_clean_before_do')
let g:vim_http_clean_before_do = 1
endif
if !exists('g:vim_http_additional_curl_args')
let g:vim_http_additional_curl_args = ''
endif
if !exists('g:vim_http_split_vertically')
let g:vim_http_split_vertically = 0
endif
if !exists('g:vim_http_right_below')
let g:vim_http_right_below = 0
endif
if !exists('g:vim_http_tempbuffer')
let g:vim_http_tempbuffer = 0
endif
command! -bang -range Http call http#do('<bang>', '<count>', '<line1>', '<line2>')
command! -bang -range HttpShowCurl call http#show_curl('<bang>', '<count>', '<line1>', '<line2>')
command! -bang -range HttpShowRequest call http#show_request('<bang>', '<count>', '<line1>', '<line2>')
command! HttpClean call http#clean()
command! HttpAuth call http#auth()
================================================
FILE: syntax/http.vim
================================================
if version < 600
syntax clear
elseif exists("b:current_syntax")
finish
endif
let s:cpo_save = &cpo
set cpo&vim
syn keyword httpMethod OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT PATCH contained
syn match httpProto 'HTTP/[0-9.]\+' contained
syn match httpStatusCode '[0-9]\{3\}' contained
syn match httpStatus '[0-9]\{3\} .*$' contained contains=httpStatusCode
syn match httpHeaderKey '^[A-Z][A-Za-z0-9\-]*:' contained
syn match httpURILine '^\(OPTIONS\|GET\|HEAD\|POST\|PUT\|DELETE\|TRACE\|CONNECT\|PATCH\)\( .*\)\?\(HTTP/[0-9.]\+\)\?$' contains=httpMethod,httpProto contained
syn match httpResponseLine '^HTTP/[0-9.]\+ [0-9]\{3\}.*$' contains=httpProto,httpStatus contained
syn match httpHeaderLine '^[A-Z][A-Za-z0-9\-]*: .*$' contains=httpHeaderKey contained
syn region httpHeader start='^\(OPTIONS\|GET\|HEAD\|POST\|PUT\|DELETE\|TRACE\|CONNECT\|PATCH\)\( .*\)\?\(HTTP/[0-9.]\+\)\?$' end='\n\s*\n' contains=httpURILine,httpHeaderLine
syn region httpHeader start='^HTTP/[0-9.]\+ [0-9]\{3\}.*$' end='\n\s*\n' contains=httpResponseLine,httpHeaderLine
hi link httpMethod Type
hi link httpProto Statement
hi link httpHeaderKey Identifier
hi link httpStatus String
hi link httpStatusCode Number
let b:current_syntax = 'http'
let &cpo = s:cpo_save
unlet s:cpo_save
================================================
FILE: test/.themisrc
================================================
let g:http_test_files = expand('<sfile>:p:h') . '/test-http-files/'
call themis#log('test_files_location: ' . g:http_test_files)
================================================
FILE: test/integration.vim
================================================
let s:suite = themis#suite('integration')
let s:assert = themis#helper('assert')
" Setup: {{{1
let s:original_buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)')
function! s:suite.before_each()
let l:current_buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)')
let l:buffers_to_wipe = filter(copy(l:current_buffers), 'index(s:original_buffers, v:val) == -1')
for l:buffer in l:buffers_to_wipe
execute 'bw!' . l:buffer
endfor
endfunction
function! s:load_request_expected(name) abort
let l:request_path = g:http_test_files . a:name . '.http'
execute 'edit! ' . l:request_path
endfunction
function! s:load_request_expected_explicit(name) abort
let l:request_path = g:http_test_files . a:name
execute 'edit! ' . l:request_path
endfunction
function s:assert_response_explicit(filepath, name) abort
let l:expected_path = g:http_test_files . a:filepath
let l:expected = readfile(l:expected_path)
let l:lines = getbufline(a:name, 0, '$')
let l:bad = 0
let l:lnr = 0
for l:expect in l:expected
let l:line = l:lines[l:lnr]
if l:expect =~ '.*%%[^%]\+%%.*'
let l:patterns = split(l:expect, '%%')
let l:in_pattern = 0
let l:line_index = 0
let l:pattern_nr = 0
for l:pattern in l:patterns
if l:in_pattern
let l:p = '\(' . substitute(l:pattern, '^\s*\([''"]\)\(.*\)\1\s*$', '\2', '') . '\)'
let l:pp = l:p
if len(l:patterns) > l:pattern_nr+1
let l:pp = l:p . l:patterns[l:pattern_nr+1]
endif
if l:line[l:line_index :] !~ l:pp
let l:bad = 1
break
endif
let l:line_index += len(substitute(l:line[l:line_index :], l:pp, '\1', ''))
let l:in_pattern = 0
else
if l:pattern != l:line[l:line_index : l:line_index + len(l:pattern)-1]
let l:bad = 1
break
end
let l:line_index += len(l:pattern)
let l:in_pattern = 1
endif
let l:pattern_nr += 1
endfor
else
if l:line != l:expect
let l:bad = 1
endif
endif
if l:bad == 1
break
end
let l:lnr += 1
endfor
if l:bad
let l:msg = [printf('Got response for %s.http:', a:name)]
let l:msg = l:msg + [''] + l:lines + ['', 'Expected:'] + l:expected
throw themis#failure(msg)
endif
endfunction
function s:assert_response(name) abort
call s:assert_response_explicit(a:name . '.expected.http', a:name . '.response.*.http')
endfunction
function! s:command_with_input(cmd, input)
exe 'normal :' . a:cmd .'
'.join(a:input, '
').'
'
endfunction
" }}}
" GET: {{{1
function! s:suite.simple_get()
call s:load_request_expected('simple_get')
Http
call s:assert_response('simple_get')
endfunction
function! s:suite.get_with_url_params()
call s:load_request_expected('get_url_params')
Http
call s:assert_response('get_url_params')
endfunction
function! s:suite.get_with_body_params()
call s:load_request_expected('get_body_params')
Http
call s:assert_response('get_body_params')
endfunction
function! s:suite.http_1_0()
call s:load_request_expected('http_1_0')
Http!
call s:assert_response('http_1_0')
endfunction
function! s:suite.http_1_1()
call s:load_request_expected('http_1_1')
Http!
call s:assert_response('http_1_1')
endfunction
function! s:suite.redirect()
call s:load_request_expected('redirect')
Http
call s:assert_response('redirect')
endfunction
function! s:suite.redirect_with_follow()
call s:load_request_expected('redirect_follow')
Http!
call s:assert_response('redirect_follow')
endfunction
" }}}
" POST :{{{1
function! s:suite.post_json()
call s:load_request_expected('post_json')
Http!
call s:assert_response('post_json')
endfunction
" }}}
" PATCH :{{{1
function! s:suite.patch_json()
call s:load_request_expected('patch_json')
Http!
call s:assert_response('patch_json')
endfunction
" }}}
" Clean :{{{1
function! s:suite.clean()
call s:load_request_expected('post_incomplete')
HttpClean
let l:contents = getline(0, '$')
let l:expected = ['POST http://localhost:8000/post HTTP/1.1',
\ 'Host: localhost:8000',
\ 'Content-Length: 43',
\ 'Content-Type: application/json',
\ '',
\ '',
\ '{',
\ ' "foo": "bar",',
\ ' "lol": "beans"',
\ '}',
\ ]
call s:assert.equal(l:contents, l:expected)
endfunction
function! s:suite.clean_adds_host_on_http2_requests()
call s:load_request_expected('http_2')
HttpClean
let l:contents = getline(0, '$')
let l:expected = ['GET http://localhost:8000/get HTTP/2',
\ 'Host: localhost:8000',
\ ]
call s:assert.equal(l:contents, l:expected)
endfunction
function! s:suite.clean_replaces_invalid_content_length_header()
call s:load_request_expected('post_invalid_content_length')
call s:command_with_input('HttpClean', ['Y'])
let l:contents = getline(0, '$')
let l:expected = ['POST http://localhost:8000/post HTTP/1.1',
\ 'Host: localhost:8000',
\ 'Content-Type: application/json',
\ 'Content-Length: 43',
\ '',
\ '',
\ '{',
\ ' "foo": "bar",',
\ ' "lol": "beans"',
\ '}',
\ ]
call s:assert.equal(l:contents, l:expected)
endfunction
function! s:suite.clean_appends_protocol()
call s:load_request_expected('simple_get_without_protocol')
call s:command_with_input('HttpClean', ['Y'])
let l:contents = getline(0, '$')
let l:expected = ['GET http://localhost:8000/get HTTP/1.1']
call s:assert.equal(l:contents, l:expected)
endfunction
" }}}
" Auth: {{{1
function! s:suite.auth()
call s:load_request_expected('simple_get')
call s:command_with_input('HttpAuth', ['Bearer', 'borisjohnson', 'ijustcantwaittobeking'])
let l:contents = getline(0, '$')
let l:expected = ['GET http://localhost:8000/get HTTP/1.1',
\ 'Host: localhost:8000',
\ 'Authorization: Bearer Ym9yaXNqb2huc29uOmlqdXN0Y2FudHdhaXR0b2Jla2luZw==',
\ ]
call s:assert.equal(l:contents, l:expected)
endfunction
" }}}
" Headers: {{{1
function! s:suite.remove_header()
call s:load_request_expected('get_with_multiple_headers')
call http#remove_header('User-Agent')
let l:contents = getline(0, '$')
let l:expected = ['GET http://localhost:8000/get HTTP/1.1',
\ 'Host: localhost:8000',
\ 'Accept: text/html',
\ 'Accept-Encoding: gzip, deflate',
\ 'Accept-Language: en-US,en;q=0.5 ',
\ 'Connection: close',
\ 'Upgrade-Insecure-Requests: 1 ',
\ ]
call s:assert.equal(l:contents, l:expected)
endfunction
function! s:suite.set_header()
call s:load_request_expected('get_with_multiple_headers')
call http#set_header('User-Agent', 'qutebrowser/1.0.0')
let l:contents = getline(0, '$')
let l:expected = ['GET http://localhost:8000/get HTTP/1.1',
\ 'Host: localhost:8000',
\ 'Accept: text/html',
\ 'Accept-Encoding: gzip, deflate',
\ 'Accept-Language: en-US,en;q=0.5 ',
\ 'Connection: close',
\ 'Upgrade-Insecure-Requests: 1 ',
\ 'User-Agent: qutebrowser/1.0.0',
\ ]
call s:assert.equal(l:contents, l:expected)
endfunction
" }}}
" Misc: {{{1
function! s:suite.tempbuffer()
let l:vim_http_tempbuffer = g:vim_http_tempbuffer
let g:vim_http_tempbuffer = 1
call s:load_request_expected('simple_get')
call s:load_request_expected('get_url_params')
let l:expected = filter(range(1, bufnr('$')), 'bufexists(v:val)')
Http " test get_url_params.http
wincmd p " switch from response window back to get_url_params.http window
buffer # " switch to simple_get.http buffer
Http " test simple_get.http
wincmd c " close response window
let l:contents = filter(range(1, bufnr('$')), 'bufexists(v:val)')
let g:vim_http_tempbuffer = l:vim_http_tempbuffer
call s:assert.equal(l:contents, l:expected)
endfunction
" Range: {{{1
function! s:suite.do_with_range()
call s:load_request_expected_explicit('example_in_docs.md')
14,15Http!
call s:assert_response_explicit('simple_get.expected.http', 'example_in_docs.response.*.http')
endfunction
" vim:fdm=marker
================================================
FILE: test/test-http-files/example_in_docs.md
================================================
Imagine this was some documentation and we had an HTTP request right there in
the file. Why do I have to create a new buffer for it? That's just dumb lol!
```http
POST http://localhost:8000/post HTTP/1.1
Host: "localhost:8000"
I AM A READMEFILE HERE ME ROAR!
```
So let's execute this and see what happens:
```http
GET http://localhost:8000/get HTTP/1.1
Host: localhost:8000
```
Did it work?
```http
PATCH http://localhost:8000/patch HTTP/1.1
Host: "localhost:8000"
LOLOLOLOLOLOL
```
I hope so
Here is a picture of a cat:

================================================
FILE: test/test-http-files/get_body_params.expected.http
================================================
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%
{
"args": {},
"headers": {
"Accept": "*/*",
"Content-Length": "17",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "localhost:8000",
"User-Agent": "curl/%% '.*' %%"
},
"origin": "%% '[0-9.]\+' %%",
"url": "http://localhost:8000/get"
}
================================================
FILE: test/test-http-files/get_body_params.http
================================================
GET http://localhost:8000/get HTTP/1.1
Host: localhost:8000
foo=bar&lol=beans
================================================
FILE: test/test-http-files/get_url_params.expected.http
================================================
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%
{
"args": {
"foo": "bar",
"lol": "beans"
},
"headers": {
"Accept": "*/*",
"Host": "localhost:8000",
"User-Agent": "curl/%% '.*' %%"
},
"origin": "%% '[0-9.]\+' %%",
"url": "http://localhost:8000/get?foo=bar&lol=beans"
}
================================================
FILE: test/test-http-files/get_url_params.http
================================================
GET http://localhost:8000/get?foo=bar&lol=beans HTTP/1.1
Host: localhost:8000
================================================
FILE: test/test-http-files/get_with_multiple_headers.http
================================================
GET http://localhost:8000/get HTTP/1.1
Host: localhost:8000
Accept: text/html
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0
Accept-Language: en-US,en;q=0.5
Connection: close
User-Agent: Mozilla/4.0
Upgrade-Insecure-Requests: 1
================================================
FILE: test/test-http-files/http_1_0.expected.http
================================================
HTTP/1.0 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "localhost:8000",
"User-Agent": "curl/%% '.*' %%"
},
"origin": "%% '[0-9.]\+' %%",
"url": "http://localhost:8000/get"
}
================================================
FILE: test/test-http-files/http_1_0.http
================================================
GET http://localhost:8000/get HTTP/1.0
Host: localhost:8000
================================================
FILE: test/test-http-files/http_1_1.expected.http
================================================
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "localhost:8000",
"User-Agent": "curl/%% '.*' %%"
},
"origin": "%% '[0-9.]\+' %%",
"url": "http://localhost:8000/get"
}
================================================
FILE: test/test-http-files/http_1_1.http
================================================
GET http://localhost:8000/get HTTP/1.1
Host: localhost:8000
================================================
FILE: test/test-http-files/http_2.http
================================================
GET http://localhost:8000/get HTTP/2
================================================
FILE: test/test-http-files/patch_json.expected.http
================================================
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%
{
"args": {},
"data": "{\r\n \"foo\": \"bar\",\r\n \"lol\": \"beans\"\r\n}",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Content-Length": "43",
"Content-Type": "application/json",
"Host": "localhost:8000",
"User-Agent": "curl/%% '.*' %%"
},
"json": {
"foo": "bar",
"lol": "beans"
},
"origin": "%% '[0-9.]\+' %%",
"url": "http://localhost:8000/patch"
}
================================================
FILE: test/test-http-files/patch_json.http
================================================
PATCH http://localhost:8000/patch HTTP/1.1
Host: localhost:8000
Content-Type: application/json
Content-Length: 43
{
"foo": "bar",
"lol": "beans"
}
================================================
FILE: test/test-http-files/post_incomplete.http
================================================
POST http://localhost:8000/post HTTP/1.1
Content-Type: application/json
{
"foo": "bar",
"lol": "beans"
}
================================================
FILE: test/test-http-files/post_invalid_content_length.http
================================================
POST http://localhost:8000/post HTTP/1.1
Host: localhost:8000
Content-Length: 20
Content-Type: application/json
{
"foo": "bar",
"lol": "beans"
}
================================================
FILE: test/test-http-files/post_json.expected.http
================================================
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%
{
"args": {},
"data": "{\r\n \"foo\": \"bar\",\r\n \"lol\": \"beans\"\r\n}",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Content-Length": "43",
"Content-Type": "application/json",
"Host": "localhost:8000",
"User-Agent": "curl/%% '.*' %%"
},
"json": {
"foo": "bar",
"lol": "beans"
},
"origin": "%% '[0-9.]\+' %%",
"url": "http://localhost:8000/post"
}
================================================
FILE: test/test-http-files/post_json.http
================================================
POST http://localhost:8000/post HTTP/1.1
Host: "localhost:8000"
Content-Type: application/json
Content-Length: 43
{
"foo": "bar",
"lol": "beans"
}
================================================
FILE: test/test-http-files/redirect.expected.http
================================================
HTTP/1.1 302 FOUND
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 0
Location: http://localhost:8000/get
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
================================================
FILE: test/test-http-files/redirect.http
================================================
GET http://localhost:8000/redirect-to?url=http://localhost:8000/get HTTP/1.1
Host: localhost:8000
================================================
FILE: test/test-http-files/redirect_follow.expected.http
================================================
HTTP/1.1 302 FOUND
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 0
Location: http://localhost:8000/get
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "localhost:8000",
"User-Agent": "curl/%% '.*' %%"
},
"origin": "%% '[0-9.]\+' %%",
"url": "http://localhost:8000/get"
}
================================================
FILE: test/test-http-files/redirect_follow.http
================================================
GET http://localhost:8000/redirect-to?url=http://localhost:8000/get HTTP/1.1
Host: localhost:8000
================================================
FILE: test/test-http-files/simple_get.expected.http
================================================
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "localhost:8000",
"User-Agent": "curl/%% '.*' %%"
},
"origin": "%% '[0-9.]\+' %%",
"url": "http://localhost:8000/get"
}
================================================
FILE: test/test-http-files/simple_get.http
================================================
GET http://localhost:8000/get HTTP/1.1
Host: localhost:8000
================================================
FILE: test/test-http-files/simple_get_without_protocol.http
================================================
GET http://localhost:8000/get
gitextract_413bwcyb/
├── .circleci/
│ └── config.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── autoload/
│ ├── http.vim
│ ├── vital/
│ │ ├── _vim_http/
│ │ │ └── Data/
│ │ │ └── Base64.vim
│ │ ├── _vim_http.vim
│ │ ├── vim_http.vim
│ │ └── vim_http.vital
│ └── vital.vim
├── docker-compose.yml
├── ftdetect/
│ └── http.vim
├── plugin/
│ └── http.vim
├── syntax/
│ └── http.vim
└── test/
├── .themisrc
├── integration.vim
└── test-http-files/
├── example_in_docs.md
├── get_body_params.expected.http
├── get_body_params.http
├── get_url_params.expected.http
├── get_url_params.http
├── get_with_multiple_headers.http
├── http_1_0.expected.http
├── http_1_0.http
├── http_1_1.expected.http
├── http_1_1.http
├── http_2.http
├── patch_json.expected.http
├── patch_json.http
├── post_incomplete.http
├── post_invalid_content_length.http
├── post_json.expected.http
├── post_json.http
├── redirect.expected.http
├── redirect.http
├── redirect_follow.expected.http
├── redirect_follow.http
├── simple_get.expected.http
├── simple_get.http
└── simple_get_without_protocol.http
Condensed preview — 43 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (55K chars).
[
{
"path": ".circleci/config.yml",
"chars": 1034,
"preview": "version: 2\njobs:\n latest:\n docker:\n - image: thinca/vim:latest\n - image: citizenstig/httpbin\n steps: &s"
},
{
"path": ".gitignore",
"chars": 241,
"preview": "\n# Created by https://www.gitignore.io/api/vim\n\n### Vim ###\n# swap\n[._]*.s[a-w][a-z]\n[._]s[a-w][a-z]\n# session\nSession.v"
},
{
"path": "CHANGELOG.md",
"chars": 970,
"preview": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changel"
},
{
"path": "CONTRIBUTING.md",
"chars": 2531,
"preview": "Contributing\n============\n\nAll issues/pull requests are welcome.\n\nThat said, things that I'm unlikely to merge:\n\n* Devia"
},
{
"path": "LICENSE.md",
"chars": 1210,
"preview": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, c"
},
{
"path": "Makefile",
"chars": 460,
"preview": ".PHONY: test setup httpbin\n\ntest:\n\tvim-themis/bin/themis --reporter dot test\n\nsetup:\n\tdocker pull citizenstig/httpbin\n\tg"
},
{
"path": "README.md",
"chars": 2491,
"preview": "[](https://circleci.com/gh/nicwest/vim-ht"
},
{
"path": "autoload/http.vim",
"chars": 8938,
"preview": "let s:save_cpo = &cpo\nset cpo&vim\n\nlet s:V = vital#vim_http#new()\nlet s:Base64 = s:V.import('Data.Base64')\n\nfunction! s:"
},
{
"path": "autoload/vital/_vim_http/Data/Base64.vim",
"chars": 3483,
"preview": "\" ___vital___\n\" NOTE: lines between '\" ___vital___' is generated by :Vitalize.\n\" Do not mofidify the code nor insert new"
},
{
"path": "autoload/vital/_vim_http.vim",
"chars": 143,
"preview": "let s:_plugin_name = expand('<sfile>:t:r')\n\nfunction! vital#{s:_plugin_name}#new() abort\n return vital#{s:_plugin_name["
},
{
"path": "autoload/vital/vim_http.vim",
"chars": 10082,
"preview": "let s:plugin_name = expand('<sfile>:t:r')\nlet s:vital_base_dir = expand('<sfile>:h')\nlet s:project_root = expand('<sfile"
},
{
"path": "autoload/vital/vim_http.vital",
"chars": 63,
"preview": "vim_http\nf07284453d294e2159424aa76b9b902a5b25d5ae\n\nData.Base64\n"
},
{
"path": "autoload/vital.vim",
"chars": 400,
"preview": "function! vital#of(name) abort\n let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital', 1)\n let file"
},
{
"path": "docker-compose.yml",
"chars": 215,
"preview": "version: \"3.4\"\nservices:\n httpbin:\n image: citizenstig/httpbin\n oldest:\n image: thinca/vim:v7.4.566\n volumes:"
},
{
"path": "ftdetect/http.vim",
"chars": 109,
"preview": "augroup filetypedetect\n au! BufRead,BufNewFile *.http setlocal filetype=http fileformat=dos\naugroup END\n"
},
{
"path": "plugin/http.vim",
"chars": 773,
"preview": "if !exists('g:vim_http_clean_before_do')\n let g:vim_http_clean_before_do = 1\nendif\nif !exists('g:vim_http_additional_cu"
},
{
"path": "syntax/http.vim",
"chars": 1343,
"preview": "if version < 600\n syntax clear\nelseif exists(\"b:current_syntax\")\n finish\nendif\n\nlet s:cpo_save = &cpo\nset cpo&vim\n\nsyn"
},
{
"path": "test/.themisrc",
"chars": 131,
"preview": "\nlet g:http_test_files = expand('<sfile>:p:h') . '/test-http-files/'\n\ncall themis#log('test_files_location: ' . g:http_t"
},
{
"path": "test/integration.vim",
"chars": 8715,
"preview": "let s:suite = themis#suite('integration')\nlet s:assert = themis#helper('assert')\n\n\" Setup: {{{1\nlet s:original_buffers ="
},
{
"path": "test/test-http-files/example_in_docs.md",
"chars": 573,
"preview": "Imagine this was some documentation and we had an HTTP request right there in\nthe file. Why do I have to create a new bu"
},
{
"path": "test/test-http-files/get_body_params.expected.http",
"chars": 520,
"preview": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
},
{
"path": "test/test-http-files/get_body_params.http",
"chars": 85,
"preview": "GET http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\n\r\n\r\nfoo=bar&lol=beans\r\n"
},
{
"path": "test/test-http-files/get_url_params.expected.http",
"chars": 493,
"preview": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
},
{
"path": "test/test-http-files/get_url_params.http",
"chars": 80,
"preview": "GET http://localhost:8000/get?foo=bar&lol=beans HTTP/1.1\r\nHost: localhost:8000\r\n"
},
{
"path": "test/test-http-files/get_with_multiple_headers.http",
"chars": 247,
"preview": "GET http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\nAccept: text/html\r\nAccept-Encoding: gzip, deflate\r\nUser-Ag"
},
{
"path": "test/test-http-files/http_1_0.expected.http",
"chars": 431,
"preview": "HTTP/1.0 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
},
{
"path": "test/test-http-files/http_1_0.http",
"chars": 62,
"preview": "GET http://localhost:8000/get HTTP/1.0\r\nHost: localhost:8000\r\n"
},
{
"path": "test/test-http-files/http_1_1.expected.http",
"chars": 431,
"preview": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
},
{
"path": "test/test-http-files/http_1_1.http",
"chars": 62,
"preview": "GET http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\n"
},
{
"path": "test/test-http-files/http_2.http",
"chars": 38,
"preview": "GET http://localhost:8000/get HTTP/2\r\n"
},
{
"path": "test/test-http-files/patch_json.expected.http",
"chars": 671,
"preview": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
},
{
"path": "test/test-http-files/patch_json.http",
"chars": 167,
"preview": "PATCH http://localhost:8000/patch HTTP/1.1\r\nHost: localhost:8000\r\nContent-Type: application/json\r\nContent-Length: 43\r\n\r\n"
},
{
"path": "test/test-http-files/post_incomplete.http",
"chars": 123,
"preview": "POST http://localhost:8000/post HTTP/1.1\r\nContent-Type: application/json\r\n\r\n\r\n{\r\n \"foo\": \"bar\",\r\n \"lol\": \"beans\"\r\n"
},
{
"path": "test/test-http-files/post_invalid_content_length.http",
"chars": 165,
"preview": "POST http://localhost:8000/post HTTP/1.1\r\nHost: localhost:8000\r\nContent-Length: 20\r\nContent-Type: application/json\r\n\r\n\r\n"
},
{
"path": "test/test-http-files/post_json.expected.http",
"chars": 670,
"preview": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
},
{
"path": "test/test-http-files/post_json.http",
"chars": 167,
"preview": "POST http://localhost:8000/post HTTP/1.1\r\nHost: \"localhost:8000\"\r\nContent-Type: application/json\r\nContent-Length: 43\r\n\r\n"
},
{
"path": "test/test-http-files/redirect.expected.http",
"chars": 256,
"preview": "HTTP/1.1 302 FOUND\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: text/html; charset=u"
},
{
"path": "test/test-http-files/redirect.http",
"chars": 100,
"preview": "GET http://localhost:8000/redirect-to?url=http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\n"
},
{
"path": "test/test-http-files/redirect_follow.expected.http",
"chars": 687,
"preview": "HTTP/1.1 302 FOUND\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: text/html; charset=u"
},
{
"path": "test/test-http-files/redirect_follow.http",
"chars": 100,
"preview": "GET http://localhost:8000/redirect-to?url=http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\n"
},
{
"path": "test/test-http-files/simple_get.expected.http",
"chars": 431,
"preview": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
},
{
"path": "test/test-http-files/simple_get.http",
"chars": 62,
"preview": "GET http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\n"
},
{
"path": "test/test-http-files/simple_get_without_protocol.http",
"chars": 31,
"preview": "GET http://localhost:8000/get\r\n"
}
]
About this extraction
This page contains the full source code of the nicwest/vim-http GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 43 files (48.8 KB), approximately 16.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.