VIM command to insert multiline text with argument - python

new VIM user. I'm trying to make creating python properties easier for my class definitions. What I would like for say I type
:pyp x
then VIM will autofill where my cursor is
#property
def x(self):
return self.x
#property.setter
def x(self,val):
self._x = val
or more abstractly I type
:pyp <property_name>
and VIM fills
#property
def <property_name>(self):
return self.<property_name>
#property.setter
def <property_name>(self,val):
self._<property_name> = val
I've looked at a few posts and the wikis on functions, macros but I'm very unsure of how to go about it or what to even look up as I am brand new VIM user, less than a week old.
I tried using [this][1] as an example, in my .vimrc but I couldn't even get that to work.
Edit:
So the code I am currently trying is
function! PythonProperty(prop_name)
let cur_line = line('.')
let num_spaces = indent('.')
let spaces = repeat(' ',num_spaces)
let lines = [ spaces."#property",
\ spaces."def ".prop_name."(self):",
\ spaces." return self.".property,
\ spaces."#property.setter",
\ spaces."def".prop_name."(self,val)",
\ spaces." self._".prop_name." = val" ]
call append(cur_line,lines)
endfunction
and I am getting the errors
E121: Undefined variable: prop_name
I am typing
`:call PythonProperty("x")`
[1]: https://vi.stackexchange.com/questions/9644/how-to-use-a-variable-in-the-expression-of-a-normal-command

E121: Undefined variable: prop_name
In VimScript variables have scopes. The scope for function arguments is a:, while the default inside a function is l: (local variable). So the error means that l:prop_name was not yet defined.
Now how I do this:
function! s:insert_pyp(property)
let l:indent = repeat(' ', indent('.'))
let l:text = [
\ '#property',
\ 'def <TMPL>(self):',
\ ' return self.<TMPL>',
\ '#property.setter',
\ ' def <TMPL>(self,val):',
\ ' self._<TMPL> = val'
\ ]
call map(l:text, {k, v -> l:indent . substitute(v, '\C<TMPL>', a:property, 'g')})
call append('.', l:text)
endfunction
command! -nargs=1 Pyp :call <SID>insert_pyp(<q-args>)
Alternatively, we can simulate actual key presses (note that we don't need to put indents in the template anymore; hopefully, the current buffer has set ft=python):
function! s:insert_pyp2(property)
let l:text = [
\ '#property',
\ 'def <TMPL>(self):',
\ 'return self.<TMPL>',
\ '#property.setter',
\ 'def <TMPL>(self,val):',
\ 'self._<TMPL> = val'
\ ]
execute "normal! o" . substitute(join(l:text, "\n"), '\C<TMPL>', a:property, 'g') . "\<Esc>"
endfunction
command! -nargs=1 Pyp2 :call <SID>insert_pyp2(<q-args>)
its very very difficult if not impossible to get pluggins
I suggest you to watch this video on youtube. In fact, many of Vim plugins are just overkill.

Related

Behave - Testing using blank Example fields

I am using Behave to automate the testing of a config file, as part of this test I need to populate various fields in the config file with invalid and blank fields. Where I am entering values I can do this using a Scenario Outline entering the values in the Examples. However when I try entering a blank field using this method Behave does not like the fact there is no value.
Is there an easy way to pass a blank value from the Examples file, or will I need to test these conditions using a separate behave test
feature
Scenario Outline:Misconfigured Identity Listener
Given an already stopped Identity Listener
And parameter <parameter> is configured to value <config_value>
When the Identity Listener is started
Then the identity listener process is not present on the system
And the log contains a <message> showing that the parameter is not configured
Examples: Protocols
|parameter |message |config_value|
|cache_ip_address | cache_ip_address | |
|cache_ip_address | cache_ip_address | 123.123.12 |
the step where I define the config value
#given('parameter {parameter} is configured to value {config_value}')
def step_impl(context, parameter, config_value):
context.parameter = parameter
context.config_value = config_value
context.identity_listener.update_config(parameter, config_value)
changing the config file using sed -i (I am interacting with a linux box in this test)
def update_config(self, param, config_value):
command = 'sudo sh -c "sed -i'
command = command + " '/" + param + "/c\\" + param + "= "+ config_value + " \\' {0}\""
command = command.format(self.config_file)
self.il_ssh.runcmd(command)
Thanks to answer from #Verv i got this working solution below
passed an empty value in for fields where I don't want a value passed
|parameter |message |config_value|
|cache_ip_address | cache_ip_address | empty |
Added an if else statement into my update config step
def update_config(self, param, config_value):
if config_value == "empty":
il_config = ""
else:
il_config = config_value
command = 'sudo sh -c "sed -i'
command = command + " '/" + param + "/c\\" + param + "= " + il_config + " \\' {0}\""
command = command.format(self.config_file)
self.il_ssh.runcmd(command)
You could put something like empty in the field, and tweak your method so that whenever the field's value is empty, you treat it as an actual empty string (i.e. "")

Python pandas apply function defined in class

I am currently using Spyder IDE. I am trying to apply a self-compiled function sfill to my dataframe based on the different groups. Whenever I create this function, I get the following error:
TypeError: sfill() takes exactly 1 argument (2 given)
I am certain the function sfill() works correctly, when run standalone. What am I doing wrong?
How do I allow Python to show the dataframe want without using statement global want in the variable explorer? How do I use return in this instant?
My code is as follows:
import pandas as pd
have = pd.DataFrame({ \
"groups": pd.Series(["group1","group1","group1","group2","group2","group2"]) \
,"a0": pd.Series(["abc","1","something here","abc2","1","something here"]) \
,"a1": pd.Series(["","2","something here","","","something here"]) \
,"a2": pd.Series(["","3","something here","","3","something here"]) \
,"a3": pd.Series(["something","1","something here","something","1","something here"]) \
,"a4": pd.Series(["","2","something here","","2","something here"]) \
,"a5": pd.Series(["","","something here","","","something here"]) \
,"a6": pd.Series(["","","something here","","","something here"]) \
,"a7": pd.Series(["cdf","5","something here","mnop","5","something here"]) \
,"a8": pd.Series(["","6","something here","","6","something here"]) \
,"a9": pd.Series(["xyz","1","something here","xyz","1","something here"]) \
})
class main(object):
def sfill(vector):
vector = vector.copy()
vector.i0, vector.i1 = vector.index[0], vector.index[1]
vector.cond = have.loc[vector.i1].ne('')
vector.loc[vector.i0, vector.cond] = vector.loc[vector.i0, vector.cond].str.strip().replace('', None)
return vector
def Development_plan(selfdev):
global want
want=have.groupby('groups', group_keys=False, sort=False).apply(selfdev.sfill)
a=main()
a.Development_plan() # this is where the issue exists
Thanks.
functions in a class should take self as first argument
def sfill(self, vector):
...
when you call selfdev.sfill(soemthing) it actually calls sfill(selfdev, something)
self is only a naming convention, in your Development_plan() you use selfdev for this.

Why do I get E127 from this vimscript?

I have the following vimscript .vim/ftplugin dir:
" change to header file from c file or vice versa
function! CppAlter()
python << endpy
import vim
import os
bufferNames = [os.path.basename(b.name) for b in vim.buffers]
currentBufName = vim.eval("expand('%:p:t')")
currentBufStem, currentBufExt = os.path.splitext(currentBufName)
if currentBufExt == ".cpp" or currentBufExt == ".c" or currentBufExt == ".cc":
altBufName1 = currentBufStem + ".h"
altBufName2 = currentBufStem + ".hpp"
if altBufName1 in bufferNames:
vim.command("b " + altBufName1)
elif altBufName2 in bufferNames:
vim.command("b " + altBufName2)
else:
raise ValueError("No header file corresponding to this c file")
elif currentBufExt == ".h" or currentBufExt == ".hpp":
altBufName1 = currentBufStem + ".cpp"
altBufName2 = currentBufStem + ".c"
altBufName3 = currentBufStem + ".cc"
if altBufName1 in bufferNames:
vim.command("b " + altBufName1)
elif altBufName2 in bufferNames:
vim.command("b " + altBufName2)
elif altBufName3 in bufferNames:
vim.command("b " + altBufName3)
else:
raise ValueError("No c file corresponding to this header file")
else:
raise ValueError("This is not a c type file")
endpy
endfunction
nnoremap <leader>vc :call CppAlter()<cr>
inoremap <leader>vc <esc>:call CppAlter()<cr>
When I open vim I get an error:
" vim.error: Vim(function):E127: Cannot redefine function CppAlter: It is in use
But if I save it in /tmp and explicitly :so /tmp/x.vim, there is no error msg.
Wondering what is wrong here.
Inside your function, you're loading another buffer (e.g. vim.command("b " + altBufName1)). When that buffer has the same filetype, the current ftplugin script is sourced again as part of the filetype plugin handling, but the original function hasn't returned yet, so you get the E127.
Solution
I recommend putting the function itself into an autoload script, e.g. in ~/.vim/autoload/ft/cppalter.vim:
function! ft#cppalter#CppAlter()
...
Your ftplugin script becomes much smaller and efficient, as the function is only sourced once:
nnoremap <leader>vc :call ft#cppalter#CppAlter()<cr>
...
(You should probably use :nnoremap <buffer> here to limit the mapping's scope.)
Alternative
If you don't want to break this up, move the function definition(s) to the bottom and add a guard, like:
nnoremap <leader>vc :...
if exists('*CppAlter')
finish
endif
function! CppAlter()
...
I encountered an interesting case of E127, which pretty much sums up why it would occur in almost any situation. Let me explain.
First, let's look at what the docs say.
E127 E122
When a function by this name already exists and [!] is
not used an error message is given. There is one
exception: When sourcing a script again, a function
that was previously defined in that script will be
silently replaced.
When [!] is used, an existing function is silently
replaced. **Unless it is currently being executed, that
is an error.**
For the next part notice what the last line has to say.
Let's understand this by an example. Below is a function that guesses and sources the current script based on what filetype it has. Notice how the exec command will initiate an endless recursive sourcing of the current file on calling this function.
function! s:SourceScriptImplicit()
if !&readonly
w
endif
let l:bin=system("which " . &filetype)[:-2]
let l:sourcecommand=
\ #{
\ vim: "source %",
\ sh: "!source %",
\ javascript: "!node %",
\ python: "!python3 %"
\ }
exec l:sourcecommand[split(l:bin, "/")[-1]]
endfunction
To fix this, simply remove the recursive part out of the function.
function! s:SourceScriptImplicit()
if !&readonly
w
endif
let l:bin=system("which " . &filetype)[:-2]
let l:sourcecommand=
\ #{
\ vim: "source %",
\ sh: "!source %",
\ javascript: "!node %",
\ python: "!python3 %"
\ }
return l:sourcecommand[split(l:bin, "/")[-1]]
endfunction
nn <leader>so :exec <SID>SourceScriptImplicit()<cr>
Now it works perfectly!
I think you should notice that | can be useful for execute command in the bottom command line,such as :source expand("%") | source ./awesome.vim
Here is my .init.vim(or .vimrc) snippet whose func is sourcing all .vim files in /home/zarkli/.config/nvim/myInitCustom/ directory:
function SourceVimScripts()
let l:command = ""
let l:files = split(globpath('/home/zarkli/.config/nvim/myInitCustom/','*.vim'),'\n') " use absolute path to avoid problems when opening an non-nvim_init file
for l:file in l:files
let l:command .= "source ".l:file." |"
endfor
" the end of the l:command should be '|',but that doesn't matter
return l:command
endfunction
exec SourceVimScripts()
It can perfectly deals with your problem.

IndentationError: expected an indented block

I have just started with python. I was executing a simple program given in 'Dive into Python' by Mark Pilgrim in Ubuntu. The program is as follows:
def buildConnectionString(params):
"""Build a connection string from a dictionary of parameters.
Returns string."""
return ";".join(["%s=%s" % (k, v) for k, v in params.items()])
if __name__ == "__main__":
myParams = {"server":"mpilgrim", \
"database":"master", \
"uid":"sa", \
"pwd":"secret" \
}
print buildConnectionString(myParams)
But it is showing error as follows:
File "./1.py", line 3
Returns string."""
^
IndentationError: expected an indented block
I have tried few things like giving a space in front of return on line 3, then instead of space using a tab.
Can anybody help me in finding out what the error is about, why it has came, etc. and also with some easy tutorials with which a can go ahead.
Thanks in advance..
Try it like this:
def buildConnectionString(params):
"""Build a connection string from a dictionary of parameters.
Returns string."""
return ";".join(["%s=%s" % (k, v) for k, v in params.items()])
if __name__ == "__main__":
myParams = {"server":"mpilgrim", \
"database":"master", \
"uid":"sa", \
"pwd":"secret" \
}
print buildConnectionString(myParams)
BTW: Do you understand the structure? Function, if __name__=="__main__": block etc.?
Why not read the Python documentation? It might help. ;)
http://docs.python.org/2/reference/lexical_analysis.html#indentation

Grep reliably all C #defines

I need to analyse some C files and print out all the #define found.
It's not that hard with a regexp (for example)
def with_regexp(fname):
print("{0}:".format(fname))
for line in open(fname):
match = macro_regexp.match(line)
if match is not None:
print(match.groups())
But for example it doesn't handle multiline defines for example.
There is a nice way to do it in C for example with
gcc -E -dM file.c
the problem is that it returns all the #defines, not just the one from the given file, and I don't find any option to only use the given file..
Any hint?
Thanks
EDIT:
This is a first solution to filter out the unwanted defines, simply checking that the name of the define is actually part of the original file, not perfect but seems to work nicely..
def with_gcc(fname):
cmd = "gcc -dM -E {0}".format(fname)
proc = Popen(cmd, shell=True, stdout=PIPE)
out, err = proc.communicate()
source = open(fname).read()
res = set()
for define in out.splitlines():
name = define.split(' ')[1]
if re.search(name, source):
res.add(define)
return res
Sounds like a job for a shell one-liner!
What I want to do is remove the all #includes from the C file (so we don't get junk from other files), pass that off to gcc -E -dM, then remove all the built in #defines - those start with _, and apparently linux and unix.
If you have #defines that start with an underscore this won't work exactly as promised.
It goes like this:
sed -e '/#include/d' foo.c | gcc -E -dM - | sed -e '/#define \(linux\|unix\|_\)/d'
You could probably do it in a few lines of Python too.
In PowerShell you could do something like the following:
function Get-Defines {
param([string] $Path)
"$Path`:"
switch -regex -file $Path {
'\\$' {
if ($multiline) { $_ }
}
'^\s*#define(.*)$' {
$multiline = $_.EndsWith('\');
$_
}
default {
if ($multiline) { $_ }
$multiline = $false
}
}
}
Using the following sample file
#define foo "bar"
blah
#define FOO \
do { \
do_stuff_here \
do_more_stuff \
} while (0)
blah
blah
#define X
it prints
\x.c:
#define foo "bar"
#define FOO \
do { \
do_stuff_here \
do_more_stuff \
} while (0)
#define X
Not ideal, at least how idiomatic PowerShell functions should work, but should work well enough for your needs.
Doing this in pure python I'd use a small state machine:
def getdefines(fname):
""" return a list of all define statements in the file """
lines = open(fname).read().split("\n") #read in the file as a list of lines
result = [] #the result list
current = []#a temp list that holds all lines belonging to a define
lineContinuation = False #was the last line break escaped with a '\'?
for line in lines:
#is the current line the start or continuation of a define statement?
isdefine = line.startswith("#define") or lineContinuation
if isdefine:
current.append(line) #append to current result
lineContinuation = line.endswith("\\") #is the line break escaped?
if not lineContinuation:
#we reached the define statements end - append it to result list
result.append('\n'.join(current))
current = [] #empty the temp list
return result

Categories