diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index 806ff5ea11..4bf3259815 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -13,6 +13,7 @@ from evennia.commands.default.muxcommand import MuxCommand from evennia.commands.cmdhandler import get_and_merge_cmdsets from evennia.utils import create, utils, search from evennia.utils.utils import inherits_from +from evennia.utils.eveditor import EvEditor from evennia.utils.spawner import spawn from evennia.utils.ansi import raw @@ -554,12 +555,11 @@ class CmdDesc(MuxCommand): self.caller.msg("Saved.") return True - self.editor = utils.get_line_editor()( - self.caller, - loadfunc=load, - savefunc=save, - key="desc", - ) + self.editor = EvEditor( + self.caller, + loadfunc=load, + savefunc=save, + key="desc") return def func(self): diff --git a/evennia/settings_default.py b/evennia/settings_default.py index 66372d420d..883004e5c7 100644 --- a/evennia/settings_default.py +++ b/evennia/settings_default.py @@ -300,11 +300,6 @@ CMDSET_PLAYER = "commands.default_cmdsets.PlayerCmdSet" # Location to search for cmdsets if full path not given CMDSET_PATHS = ["commands", "evennia", "contribs"] -# Line editor path. Points to a line editor class that commands may use to give -# users extended editing control. See the default path for a reference implementation -# and usage. -LINE_EDITOR = 'evennia.commands.default.lineeditor.LineEditor' - ###################################################################### # Typeclasses and other paths ###################################################################### diff --git a/evennia/commands/default/lineeditor.py b/evennia/utils/eveditor.py similarity index 81% rename from evennia/commands/default/lineeditor.py rename to evennia/utils/eveditor.py index 22a42c6c77..122931029d 100644 --- a/evennia/commands/default/lineeditor.py +++ b/evennia/utils/eveditor.py @@ -1,9 +1,9 @@ """ -Evennia Line Editor +EvEditor (Evennia Line Editor) This implements an advanced line editor for editing longer texts -in-game. The editor mimics the command mechanisms of the VI editor as -far as possible. +in-game. The editor mimics the command mechanisms of the "VI" editor +(a famous line-by-line editor) as far as reasonable. Features of the editor: @@ -13,36 +13,99 @@ Features of the editor: - formatting of buffer, or selection, to certain width + indentations. - allow to echo the input or not, depending on your client. -Testing / Tutorial: +To use the editor, just import EvEditor from this module +and initialize it: -Whereas the editor is intended to be called from other commands that -requires more elaborate text editing of data, there is also a -stand-alone editor command for editing Attributes at the end of this -module. To test it it just import and add it to your default `cmdset`. + from evennia.utils.eveditor import EvEditor + + EvEditor(caller, + loadfunc=None, loadfunc_args=None, + savefunc=None, savefunc_args=None, + quitfunc=None, quitfunc_args=None, + key=""): + +Where the load/save/quitfunc are callbacks (with matching arguments) +to trigger when the editor loads, saves and quits respectively. """ import re from django.conf import settings from evennia import Command, CmdSet, utils -from evennia import syscmdkeys +from evennia.commands import cmdhandler -__all__ = ("CmdEditor", ) +# we use cmdhandler instead of evennia.syscmdkeys to +# avoid some cases of loading before evennia init'd +_CMD_NOMATCH = cmdhandler.CMD_NOMATCH +_CMD_NOINPUT = cmdhandler.CMD_NOINPUT -CMD_NOMATCH = syscmdkeys.CMD_NOMATCH -CMD_NOINPUT = syscmdkeys.CMD_NOINPUT - -RE_GROUP = re.compile(r"\".*?\"|\'.*?\'|\S*") +_RE_GROUP = re.compile(r"\".*?\"|\'.*?\'|\S*") +# use NAWS in the future? _DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH +#------------------------------------------------------------ +# +# texts +# +#------------------------------------------------------------ + +_HELP_TEXT = \ +""" + - any non-command is appended to the end of the buffer. + : - view buffer or only line + :: - view buffer without line numbers or other parsing + ::: - print a ':' as the only character on the line... + :h - this help. + + :w - saves the buffer (don't quit) + :wq - save buffer and quit + :q - quits (will be asked to save if buffer was changed) + :q! - quit without saving, no questions asked + + :u - (undo) step backwards in undo history + :uu - (redo) step forward in undo history + :UU - reset all changes back to initial + + :dd - delete line + :dw - delete word or regex in entire buffer or on line + :DD - clear buffer + + :y - yank (copy) line to the copy buffer + :x - cut line and store it in the copy buffer + :p - put (paste) previously copied line directly after + :i - insert new text at line . Old line will move down + :r - replace line with text + :I - insert text at the beginning of line + :A - append text after the end of line + + :s - search/replace word or regex in buffer or on line + + :f - flood-fill entire buffer or line + :fi - indent entire buffer or line + :fd - de-indent entire buffer or line + + :echo - turn echoing of the input on/off (helpful for some clients) + + Legend: + - line numbers, or range lstart:lend, e.g. '3:7'. + - one word or several enclosed in quotes. + - longer string, usually not needed to be enclosed in quotes. +""" + +#------------------------------------------------------------ +# +# Handle yes/no quit question +# +#------------------------------------------------------------ + class CmdSaveYesNo(Command): """ Save the editor state on quit. This catches nomatches (defaults to Yes), and avoid saves only if command was given specifically as "no" or "n". """ - key = CMD_NOMATCH - aliases = CMD_NOINPUT + key = _CMD_NOMATCH + aliases = _CMD_NOINPUT locks = "cmd:all()" help_cateogory = "LineEditor" @@ -72,6 +135,12 @@ class SaveYesNoCmdSet(CmdSet): self.add(CmdSaveYesNo()) +#------------------------------------------------------------ +# +# Editor commands +# +#------------------------------------------------------------ + class CmdEditorBase(Command): """ Base parent for editor commands @@ -107,7 +176,7 @@ class CmdEditorBase(Command): # will be kept together and extra whitespace preserved. You # can input quotes on the line by alternating single and # double quotes. - arglist = [part for part in RE_GROUP.findall(self.args) if part] + arglist = [part for part in _RE_GROUP.findall(self.args) if part] temp = [] for arg in arglist: # we want to clean the quotes, but only one type, @@ -186,8 +255,8 @@ class CmdLineInput(CmdEditorBase): """ No command match - Inputs line of text into buffer. """ - key = CMD_NOMATCH - aliases = [CMD_NOINPUT] + key = _CMD_NOMATCH + aliases = _CMD_NOINPUT def func(self): """ @@ -267,9 +336,6 @@ class CmdEditorGroup(CmdEditorBase): if self.editor.unsaved: caller.cmdset.add(SaveYesNoCmdSet) caller.msg("Save before quitting? {lcyes{lt[Y]{le/{lcno{ltN{le") - #prompt_yesno(caller, "Save before quitting?", - # yescode = "self.caller.ndb._lineeditor.save_buffer()\nself.caller.ndb._lineeditor.quit()", - # nocode = "self.caller.msg(self.caller.ndb._lineeditor.quit())", default="Y") else: string = editor.quit() elif cmd == ":q!": @@ -429,17 +495,23 @@ class CmdEditorGroup(CmdEditorBase): caller.msg(string) -class EditorCmdSet(CmdSet): +class EvEditorCmdSet(CmdSet): "CmdSet for the editor commands" key = "editorcmdset" mergetype = "Replace" +#------------------------------------------------------------ +# +# Main Editor object +# +#------------------------------------------------------------ -class LineEditor(object): +class EvEditor(object): """ This defines a line editor object. It creates all relevant commands and tracks the current state of the buffer. It also cleans up after itself. + """ def __init__(self, caller, @@ -448,23 +520,29 @@ class LineEditor(object): quitfunc=None, quitfunc_args=None, key=""): """ - caller - who is using the editor. + Args: + caller (Object): Who is using the editor. + loadfunc (callable, optional): This will be called as + `func(*loadfunc_args)` when the editor is first started, + e.g. for pre-loading text into it. + loadfunc_args (tuple, optional): Optional tuple of + arguments to supply to `loadfunc`. + savefunc (callable, optional): This will be called as + `func(*savefunc_args)` when the save-command is given and + is used to actually determine where/how result is saved. + It should return `True` if save was successful and also + handle any feedback to the user. + savefunc_args (tuple, optional): Optional tuple of + arguments to supply to `savefunc`. + quitfunc (callable, optional): This will optionally be + called as `func(*quitfunc_args)` when the editor is + exited. If defined, it should handle all wanted feedback + to the user. + quitfunc_args (tuple, optional): Optional tuple of arguments to + supply to `quitfunc`. + key (str, optional): An optional key for naming this + session and make it unique from other editing sessions. - loadfunc - this will be called as `func(*loadfunc_args)` when the - editor is first started, e.g. for pre-loading text into it. - loadfunc_args - optional tuple of arguments to supply to `loadfunc`. - savefunc - this will be called as `func(*savefunc_args)` when the - save-command is given and is used to actually determine - where/how result is saved. It should return `True` if save - was successful and also handle any feedback to the user. - savefunc_args - optional tuple of arguments to supply to `savefunc`. - quitfunc - this will optionally be called as `func(*quitfunc_args)` - when the editor is exited. If defined, it should handle - all wanted feedback to the user. - quitfunc_args - optional tuple of arguments to supply to `quitfunc`. - - key = an optional key for naming this session (such as which attribute - is being edited). """ self.key = key self.caller = caller @@ -497,7 +575,7 @@ class LineEditor(object): cmd2.obj = self cmd2.editor = self # Populate cmdset and add it to caller - editor_cmdset = EditorCmdSet() + editor_cmdset = EvEditorCmdSet() editor_cmdset.add(cmd1) editor_cmdset.add(cmd2) self.caller.cmdset.add(editor_cmdset) @@ -544,7 +622,7 @@ class LineEditor(object): except Exception, e: self.caller.msg("%s\n{Quit function gave an error. Skipping.{n" % e) del self.caller.ndb._lineeditor - self.caller.cmdset.remove(EditorCmdSet) + self.caller.cmdset.remove(EvEditorCmdSet) if self.quitfunc: # if quitfunc is defined, it should manage exit messages. return "" @@ -620,53 +698,15 @@ class LineEditor(object): """ Shows the help entry for the editor. """ - string = self.sep * _DEFAULT_WIDTH + """ - - any non-command is appended to the end of the buffer. -: - view buffer or only line -:: - view buffer without line numbers or other parsing -::: - print a ':' as the only character on the line... -:h - this help. - -:w - saves the buffer (don't quit) -:wq - save buffer and quit -:q - quits (will be asked to save if buffer was changed) -:q! - quit without saving, no questions asked - -:u - (undo) step backwards in undo history -:uu - (redo) step forward in undo history -:UU - reset all changes back to initial - -:dd - delete line -:dw - delete word or regex in entire buffer or on line -:DD - clear buffer - -:y - yank (copy) line to the copy buffer -:x - cut line and store it in the copy buffer -:p - put (paste) previously copied line directly after -:i - insert new text at line . Old line will move down -:r - replace line with text -:I - insert text at the beginning of line -:A - append text after the end of line - -:s - search/replace word or regex in buffer or on line - -:f - flood-fill entire buffer or line -:fi - indent entire buffer or line -:fd - de-indent entire buffer or line - -:echo - turn echoing of the input on/off (helpful for some clients) - - Legend: - - line numbers, or range lstart:lend, e.g. '3:7'. - - one word or several enclosed in quotes. - - longer string, usually not needed to be enclosed in quotes. -""" + self.sep * _DEFAULT_WIDTH + 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): """ @@ -722,11 +762,12 @@ class CmdEditor(Command): self.caller.msg("Exited Editor.") editor_key = "%s/%s" % (self.objname, self.attrname) - # start editor, it will handle things from here. - self.editor = utils.get_line_editor()( - self.caller, - loadfunc=load_attr, - savefunc=save_attr, - quitfunc=quit_hook, - key=editor_key - ) + + # 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) diff --git a/evennia/utils/evmenu.py b/evennia/utils/evmenu.py index 3eb2895e1b..505bc06c22 100644 --- a/evennia/utils/evmenu.py +++ b/evennia/utils/evmenu.py @@ -130,17 +130,19 @@ evennia.utils.evdemo`. from textwrap import dedent from inspect import isfunction, getargspec from django.conf import settings -from evennia import syscmdkeys from evennia import Command, CmdSet from evennia.utils.evtable import EvTable from evennia.utils.ansi import ANSIString, strip_ansi from evennia.utils.utils import mod_import, make_iter, pad, m_len +from evennia.commands import cmdhandler # read from protocol NAWS later? _MAX_TEXT_WIDTH = settings.CLIENT_DEFAULT_WIDTH -_CMD_NOMATCH = syscmdkeys.CMD_NOMATCH -_CMD_NOINPUT = syscmdkeys.CMD_NOINPUT +# we use cmdhandler instead of evennia.syscmdkeys to +# avoid some cases of loading before evennia init'd +_CMD_NOMATCH = cmdhandler.CMD_NOMATCH +_CMD_NOINPUT = cmdhandler.CMD_NOINPUT # Return messages diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index a73f7e70fa..6ce5fb1b7e 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -1226,10 +1226,3 @@ def m_len(target): if inherits_from(target, basestring): return len(ANSI_PARSER.strip_mxp(target)) return len(target) - - -def get_line_editor(): - """ - Get the line editor for this game. - """ - return variable_from_module(*settings.LINE_EDITOR.rsplit('.', 1))