mirror of
https://github.com/evennia/evennia.git
synced 2026-03-22 07:46:30 +01:00
Add the 'code' keyword to EvEditor, with basic handling of indentation
This commit is contained in:
parent
fe89f2eb52
commit
bcba90f27a
1 changed files with 156 additions and 8 deletions
|
|
@ -31,11 +31,15 @@ and initialize it:
|
|||
no automatic quit messages will be given.
|
||||
- key is an optional identifier for the editing session, to be
|
||||
displayed in the editor.
|
||||
- persistent means the editor state will be saved to the database making it
|
||||
- persistent means the editor state will be saved to the database making it
|
||||
survive a server reload. Note that using this mode, the load- save-
|
||||
and quit-funcs must all be possible to pickle - notable unusable
|
||||
callables are class methods and functions defined inside other
|
||||
functions. With persistent=False, no such restriction exists.
|
||||
- code set to True activates features on the EvEditor to enter Python code.
|
||||
|
||||
In addition, the EvEditor can be used to enter Python source code,
|
||||
and offers basic handling of indentation.
|
||||
|
||||
"""
|
||||
from builtins import object
|
||||
|
|
@ -99,13 +103,23 @@ _HELP_TEXT = \
|
|||
:fd <l> - de-indent entire buffer or line <l>
|
||||
|
||||
:echo - turn echoing of the input on/off (helpful for some clients)
|
||||
"""
|
||||
|
||||
_HELP_LEGEND = \
|
||||
"""
|
||||
Legend:
|
||||
<l> - line number, like '5' or range, like '3:7'.
|
||||
<w> - a single word, or multiple words with quotes around them.
|
||||
<txt> - longer string, usually not needing quotes.
|
||||
"""
|
||||
|
||||
_HELP_CODE = \
|
||||
"""
|
||||
:- - Decrease the level of automatic indentation for the next lines
|
||||
:+ - Increase the level of automatic indentation for the next lines
|
||||
:= - Switch automatic indentation on/off
|
||||
""".strip("\n")
|
||||
|
||||
_ERROR_LOADFUNC = \
|
||||
"""
|
||||
{error}
|
||||
|
|
@ -321,6 +335,8 @@ def _load_editor(caller):
|
|||
saved_options = caller.attributes.get("_eveditor_saved")
|
||||
saved_buffer, saved_undo = caller.attributes.get("_eveditor_buffer_temp", (None, None))
|
||||
unsaved = caller.attributes.get("_eveditor_unsaved", False)
|
||||
code = caller.attributes.get("_eveditor_code", False)
|
||||
indent = caller.attributes.get("_eveditor_indent", 0)
|
||||
if saved_options:
|
||||
eveditor = EvEditor(caller, **saved_options[0])
|
||||
if saved_buffer:
|
||||
|
|
@ -330,6 +346,8 @@ def _load_editor(caller):
|
|||
setattr(eveditor, "_undo_buffer", saved_undo)
|
||||
setattr(eveditor, "_undo_pos", len(saved_undo) - 1)
|
||||
setattr(eveditor, "_unsaved", unsaved)
|
||||
setattr(eveditor, "_code", code)
|
||||
setattr(eveditor, "_indent", indent)
|
||||
for key, value in saved_options[1].iteritems():
|
||||
setattr(eveditor, key, value)
|
||||
else:
|
||||
|
|
@ -347,6 +365,9 @@ class CmdLineInput(CmdEditorBase):
|
|||
def func(self):
|
||||
"""
|
||||
Adds the line without any formatting changes.
|
||||
|
||||
If the editor handles code, it might add automatic
|
||||
indentation.
|
||||
"""
|
||||
caller = self.caller
|
||||
editor = caller.ndb._eveditor
|
||||
|
|
@ -354,15 +375,34 @@ class CmdLineInput(CmdEditorBase):
|
|||
buf = editor.get_buffer()
|
||||
|
||||
# add a line of text to buffer
|
||||
if not buf:
|
||||
buf = self.args
|
||||
line = self.args
|
||||
if not editor._code:
|
||||
if not buf:
|
||||
buf = self.args
|
||||
else:
|
||||
buf = buf + "\n%s" % self.args
|
||||
else:
|
||||
buf = buf + "\n%s" % self.args
|
||||
# if automatic indentation is active, add spaces
|
||||
if editor._indent >= 0:
|
||||
line = editor.deduce_indent(line, buf)
|
||||
else:
|
||||
line = self.args
|
||||
|
||||
if not buf:
|
||||
buf = line
|
||||
else:
|
||||
buf = buf + "\n%s" % line
|
||||
self.editor.update_buffer(buf)
|
||||
if self.editor._echo_mode:
|
||||
# need to do it here or we will be off one line
|
||||
cline = len(self.editor.get_buffer().split('\n'))
|
||||
self.caller.msg("{b%02i|{n %s" % (cline, self.args))
|
||||
if editor._code:
|
||||
# display the current level of identation
|
||||
self.caller.msg("{b%02i|{n ({g%d{n) %s" % (
|
||||
cline, editor._indent, line))
|
||||
else:
|
||||
self.caller.msg("{b%02i|{n %s" % (cline, self.args))
|
||||
|
||||
|
||||
|
||||
class CmdEditorGroup(CmdEditorBase):
|
||||
|
|
@ -372,7 +412,8 @@ class CmdEditorGroup(CmdEditorBase):
|
|||
key = ":editor_command_group"
|
||||
aliases = [":","::", ":::", ":h", ":w", ":wq", ":q", ":q!", ":u", ":uu", ":UU",
|
||||
":dd", ":dw", ":DD", ":y", ":x", ":p", ":i", ":j",
|
||||
":r", ":I", ":A", ":s", ":S", ":f", ":fi", ":fd", ":echo"]
|
||||
":r", ":I", ":A", ":s", ":S", ":f", ":fi", ":fd", ":echo",
|
||||
":-", "-+", "-="]
|
||||
arg_regex = r"\s.*?|$"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -602,6 +643,41 @@ class CmdEditorGroup(CmdEditorBase):
|
|||
# set echoing on/off
|
||||
editor._echo_mode = not editor._echo_mode
|
||||
caller.msg("Echo mode set to %s" % editor._echo_mode)
|
||||
elif cmd == ":-":
|
||||
# :-
|
||||
if editor._code:
|
||||
editor.decrease_indent()
|
||||
indent = editor._indent
|
||||
if indent >= 0:
|
||||
caller.msg("Decreased indentation: new indentation is {}.".format(
|
||||
indent))
|
||||
else:
|
||||
caller.msg("|rManual indentation is OFF.|n Use := to turn it on.")
|
||||
else:
|
||||
caller.msg("This is not a code editor, you cannot use this option.")
|
||||
elif cmd == ":+":
|
||||
# :+
|
||||
if editor._code:
|
||||
editor.increase_indent()
|
||||
indent = editor._indent
|
||||
if indent >= 0:
|
||||
caller.msg("Increased indentation: new indentation is {}.".format(
|
||||
indent))
|
||||
else:
|
||||
caller.msg("|rManual indentation is OFF.|n Use := to turn it on.")
|
||||
else:
|
||||
caller.msg("This is not a code editor, you cannot use this option.")
|
||||
elif cmd == ":=":
|
||||
# :=
|
||||
if editor._code:
|
||||
editor.swap_autoindent()
|
||||
indent = editor._indent
|
||||
if indent >= 0:
|
||||
caller.msg("Auto-indentation turned on.")
|
||||
else:
|
||||
caller.msg("Auto-indentation turned off.")
|
||||
else:
|
||||
caller.msg("This is not a code editor, you cannot use this option.")
|
||||
|
||||
|
||||
class EvEditorCmdSet(CmdSet):
|
||||
|
|
@ -627,7 +703,7 @@ class EvEditor(object):
|
|||
"""
|
||||
|
||||
def __init__(self, caller, loadfunc=None, savefunc=None,
|
||||
quitfunc=None, key="", persistent=False):
|
||||
quitfunc=None, key="", persistent=False, code=False):
|
||||
"""
|
||||
Launches a full in-game line editor, mimicking the functionality of VIM.
|
||||
|
||||
|
|
@ -651,6 +727,7 @@ class EvEditor(object):
|
|||
session and make it unique from other editing sessions.
|
||||
persistent (bool, optional): Make the editor survive a reboot. Note
|
||||
that if this is set, all callables must be possible to pickle
|
||||
code (bool, optional): activate options in a code-like editor
|
||||
|
||||
Notes:
|
||||
In persistent mode, all the input callables (savefunc etc)
|
||||
|
|
@ -668,6 +745,8 @@ class EvEditor(object):
|
|||
self._buffer = ""
|
||||
self._unsaved = False
|
||||
self._persistent = persistent
|
||||
self._code = code
|
||||
self._indent = 0
|
||||
|
||||
if loadfunc:
|
||||
self._loadfunc = loadfunc
|
||||
|
|
@ -705,6 +784,8 @@ class EvEditor(object):
|
|||
"_sep": self._sep}))
|
||||
caller.attributes.add("_eveditor_buffer_temp", (self._buffer, self._undo_buffer))
|
||||
caller.attributes.add("_eveditor_unsaved", False)
|
||||
caller.attributes.add("_eveditor_code", code)
|
||||
caller.attributes.add("_eveditor_indent", 0)
|
||||
except Exception, err:
|
||||
caller.msg(_ERROR_PERSISTENT_SAVING.format(error=err))
|
||||
logger.log_trace(_TRACE_PERSISTENT_SAVING)
|
||||
|
|
@ -760,6 +841,8 @@ class EvEditor(object):
|
|||
if self._persistent:
|
||||
self._caller.attributes.add("_eveditor_buffer_temp", (self._buffer, self._undo_buffer))
|
||||
self._caller.attributes.add("_eveditor_unsaved", True)
|
||||
self._caller.attributes.add("_eveditor_code", self._code)
|
||||
self._caller.attributes.add("_eveditor_indent", self._indent)
|
||||
|
||||
def quit(self):
|
||||
"""
|
||||
|
|
@ -774,6 +857,8 @@ class EvEditor(object):
|
|||
self._caller.attributes.remove("_eveditor_buffer_temp")
|
||||
self._caller.attributes.remove("_eveditor_saved")
|
||||
self._caller.attributes.remove("_eveditor_unsaved")
|
||||
self._caller.attributes.remove("_eveditor_code")
|
||||
self._caller.attributes.remove("_eveditor_indent")
|
||||
self._caller.cmdset.remove(EvEditorCmdSet)
|
||||
|
||||
def save_buffer(self):
|
||||
|
|
@ -865,5 +950,68 @@ class EvEditor(object):
|
|||
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
|
||||
if self.code:
|
||||
string += _HELP_CODE
|
||||
string += _HELP_LEGEND + self._sep * _DEFAULT_WIDTH
|
||||
self._caller.msg(string)
|
||||
|
||||
def deduce_indent(self, line, buffer):
|
||||
"""
|
||||
Try to deduce the level of indentation of the given line.
|
||||
|
||||
"""
|
||||
keywords = {
|
||||
"elif ": ["if "],
|
||||
"else:": ["if ", "try"],
|
||||
"except": ["try:"],
|
||||
"finally:": ["try:"],
|
||||
}
|
||||
opening_tags = ("if ", "try:", "for ", "while ")
|
||||
|
||||
# If the line begins by one of the given keywords
|
||||
indent = self._indent
|
||||
if any(line.startswith(kw) for kw in keywords.keys()):
|
||||
# Get the keyword and matching begin tags
|
||||
keyword = [kw for kw in keywords if line.startswith(kw)][0]
|
||||
begin_tags = keywords[keyword]
|
||||
for oline in reversed(buffer.splitlines()):
|
||||
if any(oline.lstrip(" ").startswith(tag) for tag in begin_tags):
|
||||
# This line begins with a begin tag, takes the identation
|
||||
indent = (len(oline) - len(oline.lstrip(" "))) / 4
|
||||
break
|
||||
|
||||
self._indent = indent + 1
|
||||
if self._persistent:
|
||||
self._caller.attributes.add("_eveditor_indent", self._indent)
|
||||
elif any(line.startswith(kw) for kw in opening_tags):
|
||||
self._indent = indent + 1
|
||||
if self._persistent:
|
||||
self._caller.attributes.add("_eveditor_indent", self._indent)
|
||||
|
||||
line = " " * 4 * indent + line
|
||||
return line
|
||||
|
||||
def decrease_indent(self):
|
||||
"""Decrease automatic indentation by 1 level."""
|
||||
if self._code and self._indent > 0:
|
||||
self._indent -= 1
|
||||
if self._persistent:
|
||||
self._caller.attributes.add("_eveditor_indent", self._indent)
|
||||
|
||||
def increase_indent(self):
|
||||
"""Increase automatic indentation by 1 level."""
|
||||
if self._code and self._indent >= 0:
|
||||
self._indent += 1
|
||||
if self._persistent:
|
||||
self._caller.attributes.add("_eveditor_indent", self._indent)
|
||||
def swap_autoindent(self):
|
||||
"""Swap automatic indentation on or off."""
|
||||
if self._code:
|
||||
if self._indent >= 0:
|
||||
self._indent = -1
|
||||
else:
|
||||
self._indent = 0
|
||||
|
||||
if self._persistent:
|
||||
self._caller.attributes.add("_eveditor_indent", self._indent)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue