diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index 201b0c8a9c..d94a95405d 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -533,7 +533,7 @@ class CmdDesc(MuxCommand): def edit_handler(self): 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") return if self.args: @@ -551,15 +551,12 @@ class CmdDesc(MuxCommand): Save line buffer to the desc prop. This should return True if successful and also report its status to the user. """ - obj.db.desc = self.editor.buffer - self.caller.msg("Saved.") + obj.db.desc = buf + caller.msg("Saved.") return True - self.editor = EvEditor( - self.caller, - loadfunc=load, - savefunc=save, - key="desc") + # launch the editor + EvEditor(self.caller, loadfunc=load, savefunc=save, key="desc") return def func(self): @@ -1351,6 +1348,9 @@ class CmdSetAttribute(ObjManipCommand): @set / @set */attr = + Switch: + edit: Open the line editor (string values only) + Sets attributes on objects. The second form clears a previously set attribute while the last form inspects the current value of the attribute @@ -1430,6 +1430,25 @@ class CmdSetAttribute(ObjManipCommand): "to put quotes around all strings inside lists and " "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): "Implement the set attribute - a limited form of @py." @@ -1454,6 +1473,14 @@ class CmdSetAttribute(ObjManipCommand): return 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 self.rhs is None: # no = means we inspect the attribute(s) diff --git a/evennia/utils/eveditor.py b/evennia/utils/eveditor.py index 0034c85449..739eacf186 100644 --- a/evennia/utils/eveditor.py +++ b/evennia/utils/eveditor.py @@ -105,15 +105,17 @@ _ERROR_LOADFUNC = \ {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 = \ -""" -Exited editor. -""" +_ERROR_NO_SAVEFUNC = "{rNo save function defined. Buffer cannot be saved.{n" + +_MSG_SAVE_NO_CHANGE = "No changes need saving" +_DEFAULT_NO_QUITFUNC = "Exited editor." _ERROR_QUITFUNC = \ """ @@ -122,6 +124,11 @@ _ERROR_QUITFUNC = \ {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 @@ -325,78 +332,75 @@ class CmdEditorGroup(CmdEditorBase): lstart, lend = self.lstart, self.lend cmd = self.cmdstring echo_mode = self.editor._echo_mode - string = "" if cmd == ":": # Echo buffer if self.linerange: buf = linebuffer[lstart:lend] - string = editor.display_buffer(buf=buf, offset=lstart) + editor.display_buffer(buf=buf, offset=lstart) else: - string = editor.display_buffer() + editor.display_buffer() elif cmd == "::": # Echo buffer without the line numbers and syntax parsing if self.linerange: buf = linebuffer[lstart:lend] - string = editor.display_buffer(buf=buf, - offset=lstart, - linenums=False) + editor.display_buffer(buf=buf, + offset=lstart, + linenums=False, raw=True) else: - string = editor.display_buffer(linenums=False) - self.caller.msg(string, raw=True) - return + editor.display_buffer(linenums=False, raw=True) elif cmd == ":::": # Insert single colon alone on a line editor.update_buffer(editor.buffer + "\n:") if echo_mode: - string = "Single ':' added to buffer." + caller.msg("Single ':' added to buffer.") elif cmd == ":h": # help entry - string = editor.display_help() + editor.display_help() elif cmd == ":w": # save without quitting - string = editor.save_buffer() + editor.save_buffer() elif cmd == ":wq": # save and quit - string = editor.save_buffer() - string += " " + editor.quit() + editor.save_buffer() + editor.quit() elif cmd == ":q": # quit. If not saved, will ask if self.editor._unsaved: caller.cmdset.add(SaveYesNoCmdSet) caller.msg("Save before quitting? {lcyes{lt[Y]{le/{lcno{ltN{le") else: - string = editor.quit() + editor.quit() elif cmd == ":q!": # force quit, not checking saving - string = editor.quit() + editor.quit() elif cmd == ":u": # undo - string = editor.update_undo(-1) + editor.update_undo(-1) elif cmd == ":uu": # redo - string = editor.update_undo(1) + editor.update_undo(1) elif cmd == ":UU": # reset 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": # :dd - delete line buf = linebuffer[:lstart] + linebuffer[lend:] editor.update_buffer(buf) - string = "Deleted %s." % (self.lstr) + caller.msg("Deleted %s." % (self.lstr)) elif cmd == ":dw": # :dw - delete word in entire buffer # :dw delete word only on line(s) if not self.arg1: - string = "You must give a search word to delete." + caller.msg("You must give a search word to delete.") else: if not self.linerange: lstart = 0 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: - 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 = re.sub(r"%s" % self.arg1.strip("\'").strip('\"'), "", sarea, re.MULTILINE) buf = linebuffer[:lstart] + sarea.split("\n") + linebuffer[lend:] @@ -404,73 +408,73 @@ class CmdEditorGroup(CmdEditorBase): elif cmd == ":DD": # clear buffer editor.update_buffer("") - string = "Cleared %i lines from buffer." % self.nlines + caller.msg("Cleared %i lines from buffer." % self.nlines) elif cmd == ":y": # :y - yank line(s) to copy buffer cbuf = linebuffer[lstart:lend] editor._copy_buffer = cbuf - string = "%s, %s yanked." % (self.lstr.capitalize(), cbuf) + caller.msg("%s, %s yanked." % (self.lstr.capitalize(), cbuf)) elif cmd == ":x": # :x - cut line to copy buffer cbuf = linebuffer[lstart:lend] editor._copy_buffer = cbuf buf = linebuffer[:lstart] + linebuffer[lend:] editor.update_buffer(buf) - string = "%s, %s cut." % (self.lstr.capitalize(), cbuf) + caller.msg("%s, %s cut." % (self.lstr.capitalize(), cbuf)) elif cmd == ":p": # :p paste line(s) from copy buffer if not editor._copy_buffer: - string = "Copy buffer is empty." + caller.msg("Copy buffer is empty.") else: buf = linebuffer[:lstart] + editor._copy_buffer + linebuffer[lstart:] 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": # :i - insert new line new_lines = self.args.split('\n') 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: buf = linebuffer[:lstart] + new_lines + linebuffer[lstart:] 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": # :r - replace lines new_lines = self.args.split('\n') if not new_lines: - string = "You need to enter a replacement string." + caller.msg("You need to enter a replacement string.") else: buf = linebuffer[:lstart] + new_lines + linebuffer[lend:] 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": # :I - insert text at beginning of line(s) if not self.args: - string = "You need to enter text to insert." + caller.msg("You need to enter text to insert.") else: buf = linebuffer[:lstart] + ["%s%s" % (self.args, line) for line in linebuffer[lstart:lend]] + linebuffer[lend:] 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": # :A - append text after end of line(s) if not self.args: - string = "You need to enter text to append." + caller.msg("You need to enter text to append.") else: buf = linebuffer[:lstart] + ["%s%s" % (line, self.args) for line in linebuffer[lstart:lend]] + linebuffer[lend:] 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": # :s
  • - search and replace words # in entire buffer or on certain lines 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: if not self.linerange: lstart = 0 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: - 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]) regex = r"%s|^%s(?=\s)|(?<=\s)%s(?=\s)|^%s$|(?<=\s)%s$" @@ -486,9 +490,9 @@ class CmdEditorGroup(CmdEditorBase): if not self.linerange: lstart = 0 lend = self.cline + 1 - string = "Flood filled lines %i-%i." % (lstart + 1 , lend) + caller.msg("Flood filled lines %i-%i." % (lstart + 1 , lend)) else: - string = "Flood filled %s." % self.lstr + caller.msg("Flood filled %s." % self.lstr) fbuf = "\n".join(linebuffer[lstart:lend]) fbuf = fill(fbuf, width=width) buf = linebuffer[:lstart] + fbuf.split("\n") + linebuffer[lend:] @@ -499,9 +503,9 @@ class CmdEditorGroup(CmdEditorBase): if not self.linerange: lstart = 0 lend = self.cline + 1 - string = "Indented lines %i-%i." % (lstart + 1 , lend) + caller.msg("Indented lines %i-%i." % (lstart + 1 , lend)) else: - string = "Indented %s." % self.lstr + caller.msg("Indented %s." % self.lstr) fbuf = [indent + line for line in linebuffer[lstart:lend]] buf = linebuffer[:lstart] + fbuf + linebuffer[lend:] editor.update_buffer(buf) @@ -510,9 +514,9 @@ class CmdEditorGroup(CmdEditorBase): if not self.linerange: lstart = 0 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: - string = "Removed left margin (dedented) %s." % self.lstr + caller.msg("Removed left margin (dedented) %s." % self.lstr) fbuf = "\n".join(linebuffer[lstart:lend]) fbuf = dedent(fbuf) buf = linebuffer[:lstart] + fbuf.split("\n") + linebuffer[lend:] @@ -520,8 +524,7 @@ class CmdEditorGroup(CmdEditorBase): elif cmd == ":echo": # set echoing on/off editor._echo_mode = not editor._echo_mode - string = "Echo mode set to %s" % editor._echo_mode - caller.msg(string) + caller.msg("Echo mode set to %s" % editor._echo_mode) class EvEditorCmdSet(CmdSet): @@ -615,7 +618,7 @@ class EvEditor(object): self._echo_mode = True # show the buffer ui - self._caller.msg(self.display_buffer()) + self.display_buffer() def load_buffer(self): """ @@ -668,11 +671,10 @@ class EvEditor(object): # save worked. The saving function is responsible for # any status messages. self._unsaved = False - return "" except Exception, e: - return "%s\n{rSave function gave an error. Buffer not saved." % e + self._caller.msg(_ERROR_SAVEFUNC.format(error=e)) else: - return "No changes need saving." + self._caller.msg(_MSG_SAVE_NO_CHANGE) def update_undo(self, step=None): """ @@ -682,24 +684,25 @@ class EvEditor(object): if step and step < 0: # undo if self._undo_pos <= 0: - return "Nothing to undo." - self._undo_pos = max(0, self._undo_pos + step) - self._buffer = self._undo_buffer[self._undo_pos] - return "Undo." + self._caller.msg(_MSG_NO_UNDO) + else: + self._undo_pos = max(0, self._undo_pos + step) + self._buffer = self._undo_buffer[self._undo_pos] + self._caller.msg(_MSG_UNDO) elif step and step > 0: # redo if self._undo_pos >= len(self._undo_buffer) - 1 or self._undo_pos + 1 >= self._undo_max: - return "Nothing to redo." - 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] - return "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._buffer = self._undo_buffer[self._undo_pos] + self._caller.msg(_MSG_REDO) if not self._undo_buffer or (self._undo_buffer and self._buffer != self._undo_buffer[self._undo_pos]): # save undo state self._undo_buffer = self._undo_buffer[:self._undo_pos + 1] + [self._buffer] 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. @@ -725,86 +728,11 @@ class EvEditor(object): else: main = "\n".join(lines) string = "%s\n%s\n%s" % (header, main, footer) - return string + self._caller.msg(string, raw=raw) def display_help(self): """ Shows the help entry for the editor. """ string = self._sep * _DEFAULT_WIDTH + _HELP_TEXT + self._sep * _DEFAULT_WIDTH - return string - - -#------------------------------------------------------------------ -# -# Editor access command for editing a given attribute on an object. -# -#------------------------------------------------------------------ - -class CmdEditor(Command): - """ - Start editor - - Usage: - @editor / - - 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 /") - 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) + self._caller.msg(string)