Compare commits
9 Commits
6f5b110c9e
...
master
Author | SHA1 | Date | |
---|---|---|---|
5ed8f515a9 | |||
5c60b8f226 | |||
ea71cae43c | |||
17627834d6 | |||
ebf03f204f | |||
2506617bc1 | |||
1578254301 | |||
57b3a8e9bc | |||
9340a6012a |
@ -5,5 +5,7 @@ targeting academic wrinting in social sciences and the humanities.
|
||||
|
||||
All filters are under the MIT License, except those under `/misc`,
|
||||
which are trivial scripts in the public domain.
|
||||
|
||||
Issues and pull requests are wellcome: you can register via
|
||||
[OpenID](https://en.wikipedia.org/wiki/OpenID).
|
||||
[OpenID](https://en.wikipedia.org/wiki/OpenID)
|
||||
or email me at `bastien [dot] dumont [at] posteo [dot] net`.
|
||||
|
21
inner-parens-to-brackets/LICENSE.txt
Normal file
21
inner-parens-to-brackets/LICENSE.txt
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright © 2024 Bastien Dumont
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
4
inner-parens-to-brackets/README.md
Normal file
4
inner-parens-to-brackets/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
This filter converts nested parentheses to brackets (and conversely).
|
||||
|
||||
I use it mostly to post-process citeproc output.
|
||||
|
91
inner-parens-to-brackets/inner-parens-to-brackets.lua
Normal file
91
inner-parens-to-brackets/inner-parens-to-brackets.lua
Normal file
@ -0,0 +1,91 @@
|
||||
-- inner-parens-to-brackets.lua
|
||||
-- A Pandoc Lua filter that converts nested parentheses to brackets (and conversely).
|
||||
-- Copyright 2024 Bastien Dumont (bastien.dumont [at] posteo.net)
|
||||
-- This file is under the MIT License: see LICENSE for more details.
|
||||
|
||||
|
||||
local previously_signalled = {}
|
||||
|
||||
local function signal_if_new(id, msg)
|
||||
if not previously_signalled[id] then
|
||||
io.stdout:write('INFO: ' .. msg .. '\n')
|
||||
previously_signalled[id] = true
|
||||
end
|
||||
end
|
||||
|
||||
function Para(para)
|
||||
local whole_para = pandoc.utils.stringify(para)
|
||||
local parens_trace = {}
|
||||
local i_str = 0
|
||||
local isolate_parens = {
|
||||
-- In case there are several level of parentheses in the same Str object
|
||||
-- (could theoretically happen due to non-breaking spaces).
|
||||
-- Also simplifies check_and_replace() below.
|
||||
Str = function(str)
|
||||
local text = str.text
|
||||
if string.match(text, '[][()]') then
|
||||
local substrings = {}
|
||||
for a, b, c in string.gmatch(text, '([^][()]*)([][()]?)([^][()]*)') do
|
||||
for _, sub in ipairs({ a, b, c }) do
|
||||
if sub ~= '' then table.insert(substrings, pandoc.Str(sub)) end
|
||||
end
|
||||
end
|
||||
return substrings
|
||||
end
|
||||
end
|
||||
}
|
||||
local check_and_replace = {
|
||||
traverse = 'topdown',
|
||||
Str = function(str)
|
||||
i_str = i_str + 1
|
||||
local text = str.text
|
||||
if text == '(' then
|
||||
if #parens_trace > 0 then
|
||||
if parens_trace[#parens_trace] == '(' then
|
||||
str.text = '['
|
||||
signal_if_new(whole_para .. i_str,
|
||||
'Replacing a left parenthesis with a bracket ' ..
|
||||
'in the following item: ' .. whole_para)
|
||||
end
|
||||
end
|
||||
table.insert(parens_trace, str.text)
|
||||
elseif text == ')' then
|
||||
if #parens_trace > 0 then
|
||||
if parens_trace[#parens_trace] == '[' then
|
||||
str.text = ']'
|
||||
signal_if_new(whole_para .. i_str,
|
||||
'Replacing a right parenthesis with a bracket ' ..
|
||||
'in the following item: ' .. whole_para)
|
||||
end
|
||||
end
|
||||
table.remove(parens_trace)
|
||||
elseif text == '[' then
|
||||
if #parens_trace > 0 then
|
||||
if parens_trace[#parens_trace] == '[' then
|
||||
str.text = '('
|
||||
signal_if_new(whole_para .. i_str,
|
||||
'Replacing a left bracket with a parenthesis ' ..
|
||||
'in the following item: ' .. whole_para)
|
||||
end
|
||||
end
|
||||
table.insert(parens_trace, str.text)
|
||||
elseif text == ']' then
|
||||
if #parens_trace > 0 then
|
||||
if parens_trace[#parens_trace] == '(' then
|
||||
str.text = ')'
|
||||
signal_if_new(whole_para .. i_str,
|
||||
'Replacing a right bracket with a parenthesis ' ..
|
||||
'in the following item: ' .. whole_para)
|
||||
end
|
||||
end
|
||||
table.remove(parens_trace)
|
||||
end
|
||||
return str
|
||||
end
|
||||
}
|
||||
local processed_para = para:walk(isolate_parens):walk(check_and_replace)
|
||||
if #parens_trace > 0 then
|
||||
io.stdout:write('WARNING: Unbalanced parentheses or brackets found in ' .. whole_para .. '\n')
|
||||
end
|
||||
return processed_para
|
||||
end
|
21
text-crossrefs/LICENSE.txt
Normal file
21
text-crossrefs/LICENSE.txt
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright © 2024 Bastien Dumont
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -56,7 +56,7 @@ Here are some valid invocations:
|
||||
It is up to you to define `\crossrefenum` in your preamble.
|
||||
If your target format is LaTeX, it should be possible to define it as a wrapper
|
||||
for the `\zcref` macro provided by [the zref-clever package](https://ctan.org/pkg/zref-clever).
|
||||
Alternatively, you can use [my implementation](TODO),
|
||||
Alternatively, you can use [my implementation](https://ctan.org/pkg/crossrefenum),
|
||||
which currently supports ConTeXt and LaTeX.
|
||||
Here are some hints about the implementation of the `\crossrefenum` macro:
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
, Space
|
||||
, Str "published"
|
||||
, Space
|
||||
, RawInline (Format "latex") "\\label{publication}"
|
||||
, Str ""
|
||||
, Span
|
||||
( "publication" , [] , [] )
|
||||
[ Emph [ Str "L\8217Affaire" , Space , Str "Lerouge" ]
|
||||
@ -43,7 +43,7 @@
|
||||
, Space
|
||||
, Str "very"
|
||||
, Space
|
||||
, RawInline (Format "latex") "\\label{my-evaluation}"
|
||||
, Str ""
|
||||
, Span
|
||||
( "my-evaluation" , [] , [] )
|
||||
[ Str "fine"
|
||||
@ -60,7 +60,7 @@
|
||||
, RawInline (Format "latex") ""
|
||||
]
|
||||
, Para
|
||||
[ RawInline (Format "latex") "\\label{reception}"
|
||||
[ Str ""
|
||||
, Span
|
||||
( "reception" , [] , [] )
|
||||
[ Str "It"
|
||||
@ -148,7 +148,7 @@
|
||||
, RawInline (Format "latex") ""
|
||||
, Note
|
||||
[ Para
|
||||
[ RawInline (Format "latex") "\\label{format}"
|
||||
[ Str ""
|
||||
, Span
|
||||
( "format" , [] , [] )
|
||||
[ Str "Whatever" , Space , Str "format" ]
|
||||
@ -161,7 +161,7 @@
|
||||
, Space
|
||||
, Str "can"
|
||||
, Space
|
||||
, RawInline (Format "latex") "\\label{refer-to-note}"
|
||||
, Str ""
|
||||
, Span
|
||||
( "refer-to-note" , [] , [] )
|
||||
[ Str "refer"
|
||||
@ -181,7 +181,7 @@
|
||||
, Space
|
||||
, Str "of"
|
||||
, Space
|
||||
, RawInline (Format "latex") "\\label{which-identifier}"
|
||||
, Str ""
|
||||
, Span
|
||||
( "which-identifier" , [] , [] )
|
||||
[ Str "any"
|
||||
@ -198,7 +198,7 @@
|
||||
, Space
|
||||
, Str "even"
|
||||
, Space
|
||||
, RawInline (Format "latex") "\\label{nested-spans}"
|
||||
, Str ""
|
||||
, Span
|
||||
( "nested-spans" , [] , [] )
|
||||
[ Str "nest" , Space , Str "spans" ]
|
||||
@ -209,7 +209,7 @@
|
||||
, RawInline (Format "latex") ""
|
||||
]
|
||||
, Para
|
||||
[ RawInline (Format "latex") "\\label{toc-notes-begin}"
|
||||
[ Str ""
|
||||
, Span
|
||||
( "toc-notes-begin" , [] , [] )
|
||||
[ Str "I"
|
||||
@ -384,15 +384,12 @@
|
||||
]
|
||||
]
|
||||
]
|
||||
, Para [ Str "" , Span ( "toc-notes-end" , [] , [] ) [] ]
|
||||
, Para
|
||||
[ RawInline (Format "latex") "\\label{toc-notes-end}"
|
||||
, Span ( "toc-notes-end" , [] , [] ) []
|
||||
]
|
||||
, Para
|
||||
[ RawInline (Format "latex") "\\label{doubledlbl}"
|
||||
[ Str ""
|
||||
, Span
|
||||
( "doubledlbl" , [] , [ ( "refanchor" , "both" ) ] )
|
||||
[ RawInline (Format "latex") "\\label{doubledlbl-beg}"
|
||||
[ Str ""
|
||||
, Span ( "doubledlbl-beg" , [] , [] ) []
|
||||
, Str "A"
|
||||
, Space
|
||||
@ -413,12 +410,12 @@
|
||||
, Str "page"
|
||||
, Space
|
||||
, Str "break."
|
||||
, RawInline (Format "latex") "\\label{doubledlbl-end}"
|
||||
, Str ""
|
||||
, Span ( "doubledlbl-end" , [] , [] ) []
|
||||
]
|
||||
]
|
||||
, Para
|
||||
[ RawInline (Format "latex") "\\label{lblatend}"
|
||||
[ Str ""
|
||||
, Span
|
||||
( "lblatend" , [] , [ ( "refanchor" , "end" ) ] )
|
||||
[ Str "And"
|
||||
@ -436,7 +433,7 @@
|
||||
, Str "the"
|
||||
, Space
|
||||
, Str "end."
|
||||
, RawInline (Format "latex") "\\label{lblatend}"
|
||||
, Str ""
|
||||
, Span ( "lblatend" , [] , [] ) []
|
||||
]
|
||||
]
|
||||
|
@ -1,3 +1,11 @@
|
||||
-- text-crossrefs.lua
|
||||
-- A Pandoc Lua filter that extends Pandoc's cross-referencing abilities
|
||||
-- with references to any portion of text
|
||||
-- by its page number, its note number (when applicable)
|
||||
-- or an arbitrary reference type (with ConTeXt or LaTeX output).
|
||||
-- Copyright 2024 Bastien Dumont (bastien.dumont [at] posteo.net)
|
||||
-- This file is under the MIT License: see LICENSE for more details
|
||||
|
||||
local stringify = pandoc.utils.stringify
|
||||
|
||||
local TEXT_CROSSREF_CLASS = 'tcrf'
|
||||
@ -7,6 +15,10 @@ local PLACE_LABEL_ATTR = 'refanchor'
|
||||
local IS_CONFIG_ARRAY = { ['additional_types'] = true }
|
||||
local RAW_ATTRIBUTE
|
||||
|
||||
local function warning(message)
|
||||
io.stderr:write('WARNING [text-crossrefs]: ' .. message .. '\n')
|
||||
end
|
||||
|
||||
-- ConTeXt-specific tweak in order to add the label to the footnote
|
||||
--[[
|
||||
Placing the label in square brackets immediatly after \footnote
|
||||
@ -59,7 +71,11 @@ local function define_label_template()
|
||||
IS_LABEL_SET_BY_PANDOC = true
|
||||
end
|
||||
elseif RAW_ATTRIBUTE == 'latex' then
|
||||
LABEL_TEMPLATE = '\\label{{{label}}}'
|
||||
if PANDOC_VERSION < pandoc.types.Version('3.1.7') then
|
||||
LABEL_TEMPLATE = '\\label{{{label}}}'
|
||||
else
|
||||
IS_LABEL_SET_BY_PANDOC = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -200,8 +216,8 @@ local function control_label_placement(span)
|
||||
span.content:insert(pandoc.Span({}, { id = id .. '-end' }))
|
||||
span.identifier = nil
|
||||
elseif label_placement ~= 'beg' then
|
||||
error('Invalid value ' .. label_placement .. ' on attribute ' .. PLACE_LABEL_ATTR .. ': ' ..
|
||||
'shoud be “beg” (default), “end” of “both”.')
|
||||
warning('Invalid value ' .. label_placement .. ' on attribute ' .. PLACE_LABEL_ATTR .. ': ' ..
|
||||
'shoud be “beg” (default), “end” of “both”. Using the defaults.')
|
||||
end
|
||||
end
|
||||
return span
|
||||
@ -225,14 +241,14 @@ local function labelize_span(span)
|
||||
end
|
||||
end
|
||||
|
||||
local current_note_labels = {}
|
||||
local labels_in_current_note = {}
|
||||
|
||||
local collect_note_labels = {
|
||||
Span = function(span)
|
||||
if span.identifier ~= ''
|
||||
and (config.only_explicit_labels == 'false' or span.classes:includes('label'))
|
||||
then
|
||||
table.insert(current_note_labels, span.identifier)
|
||||
table.insert(labels_in_current_note, span.identifier)
|
||||
end
|
||||
end
|
||||
}
|
||||
@ -245,14 +261,14 @@ local function make_notelabel(pos)
|
||||
if RAW_ATTRIBUTE == 'openxml' then
|
||||
raw_code = string.gsub(
|
||||
'<w:bookmarkStart w:id="{{label}}_Note" w:name="{{label}}_Note"/>',
|
||||
'{{label}}', current_note_labels[1])
|
||||
'{{label}}', labels_in_current_note[1])
|
||||
elseif RAW_ATTRIBUTE == 'context' then
|
||||
raw_code = '\\withfirstopt[note:' .. current_note_labels[1] .. ']'
|
||||
raw_code = '\\withfirstopt[note:' .. labels_in_current_note[1] .. ']'
|
||||
end
|
||||
elseif pos == 'end' then
|
||||
if RAW_ATTRIBUTE == 'openxml' then
|
||||
raw_code = string.gsub('<w:bookmarkEnd w:id="{{label}}_Note"/>',
|
||||
'{{label}}', current_note_labels[1])
|
||||
'{{label}}', labels_in_current_note[1])
|
||||
end
|
||||
end
|
||||
return pandoc.RawInline(RAW_ATTRIBUTE, raw_code)
|
||||
@ -265,18 +281,18 @@ local function labelize_note(note)
|
||||
return { label_begin, note, label_end }
|
||||
end
|
||||
|
||||
local function map_text_to_note_labels(current_note_labels)
|
||||
local note_label = 'note:' .. current_note_labels[1]
|
||||
for _, text_label in ipairs(current_note_labels) do
|
||||
text_to_note_labels[text_label] = note_label
|
||||
local function map_text_to_note_labels(labels_in_current_note)
|
||||
local note_label = 'note:' .. labels_in_current_note[1]
|
||||
for _, label in ipairs(labels_in_current_note) do
|
||||
text_to_note_labels[label] = note_label
|
||||
end
|
||||
end
|
||||
|
||||
function set_notelabels(note)
|
||||
current_note_labels = {}
|
||||
labels_in_current_note = {}
|
||||
pandoc.walk_inline(note, collect_note_labels)
|
||||
if #current_note_labels > 0 then
|
||||
map_text_to_note_labels(current_note_labels)
|
||||
if #labels_in_current_note > 0 then
|
||||
map_text_to_note_labels(labels_in_current_note)
|
||||
return labelize_note(note)
|
||||
end
|
||||
end
|
||||
@ -326,8 +342,8 @@ local function parse_next_reference(raw_references, beg_of_search)
|
||||
if beg_of_search < #raw_references then
|
||||
-- The delimiter can be composed of more than one character.
|
||||
local delim_beg, delim_end = string.find(raw_references,
|
||||
config.references_enum_separator,
|
||||
beg_of_search, true)
|
||||
config.references_enum_separator,
|
||||
beg_of_search, true)
|
||||
if delim_beg then
|
||||
reference = string.sub(raw_references, beg_of_search, delim_beg - 1)
|
||||
next_ref_beg = delim_end + 1
|
||||
@ -354,15 +370,17 @@ local function parse_references_enum(raw_references)
|
||||
end
|
||||
|
||||
local function error_on_attr(attr_key, attr_value, span_content)
|
||||
error('Invalid value "' .. attr_value .. '" for attribute "' .. attr_key ..
|
||||
warning('Invalid value "' .. attr_value .. '" for attribute "' .. attr_key ..
|
||||
'" in the span with class "' .. TEXT_CROSSREF_CLASS ..
|
||||
'" whose content is "' .. stringify(span_content) .. '".')
|
||||
'" whose content is "' .. stringify(span_content) .. '". ' ..
|
||||
'Using the defaults.')
|
||||
end
|
||||
|
||||
local function get_ref_type(span)
|
||||
local ref_type = span.attributes[REF_TYPE_ATTR] or config.default_reftype
|
||||
if not accepted_types[ref_type] then
|
||||
error_on_attr(REF_TYPE_ATTR, ref_type, span.content)
|
||||
ref_type = config.default_reftype
|
||||
end
|
||||
return ref_type
|
||||
end
|
||||
@ -371,6 +389,7 @@ local function if_prefixed(span)
|
||||
local prefixed_attr_value = span.attributes[PREFIXED_ATTR] or config.default_prefixref
|
||||
if prefixed_attr_value ~= 'yes' and prefixed_attr_value ~= 'no' then
|
||||
error_on_attr(PREFIXED_ATTR, prefixed_attr_value, span.content)
|
||||
prefixed_attr_value = config.default_prefixref
|
||||
end
|
||||
local is_prefixed = true
|
||||
if prefixed_attr_value == 'no' then is_prefixed = false end
|
||||
@ -413,7 +432,12 @@ local function make_crossrefenum_references_list(refs, ref_type)
|
||||
if FORMAT == 'context'
|
||||
and (ref_type == 'note' or ref_type == 'pagenote')
|
||||
then
|
||||
anchor = text_to_note_labels[anchor]
|
||||
local note_label = text_to_note_labels[anchor]
|
||||
if note_label then
|
||||
anchor = note_label
|
||||
else
|
||||
warning('Wrong reference to non-existent label "' .. anchor .. '".')
|
||||
end
|
||||
end
|
||||
local texified_ref = '{' .. anchor
|
||||
if ref.end_of_range then
|
||||
|
Reference in New Issue
Block a user