comparison mercurial/help.py @ 51840:76387080f238

help: add :config-doc:`section.key` shorthand to insert documentation The config items defined in the configitems.toml file can already hold their documentation. Having some way to automatically insert it was a long standing low hanging fruit. So I did a first implementation on that. It fairly simple, but it open the door to more. It will be used in the next changeset.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Thu, 05 Sep 2024 12:28:12 +0200
parents 18c8c18993f0
children 607e94e01851
comparison
equal deleted inserted replaced
51839:fe08a0bfa9fd 51840:76387080f238
155 continue 155 continue
156 rst.append(b'%s:%s: %s\n' % (b' ' * indent, name, desc)) 156 rst.append(b'%s:%s: %s\n' % (b' ' * indent, name, desc))
157 if rst: 157 if rst:
158 rst.insert(0, b'\n%s\n\n' % header) 158 rst.insert(0, b'\n%s\n\n' % header)
159 return rst 159 return rst
160
161
162 def ext_help(ui: uimod.ui, ext) -> bytes:
163 doc = pycompat.getdoc(ext)
164 if doc is None:
165 return b""
166 assert doc is not None
167 doc = gettext(doc)
168 return sub_config_item_help(ui, doc)
160 169
161 170
162 def extshelp(ui: uimod.ui) -> bytes: 171 def extshelp(ui: uimod.ui) -> bytes:
163 rst = loaddoc(b'extensions')(ui).splitlines(True) 172 rst = loaddoc(b'extensions')(ui).splitlines(True)
164 rst.extend( 173 rst.extend(
363 package += b'.' + subdir 372 package += b'.' + subdir
364 with resourceutil.open_resource(package, topic + b'.txt') as fp: 373 with resourceutil.open_resource(package, topic + b'.txt') as fp:
365 doc = gettext(fp.read()) 374 doc = gettext(fp.read())
366 for rewriter in helphooks.get(topic, []): 375 for rewriter in helphooks.get(topic, []):
367 doc = rewriter(ui, topic, doc) 376 doc = rewriter(ui, topic, doc)
377 doc = sub_config_item_help(ui, doc)
368 return doc 378 return doc
369 379
370 return loader 380 return loader
371 381
372 382
693 return b'\n'.join(lines) 703 return b'\n'.join(lines)
694 704
695 return re.sub(br'( *)%s' % re.escape(marker), sub, doc) 705 return re.sub(br'( *)%s' % re.escape(marker), sub, doc)
696 706
697 707
708 _CONFIG_DOC_RE = re.compile(b'(^ *)?:config-doc:`([^`]+)`', flags=re.MULTILINE)
709
710
711 def sub_config_item_help(ui: uimod.ui, doc: bytes) -> bytes:
712 """replace :config-doc:`foo.bar` markup with the item documentation
713
714 This allow grouping config item declaration and help without having to
715 repeat it in the help text file and keep that in sync.
716 """
717 pieces = []
718 last_match_end = 0
719 for match in _CONFIG_DOC_RE.finditer(doc):
720 # finditer is expected to yield result in order
721 start = match.start()
722 assert last_match_end <= match.start()
723 pieces.append(doc[last_match_end:start])
724 item_name = match.group(2)
725 section, key = item_name.split(b'.', 1)
726 section_items = ui._knownconfig.get(section)
727 if section_items is None:
728 item = None
729 else:
730 item = section_items.get(key)
731 if item is None or not item.documentation:
732 item_doc = b'<missing help text for `%s`>' % item_name
733 else:
734 item_doc = gettext(item.documentation)
735 item_doc = sub_config_item_help(ui, item_doc)
736 indent = match.group(1)
737 if indent: # either None or 0 should be ignored
738 indent = indent
739 item_doc = indent + item_doc.replace(b'\n', b'\n' + indent)
740 pieces.append(item_doc)
741 last_match_end = match.end()
742 pieces.append(doc[last_match_end:])
743 return b''.join(pieces)
744
745
698 def _getcategorizedhelpcmds( 746 def _getcategorizedhelpcmds(
699 ui: uimod.ui, cmdtable, name: bytes, select: Optional[_SelectFn] = None 747 ui: uimod.ui, cmdtable, name: bytes, select: Optional[_SelectFn] = None
700 ) -> Tuple[Dict[bytes, List[bytes]], Dict[bytes, bytes], _SynonymTable]: 748 ) -> Tuple[Dict[bytes, List[bytes]], Dict[bytes, bytes], _SynonymTable]:
701 # Category -> list of commands 749 # Category -> list of commands
702 cats = {} 750 cats = {}
820 doc = _(b'alias for: hg %s\n\n%s\n\ndefined by: %s\n') % ( 868 doc = _(b'alias for: hg %s\n\n%s\n\ndefined by: %s\n') % (
821 entry[0].definition, 869 entry[0].definition,
822 doc, 870 doc,
823 source, 871 source,
824 ) 872 )
873 doc = sub_config_item_help(ui, doc)
825 doc = doc.splitlines(True) 874 doc = doc.splitlines(True)
826 if ui.quiet or not full: 875 if ui.quiet or not full:
827 rst.append(doc[0]) 876 rst.append(doc[0])
828 else: 877 else:
829 rst.extend(doc) 878 rst.extend(doc)
1040 return rst 1089 return rst
1041 1090
1042 def helpext(name: bytes, subtopic: Optional[bytes] = None) -> List[bytes]: 1091 def helpext(name: bytes, subtopic: Optional[bytes] = None) -> List[bytes]:
1043 try: 1092 try:
1044 mod = extensions.find(name) 1093 mod = extensions.find(name)
1045 doc = gettext(pycompat.getdoc(mod)) or _(b'no help text available') 1094 doc = ext_help(ui, mod)
1095 if not doc:
1096 doc = _(b'no help text available')
1046 except KeyError: 1097 except KeyError:
1047 mod = None 1098 mod = None
1048 doc = extensions.disabled_help(name) 1099 doc = extensions.disabled_help(name)
1049 if not doc: 1100 if not doc:
1050 raise error.UnknownCommand(name) 1101 raise error.UnknownCommand(name)
1102 doc = sub_config_item_help(ui, doc)
1051 1103
1052 if b'\n' not in doc: 1104 if b'\n' not in doc:
1053 head, tail = doc, b"" 1105 head, tail = doc, b""
1054 else: 1106 else:
1055 head, tail = doc.split(b'\n', 1) 1107 head, tail = doc.split(b'\n', 1)