mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-04-14 07:09:11 +08:00
chore(lang#python): use bundle python plugins
This commit is contained in:
parent
f3093ccdcf
commit
5d7bd47ccf
@ -110,11 +110,11 @@ function! SpaceVim#layers#lang#python#plugins() abort
|
||||
endif
|
||||
call add(plugins, ['heavenshell/vim-pydocstring',
|
||||
\ { 'on_cmd' : 'Pydocstring'}])
|
||||
call add(plugins, ['Vimjas/vim-python-pep8-indent',
|
||||
call add(plugins, [g:_spacevim_root_dir . 'bundle/vim-python-pep8-indent',
|
||||
\ { 'on_ft' : 'python'}])
|
||||
call add(plugins, ['jeetsukumaran/vim-pythonsense',
|
||||
call add(plugins, [g:_spacevim_root_dir . 'bundle/vim-pythonsense',
|
||||
\ { 'on_ft' : 'python'}])
|
||||
call add(plugins, ['alfredodeza/coveragepy.vim',
|
||||
call add(plugins, [g:_spacevim_root_dir . 'bundle/coveragepy.vim',
|
||||
\ { 'merged' : 0}])
|
||||
call add(plugins, [g:_spacevim_root_dir . 'bundle/vim-virtualenv',
|
||||
\ { 'merged' : 0}])
|
||||
|
18
bundle/README.md
vendored
18
bundle/README.md
vendored
@ -2,6 +2,15 @@
|
||||
|
||||
In `bundle/` directory, there are two kinds of plugins: forked plugins without changes and forked plugins which have been changed.
|
||||
|
||||
<!-- vim-markdown-toc GFM -->
|
||||
|
||||
- [Changed plugin:](#changed-plugin)
|
||||
- [No changed plugins](#no-changed-plugins)
|
||||
- [`lang#ruby` layer](#langruby-layer)
|
||||
- [`lang#python` layer](#langpython-layer)
|
||||
|
||||
<!-- vim-markdown-toc -->
|
||||
|
||||
### Changed plugin:
|
||||
|
||||
- `vim-bookmarks`: based on [`MattesGroeger/vim-bookmarks@3adeae1`](https://github.com/MattesGroeger/vim-bookmarks/commit/3adeae10639edcba29ea80dafa1c58cf545cb80e)
|
||||
@ -33,4 +42,13 @@ In `bundle/` directory, there are two kinds of plugins: forked plugins without c
|
||||
- [vim-autohotkey@6bf1e71](https://github.com/wsdjeg/vim-autohotkey/tree/6bf1e718c73cad22caad3ecd8c4db96db05b37f7)
|
||||
- [vim-cmake-syntax@bcc3a97a](https://github.com/pboettch/vim-cmake-syntax/tree/bcc3a97ab934f03e112becd4ce79286793152b47)
|
||||
- [itchyny/calendar.vim@896360bfd](https://github.com/itchyny/calendar.vim/tree/896360bfd9d5347b2726dd247df2d2cbdb8cf1d6)
|
||||
|
||||
#### `lang#ruby` layer
|
||||
|
||||
- [vim-ruby@55335f261](https://github.com/vim-ruby/vim-ruby/tree/55335f2614f914b117f02995340886f409eddc02)
|
||||
|
||||
#### `lang#python` layer
|
||||
|
||||
- [jeetsukumaran/vim-pythonsense@9200a57](https://github.com/jeetsukumaran/vim-pythonsense/tree/9200a57629c904ed2ab8c9b2e8c5649d311794ba)
|
||||
- [alfredodeza/coveragepy.vim@afcef30](https://github.com/alfredodeza/coveragepy.vim/tree/afcef301b723048c25250d2d539b9473a8e4f747)
|
||||
- [Vimjas/vim-python-pep8-indent@60ba5e](https://github.com/Vimjas/vim-python-pep8-indent/tree/60ba5e11a61618c0344e2db190210145083c91f8)
|
||||
|
1
bundle/coveragepy.vim/.gitignore
vendored
Normal file
1
bundle/coveragepy.vim/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/doc/tags
|
137
bundle/coveragepy.vim/README.rst
vendored
Normal file
137
bundle/coveragepy.vim/README.rst
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
coveragepy.vim
|
||||
==============
|
||||
A Vim plugin to help integrate Ned Batchelder's excellent ``coverage.py`` (see:
|
||||
http://nedbatchelder.com/code/coverage/) tool into the editor.
|
||||
|
||||
Allows you to bring up a buffer with detailed information from a coverage
|
||||
report command and mark each line in your source that is not being covered.
|
||||
|
||||
You can also use that buffer to navigate into files that have reported missing
|
||||
statements and display the missed lines.
|
||||
|
||||
Optionally, you can also hide or display the marks as you make progress.
|
||||
|
||||
Showing a Coverage Report
|
||||
-------------------------
|
||||
|
||||
.. image:: https://github.com/alfredodeza/coveragepy.vim/raw/master/extras/session.png
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
If you have Tim Pope's Pathogen you only need to place the plugin directory
|
||||
inside your bundle dir, otherwise it is a single file that should go into::
|
||||
|
||||
vim/ftplugin/python/
|
||||
|
||||
Usage
|
||||
=====
|
||||
This plugin provides a single command: ``Coveragepy`` that accepts a few
|
||||
arguments. Each argument and its usage is described in detail below.
|
||||
|
||||
Whenever a ``report`` or a ``session`` is called the cursor will be placed on
|
||||
the first uncovered line if any.
|
||||
|
||||
``report``
|
||||
--------
|
||||
The main action is performed with this command (same as with ``coverage.py``) and
|
||||
when it runs it calls ``coverage.py`` and loads the information into a split
|
||||
buffer.
|
||||
|
||||
It also collects all the information needed to be able to mark all lines from
|
||||
files that have reported missing coverage statements. To run this command do::
|
||||
|
||||
:Coveragepy report
|
||||
|
||||
|
||||
``session``
|
||||
-----------
|
||||
This argument toggles the reporting buffer (closes it is open or opens if it is
|
||||
not already there). Makes sense to map it directly as a shortcut as it is
|
||||
completely toggable.
|
||||
|
||||
A big plus on this session buffer is that you can navigate through the list of
|
||||
reported paths (it actually circles through!) with the arrow keys or j and k.
|
||||
|
||||
If you want to navigate to a reported path that has missing lines just hit
|
||||
Enter (or Return) and the plugin will go to the previous window and open that
|
||||
selected file and then display the missing lines.
|
||||
|
||||
|
||||
``show``
|
||||
--------
|
||||
Shows or hides the actual Vim `sign` marks that display which lines are missing
|
||||
coverage. It is implemented as a toggable argument so it will do the opposite
|
||||
of what is currently shown.
|
||||
It is useful to be able to hide these if you are already aware about the lines
|
||||
that need to be covered and do not want to be visually disturbed by the signs.
|
||||
|
||||
|
||||
``refresh``
|
||||
--------
|
||||
Reloads and parses coverage data similar to ``:Coveragepy report`` but does
|
||||
not open report window and only updates line coverage marks (displayed by
|
||||
``show`` command above).
|
||||
|
||||
|
||||
``version``
|
||||
-----------
|
||||
Displays the current plugin version
|
||||
|
||||
|
||||
sign configuration
|
||||
------------------
|
||||
By default, the character used for identifying uncovered lines is '^', but this
|
||||
can be overridden with the following configuration flag::
|
||||
|
||||
g:coveragepy_uncovered_sign
|
||||
|
||||
In a ``.vimrc`` file or locally in a buffer, changing this value to `-` would
|
||||
look like::
|
||||
|
||||
let g:coveragepy_uncovered_sign = '-'
|
||||
|
||||
|
||||
Executable auto-detection
|
||||
-------------------------
|
||||
The plugin tries to detect the right executable name for ``coverage`` in the
|
||||
following order of precedence:
|
||||
|
||||
* ``coverage``
|
||||
* ``python-coverage``
|
||||
* ``python3-coverage``
|
||||
* ``python2-coverage``
|
||||
* ``python2.7-coverage``
|
||||
|
||||
If none of the above match or if multiple exist but are not suitable for the
|
||||
project (e.g. both python2 and python3 exist), it is possible to force it by
|
||||
using the following configuration flag::
|
||||
|
||||
let g:coveragepy_executable = "/path/to/prefered/coverage"
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
MIT
|
||||
Copyright (c) 2011 Alfredo Deza <alfredodeza [at] gmail [dot] com>
|
||||
|
||||
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.
|
||||
|
||||
|
121
bundle/coveragepy.vim/doc/coveragepy.txt
vendored
Normal file
121
bundle/coveragepy.vim/doc/coveragepy.txt
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
*coveragepy.txt* Runs coverage.py within vim and displays and interactive
|
||||
buffer that allows to mark missing statements.
|
||||
|
||||
==============================================================================
|
||||
CONTENTS *Coveragepy-contents*
|
||||
|
||||
1. Intro ............................ |CoveragepyIntro|
|
||||
2. Usage ............................ |CoveragepyUsage|
|
||||
3. report .......................... |CoveragepyReport|
|
||||
4. Session .................................. |Session|
|
||||
5. Show .................................. |ShowNoshow|
|
||||
6. License ........................ |CoveragepyLicense|
|
||||
7. Bugs .............................. |CoveragepyBugs|
|
||||
8. Credits ........................ |CoveragepyCredits|
|
||||
|
||||
==============================================================================
|
||||
1. Intro *CoveragepyIntro*
|
||||
|
||||
A Vim plugin to help integrate Ned Batchelder's excellent ``coverage.py`` (see:
|
||||
http://nedbatchelder.com/code/coverage/) tool into the editor.
|
||||
|
||||
Allows you to bring up a buffer with detailed information from a coverage
|
||||
report command and mark each line in your source that is not being covered.
|
||||
|
||||
You can also use that buffer to navigate into files that have reported missing
|
||||
statements and display the missed lines.
|
||||
|
||||
Optionally, you can also hide or display the marks as you make progress.
|
||||
|
||||
==============================================================================
|
||||
2. Usage *CoveragepyUsage*
|
||||
|
||||
This plugin provides a single command::
|
||||
|
||||
Coveragepy
|
||||
|
||||
All arguments are able to be tab-completed.
|
||||
|
||||
For running tests the plugin provides 3 arguments with an optional one.
|
||||
These arguments are::
|
||||
|
||||
report
|
||||
session
|
||||
show
|
||||
version
|
||||
|
||||
==============================================================================
|
||||
3. Report *CoveragepyReport*
|
||||
|
||||
The main action is performed with this command (same as with ``coverage.py``)
|
||||
and when it runs it calls ``coverage.py`` and loads the information into
|
||||
a split buffer.
|
||||
|
||||
It also collects all the information needed to be able to mark all lines from
|
||||
files that have reported missing coverage statements. To run this command do::
|
||||
|
||||
:Coveragepy report
|
||||
|
||||
==============================================================================
|
||||
4. Session *Session*
|
||||
|
||||
This argument toggles the reporting buffer (closes it is open or opens if it is
|
||||
not already there). Makes sense to map it directly as a shortcut as it is
|
||||
completely toggable.
|
||||
|
||||
A big plus on this session buffer is that you can navigate through the list of
|
||||
reported paths (it actually circles through!) with the arrow keys or j and k.
|
||||
|
||||
If you want to navigate to a reported path that has missing lines just hit
|
||||
Enter (or Return) and the plugin will go to the previous window and open that
|
||||
selected file and then display the missing lines.
|
||||
|
||||
==============================================================================
|
||||
5. Show *Show*
|
||||
|
||||
Shows or hides the actual Vim `sign` marks that display which lines are missing
|
||||
coverage.
|
||||
The ``show`` command is implemented as a toggable argument, so whenever you
|
||||
call it and the signs are showing it will hide them, if they are not it will
|
||||
show them.
|
||||
|
||||
It is useful to be able to hide these if you are already aware about the lines
|
||||
that need to be covered and do not want to be visually disturbed by the signs.
|
||||
|
||||
==============================================================================
|
||||
5. License *CoveragepyLicense*
|
||||
|
||||
MIT
|
||||
Copyright (c) 2011 Alfredo Deza <alfredodeza [at] gmail [dot] com>
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
6. Bugs *CoveragepyBugs*
|
||||
|
||||
If you find a bug please post it on the issue tracker:
|
||||
https://github.com/alfredodeza/coveragepy.vim/issues
|
||||
|
||||
==============================================================================
|
||||
7. Credits *CoveragepyCredits*
|
||||
|
||||
Thanks to Ned Batchelder for the awesome ``coverage.py`` tool, continuously
|
||||
making a huge difference in my day to day testing.
|
||||
|
||||
==============================================================================
|
BIN
bundle/coveragepy.vim/extras/session.png
vendored
Normal file
BIN
bundle/coveragepy.vim/extras/session.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 185 KiB |
387
bundle/coveragepy.vim/ftplugin/python/coveragepy.vim
vendored
Normal file
387
bundle/coveragepy.vim/ftplugin/python/coveragepy.vim
vendored
Normal file
@ -0,0 +1,387 @@
|
||||
" File: coveragepy.vim
|
||||
" Description: Displays coverage reports from Ned Batchelder's excellent
|
||||
" coverage.py tool
|
||||
" (see: http://nedbatchelder.com/code/coverage )
|
||||
" Maintainer: Alfredo Deza <alfredodeza AT gmail.com>
|
||||
" License: MIT
|
||||
"============================================================================
|
||||
|
||||
|
||||
if exists("g:loaded_coveragepy") || &cp
|
||||
finish
|
||||
endif
|
||||
|
||||
function! s:HasCoverage() abort
|
||||
if (g:coveragepy_executable != "")
|
||||
return
|
||||
endif
|
||||
let executable_list = ["coverage", "python-coverage", "python3-coverage", "python2-coverage", "python2.7-coverage"]
|
||||
for executable_name in executable_list
|
||||
if (executable(executable_name) == 1)
|
||||
let g:coveragepy_executable = executable_name
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
if (g:coveragepy_executable == "")
|
||||
echoerr("This plugin needs coverage.py installed and accessible")
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Global variables for registering next/previous error
|
||||
let g:coveragepy_last_session = ""
|
||||
let g:coveragepy_marks = []
|
||||
let g:coveragepy_session_map = {}
|
||||
let g:coveragepy_executable = ""
|
||||
|
||||
|
||||
function! s:ToggleSigns()
|
||||
|
||||
if exists("b:coveragepy_is_displaying") && b:coveragepy_is_displaying
|
||||
call s:ClearSigns()
|
||||
let b:coveragepy_is_displaying = 0
|
||||
else
|
||||
call s:HighlightMissing()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:CoveragepySyntax() abort
|
||||
let b:current_syntax = 'Coveragepy'
|
||||
syn match CoveragepyTitleDecoration "\v\-{2,}"
|
||||
syn match CoveragepyHeaders '\v(^Name\s+|\s*Stmts\s*|\s*Miss\s+|Cover|Missing$)'
|
||||
syn match CoveragepyDelimiter "\v^(\-\-)\s+"
|
||||
syn match CoveragepyPercent "\v(\d+\%\s+)"
|
||||
syn match CoveragepyLineNumbers "\v(\s*\d+,|\d+-\d+,|\d+-\d+$|\d+$)"
|
||||
|
||||
hi def link CoveragepyFiles Number
|
||||
hi def link CoveragepyHeaders Comment
|
||||
hi def link CoveragepyTitleDecoration Keyword
|
||||
hi def link CoveragepyDelimiter Comment
|
||||
hi def link CoveragepyPercent Boolean
|
||||
hi def link CoveragepyLineNumbers Error
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:Echo(msg, ...) abort
|
||||
redraw!
|
||||
let x=&ruler | let y=&showcmd
|
||||
set noruler noshowcmd
|
||||
if (a:0 == 1)
|
||||
echo a:msg
|
||||
else
|
||||
echohl WarningMsg | echo a:msg | echohl None
|
||||
endif
|
||||
|
||||
let &ruler=x | let &showcmd=y
|
||||
endfun
|
||||
|
||||
function! s:FindCoverage() abort
|
||||
let found = findfile(".coverage", ".;")
|
||||
if (found !~ '.coverage')
|
||||
return ""
|
||||
endif
|
||||
" Return the actual directory where .coverage is found
|
||||
return fnamemodify(found, ":h")
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:ClearSigns() abort
|
||||
execute("sign unplace * group=uncovered buffer=".bufnr('%'))
|
||||
execute("sign unplace * group=branchuncovered buffer=".bufnr('%'))
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:SetHighlight()
|
||||
if exists('g:coveragepy_uncovered_sign')
|
||||
let text = g:coveragepy_uncovered_sign
|
||||
else
|
||||
let text = '^'
|
||||
endif
|
||||
highlight default NoCoverage ctermfg=red guifg=#ef0000
|
||||
highlight default NoBranchCoverage ctermfg=yellow guifg=#ebef00
|
||||
|
||||
execute 'sign define uncovered text=' . text . ' texthl=NoCoverage'
|
||||
execute 'sign define branchuncovered text=' . text . ' texthl=NoBranchCoverage'
|
||||
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:HighlightMissing() abort
|
||||
call s:SetHighlight()
|
||||
let b:coveragepy_is_displaying = 1
|
||||
if (g:coveragepy_session_map == {})
|
||||
call s:CoveragepyReport()
|
||||
endif
|
||||
call s:ClearSigns()
|
||||
|
||||
let current_buffer_py = matchlist(expand("%:p"), '\v(.*)(.py)')[0]
|
||||
let current_buffer = matchlist(expand("%:p"), '\v(.*)(.py)')[1]
|
||||
|
||||
for path in keys(g:coveragepy_session_map)
|
||||
if (current_buffer =~ path) || (current_buffer_py =~ path)
|
||||
for position in g:coveragepy_session_map[path]
|
||||
execute(":sign place ". position ." line=". position ." group=uncovered name=uncovered buffer=".bufnr("%"))
|
||||
endfor
|
||||
for position in g:coveragepy_session_map['BRANCH' . path]
|
||||
execute(":sign place ". position ." line=". position ." group=branchuncovered name=branchuncovered buffer=".bufnr("%"))
|
||||
endfor
|
||||
" FIXME: I had to comment this out because it was no longer correct
|
||||
" after adding branch support
|
||||
"execute g:coveragepy_session_map[path][0]
|
||||
redraw!
|
||||
return
|
||||
endif
|
||||
endfor
|
||||
call s:Echo("Coveragepy ==> 100% covered", 1)
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:Strip(input_string) abort
|
||||
return split(a:input_string, " ")[0]
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:Roulette(direction) abort
|
||||
let orig_line = line('.')
|
||||
let last_line = line('$') - 3
|
||||
|
||||
" if for some reason there is not enough
|
||||
" coverage output return
|
||||
if last_line < 3
|
||||
return
|
||||
endif
|
||||
|
||||
" Move to the line we need
|
||||
let move_to = orig_line + a:direction
|
||||
|
||||
if move_to > last_line
|
||||
let move_to = 3
|
||||
exe move_to
|
||||
elseif (move_to < 3) && (a:direction == -1)
|
||||
let move_to = last_line
|
||||
exe move_to
|
||||
elseif (move_to < 3) && (a:direction == 1)
|
||||
let move_to = 3
|
||||
exe move_to
|
||||
else
|
||||
exe move_to
|
||||
endif
|
||||
|
||||
if move_to == 1
|
||||
let _num = move_to
|
||||
else
|
||||
let _num = move_to - 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:CoveragepyReport() abort
|
||||
" Run a report, ignore errors and show missing lines,
|
||||
" which is what we are interested after all :)
|
||||
let original_dir = getcwd()
|
||||
" First try to see if we actually have a .coverage file
|
||||
" to work with
|
||||
let has_coverage = s:FindCoverage()
|
||||
if (has_coverage == "")
|
||||
return 0
|
||||
else
|
||||
" set the original directory path
|
||||
" as a global
|
||||
let g:coveragepy_path = has_coverage
|
||||
" change dir to where coverage is
|
||||
" and do all the magic we need
|
||||
exe "cd " . has_coverage
|
||||
call s:ClearSigns()
|
||||
let g:coveragepy_last_session = ""
|
||||
|
||||
" Allow for rcfile
|
||||
if exists("g:coveragepy_rcfile")
|
||||
let s:coveragepy_rcfile=" --rcfile=".resolve(expand(g:coveragepy_rcfile))
|
||||
else
|
||||
let s:coveragepy_rcfile=""
|
||||
endif
|
||||
|
||||
let cmd = g:coveragepy_executable." report -m -i".s:coveragepy_rcfile
|
||||
let out = system(cmd)
|
||||
let g:coveragepy_last_session = out
|
||||
call s:ReportParse()
|
||||
|
||||
" Finally get back to where we initially where
|
||||
exe "cd " . original_dir
|
||||
return 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:ReportParse() abort
|
||||
" After coverage runs, parse the content so we can get
|
||||
" line numbers mapped to files
|
||||
let path_to_lines = {}
|
||||
for line in split(g:coveragepy_last_session, '\n')
|
||||
if (line =~ '\v(\s*\d+,|\d+-\d+,|\d+-\d+$|\d+$)') && line !~ '\v(100\%)'
|
||||
let path = split(line, ' ')[0]
|
||||
let match_split = split(line, '%')
|
||||
let line_nos = match_split[-1]
|
||||
let all_line_nos = s:LineNumberParse(line_nos)
|
||||
let all_branch_line_nos = s:BranchLineNumberParse(line_nos)
|
||||
let path_to_lines[path] = all_line_nos
|
||||
let path_to_lines['BRANCH' . path] = all_branch_line_nos
|
||||
endif
|
||||
endfor
|
||||
let g:coveragepy_session_map = path_to_lines
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:BranchLineNumberParse(numbers) abort
|
||||
" Line numbers will come with a possible comma in them
|
||||
" and lots of extra space. Let's remove them and strip them
|
||||
let parsed_list = []
|
||||
let splitted = split(a:numbers, ',')
|
||||
for line_no in splitted
|
||||
" only add numbers that are branch-coverage numbers
|
||||
if len(split(line_no, '->')) > 1
|
||||
if line_no =~ '->-'
|
||||
let split_char = '->-'
|
||||
else
|
||||
let split_char = '->'
|
||||
endif
|
||||
if line_no =~ '-'
|
||||
let split_nos = split(line_no, split_char)
|
||||
let first = s:Strip(split_nos[0])
|
||||
call add(parsed_list, first)
|
||||
else
|
||||
call add(parsed_list, s:Strip(line_no))
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
return parsed_list
|
||||
endfunction
|
||||
|
||||
function! s:LineNumberParse(numbers) abort
|
||||
" Line numbers will come with a possible comma in them
|
||||
" and lots of extra space. Let's remove them and strip them
|
||||
let parsed_list = []
|
||||
let splitted = split(a:numbers, ',')
|
||||
for line_no in splitted
|
||||
" only add numbers that are not branch-coverage numbers
|
||||
if len(split(line_no, '->')) == 1
|
||||
if line_no =~ '-'
|
||||
let split_nos = split(line_no, '-')
|
||||
let first = s:Strip(split_nos[0])
|
||||
let second = s:Strip(split_nos[1])
|
||||
for range_no in range(first, second)
|
||||
call add(parsed_list, range_no)
|
||||
endfor
|
||||
else
|
||||
call add(parsed_list, s:Strip(line_no))
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
return parsed_list
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:ClearAll() abort
|
||||
let bufferL = ['LastSession.coveragepy']
|
||||
for b in bufferL
|
||||
let _window = bufwinnr(b)
|
||||
if (_window != -1)
|
||||
silent! execute _window . 'wincmd w'
|
||||
silent! execute 'q'
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:LastSession() abort
|
||||
call s:ClearAll()
|
||||
let winnrback = bufwinnr(expand("%"))
|
||||
if (len(g:coveragepy_last_session) == 0)
|
||||
call s:CoveragepyReport()
|
||||
endif
|
||||
let winnr = bufwinnr('LastSession.coveragepy')
|
||||
silent! execute winnr < 0 ? 'botright new ' . 'LastSession.coveragepy' : winnr . 'wincmd w'
|
||||
setlocal buftype=nowrite bufhidden=wipe nobuflisted noswapfile nowrap number filetype=coveragepy
|
||||
let session = split(g:coveragepy_last_session, '\n')
|
||||
call append(0, session)
|
||||
silent! execute 'resize ' . line('$')
|
||||
silent! execute 'normal gg'
|
||||
silent! execute 'nnoremap <silent> <buffer> q :q! <CR>'
|
||||
nnoremap <silent><script> <buffer> <C-n> :call <sid>Roulette(1)<CR>
|
||||
nnoremap <silent><script> <buffer> <down> :call <sid>Roulette(1)<CR>
|
||||
nnoremap <silent><script> <buffer> j :call <sid>Roulette(1)<CR>
|
||||
nnoremap <silent><script> <buffer> <C-p> :call <sid>Roulette(-1)<CR>
|
||||
nnoremap <silent><script> <buffer> <up> :call <sid>Roulette(-1)<CR>
|
||||
nnoremap <silent><script> <buffer> k :call <sid>Roulette(-1)<CR>
|
||||
nnoremap <silent> <buffer> <Enter> :call <sid>OpenBuffer()<CR>
|
||||
call s:CoveragepySyntax()
|
||||
exe winnrback . 'wincmd w'
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:OpenBuffer() abort
|
||||
let raw_path = split(getline('.'), ' ')[0] . '.py'
|
||||
" newer coverage versions use the .py extension, previously it
|
||||
" didn't.
|
||||
let path = split(raw_path, '.py')[0] . '.py'
|
||||
let absolute_path = g:coveragepy_path . '/' . path
|
||||
if filereadable(absolute_path)
|
||||
execute 'wincmd p'
|
||||
silent! execute ":e " . absolute_path
|
||||
call s:HighlightMissing()
|
||||
execute 'wincmd p'
|
||||
call s:CoveragepySyntax()
|
||||
else
|
||||
call s:Echo("Could not load file: " . path)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:Version() abort
|
||||
call s:Echo("coveragepy version 1.1dev", 1)
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:Completion(ArgLead, CmdLine, CursorPos) abort
|
||||
let actions = "report\nshow\nnoshow\nrefresh\nsession\n"
|
||||
let extras = "version\n"
|
||||
return actions . extras
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:Proxy(action, ...) abort
|
||||
" Make sure that if we are called, we have coverage installed
|
||||
call s:HasCoverage()
|
||||
if (a:action == "show")
|
||||
call s:ToggleSigns()
|
||||
elseif (a:action == "noshow")
|
||||
call s:ClearSigns()
|
||||
elseif (a:action == "refresh")
|
||||
call s:ClearAll()
|
||||
let g:coveragepy_session_map = {}
|
||||
call s:HighlightMissing()
|
||||
elseif (a:action == "session")
|
||||
let winnr = bufwinnr('LastSession.coveragepy')
|
||||
if (winnr != -1)
|
||||
silent! execute 'wincmd b'
|
||||
silent! execute 'q'
|
||||
return
|
||||
else
|
||||
call s:LastSession()
|
||||
endif
|
||||
elseif (a:action == "report")
|
||||
let report = s:CoveragepyReport()
|
||||
if report == 1
|
||||
call s:LastSession()
|
||||
call s:HighlightMissing()
|
||||
else
|
||||
call s:Echo("No .coverage was found in current or parent dirs")
|
||||
endif
|
||||
elseif (a:action == "version")
|
||||
call s:Version()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
command! -nargs=+ -complete=custom,s:Completion Coveragepy call s:Proxy(<f-args>)
|
||||
|
37
bundle/vim-python-pep8-indent/.circleci/config.yml
vendored
Normal file
37
bundle/vim-python-pep8-indent/.circleci/config.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
version: 2
|
||||
|
||||
common: &common
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: blueyed/vim-python-pep8-indent-vims-for-test:3@sha256:e7e3c4f4b021954a40f2f1d88dc470f119dc65603c63724d1c58cbe437fdc2d4
|
||||
|
||||
jobs:
|
||||
test:
|
||||
<<: *common
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Run tests
|
||||
command: |
|
||||
spec/make-coverage
|
||||
- run:
|
||||
name: Report coverage
|
||||
command: |
|
||||
covimerage xml
|
||||
codecov -X search gcov pycov -f coverage.xml
|
||||
|
||||
checkqa:
|
||||
<<: *common
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Lint
|
||||
command: |
|
||||
vint **/*.vim
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
test:
|
||||
jobs:
|
||||
- test
|
||||
- checkqa
|
7
bundle/vim-python-pep8-indent/.coveragerc
vendored
Normal file
7
bundle/vim-python-pep8-indent/.coveragerc
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
[run]
|
||||
plugins = covimerage
|
||||
data_file = .coverage_covimerage
|
||||
source = indent/python.vim
|
||||
|
||||
[report]
|
||||
include = indent/python.vim
|
2
bundle/vim-python-pep8-indent/.dockerignore
vendored
Normal file
2
bundle/vim-python-pep8-indent/.dockerignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!Gemfile
|
3
bundle/vim-python-pep8-indent/.gitignore
vendored
Normal file
3
bundle/vim-python-pep8-indent/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.*.swp
|
||||
.coverage_covimerage
|
||||
Gemfile.lock
|
37
bundle/vim-python-pep8-indent/CONTRIBUTING.rst
vendored
Normal file
37
bundle/vim-python-pep8-indent/CONTRIBUTING.rst
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
How To Contribute
|
||||
=================
|
||||
|
||||
``vim-python-pep8-indent`` is always open for suggestions and contributions by generous developers.
|
||||
I’ve collected a few tips to get you started.
|
||||
|
||||
Please:
|
||||
|
||||
- *Always* add tests for your code.
|
||||
- Write `good commit messages`_.
|
||||
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
|
||||
- They are written in Ruby_ (sorry :() using vimrunner_ which requires rspec_.
|
||||
- The tests go into ``spec/indent/indent_spec.rb``.
|
||||
Look at the ``describe`` blocks to get the hang of it.
|
||||
- Run the tests with the command::
|
||||
|
||||
$ rspec spec
|
||||
- Alternatively you can use Docker::
|
||||
|
||||
$ make test_docker
|
||||
|
||||
- You can select tests based on line numbers, e.g.::
|
||||
|
||||
$ rspec ./spec/indent/indent_spec.rb:385
|
||||
$ make test_docker RSPEC_ARGS=./spec/indent/indent_spec.rb:385
|
||||
|
||||
Thank you for considering to contribute!
|
||||
|
||||
|
||||
.. _Ruby: https://www.ruby-lang.org/
|
||||
.. _`good commit messages`: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||
.. _vimrunner: https://github.com/AndrewRadev/vimrunner
|
||||
.. _rspec: https://github.com/rspec/rspec
|
121
bundle/vim-python-pep8-indent/COPYING.txt
vendored
Normal file
121
bundle/vim-python-pep8-indent/COPYING.txt
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
24
bundle/vim-python-pep8-indent/Dockerfile
vendored
Normal file
24
bundle/vim-python-pep8-indent/Dockerfile
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
FROM testbed/vim:latest
|
||||
|
||||
RUN apk --no-cache add gtk+2.0-dev libx11-dev libxt-dev mcookie xauth xvfb
|
||||
# NOTE: +profile needs huge features.
|
||||
RUN install_vim -tag v8.1.0129 -name vim --with-features=huge \
|
||||
--disable-channel --disable-netbeans --disable-xim \
|
||||
--enable-gui=gtk2 --with-x -build
|
||||
RUN ln -s /vim-build/bin/vim /usr/bin/gvim
|
||||
RUN gvim --version
|
||||
|
||||
# Install covimerage and vint.
|
||||
# NOTE: we have py2 already via gtk+2.0-dev.
|
||||
# NOTE: enum34+pathlib+typing gets installed as workaround for broken vim-vint wheel.
|
||||
RUN apk --no-cache add py2-pip \
|
||||
&& pip install --no-cache-dir codecov covimerage==0.0.9 vim-vint enum34 pathlib typing \
|
||||
&& rm -rf /usr/include /usr/lib/python*/turtle* /usr/lib/python*/tkinter
|
||||
|
||||
WORKDIR /vim-python-pep8-indent
|
||||
|
||||
ADD Gemfile .
|
||||
RUN apk --no-cache add coreutils ruby-bundler
|
||||
RUN bundle install
|
||||
|
||||
ENTRYPOINT ["rspec", "spec"]
|
3
bundle/vim-python-pep8-indent/Gemfile
vendored
Normal file
3
bundle/vim-python-pep8-indent/Gemfile
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
source 'https://rubygems.org'
|
||||
gem "vimrunner", "0.3.4"
|
||||
gem "rspec"
|
25
bundle/vim-python-pep8-indent/Makefile
vendored
Normal file
25
bundle/vim-python-pep8-indent/Makefile
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
test:
|
||||
VIMRUNNER_REUSE_SERVER=1 xvfb-run bundle exec rspec
|
||||
|
||||
# Run tests in dockerized Vims.
|
||||
DOCKER_REPO:=blueyed/vim-python-pep8-indent-vims-for-test
|
||||
DOCKER_TAG:=3
|
||||
DOCKER_IMAGE:=$(DOCKER_REPO):$(DOCKER_TAG)
|
||||
|
||||
docker_image:
|
||||
docker build -t $(DOCKER_REPO):$(DOCKER_TAG) .
|
||||
docker_push:
|
||||
docker push $(DOCKER_REPO):$(DOCKER_TAG)
|
||||
docker_update_latest:
|
||||
docker tag $(DOCKER_REPO):$(DOCKER_TAG) $(DOCKER_REPO):latest
|
||||
docker push $(DOCKER_REPO):latest
|
||||
|
||||
test_docker: XVFB_ERRORFILE:=/dev/null
|
||||
test_docker:
|
||||
@set -x; export DISPLAY=$(if $(VIMRUNNER_TEST_DISPLAY),$(VIMRUNNER_TEST_DISPLAY),172.17.0.1:99; Xvfb -ac -listen tcp :99 >$(XVFB_ERRORFILE) 2>&1 & XVFB_PID=$$!); \
|
||||
docker run --rm -ti -e DISPLAY -e VIMRUNNER_REUSE_SERVER=1 \
|
||||
-v $(CURDIR):/vim-python-pep8-indent $(DOCKER_IMAGE) $(RSPEC_ARGS) \
|
||||
$(if $(VIMRUNNER_TEST_DISPLAY),,; ret=$$?; kill $$XVFB_PID; exit $$ret)
|
||||
|
||||
test_coverage:
|
||||
spec/make-coverage
|
169
bundle/vim-python-pep8-indent/README.rst
vendored
Normal file
169
bundle/vim-python-pep8-indent/README.rst
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
vim-python-pep8-indent
|
||||
======================
|
||||
|
||||
.. image:: https://circleci.com/gh/Vimjas/vim-python-pep8-indent.svg?style=svg
|
||||
:target: https://circleci.com/gh/Vimjas/vim-python-pep8-indent
|
||||
.. image:: https://codecov.io/gh/Vimjas/vim-python-pep8-indent/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/Vimjas/vim-python-pep8-indent
|
||||
|
||||
This small script modifies Vim_’s indentation behavior to comply with PEP8_ and my aesthetic preferences.
|
||||
Most importantly::
|
||||
|
||||
foobar(foo,
|
||||
bar)
|
||||
|
||||
and::
|
||||
|
||||
foobar(
|
||||
foo,
|
||||
bar
|
||||
)
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Install the plugin using your favorite plugin manager / method, a few examples
|
||||
follow:
|
||||
|
||||
Pathogen
|
||||
^^^^^^^^
|
||||
|
||||
Follow the instructions on installing Pathogen_ and then:
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ cd ~/.vim/bundle
|
||||
$ git clone https://github.com/Vimjas/vim-python-pep8-indent.git
|
||||
|
||||
|
||||
Vundle
|
||||
^^^^^^
|
||||
|
||||
Follow the instructions on installing Vundle_ and add the appropriate plugin line into your ``.vimrc``:
|
||||
|
||||
.. code-block:: vim
|
||||
|
||||
Plugin 'Vimjas/vim-python-pep8-indent'
|
||||
|
||||
|
||||
NeoBundle
|
||||
^^^^^^^^^
|
||||
|
||||
Follow the instructions on installing NeoBundle_ and add the appropriate NeoBundle line into your ``.vimrc``:
|
||||
|
||||
.. code-block:: vim
|
||||
|
||||
NeoBundle 'Vimjas/vim-python-pep8-indent'
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
g:python_pep8_indent_multiline_string
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can configure the initial indentation of multiline strings using ``g:python_pep8_indent_multiline_string`` (which can also be set per buffer).
|
||||
This defaults to ``0``, which means that multiline strings are not indented.
|
||||
``-1`` and positive values will be used as-is, where ``-1`` is a special value for Vim's ``indentexpr``, and will keep the existing indent (using Vim's ``autoindent`` setting).
|
||||
``-2`` is meant to be used for strings that are wrapped with ``textwrap.dedent`` etc. It will add a level of indentation if the multiline string started in the previous line, without any content in it already::
|
||||
|
||||
testdir.makeconftest("""
|
||||
_
|
||||
|
||||
With content already, it will be aligned to the opening parenthesis::
|
||||
|
||||
testdir.makeconftest("""def pytest_addoption(parser):
|
||||
_
|
||||
|
||||
Existing indentation (including ``0``) in multiline strings will be kept, so this setting only applies to the indentation of new/empty lines.
|
||||
|
||||
g:python_pep8_indent_hang_closing
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Control closing bracket indentation with ``python_pep8_indent_hang_closing``, set globally or per buffer.
|
||||
|
||||
By default (set to ``0``), closing brackets line up with the opening line::
|
||||
|
||||
my_list = [
|
||||
1, 2, 3,
|
||||
4, 5, 6,
|
||||
]
|
||||
result = some_function_that_takes_arguments(
|
||||
'a', 'b', 'c',
|
||||
'd', 'e', 'f',
|
||||
)
|
||||
|
||||
With ``python_pep8_indent_hang_closing = 1``, closing brackets line up with the items::
|
||||
|
||||
my_list = [
|
||||
1, 2, 3,
|
||||
4, 5, 6,
|
||||
]
|
||||
result = some_function_that_takes_arguments(
|
||||
'a', 'b', 'c',
|
||||
'd', 'e', 'f',
|
||||
)
|
||||
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
In case it is not working, please make sure your Vim is configured to load
|
||||
indent files (``filetype indent on``).
|
||||
This is typically the case when using a plugin manager, but check its docs.
|
||||
|
||||
Check ``:verbose set indentexpr?`` in a Python file, which should show
|
||||
something like the following:
|
||||
|
||||
indentexpr=GetPythonPEPIndent(v:lnum)
|
||||
Last set from ~/…/plugged/vim-python-pep8-indent/indent/python.vim
|
||||
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
Please note that Kirill Klenov’s python-mode_ ships its own version of this bundle.
|
||||
Therefore, if you want to use this version specifically, you’ll have to disable python-mode’s using:
|
||||
|
||||
.. code-block:: vim
|
||||
|
||||
let g:pymode_indent = 0
|
||||
|
||||
|
||||
License and Authorship
|
||||
----------------------
|
||||
|
||||
This script is based on one from Vim’s official `script repo`_ that was *not* originally written by me.
|
||||
Unfortunately the indentation was off by one character in one case and the script hasn’t been updated since 2005.
|
||||
|
||||
Even more unfortunately, I wasn’t able to reach any of the original authors/maintainers:
|
||||
**David Bustos** and **Eric Mc Sween**.
|
||||
|
||||
So I fixed the annoyance with the help of `Steve Losh`_ and am putting it out here so you don’t have to patch the original yourself.
|
||||
The original patch is still available here_.
|
||||
|
||||
Over the time a lot more improvements have been contributed_ by `generous people`_.
|
||||
|
||||
I’d like to thank the original authors here for their work and release it hereby to the *Public Domain* (using the CC0_ licence) since I hope that would be in their spirit.
|
||||
If anyone with a say in this objects, please let me_ know immediately.
|
||||
Also, if someone is in contact with one of them, I would appreciate being introduced.
|
||||
|
||||
While my Vimscript_ skills are still feeble, I intend to maintain it for now.
|
||||
This mainly means that I’ll triage through bugs and pull requests but won’t be fixing much myself.
|
||||
|
||||
|
||||
.. _Vim: http://www.vim.org/
|
||||
.. _PEP8: http://www.python.org/dev/peps/pep-0008/
|
||||
.. _`script repo`: http://www.vim.org/scripts/script.php?script_id=974
|
||||
.. _`Steve Losh`: http://stevelosh.com/
|
||||
.. _here: https://gist.github.com/2965846
|
||||
.. _Neobundle: https://github.com/Shougo/neobundle.vim
|
||||
.. _Pathogen: https://github.com/tpope/vim-pathogen
|
||||
.. _python-mode: https://github.com/klen/python-mode
|
||||
.. _`Vimscript`: http://learnvimscriptthehardway.stevelosh.com/
|
||||
.. _vundle: https://github.com/gmarik/Vundle.vim
|
||||
.. _me: https://hynek.me/
|
||||
.. _CC0: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
.. _contributed: https://github.com/hynek/vim-python-pep8-indent/blob/master/CONTRIBUTING.rst
|
||||
.. _`generous people`: https://github.com/hynek/vim-python-pep8-indent/graphs/contributors
|
6
bundle/vim-python-pep8-indent/docker-compose.yml
vendored
Normal file
6
bundle/vim-python-pep8-indent/docker-compose.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
version: '2'
|
||||
services:
|
||||
rspec:
|
||||
build: .
|
||||
volumes:
|
||||
- .:/vim-python-pep8-indent
|
1
bundle/vim-python-pep8-indent/indent/cython.vim
vendored
Normal file
1
bundle/vim-python-pep8-indent/indent/cython.vim
vendored
Normal file
@ -0,0 +1 @@
|
||||
python.vim
|
454
bundle/vim-python-pep8-indent/indent/python.vim
vendored
Normal file
454
bundle/vim-python-pep8-indent/indent/python.vim
vendored
Normal file
@ -0,0 +1,454 @@
|
||||
" PEP8 compatible Python indent file
|
||||
" Language: Python
|
||||
" Maintainer: Daniel Hahler <https://daniel.hahler.de/>
|
||||
" Prev Maintainer: Hynek Schlawack <hs@ox.cx>
|
||||
" Prev Maintainer: Eric Mc Sween <em@tomcom.de> (address invalid)
|
||||
" Original Author: David Bustos <bustos@caltech.edu> (address invalid)
|
||||
" License: CC0
|
||||
"
|
||||
" vim-python-pep8-indent - A nicer Python indentation style for vim.
|
||||
" Written in 2004 by David Bustos <bustos@caltech.edu>
|
||||
" Maintained from 2004-2005 by Eric Mc Sween <em@tomcom.de>
|
||||
" Maintained from 2013 by Hynek Schlawack <hs@ox.cx>
|
||||
" Maintained from 2017 by Daniel Hahler <https://daniel.hahler.de/>
|
||||
"
|
||||
" To the extent possible under law, the author(s) have dedicated all copyright
|
||||
" and related and neighboring rights to this software to the public domain
|
||||
" worldwide. This software is distributed without any warranty.
|
||||
" You should have received a copy of the CC0 Public Domain Dedication along
|
||||
" with this software. If not, see
|
||||
" <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
|
||||
" Only load this indent file when no other was loaded.
|
||||
if exists('b:did_indent')
|
||||
finish
|
||||
endif
|
||||
let b:did_indent = 1
|
||||
|
||||
setlocal nolisp
|
||||
setlocal autoindent
|
||||
setlocal indentexpr=GetPythonPEPIndent(v:lnum)
|
||||
setlocal indentkeys=!^F,o,O,<:>,0),0],0},=elif,=except
|
||||
|
||||
if !exists('g:python_pep8_indent_multiline_string')
|
||||
let g:python_pep8_indent_multiline_string = 0
|
||||
endif
|
||||
|
||||
if !exists('g:python_pep8_indent_hang_closing')
|
||||
let g:python_pep8_indent_hang_closing = 0
|
||||
endif
|
||||
|
||||
" TODO: check required patch for timeout argument, likely lower than 7.3.429 though.
|
||||
if !exists('g:python_pep8_indent_searchpair_timeout')
|
||||
if has('patch-8.0.1483')
|
||||
let g:python_pep8_indent_searchpair_timeout = 150
|
||||
else
|
||||
let g:python_pep8_indent_searchpair_timeout = 0
|
||||
endif
|
||||
endif
|
||||
|
||||
let s:block_rules = {
|
||||
\ '^\s*elif\>': [['if', 'elif'], ['else']],
|
||||
\ '^\s*except\>': [['try', 'except'], []],
|
||||
\ '^\s*finally\>': [['try', 'except', 'else'], []]
|
||||
\ }
|
||||
let s:block_rules_multiple = {
|
||||
\ '^\s*else\>': [['if', 'elif', 'for', 'try', 'except'], []]
|
||||
\ }
|
||||
" Pairs to look for when searching for opening parenthesis.
|
||||
" The value is the maximum offset in lines.
|
||||
let s:paren_pairs = {'()': 50, '[]': 100, '{}': 1000}
|
||||
|
||||
if &filetype ==# 'pyrex' || &filetype ==# 'cython'
|
||||
let b:control_statement = '\v^\s*(class|def|if|while|with|for|except|cdef|cpdef)>'
|
||||
else
|
||||
let b:control_statement = '\v^\s*(class|def|if|while|with|for|except)>'
|
||||
endif
|
||||
let s:stop_statement = '^\s*\(break\|continue\|raise\|return\|pass\)\>'
|
||||
|
||||
let s:skip_after_opening_paren = 'synIDattr(synID(line("."), col("."), 0), "name") ' .
|
||||
\ '=~? "\\vcomment|jedi\\S"'
|
||||
|
||||
let s:special_chars_syn_pattern = "\\vstring|comment|^pythonbytes%(contents)=$|pythonTodo|jedi\\S"
|
||||
|
||||
if !get(g:, 'python_pep8_indent_skip_concealed', 0) || !has('conceal')
|
||||
" Skip strings and comments. Return 1 for chars to skip.
|
||||
" jedi* refers to syntax definitions from jedi-vim for call signatures, which
|
||||
" are inserted temporarily into the buffer.
|
||||
function! s:_skip_special_chars(line, col)
|
||||
return synIDattr(synID(a:line, a:col, 0), 'name')
|
||||
\ =~? s:special_chars_syn_pattern
|
||||
endfunction
|
||||
else
|
||||
" Also ignore anything concealed.
|
||||
" TODO: doc; likely only necessary with jedi-vim, where a better version is
|
||||
" planned (https://github.com/Vimjas/vim-python-pep8-indent/pull/98).
|
||||
|
||||
" Wrapper around synconcealed for older Vim (7.3.429, used on Travis CI).
|
||||
function! s:is_concealed(line, col)
|
||||
let concealed = synconcealed(a:line, a:col)
|
||||
return len(concealed) && concealed[0]
|
||||
endfunction
|
||||
|
||||
function! s:_skip_special_chars(line, col)
|
||||
return synIDattr(synID(a:line, a:col, 0), 'name')
|
||||
\ =~? s:special_chars_syn_pattern
|
||||
\ || s:is_concealed(a:line, a:col)
|
||||
endfunction
|
||||
endif
|
||||
|
||||
" Use 'shiftwidth()' instead of '&sw'.
|
||||
" (Since Vim patch 7.3.629, 'shiftwidth' can be set to 0 to follow 'tabstop').
|
||||
if exists('*shiftwidth')
|
||||
function! s:sw()
|
||||
return shiftwidth()
|
||||
endfunction
|
||||
else
|
||||
function! s:sw()
|
||||
return &shiftwidth
|
||||
endfunction
|
||||
endif
|
||||
|
||||
" Find backwards the closest open parenthesis/bracket/brace.
|
||||
function! s:find_opening_paren(lnum, col)
|
||||
" Return if cursor is in a comment.
|
||||
if synIDattr(synID(a:lnum, a:col, 0), 'name') =~? 'comment'
|
||||
return [0, 0]
|
||||
endif
|
||||
|
||||
call cursor(a:lnum, a:col)
|
||||
|
||||
let nearest = [0, 0]
|
||||
let timeout = g:python_pep8_indent_searchpair_timeout
|
||||
let skip_special_chars = 's:_skip_special_chars(line("."), col("."))'
|
||||
for [p, maxoff] in items(s:paren_pairs)
|
||||
let stopline = max([0, line('.') - maxoff, nearest[0]])
|
||||
let next = searchpairpos(
|
||||
\ '\V'.p[0], '', '\V'.p[1], 'bnW', skip_special_chars, stopline, timeout)
|
||||
if next[0] && (next[0] > nearest[0] || (next[0] == nearest[0] && next[1] > nearest[1]))
|
||||
let nearest = next
|
||||
endif
|
||||
endfor
|
||||
return nearest
|
||||
endfunction
|
||||
|
||||
" Find the start of a multi-line statement
|
||||
function! s:find_start_of_multiline_statement(lnum)
|
||||
let lnum = a:lnum
|
||||
while lnum > 0
|
||||
if getline(lnum - 1) =~# '\\$'
|
||||
let lnum = prevnonblank(lnum - 1)
|
||||
else
|
||||
let [paren_lnum, _] = s:find_opening_paren(lnum, 1)
|
||||
if paren_lnum < 1
|
||||
return lnum
|
||||
else
|
||||
let lnum = paren_lnum
|
||||
endif
|
||||
endif
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
" Find possible indent(s) of the block starter that matches the current line.
|
||||
function! s:find_start_of_block(lnum, types, skip, multiple) abort
|
||||
let r = []
|
||||
let re = '\V\^\s\*\('.join(a:types, '\|').'\)\>'
|
||||
if !empty(a:skip)
|
||||
let re_skip = '\V\^\s\*\('.join(a:skip, '\|').'\)\>'
|
||||
else
|
||||
let re_skip = ''
|
||||
endif
|
||||
let last_indent = indent(a:lnum) + 1
|
||||
let lnum = a:lnum - 1
|
||||
while lnum > 0 && last_indent > 0
|
||||
let indent = indent(lnum)
|
||||
if indent < last_indent
|
||||
let line = getline(lnum)
|
||||
if !empty(re_skip) && line =~# re_skip
|
||||
let last_indent = indent
|
||||
elseif line =~# re
|
||||
if !a:multiple
|
||||
return [indent]
|
||||
endif
|
||||
if index(r, indent) == -1
|
||||
let r += [indent]
|
||||
endif
|
||||
let last_indent = indent
|
||||
endif
|
||||
endif
|
||||
let lnum = prevnonblank(lnum - 1)
|
||||
endwhile
|
||||
return r
|
||||
endfunction
|
||||
|
||||
" Is "expr" true for every position in "lnum", beginning at "start"?
|
||||
" (optionally up to a:1 / 4th argument)
|
||||
function! s:match_expr_on_line(expr, lnum, start, ...)
|
||||
let text = getline(a:lnum)
|
||||
let end = a:0 ? a:1 : len(text)
|
||||
if a:start > end
|
||||
return 1
|
||||
endif
|
||||
let save_pos = getpos('.')
|
||||
let r = 1
|
||||
for i in range(a:start, end)
|
||||
call cursor(a:lnum, i)
|
||||
if !(eval(a:expr) || text[i-1] =~# '\s')
|
||||
let r = 0
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
call setpos('.', save_pos)
|
||||
return r
|
||||
endfunction
|
||||
|
||||
" Line up with open parenthesis/bracket/brace.
|
||||
function! s:indent_like_opening_paren(lnum)
|
||||
let [paren_lnum, paren_col] = s:find_opening_paren(a:lnum, 1)
|
||||
if paren_lnum <= 0
|
||||
return -2
|
||||
endif
|
||||
let text = getline(paren_lnum)
|
||||
let base = indent(paren_lnum)
|
||||
|
||||
let nothing_after_opening_paren = s:match_expr_on_line(
|
||||
\ s:skip_after_opening_paren, paren_lnum, paren_col+1)
|
||||
let starts_with_closing_paren = getline(a:lnum) =~# '^\s*[])}]'
|
||||
|
||||
let hang_closing = get(b:, 'python_pep8_indent_hang_closing',
|
||||
\ get(g:, 'python_pep8_indent_hang_closing', 0))
|
||||
|
||||
if nothing_after_opening_paren
|
||||
if starts_with_closing_paren && !hang_closing
|
||||
let res = base
|
||||
else
|
||||
let res = base + s:sw()
|
||||
|
||||
" Special case for parenthesis.
|
||||
if text[paren_col-1] ==# '(' && getline(a:lnum) !~# '\v\)\s*:?\s*$'
|
||||
return res
|
||||
endif
|
||||
endif
|
||||
else
|
||||
" Indent to match position of opening paren.
|
||||
let res = paren_col
|
||||
endif
|
||||
|
||||
" If this line is the continuation of a control statement
|
||||
" indent further to distinguish the continuation line
|
||||
" from the next logical line.
|
||||
if text =~# b:control_statement && res == base + s:sw()
|
||||
" But only if not inside parens itself (Flake's E127).
|
||||
let [paren_lnum, _] = s:find_opening_paren(paren_lnum, 1)
|
||||
if paren_lnum <= 0
|
||||
return res + s:sw()
|
||||
endif
|
||||
endif
|
||||
return res
|
||||
endfunction
|
||||
|
||||
" Match indent of first block of this type.
|
||||
function! s:indent_like_block(lnum)
|
||||
let text = getline(a:lnum)
|
||||
for [multiple, block_rules] in [
|
||||
\ [0, s:block_rules],
|
||||
\ [1, s:block_rules_multiple],
|
||||
\ ]
|
||||
for [line_re, blocks_ignore] in items(block_rules)
|
||||
if text !~# line_re
|
||||
continue
|
||||
endif
|
||||
|
||||
let [blocks, skip] = blocks_ignore
|
||||
let indents = s:find_start_of_block(a:lnum, blocks, skip, multiple)
|
||||
if empty(indents)
|
||||
return -1
|
||||
endif
|
||||
if len(indents) == 1
|
||||
return indents[0]
|
||||
endif
|
||||
|
||||
" Multiple valid indents, e.g. for 'else' with both try and if.
|
||||
let indent = indent(a:lnum)
|
||||
if index(indents, indent) != -1
|
||||
" The indent is valid, keep it.
|
||||
return indent
|
||||
endif
|
||||
" Fallback to the first/nearest one.
|
||||
return indents[0]
|
||||
endfor
|
||||
endfor
|
||||
return -2
|
||||
endfunction
|
||||
|
||||
function! s:indent_like_previous_line(lnum)
|
||||
let lnum = prevnonblank(a:lnum - 1)
|
||||
|
||||
" No previous line, keep current indent.
|
||||
if lnum < 1
|
||||
return -1
|
||||
endif
|
||||
|
||||
let text = getline(lnum)
|
||||
let start = s:find_start_of_multiline_statement(lnum)
|
||||
let base = indent(start)
|
||||
let current = indent(a:lnum)
|
||||
|
||||
" Ignore last character in previous line?
|
||||
let lastcol = len(text)
|
||||
let col = lastcol
|
||||
|
||||
" Search for final colon that is not inside something to be ignored.
|
||||
while 1
|
||||
if col == 1 | break | endif
|
||||
if text[col-1] =~# '\s' || s:_skip_special_chars(lnum, col)
|
||||
let col = col - 1
|
||||
continue
|
||||
elseif text[col-1] ==# ':'
|
||||
return base + s:sw()
|
||||
endif
|
||||
break
|
||||
endwhile
|
||||
|
||||
if text =~# '\\$' && !s:_skip_special_chars(lnum, lastcol)
|
||||
" If this line is the continuation of a control statement
|
||||
" indent further to distinguish the continuation line
|
||||
" from the next logical line.
|
||||
if getline(start) =~# b:control_statement
|
||||
return base + s:sw() * 2
|
||||
endif
|
||||
|
||||
" Nest (other) explicit continuations only one level deeper.
|
||||
return base + s:sw()
|
||||
endif
|
||||
|
||||
let empty = getline(a:lnum) =~# '^\s*$'
|
||||
|
||||
" Current and prev line are empty, next is not -> indent like next.
|
||||
if empty && a:lnum > 1 &&
|
||||
\ (getline(a:lnum - 1) =~# '^\s*$') &&
|
||||
\ !(getline(a:lnum + 1) =~# '^\s*$')
|
||||
return indent(a:lnum + 1)
|
||||
endif
|
||||
|
||||
" If the previous statement was a stop-execution statement or a pass
|
||||
if getline(start) =~# s:stop_statement
|
||||
" Remove one level of indentation if the user hasn't already dedented
|
||||
if empty || current > base - s:sw()
|
||||
return base - s:sw()
|
||||
endif
|
||||
" Otherwise, trust the user
|
||||
return -1
|
||||
endif
|
||||
|
||||
if (current || !empty) && s:is_dedented_already(current, base)
|
||||
return -1
|
||||
endif
|
||||
|
||||
" In all other cases, line up with the start of the previous statement.
|
||||
return base
|
||||
endfunction
|
||||
|
||||
" If this line is dedented and the number of indent spaces is valid
|
||||
" (multiple of the indentation size), trust the user.
|
||||
function! s:is_dedented_already(current, base)
|
||||
let dedent_size = a:current - a:base
|
||||
return (dedent_size < 0 && a:current % s:sw() == 0) ? 1 : 0
|
||||
endfunction
|
||||
|
||||
" Is the syntax at lnum (and optionally cnum) a python string?
|
||||
function! s:is_python_string(lnum, ...)
|
||||
let line = getline(a:lnum)
|
||||
if a:0
|
||||
let cols = type(a:1) != type([]) ? [a:1] : a:1
|
||||
else
|
||||
let cols = range(1, max([1, len(line)]))
|
||||
endif
|
||||
for cnum in cols
|
||||
if match(map(synstack(a:lnum, cnum),
|
||||
\ "synIDattr(v:val, 'name')"), 'python\S*String') == -1
|
||||
return 0
|
||||
end
|
||||
endfor
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! GetPythonPEPIndent(lnum)
|
||||
" First line has indent 0
|
||||
if a:lnum == 1
|
||||
return 0
|
||||
endif
|
||||
|
||||
let line = getline(a:lnum)
|
||||
let prevline = getline(a:lnum-1)
|
||||
|
||||
" Multilinestrings: continous, docstring or starting.
|
||||
if s:is_python_string(a:lnum-1, max([1, len(prevline)]))
|
||||
\ && (s:is_python_string(a:lnum, 1)
|
||||
\ || match(line, '^\%("""\|''''''\)') != -1)
|
||||
|
||||
" Indent closing quotes as the line with the opening ones.
|
||||
let match_quotes = match(line, '^\s*\zs\%("""\|''''''\)')
|
||||
if match_quotes != -1
|
||||
" closing multiline string
|
||||
let quotes = line[match_quotes:(match_quotes+2)]
|
||||
call cursor(a:lnum, 1)
|
||||
let pairpos = searchpairpos(quotes, '', quotes, 'bW', '', 0, g:python_pep8_indent_searchpair_timeout)
|
||||
if pairpos[0] != 0
|
||||
return indent(pairpos[0])
|
||||
else
|
||||
return -1
|
||||
endif
|
||||
endif
|
||||
|
||||
if s:is_python_string(a:lnum-1)
|
||||
" Previous line is (completely) a string: keep current indent.
|
||||
return -1
|
||||
endif
|
||||
|
||||
if match(prevline, '^\s*\%("""\|''''''\)') != -1
|
||||
" docstring.
|
||||
return indent(a:lnum-1)
|
||||
endif
|
||||
|
||||
let indent_multi = get(b:, 'python_pep8_indent_multiline_string',
|
||||
\ get(g:, 'python_pep8_indent_multiline_string', 0))
|
||||
if match(prevline, '\v%("""|'''''')$') != -1
|
||||
" Opening multiline string, started in previous line.
|
||||
if (&autoindent && indent(a:lnum) == indent(a:lnum-1))
|
||||
\ || match(line, '\v^\s+$') != -1
|
||||
" <CR> with empty line or to split up 'foo("""bar' into
|
||||
" 'foo("""' and 'bar'.
|
||||
if indent_multi == -2
|
||||
return indent(a:lnum-1) + s:sw()
|
||||
endif
|
||||
return indent_multi
|
||||
endif
|
||||
endif
|
||||
|
||||
" Keep existing indent.
|
||||
if match(line, '\v^\s*\S') != -1
|
||||
return -1
|
||||
endif
|
||||
|
||||
if indent_multi != -2
|
||||
return indent_multi
|
||||
endif
|
||||
|
||||
return s:indent_like_opening_paren(a:lnum)
|
||||
endif
|
||||
|
||||
" Parens: If we can find an open parenthesis/bracket/brace, line up with it.
|
||||
let indent = s:indent_like_opening_paren(a:lnum)
|
||||
if indent >= -1
|
||||
return indent
|
||||
endif
|
||||
|
||||
" Blocks: Match indent of first block of this type.
|
||||
let indent = s:indent_like_block(a:lnum)
|
||||
if indent >= -1
|
||||
return indent
|
||||
endif
|
||||
|
||||
return s:indent_like_previous_line(a:lnum)
|
||||
endfunction
|
36
bundle/vim-python-pep8-indent/spec/indent/bytes_spec.rb
vendored
Normal file
36
bundle/vim-python-pep8-indent/spec/indent/bytes_spec.rb
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
require "spec_helper"
|
||||
|
||||
describe "handles byte strings" do
|
||||
before(:all) {
|
||||
vim.command 'syn region pythonBytes start=+[bB]"+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end=+$+ keepend contains=pythonBytesError,pythonBytesContent,@Spell'
|
||||
vim.command "syn match pythonBytesEscape '\\\\$'"
|
||||
}
|
||||
|
||||
before(:each) {
|
||||
# clear buffer
|
||||
vim.normal 'gg"_dG'
|
||||
|
||||
# Insert two blank lines.
|
||||
# The first line is a corner case in this plugin that would shadow the
|
||||
# correct behaviour of other tests. Thus we explicitly jump to the first
|
||||
# line when we require so.
|
||||
vim.feedkeys 'i\<CR>\<CR>\<ESC>'
|
||||
}
|
||||
|
||||
it "it does not indent to bracket in byte string" do
|
||||
vim.feedkeys 'ireg = b"["\<Esc>'
|
||||
vim.echo('map(synstack(line("."), col(".")), "synIDattr(v:val, \"name\")")'
|
||||
).should == "['pythonBytes']"
|
||||
vim.feedkeys 'o'
|
||||
indent.should == 0
|
||||
end
|
||||
|
||||
it "it indents backslash continuation correctly" do
|
||||
vim.feedkeys 'iwith foo, \<Bslash>\<Esc>'
|
||||
vim.echo('getline(".")').should == "with foo, \\"
|
||||
vim.echo('map(synstack(line("."), col(".")), "synIDattr(v:val, \"name\")")'
|
||||
).should == "['pythonBytesEscape']"
|
||||
vim.feedkeys 'o'
|
||||
indent.should == 8
|
||||
end
|
||||
end
|
36
bundle/vim-python-pep8-indent/spec/indent/cython_spec.rb
vendored
Normal file
36
bundle/vim-python-pep8-indent/spec/indent/cython_spec.rb
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
require "spec_helper"
|
||||
|
||||
describe "vim for cython" do
|
||||
before(:all) {
|
||||
vim.command "new"
|
||||
vim.command "set ft=cython"
|
||||
vim.command("set indentexpr?").should include "GetPythonPEPIndent("
|
||||
}
|
||||
before(:each) {
|
||||
# clear buffer
|
||||
vim.normal 'gg"_dG'
|
||||
|
||||
# Insert two blank lines.
|
||||
# The first line is a corner case in this plugin that would shadow the
|
||||
# correct behaviour of other tests. Thus we explicitly jump to the first
|
||||
# line when we require so.
|
||||
vim.feedkeys 'i\<CR>\<CR>\<ESC>'
|
||||
}
|
||||
after(:all) {
|
||||
vim.command "bwipe!"
|
||||
}
|
||||
|
||||
describe "when using a cdef function definition" do
|
||||
it "indents shiftwidth spaces" do
|
||||
vim.feedkeys 'icdef long_function_name(\<CR>arg'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when using a cpdef function definition" do
|
||||
it "indents shiftwidth spaces" do
|
||||
vim.feedkeys 'icpdef long_function_name(\<CR>arg'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
end
|
796
bundle/vim-python-pep8-indent/spec/indent/indent_spec.rb
vendored
Normal file
796
bundle/vim-python-pep8-indent/spec/indent/indent_spec.rb
vendored
Normal file
@ -0,0 +1,796 @@
|
||||
require "spec_helper"
|
||||
|
||||
shared_examples_for "vim" do
|
||||
before(:each) {
|
||||
# clear buffer
|
||||
vim.normal 'gg"_dG'
|
||||
|
||||
# Insert two blank lines.
|
||||
# The first line is a corner case in this plugin that would shadow the
|
||||
# correct behaviour of other tests. Thus we explicitly jump to the first
|
||||
# line when we require so.
|
||||
vim.feedkeys 'i\<CR>\<CR>\<ESC>'
|
||||
}
|
||||
|
||||
describe "when using the indent plugin" do
|
||||
it "sets the indentexpr and indentkeys options" do
|
||||
vim.command("set indentexpr?").should include "GetPythonPEPIndent("
|
||||
vim.command("set indentkeys?").should include "=elif"
|
||||
end
|
||||
|
||||
it "sets autoindent and expandtab" do
|
||||
vim.command("set autoindent?").should match(/\s*autoindent/)
|
||||
vim.command("set expandtab?").should match(/\s*expandtab/)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when entering the first line" do
|
||||
before { vim.feedkeys '0ggipass' }
|
||||
|
||||
it "does not indent" do
|
||||
indent.should == 0
|
||||
proposed_indent.should == 0
|
||||
end
|
||||
|
||||
it "does not indent when using '=='" do
|
||||
vim.normal "=="
|
||||
indent.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "when after a '(' that is at the end of its line" do
|
||||
before { vim.feedkeys 'itest(\<CR>' }
|
||||
|
||||
it "indents by one level" do
|
||||
proposed_indent.should == shiftwidth
|
||||
vim.feedkeys 'something'
|
||||
indent.should == shiftwidth
|
||||
vim.normal '=='
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
|
||||
it "puts the closing parenthesis at the same level" do
|
||||
vim.feedkeys ')'
|
||||
indent.should == (hang_closing ? shiftwidth : 0)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when after an '(' that is followed by something" do
|
||||
before { vim.feedkeys 'itest(something,\<CR>' }
|
||||
|
||||
it "lines up on following lines" do
|
||||
indent.should == 5
|
||||
vim.feedkeys 'more,\<CR>'
|
||||
indent.should == 5
|
||||
end
|
||||
|
||||
it "lines up the closing parenthesis" do
|
||||
vim.feedkeys ')'
|
||||
indent.should == 5
|
||||
end
|
||||
|
||||
it "does not touch the closing parenthesis if it is already indented further" do
|
||||
vim.feedkeys ' )'
|
||||
indent.should == 7
|
||||
end
|
||||
end
|
||||
|
||||
describe "when after an '{' that is followed by a comment" do
|
||||
before { vim.feedkeys 'imydict = { # comment\<CR>' }
|
||||
|
||||
it "indent by one level" do
|
||||
indent.should == shiftwidth
|
||||
vim.feedkeys '1: 1,\<CR>'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
|
||||
it "lines up the closing parenthesis" do
|
||||
vim.feedkeys '}'
|
||||
indent.should == (hang_closing ? shiftwidth : 0)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when using gq to reindent a '(' that is" do
|
||||
before { vim.feedkeys 'itest(' }
|
||||
it "something and has a string without spaces at the end" do
|
||||
vim.feedkeys 'something_very_long_blaaaaaaaaa, "some_very_long_string_blaaaaaaaaaaaaaaaaaaaa"\<esc>gqq'
|
||||
indent.should == 5
|
||||
end
|
||||
end
|
||||
|
||||
describe "when after multiple parens of different types" do
|
||||
it "indents by one level" do
|
||||
vim.feedkeys 'if({\<CR>'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
|
||||
it "lines up with the last paren" do
|
||||
vim.feedkeys 'ifff({123: 456,\<CR>'
|
||||
indent.should == 5
|
||||
end
|
||||
end
|
||||
|
||||
describe "when '#' is contained in a string that is followed by a colon" do
|
||||
it "indents by one level" do
|
||||
vim.feedkeys 'iif "some#thing" == "test":#test\<CR>pass'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when '#' is not contained in a string and is followed by a colon" do
|
||||
it "does not indent" do
|
||||
vim.feedkeys 'iif "some#thing" == "test"#:test\<CR>'
|
||||
indent.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "when inside an unfinished string" do
|
||||
it "does not indent" do
|
||||
vim.feedkeys 'i"test:\<ESC>'
|
||||
vim.echo('synIDattr(synID(line("."), col("."), 0), "name")'
|
||||
).downcase.should include 'string'
|
||||
vim.feedkeys 'a\<CR>'
|
||||
proposed_indent.should == -1
|
||||
indent.should == 0
|
||||
end
|
||||
|
||||
it "does not dedent" do
|
||||
vim.feedkeys 'iif True:\<CR>"test:\<ESC>'
|
||||
vim.echo('synIDattr(synID(line("."), col("."), 0), "name")'
|
||||
).downcase.should include 'string'
|
||||
proposed_indent.should == shiftwidth
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the previous line has a colon in a string" do
|
||||
before { vim.feedkeys 'itest(":".join(["1","2"]))\<CR>' }
|
||||
it "does not indent" do
|
||||
vim.feedkeys 'if True:'
|
||||
indent.should == 0
|
||||
proposed_indent.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the previous line has a list slice" do
|
||||
it "does not indent" do
|
||||
vim.feedkeys 'ib = a[2:]\<CR>'
|
||||
indent.should == 0
|
||||
proposed_indent.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "when line is empty inside a block" do
|
||||
it "is indented like the previous line" do
|
||||
vim.feedkeys 'idef a():\<CR>1\<CR>\<CR>2\<ESC>kcc'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when an empty line is after empty line / before non-empty" do
|
||||
it "is indented like the next line" do
|
||||
vim.feedkeys 'idef a():\<CR>1\<CR>\<CR>\<CR>2\<ESC><<kcc'
|
||||
indent.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "when an empty line is after empty line / before non-empty (nested)" do
|
||||
it "is indented like the next line" do
|
||||
vim.feedkeys 'idef a():\<CR>1\<CR>\<CR>\<CR>\<ESC>0i\<TAB>2\<ESC>kcc'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when line is empty inside a block following multi-line statement" do
|
||||
it "is indented like the previous line" do
|
||||
vim.feedkeys 'idef a():\<CR>x = (1 +\<CR>2)\<CR>\<CR>y\<ESC>kcc'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when line is empty inside a block following stop statement" do
|
||||
it "is indented like the previous line minus shiftwidth" do
|
||||
vim.feedkeys 'iif x:\<CR>if y:\<CR>pass\<CR>\<CR>z\<ESC>kcc'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when using simple control structures" do
|
||||
it "indents shiftwidth spaces" do
|
||||
vim.feedkeys 'iwhile True:\<CR>pass'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when using a function definition" do
|
||||
it "handles indent with closing parenthesis on same line" do
|
||||
vim.feedkeys 'idef long_function_name(\<CR>arg'
|
||||
indent.should == shiftwidth
|
||||
vim.feedkeys '):'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
|
||||
it "handles indent with closing parenthesis on new line" do
|
||||
vim.feedkeys 'idef long_function_name(\<CR>arg'
|
||||
indent.should == shiftwidth
|
||||
vim.feedkeys '\<CR>'
|
||||
indent.should == shiftwidth
|
||||
vim.feedkeys ')'
|
||||
indent.should == (hang_closing ? shiftwidth * 2 : 0)
|
||||
vim.feedkeys ':'
|
||||
indent.should == (hang_closing ? shiftwidth * 2 : 0)
|
||||
vim.feedkeys '\<Esc>k'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when using a class definition" do
|
||||
it "indents shiftwidth spaces" do
|
||||
vim.feedkeys 'iclass Foo(\<CR>'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when writing an 'else' block" do
|
||||
it "aligns to the preceeding 'for' block" do
|
||||
vim.feedkeys 'ifor x in "abc":\<CR>pass\<CR>else:'
|
||||
indent.should == 0
|
||||
end
|
||||
|
||||
it "aligns to the preceeding 'if' block" do
|
||||
vim.feedkeys 'ifor x in "abc":\<CR>if True:\<CR>pass\<CR>else:'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when using parens and control statements" do
|
||||
it "avoids ambiguity by using extra indentation" do
|
||||
vim.feedkeys 'iif (111 and\<CR>'
|
||||
if shiftwidth == 4
|
||||
indent.should == shiftwidth * 2
|
||||
else
|
||||
indent.should == 4
|
||||
end
|
||||
vim.feedkeys '222):\<CR>'
|
||||
indent.should == shiftwidth
|
||||
vim.feedkeys 'pass\<CR>'
|
||||
indent.should == 0
|
||||
end
|
||||
|
||||
it "still aligns parens properly if not ambiguous" do
|
||||
vim.feedkeys 'iwhile (111 and\<CR>'
|
||||
indent.should == 7
|
||||
vim.feedkeys '222):\<CR>'
|
||||
indent.should == shiftwidth
|
||||
vim.feedkeys 'pass\<CR>'
|
||||
indent.should == 0
|
||||
end
|
||||
|
||||
it "handles nested expressions (Flake8's E127)" do
|
||||
vim.feedkeys 'i[\<CR>x for x in foo\<CR>if (\<CR>'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
|
||||
it "still handles multiple parens correctly" do
|
||||
vim.feedkeys 'iif (111 and (222 and 333\<CR>'
|
||||
indent.should == 13
|
||||
vim.feedkeys 'and 444\<CR>'
|
||||
indent.should == 13
|
||||
vim.feedkeys ')\<CR>'
|
||||
if shiftwidth == 4
|
||||
indent.should == shiftwidth * 2
|
||||
else
|
||||
indent.should == 4
|
||||
end
|
||||
vim.feedkeys 'and 555):\<CR>'
|
||||
indent.should == shiftwidth
|
||||
vim.feedkeys 'pass\<CR>'
|
||||
indent.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "when a line breaks with a manual '\\'" do
|
||||
it "indents shiftwidth spaces on normal line" do
|
||||
vim.feedkeys 'ivalue = test + \\\\\<CR>'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
|
||||
it "indents 2x shiftwidth spaces for control structures" do
|
||||
vim.feedkeys 'iif somevalue == xyz and \\\\\<CR>'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
|
||||
it "indents relative to line above" do
|
||||
vim.feedkeys 'i\<TAB>value = test + \\\\\<CR>'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "when current line is dedented compared to previous line" do
|
||||
before { vim.feedkeys 'i\<TAB>\<TAB>if x:\<CR>y = True\<CR>\<ESC>' }
|
||||
it "and current line has a valid indentation (Part 1)" do
|
||||
vim.feedkeys '0i\<TAB>if y:'
|
||||
proposed_indent.should == -1
|
||||
end
|
||||
|
||||
it "and current line has a valid indentation (Part 2)" do
|
||||
vim.feedkeys '0i\<TAB>\<TAB>if y:'
|
||||
proposed_indent.should == -1
|
||||
end
|
||||
|
||||
it "and current line has an invalid indentation" do
|
||||
vim.feedkeys 'i while True:\<CR>'
|
||||
indent.should == previous_indent + shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when current line is dedented compared to the last non-empty line" do
|
||||
before { vim.feedkeys 'i\<TAB>\<TAB>if x:\<CR>y = True\<CR>\<CR>\<ESC>' }
|
||||
it "and current line has a valid indentation" do
|
||||
vim.feedkeys '0i\<TAB>if y:'
|
||||
proposed_indent.should == -1
|
||||
end
|
||||
end
|
||||
|
||||
describe "when an 'if' is followed by" do
|
||||
before { vim.feedkeys 'i\<TAB>\<TAB>if x:\<CR>' }
|
||||
it "an elif, it lines up with the 'if'" do
|
||||
vim.feedkeys 'elif y:'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
|
||||
it "an 'else', it lines up with the 'if'" do
|
||||
vim.feedkeys 'else:'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "when an 'if' contains a try-except" do
|
||||
before {
|
||||
vim.feedkeys 'iif x:\<CR>try:\<CR>pass\<CR>except:\<CR>pass\<CR>'
|
||||
indent.should == shiftwidth
|
||||
}
|
||||
it "an 'else' should be indented to the try" do
|
||||
vim.feedkeys 'else:'
|
||||
indent.should == shiftwidth
|
||||
proposed_indent.should == shiftwidth
|
||||
end
|
||||
it "an 'else' should keep the indent of the 'if'" do
|
||||
vim.feedkeys 'else:\<ESC><<'
|
||||
indent.should == 0
|
||||
proposed_indent.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "when a 'for' is followed by" do
|
||||
before { vim.feedkeys 'i\<TAB>\<TAB>for x in y:\<CR>' }
|
||||
it "an 'else', it lines up with the 'for'" do
|
||||
vim.feedkeys 'else:'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "when an 'else' is followed by" do
|
||||
before { vim.feedkeys 'i\<TAB>\<TAB>else:\<CR>XXX\<CR>' }
|
||||
it "a 'finally', it lines up with the 'else'" do
|
||||
vim.feedkeys 'finally:'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "when a 'try' is followed by" do
|
||||
before { vim.feedkeys 'i\<TAB>\<TAB>try:\<CR>' }
|
||||
it "an 'except', it lines up with the 'try'" do
|
||||
vim.feedkeys 'except:'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
|
||||
it "an 'else', it lines up with the 'try'" do
|
||||
vim.feedkeys 'else:'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
|
||||
it "a 'finally', it lines up with the 'try'" do
|
||||
vim.feedkeys 'finally:'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "when an 'except' is followed by" do
|
||||
before { vim.feedkeys 'i\<TAB>\<TAB>except:\<CR>' }
|
||||
it "an 'else', it lines up with the 'except'" do
|
||||
vim.feedkeys 'else:'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
|
||||
it "another 'except', it lines up with the previous 'except'" do
|
||||
vim.feedkeys 'except:'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
|
||||
it "a 'finally', it lines up with the 'except'" do
|
||||
vim.feedkeys 'finally:'
|
||||
indent.should == shiftwidth * 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "when an else is used inside of a nested if" do
|
||||
before { vim.feedkeys 'iif foo:\<CR>if bar:\<CR>pass\<CR>' }
|
||||
it "indents the else to the inner if" do
|
||||
vim.feedkeys 'else:'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "when an else is used outside of a nested if" do
|
||||
before { vim.feedkeys 'iif True:\<CR>if True:\<CR>pass\<CR>\<Esc>0' }
|
||||
it "indents the else to the outer if" do
|
||||
indent.should == 0
|
||||
proposed_indent.should == shiftwidth
|
||||
|
||||
vim.feedkeys 'ielse:'
|
||||
indent.should == 0
|
||||
proposed_indent.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "when jedi-vim call signatures are used" do
|
||||
before { vim.command 'syn match jediFunction "JEDI_CALL_SIGNATURE" keepend extend' }
|
||||
|
||||
it "ignores the call signature after a colon" do
|
||||
vim.feedkeys 'iif True: JEDI_CALL_SIGNATURE\<CR>'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
|
||||
it "ignores the call signature after a function" do
|
||||
vim.feedkeys 'idef f( JEDI_CALL_SIGNATURE\<CR>'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for "multiline strings" do
|
||||
before(:each) {
|
||||
# clear buffer
|
||||
vim.normal 'gg"_dG'
|
||||
|
||||
# Insert two blank lines.
|
||||
# The first line is a corner case in this plugin that would shadow the
|
||||
# correct behaviour of other tests. Thus we explicitly jump to the first
|
||||
# line when we require so.
|
||||
vim.feedkeys 'i\<CR>\<CR>\<ESC>'
|
||||
}
|
||||
|
||||
describe "when after an '(' that is followed by an unfinished string" do
|
||||
before { vim.feedkeys 'itest("""' }
|
||||
|
||||
it "it indents the next line" do
|
||||
vim.feedkeys '\<CR>'
|
||||
expected_proposed, expected_indent = multiline_indent(0, shiftwidth)
|
||||
proposed_indent.should == expected_proposed
|
||||
indent.should == expected_indent
|
||||
end
|
||||
|
||||
it "with contents it indents the second line to the parenthesis" do
|
||||
vim.feedkeys 'second line\<CR>'
|
||||
expected_proposed, expected_indent = multiline_indent(0, 5)
|
||||
proposed_indent.should == expected_proposed
|
||||
indent.should == expected_indent
|
||||
end
|
||||
end
|
||||
|
||||
describe "when after assigning an unfinished string" do
|
||||
before { vim.feedkeys 'itest = """' }
|
||||
|
||||
it "it indents the next line" do
|
||||
vim.feedkeys '\<CR>'
|
||||
expected_proposed, expected_indent = multiline_indent(0, shiftwidth)
|
||||
proposed_indent.should == expected_proposed
|
||||
indent.should == expected_indent
|
||||
end
|
||||
end
|
||||
|
||||
describe "when after assigning an indented unfinished string" do
|
||||
before { vim.feedkeys 'i test = """' }
|
||||
|
||||
it "it indents the next line" do
|
||||
vim.feedkeys '\<CR>'
|
||||
expected_proposed, expected_indent = multiline_indent(4, shiftwidth + 4)
|
||||
proposed_indent.should == expected_proposed
|
||||
indent.should == expected_indent
|
||||
end
|
||||
end
|
||||
|
||||
describe "when after assigning an indented finished string" do
|
||||
before { vim.feedkeys 'i test = ""' }
|
||||
|
||||
it "it does indent the next line" do
|
||||
vim.feedkeys '\<CR>'
|
||||
indent.should == 4
|
||||
end
|
||||
|
||||
it "and writing a new string, it does indent the next line" do
|
||||
vim.feedkeys '\<CR>""'
|
||||
indent.should == 4
|
||||
end
|
||||
end
|
||||
|
||||
describe "when after a docstring" do
|
||||
it "it does indent the next line to the docstring" do
|
||||
vim.feedkeys 'i """\<CR>'
|
||||
indent.should == 4
|
||||
proposed_indent.should == 4
|
||||
end
|
||||
|
||||
it "indents the closing docstring quotes" do
|
||||
vim.feedkeys 'i """\<CR>\<CR>"""'
|
||||
indent.should == 4
|
||||
proposed_indent.should == 4
|
||||
vim.echo('getline(3)').should == ' """'
|
||||
end
|
||||
|
||||
it "indents non-matching docstring quotes" do
|
||||
vim.feedkeys 'i """\<CR>\<Esc>'
|
||||
vim.feedkeys "0C'''"
|
||||
vim.echo('line(".")').should == "4"
|
||||
vim.echo('getline(".")').should == "'''"
|
||||
indent.should == 0
|
||||
proposed_indent.should == -1
|
||||
end
|
||||
end
|
||||
|
||||
describe "when after a docstring with contents" do
|
||||
before { vim.feedkeys 'i """First line' }
|
||||
it "it does indent the next line to the docstring" do
|
||||
vim.feedkeys '\<CR>'
|
||||
indent.should == 4
|
||||
proposed_indent.should == 4
|
||||
end
|
||||
end
|
||||
|
||||
describe "when breaking a string after opening parenthesis" do
|
||||
before { vim.feedkeys 'i foo("""bar\<Left>\<Left>\<Left>' }
|
||||
it "it does indent the next line as after an opening multistring" do
|
||||
vim.feedkeys '\<CR>'
|
||||
_, expected_indent = multiline_indent(4, 4 + shiftwidth)
|
||||
indent.should == expected_indent
|
||||
proposed_indent.should == -1
|
||||
|
||||
# it keeps the indent after an empty line
|
||||
vim.feedkeys '\<CR>'
|
||||
proposed_indent, expected_indent = multiline_indent(4, 4 + shiftwidth)
|
||||
indent.should == expected_indent
|
||||
proposed_indent.should == proposed_indent
|
||||
|
||||
# it keeps the indent of nonblank above
|
||||
vim.feedkeys '\<End>\<CR>'
|
||||
proposed_indent, expected_indent = multiline_indent(4, 4 + shiftwidth)
|
||||
indent.should == expected_indent
|
||||
proposed_indent.should == proposed_indent
|
||||
|
||||
# it keeps the indent of nonblank above before an empty line
|
||||
vim.feedkeys '\<CR>'
|
||||
proposed_indent, expected_indent = multiline_indent(4, 4 + shiftwidth)
|
||||
indent.should == expected_indent
|
||||
proposed_indent.should == proposed_indent
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
SUITE_SHIFTWIDTHS = [4, 3]
|
||||
SUITE_HANG_CLOSINGS = [false, true]
|
||||
|
||||
SUITE_SHIFTWIDTHS.each do |sw|
|
||||
describe "vim when using width of #{sw}" do
|
||||
before {
|
||||
vim.command("set sw=#{sw} ts=#{sw} sts=#{sw} et")
|
||||
}
|
||||
it "sets shiftwidth to #{sw}" do
|
||||
shiftwidth.should == sw
|
||||
end
|
||||
|
||||
SUITE_HANG_CLOSINGS.each do |hc|
|
||||
describe "vim when hang_closing is set to #{hc}" do
|
||||
before {
|
||||
set_hang_closing hc
|
||||
}
|
||||
it "sets hang_closing to #{hc}" do
|
||||
hang_closing.should == !!hc
|
||||
end
|
||||
|
||||
it_behaves_like "vim"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "vim when not using python_pep8_indent_multiline_string" do
|
||||
before {
|
||||
vim.command("set sw=4 ts=4 sts=4 et")
|
||||
vim.command("unlet! g:python_pep8_indent_multiline_string")
|
||||
}
|
||||
it_behaves_like "multiline strings"
|
||||
end
|
||||
|
||||
describe "vim when using python_pep8_indent_multiline_first=0" do
|
||||
before {
|
||||
vim.command("set sw=4 ts=4 sts=4 et")
|
||||
vim.command("let g:python_pep8_indent_multiline_string=0")
|
||||
}
|
||||
it_behaves_like "multiline strings"
|
||||
end
|
||||
|
||||
describe "vim when using python_pep8_indent_multiline_string=-1" do
|
||||
before {
|
||||
vim.command("set sw=4 ts=4 sts=4 et")
|
||||
vim.command("let g:python_pep8_indent_multiline_string=-1")
|
||||
}
|
||||
it_behaves_like "multiline strings"
|
||||
end
|
||||
|
||||
describe "vim when using python_pep8_indent_multiline_string=-2" do
|
||||
before {
|
||||
vim.command("set sw=4 ts=4 sts=4 et")
|
||||
vim.command("let g:python_pep8_indent_multiline_string=-2")
|
||||
}
|
||||
it_behaves_like "multiline strings"
|
||||
end
|
||||
|
||||
describe "Handles far away opening parens" do
|
||||
before { vim.feedkeys '\<ESC>ggdGifrom foo import (' }
|
||||
|
||||
it "indents by one level" do
|
||||
vim.feedkeys '\<CR>'
|
||||
proposed_indent.should == shiftwidth
|
||||
end
|
||||
|
||||
it "indents by one level for 10 lines" do
|
||||
vim.command('set paste | exe "norm 9o" | set nopaste')
|
||||
vim.feedkeys '\<Esc>o'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
|
||||
it "indents by one level for 50 lines" do
|
||||
vim.command('set paste | exe "norm 49o" | set nopaste')
|
||||
vim.feedkeys '\<Esc>o'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "Handles far away opening square brackets" do
|
||||
before { vim.feedkeys '\<ESC>ggdGibar = [' }
|
||||
|
||||
it "indents by one level" do
|
||||
vim.feedkeys '\<CR>'
|
||||
proposed_indent.should == shiftwidth
|
||||
end
|
||||
|
||||
it "indents by one level for 10 lines" do
|
||||
vim.command('set paste | exe "norm 9o" | set nopaste')
|
||||
vim.feedkeys '\<Esc>o'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
|
||||
it "indents by one level for 100 lines" do
|
||||
vim.command('set paste | exe "norm 99o" | set nopaste')
|
||||
vim.feedkeys '\<Esc>o'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "Handles far away opening curly brackets" do
|
||||
before { vim.feedkeys '\<ESC>ggdGijson = {' }
|
||||
|
||||
it "indents by one level" do
|
||||
vim.feedkeys '\<CR>'
|
||||
vim.feedkeys '\<Esc>o'
|
||||
proposed_indent.should == shiftwidth
|
||||
end
|
||||
|
||||
it "indents by one level for 10 lines" do
|
||||
vim.command('set paste | exe "norm 9o" | set nopaste')
|
||||
vim.feedkeys '\<Esc>o'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
|
||||
it "indents by one level for 1000 lines" do
|
||||
vim.command('set paste | exe "norm 999o" | set nopaste')
|
||||
vim.feedkeys '\<Esc>o'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "Compact multiline dict" do
|
||||
before { vim.feedkeys '\<ESC>ggdGid = {"one": 1,' }
|
||||
|
||||
it "gets indented correctly" do
|
||||
vim.feedkeys '\<CR>'
|
||||
proposed_indent.should == 5
|
||||
|
||||
vim.feedkeys '"two": 2}'
|
||||
proposed_indent.should == 5
|
||||
|
||||
vim.feedkeys '\<CR>'
|
||||
proposed_indent.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "Using O" do
|
||||
before {
|
||||
vim.feedkeys '\<ESC>ggdG'
|
||||
vim.feedkeys 'iif foo:\<CR>'
|
||||
}
|
||||
|
||||
it "respects autoindent" do
|
||||
vim.feedkeys '1\<CR>\<CR>'
|
||||
indent.should == shiftwidth
|
||||
vim.feedkeys '\<Esc>ko'
|
||||
indent.should == shiftwidth
|
||||
vim.feedkeys '\<Esc>kO'
|
||||
indent.should == shiftwidth
|
||||
# Uses/keeps indent from line above
|
||||
vim.feedkeys '\<Esc>i2\<Esc>O'
|
||||
indent.should == shiftwidth
|
||||
# Uses/keeps indent from line above
|
||||
vim.feedkeys '\<Esc>j\<Esc>O'
|
||||
indent.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "searchpairpos" do
|
||||
before { vim.feedkeys '\<ESC>ggdG' }
|
||||
it "handles nested parenthesis" do
|
||||
vim.feedkeys 'iif foo.startswith("("):\<CR>'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "o within TODO" do
|
||||
before {
|
||||
vim.feedkeys '\<ESC>ggdG'
|
||||
vim.feedkeys 'iif 1: # TODO\<Esc>'
|
||||
# Assertion that we have a pythonTodo here.
|
||||
vim.echo('synIDattr(synID(line("."), col("."), 0), "name")').should match 'pythonTodo'
|
||||
}
|
||||
|
||||
it "respects autoindent" do
|
||||
vim.feedkeys 'o'
|
||||
indent.should == shiftwidth
|
||||
end
|
||||
end
|
||||
|
||||
describe "elif after else" do
|
||||
before {
|
||||
vim.feedkeys '\<ESC>ggdG'
|
||||
}
|
||||
|
||||
it "is indented to the outer if" do
|
||||
vim.feedkeys 'iif 1:\<CR>if 2:\<CR>pass\<CR>else:\<CR>pass\<CR>elif 3:\<Esc>'
|
||||
indent.should == 0
|
||||
|
||||
vim.feedkeys '\<ESC>ggdG'
|
||||
vim.feedkeys 'i if 1:\<CR>if 2:\<CR>pass\<CR>else:\<CR>pass\<CR>elif 3:\<Esc>'
|
||||
indent.should == 4
|
||||
end
|
||||
end
|
||||
|
||||
describe "elif after two ifs" do
|
||||
before {
|
||||
vim.feedkeys '\<ESC>ggdG'
|
||||
}
|
||||
|
||||
it "keeps its indent to the outer if" do
|
||||
vim.feedkeys 'iif 1:\<CR>if 2:\<CR>pass\<CR>elif 3:\<CR>pass\<CR>'
|
||||
indent.should == 4
|
||||
vim.feedkeys '\<Esc>'
|
||||
indent.should == 0
|
||||
proposed_indent.should == shiftwidth
|
||||
vim.feedkeys 'ielif 4:'
|
||||
indent.should == 0
|
||||
proposed_indent.should == 0
|
||||
vim.feedkeys '\<CR>'
|
||||
indent.should == 4
|
||||
proposed_indent.should == 4
|
||||
end
|
||||
end
|
24
bundle/vim-python-pep8-indent/spec/make-coverage
vendored
Normal file
24
bundle/vim-python-pep8-indent/spec/make-coverage
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
rm -f .coverage_covimerage
|
||||
export PYTHON_PEP8_INDENT_TEST_PROFILE_BASE=/tmp/vim-python-pep8-profile
|
||||
|
||||
Xvfb :99 2>/dev/null >&2 &
|
||||
export DISPLAY=:99
|
||||
|
||||
export VIMRUNNER_REUSE_SERVER=1
|
||||
|
||||
ret=0
|
||||
for file in ./spec/indent/*_spec.rb; do
|
||||
# shellcheck disable=SC2086
|
||||
bundle exec rspec "$file" $RSPEC_OPTIONS || ret=1
|
||||
|
||||
for p in "${PYTHON_PEP8_INDENT_TEST_PROFILE_BASE}".*; do
|
||||
covimerage write_coverage --append "$p"
|
||||
rm "$p"
|
||||
covimerage report -m
|
||||
done
|
||||
done
|
||||
exit $ret
|
70
bundle/vim-python-pep8-indent/spec/spec_helper.rb
vendored
Normal file
70
bundle/vim-python-pep8-indent/spec/spec_helper.rb
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
require 'vimrunner'
|
||||
require 'vimrunner/rspec'
|
||||
require 'vimrunner/server'
|
||||
|
||||
# Explicitly enable usage of "should".
|
||||
RSpec.configure do |config|
|
||||
config.expect_with(:rspec) { |c| c.syntax = :should }
|
||||
end
|
||||
|
||||
Vimrunner::RSpec.configure do |config|
|
||||
# Use a single Vim instance for the test suite. Set to false to use an
|
||||
# instance per test (slower, but can be easier to manage).
|
||||
# This requires using gvim, otherwise it hangs after a few tests.
|
||||
config.reuse_server = ENV['VIMRUNNER_REUSE_SERVER'] == '1' ? true : false
|
||||
|
||||
config.start_vim do
|
||||
exe = config.reuse_server ? Vimrunner::Platform.gvim : Vimrunner::Platform.vim
|
||||
vimrc = File.expand_path("../vimrc", __FILE__)
|
||||
vim = Vimrunner::Server.new(:executable => exe,
|
||||
:vimrc => vimrc).start
|
||||
# More friendly killing.
|
||||
# Otherwise profiling information might not be written.
|
||||
def vim.kill
|
||||
normal(':qall!<CR>')
|
||||
|
||||
Timeout.timeout(5) do
|
||||
sleep 0.1 while server.running?
|
||||
end
|
||||
end
|
||||
|
||||
plugin_path = File.expand_path('../..', __FILE__)
|
||||
vim.command "set rtp^=#{plugin_path}"
|
||||
vim.command "set filetype=python"
|
||||
|
||||
def shiftwidth
|
||||
@shiftwidth ||= vim.echo("exists('*shiftwidth') ? shiftwidth() : &sw").to_i
|
||||
end
|
||||
def tabstop
|
||||
@tabstop ||= vim.echo("&tabstop").to_i
|
||||
end
|
||||
def indent
|
||||
vim.echo("indent('.')").to_i
|
||||
end
|
||||
def previous_indent
|
||||
pline = vim.echo("line('.')").to_i - 1
|
||||
vim.echo("indent('#{pline}')").to_i
|
||||
end
|
||||
def proposed_indent
|
||||
line = vim.echo("line('.')")
|
||||
col = vim.echo("col('.')")
|
||||
indent_value = vim.echo("GetPythonPEPIndent(#{line})").to_i
|
||||
vim.command("call cursor(#{line}, #{col})")
|
||||
return indent_value
|
||||
end
|
||||
def multiline_indent(prev, default)
|
||||
i = vim.echo("get(g:, 'python_pep8_indent_multiline_string', 0)").to_i
|
||||
return (i == -2 ? default : i), i < 0 ? (i == -1 ? prev : default) : i
|
||||
end
|
||||
def hang_closing
|
||||
i = vim.echo("get(g:, 'python_pep8_indent_hang_closing', 0)").to_i
|
||||
return (i != 0)
|
||||
end
|
||||
def set_hang_closing(value)
|
||||
i = value ? 1 : 0
|
||||
vim.command("let g:python_pep8_indent_hang_closing=#{i}")
|
||||
end
|
||||
|
||||
vim
|
||||
end
|
||||
end
|
22
bundle/vim-python-pep8-indent/spec/vimrc
vendored
Normal file
22
bundle/vim-python-pep8-indent/spec/vimrc
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
" vint: -ProhibitSetNoCompatible
|
||||
set nocompatible
|
||||
|
||||
filetype plugin on
|
||||
filetype indent on
|
||||
syntax on
|
||||
|
||||
set noswapfile nobackup
|
||||
|
||||
" remove default ~/.vim directories to avoid loading plugins
|
||||
set runtimepath-=~/.vim
|
||||
set runtimepath-=~/.vim/after
|
||||
|
||||
let sfile = expand('<sfile>')
|
||||
let plugin_path = fnamemodify(sfile, ':p:h:h')
|
||||
exe 'set runtimepath^='.plugin_path
|
||||
|
||||
if !empty($PYTHON_PEP8_INDENT_TEST_PROFILE_BASE)
|
||||
execute printf('profile start %s.%s',
|
||||
\ $PYTHON_PEP8_INDENT_TEST_PROFILE_BASE, getpid())
|
||||
execute 'profile! file '. plugin_path . '/indent/python.vim'
|
||||
endif
|
32
bundle/vim-pythonsense/.gitignore
vendored
Normal file
32
bundle/vim-pythonsense/.gitignore
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
# svn crud
|
||||
.svn*
|
||||
|
||||
# OS X / Darwin
|
||||
.DS_Store*
|
||||
|
||||
# IDE/Editor Project Files
|
||||
tags
|
||||
|
||||
# Python
|
||||
*.py[cod]
|
||||
*.so
|
||||
*.egg
|
||||
pip-log.txt
|
||||
nosetests.xml
|
||||
setup.cfg
|
||||
*.mo
|
||||
.installed.cfg
|
||||
.coverage
|
||||
.tox
|
||||
*.egg-info/*
|
||||
dist/*
|
||||
build/*
|
||||
eggs
|
||||
parts
|
||||
var
|
||||
sdist/*
|
||||
develop-eggs
|
||||
lib
|
||||
lib64
|
||||
__pycache__
|
||||
setuptools-*.egg
|
9
bundle/vim-pythonsense/LICENSE
vendored
Normal file
9
bundle/vim-pythonsense/LICENSE
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
Copyright (C) 2018 Jeet Sukumaran.
|
||||
|
||||
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.
|
||||
|
||||
|
297
bundle/vim-pythonsense/README.md
vendored
Normal file
297
bundle/vim-pythonsense/README.md
vendored
Normal file
@ -0,0 +1,297 @@
|
||||
# Pythonsense
|
||||
|
||||
## Description
|
||||
|
||||
`Pythonsense` is a Vim plugin that provides [text objects](http://vimdoc.sourceforge.net/htmldoc/motion.html#object-select) and [motions](http://vimdoc.sourceforge.net/htmldoc/motion.html#object-motions) for Python classes, methods, functions, and doc strings.
|
||||
|
||||
### Python Text Objects
|
||||
|
||||
- "`ac`" : Outer class text object. This includes the entire class, including the header (class name declaration) and decorators, the class body, as well as a blank line if this is given after the class definition.
|
||||
- "`ic`" : Inner class text object. This is the class body *only*, thus excluding the class name declaration line and any blank lines after the class definition.
|
||||
- "`af`" : Outer function text object. This includes the entire function, including the header (function name declaration) and decorators, the function body, as well as a blank line if this is given after the function definition.
|
||||
- "`if`" : Inner function text object. This is the function body *only*, thus excluding the function name declaration line and any blank lines after the function definition.
|
||||
- "`ad`" : Outer docstring text object.
|
||||
- "`id`" : Inner docstring text object.
|
||||
|
||||
The differences between inner and outer class and method/function text objects are illustrated by the following:
|
||||
|
||||
```
|
||||
class OneRing(object): -----------------------------+
|
||||
--------------------+ |
|
||||
def __init__(self): | |
|
||||
print("One ring to ...") | |
|
||||
| |
|
||||
def rule_them_all(self): | |
|
||||
self.find_them() | |
|
||||
| |
|
||||
def find_them(self): ------------+ | |
|
||||
a = [3, 7, 9, 1] ----+ | | |
|
||||
self.bring_them(a) |- `if` |- `af` |- `ic` | - `ac`
|
||||
self.bind_them("darkness") ----+ | | |
|
||||
------------+ | |
|
||||
def bring_them_all(self, a): | |
|
||||
self.bind_them(a, "#000") | |
|
||||
| |
|
||||
def bind_them(self, a, c): | |
|
||||
print("shadows lie.") --------------------+ |
|
||||
-----------------------------+
|
||||
```
|
||||
|
||||
### Python Object Motions
|
||||
|
||||
- "`]]`" : Move (forward) to the beginning of the *next* Python class.
|
||||
- "`][`" : Move (forward) to the end of the *current* Python class.
|
||||
- "`[[`" : Move (backward) to beginning of the *current* Python class (or beginning of the previous Python class if not currently in a class or already at the beginning of a class).
|
||||
- "`[]`" : Move (backward) to end of the *previous* Python class.
|
||||
|
||||
- "`]m`" : Move (forward) to the beginning of the *next* Python method or function.
|
||||
- "`]M`" : Move (forward) to the end of the *current* Python method or function.
|
||||
- "`[m`" : Move (backward) to the beginning of the *current* Python method or function (or to the beginning of the previous method or function if not currently in a method/function or already at the beginning of a method/function).
|
||||
- "`[M`" : Move (backward) to the end of the *previous* Python method or function.
|
||||
|
||||
Note that in Vim 8.0 or later, motions with these key-bindings are provided by default.
|
||||
While in the most simplest of cases they converge with "Pythonsense" in operation, they result in very different behavior in more complex cases.
|
||||
This difference ([discussed in detail below](#stock-vim-vs-pythonsense-motions)) basically comes down to "Pythonsense" providing semantically aware and contextual class-wise and method- or functionwise motions, while the stock Vim 8+ provides non-semantically aware indent-level based motions.
|
||||
If you prefer the stock Vim 8.0+ motions to the ones overridden by "Pythonsense", then, as described in the [section on suppressing the default key mappings](#suppressing-the-key-mappings), just specify the following in your "~/.vimrc":
|
||||
|
||||
```
|
||||
let g:is_pythonsense_suppress_motion_keymaps = 1
|
||||
```
|
||||
|
||||
If you want *both* the stock Vim 8.0+ motions, then you can [activate alternate mappings](#activating-alternative-motion-key-mappings) to the Pythonsense semantic motions that do not hide the the stock Vim 8.0+ motions by specifying the following in your "~/.vimrc":
|
||||
|
||||
```
|
||||
let g:is_pythonsense_alternate_motion_keymaps = 1
|
||||
```
|
||||
|
||||
See [below](#activating-alternative-motion-key-mappings) for more details on these key mappings.
|
||||
|
||||
### Python Location Information
|
||||
|
||||
- "`g:`" : print (echo) current semantic location (e.g. ""``(class:)Foo > (def:)bar"``")
|
||||
|
||||
## Installation
|
||||
|
||||
### Manually (Example)
|
||||
|
||||
If you are using Vim 8.0 and above, I recommend you take advantage of the '[package](https://vi.stackexchange.com/a/9523/17621)' feature and do something like this:
|
||||
|
||||
$ cd ~/.vim
|
||||
$ mkdir -p pack/import/start
|
||||
$ cd pack/import/start && git clone git://github.com/jeetsukumaran/vim-pythonsense.git
|
||||
|
||||
You do not have to name the mid-level directory "import"; it can be anything else that makes sense to you for this plugin (e.g., "bundle", "jeetsukumaran", "python", etc.). For more information, see the documentation on "[packages](http://vimhelp.appspot.com/repeat.txt.html#packages)".
|
||||
|
||||
If you are using an older Vim version and do not want to upgrade, then you would be much, much, *much* better served by using a plugin manager like [Vim-Plug](https://github.com/junegunn/vim-plug) (described below).
|
||||
However, if you are stuck with an old version of Vim and *really* do not or cannot use a plugin manager, then will need to manually copy the files to the corresponding locations in your home directory:
|
||||
|
||||
- "vim-pythonsense/after/ftplugin/python/pythonsense.vim" to: "~/.vim/after/ftplugin/python/pythonsense.vim"
|
||||
- "vim-pythonsense/autoload/pythonsense.vim" to: "~/.vim/autoload/pythonsense.vim"
|
||||
- "vim-pythonsense/ftplugin/python/pythonsense.vim" to: "~/.vim/ftplugin/python/pythonsense.vim"
|
||||
|
||||
### [pathogen.vim](https://github.com/tpope/vim-pathogen)
|
||||
|
||||
$ cd ~/.vim/bundle
|
||||
$ git clone git://github.com/jeetsukumaran/vim-pythonsense.git
|
||||
|
||||
### [Vim-Plug](https://github.com/junegunn/vim-plug)
|
||||
|
||||
Add the line below into your "~/.vimrc":
|
||||
|
||||
Plug 'jeetsukumaran/vim-pythonsense'
|
||||
|
||||
and run:
|
||||
|
||||
:PlugInstall
|
||||
|
||||
More information on setting up [Vim-Plug](https://github.com/junegunn/vim-plug) to manage your plugins can be found [here](https://github.com/junegunn/vim-plug/wiki/tutorial).
|
||||
|
||||
### [Vundle](https://github.com/gmarik/vundle.git)
|
||||
|
||||
Add the line below into your "~/.vimrc":
|
||||
|
||||
Vundle 'jeetsukumaran/vim-pythonsense'
|
||||
|
||||
or run:
|
||||
|
||||
:VundleInstall jeetsukumaran/vim-pythonsense
|
||||
|
||||
## Customization
|
||||
|
||||
### [Changing the Key Mappings](#changing-the-key-mappings)
|
||||
|
||||
If you are unhappy with the default key-mappings you can define your own which will individually override the default ones.
|
||||
However, instead of doing so in your "~/.vimrc", you need to do so in a file located in your "~/.vim/ftplugin/python/" directory, so that this key mappings are only enabled when editing a Python file.
|
||||
Furthermore, you should make sure that you limit the key mapping to the "``<buffer>``" scope.
|
||||
For example, to override yet replicate the default mappings you would define, the following in "~/.vim/ftplugin/python/pythonsense-custom.vim":
|
||||
|
||||
```
|
||||
map <buffer> ac <Plug>(PythonsenseOuterClassTextObject)
|
||||
map <buffer> ic <Plug>(PythonsenseInnerClassTextObject)
|
||||
map <buffer> af <Plug>(PythonsenseOuterFunctionTextObject)
|
||||
map <buffer> if <Plug>(PythonsenseInnerFunctionTextObject)
|
||||
map <buffer> ad <Plug>(PythonsenseOuterDocStringTextObject)
|
||||
map <buffer> id <Plug>(PythonsenseInnerDocStringTextObject)
|
||||
|
||||
map <buffer> ]] <Plug>(PythonsenseStartOfNextPythonClass)
|
||||
map <buffer> ][ <Plug>(PythonsenseEndOfPythonClass)
|
||||
map <buffer> [[ <Plug>(PythonsenseStartOfPythonClass)
|
||||
map <buffer> [] <Plug>(PythonsenseEndOfPreviousPythonClass)
|
||||
map <buffer> ]m <Plug>(PythonsenseStartOfNextPythonFunction)
|
||||
map <buffer> ]M <Plug>(PythonsenseEndOfPythonFunction)
|
||||
map <buffer> [m <Plug>(PythonsenseStartOfPythonFunction)
|
||||
map <buffer> [M <Plug>(PythonsenseEndOfPreviousPythonFunction)
|
||||
|
||||
map <buffer> g: <Plug>(PythonsensePyWhere)
|
||||
```
|
||||
|
||||
Note that you do not need to specify *all* the key mappings if you just want to customize a few.
|
||||
Simply provide your own key mapping to each of the "``<Plug>``" mappings you want to override, and these will be respected, while any "``<Plug>``" maps that are not so explicitly bound will be assigned to the default key maps.
|
||||
|
||||
### [Suppressing the Key Mappings](#suppressing-the-key-mappings)
|
||||
|
||||
If you want to suppress some of the key mappings entirely (i.e., without providing your own to override the functionality), you can specify one or more of the following in your "~/.vimrc":
|
||||
|
||||
```
|
||||
let g:is_pythonsense_suppress_object_keymaps = 1
|
||||
let g:is_pythonsense_suppress_motion_keymaps = 1
|
||||
let g:is_pythonsense_suppress_location_keymaps = 1
|
||||
```
|
||||
|
||||
to selectively suppress just the text object, motion, or information key mapping sets by respectively.
|
||||
|
||||
You can also suppress *all* default key mappings by specifying the following in your "~/.vimrc":
|
||||
|
||||
```
|
||||
let g:is_pythonsense_suppress_keymaps = 1
|
||||
```
|
||||
|
||||
### [Activating Alternative Motion Key Mappings](#activating-alternative-motion-key-mappings)
|
||||
|
||||
As discussed above, "Pythonsense" overrides some native Vim 8.0+ motion key mappings, replacing the indent-based non-semantic ones with semantically-aware ones.
|
||||
If you wish to have access to both types of motions, i.e., the stock Vim 8.0+ non-semantic indent-based block motions as well as the "Pythonsense" semantically aware class/method/function motions, then you can specify
|
||||
|
||||
```
|
||||
let g:is_pythonsense_alternate_motion_keymaps = 1
|
||||
```
|
||||
in your "~/.vimrc".
|
||||
|
||||
Then (unless you specify you want all or motion key mappings suppressed entirely), the "Pythonsense" semantic class/method/function motions will be bound to the alternate keys below:
|
||||
|
||||
- "`]k`" : Move (forward) to the beginning of the *next* Python class.
|
||||
- "`]K`" : Move (forward) to the end of the *current* Python class.
|
||||
- "`[k`" : Move (backward) to beginning of the *current* Python class (or beginning of the previous Python class if not currently in a class or already at the beginning of a class).
|
||||
- "`[K`" : Move (backward) to end of the *previous* Python class.
|
||||
|
||||
- "`]f`" : Move (forward) to the beginning of the *next* Python method or function.
|
||||
- "`]F`" : Move (forward) to the end of the *current* Python method or function.
|
||||
- "`[f`" : Move (backward) to the beginning of the *current* Python method or function (or to the beginning of the previous method or function if not currently in a method/function or already at the beginning of a method/function).
|
||||
- "`[F`" : Move (backward) to the end of the *previous* Python method or function.
|
||||
|
||||
## Similar Projects
|
||||
|
||||
Most notable is the [vim-textobj-python](https://github.com/bps/vim-textobj-python) plugin.
|
||||
Pythonsense distinguishes itself from this plugin in the following ways:
|
||||
|
||||
- Stand-alone and lightweight: does not rely on [vim-textobj-user](https://github.com/kana/vim-textobj-user/wiki).
|
||||
- Also includes a docstring text object (originally from [here](https://pastebin.com/u/gfixler)).
|
||||
- Crucially (for me!) selecting an outer class, method, or function also selects the blank line following. Without this, I find I always have to (a) add a new blank line after I have, e.g. pasted the class/method/function in its new location, and (b) delete the extra blank like in the old class/method/function location. With Pythonsense, this is taken care of automatically and the cleaner approach (for me!) not only saves time and key-strokes, but the oh-so-valuable headspace taskload. For a discussion of this issue, see [here](https://github.com/bps/vim-textobj-python/issues/17), and, in particular, [here](https://github.com/bps/vim-textobj-python/issues/17#issuecomment-187735637).
|
||||
- Allows for successive invocations of text object selectors to select enclosing classes/functions.
|
||||
- Handles nested classes/functions better (though with some failing corner cases).
|
||||
- Seems to handle functions/classes with multiline arguments better.
|
||||
- Handles folds better (opens them automatically if needed).
|
||||
- Of course, also provides a docstring text object as well as the semantic location information helper ("``g:``").
|
||||
|
||||
## More Information on Text Objects
|
||||
|
||||
If you are reading all this and wondering what is a text object or why are text objects such a big deal, then the short answer is that text objects are like using a socket and ratchet as opposed to a monkey wrench: as long as you can find a fit, you cannot beat the efficiency, speed, and precision (as well as elegance and pure pleasure). For more details, you could look at some of the following:
|
||||
|
||||
- https://blog.carbonfive.com/2011/10/17/vim-text-objects-the-definitive-guide/
|
||||
- http://owen.cymru/vim-text-objects-extend-vims-natural-language-2/
|
||||
- http://codyveal.com/posts/vim-killer-features-part-1-text-objects/
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
- The seed/inspiration (and basic logic) for the code for Python class and function text objects came from Nat Williams' "[pythontextobj.vim](https://github.com/natw/vim-pythontextobj)".
|
||||
From a practical usage standpoint, principal differences are:
|
||||
- The semantics of outer ("a") vs inner ("i") function/class. In the original, both "a" and "i" include the class/function definition line, but "i" extends to include the space after the class/function definition. I felt that "i" should exclude the class/function declaration heading line *and* the space after the definition (i.e., just the body of the class/function), while "a" should include both the class/function declaration heading line and a space after the definition.
|
||||
- Handles nested cases better (or at all): multiple invocations of "``ic``", "``ac``", "``if``", "``af``", etc. grab successively enclosing classes/functions. The seed/inspiration for the logic for *this*, in turn, came from Michael Smith's "[vim-indent-object](http://github.com/michaeljsmith/vim-indent-object)".
|
||||
- Code for the docstring text objects taken from the [pastebin shared by gfixler](https://pastebin.com/u/gfixler).
|
||||
|
||||
## Appendices
|
||||
|
||||
### [Stock Vim vs Pythonsense Motions](#stock-vim-vs-pythonsense-motions)
|
||||
|
||||
The stock Vim 8.0 "class" motions ("``]]``", "`[[`", etc.), find blocks that begin at the first column, *regardless* of whether or not these are class or function blocks, while its method/function motions ("``[m``", "``]m``", etc.) find all blocks at any indent *regardless* of whether or not these are class or function blocks.
|
||||
In contrast, "Pythonsense" class motions work on finding *all* and *only* class definitions, *regardless* of their indent level, while its method/function motions work on finding *all* and *only* method/function definitions, *regardless* of their indent level.
|
||||
The stock Vim 8.0 motions can thus be seen as non-semantically aware motions that target indent levels rather than Python classes/methods/functions, while the "Pythonsense" motions, on the other hand, can been seen as semantically aware motions that target Python classes/methods/functions rather than indent levels.
|
||||
In a simple structured file (without even bare functions), e.g.,
|
||||
|
||||
```
|
||||
class Foo1(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
def bar(self):
|
||||
pass
|
||||
def baz(self):
|
||||
pass
|
||||
class Foo2(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
def bar(self):
|
||||
pass
|
||||
def baz(self):
|
||||
pass
|
||||
```
|
||||
|
||||
both the stock Vim and "Pythonsense" motions work the same.
|
||||
|
||||
However, in a file like the following:
|
||||
|
||||
```
|
||||
|
||||
class Foo1(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
def bar(self):
|
||||
pass
|
||||
def baz(self):
|
||||
pass
|
||||
|
||||
class Foo2(object):
|
||||
class Foo2Exception1(Exception):
|
||||
def __init__(self):
|
||||
pass
|
||||
class Foo2Exception2(Exception):
|
||||
def __init__(self):
|
||||
pass
|
||||
def __init__(self):
|
||||
pass
|
||||
def bar(self):
|
||||
pass
|
||||
def baz(self):
|
||||
pass
|
||||
|
||||
def fn1(a):
|
||||
pass
|
||||
|
||||
def fn2(a):
|
||||
pass
|
||||
|
||||
def fn3(a):
|
||||
pass
|
||||
|
||||
```
|
||||
|
||||
the differences in behavior show up:
|
||||
|
||||
- With respect to the "``]]``"/"``[[``"/etc. motions, the stock Vim 8.0+ implementation will visit both top-level class and function definitions freely and miss the nested classes, while the "Pythonsense" implementation will visit *just* the class definitions exclusively and comprehensively (i.e., no functions and also include the nested classes in the visits).
|
||||
- With respect to the "``[m``"/"``]m``"/etc. motions, the stock Vim 8.0+ implementation will visit all class, method, and function definitions in the file, whereas the "Pythonsense" implementation will visit *just* the method and function definitions exclusively (i.e., no classes).
|
||||
|
||||
## Copyright, License, and Warranty
|
||||
|
||||
Copyright (c) 2018 Jeet Sukumaran. All rights reserved.
|
||||
|
||||
This work is licensed under the terms of the (expat) MIT license.
|
||||
See the file "LICENSE" (or https://opensource.org/licenses/MIT) for specific terms and details.
|
||||
|
58
bundle/vim-pythonsense/after/ftplugin/python/pythonsense.vim
vendored
Normal file
58
bundle/vim-pythonsense/after/ftplugin/python/pythonsense.vim
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
" Key Mappings to Override Default Ones
|
||||
|
||||
if ! get(g:, "is_pythonsense_suppress_keymaps", 0) && ! get(g:, "is_pythonsense_suppress_motion_keymaps", 0)
|
||||
if get(g:, "is_pythonsense_alternate_motion_keymaps", 0)
|
||||
if !hasmapto('<Plug>PythonsenseStartOfNextPythonClass')
|
||||
map <buffer> ]k <Plug>(PythonsenseStartOfNextPythonClass)
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseEndOfPythonClass')
|
||||
map <buffer> ]K <Plug>(PythonsenseEndOfPythonClass)
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseStartOfPythonClass')
|
||||
map <buffer> [k <Plug>(PythonsenseStartOfPythonClass)
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseEndOfPreviousPythonClass')
|
||||
map <buffer> [K <Plug>(PythonsenseEndOfPreviousPythonClass)
|
||||
endif
|
||||
|
||||
if !hasmapto('<Plug>PythonsenseStartOfNextPythonFunction')
|
||||
map <buffer> ]f <Plug>(PythonsenseStartOfNextPythonFunction)
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseEndOfPythonFunction')
|
||||
map <buffer> ]F <Plug>(PythonsenseEndOfPythonFunction)
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseStartOfPythonFunction')
|
||||
map <buffer> [f <Plug>(PythonsenseStartOfPythonFunction)
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseEndOfPreviousPythonFunction')
|
||||
map <buffer> [F <Plug>(PythonsenseEndOfPreviousPythonFunction)
|
||||
endif
|
||||
else
|
||||
if !hasmapto('<Plug>PythonsenseStartOfNextPythonClass')
|
||||
map <buffer> ]] <Plug>(PythonsenseStartOfNextPythonClass)
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseEndOfPythonClass')
|
||||
map <buffer> ][ <Plug>(PythonsenseEndOfPythonClass)
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseStartOfPythonClass')
|
||||
map <buffer> [[ <Plug>(PythonsenseStartOfPythonClass)
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseEndOfPreviousPythonClass')
|
||||
map <buffer> [] <Plug>(PythonsenseEndOfPreviousPythonClass)
|
||||
endif
|
||||
|
||||
if !hasmapto('<Plug>PythonsenseStartOfNextPythonFunction')
|
||||
map <buffer> ]m <Plug>(PythonsenseStartOfNextPythonFunction)
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseEndOfPythonFunction')
|
||||
map <buffer> ]M <Plug>(PythonsenseEndOfPythonFunction)
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseStartOfPythonFunction')
|
||||
map <buffer> [m <Plug>(PythonsenseStartOfPythonFunction)
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseEndOfPreviousPythonFunction')
|
||||
map <buffer> [M <Plug>(PythonsenseEndOfPreviousPythonFunction)
|
||||
endif
|
||||
endif
|
||||
|
||||
endif
|
699
bundle/vim-pythonsense/autoload/pythonsense.vim
vendored
Normal file
699
bundle/vim-pythonsense/autoload/pythonsense.vim
vendored
Normal file
@ -0,0 +1,699 @@
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" File: autoload/pythonsense.vim
|
||||
" Author: Jeet Sukumaran
|
||||
"
|
||||
" Copyright: (C) 2018 Jeet Sukumaran
|
||||
"
|
||||
" License: 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.
|
||||
"
|
||||
" Credits: - pythontextobj.vim by Nat Williams (https://github.com/natw/vim-pythontextobj)
|
||||
" - chapa.vim by Alfredo Deza (https://github.com/alfredodeza/chapa.vim)
|
||||
" - indentobj by Austin Taylor (https://github.com/austintaylor/vim-indentobject)
|
||||
" - Python Docstring Text Objects by gfixler (https://pastebin.com/u/gfixler)
|
||||
" - vim-indent-object by Michael Smith (http://github.com/michaeljsmith/vim-indent-object)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
" Support Functions {{{1
|
||||
function! pythonsense#trawl_search(pattern, start_line, fwd)
|
||||
let current_line = a:start_line
|
||||
let lastline = line('$')
|
||||
if a:fwd
|
||||
let stepvalue = 1
|
||||
else
|
||||
let stepvalue = -1
|
||||
endif
|
||||
while (current_line > 0 && current_line <= lastline)
|
||||
let line_text = getline(current_line)
|
||||
let m = match(line_text, a:pattern)
|
||||
if m >= 0
|
||||
return current_line
|
||||
endif
|
||||
let current_line = current_line + stepvalue
|
||||
endwhile
|
||||
return 0
|
||||
endfunction
|
||||
" }}}1
|
||||
|
||||
" Python (Statement) Text Objects {{{1
|
||||
" Based on:
|
||||
" - https://github.com/natw/vim-pythontextobj
|
||||
" - https://github.com/alfredodeza/chapa.vim
|
||||
" - https://github.com/austintaylor/vim-indentobject
|
||||
" - http://github.com/michaeljsmith/vim-indent-object
|
||||
|
||||
" Select an object ("class"/"function")
|
||||
let s:pythonsense_obj_start_line = -1
|
||||
let s:pythonsense_obj_end_line = -1
|
||||
function! pythonsense#select_named_object(obj_name, inner, range)
|
||||
" Is this a new selection?
|
||||
let new_vis = 0
|
||||
let new_vis = new_vis || s:pythonsense_obj_start_line != a:range[0]
|
||||
let new_vis = new_vis || s:pythonsense_obj_end_line != a:range[1]
|
||||
|
||||
" store current range
|
||||
let s:pythonsense_obj_start_line = a:range[0]
|
||||
let s:pythonsense_obj_end_line = a:range[1]
|
||||
|
||||
" Repeatedly increase the scope of the selection.
|
||||
let cnt = 1
|
||||
let scan_start_line = s:pythonsense_obj_start_line
|
||||
while scan_start_line > 0
|
||||
if getline(scan_start_line) !~ '^\s*$'
|
||||
break
|
||||
endif
|
||||
let scan_start_line -= 1
|
||||
endwhile
|
||||
if scan_start_line == 0
|
||||
return [-1, -1]
|
||||
endif
|
||||
let obj_max_indent_level = -1
|
||||
|
||||
while cnt > 0
|
||||
let current_line_nr = scan_start_line
|
||||
|
||||
let [obj_start_line, obj_end_line] = pythonsense#get_object_line_range(a:obj_name, obj_max_indent_level, current_line_nr, s:pythonsense_obj_end_line, a:inner)
|
||||
if obj_start_line == -1
|
||||
return [-1, -1]
|
||||
endif
|
||||
|
||||
let is_changed = 0
|
||||
let is_changed = is_changed || s:pythonsense_obj_start_line != obj_start_line
|
||||
let is_changed = is_changed || s:pythonsense_obj_end_line != obj_end_line
|
||||
if new_vis
|
||||
let is_changed = 1
|
||||
endif
|
||||
|
||||
let s:pythonsense_obj_start_line = obj_start_line
|
||||
let s:pythonsense_obj_end_line = obj_end_line
|
||||
|
||||
" If there was no change, then don't decrement the count (it didn't
|
||||
" count because it didn't do anything).
|
||||
if is_changed
|
||||
let cnt = cnt - 1
|
||||
else
|
||||
" no change to selection;
|
||||
" move to line above selection and try again
|
||||
if scan_start_line == 0
|
||||
return [-1, -1]
|
||||
endif
|
||||
let [min_indent, max_indent] = pythonsense#get_minmax_indent_count('\(class\|def\|async def\)', scan_start_line, obj_end_line)
|
||||
if min_indent == 0
|
||||
return [-1, -1]
|
||||
endif
|
||||
let obj_max_indent_level = min_indent - 1
|
||||
let scan_start_line -= 1
|
||||
endif
|
||||
endwhile
|
||||
|
||||
" select range
|
||||
if obj_end_line >= obj_start_line
|
||||
exec obj_start_line
|
||||
execute "normal! V" . obj_end_line . "G"
|
||||
return [obj_start_line, obj_end_line]
|
||||
else
|
||||
return [-1, -1]
|
||||
endif
|
||||
|
||||
endfunction
|
||||
|
||||
function! pythonsense#get_object_line_range(obj_name, obj_max_indent_level, line_range_start, line_range_end, inner)
|
||||
" find definition line
|
||||
let current_line_nr = a:line_range_start
|
||||
if a:line_range_start == a:line_range_end
|
||||
let search_past_decorator_last_line = line("$")
|
||||
else
|
||||
let search_past_decorator_last_line = a:line_range_end
|
||||
endif
|
||||
|
||||
while current_line_nr <= search_past_decorator_last_line
|
||||
if getline(current_line_nr) !~ '^\s*@.*$'
|
||||
break
|
||||
end
|
||||
let current_line_nr += 1
|
||||
endwhile
|
||||
if current_line_nr > search_past_decorator_last_line
|
||||
return [-1, -1]
|
||||
endif
|
||||
if current_line_nr > a:line_range_end
|
||||
let effective_line_range_end = current_line_nr
|
||||
else
|
||||
let effective_line_range_end = a:line_range_end
|
||||
endif
|
||||
let obj_start_line = pythonsense#get_named_python_obj_start_line_nr(a:obj_name, a:obj_max_indent_level, current_line_nr, 0)
|
||||
" no object definition line in file
|
||||
if (! obj_start_line)
|
||||
return [-1, -1]
|
||||
endif
|
||||
let obj_header_line = obj_start_line
|
||||
let obj_header_indent = pythonsense#get_line_indent_count(obj_header_line)
|
||||
if obj_header_indent > 0
|
||||
let obj_header_indent -= 1
|
||||
endif
|
||||
|
||||
let obj_end_line = pythonsense#get_object_end_line_nr(obj_start_line, obj_start_line, a:inner)
|
||||
|
||||
" in case of a class definition, the parentheses are optional
|
||||
if a:obj_name == "def"
|
||||
let pattern = '^[^#]*)[^#]*:\(\s*$\|\s*#.*$\)'
|
||||
else
|
||||
let pattern = '^[^#]*)\?[^#]*:\(\s*$\|\s*#.*$\)'
|
||||
endif
|
||||
|
||||
if (a:inner)
|
||||
" find class/function body
|
||||
let inner_start_line = obj_start_line
|
||||
while inner_start_line <= line('$')
|
||||
if getline(inner_start_line) =~# pattern
|
||||
break
|
||||
endif
|
||||
let inner_start_line += 1
|
||||
endwhile
|
||||
if inner_start_line <= line('$')
|
||||
let obj_start_line = inner_start_line + 1
|
||||
endif
|
||||
else
|
||||
" include decorators
|
||||
let dec_line = pythonsense#get_start_decorators_line_nr(obj_start_line)
|
||||
if dec_line < obj_start_line
|
||||
let obj_start_line = dec_line
|
||||
endif
|
||||
endif
|
||||
|
||||
" This is an ugly hack to deal with (some) specially indented cases
|
||||
" (especially when searching for a 'class' while inside a non-class member
|
||||
" function, or when searching for a 'def' with nothing but class
|
||||
" definitions above)
|
||||
" Make sure there is no statement line with a lower indentation than the
|
||||
" definition line in between the current line and the definition line
|
||||
if a:obj_name == 'class'
|
||||
let pattern = 'def\|async def'
|
||||
else
|
||||
let pattern = 'class'
|
||||
endif
|
||||
let pattern = '^\s*[^#]*\s*\k'
|
||||
if pythonsense#is_statement_encountered_between_two_lines(pattern, obj_header_indent, obj_start_line, current_line_nr)
|
||||
return [-1, -1]
|
||||
endif
|
||||
|
||||
return [obj_start_line, obj_end_line]
|
||||
endfunction
|
||||
|
||||
function! pythonsense#get_object_end_line_nr(obj_start, search_start, inner)
|
||||
let obj_indent = pythonsense#get_line_indent_count(a:obj_start)
|
||||
let obj_end = pythonsense#get_next_indent_line_nr(a:search_start, obj_indent)
|
||||
if a:inner
|
||||
let obj_end = prevnonblank(obj_end)
|
||||
endif
|
||||
return obj_end
|
||||
endfunction
|
||||
|
||||
function! pythonsense#get_next_indent_line_nr(search_start, obj_indent)
|
||||
let line = a:search_start
|
||||
|
||||
" Handle multiline definition
|
||||
let saved_cursor = getcurpos()
|
||||
call cursor(line, 0)
|
||||
normal! f(%
|
||||
let line = line('.')
|
||||
call setpos('.', saved_cursor)
|
||||
|
||||
let lastline = line('$')
|
||||
while (line > 0 && line <= lastline)
|
||||
let line = line + 1
|
||||
if (pythonsense#get_line_indent_count(line) <= a:obj_indent && getline(line) !~ '^\s*$')
|
||||
return line - 1
|
||||
endif
|
||||
endwhile
|
||||
return lastline
|
||||
endfunction
|
||||
|
||||
function! pythonsense#get_start_decorators_line_nr(start)
|
||||
" Returns the line of the first decorator line above the starting line,
|
||||
" counting only decorators with the same level.
|
||||
let start_line_indent = pythonsense#get_line_indent_count(a:start)
|
||||
let last_non_blank_line = a:start
|
||||
let current_line = a:start - 1
|
||||
while current_line > 0
|
||||
if getline(current_line) !~ '^\s*$'
|
||||
if pythonsense#get_line_indent_count(current_line) != start_line_indent
|
||||
break
|
||||
endif
|
||||
if getline(current_line) !~ '^\s*@'
|
||||
break
|
||||
endif
|
||||
let last_non_blank_line = current_line
|
||||
endif
|
||||
let current_line -= 1
|
||||
endwhile
|
||||
return last_non_blank_line
|
||||
endfunction
|
||||
|
||||
function! pythonsense#get_line_indent_count(line_nr)
|
||||
if b:pythonsense_is_tab_indented
|
||||
let indent_count = matchstrpos(getline(a:line_nr), '^\t\+')[2]
|
||||
else
|
||||
let indent_count = indent(a:line_nr)
|
||||
endif
|
||||
return indent_count
|
||||
endfunction
|
||||
|
||||
function! pythonsense#get_minmax_indent_count(pattern, line_range_start, line_range_end)
|
||||
let current_line = a:line_range_start
|
||||
let min_indent_count = -1
|
||||
let max_indent_count = -1
|
||||
while current_line <= a:line_range_end && current_line <= line('$')
|
||||
if getline(current_line) !~ '^\s*$'
|
||||
if getline(current_line) =~# a:pattern
|
||||
let current_indent = pythonsense#get_line_indent_count(current_line)
|
||||
if min_indent_count < 0 || current_indent < min_indent_count
|
||||
let min_indent_count = current_indent
|
||||
endif
|
||||
if max_indent_count < 0 || current_indent > max_indent_count
|
||||
let max_indent_count = current_indent
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
let current_line = current_line + 1
|
||||
endwhile
|
||||
return [min_indent_count, max_indent_count]
|
||||
endfunction
|
||||
|
||||
function! pythonsense#is_statement_encountered_between_two_lines(pattern, max_indent, line_range_start, line_range_end)
|
||||
let current_line = a:line_range_start
|
||||
while current_line <= a:line_range_end && current_line <= line('$')
|
||||
if getline(current_line) !~ '^\s*$'
|
||||
if getline(current_line) =~# a:pattern
|
||||
let current_indent = pythonsense#get_line_indent_count(current_line)
|
||||
if a:max_indent > -1 && current_indent < a:max_indent
|
||||
return 1
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
let current_line += 1
|
||||
endwhile
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! pythonsense#get_indent_char()
|
||||
if b:pythonsense_is_tab_indented
|
||||
let indent_char = '\t'
|
||||
else
|
||||
let indent_char = " "
|
||||
endif
|
||||
return indent_char
|
||||
endfunction
|
||||
|
||||
function! pythonsense#get_named_python_obj_start_line_nr(obj_name, obj_max_indent_level, start_line, fwd)
|
||||
let lastline = line('$')
|
||||
if a:fwd
|
||||
let stepvalue = 1
|
||||
else
|
||||
let stepvalue = -1
|
||||
endif
|
||||
let indent_char = pythonsense#get_indent_char()
|
||||
|
||||
let current_line = a:start_line
|
||||
while (current_line > 0 && current_line <= lastline)
|
||||
" if getline(current_line) !~ '\(^\s*$\|^\s*[#@].*$\)'
|
||||
if getline(current_line) !~ '^\s*$'
|
||||
break
|
||||
endif
|
||||
let current_line = current_line + stepvalue
|
||||
endwhile
|
||||
if a:obj_max_indent_level > -1
|
||||
let pattern = '^' . indent_char . '\{0,' . a:obj_max_indent_level . '}' . '\(class\|def\|async def\)'
|
||||
else
|
||||
let pattern = '^\s*' . '\(class\|def\|async def\)'
|
||||
endif
|
||||
if getline(current_line) =~# pattern
|
||||
if getline(current_line) =~# a:obj_name
|
||||
return current_line
|
||||
endif
|
||||
endif
|
||||
|
||||
let target_line_indent = pythonsense#get_line_indent_count(current_line) - 1
|
||||
if target_line_indent < 0
|
||||
let target_line_indent = 0
|
||||
endif
|
||||
if a:obj_max_indent_level > -1 && target_line_indent > a:obj_max_indent_level
|
||||
let target_line_indent = a:obj_max_indent_level
|
||||
endif
|
||||
let max_indent = target_line_indent
|
||||
while (current_line > 0 && current_line <= lastline)
|
||||
let pattern = '^' . indent_char . '\{0,' . max_indent . '}' . '\(class\|def\|async def\)'
|
||||
if getline(current_line) =~# pattern
|
||||
if getline(current_line) =~# a:obj_name
|
||||
return current_line
|
||||
else
|
||||
if a:obj_name != 'class' && pythonsense#get_line_indent_count(current_line) <= max_indent
|
||||
" encountered a scope block at a lower indent level before
|
||||
" encountering object definition
|
||||
return 0
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
" let m = match(getline(current_line), pattern)
|
||||
" if m >= 0
|
||||
" return current_line
|
||||
" endif
|
||||
|
||||
let target_line_indent = pythonsense#get_line_indent_count(current_line) - 1
|
||||
" Special case for multiline argument lines, with the parameter being
|
||||
" indented one step more than the open def/class and the closing
|
||||
" parenthesis.
|
||||
let closing_pattern = '^' . indent_char . '*)'
|
||||
if target_line_indent > 0 && target_line_indent < max_indent && getline(current_line) !~# closing_pattern
|
||||
let max_indent = target_line_indent
|
||||
endif
|
||||
if a:obj_max_indent_level > -1 && target_line_indent > a:obj_max_indent_level
|
||||
let target_line_indent = a:obj_max_indent_level
|
||||
endif
|
||||
|
||||
let current_line = current_line + stepvalue
|
||||
endwhile
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! pythonsense#python_text_object(obj_name, inner, mode)
|
||||
if a:mode == "o"
|
||||
let lnrange = [line("."), line(".")]
|
||||
else
|
||||
let lnrange = [line("'<"), line("'>")]
|
||||
endif
|
||||
let nreps_left = 1 "v:count1
|
||||
while nreps_left > 0
|
||||
let lnrange = pythonsense#select_named_object(a:obj_name, a:inner, lnrange)
|
||||
if lnrange[0] == -1
|
||||
break
|
||||
endif
|
||||
let s:pythonsense_obj_start_line = lnrange[0]
|
||||
let s:pythonsense_obj_end_line = lnrange[1]
|
||||
let nreps_left -= 1
|
||||
endwhile
|
||||
if lnrange[0] != -1
|
||||
if has("folding") && foldclosed(line('.')) != -1
|
||||
" try
|
||||
" execute "normal! zO"
|
||||
" catch /E490/ " no fold found
|
||||
" endtry
|
||||
execute "normal! zO"
|
||||
endif
|
||||
" let s:pythonsense_obj_start_line = -1
|
||||
" let s:pythonsense_obj_end_line = -1
|
||||
" execute "normal! \<ESC>gv"
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! pythonsense#python_function_text_object(inner, mode)
|
||||
call pythonsense#python_text_object('def', a:inner, a:mode)
|
||||
endfunction
|
||||
|
||||
function! pythonsense#python_class_text_object(inner, mode)
|
||||
call pythonsense#python_text_object('class', a:inner, a:mode)
|
||||
endfunction
|
||||
|
||||
" }}}1
|
||||
|
||||
" Python Docstring Text Objects {{{1
|
||||
" From: https://pastebin.com/u/gfixler
|
||||
function! pythonsense#python_docstring_text_object (inner)
|
||||
" get current line number
|
||||
let s = line('.')
|
||||
" climb up to first def/class line, or first line of buffer
|
||||
while s > 0 && getline(s) !~# '^\s*\(def\|async def\|class\)'
|
||||
let s = s - 1
|
||||
endwhile
|
||||
" set search start to just after def/class line, or on first buffer line
|
||||
let s = s + 1
|
||||
" descend lines obj_end_line end of buffer or def/class line
|
||||
while s < line('$') && getline(s) !~# '^\s*\(def\|async def\|class\)'
|
||||
" if line begins with optional whitespace followed by '''
|
||||
if getline(s) =~ "^\\s*'''" || getline(s) =~ '^\s*"""'
|
||||
if getline(s) =~ "^\\s*'''"
|
||||
let close_pattern = "'''\\s*$"
|
||||
else
|
||||
let close_pattern = '"""\s*$'
|
||||
endif
|
||||
" set search end to just after found start line
|
||||
let e = s + 1
|
||||
" descend lines obj_end_line end of buffer or def/class line
|
||||
while e <= line('$') && getline(e) !~# '^\s*\(def\|async def\|class\)'
|
||||
" if line ends with ''' followed by optional whitespace
|
||||
if getline(e) =~ close_pattern
|
||||
" TODO check first for blank lines above to select instead
|
||||
" for 'around', extend search end through blank lines
|
||||
if a:inner
|
||||
let e -= 1
|
||||
let s += 1
|
||||
else
|
||||
let x = e + 1
|
||||
while x <= line('$') && getline(x) =~ '^\s*$'
|
||||
let e = x
|
||||
let x = x + 1
|
||||
endwhile
|
||||
endif
|
||||
" visual line select from start to end (first cursor move)
|
||||
exe 'norm '.s.'ggV'.e.'gg'
|
||||
return
|
||||
endif
|
||||
" move search end down a line
|
||||
let e = e + 1
|
||||
endwhile
|
||||
endif
|
||||
" move search start down a line
|
||||
let s = s + 1
|
||||
endwhile
|
||||
endfunction
|
||||
" }}}1
|
||||
|
||||
" Python Movements {{{1
|
||||
|
||||
function! pythonsense#move_to_python_object(obj_name, to_end, fwd, vim_mode) range
|
||||
if a:fwd
|
||||
let initial_search_start_line = a:lastline
|
||||
else
|
||||
let initial_search_start_line = a:firstline
|
||||
endif
|
||||
if a:to_end
|
||||
let target_line = pythonsense#find_end_of_python_object_to_move_to(a:obj_name, initial_search_start_line, a:fwd, v:count1)
|
||||
else
|
||||
let target_line = pythonsense#find_start_of_python_object_to_move_to(a:obj_name, initial_search_start_line, a:fwd, -1, v:count1)
|
||||
endif
|
||||
if target_line < 0 || target_line > line('$')
|
||||
return
|
||||
endif
|
||||
let current_column = col('.')
|
||||
let preserve_col_pos = get(b:, "pythonsense_preserve_col_pos", get(g:, "pythonsense_preserve_col_pos", 0))
|
||||
let fold_open = ""
|
||||
if a:vim_mode == "v"
|
||||
normal! gv
|
||||
endif
|
||||
if has("folding") && foldclosed(line('.')) != -1
|
||||
let fold_open = "zO"
|
||||
else
|
||||
let fold_open = ""
|
||||
endif
|
||||
try
|
||||
if preserve_col_pos
|
||||
execute "normal! " . target_line . "G" . current_column . "|" . preserve_col_pos . fold_open
|
||||
else
|
||||
execute "normal! " . target_line . "G^" . fold_open
|
||||
endif
|
||||
catch /E490/ " no fold found
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! pythonsense#find_end_of_python_object_to_move_to(obj_name, start_line, fwd, nreps)
|
||||
let initial_search_start_line = a:start_line
|
||||
let effective_start_line = initial_search_start_line
|
||||
let niters = 0
|
||||
while niters < 2
|
||||
let [start_line, nreps_remaining] = pythonsense#find_start_line_for_end_movement(a:obj_name, effective_start_line, a:fwd, a:nreps)
|
||||
if start_line <= 0
|
||||
let start_line = 1
|
||||
endif
|
||||
let start_of_object_line = pythonsense#find_start_of_python_object_to_move_to(a:obj_name, start_line, a:fwd, -1, nreps_remaining)
|
||||
if start_of_object_line < 0 || start_of_object_line > line('$')
|
||||
return -1
|
||||
endif
|
||||
let target_line = pythonsense#get_object_end_line_nr(start_of_object_line, start_of_object_line, 1)
|
||||
if target_line > 0
|
||||
\ && niters == 0
|
||||
\ && (
|
||||
\ target_line == initial_search_start_line
|
||||
\ || (a:fwd && target_line < initial_search_start_line)
|
||||
\ || (!a:fwd && target_line > initial_search_start_line)
|
||||
\ )
|
||||
" no change; possibly because we are already at an end boundary;
|
||||
" make ONE more attempt at trying again
|
||||
let niters += 1
|
||||
if a:fwd
|
||||
let effective_start_line = pythonsense#find_start_of_python_object_to_move_to(a:obj_name, target_line, a:fwd, -1, 1)
|
||||
if effective_start_line <= 0
|
||||
break
|
||||
endif
|
||||
else
|
||||
let prev_obj_indent = pythonsense#get_line_indent_count(start_of_object_line)
|
||||
let [new_start_line, nreps_remaining] = pythonsense#find_start_line_for_end_movement(a:obj_name, start_of_object_line, a:fwd, a:nreps)
|
||||
while new_start_line > 0 && getline(new_start_line) =~ '^\s*$'
|
||||
let new_start_line -= 1
|
||||
endwhile
|
||||
if new_start_line <= 0
|
||||
break
|
||||
endif
|
||||
let start_of_object_line = pythonsense#find_start_of_python_object_to_move_to(a:obj_name, new_start_line, a:fwd, prev_obj_indent, nreps_remaining)
|
||||
let target_line = pythonsense#get_object_end_line_nr(start_of_object_line, start_of_object_line, 1)
|
||||
break
|
||||
endif
|
||||
else
|
||||
break
|
||||
endif
|
||||
endwhile
|
||||
if a:fwd && target_line < initial_search_start_line
|
||||
return -1
|
||||
elseif !a:fwd && target_line > initial_search_start_line
|
||||
return -1
|
||||
else
|
||||
return target_line
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! pythonsense#find_start_of_python_object_to_move_to(obj_name, start_line, fwd, max_indent, nreps)
|
||||
let current_line = a:start_line
|
||||
if a:fwd
|
||||
let stepvalue = 1
|
||||
else
|
||||
let stepvalue = -1
|
||||
endif
|
||||
let target_pattern = '^\s*' . a:obj_name . '\s\+'
|
||||
let nreps_left = a:nreps
|
||||
let start_line = current_line
|
||||
let target_line = current_line
|
||||
let scope_block_indent = a:max_indent
|
||||
if getline(start_line) =~# target_pattern
|
||||
let start_line += stepvalue
|
||||
endif
|
||||
while nreps_left > 0
|
||||
while start_line > 0 && start_line <= line("$")
|
||||
if getline(start_line) =~ '^\s*\(class\|def\|async def\)'
|
||||
let current_line_indent = pythonsense#get_line_indent_count(start_line)
|
||||
if getline(start_line) =~# target_pattern
|
||||
if a:max_indent < 0 || current_line_indent < scope_block_indent
|
||||
let target_line = start_line
|
||||
break
|
||||
endif
|
||||
endif
|
||||
if scope_block_indent == -1 || current_line_indent < scope_block_indent
|
||||
let scope_block_indent = current_line_indent
|
||||
endif
|
||||
endif
|
||||
let start_line += stepvalue
|
||||
endwhile
|
||||
if start_line < 1 || start_line > line("$")
|
||||
break
|
||||
endif
|
||||
let start_line += stepvalue
|
||||
let nreps_left -= 1
|
||||
endwhile
|
||||
return target_line
|
||||
endfunction
|
||||
|
||||
function! pythonsense#find_start_line_for_end_movement(obj_name, initial_search_start_line, fwd, nreps_requested)
|
||||
let start_line = a:initial_search_start_line
|
||||
let nreps_remaining = a:nreps_requested
|
||||
let target_pattern = '^\s*' . a:obj_name . '\s\+'
|
||||
let scope_block_indent = -1
|
||||
let is_found = 0
|
||||
while start_line > 0
|
||||
if getline(start_line) =~ '^\s*\(class\|def\|async def\)'
|
||||
let current_line_indent = pythonsense#get_line_indent_count(start_line)
|
||||
if getline(start_line) =~ target_pattern
|
||||
if scope_block_indent == -1 || current_line_indent < scope_block_indent
|
||||
let is_found = 1
|
||||
break
|
||||
endif
|
||||
endif
|
||||
if scope_block_indent == -1 || current_line_indent < scope_block_indent
|
||||
let scope_block_indent = current_line_indent
|
||||
endif
|
||||
endif
|
||||
let start_line -= 1
|
||||
endwhile
|
||||
if is_found
|
||||
if !a:fwd
|
||||
let start_line -= 1
|
||||
else
|
||||
let nreps_remaining -= 1 " skip finding this block
|
||||
endif
|
||||
endif
|
||||
return [start_line, nreps_remaining]
|
||||
endfunction
|
||||
|
||||
" }}}1
|
||||
|
||||
" Python Location Information {{{1
|
||||
function! pythonsense#echo_python_location()
|
||||
let indent_char = pythonsense#get_indent_char()
|
||||
let pyloc = []
|
||||
let current_line = line('.')
|
||||
let obj_pattern = '\(class\|def\|async def\)'
|
||||
while current_line > 0
|
||||
if getline(current_line) !~ '^\s*$'
|
||||
break
|
||||
endif
|
||||
let current_line = current_line - 1
|
||||
endwhile
|
||||
if current_line == 0
|
||||
return
|
||||
endif
|
||||
let target_line_indent = pythonsense#get_line_indent_count(current_line)
|
||||
if target_line_indent < 0
|
||||
break
|
||||
endif
|
||||
let previous_line = current_line
|
||||
while current_line > 0
|
||||
let pattern = '^' . indent_char . '\{0,' . target_line_indent . '}' . obj_pattern
|
||||
let current_line_text = getline(current_line)
|
||||
if current_line_text =~# pattern
|
||||
let obj_name = matchstr(current_line_text, '^\s*\(class\|def\|async def\)\s\+\zs\k\+')
|
||||
if get(g:, "pythonsense_extended_location_info", 1)
|
||||
let obj_type = matchstr(current_line_text, '^\s*\zs\(class\|def\|async def\)')
|
||||
call add(pyloc, "(" . obj_type . ":)" . obj_name)
|
||||
else
|
||||
call add(pyloc, obj_name)
|
||||
endif
|
||||
let target_line_indent = pythonsense#get_line_indent_count(current_line) - 1
|
||||
endif
|
||||
if target_line_indent < 0
|
||||
break
|
||||
endif
|
||||
let previous_line = current_line
|
||||
let current_line = current_line - 1
|
||||
endwhile
|
||||
if get(g:, "pythonsense_extended_location_info", 1)
|
||||
let joiner = " > "
|
||||
else
|
||||
let joiner = "."
|
||||
endif
|
||||
echo join(reverse(pyloc), joiner)
|
||||
return
|
||||
endfunction
|
||||
" }}}1
|
147
bundle/vim-pythonsense/doc/pythonsense.txt
vendored
Normal file
147
bundle/vim-pythonsense/doc/pythonsense.txt
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
*pythonsense.txt* For Vim version 7.3 Last change: 2018 May 31
|
||||
|
||||
===============================================================================
|
||||
*pythonsense*
|
||||
|
||||
Pythonsense provides text selection and motion objects for Python classes,
|
||||
methods, functions, and doc strings.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
*pythonsense-object-select*
|
||||
|
||||
The following text objects are provided (see |pythonsense-custom-key-maps|
|
||||
to use your own custom key maps):
|
||||
|
||||
ac "a class" text object. This includes the entire class, including
|
||||
the header (class name declaration) and decorators, the class body,
|
||||
as well as a blank line if this is given after the class
|
||||
definition.
|
||||
ic "inner class" text object. This is the class body only, thus
|
||||
excluding the class name declaration line and any blank lines after
|
||||
the class definition.
|
||||
af "a function" (or method) text object. This includes the entire
|
||||
function, including the header (function name declaration) and
|
||||
decorators, the function body, as well as a blank line if this is
|
||||
given after the function definition.
|
||||
if "inner function" (or method) text object. This is the function
|
||||
body only, thus excluding the function name declaration line and
|
||||
any blank lines after the function definition.
|
||||
ad "a docstring" text object; includes triple quotes as well as
|
||||
docstring body.
|
||||
id "inner docstring" text object; includes docstring body only, and
|
||||
excludes triple quotes.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
*pythonsense-object-motion*
|
||||
|
||||
The following motions are provided (see pythonsense-alternate-motion-keymaps|
|
||||
to use an alternate set of mappings that do not override the native Vim ones,
|
||||
or |pythonsense-custom-key-maps| to use your own custom key maps):
|
||||
|
||||
{count}]] Move (forward) to the beginning of the next Python class.
|
||||
{count}][ Move (forward) to the end of the current Python class.
|
||||
{count}[[ Move (backward) to beginning of the current Python class
|
||||
(or beginning of the previous Python class if not currently in a
|
||||
class or already at the beginning of a class).
|
||||
{count}[] Move (backward) to end of the previous Python class.
|
||||
|
||||
{count}]m Move (forward) to the beginning of the next Python method or
|
||||
function.
|
||||
{count}]M Move (forward) to the end of the current Python method or
|
||||
function.
|
||||
{count}[m Move (backward) to the beginning of the current Python method or
|
||||
function (or to the beginning of the previous method or function if
|
||||
not currently in a method/function or already at the beginning of a
|
||||
method/function).
|
||||
{count}[M Move (backward) to the end of the previous Python method or
|
||||
function.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
*pythonsense-object-information*
|
||||
|
||||
The following information key maps are also provided:
|
||||
|
||||
g: Echo information about the current semantic Python location.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
*pythonsense-options*
|
||||
|
||||
g:is_pythonsense_suppress_object_keymaps
|
||||
Specify this in your '~/.vimrc' to disable the object selection key mappings: >
|
||||
let g:is_pythonsense_suppress_object_keymaps = 1
|
||||
<
|
||||
|
||||
g:is_pythonsense_suppress_motion_keymaps
|
||||
Specify this in your '~/.vimrc' to disable the motion key mappings: >
|
||||
let g:is_pythonsense_suppress_motion_keymaps = 1
|
||||
<
|
||||
|
||||
g:is_pythonsense_suppress_location_keymaps
|
||||
Specify this in your '~/.vimrc' to disable the information key mappings: >
|
||||
let g:is_pythonsense_suppress_location_keymaps = 1
|
||||
<
|
||||
|
||||
g:is_pythonsense_alternate_motion_keymaps *pythonsense-alternate-motion-keymaps*
|
||||
Specify this in your '~/.vimrc' to activate an alternate set of key mappings for object motions: >
|
||||
let g:is_pythonsense_alternate_motion_keymaps = 1
|
||||
<
|
||||
The above option will result in the following motion mappings as opposed to the
|
||||
ones described above:
|
||||
|
||||
{count}]k Move (forward) to the beginning of the next Python class.
|
||||
{count}]K Move (forward) to the end of the current Python class.
|
||||
{count}[k Move (backward) to beginning of the current Python class
|
||||
(or beginning of the previous Python class if not currently in a
|
||||
class or already at the beginning of a class).
|
||||
{count}[K Move (backward) to end of the previous Python class.
|
||||
|
||||
{count}]f Move (forward) to the beginning of the next Python method or
|
||||
function.
|
||||
{count}]F Move (forward) to the end of the current Python method or
|
||||
function.
|
||||
{count}[f Move (backward) to the beginning of the current Python method
|
||||
or function (or to the beginning of the previous method or
|
||||
function if not currently in a method/function or already at
|
||||
the beginning of a method/function).
|
||||
{count}[F Move (backward) to the end of the previous Python method or
|
||||
function.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
*pythonsense-custom-key-maps*
|
||||
|
||||
If you are unhappy with the default key-mappings you can define your own which
|
||||
will individually override the default ones. However, instead of doing so in
|
||||
your "~/.vimrc", you need to do so in a file located in your
|
||||
"~/.vim/ftplugin/python/" directory, so that this key mappings are only enabled
|
||||
when editing a Python file. Furthermore, you should make sure that you limit
|
||||
the key mapping to the buffer scope. For example, to override yet
|
||||
replicate the default mappings you would define, the following in
|
||||
"~/.vim/ftplugin/python/pythonsense-custom.vim": >
|
||||
|
||||
map <buffer> ac <Plug>(PythonsenseOuterClassTextObject)
|
||||
map <buffer> ic <Plug>(PythonsenseInnerClassTextObject)
|
||||
map <buffer> af <Plug>(PythonsenseOuterFunctionTextObject)
|
||||
map <buffer> if <Plug>(PythonsenseInnerFunctionTextObject)
|
||||
map <buffer> ad <Plug>(PythonsenseOuterDocStringTextObject)
|
||||
map <buffer> id <Plug>(PythonsenseInnerDocStringTextObject)
|
||||
|
||||
map <buffer> ]] <Plug>(PythonsenseStartOfNextPythonClass)
|
||||
map <buffer> ][ <Plug>(PythonsenseEndOfPythonClass)
|
||||
map <buffer> [[ <Plug>(PythonsenseStartOfPythonClass)
|
||||
map <buffer> [] <Plug>(PythonsenseEndOfPreviousPythonClass)
|
||||
map <buffer> ]m <Plug>(PythonsenseStartOfNextPythonFunction)
|
||||
map <buffer> ]M <Plug>(PythonsenseEndOfPythonFunction)
|
||||
map <buffer> [m <Plug>(PythonsenseStartOfPythonFunction)
|
||||
map <buffer> [M <Plug>(PythonsenseEndOfPreviousPythonFunction)
|
||||
|
||||
map <buffer> g: <Plug>(PythonsensePyWhere)
|
||||
<
|
||||
|
||||
You do not need to specify all the key mappings if you just want to
|
||||
customize a few. Simply provide your own key mapping to each of the
|
||||
"<Plug>" mappings you want to override, and these will be respected, while
|
||||
any "<Plug>" maps that are not so explicitly bound will be assigned to the
|
||||
default key maps.
|
||||
|
||||
vim:tw=78:ts=8:ft=help:norl:
|
||||
|
140
bundle/vim-pythonsense/ftplugin/python/pythonsense.vim
vendored
Normal file
140
bundle/vim-pythonsense/ftplugin/python/pythonsense.vim
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
" File: ftplugin/python/pythonsense.vim
|
||||
" Author: Jeet Sukumaran
|
||||
"
|
||||
" Copyright: (C) 2018 Jeet Sukumaran
|
||||
"
|
||||
" License: 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.
|
||||
"
|
||||
" Credits: - pythontextobj.vim by Nat Williams (https://github.com/natw/vim-pythontextobj)
|
||||
" - chapa.vim by Alfredo Deza (https://github.com/alfredodeza/chapa.vim)
|
||||
" - indentobj by Austin Taylor's (https://github.com/austintaylor/vim-indentobject)
|
||||
" - Python Docstring Text Objects by gfixler (https://pastebin.com/u/gfixler)
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
" Book-Keeping Variables {{{1
|
||||
let b:pythonsense_is_tab_indented = 0
|
||||
" 1}}}
|
||||
|
||||
" Commands {{{1
|
||||
command! Pywhere :call pythonsense#echo_python_location()
|
||||
" }}}1
|
||||
"
|
||||
" Plug Definitions {{{1
|
||||
|
||||
" Text Objects {{{2
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseOuterFunctionTextObject) :<C-u>call pythonsense#python_function_text_object(0, "o")<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseInnerFunctionTextObject) :<C-u>call pythonsense#python_function_text_object(1, "o")<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseOuterClassTextObject) :<C-u>call pythonsense#python_class_text_object(0, "o")<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseInnerClassTextObject) :<C-u>call pythonsense#python_class_text_object(1, "o")<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseOuterDocStringTextObject) :<C-u>call pythonsense#python_docstring_text_object(0)<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseInnerDocStringTextObject) :<C-u>call pythonsense#python_docstring_text_object(1)<CR>
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseOuterFunctionTextObject) :<C-u>call pythonsense#python_function_text_object(0, "v")<CR><Esc>gv
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseInnerFunctionTextObject) :<C-u>call pythonsense#python_function_text_object(1, "v")<CR><Esc>gv
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseOuterClassTextObject) :<C-u>call pythonsense#python_class_text_object(0, "v")<CR><Esc>gv
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseInnerClassTextObject) :<C-u>call pythonsense#python_class_text_object(1, "v")<CR><Esc>gv
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseOuterDocStringTextObject) :<C-u>cal pythonsense#python_docstring_text_object(0)<CR>
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseInnerDocStringTextObject) :<C-u>cal pythonsense#python_docstring_text_object(1)<CR>
|
||||
" }}}2
|
||||
|
||||
" Motions {{{2
|
||||
|
||||
nnoremap <buffer> <silent> <Plug>(PythonsenseStartOfNextPythonClass) :<C-u>call pythonsense#move_to_python_object("class", 0, 1, "n")<CR>
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseStartOfNextPythonClass) :call pythonsense#move_to_python_object("class", 0, 1, "v")<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseStartOfNextPythonClass) V:<C-u>call pythonsense#move_to_python_object("class", 0, 1, "o")<CR>
|
||||
nnoremap <buffer> <silent> <Plug>(PythonsenseEndOfPythonClass) :<C-u>call pythonsense#move_to_python_object("class", 1, 1, "n")<CR>
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseEndOfPythonClass) :call pythonsense#move_to_python_object("class", 1, 1, "v")<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseEndOfPythonClass) V:<C-u>call pythonsense#move_to_python_object("class", 1, 1, "o")<CR>
|
||||
nnoremap <buffer> <silent> <Plug>(PythonsenseStartOfPythonClass) :<C-u>call pythonsense#move_to_python_object("class", 0, 0, "n")<CR>
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseStartOfPythonClass) :call pythonsense#move_to_python_object("class", 0, 0, "v")<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseStartOfPythonClass) V:<C-u>call pythonsense#move_to_python_object("class", 0, 0, "o")<CR>
|
||||
nnoremap <buffer> <silent> <Plug>(PythonsenseEndOfPreviousPythonClass) :<C-u>call pythonsense#move_to_python_object("class", 1, 0, "n")<CR>
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseEndOfPreviousPythonClass) :call pythonsense#move_to_python_object("class", 1, 0, "v")<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseEndOfPreviousPythonClass) V:<C-u>call pythonsense#move_to_python_object("class", 1, 0, "o")<CR>
|
||||
|
||||
nnoremap <buffer> <silent> <Plug>(PythonsenseStartOfNextPythonFunction) :<C-u>call pythonsense#move_to_python_object('\(def\\|async def\)', 0, 1, "n")<CR>
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseStartOfNextPythonFunction) :call pythonsense#move_to_python_object('\(def\\|async def\)', 0, 1, "v")<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseStartOfNextPythonFunction) V:<C-u>call pythonsense#move_to_python_object('\(def\\|async def\)', 0, 1, "o")<CR>
|
||||
nnoremap <buffer> <silent> <Plug>(PythonsenseEndOfPythonFunction) :<C-u>call pythonsense#move_to_python_object('\(def\\|async def\)', 1, 1, "n")<CR>
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseEndOfPythonFunction) :call pythonsense#move_to_python_object('\(def\\|async def\)', 1, 1, "v")<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseEndOfPythonFunction) V:<C-u>call pythonsense#move_to_python_object('\(def\\|async def\)', 1, 1, "o")<CR>
|
||||
nnoremap <buffer> <silent> <Plug>(PythonsenseStartOfPythonFunction) :<C-u>call pythonsense#move_to_python_object('\(def\\|async def\)', 0, 0, "n")<CR>
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseStartOfPythonFunction) :call pythonsense#move_to_python_object('\(def\\|async def\)', 0, 0, "v")<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseStartOfPythonFunction) V:<C-u>call pythonsense#move_to_python_object('\(def\\|async def\)', 0, 0, "o")<CR>
|
||||
nnoremap <buffer> <silent> <Plug>(PythonsenseEndOfPreviousPythonFunction) :<C-u>call pythonsense#move_to_python_object('\(def\\|async def\)', 1, 0, "n")<CR>
|
||||
vnoremap <buffer> <silent> <Plug>(PythonsenseEndOfPreviousPythonFunction) :call pythonsense#move_to_python_object('\(def\\|async def\)', 1, 0, "v")<CR>
|
||||
onoremap <buffer> <silent> <Plug>(PythonsenseEndOfPreviousPythonFunction) V:<C-u>call pythonsense#move_to_python_object('\(def\\|async def\)', 1, 0, "o")<CR>
|
||||
" }}}2
|
||||
|
||||
" Information {{{2
|
||||
nnoremap <buffer> <silent> <Plug>(PythonsensePyWhere) :Pywhere<CR>
|
||||
" }}}2
|
||||
|
||||
" }}}1
|
||||
|
||||
" Plug Binding Key Maps {{{1
|
||||
|
||||
if ! get(g:, "is_pythonsense_suppress_keymaps", 0) && ! get(g:, "is_pythonsense_suppress_object_keymaps", 0)
|
||||
|
||||
if !hasmapto('<Plug>PythonsenseOuterClassTextObject')
|
||||
vmap <buffer> ac <Plug>(PythonsenseOuterClassTextObject)
|
||||
omap <buffer> ac <Plug>(PythonsenseOuterClassTextObject)
|
||||
sunmap <buffer> ac
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseInnerClassTextObject')
|
||||
vmap <buffer> ic <Plug>(PythonsenseInnerClassTextObject)
|
||||
omap <buffer> ic <Plug>(PythonsenseInnerClassTextObject)
|
||||
sunmap <buffer> ic
|
||||
endif
|
||||
|
||||
if !hasmapto('<Plug>PythonsenseOuterFunctionTextObject')
|
||||
vmap <buffer> af <Plug>(PythonsenseOuterFunctionTextObject)
|
||||
omap <buffer> af <Plug>(PythonsenseOuterFunctionTextObject)
|
||||
sunmap <buffer> af
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseInnerFunctionTextObject')
|
||||
vmap <buffer> if <Plug>(PythonsenseInnerFunctionTextObject)
|
||||
omap <buffer> if <Plug>(PythonsenseInnerFunctionTextObject)
|
||||
sunmap <buffer> if
|
||||
endif
|
||||
|
||||
if !hasmapto('<Plug>PythonsenseOuterDocStringTextObject')
|
||||
omap <buffer> ad <Plug>(PythonsenseOuterDocStringTextObject)
|
||||
vmap <buffer> ad <Plug>(PythonsenseOuterDocStringTextObject)
|
||||
sunmap <buffer> ad
|
||||
endif
|
||||
if !hasmapto('<Plug>PythonsenseInnerDocStringTextObject')
|
||||
omap <buffer> id <Plug>(PythonsenseInnerDocStringTextObject)
|
||||
vmap <buffer> id <Plug>(PythonsenseInnerDocStringTextObject)
|
||||
sunmap <buffer> id
|
||||
endif
|
||||
endif
|
||||
|
||||
if ! get(g:, "is_pythonsense_suppress_keymaps", 0) && ! get(g:, "is_pythonsense_suppress_location_keymaps", 0)
|
||||
|
||||
if !hasmapto('<Plug>(PythonsensePyWhere)')
|
||||
map g: <Plug>(PythonsensePyWhere)
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
" }}}1
|
||||
|
Loading…
x
Reference in New Issue
Block a user