# -*- coding: utf-8 -*- """ sphinx.domains.javascript ~~~~~~~~~~~~~~~~~~~~~~~~~ The JavaScript domain. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ from sphinx import addnodes from sphinx.domains import Domain, ObjType from sphinx.locale import l_, _ from sphinx.directives import ObjectDescription from sphinx.roles import XRefRole from sphinx.domains.python import _pseudo_parse_arglist from sphinx.util.nodes import make_refnode from sphinx.util.docfields import Field, GroupedField, TypedField class JSObject(ObjectDescription): """ Description of a JavaScript object. """ #: If set to ``True`` this object is callable and a `desc_parameterlist` is #: added has_arguments = False #: what is displayed right before the documentation entry display_prefix = None def handle_signature(self, sig, signode): sig = sig.strip() if '(' in sig and sig[-1:] == ')': prefix, arglist = sig.split('(', 1) prefix = prefix.strip() arglist = arglist[:-1].strip() else: prefix = sig arglist = None if '.' in prefix: nameprefix, name = prefix.rsplit('.', 1) else: nameprefix = None name = prefix objectname = self.env.temp_data.get('js:object') if nameprefix: if objectname: # someone documenting the method of an attribute of the current # object? shouldn't happen but who knows... nameprefix = objectname + '.' + nameprefix fullname = nameprefix + '.' + name elif objectname: fullname = objectname + '.' + name else: # just a function or constructor objectname = '' fullname = name signode['object'] = objectname signode['fullname'] = fullname if self.display_prefix: signode += addnodes.desc_annotation(self.display_prefix, self.display_prefix) if nameprefix: signode += addnodes.desc_addname(nameprefix + '.', nameprefix + '.') signode += addnodes.desc_name(name, name) if self.has_arguments: if not arglist: signode += addnodes.desc_parameterlist() else: _pseudo_parse_arglist(signode, arglist) return fullname, nameprefix def add_target_and_index(self, name_obj, sig, signode): objectname = self.options.get( 'object', self.env.temp_data.get('js:object')) fullname = name_obj[0] if fullname not in self.state.document.ids: signode['names'].append(fullname) signode['ids'].append(fullname.replace('$', '_S_')) signode['first'] = not self.names self.state.document.note_explicit_target(signode) objects = self.env.domaindata['js']['objects'] if fullname in objects: self.state_machine.reporter.warning( 'duplicate object description of %s, ' % fullname + 'other instance in ' + self.env.doc2path(objects[fullname][0]), line=self.lineno) objects[fullname] = self.env.docname, self.objtype indextext = self.get_index_text(objectname, name_obj) if indextext: self.indexnode['entries'].append(('single', indextext, fullname.replace('$', '_S_'), '')) def get_index_text(self, objectname, name_obj): name, obj = name_obj if self.objtype == 'function': if not obj: return _('%s() (built-in function)') % name return _('%s() (%s method)') % (name, obj) elif self.objtype == 'class': return _('%s() (class)') % name elif self.objtype == 'data': return _('%s (global variable or constant)') % name elif self.objtype == 'attribute': return _('%s (%s attribute)') % (name, obj) return '' class JSCallable(JSObject): """Description of a JavaScript function, method or constructor.""" has_arguments = True doc_field_types = [ TypedField('arguments', label=l_('Arguments'), names=('argument', 'arg', 'parameter', 'param'), typerolename='func', typenames=('paramtype', 'type')), GroupedField('errors', label=l_('Throws'), rolename='err', names=('throws', ), can_collapse=True), Field('returnvalue', label=l_('Returns'), has_arg=False, names=('returns', 'return')), Field('returntype', label=l_('Return type'), has_arg=False, names=('rtype',)), ] class JSConstructor(JSCallable): """Like a callable but with a different prefix.""" display_prefix = 'class ' class JSXRefRole(XRefRole): def process_link(self, env, refnode, has_explicit_title, title, target): # basically what sphinx.domains.python.PyXRefRole does refnode['js:object'] = env.temp_data.get('js:object') if not has_explicit_title: title = title.lstrip('.') target = target.lstrip('~') if title[0:1] == '~': title = title[1:] dot = title.rfind('.') if dot != -1: title = title[dot+1:] if target[0:1] == '.': target = target[1:] refnode['refspecific'] = True return title, target class JavaScriptDomain(Domain): """JavaScript language domain.""" name = 'js' label = 'JavaScript' # if you add a new object type make sure to edit JSObject.get_index_string object_types = { 'function': ObjType(l_('function'), 'func'), 'class': ObjType(l_('class'), 'class'), 'data': ObjType(l_('data'), 'data'), 'attribute': ObjType(l_('attribute'), 'attr'), } directives = { 'function': JSCallable, 'class': JSConstructor, 'data': JSObject, 'attribute': JSObject, } roles = { 'func': JSXRefRole(fix_parens=True), 'class': JSXRefRole(fix_parens=True), 'data': JSXRefRole(), 'attr': JSXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype } def clear_doc(self, docname): for fullname, (fn, _) in self.data['objects'].items(): if fn == docname: del self.data['objects'][fullname] def find_obj(self, env, obj, name, typ, searchorder=0): if name[-2:] == '()': name = name[:-2] objects = self.data['objects'] newname = None if searchorder == 1: if obj and obj + '.' + name in objects: newname = obj + '.' + name else: newname = name else: if name in objects: newname = name elif obj and obj + '.' + name in objects: newname = obj + '.' + name return newname, objects.get(newname) def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): objectname = node.get('js:object') searchorder = node.hasattr('refspecific') and 1 or 0 name, obj = self.find_obj(env, objectname, target, typ, searchorder) if not obj: return None return make_refnode(builder, fromdocname, obj[0], name.replace('$', '_S_'), contnode, name) def get_objects(self): for refname, (docname, type) in self.data['objects'].iteritems(): yield refname, refname, type, docname, \ refname.replace('$', '_S_'), 1