Add initial javascript support with jsdoc 3.

pull/6/head
Eric Holscher 9 years ago
parent b0c4a8870c
commit 9bc6072283

@ -1,3 +1,4 @@
from .dotnet import DotNetDomain
from .python import PythonDomain
from .go import GoDomain
from .go import GoDomain
from .javascript import JavaScriptDomain

@ -0,0 +1,201 @@
import os
import json
import subprocess
from sphinx.util.osutil import ensuredir
from .base import AutoAPIBase, AutoAPIDomain
from ..settings import env
class JavaScriptDomain(AutoAPIDomain):
'''Auto API domain handler for Javascript
Parses directly from Javascript files.
:param app: Sphinx application passed in as part of the extension
'''
def create_class(self, data):
'''Return instance of class based on Javascript data
Data keys handled here:
type
Set the object class
consts, types, vars, funcs
Recurse into :py:meth:`create_class` to create child object
instances
:param data: dictionary data from godocjson output
'''
obj_map = dict(
(cls.type, cls) for cls
in ALL_CLASSES
)
try:
cls = obj_map[data['kind']]
except KeyError:
self.app.warn('Unknown Type: %s' % data)
else:
# Recurse for children
obj = cls(data)
if 'children' in data:
for child_data in data['children']:
for child_obj in self.create_class(child_data):
obj.children.append(child_obj)
yield obj
def read_file(self, path, **kwargs):
'''Read file input into memory, returning deserialized objects
:param path: Path of file to read
'''
# TODO support JSON here
# TODO sphinx way of reporting errors in logs?
try:
parsed_data = json.loads(subprocess.check_output(['jsdoc', '-X', path]))
return parsed_data
except IOError:
print Warning('Error reading file: {0}'.format(path))
except TypeError:
print Warning('Error reading file: {0}'.format(path))
return None
def get_objects(self, pattern, format='yaml'):
'''Trigger find of serialized sources and build objects'''
for path in self.find_files(pattern):
data = self.read_file(path, format=format)
if data:
# Returns a list of objects
for item in data:
for obj in self.create_class(item):
self.add_object(obj)
def full(self):
self.get_objects(self.get_config('autoapi_file_pattern'), format='json')
self.generate_output()
self.write_indexes()
def generate_output(self):
for obj in self.app.env.autoapi_data:
if not obj:
continue
rst = obj.render()
# Detail
try:
filename = obj.name.split('(')[0]
except IndexError:
filename = obj.name
detail_dir = os.path.join(self.get_config('autoapi_root'),
*filename.split('.'))
ensuredir(detail_dir)
# TODO: Better way to determine suffix?
path = os.path.join(detail_dir, '%s%s' % ('index', self.get_config('source_suffix')[0]))
if rst:
with open(path, 'w+') as detail_file:
detail_file.write(rst.encode('utf-8'))
def write_indexes(self):
# Write Index
top_level_index = os.path.join(self.get_config('autoapi_root'),
'index.rst')
with open(top_level_index, 'w+') as top_level_file:
content = env.get_template('index.rst')
top_level_file.write(content.render())
class JavaScriptBase(AutoAPIBase):
language = 'javascript'
def __init__(self, obj):
super(JavaScriptBase, self).__init__(obj)
self.name = obj.get('name')
self.id = self.name
# Second level
self.docstring = obj.get('description', '')
#self.docstring = obj.get('comment', '')
self.imports = obj.get('imports', [])
self.children = []
self.parameters = map(
lambda n: {'name': n['name'],
'type': n['type'][0]},
obj.get('param', [])
)
# Language Specific
pass
def __str__(self):
return '<{cls} {id}>'.format(cls=self.__class__.__name__,
id=self.id)
@property
def short_name(self):
'''Shorten name property'''
return self.name.split('.')[-1]
@property
def namespace(self):
pieces = self.id.split('.')[:-1]
if pieces:
return '.'.join(pieces)
@property
def ref_type(self):
return self.type
@property
def ref_directive(self):
return self.type
@property
def methods(self):
return self.obj.get('methods', [])
def __lt__(self, other):
'''Sort object by name'''
if isinstance(other, JavaScriptBase):
return self.name.lower() < other.name.lower()
return self.name < other
class JavaScriptClass(JavaScriptBase):
type = 'class'
ref_directive = 'class'
class JavaScriptFunction(JavaScriptBase):
type = 'function'
ref_type = 'func'
class JavaScriptData(JavaScriptBase):
type = 'data'
ref_directive = 'data'
class JavaScriptMember(JavaScriptBase):
type = 'member'
ref_directive = 'member'
class JavaScriptAttribute(JavaScriptBase):
type = 'attribute'
ref_directive = 'attr'
ALL_CLASSES = [
JavaScriptFunction,
JavaScriptClass,
JavaScriptData,
JavaScriptAttribute,
JavaScriptMember,
]

@ -6,7 +6,7 @@ Sphinx Auto-API
import fnmatch
import shutil
from .domains import DotNetDomain, PythonDomain, GoDomain
from .domains import DotNetDomain, PythonDomain, GoDomain, JavaScriptDomain
def ignore_file(app, filename):
@ -32,6 +32,8 @@ def load_yaml(app):
domain = PythonDomain(app)
elif app.config.autoapi_type == 'go':
domain = GoDomain(app)
elif app.config.autoapi_type == 'javascript':
domain = JavaScriptDomain(app)
domain.full()

@ -0,0 +1,20 @@
.. js:class:: {{ obj.name }}{% if obj.args %}({{ obj.args|join(',') }}){% endif %}
{% if obj.docstring %}
.. rubric:: Summary
{{ obj.docstring|indent(3) }}
{% endif %}
{% if obj.methods %}
{% for method in obj.methods %}
{% macro render() %}{{ method.render() }}{% endmacro %}
{{ render()|indent(3) }}
{%- endfor %}
{% endif %}

@ -0,0 +1,14 @@
{# Identention in this file is important #}
{% if is_method %}
{# Slice self off #}
.. method:: {{ obj.name.split('.')[-1] }}({{ args[1:]|join(',') }})
{% else %}
.. function:: {{ obj.name.split('.')[-1] }}({{ args|join(',') }})
{% endif %}
{% if obj.docstring %}
{{ obj.docstring|indent(3) }}
{% endif %}

@ -0,0 +1,7 @@
{# Identention in this file is important #}
.. {{ obj.type }}:: {{ obj.name }}
{{ obj.docstring|indent(3) }}

@ -0,0 +1,52 @@
{{ obj.name }}
{{ "-" * obj.name|length }}
{% block toc %}
{% if obj.children %}
.. toctree::
:maxdepth: 4
{% for item in obj.children|sort %}
/autoapi/{{ item.id.split('.')|join('/') }}/index
{%- endfor %}
{% endif %}
{% endblock %}
{% if obj.docstring %}
.. rubric:: Summary
{{ obj.docstring }}
{% endif %}
.. js:module:: {{ obj.name }}
{% block content %}
{%- macro display_type(item_type) %}
{{ item_type.title() }}
{{ "*" * item_type|length }}
{%- for obj_item in obj.item_map.get(item_type, []) %}
{% macro render() %}{{ obj_item.render() }}{% endmacro %}
{{ render()|indent(4) }}
{%- endfor %}
{%- endmacro %}
{%- for item_type in obj.item_map.keys() %}
{% if item_type.lower() != 'module' %}
{{ display_type(item_type) }}
{% endif %}
{%- endfor %}
{% endblock %}

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
project = u'jsexample'
copyright = u'2015, rtfd'
author = u'rtfd'
version = '0.1'
release = '0.1'
language = None
exclude_patterns = ['_build']
pygments_style = 'sphinx'
todo_include_todos = False
html_theme = 'alabaster'
html_static_path = ['_static']
htmlhelp_basename = 'jsexampledoc'
extensions = ['autoapi.extension']
autoapi_type = 'javascript'
autoapi_dir = 'example'
autoapi_file_pattern = '*.js'

@ -0,0 +1,53 @@
/**
* Creates an instance of Circle.
*
* @constructor
* @this {Circle}
* @param {number} r The desired radius of the circle.
*/
function Circle(r) {
/** @private */ this.radius = r;
/** @private */ this.circumference = 2 * Math.PI * r;
}
/**
* Creates a new Circle from a diameter.
*
* @param {number} d The desired diameter of the circle.
* @return {Circle} The new Circle object.
*/
Circle.fromDiameter = function (d) {
return new Circle(d / 2);
};
/**
* Calculates the circumference of the Circle.
*
* @deprecated
* @this {Circle}
* @return {number} The circumference of the circle.
*/
Circle.prototype.calculateCircumference = function () {
return 2 * Math.PI * this.radius;
};
/**
* Returns the pre-computed circumference of the Circle.
*
* @this {Circle}
* @return {number} The circumference of the circle.
*/
Circle.prototype.getCircumference = function () {
return this.circumference;
};
/**
* Find a String representation of the Circle.
*
* @override
* @this {Circle}
* @return {string} Human-readable representation of this Circle.
*/
Circle.prototype.toString = function () {
return "A Circle object with radius of " + this.radius + ".";
};

@ -0,0 +1,21 @@
Welcome to jsexample's documentation!
=====================================
.. toctree::
autoapi/index
Contents:
.. toctree::
:maxdepth: 2
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

@ -31,3 +31,20 @@ Function
finally:
os.chdir('../..')
class FullJavaScriptTests(unittest.TestCase):
def test_full_run(self):
os.chdir('tests/jsexample')
try:
if os.path.exists('_build'):
shutil.rmtree('_build')
os.mkdir('_build')
sp.check_call('sphinx-build -b text -d ./doctrees . _build/text', shell=True)
with open('_build/text/autoapi/Circle/index.txt') as fin:
text = fin.read().strip()
self.assertIn('Creates an instance of Circle', text)
finally:
os.chdir('../..')

Loading…
Cancel
Save