> Nice touch...
Thanks! :-)
> I've just commited your code, but I did do one change:
Cool! I also made one change, though... AssistProposal now also has a
priority attribute, so you can move the 'chattier' ones down to the
bottom of the list (i.e: assign dict and list) while keeping the more
specialized at the top (i.e: create error message, attached).
> instead of
> letting 3 options: for list(), dict() or self.var_value, I've only let
> one that does:
> <snip>
> and let the '[]' selected for the user to change it.
Yeah... That should be reasonable for most users. I do this a lot
though, so I like my 'usual suspects' to be there from the start.
That's also the reason I split the actual incorporation into the
assistant into three separate pyedit_*.py scripts, so that people could
decide for themselves which ones that they would want to use. Perhaps I
should have put a Bool check at the top of the scripts to help people
turn it on or off? Say for example:
USE_THIS_ASSISTANT_PROPOSAL = True
if not USE_THIS_ASSISTANT_PROPOSAL:
raise ExitScriptException()
Furthermore, I intentionally used list() instead of [], so that you
reuse the parantheses for function calls, like so:
class MyClass:
def method(self, arg = None):
if arg is None:
arg = self._get_value()
...
Oh, and speaking of selected things, I was wondering how do you do those
tabbed field things that you get if you type 'inip' or 'classs' and then
hit Ctrl-Space? I've whipped up a proposal for creating error messages
(attached), and it would have been so much better if I could have tabbed
fields for the message template, formatting vars and exception class.
I suppose I could have done this by just adding a code completion
template for it, but since there are no code completion variables for
'initial indent' and 'additional indent', the result would just be messy.
> I did it mostly because I think that presenting too many options for a
> very similar thing makes things a bit confusing (so, you actually have
> to stop for a moment and read each option carefully
Yeah, I see your point. In fact, I already did before I read this mail
:-) But since I use those similar ones a lot and didn't want to get rid
of them, I hacked up that priority thing as a workaround which at least
works well for me. I find that by stuffing the similar ones further down
the list, you will in fact rarely read them if you're not actively
looking for them, simply because the more specialized proposals will
always be readily available at the top.
> def met1(self, arg=None): |<-- ctrl+1 here would bring the suggestion,
> instead of having to create another line with arg to make the choice
> (and if more than one argument had '=None', add one option for each).
>
> What do you think?
Shouldn't be too hard. I'll see if I can find the time to implement it.
> As for the assign code, I didn't add it because there was an option to
> do it already (I've just relaxed the restriction of just doing it on a
> call).
That's probably for the best.
Take care!
/Joel
"""Quick Assistant: Create error message and raise.
Effect
======
Generates nice "fill in the form" code that generates a string formatted
error message and raises it.
Valid when
==========
When the current line contains only the word 'msg'.
Installation
============
Place this file in your pydev jython script dir, along with
assist_proposal.py and assist_regex_based_proposal.py, open a new editor,
and you are ready to go. See the pydev docs if you don't know where your
dir is.
Use case
========
Descriptive error messages are good, but are tedious to write. This hack
eases your burden somewhat.
---------------------------------------------
if input is None:
msg = ("in the %s context, no %s can be "
"left empty. a good error mesage "
"uses %s string formatting.")
msg %= context, input_type, 'descriptive'
raise ValueError(msg)
---------------------------------------------
This proposal writes much of the boilerplate code for you. You only need to
type 'msg' and activate the Quick Assistant (and think of what to put in
the error message :-).
Example
=======
Before:
--------------------------------------------------
msg <- place cursor here and hit Ctrl-1!
--------------------------------------------------
After:
--------------------------------------------------
msg = ("") <- Cursor ends up between the quotes
msg %= vars
raise ValueError(msg)
--------------------------------------------------
"""
__author__ = """Joel Hedlund <joel.hedlund at gmail.com>"""
__version__ = "1.0.0"
__copyright__ = '''Available under the same conditions as PyDev.
See PyDev license for details.
http://pydev.sourceforge.net
'''
#
# Boring boilerplate preamble code. This can be safely copied to every pydev
# jython script that you write. The interesting stuff is further down below.
#
# Set to True to do inefficient stuff that is only useful for debugging
# and development purposes. Should always be False if not debugging.
DEBUG = False
# This is a magic trick that tells the PyDev Extensions editor about the
# namespace provided for pydev scripts:
if False:
from org.python.pydev.editor import PyEdit [EMAIL PROTECTED]
cmd = 'command string'
editor = PyEdit
assert cmd is not None
assert editor is not None
True, False = 1,0
# We don't need to add the same assist proposal more than once.
if not (cmd == 'onCreateActions' or (DEBUG and cmd == 'onSave')):
from org.python.pydev.jython import ExitScriptException [EMAIL PROTECTED]
raise ExitScriptException()
# We want a fresh interpreter if we're debugging this script!
if DEBUG and cmd == 'onSave':
from org.python.pydev.jython import JythonPlugin [EMAIL PROTECTED]
editor.pyEditScripting.interpreter = JythonPlugin.newPythonInterpreter()
#
# Interesting stuff starts here!
#
import re
from org.python.pydev.core.docutils import PySelection [EMAIL PROTECTED]
import assist_proposal
from assist_regex_based_proposal import RegexBasedAssistProposal
class CreateErrorMessage(RegexBasedAssistProposal):
"""Help write good error messages for exceptions."""
description = "Create error message and raise"
tag = "CREATE_ERROR_MESSAGE_AND_RAISE"
regex = re.compile("(?P<initial>\s*)msg\s*$")
template = ('%(initial)smsg = ("")%(newline)s'
'%(initial)smsg %%= vars%(newline)s'
'%(initial)sraise ValueError(msg)')
def apply(self, document):
iLineStartOffset = self.selection.getLineOffset(self.offset)
RegexBasedAssistProposal.apply(self, document)
iCursorPosition = iLineStartOffset + len(self.vars['initial']) + 8
self.editor.setSelection(iCursorPosition, 0)
o = CreateErrorMessage()
assist_proposal.register_proposal(o, DEBUG)
"""Convenience module for scripting PyDev Quick Assist proposals in Jyton.
USAGE
=====
Create pyedit_*.py file in your jython script dir of choice, import this
module, subclass AssistProposal, instantiate it and register the instance
with Pydev.
Example:
-------------------------------------------------------------
from assist_proposal import AssistProposal, register_proposal
class MyProposal(AssistProposal):
implementation_goes_here
register_proposal(MyProposal())
-------------------------------------------------------------
The cmd variable is provided automatically by pydev and will be a string
such as 'onSave' or 'onCreateActions' etc...
See docs in source for further details.
"""
__author__ = """Joel Hedlund <joel.hedlund at gmail.com>
Some ideas borrowed from Fabio Zadrozny. These cases are explicitly noted
in the relevant code docs.
"""
__version__ = "1.0.0"
__copyright__ = """Available under the same conditions as PyDev.
See PyDev license for details.
http://pydev.sourceforge.net
"""
from org.python.pydev.editor.correctionassist.heuristics import IAssistProps
[EMAIL PROTECTED]
from org.python.pydev.editor.codecompletion import PyCompletionProposal [EMAIL
PROTECTED]
from org.python.pydev.ui import UIConstants [EMAIL PROTECTED]
from org.python.pydev.editor.codecompletion import IPyCompletionProposal [EMAIL
PROTECTED]
from java.util import ArrayList
True, False = 1,0
class AssistProposal:
"""Convenience class for adding assist proposals to pydev.
This class does nothing useful. Subclasses should assign proper values
to data members and provide sane implementations for methods.
Class data members
==================
description: <str>
The text displayed to the user in the quick assist menu (Ctrl-1).
priority = 10: <int>
Lower values end up higher in the proposal menu. 10 is default
(actually, 10 is IPyCompletionProposal.PRIORITY_DEFAULT :-)
tag: <str>
Unique descriptive identifier for the assist.
"""
description = "Remember to change this description"
tag = "REMEMBER_TO_CHANGE_THIS_TAG"
priority = IPyCompletionProposal.PRIORITY_DEFAULT
def isValid(self, selection, current_line, editor, offset):
"""Return True if the proposal is applicable, False otherwise.
This method should provide the same interface as the method with
the same name in IAssistProps.
If desirable, subclasses may store the isValid args as instance
data members for use with .apply().
IN:
pyselection: <PySelection>
The current selection. Highly useful.
current_line: <str>
The text on the current line.
editor: <PyEdit>
The current editor.
offset: <int>
The current position in the editor.
OUT:
Boolean. Is the proposal applicable in the current situation?
"""
return False
def apply(self, document):
"""Do what the assist is supposed to do when activated.
This method should provide the same interface as the method with
same name in PyCompletionProposal.
See also docs for the .isValid() method. You might like to use data
from there.
IN:
document: <IDocument>
The edited document.
OUT:
None.
"""
def register_proposal(proposal, debug = False):
"""Register the proposal with the quick assistant.
IN:
proposal: <AssistantProposal>
The object that holds all relevant information and does all the
necessary work for the proposal.
force = False: <bool>
If False (default), we will not attempt to re-register the assist
proposal if an assist proposal with the same tag is already
registered. If True, then we will override the registered proposal
with our own. This is mainly useful for debugging.
OUT:
None.
"""
from org.python.pydev.editor.correctionassist import
PythonCorrectionProcessor [EMAIL PROTECTED]
bTagInUse = PythonCorrectionProcessor.hasAdditionalAssist(proposal.tag)
if debug or not bTagInUse:
oInterface = AssistantInterface(proposal)
PythonCorrectionProcessor.addAdditionalAssist(proposal.tag, oInterface)
class Prop(PyCompletionProposal):
"""This is the proposal that Ctrl+1 will require.
Adapted from Fabio Zadroznys Prop class in
assign_params_to_attributes_assist.py.
Instance data members
=====================
proposal: <AssistantProposal>
The object that holds all relevant information and does all the
necessary work for the proposal.
"""
def __init__(self, proposal, *args):
PyCompletionProposal.__init__(self, *args)
self.proposal = proposal
def apply(self, document):
"""java: public void apply(IDocument document)
"""
self.proposal.apply(document)
def getSelection(self, document):
return None
class AssistantInterface(IAssistProps):
"""Assistant interface wrapper for AssistProposal instances.
The Quick Assistant will ask this class if we can apply the proposal,
and if so, which properties does it have?
Adapted from Fabio Zadroznys AssistAssignParamsToAttributes class in
assign_params_to_attributes_assist.py.
Instance data members
=====================
proposal: <AssistantProposal>
The object that holds all relevant information and does all the
necessary work for the proposal.
"""
def __init__(self, proposal, *args):
"""A new Assistant Interface.
IN:
proposal: <AssistantProposal>
"""
self.proposal = proposal
def getImage(self,imageCache, c):
if imageCache is not None:
return imageCache.get(c)
return None
def isValid(self, ps, sel, editor, offset):
"""java: boolean isValid(PySelection ps, String sel, PyEdit edit, int
offset);
"""
return self.proposal.isValid(ps, sel, editor, offset)
def getProps(self, ps, imageCache, f, nature, editor, offset):
'''java: List<ICompletionProposal> getProps(PySelection ps, ImageCache
imageCache, File f,
IPythonNature nature,
PyEdit edit, int offset)
'''
oProp = Prop(self.proposal,
'', 0, 0, 0,
self.getImage(imageCache, UIConstants.ASSIST_DOCSTRING),
self.proposal.description,
None, None,
self.proposal.priority)
l = ArrayList()
l.add(oProp)
return l
"""Quick Assistant: Regex based proposals.
This module combines AssistProposal, regexes and string formatting to
provide a way of swiftly coding your own custom Quick Assistant proposals.
These proposals are ready for instatiation and registering with
assist_proposal.register_proposal(): AssignToAttributeOfSelf,
AssignEmptyDictToVarIfNone, AssignEmptyDictToVarIfNone and
AssignAttributeOfSelfToVarIfNone. Using these as examples it should be
straightforward to code your own regex driven Quick Assistant proposals.
"""
__author__ = """Joel Hedlund <joel.hedlund at gmail.com>"""
__version__ = "1.0.0"
__copyright__ = '''Available under the same conditions as PyDev.
See PyDev license for details.
http://pydev.sourceforge.net
'''
import re
from org.python.pydev.core.docutils import PySelection [EMAIL PROTECTED]
from org.python.pydev.editor.actions import PyAction [EMAIL PROTECTED]
import assist_proposal
# For older python versions.
True, False = 1,0
class RegexBasedAssistProposal(assist_proposal.AssistProposal):
"""Base class for regex driven Quick Assist proposals.
More docs available in base class source.
New class data members
======================
regex = re.compile(r'^(?P<initial>\s*)(?P<name>\w+)\s*$'): <regex>
Must .match() current line for .isValid() to return true. Any named
groups will be available in self.vars.
template = "%(initial)sprint 'Hello World!'": <str>
This will replace what's currently on the line on .apply(). May use
string formatters with names from self.vars.
base_vars = {}: <dict <str>:<str>>
Used to initiallize self.vars.
New instance data members
=========================
vars = <dict <str>:<str>>
Variables used with self.template to produce the code that replaces
the current line. This will contain values from self.base_vars, all
named groups in self.regex, as well with these two additional ones:
'indent': the static indentation string
'newline': the line delimiter string
selection, current_line, editor, offset:
Same as the corresponding args to .isValid().
"""
template = ""
base_vars = {}
regex = re.compile(r'^(?P<initial>\s*)(?P<name>\w+)\s*$')
def isValid(self, selection, current_line, editor, offset):
"""Is this proposal applicable to this line of code?
If current_line .match():es against self.regex then we will store
a lot of information on the match and environment, and return True.
Otherwise return False.
IN:
pyselection: <PySelection>
The current selection. Highly useful.
current_line: <str>
The text on the current line.
editor: <PyEdit>
The current editor.
offset: <int>
The current position in the editor.
OUT:
Boolean. Is the proposal applicable in the current situation?
"""
m = self.regex.match(current_line)
if not m:
return False
self.vars = {'indent': PyAction.getStaticIndentationString(editor)}
self.vars.update(self.base_vars)
self.vars.update(m.groupdict())
self.selection = selection
self.current_line = current_line
self.editor = editor
self.offset = offset
return True
def apply(self, document):
"""Replace the current line with the populated template.
IN:
document: <IDocument>
The edited document.
OUT:
None.
"""
self.vars['newline'] = PyAction.getDelimiter(document)
sNewCode = self.template % self.vars
# Move to insert point:
iStartLineOffset = self.selection.getLineOffset()
iEndLineOffset = iStartLineOffset + len(self.current_line)
self.editor.setSelection(iEndLineOffset, 0)
self.selection = PySelection(self.editor)
# Replace the old code with the new assignment expression:
self.selection.replaceLineContentsToSelection(sNewCode)
class AssignToAttributeOfSelf(RegexBasedAssistProposal):
"""Assign variable to attribute of self.
Effect
======
Generates code that assigns a variable to attribute of self with the
same name.
Valid when
==========
When the current line contains exactly one alphanumeric word. No check
is performed to see if the word is defined or valid in any other way.
Use case
========
It's often a good idea to use the same names in args, variables and
data members. This keeps the terminology consistent. This way
customer_id should always contain a customer id, and any other
variants are misspellings that probably will lead to bugs. This
proposal helps you do this by assigning variables to data members with
the same name.
"""
description = "Assign to attribute of self"
tag = "ASSIGN_VARIABLE_TO_ATTRIBUTE_OF_SELF"
regex = re.compile(r'^(?P<initial> {8}\s*)(?P<name>\w+)\s*$')
template = "%(initial)sself.%(name)s = %(name)s"
class AssignDefaultToVarIfNone(RegexBasedAssistProposal):
"""Assign default value to variable if None.
This is a base class intended for subclassing.
Effect
======
Generates code that tests if a variable is none, and if so, assigns a
default value to it.
Valid when
==========
When the current line contains exactly one alphanumeric word. No check
is performed to see if the word is defined or valid in any other way.
Use case
========
It's generally a bad idea to use mutable objects as default values to
methods and functions. The common way around it is to use None as the
default value, check the arg in the fuction body, and then assign
the desired mutable to it. This proposal does the check/assignment for
you. You only need to type the arg name where you want the check, and
then activate the Quick Assistant.
"""
description = "Assign default value to var if None"
tag = "ASSIGN_DEFAULT_VALUE_TO_VARIABLE_IF_NONE"
regex = re.compile(r'^(?P<initial>\s*)(?P<name>\w+)\s*$')
template = ("%(initial)sif %(name)s is None:%(newline)s"
"%(initial)s%(indent)s%(name)s = %(value)s")
base_vars = {'value': "list()"}
class AssignEmptyListToVarIfNone(AssignDefaultToVarIfNone):
"""Assign empty list to variable if None."""
description = "Assign empty list to var if None"
tag = "ASSIGN_EMPTY_LIST_TO_VARIABLE_IF_NONE"
priority = 11
class AssignEmptyDictToVarIfNone(AssignEmptyListToVarIfNone):
"""Assign empty dictionary to variable if None."""
description = "Assign empty dict to var if None"
tag = "ASSIGN_EMPTY_DICT_TO_VARIABLE_IF_NONE"
base_vars = {'value': "dict()"}
priority = 11
class AssignAttributeOfSelfToVarIfNone(AssignDefaultToVarIfNone):
"""Assign an attribute of self with same name to variable if None.
Valid when
==========
When the current line contains exactly one alphanumeric word indented
by more than 8 spaces. This script does not check if the word is
defined or valid in any other way.
Use case
========
If a method does something using a data member, but just as well could do
the same thing using an argument, it's generally a good idea to let the
implementation reflect that. This makes the code more flexible. This is
usually done like so:
--------------------------
class MyClass:
def func(arg = None):
if arg is None:
arg = self.arg
...
--------------------------
This proposal does the check/assignment for you. You only need to type the
arg name where you want the check, and then activate the Quick Assistant.
"""
description = "Assign attribute of self to var if None"
tag = "ASSIGN_ATTRIBUTE_OF_SELF_TO_VARIABLE_IF_NONE"
regex = re.compile(r'^(?P<initial> {8}\s*)(?P<name>\w+)\s*$')
template = ("%(initial)sif %(name)s is None:%(newline)s"
"%(initial)s%(indent)s%(name)s = self.%(name)s")
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
pydev-code mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/pydev-code