diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 8c37b94..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -include README.rst -include LICENSE -recursive-include doc * -prune doc/_build diff --git a/Makefile b/Makefile index 82ed607..a726101 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,8 @@ upload: dist/ $(PY) -m twine upload --repository pypi $<* .PHONY: install -install: - $(PY) -m pip install -U dist/*.whl +install: dist + $(PY) -m pip install --user --no-deps --force-reinstall dist/*.whl .PHONY: test test: diff --git a/setup.py b/setup.py index 94d131f..3c153a4 100644 --- a/setup.py +++ b/setup.py @@ -41,6 +41,7 @@ packages=find_namespace_packages(include=['sphinxnotes.*'], exclude=['sphinxnotes.snippet.tests']), include_package_data=True, + package_data={'sphinxnotes.snippet': ['integration/*']}, entry_points={ 'console_scripts': [ 'snippet=sphinxnotes.snippet.cli:main', diff --git a/sphinxnotes/snippet/cli.py b/sphinxnotes/snippet/cli.py index 4474b8e..7bce8bc 100644 --- a/sphinxnotes/snippet/cli.py +++ b/sphinxnotes/snippet/cli.py @@ -12,13 +12,14 @@ from typing import List from os import path from textwrap import dedent +from shutil import get_terminal_size from xdg.BaseDirectory import xdg_config_home from . import __title__, __version__, __description__ from .config import Config from .cache import Cache -from .table import tablify, VISIABLE_COLUMNS +from .table import tablify, COLUMNS DEFAULT_CONFIG_FILE = path.join(xdg_config_home, __title__, 'conf.py') @@ -26,6 +27,16 @@ class HelpFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter): pass +def get_integration_file(fn:str) -> str: + """ + Get file path of integration files. + + .. note:: files are included by ``setup(package_data=xxx)`` + """ + prefix = path.abspath(path.dirname(__file__)) + return path.join(prefix, 'integration', fn) + + def main(argv:List[str]=sys.argv[1:]) -> int: """Command line entrypoint.""" @@ -43,44 +54,39 @@ def main(argv:List[str]=sys.argv[1:]) -> int: # Init subcommands subparsers = parser.add_subparsers() - mgmtparser = subparsers.add_parser('stat', - aliases=['s'], + mgmtparser = subparsers.add_parser('stat', aliases=['s'], formatter_class=HelpFormatter, help='snippets statistic') mgmtparser.set_defaults(func=_on_command_mgmt) - listparser = subparsers.add_parser('list', - aliases=['l'], + listparser = subparsers.add_parser('list', aliases=['l'], formatter_class=HelpFormatter, - help='list snippet indexes, columns of indexes: %s' % - VISIABLE_COLUMNS) - listparser.add_argument('--kinds', '-k', - action='store_true', - default='*', + help='list snippet indexes, columns of indexes: %s' % COLUMNS) + listparser.add_argument('--kinds', '-k', type=str, default='*', help='list specified kinds only') - listparser.add_argument('--width', '-w', - action='store_true', - default=120, + listparser.add_argument('--width', '-w', type=int, + default=get_terminal_size((120, 0)).columns, help='width in characters of output') listparser.set_defaults(func=_on_command_list) - getparser = subparsers.add_parser('get', - aliases=['g'], + getparser = subparsers.add_parser('get', aliases=['g'], formatter_class=HelpFormatter, help='get information of snippet by index ID') - getparser.add_argument('--file', '-f', - action='store_true', + getparser.add_argument('--file', '-f', action='store_true', help='get source file path of snippet') - getparser.add_argument('--text', '-t', - action='store_true', - default=True, + getparser.add_argument('--text', '-t', action='store_true', default=True, help='get source reStructuredText of snippet') - getparser.add_argument('index_id', - type=str, - nargs='+', - help='index ID') + getparser.add_argument('index_id', type=str, nargs='+', help='index ID') getparser.set_defaults(func=_on_command_get) + igparser = subparsers.add_parser('integration', aliases=['i'], + formatter_class=HelpFormatter, + help='integration related commands') + igparser.add_argument('--zsh', '-z', action='store_true', help='dump zsh integration script') + igparser.add_argument('--nvim', '-n', action='store_true', help='dump neovim integration script') + igparser.set_defaults(func=_on_command_integration, parser=igparser) + + # Parse command line arguments args = parser.parse_args(argv) @@ -100,6 +106,8 @@ def main(argv:List[str]=sys.argv[1:]) -> int: # Call subcommand if hasattr(args, 'func'): args.func(args) + else: + parser.print_help() def _on_command_mgmt(args:argparse.Namespace): @@ -109,6 +117,8 @@ def _on_command_mgmt(args:argparse.Namespace): num_docs = len(cache.num_snippets_by_docid) num_snippets = sum(cache.num_snippets_by_project.values()) print(f'snippets are loaded from {cache.dirname}') + print(f'integration files are located at {get_integration_file("")}') + print('') print(f'I have {num_projects} project(s), {num_docs} documentation(s) and {num_snippets} snippet(s)') for i, v in cache.num_snippets_by_project.items(): print(f'project {i}:') @@ -127,11 +137,24 @@ def _on_command_get(args:argparse.Namespace): if not item: print('no such index ID', file=sys.stderr) sys.exit(1) + # FIXME: get multi attrs at once if args.file: print(item.snippet.file()) - if args.text: + elif args.text: print('\n'.join(item.snippet.text())) +def _on_command_integration(args:argparse.Namespace): + if args.zsh: + with open(get_integration_file('plugin.zsh'), 'r') as f: + print(f.read()) + return + if args.nvim: + with open(get_integration_file('plugin.nvim'), 'r') as f: + print(f.read()) + return + args.parser.print_help() + + if __name__ == '__main__': sys.exit(main()) diff --git a/sphinxnotes/snippet/integration/plugin.nvim b/sphinxnotes/snippet/integration/plugin.nvim new file mode 100644 index 0000000..88d9815 --- /dev/null +++ b/sphinxnotes/snippet/integration/plugin.nvim @@ -0,0 +1,90 @@ +" NeoVim integration for sphinxnotes-snippet +" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +" +" :Author: Shengyu Zhang +" :Date: 2021-04-01 +" :License: BSD +" :Version: 20210401 +" +" NOTE: junegunn/fzf.vim is required +" +" The recommanded key bindings are:: +" +" nmap v :call g:SphinxNotesSnippetListAndView() +" nmap e :call g:SphinxNotesSnippetListAndEdit() + + +let s:snippet = 'snippet' + +function! s:SplitID(row) + return split(a:row, ' ')[0] +endfunction + +function! g:SphinxNotesSnippetList(callback, kinds) + let cmd = [s:snippet, 'list', + \ '--kinds', a:kinds, + \ '--width', &columns - 2, + \ ] + call fzf#run({ + \ 'source': join(cmd, ' '), + \ 'sink': a:callback, + \ 'options': ['--with-nth', '2..', '--no-hscroll', '--header-lines', '1'], + \ }) +endfunction + +function! g:SphinxNotesSnippetListAndView() + function! s:CallView(selection) + call g:SphinxNotesSnippetView(s:SplitID(a:selection)) + endfunction + call g:SphinxNotesSnippetList(function('s:CallView'), 'c') +endfunction + +" https://github.com/anhmv/vim-float-window/blob/master/plugin/float-window.vim +function! g:SphinxNotesSnippetView(id) + let height = float2nr((&lines - 2) / 1.5) + let row = float2nr((&lines - height) / 2) + let width = float2nr(&columns / 1.5) + let col = float2nr((&columns - width) / 2) + + " Main Window + let opts = { + \ 'relative': 'editor', + \ 'style': 'minimal', + \ 'width': width, + \ 'height': height, + \ 'col': col, + \ 'row': row, + \ } + + let buf = nvim_create_buf(v:false, v:true) + " Global for :call + let g:sphinx_notes_snippet_win = nvim_open_win(buf, v:true, opts) + + " The content is always reStructuredText for now + set filetype=rst + " Press enter to return + nmap :call nvim_win_close(g:sphinx_notes_snippet_win, v:true) + + let cmd = [s:snippet, 'get', '--text', a:id] + execute '$read !' . join(cmd, ' ') + execute '$read !' . '..' + call append(line('$'), [ + \ '.. Inserted By sphinxnotes-snippet:', + \ '', + \ ' Press to return']) +endfunction + +function! g:SphinxNotesSnippetEdit(id) + let cmd = [s:snippet, 'get', '--file', a:id] + execute '$tabedit ' . system(join(cmd, ' ')) +endfunction + +function! g:SphinxNotesSnippetListAndEdit() + function! s:CallEdit(selection) + call g:SphinxNotesSnippetEdit(s:SplitID(a:selection)) + endfunction + call g:SphinxNotesSnippetList(function('s:CallEdit'), 'dc') +endfunction + +" vim: set shiftwidth=2: +" vim: set filetype=vim: diff --git a/sphinxnotes/snippet/integration/plugin.zsh b/sphinxnotes/snippet/integration/plugin.zsh new file mode 100644 index 0000000..5d5694c --- /dev/null +++ b/sphinxnotes/snippet/integration/plugin.zsh @@ -0,0 +1,36 @@ +# Z Shell integration for sphinxnotes-snippet +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# :Author: Shengyu Zhang +# :Date: 2021-03-20 +# :License: BSD +# :Version: 20210401 +# +# The recommanded key bindings are:: +# +# bindkey ^kv snippet-view +# bindkey ^ke snippet-edit + +snippet="snippet" + +# $1: kinds +function snippet_list() { + $snippet list --kinds $1 | \ + fzf --with-nth 2.. --no-hscroll --header-lines 1 | \ + cut -d ' ' -f1 +} + +function snippet_view() { + $snippet get --text $(snippet_list c) +} + +function snippet_edit() { + BUFFER="$BUFFER $EDITOR $($snippet get --file $(snippet_list dc))" + zle accept-line +} + +# Define a widget, mapped to our function above. +zle -N snippet-view snippet_view +zle -N snippet-edit snippet_edit + +# vim: set shiftwidth=2: diff --git a/sphinxnotes/snippet/table.py b/sphinxnotes/snippet/table.py index 7edcb4c..e51e25b 100644 --- a/sphinxnotes/snippet/table.py +++ b/sphinxnotes/snippet/table.py @@ -33,7 +33,7 @@ def tablify(indexes: Dict[IndexID,Index], kinds:str, width:int) -> Iterator[str] ellipsis.ellipsis(COLUMNS[1].upper(), kind_width, blank_sym=' '), ellipsis.ellipsis(COLUMNS[2].upper(), excerpt_width, blank_sym=' '), ellipsis.ellipsis(COLUMNS[3].upper(), path_width, blank_sym=' ' ), - COLUMNS[4].upper()]) + '\n' + COLUMNS[4].upper()]) yield header # Write rows @@ -46,5 +46,5 @@ def tablify(indexes: Dict[IndexID,Index], kinds:str, width:int) -> Iterator[str] ellipsis.ellipsis(f'[{index[0]}]', kind_width, blank_sym=' '), # Kind ellipsis.ellipsis(index[1], excerpt_width, blank_sym=' '), # Excerpt ellipsis.join(index[2], path_width, path_comp_width, blank_sym=' ' ), # Titleppath - ','.join(index[3])]) + '\n' # Keywords + ','.join(index[3])]) # Keywords yield row