Commit 418a82f0 authored by Leonid Onokhov's avatar Leonid Onokhov

Docs: added cabal domain for sphinx

It features simple `pkg-section` directive for marking sections for
which subsequent `pkg-field` fields are defined. Needed to disambiguate
fields such as `type` or `main-is`.

Converted most of `developing-packages.rst` to use new directives

Future work:

* Add directives for describing project.local, make it reference
  relevant cabal arguments

* Add more meta, like since or deprecated which can be rendered in link
  titles and index.

* Add "quick reference" indices for cabal args, package.cabal and
 project.local fields. Reference these from sidebar.

* Using "since" and "deprectated field it is possible to create "what's
  new" index

[ci skip]
parent f3909074
......@@ -55,3 +55,6 @@ dist-test
register.sh
/Cabal/tests/PackageTests/Configure/include/HsZlibConfig.h
/Cabal/tests/PackageTests/Configure/zlib.buildinfo
# python artifacts from documentation builds
*.pyc
import re
from docutils import nodes
from docutils.parsers.rst import Directive, directives, roles
import pygments.lexer as lexer
import pygments.token as token
from sphinx import addnodes
from sphinx.directives import ObjectDescription
from sphinx.domains import ObjType, Domain
from sphinx.domains.std import StandardDomain
from sphinx.locale import l_, _
from sphinx.roles import XRefRole
from sphinx.util.nodes import make_refnode
class ExtraData(object):
def __init__(self, title=None, parent=None):
self.title = title
self.parent = parent
class CabalPackageSection(Directive):
"""
Directive marks a package.cabal section described next
and adds it to index. Can be referenced with pkg-directive.
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {}
def run(self):
env = self.state.document.settings.env
section = self.arguments[0].strip()
if section == 'None':
env.ref_context.pop('cabal:section', None)
return []
env.ref_context['cabal:section'] = section
targetname = 'pkg-section-' + section
node = nodes.target('', '', ids=[targetname])
self.state.document.note_explicit_target(node)
indexentry = section + '; package.cabal section'
inode = addnodes.index(entries=[('pair', indexentry,
targetname, '', None)])
extra = ExtraData(title = section)
env.domaindata['cabal']['pkg-sections'][section] = \
env.docname, targetname, extra
return [inode, node]
class CabalPackageField(ObjectDescription):
def handle_signature(self, sig, signode):
sig.strip()
parts = sig.split(':',1)
name = parts[0]
signode += addnodes.desc_name(name+':', name+':')
if len(parts) > 1:
rest = parts[1]
rest.strip()
signode += addnodes.desc_annotation(rest, rest)
return name
def add_target_and_index(self, name, sig, signode):
env = self.state.document.settings.env
section = self.env.ref_context.get('cabal:section')
if section is not None:
parts = (self.objtype, section, name)
indexentry = name + '; package.config ' + section + ' field '
else:
parts = (self.objtype, name)
indexentry = name + '; package.config field'
targetname = '-'.join(parts)
signode['ids'].append(targetname)
self.state.document.note_explicit_target(signode)
inode = addnodes.index(entries=[('pair', indexentry,
targetname, '', None)])
signode.insert(0, inode)
#for ref finding
extra = ExtraData(title = section, parent=section)
env.domaindata['cabal']['pkg-fields'][section,name] = \
env.docname, targetname, extra
class CabalPackageFieldXRef(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title, target):
parts = target.split(':',1)
if len(parts) == 2:
section, target = parts
section.strip()
target.strip()
refnode['cabal:section'] = section
else:
refnode['cabal:section'] = env.ref_context.get('cabal:section')
return title, target
def make_data_keys(typ, target, node):
if typ == 'pkg-field':
section = node.get('cabal:section')
return [(section, target), (None, target)]
else:
return [target]
def make_title(typ, name, extras):
title = extras.title if extras.title else name
if typ == 'pkg-section':
return "package.cabal " + title + "section"
if typ == 'pkg-field':
if extras.parent is not None:
base = "package.cabal " + extras.parent + "section field "
else:
base = "package.cabal field "
return base + title
class CabalDomain(Domain):
name = 'cabal'
label = 'Cabal'
object_types = {
'pkg-section': ObjType(l_('pkg-section'), 'pkg-section'),
'pkg-field' : ObjType(l_('pkg-field') , 'pkg-field' ),
}
directives = {
'pkg-section': CabalPackageSection,
'pkg-field' : CabalPackageField
}
roles = {
'pkg-section': XRefRole(warn_dangling=True),
'pkg-field' : CabalPackageFieldXRef(warn_dangling=True),
}
initial_data = {
'objects': {},
'pkg-sections': {},
'pkg-fields': {},
}
indices = [
]
types = {
'pkg-section': 'pkg-sections',
'pkg-field' : 'pkg-fields'
}
def clear_doc(self, docname):
for fullname, (fn, _) in self.data['objects'].items():
if fn == docname:
del self.data['objects'][fullname]
for k in ['pkg-sections', 'pkg-fields']:
for name, (fn, _, _) in self.data[k].items():
if fn == docname:
del self.data[k][comname]
def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode):
objtypes = self.objtypes_for_role(type)
for typ, key in ((typ, key) for typ in objtypes
for key in make_data_keys(typ, target, node)):
try:
data = env.domaindata['cabal'][self.types[typ]][key]
except KeyError:
continue
doc, name, extras = data
title = make_title(typ, name, extras)
return make_refnode(builder, fromdocname, doc, name, contnode, title)
class CabalLexer(lexer.RegexLexer):
name = 'Cabal'
aliases = ['cabal']
filenames = ['.cabal']
flags = re.MULTILINE
tokens = {
'root' : [
(r'\n', token.Text),
(r'^\s*(--.*)$', token.Comment.Single),
# key: value
(r'^(\s*)([\w\-_]+)(:)',
lexer.bygroups(token.Whitespace, token.Keyword, token.Punctuation)),
(r'^([\w\-_]+)', token.Keyword), # library, executable, flag etc.
(r'[^\S\n]+', token.Text),
(r'(\n\s*|\t)', token.Whitespace),
(r'&&|\|\||==|<=|>=|<|>|^>=', token.Operator),
(r',|:|{|}', token.Punctuation),
(r'.', token.Text)
],
}
def setup(app):
app.add_domain(CabalDomain)
app.add_lexer('cabal', CabalLexer())
......@@ -11,6 +11,8 @@ import sphinx_rtd_theme
# Support for :base-ref:, etc.
sys.path.insert(0, os.path.abspath('.'))
import cabaldomain
version = "1.25"
extensions = ['sphinx.ext.extlinks']
......@@ -40,6 +42,8 @@ release = version # The full version, including alpha/beta/rc tags.
highlight_language = 'cabal'
#pygments_style = 'tango'
primary_domain = 'cabal'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['.build', "*.gen.rst"]
......@@ -190,7 +194,8 @@ def setup(app):
doc_field_types=[
Field('since', label='Introduced in GHC version', names=['since']),
])
app.add_lexer('cabal', CabalLexer())
cabaldomain.setup(app)
def increase_python_stack():
# Workaround sphinx-build recursion limit overflow:
......@@ -200,29 +205,3 @@ def increase_python_stack():
# Default python allows recursion depth of 1000 calls.
sys.setrecursionlimit(10000)
import pygments.lexer as lexer
import pygments.token as token
import re
class CabalLexer(lexer.RegexLexer):
name = 'Cabal'
aliases = ['cabal']
filenames = ['.cabal']
flags = re.MULTILINE
tokens = {
'root' : [
(r'\n', token.Text),
(r'^\s*(--.*)$', token.Comment.Single),
# key: value
(r'^(\s*)([\w\-_]+)(:)',
lexer.bygroups(token.Whitespace, token.Keyword, token.Punctuation)),
(r'^([\w\-_]+)', token.Keyword), # library, executable, flag etc.
(r'[^\S\n]+', token.Text),
(r'(\n\s*|\t)', token.Whitespace),
(r'&&|\|\||==|<=|>=|<|>|^>=', token.Operator),
(r',|:|{|}', token.Punctuation),
(r'.', token.Text)
],
}
This diff is collapsed.
......@@ -146,6 +146,8 @@ The layout of these secure local repos matches the layout of remote
repositories exactly; the :hackage-pkg:`hackage-repo-tool`
can be used to create and manage such repositories.
.. _installing-packages:
Building and installing packages
================================
......@@ -441,6 +443,8 @@ The various commands and the additional options they support are
described below. In the simple build infrastructure, any other options
will be reported as errors.
.. _setup-configure:
setup configure
---------------
......@@ -1252,6 +1256,8 @@ Miscellaneous options
Specify a soft constraint on versions of a package. The solver will
attempt to satisfy these preferences on a "best-effort" basis.
.. _setup-build:
setup build
-----------
......@@ -1271,6 +1277,8 @@ This command takes the following options:
specified at the build step are in addition not in replacement of
any options specified at the configure step.
.. _setup-haddock:
setup haddock
-------------
......@@ -1338,6 +1346,8 @@ This command takes the following options:
runhaskell Setup.hs hscolour --css=*path*
.. _setup-hscolour:
setup hscolour
--------------
......@@ -1360,6 +1370,8 @@ This command takes the following options:
the given CSS file to the directory with the generated HTML files
(renamed to ``hscolour.css``) rather than linking to it.
.. _setup-install:
setup install
-------------
......@@ -1370,7 +1382,7 @@ register the package with the compiler, i.e. make the modules it
contains available to programs.
The `install locations <#installation-paths>`__ are determined by
options to ``setup configure``.
options to `setup configure`_.
This command takes the following options:
......@@ -1386,6 +1398,8 @@ This command takes the following options:
the default if the :option:`setup configure --user` option was supplied
to the ``configure`` command.)
.. _setup-copy:
setup copy
----------
......@@ -1401,6 +1415,8 @@ This command takes the following option:
Specify the directory under which to place installed files. If this is
not given, then the root directory is assumed.
.. _setup-register:
setup register
--------------
......@@ -1464,6 +1480,8 @@ This command takes the following options:
supplemental files installed --- plain Haskell libraries should be
fine.
.. _setup-unregister:
setup unregister
----------------
......@@ -1489,12 +1507,14 @@ This command takes the following options:
``unregister.sh``, on Windows, ``unregister.bat``. This script might
be included in a binary bundle, to be run on the target system.
.. _setup-clean:
setup clean
-----------
Remove any local files created during the ``configure``, ``build``,
``haddock``, ``register`` or ``unregister`` steps, and also any files
and directories listed in the ``extra-tmp-files`` field.
and directories listed in the :pkg-field:`extra-tmp-files` field.
This command takes the following options:
......@@ -1553,6 +1573,8 @@ the package.
quote options containing spaces because a single option is assumed,
so options will not be split on spaces.
.. _setup-sdist:
setup sdist
-----------
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment