Improve new text editor support to be production quality.
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
import renpy
|
||||
|
||||
# Do nothing when the editor is invoked.
|
||||
Editor = renpy.editor.Editor
|
||||
@@ -1,6 +0,0 @@
|
||||
# Name: None
|
||||
# Version: 1
|
||||
# Description: Disables the automatic launching of an editor.
|
||||
|
||||
config.editor = None
|
||||
config.editor_transient = None
|
||||
@@ -0,0 +1,4 @@
|
||||
import renpy
|
||||
|
||||
# Pass the file off to the system editor (as determined by file associations).
|
||||
Editor = renpy.editor.SystemEditor
|
||||
+39
-82
@@ -13,75 +13,54 @@ init python:
|
||||
if persistent.editor is None:
|
||||
persistent.editor = "jEdit"
|
||||
|
||||
# A map from editor name to the file containing information about
|
||||
# that editor.
|
||||
# Should we set up the editor?
|
||||
set_editor = "RENPY_EDIT_PY" not in os.environ
|
||||
|
||||
# A map from editor name to EditorInfo object.
|
||||
editors = { }
|
||||
|
||||
# A map from editor to the version of that editor.
|
||||
editor_versions = { }
|
||||
|
||||
# A map from editor to a description of that editor.
|
||||
editor_descriptions = { }
|
||||
|
||||
class EditorInfo(object):
|
||||
def __init__(self, filename):
|
||||
# The path to the editor info file.
|
||||
self.filename = filename
|
||||
|
||||
# The name of the editor.
|
||||
self.name = os.path.basename(filename)[:-len(".edit.py")]
|
||||
|
||||
# The time the editor file was last modified. We use this
|
||||
# to decide if we should update the editors mat when we
|
||||
# have multiple versions of an editor in contention.
|
||||
self.mtime = os.path.getmtime(filename)
|
||||
|
||||
# Should we set up the editor? How about the transient editor?
|
||||
set_editor = "RENPY_EDITOR" not in os.environ
|
||||
set_editor_transient = "RENPY_EDITOR_TRANSIENT" not in os.environ
|
||||
|
||||
if set_editor and not set_editor_transient:
|
||||
config.editor_transient = config.editor
|
||||
os.environ['RENPY_EDITOR_TRANSIENT'] = config.editor
|
||||
set_editor_transient = False
|
||||
|
||||
def scan_editor(ef):
|
||||
def scan_editor(filename):
|
||||
"""
|
||||
Inserts an editor into editors if there isn't a newer
|
||||
editor there already.
|
||||
"""
|
||||
Scans a single editor file to get the meta-information. If it
|
||||
checks out, adds it to editors. Uses editor_versions as a cache
|
||||
so we only add the newest version of each editor.
|
||||
"""
|
||||
|
||||
info = { }
|
||||
ei = EditorInfo(filename)
|
||||
|
||||
if ei.name in editors:
|
||||
if editors[ei.name].mtime >= ei.mtime:
|
||||
return
|
||||
|
||||
editors[ei.name] = ei
|
||||
|
||||
f = file(ef, "r")
|
||||
for l in f:
|
||||
m = re.match("#\s*(\w+):\s*(.*?)\s*$", l)
|
||||
if not m:
|
||||
break
|
||||
|
||||
info[m.group(1)] = m.group(2)
|
||||
|
||||
f.close()
|
||||
|
||||
try:
|
||||
name = info["Name"]
|
||||
version = int(info["Version"])
|
||||
description = info.get("Description", "")
|
||||
except:
|
||||
traceback.print_exc()
|
||||
print >>sys.stderr, ef
|
||||
|
||||
|
||||
if version > editor_versions.get(name, -1):
|
||||
editors[name] = ef
|
||||
editor_versions[name] = version
|
||||
editor_descriptions[name] = description
|
||||
|
||||
|
||||
def scan_editors():
|
||||
"""
|
||||
Finds all *.editor.py files, and uses them to populate the list
|
||||
of editors.
|
||||
"""
|
||||
Finds all *.edit.py files, and uses them to populate the list
|
||||
of editors.
|
||||
"""
|
||||
|
||||
editors.clear()
|
||||
editor_versions.clear()
|
||||
editor_descriptions.clear()
|
||||
|
||||
for d in [ config.renpy_base, persistent.projects_directory ]:
|
||||
if d is None:
|
||||
continue
|
||||
|
||||
for ef in glob.glob(d + "/*/*.editor.py"):
|
||||
scan_editor(ef)
|
||||
for filename in glob.glob(d + "/*/*.edit.py"):
|
||||
scan_editor(filename)
|
||||
|
||||
|
||||
def setup_editor():
|
||||
@@ -93,44 +72,24 @@ init python:
|
||||
if not set_editor:
|
||||
return
|
||||
|
||||
ef = None
|
||||
ei = None
|
||||
|
||||
for i in [ persistent.editor, "jEdit", "None" ]:
|
||||
if i in editors:
|
||||
ef = editors[i]
|
||||
ei = editors[i]
|
||||
break
|
||||
else:
|
||||
return
|
||||
|
||||
ctx = {
|
||||
"renpy" : renpy,
|
||||
"config" : config,
|
||||
"persistent" : persistent,
|
||||
"base" : os.path.dirname(renpy.fsencode(ef)),
|
||||
}
|
||||
|
||||
execfile(renpy.fsencode(ef), ctx, ctx)
|
||||
|
||||
if set_editor:
|
||||
if config.editor:
|
||||
os.environ['RENPY_EDITOR'] = config.editor
|
||||
else:
|
||||
if 'RENPY_EDITOR' in os.environ:
|
||||
del os.environ['RENPY_EDITOR']
|
||||
|
||||
if set_editor_transient:
|
||||
if config.editor_transient:
|
||||
os.environ['RENPY_EDITOR_TRANSIENT'] = config.editor_transient
|
||||
else:
|
||||
if 'RENPY_EDITOR_TRANSIENT' in os.environ:
|
||||
del os.environ['RENPY_EDITOR_TRANSIENT']
|
||||
os.environ["RENPY_EDIT_PY"] = renpy.fsencode(os.path.abspath(ei.filename))
|
||||
renpy.editor.init()
|
||||
|
||||
label editor:
|
||||
|
||||
python hide:
|
||||
|
||||
if not set_editor:
|
||||
error(_(u"The editor has been set from the RENPY_EDITOR environment variable, and cannot be changed."), "options")
|
||||
error(_(u"The editor has been set from the RENPY_EDIT_PY environment variable, and cannot be changed."), "options")
|
||||
|
||||
set_tooltip("")
|
||||
|
||||
@@ -147,9 +106,7 @@ label editor:
|
||||
ui.vbox()
|
||||
|
||||
for i in sorted(editors, key=lambda a : a.lower()):
|
||||
button(i,
|
||||
ui.returns(i),
|
||||
editor_descriptions[i])
|
||||
button(i, ui.returns(i))
|
||||
|
||||
ui.close() # Vbox
|
||||
ui.close() # Scrolled
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import os
|
||||
import renpy
|
||||
import subprocess
|
||||
|
||||
class Editor(renpy.editor.Editor):
|
||||
|
||||
def begin(self, new_window=False, **kwargs):
|
||||
|
||||
args = [ ]
|
||||
|
||||
mydir = os.path.dirname(__file__)
|
||||
jar = os.path.join(mydir, "../jedit/jedit.jar")
|
||||
|
||||
# My Java does not like having non-ASCII characters in jar paths.
|
||||
# Using relpath won't guarantee those characters won't exist - but it
|
||||
# makes them less likely in common use cases.
|
||||
jar = os.path.relpath(jar)
|
||||
|
||||
if renpy.windows:
|
||||
args = [ "javaw.exe", "-jar", jar ]
|
||||
else:
|
||||
args = [ "java", "-jar", jar ]
|
||||
|
||||
if new_window:
|
||||
args.append("-newplainview")
|
||||
else:
|
||||
args.append("-reuseview")
|
||||
|
||||
self.arguments = args
|
||||
|
||||
def open(self, filename, line=None, **kwargs):
|
||||
filename = renpy.exports.fsencode(filename)
|
||||
self.arguments.append(filename)
|
||||
|
||||
if line is not None:
|
||||
self.arguments.append("+line:{0}".format(line))
|
||||
|
||||
def end(self, **kwargs):
|
||||
print self.arguments
|
||||
subprocess.Popen(self.arguments)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
# Name: jEdit
|
||||
# Version: 1
|
||||
# Description: jEdit requires Java be installed on your computer.
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
|
||||
if sys.platform == 'win32':
|
||||
editor = os.path.normpath(base + "/../jedit/jedit.exe")
|
||||
editor = renpy.shell_escape(editor)
|
||||
config.editor = '"' + editor + '" -reuseview "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
config.editor_transient = '"' + editor + '" -newplainview "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
else:
|
||||
editor = os.path.normpath(base + "/../jedit/jedit.jar")
|
||||
editor = renpy.shell_escape(editor)
|
||||
config.editor = 'java -jar "' + editor + '" -reuseview "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
config.editor_transient = 'java -jar "' + editor + '" -newplainview "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
+1
-1
@@ -11,7 +11,7 @@ label options:
|
||||
|
||||
editor = persistent.editor
|
||||
if not set_editor:
|
||||
editor = _(u"Using RENPY_EDITOR")
|
||||
editor = _(u"Using RENPY_EDIT_PY")
|
||||
|
||||
text_variable(_("Text Editor"), editor, "editor",
|
||||
_(u"Change the default text editor."))
|
||||
|
||||
@@ -254,7 +254,7 @@ label edit_script:
|
||||
|
||||
python hide:
|
||||
|
||||
if not config.editor:
|
||||
if not "RENPY_EDIT_PY" in os.environ:
|
||||
error(_(u"No editor has been selected."))
|
||||
|
||||
files = [ project.gamedir + "/" + i for i in os.listdir(project.gamedir) if i.endswith(".rpy") if not i[0] == "."]
|
||||
|
||||
+6
-6
@@ -284,16 +284,16 @@ framerate = 100
|
||||
# The number of frames that Ren'Py has shown.
|
||||
frames = 0
|
||||
|
||||
# A text editor that is launched at the location of the current
|
||||
# NOT USED: A text editor that is launched at the location of the current
|
||||
# statement.
|
||||
editor = os.environ.get('RENPY_EDITOR', None)
|
||||
editor = None # os.environ.get('RENPY_EDITOR', None)
|
||||
|
||||
# Text editor, with arguments to reload or clobber the file - used,
|
||||
# NOT USED: Text editor, with arguments to reload or clobber the file - used,
|
||||
# for example, to display traceback.txt.
|
||||
editor_transient = os.environ.get('RENPY_EDITOR_TRANSIENT', editor)
|
||||
editor_transient = None # os.environ.get('RENPY_EDITOR_TRANSIENT', editor)
|
||||
|
||||
# The separator used between files in the text editor.
|
||||
editor_file_separator = os.environ.get('RENPY_EDITOR_FILE_SEPARATOR', '" "')
|
||||
# NOT USED: The separator used between files in the text editor.
|
||||
editor_file_separator = None # os.environ.get('RENPY_EDITOR_FILE_SEPARATOR', '" "')
|
||||
|
||||
# Enable developer mode?
|
||||
developer = False
|
||||
|
||||
+39
-56
@@ -45,44 +45,43 @@ class Editor(object):
|
||||
Each operation takes a path to operate on. If the editor has a buffer
|
||||
corresponding to that path, that buffer is used. Otherwise, the editor
|
||||
is implicitly opened.
|
||||
|
||||
We reserve the right to add new keyword arguments to methods of this class,
|
||||
so please ensure that subclasses accept and ignore unknown keyword
|
||||
arguments.
|
||||
"""
|
||||
|
||||
def open(self, path): #@ReservedAssignment
|
||||
"""
|
||||
Ensures `path` is open in the editor.
|
||||
"""
|
||||
|
||||
def reopen(self, path):
|
||||
"""
|
||||
Causes the editor to reopen the file at `path`.
|
||||
"""
|
||||
|
||||
def line(self, path, number):
|
||||
"""
|
||||
Moves the cursor for `path` to `line`. Lines in a file are numbered
|
||||
starting with 1.
|
||||
"""
|
||||
|
||||
def focus(self, path):
|
||||
"""
|
||||
Focuses `path`, which means that the buffer containing it should be
|
||||
presented to the user. Ideally, the window containing it will also
|
||||
pop to the top of the OS's window stack.
|
||||
"""
|
||||
|
||||
def begin(self):
|
||||
def begin(self, new_window=False, **kwargs):
|
||||
"""
|
||||
Begins an editor transaction.
|
||||
|
||||
`new_window`
|
||||
If True, a new editor window will be created and presented to the
|
||||
user. Otherwise, and existing editor window will be used.
|
||||
"""
|
||||
|
||||
def end(self):
|
||||
def end(self, **kwargs):
|
||||
"""
|
||||
Ends an editor transaction.
|
||||
"""
|
||||
|
||||
def open(self, filename, line=None, **kwargs): #@ReservedAssignment
|
||||
"""
|
||||
Ensures `path` is open in the editor. This may be called multiple
|
||||
times per transaction.
|
||||
|
||||
`line`
|
||||
If not None, this should be a line number to open in the
|
||||
editor.
|
||||
|
||||
The first open call in a transaction is somewhat special - that file
|
||||
should be given focus in a tabbed editor environment.
|
||||
"""
|
||||
|
||||
|
||||
class SystemEditor(Editor):
|
||||
|
||||
def open(self, filename): #@ReservedAssignment
|
||||
def open(self, filename, line=None, **kwargs): #@ReservedAssignment
|
||||
|
||||
filename = renpy.exports.fsencode(filename)
|
||||
|
||||
@@ -104,66 +103,50 @@ editor = None
|
||||
|
||||
def init():
|
||||
"""
|
||||
Creates the editor object, based on the contents of the RENPY_EDITOR_PY
|
||||
Creates the editor object, based on the contents of the RENPY_EDIT_PY
|
||||
file.
|
||||
"""
|
||||
|
||||
global editor
|
||||
editor = SystemEditor()
|
||||
|
||||
path = os.environ.get("RENPY_EDITOR_PY", None)
|
||||
path = os.environ.get("RENPY_EDIT_PY", None)
|
||||
if path is None:
|
||||
return
|
||||
|
||||
try:
|
||||
f = file(path, "rU")
|
||||
code = f.read()
|
||||
f.close()
|
||||
except:
|
||||
raise Exception("{0} could not be opened.".format(path))
|
||||
|
||||
scope = { }
|
||||
exec code in scope
|
||||
scope = { "__file__" : path }
|
||||
execfile(path, scope, scope)
|
||||
|
||||
if "editor" in scope:
|
||||
editor = scope["editor"]
|
||||
if "Editor" in scope:
|
||||
editor = scope["Editor"]()
|
||||
return
|
||||
|
||||
raise Exception("{0} did not create an editor variable.".format(path))
|
||||
raise Exception("{0} did not define an Editor class.".format(path))
|
||||
|
||||
def launch_editor(filenames, line=1, transient=False):
|
||||
"""
|
||||
Causes the editor to be launched.
|
||||
"""
|
||||
|
||||
if editor is None:
|
||||
init()
|
||||
|
||||
if editor is None:
|
||||
return False
|
||||
|
||||
filenames = [ renpy.parser.unelide_filename(i) for i in filenames ]
|
||||
|
||||
try:
|
||||
|
||||
editor.begin()
|
||||
editor.begin(new_window=transient)
|
||||
|
||||
for i in filenames:
|
||||
|
||||
if transient:
|
||||
editor.open(i)
|
||||
else:
|
||||
editor.reopen(i)
|
||||
|
||||
editor.line(filenames[0], line)
|
||||
editor.focus(filenames[0])
|
||||
editor.open(i, line)
|
||||
line = None # The line number only applies to the first filename.
|
||||
|
||||
editor.end()
|
||||
|
||||
return True
|
||||
|
||||
except:
|
||||
|
||||
if renpy.config.debug:
|
||||
raise
|
||||
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
init()
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
@@ -1676,6 +1677,8 @@ def set_physical_size(size):
|
||||
|
||||
def fsencode(s):
|
||||
"""
|
||||
:doc: other
|
||||
|
||||
Converts s from unicode to the filesystem encoding.
|
||||
"""
|
||||
|
||||
@@ -1687,6 +1690,8 @@ def fsencode(s):
|
||||
|
||||
def fsdecode(s):
|
||||
"""
|
||||
:doc: other
|
||||
|
||||
Converts s from filesystem encoding to unicode.
|
||||
"""
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ The new RAPT tool makes it far easier to package a Ren'Py game for Android.
|
||||
It can semi-automatically set up an Android build environment on your
|
||||
system, build a package, and install that package on your Android device.
|
||||
|
||||
To fix some editor-related problems, backported the 6.14 editor system. This
|
||||
changes how editors are configured. Please see :ref:`text-editor-integration`
|
||||
for a description of the new system.
|
||||
|
||||
The new :var:`config.save_dump` variable causes Ren'Py to write out
|
||||
save_dump.txt each time it saves. This file describes the contents of the
|
||||
save, making it possible to figure out what's causing an overly large save
|
||||
|
||||
@@ -565,59 +565,6 @@ Rarely or Internally Used
|
||||
|
||||
A list of layers that are cleared when entering a new context.
|
||||
|
||||
.. var:: config.editor = None
|
||||
|
||||
If not None, this is expected to be a command line for an editor
|
||||
that is invoked when the launch_editor (normally shift-E) key is
|
||||
pressed. The following substitutions make sense here:
|
||||
|
||||
%(filename)s
|
||||
The filename of the most interesting file to be edited. This
|
||||
is the file that should be shown to the user.
|
||||
|
||||
%(line)d
|
||||
The line number of the most interesting file to show to the user.
|
||||
|
||||
%(otherfiles)s
|
||||
Other, less-interesting files to show to the user.
|
||||
|
||||
%(allfiles)s
|
||||
All the files.
|
||||
|
||||
|
||||
Filename, otherfiles, and allfiles have shell-relevant characters
|
||||
escaped with backslashes. otherfiles and allfiles separate files
|
||||
with config.editor_file_separator (by default '" "', a
|
||||
double-quote, a space, and a quote). Since all filenames should be
|
||||
enclosed in double-quotes, this means that otherfiles and allfiles
|
||||
will create several quoted files. If two double-quotes occur in a
|
||||
row the string, then they are both removed. (This allows an empty
|
||||
allfiles to be used.)
|
||||
|
||||
A reasonable example is::
|
||||
|
||||
init python:
|
||||
config.editor = 'myeditor "%(filename)s" +line:%(line)d "%(otherfiles)s"'
|
||||
|
||||
This defaults to the value of the RENPY_EDITOR environment
|
||||
variable. If not defined by that variable or user code, this is
|
||||
set automatically by the Ren'Py launcher.
|
||||
|
||||
.. var:: config.editor_file_separator = '" "'
|
||||
|
||||
The separator used between filenames when lists of files are
|
||||
provided to the editor.
|
||||
|
||||
.. var:: config.editor_transient = None
|
||||
|
||||
If not None, this is expected to be a command line for an editor
|
||||
that is invoked on transient files, such as lint results, parse
|
||||
errors, and tracebacks. Substitutions are as for config.editor.
|
||||
|
||||
This defaults to the value of the RENPY_EDITOR_TRANSIENT
|
||||
environment variable. If not defined by that variable or user
|
||||
code, this is set automatically by the Ren'Py launcher.
|
||||
|
||||
.. var:: config.fade_music = 0.0
|
||||
|
||||
This is the amount of time in seconds to spend fading the old
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
.. _text-editor-integration:
|
||||
|
||||
=======================
|
||||
Text Editor Integration
|
||||
=======================
|
||||
|
||||
Ren'Py uses a text editor to allow the user to edit game scripts from the
|
||||
launcher, and to report errors to the user. By default, Ren'Py uses jEdit
|
||||
as the text editor when launched from the launcher and the system default
|
||||
editor otherwise. This can be customized by the user as necessary.
|
||||
|
||||
The editor is customized by creating an Editor class in a .edit.py file. This
|
||||
class contains methods that are called to manage text editing.
|
||||
|
||||
When run directly, Ren'Py first looks at the RENPY_EDIT_PY environment
|
||||
variable to find an .edit.py file to use. If it can find one, it uses the
|
||||
Editor class defined in that file. If not, it uses a built-in editor class
|
||||
that launches the editor in a system-specific manner.
|
||||
|
||||
When the Ren'Py Launcher is run, it scans subdirectories of the projects
|
||||
directory and Ren'Py directory to find files of the form `name`.edit.py. (For
|
||||
example, it would find launcher/jEdit.edit.py and myeditor/MyEditor.edit.py.)
|
||||
The latest editor with a given `name` is presented to the creator as part of
|
||||
the launcher options. The launcher also sets RENPY_EDIT_PY to the selected
|
||||
file, so that games launched from the launcher will use the selected editor.
|
||||
|
||||
|
||||
Writing an .edit.py File
|
||||
------------------------
|
||||
|
||||
An edit.py file is a Python (not Ren'Py) file that must define a single
|
||||
class, named Editor. Ren'Py will call methods on this class to cause
|
||||
editing to occur.
|
||||
|
||||
Use of the editor is done as part of an editor transaction, which groups
|
||||
related operations together. For example, if an editor transaction asks
|
||||
for a new window, all of the files in that transaction should be opened
|
||||
in the same new window. An editor transaction starts with a call to the
|
||||
begin method, may contain one or more calls to operation methods, and ends
|
||||
with a call to the end method.
|
||||
|
||||
The edit.py file should import renpy.editor, and the Editor class should
|
||||
inherit from renpy.editor.Editor. As additional keyword arguments may be
|
||||
added to methods, each method you define should ignore unknown keyword
|
||||
arguments. Since you're expected to define your own
|
||||
Editor subclass, we present the methods with the `self` parameter.
|
||||
|
||||
.. class:: Editor
|
||||
|
||||
.. method:: begin(self, new_window=False, **kwargs)
|
||||
|
||||
Starts an editor transaction.
|
||||
|
||||
If `new_window` is true, the editor should attempt to open a new window.
|
||||
Otherwise, it should attempt to perform the transaction in an existing editor
|
||||
window.
|
||||
|
||||
.. method:: end(self, **kwargs)
|
||||
|
||||
Ends a transaction.
|
||||
|
||||
.. method:: open(self, filename, line=None, **kwargs)
|
||||
|
||||
Opens a `filename` in the editor.
|
||||
|
||||
If `line` is not None, attempts to position the editing cursor at `line`.
|
||||
@@ -6,6 +6,14 @@
|
||||
displayable. If it can, it will return them as a (x, y, w, h)
|
||||
tuple. If not, it will return a (None, None, None, None) tuple.
|
||||
|
||||
.. function:: renpy.fsdecode(s)
|
||||
|
||||
Converts s from filesystem encoding to unicode.
|
||||
|
||||
.. function:: renpy.fsencode(s)
|
||||
|
||||
Converts s from unicode to the filesystem encoding.
|
||||
|
||||
.. function:: renpy.get_physical_size()
|
||||
|
||||
Returns the size of the physical window.
|
||||
|
||||
+11
-3
@@ -12,7 +12,7 @@ Much of Ren'Py is only documented in the older documentation, which is
|
||||
stored in the Ren'Py Wiki:
|
||||
|
||||
http://www.renpy.org/wiki/
|
||||
|
||||
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
@@ -69,7 +69,7 @@ Advanced Displayables
|
||||
|
||||
Python and Ren'Py
|
||||
-----------------
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
@@ -88,7 +88,6 @@ Other Platforms
|
||||
|
||||
android
|
||||
|
||||
|
||||
End-User Documentation
|
||||
----------------------
|
||||
|
||||
@@ -97,6 +96,15 @@ End-User Documentation
|
||||
|
||||
display_problems
|
||||
|
||||
|
||||
Engine Developer Documentation
|
||||
------------------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
editor
|
||||
|
||||
Changes and License
|
||||
-------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user