mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-23 07:00:04 +08:00
feat(edit): add grammar checking plugin
This commit is contained in:
parent
1fdc034d7e
commit
54b1170497
@ -102,6 +102,7 @@ function! SpaceVim#layers#edit#plugins() abort
|
||||
\ [g:_spacevim_root_dir . 'bundle/vim-surround'],
|
||||
\ [g:_spacevim_root_dir . 'bundle/vim-repeat'],
|
||||
\ [g:_spacevim_root_dir . 'bundle/vim-emoji'],
|
||||
\ [g:_spacevim_root_dir . 'bundle/vim-grammarous', {'merged' : 0}],
|
||||
\ [g:_spacevim_root_dir . 'bundle/vim-expand-region', { 'loadconf' : 1}],
|
||||
\ [g:_spacevim_root_dir . 'bundle/vim-textobj-user'],
|
||||
\ [g:_spacevim_root_dir . 'bundle/vim-textobj-indent'],
|
||||
|
5
bundle/README.md
vendored
5
bundle/README.md
vendored
@ -7,6 +7,7 @@ In `bundle/` directory, there are two kinds of plugins: forked plugins without c
|
||||
- [Changed plugin:](#changed-plugin)
|
||||
- [No changed plugins](#no-changed-plugins)
|
||||
- [`core` layer](#core-layer)
|
||||
- [`edit` layer](#edit-layer)
|
||||
- [`lang#ruby` layer](#langruby-layer)
|
||||
- [`lang#python` layer](#langpython-layer)
|
||||
- [`lang#liquid` layer](#langliquid-layer)
|
||||
@ -58,6 +59,10 @@ This plugins are changed based on a specific version of origin plugin.
|
||||
|
||||
- [nerdtree@fc85a6f07](https://github.com/preservim/nerdtree/tree/fc85a6f07c2cd694be93496ffad75be126240068)
|
||||
|
||||
### `edit` layer
|
||||
|
||||
- [rhysd/vim-grammarous@db46357](https://github.com/rhysd/vim-grammarous/tree/db46357465ce587d5325e816235b5e92415f8c05)
|
||||
|
||||
#### `lang#ruby` layer
|
||||
|
||||
- [vim-ruby@55335f261](https://github.com/vim-ruby/vim-ruby/tree/55335f2614f914b117f02995340886f409eddc02)
|
||||
|
2
bundle/vim-grammarous/.gitignore
vendored
Normal file
2
bundle/vim-grammarous/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
misc
|
||||
doc/tags
|
267
bundle/vim-grammarous/README.md
vendored
Normal file
267
bundle/vim-grammarous/README.md
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
vim-grammarous
|
||||
==============
|
||||
|
||||
vim-grammarous is a powerful grammar checker for Vim. Simply do `:GrammarousCheck` to see the powerful checking.
|
||||
This plugin automatically downloads [LanguageTool](https://www.languagetool.org/), which requires Java 8+.
|
||||
|
||||
This plugin can use job feature on Vim 8.0.27 (or later) or Neovim. It enables asynchronous command execution so you don't need to
|
||||
be blocked until the check has been done on Vim8+ or Neovim.
|
||||
|
||||
![demo screen cast](https://github.com/rhysd/ss/blob/master/vim-grammarous/demo.gif?raw=true)
|
||||
|
||||
|
||||
## Commands
|
||||
|
||||
```
|
||||
:[range]GrammarousCheck [--lang={lang}] [--(no-)preview] [--(no-)comments-only]
|
||||
```
|
||||
|
||||
Execute the grammar checker for current buffer (when `[range]` is specified, the target is a text in the range).
|
||||
|
||||
1. It makes LanguageTool check grammar (It takes a while)
|
||||
2. It highlights the locations of detected grammar errors
|
||||
3. When you move the cursor to a location of an error, it automatically shows the error with the information window (named `[Grammarous]`).
|
||||
|
||||
Please do `:GrammarousCheck --help` to show more detail about the command.
|
||||
|
||||
```
|
||||
:GrammarousReset
|
||||
```
|
||||
|
||||
Reset the current check.
|
||||
|
||||
|
||||
## Mappings
|
||||
|
||||
### Local mappings in the information window
|
||||
|
||||
You can use some mappings in the information window, which is opened to show the detail of an error when the cursor moves to the error.
|
||||
|
||||
| Mappings | Description |
|
||||
| -------- |:---------------------------------------------- |
|
||||
| `q` | Quit the info window |
|
||||
| `<CR>` | Move to the location of the error |
|
||||
| `f` | Fix the error __automatically__ |
|
||||
| `r` | Remove the error without fix |
|
||||
| `R` | Disable the grammar rule in the checked buffer |
|
||||
| `n` | Move to the next error's location |
|
||||
| `p` | Move to the previous error's location |
|
||||
| `?` | Show help of the mapping in info window |
|
||||
|
||||
### `<Plug>` mappings to execute actions anywhere
|
||||
|
||||
The above local mappings are enough to deal with grammar errors.
|
||||
|
||||
However, for a more convenient use, vim-grammarous provides the following global mappings to enable
|
||||
using all grammarous actions globally within vim. This might be beneficial, as the standard mappings
|
||||
only work within the info window, which loses focus after every action.
|
||||
|
||||
By mapping the actions listed below to your favorite shortcuts, it is possible to map all actions
|
||||
that work within the info window, to work globally within vim. This is done via `:nmap` and an example
|
||||
for a mapping would be `:nmap <F5> (grammarous-move-to-next-error)`.
|
||||
|
||||
| Mappings | Description |
|
||||
| ------------------------------------------- |:---------------------------------------------------- |
|
||||
| `<Plug>(grammarous-move-to-info-window)` | Move the cursor to the info window |
|
||||
| `<Plug>(grammarous-open-info-window)` | Open the info window for the error under the cursor |
|
||||
| `<Plug>(grammarous-reset)` | Reset the current check |
|
||||
| `<Plug>(grammarous-fixit)` | Fix the error under the cursor automatically |
|
||||
| `<Plug>(grammarous-fixall)` | Fix all the errors in a current buffer automatically |
|
||||
| `<Plug>(grammarous-close-info-window)` | Close the information window from checked buffer |
|
||||
| `<Plug>(grammarous-remove-error)` | Remove the error under the cursor |
|
||||
| `<Plug>(grammarous-disable-rule)` | Disable the grammar rule under the cursor |
|
||||
| `<Plug>(grammarous-move-to-next-error)` | Move cursor to the next error |
|
||||
| `<Plug>(grammarous-move-to-previous-error)` | Move cursor to the previous error |
|
||||
|
||||
### Operator mappings
|
||||
|
||||
Operator mapping checks grammatical errors in the extent which the text object specifies.
|
||||
This mapping is available when [vim-operator-user](https://github.com/kana/vim-operator-user) is installed.
|
||||
|
||||
| Mappings | Description |
|
||||
| ----------------------------- |:-------------------------------------- |
|
||||
| `<Plug>(operator-grammarous)` | Execute grammar check to a text object |
|
||||
|
||||
### `grammarous` unite.vim source
|
||||
|
||||
If you are [unite.vim](https://github.com/Shougo/unite.vim) user, `grammarous` unite source is available to look and search the error list incrementally.
|
||||
To the candidates of the list, you can do the actions which are the same as ones in the info window. (`fixit`, `remove error` and `disable rule`)
|
||||
Execute below command in the buffer already checked or you want to check.
|
||||
|
||||
```
|
||||
:Unite grammarous
|
||||
```
|
||||
|
||||
### `grammarous` denite.nvim source
|
||||
|
||||
For [denite.nvim](https://github.com/Shougo/denite.nvim) users, `grammarous` denite source is available. Note that the kind is currently set to `file`,
|
||||
which means that actions a user can use are limited to open(jump), preview, etc. Execute below command in the buffer already checked.
|
||||
|
||||
```
|
||||
:Denite grammarous
|
||||
```
|
||||
|
||||
## Fix examples
|
||||
|
||||
- [vim-themis](https://github.com/rhysd/vim-themis/commit/b2f838b29f47180ccee50488e01d6774a21d0c03)
|
||||
- [unite.vim](https://github.com/rhysd/unite.vim/commit/5716eac38781e7a233c98f2a3d7aee8909326791)
|
||||
- [vim-quickrun](https://github.com/rhysd/vim-quickrun/commit/236c753e0572266670d176e667054d55ad52a3f3)
|
||||
- [neosnippet.vim](https://github.com/rhysd/neosnippet/commit/c72e26e50ccf53f9d66a31fd9d70696c85c62873)
|
||||
|
||||
|
||||
## FAQ
|
||||
|
||||
### How do I check comments only in source code by default?
|
||||
|
||||
Please use `g:grammarous#default_comments_only_filetypes`.
|
||||
|
||||
For example, below setting makes grammar checker check comments only except for markdown and vim help.
|
||||
|
||||
```vim
|
||||
let g:grammarous#default_comments_only_filetypes = {
|
||||
\ '*' : 1, 'help' : 0, 'markdown' : 0,
|
||||
\ }
|
||||
```
|
||||
|
||||
### How are rules added to the default rule set?
|
||||
|
||||
Please use `g:grammarous#enabled_rules` to enable additional rules. The value is dictionary whose keys
|
||||
are a filetype (`*` means 'any') and whose values are a list of rule names.
|
||||
|
||||
For example, below setting enables `PASSIVE_VOICE` rule in all filetypes.
|
||||
|
||||
```vim
|
||||
let g:grammarous#enabled_rules = {'*' : ['PASSIVE_VOICE']}
|
||||
```
|
||||
|
||||
### Some rules annoy me.
|
||||
|
||||
Please use `g:grammarous#disabled_rules` to disable specific rules.
|
||||
|
||||
For example, below setting disables some rules for each filetype. `*` means all filetypes, `help` means vim help.
|
||||
|
||||
```vim
|
||||
let g:grammarous#disabled_rules = {
|
||||
\ '*' : ['WHITESPACE_RULE', 'EN_QUOTES'],
|
||||
\ 'help' : ['WHITESPACE_RULE', 'EN_QUOTES', 'SENTENCE_WHITESPACE', 'UPPERCASE_SENTENCE_START'],
|
||||
\ }
|
||||
```
|
||||
|
||||
The rule names are displayed in Vim command line when you disable the rule in the info window or by `<Plug>(grammarous-disable-rule)`.
|
||||
|
||||
### How are categories added to the default rule set?
|
||||
|
||||
Please use `g:grammarous#enabled_categories` to enable additional categories. The value is dictionary whose keys
|
||||
are a filetype (`*` means 'any') and whose values are a list of categories names.
|
||||
|
||||
For example, below setting enables `PASSIVE_VOICE` rule in all filetypes.
|
||||
|
||||
```vim
|
||||
let g:grammarous#enabled_categories = {'*' : ['PUNCTUATION']}
|
||||
```
|
||||
|
||||
### Some categories annoy me.
|
||||
|
||||
Please use `g:grammarous#disabled_categories` to disable specific categories.
|
||||
|
||||
For example, below setting disables some categories for each filetype. `*` means all filetypes, `help` means vim help.
|
||||
|
||||
```vim
|
||||
let g:grammarous#disabled_categories = {
|
||||
\ '*' : ['PUNCTUATION'],
|
||||
\ 'help' : ['PUNCTUATION', 'TYPOGRAPHY'],
|
||||
\ }
|
||||
```
|
||||
|
||||
The category names are displayed in Vim command line when you disable the category in the info window or by `<Plug>(grammarous-disable-category)`.
|
||||
|
||||
### How do I use this plugin with vim's spelllang?
|
||||
|
||||
Please use `g:grammarous#use_vim_spelllang`. Default 0, to enable 1.
|
||||
|
||||
### I want to use above `<Plug>` mappings only after checking.
|
||||
|
||||
`on_check` and `on_reset` are available.
|
||||
|
||||
For example, below setting defines `<C-n>` and `<C-p>` mappings as buffer-local mappings when the check has been completed.
|
||||
They are cleared when the check is reset.
|
||||
|
||||
```vim
|
||||
let g:grammarous#hooks = {}
|
||||
function! g:grammarous#hooks.on_check(errs) abort
|
||||
nmap <buffer><C-n> <Plug>(grammarous-move-to-next-error)
|
||||
nmap <buffer><C-p> <Plug>(grammarous-move-to-previous-error)
|
||||
endfunction
|
||||
|
||||
function! g:grammarous#hooks.on_reset(errs) abort
|
||||
nunmap <buffer><C-n>
|
||||
nunmap <buffer><C-p>
|
||||
endfunction
|
||||
```
|
||||
|
||||
### I want to use system global LanguageTool command
|
||||
|
||||
`g:grammarous#languagetool_cmd` is available for the purpose.
|
||||
If some command is set to `g:grammarous#languagetool_cmd` in your `.vimrc`, vim-grammarous does not install
|
||||
its own LanguageTool jar and use the command to run LanguageTool.
|
||||
|
||||
```vim
|
||||
let g:grammarous#languagetool_cmd = 'languagetool'
|
||||
```
|
||||
|
||||
### I want to see the first error in an information window soon after `:GrammarousCheck`
|
||||
|
||||
Please set `g:grammarous#show_first_error` to `1`. It opens an information window after `:GrammarousCheck` immediately when some error detected.
|
||||
|
||||
### I want to use a location list to jump among errors
|
||||
|
||||
Please set `g:grammarous#use_location_list` to `1`. It sets all grammatical errors to location list.
|
||||
This variable is set to `0` by default to avoid conflicts of location list usage with other plugins.
|
||||
|
||||
## Automatic installation
|
||||
|
||||
This plugin attempts to install [LanguageTool](https://www.languagetool.org/) using `curl` or `wget` command at first time.
|
||||
If it fails, you should install it manually. Please download zip file of LanguageTool and extract it to `path/to/vim-grammarous/misc`.
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
- Java8+ (required)
|
||||
- [vimproc.vim](https://github.com/Shougo/vimproc.vim) (optional for Vim 8.0.25 or earlier on Windows)
|
||||
- [unite.vim](https://github.com/Shougo/unite.vim) (optional)
|
||||
- [vim-operator-user](https://github.com/kana/vim-operator-user) (optional)
|
||||
|
||||
|
||||
## Future
|
||||
|
||||
- __Ignore specific regions__ : Enable to specify the region which vim-grammarous should not check. It is helpful for GFM's fenced code blocks.
|
||||
- __Incremental grammarous check__ : Check only the sentences you input while starting from entering and leaving insert mode.
|
||||
|
||||
|
||||
## Contribution
|
||||
|
||||
If you find some bugs, please report it to [issues page](https://github.com/rhysd/vim-grammarous/issues).
|
||||
Pull requests are welcome. None of them is too short.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2014 rhysd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
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 OR COPYRIGHT HOLDERS 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.
|
||||
|
798
bundle/vim-grammarous/autoload/grammarous.vim
vendored
Normal file
798
bundle/vim-grammarous/autoload/grammarous.vim
vendored
Normal file
@ -0,0 +1,798 @@
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
let s:V = vital#grammarous#new()
|
||||
let s:XML = s:V.import('Web.XML')
|
||||
let s:O = s:V.import('OptionParser')
|
||||
let s:P = s:V.import('Process')
|
||||
let s:is_cygwin = has('win32unix')
|
||||
let s:is_windows = has('win32') || has('win64')
|
||||
let s:job_is_available = has('job') && has('patch-8.0.0027')
|
||||
|
||||
let s:grammarous_root = fnamemodify(expand('<sfile>'), ':p:h:h')
|
||||
|
||||
let g:grammarous#jar_dir = get(g:, 'grammarous#jar_dir', s:grammarous_root . '/misc')
|
||||
let g:grammarous#jar_url = get(g:, 'grammarous#jar_url', 'https://www.languagetool.org/download/LanguageTool-stable.zip')
|
||||
let g:grammarous#java_cmd = get(g:, 'grammarous#java_cmd', 'java')
|
||||
let g:grammarous#default_lang = get(g:, 'grammarous#default_lang', 'en')
|
||||
let g:grammarous#use_vim_spelllang = get(g:, 'grammarous#use_vim_spelllang', 0)
|
||||
let g:grammarous#info_window_height = get(g:, 'grammarous#info_window_height', 10)
|
||||
let g:grammarous#info_win_direction = get(g:, 'grammarous#info_win_direction', 'botright')
|
||||
let g:grammarous#use_fallback_highlight = get(g:, 'grammarous#use_fallback_highlight', !exists('*matchaddpos'))
|
||||
let g:grammarous#enabled_rules = get(g:, 'grammarous#enabled_rules', {})
|
||||
let g:grammarous#disabled_rules = get(g:, 'grammarous#disabled_rules', {'*' : ['WHITESPACE_RULE', 'EN_QUOTES']})
|
||||
let g:grammarous#enabled_categories = get(g:, 'grammarous#enabled_categories', {})
|
||||
let g:grammarous#disabled_categories = get(g:, 'grammarous#disabled_categories', {})
|
||||
let g:grammarous#default_comments_only_filetypes = get(g:, 'grammarous#default_comments_only_filetypes', {'*' : 0})
|
||||
let g:grammarous#enable_spell_check = get(g:, 'grammarous#enable_spell_check', 0)
|
||||
let g:grammarous#move_to_first_error = get(g:, 'grammarous#move_to_first_error', 1)
|
||||
let g:grammarous#hooks = get(g:, 'grammarous#hooks', {})
|
||||
let g:grammarous#languagetool_cmd = get(g:, 'grammarous#languagetool_cmd', '')
|
||||
let g:grammarous#show_first_error = get(g:, 'grammarous#show_first_error', 0)
|
||||
let g:grammarous#use_location_list = get(g:, 'grammarous#use_location_list', 0)
|
||||
|
||||
highlight default link GrammarousError SpellBad
|
||||
highlight default link GrammarousInfoError ErrorMsg
|
||||
highlight default link GrammarousInfoSection Keyword
|
||||
highlight default link GrammarousInfoHelp Special
|
||||
|
||||
augroup pluging-rammarous-highlight
|
||||
autocmd ColorScheme * highlight default link GrammarousError SpellBad
|
||||
autocmd ColorScheme * highlight default link GrammarousInfoError ErrorMsg
|
||||
autocmd ColorScheme * highlight default link GrammarousInfoSection Keyword
|
||||
autocmd ColorScheme * highlight default link GrammarousInfoHelp Special
|
||||
augroup END
|
||||
|
||||
function! s:get_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\d\+_\zeget_SID$')
|
||||
endfunction
|
||||
let s:SID = s:get_SID()
|
||||
delfunction s:get_SID
|
||||
|
||||
function! grammarous#_import_vital_modules()
|
||||
return [s:XML, s:O, s:P]
|
||||
endfunction
|
||||
|
||||
function! grammarous#error(...)
|
||||
echohl ErrorMsg
|
||||
try
|
||||
if a:0 > 1
|
||||
let msg = 'vim-grammarous: ' . call('printf', a:000)
|
||||
else
|
||||
let msg = 'vim-grammarous: ' . a:1
|
||||
endif
|
||||
for l in split(msg, "\n")
|
||||
echomsg l
|
||||
endfor
|
||||
finally
|
||||
echohl None
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:delete_jar_dir() abort
|
||||
if !isdirectory(g:grammarous#jar_dir)
|
||||
return
|
||||
endif
|
||||
|
||||
let dir = g:grammarous#jar_dir
|
||||
if s:is_cygwin
|
||||
let dir = s:cygpath(dir)
|
||||
endif
|
||||
|
||||
if dir ==# '' || !isdirectory(dir)
|
||||
call grammarous#error("Directory '%s' does not exist", dir)
|
||||
return
|
||||
endif
|
||||
|
||||
if s:is_windows && !s:is_cygwin
|
||||
let cmd = 'rmdir /s /q ' . dir
|
||||
else
|
||||
let cmd = 'rm -rf ' . dir
|
||||
endif
|
||||
|
||||
let out = system(cmd)
|
||||
if v:shell_error
|
||||
call grammarous#error("Cannot remove the directory '%s': %s", dir, out)
|
||||
return
|
||||
endif
|
||||
|
||||
echomsg 'Deleted ' . dir
|
||||
unlet! s:jar_file
|
||||
endfunction
|
||||
|
||||
function! s:find_jar(dir)
|
||||
return findfile('languagetool-commandline.jar', a:dir . '/**')
|
||||
endfunction
|
||||
|
||||
function! s:prepare_jar(dir)
|
||||
let jar = s:find_jar(a:dir)
|
||||
if jar ==# ''
|
||||
if grammarous#downloader#download(a:dir)
|
||||
let jar = s:find_jar(a:dir)
|
||||
endif
|
||||
endif
|
||||
return fnamemodify(jar, ':p')
|
||||
endfunction
|
||||
|
||||
function! s:find_jar_path()
|
||||
if exists('s:jar_file')
|
||||
return s:jar_file
|
||||
endif
|
||||
|
||||
if !executable(g:grammarous#java_cmd)
|
||||
call grammarous#error('"java" command not found. Please install Java 8+')
|
||||
return ''
|
||||
endif
|
||||
|
||||
" TODO:
|
||||
" Check java version
|
||||
|
||||
let jar = s:prepare_jar(g:grammarous#jar_dir)
|
||||
if jar ==# ''
|
||||
call grammarous#error('Failed to get LanguageTool')
|
||||
return ''
|
||||
endif
|
||||
|
||||
if s:is_cygwin
|
||||
let jar = s:cygpath(jar)
|
||||
endif
|
||||
|
||||
let s:jar_file = jar
|
||||
return jar
|
||||
endfunction
|
||||
|
||||
function! s:cygpath(path) abort
|
||||
if !executable('cygpath')
|
||||
return a:path
|
||||
endif
|
||||
|
||||
" On Cygwin environment, paths should be converted with cygpath.
|
||||
" /cygdrive/c/... -> C:/...
|
||||
" https://github.com/rhysd/vim-grammarous/issues/30
|
||||
let converted = substitute(s:P.system('cygpath -aw ' . a:path), '\n\+$', '', '')
|
||||
|
||||
if s:P.get_last_status()
|
||||
return a:path
|
||||
endif
|
||||
|
||||
return converted
|
||||
endfunction
|
||||
|
||||
function! s:make_text(text)
|
||||
if type(a:text) == type('')
|
||||
return a:text
|
||||
else
|
||||
return join(a:text, "\n")
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:set_errors_to_location_list() abort
|
||||
let f = expand('%:p')
|
||||
let saved_efm = &l:errorformat
|
||||
try
|
||||
setlocal errorformat=%f:%l:%c:%m
|
||||
let lines = map(copy(b:grammarous_result), '
|
||||
\ printf("%s:%s:%s:%s [%s]", f, v:val.fromy + 1, v:val.fromx + 1, v:val.msg, v:val.category)
|
||||
\')
|
||||
lgetexpr lines
|
||||
finally
|
||||
let &l:errorformat = saved_efm
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:set_errors_from_xml_string(xml) abort
|
||||
let b:grammarous_result = grammarous#get_errors_from_xml(s:XML.parse(substitute(a:xml, "\n", '', 'g')))
|
||||
let parsed = s:last_parsed_options
|
||||
|
||||
if s:is_comment_only(parsed['comments-only'])
|
||||
call filter(b:grammarous_result, 'synIDattr(synID(v:val.fromy+1, v:val.fromx+1, 0), "name") =~? "comment"')
|
||||
endif
|
||||
|
||||
redraw!
|
||||
if empty(b:grammarous_result)
|
||||
echomsg 'Yay! No grammatical errors detected.'
|
||||
return
|
||||
endif
|
||||
|
||||
let len = len(b:grammarous_result)
|
||||
echomsg printf('Detected %d grammatical error%s', len, len > 1 ? 's' : '')
|
||||
call grammarous#highlight_errors_in_current_buffer(b:grammarous_result)
|
||||
if parsed['move-to-first-error']
|
||||
call cursor(b:grammarous_result[0].fromy+1, b:grammarous_result[0].fromx+1)
|
||||
endif
|
||||
|
||||
if g:grammarous#enable_spell_check
|
||||
let s:saved_spell = &l:spell
|
||||
setlocal spell
|
||||
endif
|
||||
|
||||
if g:grammarous#use_location_list
|
||||
call s:set_errors_to_location_list()
|
||||
endif
|
||||
|
||||
if g:grammarous#show_first_error
|
||||
call grammarous#create_update_info_window_of(b:grammarous_result)
|
||||
endif
|
||||
|
||||
if has_key(g:grammarous#hooks, 'on_check')
|
||||
call call(g:grammarous#hooks.on_check, [b:grammarous_result], g:grammarous#hooks)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:on_check_done_vim8(channel) abort
|
||||
let xml = ''
|
||||
while ch_status(a:channel, {'part' : 'out'}) ==# 'buffered'
|
||||
let xml .= ch_read(a:channel)
|
||||
endwhile
|
||||
if xml ==# ''
|
||||
return
|
||||
endif
|
||||
call s:set_errors_from_xml_string(xml)
|
||||
endfunction
|
||||
|
||||
function! s:on_check_exit_vim8(channel, status) abort
|
||||
if a:status == 0
|
||||
return
|
||||
endif
|
||||
let err = ''
|
||||
while ch_status(a:channel, {'part' : 'err'}) ==# 'buffered'
|
||||
let err .= ch_read(a:channel, {'part' : 'err'})
|
||||
endwhile
|
||||
call grammarous#error('Grammar check failed with exit status ' . a:status . ': ' . err)
|
||||
endfunction
|
||||
|
||||
function! s:on_exit_nvim(job, status, event) abort dict
|
||||
if a:status != 0
|
||||
call grammarous#error('Grammar check failed: ' . self._stderr)
|
||||
return
|
||||
endif
|
||||
|
||||
call s:set_errors_from_xml_string(self._stdout)
|
||||
endfunction
|
||||
|
||||
function! s:on_output_nvim(job, lines, event) abort dict
|
||||
let output = join(a:lines, "\n")
|
||||
if a:event ==# 'stdout'
|
||||
let self._stdout .= output
|
||||
else
|
||||
let self._stderr .= output
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:invoke_check(range_start, ...)
|
||||
if g:grammarous#languagetool_cmd ==# ''
|
||||
let jar = s:find_jar_path()
|
||||
if jar ==# ''
|
||||
return
|
||||
endif
|
||||
endif
|
||||
|
||||
if a:0 < 1
|
||||
call grammarous#error('Invalid argument. At least one argument is required.')
|
||||
return
|
||||
endif
|
||||
|
||||
if g:grammarous#use_vim_spelllang
|
||||
" Convert vim spelllang to languagetool spelllang
|
||||
if len(split(&spelllang, '_')) == 1
|
||||
let lang = split(&spelllang, '_')[0]
|
||||
elseif len(split(&spelllang, '_')) == 2
|
||||
let lang = split(&spelllang, '_')[0].'-'.toupper(split(&spelllang, '_')[1])
|
||||
endif
|
||||
else
|
||||
let lang = a:0 == 1 ? g:grammarous#default_lang : a:1
|
||||
endif
|
||||
let text = s:make_text(a:0 == 1 ? a:1 : a:2)
|
||||
|
||||
let tmpfile = tempname()
|
||||
execute 'redir! >' tmpfile
|
||||
let l = 1
|
||||
while l < a:range_start
|
||||
silent echo ""
|
||||
let l += 1
|
||||
endwhile
|
||||
silent echon text
|
||||
redir END
|
||||
|
||||
if s:is_cygwin
|
||||
let tmpfile = s:cygpath(tmpfile)
|
||||
endif
|
||||
|
||||
let cmdargs = printf(
|
||||
\ '-c %s -l %s --api %s',
|
||||
\ &fileencoding ? &fileencoding : &encoding,
|
||||
\ lang,
|
||||
\ substitute(tmpfile, '\\\s\@!', '\\\\', 'g')
|
||||
\ )
|
||||
|
||||
let disabled_rules = get(g:grammarous#disabled_rules, &filetype, get(g:grammarous#disabled_rules, '*', []))
|
||||
if !empty(disabled_rules)
|
||||
let cmdargs = '-d ' . join(disabled_rules, ',') . ' ' . cmdargs
|
||||
endif
|
||||
|
||||
let enabled_rules = get(g:grammarous#enabled_rules, &filetype, get(g:grammarous#enabled_rules, '*', []))
|
||||
if !empty(enabled_rules)
|
||||
let cmdargs = '-e ' . join(enabled_rules, ',') . ' ' . cmdargs
|
||||
endif
|
||||
|
||||
let disabled_categories = get(g:grammarous#disabled_categories, &filetype, get(g:grammarous#disabled_categories, '*', []))
|
||||
if !empty(disabled_categories)
|
||||
let cmdargs = '--disablecategories ' . join(disabled_categories, ',') . ' ' . cmdargs
|
||||
endif
|
||||
|
||||
let enabled_categories = get(g:grammarous#enabled_categories, &filetype, get(g:grammarous#enabled_categories, '*', []))
|
||||
if !empty(enabled_categories)
|
||||
let cmdargs = '--enablecategories ' . join(enabled_categories, ',') . ' ' . cmdargs
|
||||
endif
|
||||
|
||||
if g:grammarous#languagetool_cmd !=# ''
|
||||
let cmd = printf('%s %s', g:grammarous#languagetool_cmd, cmdargs)
|
||||
else
|
||||
let cmd = printf('%s -jar %s %s', g:grammarous#java_cmd, substitute(jar, '\\\s\@!', '\\\\', 'g'), cmdargs)
|
||||
endif
|
||||
|
||||
if s:job_is_available
|
||||
let job = job_start(cmd, {'close_cb' : s:SID . 'on_check_done_vim8', 'exit_cb' : s:SID . 'on_check_exit_vim8'})
|
||||
echo 'Grammar check has started with job(' . job . ')...'
|
||||
return
|
||||
endif
|
||||
|
||||
if has('nvim')
|
||||
let opts = {
|
||||
\ 'on_stdout' : function('s:on_output_nvim'),
|
||||
\ 'on_stderr' : function('s:on_output_nvim'),
|
||||
\ 'on_exit' : function('s:on_exit_nvim'),
|
||||
\ '_stdout' : '',
|
||||
\ '_stderr' : '',
|
||||
\ }
|
||||
let job = jobstart(cmd, opts)
|
||||
echo 'Grammar check has started with job(id: ' . job . ')...'
|
||||
return
|
||||
endif
|
||||
|
||||
let xml = s:P.system(cmd)
|
||||
call delete(tmpfile)
|
||||
|
||||
if s:P.get_last_status()
|
||||
call grammarous#error("Command '%s' failed:\n%s", cmd, xml)
|
||||
return
|
||||
endif
|
||||
call s:set_errors_from_xml_string(xml)
|
||||
endfunction
|
||||
|
||||
function! s:sanitize(s)
|
||||
return substitute(escape(a:s, "'\\"), ' ', '\\_\\s', 'g')
|
||||
endfunction
|
||||
|
||||
function! grammarous#generate_highlight_pattern(error)
|
||||
let line = a:error.fromy + 1
|
||||
let prefix = a:error.contextoffset > 0 ? s:sanitize(a:error.context[: a:error.contextoffset-1]) : ''
|
||||
let rest = a:error.context[a:error.contextoffset :]
|
||||
let the_error = s:sanitize(rest[: a:error.errorlength-1])
|
||||
let rest = s:sanitize(rest[a:error.errorlength :])
|
||||
return '\V' . prefix . '\zs' . the_error . '\ze' . rest
|
||||
endfunction
|
||||
|
||||
function! s:unescape_xml(str)
|
||||
let s = substitute(a:str, '"', '"', 'g')
|
||||
let s = substitute(s, ''', "'", 'g')
|
||||
let s = substitute(s, '>', '>', 'g')
|
||||
let s = substitute(s, '<', '<', 'g')
|
||||
return substitute(s, '&', '\&', 'g')
|
||||
endfunction
|
||||
|
||||
function! s:unescape_error(err)
|
||||
for e in ['context', 'msg', 'replacements']
|
||||
let a:err[e] = s:unescape_xml(a:err[e])
|
||||
endfor
|
||||
return a:err
|
||||
endfunction
|
||||
|
||||
function! grammarous#get_errors_from_xml(xml)
|
||||
return map(filter(a:xml.childNodes(), 'v:val.name ==# "error"'), 's:unescape_error(v:val.attr)')
|
||||
endfunction
|
||||
|
||||
function! s:matcherrpos(...)
|
||||
return matchaddpos('GrammarousError', [a:000], 999)
|
||||
endfunction
|
||||
|
||||
function! s:highlight_error(from, to)
|
||||
if a:from[0] == a:to[0]
|
||||
return s:matcherrpos(a:from[0], a:from[1], a:to[1] - a:from[1])
|
||||
endif
|
||||
|
||||
let ids = [s:matcherrpos(a:from[0], a:from[1], strlen(getline(a:from[0]))+1 - a:from[1])]
|
||||
let line = a:from[0] + 1
|
||||
while line < a:to[0]
|
||||
call add(ids, s:matcherrpos(line))
|
||||
let line += 1
|
||||
endwhile
|
||||
call add(ids, s:matcherrpos(a:to[0], 1, a:to[1] - 1))
|
||||
return ids
|
||||
endfunction
|
||||
|
||||
function! s:remove_3dots(str)
|
||||
return substitute(substitute(a:str, '\.\.\.$', '', ''), '\\V\zs\.\.\.', '', '')
|
||||
endfunction
|
||||
|
||||
function! grammarous#highlight_errors_in_current_buffer(errs)
|
||||
if !g:grammarous#use_fallback_highlight
|
||||
for e in a:errs
|
||||
let e.id = s:highlight_error(
|
||||
\ [str2nr(e.fromy)+1, str2nr(e.fromx)+1],
|
||||
\ [str2nr(e.toy)+1, str2nr(e.tox)+1],
|
||||
\ )
|
||||
endfor
|
||||
else
|
||||
for e in a:errs
|
||||
let e.id = matchadd(
|
||||
\ 'GrammarousError',
|
||||
\ s:remove_3dots(grammarous#generate_highlight_pattern(e)),
|
||||
\ 999
|
||||
\ )
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! grammarous#reset_highlights()
|
||||
for m in filter(getmatches(), 'v:val.group ==# "GrammarousError"')
|
||||
call matchdelete(m.id)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! grammarous#find_checked_winnr() abort
|
||||
if exists('b:grammarous_result')
|
||||
return winnr()
|
||||
endif
|
||||
for bufnr in tabpagebuflist()
|
||||
let result = getbufvar(bufnr, 'grammarous_result', [])
|
||||
if empty(result)
|
||||
continue
|
||||
endif
|
||||
|
||||
let winnr = bufwinnr(bufnr)
|
||||
if winnr == -1
|
||||
continue
|
||||
endif
|
||||
|
||||
return winnr
|
||||
endfor
|
||||
return -1
|
||||
endfunction
|
||||
|
||||
function! grammarous#reset()
|
||||
let win = grammarous#find_checked_winnr()
|
||||
if win == -1
|
||||
return
|
||||
endif
|
||||
|
||||
let prev_win = winnr()
|
||||
if win != prev_win
|
||||
execute win . 'wincmd w'
|
||||
endif
|
||||
|
||||
if g:grammarous#use_location_list
|
||||
lclose
|
||||
lgetexpr []
|
||||
endif
|
||||
|
||||
call grammarous#reset_highlights()
|
||||
call grammarous#info_win#stop_auto_preview()
|
||||
call grammarous#info_win#close()
|
||||
if exists('s:saved_spell')
|
||||
let &l:spell = s:saved_spell
|
||||
unlet s:saved_spell
|
||||
endif
|
||||
if has_key(g:grammarous#hooks, 'on_reset')
|
||||
call call(g:grammarous#hooks.on_reset, [b:grammarous_result], g:grammarous#hooks)
|
||||
endif
|
||||
unlet! b:grammarous_result b:grammarous_preview_bufnr
|
||||
|
||||
if win != prev_win
|
||||
wincmd p
|
||||
endif
|
||||
endfunction
|
||||
|
||||
let s:opt_parser = s:O.new()
|
||||
\.on('--lang=VALUE', 'language to check', {'default' : g:grammarous#default_lang})
|
||||
\.on('--[no-]preview', 'enable auto preview', {'default' : 1})
|
||||
\.on('--[no-]comments-only', 'check comment only', {'default' : ''})
|
||||
\.on('--[no-]move-to-first-error', 'move to first error', {'default' : g:grammarous#move_to_first_error})
|
||||
\.on('--reinstall-languagetool', 'reinstall LanguageTool', {'default' : 0})
|
||||
|
||||
function! grammarous#complete_opt(arglead, cmdline, cursorpos)
|
||||
return s:opt_parser.complete(a:arglead, a:cmdline, a:cursorpos)
|
||||
endfunction
|
||||
|
||||
function! s:is_comment_only(option)
|
||||
if type(a:option) == type(0)
|
||||
return a:option
|
||||
endif
|
||||
|
||||
return get(
|
||||
\ g:grammarous#default_comments_only_filetypes,
|
||||
\ &filetype,
|
||||
\ get(g:grammarous#default_comments_only_filetypes, '*', 0)
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! grammarous#check_current_buffer(qargs, range)
|
||||
if exists('b:grammarous_result')
|
||||
call grammarous#reset()
|
||||
redraw!
|
||||
endif
|
||||
|
||||
let parsed = s:opt_parser.parse(a:qargs, a:range, '')
|
||||
if has_key(parsed, 'help')
|
||||
return
|
||||
endif
|
||||
|
||||
let b:grammarous_auto_preview = parsed.preview
|
||||
if parsed.preview
|
||||
call grammarous#info_win#start_auto_preview()
|
||||
endif
|
||||
|
||||
if parsed['reinstall-languagetool']
|
||||
call s:delete_jar_dir()
|
||||
endif
|
||||
|
||||
" XXX
|
||||
let s:last_parsed_options = parsed
|
||||
|
||||
call s:invoke_check(
|
||||
\ parsed.__range__[0],
|
||||
\ parsed.lang,
|
||||
\ getline(parsed.__range__[0], parsed.__range__[1])
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! s:less_position(p1, p2)
|
||||
if a:p1[0] != a:p2[0]
|
||||
return a:p1[0] < a:p2[0]
|
||||
endif
|
||||
|
||||
return a:p1[1] < a:p2[1]
|
||||
endfunction
|
||||
|
||||
function! s:binary_search_by_pos(errors, the_pos, start, end)
|
||||
if a:start > a:end
|
||||
return {}
|
||||
endif
|
||||
|
||||
let m = (a:start + a:end) / 2
|
||||
let from = [a:errors[m].fromy+1, a:errors[m].fromx+1]
|
||||
let to = [a:errors[m].toy+1, a:errors[m].tox]
|
||||
|
||||
if s:less_position(a:the_pos, from)
|
||||
return s:binary_search_by_pos(a:errors, a:the_pos, a:start, m-1)
|
||||
endif
|
||||
|
||||
if s:less_position(to, a:the_pos)
|
||||
return s:binary_search_by_pos(a:errors, a:the_pos, m+1, a:end)
|
||||
endif
|
||||
|
||||
return a:errors[m]
|
||||
endfunction
|
||||
|
||||
" Note:
|
||||
" It believes all errors are sorted by its position
|
||||
function! grammarous#get_error_at(pos, errs)
|
||||
return s:binary_search_by_pos(a:errs, a:pos, 0, len(a:errs)-1)
|
||||
endfunction
|
||||
|
||||
function! grammarous#fixit(err)
|
||||
if empty(a:err)
|
||||
\ || !grammarous#move_to_checked_buf(a:err.fromy+1, a:err.fromx+1)
|
||||
\ || a:err.replacements ==# ''
|
||||
call grammarous#error('Cannot fix this error automatically.')
|
||||
return
|
||||
endif
|
||||
|
||||
let sel_save = &l:selection
|
||||
let &l:selection = 'inclusive'
|
||||
let save_g_reg = getreg('g', 1)
|
||||
let save_g_regtype = getregtype('g')
|
||||
try
|
||||
normal! v
|
||||
call cursor(a:err.toy+1, a:err.tox)
|
||||
noautocmd normal! "gy
|
||||
let from = getreg('g')
|
||||
let to = split(a:err.replacements, '#', 1)[0]
|
||||
call setreg('g', to, 'v')
|
||||
normal! gv"gp
|
||||
|
||||
call grammarous#remove_error(a:err, get(a:, 1, b:grammarous_result))
|
||||
|
||||
echomsg printf("Fixed: '%s' -> '%s'", from, to)
|
||||
finally
|
||||
call setreg('g', save_g_reg, save_g_regtype)
|
||||
let &l:selection = sel_save
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! grammarous#fixall(errs)
|
||||
for e in a:errs
|
||||
call grammarous#fixit(e)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:move_to_pos(pos)
|
||||
let p = type(a:pos[0]) == type([]) ? a:pos[0] : a:pos
|
||||
return cursor(a:pos[0], a:pos[1]) != -1
|
||||
endfunction
|
||||
|
||||
function! s:move_to(buf, pos)
|
||||
if a:buf != bufnr('%')
|
||||
let winnr = bufwinnr(a:buf)
|
||||
if winnr == -1
|
||||
return 0
|
||||
endif
|
||||
|
||||
execute winnr . 'wincmd w'
|
||||
endif
|
||||
return s:move_to_pos(a:pos)
|
||||
endfunction
|
||||
|
||||
function! grammarous#move_to_checked_buf(...)
|
||||
if exists('b:grammarous_result')
|
||||
return s:move_to_pos(a:000)
|
||||
endif
|
||||
|
||||
if exists('b:grammarous_preview_original_bufnr')
|
||||
return s:move_to(b:grammarous_preview_original_bufnr, a:000)
|
||||
endif
|
||||
|
||||
for b in tabpagebuflist()
|
||||
if !empty(getbufvar(b, 'grammarous_result', []))
|
||||
return s:move_to(b, a:000)
|
||||
endif
|
||||
endfor
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! grammarous#create_update_info_window_of(errs)
|
||||
let e = grammarous#get_error_at(getpos('.')[1 : 2], a:errs)
|
||||
if empty(e)
|
||||
return
|
||||
endif
|
||||
|
||||
if exists('b:grammarous_preview_bufnr')
|
||||
let winnr = bufwinnr(b:grammarous_preview_bufnr)
|
||||
if winnr == -1
|
||||
let bufnr = grammarous#info_win#open(e, bufnr('%'))
|
||||
else
|
||||
execute winnr . 'wincmd w'
|
||||
let bufnr = grammarous#info_win#update(e)
|
||||
endif
|
||||
else
|
||||
let bufnr = grammarous#info_win#open(e, bufnr('%'))
|
||||
endif
|
||||
|
||||
wincmd p
|
||||
let b:grammarous_preview_bufnr = bufnr
|
||||
endfunction
|
||||
|
||||
function! grammarous#create_and_jump_to_info_window_of(errs)
|
||||
call grammarous#create_update_info_window_of(a:errs)
|
||||
wincmd p
|
||||
endfunction
|
||||
|
||||
function! s:remove_error_highlight(e)
|
||||
let ids = type(a:e.id) == type([]) ? a:e.id : [a:e.id]
|
||||
for i in ids
|
||||
silent! if matchdelete(i) == -1
|
||||
return 0
|
||||
endif
|
||||
endfor
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! grammarous#remove_error(e, errs)
|
||||
if !s:remove_error_highlight(a:e)
|
||||
return 0
|
||||
endif
|
||||
|
||||
for i in range(len(a:errs))
|
||||
if type(a:errs[i].id) == type(a:e.id) && a:errs[i].id == a:e.id
|
||||
call grammarous#info_win#close()
|
||||
unlet a:errs[i]
|
||||
return 1
|
||||
endif
|
||||
endfor
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! grammarous#remove_error_at(pos, errs)
|
||||
let e = grammarous#get_error_at(a:pos, a:errs)
|
||||
if empty(e)
|
||||
return 0
|
||||
endif
|
||||
|
||||
return grammarous#remove_error(e, a:errs)
|
||||
endfunction
|
||||
|
||||
function! grammarous#disable_rule(rule, errs)
|
||||
call grammarous#info_win#close()
|
||||
|
||||
" Note:
|
||||
" reverse() is needed because of removing elements in list
|
||||
for i in reverse(range(len(a:errs)))
|
||||
let e = a:errs[i]
|
||||
if e.ruleId ==# a:rule
|
||||
if !s:remove_error_highlight(e)
|
||||
return 0
|
||||
endif
|
||||
unlet a:errs[i]
|
||||
endif
|
||||
endfor
|
||||
|
||||
echomsg 'Disabled rule: ' . a:rule
|
||||
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! grammarous#disable_rule_at(pos, errs)
|
||||
let e = grammarous#get_error_at(a:pos, a:errs)
|
||||
if empty(e)
|
||||
return 0
|
||||
endif
|
||||
|
||||
return grammarous#disable_rule(e.ruleId, a:errs)
|
||||
endfunction
|
||||
|
||||
function! grammarous#disable_category(category, errs)
|
||||
call grammarous#info_win#close()
|
||||
|
||||
" Note:
|
||||
" reverse() is needed because of removing elements in list
|
||||
for i in reverse(range(len(a:errs)))
|
||||
let e = a:errs[i]
|
||||
|
||||
if e.categoryid ==# a:category
|
||||
if !s:remove_error_highlight(e)
|
||||
return 0
|
||||
endif
|
||||
unlet a:errs[i]
|
||||
endif
|
||||
endfor
|
||||
|
||||
echomsg 'Disabled category: ' . a:category
|
||||
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! grammarous#disable_category_at(pos, errs)
|
||||
let e = grammarous#get_error_at(a:pos, a:errs)
|
||||
if empty(e)
|
||||
return 0
|
||||
endif
|
||||
|
||||
return grammarous#disable_category(e.categoryid, a:errs)
|
||||
endfunction
|
||||
|
||||
function! grammarous#move_to_next_error(pos, errs)
|
||||
for e in a:errs
|
||||
let p = [e.fromy+1, e.fromx+1]
|
||||
if s:less_position(a:pos, p)
|
||||
return s:move_to_pos(p)
|
||||
endif
|
||||
endfor
|
||||
call grammarous#error('No next error found.')
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! grammarous#move_to_previous_error(pos, errs)
|
||||
for e in reverse(copy(a:errs))
|
||||
let p = [e.fromy+1, e.fromx+1]
|
||||
if s:less_position(p, a:pos)
|
||||
return s:move_to_pos(p)
|
||||
endif
|
||||
endfor
|
||||
call grammarous#error('No previous error found.')
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
44
bundle/vim-grammarous/autoload/grammarous/downloader.vim
vendored
Normal file
44
bundle/vim-grammarous/autoload/grammarous/downloader.vim
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
function! s:error(about, dir)
|
||||
let msg = printf('Could not download jar file because %s. Please download zip from %s and extract it to %s.', a:about, g:grammarous#jar_url, a:dir)
|
||||
call grammarous#error(msg)
|
||||
endfunction
|
||||
|
||||
function! grammarous#downloader#download(jar_dir)
|
||||
if !isdirectory(a:jar_dir)
|
||||
call mkdir(a:jar_dir, 'p')
|
||||
endif
|
||||
|
||||
let tmp_file = tempname() . '.zip'
|
||||
|
||||
if !executable('unzip')
|
||||
call s:error("'unzip' is not found", a:jar_dir)
|
||||
return 0
|
||||
endif
|
||||
|
||||
if executable('axel')
|
||||
let cmd = printf('axel -a -n 2 -o %s %s 2>&1', tmp_file, g:grammarous#jar_url)
|
||||
elseif executable('wget')
|
||||
let cmd = printf('wget -O %s %s 2>&1', tmp_file, g:grammarous#jar_url)
|
||||
elseif executable('curl')
|
||||
let cmd = printf('curl -L -o %s %s 2>&1', tmp_file, g:grammarous#jar_url)
|
||||
else
|
||||
call s:error("could not find 'axel', 'curl', or 'wget'", a:jar_dir)
|
||||
return 0
|
||||
endif
|
||||
|
||||
echomsg 'Downloading jar file from ' . g:grammarous#jar_url . '...'
|
||||
|
||||
let cmd = printf('%s && unzip %s -d %s', cmd, tmp_file, a:jar_dir)
|
||||
let result = system(cmd)
|
||||
if v:shell_error
|
||||
call s:error(printf("'%s' failed: %s", cmd, result), a:jar_dir)
|
||||
return 0
|
||||
endif
|
||||
|
||||
echomsg 'Done!'
|
||||
|
||||
" Should error handling?
|
||||
call delete(tmp_file)
|
||||
|
||||
return 1
|
||||
endfunction
|
232
bundle/vim-grammarous/autoload/grammarous/info_win.vim
vendored
Normal file
232
bundle/vim-grammarous/autoload/grammarous/info_win.vim
vendored
Normal file
@ -0,0 +1,232 @@
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
function! grammarous#info_win#action_return()
|
||||
call grammarous#move_to_checked_buf(b:grammarous_preview_error.fromy+1, b:grammarous_preview_error.fromx+1)
|
||||
endfunction
|
||||
|
||||
function! grammarous#info_win#action_fixit()
|
||||
call grammarous#fixit(b:grammarous_preview_error)
|
||||
endfunction
|
||||
|
||||
function! grammarous#info_win#action_remove_error()
|
||||
let e = b:grammarous_preview_error
|
||||
if !grammarous#move_to_checked_buf(
|
||||
\ b:grammarous_preview_error.fromy+1,
|
||||
\ b:grammarous_preview_error.fromx+1 )
|
||||
return
|
||||
endif
|
||||
|
||||
call grammarous#remove_error(e, b:grammarous_result)
|
||||
endfunction
|
||||
|
||||
function! grammarous#info_win#action_disable_rule()
|
||||
let e = b:grammarous_preview_error
|
||||
if !grammarous#move_to_checked_buf(
|
||||
\ b:grammarous_preview_error.fromy+1,
|
||||
\ b:grammarous_preview_error.fromx+1 )
|
||||
return
|
||||
endif
|
||||
|
||||
call grammarous#disable_rule(e.ruleId, b:grammarous_result)
|
||||
endfunction
|
||||
|
||||
function! grammarous#info_win#action_next_error()
|
||||
if !grammarous#move_to_checked_buf(
|
||||
\ b:grammarous_preview_error.fromy+1,
|
||||
\ b:grammarous_preview_error.fromx+1 )
|
||||
return
|
||||
endif
|
||||
|
||||
if !grammarous#move_to_next_error(getpos('.')[1 : 2], b:grammarous_result)
|
||||
wincmd p
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! grammarous#info_win#action_previous_error()
|
||||
if !grammarous#move_to_checked_buf(
|
||||
\ b:grammarous_preview_error.fromy+1,
|
||||
\ b:grammarous_preview_error.fromx+1 )
|
||||
return
|
||||
endif
|
||||
|
||||
if !grammarous#move_to_previous_error(getpos('.')[1 : 2], b:grammarous_result)
|
||||
wincmd p
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! grammarous#info_win#action_help()
|
||||
echo join([
|
||||
\ '| Mappings | Description |',
|
||||
\ '| -------- |:---------------------------------------------- |',
|
||||
\ '| q | Quit the info window |',
|
||||
\ '| <CR> | Move to the location of the error |',
|
||||
\ '| f | Fix the error automatically |',
|
||||
\ '| r | Remove the error without fix |',
|
||||
\ '| R | Disable the grammar rule in the checked buffer |',
|
||||
\ '| n | Move to the next error |',
|
||||
\ '| p | Move to the previous error |',
|
||||
\ ], "\n")
|
||||
endfunction
|
||||
|
||||
function! s:get_info_buffer(e)
|
||||
let lines =
|
||||
\ [
|
||||
\ 'Error: ' . a:e.category,
|
||||
\ ' ' . a:e.msg,
|
||||
\ '',
|
||||
\ ]
|
||||
if a:e.replacements !=# ''
|
||||
let lines +=
|
||||
\ [
|
||||
\ 'Corrections:',
|
||||
\ ' ' . join(split(a:e.replacements, '#', 1), '; '),
|
||||
\ '',
|
||||
\ ]
|
||||
endif
|
||||
let lines +=
|
||||
\ [
|
||||
\ 'Context:',
|
||||
\ ' ' . a:e.context,
|
||||
\ '',
|
||||
\ "Press '?' in this window to show help",
|
||||
\ ]
|
||||
return lines
|
||||
endfunction
|
||||
|
||||
function! grammarous#info_win#action_quit()
|
||||
let s:do_not_preview = 1
|
||||
let preview_bufnr = bufnr('%')
|
||||
|
||||
quit!
|
||||
|
||||
" Consider the case where :quit! does not navigate to the buffer
|
||||
" where :GrammarousCheck checked.
|
||||
for bufnr in tabpagebuflist()
|
||||
let b = getbufvar(bufnr, 'grammarous_preview_bufnr', -1)
|
||||
if b != preview_bufnr
|
||||
continue
|
||||
endif
|
||||
|
||||
let winnr = bufwinnr(bufnr)
|
||||
if winnr == -1
|
||||
continue
|
||||
endif
|
||||
|
||||
execute winnr . 'wincmd w'
|
||||
unlet b:grammarous_preview_bufnr
|
||||
return
|
||||
endfor
|
||||
" Reach here when the original buffer was already closed
|
||||
endfunction
|
||||
|
||||
function! grammarous#info_win#update(e)
|
||||
let b:grammarous_preview_error = a:e
|
||||
silent normal! gg"_dG
|
||||
silent %delete _
|
||||
call setline(1, s:get_info_buffer(a:e))
|
||||
execute 1
|
||||
setlocal modified
|
||||
|
||||
return bufnr('%')
|
||||
endfunction
|
||||
|
||||
function! grammarous#info_win#open(e, bufnr)
|
||||
execute g:grammarous#info_win_direction g:grammarous#info_window_height . 'new' '[Grammarous]'
|
||||
let b:grammarous_preview_original_bufnr = a:bufnr
|
||||
let b:grammarous_preview_error = a:e
|
||||
call setline(1, s:get_info_buffer(a:e))
|
||||
execute 1
|
||||
syntax match GrammarousInfoSection "\%(Context\|Correction\):"
|
||||
syntax match GrammarousInfoError "Error:.*$"
|
||||
syntax match GrammarousInfoHelp "^Press '?' in this window to show help$"
|
||||
execute 'syntax match GrammarousError "' . escape(grammarous#generate_highlight_pattern(a:e), '"') . '"'
|
||||
setlocal nonumber
|
||||
setlocal bufhidden=hide
|
||||
setlocal buftype=nofile
|
||||
setlocal readonly
|
||||
setlocal nolist
|
||||
setlocal nobuflisted
|
||||
setlocal noswapfile
|
||||
setlocal nospell
|
||||
setlocal nomodeline
|
||||
setlocal nofoldenable
|
||||
setlocal noreadonly
|
||||
setlocal foldcolumn=0
|
||||
setlocal nomodified
|
||||
nnoremap <silent><buffer>q :<C-u>call grammarous#info_win#action_quit()<CR>
|
||||
nnoremap <silent><buffer><CR> :<C-u>call grammarous#info_win#action_return()<CR>
|
||||
nnoremap <buffer>f :<C-u>call grammarous#info_win#action_fixit()<CR>
|
||||
nnoremap <silent><buffer>r :<C-u>call grammarous#info_win#action_remove_error()<CR>
|
||||
nnoremap <silent><buffer>R :<C-u>call grammarous#info_win#action_disable_rule()<CR>
|
||||
nnoremap <silent><buffer>n :<C-u>call grammarous#info_win#action_next_error()<CR>
|
||||
nnoremap <silent><buffer>p :<C-u>call grammarous#info_win#action_previous_error()<CR>
|
||||
nnoremap <silent><buffer>? :<C-u>call grammarous#info_win#action_help()<CR>
|
||||
return bufnr('%')
|
||||
endfunction
|
||||
|
||||
function! s:lookup_preview_bufnr()
|
||||
for b in tabpagebuflist()
|
||||
let the_buf = getbufvar(b, 'grammarous_preview_bufnr', -1)
|
||||
if the_buf != -1
|
||||
return the_buf
|
||||
endif
|
||||
endfor
|
||||
return -1
|
||||
endfunction
|
||||
|
||||
function! grammarous#info_win#close()
|
||||
let cur_win = winnr()
|
||||
if exists('b:grammarous_preview_bufnr')
|
||||
let prev_win = bufwinnr(b:grammarous_preview_bufnr)
|
||||
else
|
||||
let the_buf = s:lookup_preview_bufnr()
|
||||
if the_buf == -1
|
||||
return 0
|
||||
endif
|
||||
let prev_win = bufwinnr(the_buf)
|
||||
endif
|
||||
|
||||
if prev_win == -1
|
||||
return 0
|
||||
end
|
||||
|
||||
execute prev_win . 'wincmd w'
|
||||
wincmd c
|
||||
execute cur_win . 'wincmd w'
|
||||
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! s:do_auto_preview()
|
||||
let mode = mode()
|
||||
if mode ==? 'v' || mode ==# "\<C-v>"
|
||||
return
|
||||
endif
|
||||
|
||||
if exists('s:do_not_preview')
|
||||
unlet s:do_not_preview
|
||||
return
|
||||
endif
|
||||
|
||||
if !exists('b:grammarous_result') || empty(b:grammarous_result)
|
||||
autocmd! plugin-grammarous-auto-preview
|
||||
return
|
||||
endif
|
||||
|
||||
call grammarous#create_update_info_window_of(b:grammarous_result)
|
||||
endfunction
|
||||
|
||||
function! grammarous#info_win#start_auto_preview()
|
||||
augroup plugin-grammarous-auto-preview
|
||||
autocmd!
|
||||
autocmd CursorMoved <buffer> call <SID>do_auto_preview()
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
function! grammarous#info_win#stop_auto_preview()
|
||||
silent! autocmd! plugin-grammarous-auto-preview
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
11
bundle/vim-grammarous/autoload/operator/grammarous.vim
vendored
Normal file
11
bundle/vim-grammarous/autoload/operator/grammarous.vim
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
function! s:is_empty_region(begin, end)
|
||||
return a:begin[1] > a:end[1] || (a:begin[1] == a:end[1] && a:end[2] < a:begin[2])
|
||||
endfunction
|
||||
|
||||
function! operator#grammarous#do(visual_kind)
|
||||
if s:is_empty_region(getpos("'["), getpos("']"))
|
||||
return
|
||||
endif
|
||||
|
||||
call grammarous#check_current_buffer('', [getpos("'[")[1], getpos("']")[1]])
|
||||
endfunction
|
120
bundle/vim-grammarous/autoload/unite/sources/grammarous.vim
vendored
Normal file
120
bundle/vim-grammarous/autoload/unite/sources/grammarous.vim
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
let g:unite#sources#grammarous#one_line = get(g:, 'unite#sources#grammarous#one_line', 0)
|
||||
|
||||
let s:source = {
|
||||
\ 'name' : 'grammarous',
|
||||
\ 'description' : 'Show result of grammar check by vim-grammarous',
|
||||
\ 'default_kind' : 'jump_list',
|
||||
\ 'default_action' : 'open',
|
||||
\ 'hooks' : {},
|
||||
\ 'action_table' : {},
|
||||
\ 'syntax' : 'uniteSource__Grammarous',
|
||||
\ }
|
||||
|
||||
function! unite#sources#grammarous#define()
|
||||
return s:source
|
||||
endfunction
|
||||
|
||||
function! s:source.hooks.on_init(args, context)
|
||||
if exists('b:unite') && has_key(b:unite, 'prev_bufnr')
|
||||
let a:context.source__checked_bufnr = b:unite.prev_bufnr
|
||||
else
|
||||
let a:context.source__checked_bufnr = bufnr('%')
|
||||
endif
|
||||
let a:context.source__checked_bufnr
|
||||
\ = getbufvar(
|
||||
\ a:context.source__checked_bufnr,
|
||||
\ 'grammarous_preview_original_bufnr',
|
||||
\ a:context.source__checked_bufnr
|
||||
\ )
|
||||
if type(getbufvar(a:context.source__checked_bufnr, 'grammarous_result', 0)) == type(0)
|
||||
let should_check_current_buf = a:context.source__checked_bufnr == bufnr('%')
|
||||
if should_check_current_buf
|
||||
execute 'GrammarousCheck' join(a:args, ' ')
|
||||
else
|
||||
let w = bufwinnr(a:context.source__checked_bufnr)
|
||||
execute w . 'wincmd w'
|
||||
execute 'GrammarousCheck' join(a:args, ' ')
|
||||
wincmd p
|
||||
endif
|
||||
endif
|
||||
|
||||
call grammarous#info_win#close()
|
||||
endfunction
|
||||
|
||||
function! s:source.hooks.on_syntax(args, context)
|
||||
if g:unite#sources#grammarous#one_line
|
||||
syntax region uniteSource__GrammarousError start="'" end="'" oneline contained containedin=uniteSource__Grammarous
|
||||
syntax match uniteSource__GrammarousArrow "->" contained containedin=uniteSource__Grammarous
|
||||
highlight default link uniteSource__GrammarousArrow Keyword
|
||||
highlight default link uniteSource__GrammarousError ErrorMsg
|
||||
else
|
||||
syntax match uniteSource__GrammarousKeyword "\%(Context\|Correct\):" contained containedin=uniteSource__Grammarous
|
||||
syntax keyword uniteSource__GrammarousError Error contained containedin=uniteSource__Grammarous
|
||||
highlight default link uniteSource__GrammarousKeyword Keyword
|
||||
highlight default link uniteSource__GrammarousError ErrorMsg
|
||||
for err in getbufvar(a:context.source__checked_bufnr, 'grammarous_result', [])
|
||||
call matchadd('GrammarousError', grammarous#generate_highlight_pattern(err), 999)
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:make_word(e)
|
||||
if g:unite#sources#grammarous#one_line
|
||||
return printf("'%s' -> %s", a:e.context[a:e.contextoffset : a:e.contextoffset+a:e.errorlength-1], a:e.msg)
|
||||
else
|
||||
let word = printf('Error: %s\nContext: %s', a:e.msg, a:e.context)
|
||||
if a:e.replacements !=# ''
|
||||
let word .= '\nCorrect: ' . split(a:e.replacements, '#', 1)[0]
|
||||
endif
|
||||
return word
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:source.change_candidates(args, context)
|
||||
return map(copy(getbufvar(a:context.source__checked_bufnr, 'grammarous_result', [])), '{
|
||||
\ "word" : s:make_word(v:val),
|
||||
\ "action__buffer_nr" : a:context.source__checked_bufnr,
|
||||
\ "action__line" : str2nr(v:val.fromy)+1,
|
||||
\ "action__col" : str2nr(v:val.fromx)+1,
|
||||
\ "action__grammar_error" : v:val,
|
||||
\ "is_multiline" : 1,
|
||||
\}')
|
||||
endfunction
|
||||
|
||||
function! s:prepare_bufvar(c)
|
||||
let b:grammarous_preview_error = a:c.action__grammar_error
|
||||
let b:grammarous_preview_original_bufnr = a:c.action__buffer_nr
|
||||
endfunction
|
||||
|
||||
let s:source.action_table.fixit = {
|
||||
\ 'description' : 'Fix the error automatically',
|
||||
\ }
|
||||
|
||||
function! s:source.action_table.fixit.func(candidate)
|
||||
call s:prepare_bufvar(a:candidate)
|
||||
call grammarous#info_win#action_fixit()
|
||||
endfunction
|
||||
|
||||
let s:source.action_table.remove_error = {
|
||||
\ 'description' : 'Remove the error without fix'
|
||||
\ }
|
||||
|
||||
function! s:source.action_table.remove_error.func(candidate)
|
||||
call s:prepare_bufvar(a:candidate)
|
||||
call grammarous#info_win#action_remove_error()
|
||||
endfunction
|
||||
|
||||
let s:source.action_table.disable_rule = {
|
||||
\ 'description' : 'Disable the grammar rule in the checked buffer'
|
||||
\ }
|
||||
|
||||
function! s:source.action_table.disable_rule.func(candidate)
|
||||
call s:prepare_bufvar(a:candidate)
|
||||
call grammarous#info_win#action_disable_rule()
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
12
bundle/vim-grammarous/autoload/vital.vim
vendored
Normal file
12
bundle/vim-grammarous/autoload/vital.vim
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
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
|
5
bundle/vim-grammarous/autoload/vital/_grammarous.vim
vendored
Normal file
5
bundle/vim-grammarous/autoload/vital/_grammarous.vim
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
let s:_plugin_name = expand('<sfile>:t:r')
|
||||
|
||||
function! vital#{s:_plugin_name}#new() abort
|
||||
return vital#{s:_plugin_name[1:]}#new()
|
||||
endfunction
|
462
bundle/vim-grammarous/autoload/vital/_grammarous/Data/List.vim
vendored
Normal file
462
bundle/vim-grammarous/autoload/vital/_grammarous/Data/List.vim
vendored
Normal file
@ -0,0 +1,462 @@
|
||||
" ___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#_grammarous#Data#List#import() abort
|
||||
return map({'combinations': '', 'and': '', 'sort_by': '', 'foldr1': '', 'sort': '', 'flatten': '', 'has_index': '', 'find_indices': '', 'any': '', 'unshift': '', 'span': '', 'pop': '', 'binary_search': '', 'uniq_by': '', 'or': '', 'all': '', 'zip': '', 'find_last_index': '', 'find': '', 'partition': '', 'shift': '', 'permutations': '', 'break': '', 'max_by': '', 'foldl': '', 'foldr': '', 'find_index': '', 'drop_while': '', 'group_by': '', 'take_while': '', 'conj': '', 'push': '', 'char_range': '', 'cons': '', 'foldl1': '', 'intersect': '', 'concat': '', 'map_accum': '', 'clear': '', 'has_common_items': '', 'product': '', 'zip_fill': '', 'uniq': '', 'has': '', 'min_by': '', 'with_index': ''}, 'function("s:" . v:key)')
|
||||
endfunction
|
||||
else
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_grammarous#Data#List#import() abort', printf("return map({'combinations': '', 'and': '', 'sort_by': '', 'foldr1': '', 'sort': '', 'flatten': '', 'has_index': '', 'find_indices': '', 'any': '', 'unshift': '', 'span': '', 'pop': '', 'binary_search': '', 'uniq_by': '', 'or': '', 'all': '', 'zip': '', 'find_last_index': '', 'find': '', 'partition': '', 'shift': '', 'permutations': '', 'break': '', 'max_by': '', 'foldl': '', 'foldr': '', 'find_index': '', 'drop_while': '', 'group_by': '', 'take_while': '', 'conj': '', 'push': '', 'char_range': '', 'cons': '', 'foldl1': '', 'intersect': '', 'concat': '', 'map_accum': '', 'clear': '', 'has_common_items': '', 'product': '', 'zip_fill': '', 'uniq': '', 'has': '', 'min_by': '', 'with_index': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
endif
|
||||
" ___vital___
|
||||
" Utilities for list.
|
||||
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
function! s:pop(list) abort
|
||||
return remove(a:list, -1)
|
||||
endfunction
|
||||
|
||||
function! s:push(list, val) abort
|
||||
call add(a:list, a:val)
|
||||
return a:list
|
||||
endfunction
|
||||
|
||||
function! s:shift(list) abort
|
||||
return remove(a:list, 0)
|
||||
endfunction
|
||||
|
||||
function! s:unshift(list, val) abort
|
||||
return insert(a:list, a:val)
|
||||
endfunction
|
||||
|
||||
function! s:cons(x, xs) abort
|
||||
return [a:x] + a:xs
|
||||
endfunction
|
||||
|
||||
function! s:conj(xs, x) abort
|
||||
return a:xs + [a:x]
|
||||
endfunction
|
||||
|
||||
" Removes duplicates from a list.
|
||||
function! s:uniq(list) abort
|
||||
return s:uniq_by(a:list, 'v:val')
|
||||
endfunction
|
||||
|
||||
" Removes duplicates from a list.
|
||||
function! s:uniq_by(list, f) abort
|
||||
let list = map(copy(a:list), printf('[v:val, %s]', a:f))
|
||||
let i = 0
|
||||
let seen = {}
|
||||
while i < len(list)
|
||||
let key = string(list[i][1])
|
||||
if has_key(seen, key)
|
||||
call remove(list, i)
|
||||
else
|
||||
let seen[key] = 1
|
||||
let i += 1
|
||||
endif
|
||||
endwhile
|
||||
return map(list, 'v:val[0]')
|
||||
endfunction
|
||||
|
||||
function! s:clear(list) abort
|
||||
if !empty(a:list)
|
||||
unlet! a:list[0 : len(a:list) - 1]
|
||||
endif
|
||||
return a:list
|
||||
endfunction
|
||||
|
||||
" Concatenates a list of lists.
|
||||
" XXX: Should we verify the input?
|
||||
function! s:concat(list) abort
|
||||
let memo = []
|
||||
for Value in a:list
|
||||
let memo += Value
|
||||
endfor
|
||||
return memo
|
||||
endfunction
|
||||
|
||||
" Take each elements from lists to a new list.
|
||||
function! s:flatten(list, ...) abort
|
||||
let limit = a:0 > 0 ? a:1 : -1
|
||||
let memo = []
|
||||
if limit == 0
|
||||
return a:list
|
||||
endif
|
||||
let limit -= 1
|
||||
for Value in a:list
|
||||
let memo +=
|
||||
\ type(Value) == type([]) ?
|
||||
\ s:flatten(Value, limit) :
|
||||
\ [Value]
|
||||
unlet! Value
|
||||
endfor
|
||||
return memo
|
||||
endfunction
|
||||
|
||||
" Sorts a list with expression to compare each two values.
|
||||
" a:a and a:b can be used in {expr}.
|
||||
function! s:sort(list, expr) abort
|
||||
if type(a:expr) == type(function('function'))
|
||||
return sort(a:list, a:expr)
|
||||
endif
|
||||
let s:expr = a:expr
|
||||
return sort(a:list, 's:_compare')
|
||||
endfunction
|
||||
|
||||
function! s:_compare(a, b) abort
|
||||
return eval(s:expr)
|
||||
endfunction
|
||||
|
||||
" Sorts a list using a set of keys generated by mapping the values in the list
|
||||
" through the given expr.
|
||||
" v:val is used in {expr}
|
||||
function! s:sort_by(list, expr) abort
|
||||
let pairs = map(a:list, printf('[v:val, %s]', a:expr))
|
||||
return map(s:sort(pairs,
|
||||
\ 'a:a[1] ==# a:b[1] ? 0 : a:a[1] ># a:b[1] ? 1 : -1'), 'v:val[0]')
|
||||
endfunction
|
||||
|
||||
" Returns a maximum value in {list} through given {expr}.
|
||||
" Returns 0 if {list} is empty.
|
||||
" v:val is used in {expr}
|
||||
function! s:max_by(list, expr) abort
|
||||
if empty(a:list)
|
||||
return 0
|
||||
endif
|
||||
let list = map(copy(a:list), a:expr)
|
||||
return a:list[index(list, max(list))]
|
||||
endfunction
|
||||
|
||||
" Returns a minimum value in {list} through given {expr}.
|
||||
" Returns 0 if {list} is empty.
|
||||
" v:val is used in {expr}
|
||||
" FIXME: -0x80000000 == 0x80000000
|
||||
function! s:min_by(list, expr) abort
|
||||
return s:max_by(a:list, '-(' . a:expr . ')')
|
||||
endfunction
|
||||
|
||||
" Returns List of character sequence between [a:from, a:to]
|
||||
" e.g.: s:char_range('a', 'c') returns ['a', 'b', 'c']
|
||||
function! s:char_range(from, to) abort
|
||||
return map(
|
||||
\ range(char2nr(a:from), char2nr(a:to)),
|
||||
\ 'nr2char(v:val)'
|
||||
\)
|
||||
endfunction
|
||||
|
||||
" Returns true if a:list has a:value.
|
||||
" Returns false otherwise.
|
||||
function! s:has(list, value) abort
|
||||
return index(a:list, a:value) isnot -1
|
||||
endfunction
|
||||
|
||||
" Returns true if a:list[a:index] exists.
|
||||
" Returns false otherwise.
|
||||
" NOTE: Returns false when a:index is negative number.
|
||||
function! s:has_index(list, index) abort
|
||||
" Return true when negative index?
|
||||
" let index = a:index >= 0 ? a:index : len(a:list) + a:index
|
||||
return 0 <= a:index && a:index < len(a:list)
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Data.List.span
|
||||
function! s:span(f, xs) abort
|
||||
let border = len(a:xs)
|
||||
for i in range(len(a:xs))
|
||||
if !eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
|
||||
let border = i
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
return border == 0 ? [[], copy(a:xs)] : [a:xs[: border - 1], a:xs[border :]]
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Data.List.break
|
||||
function! s:break(f, xs) abort
|
||||
return s:span(printf('!(%s)', a:f), a:xs)
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Data.List.takeWhile
|
||||
function! s:take_while(f, xs) abort
|
||||
return s:span(a:f, a:xs)[0]
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Data.List.dropWhile
|
||||
function! s:drop_while(f, xs) abort
|
||||
return s:span(a:f, a:xs)[1]
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Data.List.partition
|
||||
function! s:partition(f, xs) abort
|
||||
return [filter(copy(a:xs), a:f), filter(copy(a:xs), '!(' . a:f . ')')]
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Prelude.all
|
||||
function! s:all(f, xs) abort
|
||||
return !s:any(printf('!(%s)', a:f), a:xs)
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Prelude.any
|
||||
function! s:any(f, xs) abort
|
||||
return !empty(filter(map(copy(a:xs), a:f), 'v:val'))
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Prelude.and
|
||||
function! s:and(xs) abort
|
||||
return s:all('v:val', a:xs)
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Prelude.or
|
||||
function! s:or(xs) abort
|
||||
return s:any('v:val', a:xs)
|
||||
endfunction
|
||||
|
||||
function! s:map_accum(expr, xs, init) abort
|
||||
let memo = []
|
||||
let init = a:init
|
||||
for x in a:xs
|
||||
let expr = substitute(a:expr, 'v:memo', init, 'g')
|
||||
let expr = substitute(expr, 'v:val', x, 'g')
|
||||
let [tmp, init] = eval(expr)
|
||||
call add(memo, tmp)
|
||||
endfor
|
||||
return memo
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Prelude.foldl
|
||||
function! s:foldl(f, init, xs) abort
|
||||
let memo = a:init
|
||||
for x in a:xs
|
||||
let expr = substitute(a:f, 'v:val', string(x), 'g')
|
||||
let expr = substitute(expr, 'v:memo', string(memo), 'g')
|
||||
unlet memo
|
||||
let memo = eval(expr)
|
||||
endfor
|
||||
return memo
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Prelude.foldl1
|
||||
function! s:foldl1(f, xs) abort
|
||||
if len(a:xs) == 0
|
||||
throw 'vital: Data.List: foldl1'
|
||||
endif
|
||||
return s:foldl(a:f, a:xs[0], a:xs[1:])
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Prelude.foldr
|
||||
function! s:foldr(f, init, xs) abort
|
||||
return s:foldl(a:f, a:init, reverse(copy(a:xs)))
|
||||
endfunction
|
||||
|
||||
" similar to Haskell's Prelude.fold11
|
||||
function! s:foldr1(f, xs) abort
|
||||
if len(a:xs) == 0
|
||||
throw 'vital: Data.List: foldr1'
|
||||
endif
|
||||
return s:foldr(a:f, a:xs[-1], a:xs[0:-2])
|
||||
endfunction
|
||||
|
||||
" similar to python's zip()
|
||||
function! s:zip(...) abort
|
||||
return map(range(min(map(copy(a:000), 'len(v:val)'))), "map(copy(a:000), 'v:val['.v:val.']')")
|
||||
endfunction
|
||||
|
||||
" similar to zip(), but goes until the longer one.
|
||||
function! s:zip_fill(xs, ys, filler) abort
|
||||
if empty(a:xs) && empty(a:ys)
|
||||
return []
|
||||
elseif empty(a:ys)
|
||||
return s:cons([a:xs[0], a:filler], s:zip_fill(a:xs[1 :], [], a:filler))
|
||||
elseif empty(a:xs)
|
||||
return s:cons([a:filler, a:ys[0]], s:zip_fill([], a:ys[1 :], a:filler))
|
||||
else
|
||||
return s:cons([a:xs[0], a:ys[0]], s:zip_fill(a:xs[1 :], a:ys[1: ], a:filler))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Inspired by Ruby's with_index method.
|
||||
function! s:with_index(list, ...) abort
|
||||
let base = a:0 > 0 ? a:1 : 0
|
||||
return map(copy(a:list), '[v:val, v:key + base]')
|
||||
endfunction
|
||||
|
||||
" similar to Ruby's detect or Haskell's find.
|
||||
function! s:find(list, default, f) abort
|
||||
for x in a:list
|
||||
if eval(substitute(a:f, 'v:val', string(x), 'g'))
|
||||
return x
|
||||
endif
|
||||
endfor
|
||||
return a:default
|
||||
endfunction
|
||||
|
||||
" Returns the index of the first element which satisfies the given expr.
|
||||
function! s:find_index(xs, f, ...) abort
|
||||
let len = len(a:xs)
|
||||
let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : 0
|
||||
let default = a:0 > 1 ? a:2 : -1
|
||||
if start >=# len || start < 0
|
||||
return default
|
||||
endif
|
||||
for i in range(start, len - 1)
|
||||
if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
|
||||
return i
|
||||
endif
|
||||
endfor
|
||||
return default
|
||||
endfunction
|
||||
|
||||
" Returns the index of the last element which satisfies the given expr.
|
||||
function! s:find_last_index(xs, f, ...) abort
|
||||
let len = len(a:xs)
|
||||
let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : len - 1
|
||||
let default = a:0 > 1 ? a:2 : -1
|
||||
if start >=# len || start < 0
|
||||
return default
|
||||
endif
|
||||
for i in range(start, 0, -1)
|
||||
if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
|
||||
return i
|
||||
endif
|
||||
endfor
|
||||
return default
|
||||
endfunction
|
||||
|
||||
" Similar to find_index but returns the list of indices satisfying the given expr.
|
||||
function! s:find_indices(xs, f, ...) abort
|
||||
let len = len(a:xs)
|
||||
let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : 0
|
||||
let result = []
|
||||
if start >=# len || start < 0
|
||||
return result
|
||||
endif
|
||||
for i in range(start, len - 1)
|
||||
if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
|
||||
call add(result, i)
|
||||
endif
|
||||
endfor
|
||||
return result
|
||||
endfunction
|
||||
|
||||
" Return non-zero if a:list1 and a:list2 have any common item(s).
|
||||
" Return zero otherwise.
|
||||
function! s:has_common_items(list1, list2) abort
|
||||
return !empty(filter(copy(a:list1), 'index(a:list2, v:val) isnot -1'))
|
||||
endfunction
|
||||
|
||||
function! s:intersect(list1, list2) abort
|
||||
let items = []
|
||||
" for funcref
|
||||
for X in a:list1
|
||||
if index(a:list2, X) != -1 && index(items, X) == -1
|
||||
let items += [X]
|
||||
endif
|
||||
endfor
|
||||
return items
|
||||
endfunction
|
||||
|
||||
" similar to Ruby's group_by.
|
||||
function! s:group_by(xs, f) abort
|
||||
let result = {}
|
||||
let list = map(copy(a:xs), printf('[v:val, %s]', a:f))
|
||||
for x in list
|
||||
let Val = x[0]
|
||||
let key = type(x[1]) !=# type('') ? string(x[1]) : x[1]
|
||||
if has_key(result, key)
|
||||
call add(result[key], Val)
|
||||
else
|
||||
let result[key] = [Val]
|
||||
endif
|
||||
unlet Val
|
||||
endfor
|
||||
return result
|
||||
endfunction
|
||||
|
||||
function! s:_default_compare(a, b) abort
|
||||
return a:a <# a:b ? -1 : a:a ># a:b ? 1 : 0
|
||||
endfunction
|
||||
|
||||
function! s:binary_search(list, value, ...) abort
|
||||
let Predicate = a:0 >= 1 ? a:1 : 's:_default_compare'
|
||||
let dic = a:0 >= 2 ? a:2 : {}
|
||||
let start = 0
|
||||
let end = len(a:list) - 1
|
||||
|
||||
while 1
|
||||
if start > end
|
||||
return -1
|
||||
endif
|
||||
|
||||
let middle = (start + end) / 2
|
||||
|
||||
let compared = call(Predicate, [a:value, a:list[middle]], dic)
|
||||
|
||||
if compared < 0
|
||||
let end = middle - 1
|
||||
elseif compared > 0
|
||||
let start = middle + 1
|
||||
else
|
||||
return middle
|
||||
endif
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
function! s:product(lists) abort
|
||||
let result = [[]]
|
||||
for pool in a:lists
|
||||
let tmp = []
|
||||
for x in result
|
||||
let tmp += map(copy(pool), 'x + [v:val]')
|
||||
endfor
|
||||
let result = tmp
|
||||
endfor
|
||||
return result
|
||||
endfunction
|
||||
|
||||
function! s:permutations(list, ...) abort
|
||||
if a:0 > 1
|
||||
throw 'vital: Data.List: too many arguments'
|
||||
endif
|
||||
let r = a:0 == 1 ? a:1 : len(a:list)
|
||||
if r > len(a:list)
|
||||
return []
|
||||
elseif r < 0
|
||||
throw 'vital: Data.List: {r} must be non-negative integer'
|
||||
endif
|
||||
let n = len(a:list)
|
||||
let result = []
|
||||
for indices in s:product(map(range(r), 'range(n)'))
|
||||
if len(s:uniq(indices)) == r
|
||||
call add(result, map(indices, 'a:list[v:val]'))
|
||||
endif
|
||||
endfor
|
||||
return result
|
||||
endfunction
|
||||
|
||||
function! s:combinations(list, r) abort
|
||||
if a:r > len(a:list)
|
||||
return []
|
||||
elseif a:r < 0
|
||||
throw 'vital: Data.List: {r} must be non-negative integer'
|
||||
endif
|
||||
let n = len(a:list)
|
||||
let result = []
|
||||
for indices in s:permutations(range(n), a:r)
|
||||
if s:sort(copy(indices), 'a:a - a:b') == indices
|
||||
call add(result, map(indices, 'a:list[v:val]'))
|
||||
endif
|
||||
endfor
|
||||
return result
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
||||
" vim:set et ts=2 sts=2 sw=2 tw=0:
|
633
bundle/vim-grammarous/autoload/vital/_grammarous/Data/String.vim
vendored
Normal file
633
bundle/vim-grammarous/autoload/vital/_grammarous/Data/String.vim
vendored
Normal file
@ -0,0 +1,633 @@
|
||||
" ___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#_grammarous#Data#String#import() abort
|
||||
return map({'starts_with': '', 'split3': '', 'replace_first': '', 'chop': '', 'unescape': '', 'split_posix_text': '', 'replace': '', 'scan': '', 'strwidthpart': '', 'common_head': '', 'reverse': '', 'escape_pattern': '', 'trim_end': '', '_vital_depends': '', 'wrap': '', 'join_posix_lines': '', 'contains_multibyte': '', 'truncate_skipping': '', 'split_leftright': '', 'ends_with': '', 'nsplit': '', 'strwidthpart_reverse': '', 'unescape_pattern': '', 'levenshtein_distance': '', 'trim_start': '', 'justify_equal_spacing': '', 'nr2hex': '', 'iconv': '', 'pad_left': '', 'nr2enc_char': '', 'lines': '', 'repair_posix_text': '', 'nr2byte': '', 'trim': '', 'diffidx': '', 'truncate': '', 'split_by_displaywidth': '', '_vital_created': '', 'padding_by_displaywidth': '', 'hash': '', 'chomp': '', 'pad_between_letters': '', 'dstring': '', 'pad_both_sides': '', 'substitute_last': '', 'pad_right': '', 'remove_ansi_sequences': '', '_vital_loaded': ''}, 'function("s:" . v:key)')
|
||||
endfunction
|
||||
else
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_grammarous#Data#String#import() abort', printf("return map({'starts_with': '', 'split3': '', 'replace_first': '', 'chop': '', 'unescape': '', 'split_posix_text': '', 'replace': '', 'scan': '', 'strwidthpart': '', 'common_head': '', 'reverse': '', 'escape_pattern': '', 'trim_end': '', '_vital_depends': '', 'wrap': '', 'join_posix_lines': '', 'contains_multibyte': '', 'truncate_skipping': '', 'split_leftright': '', 'ends_with': '', 'nsplit': '', 'strwidthpart_reverse': '', 'unescape_pattern': '', 'levenshtein_distance': '', 'trim_start': '', 'justify_equal_spacing': '', 'nr2hex': '', 'iconv': '', 'pad_left': '', 'nr2enc_char': '', 'lines': '', 'repair_posix_text': '', 'nr2byte': '', 'trim': '', 'diffidx': '', 'truncate': '', 'split_by_displaywidth': '', '_vital_created': '', 'padding_by_displaywidth': '', 'hash': '', 'chomp': '', 'pad_between_letters': '', 'dstring': '', 'pad_both_sides': '', 'substitute_last': '', 'pad_right': '', 'remove_ansi_sequences': '', '_vital_loaded': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
endif
|
||||
" ___vital___
|
||||
" Utilities for string.
|
||||
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
function! s:_vital_loaded(V) abort
|
||||
let s:V = a:V
|
||||
let s:L = s:V.import('Data.List')
|
||||
endfunction
|
||||
|
||||
function! s:_vital_depends() abort
|
||||
return ['Data.List']
|
||||
endfunction
|
||||
|
||||
function! s:_vital_created(module) abort
|
||||
" Expose script-local funcref
|
||||
if exists('s:strchars')
|
||||
let a:module.strchars = s:strchars
|
||||
endif
|
||||
if exists('s:wcswidth')
|
||||
let a:module.wcswidth = s:wcswidth
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Substitute a:from => a:to by string.
|
||||
" To substitute by pattern, use substitute() instead.
|
||||
function! s:replace(str, from, to) abort
|
||||
return s:_replace(a:str, a:from, a:to, 'g')
|
||||
endfunction
|
||||
|
||||
" Substitute a:from => a:to only once.
|
||||
" cf. s:replace()
|
||||
function! s:replace_first(str, from, to) abort
|
||||
return s:_replace(a:str, a:from, a:to, '')
|
||||
endfunction
|
||||
|
||||
" implement of replace() and replace_first()
|
||||
function! s:_replace(str, from, to, flags) abort
|
||||
return substitute(a:str, '\V'.escape(a:from, '\'), escape(a:to, '\'), a:flags)
|
||||
endfunction
|
||||
|
||||
function! s:scan(str, pattern) abort
|
||||
let list = []
|
||||
call substitute(a:str, a:pattern, '\=add(list, submatch(0)) == [] ? "" : ""', 'g')
|
||||
return list
|
||||
endfunction
|
||||
|
||||
function! s:reverse(str) abort
|
||||
return join(reverse(split(a:str, '.\zs')), '')
|
||||
endfunction
|
||||
|
||||
function! s:starts_with(str, prefix) abort
|
||||
return stridx(a:str, a:prefix) == 0
|
||||
endfunction
|
||||
|
||||
function! s:ends_with(str, suffix) abort
|
||||
let idx = strridx(a:str, a:suffix)
|
||||
return 0 <= idx && idx + len(a:suffix) == len(a:str)
|
||||
endfunction
|
||||
|
||||
function! s:common_head(strs) abort
|
||||
if empty(a:strs)
|
||||
return ''
|
||||
endif
|
||||
let len = len(a:strs)
|
||||
if len == 1
|
||||
return a:strs[0]
|
||||
endif
|
||||
let strs = len == 2 ? a:strs : sort(copy(a:strs))
|
||||
let pat = substitute(strs[0], '.', '\="[" . escape(submatch(0), "^\\") . "]"', 'g')
|
||||
return pat ==# '' ? '' : matchstr(strs[-1], '\C^\%[' . pat . ']')
|
||||
endfunction
|
||||
|
||||
" Split to two elements of List. ([left, right])
|
||||
" e.g.: s:split3('neocomplcache', 'compl') returns ['neo', 'compl', 'cache']
|
||||
function! s:split_leftright(expr, pattern) abort
|
||||
let [left, _, right] = s:split3(a:expr, a:pattern)
|
||||
return [left, right]
|
||||
endfunction
|
||||
|
||||
function! s:split3(expr, pattern) abort
|
||||
let ERROR = ['', '', '']
|
||||
if a:expr ==# '' || a:pattern ==# ''
|
||||
return ERROR
|
||||
endif
|
||||
let begin = match(a:expr, a:pattern)
|
||||
if begin is -1
|
||||
return ERROR
|
||||
endif
|
||||
let end = matchend(a:expr, a:pattern)
|
||||
let left = begin <=# 0 ? '' : a:expr[: begin - 1]
|
||||
let right = a:expr[end :]
|
||||
return [left, a:expr[begin : end-1], right]
|
||||
endfunction
|
||||
|
||||
" Slices into strings determines the number of substrings.
|
||||
" e.g.: s:nsplit("neo compl cache", 2, '\s') returns ['neo', 'compl cache']
|
||||
function! s:nsplit(expr, n, ...) abort
|
||||
let pattern = get(a:000, 0, '\s')
|
||||
let keepempty = get(a:000, 1, 1)
|
||||
let ret = []
|
||||
let expr = a:expr
|
||||
if a:n <= 1
|
||||
return [expr]
|
||||
endif
|
||||
while 1
|
||||
let pos = match(expr, pattern)
|
||||
if pos == -1
|
||||
if expr !~ pattern || keepempty
|
||||
call add(ret, expr)
|
||||
endif
|
||||
break
|
||||
elseif pos >= 0
|
||||
let left = pos > 0 ? expr[:pos-1] : ''
|
||||
if pos > 0 || keepempty
|
||||
call add(ret, left)
|
||||
endif
|
||||
let ml = len(matchstr(expr, pattern))
|
||||
if pos == 0 && ml == 0
|
||||
let pos = 1
|
||||
endif
|
||||
let expr = expr[pos+ml :]
|
||||
endif
|
||||
if len(expr) == 0
|
||||
break
|
||||
endif
|
||||
if len(ret) == a:n - 1
|
||||
call add(ret, expr)
|
||||
break
|
||||
endif
|
||||
endwhile
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
" Returns the number of character in a:str.
|
||||
" NOTE: This returns proper value
|
||||
" even if a:str contains multibyte character(s).
|
||||
" s:strchars(str) {{{
|
||||
if exists('*strchars')
|
||||
let s:strchars = function('strchars')
|
||||
else
|
||||
function! s:strchars(str) abort
|
||||
return strlen(substitute(copy(a:str), '.', 'x', 'g'))
|
||||
endfunction
|
||||
endif "}}}
|
||||
|
||||
" Returns the bool of contains any multibyte character in s:str
|
||||
function! s:contains_multibyte(str) abort "{{{
|
||||
return strlen(a:str) != s:strchars(a:str)
|
||||
endfunction "}}}
|
||||
|
||||
" Remove last character from a:str.
|
||||
" NOTE: This returns proper value
|
||||
" even if a:str contains multibyte character(s).
|
||||
function! s:chop(str) abort "{{{
|
||||
return substitute(a:str, '.$', '', '')
|
||||
endfunction "}}}
|
||||
|
||||
" Remove last \r,\n,\r\n from a:str.
|
||||
function! s:chomp(str) abort "{{{
|
||||
return substitute(a:str, '\%(\r\n\|[\r\n]\)$', '', '')
|
||||
endfunction "}}}
|
||||
|
||||
" wrap() and its internal functions
|
||||
" * _split_by_wcswidth_once()
|
||||
" * _split_by_wcswidth()
|
||||
" * _concat()
|
||||
" * wrap()
|
||||
"
|
||||
" NOTE _concat() is just a copy of Data.List.concat().
|
||||
" FIXME don't repeat yourself
|
||||
function! s:_split_by_wcswidth_once(body, x) abort
|
||||
let fst = s:strwidthpart(a:body, a:x)
|
||||
let snd = s:strwidthpart_reverse(a:body, s:wcswidth(a:body) - s:wcswidth(fst))
|
||||
return [fst, snd]
|
||||
endfunction
|
||||
|
||||
function! s:_split_by_wcswidth(body, x) abort
|
||||
let memo = []
|
||||
let body = a:body
|
||||
while s:wcswidth(body) > a:x
|
||||
let [tmp, body] = s:_split_by_wcswidth_once(body, a:x)
|
||||
call add(memo, tmp)
|
||||
endwhile
|
||||
call add(memo, body)
|
||||
return memo
|
||||
endfunction
|
||||
|
||||
function! s:trim(str) abort
|
||||
return matchstr(a:str,'^\s*\zs.\{-}\ze\s*$')
|
||||
endfunction
|
||||
|
||||
function! s:trim_start(str) abort
|
||||
return matchstr(a:str,'^\s*\zs.\{-}$')
|
||||
endfunction
|
||||
|
||||
function! s:trim_end(str) abort
|
||||
return matchstr(a:str,'^.\{-}\ze\s*$')
|
||||
endfunction
|
||||
|
||||
function! s:wrap(str,...) abort
|
||||
let _columns = a:0 > 0 ? a:1 : &columns
|
||||
return s:L.concat(
|
||||
\ map(split(a:str, '\r\n\|[\r\n]'), 's:_split_by_wcswidth(v:val, _columns - 1)'))
|
||||
endfunction
|
||||
|
||||
function! s:nr2byte(nr) abort
|
||||
if a:nr < 0x80
|
||||
return nr2char(a:nr)
|
||||
elseif a:nr < 0x800
|
||||
return nr2char(a:nr/64+192).nr2char(a:nr%64+128)
|
||||
else
|
||||
return nr2char(a:nr/4096%16+224).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:nr2enc_char(charcode) abort
|
||||
if &encoding ==# 'utf-8'
|
||||
return nr2char(a:charcode)
|
||||
endif
|
||||
let char = s:nr2byte(a:charcode)
|
||||
if strlen(char) > 1
|
||||
let char = strtrans(iconv(char, 'utf-8', &encoding))
|
||||
endif
|
||||
return char
|
||||
endfunction
|
||||
|
||||
function! s:nr2hex(nr) abort
|
||||
let n = a:nr
|
||||
let r = ''
|
||||
while n
|
||||
let r = '0123456789ABCDEF'[n % 16] . r
|
||||
let n = n / 16
|
||||
endwhile
|
||||
return r
|
||||
endfunction
|
||||
|
||||
" If a ==# b, returns -1.
|
||||
" If a !=# b, returns first index of different character.
|
||||
function! s:diffidx(a, b) abort
|
||||
return a:a ==# a:b ? -1 : strlen(s:common_head([a:a, a:b]))
|
||||
endfunction
|
||||
|
||||
function! s:substitute_last(expr, pat, sub) abort
|
||||
return substitute(a:expr, printf('.*\zs%s', a:pat), a:sub, '')
|
||||
endfunction
|
||||
|
||||
function! s:dstring(expr) abort
|
||||
let x = substitute(string(a:expr), "^'\\|'$", '', 'g')
|
||||
let x = substitute(x, "''", "'", 'g')
|
||||
return printf('"%s"', escape(x, '"'))
|
||||
endfunction
|
||||
|
||||
function! s:lines(str) abort
|
||||
return split(a:str, '\r\?\n')
|
||||
endfunction
|
||||
|
||||
function! s:_pad_with_char(str, left, right, char) abort
|
||||
return repeat(a:char, a:left). a:str. repeat(a:char, a:right)
|
||||
endfunction
|
||||
|
||||
function! s:pad_left(str, width, ...) abort
|
||||
let char = get(a:, 1, ' ')
|
||||
if strdisplaywidth(char) != 1
|
||||
throw "vital: Data.String: Can't use non-half-width characters for padding."
|
||||
endif
|
||||
let left = max([0, a:width - strdisplaywidth(a:str)])
|
||||
return s:_pad_with_char(a:str, left, 0, char)
|
||||
endfunction
|
||||
|
||||
function! s:pad_right(str, width, ...) abort
|
||||
let char = get(a:, 1, ' ')
|
||||
if strdisplaywidth(char) != 1
|
||||
throw "vital: Data.String: Can't use non-half-width characters for padding."
|
||||
endif
|
||||
let right = max([0, a:width - strdisplaywidth(a:str)])
|
||||
return s:_pad_with_char(a:str, 0, right, char)
|
||||
endfunction
|
||||
|
||||
function! s:pad_both_sides(str, width, ...) abort
|
||||
let char = get(a:, 1, ' ')
|
||||
if strdisplaywidth(char) != 1
|
||||
throw "vital: Data.String: Can't use non-half-width characters for padding."
|
||||
endif
|
||||
let space = max([0, a:width - strdisplaywidth(a:str)])
|
||||
let left = space / 2
|
||||
let right = space - left
|
||||
return s:_pad_with_char(a:str, left, right, char)
|
||||
endfunction
|
||||
|
||||
function! s:pad_between_letters(str, width, ...) abort
|
||||
let char = get(a:, 1, ' ')
|
||||
if strdisplaywidth(char) != 1
|
||||
throw "vital: Data.String: Can't use non-half-width characters for padding."
|
||||
endif
|
||||
let letters = split(a:str, '\zs')
|
||||
let each_width = a:width / len(letters)
|
||||
let str = join(map(letters, 's:pad_both_sides(v:val, each_width, char)'), '')
|
||||
if a:width - strdisplaywidth(str) > 0
|
||||
return char. s:pad_both_sides(str, a:width - 1, char)
|
||||
endif
|
||||
return str
|
||||
endfunction
|
||||
|
||||
function! s:justify_equal_spacing(str, width, ...) abort
|
||||
let char = get(a:, 1, ' ')
|
||||
if strdisplaywidth(char) != 1
|
||||
throw "vital: Data.String: Can't use non-half-width characters for padding."
|
||||
endif
|
||||
let letters = split(a:str, '\zs')
|
||||
let first_letter = letters[0]
|
||||
" {width w/o the first letter} / {length w/o the first letter}
|
||||
let each_width = (a:width - strdisplaywidth(first_letter)) / (len(letters) - 1)
|
||||
let remainder = (a:width - strdisplaywidth(first_letter)) % (len(letters) - 1)
|
||||
return first_letter. join(s:L.concat([
|
||||
\ map(letters[1:remainder], 's:pad_left(v:val, each_width + 1, char)'),
|
||||
\ map(letters[remainder + 1:], 's:pad_left(v:val, each_width, char)')
|
||||
\ ]), '')
|
||||
endfunction
|
||||
|
||||
function! s:levenshtein_distance(str1, str2) abort
|
||||
let letters1 = split(a:str1, '\zs')
|
||||
let letters2 = split(a:str2, '\zs')
|
||||
let length1 = len(letters1)
|
||||
let length2 = len(letters2)
|
||||
let distances = map(range(1, length1 + 1), 'map(range(1, length2 + 1), ''0'')')
|
||||
|
||||
for i1 in range(0, length1)
|
||||
let distances[i1][0] = i1
|
||||
endfor
|
||||
for i2 in range(0, length2)
|
||||
let distances[0][i2] = i2
|
||||
endfor
|
||||
|
||||
for i1 in range(1, length1)
|
||||
for i2 in range(1, length2)
|
||||
let cost = (letters1[i1 - 1] ==# letters2[i2 - 1]) ? 0 : 1
|
||||
|
||||
let distances[i1][i2] = min([
|
||||
\ distances[i1 - 1][i2 ] + 1,
|
||||
\ distances[i1 ][i2 - 1] + 1,
|
||||
\ distances[i1 - 1][i2 - 1] + cost,
|
||||
\])
|
||||
endfor
|
||||
endfor
|
||||
|
||||
return distances[length1][length2]
|
||||
endfunction
|
||||
|
||||
function! s:padding_by_displaywidth(expr, width, float) abort
|
||||
let padding_char = ' '
|
||||
let n = a:width - strdisplaywidth(a:expr)
|
||||
if n <= 0
|
||||
let n = 0
|
||||
endif
|
||||
if a:float < 0
|
||||
return a:expr . repeat(padding_char, n)
|
||||
elseif 0 < a:float
|
||||
return repeat(padding_char, n) . a:expr
|
||||
else
|
||||
if n % 2 is 0
|
||||
return repeat(padding_char, n / 2) . a:expr . repeat(padding_char, n / 2)
|
||||
else
|
||||
return repeat(padding_char, (n - 1) / 2) . a:expr . repeat(padding_char, (n - 1) / 2) . padding_char
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:split_by_displaywidth(expr, width, float, is_wrap) abort
|
||||
if a:width is 0
|
||||
return ['']
|
||||
endif
|
||||
|
||||
let lines = []
|
||||
|
||||
let cs = split(a:expr, '\zs')
|
||||
let cs_index = 0
|
||||
|
||||
let text = ''
|
||||
while cs_index < len(cs)
|
||||
if cs[cs_index] is# "\n"
|
||||
let text = s:padding_by_displaywidth(text, a:width, a:float)
|
||||
let lines += [text]
|
||||
let text = ''
|
||||
else
|
||||
let w = strdisplaywidth(text . cs[cs_index])
|
||||
|
||||
if w < a:width
|
||||
let text .= cs[cs_index]
|
||||
elseif a:width < w
|
||||
let text = s:padding_by_displaywidth(text, a:width, a:float)
|
||||
else
|
||||
let text .= cs[cs_index]
|
||||
endif
|
||||
|
||||
if a:width <= w
|
||||
let lines += [text]
|
||||
let text = ''
|
||||
if a:is_wrap
|
||||
if a:width < w
|
||||
if a:width < strdisplaywidth(cs[cs_index])
|
||||
while get(cs, cs_index, "\n") isnot# "\n"
|
||||
let cs_index += 1
|
||||
endwhile
|
||||
continue
|
||||
else
|
||||
let text = cs[cs_index]
|
||||
endif
|
||||
endif
|
||||
else
|
||||
while get(cs, cs_index, "\n") isnot# "\n"
|
||||
let cs_index += 1
|
||||
endwhile
|
||||
continue
|
||||
endif
|
||||
endif
|
||||
|
||||
endif
|
||||
let cs_index += 1
|
||||
endwhile
|
||||
|
||||
if !empty(text)
|
||||
let lines += [ s:padding_by_displaywidth(text, a:width, a:float) ]
|
||||
endif
|
||||
|
||||
return lines
|
||||
endfunction
|
||||
|
||||
function! s:hash(str) abort
|
||||
if exists('*sha256')
|
||||
return sha256(a:str)
|
||||
else
|
||||
" This gives up sha256ing but just adds up char with index.
|
||||
let sum = 0
|
||||
for i in range(len(a:str))
|
||||
let sum += char2nr(a:str[i]) * (i + 1)
|
||||
endfor
|
||||
|
||||
return printf('%x', sum)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:truncate(str, width) abort
|
||||
" Original function is from mattn.
|
||||
" http://github.com/mattn/googlereader-vim/tree/master
|
||||
|
||||
if a:str =~# '^[\x00-\x7f]*$'
|
||||
return len(a:str) < a:width
|
||||
\ ? printf('%-' . a:width . 's', a:str)
|
||||
\ : strpart(a:str, 0, a:width)
|
||||
endif
|
||||
|
||||
let ret = a:str
|
||||
let width = s:wcswidth(a:str)
|
||||
if width > a:width
|
||||
let ret = s:strwidthpart(ret, a:width)
|
||||
let width = s:wcswidth(ret)
|
||||
endif
|
||||
|
||||
if width < a:width
|
||||
let ret .= repeat(' ', a:width - width)
|
||||
endif
|
||||
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
function! s:truncate_skipping(str, max, footer_width, separator) abort
|
||||
let width = s:wcswidth(a:str)
|
||||
if width <= a:max
|
||||
let ret = a:str
|
||||
else
|
||||
let header_width = a:max - s:wcswidth(a:separator) - a:footer_width
|
||||
let ret = s:strwidthpart(a:str, header_width) . a:separator
|
||||
\ . s:strwidthpart_reverse(a:str, a:footer_width)
|
||||
endif
|
||||
return s:truncate(ret, a:max)
|
||||
endfunction
|
||||
|
||||
function! s:strwidthpart(str, width) abort
|
||||
let str = tr(a:str, "\t", ' ')
|
||||
let vcol = a:width + 2
|
||||
return matchstr(str, '.*\%<' . (vcol < 0 ? 0 : vcol) . 'v')
|
||||
endfunction
|
||||
|
||||
function! s:strwidthpart_reverse(str, width) abort
|
||||
let str = tr(a:str, "\t", ' ')
|
||||
let vcol = s:wcswidth(str) - a:width
|
||||
return matchstr(str, '\%>' . (vcol < 0 ? 0 : vcol) . 'v.*')
|
||||
endfunction
|
||||
|
||||
if v:version >= 703
|
||||
" Use builtin function.
|
||||
let s:wcswidth = function('strwidth')
|
||||
else
|
||||
function! s:wcswidth(str) abort
|
||||
if a:str =~# '^[\x00-\x7f]*$'
|
||||
return strlen(a:str)
|
||||
endif
|
||||
let mx_first = '^\(.\)'
|
||||
let str = a:str
|
||||
let width = 0
|
||||
while 1
|
||||
let ucs = char2nr(substitute(str, mx_first, '\1', ''))
|
||||
if ucs == 0
|
||||
break
|
||||
endif
|
||||
let width += s:_wcwidth(ucs)
|
||||
let str = substitute(str, mx_first, '', '')
|
||||
endwhile
|
||||
return width
|
||||
endfunction
|
||||
|
||||
" UTF-8 only.
|
||||
function! s:_wcwidth(ucs) abort
|
||||
let ucs = a:ucs
|
||||
if (ucs >= 0x1100
|
||||
\ && (ucs <= 0x115f
|
||||
\ || ucs == 0x2329
|
||||
\ || ucs == 0x232a
|
||||
\ || (ucs >= 0x2e80 && ucs <= 0xa4cf
|
||||
\ && ucs != 0x303f)
|
||||
\ || (ucs >= 0xac00 && ucs <= 0xd7a3)
|
||||
\ || (ucs >= 0xf900 && ucs <= 0xfaff)
|
||||
\ || (ucs >= 0xfe30 && ucs <= 0xfe6f)
|
||||
\ || (ucs >= 0xff00 && ucs <= 0xff60)
|
||||
\ || (ucs >= 0xffe0 && ucs <= 0xffe6)
|
||||
\ || (ucs >= 0x20000 && ucs <= 0x2fffd)
|
||||
\ || (ucs >= 0x30000 && ucs <= 0x3fffd)
|
||||
\ ))
|
||||
return 2
|
||||
endif
|
||||
return 1
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! s:remove_ansi_sequences(text) abort
|
||||
return substitute(a:text, '\e\[\%(\%(\d\+;\)*\d\+\)\?[mK]', '', 'g')
|
||||
endfunction
|
||||
|
||||
function! s:escape_pattern(str) abort
|
||||
" escape characters for no-magic
|
||||
return escape(a:str, '^$~.*[]\')
|
||||
endfunction
|
||||
|
||||
function! s:unescape_pattern(str) abort
|
||||
" unescape characters for no-magic
|
||||
return s:unescape(a:str, '^$~.*[]\')
|
||||
endfunction
|
||||
|
||||
function! s:unescape(str, chars) abort
|
||||
let chars = map(split(a:chars, '\zs'), 'escape(v:val, ''^$~.*[]\'')')
|
||||
return substitute(a:str, '\\\(' . join(chars, '\|') . '\)', '\1', 'g')
|
||||
endfunction
|
||||
|
||||
function! s:iconv(expr, from, to) abort
|
||||
if a:from ==# '' || a:to ==# '' || a:from ==? a:to
|
||||
return a:expr
|
||||
endif
|
||||
let result = iconv(a:expr, a:from, a:to)
|
||||
return empty(result) ? a:expr : result
|
||||
endfunction
|
||||
|
||||
" NOTE:
|
||||
" A definition of a TEXT file is "A file that contains characters organized
|
||||
" into one or more lines."
|
||||
" A definition of a LINE is "A sequence of zero or more non- <newline>s
|
||||
" plus a terminating <newline>"
|
||||
" That's why {stdin} always ends with <newline> ideally. However, there are
|
||||
" some programs which does not follow the POSIX rule and a Vim's way to join
|
||||
" List into TEXT; join({text}, "\n"); does not add <newline> to the end of
|
||||
" the last line.
|
||||
" That's why add a trailing <newline> if it does not exist.
|
||||
" REF:
|
||||
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392
|
||||
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205
|
||||
" :help split()
|
||||
" NOTE:
|
||||
" it does nothing if the text is a correct POSIX text
|
||||
function! s:repair_posix_text(text, ...) abort
|
||||
let newline = get(a:000, 0, "\n")
|
||||
return a:text =~# '\n$' ? a:text : a:text . newline
|
||||
endfunction
|
||||
|
||||
" NOTE:
|
||||
" A definition of a TEXT file is "A file that contains characters organized
|
||||
" into one or more lines."
|
||||
" A definition of a LINE is "A sequence of zero or more non- <newline>s
|
||||
" plus a terminating <newline>"
|
||||
" REF:
|
||||
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392
|
||||
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205
|
||||
function! s:join_posix_lines(lines, ...) abort
|
||||
let newline = get(a:000, 0, "\n")
|
||||
return join(a:lines, newline) . newline
|
||||
endfunction
|
||||
|
||||
" NOTE:
|
||||
" A definition of a TEXT file is "A file that contains characters organized
|
||||
" into one or more lines."
|
||||
" A definition of a LINE is "A sequence of zero or more non- <newline>s
|
||||
" plus a terminating <newline>"
|
||||
" TEXT into List; split({text}, '\r\?\n', 1); add an extra empty line at the
|
||||
" end of List because the end of TEXT ends with <newline> and keepempty=1 is
|
||||
" specified. (btw. keepempty=0 cannot be used because it will remove
|
||||
" emptylines in the head and the tail).
|
||||
" That's why removing a trailing <newline> before proceeding to 'split' is required
|
||||
" REF:
|
||||
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_392
|
||||
" http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_205
|
||||
function! s:split_posix_text(text, ...) abort
|
||||
let newline = get(a:000, 0, '\r\?\n')
|
||||
let text = substitute(a:text, newline . '$', '', '')
|
||||
return split(text, newline, 1)
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
" vim:set et ts=2 sts=2 sw=2 tw=0:
|
423
bundle/vim-grammarous/autoload/vital/_grammarous/OptionParser.vim
vendored
Normal file
423
bundle/vim-grammarous/autoload/vital/_grammarous/OptionParser.vim
vendored
Normal file
@ -0,0 +1,423 @@
|
||||
" ___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#_grammarous#OptionParser#import() abort
|
||||
return map({'_vital_depends': '', 'new': '', '_vital_loaded': ''}, 'function("s:" . v:key)')
|
||||
endfunction
|
||||
else
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_grammarous#OptionParser#import() abort', printf("return map({'_vital_depends': '', 'new': '', '_vital_loaded': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
endif
|
||||
" ___vital___
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
let s:_STRING_TYPE = type('')
|
||||
let s:_LIST_TYPE = type([])
|
||||
let s:_DICT_TYPE = type({})
|
||||
let s:_NUM_TYPE = type(0)
|
||||
|
||||
function! s:_vital_loaded(V) abort
|
||||
let s:L = a:V.import('Data.List')
|
||||
endfunction
|
||||
|
||||
function! s:_vital_depends() abort
|
||||
return ['Data.List']
|
||||
endfunction
|
||||
|
||||
let s:_PRESET_COMPLETER = {}
|
||||
function! s:_PRESET_COMPLETER.file(optlead, cmdline, cursorpos) abort
|
||||
let candidates = glob(a:optlead . '*', 0, 1)
|
||||
if a:optlead =~# '^\~'
|
||||
let home_matcher = '^' . expand('~') . '/'
|
||||
call map(candidates, "substitute(v:val, home_matcher, '~/', '')")
|
||||
endif
|
||||
call map(candidates, "escape(isdirectory(v:val) ? v:val.'/' : v:val, ' \\')")
|
||||
return candidates
|
||||
endfunction
|
||||
|
||||
function! s:_make_option_description_for_help(opt) abort
|
||||
let extra = ''
|
||||
if has_key(a:opt, 'default_value')
|
||||
let extra .= 'DEFAULT: ' . string(a:opt.default_value) . ', '
|
||||
endif
|
||||
if get(a:opt, 'required_option', 0)
|
||||
let extra .= 'REQUIRED, '
|
||||
endif
|
||||
if has_key(a:opt, 'pattern_option')
|
||||
let extra .= 'PATTERN: ' . string(a:opt.pattern_option) . ', '
|
||||
endif
|
||||
let extra = substitute(extra, ', $', '', '')
|
||||
if extra !=# ''
|
||||
let extra = ' (' . extra . ')'
|
||||
endif
|
||||
return a:opt.description . extra
|
||||
endfunction
|
||||
|
||||
function! s:_make_option_definition_for_help(opt) abort
|
||||
let key = a:opt.definition
|
||||
if has_key(a:opt, 'short_option_definition')
|
||||
let key .= ', ' . a:opt.short_option_definition
|
||||
endif
|
||||
return key
|
||||
endfunction
|
||||
|
||||
function! s:_extract_special_opts(argc, argv) abort
|
||||
let ret = {'specials' : {}}
|
||||
if a:argc <= 0
|
||||
return ret
|
||||
endif
|
||||
|
||||
let ret.q_args = a:argv[0]
|
||||
for arg in a:argv[1:]
|
||||
let arg_type = type(arg)
|
||||
if arg_type == s:_LIST_TYPE
|
||||
let ret.specials.__range__ = arg
|
||||
elseif arg_type == type(0)
|
||||
let ret.specials.__count__ = arg
|
||||
elseif arg_type == s:_STRING_TYPE
|
||||
if arg ==# '!'
|
||||
let ret.specials.__bang__ = arg
|
||||
elseif arg !=# ''
|
||||
let ret.specials.__reg__ = arg
|
||||
endif
|
||||
endif
|
||||
unlet arg
|
||||
endfor
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
function! s:_make_args(cmd_args) abort
|
||||
let type = type(a:cmd_args)
|
||||
if type == s:_STRING_TYPE
|
||||
return split(a:cmd_args)
|
||||
elseif type == s:_LIST_TYPE
|
||||
return map(copy(a:cmd_args), 'type(v:val) == s:_STRING_TYPE ? v:val : string(v:val)')
|
||||
else
|
||||
throw 'vital: OptionParser: Invalid type: first argument of parse() should be string or list of string'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:_expand_short_option(arg, options) abort
|
||||
let short_opt = matchstr(a:arg, '^-[^- =]\>')
|
||||
for [name, value] in items(a:options)
|
||||
if get(value, 'short_option_definition', '') ==# short_opt
|
||||
return substitute(a:arg, short_opt, '--' . name, '')
|
||||
endif
|
||||
endfor
|
||||
return a:arg
|
||||
endfunction
|
||||
|
||||
function! s:_check_extra_option(parsed_args, options) abort
|
||||
for [name, option] in items(a:options)
|
||||
if has_key(option, 'default_value') && ! has_key(a:parsed_args, name)
|
||||
let a:parsed_args[name] = option.default_value
|
||||
endif
|
||||
if get(option, 'required_option', 0) && ! has_key(a:parsed_args, name)
|
||||
throw 'vital: OptionParser: parameter is required: ' . name
|
||||
endif
|
||||
if has_key(option, 'pattern_option') && has_key(a:parsed_args, name) && a:parsed_args[name] !~# option.pattern_option
|
||||
throw 'vital: OptionParser: parameter doesn''t match pattern: ' . name . ' ' . option.pattern_option
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:_parse_arg(arg, options) abort
|
||||
" if --no-hoge pattern
|
||||
if a:arg =~# '^--no-[^= ]\+'
|
||||
" get hoge from --no-hoge
|
||||
let key = matchstr(a:arg, '^--no-\zs[^= ]\+')
|
||||
if has_key(a:options, key) && has_key(a:options[key], 'no')
|
||||
return [key, 0]
|
||||
endif
|
||||
|
||||
" if --hoge pattern
|
||||
elseif a:arg =~# '^--[^= ]\+$'
|
||||
" get hoge from --hoge
|
||||
let key = matchstr(a:arg, '^--\zs[^= ]\+')
|
||||
if has_key(a:options, key)
|
||||
if has_key(a:options[key], 'has_value')
|
||||
throw 'vital: OptionParser: Must specify value for option: ' . key
|
||||
endif
|
||||
return [key, 1]
|
||||
endif
|
||||
|
||||
" if --hoge=poyo pattern
|
||||
else
|
||||
" get hoge from --hoge=poyo
|
||||
let key = matchstr(a:arg, '^--\zs[^= ]\+')
|
||||
if has_key(a:options, key)
|
||||
" get poyo from --hoge=poyo
|
||||
return [key, matchstr(a:arg, '^--[^= ]\+=\zs\S\+$')]
|
||||
endif
|
||||
endif
|
||||
|
||||
return a:arg
|
||||
endfunction
|
||||
|
||||
function! s:_parse_args(cmd_args, options) abort
|
||||
let parsed_args = {}
|
||||
let unknown_args = []
|
||||
let args = s:_make_args(a:cmd_args)
|
||||
|
||||
for arg in args
|
||||
|
||||
" replace short option with long option if short option is available
|
||||
if arg =~# '^-[^- =]\>'
|
||||
let arg = s:_expand_short_option(arg, a:options)
|
||||
endif
|
||||
|
||||
" check if arg is --[no-]hoge[=VALUE]
|
||||
if arg !~# '^--\%(no-\)\=[^= ]\+\%(=\S\+\)\=$'
|
||||
call add(unknown_args, arg)
|
||||
continue
|
||||
endif
|
||||
|
||||
let parsed_arg = s:_parse_arg(arg, a:options)
|
||||
if type(parsed_arg) == s:_LIST_TYPE
|
||||
let parsed_args[parsed_arg[0]] = parsed_arg[1]
|
||||
else
|
||||
call add(unknown_args, parsed_arg)
|
||||
endif
|
||||
unlet parsed_arg
|
||||
endfor
|
||||
|
||||
return [parsed_args, unknown_args]
|
||||
endfunction
|
||||
|
||||
let s:_DEFAULT_PARSER = {'options' : {}}
|
||||
|
||||
function! s:_DEFAULT_PARSER.help() abort
|
||||
let definitions = map(values(self.options), '[s:_make_option_definition_for_help(v:val), s:_make_option_description_for_help(v:val)]')
|
||||
let key_width = len(s:L.max_by(definitions, 'len(v:val[0])')[0])
|
||||
return "Options:\n" .
|
||||
\ join(map(definitions, '
|
||||
\ " " . v:val[0] .
|
||||
\ repeat(" ", key_width - len(v:val[0])) . " : " .
|
||||
\ v:val[1]
|
||||
\ '), "\n")
|
||||
endfunction
|
||||
|
||||
function! s:_DEFAULT_PARSER.parse(...) abort
|
||||
let opts = s:_extract_special_opts(a:0, a:000)
|
||||
if ! has_key(opts, 'q_args')
|
||||
return opts.specials
|
||||
endif
|
||||
|
||||
if ! get(self, 'disable_auto_help', 0)
|
||||
\ && opts.q_args ==# '--help'
|
||||
\ && ! has_key(self.options, 'help')
|
||||
echo self.help()
|
||||
return extend(opts.specials, {'help' : 1, '__unknown_args__' : []})
|
||||
endif
|
||||
|
||||
let parsed_args = s:_parse_args(opts.q_args, self.options)
|
||||
|
||||
let ret = parsed_args[0]
|
||||
call s:_check_extra_option(ret, self.options)
|
||||
call extend(ret, opts.specials)
|
||||
let ret.__unknown_args__ = parsed_args[1]
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
function! s:_DEFAULT_PARSER.on(def, desc, ...) abort
|
||||
if a:0 > 1
|
||||
throw 'vital: OptionParser: Wrong number of arguments: ' . a:0 + 2 . ' for 2 or 3'
|
||||
endif
|
||||
|
||||
" get hoge and huga from --hoge=huga
|
||||
let matched = matchlist(a:def, '^--\([^= ]\+\)\(=\S\+\)\=$')[1:2]
|
||||
if len(matched) != 2
|
||||
throw 'vital: OptionParser: Invalid option "' . a:def . '"'
|
||||
endif
|
||||
let [name, value] = matched
|
||||
let has_value = value !=# ''
|
||||
|
||||
let no = name =~# '^\[no-]'
|
||||
if no
|
||||
let name = matchstr(name, '^\[no-]\zs.\+')
|
||||
endif
|
||||
|
||||
if name ==# ''
|
||||
throw 'vital: OptionParser: Option of key is invalid: ' . a:def
|
||||
endif
|
||||
|
||||
let self.options[name] = {'definition' : a:def, 'description' : a:desc}
|
||||
if no
|
||||
let self.options[name].no = 1
|
||||
endif
|
||||
if has_value
|
||||
let self.options[name].has_value = 1
|
||||
endif
|
||||
|
||||
" if short option is specified
|
||||
if a:0 == 1
|
||||
if type(a:1) == type({})
|
||||
if has_key(a:1, 'short')
|
||||
if (a:1.short !~# '^-[[:alnum:]]\>')
|
||||
throw 'vital: OptionParser: Invalid short option: ' . a:1.short
|
||||
endif
|
||||
let self.options[name].short_option_definition = a:1.short
|
||||
endif
|
||||
if has_key(a:1, 'default')
|
||||
let self.options[name].default_value = a:1.default
|
||||
endif
|
||||
if has_key(a:1, 'completion')
|
||||
if type(a:1.completion) == s:_STRING_TYPE
|
||||
let self.options[name].completion = s:_PRESET_COMPLETER[a:1.completion]
|
||||
else
|
||||
let self.options[name].completion = a:1.completion
|
||||
endif
|
||||
endif
|
||||
if has_key(a:1, 'required')
|
||||
if a:1.required isnot 0 && a:1.required isnot 1
|
||||
throw 'vital: OptionParser: Invalid required option: ' . string(a:1.required)
|
||||
endif
|
||||
let self.options[name].required_option = a:1.required
|
||||
endif
|
||||
if has_key(a:1, 'pattern')
|
||||
try
|
||||
call match('', a:1.pattern)
|
||||
catch
|
||||
throw printf('vital: OptionParser: Invalid pattern option: exception="%s" pattern="%s"', v:exception, a:1.pattern)
|
||||
endtry
|
||||
let self.options[name].pattern_option = a:1.pattern
|
||||
endif
|
||||
else
|
||||
let self.options[name].default_value = a:1
|
||||
endif
|
||||
endif
|
||||
|
||||
return self
|
||||
endfunction
|
||||
|
||||
function! s:_complete_long_option(arglead, options) abort
|
||||
let candidates = []
|
||||
for [name, option] in items(a:options)
|
||||
let has_value = get(option, 'has_value', 0)
|
||||
call add(candidates, '--' . name . (has_value ? '=' : ''))
|
||||
if get(option, 'no', 0)
|
||||
call add(candidates, '--no-' . name . (has_value ? '=' : ''))
|
||||
endif
|
||||
endfor
|
||||
let lead_pattern = '^' . a:arglead
|
||||
return filter(candidates, 'v:val =~# lead_pattern')
|
||||
endfunction
|
||||
|
||||
function! s:_complete_short_option(arglead, options) abort
|
||||
let candidates = []
|
||||
for option in values(a:options)
|
||||
let has_value = get(option, 'has_value', 0)
|
||||
if has_key(option, 'short_option_definition')
|
||||
call add(candidates, option.short_option_definition . (has_value ? '=' : ''))
|
||||
if get(option, 'no', 0)
|
||||
call add(candidates, '-no' . option.short_option_definition . (has_value ? '=' : ''))
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
let lead_pattern = '^' . a:arglead
|
||||
return filter(candidates, 'v:val =~# lead_pattern')
|
||||
endfunction
|
||||
|
||||
function! s:_complete_user_specified_option(options, arglead, cmdline, cursorpos) abort
|
||||
let lead = matchstr(a:arglead, '=\zs.*$')
|
||||
let name = matchstr(a:arglead, '^--\zs[^=]\+')
|
||||
if ! has_key(a:options, name) || ! has_key(a:options[name], 'completion')
|
||||
return []
|
||||
endif
|
||||
return a:options[name].completion(lead, a:cmdline, a:cursorpos)
|
||||
endfunction
|
||||
|
||||
function! s:_complete_user_specified_short_option(options, arglead, cmdline, cursorpos) abort
|
||||
let lead = matchstr(a:arglead, '=\zs.*$')
|
||||
let def = matchstr(a:arglead, '^-[^-=]')
|
||||
for option in values(a:options)
|
||||
if has_key(option, 'short_option_definition')
|
||||
\ && option.short_option_definition ==# def
|
||||
\ && has_key(option, 'completion')
|
||||
return option.completion(lead, a:cmdline, a:cursorpos)
|
||||
endif
|
||||
endfor
|
||||
return []
|
||||
endfunction
|
||||
|
||||
function! s:_complete_unknown_option(Completer, arglead, cmdline, cursorpos) abort
|
||||
if type(a:Completer) == s:_STRING_TYPE
|
||||
return s:_PRESET_COMPLETER[a:Completer](a:arglead, a:cmdline, a:cursorpos)
|
||||
else
|
||||
return a:Completer(a:arglead, a:cmdline, a:cursorpos)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:_DEFAULT_PARSER.complete(arglead, cmdline, cursorpos) abort
|
||||
if a:arglead =~# '^--[^=]*$'
|
||||
" when long option
|
||||
return s:_complete_long_option(a:arglead, self.options)
|
||||
|
||||
elseif a:arglead =~# '^-[^-=]\?$'
|
||||
" when short option
|
||||
return s:_complete_short_option(a:arglead, self.options)
|
||||
|
||||
elseif a:arglead =~# '^--.\+=.*$'
|
||||
let prefix = matchstr(a:arglead, '^.\+=')
|
||||
return map(
|
||||
\ s:_complete_user_specified_option(self.options, a:arglead, a:cmdline, a:cursorpos),
|
||||
\ 'prefix . v:val'
|
||||
\ )
|
||||
|
||||
elseif a:arglead =~# '^-[^-=]=.*$'
|
||||
let prefix = matchstr(a:arglead, '^-[^-=]=')
|
||||
return map(
|
||||
\ s:_complete_user_specified_short_option(self.options, a:arglead, a:cmdline, a:cursorpos),
|
||||
\ 'prefix . v:val'
|
||||
\ )
|
||||
|
||||
elseif has_key(self, 'unknown_options_completion')
|
||||
return s:_complete_unknown_option(self.unknown_options_completion, a:arglead, a:cmdline, a:cursorpos)
|
||||
endif
|
||||
|
||||
return []
|
||||
endfunction
|
||||
|
||||
function! s:_DEFAULT_PARSER.complete_greedily(arglead, cmdline, cursorpos) abort
|
||||
|
||||
if a:arglead =~# '^--.\+=.*$'
|
||||
let prefix = matchstr(a:arglead, '^.\+=')
|
||||
return map(
|
||||
\ s:_complete_user_specified_option(self.options, a:arglead, a:cmdline, a:cursorpos),
|
||||
\ 'prefix . v:val'
|
||||
\ )
|
||||
|
||||
elseif a:arglead =~# '^-[^-=]=.*$'
|
||||
let prefix = matchstr(a:arglead, '^-[^-=]=')
|
||||
return map(
|
||||
\ s:_complete_user_specified_short_option(self.options, a:arglead, a:cmdline, a:cursorpos),
|
||||
\ 'prefix . v:val'
|
||||
\ )
|
||||
endif
|
||||
|
||||
let long_opts = s:_complete_long_option(a:arglead, self.options)
|
||||
if has_key(self, 'unknown_options_completion')
|
||||
return long_opts + s:_complete_unknown_option(
|
||||
\ self.unknown_options_completion,
|
||||
\ a:arglead,
|
||||
\ a:cmdline,
|
||||
\ a:cursorpos
|
||||
\ )
|
||||
else
|
||||
return long_opts
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:new() abort
|
||||
return deepcopy(s:_DEFAULT_PARSER)
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
||||
" vim:set et ts=2 sts=2 sw=2 tw=0:
|
430
bundle/vim-grammarous/autoload/vital/_grammarous/Prelude.vim
vendored
Normal file
430
bundle/vim-grammarous/autoload/vital/_grammarous/Prelude.vim
vendored
Normal file
@ -0,0 +1,430 @@
|
||||
" ___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#_grammarous#Prelude#import() abort
|
||||
return map({'escape_pattern': '', 'is_funcref': '', 'path2directory': '', 'wcswidth': '', 'is_string': '', 'input_helper': '', 'is_number': '', 'is_cygwin': '', 'path2project_directory': '', 'strwidthpart_reverse': '', 'input_safe': '', 'is_list': '', 'truncate_skipping': '', 'glob': '', 'truncate': '', 'is_dict': '', 'set_default': '', 'is_numeric': '', 'getchar_safe': '', 'substitute_path_separator': '', 'is_mac': '', 'strwidthpart': '', 'getchar': '', 'is_unix': '', 'is_windows': '', 'globpath': '', 'escape_file_searching': '', 'is_float': '', 'smart_execute_command': ''}, 'function("s:" . v:key)')
|
||||
endfunction
|
||||
else
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_grammarous#Prelude#import() abort', printf("return map({'escape_pattern': '', 'is_funcref': '', 'path2directory': '', 'wcswidth': '', 'is_string': '', 'input_helper': '', 'is_number': '', 'is_cygwin': '', 'path2project_directory': '', 'strwidthpart_reverse': '', 'input_safe': '', 'is_list': '', 'truncate_skipping': '', 'glob': '', 'truncate': '', 'is_dict': '', 'set_default': '', 'is_numeric': '', 'getchar_safe': '', 'substitute_path_separator': '', 'is_mac': '', 'strwidthpart': '', 'getchar': '', 'is_unix': '', 'is_windows': '', 'globpath': '', 'escape_file_searching': '', 'is_float': '', 'smart_execute_command': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
endif
|
||||
" ___vital___
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
if v:version > 703 ||
|
||||
\ (v:version == 703 && has('patch465'))
|
||||
function! s:glob(expr) abort
|
||||
return glob(a:expr, 1, 1)
|
||||
endfunction
|
||||
else
|
||||
function! s:glob(expr) abort
|
||||
return split(glob(a:expr, 1), '\n')
|
||||
endfunction
|
||||
endif
|
||||
|
||||
if v:version > 704 ||
|
||||
\ (v:version == 704 && has('patch279'))
|
||||
function! s:globpath(path, expr) abort
|
||||
return globpath(a:path, a:expr, 1, 1)
|
||||
endfunction
|
||||
else
|
||||
function! s:globpath(path, expr) abort
|
||||
return split(globpath(a:path, a:expr, 1), '\n')
|
||||
endfunction
|
||||
endif
|
||||
|
||||
" Wrapper functions for type().
|
||||
" NOTE: __TYPE_FLOAT = -1 when -float.
|
||||
" this doesn't match to anything.
|
||||
if has('patch-7.4.2071')
|
||||
let [
|
||||
\ s:__TYPE_NUMBER,
|
||||
\ s:__TYPE_STRING,
|
||||
\ s:__TYPE_FUNCREF,
|
||||
\ s:__TYPE_LIST,
|
||||
\ s:__TYPE_DICT,
|
||||
\ s:__TYPE_FLOAT] = [
|
||||
\ v:t_number,
|
||||
\ v:t_string,
|
||||
\ v:t_func,
|
||||
\ v:t_list,
|
||||
\ v:t_dict,
|
||||
\ v:t_float]
|
||||
else
|
||||
let [
|
||||
\ s:__TYPE_NUMBER,
|
||||
\ s:__TYPE_STRING,
|
||||
\ s:__TYPE_FUNCREF,
|
||||
\ s:__TYPE_LIST,
|
||||
\ s:__TYPE_DICT,
|
||||
\ s:__TYPE_FLOAT] = [
|
||||
\ type(3),
|
||||
\ type(''),
|
||||
\ type(function('tr')),
|
||||
\ type([]),
|
||||
\ type({}),
|
||||
\ has('float') ? type(str2float('0')) : -1]
|
||||
endif
|
||||
|
||||
" Number or Float
|
||||
function! s:is_numeric(Value) abort
|
||||
let _ = type(a:Value)
|
||||
return _ ==# s:__TYPE_NUMBER
|
||||
\ || _ ==# s:__TYPE_FLOAT
|
||||
endfunction
|
||||
|
||||
" Number
|
||||
function! s:is_number(Value) abort
|
||||
return type(a:Value) ==# s:__TYPE_NUMBER
|
||||
endfunction
|
||||
|
||||
" String
|
||||
function! s:is_string(Value) abort
|
||||
return type(a:Value) ==# s:__TYPE_STRING
|
||||
endfunction
|
||||
|
||||
" Funcref
|
||||
function! s:is_funcref(Value) abort
|
||||
return type(a:Value) ==# s:__TYPE_FUNCREF
|
||||
endfunction
|
||||
|
||||
" List
|
||||
function! s:is_list(Value) abort
|
||||
return type(a:Value) ==# s:__TYPE_LIST
|
||||
endfunction
|
||||
|
||||
" Dictionary
|
||||
function! s:is_dict(Value) abort
|
||||
return type(a:Value) ==# s:__TYPE_DICT
|
||||
endfunction
|
||||
|
||||
" Float
|
||||
function! s:is_float(Value) abort
|
||||
return type(a:Value) ==# s:__TYPE_FLOAT
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:truncate_skipping(str, max, footer_width, separator) abort
|
||||
call s:_warn_deprecated('truncate_skipping', 'Data.String.truncate_skipping')
|
||||
|
||||
let width = s:wcswidth(a:str)
|
||||
if width <= a:max
|
||||
let ret = a:str
|
||||
else
|
||||
let header_width = a:max - s:wcswidth(a:separator) - a:footer_width
|
||||
let ret = s:strwidthpart(a:str, header_width) . a:separator
|
||||
\ . s:strwidthpart_reverse(a:str, a:footer_width)
|
||||
endif
|
||||
|
||||
return s:truncate(ret, a:max)
|
||||
endfunction
|
||||
|
||||
function! s:truncate(str, width) abort
|
||||
" Original function is from mattn.
|
||||
" http://github.com/mattn/googlereader-vim/tree/master
|
||||
|
||||
call s:_warn_deprecated('truncate', 'Data.String.truncate')
|
||||
|
||||
if a:str =~# '^[\x00-\x7f]*$'
|
||||
return len(a:str) < a:width ?
|
||||
\ printf('%-'.a:width.'s', a:str) : strpart(a:str, 0, a:width)
|
||||
endif
|
||||
|
||||
let ret = a:str
|
||||
let width = s:wcswidth(a:str)
|
||||
if width > a:width
|
||||
let ret = s:strwidthpart(ret, a:width)
|
||||
let width = s:wcswidth(ret)
|
||||
endif
|
||||
|
||||
if width < a:width
|
||||
let ret .= repeat(' ', a:width - width)
|
||||
endif
|
||||
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
function! s:strwidthpart(str, width) abort
|
||||
call s:_warn_deprecated('strwidthpart', 'Data.String.strwidthpart')
|
||||
|
||||
if a:width <= 0
|
||||
return ''
|
||||
endif
|
||||
let ret = a:str
|
||||
let width = s:wcswidth(a:str)
|
||||
while width > a:width
|
||||
let char = matchstr(ret, '.$')
|
||||
let ret = ret[: -1 - len(char)]
|
||||
let width -= s:wcswidth(char)
|
||||
endwhile
|
||||
|
||||
return ret
|
||||
endfunction
|
||||
function! s:strwidthpart_reverse(str, width) abort
|
||||
call s:_warn_deprecated('strwidthpart_reverse', 'Data.String.strwidthpart_reverse')
|
||||
|
||||
if a:width <= 0
|
||||
return ''
|
||||
endif
|
||||
let ret = a:str
|
||||
let width = s:wcswidth(a:str)
|
||||
while width > a:width
|
||||
let char = matchstr(ret, '^.')
|
||||
let ret = ret[len(char) :]
|
||||
let width -= s:wcswidth(char)
|
||||
endwhile
|
||||
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
if v:version >= 703
|
||||
" Use builtin function.
|
||||
function! s:wcswidth(str) abort
|
||||
call s:_warn_deprecated('wcswidth', 'Data.String.wcswidth')
|
||||
return strwidth(a:str)
|
||||
endfunction
|
||||
else
|
||||
function! s:wcswidth(str) abort
|
||||
call s:_warn_deprecated('wcswidth', 'Data.String.wcswidth')
|
||||
|
||||
if a:str =~# '^[\x00-\x7f]*$'
|
||||
return strlen(a:str)
|
||||
end
|
||||
|
||||
let mx_first = '^\(.\)'
|
||||
let str = a:str
|
||||
let width = 0
|
||||
while 1
|
||||
let ucs = char2nr(substitute(str, mx_first, '\1', ''))
|
||||
if ucs == 0
|
||||
break
|
||||
endif
|
||||
let width += s:_wcwidth(ucs)
|
||||
let str = substitute(str, mx_first, '', '')
|
||||
endwhile
|
||||
return width
|
||||
endfunction
|
||||
|
||||
" UTF-8 only.
|
||||
function! s:_wcwidth(ucs) abort
|
||||
let ucs = a:ucs
|
||||
if (ucs >= 0x1100
|
||||
\ && (ucs <= 0x115f
|
||||
\ || ucs == 0x2329
|
||||
\ || ucs == 0x232a
|
||||
\ || (ucs >= 0x2e80 && ucs <= 0xa4cf
|
||||
\ && ucs != 0x303f)
|
||||
\ || (ucs >= 0xac00 && ucs <= 0xd7a3)
|
||||
\ || (ucs >= 0xf900 && ucs <= 0xfaff)
|
||||
\ || (ucs >= 0xfe30 && ucs <= 0xfe6f)
|
||||
\ || (ucs >= 0xff00 && ucs <= 0xff60)
|
||||
\ || (ucs >= 0xffe0 && ucs <= 0xffe6)
|
||||
\ || (ucs >= 0x20000 && ucs <= 0x2fffd)
|
||||
\ || (ucs >= 0x30000 && ucs <= 0x3fffd)
|
||||
\ ))
|
||||
return 2
|
||||
endif
|
||||
return 1
|
||||
endfunction
|
||||
endif
|
||||
|
||||
let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95')
|
||||
let s:is_cygwin = has('win32unix')
|
||||
let s:is_mac = !s:is_windows && !s:is_cygwin
|
||||
\ && (has('mac') || has('macunix') || has('gui_macvim') ||
|
||||
\ (!isdirectory('/proc') && executable('sw_vers')))
|
||||
let s:is_unix = has('unix')
|
||||
|
||||
function! s:is_windows() abort
|
||||
return s:is_windows
|
||||
endfunction
|
||||
|
||||
function! s:is_cygwin() abort
|
||||
return s:is_cygwin
|
||||
endfunction
|
||||
|
||||
function! s:is_mac() abort
|
||||
return s:is_mac
|
||||
endfunction
|
||||
|
||||
function! s:is_unix() abort
|
||||
return s:is_unix
|
||||
endfunction
|
||||
|
||||
function! s:_warn_deprecated(name, alternative) abort
|
||||
try
|
||||
echohl Error
|
||||
echomsg 'Prelude.' . a:name . ' is deprecated! Please use ' . a:alternative . ' instead.'
|
||||
finally
|
||||
echohl None
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:smart_execute_command(action, word) abort
|
||||
execute a:action . ' ' . (a:word ==# '' ? '' : '`=a:word`')
|
||||
endfunction
|
||||
|
||||
function! s:escape_file_searching(buffer_name) abort
|
||||
return escape(a:buffer_name, '*[]?{}, ')
|
||||
endfunction
|
||||
|
||||
function! s:escape_pattern(str) abort
|
||||
call s:_warn_deprecated(
|
||||
\ 'escape_pattern',
|
||||
\ 'Data.String.escape_pattern',
|
||||
\)
|
||||
return escape(a:str, '~"\.^$[]*')
|
||||
endfunction
|
||||
|
||||
function! s:getchar(...) abort
|
||||
let c = call('getchar', a:000)
|
||||
return type(c) == type(0) ? nr2char(c) : c
|
||||
endfunction
|
||||
|
||||
function! s:getchar_safe(...) abort
|
||||
let c = s:input_helper('getchar', a:000)
|
||||
return type(c) == type('') ? c : nr2char(c)
|
||||
endfunction
|
||||
|
||||
function! s:input_safe(...) abort
|
||||
return s:input_helper('input', a:000)
|
||||
endfunction
|
||||
|
||||
function! s:input_helper(funcname, args) abort
|
||||
let success = 0
|
||||
if inputsave() !=# success
|
||||
throw 'vital: Prelude: inputsave() failed'
|
||||
endif
|
||||
try
|
||||
return call(a:funcname, a:args)
|
||||
finally
|
||||
if inputrestore() !=# success
|
||||
throw 'vital: Prelude: inputrestore() failed'
|
||||
endif
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:set_default(var, val) abort
|
||||
if !exists(a:var) || type({a:var}) != type(a:val)
|
||||
let {a:var} = a:val
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:substitute_path_separator(path) abort
|
||||
return s:is_windows ? substitute(a:path, '\\', '/', 'g') : a:path
|
||||
endfunction
|
||||
|
||||
function! s:path2directory(path) abort
|
||||
return s:substitute_path_separator(isdirectory(a:path) ? a:path : fnamemodify(a:path, ':p:h'))
|
||||
endfunction
|
||||
|
||||
function! s:_path2project_directory_git(path) abort
|
||||
let parent = a:path
|
||||
|
||||
while 1
|
||||
let path = parent . '/.git'
|
||||
if isdirectory(path) || filereadable(path)
|
||||
return parent
|
||||
endif
|
||||
let next = fnamemodify(parent, ':h')
|
||||
if next == parent
|
||||
return ''
|
||||
endif
|
||||
let parent = next
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
function! s:_path2project_directory_svn(path) abort
|
||||
let search_directory = a:path
|
||||
let directory = ''
|
||||
|
||||
let find_directory = s:escape_file_searching(search_directory)
|
||||
let d = finddir('.svn', find_directory . ';')
|
||||
if d ==# ''
|
||||
return ''
|
||||
endif
|
||||
|
||||
let directory = fnamemodify(d, ':p:h:h')
|
||||
|
||||
" Search parent directories.
|
||||
let parent_directory = s:path2directory(
|
||||
\ fnamemodify(directory, ':h'))
|
||||
|
||||
if parent_directory !=# ''
|
||||
let d = finddir('.svn', parent_directory . ';')
|
||||
if d !=# ''
|
||||
let directory = s:_path2project_directory_svn(parent_directory)
|
||||
endif
|
||||
endif
|
||||
return directory
|
||||
endfunction
|
||||
|
||||
function! s:_path2project_directory_others(vcs, path) abort
|
||||
let vcs = a:vcs
|
||||
let search_directory = a:path
|
||||
|
||||
let find_directory = s:escape_file_searching(search_directory)
|
||||
let d = finddir(vcs, find_directory . ';')
|
||||
if d ==# ''
|
||||
return ''
|
||||
endif
|
||||
return fnamemodify(d, ':p:h:h')
|
||||
endfunction
|
||||
|
||||
function! s:path2project_directory(path, ...) abort
|
||||
let is_allow_empty = get(a:000, 0, 0)
|
||||
let search_directory = s:path2directory(a:path)
|
||||
let directory = ''
|
||||
|
||||
" Search VCS directory.
|
||||
for vcs in ['.git', '.bzr', '.hg', '.svn']
|
||||
if vcs ==# '.git'
|
||||
let directory = s:_path2project_directory_git(search_directory)
|
||||
elseif vcs ==# '.svn'
|
||||
let directory = s:_path2project_directory_svn(search_directory)
|
||||
else
|
||||
let directory = s:_path2project_directory_others(vcs, search_directory)
|
||||
endif
|
||||
if directory !=# ''
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
" Search project file.
|
||||
if directory ==# ''
|
||||
for d in ['build.xml', 'prj.el', '.project', 'pom.xml', 'package.json',
|
||||
\ 'Makefile', 'configure', 'Rakefile', 'NAnt.build',
|
||||
\ 'P4CONFIG', 'tags', 'gtags']
|
||||
let d = findfile(d, s:escape_file_searching(search_directory) . ';')
|
||||
if d !=# ''
|
||||
let directory = fnamemodify(d, ':p:h')
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
if directory ==# ''
|
||||
" Search /src/ directory.
|
||||
let base = s:substitute_path_separator(search_directory)
|
||||
if base =~# '/src/'
|
||||
let directory = base[: strridx(base, '/src/') + 3]
|
||||
endif
|
||||
endif
|
||||
|
||||
if directory ==# '' && !is_allow_empty
|
||||
" Use original path.
|
||||
let directory = search_directory
|
||||
endif
|
||||
|
||||
return s:substitute_path_separator(directory)
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
||||
" vim:set et ts=2 sts=2 sw=2 tw=0:
|
184
bundle/vim-grammarous/autoload/vital/_grammarous/Process.vim
vendored
Normal file
184
bundle/vim-grammarous/autoload/vital/_grammarous/Process.vim
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
" ___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#_grammarous#Process#import() abort
|
||||
return map({'shellescape': '', 'has_vimproc': '', 'system': '', 'iconv': '', 'spawn': '', 'get_last_status': ''}, 'function("s:" . v:key)')
|
||||
endfunction
|
||||
else
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_grammarous#Process#import() abort', printf("return map({'shellescape': '', 'has_vimproc': '', 'system': '', 'iconv': '', 'spawn': '', 'get_last_status': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
endif
|
||||
" ___vital___
|
||||
" TODO: move all comments to doc file.
|
||||
"
|
||||
"
|
||||
" FIXME: This module name should be Vital.System ?
|
||||
" But the name has been already taken.
|
||||
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
|
||||
" FIXME: Unfortunately, can't use s:_vital_loaded() for this purpose.
|
||||
" Because these variables are used when this script file is loaded.
|
||||
let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95')
|
||||
let s:is_unix = has('unix')
|
||||
" As of 7.4.122, the system()'s 1st argument is converted internally by Vim.
|
||||
" Note that Patch 7.4.122 does not convert system()'s 2nd argument and
|
||||
" return-value. We must convert them manually.
|
||||
let s:need_trans = v:version < 704 || (v:version == 704 && !has('patch122'))
|
||||
|
||||
let s:TYPE_DICT = type({})
|
||||
let s:TYPE_LIST = type([])
|
||||
let s:TYPE_STRING = type('')
|
||||
|
||||
function! s:spawn(expr, ...) abort
|
||||
let shellslash = 0
|
||||
if s:is_windows
|
||||
let shellslash = &l:shellslash
|
||||
setlocal noshellslash
|
||||
endif
|
||||
try
|
||||
if type(a:expr) is s:TYPE_LIST
|
||||
let special = 1
|
||||
let cmdline = join(map(a:expr, 'shellescape(v:val, special)'), ' ')
|
||||
elseif type(a:expr) is s:TYPE_STRING
|
||||
let cmdline = a:expr
|
||||
if a:0 && a:1
|
||||
" for :! command
|
||||
let cmdline = substitute(cmdline, '\([!%#]\|<[^<>]\+>\)', '\\\1', 'g')
|
||||
endif
|
||||
else
|
||||
throw 'vital: Process: invalid argument (value type:' . type(a:expr) . ')'
|
||||
endif
|
||||
if s:is_windows
|
||||
silent execute '!start' cmdline
|
||||
else
|
||||
silent execute '!' cmdline '&'
|
||||
endif
|
||||
finally
|
||||
if s:is_windows
|
||||
let &l:shellslash = shellslash
|
||||
endif
|
||||
endtry
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
" iconv() wrapper for safety.
|
||||
function! s:iconv(expr, from, to) abort
|
||||
if a:from ==# '' || a:to ==# '' || a:from ==? a:to
|
||||
return a:expr
|
||||
endif
|
||||
let result = iconv(a:expr, a:from, a:to)
|
||||
return result !=# '' ? result : a:expr
|
||||
endfunction
|
||||
|
||||
" Check vimproc.
|
||||
function! s:has_vimproc() abort
|
||||
if !exists('s:exists_vimproc')
|
||||
try
|
||||
call vimproc#version()
|
||||
let s:exists_vimproc = 1
|
||||
catch
|
||||
let s:exists_vimproc = 0
|
||||
endtry
|
||||
endif
|
||||
return s:exists_vimproc
|
||||
endfunction
|
||||
|
||||
" * {command} [, {input} [, {timeout}]]
|
||||
" * {command} [, {dict}]
|
||||
" {dict} = {
|
||||
" use_vimproc: bool,
|
||||
" input: string,
|
||||
" timeout: bool,
|
||||
" background: bool,
|
||||
" }
|
||||
function! s:system(str, ...) abort
|
||||
" Process optional arguments at first
|
||||
" because use_vimproc is required later
|
||||
" for a:str argument.
|
||||
let input = ''
|
||||
let use_vimproc = s:has_vimproc()
|
||||
let background = 0
|
||||
let args = []
|
||||
if a:0 ==# 1
|
||||
" {command} [, {dict}]
|
||||
" a:1 = {dict}
|
||||
if type(a:1) is s:TYPE_DICT
|
||||
if has_key(a:1, 'use_vimproc')
|
||||
let use_vimproc = a:1.use_vimproc
|
||||
endif
|
||||
if has_key(a:1, 'input')
|
||||
let args += [s:iconv(a:1.input, &encoding, 'char')]
|
||||
endif
|
||||
if use_vimproc && has_key(a:1, 'timeout')
|
||||
" ignores timeout unless you have vimproc.
|
||||
let args += [a:1.timeout]
|
||||
endif
|
||||
if has_key(a:1, 'background')
|
||||
let background = a:1.background
|
||||
endif
|
||||
elseif type(a:1) is s:TYPE_STRING
|
||||
let args += [s:iconv(a:1, &encoding, 'char')]
|
||||
else
|
||||
throw 'vital: Process: invalid argument (value type:' . type(a:1) . ')'
|
||||
endif
|
||||
elseif a:0 >= 2
|
||||
" {command} [, {input} [, {timeout}]]
|
||||
" a:000 = [{input} [, {timeout}]]
|
||||
let [input; rest] = a:000
|
||||
let input = s:iconv(input, &encoding, 'char')
|
||||
let args += [input] + rest
|
||||
endif
|
||||
|
||||
" Process a:str argument.
|
||||
if type(a:str) is s:TYPE_LIST
|
||||
let expr = use_vimproc ? '"''" . v:val . "''"' : 's:shellescape(v:val)'
|
||||
let command = join(map(copy(a:str), expr), ' ')
|
||||
elseif type(a:str) is s:TYPE_STRING
|
||||
let command = a:str
|
||||
else
|
||||
throw 'vital: Process: invalid argument (value type:' . type(a:str) . ')'
|
||||
endif
|
||||
if s:need_trans
|
||||
let command = s:iconv(command, &encoding, 'char')
|
||||
endif
|
||||
let args = [command] + args
|
||||
if background && (use_vimproc || !s:is_windows)
|
||||
if has('nvim')
|
||||
throw "vital: Process: neovim's system() doesn't support background(&) process (cmdline:" . a:str . ')'
|
||||
endif
|
||||
let args[0] = args[0] . ' &'
|
||||
endif
|
||||
|
||||
let funcname = use_vimproc ? 'vimproc#system' : 'system'
|
||||
let output = call(funcname, args)
|
||||
let output = s:iconv(output, 'char', &encoding)
|
||||
return output
|
||||
endfunction
|
||||
|
||||
function! s:get_last_status() abort
|
||||
return s:has_vimproc() ?
|
||||
\ vimproc#get_last_status() : v:shell_error
|
||||
endfunction
|
||||
|
||||
if s:is_windows
|
||||
function! s:shellescape(command) abort
|
||||
return substitute(a:command, '[&()[\]{}^=;!''+,`~]', '^\0', 'g')
|
||||
endfunction
|
||||
else
|
||||
function! s:shellescape(...) abort
|
||||
return call('shellescape', a:000)
|
||||
endfunction
|
||||
endif
|
||||
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
||||
" vim:set et ts=2 sts=2 sw=2 tw=0:
|
665
bundle/vim-grammarous/autoload/vital/_grammarous/Web/HTTP.vim
vendored
Normal file
665
bundle/vim-grammarous/autoload/vital/_grammarous/Web/HTTP.vim
vendored
Normal file
@ -0,0 +1,665 @@
|
||||
" ___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#_grammarous#Web#HTTP#import() abort
|
||||
return map({'decodeURI': '', '_vital_depends': '', 'parseHeader': '', 'encodeURIComponent': '', 'encodeURI': '', 'escape': '', 'post': '', 'get': '', 'request': '', '_vital_loaded': ''}, 'function("s:" . v:key)')
|
||||
endfunction
|
||||
else
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_grammarous#Web#HTTP#import() abort', printf("return map({'decodeURI': '', '_vital_depends': '', 'parseHeader': '', 'encodeURIComponent': '', 'encodeURI': '', 'escape': '', 'post': '', 'get': '', 'request': '', '_vital_loaded': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
endif
|
||||
" ___vital___
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
|
||||
function! s:_vital_loaded(V) abort
|
||||
let s:V = a:V
|
||||
let s:Prelude = s:V.import('Prelude')
|
||||
let s:Process = s:V.import('Process')
|
||||
let s:String = s:V.import('Data.String')
|
||||
endfunction
|
||||
|
||||
function! s:_vital_depends() abort
|
||||
return ['Prelude', 'Data.String', 'Process']
|
||||
endfunction
|
||||
|
||||
function! s:__urlencode_char(c) abort
|
||||
return printf('%%%02X', char2nr(a:c))
|
||||
endfunction
|
||||
|
||||
function! s:decodeURI(str) abort
|
||||
let ret = a:str
|
||||
let ret = substitute(ret, '+', ' ', 'g')
|
||||
let ret = substitute(ret, '%\(\x\x\)', '\=printf("%c", str2nr(submatch(1), 16))', 'g')
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
function! s:escape(str) abort
|
||||
let result = ''
|
||||
for i in range(len(a:str))
|
||||
if a:str[i] =~# '^[a-zA-Z0-9_.~-]$'
|
||||
let result .= a:str[i]
|
||||
else
|
||||
let result .= s:__urlencode_char(a:str[i])
|
||||
endif
|
||||
endfor
|
||||
return result
|
||||
endfunction
|
||||
|
||||
function! s:encodeURI(items) abort
|
||||
let ret = ''
|
||||
if s:Prelude.is_dict(a:items)
|
||||
for key in sort(keys(a:items))
|
||||
if strlen(ret)
|
||||
let ret .= '&'
|
||||
endif
|
||||
let ret .= key . '=' . s:encodeURI(a:items[key])
|
||||
endfor
|
||||
elseif s:Prelude.is_list(a:items)
|
||||
for item in sort(a:items)
|
||||
if strlen(ret)
|
||||
let ret .= '&'
|
||||
endif
|
||||
let ret .= item
|
||||
endfor
|
||||
else
|
||||
let ret = s:escape(a:items)
|
||||
endif
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
function! s:encodeURIComponent(items) abort
|
||||
let ret = ''
|
||||
if s:Prelude.is_dict(a:items)
|
||||
for key in sort(keys(a:items))
|
||||
if strlen(ret) | let ret .= '&' | endif
|
||||
let ret .= key . '=' . s:encodeURIComponent(a:items[key])
|
||||
endfor
|
||||
elseif s:Prelude.is_list(a:items)
|
||||
for item in sort(a:items)
|
||||
if strlen(ret) | let ret .= '&' | endif
|
||||
let ret .= item
|
||||
endfor
|
||||
else
|
||||
let items = iconv(a:items, &enc, 'utf-8')
|
||||
let len = strlen(items)
|
||||
let i = 0
|
||||
while i < len
|
||||
let ch = items[i]
|
||||
if ch =~# '[0-9A-Za-z-._~!''()*]'
|
||||
let ret .= ch
|
||||
elseif ch ==# ' '
|
||||
let ret .= '+'
|
||||
else
|
||||
let ret .= '%' . substitute('0' . s:String.nr2hex(char2nr(ch)), '^.*\(..\)$', '\1', '')
|
||||
endif
|
||||
let i = i + 1
|
||||
endwhile
|
||||
endif
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
function! s:request(...) abort
|
||||
let settings = s:_build_settings(a:000)
|
||||
let settings.method = toupper(settings.method)
|
||||
if !has_key(settings, 'url')
|
||||
throw 'vital: Web.HTTP: "url" parameter is required.'
|
||||
endif
|
||||
if !s:Prelude.is_list(settings.client)
|
||||
let settings.client = [settings.client]
|
||||
endif
|
||||
let client = s:_get_client(settings)
|
||||
if empty(client)
|
||||
throw 'vital: Web.HTTP: Available client not found: '
|
||||
\ . string(settings.client)
|
||||
endif
|
||||
if has_key(settings, 'contentType')
|
||||
let settings.headers['Content-Type'] = settings.contentType
|
||||
endif
|
||||
if has_key(settings, 'param')
|
||||
if s:Prelude.is_dict(settings.param)
|
||||
let getdatastr = s:encodeURI(settings.param)
|
||||
else
|
||||
let getdatastr = settings.param
|
||||
endif
|
||||
if strlen(getdatastr)
|
||||
let settings.url .= '?' . getdatastr
|
||||
endif
|
||||
endif
|
||||
if has_key(settings, 'data')
|
||||
let settings.data = s:_postdata(settings.data)
|
||||
let settings.headers['Content-Length'] = len(join(settings.data, "\n"))
|
||||
endif
|
||||
let settings._file = {}
|
||||
|
||||
let responses = client.request(settings)
|
||||
|
||||
for file in values(settings._file)
|
||||
if filereadable(file)
|
||||
call delete(file)
|
||||
endif
|
||||
endfor
|
||||
|
||||
call map(responses, 's:_build_response(v:val[0], v:val[1])')
|
||||
return s:_build_last_response(responses)
|
||||
endfunction
|
||||
|
||||
function! s:get(url, ...) abort
|
||||
let settings = {
|
||||
\ 'url': a:url,
|
||||
\ 'param': a:0 > 0 ? a:1 : {},
|
||||
\ 'headers': a:0 > 1 ? a:2 : {},
|
||||
\ }
|
||||
return s:request(settings)
|
||||
endfunction
|
||||
|
||||
function! s:post(url, ...) abort
|
||||
let settings = {
|
||||
\ 'url': a:url,
|
||||
\ 'data': a:0 > 0 ? a:1 : {},
|
||||
\ 'headers': a:0 > 1 ? a:2 : {},
|
||||
\ 'method': a:0 > 2 ? a:3 : 'POST',
|
||||
\ }
|
||||
return s:request(settings)
|
||||
endfunction
|
||||
|
||||
function! s:_readfile(file) abort
|
||||
if filereadable(a:file)
|
||||
return join(readfile(a:file, 'b'), "\n")
|
||||
endif
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! s:_make_postfile(data) abort
|
||||
let fname = s:_tempname()
|
||||
call writefile(a:data, fname, 'b')
|
||||
return fname
|
||||
endfunction
|
||||
|
||||
function! s:_tempname() abort
|
||||
return tr(tempname(), '\', '/')
|
||||
endfunction
|
||||
|
||||
function! s:_postdata(data) abort
|
||||
if s:Prelude.is_dict(a:data)
|
||||
return [s:encodeURI(a:data)]
|
||||
elseif s:Prelude.is_list(a:data)
|
||||
return a:data
|
||||
else
|
||||
return split(a:data, "\n")
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:_build_response(header, content) abort
|
||||
let response = {
|
||||
\ 'header' : a:header,
|
||||
\ 'content': a:content,
|
||||
\ 'status': 0,
|
||||
\ 'statusText': '',
|
||||
\ 'success': 0,
|
||||
\ }
|
||||
|
||||
if !empty(a:header)
|
||||
let status_line = get(a:header, 0)
|
||||
let matched = matchlist(status_line, '^HTTP/1\.\d\s\+\(\d\+\)\s\+\(.*\)')
|
||||
if !empty(matched)
|
||||
let [status, status_text] = matched[1 : 2]
|
||||
let response.status = status - 0
|
||||
let response.statusText = status_text
|
||||
let response.success = status =~# '^2'
|
||||
call remove(a:header, 0)
|
||||
endif
|
||||
endif
|
||||
return response
|
||||
endfunction
|
||||
|
||||
function! s:_build_last_response(responses) abort
|
||||
let all_headers = []
|
||||
for response in a:responses
|
||||
call extend(all_headers, response.header)
|
||||
endfor
|
||||
let last_response = remove(a:responses, -1)
|
||||
let last_response.redirectInfo = a:responses
|
||||
let last_response.allHeaders = all_headers
|
||||
return last_response
|
||||
endfunction
|
||||
|
||||
function! s:_build_settings(args) abort
|
||||
let settings = {
|
||||
\ 'method': 'GET',
|
||||
\ 'headers': {},
|
||||
\ 'client': ['python', 'curl', 'wget'],
|
||||
\ 'maxRedirect': 20,
|
||||
\ 'retry': 1,
|
||||
\ }
|
||||
let args = copy(a:args)
|
||||
if len(args) == 0
|
||||
throw 'vital: Web.HTTP: request() needs one or more arguments.'
|
||||
endif
|
||||
if s:Prelude.is_dict(args[-1])
|
||||
call extend(settings, remove(args, -1))
|
||||
endif
|
||||
if len(args) == 2
|
||||
let settings.method = remove(args, 0)
|
||||
endif
|
||||
if !empty(args)
|
||||
let settings.url = args[0]
|
||||
endif
|
||||
|
||||
return settings
|
||||
endfunction
|
||||
|
||||
function! s:_make_header_args(headdata, option, quote) abort
|
||||
let args = ''
|
||||
for [key, value] in items(a:headdata)
|
||||
if s:Prelude.is_windows()
|
||||
let value = substitute(value, '"', '"""', 'g')
|
||||
endif
|
||||
let args .= ' ' . a:option . a:quote . key . ': ' . value . a:quote
|
||||
endfor
|
||||
return args
|
||||
endfunction
|
||||
|
||||
function! s:parseHeader(headers) abort
|
||||
" FIXME: User should be able to specify the treatment method of the duplicate item.
|
||||
let header = {}
|
||||
for h in a:headers
|
||||
let matched = matchlist(h, '^\([^:]\+\):\s*\(.*\)$')
|
||||
if !empty(matched)
|
||||
let [name, value] = matched[1 : 2]
|
||||
let header[name] = value
|
||||
endif
|
||||
endfor
|
||||
return header
|
||||
endfunction
|
||||
|
||||
" Clients
|
||||
function! s:_get_client(settings) abort
|
||||
for name in a:settings.client
|
||||
if has_key(s:clients, name) && s:clients[name].available(a:settings)
|
||||
return s:clients[name]
|
||||
endif
|
||||
endfor
|
||||
return {}
|
||||
endfunction
|
||||
let s:clients = {}
|
||||
|
||||
let s:clients.python = {}
|
||||
|
||||
function! s:clients.python.available(settings) abort
|
||||
if !has('python')
|
||||
return 0
|
||||
endif
|
||||
if has_key(a:settings, 'outputFile')
|
||||
" 'outputFile' is not supported yet
|
||||
return 0
|
||||
endif
|
||||
if get(a:settings, 'retry', 0) != 1
|
||||
" 'retry' is not supported yet
|
||||
return 0
|
||||
endif
|
||||
if has_key(a:settings, 'authMethod')
|
||||
return 0
|
||||
endif
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! s:clients.python.request(settings) abort
|
||||
" TODO: retry, outputFile
|
||||
let responses = []
|
||||
python << endpython
|
||||
try:
|
||||
class DummyClassForLocalScope:
|
||||
def main():
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
import vim, urllib2, socket, gzip
|
||||
|
||||
responses = vim.bindeval('responses')
|
||||
|
||||
class CustomHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
|
||||
def __init__(self, max_redirect):
|
||||
self.max_redirect = max_redirect
|
||||
|
||||
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
||||
if self.max_redirect == 0:
|
||||
return None
|
||||
if 0 < self.max_redirect:
|
||||
self.max_redirect -= 1
|
||||
header_list = filter(None, str(headers).split("\r\n"))
|
||||
responses.extend([[[status(code, msg)] + header_list, fp.read()]])
|
||||
return urllib2.HTTPRedirectHandler.redirect_request(self, req, fp, code, msg, headers, newurl)
|
||||
|
||||
def vimlist2str(list):
|
||||
if not list:
|
||||
return None
|
||||
return "\n".join([s.replace("\n", "\0") for s in list])
|
||||
|
||||
def status(code, msg):
|
||||
return "HTTP/1.0 %d %s\r\n" % (code, msg)
|
||||
|
||||
def access():
|
||||
settings = vim.eval('a:settings')
|
||||
data = vimlist2str(settings.get('data'))
|
||||
timeout = settings.get('timeout')
|
||||
if timeout:
|
||||
timeout = float(timeout)
|
||||
request_headers = settings.get('headers')
|
||||
max_redirect = int(settings.get('maxRedirect'))
|
||||
director = urllib2.build_opener(CustomHTTPRedirectHandler(max_redirect))
|
||||
if settings.has_key('username'):
|
||||
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
|
||||
passman.add_password(
|
||||
None,
|
||||
settings['url'],
|
||||
settings['username'],
|
||||
settings.get('password', ''))
|
||||
basicauth = urllib2.HTTPBasicAuthHandler(passman)
|
||||
digestauth = urllib2.HTTPDigestAuthHandler(passman)
|
||||
director.add_handler(basicauth)
|
||||
director.add_handler(digestauth)
|
||||
req = urllib2.Request(settings['url'], data, request_headers)
|
||||
req.get_method = lambda: settings['method']
|
||||
default_timeout = socket.getdefaulttimeout()
|
||||
try:
|
||||
# for Python 2.5 or before
|
||||
socket.setdefaulttimeout(timeout)
|
||||
res = director.open(req, timeout=timeout)
|
||||
except urllib2.HTTPError as res:
|
||||
pass
|
||||
except urllib2.URLError:
|
||||
return ('', '')
|
||||
except socket.timeout:
|
||||
return ('', '')
|
||||
finally:
|
||||
socket.setdefaulttimeout(default_timeout)
|
||||
|
||||
st = status(res.code, res.msg)
|
||||
response_headers = st + ''.join(res.info().headers)
|
||||
response_body = res.read()
|
||||
|
||||
gzip_decompress = settings.get('gzipDecompress', False)
|
||||
if gzip_decompress:
|
||||
buf = StringIO(response_body)
|
||||
f = gzip.GzipFile(fileobj=buf)
|
||||
response_body = f.read()[:-1]
|
||||
|
||||
return (response_headers, response_body)
|
||||
|
||||
(header, body) = access()
|
||||
responses.extend([[header.split("\r\n"), body]])
|
||||
|
||||
main()
|
||||
raise RuntimeError("Exit from local scope")
|
||||
|
||||
except RuntimeError as exception:
|
||||
if exception.args != ("Exit from local scope",):
|
||||
raise exception
|
||||
|
||||
endpython
|
||||
return responses
|
||||
endfunction
|
||||
|
||||
let s:clients.curl = {}
|
||||
|
||||
let s:clients.curl.errcode = {}
|
||||
let s:clients.curl.errcode[1] = 'Unsupported protocol. This build of curl has no support for this protocol.'
|
||||
let s:clients.curl.errcode[2] = 'Failed to initialize.'
|
||||
let s:clients.curl.errcode[3] = 'URL malformed. The syntax was not correct.'
|
||||
let s:clients.curl.errcode[4] = 'A feature or option that was needed to perform the desired request was not enabled or was explicitly disabled at buildtime. To make curl able to do this, you probably need another build of libcurl!'
|
||||
let s:clients.curl.errcode[5] = 'Couldn''t resolve proxy. The given proxy host could not be resolved.'
|
||||
let s:clients.curl.errcode[6] = 'Couldn''t resolve host. The given remote host was not resolved.'
|
||||
let s:clients.curl.errcode[7] = 'Failed to connect to host.'
|
||||
let s:clients.curl.errcode[8] = 'FTP weird server reply. The server sent data curl couldn''t parse.'
|
||||
let s:clients.curl.errcode[9] = 'FTP access denied. The server denied login or denied access to the particular resource or directory you wanted to reach. Most often you tried to change to a directory that doesn''t exist on the server.'
|
||||
let s:clients.curl.errcode[11] = 'FTP weird PASS reply. Curl couldn''t parse the reply sent to the PASS request.'
|
||||
let s:clients.curl.errcode[13] = 'FTP weird PASV reply, Curl couldn''t parse the reply sent to the PASV request.'
|
||||
let s:clients.curl.errcode[14] = 'FTP weird 227 format. Curl couldn''t parse the 227-line the server sent.'
|
||||
let s:clients.curl.errcode[15] = 'FTP can''t get host. Couldn''t resolve the host IP we got in the 227-line.'
|
||||
let s:clients.curl.errcode[17] = 'FTP couldn''t set binary. Couldn''t change transfer method to binary.'
|
||||
let s:clients.curl.errcode[18] = 'Partial file. Only a part of the file was transferred.'
|
||||
let s:clients.curl.errcode[19] = 'FTP couldn''t download/access the given file, the RETR (or similar) command failed.'
|
||||
let s:clients.curl.errcode[21] = 'FTP quote error. A quote command returned error from the server.'
|
||||
let s:clients.curl.errcode[22] = 'HTTP page not retrieved. The requested url was not found or returned another error with the HTTP error code being 400 or above. This return code only appears if -f, --fail is used.'
|
||||
let s:clients.curl.errcode[23] = 'Write error. Curl couldn''t write data to a local filesystem or similar.'
|
||||
let s:clients.curl.errcode[25] = 'FTP couldn''t STOR file. The server denied the STOR operation, used for FTP uploading.'
|
||||
let s:clients.curl.errcode[26] = 'Read error. Various reading problems.'
|
||||
let s:clients.curl.errcode[27] = 'Out of memory. A memory allocation request failed.'
|
||||
let s:clients.curl.errcode[28] = 'Operation timeout. The specified time-out period was reached according to the conditions.'
|
||||
let s:clients.curl.errcode[30] = 'FTP PORT failed. The PORT command failed. Not all FTP servers support the PORT command, try doing a transfer using PASV instead!'
|
||||
let s:clients.curl.errcode[31] = 'FTP couldn''t use REST. The REST command failed. This command is used for resumed FTP transfers.'
|
||||
let s:clients.curl.errcode[33] = 'HTTP range error. The range "command" didn''t work.'
|
||||
let s:clients.curl.errcode[34] = 'HTTP post error. Internal post-request generation error.'
|
||||
let s:clients.curl.errcode[35] = 'SSL connect error. The SSL handshaking failed.'
|
||||
let s:clients.curl.errcode[36] = 'FTP bad download resume. Couldn''t continue an earlier aborted download.'
|
||||
let s:clients.curl.errcode[37] = 'FILE couldn''t read file. Failed to open the file. Permissions?'
|
||||
let s:clients.curl.errcode[38] = 'LDAP cannot bind. LDAP bind operation failed.'
|
||||
let s:clients.curl.errcode[39] = 'LDAP search failed.'
|
||||
let s:clients.curl.errcode[41] = 'Function not found. A required LDAP function was not found.'
|
||||
let s:clients.curl.errcode[42] = 'Aborted by callback. An application told curl to abort the operation.'
|
||||
let s:clients.curl.errcode[43] = 'Internal error. A function was called with a bad parameter.'
|
||||
let s:clients.curl.errcode[45] = 'Interface error. A specified outgoing interface could not be used.'
|
||||
let s:clients.curl.errcode[47] = 'Too many redirects. When following redirects, curl hit the maximum amount.'
|
||||
let s:clients.curl.errcode[48] = 'Unknown option specified to libcurl. This indicates that you passed a weird option to curl that was passed on to libcurl and rejected. Read up in the manual!'
|
||||
let s:clients.curl.errcode[49] = 'Malformed telnet option.'
|
||||
let s:clients.curl.errcode[51] = 'The peer''s SSL certificate or SSH MD5 fingerprint was not OK.'
|
||||
let s:clients.curl.errcode[52] = 'The server didn''t reply anything, which here is considered an error.'
|
||||
let s:clients.curl.errcode[53] = 'SSL crypto engine not found.'
|
||||
let s:clients.curl.errcode[54] = 'Cannot set SSL crypto engine as default.'
|
||||
let s:clients.curl.errcode[55] = 'Failed sending network data.'
|
||||
let s:clients.curl.errcode[56] = 'Failure in receiving network data.'
|
||||
let s:clients.curl.errcode[58] = 'Problem with the local certificate.'
|
||||
let s:clients.curl.errcode[59] = 'Couldn''t use specified SSL cipher.'
|
||||
let s:clients.curl.errcode[60] = 'Peer certificate cannot be authenticated with known CA certificates.'
|
||||
let s:clients.curl.errcode[61] = 'Unrecognized transfer encoding.'
|
||||
let s:clients.curl.errcode[62] = 'Invalid LDAP URL.'
|
||||
let s:clients.curl.errcode[63] = 'Maximum file size exceeded.'
|
||||
let s:clients.curl.errcode[64] = 'Requested FTP SSL level failed.'
|
||||
let s:clients.curl.errcode[65] = 'Sending the data requires a rewind that failed.'
|
||||
let s:clients.curl.errcode[66] = 'Failed to initialise SSL Engine.'
|
||||
let s:clients.curl.errcode[67] = 'The user name, password, or similar was not accepted and curl failed to log in.'
|
||||
let s:clients.curl.errcode[68] = 'File not found on TFTP server.'
|
||||
let s:clients.curl.errcode[69] = 'Permission problem on TFTP server.'
|
||||
let s:clients.curl.errcode[70] = 'Out of disk space on TFTP server.'
|
||||
let s:clients.curl.errcode[71] = 'Illegal TFTP operation.'
|
||||
let s:clients.curl.errcode[72] = 'Unknown TFTP transfer ID.'
|
||||
let s:clients.curl.errcode[73] = 'File already exists (TFTP).'
|
||||
let s:clients.curl.errcode[74] = 'No such user (TFTP).'
|
||||
let s:clients.curl.errcode[75] = 'Character conversion failed.'
|
||||
let s:clients.curl.errcode[76] = 'Character conversion functions required.'
|
||||
let s:clients.curl.errcode[77] = 'Problem with reading the SSL CA cert (path? access rights?).'
|
||||
let s:clients.curl.errcode[78] = 'The resource referenced in the URL does not exist.'
|
||||
let s:clients.curl.errcode[79] = 'An unspecified error occurred during the SSH session.'
|
||||
let s:clients.curl.errcode[80] = 'Failed to shut down the SSL connection.'
|
||||
let s:clients.curl.errcode[82] = 'Could not load CRL file, missing or wrong format (added in 7.19.0).'
|
||||
let s:clients.curl.errcode[83] = 'Issuer check failed (added in 7.19.0).'
|
||||
let s:clients.curl.errcode[84] = 'The FTP PRET command failed'
|
||||
let s:clients.curl.errcode[85] = 'RTSP: mismatch of CSeq numbers'
|
||||
let s:clients.curl.errcode[86] = 'RTSP: mismatch of Session Identifiers'
|
||||
let s:clients.curl.errcode[87] = 'unable to parse FTP file list'
|
||||
let s:clients.curl.errcode[88] = 'FTP chunk callback reported error'
|
||||
let s:clients.curl.errcode[89] = 'No connection available, the session will be queued'
|
||||
let s:clients.curl.errcode[90] = 'SSL public key does not matched pinned public key'
|
||||
|
||||
|
||||
function! s:clients.curl.available(settings) abort
|
||||
return executable(self._command(a:settings))
|
||||
endfunction
|
||||
|
||||
function! s:clients.curl._command(settings) abort
|
||||
return get(get(a:settings, 'command', {}), 'curl', 'curl')
|
||||
endfunction
|
||||
|
||||
function! s:clients.curl.request(settings) abort
|
||||
let quote = s:_quote()
|
||||
let command = self._command(a:settings)
|
||||
let a:settings._file.header = s:_tempname()
|
||||
let command .= ' --dump-header ' . quote . a:settings._file.header . quote
|
||||
let has_output_file = has_key(a:settings, 'outputFile')
|
||||
if has_output_file
|
||||
let output_file = a:settings.outputFile
|
||||
else
|
||||
let output_file = s:_tempname()
|
||||
let a:settings._file.content = output_file
|
||||
endif
|
||||
let command .= ' --output ' . quote . output_file . quote
|
||||
if has_key(a:settings, 'gzipDecompress') && a:settings.gzipDecompress
|
||||
let command .= ' --compressed '
|
||||
endif
|
||||
let command .= ' -L -s -k -X ' . a:settings.method
|
||||
let command .= ' --max-redirs ' . a:settings.maxRedirect
|
||||
let command .= s:_make_header_args(a:settings.headers, '-H ', quote)
|
||||
let timeout = get(a:settings, 'timeout', '')
|
||||
let command .= ' --retry ' . a:settings.retry
|
||||
if timeout =~# '^\d\+$'
|
||||
let command .= ' --max-time ' . timeout
|
||||
endif
|
||||
if has_key(a:settings, 'username')
|
||||
let auth = a:settings.username . ':' . get(a:settings, 'password', '')
|
||||
let auth = escape(auth, quote)
|
||||
if has_key(a:settings, 'authMethod')
|
||||
if index(['basic', 'digest', 'ntlm', 'negotiate'], a:settings.authMethod) == -1
|
||||
throw 'vital: Web.HTTP: Invalid authorization method: ' . a:settings.authMethod
|
||||
endif
|
||||
let method = a:settings.authMethod
|
||||
else
|
||||
let method = 'anyauth'
|
||||
endif
|
||||
let command .= ' --' . method . ' --user ' . quote . auth . quote
|
||||
endif
|
||||
if has_key(a:settings, 'data')
|
||||
let a:settings._file.post = s:_make_postfile(a:settings.data)
|
||||
let command .= ' --data-binary @' . quote . a:settings._file.post . quote
|
||||
endif
|
||||
let command .= ' ' . quote . a:settings.url . quote
|
||||
|
||||
call s:Process.system(command)
|
||||
let retcode = s:Process.get_last_status()
|
||||
|
||||
let headerstr = s:_readfile(a:settings._file.header)
|
||||
let header_chunks = split(headerstr, "\r\n\r\n")
|
||||
let headers = map(header_chunks, 'split(v:val, "\r\n")')
|
||||
if retcode != 0 && empty(headers)
|
||||
if has_key(s:clients.curl.errcode, retcode)
|
||||
throw 'vital: Web.HTTP: ' . s:clients.curl.errcode[retcode]
|
||||
else
|
||||
throw 'vital: Web.HTTP: Unknown error code has occured in curl: code=' . retcode
|
||||
endif
|
||||
endif
|
||||
if !empty(headers)
|
||||
let responses = map(headers, '[v:val, ""]')
|
||||
else
|
||||
let responses = [[[], '']]
|
||||
endif
|
||||
if has_output_file
|
||||
let content = ''
|
||||
else
|
||||
let content = s:_readfile(output_file)
|
||||
endif
|
||||
let responses[-1][1] = content
|
||||
return responses
|
||||
endfunction
|
||||
|
||||
let s:clients.wget = {}
|
||||
let s:clients.wget.errcode = {}
|
||||
let s:clients.wget.errcode[1] = 'Generic error code.'
|
||||
let s:clients.wget.errcode[2] = 'Parse error---for instance, when parsing command-line options, the .wgetrc or .netrc...'
|
||||
let s:clients.wget.errcode[3] = 'File I/O error.'
|
||||
let s:clients.wget.errcode[4] = 'Network failure.'
|
||||
let s:clients.wget.errcode[5] = 'SSL verification failure.'
|
||||
let s:clients.wget.errcode[6] = 'Username/password authentication failure.'
|
||||
let s:clients.wget.errcode[7] = 'Protocol errors.'
|
||||
let s:clients.wget.errcode[8] = 'Server issued an error response.'
|
||||
|
||||
|
||||
function! s:clients.wget.available(settings) abort
|
||||
if has_key(a:settings, 'authMethod')
|
||||
return 0
|
||||
endif
|
||||
return executable(self._command(a:settings))
|
||||
endfunction
|
||||
|
||||
function! s:clients.wget._command(settings) abort
|
||||
return get(get(a:settings, 'command', {}), 'wget', 'wget')
|
||||
endfunction
|
||||
|
||||
function! s:clients.wget.request(settings) abort
|
||||
let quote = s:_quote()
|
||||
let command = self._command(a:settings)
|
||||
let method = a:settings.method
|
||||
if method ==# 'HEAD'
|
||||
let command .= ' --spider'
|
||||
elseif method !=# 'GET' && method !=# 'POST'
|
||||
let a:settings.headers['X-HTTP-Method-Override'] = a:settings.method
|
||||
endif
|
||||
let a:settings._file.header = s:_tempname()
|
||||
let command .= ' -o ' . quote . a:settings._file.header . quote
|
||||
let has_output_file = has_key(a:settings, 'outputFile')
|
||||
if has_output_file
|
||||
let output_file = a:settings.outputFile
|
||||
else
|
||||
let output_file = s:_tempname()
|
||||
let a:settings._file.content = output_file
|
||||
endif
|
||||
let command .= ' -O ' . quote . output_file . quote
|
||||
let command .= ' --server-response -q -L '
|
||||
let command .= ' --max-redirect=' . a:settings.maxRedirect
|
||||
let command .= s:_make_header_args(a:settings.headers, '--header=', quote)
|
||||
let timeout = get(a:settings, 'timeout', '')
|
||||
let command .= ' --tries=' . a:settings.retry
|
||||
if timeout =~# '^\d\+$'
|
||||
let command .= ' --timeout=' . timeout
|
||||
endif
|
||||
if has_key(a:settings, 'username')
|
||||
let command .= ' --http-user=' . quote . escape(a:settings.username, quote) . quote
|
||||
endif
|
||||
if has_key(a:settings, 'password')
|
||||
let command .= ' --http-password=' . quote . escape(a:settings.password, quote) . quote
|
||||
endif
|
||||
let command .= ' ' . quote . a:settings.url . quote
|
||||
if has_key(a:settings, 'data')
|
||||
let a:settings._file.post = s:_make_postfile(a:settings.data)
|
||||
let command .= ' --post-file=' . quote . a:settings._file.post . quote
|
||||
endif
|
||||
|
||||
call s:Process.system(command)
|
||||
let retcode = s:Process.get_last_status()
|
||||
|
||||
if filereadable(a:settings._file.header)
|
||||
let header_lines = readfile(a:settings._file.header, 'b')
|
||||
call map(header_lines, 'matchstr(v:val, "^\\s*\\zs.*")')
|
||||
let headerstr = join(header_lines, "\r\n")
|
||||
let header_chunks = split(headerstr, '\r\n\zeHTTP/1\.\d')
|
||||
let headers = map(header_chunks, 'split(v:val, "\r\n")')
|
||||
let responses = map(headers, '[v:val, ""]')
|
||||
else
|
||||
let headers = []
|
||||
let responses = [[[], '']]
|
||||
endif
|
||||
if has_key(s:clients.wget.errcode, retcode) && empty(headers)
|
||||
throw 'vital: Web.HTTP: ' . s:clients.wget.errcode[retcode]
|
||||
endif
|
||||
if has_output_file
|
||||
let content = ''
|
||||
else
|
||||
let content = s:_readfile(output_file)
|
||||
endif
|
||||
let responses[-1][1] = content
|
||||
return responses
|
||||
endfunction
|
||||
|
||||
function! s:_quote() abort
|
||||
return &shellxquote ==# '"' ? "'" : '"'
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
||||
" vim:set et ts=2 sts=2 sw=2 tw=0:
|
315
bundle/vim-grammarous/autoload/vital/_grammarous/Web/XML.vim
vendored
Normal file
315
bundle/vim-grammarous/autoload/vital/_grammarous/Web/XML.vim
vendored
Normal file
@ -0,0 +1,315 @@
|
||||
" ___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#_grammarous#Web#XML#import() abort
|
||||
return map({'parseFile': '', '_vital_depends': '', 'createElement': '', 'parse': '', 'decodeEntityReference': '', 'encodeEntityReference': '', 'parseURL': '', '_vital_loaded': ''}, 'function("s:" . v:key)')
|
||||
endfunction
|
||||
else
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_grammarous#Web#XML#import() abort', printf("return map({'parseFile': '', '_vital_depends': '', 'createElement': '', 'parse': '', 'decodeEntityReference': '', 'encodeEntityReference': '', 'parseURL': '', '_vital_loaded': ''}, \"function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
endif
|
||||
" ___vital___
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
function! s:_vital_loaded(V) abort
|
||||
let s:V = a:V
|
||||
|
||||
let s:S = s:V.import('Data.String')
|
||||
let s:H = s:V.import('Web.HTTP')
|
||||
endfunction
|
||||
|
||||
function! s:_vital_depends() abort
|
||||
return ['Data.String', 'Web.HTTP']
|
||||
endfunction
|
||||
|
||||
let s:__template = { 'name': '', 'attr': {}, 'child': [] }
|
||||
|
||||
function! s:decodeEntityReference(str) abort
|
||||
let str = a:str
|
||||
let str = substitute(str, '>', '>', 'g')
|
||||
let str = substitute(str, '<', '<', 'g')
|
||||
"let str = substitute(str, '"', '"', 'g')
|
||||
"let str = substitute(str, ''', "'", 'g')
|
||||
"let str = substitute(str, ' ', ' ', 'g')
|
||||
"let str = substitute(str, '¥', '\¥', 'g')
|
||||
let str = substitute(str, '&#x\([0-9a-fA-F]\+\);', '\=s:S.nr2enc_char("0x".submatch(1))', 'g')
|
||||
let str = substitute(str, '&#\(\d\+\);', '\=s:S.nr2enc_char(submatch(1))', 'g')
|
||||
let str = substitute(str, '&', '\&', 'g')
|
||||
return str
|
||||
endfunction
|
||||
|
||||
function! s:encodeEntityReference(str) abort
|
||||
let str = a:str
|
||||
let str = substitute(str, '&', '\&', 'g')
|
||||
let str = substitute(str, '>', '\>', 'g')
|
||||
let str = substitute(str, '<', '\<', 'g')
|
||||
let str = substitute(str, '"', '\"', 'g')
|
||||
"let str = substitute(str, "\n", '\
', 'g')
|
||||
"let str = substitute(str, '"', '"', 'g')
|
||||
"let str = substitute(str, "'", ''', 'g')
|
||||
"let str = substitute(str, ' ', ' ', 'g')
|
||||
return str
|
||||
endfunction
|
||||
|
||||
function! s:__matchNode(node, cond) abort
|
||||
if type(a:cond) == 1 && a:node.name == a:cond
|
||||
return 1
|
||||
endif
|
||||
if type(a:cond) == 2
|
||||
return a:cond(a:node)
|
||||
endif
|
||||
if type(a:cond) == 3
|
||||
let ret = 1
|
||||
for R in a:cond
|
||||
if !s:__matchNode(a:node, R) | let ret = 0 | endif
|
||||
unlet R
|
||||
endfor
|
||||
return ret
|
||||
endif
|
||||
if type(a:cond) == 4
|
||||
for k in keys(a:cond)
|
||||
if has_key(a:node.attr, k) && a:node.attr[k] == a:cond[k] | return 1 | endif
|
||||
endfor
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! s:__template.childNode(...) dict abort
|
||||
for c in self.child
|
||||
if type(c) == 4 && s:__matchNode(c, a:000)
|
||||
return c
|
||||
endif
|
||||
unlet c
|
||||
endfor
|
||||
return {}
|
||||
endfunction
|
||||
|
||||
function! s:__template.childNodes(...) dict abort
|
||||
let ret = []
|
||||
for c in self.child
|
||||
if type(c) == 4 && s:__matchNode(c, a:000)
|
||||
let ret += [c]
|
||||
endif
|
||||
unlet c
|
||||
endfor
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
function! s:__template.value(...) dict abort
|
||||
if a:0
|
||||
let self.child = a:000
|
||||
return
|
||||
endif
|
||||
let ret = ''
|
||||
for c in self.child
|
||||
if type(c) <= 1 || type(c) == 5
|
||||
let ret .= c
|
||||
elseif type(c) == 4
|
||||
let ret .= c.value()
|
||||
endif
|
||||
unlet c
|
||||
endfor
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
function! s:__template.find(...) dict abort
|
||||
for c in self.child
|
||||
if type(c) == 4
|
||||
if s:__matchNode(c, a:000)
|
||||
return c
|
||||
endif
|
||||
unlet! ret
|
||||
let ret = c.find(a:000)
|
||||
if !empty(ret)
|
||||
return ret
|
||||
endif
|
||||
endif
|
||||
unlet c
|
||||
endfor
|
||||
return {}
|
||||
endfunction
|
||||
|
||||
function! s:__template.findAll(...) dict abort
|
||||
let ret = []
|
||||
for c in self.child
|
||||
if type(c) == 4
|
||||
if s:__matchNode(c, a:000)
|
||||
call add(ret, c)
|
||||
endif
|
||||
let ret += c.findAll(a:000)
|
||||
endif
|
||||
unlet c
|
||||
endfor
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
function! s:__template.toString() dict abort
|
||||
let xml = '<' . self.name
|
||||
for attr in keys(self.attr)
|
||||
let xml .= ' ' . attr . '="' . s:encodeEntityReference(self.attr[attr]) . '"'
|
||||
endfor
|
||||
if len(self.child)
|
||||
let xml .= '>'
|
||||
for c in self.child
|
||||
if type(c) == 4
|
||||
let xml .= c.toString()
|
||||
elseif type(c) > 1
|
||||
let xml .= s:encodeEntityReference(string(c))
|
||||
else
|
||||
let xml .= s:encodeEntityReference(c)
|
||||
endif
|
||||
unlet c
|
||||
endfor
|
||||
let xml .= '</' . self.name . '>'
|
||||
else
|
||||
let xml .= ' />'
|
||||
endif
|
||||
return xml
|
||||
endfunction
|
||||
|
||||
function! s:createElement(name) abort
|
||||
let node = deepcopy(s:__template)
|
||||
let node.name = a:name
|
||||
return node
|
||||
endfunction
|
||||
|
||||
" @vimlint(EVL102, 1, l:content)
|
||||
function! s:__parse_tree(ctx, top) abort
|
||||
let node = a:top
|
||||
let stack = [a:top]
|
||||
" content accumulates the text only tags
|
||||
let content = ''
|
||||
let append_content_to_parent = 'if len(stack) && content != "" | call add(stack[-1].child, content) | let content ="" | endif'
|
||||
|
||||
let mx = '^\s*\(<?xml[^>]\+>\)'
|
||||
if a:ctx['xml'] =~ mx
|
||||
let match = matchstr(a:ctx['xml'], mx)
|
||||
let a:ctx['xml'] = a:ctx['xml'][stridx(a:ctx['xml'], match) + len(match):]
|
||||
let mx = 'encoding\s*=\s*["'']\{0,1}\([^"'' \t]\+\|[^"'']\+\)["'']\{0,1}'
|
||||
let matches = matchlist(match, mx)
|
||||
if len(matches)
|
||||
let encoding = matches[1]
|
||||
if encoding !=# '' && a:ctx['encoding'] ==# ''
|
||||
let a:ctx['encoding'] = encoding
|
||||
let a:ctx['xml'] = iconv(a:ctx['xml'], encoding, &encoding)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
" this regex matches
|
||||
" 1) the remaining until the next tag begins
|
||||
" 2) maybe closing "/" of tag name
|
||||
" 3) tagname
|
||||
" 4) the attributes of the text (optional)
|
||||
" 5) maybe closing "/" (end of tag name)
|
||||
" or
|
||||
" 6) CDATA or ''
|
||||
" 7) text content of CDATA
|
||||
" or
|
||||
" 8) comment
|
||||
" (These numbers correspond to the indexes in matched list m)
|
||||
let tag_mx = '^\(\_.\{-}\)\%(\%(<\(/\?\)\([^!/>[:space:]]\+\)\(\%([[:space:]]*[^/>=[:space:]]\+[[:space:]]*=[[:space:]]*\%([^"'' >\t]\+\|"[^"]*"\|''[^'']*''\)\|[[:space:]]\+[^/>=[:space:]]\+[[:space:]]*\)*\)[[:space:]]*\(/\?\)>\)\|\%(<!\[\(CDATA\)\[\(.\{-}\)\]\]>\)\|\(<!--.\{-}-->\)\)'
|
||||
|
||||
while a:ctx.xml !=# ''
|
||||
let m = matchlist(a:ctx.xml, tag_mx)
|
||||
if empty(m) | break | endif
|
||||
let a:ctx.xml = a:ctx.xml[len(m[0]) :]
|
||||
let is_end_tag = m[2] ==# '/' && m[5] ==# ''
|
||||
let is_start_and_end_tag = m[2] ==# '' && m[5] ==# '/'
|
||||
let tag_name = m[3]
|
||||
let attrs = m[4]
|
||||
|
||||
if m[1] !=# ''
|
||||
let content .= s:decodeEntityReference(m[1])
|
||||
endif
|
||||
|
||||
if is_end_tag
|
||||
" closing tag: pop from stack and continue at upper level
|
||||
exec append_content_to_parent
|
||||
|
||||
if len(stack) " TODO: checking whether opened tag is exist.
|
||||
call remove(stack, -1)
|
||||
endif
|
||||
continue
|
||||
endif
|
||||
|
||||
" comment tag
|
||||
if m[8] !=# ''
|
||||
continue
|
||||
endif
|
||||
|
||||
" if element is a CDATA
|
||||
if m[6] !=# ''
|
||||
let content .= m[7]
|
||||
continue
|
||||
endif
|
||||
|
||||
let node = deepcopy(s:__template)
|
||||
let node.name = tag_name
|
||||
let attr_mx = '\([^=[:space:]]\+\)\s*\%(=\s*''\([^'']*\)''\|=\s*"\([^"]*\)"\|=\s*\(\w\+\)\|\)'
|
||||
while attrs !=# ''
|
||||
let attr_match = matchlist(attrs, attr_mx)
|
||||
if len(attr_match) == 0
|
||||
break
|
||||
endif
|
||||
let name = attr_match[1]
|
||||
let value = attr_match[2] !=# '' ? attr_match[2] : attr_match[3] !=# '' ? attr_match[3] : attr_match[4] !=# '' ? attr_match[4] : ''
|
||||
if value ==# ''
|
||||
let node.attr[name] = ''
|
||||
else
|
||||
let node.attr[name] = s:decodeEntityReference(value)
|
||||
endif
|
||||
let attrs = attrs[stridx(attrs, attr_match[0]) + len(attr_match[0]):]
|
||||
endwhile
|
||||
|
||||
exec append_content_to_parent
|
||||
|
||||
if len(stack)
|
||||
call add(stack[-1].child, node)
|
||||
endif
|
||||
if !is_start_and_end_tag
|
||||
" opening tag, continue parsing its contents
|
||||
call add(stack, node)
|
||||
endif
|
||||
endwhile
|
||||
endfunction
|
||||
" @vimlint(EVL102, 0, l:content)
|
||||
|
||||
function! s:parse(xml) abort
|
||||
let top = deepcopy(s:__template)
|
||||
let oldmaxmempattern = &maxmempattern
|
||||
let oldmaxfuncdepth = &maxfuncdepth
|
||||
let &maxmempattern = 2000000
|
||||
let &maxfuncdepth = 2000
|
||||
try
|
||||
call s:__parse_tree({'xml': a:xml, 'encoding': ''}, top)
|
||||
for node in top.child
|
||||
if type(node) == 4
|
||||
return node
|
||||
endif
|
||||
unlet node
|
||||
endfor
|
||||
finally
|
||||
let &maxmempattern = oldmaxmempattern
|
||||
let &maxfuncdepth = oldmaxfuncdepth
|
||||
endtry
|
||||
throw 'vital: Web.XML: Parse Error'
|
||||
endfunction
|
||||
|
||||
function! s:parseFile(fname) abort
|
||||
return s:parse(join(readfile(a:fname), "\n"))
|
||||
endfunction
|
||||
|
||||
function! s:parseURL(url) abort
|
||||
return s:parse(s:H.get(a:url).content)
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
||||
" vim:set et ts=2 sts=2 sw=2 tw=0:
|
339
bundle/vim-grammarous/autoload/vital/grammarous.vim
vendored
Normal file
339
bundle/vim-grammarous/autoload/vital/grammarous.vim
vendored
Normal file
@ -0,0 +1,339 @@
|
||||
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
|
5
bundle/vim-grammarous/autoload/vital/grammarous.vital
vendored
Normal file
5
bundle/vim-grammarous/autoload/vital/grammarous.vital
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
grammarous
|
||||
cb43c297bbeb2526226bd8775674a1b7c6006657
|
||||
|
||||
Web.XML
|
||||
OptionParser
|
71
bundle/vim-grammarous/doc/grammarous.txt
vendored
Normal file
71
bundle/vim-grammarous/doc/grammarous.txt
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
*grammarous.txt* A powerful grammar checker.
|
||||
|
||||
Author : rhysd <lin90162@yahoo.co.jp>
|
||||
|
||||
CONTENTS *vim-grammarous-contents*
|
||||
|
||||
Introduction |vim-grammarous-introduction|
|
||||
Usage |vim-grammarous-usage|
|
||||
License |vim-grammarous-license|
|
||||
|
||||
|
||||
==============================================================================
|
||||
INTRODUCTION *vim-grammarous-introduction*
|
||||
|
||||
*vim-grammarous* is a powerful grammar checker for Vim. Simply do
|
||||
|:GrammarousCheck| to see the powerful checking. This plugin automatically
|
||||
downloads LanguageTool, which requires Java 8+.
|
||||
|
||||
|vim-grammarous| uses job feature on Vim 8.0.27 (or later) or Neovim. You need
|
||||
not to wait until the check has done.
|
||||
|
||||
|
||||
==============================================================================
|
||||
USAGE *vim-grammarous-usage*
|
||||
|
||||
*:GrammarousCheck* to check grammar in the buffer.
|
||||
|
||||
:[range]GrammarousCheck [--lang={lang}] [--(no-)preview] [--(no-)comments-only]
|
||||
|
||||
Execute the grammar checker for current buffer (when [range] is specified, the
|
||||
target is a text in the range).
|
||||
|
||||
1. It makes LanguageTool check grammar (It takes a while)
|
||||
2. It highlights the locations of detected grammar errors
|
||||
3. When you move the cursor on a location of error, it automatically shows
|
||||
the error with the information window.
|
||||
|
||||
Please do ":GrammarousCheck --help" to show more detail about the command.
|
||||
In information window, some local mappings are available. Please enter "?" in
|
||||
the window to show the help.
|
||||
|
||||
And *:GrammarousReset* resets the current check.
|
||||
|
||||
If you want to customize something (e.g. define mappings, disable rules),
|
||||
please see https://github.com/rhysd/vim-grammarous for more detail.
|
||||
|
||||
|
||||
==============================================================================
|
||||
LICENSE *vim-grammarous-license*
|
||||
|
||||
Copyright (c) 2014 rhysd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
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 OR COPYRIGHT HOLDERS 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.
|
||||
|
||||
==============================================================================
|
||||
vim:tw=78:colorcolumn=78:ts=8:ft=help:norl:et:fen:fdl=0:
|
26
bundle/vim-grammarous/plugin/grammarous.vim
vendored
Normal file
26
bundle/vim-grammarous/plugin/grammarous.vim
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
if (exists('g:loaded_grammarous') && g:loaded_grammarous) || &cp
|
||||
finish
|
||||
endif
|
||||
|
||||
command! -nargs=* -bar -range=% -complete=customlist,grammarous#complete_opt GrammarousCheck call grammarous#check_current_buffer(<q-args>, [<line1>, <line2>])
|
||||
command! -nargs=0 -bar GrammarousReset call grammarous#reset()
|
||||
|
||||
nnoremap <silent><Plug>(grammarous-move-to-info-window) :<C-u>call grammarous#create_and_jump_to_info_window_of(b:grammarous_result)<CR>
|
||||
nnoremap <silent><Plug>(grammarous-open-info-window) :<C-u>call grammarous#create_update_info_window_of(b:grammarous_result)<CR>
|
||||
nnoremap <silent><Plug>(grammarous-reset) :<C-u>call grammarous#reset()<CR>
|
||||
nnoremap <silent><Plug>(grammarous-fixit) :<C-u>call grammarous#fixit(grammarous#get_error_at(getpos('.')[1 : 2], b:grammarous_result))<CR>
|
||||
nnoremap <silent><Plug>(grammarous-fixall) :<C-u>call grammarous#fixall(b:grammarous_result)<CR>
|
||||
nnoremap <silent><Plug>(grammarous-close-info-window) :<C-u>call grammarous#info_win#close()<CR>
|
||||
nnoremap <silent><Plug>(grammarous-remove-error) :<C-u>call grammarous#remove_error_at(getpos('.')[1 : 2], b:grammarous_result)<CR>
|
||||
nnoremap <silent><Plug>(grammarous-disable-rule) :<C-u>call grammarous#disable_rule_at(getpos('.')[1 : 2], b:grammarous_result)<CR>
|
||||
nnoremap <silent><Plug>(grammarous-disable-category) :<C-u>call grammarous#disable_category_at(getpos('.')[1 : 2], b:grammarous_result)<CR>
|
||||
nnoremap <silent><Plug>(grammarous-move-to-next-error) :<C-u>call grammarous#move_to_next_error(getpos('.')[1 : 2], b:grammarous_result)<CR>
|
||||
nnoremap <silent><Plug>(grammarous-move-to-previous-error) :<C-u>call grammarous#move_to_previous_error(getpos('.')[1 : 2], b:grammarous_result)<CR>
|
||||
|
||||
try
|
||||
call operator#user#define('grammarous', 'operator#grammarous#do')
|
||||
catch /^Vim\%((\a\+)\)\=:E117/
|
||||
" vim-operator-user is not installed
|
||||
endtry
|
||||
|
||||
let g:loaded_grammarous = 1
|
56
bundle/vim-grammarous/rplugin/python3/denite/source/grammarous.py
vendored
Normal file
56
bundle/vim-grammarous/rplugin/python3/denite/source/grammarous.py
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
from .base import Base
|
||||
|
||||
|
||||
class Source(Base):
|
||||
def __init__(self, vim):
|
||||
super().__init__(vim)
|
||||
self.name = 'grammarous'
|
||||
self.kind = 'file'
|
||||
|
||||
def on_init(self, context):
|
||||
context['__path'] = self.vim.current.buffer.name
|
||||
|
||||
def convert(self, item, context):
|
||||
"""convert one item from the search result to a candidate"""
|
||||
|
||||
word = "'{0}' -> {1}".format(
|
||||
item['context'][int(item['contextoffset']):(
|
||||
int(item['contextoffset']) + int(item['errorlength'])
|
||||
)], item['msg']
|
||||
)
|
||||
|
||||
return {
|
||||
'word': word,
|
||||
'action__path': context['__path'],
|
||||
'action__line': int(item['fromy']) + 1,
|
||||
'action__col': int(item['fromx']) + 1
|
||||
}
|
||||
|
||||
def gather_candidates(self, context):
|
||||
try:
|
||||
result = self.vim.eval('b:grammarous_result')
|
||||
except Exception as e:
|
||||
result = []
|
||||
|
||||
return [self.convert(item, context) for item in result]
|
||||
|
||||
def define_syntax(self):
|
||||
self.vim.command(
|
||||
"""syntax match deniteSource_grammarous /\\v^.*$/"""
|
||||
)
|
||||
self.vim.command(
|
||||
"""syntax region deniteSource__GrammarousError start="'" """
|
||||
"""end="'" oneline contained containedin=deniteSource_grammarous"""
|
||||
)
|
||||
self.vim.command(
|
||||
"""syntax match deniteSource__GrammarousArrow "->" """
|
||||
"""contained containedin=deniteSource_grammarous"""
|
||||
)
|
||||
|
||||
def highlight(self):
|
||||
self.vim.command(
|
||||
'highlight default link deniteSource__GrammarousArrow Keyword'
|
||||
)
|
||||
self.vim.command(
|
||||
'highlight default link deniteSource__GrammarousError ErrorMsg'
|
||||
)
|
Loading…
Reference in New Issue
Block a user