This is done by removing custom FileType indent options, and using
`vim-python-pep8-indent` plugin.
pull/547/head
Amir Salihefendic 3 years ago
parent 97e3db7fe9
commit 966965a156
  1. 2
      README.md
  2. 37
      sources_non_forked/vim-python-pep8-indent/.circleci/config.yml
  3. 7
      sources_non_forked/vim-python-pep8-indent/.coveragerc
  4. 2
      sources_non_forked/vim-python-pep8-indent/.dockerignore
  5. 3
      sources_non_forked/vim-python-pep8-indent/.gitignore
  6. 37
      sources_non_forked/vim-python-pep8-indent/CONTRIBUTING.rst
  7. 121
      sources_non_forked/vim-python-pep8-indent/COPYING.txt
  8. 24
      sources_non_forked/vim-python-pep8-indent/Dockerfile
  9. 3
      sources_non_forked/vim-python-pep8-indent/Gemfile
  10. 25
      sources_non_forked/vim-python-pep8-indent/Makefile
  11. 169
      sources_non_forked/vim-python-pep8-indent/README.rst
  12. 6
      sources_non_forked/vim-python-pep8-indent/docker-compose.yml
  13. 1
      sources_non_forked/vim-python-pep8-indent/indent/cython.vim
  14. 454
      sources_non_forked/vim-python-pep8-indent/indent/python.vim
  15. 36
      sources_non_forked/vim-python-pep8-indent/spec/indent/bytes_spec.rb
  16. 36
      sources_non_forked/vim-python-pep8-indent/spec/indent/cython_spec.rb
  17. 776
      sources_non_forked/vim-python-pep8-indent/spec/indent/indent_spec.rb
  18. 24
      sources_non_forked/vim-python-pep8-indent/spec/make-coverage
  19. 70
      sources_non_forked/vim-python-pep8-indent/spec/spec_helper.rb
  20. 22
      sources_non_forked/vim-python-pep8-indent/spec/vimrc
  21. 3
      update_plugins.py
  22. 3
      vimrcs/filetypes.vim

@ -126,9 +126,11 @@ I recommend reading the docs of these plugins to understand them better. Each pl
* [vim-markdown](https://github.com/tpope/vim-markdown)
* [nginx.vim](https://github.com/vim-scripts/nginx.vim): Highlights configuration files for nginx
* [vim-go](https://github.com/fatih/vim-go)
* [rust.vim](https://github.com/rust-lang/rust.vim)
* [vim-ruby](https://github.com/vim-ruby/vim-ruby)
* [typescript-vim](https://github.com/leafgarland/typescript-vim)
* [vim-javascript](https://github.com/pangloss/vim-javascript)
* [vim-python-pep8-indent](https://github.com/Vimjas/vim-python-pep8-indent)
## How to include your own stuff?

@ -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

@ -0,0 +1,7 @@
[run]
plugins = covimerage
data_file = .coverage_covimerage
source = indent/python.vim
[report]
include = indent/python.vim

@ -0,0 +1,3 @@
.*.swp
.coverage_covimerage
Gemfile.lock

@ -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

@ -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.

@ -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"]

@ -0,0 +1,3 @@
source 'https://rubygems.org'
gem "vimrunner", "0.3.4"
gem "rspec"

@ -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

@ -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

@ -0,0 +1,6 @@
version: '2'
services:
rspec:
build: .
volumes:
- .:/vim-python-pep8-indent

@ -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 lnum = a:lnum
let last_indent = indent(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 - 1, 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

@ -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

@ -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

@ -0,0 +1,776 @@
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)