├── .github └── workflows │ └── ci.yaml ├── LICENSE ├── Makefile ├── README.md ├── _extensions └── cito │ ├── _extension.yml │ └── cito.lua ├── cito.lua └── test ├── expected.md ├── input.md ├── perevirka.md ├── runtests.lua ├── sample.bib └── test.yaml /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | # Run on all pull requests that change code. 5 | pull_request: 6 | paths-ignore: 7 | - 'README.md' 8 | - LICENSE 9 | - .editorconfig 10 | # Run every time a code change is pushed. 11 | push: 12 | paths-ignore: 13 | - 'README.md' 14 | - LICENSE 15 | - .editorconfig 16 | # Test if things still work each Tuesday morning at 5:39 UTC. 17 | # This way we will catch incompatible pandoc changes in a timely 18 | # manner. 19 | schedule: 20 | # At 5:39am each Tuesday 21 | - cron: '39 5 * * 2' 22 | 23 | jobs: 24 | test: 25 | runs-on: ubuntu-latest 26 | strategy: 27 | fail-fast: true 28 | matrix: 29 | pandoc: 30 | - edge 31 | - latest 32 | # This should be the oldest version that's supported 33 | # - 2.19.2 34 | 35 | container: 36 | image: pandoc/core:${{ matrix.pandoc }} 37 | 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v4 41 | 42 | - name: Install dependencies 43 | run: apk add luarocks5.4 make 44 | 45 | - name: Install perevir 46 | run: luarocks-5.4 install perevir 47 | 48 | - name: Test 49 | run: | 50 | eval $(luarocks-5.4 path) 51 | make test 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2017–2022 Albert Krewinkel, Robert Winkler, 4 | © 2023-2024 Albert Krewinkel, Egon Willighagen 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Allow to use a different pandoc binary, e.g. when testing. 2 | PANDOC ?= pandoc 3 | 4 | # Test that running the filter on the sample input document yields 5 | # the expected output. 6 | # 7 | # The automatic variable `$<` refers to the first dependency 8 | # (i.e., the filter file). 9 | test: test/perevirka.md 10 | $(PANDOC) lua test/runtests.lua $< 11 | 12 | # Ensure that the `test` target is run each time it's called. 13 | .PHONY: test 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cito 2 | 3 | This filter extracts optional CiTO (Citation Typing Ontology) 4 | information from citations and stores the information in the 5 | document's metadata. The extracted info is intended to be used in 6 | combination with other filters, templates, or custom writers. It 7 | is mandatory to run citeproc *after* this filter if CiTO data is 8 | embedded in the document; otherwise citeproc will interpret CiTO 9 | properties as part of the citation ID. 10 | 11 | ## Using the Citation Typing Ontology 12 | 13 | The [citation typing ontology] (CiTO) allows authors to specify the 14 | reason a citation is given. This is helpful for the authors and 15 | their co-authors, and furthermore adds data that can be used by 16 | readers to search and navigate relevant publications. 17 | 18 | A CiTO annotation must come before the citation key and be 19 | followed by a colon. E.g., `@method_in:towbin_1979` signifies 20 | that the citation with ID *towbin_1979* is cited because the 21 | method described in that paper has been used in the paper at 22 | hand. 23 | 24 | [citation typing ontology]: http://purl.org/spar/cito 25 | 26 | ## Recognized CiTO properties 27 | 28 | Below is the list of CiTO properties recognized by the filter, 29 | together with the aliases that can be used as shorthands. 30 | 31 | - agrees_with 32 | - agree_with 33 | - citation 34 | - cites 35 | - cites_as_authority 36 | - as_authority 37 | - authority 38 | - cites_as_data_source 39 | - cites_as_evidence 40 | - as_evidence 41 | - evidence 42 | - cites_as_metadata_document 43 | - as_metadata_document 44 | - metadata_document 45 | - metadata 46 | - cites_as_recommended_reading 47 | - as_recommended_reading 48 | - recommended_reading 49 | - disagrees_with 50 | - disagree 51 | - disagrees 52 | - disputes 53 | - documents 54 | - extends 55 | - includes_excerpt_from 56 | - excerpt 57 | - excerpt_from 58 | - includes_quotation_from 59 | - quotation 60 | - quotation_from 61 | - obtains_background_from 62 | - background 63 | - background_from 64 | - refutes 65 | - replies_to 66 | - updates 67 | - uses_data_from 68 | - data 69 | - data_from 70 | - uses_method_in 71 | - method 72 | - method_in 73 | 74 | ## Filter interactions 75 | 76 | Note that the [pandoc-crossref] filter uses citations to link to 77 | other elements. The crossref extensions are not understood by 78 | cito.lua. pandoc-crossref, if used, should always be invoked 79 | first: `pandoc --filter=pandoc-crossref --lua-filter=cito.lua …` 80 | 81 | [pandoc-crossref]: https://lierdakil.github.io/pandoc-crossref/ 82 | 83 | ## References 84 | 85 | This approach was described in . 86 | -------------------------------------------------------------------------------- /_extensions/cito/_extension.yml: -------------------------------------------------------------------------------- 1 | title: cito 2 | author: Albert Krewinkel 3 | version: 0.0.1 4 | contributes: 5 | filters: 6 | - cito.lua 7 | -------------------------------------------------------------------------------- /_extensions/cito/cito.lua: -------------------------------------------------------------------------------- 1 | -- Copyright © 2017–2022 Albert Krewinkel, Robert Winkler, 2 | --+ © 2023-2024 Albert Krewinkel, Egon Willighagen 3 | -- 4 | -- This library is free software; you can redistribute it and/or modify it 5 | -- under the terms of the MIT license. See LICENSE for details. 6 | 7 | local _version = '1.1.0' 8 | local properties_and_aliases = { 9 | agreesWith = { 10 | 'agreeWith', 11 | 'agree_with', 12 | 'agrees_with', 13 | }, 14 | citation = { 15 | }, 16 | cites = { 17 | }, 18 | citesAsAuthority = { 19 | 'asAuthority', 20 | 'cites_as_authority', 21 | 'as_authority', 22 | 'authority' 23 | }, 24 | citesAsDataSource = { 25 | "asDataSource", 26 | "dataSource", 27 | 'cites_as_data_source', 28 | "as_data_source", 29 | "data_source" 30 | }, 31 | citesAsEvidence = { 32 | 'asEvidence', 33 | 'cites_as_evidence', 34 | 'as_evidence', 35 | 'evidence' 36 | }, 37 | citesAsMetadataDocument = { 38 | 'asMetadataDocument', 39 | 'metadataDocument', 40 | 'cites_as_metadata_document', 41 | 'as_metadata_document', 42 | 'metadata_document', 43 | 'metadata' 44 | }, 45 | citesAsPotentialSolution = { 46 | 'cites_as_potential_solution', 47 | 'potentialSolution', 48 | 'potential_solution', 49 | 'solution' 50 | }, 51 | citesAsRecommendedReading = { 52 | 'asRecommendedReading', 53 | 'recommendedReading', 54 | 'cites_as_recommended_reading', 55 | 'as_recommended_reading', 56 | 'recommended_reading' 57 | }, 58 | citesAsRelated = { 59 | 'cites_as_related', 60 | 'related', 61 | }, 62 | citesAsSourceDocument = { 63 | 'cites_as_source_document', 64 | 'sourceDocument', 65 | 'source_document' 66 | }, 67 | citesForInformation = { 68 | 'cites_for_information', 69 | 'information', 70 | }, 71 | compiles = { 72 | }, 73 | confirms = { 74 | }, 75 | containsAssertionFrom = { 76 | }, 77 | corrects = { 78 | }, 79 | credits = { 80 | }, 81 | critiques = { 82 | }, 83 | derides = { 84 | }, 85 | describes = { 86 | }, 87 | disagreesWith = { 88 | 'disagrees_with', 89 | 'disagree', 90 | 'disagrees' 91 | }, 92 | discusses = { 93 | }, 94 | disputes = { 95 | }, 96 | documents = { 97 | }, 98 | extends = { 99 | }, 100 | includesExcerptFrom = { 101 | 'excerptFrom', 102 | 'excerpt', 103 | 'excerpt_from', 104 | 'includes_excerpt_from', 105 | }, 106 | includesQuotationFrom = { 107 | 'quotationFrom', 108 | 'includes_quotation_from', 109 | 'quotation', 110 | 'quotation_from' 111 | }, 112 | linksTo = { 113 | 'links_to', 114 | 'link' 115 | }, 116 | obtainsBackgroundFrom = { 117 | 'backgroundFrom', 118 | 'obtains_background_from', 119 | 'background', 120 | 'background_from' 121 | }, 122 | providesDataFor = { 123 | }, 124 | obtainsSupportFrom = { 125 | }, 126 | qualifies = { 127 | }, 128 | parodies = { 129 | }, 130 | refutes = { 131 | }, 132 | repliesTo = { 133 | 'replies_to', 134 | }, 135 | retracts = { 136 | }, 137 | reviews = { 138 | }, 139 | ridicules = { 140 | }, 141 | speculatesOn = { 142 | }, 143 | supports = { 144 | }, 145 | updates = { 146 | }, 147 | usesConclusionsFrom = { 148 | 'uses_conclusions_from' 149 | }, 150 | usesDataFrom = { 151 | 'dataFrom', 152 | 'uses_data_from', 153 | 'data', 154 | 'data_from' 155 | }, 156 | usesMethodIn = { 157 | 'methodIn', 158 | 'uses_method_in', 159 | 'method', 160 | 'method_in' 161 | }, 162 | } 163 | 164 | local default_cito_property = 'citation' 165 | 166 | --- Map from cito aliases to the actual cito property. 167 | local properties_by_alias = {} 168 | for property, aliases in pairs(properties_and_aliases) do 169 | -- every property is an alias for itself 170 | properties_by_alias[property] = property 171 | for _, alias in pairs(aliases) do 172 | properties_by_alias[alias] = property 173 | end 174 | end 175 | 176 | --- Split citation ID into cito property and the actual citation ID. If 177 | --- the ID does not seem to contain a CiTO property, the 178 | --- `default_cito_property` will be returned, together with the 179 | --- unchanged input ID. 180 | local function split_cito_from_id (citation_id) 181 | local pattern = '^([^:]+):(.+)$' 182 | local prop_alias, split_citation_id = citation_id:match(pattern) 183 | 184 | if properties_by_alias[prop_alias] then 185 | return properties_by_alias[prop_alias], split_citation_id 186 | end 187 | 188 | return default_cito_property, citation_id 189 | end 190 | 191 | --- Citations by CiTO properties. 192 | local function store_cito (cito_cites, prop, cite_id) 193 | if not prop then 194 | return 195 | end 196 | if not cito_cites[prop] then 197 | cito_cites[prop] = {} 198 | end 199 | table.insert(cito_cites[prop], cite_id) 200 | end 201 | 202 | --- Returns a Cite filter function which extracts CiTO information and 203 | --- add it to the given collection table. 204 | local function extract_cito (cito_cites) 205 | return function (cite) 206 | local placeholder = cite.content 207 | for _, citation in pairs(cite.citations) do 208 | local cito_prop, cite_id = split_cito_from_id(citation.id) 209 | store_cito(cito_cites, cito_prop, cite_id) 210 | cite.content = cite.content:walk { 211 | -- replace in placeholder 212 | Str = function (str) 213 | return str.text:gsub(citation.id, cite_id) 214 | end 215 | } 216 | citation.id = cite_id 217 | end 218 | 219 | return cite 220 | end 221 | end 222 | 223 | return { 224 | { 225 | Pandoc = function (doc) 226 | --- Lists of citation IDs, indexed by CiTO properties. 227 | local citations_by_property = {} 228 | doc = doc:walk{Cite = extract_cito(citations_by_property)} 229 | doc.meta.cito_cites = citations_by_property 230 | return doc 231 | end 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /cito.lua: -------------------------------------------------------------------------------- 1 | _extensions/cito/cito.lua -------------------------------------------------------------------------------- /test/expected.md: -------------------------------------------------------------------------------- 1 | --- 2 | cito_cites: 3 | citesAsEvidence: 4 | - Li95 5 | citesAsRecommendedReading: 6 | - Upper_writers_1974 7 | --- 8 | 9 | # Abstract 10 | 11 | This is an example article. It was written under the influence of 12 | coffee, which acts to counter fatigue [@Li95]. 13 | 14 | # Further reading 15 | 16 | Authors struggling to fill their document with content are referred to 17 | @Upper_writers_1974. 18 | -------------------------------------------------------------------------------- /test/input.md: -------------------------------------------------------------------------------- 1 | # Abstract 2 | 3 | This is an example article. It was written under the influence of 4 | coffee, which acts to counter fatigue [@cites_as_evidence:Li95]. 5 | 6 | 7 | # Further reading 8 | 9 | Authors struggling to fill their document with content are referred to 10 | @recommended_reading:Upper_writers_1974. 11 | -------------------------------------------------------------------------------- /test/perevirka.md: -------------------------------------------------------------------------------- 1 | --- 2 | perevir: 3 | filters: 4 | - cito.lua 5 | metastrings-to-inlines: true 6 | --- 7 | 8 | ``` markdown {#input} 9 | # Abstract 10 | 11 | This is an example article. It was written under the influence of 12 | coffee, which acts to counter fatigue [@cites_as_evidence:Li95]. 13 | 14 | # Further reading 15 | 16 | Authors struggling to fill their document with content are referred to 17 | @recommended_reading:Upper_writers_1974. 18 | ``` 19 | 20 | ``` markdown {#expected} 21 | --- 22 | cito_cites: 23 | citesAsEvidence: 24 | - Li95 25 | citesAsRecommendedReading: 26 | - Upper_writers_1974 27 | --- 28 | 29 | # Abstract 30 | 31 | This is an example article. It was written under the influence of 32 | coffee, which acts to counter fatigue [@Li95]. 33 | 34 | # Further reading 35 | 36 | Authors struggling to fill their document with content are referred to 37 | @Upper_writers_1974. 38 | ``` 39 | -------------------------------------------------------------------------------- /test/runtests.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pandoc-lua 2 | --- Run tests with perevir. 3 | -- 4 | -- Copyright: © 2024 Albert Krewinkel 5 | -- License: MIT 6 | 7 | local perevir = require 'perevir' 8 | local opts = perevir.parse_args(arg) 9 | perevir.do_checks(opts) 10 | -------------------------------------------------------------------------------- /test/sample.bib: -------------------------------------------------------------------------------- 1 | @article{Li95, 2 | author = {L. Linde}, 3 | journal = {Ergonomics}, 4 | pages = {864--885}, 5 | title = {Mental effects of caffeine in fatigued and 6 | non-fatigued female and male subjects}, 7 | volume = {38}, 8 | year = {1995}, 9 | } 10 | 11 | @article{Upper_writers_1974, 12 | author = {Upper, Dennis}, 13 | journal = {Journal of Applied Behavior Analysis}, 14 | number = {3}, 15 | pages = {497--497}, 16 | publisher = {Blackwell Publishing Ltd}, 17 | title = {The unsuccessful self-treatment of a case of 18 | “writer's block”}, 19 | volume = {7}, 20 | year = {1974}, 21 | doi = {10.1901/jaba.1974.7-497a}, 22 | issn = {1938-3703}, 23 | url = {http://dx.doi.org/10.1901/jaba.1974.7-497a}, 24 | } 25 | -------------------------------------------------------------------------------- /test/test.yaml: -------------------------------------------------------------------------------- 1 | input-files: ["test/input.md"] 2 | to: markdown 3 | standalone: true 4 | filters: 5 | - {type: lua, path: cito.lua} 6 | --------------------------------------------------------------------------------