integrated the line editor functionality with the @set command (using the /edit switch), removing the example CmdEdit command from the eveditor module.

This commit is contained in:
Griatch 2015-07-08 20:07:09 +02:00
parent d2a69910a9
commit d47bd96ddd
2 changed files with 107 additions and 152 deletions

View file

@ -533,7 +533,7 @@ class CmdDesc(MuxCommand):
def edit_handler(self): def edit_handler(self):
if self.rhs: if self.rhs:
self.msg("{rYou may specify a description, or use the edit switch, " self.msg("{rYou may specify a value, or use the edit switch, "
"but not both.{n") "but not both.{n")
return return
if self.args: if self.args:
@ -551,15 +551,12 @@ class CmdDesc(MuxCommand):
Save line buffer to the desc prop. This should Save line buffer to the desc prop. This should
return True if successful and also report its status to the user. return True if successful and also report its status to the user.
""" """
obj.db.desc = self.editor.buffer obj.db.desc = buf
self.caller.msg("Saved.") caller.msg("Saved.")
return True return True
self.editor = EvEditor( # launch the editor
self.caller, EvEditor(self.caller, loadfunc=load, savefunc=save, key="desc")
loadfunc=load,
savefunc=save,
key="desc")
return return
def func(self): def func(self):
@ -1351,6 +1348,9 @@ class CmdSetAttribute(ObjManipCommand):
@set <obj>/<attr> @set <obj>/<attr>
@set *<player>/attr = <value> @set *<player>/attr = <value>
Switch:
edit: Open the line editor (string values only)
Sets attributes on objects. The second form clears Sets attributes on objects. The second form clears
a previously set attribute while the last form a previously set attribute while the last form
inspects the current value of the attribute inspects the current value of the attribute
@ -1430,6 +1430,25 @@ class CmdSetAttribute(ObjManipCommand):
"to put quotes around all strings inside lists and " "to put quotes around all strings inside lists and "
"dicts.{n") "dicts.{n")
def edit_handler(self, obj, attr):
"Activate the line editor"
def load(caller):
"Called for the editor to load the buffer"
old_value = obj.attributes.get(attr)
if old_value is not None and not isinstance(old_value, basestring):
typ = type(old_value).__name__
self.caller.msg("{RWARNING! Saving this buffer will overwrite the "\
"current attribute (of type %s) with a string!{n" % typ)
return str(old_value)
return old_value
def save(caller, buf):
"Called when editor saves its buffer."
obj.attributes.add(attr, buf)
caller.msg("Saved Attribute %s." % attr)
# start the editor
EvEditor(self.caller, load, save, key="%s/%s" % (obj, attr))
def func(self): def func(self):
"Implement the set attribute - a limited form of @py." "Implement the set attribute - a limited form of @py."
@ -1454,6 +1473,14 @@ class CmdSetAttribute(ObjManipCommand):
return return
string = "" string = ""
if "edit" in self.switches:
# edit in the line editor
if len(attrs) > 1:
caller.msg("The Line editor can only be applied " \
"to one attribute at a time.")
return
self.edit_handler(obj, attrs[0])
return
if not value: if not value:
if self.rhs is None: if self.rhs is None:
# no = means we inspect the attribute(s) # no = means we inspect the attribute(s)

View file

@ -105,15 +105,17 @@ _ERROR_LOADFUNC = \
{rBuffer load function error. Could not load initial data.{n {rBuffer load function error. Could not load initial data.{n
""" """
_ERROR_NO_SAVEFUNC = \ _ERROR_SAVEFUNC = \
""" """
{rNo save function defined. Buffer cannot be saved.{n {error}
{rSave function returned an error. Buffer not saved.{n
""" """
_DEFAULT_NO_QUITFUNC = \ _ERROR_NO_SAVEFUNC = "{rNo save function defined. Buffer cannot be saved.{n"
"""
Exited editor. _MSG_SAVE_NO_CHANGE = "No changes need saving"
""" _DEFAULT_NO_QUITFUNC = "Exited editor."
_ERROR_QUITFUNC = \ _ERROR_QUITFUNC = \
""" """
@ -122,6 +124,11 @@ _ERROR_QUITFUNC = \
{rQuit function gave an error. Skipping.{n {rQuit function gave an error. Skipping.{n
""" """
_MSG_NO_UNDO = "Nothing to undo"
_MSG_NO_REDO = "Nothing to redo"
_MSG_UNDO = "Undid one step."
_MSG_REDO = "Redid one step."
#------------------------------------------------------------ #------------------------------------------------------------
# #
# Handle yes/no quit question # Handle yes/no quit question
@ -325,78 +332,75 @@ class CmdEditorGroup(CmdEditorBase):
lstart, lend = self.lstart, self.lend lstart, lend = self.lstart, self.lend
cmd = self.cmdstring cmd = self.cmdstring
echo_mode = self.editor._echo_mode echo_mode = self.editor._echo_mode
string = ""
if cmd == ":": if cmd == ":":
# Echo buffer # Echo buffer
if self.linerange: if self.linerange:
buf = linebuffer[lstart:lend] buf = linebuffer[lstart:lend]
string = editor.display_buffer(buf=buf, offset=lstart) editor.display_buffer(buf=buf, offset=lstart)
else: else:
string = editor.display_buffer() editor.display_buffer()
elif cmd == "::": elif cmd == "::":
# Echo buffer without the line numbers and syntax parsing # Echo buffer without the line numbers and syntax parsing
if self.linerange: if self.linerange:
buf = linebuffer[lstart:lend] buf = linebuffer[lstart:lend]
string = editor.display_buffer(buf=buf, editor.display_buffer(buf=buf,
offset=lstart, offset=lstart,
linenums=False) linenums=False, raw=True)
else: else:
string = editor.display_buffer(linenums=False) editor.display_buffer(linenums=False, raw=True)
self.caller.msg(string, raw=True)
return
elif cmd == ":::": elif cmd == ":::":
# Insert single colon alone on a line # Insert single colon alone on a line
editor.update_buffer(editor.buffer + "\n:") editor.update_buffer(editor.buffer + "\n:")
if echo_mode: if echo_mode:
string = "Single ':' added to buffer." caller.msg("Single ':' added to buffer.")
elif cmd == ":h": elif cmd == ":h":
# help entry # help entry
string = editor.display_help() editor.display_help()
elif cmd == ":w": elif cmd == ":w":
# save without quitting # save without quitting
string = editor.save_buffer() editor.save_buffer()
elif cmd == ":wq": elif cmd == ":wq":
# save and quit # save and quit
string = editor.save_buffer() editor.save_buffer()
string += " " + editor.quit() editor.quit()
elif cmd == ":q": elif cmd == ":q":
# quit. If not saved, will ask # quit. If not saved, will ask
if self.editor._unsaved: if self.editor._unsaved:
caller.cmdset.add(SaveYesNoCmdSet) caller.cmdset.add(SaveYesNoCmdSet)
caller.msg("Save before quitting? {lcyes{lt[Y]{le/{lcno{ltN{le") caller.msg("Save before quitting? {lcyes{lt[Y]{le/{lcno{ltN{le")
else: else:
string = editor.quit() editor.quit()
elif cmd == ":q!": elif cmd == ":q!":
# force quit, not checking saving # force quit, not checking saving
string = editor.quit() editor.quit()
elif cmd == ":u": elif cmd == ":u":
# undo # undo
string = editor.update_undo(-1) editor.update_undo(-1)
elif cmd == ":uu": elif cmd == ":uu":
# redo # redo
string = editor.update_undo(1) editor.update_undo(1)
elif cmd == ":UU": elif cmd == ":UU":
# reset buffer # reset buffer
editor.update_buffer(editor._pristine_buffer) editor.update_buffer(editor._pristine_buffer)
string = "Reverted all changes to the buffer back to original state." caller.msg("Reverted all changes to the buffer back to original state.")
elif cmd == ":dd": elif cmd == ":dd":
# :dd <l> - delete line <l> # :dd <l> - delete line <l>
buf = linebuffer[:lstart] + linebuffer[lend:] buf = linebuffer[:lstart] + linebuffer[lend:]
editor.update_buffer(buf) editor.update_buffer(buf)
string = "Deleted %s." % (self.lstr) caller.msg("Deleted %s." % (self.lstr))
elif cmd == ":dw": elif cmd == ":dw":
# :dw <w> - delete word in entire buffer # :dw <w> - delete word in entire buffer
# :dw <l> <w> delete word only on line(s) <l> # :dw <l> <w> delete word only on line(s) <l>
if not self.arg1: if not self.arg1:
string = "You must give a search word to delete." caller.msg("You must give a search word to delete.")
else: else:
if not self.linerange: if not self.linerange:
lstart = 0 lstart = 0
lend = self.cline + 1 lend = self.cline + 1
string = "Removed %s for lines %i-%i." % (self.arg1, lstart + 1, lend + 1) caller.msg("Removed %s for lines %i-%i." % (self.arg1, lstart + 1, lend + 1))
else: else:
string = "Removed %s for %s." % (self.arg1, self.lstr) caller.msg("Removed %s for %s." % (self.arg1, self.lstr))
sarea = "\n".join(linebuffer[lstart:lend]) sarea = "\n".join(linebuffer[lstart:lend])
sarea = re.sub(r"%s" % self.arg1.strip("\'").strip('\"'), "", sarea, re.MULTILINE) sarea = re.sub(r"%s" % self.arg1.strip("\'").strip('\"'), "", sarea, re.MULTILINE)
buf = linebuffer[:lstart] + sarea.split("\n") + linebuffer[lend:] buf = linebuffer[:lstart] + sarea.split("\n") + linebuffer[lend:]
@ -404,73 +408,73 @@ class CmdEditorGroup(CmdEditorBase):
elif cmd == ":DD": elif cmd == ":DD":
# clear buffer # clear buffer
editor.update_buffer("") editor.update_buffer("")
string = "Cleared %i lines from buffer." % self.nlines caller.msg("Cleared %i lines from buffer." % self.nlines)
elif cmd == ":y": elif cmd == ":y":
# :y <l> - yank line(s) to copy buffer # :y <l> - yank line(s) to copy buffer
cbuf = linebuffer[lstart:lend] cbuf = linebuffer[lstart:lend]
editor._copy_buffer = cbuf editor._copy_buffer = cbuf
string = "%s, %s yanked." % (self.lstr.capitalize(), cbuf) caller.msg("%s, %s yanked." % (self.lstr.capitalize(), cbuf))
elif cmd == ":x": elif cmd == ":x":
# :x <l> - cut line to copy buffer # :x <l> - cut line to copy buffer
cbuf = linebuffer[lstart:lend] cbuf = linebuffer[lstart:lend]
editor._copy_buffer = cbuf editor._copy_buffer = cbuf
buf = linebuffer[:lstart] + linebuffer[lend:] buf = linebuffer[:lstart] + linebuffer[lend:]
editor.update_buffer(buf) editor.update_buffer(buf)
string = "%s, %s cut." % (self.lstr.capitalize(), cbuf) caller.msg("%s, %s cut." % (self.lstr.capitalize(), cbuf))
elif cmd == ":p": elif cmd == ":p":
# :p <l> paste line(s) from copy buffer # :p <l> paste line(s) from copy buffer
if not editor._copy_buffer: if not editor._copy_buffer:
string = "Copy buffer is empty." caller.msg("Copy buffer is empty.")
else: else:
buf = linebuffer[:lstart] + editor._copy_buffer + linebuffer[lstart:] buf = linebuffer[:lstart] + editor._copy_buffer + linebuffer[lstart:]
editor.update_buffer(buf) editor.update_buffer(buf)
string = "Copied buffer %s to %s." % (editor._copy_buffer, self.lstr) caller.msg("Copied buffer %s to %s." % (editor._copy_buffer, self.lstr))
elif cmd == ":i": elif cmd == ":i":
# :i <l> <txt> - insert new line # :i <l> <txt> - insert new line
new_lines = self.args.split('\n') new_lines = self.args.split('\n')
if not new_lines: if not new_lines:
string = "You need to enter a new line and where to insert it." caller.msg("You need to enter a new line and where to insert it.")
else: else:
buf = linebuffer[:lstart] + new_lines + linebuffer[lstart:] buf = linebuffer[:lstart] + new_lines + linebuffer[lstart:]
editor.update_buffer(buf) editor.update_buffer(buf)
string = "Inserted %i new line(s) at %s." % (len(new_lines), self.lstr) caller.msg("Inserted %i new line(s) at %s." % (len(new_lines), self.lstr))
elif cmd == ":r": elif cmd == ":r":
# :r <l> <txt> - replace lines # :r <l> <txt> - replace lines
new_lines = self.args.split('\n') new_lines = self.args.split('\n')
if not new_lines: if not new_lines:
string = "You need to enter a replacement string." caller.msg("You need to enter a replacement string.")
else: else:
buf = linebuffer[:lstart] + new_lines + linebuffer[lend:] buf = linebuffer[:lstart] + new_lines + linebuffer[lend:]
editor.update_buffer(buf) editor.update_buffer(buf)
string = "Replaced %i line(s) at %s." % (len(new_lines), self.lstr) caller.msg("Replaced %i line(s) at %s." % (len(new_lines), self.lstr))
elif cmd == ":I": elif cmd == ":I":
# :I <l> <txt> - insert text at beginning of line(s) <l> # :I <l> <txt> - insert text at beginning of line(s) <l>
if not self.args: if not self.args:
string = "You need to enter text to insert." caller.msg("You need to enter text to insert.")
else: else:
buf = linebuffer[:lstart] + ["%s%s" % (self.args, line) for line in linebuffer[lstart:lend]] + linebuffer[lend:] buf = linebuffer[:lstart] + ["%s%s" % (self.args, line) for line in linebuffer[lstart:lend]] + linebuffer[lend:]
editor.update_buffer(buf) editor.update_buffer(buf)
string = "Inserted text at beginning of %s." % self.lstr caller.msg("Inserted text at beginning of %s." % self.lstr)
elif cmd == ":A": elif cmd == ":A":
# :A <l> <txt> - append text after end of line(s) # :A <l> <txt> - append text after end of line(s)
if not self.args: if not self.args:
string = "You need to enter text to append." caller.msg("You need to enter text to append.")
else: else:
buf = linebuffer[:lstart] + ["%s%s" % (line, self.args) for line in linebuffer[lstart:lend]] + linebuffer[lend:] buf = linebuffer[:lstart] + ["%s%s" % (line, self.args) for line in linebuffer[lstart:lend]] + linebuffer[lend:]
editor.update_buffer(buf) editor.update_buffer(buf)
string = "Appended text to end of %s." % self.lstr caller.msg("Appended text to end of %s." % self.lstr)
elif cmd == ":s": elif cmd == ":s":
# :s <li> <w> <txt> - search and replace words # :s <li> <w> <txt> - search and replace words
# in entire buffer or on certain lines # in entire buffer or on certain lines
if not self.arg1 or not self.arg2: if not self.arg1 or not self.arg2:
string = "You must give a search word and something to replace it with." caller.msg("You must give a search word and something to replace it with.")
else: else:
if not self.linerange: if not self.linerange:
lstart = 0 lstart = 0
lend = self.cline + 1 lend = self.cline + 1
string = "Search-replaced %s -> %s for lines %i-%i." % (self.arg1, self.arg2, lstart + 1 , lend) caller.msg("Search-replaced %s -> %s for lines %i-%i." % (self.arg1, self.arg2, lstart + 1 , lend))
else: else:
string = "Search-replaced %s -> %s for %s." % (self.arg1, self.arg2, self.lstr) caller.msg("Search-replaced %s -> %s for %s." % (self.arg1, self.arg2, self.lstr))
sarea = "\n".join(linebuffer[lstart:lend]) sarea = "\n".join(linebuffer[lstart:lend])
regex = r"%s|^%s(?=\s)|(?<=\s)%s(?=\s)|^%s$|(?<=\s)%s$" regex = r"%s|^%s(?=\s)|(?<=\s)%s(?=\s)|^%s$|(?<=\s)%s$"
@ -486,9 +490,9 @@ class CmdEditorGroup(CmdEditorBase):
if not self.linerange: if not self.linerange:
lstart = 0 lstart = 0
lend = self.cline + 1 lend = self.cline + 1
string = "Flood filled lines %i-%i." % (lstart + 1 , lend) caller.msg("Flood filled lines %i-%i." % (lstart + 1 , lend))
else: else:
string = "Flood filled %s." % self.lstr caller.msg("Flood filled %s." % self.lstr)
fbuf = "\n".join(linebuffer[lstart:lend]) fbuf = "\n".join(linebuffer[lstart:lend])
fbuf = fill(fbuf, width=width) fbuf = fill(fbuf, width=width)
buf = linebuffer[:lstart] + fbuf.split("\n") + linebuffer[lend:] buf = linebuffer[:lstart] + fbuf.split("\n") + linebuffer[lend:]
@ -499,9 +503,9 @@ class CmdEditorGroup(CmdEditorBase):
if not self.linerange: if not self.linerange:
lstart = 0 lstart = 0
lend = self.cline + 1 lend = self.cline + 1
string = "Indented lines %i-%i." % (lstart + 1 , lend) caller.msg("Indented lines %i-%i." % (lstart + 1 , lend))
else: else:
string = "Indented %s." % self.lstr caller.msg("Indented %s." % self.lstr)
fbuf = [indent + line for line in linebuffer[lstart:lend]] fbuf = [indent + line for line in linebuffer[lstart:lend]]
buf = linebuffer[:lstart] + fbuf + linebuffer[lend:] buf = linebuffer[:lstart] + fbuf + linebuffer[lend:]
editor.update_buffer(buf) editor.update_buffer(buf)
@ -510,9 +514,9 @@ class CmdEditorGroup(CmdEditorBase):
if not self.linerange: if not self.linerange:
lstart = 0 lstart = 0
lend = self.cline + 1 lend = self.cline + 1
string = "Removed left margin (dedented) lines %i-%i." % (lstart + 1 , lend) caller.msg("Removed left margin (dedented) lines %i-%i." % (lstart + 1 , lend))
else: else:
string = "Removed left margin (dedented) %s." % self.lstr caller.msg("Removed left margin (dedented) %s." % self.lstr)
fbuf = "\n".join(linebuffer[lstart:lend]) fbuf = "\n".join(linebuffer[lstart:lend])
fbuf = dedent(fbuf) fbuf = dedent(fbuf)
buf = linebuffer[:lstart] + fbuf.split("\n") + linebuffer[lend:] buf = linebuffer[:lstart] + fbuf.split("\n") + linebuffer[lend:]
@ -520,8 +524,7 @@ class CmdEditorGroup(CmdEditorBase):
elif cmd == ":echo": elif cmd == ":echo":
# set echoing on/off # set echoing on/off
editor._echo_mode = not editor._echo_mode editor._echo_mode = not editor._echo_mode
string = "Echo mode set to %s" % editor._echo_mode caller.msg("Echo mode set to %s" % editor._echo_mode)
caller.msg(string)
class EvEditorCmdSet(CmdSet): class EvEditorCmdSet(CmdSet):
@ -615,7 +618,7 @@ class EvEditor(object):
self._echo_mode = True self._echo_mode = True
# show the buffer ui # show the buffer ui
self._caller.msg(self.display_buffer()) self.display_buffer()
def load_buffer(self): def load_buffer(self):
""" """
@ -668,11 +671,10 @@ class EvEditor(object):
# save worked. The saving function is responsible for # save worked. The saving function is responsible for
# any status messages. # any status messages.
self._unsaved = False self._unsaved = False
return ""
except Exception, e: except Exception, e:
return "%s\n{rSave function gave an error. Buffer not saved." % e self._caller.msg(_ERROR_SAVEFUNC.format(error=e))
else: else:
return "No changes need saving." self._caller.msg(_MSG_SAVE_NO_CHANGE)
def update_undo(self, step=None): def update_undo(self, step=None):
""" """
@ -682,24 +684,25 @@ class EvEditor(object):
if step and step < 0: if step and step < 0:
# undo # undo
if self._undo_pos <= 0: if self._undo_pos <= 0:
return "Nothing to undo." self._caller.msg(_MSG_NO_UNDO)
else:
self._undo_pos = max(0, self._undo_pos + step) self._undo_pos = max(0, self._undo_pos + step)
self._buffer = self._undo_buffer[self._undo_pos] self._buffer = self._undo_buffer[self._undo_pos]
return "Undo." self._caller.msg(_MSG_UNDO)
elif step and step > 0: elif step and step > 0:
# redo # redo
if self._undo_pos >= len(self._undo_buffer) - 1 or self._undo_pos + 1 >= self._undo_max: if self._undo_pos >= len(self._undo_buffer) - 1 or self._undo_pos + 1 >= self._undo_max:
return "Nothing to redo." self._caller.msg(_MSG_NO_REDO)
else:
self._undo_pos = min(self._undo_pos + step, min(len(self._undo_buffer), self._undo_max) - 1) self._undo_pos = min(self._undo_pos + step, min(len(self._undo_buffer), self._undo_max) - 1)
self._buffer = self._undo_buffer[self._undo_pos] self._buffer = self._undo_buffer[self._undo_pos]
return "Redo." self._caller.msg(_MSG_REDO)
if not self._undo_buffer or (self._undo_buffer and self._buffer != self._undo_buffer[self._undo_pos]): if not self._undo_buffer or (self._undo_buffer and self._buffer != self._undo_buffer[self._undo_pos]):
# save undo state # save undo state
self._undo_buffer = self._undo_buffer[:self._undo_pos + 1] + [self._buffer] self._undo_buffer = self._undo_buffer[:self._undo_pos + 1] + [self._buffer]
self._undo_pos = len(self._undo_buffer) - 1 self._undo_pos = len(self._undo_buffer) - 1
print "saving undo buffer:", self._undo_buffer, self._undo_pos
def display_buffer(self, buf=None, offset=0, linenums=True): def display_buffer(self, buf=None, offset=0, linenums=True, raw=False):
""" """
This displays the line editor buffer, or selected parts of it. This displays the line editor buffer, or selected parts of it.
@ -725,86 +728,11 @@ class EvEditor(object):
else: else:
main = "\n".join(lines) main = "\n".join(lines)
string = "%s\n%s\n%s" % (header, main, footer) string = "%s\n%s\n%s" % (header, main, footer)
return string self._caller.msg(string, raw=raw)
def display_help(self): def display_help(self):
""" """
Shows the help entry for the editor. Shows the help entry for the editor.
""" """
string = self._sep * _DEFAULT_WIDTH + _HELP_TEXT + self._sep * _DEFAULT_WIDTH string = self._sep * _DEFAULT_WIDTH + _HELP_TEXT + self._sep * _DEFAULT_WIDTH
return string self._caller.msg(string)
#------------------------------------------------------------------
#
# Editor access command for editing a given attribute on an object.
#
#------------------------------------------------------------------
class CmdEditor(Command):
"""
Start editor
Usage:
@editor <obj>/<attr>
This will start Evennia's powerful line editor to edit an
Attribute. The editor has a host of commands on its own. Use :h
for a list of commands.
"""
key = "@editor"
aliases = ["@edit"]
locks = "cmd:perm(editor) or perm(Builders)"
help_category = "Building"
def func(self):
"setup and start the editor"
if not self.args or not '/' in self.args:
self.caller.msg("Usage: @editor <obj>/<attrname>")
return
caller = self.caller
objname, attrname = [part.strip() for part in self.args.split("/", 1)]
target = caller.search(objname)
if not target:
return
# hook save/load functions
def load_attr(caller):
"inital loading of buffer data from given attribute."
old_value = target.attributes.get(attrname)
if old_value is not None and not isinstance(old_value, basestring):
typ = type(old_value).__name__
self.caller.msg("{RWARNING! Saving this buffer will overwrite the "\
"current attribute (of type %s) with a string!{n" % typ)
return old_value and str(old_value) or ""
def save_attr(caller, buf):
"""
Save line buffer to given attribute name. This should
return True if successful and also report its status.
"""
target.attributes.add(attrname, buf)
self.caller.msg("Saved.")
return True
def quit_hook(caller):
"""
Example quit hook. Since it's given, it's responsible for
giving feedback messages.
"""
del caller.ndb._editing_attr
caller.msg("Exited Editor.")
editor_key = "%s/%s" % (self.objname, self.attrname)
# start editor, it will handle things from here. We need to
# store it on the command object since we set up callback functions
# to refer to it that way.
self.editor = EvEditor(self.caller,
loadfunc=load_attr,
savefunc=save_attr,
quitfunc=quit_hook,
key=editor_key)