From bf488a43a82d44264993f88a2670fcb6d8e450b2 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Mon, 11 Nov 2024 12:26:17 +0000 Subject: [PATCH 1/4] Several changes: - As requested, document_to_rst() now takes an indent. It also takes a prefix in case the RST is a list item. - Fixes to escaping when generating HTML. - Change the documentation in formatter_functions.py to use 'r' strings, as discussed. - Fixes to the new DocViewer class. - Show stored templates and user template functions in DocViewer. This commit contains un_pogaz's changes (I think). --- src/calibre/gui2/dialogs/template_dialog.py | 36 ++- src/calibre/gui2/dialogs/template_dialog.ui | 8 +- src/calibre/utils/ffml_processor.py | 27 ++- src/calibre/utils/formatter_functions.py | 252 ++++++++++---------- 4 files changed, 176 insertions(+), 147 deletions(-) diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index d2e6a35d88..057b8356b6 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -60,8 +60,10 @@ from calibre.utils.resources import get_path as P class DocViewer(Dialog): - def __init__(self, docs_dsl, parent=None): + def __init__(self, docs_dsl, builtins, function_type_string_method, parent=None): self.docs_dsl = docs_dsl + self.builtins = builtins + self.function_type_string = function_type_string_method super().__init__(title=_('Template function documentation'), name='template_editor_doc_viewer_dialog', default_buttons=QDialogButtonBox.StandardButton.Close, parent=parent) @@ -69,7 +71,6 @@ class DocViewer(Dialog): return QSize(800, 600) def set_html(self, html): - print(html) self.doc_viewer_widget.setHtml(html) def setup_ui(self): @@ -84,18 +85,28 @@ class DocViewer(Dialog): b.clicked.connect(self.show_all_functions) b.setToolTip((_('Shows a list of all built-in functions in alphabetic order'))) - def show_function(self): - self.set_html( - self.docs_dsl.document_to_html(self.all_functions[self.current_function_name].doc, - self.current_function_name)) + def header_line(self, name): + return f'\n

{name} ({self.function_type_string(name, longform=False)})

\n' + + def show_function(self, function_name): + if function_name not in self.builtins or not self.builtins[function_name].doc: + self.set_html(self.header_line(function_name) + + ('No documentation provided')) + else: + self.set_html(self.header_line(function_name) + + self.docs_dsl.document_to_html(self.builtins[function_name].doc, function_name)) + def show_all_functions(self): - funcs = formatter_functions().get_builtins() result = [] a = result.append - for name in sorted(funcs): - a(f'\n

{name}

\n') + for name in sorted(self.builtins): + a(self.header_line(name)) try: - a(self.docs_dsl.document_to_html(funcs[name].doc.strip(), name)) + doc = self.builtins[name].doc + if not doc: + a(_('No documentation provided')) + else: + a(self.docs_dsl.document_to_html(self.builtins[name].doc.strip(), name)) except Exception: print('Exception in', name) raise @@ -572,7 +583,8 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): def open_documentation_viewer(self): if self.doc_viewer is None: - dv = self.doc_viewer = DocViewer(self.docs_dsl, self) + dv = self.doc_viewer = DocViewer(self.docs_dsl, self.all_functions, + self.function_type_string, parent=self) dv.finished.connect(self.doc_viewer_finished) dv.show() if self.current_function_name is not None: @@ -1018,7 +1030,7 @@ def evaluate(book, context): doc = self.all_functions[name].doc.strip() self.documentation.setHtml(self.docs_dsl.document_to_html(doc, name)) if self.doc_viewer is not None: - self.doc_viewer_widget.setHtml(self.docs_dsl.document_to_html(self.all_functions[name].doc, name)) + self.doc_viewer.show_function(name) if name in self.builtins and name in self.builtin_source_dict: self.source_code.setPlainText(self.builtin_source_dict[name]) else: diff --git a/src/calibre/gui2/dialogs/template_dialog.ui b/src/calibre/gui2/dialogs/template_dialog.ui index 171a72a54c..be39151dcf 100644 --- a/src/calibre/gui2/dialogs/template_dialog.ui +++ b/src/calibre/gui2/dialogs/template_dialog.ui @@ -747,7 +747,9 @@ you the value as well as all the local variables</p> &Documentation - Click this button to open the documentation in a separate dialog + Click this button to open the documentation in a separate dialog. +If no function is selected above then show all functions. +Selecting a function will show only that function's documentation @@ -762,8 +764,8 @@ you the value as well as all the local variables</p> <p>When using functions in a Single Function Mode template, for example {title:uppercase()}, the first parameter 'value' is omitted. It is automatically replaced -by the value of the specified field.</p> In all the other modes the value parameter -must be supplied. +by the value of the specified field.</p> <p>In all the other modes the value parameter +must be supplied.</p> diff --git a/src/calibre/utils/ffml_processor.py b/src/calibre/utils/ffml_processor.py index acd83ec898..9e7b3b4bdf 100644 --- a/src/calibre/utils/ffml_processor.py +++ b/src/calibre/utils/ffml_processor.py @@ -43,7 +43,7 @@ class Node: return self._text def escaped_text(self): - return prepare_string_for_xml(self._text) #.replace('<', 'LESS_THAN') #.replace('>', '>') + return prepare_string_for_xml(self._text) class DocumentNode(Node): @@ -90,9 +90,15 @@ class UrlNode(Node): def label(self): return self._label + def escaped_label(self): + return prepare_string_for_xml(self._label) + def url(self): return self._url + def escaped_url(self): + return prepare_string_for_xml(self._url) + class ListNode(Node): @@ -258,7 +264,7 @@ class FFMLProcessor: elif tree.node_kind() == NodeKinds.BLANK_LINE: result += '\n
\n
\n' elif tree.node_kind() == NodeKinds.URL: - result += f'{tree.label()}' + result += f'{tree.escaped_label()}' elif tree.node_kind() == NodeKinds.LIST: result += '\n