mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-12-10 07:05:09 -05:00
Add the function "f_string()", similar to python's 'f strings', to the template language.
This commit is contained in:
parent
6e20528004
commit
4887d6990b
@ -61,6 +61,7 @@ class Node:
|
||||
NODE_SWITCH_IF = 32
|
||||
NODE_LIST_COUNT_FIELD = 33
|
||||
NODE_WITH = 34
|
||||
NODE_FSTRING = 35
|
||||
|
||||
def __init__(self, line_number, name):
|
||||
self.my_line_number = line_number
|
||||
@ -77,7 +78,7 @@ class Node:
|
||||
|
||||
class WithNode(Node):
|
||||
def __init__(self, line_number, book_id, block):
|
||||
Node.__init__(self, line_number, 'if ...')
|
||||
Node.__init__(self, line_number, 'with ...')
|
||||
self.node_type = self.NODE_WITH
|
||||
self.book_id = book_id
|
||||
self.block = block
|
||||
@ -348,6 +349,13 @@ class ListCountFieldNode(Node):
|
||||
self.expression = expression
|
||||
|
||||
|
||||
class FStringNode(Node):
|
||||
def __init__(self, line_number, string):
|
||||
Node.__init__(self, line_number, 'f_string')
|
||||
self.node_type = self.NODE_FSTRING
|
||||
self.string = string
|
||||
|
||||
|
||||
class _Parser:
|
||||
LEX_OP = 1
|
||||
LEX_ID = 2
|
||||
@ -743,7 +751,9 @@ class _Parser:
|
||||
'strcat': (lambda _: True,
|
||||
lambda ln, args: StrcatNode(ln, args)),
|
||||
'list_count_field': (lambda args: len(args) == 1,
|
||||
lambda ln, args: ListCountFieldNode(ln, args[0]))
|
||||
lambda ln, args: ListCountFieldNode(ln, args[0])),
|
||||
'f_string': (lambda args: len(args) == 1,
|
||||
lambda ln, args: FStringNode(ln, args[0])),
|
||||
}
|
||||
|
||||
def expr(self):
|
||||
@ -1392,6 +1402,14 @@ class _Interpreter:
|
||||
self.break_reporter(prog.node_name, res, prog.line_number)
|
||||
return res
|
||||
|
||||
def do_node_f_string(self, prog):
|
||||
def repl(mo):
|
||||
print(mo.group()[1:-1])
|
||||
p = self.parent.gpm_parser.program(self.parent, self.funcs,
|
||||
self.parent.lex_scanner.scan(mo.group()[1:-1]))
|
||||
return self.expr(p)
|
||||
return str(re.sub(r'\{.*?\}', repl, self.expr(prog.string)))
|
||||
|
||||
def do_node_list_count_field(self, prog):
|
||||
name = field_metadata.search_term_to_field_key(self.expr(prog.expression))
|
||||
res = getattr(self.parent_book, name, None)
|
||||
@ -1654,6 +1672,7 @@ class _Interpreter:
|
||||
Node.NODE_LOCAL_FUNCTION_CALL: do_node_local_function_call,
|
||||
Node.NODE_LIST_COUNT_FIELD: do_node_list_count_field,
|
||||
Node.NODE_WITH: do_node_with,
|
||||
Node.NODE_FSTRING: do_node_f_string,
|
||||
}
|
||||
|
||||
def expr(self, prog):
|
||||
|
||||
@ -3761,6 +3761,49 @@ This function can be used only in the GUI.
|
||||
return '1' if d.exec() == QDialog.DialogCode.Accepted else ''
|
||||
|
||||
|
||||
class BuiltinFString(BuiltinFormatterFunction):
|
||||
name = 'f_string'
|
||||
arg_count = 1
|
||||
category = FORMATTING_VALUES
|
||||
def __doc__getter__(self): return translate_ffml(
|
||||
r'''
|
||||
``f_string(string)`` -- interpret ``string`` similar to how python interprets ``f`` strings.
|
||||
The indended use is to simplify long sequences of ``str & str`` or strcat(a,b,c) expressions.
|
||||
|
||||
Text between braces (``{`` and ``}``) must be General Program Mode template
|
||||
expressions. The expressions, which can be expression lists, are evaluated in
|
||||
the current context (current book and local variables). Text not between
|
||||
braces is passed through unchanged.
|
||||
|
||||
Examples:
|
||||
[LIST]
|
||||
[*]``f_string('Here is the title: {$title}')`` - returns the string with ``{$title}``
|
||||
replaced with the title of the current book. For example, if the book's title is
|
||||
`20,000 Leagues Under the Sea` then the ``f_string()`` returns
|
||||
`Here is the title: 20,000 Leagues Under the Sea`.
|
||||
[*]Assuming the current date is 18 Sept 2025, this ``f_string()``
|
||||
[CODE]
|
||||
f_string("Today's date: the {d = today(); format_date(d, 'd')} of {format_date(d, 'MMMM')}, {format_date(d, 'yyyy')}")
|
||||
[/CODE]
|
||||
returns the string `Today's date: the 18 of September, 2025`.
|
||||
Note the expression list (an assignment then an ``if`` statement) used in the first ``{ ... }`` group to assign today's date to a local variable.
|
||||
[*]If the book is book #3 in a series named `Foo` that has 5 books then this template
|
||||
[CODE]
|
||||
program:
|
||||
if $series then
|
||||
series_count = book_count('series:"""=' & $series & '"""', 0);
|
||||
return f_string("{$series}, book {$series_index} of {series_count}")
|
||||
fi;
|
||||
return 'This book is not in a series'
|
||||
[/CODE]
|
||||
returns `Foo, book 3 of 5`
|
||||
[/LIST]
|
||||
''')
|
||||
|
||||
def evaluate(self, formatter, kwargs, mi, locals, fstring):
|
||||
raise ValueError(_('This function cannot be called directly. It is built into the formatter'))
|
||||
|
||||
|
||||
_formatter_builtins = [
|
||||
BuiltinAdd(), BuiltinAnd(), BuiltinApproximateFormats(), BuiltinArguments(),
|
||||
BuiltinAssign(),
|
||||
@ -3776,7 +3819,7 @@ _formatter_builtins = [
|
||||
BuiltinFinishFormatting(), BuiltinFirstMatchingCmp(), BuiltinFloor(),
|
||||
BuiltinFormatDate(), BuiltinFormatDateField(), BuiltinFormatDuration(), BuiltinFormatNumber(),
|
||||
BuiltinFormatsModtimes(),BuiltinFormatsPaths(), BuiltinFormatsPathSegments(),
|
||||
BuiltinFormatsSizes(), BuiltinFractionalPart(),BuiltinGetLink(),
|
||||
BuiltinFormatsSizes(), BuiltinFractionalPart(),BuiltinFString(), BuiltinGetLink(),
|
||||
BuiltinGetNote(), BuiltinGlobals(), BuiltinHasCover(), BuiltinHasExtraFiles(),
|
||||
BuiltinHasNote(), BuiltinHumanReadable(), BuiltinIdentifierInList(),
|
||||
BuiltinIfempty(), BuiltinIsDarkMode(), BuiltinLanguageCodes(), BuiltinLanguageStrings(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user