mirror of https://github.com/amix/vimrc.git
This is done by removing custom FileType indent options, and using `vim-python-pep8-indent` plugin.pull/547/head
parent
97e3db7fe9
commit
966965a156
@ -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,2 @@ |
||||
* |
||||
!Gemfile |
@ -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 @@ |
||||
python.vim |
@ -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) |