mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-03-13 02:05:40 +08:00
parent
524ae813ad
commit
e9099b6611
@ -86,7 +86,7 @@ function! SpaceVim#layers#lang#vim#plugins() abort
|
||||
" https://github.com/maralla/completor.vim/issues/250
|
||||
endif
|
||||
endif
|
||||
call add(plugins,['tweekmonster/helpful.vim', {'on_cmd': 'HelpfulVersion'}])
|
||||
call add(plugins, [g:_spacevim_root_dir . 'bundle/helpful.vim', {'merged' : 0, 'on_cmd' : 'HelpfulVersion'}])
|
||||
return plugins
|
||||
endfunction
|
||||
|
||||
|
29
bundle/helpful.vim/.github/workflows/update-tags.yml
vendored
Normal file
29
bundle/helpful.vim/.github/workflows/update-tags.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Update Tags
|
||||
|
||||
on:
|
||||
# Run at midnight every day
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
update:
|
||||
name: Update Tags
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: update tags
|
||||
run: make update
|
||||
- name: get date
|
||||
id: date
|
||||
run: echo "::set-output name=date::$(date --rfc-3339=seconds)"
|
||||
- name: check for update
|
||||
id: diff
|
||||
run: echo "::set-output name=diff::$(git diff --name-only data/)"
|
||||
- name: push
|
||||
uses: actions-x/commit@v2
|
||||
with:
|
||||
name: Tag Update Bot
|
||||
files: data/tags doc/helpful-version.txt
|
||||
message: Auto-updated tags ${{ steps.date.outputs.date }}
|
||||
if: ${{ steps.diff.outputs.diff != '' }}
|
92
bundle/helpful.vim/.gitignore
vendored
Normal file
92
bundle/helpful.vim/.gitignore
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/support/src
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
|
||||
# Flask instance folder
|
||||
instance/
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# IPython Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# virtualenv
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
20
bundle/helpful.vim/LICENSE
Normal file
20
bundle/helpful.vim/LICENSE
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License
|
||||
Copyright (c) 2016 Tommy Allen
|
||||
|
||||
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.
|
13
bundle/helpful.vim/Makefile
Normal file
13
bundle/helpful.vim/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
.PHONY: update auto
|
||||
|
||||
update:
|
||||
cd support && python difftags.py
|
||||
|
||||
auto:
|
||||
-test "master" = "$$(git rev-parse --abbrev-ref HEAD)" \
|
||||
&& python3 support/difftags.py \
|
||||
&& test " M data/tags" = "$$(git status --porcelain | grep 'data/tags')" \
|
||||
&& git add data doc \
|
||||
&& git commit -m "Auto-updated tags $$(date --rfc-3339=seconds)" \
|
||||
&& git push
|
||||
git checkout -- doc/
|
37
bundle/helpful.vim/README.md
Normal file
37
bundle/helpful.vim/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# helpful.vim
|
||||
|
||||
A plugin for plugin developers to get the version of Vim and Neovim that
|
||||
introduced or removed features.
|
||||
|
||||

|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
The command `:HelpfulVersion` takes a Vim pattern to search for helptags and
|
||||
display version information.
|
||||
|
||||
Examples:
|
||||
|
||||
```vim
|
||||
" Search for a function
|
||||
:HelpfulVersion matchaddpos()
|
||||
|
||||
" Search for keys
|
||||
:HelpfulVersion <.*>
|
||||
|
||||
" Case-insensitive search
|
||||
:HelpfulVersion f11\c
|
||||
```
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
- `b:helpful` - If set to `1`, display version information about the text under
|
||||
the cursor on `CursorMoved` in `help` or `vim` filetypes.
|
||||
- `g:helpful` - Same as above but always on. It's also less humorous to read
|
||||
out loud.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
389
bundle/helpful.vim/autoload/helpful.vim
Normal file
389
bundle/helpful.vim/autoload/helpful.vim
Normal file
@ -0,0 +1,389 @@
|
||||
let s:base = expand('<sfile>:p:h:h')
|
||||
|
||||
" Text around the word that might produce a match, in order of 'usefulness' to
|
||||
" a developer.
|
||||
let s:ornaments = [
|
||||
\ [':', ''],
|
||||
\ ['', '()'],
|
||||
\ [':func-', ''],
|
||||
\ ["'", "'"],
|
||||
\ ['<', '>'],
|
||||
\ ['@', ''],
|
||||
\ ['"', ''],
|
||||
\ ['hl-', ''],
|
||||
\ ['syn-', ''],
|
||||
\ ['[', ']'],
|
||||
\ ['{', '}'],
|
||||
\ ['+', ''],
|
||||
\ ]
|
||||
|
||||
|
||||
function! s:load_data() abort
|
||||
if exists('s:data')
|
||||
return
|
||||
endif
|
||||
|
||||
let s:data = {}
|
||||
for line in readfile(s:base.'/data/tags')
|
||||
let parts = split(line, "\x07")
|
||||
for flavor in parts[1:]
|
||||
let name = matchstr(flavor, '^[^:]\+')
|
||||
let versions = split(matchstr(flavor, ':\zs.*'), ',', 1)
|
||||
if !has_key(s:data, parts[0])
|
||||
let s:data[parts[0]] = {}
|
||||
endif
|
||||
|
||||
let s:data[parts[0]][name] = {
|
||||
\ '+': versions[0],
|
||||
\ '-': versions[1],
|
||||
\ }
|
||||
endfor
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
|
||||
" Wrap a pattern with an outer pattern that matches likely helptag characters.
|
||||
function! s:pattern_wrap(pattern, help) abort
|
||||
let pattern = a:pattern
|
||||
if a:help
|
||||
let tagpattern = '\%(\1\@!.\)*'.pattern.'\%(\1\@!.\)*'
|
||||
return '\%(\([|*]\)\zs'.tagpattern.'\ze\1\)\|'
|
||||
\ .'\%(\zs''[^'']*'.pattern.'[^'']*''\ze\)\|'
|
||||
\ .'\%(\zs\%(:\%(\w\|[-_]\)\)\?<[^<>]*'.pattern.'[^<>]*>\ze\)\|'
|
||||
\ .'\%(\zs\[[^\[\]]*'.pattern.'[^\[\]]*\]\ze\)\|'
|
||||
\ .'\%(\zs{[^{}]*'.pattern.'[^{}]*}\ze\)\|'
|
||||
\ .'\zs\%(\k\|[_-]\)*'.pattern.'\%(\k\|[_-]\)*\ze('
|
||||
endif
|
||||
let word_atom = '\%(\w\|[&:_@#{}/\+-]\)*'
|
||||
return word_atom.pattern.word_atom
|
||||
endfunction
|
||||
|
||||
|
||||
" Get the word under the cursor.
|
||||
" <cword> and <cWORD> aren't reliable for helpful.vim
|
||||
function! s:cword() abort
|
||||
let pattern = s:pattern_wrap('\%'.col('.').'c', &filetype == 'help')
|
||||
let word = matchstr(getline('.'), pattern)
|
||||
|
||||
if &filetype == 'help'
|
||||
return word
|
||||
endif
|
||||
|
||||
if word =~# '^!'
|
||||
let word = word[1:]
|
||||
endif
|
||||
|
||||
if empty(word)
|
||||
return ''
|
||||
endif
|
||||
|
||||
" Force a match on <Key> tags, but make an exception for :map-<whatever>
|
||||
if word !~# '^:' && word =~# '<.*>'
|
||||
let word = matchstr(word, '<[^>]\+>')
|
||||
endif
|
||||
|
||||
" Find some clues about the text under the cursor.
|
||||
if word =~# '^&l:'
|
||||
let word = "'".word[3:]."'"
|
||||
elseif word =~# '^&'
|
||||
let word = "'".word[1:]."'"
|
||||
elseif word =~# '^end.'
|
||||
let word = word[3:]
|
||||
endif
|
||||
|
||||
return word
|
||||
endfunction
|
||||
|
||||
|
||||
" Find the best match for a helptag. Oranments are put around the the tag if
|
||||
" there's no first match. If there is no match, return an empty string.
|
||||
function! s:match_word(word) abort
|
||||
call s:load_data()
|
||||
|
||||
if has_key(s:data, a:word)
|
||||
return a:word
|
||||
endif
|
||||
|
||||
for [t1, t2] in s:ornaments
|
||||
if has_key(s:data, t1.a:word.t2)
|
||||
return t1.a:word.t2
|
||||
endif
|
||||
endfor
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:helptag_version(word, ...) abort
|
||||
let word = a:word
|
||||
|
||||
if !exists('b:_helpful_word') || b:_helpful_word[0] != word
|
||||
let word = s:match_word(word)
|
||||
if empty(word)
|
||||
return
|
||||
endif
|
||||
|
||||
let info = s:data[word]
|
||||
let b:_helpful_word = [a:word, word, info]
|
||||
else
|
||||
let word = b:_helpful_word[1]
|
||||
let info = b:_helpful_word[2]
|
||||
endif
|
||||
|
||||
let keys = has('nvim') ? ['neovim', 'vim'] : ['vim', 'neovim']
|
||||
|
||||
if !a:0
|
||||
redraw
|
||||
endif
|
||||
|
||||
echohl Special
|
||||
echo printf('%*S', a:0 ? a:1 : 0, word)
|
||||
echohl None
|
||||
|
||||
for k in keys
|
||||
if !has_key(info, k) || (empty(info[k]['+']) && empty(info[k]['-']))
|
||||
continue
|
||||
endif
|
||||
|
||||
echon ' | '
|
||||
echon k.': '
|
||||
|
||||
if !empty(info[k]['+'])
|
||||
echohl DiffAdd
|
||||
echon '+'.info[k]['+']
|
||||
echohl None
|
||||
endif
|
||||
|
||||
if !empty(info[k]['-'])
|
||||
if !empty(info[k]['+'])
|
||||
echon ', '
|
||||
endif
|
||||
echohl DiffDelete
|
||||
echon '-'.info[k]['-']
|
||||
echohl None
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
|
||||
" Find a helptag using a word under the cursor
|
||||
function! helpful#cursor_word() abort
|
||||
if mode() ==? 'v'
|
||||
" Need to save the original visual marks? Visual marks aren't updated
|
||||
" until visual mode is left.
|
||||
let view = winsaveview()
|
||||
let v1 = getpos("'<")
|
||||
let v2 = getpos("'>")
|
||||
|
||||
execute "normal! \<esc>gv"
|
||||
|
||||
let p1 = getpos("'<")[1:2]
|
||||
let p2 = getpos("'>")[1:2]
|
||||
|
||||
call winrestview(view)
|
||||
call setpos("'<", v1)
|
||||
call setpos("'>", v2)
|
||||
|
||||
if p1[0] != p2[0]
|
||||
return
|
||||
endif
|
||||
|
||||
let word = getline(p1[0])[p1[1]-1:p2[1]-1]
|
||||
else
|
||||
let word = s:cword()
|
||||
endif
|
||||
|
||||
if empty(word)
|
||||
echo ''
|
||||
return
|
||||
endif
|
||||
|
||||
call s:helptag_version(word)
|
||||
endfunction
|
||||
|
||||
|
||||
" Normalize Vim's goofy old versions so they can be compared with the other
|
||||
" versions.
|
||||
"
|
||||
" Where M = major, m = minor, R = rev, A = alpha
|
||||
" MmRRRRA
|
||||
"
|
||||
" Vim:
|
||||
" 7.4.300 = 7403000
|
||||
" 7.2.1234 = 7212340
|
||||
" 7.1a = 7100001
|
||||
"
|
||||
" Neovim:
|
||||
" 0.1.3 = 100030
|
||||
" 1.0.1 = 1000010
|
||||
" 2.5.22 = 2500220
|
||||
function! s:parse_version(version) abort
|
||||
let vparts = split(matchstr(a:version, '\d.*'), '\.')
|
||||
if empty(vparts)
|
||||
return 0
|
||||
endif
|
||||
|
||||
let major = str2nr(vparts[0])
|
||||
let minor = 0
|
||||
let alpha = 0
|
||||
let rev = 0
|
||||
|
||||
if vparts[1] =~ '\D'
|
||||
let minor = str2nr(matchstr(vparts[1], '\d'))
|
||||
let alpha = char2nr(tolower(matchstr(vparts[1], '\D'))) - 96
|
||||
elseif len(vparts) == 2 && vparts[1] =~# '\d\{4}'
|
||||
let minor = 0
|
||||
let rev = str2nr(vparts[1])
|
||||
else
|
||||
let minor = str2nr(vparts[1])
|
||||
endif
|
||||
|
||||
if len(vparts) > 2
|
||||
let rev = str2nr(vparts[2])
|
||||
endif
|
||||
|
||||
return major * 1000000 +
|
||||
\ minor * 100000 +
|
||||
\ rev * 10 +
|
||||
\ alpha
|
||||
endfunction
|
||||
|
||||
|
||||
" Reverses the result of s:parse_version()
|
||||
function! s:unparse_version(version) abort
|
||||
if !a:version || a:version == 99999999
|
||||
return '???'
|
||||
endif
|
||||
|
||||
let v = a:version
|
||||
let major = a:version / 1000000
|
||||
let v -= major * 1000000
|
||||
let minor = v / 100000
|
||||
let v -= minor * 100000
|
||||
let rev = v / 10
|
||||
let v -= rev * 10
|
||||
|
||||
if v
|
||||
return printf('v%d.%d%s', major, minor, nr2char(v + 96))
|
||||
elseif major == 7 && minor == 0
|
||||
return printf('v%d.0%03d', major, rev)
|
||||
endif
|
||||
|
||||
return printf('v%d.%d.%0*d', major, minor, major >= 7 ? 3 : 0, rev)
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:_lookup_sort(a, b) abort
|
||||
if a:a[1] < a:b[1]
|
||||
return -1
|
||||
elseif a:a[1] > a:b[1]
|
||||
return 1
|
||||
endif
|
||||
|
||||
let al = strlen(a:a[0])
|
||||
let bl = strlen(a:b[0])
|
||||
|
||||
if al < bl
|
||||
return -1
|
||||
elseif al > bl
|
||||
return 1
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
|
||||
" Find helptag using a pattern and print the results.
|
||||
function! helpful#lookup(pattern) abort
|
||||
call s:load_data()
|
||||
let tags = []
|
||||
let width = 0
|
||||
|
||||
for tag in keys(s:data)
|
||||
let m = match(tag, a:pattern)
|
||||
if m != -1
|
||||
call add(tags, [tag, m])
|
||||
let width = max([width, strlen(tag)])
|
||||
endif
|
||||
endfor
|
||||
|
||||
let s:search_pattern = a:pattern
|
||||
for [tag, _] in sort(tags, 's:_lookup_sort')
|
||||
call s:helptag_version(tag, width)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:min_pair(x, y) abort
|
||||
return a:x[0] < a:y[0] ? a:x : a:y
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:max_pair(x, y) abort
|
||||
return a:x[0] > a:y[0] ? a:x : a:y
|
||||
endfunction
|
||||
|
||||
|
||||
" Find functions in the buffer and print min and max versions that are
|
||||
" required. Only a proof of concept, there is no plan for it to be smarter.
|
||||
function! helpful#buffer_version() abort
|
||||
call s:load_data()
|
||||
|
||||
let view = winsaveview()
|
||||
let s = @/
|
||||
let b = getreg('b')
|
||||
let bt = getregtype('b')
|
||||
normal! qbq
|
||||
silent g/\k\+(/normal! gn"By
|
||||
let @/ = s
|
||||
call histdel('search', -1)
|
||||
|
||||
let funcs = map(split(getreg('b'), '('), 'v:val."()"')
|
||||
call setreg('b', b, bt)
|
||||
call winrestview(view)
|
||||
|
||||
let neovim_min = [0, '']
|
||||
let neovim_max = [99999999, '']
|
||||
let vim_min = [0, '']
|
||||
let vim_max = [99999999, '']
|
||||
|
||||
for f in funcs
|
||||
if has_key(s:data, f)
|
||||
let vinfo = s:data[f]
|
||||
|
||||
if has_key(vinfo, 'neovim')
|
||||
let neovim_max = s:min_pair(neovim_max,
|
||||
\ [s:parse_version(vinfo['neovim']['-']), f])
|
||||
let neovim_min = s:max_pair(neovim_min,
|
||||
\ [s:parse_version(vinfo['neovim']['+']), f])
|
||||
endif
|
||||
|
||||
if has_key(vinfo, 'vim')
|
||||
let vim_max = s:min_pair(vim_max,
|
||||
\ [s:parse_version(vinfo['vim']['-']), f])
|
||||
let vim_min = s:max_pair(vim_min,
|
||||
\ [s:parse_version(vinfo['vim']['+']), f])
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
echo printf('Neovim: %s (%s) - %s (%s)',
|
||||
\ s:unparse_version(neovim_min[0]), neovim_min[1],
|
||||
\ s:unparse_version(neovim_max[0]), neovim_max[1])
|
||||
echo printf('Vim: %s (%s) - %s (%s)',
|
||||
\ s:unparse_version(vim_min[0]), vim_min[1],
|
||||
\ s:unparse_version(vim_max[0]), vim_max[1])
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:enabled() abort
|
||||
return get(b:, 'helpful', get(g:, 'helpful', 0))
|
||||
endfunction
|
||||
|
||||
|
||||
function! helpful#setup() abort
|
||||
augroup helpful
|
||||
autocmd! * <buffer>
|
||||
autocmd CursorMoved <buffer> if s:enabled() | call helpful#cursor_word() | endif
|
||||
augroup END
|
||||
endfunction
|
58712
bundle/helpful.vim/doc/helpful-version.txt
Normal file
58712
bundle/helpful.vim/doc/helpful-version.txt
Normal file
File diff suppressed because it is too large
Load Diff
5
bundle/helpful.vim/plugin/helpful.vim
Normal file
5
bundle/helpful.vim/plugin/helpful.vim
Normal file
@ -0,0 +1,5 @@
|
||||
augroup help_versions
|
||||
autocmd! FileType vim,help call helpful#setup()
|
||||
augroup END
|
||||
|
||||
command! -nargs=+ -complete=help HelpfulVersion call helpful#lookup(<q-args>)
|
9429
bundle/helpful.vim/plugin/tags.txt
Normal file
9429
bundle/helpful.vim/plugin/tags.txt
Normal file
File diff suppressed because it is too large
Load Diff
4
bundle/helpful.vim/support/README.md
Normal file
4
bundle/helpful.vim/support/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Attention!
|
||||
|
||||
You do not need to run `difftags.py` yourself. It is a utility for keeping
|
||||
this repo up to date.
|
200
bundle/helpful.vim/support/difftags.py
Normal file
200
bundle/helpful.vim/support/difftags.py
Normal file
@ -0,0 +1,200 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
from datetime import date, datetime
|
||||
from collections import defaultdict
|
||||
|
||||
base = os.path.dirname(__file__)
|
||||
src = os.path.abspath(os.path.join(base, 'src'))
|
||||
repos = {
|
||||
'neovim': 'https://github.com/neovim/neovim.git',
|
||||
'vim': 'https://github.com/vim/vim.git',
|
||||
}
|
||||
|
||||
neovim_repo = os.path.join(src, 'neovim')
|
||||
vim_repo = os.path.join(src, 'vim')
|
||||
|
||||
_tag_re = re.compile(br'\s\*([^*\s]+)\*\s')
|
||||
|
||||
if not os.path.isdir(src):
|
||||
os.makedirs(os.path.join(src))
|
||||
|
||||
|
||||
def command(cmd, print_stdout=False, print_stderr=True, cwd=None):
|
||||
"""Run a git command and return the stdout as lines.
|
||||
|
||||
The stdout is returned as bytes in case a diff breaks up unicode bytes.
|
||||
"""
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, cwd=cwd)
|
||||
stdout, stderr = p.communicate()
|
||||
|
||||
if print_stdout and stdout:
|
||||
print(stdout.decode('utf8'))
|
||||
|
||||
if print_stderr and stderr:
|
||||
print(cmd, stderr.decode('utf8'), file=sys.stderr)
|
||||
|
||||
return list(filter(None, stdout.split(b'\n')))
|
||||
|
||||
|
||||
def get_latest(repo):
|
||||
path = os.path.join(src, repo)
|
||||
if os.path.exists(path):
|
||||
return command(['git', 'fetch', '-q', '--tags', repos.get(repo),
|
||||
'master:master'], True, cwd=path)
|
||||
else:
|
||||
return command(['git', 'clone', '-q', '--bare', '--single-branch',
|
||||
repos.get(repo), path], True)
|
||||
|
||||
|
||||
def normalize_hunk(lines):
|
||||
"""Normalize a hunk to give ample whitespace between tag delimiters.
|
||||
|
||||
Also cleans out garbage whitespace
|
||||
"""
|
||||
out = b' '
|
||||
for line in lines:
|
||||
out += re.sub(br'\s', b' ', line[1:]).replace(b'* *', b'* *') + b' '
|
||||
return out
|
||||
|
||||
|
||||
def doc_diff(path, a, b):
|
||||
"""Parse diff lines to get tags
|
||||
|
||||
A counter is used to keep track of the additions and deletions.
|
||||
"""
|
||||
tags = defaultdict(int)
|
||||
if not a or not b:
|
||||
cmd = ['git', 'show', '-U0', '--oneline', a or b, '--',
|
||||
'runtime/doc/*.txt']
|
||||
else:
|
||||
cmd = ['git', 'diff', '-U0', a, b, '--', 'runtime/doc/*.txt']
|
||||
stdout = command(cmd, cwd=path)
|
||||
i = 0
|
||||
|
||||
while i < len(stdout):
|
||||
line = stdout[i]
|
||||
i += 1
|
||||
if line[:2] != b'@@':
|
||||
continue
|
||||
|
||||
delta = line.split()
|
||||
deleted = 1
|
||||
added = 1
|
||||
if b',' in delta[1]:
|
||||
deleted = int(delta[1].split(b',')[-1])
|
||||
if b',' in delta[2]:
|
||||
added = int(delta[2].split(b',')[-1])
|
||||
|
||||
if deleted > 0:
|
||||
for t in _tag_re.findall(normalize_hunk(stdout[i:i+deleted])):
|
||||
tags[t] -= 1
|
||||
i += deleted
|
||||
|
||||
if added > 0:
|
||||
for t in _tag_re.findall(normalize_hunk(stdout[i:i+added])):
|
||||
tags[t] += 1
|
||||
i += added
|
||||
|
||||
return tags
|
||||
|
||||
|
||||
def repo_tags(path):
|
||||
"""Get tags that can be diff'd."""
|
||||
stdout = command(['git', 'log', '--pretty=oneline'], cwd=path)
|
||||
first = stdout[-1].decode('utf8').split()[0]
|
||||
last = stdout[0].decode('utf8').split()[0]
|
||||
|
||||
cmd = ['git', 'for-each-ref',
|
||||
"--format=%(*committerdate:raw)%(committerdate:raw) %(refname) %(objectname)",
|
||||
'refs/tags']
|
||||
|
||||
tags = [('alpha', first, None)]
|
||||
stdout = command(cmd, print_stderr=False, cwd=path)
|
||||
for line in sorted(stdout, key=lambda x: x.split()[0]):
|
||||
parts = line.decode('utf8').split()
|
||||
timestamp, tz = parts[:2]
|
||||
tag_date = date.fromtimestamp(int(timestamp))
|
||||
tag, ref = parts[-2:]
|
||||
tag = tag.split('/')[-1]
|
||||
if tag.startswith('untagged-'):
|
||||
continue
|
||||
tags.append((tag, ref, tag_date))
|
||||
tags.append(('dev', last, None))
|
||||
tags.append(('dev', 'HEAD', None))
|
||||
|
||||
return tags
|
||||
|
||||
|
||||
def collect(repo):
|
||||
"""Collects the helptag differences between versions."""
|
||||
path = os.path.join(src, repo)
|
||||
|
||||
tags = repo_tags(path)
|
||||
versions = defaultdict(lambda: {'added': [], 'removed': []})
|
||||
|
||||
for i in range(len(tags) - 1):
|
||||
helptags = doc_diff(path, tags[i][1], tags[i+1][1])
|
||||
version = versions[tags[i+1][0]]
|
||||
for k, v in helptags.items():
|
||||
if v < 0:
|
||||
version['removed'].append(k)
|
||||
elif v > 0:
|
||||
version['added'].append(k)
|
||||
|
||||
return [(info, versions[info[0]]) for info in tags]
|
||||
|
||||
|
||||
def generate():
|
||||
"""Generate the data file for the plugin.
|
||||
|
||||
All byte strings from here.
|
||||
"""
|
||||
get_latest('neovim')
|
||||
get_latest('vim')
|
||||
|
||||
tags = defaultdict(lambda: defaultdict(lambda: defaultdict(bytes)))
|
||||
|
||||
for target in (b'neovim', b'vim'):
|
||||
for (version, ref, tag_date), deltas in collect(target.decode('ascii')):
|
||||
for change, items in deltas.items():
|
||||
for item in items:
|
||||
tags[item][target][change] = version.encode('ascii')
|
||||
|
||||
with open(os.path.join(base, '..', 'data', 'tags'), 'wb') as fp:
|
||||
with open(os.path.join(base, '..', 'doc',
|
||||
'helpful-version.txt'), 'wb') as hfp:
|
||||
|
||||
hfp.write(b'*helpful-version.txt* Generated: ' +
|
||||
str(datetime.utcnow().replace(microsecond=0))
|
||||
.encode('utf8') + b' UTC')
|
||||
hfp.write(b'\n\n')
|
||||
hfp.write(b'A listing of helptags with the versions they became '
|
||||
b'available or were removed.\n\n')
|
||||
|
||||
for helptag, targets in sorted(tags.items(), key=lambda x: x[0]):
|
||||
# Not creating new tags since it adds too much noise to help
|
||||
# completion.
|
||||
hfp.write(b'\n|' + helptag + b'|\n\n')
|
||||
|
||||
target_versions = []
|
||||
for target, versions in sorted(targets.items(), key=lambda x: x[0]):
|
||||
hfp.write(b' ' + target.title().rjust(8) + b': ')
|
||||
if versions['added']:
|
||||
hfp.write(b'+' + versions['added'] + b' ')
|
||||
if versions['removed']:
|
||||
hfp.write(b'-' + versions['removed'] + b' ')
|
||||
hfp.write(b'\n')
|
||||
|
||||
target_versions.append(target + b':' +
|
||||
versions['added'] + b',' +
|
||||
versions['removed'])
|
||||
fp.write(b'\x07'.join([helptag] + target_versions))
|
||||
fp.write(b'\n')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
generate()
|
Loading…
x
Reference in New Issue
Block a user