Add documentation generator.

This commit is contained in:
Patman64
2014-08-10 03:36:18 -04:00
parent eb2b9d78b9
commit a693bcae7e
24 changed files with 2043 additions and 0 deletions

2
docs/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
# Ignore the temporary "build" folder.
build

2
docs/ElunaDoc/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.pyc
.idea

View File

162
docs/ElunaDoc/__main__.py Normal file
View File

@@ -0,0 +1,162 @@
import os
import shutil
from types import FileType
from jinja2 import Environment, FileSystemLoader
from typedecorator import params, returns
from parser import ClassParser, MethodDoc
import glob
@returns([(str, FileType)])
@params(search_path=str)
def find_class_files(search_path):
"""Find and open all files containing Eluna class methods in `search_path`.
:param search_path: the path to search for Eluna methods in
:return: a list of all files containing Eluna methods, and the name of their respective classes
"""
# Get the current working dir and switch to the search path.
old_dir = os.getcwd()
os.chdir(search_path)
# Search for all files ending in "Methods.h".
method_file_names = glob.glob('*Methods.h')
# Open each file.
method_files = [open(file_name, 'r') for file_name in method_file_names]
# Go back to where we were before.
os.chdir(old_dir)
return method_files
def make_renderer(template_path, link_parser_factory):
"""Return a function that can be used to render Jinja2 templates from the `template_path` directory."""
# Set up jinja2 environment to load templates from the templates folder.
env = Environment(loader=FileSystemLoader(template_path), extensions=['jinja2.ext.with_'])
def inner(template_name, output_path, level, **kwargs):
env.filters['parse_links'], env.filters['parse_data_type'] = link_parser_factory(level)
template = env.get_template(template_name)
static = make_static(level)
root = make_root(level)
with open('build/' + output_path, 'w') as out:
out.write(template.render(static=static, root=root, **kwargs))
return inner
def make_static(level):
return lambda file_name: ('../' * level) + 'static/' + file_name
def make_root(level):
return lambda file_name: ('../' * level) + file_name
if __name__ == '__main__':
# Recreate the build folder and copy static files over.
if os.path.exists('build'):
shutil.rmtree('build')
os.mkdir('build')
shutil.copytree('ElunaDoc/static', 'build/static')
# Load up all files with methods we need to parse.
print 'Finding Eluna method files...'
class_files = find_class_files('../')
# Parse all the method files.
classes = []
for f in class_files:
print 'Parsing file {}...'.format(f.name)
classes.append(ClassParser.parse_file(f))
f.close()
# Sort the classes so they are in the correct order in lists.
classes.sort(key=lambda c: c.name)
def make_parsers(level):
"""Returns a function that parses content for refs to other classes, methods, or enums,
and automatically inserts the correct link.
"""
# Make a list of all class names and method names.
class_names = []
method_names = []
for class_ in classes:
class_names.append('&' + class_.name)
for method in class_.methods:
method_names.append('&' + class_.name + ':' + method.name)
def link_parser(content):
# Split the content into small tokens.
tokens = content.split()
# The content will be reassembled from the parsed tokens.
content = ''
for token in tokens:
if token in class_names:
# Take the "&" off the front of the class's name.
class_name = token[len('&'):]
url = '{}{}/index.html'.format(('../' * level), class_name)
token = '<a class="mod" href="{}">{}</a>'.format(url, class_name)
elif token in method_names:
# Take the "amp;" off the front of the method's name.
full_name = token[len('&amp;'):]
# Split "Class:Method" into "Class" and "Method".
class_name, method_name = full_name.split(':')
url = '{}{}/{}.html'.format(('../' * level), class_name, method_name)
token = '<a class="fn" href="{}">{}</a>'.format(url, full_name)
content += token + ' '
return content
# Links to the "Programming in Lua" documentation for each Lua type.
lua_type_documentation = {
'nil': 'http://www.lua.org/pil/2.1.html',
'boolean': 'http://www.lua.org/pil/2.2.html',
'number': 'http://www.lua.org/pil/2.3.html',
'string': 'http://www.lua.org/pil/2.4.html',
'table': 'http://www.lua.org/pil/2.5.html',
'function': 'http://www.lua.org/pil/2.6.html',
}
def data_type_parser(content):
# If the type is a Lua type, return a link to Lua documentation.
if content in lua_type_documentation:
url = lua_type_documentation[content]
return '<strong><a href="{}">{}</a></strong>'.format(url, content)
# Otherwise try to build a link to the proper page.
if content in class_names:
class_name = content[len('&amp;'):]
url = '{}{}/index.html'.format(('../' * level), class_name)
return '<strong><a class="mod" href="{}">{}</a></strong>'.format(url, class_name)
return link_parser, data_type_parser
# Create the render function with the template path and parser maker.
render = make_renderer('ElunaDoc/templates', make_parsers)
# Render the index.
render('index.html', 'index.html', level=0, classes=classes)
# Render the search index.
render('search-index.js', 'search-index.js', level=0, classes=classes)
for class_ in classes:
print 'Rending pages for class {}...'.format(class_.name)
# Make a folder for the class.
os.mkdir('build/' + class_.name)
index_path = '{}/index.html'.format(class_.name)
# Render the class's index page.
render('class.html', index_path, level=1, classes=classes, current_class=class_)
# Render each method's page.
for method in class_.methods:
method_path = '{}/{}.html'.format(class_.name, method.name)
render('method.html', method_path, level=1, current_class=class_, current_method=method)

242
docs/ElunaDoc/parser.py Normal file
View File

@@ -0,0 +1,242 @@
import re
from types import FileType
import markdown
from typedecorator import params, returns, Nullable
class ParameterDoc(object):
"""The documentation data of a parameter or return value for an Eluna method."""
# The integer ranges that each C++ type is valid for. None means valid for all numbers.
valid_ranges = {
'float': None,
'double': None,
'int': ('-2,147,483,647', '2,147,483,647'), # This should be -32767..32767, but it's pretty safe to assume 32-bit.
'int8': ('-127', '127'),
'uint8': ('0', '255'),
'int16': ('-32,767', '32,767'),
'uint16': ('0', '65,535'),
'int32': ('-2,147,483,647', '2,147,483,647'),
'uint32': ('0', '4,294,967,295'),
}
@params(self=object, name=unicode, data_type=str, description=unicode, default_value=Nullable(unicode))
def __init__(self, name, data_type, description, default_value=None):
"""If `name` is not provided, the Parameter is a returned value instead of a parameter."""
self.name = name
self.data_type = data_type
self.default_value = default_value
if description:
# Capitalize the first letter, add a period, and parse as Markdown.
self.description = '{}{}. '.format(description[0].capitalize(), description[1:])
self.description = markdown.markdown(self.description)
else:
self.description = ''
# If the data type is a C++ number, convert to Lua number and add range info to description.
if self.data_type in ['float', 'double', 'int8', 'uint8', 'int16', 'uint16', 'int32', 'uint32']:
range = ParameterDoc.valid_ranges[self.data_type]
if range:
self.description += '<p><em>Valid numbers</em>: integers from {0} to {1}.</p>'.format(range[0], range[1])
else:
self.description += '<p><em>Valid numbers</em>: all decimal numbers.</p>'
self.data_type = 'number'
class MethodDoc(object):
"""The documentation data of an Eluna method."""
@params(self=object, name=unicode, description=unicode, parameters=[ParameterDoc], returned=[ParameterDoc])
def __init__(self, name, description, parameters, returned):
self.name = name
self.parameters = parameters
self.returned = returned
# Parse the description as Markdown.
self.description = markdown.markdown(description)
# Pull the first paragraph out of the description as the short description.
self.short_description = self.description.split('</p>')[0][3:]
# If it has a description, it is "documented".
self.documented = self.description != ''
class MangosClassDoc(object):
"""The documentation of a MaNGOS class that has Lua methods."""
@params(self=object, name=unicode, description=unicode, methods=[MethodDoc])
def __init__(self, name, description, methods):
self.name = name
# Parse the description as Markdown.
self.description = markdown.markdown(description)
# Pull the first paragraph out of the description as the short description.
self.short_description = self.description.split('</p>')[0][3:]
# Sort the methods by their names.
self.methods = sorted(methods, key=lambda m: m.name)
# If any of our methods are not documented, we aren't fully documented.
for method in methods:
if not method.documented:
self.fully_documented = False
break
else:
self.fully_documented = True
# In the same vein, if any of our methods are documented, we aren't fully *un*documented.
for method in methods:
if method.documented:
self.fully_undocumented = False
break
else:
self.fully_undocumented = True
class ClassParser(object):
"""Parses a file line-by-line and returns methods when enough information is received to build them."""
# Various regular expressions to parse different parts of the doc string.
# There are used to parse the class's description.
class_start_regex = re.compile(r"\s*/\*\*\*") # The start of class documentation, i.e. /***
class_body_regex = re.compile(r"\s*\*\s*(.*)") # The "body", i.e. a * and optionally some descriptive text.
class_end_regex = re.compile(r"\s*\*/") # The end of the comment portion, i.e. */
# These are used to parse method documentation.
start_regex = re.compile(r"\s*/\*\*") # The start of documentation, i.e. /**
body_regex = re.compile(r"\s*\s?\*\s*(.*)") # The "body", i.e. a * and optionally some descriptive text.
# An extra optional space (\s?) was thrown in to make it different from `class_body_regex`.
param_regex = re.compile(r"""\s*\*\s@param\s # The @param tag starts with opt. whitespace followed by "* @param ".
([&\w]+)\s(\w+) # The data type, a space, and the name of the param.
(?:\s=\s(.+))? # The default value: a = surrounded by spaces, followed by text.
(?:\s:\s(.+))? # The description: a colon surrounded by spaces, followed by text.""",
re.X)
# This is the same as the @param tag, minus the default value part.
return_regex = re.compile(r"""\s*\*\s@return\s
([&\w]+)\s(\w+)
(?:\s:\s(.+))?""", re.X)
comment_end_regex = re.compile(r"\s*\*/") # The end of the comment portion, i.e. */
end_regex = re.compile(r"\s*int\s(\w+)\s*\(") # The end of the documentation, i.e. int MethodName(
def __init__(self, class_name):
assert ClassParser.class_body_regex is not ClassParser.body_regex
# The methods that have been parsed.
self.methods = []
# The name of the class being parsed.
self.class_name = class_name
# The description of the class being parsed.
self.class_description = ''
# Reset the parser's state machine.
self.reset()
def reset(self):
# What the last handled regex was, to determine what the next should be.
self.last_regex = None
# These are used to piece together the next `Method`.
self.description = ''
self.params = []
self.returned = []
self.method_name = None
def handle_class_body(self, match):
text = match.group(1)
self.class_description += text + '\n'
def handle_body(self, match):
text = match.group(1)
self.description += text + '\n'
def handle_param(self, match):
data_type, name, default, description = match.group(1), match.group(2), match.group(3), match.group(4)
self.params.append(ParameterDoc(name, data_type, description, default))
def handle_return(self, match):
data_type, name, description = match.group(1), match.group(2), match.group(3)
self.returned.append(ParameterDoc(name, data_type, description))
def handle_end(self, match):
self.method_name = match.group(1)
self.methods.append(MethodDoc(self.method_name, self.description, self.params, self.returned))
# Table of which handler is used to handle each regular expressions.
regex_handlers = {
class_start_regex: None,
class_body_regex: handle_class_body,
class_end_regex: None,
start_regex: None,
body_regex: handle_body,
param_regex: handle_param,
return_regex: handle_return,
comment_end_regex: None,
end_regex: handle_end,
}
# Table of which regular expressions can follow the last handled regex.
# `doc_body_regex` must always come LAST when used, since it also matches param, return, and comment_end.
next_regexes = {
None: [class_start_regex, start_regex, end_regex],
class_start_regex: [class_end_regex, class_body_regex],
class_body_regex: [class_end_regex, class_body_regex],
class_end_regex: [],
start_regex: [param_regex, return_regex, comment_end_regex, body_regex],
body_regex: [param_regex, return_regex, comment_end_regex, body_regex],
param_regex: [param_regex, return_regex, comment_end_regex],
return_regex: [return_regex, comment_end_regex],
comment_end_regex: [end_regex],
end_regex: [],
}
@returns(Nullable(MethodDoc))
@params(self=object, line=str)
def next_line(self, line):
"""Parse the next line of the file.
This method returns a `Method` when enough data to form a `Method` has been parsed.
Otherwise, it returns None.
"""
# Get the list of expected regular expressions using the last one handled.
valid_regexes = self.next_regexes[self.last_regex]
# Try to find a match.
for regex in valid_regexes:
match = regex.match(line)
if match:
handler = self.regex_handlers[regex]
if handler:
handler(self, match)
# Not every regex has a handler, but keep track of where we are anyway.
self.last_regex = regex
# Break at the first match.
break
else:
# No valid regex was found, reset everything.
self.reset()
@returns(MangosClassDoc)
def to_class_doc(self):
"""Create an instance of `MangosClassDoc` from the parser's data.
Is called by `parse_file` once parsing is finished.
"""
return MangosClassDoc(self.class_name, self.class_description, self.methods)
@staticmethod
@returns(MangosClassDoc)
@params(file=FileType)
def parse_file(file):
"""Parse the file `file` into a documented class."""
# Get the class name from "ClassMethods.h" by stripping off "Methods.h".
class_name = file.name[:-len('Methods.h')]
parser = ClassParser(class_name)
line = file.readline()
while line:
parser.next_line(line)
line = file.readline()
return parser.to_class_doc()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

4
docs/ElunaDoc/static/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,520 @@
/**
* Copyright 2013 The Rust Project Developers. See the COPYRIGHT
* file at the top-level directory of this distribution and at
* http://rust-lang.org/COPYRIGHT.
*
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
* option. This file may not be copied, modified, or distributed
* except according to those terms.
*/
@font-face {
font-family: 'Fira Sans';
font-style: normal;
font-weight: 400;
src: local('Fira Sans'), url("FiraSans-Regular.woff") format('woff');
}
@font-face {
font-family: 'Fira Sans';
font-style: normal;
font-weight: 500;
src: local('Fira Sans Medium'), url("FiraSans-Medium.woff") format('woff');
}
@font-face {
font-family: 'Source Serif Pro';
font-style: normal;
font-weight: 400;
src: local('Source Serif Pro'), url("SourceSerifPro-Regular.woff") format('woff');
}
@font-face {
font-family: 'Source Serif Pro';
font-style: italic;
font-weight: 400;
src: url("Heuristica-Italic.woff") format('woff');
}
@font-face {
font-family: 'Source Serif Pro';
font-style: normal;
font-weight: 700;
src: local('Source Serif Pro Bold'), url("SourceSerifPro-Bold.woff") format('woff');
}
@font-face {
font-family: 'Source Code Pro';
font-style: normal;
font-weight: 400;
src: local('Source Code Pro'), url("SourceCodePro-Regular.woff") format('woff');
}
@font-face {
font-family: 'Source Code Pro';
font-style: normal;
font-weight: 600;
src: local('Source Code Pro Semibold'), url("SourceCodePro-Semibold.woff") format('woff');
}
@import "normalize.css";
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
/* General structure and fonts */
body {
color: #333;
min-width: 500px;
font: 16px/1.4 "Source Serif Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 0;
position: relative;
padding: 10px 15px 20px 15px;
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1.4em;
}
h3 {
font-size: 1.3em;
}
h1, h2, h3:not(.impl):not(.method), h4:not(.method) {
color: black;
font-weight: 500;
margin: 20px 0 15px 0;
padding-bottom: 6px;
}
h1.fqn {
border-bottom: 1px dashed #D5D5D5;
margin-top: 0;
}
h2, h3:not(.impl):not(.method), h4:not(.method) {
border-bottom: 1px solid #DDDDDD;
}
h3.impl, h3.method, h4.method {
font-weight: 600;
margin-top: 10px;
margin-bottom: 10px;
}
h3.impl, h3.method {
margin-top: 15px;
}
h1, h2, h3, h4, section.sidebar, a.source, .search-input, .content table a, .collapse-toggle {
font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
ol, ul {
padding-left: 25px;
}
ul ul, ol ul, ul ol, ol ol {
margin-bottom: 0;
}
p {
margin: 0 0 .6em 0;
}
code, pre {
font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", Inconsolata, monospace;
white-space: pre-wrap;
}
.docblock code {
background-color: #F5F5F5;
border-radius: 3px;
padding: 0 0.2em;
}
pre {
background-color: #F5F5F5;
padding: 14px;
}
.source pre {
padding: 20px;
}
.content.source {
margin-top: 50px;
max-width: none;
overflow: visible;
margin-left: 0px;
min-width: 70em;
}
nav.sub {
font-size: 16px;
text-transform: uppercase;
}
.sidebar {
width: 200px;
position: absolute;
left: 0;
top: 0;
min-height: 100%;
}
.content, nav { max-width: 960px; }
/* Everything else */
.js-only, .hidden { display: none; }
.sidebar {
padding: 10px;
}
.sidebar img {
margin: 20px auto;
display: block;
}
.sidebar .location {
font-size: 17px;
margin: 30px 0 20px 0;
background: #e1e1e1;
text-align: center;
color: #333;
}
.block {
padding: 0 10px;
margin-bottom: 14px;
}
.block h2 {
margin-top: 0;
margin-bottom: 8px;
text-align: center;
}
.block a {
display: block;
text-overflow: ellipsis;
overflow: hidden;
line-height: 15px;
padding: 7px 5px;
font-size: 14px;
font-weight: 300;
transition: border 500ms ease-out;
}
.block a:hover {
background: #F5F5F5;
}
.content {
padding: 15px 0;
}
.content.source pre.rust {
white-space: pre;
overflow: auto;
padding-left: 0;
}
.content pre.line-numbers { float: left; border: none; }
.line-numbers span { color: #c67e2d; }
.line-numbers .line-highlighted {
background-color: #f6fdb0;
}
.content .highlighted {
cursor: pointer;
color: #000 !important;
background-color: #ccc;
}
.content .highlighted a { color: #000 !important; }
.content .highlighted.trait { background-color: #fece7e; }
.content .highlighted.mod { background-color: #afc6e4; }
.content .highlighted.enum { background-color: #b4d1b9; }
.content .highlighted.struct { background-color: #e7b1a0; }
.content .highlighted.fn { background-color: #c6afb3; }
.docblock.short.nowrap {
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.docblock.short p {
overflow: hidden;
text-overflow: ellipsis;
margin: 0;
}
.docblock.short code { white-space: nowrap; }
.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5 {
border-bottom: 1px solid #DDD;
}
.docblock h1 { font-size: 1.3em; }
.docblock h2 { font-size: 1.15em; }
.docblock h3, .docblock h4, .docblock h5 { font-size: 1em; }
.content .out-of-band {
float: right;
font-size: 23px;
}
.content table {
border-spacing: 0 5px;
border-collapse: separate;
}
.content td { vertical-align: top; }
.content td:first-child { padding-right: 20px; }
.content td p:first-child { margin-top: 0; }
.content td h1, .content td h2 { margin-left: 0; font-size: 1.1em; }
.content .item-list {
list-style-type: none;
padding: 0;
}
.content .item-list li { margin-bottom: 3px; }
.content .multi-column {
-moz-column-count: 5;
-moz-column-gap: 2.5em;
-webkit-column-count: 5;
-webkit-column-gap: 2.5em;
column-count: 5;
column-gap: 2.5em;
}
.content .multi-column li { width: 100%; display: inline-block; }
.content .method {
font-size: 1em;
position: relative;
}
.content .methods .docblock { margin-left: 40px; }
.content .impl-methods .docblock { margin-left: 40px; }
nav {
border-bottom: 1px solid #e0e0e0;
padding-bottom: 10px;
margin-bottom: 10px;
}
nav.main {
padding: 20px 0;
text-align: center;
}
nav.main .current {
border-top: 1px solid #000;
border-bottom: 1px solid #000;
}
nav.main .separator {
border: 1px solid #000;
display: inline-block;
height: 23px;
margin: 0 20px;
}
nav.sum { text-align: right; }
nav.sub form { display: inline; }
nav, .content {
margin-left: 230px;
}
a {
text-decoration: none;
color: #000;
background: transparent;
}
p a { color: #4e8bca; }
p a:hover { text-decoration: underline; }
.content a.trait, .block a.current.trait { color: #ed9603; }
.content a.mod, .block a.current.mod { color: #4d76ae; }
.content a.enum, .block a.current.enum { color: #5e9766; }
.content a.struct, .block a.current.struct { color: #e53700; }
.content a.fn, .block a.current.fn { color: #8c6067; }
.content .fnname { color: #8c6067; }
.search-input {
width: 100%;
/* Override Normalize.css: we have margins and do
not want to overflow - the `moz` attribute is necessary
until Firefox 29, too early to drop at this point */
-moz-box-sizing: border-box !important;
box-sizing: border-box !important;
outline: none;
border: none;
border-radius: 1px;
color: #555;
margin-top: 5px;
padding: 10px 16px;
font-size: 17px;
box-shadow: 0 0 0 1px #e0e0e0, 0 0 0 2px transparent;
transition: border-color 300ms ease;
transition: border-radius 300ms ease-in-out;
transition: box-shadow 300ms ease-in-out;
}
.search-input:focus {
border-color: #66afe9;
border-radius: 2px;
border: 0;
outline: 0;
box-shadow: 0 0 8px #078dd8;
}
.search-results .desc {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: block;
}
#help {
background: #e9e9e9;
border-radius: 4px;
box-shadow: 0 0 6px rgba(0,0,0,.2);
position: absolute;
top: 300px;
left: 50%;
margin-top: -125px;
margin-left: -275px;
width: 550px;
height: 300px;
border: 1px solid #bfbfbf;
}
#help dt {
float: left;
border-radius: 3px;
border: 1px solid #bfbfbf;
background: #fff;
width: 23px;
text-align: center;
clear: left;
display: block;
margin-top: -1px;
}
#help dd { margin: 5px 33px; }
#help .infos { padding-left: 0; }
#help h1 { margin-top: 0; }
#help div {
width: 50%;
float: left;
padding: 20px;
}
.stability {
border-left: 6px solid;
padding: 3px 6px;
border-radius: 3px;
}
h1 .stability {
text-transform: lowercase;
font-weight: 400;
margin-left: 14px;
padding: 4px 10px;
}
.impl-methods .stability, .methods .stability {
margin-right: 20px;
}
.stability.Deprecated { border-color: #A071A8; color: #82478C; }
.stability.Experimental { border-color: #D46D6A; color: #AA3C39; }
.stability.Unstable { border-color: #D4B16A; color: #AA8439; }
.stability.Stable { border-color: #54A759; color: #2D8632; }
.stability.Frozen { border-color: #009431; color: #007726; }
.stability.Locked { border-color: #0084B6; color: #00668c; }
.stability.Unmarked { border-color: #BBBBBB; }
.summary {
padding-right: 0px;
}
.summary.Deprecated { background-color: #A071A8; }
.summary.Experimental { background-color: #D46D6A; }
.summary.Unstable { background-color: #D4B16A; }
.summary.Stable { background-color: #54A759; }
.summary.Unmarked { background-color: #BBBBBB; }
:target { background: #FDFFD3; }
/* Code highlighting */
pre.rust .kw { color: #8959A8; }
pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; }
pre.rust .number, pre.rust .string { color: #718C00; }
pre.rust .self, pre.rust .boolval, pre.rust .prelude-val,
pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; }
pre.rust .comment { color: #8E908C; }
pre.rust .doccomment { color: #4D4D4C; }
pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; }
pre.rust .lifetime { color: #B76514; }
.rusttest { display: none; }
pre.rust { position: relative; }
.test-arrow {
display: inline-block;
position: absolute;
top: 0;
right: 10px;
font-size: 150%;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
.methods .section-header {
/* Override parent class attributes. */
border-bottom: none !important;
font-size: 1.1em !important;
margin: 0 0 -5px;
padding: 0;
}
.section-header:hover a:after {
content: '\2002\00a7\2002';
}
/* Media Queries */
@media (max-width: 700px) {
.sidebar {
display: none;
}
.content {
margin-left: 0px;
}
nav.sub {
margin: 0 auto;
}
}
.collapse-toggle {
font-weight: 100;
position: absolute;
left: 13px;
color: #999;
margin-top: 2px;
}
.toggle-wrapper > .collapse-toggle {
left: -24px;
margin-top: 0px;
}
.toggle-wrapper {
position: relative;
}
.toggle-wrapper.collapsed {
height: 1em;
transition: height .2s;
}
.collapse-toggle > .inner {
display: inline-block;
width: 1ch;
text-align: center;
}
.toggle-label {
color: #999;
font-style: italic;
}

View File

@@ -0,0 +1,782 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*jslint browser: true, es5: true */
/*globals $: true, rootPath: true */
(function() {
"use strict";
var resizeTimeout, interval;
$('.js-only').removeClass('js-only');
function getQueryStringParams() {
var params = {};
window.location.search.substring(1).split("&").
map(function(s) {
var pair = s.split("=");
params[decodeURIComponent(pair[0])] =
typeof pair[1] === "undefined" ?
null : decodeURIComponent(pair[1]);
});
return params;
}
function browserSupportsHistoryApi() {
return window.history && typeof window.history.pushState === "function";
}
function resizeShortBlocks() {
if (resizeTimeout) {
clearTimeout(resizeTimeout);
}
resizeTimeout = setTimeout(function() {
var contentWidth = $('.content').width();
$('.docblock.short').width(function() {
return contentWidth - 40 - $(this).prev().width();
}).addClass('nowrap');
$('.summary-column').width(function() {
return contentWidth - 40 - $(this).prev().width();
})
}, 150);
}
resizeShortBlocks();
$(window).on('resize', resizeShortBlocks);
function highlightSourceLines() {
var i, from, to, match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);
if (match) {
from = parseInt(match[1], 10);
to = Math.min(50000, parseInt(match[2] || match[1], 10));
from = Math.min(from, to);
if ($('#' + from).length === 0) {
return;
}
$('#' + from)[0].scrollIntoView();
$('.line-numbers span').removeClass('line-highlighted');
for (i = from; i <= to; ++i) {
$('#' + i).addClass('line-highlighted');
}
}
}
highlightSourceLines();
$(window).on('hashchange', highlightSourceLines);
$(document).on('keyup', function(e) {
if (document.activeElement.tagName === 'INPUT') {
return;
}
if (e.which === 191 && $('#help').hasClass('hidden')) { // question mark
e.preventDefault();
$('#help').removeClass('hidden');
} else if (e.which === 27) { // esc
if (!$('#help').hasClass('hidden')) {
e.preventDefault();
$('#help').addClass('hidden');
} else if (!$('#search').hasClass('hidden')) {
e.preventDefault();
$('#search').addClass('hidden');
$('#main').removeClass('hidden');
}
} else if (e.which === 83) { // S
e.preventDefault();
$('.search-input').focus();
}
}).on('click', function(e) {
if (!$(e.target).closest('#help').length) {
$('#help').addClass('hidden');
}
});
$('.version-selector').on('change', function() {
var i, match,
url = document.location.href,
stripped = '',
len = rootPath.match(/\.\.\//g).length + 1;
for (i = 0; i < len; ++i) {
match = url.match(/\/[^\/]*$/);
if (i < len - 1) {
stripped = match[0] + stripped;
}
url = url.substring(0, url.length - match[0].length);
}
url += '/' + $('.version-selector').val() + stripped;
document.location.href = url;
});
/**
* A function to compute the Levenshtein distance between two strings
* Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
* Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
* This code is an unmodified version of the code written by Marco de Wit
* and was found at http://stackoverflow.com/a/18514751/745719
*/
var levenshtein = (function() {
var row2 = [];
return function(s1, s2) {
if (s1 === s2) {
return 0;
} else {
var s1_len = s1.length, s2_len = s2.length;
if (s1_len && s2_len) {
var i1 = 0, i2 = 0, a, b, c, c2, row = row2;
while (i1 < s1_len)
row[i1] = ++i1;
while (i2 < s2_len) {
c2 = s2.charCodeAt(i2);
a = i2;
++i2;
b = i2;
for (i1 = 0; i1 < s1_len; ++i1) {
c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
a = row[i1];
b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
row[i1] = b;
}
}
return b;
} else {
return s1_len + s2_len;
}
}
};
})();
function initSearch(rawSearchIndex) {
var currentResults, index, searchIndex;
var MAX_LEV_DISTANCE = 3;
var params = getQueryStringParams();
// Populate search bar with query string search term when provided,
// but only if the input bar is empty. This avoid the obnoxious issue
// where you start trying to do a search, and the index loads, and
// suddenly your search is gone!
if ($(".search-input")[0].value === "") {
$(".search-input")[0].value = params.search || '';
}
/**
* Executes the query and builds an index of results
* @param {[Object]} query [The user query]
* @param {[type]} max [The maximum results returned]
* @param {[type]} searchWords [The list of search words to query
* against]
* @return {[type]} [A search index of results]
*/
function execQuery(query, max, searchWords) {
var valLower = query.query.toLowerCase(),
val = valLower,
typeFilter = itemTypeFromName(query.type),
results = [],
split = valLower.split("::");
//remove empty keywords
for (var j = 0; j < split.length; ++j) {
split[j].toLowerCase();
if (split[j] === "") {
split.splice(j, 1);
}
}
// quoted values mean literal search
var nSearchWords = searchWords.length;
if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
val.charAt(val.length - 1) === val.charAt(0))
{
val = val.substr(1, val.length - 2);
for (var i = 0; i < nSearchWords; ++i) {
if (searchWords[i] === val) {
// filter type: ... queries
if (typeFilter < 0 || typeFilter === searchIndex[i].ty) {
results.push({id: i, index: -1});
}
}
if (results.length === max) {
break;
}
}
} else {
// gather matching search results up to a certain maximum
val = val.replace(/\_/g, "");
for (var i = 0; i < split.length; ++i) {
for (var j = 0; j < nSearchWords; ++j) {
var lev_distance;
if (searchWords[j].indexOf(split[i]) > -1 ||
searchWords[j].indexOf(val) > -1 ||
searchWords[j].replace(/_/g, "").indexOf(val) > -1)
{
// filter type: ... queries
if (typeFilter < 0 || typeFilter === searchIndex[j].ty) {
results.push({
id: j,
index: searchWords[j].replace(/_/g, "").indexOf(val),
lev: 0,
});
}
} else if (
(lev_distance = levenshtein(searchWords[j], val)) <=
MAX_LEV_DISTANCE) {
if (typeFilter < 0 || typeFilter === searchIndex[j].ty) {
results.push({
id: j,
index: 0,
// we want lev results to go lower than others
lev: lev_distance,
});
}
}
if (results.length === max) {
break;
}
}
}
}
var nresults = results.length;
for (var i = 0; i < nresults; ++i) {
results[i].word = searchWords[results[i].id];
results[i].item = searchIndex[results[i].id] || {};
}
// if there are no results then return to default and fail
if (results.length === 0) {
return [];
}
results.sort(function(aaa, bbb) {
var a, b;
// Sort by non levenshtein results and then levenshtein results by the distance
// (less changes required to match means higher rankings)
a = (aaa.lev);
b = (bbb.lev);
if (a !== b) return a - b;
// sort by crate (non-current crate goes later)
a = (aaa.item.crate !== window.currentCrate);
b = (bbb.item.crate !== window.currentCrate);
if (a !== b) return a - b;
// sort by exact match (mismatch goes later)
a = (aaa.word !== valLower);
b = (bbb.word !== valLower);
if (a !== b) return a - b;
// sort by item name length (longer goes later)
a = aaa.word.length;
b = bbb.word.length;
if (a !== b) return a - b;
// sort by item name (lexicographically larger goes later)
a = aaa.word;
b = bbb.word;
if (a !== b) return (a > b ? +1 : -1);
// sort by index of keyword in item name (no literal occurrence goes later)
a = (aaa.index < 0);
b = (bbb.index < 0);
if (a !== b) return a - b;
// (later literal occurrence, if any, goes later)
a = aaa.index;
b = bbb.index;
if (a !== b) return a - b;
// sort by description (no description goes later)
a = (aaa.item.desc === '');
b = (bbb.item.desc === '');
if (a !== b) return a - b;
// sort by type (later occurrence in `itemTypes` goes later)
a = aaa.item.ty;
b = bbb.item.ty;
if (a !== b) return a - b;
// sort by path (lexicographically larger goes later)
a = aaa.item.path;
b = bbb.item.path;
if (a !== b) return (a > b ? +1 : -1);
// que sera, sera
return 0;
});
// remove duplicates, according to the data provided
for (var i = results.length - 1; i > 0; i -= 1) {
if (results[i].word === results[i - 1].word &&
results[i].item.ty === results[i - 1].item.ty &&
results[i].item.path === results[i - 1].item.path)
{
results[i].id = -1;
}
}
for (var i = 0; i < results.length; ++i) {
var result = results[i],
name = result.item.name.toLowerCase(),
path = result.item.path.toLowerCase(),
parent = result.item.parent;
var valid = validateResult(name, path, split, parent);
if (!valid) {
result.id = -1;
}
}
return results;
}
/**
* Validate performs the following boolean logic. For example:
* "File::open" will give IF A PARENT EXISTS => ("file" && "open")
* exists in (name || path || parent) OR => ("file" && "open") exists in
* (name || path )
*
* This could be written functionally, but I wanted to minimise
* functions on stack.
*
* @param {[string]} name [The name of the result]
* @param {[string]} path [The path of the result]
* @param {[string]} keys [The keys to be used (["file", "open"])]
* @param {[object]} parent [The parent of the result]
* @return {[boolean]} [Whether the result is valid or not]
*/
function validateResult(name, path, keys, parent) {
for (var i=0; i < keys.length; ++i) {
// each check is for validation so we negate the conditions and invalidate
if (!(
// check for an exact name match
name.toLowerCase().indexOf(keys[i]) > -1 ||
// then an exact path match
path.toLowerCase().indexOf(keys[i]) > -1 ||
// next if there is a parent, check for exact parent match
(parent !== undefined &&
parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
// lastly check to see if the name was a levenshtein match
levenshtein(name.toLowerCase(), keys[i]) <=
MAX_LEV_DISTANCE)) {
return false;
}
}
return true;
}
function getQuery() {
var matches, type, query = $('.search-input').val();
matches = query.match(/^(fn|mod|str(uct)?|enum|trait|t(ype)?d(ef)?)\s*:\s*/i);
if (matches) {
type = matches[1].replace(/^td$/, 'typedef')
.replace(/^str$/, 'struct')
.replace(/^tdef$/, 'typedef')
.replace(/^typed$/, 'typedef');
query = query.substring(matches[0].length);
}
return {
query: query,
type: type,
id: query + type,
};
}
function initSearchNav() {
var hoverTimeout, $results = $('.search-results .result');
$results.on('click', function() {
var dst = $(this).find('a')[0];
if (window.location.pathname == dst.pathname) {
$('#search').addClass('hidden');
$('#main').removeClass('hidden');
}
document.location.href = dst.href;
}).on('mouseover', function() {
var $el = $(this);
clearTimeout(hoverTimeout);
hoverTimeout = setTimeout(function() {
$results.removeClass('highlighted');
$el.addClass('highlighted');
}, 20);
});
$(document).off('keydown.searchnav');
$(document).on('keydown.searchnav', function(e) {
var $active = $results.filter('.highlighted');
if (e.which === 38) { // up
e.preventDefault();
if (!$active.length || !$active.prev()) {
return;
}
$active.prev().addClass('highlighted');
$active.removeClass('highlighted');
} else if (e.which === 40) { // down
e.preventDefault();
if (!$active.length) {
$results.first().addClass('highlighted');
} else if ($active.next().length) {
$active.next().addClass('highlighted');
$active.removeClass('highlighted');
}
} else if (e.which === 13) { // return
e.preventDefault();
if ($active.length) {
document.location.href = $active.find('a').prop('href');
}
}
});
}
function escape(content) {
return $('<h1/>').text(content).html();
}
function showResults(results) {
var output, shown, query = getQuery();
currentResults = query.id;
output = '<h1>Results for ' + escape(query.query) +
(query.type ? ' (type: ' + escape(query.type) + ')' : '') + '</h1>';
output += '<table class="search-results">';
if (results.length > 0) {
shown = [];
results.forEach(function(item) {
var name, type;
if (shown.indexOf(item) !== -1) {
return;
}
shown.push(item);
name = item.name;
type = itemTypes[item.ty];
output += '<tr class="' + type + ' result"><td>';
if (type === 'mod') {
output += item.path +
'::<a href="' + rootPath +
item.path.replace(/::/g, '/') + '/' +
name + '/index.html" class="' +
type + '">' + name + '</a>';
} else if (type === 'static' || type === 'reexport') {
output += item.path +
'::<a href="' + rootPath +
item.path.replace(/::/g, '/') +
'/index.html" class="' + type +
'">' + name + '</a>';
} else if (item.parent !== undefined) {
var myparent = item.parent;
var anchor = '#' + type + '.' + name;
output += item.path + '::' + myparent.name +
'::<a href="' + rootPath +
item.path.replace(/::/g, '/') +
'/' + myparent.name +
'.html' + anchor +
'" class="' + type +
'">' + name + '</a>';
} else {
output += item.path +
'::<a href="' + rootPath +
item.path.replace(/::/g, '/') +
'/' + name +
'.html" class="' + type +
'">' + name + '</a>';
}
output += '</td><td><span class="desc">' + item.desc +
'</span></td></tr>';
});
} else {
output += 'No results :( <a href="https://duckduckgo.com/?q=' +
encodeURIComponent('rust ' + query.query) +
'">Try on DuckDuckGo?</a>';
}
output += "</p>";
$('#main.content').addClass('hidden');
$('#search.content').removeClass('hidden').html(output);
$('#search .desc').width($('#search').width() - 40 -
$('#search td:first-child').first().width());
initSearchNav();
}
function search(e) {
var query,
filterdata = [],
obj, i, len,
results = [],
maxResults = 200,
resultIndex;
var params = getQueryStringParams();
query = getQuery();
if (e) {
e.preventDefault();
}
if (!query.query || query.id === currentResults) {
return;
}
// Because searching is incremental by character, only the most
// recent search query is added to the browser history.
if (browserSupportsHistoryApi()) {
if (!history.state && !params.search) {
history.pushState(query, "", "?search=" +
encodeURIComponent(query.query));
} else {
history.replaceState(query, "", "?search=" +
encodeURIComponent(query.query));
}
}
resultIndex = execQuery(query, 20000, index);
len = resultIndex.length;
for (i = 0; i < len; ++i) {
if (resultIndex[i].id > -1) {
obj = searchIndex[resultIndex[i].id];
filterdata.push([obj.name, obj.ty, obj.path, obj.desc]);
results.push(obj);
}
if (results.length >= maxResults) {
break;
}
}
showResults(results);
}
// This mapping table should match the discriminants of
// `rustdoc::html::item_type::ItemType` type in Rust.
var itemTypes = ["mod",
"struct",
"type",
"fn",
"type",
"static",
"trait",
"impl",
"viewitem",
"tymethod",
"method",
"structfield",
"variant",
"ffi",
"ffs",
"macro",
"primitive"];
function itemTypeFromName(typename) {
for (var i = 0; i < itemTypes.length; ++i) {
if (itemTypes[i] === typename) return i;
}
return -1;
}
function buildIndex(rawSearchIndex) {
searchIndex = [];
var searchWords = [];
for (var crate in rawSearchIndex) {
if (!rawSearchIndex.hasOwnProperty(crate)) { continue }
// an array of [(Number) item type,
// (String) name,
// (String) full path or empty string for previous path,
// (String) description,
// (optional Number) the parent path index to `paths`]
var items = rawSearchIndex[crate].items;
// an array of [(Number) item type,
// (String) name]
var paths = rawSearchIndex[crate].paths;
// convert `paths` into an object form
var len = paths.length;
for (var i = 0; i < len; ++i) {
paths[i] = {ty: paths[i][0], name: paths[i][1]};
}
// convert `items` into an object form, and construct word indices.
//
// before any analysis is performed lets gather the search terms to
// search against apart from the rest of the data. This is a quick
// operation that is cached for the life of the page state so that
// all other search operations have access to this cached data for
// faster analysis operations
var len = items.length;
var lastPath = "";
for (var i = 0; i < len; ++i) {
var rawRow = items[i];
var row = {crate: crate, ty: rawRow[0], name: rawRow[1],
path: rawRow[2] || lastPath, desc: rawRow[3],
parent: paths[rawRow[4]]};
searchIndex.push(row);
if (typeof row.name === "string") {
var word = row.name.toLowerCase();
searchWords.push(word);
} else {
searchWords.push("");
}
lastPath = row.path;
}
}
return searchWords;
}
function startSearch() {
var keyUpTimeout;
$('.do-search').on('click', search);
$('.search-input').on('keyup', function() {
clearTimeout(keyUpTimeout);
keyUpTimeout = setTimeout(search, 100);
});
// Push and pop states are used to add search results to the browser
// history.
if (browserSupportsHistoryApi()) {
$(window).on('popstate', function(e) {
var params = getQueryStringParams();
// When browsing back from search results the main page
// visibility must be reset.
if (!params.search) {
$('#main.content').removeClass('hidden');
$('#search.content').addClass('hidden');
}
// When browsing forward to search results the previous
// search will be repeated, so the currentResults are
// cleared to ensure the search is successful.
currentResults = null;
// Synchronize search bar with query string state and
// perform the search. This will empty the bar if there's
// nothing there, which lets you really go back to a
// previous state with nothing in the bar.
$('.search-input').val(params.search);
// Some browsers fire 'onpopstate' for every page load
// (Chrome), while others fire the event only when actually
// popping a state (Firefox), which is why search() is
// called both here and at the end of the startSearch()
// function.
search();
});
}
search();
}
index = buildIndex(rawSearchIndex);
startSearch();
// Draw a convenient sidebar of known crates if we have a listing
if (rootPath == '../') {
var sidebar = $('.sidebar');
var div = $('<div>').attr('class', 'block crate');
div.append($('<h2>').text('Crates'));
var crates = [];
for (var crate in rawSearchIndex) {
if (!rawSearchIndex.hasOwnProperty(crate)) { continue }
crates.push(crate);
}
crates.sort();
for (var i = 0; i < crates.length; ++i) {
var klass = 'crate';
if (crates[i] == window.currentCrate) {
klass += ' current';
}
div.append($('<a>', {'href': '../' + crates[i] + '/index.html',
'class': klass}).text(crates[i]));
}
sidebar.append(div);
}
}
window.initSearch = initSearch;
window.register_implementors = function(imp) {
var list = $('#implementors-list');
var libs = Object.getOwnPropertyNames(imp);
for (var i = 0; i < libs.length; ++i) {
if (libs[i] == currentCrate) continue;
var structs = imp[libs[i]];
for (var j = 0; j < structs.length; ++j) {
var code = $('<code>').append(structs[j]);
$.each(code.find('a'), function(idx, a) {
var href = $(a).attr('href');
if (!href.startsWith('http')) {
$(a).attr('href', rootPath + $(a).attr('href'));
}
});
var li = $('<li>').append(code);
list.append(li);
}
}
};
if (window.pending_implementors) {
window.register_implementors(window.pending_implementors);
}
// See documentation in html/render.rs for what this is doing.
var query = getQueryStringParams();
if (query['gotosrc']) {
window.location = $('#src-' + query['gotosrc']).attr('href');
}
$("#expand-all").on("click", function() {
$(".docblock").show();
$(".toggle-label").hide();
$(".toggle-wrapper").removeClass("collapsed");
$(".collapse-toggle").children(".inner").html("-");
});
$("#collapse-all").on("click", function() {
$(".docblock").hide();
$(".toggle-label").show();
$(".toggle-wrapper").addClass("collapsed");
$(".collapse-toggle").children(".inner").html("+");
});
$(document).on("click", ".collapse-toggle", function() {
var toggle = $(this);
var relatedDoc = toggle.parent().next();
if (relatedDoc.is(".docblock")) {
if (relatedDoc.is(":visible")) {
relatedDoc.slideUp({duration:'fast', easing:'linear'});
toggle.parent(".toggle-wrapper").addClass("collapsed");
toggle.children(".inner").html("+");
toggle.children(".toggle-label").fadeIn();
} else {
relatedDoc.slideDown({duration:'fast', easing:'linear'});
toggle.parent(".toggle-wrapper").removeClass("collapsed");
toggle.children(".inner").html("-");
toggle.children(".toggle-label").hide();
}
}
});
$(function() {
var toggle = "<a href='javascript:void(0)'"
+ "class='collapse-toggle'>[<span class='inner'>-</span>]</a>";
$(".method").each(function() {
if ($(this).next().is(".docblock")) {
$(this).children().first().after(toggle);
}
});
var mainToggle = $(toggle);
mainToggle.append("<span class='toggle-label' style='display:none'>"
+ "&nbsp;Expand&nbsp;description</span></a>")
var wrapper = $("<div class='toggle-wrapper'>");
wrapper.append(mainToggle);
$("#main > .docblock").before(wrapper);
});
}());

1
docs/ElunaDoc/static/normalize.css vendored Normal file
View File

@@ -0,0 +1 @@
/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}

View File

@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{% block description %}API documentation for the Eluna engine.{% endblock %}">
<meta name="keywords" content="eluna, lua, lua engine, trinitycore, trinity, mangos, cmangos, script, scripting, doc, docs, documentation">
<title>{% block title %}Eluna API{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="{{ static('main.css') }}">
<link rel="shortcut icon" href="{{ static('favicon.ico') }}">
</head>
<body class="rustdoc">
<!--[if lte IE 8]>
<div class="warning">
This old browser is unsupported and will most likely display funky
things.
</div>
<![endif]-->
<section class="sidebar">
<a href='{{ root('index.html') }}'><img src='{{ static('eluna-logo.png') }}' alt='Eluna Logo' width='100'></a>
<div class="block">
{% block sidebar %}
<h2>All Classes</h2>
{% for class in classes -%}
<a class="mod {{ 'current' if class == current_class }}" href="{{ root(class.name + '/index.html') }}">{{ class.name }}</a>
{%- endfor %}
{% endblock %}
</div>
</section>
<nav class="sub">
<form class="search-form js-only">
<div class="search-container">
<input class="search-input" name="search"
autocomplete="off"
placeholder="Click or press 'S' to search, '?' for more options..."
type="search">
</div>
</form>
</nav>
<section id='main' class="content mod">
<h1 class='fqn'>
{% block document_title %}Title Missing{% endblock %}
<span class='out-of-band'>
<span id='render-detail'>
<a id="collapse-all" href="#">[-]</a>
<a id="expand-all" href="#">[+]</a>
</span>
</span>
</h1>
{% block content %}<h2>Content missing.</h2>{% endblock %}
</section>
<section id='search' class="content hidden"></section>
<section class="footer"></section>
<div id="help" class="hidden">
<div class="shortcuts">
<h1>Keyboard shortcuts</h1>
<dl>
<dt>?</dt>
<dd>Show this help dialog</dd>
<dt>S</dt>
<dd>Focus the search field</dd>
<dt>&larrb;</dt>
<dd>Move up in search results</dd>
<dt>&rarrb;</dt>
<dd>Move down in search results</dd>
<dt>&#9166;</dt>
<dd>Go to active search result</dd>
</dl>
</div>
<div class="infos">
<h1>Search tricks</h1>
<p>
Prefix searches with a type followed by a colon (e.g.
<code>fn:</code>) to restrict the search to a given type.
</p>
<p>
Accepted types are: <code>fn</code>, <code>mod</code>,
<code>struct</code> (or <code>str</code>), <code>enum</code>,
<code>trait</code>, <code>typedef</code> (or
<code>tdef</code>).
</p>
</div>
</div>
<script>
window.rootPath = "{{ ('../' * level) if level }}";
</script>
<script src="{{ static('jquery.js') }}"></script>
<script src="{{ static('main.js') }}"></script>
<script async src="{{ root('search-index.js') }}"></script>
</body>
</html>

View File

@@ -0,0 +1,36 @@
{% extends '_base.html' %}
{% block title -%}
{{ current_class.name }} - {{ super() }}
{%- endblock %}
{% block description -%}
API documentation for the {{ current_class.name }} class in the Eluna engine.
{%- endblock %}
{% block document_title -%}
Class <a class="mod" href="">{{ current_class.name }}</a>
{%- endblock %}
{% block content %}
{{ current_class.description|parse_links }}
<h2 id='methods' class='section-header'><a href="#methods">Methods</a></h2>
<table>
{%- for method in current_class.methods %}
<tr>
<td>
<a class='stability {{ 'Stable' if method.documented else 'Experimental' }}' title='{{ 'Documented' if method.documented else 'Undocumented' }}'></a>
<a class='fn' href='{{ method.name }}.html'>{{ method.name }}</a>
</td>
<td class='docblock short'>
<p>{{ method.short_description|parse_links }}</p>
</td>
</tr>
{%- endfor %}
</table>
{% endblock %}

View File

@@ -0,0 +1 @@
{% extends '_base.html' %}

View File

@@ -0,0 +1,77 @@
{% extends '_base.html' %}
{% block document_title -%}
Eluna API Documentation
{%- endblock %}
{% block content %}
<div class='docblock'>
<h1 id="the-eluna-engine-api" class='section-header'>
<a href="#the-eluna-engine-api">The Eluna Engine API</a>
</h1>
<p>
The Eluna Engine's API allows you to add your own Lua code to be executed when certain events (called "hooks") occur.
</p>
<p>
Add a new in-game command, give life to creatures with new AI, or even light players who try to duel on fire!
If the hook exists, you can script it.
</p>
<h2 id="about-eluna" class='section-header'>
<a href="#about-eluna">About Eluna</a>
</h2>
<p>
Eluna is a <a href="http://www.lua.org/">Lua</a> engine for World of Warcraft emulators.
Eluna supports <a href="http://cmangos.net/">CMaNGOS</a>/<a href="https://www.getmangos.eu/home.php">MaNGOS</a>
and <a href="http://www.trinitycore.org/">TrinityCore</a>.
</p>
<p>
To get Eluna, simply clone your favorite version of MaNGOS or Trinity from
<a href="https://github.com/ElunaLuaEngine">our Github account</a>.
Each fork there has Eluna already integrated, so you just need to compile and go!
</p>
<p>
Follow us on <a href="https://twitter.com/EmuDevs">our Twitter page</a> to view the latest news about what's going on with Eluna.
</p>
<h2 id="tutorials-and-guides" class='section-header'>
<a href="#tutorials-and-guides">Tutorials & Guides</a>
</h2>
<p>
We haven't written tutorials yet, but when we do, we'll put the links here.
</p>
<h2 id="about-this-documentation" class='section-header'>
<a href="#about-this-documentation">About this documentation</a>
</h2>
<p>
The layout, CSS, and Javascript code for this documentation was borrowed from <a href="http://doc.rust-lang.org/">doc.rust-lang.org</a>.
</p>
<p>
The documentation generator was originally written by <a href="https://github.com/Patman64">Patman64</a> and is maintained by the Eluna team.
</p>
</div>
<h2 id='modules' class='section-header'><a href="#modules">Classes</a></h2>
<table>
{%- for class in classes %}
<tr>
<td>
{%- if class.fully_documented %}
<a class='stability Stable' title='Fully Documented'></a>
{%- elif class.fully_undocumented %}
<a class='stability Experimental' title='Fully Undocumented'></a>
{%- else %}
<a class='stability Unstable' title='Partially Documented'></a>
{%- endif %}
<a class='mod' href='{{ root(class.name + '/index.html') }}'>{{ class.name }}</a>
</td>
<td class='docblock short'>
<p>{{ class.short_description|parse_links }}</p>
</td>
</tr>
{%- endfor %}
</table>
{% endblock %}

View File

@@ -0,0 +1,94 @@
{% extends '_base.html' %}
{% block title -%}
{{ current_class.name }}:{{ current_method.name }} - Eluna
{%- endblock %}
{% block description -%}
API documentation for the {{ current_class.name }}:{{ current_method.name }} method in the Eluna engine.
{%- endblock %}
{% block document_title -%}
Method
<a class="mod" href="{{ root(current_class.name + '/index.html') }}">
{{- current_class.name -}}
</a>:<a class="fn" href="{{ root(current_class.name + '/' + current_method.name + '.html') }}">
{{- current_method.name -}}
</a>
{%- endblock %}
{% block sidebar %}
<h2>{{ current_class.name }} Methods</h2>
{%- for method in current_class.methods %}
<a class="fn {{ 'current' if method == current_method }}" href="{{ root(current_class.name + '/' + method.name + '.html') }}">{{ method.name }}</a>
{%- endfor %}
{% endblock %}
{% block content %}
<div class='docblock'>
{%- if current_method.documented %}
{{ current_method.description|parse_links }}
{%- else %}
<p>This method is <em>undocumented</em>. <strong>Use at your own risk.</strong></p>
{%- endif %}
<h2 id="synopsis" class='section-header'>
<a href="#synopsis">Synopsis</a>
</h2>
<p>
<code>
{%- for returned in current_method.returned -%}
{{ returned.name }} {{- ', ' if not loop.last }}
{%- endfor %}
{{- ' = ' if current_method.returned|length > 0 }}
{{- current_class.name -}} : {{- current_method.name -}} (
{%- for param in current_method.parameters -%}
{{ param.name }} {{- ', ' if not loop.last }}
{%- endfor -%}
)</code>
</p>
<h2 id="arguments" class='section-header'>
<a href="#arguments">Arguments</a>
</h2>
<p>
{%- if current_method.parameters|length > 0 %}
{%- for param in current_method.parameters %}
<dl>
<dt><code>{{ param.data_type|escape|parse_data_type }} {{ param.name }} {{ param.default_value + ' ' if param.default_value }}</code></dt>
<dd class="docblock">{{ param.description|parse_links if param.description else '<em>See method description.</em>' }}</dd>
</dl>
{%- endfor %}
{%- elif not current_method.documented %}
Unknown.
{%- else %}
None.
{%- endif %}
</p>
<h2 id="returns" class='section-header'>
<a href="#returns">Returns</a>
</h2>
<p>
{%- if current_method.returned|length > 0 %}
{%- for returned in current_method.returned %}
<dl>
<dt><code>{{ returned.data_type|escape|parse_data_type }} {{ returned.name }}</code></dt>
<dd class="docblock">{{ returned.description|parse_links if returned.description else '<em>See method description.</em>' }}</dd>
</dl>
{%- endfor %}
{%- elif not current_method.documented %}
Unknown.
{%- else %}
Nothing.
{%- endif %}
</p>
</div>
{% endblock %}

View File

@@ -0,0 +1,17 @@
var searchIndex = {};
//{%- for class in classes %}
searchIndex["{{ class.name }}"] = {
"items": [
[0, "", "{{ class.name }}", "{{ class.short_description|replace('\n', ' ')|replace('\"', '&#34;') }}"],
//{%- for method in class.methods %}
[3, "{{ method.name }}", "", "{{ method.short_description|replace('\n', ' ')|replace('\"', '&#34;') }}"],
//{%- endfor %}
],
"paths": [
]
};
//{%- endfor %}
initSearch(searchIndex);