summaryrefslogtreecommitdiff
path: root/Utilities
diff options
context:
space:
mode:
authorMatthew Woehlke <matthew.woehlke@kitware.com>2023-03-13 13:31:38 -0400
committerMatthew Woehlke <matthew.woehlke@kitware.com>2023-03-13 13:31:38 -0400
commit39ecaa5da1f1e42cb62d4ce3d6870409cdefec07 (patch)
tree19cc31480f40825ec15be1656aeb552b19356e07 /Utilities
parent9db40bec4e1004baa8b397c8b52c248d8de71f67 (diff)
downloadcmake-39ecaa5da1f1e42cb62d4ce3d6870409cdefec07.tar.gz
Utilities/Sphinx: Improve word wrap of signatures
Implement logic to support several styles of parsing in the new signature directive that control where line breaks are allowed in a signature. The default is 'smart', which forbids breaks inside of square- or angle-brackets. The 'verbatim' option forbids all breaks. In all cases, breaks are always allowed where a newline appears in the source. This seems to Just Work for most writers, but HTML needs some special handling that is accomplished by a new CSS rule and assigning the 'nbsp' class to spaces that are not allowed to break. (ROFF's line wrapping is rather unfortunate here, as it prefers splitting and hyphenating words rather than breaking at a space.)
Diffstat (limited to 'Utilities')
-rw-r--r--Utilities/Sphinx/cmake.py83
-rw-r--r--Utilities/Sphinx/static/cmake.css5
2 files changed, 76 insertions, 12 deletions
diff --git a/Utilities/Sphinx/cmake.py b/Utilities/Sphinx/cmake.py
index a07b6e9091..1428e1d476 100644
--- a/Utilities/Sphinx/cmake.py
+++ b/Utilities/Sphinx/cmake.py
@@ -5,7 +5,7 @@ import os
import re
from dataclasses import dataclass
-from typing import Any, cast
+from typing import Any, List, cast
# Override much of pygments' CMakeLexer.
# We need to parse CMake syntax definitions, not CMake code.
@@ -319,14 +319,69 @@ class CMakeObject(ObjectDescription):
class CMakeSignatureObject(CMakeObject):
object_type = 'signature'
+ BREAK_ALL = 'all'
+ BREAK_SMART = 'smart'
+ BREAK_VERBATIM = 'verbatim'
+
+ BREAK_CHOICES = {BREAK_ALL, BREAK_SMART, BREAK_VERBATIM}
+
+ def break_option(argument):
+ return directives.choice(argument, CMakeSignatureObject.BREAK_CHOICES)
+
option_spec = {
'target': directives.unchanged,
+ 'break': break_option,
}
- def get_signatures(self):
+ def _break_signature_all(sig: str) -> str:
+ return ws_re.sub(' ', sig)
+
+ def _break_signature_verbatim(sig: str) -> str:
+ lines = [ws_re.sub('\xa0', line.strip()) for line in sig.split('\n')]
+ return ' '.join(lines)
+
+ def _break_signature_smart(sig: str) -> str:
+ tokens = []
+ for line in sig.split('\n'):
+ token = ''
+ delim = ''
+
+ for c in line.strip():
+ if len(delim) == 0 and ws_re.match(c):
+ if len(token):
+ tokens.append(ws_re.sub('\xa0', token))
+ token = ''
+ else:
+ if c == '[':
+ delim += ']'
+ elif c == '<':
+ delim += '>'
+ elif len(delim) and c == delim[-1]:
+ delim = delim[:-1]
+ token += c
+
+ if len(token):
+ tokens.append(ws_re.sub('\xa0', token))
+
+ return ' '.join(tokens)
+
+ def __init__(self, *args, **kwargs):
+ self.targetnames = {}
+ self.break_style = CMakeSignatureObject.BREAK_SMART
+ super().__init__(*args, **kwargs)
+
+ def get_signatures(self) -> List[str]:
content = nl_escape_re.sub('', self.arguments[0])
lines = sig_end_re.split(content)
- return [ws_re.sub(' ', line.strip()) for line in lines]
+
+ if self.break_style == CMakeSignatureObject.BREAK_VERBATIM:
+ fixup = CMakeSignatureObject._break_signature_verbatim
+ elif self.break_style == CMakeSignatureObject.BREAK_SMART:
+ fixup = CMakeSignatureObject._break_signature_smart
+ else:
+ fixup = CMakeSignatureObject._break_signature_all
+
+ return [fixup(line.strip()) for line in lines]
def handle_signature(self, sig, signode):
language = 'cmake'
@@ -344,7 +399,9 @@ class CMakeSignatureObject(CMakeObject):
raise self.warning(error)
for classes, value in tokens:
- if classes:
+ if value == '\xa0':
+ node += nodes.inline(value, value, classes=['nbsp'])
+ elif classes:
node += nodes.inline(value, value, classes=classes)
else:
node += nodes.Text(value)
@@ -354,13 +411,10 @@ class CMakeSignatureObject(CMakeObject):
return sig
- def __init__(self, *args, **kwargs):
- self.targetnames = {}
- super().__init__(*args, **kwargs)
-
def add_target_and_index(self, name, sig, signode):
- if name in self.targetnames:
- sigargs = self.targetnames[name]
+ sig = sig.replace('\xa0', ' ')
+ if sig in self.targetnames:
+ sigargs = self.targetnames[sig]
else:
def extract_keywords(params):
for p in params:
@@ -369,7 +423,7 @@ class CMakeSignatureObject(CMakeObject):
else:
return
- keywords = extract_keywords(name.split('(')[1].split())
+ keywords = extract_keywords(sig.split('(')[1].split())
sigargs = ' '.join(keywords)
targetname = sigargs.lower()
targetid = nodes.make_id(targetname)
@@ -381,7 +435,7 @@ class CMakeSignatureObject(CMakeObject):
self.state.document.note_explicit_target(signode)
# Register the signature as a command object.
- command = name.split('(')[0].lower()
+ command = sig.split('(')[0].lower()
refname = f'{command}({sigargs})'
refid = f'command:{command}({targetname})'
@@ -390,6 +444,8 @@ class CMakeSignatureObject(CMakeObject):
node_id=targetid, location=signode)
def run(self):
+ self.break_style = CMakeSignatureObject.BREAK_ALL
+
targets = self.options.get('target')
if targets is not None:
signatures = self.get_signatures()
@@ -397,6 +453,9 @@ class CMakeSignatureObject(CMakeObject):
for signature, target in zip(signatures, targets):
self.targetnames[signature] = target
+ self.break_style = (
+ self.options.get('break', CMakeSignatureObject.BREAK_SMART))
+
return super().run()
class CMakeXRefRole(XRefRole):
diff --git a/Utilities/Sphinx/static/cmake.css b/Utilities/Sphinx/static/cmake.css
index dd0dd0272d..41a74f5ac5 100644
--- a/Utilities/Sphinx/static/cmake.css
+++ b/Utilities/Sphinx/static/cmake.css
@@ -40,6 +40,11 @@ div.sphinxsidebarwrapper {
font-weight: normal;
}
+/* Implement non-breaking spaces in signatures. */
+.nbsp {
+ white-space: nowrap;
+}
+
/* Remove unwanted margin in case list item contains a div-wrapping
directive like `.. versionadded` or `.. deprecated`. */
dd > :first-child > p {