""" TypeScript domain. :copyright: Copyright 2019 by Taler Systems SA :license: LGPLv3+ :author: Florian Dold """ import re from docutils import nodes from typing import List, Optional, Iterable, Dict, Tuple from typing import cast from pygments.lexers import get_lexer_by_name from pygments.filter import Filter from pygments.token import Literal, Text, Operator, Keyword, Name, Number from pygments.token import Comment, Token, _TokenType from pygments.token import * from pygments.lexer import RegexLexer, bygroups, include from pygments.formatters import HtmlFormatter from docutils import nodes from docutils.nodes import Element, Node from sphinx.roles import XRefRole from sphinx.domains import Domain, ObjType, Index from sphinx.directives import directives from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import make_refnode from sphinx.util import logging from sphinx.highlighting import PygmentsBridge from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.pygments_styles import SphinxStyle logger = logging.getLogger(__name__) class TypeScriptDefinition(SphinxDirective): """ Directive for a code block with special highlighting or line numbering settings. """ has_content = True required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False option_spec = { 'force': directives.flag, 'linenos': directives.flag, 'dedent': int, 'lineno-start': int, 'emphasize-lines': directives.unchanged_required, 'caption': directives.unchanged_required, 'class': directives.class_option, } def run(self) -> List[Node]: document = self.state.document code = '\n'.join(self.content) location = self.state_machine.get_source_and_line(self.lineno) linespec = self.options.get('emphasize-lines') if linespec: try: nlines = len(self.content) hl_lines = parselinenos(linespec, nlines) if any(i >= nlines for i in hl_lines): logger.warning( __('line number spec is out of range(1-%d): %r') % (nlines, self.options['emphasize-lines']), location=location ) hl_lines = [x + 1 for x in hl_lines if x < nlines] except ValueError as err: return [document.reporter.warning(err, line=self.lineno)] else: hl_lines = None if 'dedent' in self.options: location = self.state_machine.get_source_and_line(self.lineno) lines = code.split('\n') lines = dedent_lines( lines, self.options['dedent'], location=location ) code = '\n'.join(lines) literal = nodes.literal_block(code, code) # type: Element if 'linenos' in self.options or 'lineno-start' in self.options: literal['linenos'] = True literal['classes'] += self.options.get('class', []) literal['force'] = 'force' in self.options literal['language'] = "tsref" extra_args = literal['highlight_args'] = {} if hl_lines is not None: extra_args['hl_lines'] = hl_lines if 'lineno-start' in self.options: extra_args['linenostart'] = self.options['lineno-start'] self.set_source_info(literal) caption = self.options.get('caption') if caption: try: literal = container_wrapper(self, literal, caption) except ValueError as exc: return [document.reporter.warning(exc, line=self.lineno)] tsid = "tsref-type-" + self.arguments[0] literal['ids'].append(tsid) tsname = self.arguments[0] ts = self.env.get_domain('ts') ts.add_object('type', tsname, self.env.docname, tsid) return [literal] class TypeScriptDomain(Domain): """TypeScript domain.""" name = 'ts' label = 'TypeScript' directives = { 'def': TypeScriptDefinition, } roles = { 'type': XRefRole( lowercase=False, warn_dangling=True, innernodeclass=nodes.inline ), } dangling_warnings = { 'type': 'undefined TypeScript type: %(target)s', } def resolve_xref( self, env, fromdocname, builder, typ, target, node, contnode ): try: info = self.objects[(str(typ), str(target))] except KeyError: logger.warn("type {}/{} not found".format(typ, target)) return None else: anchor = "tsref-type-{}".format(str(target)) title = typ.upper() + ' ' + target return make_refnode( builder, fromdocname, info[0], anchor, contnode, title ) def resolve_any_xref( self, env, fromdocname, builder, target, node, contnode ): """Resolve the pending_xref *node* with the given *target*. The reference comes from an "any" or similar role, which means that Sphinx don't know the type. For now sphinxcontrib-httpdomain doesn't resolve any xref nodes. :return: list of tuples ``('domain:role', newnode)``, where ``'domain:role'`` is the name of a role that could have created the same reference, """ ret = [] try: info = self.objects[("type", str(target))] except KeyError: pass else: anchor = "tsref-type-{}".format(str(target)) title = "TYPE" + ' ' + target node = make_refnode( builder, fromdocname, info[0], anchor, contnode, title ) ret.append(("ts:type", node)) return ret @property def objects(self) -> Dict[Tuple[str, str], Tuple[str, str]]: return self.data.setdefault( 'objects', {} ) # (objtype, name) -> docname, labelid def add_object( self, objtype: str, name: str, docname: str, labelid: str ) -> None: self.objects[objtype, name] = (docname, labelid) class BetterTypeScriptLexer(RegexLexer): """ For `TypeScript `_ source code. """ name = 'TypeScript' aliases = ['ts'] filenames = ['*.ts'] mimetypes = ['text/x-typescript'] flags = re.DOTALL tokens = { 'commentsandwhitespace': [(r'\s+', Text), (r'