Reformat a Python file to have 4 space indentations - python

I'm editing a Python file that uses two spaces for programmatic indents - I prefer 4 spaces. In my .vimrc I have the following settings related to indentation:
set tabstop=4 "Indentation levels every four columns
set expandtab "Convert all tabs typed to spaces
set shiftwidth=4 "Indent/outdent by four columns
set softtabstop=4
How do I get Vim to convert all the existing 2 space indents to be 4 space indents?
In other words:
if something:
dothis()
becomes
if something:
dothis()
When I tried gg=G
def check():
for a in list:
for b in list2:
check(a, b)
while (len > MAX) :
poll()
while(len(thelist) > 0) :
poll()
return results
became
def check():
for a in list:
for b in list2:
check(a, b)
while (len > MAX) :
poll()
while(len(thelist) > 0) :
poll()
return results

In order to double the number of spaces at the beginning of every line (and only at the beginning):
:%s/^\s*/&&/g
& in replacement pattern is the matched pattern.
Probably it will not have any side-effect for you.

Pressing gg=G is the command to re-indent everything in a file. If you have other elements that can be re-indented, vim will indent these as well, which doesn't always give the desired effects. You'll have to clean these up manually if they're ugly.
Alternately, you can use the > command to indent, with ranges to go through the file somewhat efficiently manually. 99>k, for example, would indent the 99 lines below the cursor by one level.

I've found the reindent script http://pypi.python.org/pypi/Reindent/0.1.0 works well for me. Not pure vim, but really easy!
After its installed you can use it in vim with
:%! reindent
(ie pipe the entire buffer through the reindent program) and it's done.
From the command line it can be used to reindent multiple files (eg all files in a directory, or even recursively down a directory tree).

The best current way to reformat Python, fix many other issues, and also make it PEP8 compliant is to use autopep8. See this related question. So after you've installed autopep8 (e.g. pip install autopep8) in vim you do:
:%! autopep8 -
There's also a vim-autopep8 plugin to make things even simpler.

try the following substitution command:
:%s/ / /g
(To clarify: there are two spaces between the first and second '/' and four the second and third '/'.)
One helpful command when working with whitespace issues is also the
set list
command which will visually show all whitespace. Use
set nolist to unset.

The vim plugin vim-autoformat integrates the formatter autopep8 into vim automatically, if it is installed. You can format the whole file, or the visually selected part using a single keystroke.
More importantly, vim-autoformat takes the relevant settings of your .vimrc into account, e.g. if you have
set shiftwidth=4
in your .vimrc, it will pass this information on to autopep8.

Have you tried?
:retab
I'm not in front of a machine with Vim at the moment so I can't verify this.

Related

How to make Tab insert a set number of Spaces in Emacs

I want to not use tabs in emacs, I would like to set it so that pressing the tab key inserts a set number of spaces based on the file type. For example, I would like pressing tab to insert 4 spaces when working on a python file. Here is my current .emacs file
(setq-default indent-tabs-mode nil)
(setq tab-width 4)
(setq tab-stop-list (number-sequence 4 200 4))
Currently, whenever I press tab, it only adds any spaces if it is what emacs deems a proper spot. For example, if i opened a python file and pressed tab, nothing would happen. However, if i typed "if:" and then hit tab on the next line, it would add 4 spaces. However, if i pressed tab again, it takes me back to the beginning of the line. I think I would just like to make it so that it adds a set number of spaces, plain and simple(unless someone can give me a good reason for why it's useful to have tab cycle through the line).
The main reason I want this is because I am working on a project where the commenting following a specific format involving the use of indentation based on sets of 4 spaces.
For example:
r"""
Return the value of the q-gamma function.
.. MATH::
qgamma(z,q) = \Gamma_q (z).
"""
So, for the line with "qgamma", I have to start it with 8 spaces, and I would like to be able to od that by pressing tab twice. However, emacs doesn't think this is a proper place to be able to tab twice, so whenever I press tab twice, it takes me back to the beginning of the line.
Thanks!
Edit: checking the major mode variable says that it is on python-mode.
If you're using the built in python.el, you can set indent-line-function to indent-relative instead of python-indent-line-function in python-mode-hook.
(defun jpk/python-mode-hook ()
(setq indent-line-function #'indent-relative))
(add-hook 'python-mode-hook #'jpk/python-mode-hook)
Each major mode has its own indentation function, pointed to by the indent-line-function variable. indent-relative is a very basic, generic indentation function, and is used for fundamental-mode (the most basic major mode).
Python is a bit weird in Emacs in that it has two widely used major modes, the built in python.el and third-party python-mode.el. Use whichever you want, but be aware of which you're using and which people are referring to in articles and SO answers.
M-x customize, search for py-tab-indent, toggle it off and Apply. I believe that's the behavior that you're looking for. Test it out and if so, Apply and Save the customization.
Noting, as #jpkotta pointed out, that the above is for python-mode.el, not the built-in python.el.

Python, VIM: How do i count the lines in a VIM register?

The Python, VIM bindings give me ways of manipulating: windows, buffers and ranges but absolutely no way of messing with registers.
vim.command('let #".="%s"' % vim.current.buffer[start - 1])
I can append lines to the default cut&paste register, but is there a way to count the lines in it, so i can paste. I need to make space before pasting or the lines get merged.
In python you can get the value of a vim register by vim.eval("#a") for example.
If you want to count the lines (\n) in a register you can do it either in vim or python.
E.g. in vim:
len(split(#a, '\n'))
in python
#var is the variable containing #a in vim
len(re.split("\n",var))

Python indentation borked

I saw that there are similar titles to this. But my case seems a little weirder. I somehow used a mixture of PyCharm and Vim (and within Vim I have tabstop=4 and shiftwidth=2), and my Python code seems un-fixabl-y borked, indentation-wise. I first saw that in Vim everything was mis-aligned, so I re-aligned everything; but then when I run it I get an error that there's an unexpected indentation, even though in Vim everything seems perfectly aligned. Here's an example (this is how it looks like in Vim):
for f in files:
for line in f:
items = line.strip().split()
items = items[2:]
items = ' '.join(items).split(', ')
When I run it, I get:
File "getEsSynonymLSAVectors.py", line 136
items = items[2:]
^
IndentationError: unexpected indent
I used PythonTidy, I used reindent, I tried :retab, I tried manual re-aligning - nothing seems to fix this. Any experiences/ advice will be appreciated.
Python treated a tab as 8 spaces by default, if you get indentation borked, you'll generally want to switch the tabs to spaces (or vice versa, but I generally find that spaces are easier to deal with). So make sure to set vim to show tab as 8 spaces wide (:set ts=8), to see what python sees.
To fix tab errors in vim, I usually do the following, first I need to be able to see the tabs, so I enabled highlight search (:set hlsearch) and search for tabs (/\t). Then I eyeball the areas that needs to be retabbed. Next, I try to find the right vim tab width setting for the file (:set ts=n and vary n until everything looks good), enable expand tab (:set et), then run the automatic tab fixing (:retab). When all else fail, retab manually.
If you're using version control, make sure to diff with the files before the changes and manually check that you didn't introduce a bug because of unintentional changes in the indentation level. If you don't use version control, keep a backup and run diff on the files.
Try something like this.
First set appropriate settings.
Always use 4 spaces. So change it to tabs = 4 spaces.
First convert all spaces to tabs.
And then convert all tabs to spaces.
(I use Geany)
It has worked for me before many times.

How to fold long docstrings in python source code in VIM?

Does anybody know of a method, or perhaps a plugin, that will
automatically fold long docstrings in Python? I have docstrings in my
code that span several pages, so it is troublesome to keep paging
through them. The other tricky part is that there is embedded python
testing code in the docstrings, so that might make parsing them
difficult. Note that I only need to automatically fold the entire
docstring, regardless of what is in it.
This is a bit of a dirty hack, but you can go through the python syntax file (:sp $VIMRUNTIME/syntax/python.vim) and find all the syntax regions for triple-quoted strings (search for ''' and """) and add the fold keyword to the end of those statements. Then just set foldmethod=syntax for python files and the comments should be folded.
I'm not sure about a plugin or automation, but if you type zf/ you can then search for something and it will fold up to the next instance of it. So in a document like the following (where [] is the cursor):
def foo():
"""[]
Some long docstring
that takes up many
lines
"""
pass
Look at edit2 first for the updated search string!
If you use the command zf/"""[ENTER], it should fold everything from the current line (the beginning of the docstring) to the next occurrence of """ which should be the end of the docstring.
I know this isn't automation, but perhaps it will help in the interim, or lead you down the right path to automating it. See edit2 for a better search function, although I still don't know how to automate.
Hope this helps.
Edit: in a corollary, you can search for any docstring with /"""\_.\{-}""", although this will also return the code within the docstring. To search for a function definition followed by a docstring, you can use /def\_.\{-}"""\_.\{-}""", although this breaks on a def inside the docstring.
Edit2: Actually, some more playing with regexs led me to this: /def.\{-}):\_s*"""\_.\{-}""" which should find any function followed by a docstring. It searches for def followed by any characters, then ): followed by a newline and/or whitespace followed by """ followed by any number of lines than the next """, but always ensures the 2nd triple quote is the one immediately following the first.
In your .vimrc add:
" folding
set foldmethod=indent
This will auto-fold at every indentation, which, in python, translates to docstrings. It works VERY VERY nice. Give it a try. The above answer is correct, but requires a bunch of keystrokes (blah!)
I think I've found a slight improvement to the answer of #too_much_php by trying to figure out how to do the same thing without root access. Copying the syntax region definitions from $VIMRUNTIME/syntax/python.vim to ~/.vim/after/syntax/python.vim and editing them solved the problem for me. Here's my code for ~/.vim/after/syntax/python.vim:
syn region docString1
\ start=+[uU]\=\z('''\|"""\)+ end="\z1" keepend transparent fold
syn region docString2
\ start=+[uU]\=[rR]\z('''\|"""\)+ end="\z1" keepend transparent fold
Now I can run :set foldmethod=syntax to fold docstrings.
PS: Credits to #too_much_php for the initial idea
You can do this with :set foldmethod=marker foldmarker=""",""", I think. I haven't tested it, but that should do the trick. The arguments to foldmarker are start and end markers.
I wrote a vimscript plugin many years ago to do exactly this, but never got around to publishing it. I've put it up as a gist, but have also pasted the source below. I've also replicated its functionality for Sublime Text here.
"
" Fold multi-line Python comments into one line.
"
" Also maps the "-" key to toggle expansion and <C-f> to toggle all folding.
"
setlocal foldmethod=syntax
setlocal foldtext=FoldText()
setlocal fillchars=
map <buffer> - za
map <buffer> <C-f> :call ToggleFold()<CR>
let b:folded = 1
hi Folded gui=bold cterm=bold guifg=cyan ctermfg=cyan guibg=NONE ctermbg=NONE
function! ToggleFold()
if b:folded == 0
exec "normal! zM"
let b:folded = 1
else
exec "normal! zR"
let b:folded = 0
endif
endfunction
function! s:Strip(string)
return substitute(a:string, '^[[:space:][:return:][:cntrl:]]\+\|[[:space:][:return:][:cntrl:]]\+$', '', '')
endfunction
" Extract the first line of a multi-line comment to use as the fold snippet
function! FoldText()
let l:snippet = getline(v:foldstart)
if len(s:Strip(l:snippet)) == 3
let l:snippet = strpart(l:snippet, 1) . s:Strip(getline(v:foldstart + 1))
endif
return '+' . l:snippet . ' ...'
endfunction
My solution involves using SimpylFold. After installing it using Vundle, I put this in my vimrc file:
autocmd FileType python setlocal foldlevel=2
Which makes all docstrings folded by default when you open a python file, which is super awesome.

Reverse a word in Vim

How can I reverse a word in Vim? Preferably with a regex or normal-mode commands, but other methods are welcome too:
word => drow
Thanks for your help!
PS: I'm in windows XP
Python is built in supported in my vim, but not Perl.
Here is another (pythonic) solution based on how this works:
:echo join(reverse(split('hello', '.\zs')), '')
olleh
If you want to replace all words in the buffer,
:%s/\(\<.\{-}\>\)/\=join(reverse(split(submatch(1), '.\zs')), '')/g
This works by first creating a list of characters in the word, which is reversed and joined back to form the word. The substitute command finds each word and then passes the word to the expressions and uses the result as replacement.
This Tip might help: http://vim.wikia.com/wiki/Reverse_letters
It says:
Simply enable visual mode (v), highlight the characters you want inverted, and hit \is. For a single word you can use vw (or viw): viw\is
vnoremap <silent> <Leader>is :<C-U>let old_reg_a=#a<CR>
\:let old_reg=#"<CR>
\gv"ay
\:let #a=substitute(#a, '.\(.*\)\#=',
\ '\=#a[strlen(submatch(1))]', 'g')<CR>
\gvc<C-R>a<Esc>
\:let #a=old_reg_a<CR>
\:let #"=old_reg<CR>
There are more solutions in the comments.
Assuming you've got perl support built in to vim, you can do this:
command! ReverseWord call ReverseWord()
function! ReverseWord()
perl << EOF
$curword = VIM::Eval('expand("<cword>")');
$reversed = reverse($curword);
VIM::Msg("$curword => $reversed");
VIM::DoCommand("norm lbcw$reversed");
EOF
endfun
And potentially bind that to a keystroke like so:
nmap ,r :ReverseWord<CR>
I don't have Python supported on my VIM, but it looks like it would be pretty simple to do it with Python. This article seems like a good explanation of how to use Python in VIM and I'm guessing you'd do something like this:
:python 'word'[::-1]
The article indicates that the result will appear in the status bar, which would be non-optimal if you were trying to replace the string in a document, but if you just want to check that your girlfriend is properly reversing strings in her head, this should be fine.
If you have rev installed (e.g. via MSys or Cygwin) then it's really not this difficult.
Select what you want to reverse and filter (%! <cmd>) it:
:%! rev
This pipes your selection to your shell while passing it a command.
if your version of VIM supports it you can do vw\is or viw\is (put your cursor at the first letter of the word before typing the command)... but I have had a lot of compatibility issues with that. Not sure what has to be compiled in or turned on but this only works sometimes.
EDIT:
\is is:
:<C-U>let old_reg_a=#a<CR>
\ :let old_reg=#"<CR>
\ gv"ay :let #a=substitute(#a, '.\(.*\)\#=', '\=#a[strlen(submatch(1))]', 'g')<CR>
\ gvc<C-R>a<Esc> :let #a=old_reg_a<CR>
\ :let #"=old_reg<CR>
Didn't remember where it came from but a google search come this article on vim.wikia.com. Which shows the same thing so I guess that's it.
Well you could use python itself to reverse the line through the filter command. Say the text you had written was:
Python
You could reverse it by issuing.
:1 ! python -c "print raw_input()[::-1]"
And your text will be replaced to become:
nohtyP
The "1" in the command tells vi to send line 1 to the python statement which we are executing: "print raw_input()[::-1]". So if you wanted some other line reversed, you would send that line number as argument. The python statement then reverses the line of input.
There is a tricky way to do this if you have Vim compiled with +rightleft. You set 'allowrevins' which let you hit Ctrl+_ in insert mode to start Reverse Insert mode. It was originally made for inserting bidirectional scripts.
Type your desired word in Insert mode, or move your cursor to the end of an already typed word. Hit Ctrl+_ and then pick a completion (i_Ctrl-x) method which is the most likely not to return any results for your word. Ysing Ctrl+e to cancel in-place completion does not seem to work in this case.
I.e. for an unsyntactic text file you can hit in insert mode Ctrl+x Ctrl+d which is guaranteed to fail to find any macro/function names in the current file (See :h i_CTRL-X_CTRL-D and:h complete for more information).
And voila! Completion lookup in reverse mode makes the looked up word reverse. Notice that the cursor will move to the beginning of that word (it's reversed direction of writing, remember?)
You should then hit Ctrl+_ again to get back to regular insert mode and keyboard layout and go on with editing.
Tip: You can set 'complete' exclusively (for the buffer, at least) to a completion option that is guaranteed to return no result. Just go over the options in :h 'complete'. This will make the easy i_Ctrl-N / i_Ctrl-P bindings available for a handy word reversal session. You can ofcourse further automate this with a macro, a function or a binding
Note: Setting/resetting 'paste' and 'compatible' can set/reset 'allowrevins'. See :h allowrevins.
If you have some time on your hands, you can bubble your way there by iteratively transposing characters (xp)...
I realize I'm a little late to the game, but I thought I'd just add what I think is the simplest method.
It's two things:
Vim's expression register
pyeval (py3eval on recent vim releases) function
So to reverse a word you would do the following:
"ayiw yank word into register a
<C-r>=py3eval('"".join(reversed(str(' . #a ')))') use vim's = (expression) register to call the py3eval function which evaluates python code (duh) and returns the result, which is then fed via the expression register into our document.
For more info on the expression register see https://www.brianstorti.com/vim-registers/#the-expression-and-the-search-registers
you can use revins mode in order to do it:
at the beginning type :set revins. from now on every letter you type will be inserted in a reverse order, until you type :set norevins to turn off. i.e, while revins is set, typing word will output drow.
in order to change an existing word after revins mode is set, and the cursor on beginning of the word, type:
dwi<C-r>"<ESC>
explanation:
dw deleted a word.
i to enter insert mode
<C-r>" to paste the last deleted or yaked text in insert mode, <ESC> to exit insert mode.
remember to :set norevins at the end!

Categories