8d46b7cd81
It's just names and comments, they already used `package_config.json` as the package configuration. Change-Id: Icd421ed99079fa9058426e1536565658938252ac Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/414100 Commit-Queue: Lasse Nielsen <lrn@google.com> Reviewed-by: Brian Quinlan <bquinlan@google.com>
160 lines
5.8 KiB
Python
160 lines
5.8 KiB
Python
# Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
|
|
# for details. All rights reserved. Use of this source code is governed by a
|
|
# BSD-style license that can be found in the LICENSE file.
|
|
#
|
|
"""Markdown extension for xrefs and reference to other Markdown files.
|
|
|
|
Xref is a reference of form [`symbol`][] or [text][`symbol`], where symbol
|
|
is expected to be one of the following:
|
|
|
|
* package:-scheme URI - it will be resolved using the
|
|
.dart_tool/package_config.json file in the root directory
|
|
* file path
|
|
* C++ symbol - will be resolved through xref.json file (see README.md)
|
|
|
|
Xrefs are converted to GitHub links.
|
|
|
|
Additionally this extension retargets links pointing to markdown files to
|
|
the html files produced from these markdown files.
|
|
|
|
Usage: markdown.markdown(extensions=[XrefExtension()])
|
|
"""
|
|
|
|
import json
|
|
import logging
|
|
import os
|
|
import re
|
|
|
|
import xml.etree.ElementTree as etree
|
|
from typing import Dict, Optional
|
|
from urllib.parse import urlparse
|
|
|
|
from cpp_indexer import SymbolsIndex, load_index
|
|
from markdown.extensions import Extension
|
|
from markdown.inlinepatterns import InlineProcessor
|
|
from markdown.treeprocessors import Treeprocessor
|
|
|
|
|
|
class _XrefPattern(InlineProcessor):
|
|
"""Converts xrefs into GitHub links.
|
|
|
|
Recognizes [`symbol`][] and [text][`symbol`] link formats where symbol
|
|
is expected to be one of the following:
|
|
|
|
* Fully qualified reference to a C++ class, method or function;
|
|
* Package URI pointing to one of the packages included in the SDK
|
|
checkout.
|
|
* File reference to one of the file in the SDK.
|
|
"""
|
|
|
|
XREF_RE = r'\[`(?P<symbol>[^]]+)`\]?\[\]|\[(?P<text>[^]]*)\]\[`(?P<target>[^]]+)`\]'
|
|
|
|
def __init__(self, md, symbols_index: SymbolsIndex,
|
|
packages: Dict[str, str]):
|
|
super().__init__(_XrefPattern.XREF_RE)
|
|
self.symbols_index = symbols_index
|
|
self.packages = packages
|
|
self.md = md
|
|
|
|
def handleMatch(self, m, data):
|
|
text = m.group('text')
|
|
symbol = m.group('symbol')
|
|
if symbol is None:
|
|
symbol = m.group('target')
|
|
|
|
uri = self._resolve_ref(symbol) or '#broken-link'
|
|
|
|
# Remember this xref. build process can later use this information
|
|
# to produce xref reference section at the end of the markdown file.
|
|
self.md.xrefs[f"`{symbol}`"] = uri
|
|
|
|
# Create <a href='uri'>text</a> element. If text is not defined
|
|
# simply use a slightly sanitized symbol name.
|
|
anchor = etree.Element('a')
|
|
anchor.attrib['href'] = uri
|
|
anchor.attrib['target'] = '_blank'
|
|
if text is not None:
|
|
anchor.text = text
|
|
else:
|
|
code = etree.Element('code')
|
|
code.text = re.sub(r'^dart::', '', symbol)
|
|
anchor.append(code)
|
|
|
|
# Replace the whole pattern match with anchor element.
|
|
return anchor, m.start(0), m.end(0)
|
|
|
|
def _resolve_ref(self, ref: str) -> Optional[str]:
|
|
if ref.startswith('package:'):
|
|
# Resolve as package uri via package_config.json.
|
|
uri = urlparse(ref)
|
|
(package_name, *path_to_file) = uri.path.split('/', 1)
|
|
package_path = self.packages[package_name]
|
|
if len(path_to_file) == 0:
|
|
return self._make_github_uri(package_path)
|
|
else:
|
|
return self._make_github_uri(
|
|
os.path.join(package_path, path_to_file[0]))
|
|
elif os.path.exists(ref):
|
|
# Resolve as a file link.
|
|
return self._make_github_uri(ref)
|
|
else:
|
|
# Resolve as a symbol.
|
|
loc = self.symbols_index.try_resolve(ref)
|
|
if loc is not None:
|
|
return self._make_github_uri(loc.filename, loc.lineno)
|
|
|
|
logging.error('Failed to resolve xref %s', ref)
|
|
return None
|
|
|
|
def _make_github_uri(self, file: str, lineno: Optional[int] = None) -> str:
|
|
"""Generates source link pointing to GitHub"""
|
|
fragment = f'#L{lineno}' if lineno is not None else ''
|
|
return f'https://github.com/dart-lang/sdk/blob/{self.symbols_index.commit}/{file}{fragment}'
|
|
|
|
|
|
class _MdLinkFixerTreeprocessor(Treeprocessor):
|
|
"""Redirects links pointing to .md files to .html files built from them."""
|
|
|
|
def run(self, root):
|
|
for elem in root.iter('a'):
|
|
href = elem.get('href')
|
|
if href is None:
|
|
continue
|
|
parsed_href = urlparse(href)
|
|
if parsed_href.path.endswith('.md'):
|
|
elem.set(
|
|
'href',
|
|
parsed_href._replace(path=parsed_href.path[:-3] +
|
|
'.html').geturl())
|
|
|
|
|
|
class XrefExtension(Extension):
|
|
"""Markdown extension which handles xrefs and links to markdown files."""
|
|
symbols_index: SymbolsIndex
|
|
packages: Dict[str, str]
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.symbols_index = load_index('xref.json')
|
|
self.packages = XrefExtension._load_package_config()
|
|
|
|
def extendMarkdown(self, md):
|
|
md.xrefs = {}
|
|
md.treeprocessors.register(_MdLinkFixerTreeprocessor(), 'mdlinkfixer',
|
|
0)
|
|
md.inlinePatterns.register(
|
|
_XrefPattern(md, self.symbols_index, self.packages), 'xref', 200)
|
|
|
|
@staticmethod
|
|
def _load_package_config() -> Dict[str, str]:
|
|
# Load package_config.json file into a dictionary.
|
|
with open('.dart_tool/package_config.json',
|
|
encoding='utf-8') as package_config_file:
|
|
package_config = json.load(package_config_file)
|
|
return dict([(pkg['name'],
|
|
os.path.normpath(
|
|
os.path.join('.dart_tool/', pkg['rootUri'],
|
|
pkg['packageUri'])))
|
|
for pkg in package_config['packages']
|
|
if 'packageUri' in pkg])
|