In Vim, it's a quick 3-character command to change what's inside the current quoted string (e.g., ci"), but is there a simple way to change what type of quotes are currently surrounding the cursor?
Sometimes I need to go from "blah" to """blah""" or "blah" to 'blah' (in Python source code) and I'd ideally like to do it quickly using default key bindings.
Try the surround.vim plugin. I find it an essential addition to any vim installation.
Surround.vim is great, but I don't think it'll handle your triple-quoted needs directly.
The way I've done stuff along these lines (when surround wasn't appropriate) was to use %, make the change, then double-backtick to go back to the starting point. E.g. if the cursor is somewhere in a single-quoted string, do f'%, make the change, then double-backtick and ..
Related
I want to be able to control vim/neovim on a per-key basis with python scripting. There is a function called feedkeys in the python vim module (vim.feedkeys) that is nearly what I want. However, I haven't been able to figure out how to send things like function keys, arrow keys, pgup, pgdown etc as it always takes my strings completely literally.
As per the documentation for vim's feedkeys (vimscript version, not python)
feedkeys("\<CR>") simulates pressing of the <Enter> key. But feedkeys('\<CR>') pushes 5 characters.
Things I've tried with the python counterpart that haven't worked (note, <CR> is just an example; I know I can use \n for that. Nonetheless, this should simulate an enter keypress):
vim.feedkeys("\<CR>")
vim.feedkeys("<CR>")
vim.feedkeys("\<CR\>")
vim.call("feedkeys", "\<CR>")
vim.call("feedkeys", '"\<CR>"')
All of these were interpreted literally. I want to do something like
vim.feedkeys("\<F5>") etc. Any ideas?
This isn't ideal, but it solves my issue well enough:
vim.command('call feedkeys("\<F5>")')
In case this is useful to anyone, I've written a general function that will handle the \<> escapes as well as double-quotes:
def fkeys(text):
firstsub = True
for sub in text.split('"'):
if firstsub:
firstsub = False
else:
vim.feedkeys('"')
vim.command(f'call feedkeys("{sub}")')
Sorry for being Captain Obvious, but it doesn't work, because Python is not VimScript.
See :h nvim_replace_termcodes() and :h nvim_feedkeys() for a complete example. In case of <CR>, simply byte value of 13 will do.
You don't, because the interpretation of something like "\<CR>" is a function of VimL string literals. In other words, feedkeys("\<CR>") is the same thing as (probably) feedkeys("\x0d") — the function doesn't see the difference, the interpretation happens at a source code level. Naturally, Python doesn't have the same feature in the same way.
If you don't want to figure out what the escape sequence is for F5 and code it into your Python script, perhaps you could use vim.eval() to eval a VimL expression, e.g. vim.eval(r'feedkeys("\<F5>")').
I have a long source code in python, and many functions are documented with '''xxx''', How do I replace them with """xxx""" quickly? in any text editor ? pycharm / sublime
def my_func():
'''
yeah a doc string
'''
pass
desired result:
def my_func():
"""
yeah a doc string
"""
pass
edit:
Found the solution in pycharm ( or every other text editor)
search for
'''(\n.*\n\s*)'''\n
and replace with
"""$1"""\n
ps: I can not just do a simple search for ''',and replace it with """, because in the code multi line strings are everywhere, not just in the docstring.
You can achieve it by three methods,
Method 1: Inspect whole project
Go to Code > Inspect Code. PyCharm will scan your code (this may take some time) and will show you the inspection results.
In the inspection results, under Python It will show Single Quoted docstrings
To change all the files at once
Select Single quoted docstring from the left pane and click Convert docstring to the triple double quoted string form. Now all the files will be changed to triple double quotes.
To change for individual files
Select individual files under Single quoted docstring and for each file click Convert docstring to the triple double quoted string form.
Method 2: Replace Code with Replace in Path
In PyCharm, right click on the project and select Replace in Path or Ctrl+Shift+R.
In the Text to find field, enter ''' and in Replace with field, enter """. click find. PyCharm will scan through the files and ask you whether to replace single occurrence, or all occurences at once.
Method 3: Use terminal tools or Python to read lines and replace.
As others mentioned, this method will do the job.
I recommend method 1 Since PyCharm is intelligent, it can change doctrings without changing multi-line strings (if you are using it) which is prone to replacement in other methods.
In vi:
:%s/'''/"""/g *<return>*
python -c "import sys;with open(sys.argv[1],'rb') as f: tmp = f.read();with open(sys.argv[1],'wb') as f:f.write(tmp.replace(\"'''\",'\"\"\"')"
Turning my comment into an asnwer. Regular expressions are your friend if you have lots of files. From a GNU/Linux terminal, you can write:
find path/to/dir -name '*.py' -exec perl -p -i -e \'s/'''/"""/g\' {} \;
I'm writing a python plugin for vim and it's looking like the only way to call a specific command is with the vim.command function. However, just substituting values into the function seems like a bad idea. How would I escape values so that I can pass untrusted data as an argument into a vim function? As a simple example, let's say I want to echo out untrusted input (I know I could just use print, but this is just an example). I would do something like:
value = get_data_from_untrusted_source()
vim.command("echo %s" % value)
However, if that untrusted data has a | in it, the command is ended and a new one is executed which is bad. Even if I use quotes, we end up with sql injection like attacks where an attacker can just put an apostrophe in their response to end the string. Then if we double quote, it could be possible to put a backslash somewhere to end the quote. For example if we just double quotes we would go from \' to \'' which escapes the first quote.
Basically what I'm asking is if there's a safe way to call vim functions from a python plugin and would appreciate any help.
So i was thinking that in order to implement such a feature in a console application , where appending a question mark at the end of a function name will pour out it's doc string , i would have probably used a feature like metaclasses , where upon definition/import , i'd duplicate all the module member names and produce new ones just for typing out doc strings.
Then i noticed that you don't need actual parenthesis to call the helper functions and python doesn't actually allow you to put a question mark at the end of the function name anyway.... So is this done in python or am i just wasting my time trying to figure this out?
Thanks in advance
It's not done the way you're imagining. ipython reads your command prompt input as a line of text, so it has a chance to check if it ends with a question mark before it passes it on to eval (or whatever). If it does, it runs help(name) instead of what you typed.
AST looks a little heavy-duty, but you can get a feel for how this works by checking out the module code. It gives you a lightweight interpreter that you can extend with syntax of this sort if you want.
Have a look at the IPython.core.inputsplitter module for the code that parses the raw input line for things like ?, !, /, %, etc.
ipython uses AST, you can customize the syntax parsing and create a new ipython fork.
may help
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!