mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Edit Book: Search & Replace function mode: Add a new function annotation (replace.file_order) to control what order multiple files are processed in, when doing Replace All. See the User Manual for details.
This commit is contained in:
parent
f89b974b14
commit
728f82cf44
@ -129,15 +129,24 @@ they look like :code:`<h2>1. Some text</h2>`.
|
||||
section_number = '%d. ' % number
|
||||
return match.group(1) + section_number + match.group(2)
|
||||
|
||||
# Ensure that when running over multiple files, the files are processed
|
||||
# in the order in which they appear in the book
|
||||
replace.file_order = 'spine'
|
||||
|
||||
Use it with the find expression::
|
||||
|
||||
(?s)(<h2[^<>]*>)(.+?</h2>)
|
||||
|
||||
Place the cursor at the top of the file and click :guilabel:`Replace all`. This
|
||||
function uses another of the useful extra arguments to ``replace()``: the
|
||||
Place the cursor at the top of the file and click :guilabel:`Replace all`.
|
||||
|
||||
This function uses another of the useful extra arguments to ``replace()``: the
|
||||
``number`` argument. When doing a :guilabel:`Replace All` number is
|
||||
automatically incremented for every successive match.
|
||||
|
||||
Another new feature is the use of ``replace.file_order`` -- setting that to
|
||||
``'spine'`` means that if this search is run on multiple HTML files, the files
|
||||
are processed in the order in which they appear in the book.
|
||||
|
||||
|
||||
Auto create a Table of Contents
|
||||
-------------------------------------
|
||||
@ -159,16 +168,12 @@ Contents based on these headings. Create the custom function below:
|
||||
# All matches found, output the resulting Table of Contents.
|
||||
# The argument metadata is the metadata of the book being edited
|
||||
if 'toc' in data:
|
||||
book = current_container()
|
||||
toc = data['toc']
|
||||
# Re-arrange the entries in the spine order of the book
|
||||
spine_order = {name:i for i, (name, is_linear) in enumerate(book.spine_names)}
|
||||
toc.sort(key=lambda x: spine_order.get(x[0]))
|
||||
root = TOC()
|
||||
for (file_name, tag_name, anchor, text) in toc:
|
||||
parent = root.children[-1] if tag_name == 'h2' and root.children else root
|
||||
parent.add(text, file_name, anchor)
|
||||
toc = toc_to_html(root, book, 'toc.html', 'Table of Contents for ' + metadata.title, metadata.language)
|
||||
toc = toc_to_html(root, current_container(), 'toc.html', 'Table of Contents for ' + metadata.title, metadata.language)
|
||||
print (xml2str(toc))
|
||||
else:
|
||||
print ('No headings to build ToC from found')
|
||||
@ -185,6 +190,9 @@ Contents based on these headings. Create the custom function below:
|
||||
# Ensure that we are called once after the last match is found so we can
|
||||
# output the ToC
|
||||
replace.call_after_last_match = True
|
||||
# Ensure that when running over multiple files, this function is called,
|
||||
# the files are processed in the order in which they appear in the book
|
||||
replace.file_order = 'spine'
|
||||
|
||||
And use it with the find expression::
|
||||
|
||||
@ -325,12 +333,31 @@ function from python. The output of print will be displayed in a popup window
|
||||
after the Find/replace has completed. You saw an example of using ``print()``
|
||||
to output an entire table of contents above.
|
||||
|
||||
Choose file order when running on multiple HTML files
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When you run a :guilabel:`Replace All` on multiple HTML files, the order in
|
||||
which the files are processes depends on what files you have open for editing.
|
||||
You can force the search to process files in the order in which the appear by
|
||||
setting the ``file_order`` attribute on your function, like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
|
||||
...
|
||||
|
||||
replace.file_order = 'spine'
|
||||
|
||||
``file_order`` accepts two values, ``spine`` and ``spine-reverse`` which cause
|
||||
the search to process multiple files in the order they appear in the book,
|
||||
either forwards or backwards, respectively.
|
||||
|
||||
Having your function called an extra time after the last match is found
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sometimes, as in the auto generate table of contents example above, it is
|
||||
useful to have your function called an extra time after the last match is
|
||||
found. You can do this by setting the call_after_last_match attribute on your
|
||||
found. You can do this by setting the ``call_after_last_match`` attribute on your
|
||||
function, like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -56,6 +56,7 @@ class Function(object):
|
||||
self.mod = None
|
||||
if not callable(self.func):
|
||||
raise ValueError('%r is not a function' % self.func)
|
||||
self.file_order = getattr(self.func, 'file_order', None)
|
||||
|
||||
def init_env(self, name=''):
|
||||
from calibre.gui2.tweak_book.boss import get_boss
|
||||
|
@ -1193,6 +1193,11 @@ def show_function_debug_output(func):
|
||||
from calibre.gui2.tweak_book.boss import get_boss
|
||||
get_boss().gui.sr_debug_output.show_log(func.name, val)
|
||||
|
||||
def reorder_files(names, order):
|
||||
reverse = order in {'spine-reverse', 'reverse-spine'}
|
||||
spine_order = {name:i for i, (name, is_linear) in enumerate(current_container().spine_names)}
|
||||
return sorted(frozenset(names), key=spine_order.get, reverse=reverse)
|
||||
|
||||
def run_search(
|
||||
searches, action, current_editor, current_editor_name, searchable_names,
|
||||
gui_parent, show_editor, edit_file, show_current_diff, add_savepoint, rewind_savepoint, set_modified):
|
||||
@ -1292,7 +1297,7 @@ def run_search(
|
||||
lfiles = files or {current_editor_name:editor.syntax}
|
||||
updates = set()
|
||||
raw_data = {}
|
||||
for n, syntax in lfiles.iteritems():
|
||||
for n in lfiles:
|
||||
if n in editors:
|
||||
raw = editors[n].get_raw_data()
|
||||
else:
|
||||
@ -1301,9 +1306,12 @@ def run_search(
|
||||
|
||||
for p, repl in searches:
|
||||
repl_is_func = isinstance(repl, Function)
|
||||
file_iterator = lfiles
|
||||
if repl_is_func:
|
||||
repl.init_env()
|
||||
for n, syntax in lfiles.iteritems():
|
||||
if repl.file_order is not None and len(lfiles) > 1:
|
||||
file_iterator = reorder_files(file_iterator, repl.file_order)
|
||||
for n in file_iterator:
|
||||
raw = raw_data[n]
|
||||
if replace:
|
||||
if repl_is_func:
|
||||
|
Loading…
x
Reference in New Issue
Block a user