diff options
author | Norbert Preining <norbert@preining.info> | 2022-05-26 03:01:06 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2022-05-26 03:01:06 +0000 |
commit | 02d941fa9c9895bb08a84ac9afe3559abd1ba8ad (patch) | |
tree | 481d1d368c6d295a779b590d23bcd5397bcba628 /support/texlab/src/citation | |
parent | f01a37f8311f33e32441d25bdadcda9dcdbd165d (diff) |
CTAN sync 202205260301
Diffstat (limited to 'support/texlab/src/citation')
26 files changed, 1853 insertions, 2192 deletions
diff --git a/support/texlab/src/citation/apa.csl b/support/texlab/src/citation/apa.csl deleted file mode 100644 index 8454246a98..0000000000 --- a/support/texlab/src/citation/apa.csl +++ /dev/null @@ -1,1526 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0" demote-non-dropping-particle="never" page-range-format="expanded"> - <info> - <title>American Psychological Association 6th edition</title> - <title-short>APA</title-short> - <id>http://www.zotero.org/styles/apa</id> - <link href="http://www.zotero.org/styles/apa" rel="self"/> - <link href="http://owl.english.purdue.edu/owl/resource/560/01/" rel="documentation"/> - <author> - <name>Simon Kornblith</name> - <email>simon@simonster.com</email> - </author> - <author> - <name> Brenton M. Wiernik</name> - <email>zotero@wiernik.org</email> - </author> - <contributor> - <name>Bruce D'Arcus</name> - </contributor> - <contributor> - <name>Curtis M. Humphrey</name> - </contributor> - <contributor> - <name>Richard Karnesky</name> - <email>karnesky+zotero@gmail.com</email> - <uri>http://arc.nucapt.northwestern.edu/Richard_Karnesky</uri> - </contributor> - <contributor> - <name>Sebastian Karcher</name> - </contributor> - <category citation-format="author-date"/> - <category field="psychology"/> - <category field="generic-base"/> - <updated>2016-09-28T13:09:49+00:00</updated> - <rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights> - </info> - <locale xml:lang="en"> - <terms> - <term name="editortranslator" form="short"> - <single>ed. & trans.</single> - <multiple>eds. & trans.</multiple> - </term> - <term name="translator" form="short">trans.</term> - <term name="interviewer" form="short">interviewer</term> - <term name="circa" form="short">ca.</term> - <term name="collection-editor" form="short">series ed.</term> - </terms> - </locale> - <locale xml:lang="es"> - <terms> - <term name="from">de</term> - </terms> - </locale> - <locale xml:lang="de"> - <terms> - <term name="et-al">et al.</term> - </terms> - </locale> - <locale xml:lang="da"> - <terms> - <term name="et-al">et al.</term> - </terms> - </locale> - <locale xml:lang="nn"> - <terms> - <term name="et-al">et al.</term> - </terms> - </locale> - <locale xml:lang="nb"> - <terms> - <term name="et-al">et al.</term> - </terms> - </locale> - <macro name="container-contributors-booklike"> - <choose> - <if variable="container-title"> - <names variable="editor translator" delimiter=", & "> - <name and="symbol" initialize-with=". " delimiter=", "/> - <label form="short" prefix=" (" text-case="title" suffix=")"/> - <substitute> - <names variable="editorial-director"/> - <names variable="collection-editor"/> - <names variable="container-author"/> - </substitute> - </names> - </if> - </choose> - </macro> - <macro name="container-contributors"> - <choose> - <!-- book is here to catch software with container titles --> - <if type="book broadcast chapter entry entry-dictionary entry-encyclopedia graphic map personal_communication report speech" match="any"> - <text macro="container-contributors-booklike"/> - </if> - <else-if type="paper-conference"> - <choose> - <if variable="collection-editor container-author editor" match="any"> - <text macro="container-contributors-booklike"/> - </if> - </choose> - </else-if> - </choose> - </macro> - <macro name="secondary-contributors-booklike"> - <group delimiter="; "> - <choose> - <if variable="title"> - <names variable="interviewer"> - <name and="symbol" initialize-with=". " delimiter=", "/> - <label form="short" prefix=", " text-case="title"/> - </names> - </if> - </choose> - <choose> - <if variable="container-title" match="none"> - <group delimiter="; "> - <names variable="container-author"> - <label form="verb-short" suffix=" " text-case="title"/> - <name and="symbol" initialize-with=". " delimiter=", "/> - </names> - <names variable="editor translator" delimiter="; "> - <name and="symbol" initialize-with=". " delimiter=", "/> - <label form="short" prefix=", " text-case="title"/> - </names> - </group> - </if> - </choose> - </group> - </macro> - <macro name="secondary-contributors"> - <choose> - <!-- book is here to catch software with container titles --> - <if type="book broadcast chapter entry entry-dictionary entry-encyclopedia graphic map report" match="any"> - <text macro="secondary-contributors-booklike"/> - </if> - <else-if type="personal_communication"> - <group delimiter="; "> - <group delimiter=" "> - <choose> - <if variable="genre" match="any"> - <text variable="genre" text-case="capitalize-first"/> - </if> - <else> - <text term="letter" text-case="capitalize-first"/> - </else> - </choose> - <names variable="recipient" delimiter=", "> - <label form="verb" suffix=" "/> - <name and="symbol" delimiter=", "/> - </names> - </group> - <text variable="medium" text-case="capitalize-first"/> - <choose> - <if variable="container-title" match="none"> - <names variable="editor translator" delimiter="; "> - <name and="symbol" initialize-with=". " delimiter=", "/> - <label form="short" prefix=", " text-case="title"/> - </names> - </if> - </choose> - </group> - </else-if> - <else-if type="song"> - <choose> - <if variable="original-author composer" match="any"> - <group delimiter="; "> - <!-- Replace prefix with performer label as that becomes available --> - <names variable="author" prefix="Recorded by "> - <label form="verb" text-case="title"/> - <name and="symbol" initialize-with=". " delimiter=", "/> - </names> - <names variable="translator"> - <name and="symbol" initialize-with=". " delimiter=", "/> - <label form="short" prefix=", " text-case="title"/> - </names> - </group> - </if> - </choose> - </else-if> - <else-if type="article-journal article-magazine article-newspaper" match="any"> - <group delimiter="; "> - <choose> - <if variable="title"> - <names variable="interviewer" delimiter="; "> - <name and="symbol" initialize-with=". " delimiter=", "/> - <label form="short" prefix=", " text-case="title"/> - </names> - </if> - </choose> - <names variable="translator" delimiter="; "> - <name and="symbol" initialize-with=". " delimiter=", "/> - <label form="short" prefix=", " text-case="title"/> - </names> - </group> - </else-if> - <else-if type="paper-conference"> - <choose> - <if variable="collection-editor editor" match="any"> - <text macro="secondary-contributors-booklike"/> - </if> - <else> - <group delimiter="; "> - <choose> - <if variable="title"> - <names variable="interviewer" delimiter="; "> - <name and="symbol" initialize-with=". " delimiter=", "/> - <label form="short" prefix=", " text-case="title"/> - </names> - </if> - </choose> - <names variable="translator" delimiter="; "> - <name and="symbol" initialize-with=". " delimiter=", "/> - <label form="short" prefix=", " text-case="title"/> - </names> - </group> - </else> - </choose> - </else-if> - <else> - <group delimiter="; "> - <choose> - <if variable="title"> - <names variable="interviewer"> - <name and="symbol" initialize-with=". " delimiter="; "/> - <label form="short" prefix=", " text-case="title"/> - </names> - </if> - </choose> - <names variable="editor translator" delimiter="; "> - <name and="symbol" initialize-with=". " delimiter=", "/> - <label form="short" prefix=", " text-case="title"/> - </names> - </group> - </else> - </choose> - </macro> - <macro name="author"> - <choose> - <if type="song"> - <names variable="composer" delimiter=", "> - <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/> - <substitute> - <names variable="original-author"/> - <names variable="author"/> - <names variable="translator"> - <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/> - <label form="short" prefix=" (" suffix=")" text-case="title"/> - </names> - <group delimiter=" "> - <text macro="title"/> - <text macro="description"/> - <text macro="format"/> - </group> - </substitute> - </names> - </if> - <else-if type="treaty"/> - <else> - <names variable="author" delimiter=", "> - <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/> - <substitute> - <names variable="illustrator"/> - <names variable="composer"/> - <names variable="director"> - <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/> - <label form="long" prefix=" (" suffix=")" text-case="title"/> - </names> - <choose> - <if variable="container-title"> - <choose> - <if type="book entry entry-dictionary entry-encyclopedia"> - <text macro="title"/> - </if> - <else> - <names variable="translator"/> - </else> - </choose> - <names variable="translator"> - <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/> - <label form="short" prefix=" (" suffix=")" text-case="title"/> - </names> - </if> - </choose> - <names variable="editor translator" delimiter=", "> - <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/> - <label form="short" prefix=" (" suffix=")" text-case="title"/> - </names> - <names variable="editorial-director"> - <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/> - <label form="short" prefix=" (" suffix=")" text-case="title"/> - </names> - <names variable="collection-editor"> - <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/> - <label form="short" prefix=" (" suffix=")" text-case="title"/> - </names> - <choose> - <if type="report"> - <text variable="publisher"/> - </if> - </choose> - <group delimiter=" "> - <text macro="title"/> - <text macro="description"/> - <text macro="format"/> - </group> - </substitute> - </names> - </else> - </choose> - </macro> - <macro name="author-short"> - <choose> - <if type="patent" variable="number" match="all"> - <text macro="patent-number"/> - </if> - <else-if type="treaty"> - <text variable="title" form="short"/> - </else-if> - <else-if type="personal_communication"> - <choose> - <if variable="archive DOI publisher URL" match="none"> - <group delimiter=", "> - <names variable="author"> - <name and="symbol" delimiter=", " initialize-with=". "/> - <substitute> - <text variable="title" form="short" quotes="true"/> - </substitute> - </names> - <!-- This should be localized --> - <text value="personal communication"/> - </group> - </if> - <else> - <names variable="author" delimiter=", "> - <name form="short" and="symbol" delimiter=", " initialize-with=". "/> - <substitute> - <names variable="editor"/> - <names variable="translator"/> - <choose> - <if variable="container-title"> - <text variable="title" form="short" quotes="true"/> - </if> - <else> - <text variable="title" form="short" font-style="italic"/> - </else> - </choose> - <text macro="format-short" prefix="[" suffix="]"/> - </substitute> - </names> - </else> - </choose> - </else-if> - <else-if type="song"> - <names variable="composer" delimiter=", "> - <name form="short" and="symbol" delimiter=", " initialize-with=". "/> - <substitute> - <names variable="original-author"/> - <names variable="author"/> - <names variable="translator"/> - <choose> - <if variable="container-title"> - <text variable="title" form="short" quotes="true"/> - </if> - <else> - <text variable="title" form="short" font-style="italic"/> - </else> - </choose> - <text macro="format-short" prefix="[" suffix="]"/> - </substitute> - </names> - </else-if> - <else> - <names variable="author" delimiter=", "> - <name form="short" and="symbol" delimiter=", " initialize-with=". "/> - <substitute> - <names variable="illustrator"/> - <names variable="composer"/> - <names variable="director"/> - <choose> - <if variable="container-title"> - <choose> - <if type="book entry entry-dictionary entry-encyclopedia"> - <text variable="title" form="short" quotes="true"/> - </if> - <else> - <names variable="translator"/> - </else> - </choose> - </if> - </choose> - <names variable="editor"/> - <names variable="editorial-director"/> - <names variable="translator"/> - <choose> - <if type="report" variable="publisher" match="all"> - <text variable="publisher"/> - </if> - <else-if type="legal_case"> - <text variable="title" font-style="italic"/> - </else-if> - <else-if type="bill legislation" match="any"> - <text variable="title" form="short"/> - </else-if> - <else-if variable="reviewed-author" type="review review-book" match="any"> - <text macro="format-short" prefix="[" suffix="]"/> - </else-if> - <else-if type="post post-weblog webpage" variable="container-title" match="any"> - <text variable="title" form="short" quotes="true"/> - </else-if> - <else> - <text variable="title" form="short" font-style="italic"/> - </else> - </choose> - <text macro="format-short" prefix="[" suffix="]"/> - </substitute> - </names> - </else> - </choose> - </macro> - <macro name="patent-number"> - <!-- authority: U.S. ; genre: patent ; number: 123,445 --> - <group delimiter=" "> - <text variable="authority"/> - <choose> - <if variable="genre"> - <text variable="genre" text-case="capitalize-first"/> - </if> - <else> - <!-- This should be localized --> - <text value="patent" text-case="capitalize-first"/> - </else> - </choose> - <group delimiter=" "> - <text term="issue" form="short" text-case="capitalize-first"/> - <text variable="number"/> - </group> - </group> - </macro> - <macro name="access"> - <choose> - <if type="bill legal_case legislation" match="any"/> - <else-if variable="DOI" match="any"> - <text variable="DOI" prefix="https://doi.org/"/> - </else-if> - <else-if variable="URL"> - <group delimiter=" "> - <text term="retrieved" text-case="capitalize-first"/> - <choose> - <if type="post post-weblog webpage" match="any"> - <date variable="accessed" form="text" suffix=","/> - </if> - </choose> - <text term="from"/> - <choose> - <if type="report"> - <choose> - <if variable="author editor translator" match="any"> - <!-- This should be localized --> - <text variable="publisher" suffix=" website:"/> - </if> - </choose> - </if> - <else-if type="post post-weblog webpage" match="any"> - <!-- This should be localized --> - <text variable="container-title" suffix=" website:"/> - </else-if> - </choose> - <text variable="URL"/> - </group> - </else-if> - <else-if variable="archive"> - <choose> - <if type="article article-journal article-magazine article-newspaper dataset paper-conference report speech thesis" match="any"> - <!-- This section is for electronic database locations. Physical archives for these and other item types are called in 'publisher' macro --> - <choose> - <if variable="archive-place" match="none"> - <group delimiter=" "> - <text term="retrieved" text-case="capitalize-first"/> - <text term="from"/> - <text variable="archive" suffix="."/> - <text variable="archive_location" prefix="(" suffix=")"/> - </group> - </if> - <else> - <text macro="publisher" suffix="."/> - </else> - </choose> - </if> - <else> - <text macro="publisher" suffix="."/> - </else> - </choose> - </else-if> - <else> - <text macro="publisher" suffix="."/> - </else> - </choose> - </macro> - <macro name="title"> - <choose> - <if type="treaty"> - <group delimiter=", "> - <text variable="title" text-case="title"/> - <names variable="author"> - <name initialize-with="." form="short" delimiter="-"/> - </names> - </group> - </if> - <else-if type="patent" variable="number" match="all"> - <text macro="patent-number" font-style="italic"/> - </else-if> - <else-if variable="title"> - <choose> - <if variable="version" type="book" match="all"> - <!---This is a hack until we have a software type --> - <text variable="title"/> - </if> - <else-if variable="reviewed-author reviewed-title" type="review review-book" match="any"> - <choose> - <if variable="reviewed-title"> - <choose> - <if type="post post-weblog webpage" variable="container-title" match="any"> - <text variable="title"/> - </if> - <else> - <text variable="title" font-style="italic"/> - </else> - </choose> - </if> - </choose> - </else-if> - <else-if type="post post-weblog webpage" variable="container-title" match="any"> - <text variable="title"/> - </else-if> - <else> - <text variable="title" font-style="italic"/> - </else> - </choose> - </else-if> - <else-if variable="interviewer" type="interview" match="any"> - <names variable="interviewer"> - <label form="verb-short" suffix=" " text-case="capitalize-first"/> - <name and="symbol" initialize-with=". " delimiter=", "/> - </names> - </else-if> - </choose> - </macro> - <!-- APA has four descriptive sections following the title: --> - <!-- (description), [format], container, event --> - <macro name="description"> - <group prefix="(" suffix=")"> - <choose> - <!-- book is here to catch software with container titles --> - <if type="book report" match="any"> - <choose> - <if variable="container-title"> - <text macro="secondary-contributors"/> - </if> - <else> - <group delimiter="; "> - <text macro="description-report"/> - <text macro="secondary-contributors"/> - </group> - </else> - </choose> - </if> - <else-if type="thesis"> - <group delimiter="; "> - <group delimiter=", "> - <text variable="genre" text-case="capitalize-first"/> - <choose> - <!-- In APA journals, the university of a thesis is always cited, even if another locator is given --> - <if variable="DOI URL archive" match="any"> - <text variable="publisher"/> - </if> - </choose> - </group> - <text macro="locators"/> - <text macro="secondary-contributors"/> - </group> - </else-if> - <else-if type="book interview manuscript motion_picture musical_score pamphlet post-weblog speech webpage" match="any"> - <group delimiter="; "> - <text macro="locators"/> - <text macro="secondary-contributors"/> - </group> - </else-if> - <else-if type="song"> - <choose> - <if variable="container-title" match="none"> - <text macro="locators"/> - </if> - </choose> - </else-if> - <else-if type="article dataset figure" match="any"> - <choose> - <if variable="container-title"> - <text macro="secondary-contributors"/> - </if> - <else> - <group delimiter="; "> - <text macro="locators"/> - <text macro="secondary-contributors"/> - </group> - </else> - </choose> - </else-if> - <else-if type="bill legislation legal_case patent treaty personal_communication" match="none"> - <text macro="secondary-contributors"/> - </else-if> - </choose> - </group> - </macro> - <macro name="format"> - <group prefix="[" suffix="]"> - <choose> - <if variable="reviewed-author reviewed-title" type="review review-book" match="any"> - <group delimiter=", "> - <choose> - <if variable="genre"> - <!-- Delimiting by , rather than "of" to avoid incorrect grammar --> - <group delimiter=", "> - <text variable="genre" text-case="capitalize-first"/> - <choose> - <if variable="reviewed-title"> - <text variable="reviewed-title" font-style="italic"/> - </if> - <else> - <!-- Assume `title` is title of reviewed work --> - <text variable="title" font-style="italic"/> - </else> - </choose> - </group> - </if> - <else> - <!-- This should be localized --> - <group delimiter=" "> - <text value="Review of"/> - <choose> - <if variable="reviewed-title"> - <text variable="reviewed-title" font-style="italic"/> - </if> - <else> - <!-- Assume `title` is title of reviewed work --> - <text variable="title" font-style="italic"/> - </else> - </choose> - </group> - </else> - </choose> - <names variable="reviewed-author"> - <label form="verb-short" suffix=" "/> - <name and="symbol" initialize-with=". " delimiter=", "/> - </names> - </group> - </if> - <else> - <text macro="format-short"/> - </else> - </choose> - </group> - </macro> - <macro name="format-short"> - <choose> - <if variable="reviewed-author reviewed-title" type="review review-book" match="any"> - <choose> - <if variable="reviewed-title" match="none"> - <choose> - <if variable="genre"> - <!-- Delimiting by , rather than "of" to avoid incorrect grammar --> - <group delimiter=", "> - <text variable="genre" text-case="capitalize-first"/> - <text variable="title" form="short" font-style="italic"/> - </group> - </if> - <else> - <!-- This should be localized --> - <group delimiter=" "> - <text value="Review of"/> - <text variable="title" form="short" font-style="italic"/> - </group> - </else> - </choose> - </if> - <else> - <text variable="title" form="short" quotes="true"/> - </else> - </choose> - </if> - <else-if type="speech thesis" match="any"> - <text variable="medium" text-case="capitalize-first"/> - </else-if> - <!-- book is here to catch software with container titles --> - <else-if type="book report" match="any"> - <choose> - <if variable="container-title" match="none"> - <text macro="format-report"/> - </if> - </choose> - </else-if> - <else-if type="manuscript pamphlet" match="any"> - <text variable="medium" text-case="capitalize-first"/> - </else-if> - <else-if type="personal_communication"> - <text macro="secondary-contributors"/> - </else-if> - <else-if type="song"> - <group delimiter="; "> - <text macro="secondary-contributors"/> - <choose> - <if variable="container-title" match="none"> - <group delimiter=", "> - <text variable="genre" text-case="capitalize-first"/> - <text variable="medium" text-case="capitalize-first"/> - </group> - </if> - </choose> - </group> - </else-if> - <else-if type="paper-conference"> - <group delimiter=", "> - <choose> - <if variable="collection-editor editor issue page volume" match="any"> - <text variable="genre" text-case="capitalize-first"/> - </if> - </choose> - <text variable="medium" text-case="capitalize-first"/> - </group> - </else-if> - <else-if type="bill legislation legal_case patent treaty" match="none"> - <choose> - <if variable="genre medium" match="any"> - <group delimiter=", "> - <text variable="genre" text-case="capitalize-first"/> - <text variable="medium" text-case="capitalize-first"/> - </group> - </if> - <else-if type="dataset"> - <!-- This should be localized --> - <text value="Data set"/> - </else-if> - </choose> - </else-if> - </choose> - </macro> - <macro name="description-report"> - <choose> - <if variable="number"> - <group delimiter="; "> - <group delimiter=" "> - <text variable="genre" text-case="title"/> - <!-- Replace with term="number" if that becomes available --> - <text term="issue" form="short" text-case="capitalize-first"/> - <text variable="number"/> - </group> - <text macro="locators"/> - </group> - </if> - <else> - <text macro="locators"/> - </else> - </choose> - </macro> - <macro name="format-report"> - <choose> - <if variable="number"> - <text variable="medium" text-case="capitalize-first"/> - </if> - <else> - <group delimiter=", "> - <text variable="genre" text-case="capitalize-first"/> - <text variable="medium" text-case="capitalize-first"/> - </group> - </else> - </choose> - </macro> - <macro name="archive"> - <group delimiter=". "> - <group delimiter=", "> - <choose> - <if type="manuscript"> - <text variable="genre"/> - </if> - </choose> - <group delimiter=" "> - <!-- Replace "archive" with "archive_collection" as that becomes available --> - <text variable="archive"/> - <text variable="archive_location" prefix="(" suffix=")"/> - </group> - </group> - <group delimiter=", "> - <!-- Move "archive" here when "archive_collection" becomes available --> - <text variable="archive-place"/> - </group> - </group> - </macro> - <macro name="publisher"> - <choose> - <if type="manuscript pamphlet" match="any"> - <choose> - <if variable="archive archive_location archive-place" match="any"> - <group delimiter=". "> - <group delimiter=": "> - <text variable="publisher-place"/> - <text variable="publisher"/> - </group> - <text macro="archive"/> - </group> - </if> - <else> - <group delimiter=", "> - <text variable="genre"/> - <text variable="publisher"/> - <text variable="publisher-place"/> - </group> - </else> - </choose> - </if> - <else-if type="thesis" match="any"> - <group delimiter=". "> - <group delimiter=", "> - <text variable="publisher"/> - <text variable="publisher-place"/> - </group> - <text macro="archive"/> - </group> - </else-if> - <else-if type="patent"> - <group delimiter=". "> - <group delimiter=": "> - <text variable="publisher-place"/> - <text variable="publisher"/> - </group> - <text macro="archive"/> - </group> - </else-if> - <else-if type="article-journal article-magazine article-newspaper" match="any"> - <text macro="archive"/> - </else-if> - <else-if type="post post-weblog webpage" match="none"> - <group delimiter=". "> - <choose> - <if variable="event"> - <choose> - <!-- Only print publisher info if published in a proceedings --> - <if variable="collection-editor editor issue page volume" match="any"> - <group delimiter=": "> - <text variable="publisher-place"/> - <text variable="publisher"/> - </group> - </if> - </choose> - </if> - <else> - <group delimiter=": "> - <text variable="publisher-place"/> - <text variable="publisher"/> - </group> - </else> - </choose> - <text macro="archive"/> - </group> - </else-if> - </choose> - </macro> - <macro name="event"> - <choose> - <if variable="event" type="speech paper-conference" match="any"> - <choose> - <!-- Don't print event info if published in a proceedings --> - <if variable="collection-editor editor issue page volume" match="none"> - <group delimiter=" "> - <text variable="genre" text-case="capitalize-first"/> - <group delimiter=" "> - <choose> - <if variable="genre"> - <text term="presented at"/> - </if> - <else> - <text term="presented at" text-case="capitalize-first"/> - </else> - </choose> - <group delimiter=", "> - <text variable="event"/> - <text variable="event-place"/> - </group> - </group> - </group> - </if> - </choose> - </if> - </choose> - </macro> - <macro name="issued"> - <choose> - <if type="bill legal_case legislation" match="any"/> - <else-if variable="issued"> - <group> - <date variable="issued"> - <date-part name="year"/> - </date> - <text variable="year-suffix"/> - <choose> - <if type="speech"> - <date variable="issued" delimiter=" "> - <date-part prefix=", " name="month"/> - </date> - </if> - <else-if type="article article-magazine article-newspaper broadcast interview pamphlet personal_communication post post-weblog treaty webpage" match="any"> - <date variable="issued"> - <date-part prefix=", " name="month"/> - <date-part prefix=" " name="day"/> - </date> - </else-if> - <else-if type="paper-conference"> - <choose> - <if variable="container-title" match="none"> - <date variable="issued"> - <date-part prefix=", " name="month"/> - <date-part prefix=" " name="day"/> - </date> - </if> - </choose> - </else-if> - <!-- Only year: article-journal chapter entry entry-dictionary entry-encyclopedia dataset figure graphic motion_picture manuscript map musical_score paper-conference [published] patent report review review-book song thesis --> - </choose> - </group> - </else-if> - <else-if variable="status"> - <group> - <text variable="status" text-case="lowercase"/> - <text variable="year-suffix" prefix="-"/> - </group> - </else-if> - <else> - <group> - <text term="no date" form="short"/> - <text variable="year-suffix" prefix="-"/> - </group> - </else> - </choose> - </macro> - <macro name="issued-sort"> - <choose> - <if type="article article-magazine article-newspaper broadcast interview pamphlet personal_communication post post-weblog speech treaty webpage" match="any"> - <date variable="issued"> - <date-part name="year"/> - <date-part name="month"/> - <date-part name="day"/> - </date> - </if> - <else> - <date variable="issued"> - <date-part name="year"/> - </date> - </else> - </choose> - </macro> - <macro name="issued-year"> - <group> - <choose> - <if type="personal_communication"> - <choose> - <if variable="archive DOI publisher URL" match="none"> - <!-- These variables indicate that the letter is retrievable by the reader. If not, then use the APA in-text-only personal communication format --> - <date variable="issued" form="text"/> - </if> - <else> - <date variable="issued"> - <date-part name="year"/> - </date> - </else> - </choose> - </if> - <else> - <date variable="issued"> - <date-part name="year"/> - </date> - </else> - </choose> - <text variable="year-suffix"/> - </group> - </macro> - <macro name="issued-citation"> - <choose> - <if variable="issued"> - <group delimiter="/"> - <choose> - <if is-uncertain-date="original-date"> - <group prefix="[" suffix="]" delimiter=" "> - <text term="circa" form="short"/> - <date variable="original-date"> - <date-part name="year"/> - </date> - </group> - </if> - <else> - <date variable="original-date"> - <date-part name="year"/> - </date> - </else> - </choose> - <choose> - <if is-uncertain-date="issued"> - <group prefix="[" suffix="]" delimiter=" "> - <text term="circa" form="short"/> - <text macro="issued-year"/> - </group> - </if> - <else> - <text macro="issued-year"/> - </else> - </choose> - </group> - </if> - <else-if variable="status"> - <text variable="status" text-case="lowercase"/> - <text variable="year-suffix" prefix="-"/> - </else-if> - <else> - <text term="no date" form="short"/> - <text variable="year-suffix" prefix="-"/> - </else> - </choose> - </macro> - <macro name="original-date"> - <choose> - <if type="bill legal_case legislation" match="any"/> - <else-if type="speech"> - <date variable="original-date" delimiter=" "> - <date-part name="month"/> - <date-part name="year"/> - </date> - </else-if> - <else-if type="article article-magazine article-newspaper broadcast interview pamphlet personal_communication post post-weblog treaty webpage" match="any"> - <date variable="original-date" form="text"/> - </else-if> - <else> - <date variable="original-date"> - <date-part name="year"/> - </date> - </else> - </choose> - </macro> - <macro name="original-published"> - <!--This should be localized --> - <choose> - <if type="bill legal_case legislation" match="any"/> - <else-if type="interview motion_picture song" match="any"> - <text value="Original work recorded"/> - </else-if> - <else-if type="broadcast"> - <text value="Original work broadcast"/> - </else-if> - <else> - <text value="Original work published"/> - </else> - </choose> - </macro> - <macro name="edition"> - <choose> - <if is-numeric="edition"> - <group delimiter=" "> - <number variable="edition" form="ordinal"/> - <text term="edition" form="short"/> - </group> - </if> - <else> - <text variable="edition"/> - </else> - </choose> - </macro> - <macro name="locators"> - <choose> - <if type="article-journal article-magazine figure review review-book" match="any"> - <group delimiter=", "> - <group> - <text variable="volume" font-style="italic"/> - <text variable="issue" prefix="(" suffix=")"/> - </group> - <text variable="page"/> - </group> - </if> - <else-if type="article-newspaper"> - <group delimiter=" "> - <label variable="page" form="short"/> - <text variable="page"/> - </group> - </else-if> - <else-if type="paper-conference"> - <choose> - <if variable="collection-editor editor" match="any"> - <text macro="locators-booklike"/> - </if> - <else> - <group delimiter=", "> - <group> - <text variable="volume" font-style="italic"/> - <text variable="issue" prefix="(" suffix=")"/> - </group> - <text variable="page"/> - </group> - </else> - </choose> - </else-if> - <else-if type="bill broadcast interview legal_case legislation patent post post-weblog speech treaty webpage" match="none"> - <text macro="locators-booklike"/> - </else-if> - </choose> - </macro> - <macro name="locators-booklike"> - <group delimiter=", "> - <text macro="edition"/> - <group delimiter=" "> - <text term="version" text-case="capitalize-first"/> - <text variable="version"/> - </group> - <choose> - <if variable="volume" match="any"> - <choose> - <if is-numeric="volume" match="none"/> - <else-if variable="collection-title"> - <choose> - <if variable="editor translator" match="none"> - <choose> - <if variable="collection-number"> - <group> - <text term="volume" form="short" text-case="capitalize-first" suffix=" "/> - <number variable="volume" form="numeric"/> - </group> - </if> - </choose> - </if> - </choose> - </else-if> - <else> - <group> - <text term="volume" form="short" text-case="capitalize-first" suffix=" "/> - <number variable="volume" form="numeric"/> - </group> - </else> - </choose> - </if> - <else> - <group> - <text term="volume" form="short" plural="true" text-case="capitalize-first" suffix=" "/> - <number variable="number-of-volumes" form="numeric" prefix="1–"/> - </group> - </else> - </choose> - <group> - <label variable="page" form="short" suffix=" "/> - <text variable="page"/> - </group> - </group> - </macro> - <macro name="citation-locator"> - <group> - <choose> - <if locator="chapter"> - <label variable="locator" text-case="capitalize-first"/> - </if> - <else> - <label variable="locator" form="short"/> - </else> - </choose> - <text variable="locator" prefix=" "/> - </group> - </macro> - <macro name="container"> - <choose> - <if type="article article-journal article-magazine article-newspaper review review-book" match="any"> - <group delimiter=", "> - <text macro="container-title"/> - <text macro="locators"/> - </group> - <choose> - <!--for advance online publication--> - <if variable="issued"> - <choose> - <if variable="page issue" match="none"> - <text variable="status" text-case="capitalize-first" prefix=". "/> - </if> - </choose> - </if> - </choose> - </if> - <else-if type="article dataset figure" match="any"> - <choose> - <if variable="container-title"> - <group delimiter=", "> - <text macro="container-title"/> - <text macro="locators"/> - </group> - <choose> - <!--for advance online publication--> - <if variable="issued"> - <choose> - <if variable="page issue" match="none"> - <text variable="status" text-case="capitalize-first" prefix=". "/> - </if> - </choose> - </if> - </choose> - </if> - </choose> - </else-if> - <!-- book is here to catch software with container titles --> - <else-if type="book" variable="container-title" match="all"> - <group delimiter=" "> - <text term="in" text-case="capitalize-first" suffix=" "/> - <group delimiter=", "> - <text macro="container-contributors"/> - <group delimiter=" "> - <text macro="container-title"/> - <text macro="description-report" prefix="(" suffix=")"/> - <text macro="format-report" prefix="[" suffix="]"/> - </group> - </group> - </group> - </else-if> - <else-if type="report" variable="container-title" match="all"> - <group delimiter=" "> - <text term="in" text-case="capitalize-first" suffix=" "/> - <group delimiter=", "> - <text macro="container-contributors"/> - <group delimiter=" "> - <text macro="container-title"/> - <text macro="description-report" prefix="(" suffix=")"/> - <text macro="format-report" prefix="[" suffix="]"/> - </group> - </group> - </group> - </else-if> - <else-if type="song" variable="container-title" match="all"> - <group delimiter=" "> - <text term="in" text-case="capitalize-first" suffix=" "/> - <group delimiter=", "> - <text macro="container-contributors"/> - <group delimiter=" "> - <text macro="container-title"/> - <text macro="locators" prefix="(" suffix=")"/> - <group delimiter=", " prefix="[" suffix="]"> - <text variable="genre" text-case="capitalize-first"/> - <text variable="medium" text-case="capitalize-first"/> - </group> - </group> - </group> - </group> - </else-if> - <else-if type="paper-conference"> - <choose> - <if variable="editor collection-editor container-author" match="any"> - <text macro="container-booklike"/> - </if> - <else> - <group delimiter=", "> - <text macro="container-title"/> - <text macro="locators"/> - </group> - </else> - </choose> - </else-if> - <else-if type="book"> - <choose> - <!-- book and software should not cite collection-title, only container-title --> - <if variable="container-title"> - <text macro="container-booklike"/> - </if> - </choose> - </else-if> - <else-if type="broadcast chapter entry entry-dictionary entry-encyclopedia graphic map speech" match="any"> - <text macro="container-booklike"/> - </else-if> - <else-if type="bill legal_case legislation treaty" match="any"> - <text macro="legal-cites"/> - </else-if> - </choose> - </macro> - <macro name="container-booklike"> - <choose> - <if variable="container-title collection-title" match="any"> - <group delimiter=" "> - <text term="in" text-case="capitalize-first"/> - <group delimiter=", "> - <text macro="container-contributors"/> - <choose> - <if variable="container-author editor translator" match="none"> - <group delimiter=". "> - <group delimiter=": "> - <text variable="collection-title" font-style="italic" text-case="title"/> - <choose> - <if variable="collection-title"> - <group delimiter=" "> - <text term="volume" form="short" font-style="italic" text-case="capitalize-first"/> - <number variable="collection-number" font-style="italic" form="numeric"/> - <choose> - <if variable="collection-number" match="none"> - <number variable="volume" font-style="italic" form="numeric"/> - </if> - </choose> - </group> - </if> - </choose> - </group> - <!-- Replace with volume-title as that becomes available --> - <group delimiter=": "> - <text macro="container-title"/> - <choose> - <if variable="collection-title" is-numeric="volume" match="none"> - <group delimiter=" "> - <text term="volume" form="short" font-style="italic" text-case="capitalize-first"/> - <text variable="volume" font-style="italic"/> - </group> - </if> - </choose> - </group> - </group> - </if> - <else> - <!-- Replace with volume-title as that becomes available --> - <group delimiter=": "> - <text macro="container-title"/> - <choose> - <if is-numeric="volume" match="none"> - <group delimiter=" "> - <text term="volume" form="short" font-style="italic" text-case="capitalize-first"/> - <text variable="volume" font-style="italic"/> - </group> - </if> - </choose> - </group> - </else> - </choose> - </group> - <group delimiter="; " prefix="(" suffix=")"> - <text macro="locators"/> - <names variable="container-author"> - <label form="verb-short" suffix=" " text-case="title"/> - <name and="symbol" initialize-with=". " delimiter=", "/> - </names> - </group> - </group> - </if> - </choose> - </macro> - <macro name="container-title"> - <choose> - <if type="article article-journal article-magazine article-newspaper dataset" match="any"> - <text variable="container-title" font-style="italic" text-case="title"/> - </if> - <else-if type="paper-conference speech"> - <choose> - <if variable="collection-editor container-author editor" match="any"> - <text variable="container-title" font-style="italic"/> - </if> - <else> - <text variable="container-title" font-style="italic" text-case="title"/> - </else> - </choose> - </else-if> - <else-if type="bill legal_case legislation post-weblog webpage" match="none"> - <text variable="container-title" font-style="italic"/> - </else-if> - </choose> - </macro> - <macro name="legal-cites"> - <choose> - <if type="legal_case"> - <group prefix=", " delimiter=" "> - <group delimiter=" "> - <choose> - <if variable="container-title"> - <text variable="volume"/> - <text variable="container-title"/> - <group delimiter=" "> - <!--change to label variable="section" as that becomes available --> - <text term="section" form="symbol"/> - <text variable="section"/> - </group> - <text variable="page"/> - </if> - <else> - <group delimiter=" "> - <choose> - <if is-numeric="number"> - <!-- Replace with term="number" if that becomes available --> - <text term="issue" form="short" text-case="capitalize-first"/> - </if> - </choose> - <text variable="number"/> - </group> - </else> - </choose> - </group> - <group prefix="(" suffix=")" delimiter=" "> - <text variable="authority"/> - <choose> - <if variable="container-title" match="any"> - <!--Only print year for cases published in reporters--> - <date variable="issued" form="numeric" date-parts="year"/> - </if> - <else> - <date variable="issued" form="text"/> - </else> - </choose> - </group> - </group> - </if> - <else-if type="bill legislation" match="any"> - <group prefix=", " delimiter=" "> - <group delimiter=", "> - <choose> - <if variable="number"> - <!--There's a public law number--> - <text variable="number" prefix="Pub. L. No. "/> - <group delimiter=" "> - <!--change to label variable="section" as that becomes available --> - <text term="section" form="symbol"/> - <text variable="section"/> - </group> - <group delimiter=" "> - <text variable="volume"/> - <text variable="container-title"/> - <text variable="page-first"/> - </group> - </if> - <else> - <group delimiter=" "> - <text variable="volume"/> - <text variable="container-title"/> - <!--change to label variable="section" as that becomes available --> - <text term="section" form="symbol"/> - <text variable="section"/> - </group> - </else> - </choose> - </group> - <date variable="issued" prefix="(" suffix=")"> - <date-part name="year"/> - </date> - </group> - </else-if> - <else-if type="treaty"> - <group delimiter=" "> - <number variable="volume"/> - <text variable="container-title"/> - <text variable="page"/> - </group> - </else-if> - </choose> - </macro> - <citation et-al-min="6" et-al-use-first="1" et-al-subsequent-min="3" et-al-subsequent-use-first="1" disambiguate-add-year-suffix="true" disambiguate-add-names="true" disambiguate-add-givenname="true" collapse="year" givenname-disambiguation-rule="primary-name"> - <sort> - <key macro="author" names-min="8" names-use-first="6"/> - <key macro="issued-sort"/> - </sort> - <layout prefix="(" suffix=")" delimiter="; "> - <group delimiter=", "> - <text macro="author-short"/> - <text macro="issued-citation"/> - <text macro="citation-locator"/> - </group> - </layout> - </citation> - <bibliography hanging-indent="true" et-al-min="8" et-al-use-first="6" et-al-use-last="true" entry-spacing="0" line-spacing="2"> - <sort> - <key macro="author"/> - <key macro="issued-sort" sort="ascending"/> - <key macro="title"/> - </sort> - <layout> - <group suffix="."> - <group delimiter=". "> - <text macro="author"/> - <choose> - <if is-uncertain-date="issued"> - <group prefix=" [" suffix="]" delimiter=" "> - <text term="circa" form="short"/> - <text macro="issued"/> - </group> - </if> - <else> - <text macro="issued" prefix=" (" suffix=")"/> - </else> - </choose> - <group delimiter=" "> - <text macro="title"/> - <choose> - <if variable="title interviewer" type="interview" match="any"> - <group delimiter=" "> - <text macro="description"/> - <text macro="format"/> - </group> - </if> - <else> - <group delimiter=" "> - <text macro="format"/> - <text macro="description"/> - </group> - </else> - </choose> - </group> - <text macro="container"/> - </group> - <text macro="event" prefix=". "/> - </group> - <text macro="access" prefix=" "/> - <choose> - <if is-uncertain-date="original-date"> - <group prefix=" [" suffix="]" delimiter=" "> - <text macro="original-published"/> - <text term="circa" form="short"/> - <text macro="original-date"/> - </group> - </if> - <else-if variable="original-date"> - <group prefix=" (" suffix=")" delimiter=" "> - <text macro="original-published"/> - <text macro="original-date"/> - </group> - </else-if> - </choose> - </layout> - </bibliography> -</style>
\ No newline at end of file diff --git a/support/texlab/src/citation/bibutils.rs b/support/texlab/src/citation/bibutils.rs deleted file mode 100644 index 9931ba3f9f..0000000000 --- a/support/texlab/src/citation/bibutils.rs +++ /dev/null @@ -1,195 +0,0 @@ -use std::{ffi::CString, fs, mem::MaybeUninit, path::Path}; - -use bibutils_sys::{ - bibl, bibl_free, bibl_freeparams, bibl_init, bibl_initparams, bibl_read, bibl_write, fclose, - fopen, param, BIBL_ADSABSOUT, BIBL_BIBLATEXIN, BIBL_BIBTEXIN, BIBL_BIBTEXOUT, BIBL_COPACIN, - BIBL_EBIIN, BIBL_ENDNOTEIN, BIBL_ENDNOTEOUT, BIBL_ENDNOTEXMLIN, BIBL_ISIOUT, BIBL_MEDLINEIN, - BIBL_MODSIN, BIBL_MODSOUT, BIBL_NBIBIN, BIBL_NBIBOUT, BIBL_OK, BIBL_RISIN, BIBL_RISOUT, - BIBL_WORD2007OUT, BIBL_WORDIN, FILE, -}; -use tempfile::tempdir; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[allow(unused)] -pub enum InputFormat { - Bibtex, - Biblatex, - Copac, - Ebi, - Endnote, - EndnoteXml, - Medline, - Mods, - Nbib, - Ris, - Word, -} - -impl InputFormat { - fn read_mode(self) -> u32 { - match self { - Self::Bibtex => BIBL_BIBTEXIN, - Self::Biblatex => BIBL_BIBLATEXIN, - Self::Copac => BIBL_COPACIN, - Self::Ebi => BIBL_EBIIN, - Self::Endnote => BIBL_ENDNOTEIN, - Self::EndnoteXml => BIBL_ENDNOTEXMLIN, - Self::Medline => BIBL_MEDLINEIN, - Self::Mods => BIBL_MODSIN, - Self::Nbib => BIBL_NBIBIN, - Self::Ris => BIBL_RISIN, - Self::Word => BIBL_WORDIN, - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[allow(unused)] -pub enum OutputFormat { - Adsabs, - Bibtex, - Endnote, - Isi, - Mods, - Nbib, - Ris, - Word2007, -} - -impl OutputFormat { - fn write_mode(self) -> u32 { - match self { - Self::Adsabs => BIBL_ADSABSOUT, - Self::Bibtex => BIBL_BIBTEXOUT, - Self::Endnote => BIBL_ENDNOTEOUT, - Self::Isi => BIBL_ISIOUT, - Self::Mods => BIBL_MODSOUT, - Self::Nbib => BIBL_NBIBOUT, - Self::Ris => BIBL_RISOUT, - Self::Word2007 => BIBL_WORD2007OUT, - } - } -} - -struct Context { - inner: MaybeUninit<bibl>, -} - -impl Context { - fn new() -> Self { - let mut inner = MaybeUninit::zeroed(); - unsafe { - bibl_init(inner.as_mut_ptr()); - } - Self { inner } - } -} - -impl Drop for Context { - fn drop(&mut self) { - unsafe { - bibl_free(self.inner.as_mut_ptr()); - } - } -} - -unsafe impl Send for Context {} - -struct Params { - inner: MaybeUninit<param>, -} - -impl Params { - fn new(from: InputFormat, to: OutputFormat) -> Self { - let program = CString::new("texlab").unwrap(); - let mut inner = MaybeUninit::zeroed(); - unsafe { - bibl_initparams( - inner.as_mut_ptr(), - from.read_mode() as i32, - to.write_mode() as i32, - program.as_ptr() as *mut std::os::raw::c_char, - ); - } - Self { inner } - } -} - -impl Drop for Params { - fn drop(&mut self) { - unsafe { - bibl_freeparams(self.inner.as_mut_ptr()); - } - } -} - -unsafe impl Send for Params {} - -struct File { - path: CString, - handle: *mut FILE, -} - -impl File { - fn new<M: Into<Vec<u8>>>(path: &Path, mode: M) -> Self { - let path = CString::new(path.to_str().unwrap()).unwrap(); - let mode = CString::new(mode).unwrap(); - let handle = unsafe { fopen(path.as_ptr(), mode.as_ptr()) }; - Self { path, handle } - } -} - -impl Drop for File { - fn drop(&mut self) { - unsafe { - fclose(self.handle); - } - } -} - -unsafe impl Send for File {} - -pub fn convert(input: &str, from: InputFormat, to: OutputFormat) -> Option<String> { - let mut context = Context::new(); - let mut params = Params::new(from, to); - let dir = tempdir().expect("failed to create a temporary directory"); - - let input_path = dir.path().join("input"); - fs::write(&input_path, input).ok()?; - let input_file = File::new(&input_path, "r"); - unsafe { - let status = bibl_read( - context.inner.as_mut_ptr(), - input_file.handle, - input_file.path.as_ptr() as *mut std::os::raw::c_char, - params.inner.as_mut_ptr(), - ); - - if status != BIBL_OK as i32 { - return None; - } - } - - let output_path = dir.path().join("output"); - let output_file = File::new(&output_path, "w"); - unsafe { - let status = bibl_write( - context.inner.as_mut_ptr(), - output_file.handle, - params.inner.as_mut_ptr(), - ); - - if status != BIBL_OK as i32 { - return None; - } - } - - // Remove BOM - let data = fs::read(&output_path).ok()?; - if data.is_empty() { - return None; - } - - let text = String::from_utf8_lossy(&data[3..]).into_owned(); - Some(text) -} diff --git a/support/texlab/src/citation/driver.rs b/support/texlab/src/citation/driver.rs new file mode 100644 index 0000000000..eaf98304c4 --- /dev/null +++ b/support/texlab/src/citation/driver.rs @@ -0,0 +1,733 @@ +use isocountry::CountryCode; +use itertools::Itertools; +use titlecase::titlecase; +use url::Url; + +use crate::syntax::bibtex; + +use super::{ + entry::{EntryData, EntryKind}, + field::{ + author::AuthorField, + date::DateField, + number::{NumberField, NumberFieldData}, + text::TextField, + }, + output::{Inline, InlineBuilder, Punct}, +}; + +#[derive(Debug, Default)] +pub struct Driver { + builder: InlineBuilder, +} + +impl Driver { + pub fn process(&mut self, entry: &bibtex::Entry) -> Option<()> { + let entry = EntryData::parse(entry)?; + match entry.kind { + EntryKind::Article + | EntryKind::DataSet + | EntryKind::Manual + | EntryKind::Misc + | EntryKind::Online + | EntryKind::Electronic + | EntryKind::Www + | EntryKind::Proceedings + | EntryKind::MVProceedings + | EntryKind::Set + | EntryKind::Software + | EntryKind::Thesis + | EntryKind::MasterThesis + | EntryKind::PhdThesis + | EntryKind::Unknown => self.article_like(entry), + EntryKind::Book + | EntryKind::MVBook + | EntryKind::InBook + | EntryKind::BookInBook + | EntryKind::SuppBook + | EntryKind::Booklet + | EntryKind::Collection + | EntryKind::MVCollection + | EntryKind::InCollection + | EntryKind::SuppCollection + | EntryKind::Periodical + | EntryKind::SuppPeriodical + | EntryKind::InProceedings + | EntryKind::Conference + | EntryKind::Reference + | EntryKind::MVReference + | EntryKind::InReference + | EntryKind::Report + | EntryKind::TechReport => self.book_like(entry), + EntryKind::Patent => self.patent(entry), + } + } + + fn article_like(&mut self, mut entry: EntryData) -> Option<()> { + self.author(&mut entry); + self.title(&mut entry); + self.translator(&mut entry); + self.commentator(&mut entry); + self.annotator(&mut entry); + self.version(&mut entry); + self.journal(&mut entry); + self.article_series(&mut entry); + + if let Some(volume) = entry.number.remove(&NumberField::Volume) { + let number = entry + .number + .remove(&NumberField::Number) + .map(|data| format!(".{}", data)) + .unwrap_or_default(); + + self.builder.push( + Inline::Regular(format!("{}{}", volume, number)), + Punct::Space, + Punct::Space, + ); + } + + self.eid(&mut entry); + self.issue_and_date(&mut entry); + self.issue_title(&mut entry); + self.editors(&mut entry); + self.note(&mut entry); + self.pages(&mut entry); + self.issn(&mut entry); + self.doi(&mut entry); + self.eprint(&mut entry); + self.url(&mut entry); + self.addendum(&mut entry); + self.pubstate(&mut entry); + Some(()) + } + + fn book_like(&mut self, mut entry: EntryData) -> Option<()> { + self.author(&mut entry); + self.title(&mut entry); + self.inbook_title(&mut entry); + self.main_title(&mut entry); + self.event(&mut entry); + self.editors(&mut entry); + self.translator(&mut entry); + self.commentator(&mut entry); + self.annotator(&mut entry); + self.introduction(&mut entry); + self.foreword(&mut entry); + self.afterword(&mut entry); + self.edition(&mut entry); + self.book_volume(&mut entry); + self.volumes(&mut entry); + self.book_series(&mut entry); + self.how_published(&mut entry); + self.note(&mut entry); + self.publisher(&mut entry); + self.date(&mut entry); + self.chapter(&mut entry); + self.eid(&mut entry); + self.pages(&mut entry); + self.page_total(&mut entry); + self.isbn(&mut entry); + self.doi(&mut entry); + self.eprint(&mut entry); + self.url(&mut entry); + self.addendum(&mut entry); + self.pubstate(&mut entry); + Some(()) + } + + fn patent(&mut self, mut entry: EntryData) -> Option<()> { + self.author(&mut entry); + self.title(&mut entry); + self.patent_number(&mut entry); + self.holder(&mut entry); + self.date(&mut entry); + self.doi(&mut entry); + self.eprint(&mut entry); + self.url(&mut entry); + self.addendum(&mut entry); + self.pubstate(&mut entry); + Some(()) + } + + fn author(&mut self, entry: &mut EntryData) -> Option<()> { + let author = entry.author.remove(&AuthorField::Author)?; + self.builder.push( + Inline::Regular(author.to_string()), + Punct::Nothing, + Punct::Colon, + ); + + Some(()) + } + + fn commentator(&mut self, entry: &mut EntryData) -> Option<()> { + let commentator = entry.author.remove(&AuthorField::Commentator)?; + self.builder.push( + Inline::Regular(format!("With a comment. by {}", commentator)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn translator(&mut self, entry: &mut EntryData) -> Option<()> { + let translator = entry.author.remove(&AuthorField::Translator)?; + self.builder.push( + Inline::Regular(format!("Trans. by {}", translator)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn annotator(&mut self, entry: &mut EntryData) -> Option<()> { + let annotator = entry.author.remove(&AuthorField::Annotator)?; + self.builder.push( + Inline::Regular(format!("With annots. by {}", annotator)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn editors(&mut self, entry: &mut EntryData) { + self.editor(entry, AuthorField::Editor, TextField::EditorType); + self.editor(entry, AuthorField::EditorA, TextField::EditorTypeA); + self.editor(entry, AuthorField::EditorB, TextField::EditorTypeB); + self.editor(entry, AuthorField::EditorC, TextField::EditorTypeC); + } + + fn editor( + &mut self, + entry: &mut EntryData, + name_field: AuthorField, + type_field: TextField, + ) -> Option<()> { + let editor = entry.author.remove(&name_field)?; + let editor_type = entry + .text + .remove(&type_field) + .map(|data| data.text) + .unwrap_or_else(|| "Ed. by".to_string()); + + self.builder.push( + Inline::Regular(format!("{editor_type} {editor}")), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn title(&mut self, entry: &mut EntryData) -> Option<()> { + let title = entry.text.remove(&TextField::Title)?; + let subtitle = entry + .text + .remove(&TextField::Subtitle) + .map(|data| format!(". {}", data.text)) + .unwrap_or_default(); + + self.builder.push( + Inline::Quoted(format!("{}{}", title.text, subtitle)), + Punct::Nothing, + Punct::Dot, + ); + + let addon = entry.text.remove(&TextField::TitleAddon)?; + self.builder + .push(Inline::Regular(addon.text), Punct::Dot, Punct::Dot); + + Some(()) + } + + fn main_title(&mut self, entry: &mut EntryData) -> Option<()> { + let title = entry.text.remove(&TextField::MainTitle)?; + let subtitle = entry + .text + .remove(&TextField::MainSubtitle) + .map(|data| format!(". {}", data.text)) + .unwrap_or_default(); + + self.builder.push( + Inline::Italic(titlecase(&format!("{}{}", title.text, subtitle))), + Punct::Dot, + Punct::Dot, + ); + + let addon = entry.text.remove(&TextField::MainTitleAddon)?; + self.builder + .push(Inline::Regular(addon.text), Punct::Dot, Punct::Dot); + + Some(()) + } + + fn inbook_title(&mut self, entry: &mut EntryData) -> Option<()> { + let title = entry.text.remove(&TextField::BookTitle)?; + let subtitle = entry + .text + .remove(&TextField::BookSubtitle) + .map(|data| format!(". {}", data.text)) + .unwrap_or_default(); + + self.builder.push( + Inline::Italic(titlecase(&format!("{}{}", title.text, subtitle))), + Punct::Dot, + Punct::Dot, + ); + + let addon = entry.text.remove(&TextField::BookTitleAddon)?; + self.builder + .push(Inline::Regular(addon.text), Punct::Dot, Punct::Dot); + + Some(()) + } + + fn issue_title(&mut self, entry: &mut EntryData) -> Option<()> { + let issue = entry.text.remove(&TextField::IssueTitle)?; + let subtitle = entry + .text + .remove(&TextField::IssueSubtitle) + .map(|data| format!(" {}", data.text)) + .unwrap_or_default(); + + self.builder.push( + Inline::Italic(titlecase(&format!("{}{}", issue.text, subtitle))), + Punct::Colon, + Punct::Space, + ); + + if let Some(addon) = entry.text.remove(&TextField::IssueTitleAddon) { + self.builder + .push(Inline::Regular(addon.text), Punct::Dot, Punct::Dot); + } + + Some(()) + } + + fn event(&mut self, entry: &mut EntryData) -> Option<()> { + let title = entry.text.remove(&TextField::EventTitle)?; + let addon = entry + .text + .remove(&TextField::EventTitleAddon) + .map_or(String::new(), |addon| format!(". {}", addon.text)); + + let venue = entry.text.remove(&TextField::Venue); + let date = entry.date.remove(&DateField::EventDate); + let venue_and_date = match (venue, date) { + (None, None) => String::new(), + (None, Some(date)) => format!(" ({date})"), + (Some(venue), None) => format!(" ({})", venue.text), + (Some(venue), Some(date)) => format!(" ({}, {})", venue.text, date), + }; + + self.builder.push( + Inline::Regular(format!("{}{}{}", title.text, addon, venue_and_date)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn patent_number(&mut self, entry: &mut EntryData) -> Option<()> { + let number = entry.number.remove(&NumberField::Number)?; + + let location = entry + .text + .remove(&TextField::Location) + .map_or(String::new(), |location| { + let text = location + .text + .split_whitespace() + .map(|word| { + if word == "countryuk" { + "United Kingdom" + } else { + word.strip_prefix("country") + .and_then(|code| CountryCode::for_alpha2_caseless(code).ok()) + .map_or(word, |country| country.name()) + } + }) + .join(" "); + + format!(" ({})", text) + }); + + self.builder.push( + Inline::Regular(format!("{}{}", number, location)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn holder(&mut self, entry: &mut EntryData) -> Option<()> { + let holder = entry.text.remove(&TextField::Holder)?; + self.builder + .push(Inline::Regular(holder.text), Punct::Dot, Punct::Dot); + + Some(()) + } + + fn version(&mut self, entry: &mut EntryData) -> Option<()> { + let version = entry.text.remove(&TextField::Version)?; + self.builder.push( + Inline::Regular(format!("Version {}", version.text)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn article_series(&mut self, entry: &mut EntryData) -> Option<()> { + let series = entry.text.remove(&TextField::Series)?; + self.builder + .push(Inline::Regular(series.text), Punct::Space, Punct::Space); + + Some(()) + } + + fn issue_and_date(&mut self, entry: &mut EntryData) -> Option<()> { + let date = [DateField::Date, DateField::Year, DateField::Month] + .iter() + .filter_map(|field| entry.date.remove(field)) + .reduce(|a, b| a + b)?; + + let issue = entry + .text + .remove(&TextField::Issue) + .map(|data| format!("{} ", data.text)) + .unwrap_or_default(); + + self.builder.push( + Inline::Regular(format!("({issue}{date})")), + Punct::Space, + Punct::Colon, + ); + + Some(()) + } + + fn chapter(&mut self, entry: &mut EntryData) -> Option<()> { + let chapter = entry.text.remove(&TextField::Chapter)?; + self.builder.push( + Inline::Regular(format!("Chap. {}", chapter.text)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn date(&mut self, entry: &mut EntryData) -> Option<()> { + let date = [DateField::Date, DateField::Year, DateField::Month] + .iter() + .filter_map(|field| entry.date.remove(field)) + .reduce(|a, b| a + b)?; + + self.builder + .push(Inline::Regular(format!("{date}")), Punct::Dot, Punct::Dot); + + Some(()) + } + + fn how_published(&mut self, entry: &mut EntryData) -> Option<()> { + let how_published = entry.text.remove(&TextField::HowPublished)?; + self.builder + .push(Inline::Regular(how_published.text), Punct::Dot, Punct::Dot); + + Some(()) + } + + fn publisher(&mut self, entry: &mut EntryData) -> Option<()> { + let publisher = entry.text.remove(&TextField::Publisher)?.text; + let location = entry + .text + .remove(&TextField::Location) + .map_or(String::new(), |location| format!("{}: ", location.text)); + + self.builder.push( + Inline::Regular(format!("{location}{publisher}")), + Punct::Dot, + Punct::Comma, + ); + + Some(()) + } + + fn book_series(&mut self, entry: &mut EntryData) -> Option<()> { + let series = entry.text.remove(&TextField::Series)?; + let number = entry + .number + .remove(&NumberField::Number) + .map_or(String::new(), |number| format!(" {}", number)); + + self.builder.push( + Inline::Regular(format!("{}{}", series.text, number)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn volumes(&mut self, entry: &mut EntryData) -> Option<()> { + let volumes = entry.number.remove(&NumberField::Volume)?; + self.builder.push( + Inline::Regular(format!("{volumes} vols")), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn book_volume(&mut self, entry: &mut EntryData) -> Option<()> { + let volume = entry.number.remove(&NumberField::Volume)?; + let part = entry + .number + .remove(&NumberField::Part) + .map_or(String::new(), |part| format!(".{part}")); + + self.builder.push( + Inline::Regular(format!("Vol. {}{}", volume, part)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn pubstate(&mut self, entry: &mut EntryData) -> Option<()> { + let pubstate = entry.text.remove(&TextField::Pubstate)?; + self.builder + .push(Inline::Regular(pubstate.text), Punct::Dot, Punct::Dot); + + Some(()) + } + + fn addendum(&mut self, entry: &mut EntryData) -> Option<()> { + let addendum = entry.text.remove(&TextField::Addendum)?; + self.builder + .push(Inline::Regular(addendum.text), Punct::Dot, Punct::Dot); + + Some(()) + } + + fn edition(&mut self, entry: &mut EntryData) -> Option<()> { + let text = match entry.number.remove(&NumberField::Edition)? { + NumberFieldData::Scalar(1) => "1st".to_string(), + NumberFieldData::Scalar(2) => "2nd".to_string(), + NumberFieldData::Scalar(3) => "3rd".to_string(), + NumberFieldData::Scalar(number) => format!("{}th", number), + NumberFieldData::Range(_, _) => return None, + NumberFieldData::Other(text) => text, + }; + + self.builder + .push(Inline::Regular(text), Punct::Dot, Punct::Dot); + Some(()) + } + + fn introduction(&mut self, entry: &mut EntryData) -> Option<()> { + let author = entry.author.remove(&AuthorField::Introduction)?; + self.builder.push( + Inline::Regular(format!("With an intro. by {}", author)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn foreword(&mut self, entry: &mut EntryData) -> Option<()> { + let author = entry.author.remove(&AuthorField::Commentator)?; + self.builder.push( + Inline::Regular(format!("With a forew. by {}", author)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn afterword(&mut self, entry: &mut EntryData) -> Option<()> { + let author = entry.author.remove(&AuthorField::Commentator)?; + self.builder.push( + Inline::Regular(format!("With an afterw. by {}", author)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn note(&mut self, entry: &mut EntryData) -> Option<()> { + let note = entry.text.remove(&TextField::Note)?; + self.builder + .push(Inline::Regular(note.text), Punct::Dot, Punct::Comma); + + Some(()) + } + + fn pages(&mut self, entry: &mut EntryData) -> Option<()> { + let pages = entry.number.remove(&NumberField::Pages)?; + self.builder.push( + Inline::Regular(pages.to_string()), + Punct::Comma, + Punct::Space, + ); + + Some(()) + } + + fn page_total(&mut self, entry: &mut EntryData) -> Option<()> { + let page_total = entry.number.remove(&NumberField::PageTotal)?; + self.builder.push( + Inline::Regular(format!("{} pp", page_total)), + Punct::Dot, + Punct::Dot, + ); + + Some(()) + } + + fn journal(&mut self, entry: &mut EntryData) -> Option<()> { + let title = entry + .text + .remove(&TextField::Journal) + .or_else(|| entry.text.remove(&TextField::JournalTitle))?; + + let subtitle = entry + .text + .remove(&TextField::JournalSubtitle) + .map_or(String::new(), |data| format!(" {}", data.text)); + + self.builder.push( + Inline::Italic(titlecase(&format!("{}{}", title.text, subtitle))), + Punct::Dot, + Punct::Space, + ); + + Some(()) + } + + fn eid(&mut self, entry: &mut EntryData) -> Option<()> { + let eid = entry.text.remove(&TextField::Eid)?; + self.builder + .push(Inline::Regular(eid.text), Punct::Comma, Punct::Space); + + Some(()) + } + + fn isbn(&mut self, entry: &mut EntryData) -> Option<()> { + let isbn = entry.text.remove(&TextField::Isbn)?; + self.builder.push( + Inline::Regular("ISBN".to_string()), + Punct::Dot, + Punct::Colon, + ); + + self.builder + .push(Inline::Regular(isbn.text), Punct::Space, Punct::Dot); + + Some(()) + } + + fn issn(&mut self, entry: &mut EntryData) -> Option<()> { + let issn = entry.text.remove(&TextField::Issn)?; + self.builder.push( + Inline::Regular("ISSN".to_string()), + Punct::Dot, + Punct::Colon, + ); + + self.builder + .push(Inline::Regular(issn.text), Punct::Space, Punct::Dot); + + Some(()) + } + + fn url(&mut self, entry: &mut EntryData) -> Option<()> { + let url = entry.text.remove(&TextField::Url)?; + + self.builder + .push(Inline::Regular("URL".to_string()), Punct::Dot, Punct::Colon); + + let url = url.text; + let alt = url.clone(); + + self.builder + .push(Inline::Link { url, alt }, Punct::Space, Punct::Space); + + let date = entry.date.remove(&DateField::UrlDate)?; + self.builder.push( + Inline::Regular(format!("({date})")), + Punct::Space, + Punct::Space, + ); + + Some(()) + } + + fn doi(&mut self, entry: &mut EntryData) -> Option<()> { + let doi = entry.text.remove(&TextField::Doi)?; + self.builder + .push(Inline::Regular("DOI".to_string()), Punct::Dot, Punct::Colon); + + let alt = Url::parse(&doi.text) + .ok() + .filter(|url| !url.cannot_be_a_base()) + .map_or(doi.text, |url| url.path()[1..].to_string()); + + let url = format!("https://doi.org/{alt}"); + + self.builder + .push(Inline::Link { url, alt }, Punct::Space, Punct::Dot); + + Some(()) + } + + fn eprint(&mut self, entry: &mut EntryData) -> Option<()> { + let eprint = entry.text.remove(&TextField::Eprint)?; + let eprint_type = entry + .text + .remove(&TextField::EprintType) + .map_or_else(|| "eprint".to_string(), |data| data.text); + + if eprint_type.eq_ignore_ascii_case("arxiv") { + self.builder.push( + Inline::Regular("arXiv".to_string()), + Punct::Dot, + Punct::Colon, + ); + + self.builder.push( + Inline::Link { + url: format!("https://arxiv.org/abs/{}", eprint.text), + alt: eprint.text, + }, + Punct::Space, + Punct::Dot, + ); + } else { + self.builder + .push(Inline::Regular(eprint_type), Punct::Dot, Punct::Colon); + + self.builder + .push(Inline::Regular(eprint.text), Punct::Space, Punct::Dot); + } + + Some(()) + } + + pub fn finish(self) -> impl Iterator<Item = (Inline, Punct)> { + self.builder.finish() + } +} diff --git a/support/texlab/src/citation/entry.rs b/support/texlab/src/citation/entry.rs new file mode 100644 index 0000000000..c8b3f98753 --- /dev/null +++ b/support/texlab/src/citation/entry.rs @@ -0,0 +1,123 @@ +use std::str::FromStr; + +use rustc_hash::FxHashMap; +use strum::EnumString; + +use crate::syntax::bibtex::{Entry, Field, HasName, HasType}; + +use super::field::{ + author::{AuthorField, AuthorFieldData}, + date::{DateField, DateFieldData}, + number::{NumberField, NumberFieldData}, + text::{TextField, TextFieldData}, +}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)] +#[strum(ascii_case_insensitive)] +pub enum EntryKind { + Article, + Book, + MVBook, + InBook, + BookInBook, + SuppBook, + Booklet, + Collection, + MVCollection, + InCollection, + SuppCollection, + DataSet, + Manual, + Misc, + Online, + Electronic, + Www, + Patent, + Periodical, + SuppPeriodical, + Proceedings, + MVProceedings, + InProceedings, + Conference, + Reference, + MVReference, + InReference, + Report, + Set, + Software, + Thesis, + MasterThesis, + PhdThesis, + TechReport, + Unknown, +} + +impl Default for EntryKind { + fn default() -> Self { + Self::Unknown + } +} + +#[derive(Debug, Clone, Default)] +pub struct EntryData { + pub kind: EntryKind, + pub text: FxHashMap<TextField, TextFieldData>, + pub author: FxHashMap<AuthorField, AuthorFieldData>, + pub date: FxHashMap<DateField, DateFieldData>, + pub number: FxHashMap<NumberField, NumberFieldData>, +} + +impl EntryData { + pub fn parse(entry: &Entry) -> Option<Self> { + let mut data = EntryData { + kind: entry + .type_token() + .and_then(|token| EntryKind::from_str(&token.text()[1..]).ok()) + .unwrap_or(EntryKind::Unknown), + ..EntryData::default() + }; + + for field in entry.fields() { + let _ = data.parse_field(&field); + } + + Some(data) + } + + fn parse_field(&mut self, field: &Field) -> Option<()> { + let name = field.name_token()?; + let name = name.text(); + self.parse_author_field(field, name) + .or_else(|| self.parse_date_field(field, name)) + .or_else(|| self.parse_number_field(field, name)) + .or_else(|| self.parse_text_field(field, name)) + } + + fn parse_author_field(&mut self, field: &Field, name: &str) -> Option<()> { + let name = AuthorField::parse(name)?; + let data = AuthorFieldData::parse(field)?; + self.author.insert(name, data); + Some(()) + } + + fn parse_date_field(&mut self, field: &Field, name: &str) -> Option<()> { + let name = DateField::parse(name)?; + let data = DateFieldData::parse(field)?; + self.date.insert(name, data); + Some(()) + } + + fn parse_number_field(&mut self, field: &Field, name: &str) -> Option<()> { + let name = NumberField::parse(name)?; + let data = NumberFieldData::parse(field)?; + self.number.insert(name, data); + Some(()) + } + + fn parse_text_field(&mut self, field: &Field, name: &str) -> Option<()> { + let name = TextField::parse(name).unwrap_or(TextField::Unknown); + let data = TextFieldData::parse(field)?; + self.text.insert(name, data); + Some(()) + } +} diff --git a/support/texlab/src/citation/field.rs b/support/texlab/src/citation/field.rs new file mode 100644 index 0000000000..d9f3adacab --- /dev/null +++ b/support/texlab/src/citation/field.rs @@ -0,0 +1,4 @@ +pub mod author; +pub mod date; +pub mod number; +pub mod text; diff --git a/support/texlab/src/citation/field/author.rs b/support/texlab/src/citation/field/author.rs new file mode 100644 index 0000000000..a5e4368320 --- /dev/null +++ b/support/texlab/src/citation/field/author.rs @@ -0,0 +1,73 @@ +use std::{borrow::Cow, fmt, str::FromStr}; + +use human_name::Name; +use itertools::Itertools; +use strum::EnumString; + +use crate::syntax::bibtex::Field; + +use super::text::TextFieldData; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)] +#[strum(ascii_case_insensitive)] +pub enum AuthorField { + Afterword, + Annotator, + Author, + Commentator, + Editor, + EditorA, + EditorB, + EditorC, + Foreword, + Introduction, + Translator, +} + +impl AuthorField { + pub fn parse(input: &str) -> Option<Self> { + Self::from_str(input).ok() + } +} + +#[derive(Debug, Clone, Default)] +pub struct AuthorFieldData { + pub authors: Vec<Name>, +} + +impl fmt::Display for AuthorFieldData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let names = self + .authors + .iter() + .map(|author| author.display_initial_surname()); + + for part in Itertools::intersperse(names, Cow::Borrowed(", ")) { + write!(f, "{}", part)?; + } + + Ok(()) + } +} + +impl AuthorFieldData { + pub fn parse(field: &Field) -> Option<Self> { + let TextFieldData { text } = TextFieldData::parse(field)?; + let mut authors = Vec::new(); + let mut words = Vec::new(); + for word in text.split_whitespace() { + if word.eq_ignore_ascii_case("and") { + authors.push(Name::parse(&words.join(" "))?); + words.clear(); + } else { + words.push(word); + } + } + + if !words.is_empty() { + authors.push(Name::parse(&words.join(" "))?); + } + + Some(Self { authors }) + } +} diff --git a/support/texlab/src/citation/field/date.rs b/support/texlab/src/citation/field/date.rs new file mode 100644 index 0000000000..b9b3dbb438 --- /dev/null +++ b/support/texlab/src/citation/field/date.rs @@ -0,0 +1,81 @@ +use std::{fmt, ops::Add, str::FromStr}; + +use chrono::{Datelike, Month, NaiveDate}; +use strum::EnumString; + +use crate::syntax::bibtex::Field; + +use super::text::TextFieldData; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)] +#[strum(ascii_case_insensitive)] +pub enum DateField { + Date, + EventDate, + Month, + UrlDate, + Year, +} + +impl DateField { + pub fn parse(input: &str) -> Option<Self> { + Self::from_str(input).ok() + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +pub enum DateFieldData { + Date(NaiveDate), + Year(i32), + Month(Month), + Other(String), +} + +impl Add for DateFieldData { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (date @ Self::Year(_), Self::Year(_)) + | (date @ Self::Month(_), Self::Month(_)) + | (date @ Self::Date(_), Self::Date(_)) + | (Self::Other(_), date) + | (date, Self::Other(_)) => date, + (Self::Year(year), Self::Month(month)) | (Self::Month(month), Self::Year(year)) => { + let new_date = NaiveDate::from_ymd(year, month.number_from_month(), 1); + Self::Date(new_date) + } + (Self::Year(year), Self::Date(date)) | (Self::Date(date), Self::Year(year)) => { + let new_date = date.with_year(year).unwrap_or(date); + Self::Date(new_date) + } + (Self::Date(date), Self::Month(month)) | (Self::Month(month), Self::Date(date)) => { + let new_date = date.with_month(month.number_from_month()).unwrap_or(date); + Self::Date(new_date) + } + } + } +} + +impl fmt::Display for DateFieldData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Year(year) => write!(f, "{}", year), + Self::Date(date) => write!(f, "{}", date.format("%b. %Y")), + Self::Month(month) => write!(f, "{}", month.name()), + Self::Other(text) => write!(f, "{}", text), + } + } +} + +impl DateFieldData { + pub fn parse(field: &Field) -> Option<Self> { + let TextFieldData { text } = TextFieldData::parse(field)?; + NaiveDate::from_str(&text) + .ok() + .map(Self::Date) + .or_else(|| text.parse().ok().map(Self::Year)) + .or_else(|| text.parse().ok().map(Self::Month)) + .or(Some(Self::Other(text))) + } +} diff --git a/support/texlab/src/citation/field/number.rs b/support/texlab/src/citation/field/number.rs new file mode 100644 index 0000000000..9f51ccef90 --- /dev/null +++ b/support/texlab/src/citation/field/number.rs @@ -0,0 +1,54 @@ +use std::{fmt, str::FromStr}; + +use strum::EnumString; + +use crate::syntax::bibtex::Field; + +use super::text::TextFieldData; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)] +#[strum(ascii_case_insensitive)] +pub enum NumberField { + Edition, + Number, + Pages, + PageTotal, + Part, + Volume, + Volumes, +} + +impl NumberField { + pub fn parse(input: &str) -> Option<Self> { + Self::from_str(input).ok() + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +pub enum NumberFieldData { + Scalar(u32), + Range(u32, u32), + Other(String), +} + +impl fmt::Display for NumberFieldData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Scalar(value) => write!(f, "{}", value), + Self::Range(start, end) => write!(f, "{}-{}", start, end), + Self::Other(value) => write!(f, "{}", value.replace("--", "-")), + } + } +} + +impl NumberFieldData { + pub fn parse(field: &Field) -> Option<Self> { + let TextFieldData { text } = TextFieldData::parse(field)?; + text.split_once("--") + .or_else(|| text.split_once('-')) + .and_then(|(a, b)| Some((a.parse().ok()?, b.parse().ok()?))) + .map(|(a, b)| Self::Range(a, b)) + .or_else(|| text.parse().ok().map(Self::Scalar)) + .or(Some(Self::Other(text))) + } +} diff --git a/support/texlab/src/citation/field/text.rs b/support/texlab/src/citation/field/text.rs new file mode 100644 index 0000000000..87119bd47c --- /dev/null +++ b/support/texlab/src/citation/field/text.rs @@ -0,0 +1,263 @@ +use std::str::FromStr; + +use rowan::{ast::AstNode, NodeOrToken}; +use rustc_hash::FxHashSet; +use strum::EnumString; + +use crate::syntax::bibtex::{ + Accent, Command, CurlyGroup, Field, HasAccentName, HasCommandName, HasName, HasValue, HasWord, + Join, Literal, QuoteGroup, Root, SyntaxKind::*, SyntaxToken, Value, +}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)] +#[strum(ascii_case_insensitive)] +pub enum TextField { + Abstract, + Addendum, + BookSubtitle, + BookTitle, + BookTitleAddon, + Chapter, + Doi, + EditorType, + EditorTypeA, + EditorTypeB, + EditorTypeC, + Eid, + Eprint, + EprintClass, + EprintType, + EventTitle, + EventTitleAddon, + Holder, + HowPublished, + Isbn, + Issn, + Issue, + IssueSubtitle, + IssueTitle, + IssueTitleAddon, + Journal, + JournalSubtitle, + JournalTitle, + JournalTitleAddon, + Language, + Location, + MainTitle, + MainSubtitle, + MainTitleAddon, + Note, + OrigLanguage, + Publisher, + Pubstate, + Series, + Subtitle, + Title, + TitleAddon, + Type, + Unknown, + Url, + Venue, + Version, +} + +impl TextField { + pub fn parse(input: &str) -> Option<Self> { + Self::from_str(input).ok() + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Default)] +pub struct TextFieldData { + pub text: String, +} + +impl TextFieldData { + pub fn parse(field: &Field) -> Option<Self> { + let mut builder = TextFieldDataBuilder::default(); + builder.visit_value(&field.value()?)?; + Some(builder.data) + } +} + +#[derive(Default)] +struct TextFieldDataBuilder { + data: TextFieldData, + string_stack: FxHashSet<String>, +} + +impl TextFieldDataBuilder { + fn visit_value(&mut self, value: &Value) -> Option<()> { + match value { + Value::Literal(lit) => { + self.visit_literal(lit)?; + } + Value::CurlyGroup(group) => { + self.visit_curly_group(group)?; + } + Value::QuoteGroup(group) => { + self.visit_quote_group(group)?; + } + Value::Join(join) => { + self.visit_join(join)?; + } + Value::Accent(accent) => { + let _ = self.visit_accent(accent); + } + Value::Command(command) => { + let _ = self.visit_command(command); + } + }; + + Some(()) + } + + fn visit_literal(&mut self, lit: &Literal) -> Option<()> { + if lit + .name_token() + .and_then(|name| self.visit_string_reference(&name)) + .is_none() + { + lit.syntax() + .text() + .for_each_chunk(|text| self.data.text.push_str(text)); + } + + Some(()) + } + + fn visit_string_reference(&mut self, name: &SyntaxToken) -> Option<()> { + let root = Root::cast(name.ancestors().last()?)?; + let name = name.text(); + + let value = root + .strings() + .filter(|string| { + string + .name_token() + .map_or(false, |token| token.text() == name) + }) + .find_map(|string| string.value())?; + + if !self.string_stack.insert(name.to_string()) { + return None; + } + + let _ = self.visit_value(&value); + self.string_stack.remove(name); + Some(()) + } + + fn visit_curly_group(&mut self, group: &CurlyGroup) -> Option<()> { + for child in group.syntax().children_with_tokens() { + match child { + NodeOrToken::Node(node) => { + self.visit_value(&Value::cast(node)?)?; + } + NodeOrToken::Token(token) => { + match token.kind() { + L_CURLY | R_CURLY => (), + WHITESPACE | NBSP => self.data.text.push(' '), + _ => self.data.text.push_str(token.text()), + }; + } + }; + } + + Some(()) + } + + fn visit_quote_group(&mut self, group: &QuoteGroup) -> Option<()> { + for child in group.syntax().children_with_tokens() { + match child { + NodeOrToken::Node(node) => { + self.visit_value(&Value::cast(node)?)?; + } + NodeOrToken::Token(token) => { + match token.kind() { + QUOTE => (), + WHITESPACE | NBSP => self.data.text.push(' '), + _ => self.data.text.push_str(token.text()), + }; + } + }; + } + + Some(()) + } + + fn visit_join(&mut self, join: &Join) -> Option<()> { + if let Some(left) = join.left_value() { + self.visit_value(&left)?; + } + + if let Some(right) = join.right_value() { + self.visit_value(&right)?; + } + + Some(()) + } + + fn visit_accent(&mut self, accent: &Accent) -> Option<()> { + let name = accent.accent_name_token()?; + let word = accent.word_token()?; + + let mut chars = word.text().chars(); + let a = chars.next()?; + + if chars.next().is_some() { + self.data.text.push_str(word.text()); + } else { + let b = match name.text() { + r#"\`"# => '\u{0300}', + r#"\'"# => '\u{0301}', + r#"\^"# => '\u{0302}', + r#"\""# => '\u{0308}', + r#"\H"# => '\u{030B}', + r#"\~"# => '\u{0303}', + + r#"\c"# => '\u{0327}', + r#"\k"# => '\u{0328}', + r#"\="# => '\u{0304}', + r#"\b"# => '\u{0331}', + r#"\."# => '\u{0307}', + r#"\d"# => '\u{0323}', + r#"\r"# => '\u{030A}', + r#"\u"# => '\u{0306}', + r#"\v"# => '\u{030C}', + _ => '\u{0000}', + }; + + match unicode_normalization::char::compose(a, b) { + Some(c) => self.data.text.push(c), + None => self.data.text.push_str(word.text()), + }; + } + + Some(()) + } + + fn visit_command(&mut self, command: &Command) -> Option<()> { + let name = command.command_name_token()?; + let replacement = match name.text() { + r#"\l"# => "\u{0142}", + r#"\o"# => "\u{00F8}", + r#"\i"# => "\u{0131}", + r#"\&"# => "&", + r#"\$"# => "$", + r#"\{"# => "{", + r#"\}"# => "}", + r#"\%"# => "%", + r#"\#"# => "#", + r#"\_"# => "_", + r#"\ "# | r#"\,"# | r#"\;"# => " ", + r#"\hyphen"# => "-", + r#"\TeX"# => "TeX", + r#"\LaTeX"# => "LaTeX", + text => text, + }; + + self.data.text.push_str(replacement); + Some(()) + } +} diff --git a/support/texlab/src/citation/output.rs b/support/texlab/src/citation/output.rs new file mode 100644 index 0000000000..555ec138db --- /dev/null +++ b/support/texlab/src/citation/output.rs @@ -0,0 +1,87 @@ +use std::ops::Add; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +pub enum Punct { + Nothing, + Space, + Comma, + Dot, + Colon, +} + +impl Punct { + pub fn as_str(self) -> &'static str { + match self { + Self::Nothing => "", + Self::Space => " ", + Self::Comma => ", ", + Self::Dot => ". ", + Self::Colon => ": ", + } + } +} + +impl Add for Punct { + type Output = Punct; + + fn add(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Self::Nothing, Self::Nothing) => Self::Nothing, + (Self::Nothing, Self::Space) + | (Self::Space, Self::Nothing) + | (Self::Space, Self::Space) => Self::Space, + (Self::Nothing, Self::Comma) + | (Self::Space, Self::Comma) + | (Self::Comma, Self::Nothing) + | (Self::Comma, Self::Space) + | (Self::Comma, Self::Comma) + | (Self::Comma, Self::Dot) + | (Self::Dot, Self::Comma) => Self::Comma, + (Self::Nothing, Self::Dot) + | (Self::Space, Self::Dot) + | (Self::Dot, Self::Nothing) + | (Self::Dot, Self::Space) + | (Self::Dot, Self::Dot) => Self::Dot, + (Self::Nothing, Self::Colon) + | (Self::Space, Self::Colon) + | (Self::Comma, Self::Colon) + | (Self::Dot, Self::Colon) + | (Self::Colon, Self::Nothing) + | (Self::Colon, Self::Space) + | (Self::Colon, Self::Comma) + | (Self::Colon, Self::Dot) + | (Self::Colon, Self::Colon) => Self::Colon, + } + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] +pub enum Inline { + Regular(String), + Italic(String), + Quoted(String), + Link { url: String, alt: String }, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Default)] +pub struct InlineBuilder { + items: Vec<(Inline, Punct)>, +} + +impl InlineBuilder { + pub fn push(&mut self, inline: Inline, leading: Punct, trailing: Punct) { + if let Some((_, last)) = self.items.last_mut() { + *last = *last + leading; + } + + self.items.push((inline, trailing)); + } + + pub fn finish(mut self) -> impl Iterator<Item = (Inline, Punct)> { + if let Some((_, last)) = self.items.last_mut() { + *last = Punct::Nothing; + } + + self.items.into_iter() + } +} diff --git a/support/texlab/src/citation/ris.rs b/support/texlab/src/citation/ris.rs deleted file mode 100644 index 041fb654a0..0000000000 --- a/support/texlab/src/citation/ris.rs +++ /dev/null @@ -1,471 +0,0 @@ -// Ported from: https://github.com/michel-kraemer/citeproc-java/tree/master/citeproc-java/templates -// Michel Kraemer -// Apache License 2.0 -use citeproc_io::{unicode::is_latin_cyrillic, Date, DateOrRange, Name, NumberLike, Reference}; -use csl::*; -use fnv::FnvHashMap; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] -#[serde(rename_all = "UPPERCASE")] -pub enum RisType { - Abst, - Advs, - Aggr, - Ancient, - Art, - Bill, - Blog, - Book, - Case, - Chap, - Chart, - Clswk, - Comp, - Conf, - Cpaper, - Ctlg, - Data, - Dbase, - Dict, - Ebook, - Echap, - Edbook, - Ejour, - Elec, - Encyc, - Equa, - Figure, - Gen, - Govdoc, - Grant, - Hear, - Icomm, - Inpr, - Jfull, - Jour, - Legal, - Manscpt, - Map, - Mgzn, - Mpct, - Multi, - Music, - News, - Pamp, - Pat, - Pcomm, - Rprt, - Ser, - Slide, - Sound, - Stand, - Stat, - Std, - Thes, - Unpb, - Video, -} - -impl RisType { - pub fn parse(ty: &str) -> Option<Self> { - let ty = format!("\"{}\"", ty); - serde_json::from_str(&ty).ok() - } - - pub fn csl(self) -> CslType { - match self { - Self::Abst => CslType::Article, - Self::Advs => CslType::Article, - Self::Aggr => CslType::Dataset, - Self::Ancient => CslType::Article, - Self::Art => CslType::Article, - Self::Bill => CslType::Bill, - Self::Blog => CslType::Webpage, - Self::Book => CslType::Book, - Self::Case => CslType::LegalCase, - Self::Chap => CslType::Chapter, - Self::Chart => CslType::Article, - Self::Clswk => CslType::Article, - Self::Comp => CslType::Article, - Self::Conf => CslType::PaperConference, - Self::Cpaper => CslType::PaperConference, - Self::Ctlg => CslType::Book, - Self::Data => CslType::Dataset, - Self::Dbase => CslType::Dataset, - Self::Dict => CslType::EntryDictionary, - Self::Ebook => CslType::Book, - Self::Echap => CslType::Chapter, - Self::Edbook => CslType::Book, - Self::Ejour => CslType::ArticleJournal, - Self::Elec => CslType::Article, - Self::Encyc => CslType::EntryEncyclopedia, - Self::Equa => CslType::Article, - Self::Figure => CslType::Figure, - Self::Gen => CslType::Article, - Self::Govdoc => CslType::Legislation, - Self::Grant => CslType::Legislation, - Self::Hear => CslType::Article, - Self::Icomm => CslType::PersonalCommunication, - Self::Inpr => CslType::PaperConference, - Self::Jfull => CslType::ArticleJournal, - Self::Jour => CslType::ArticleJournal, - Self::Legal => CslType::Legislation, - Self::Manscpt => CslType::Manuscript, - Self::Map => CslType::Map, - Self::Mgzn => CslType::ArticleMagazine, - Self::Mpct => CslType::MotionPicture, - Self::Multi => CslType::Webpage, - Self::Music => CslType::Song, - Self::News => CslType::ArticleNewspaper, - Self::Pamp => CslType::Pamphlet, - Self::Pat => CslType::Patent, - Self::Pcomm => CslType::PersonalCommunication, - Self::Rprt => CslType::Report, - Self::Ser => CslType::Article, - Self::Slide => CslType::Article, - Self::Sound => CslType::Song, - Self::Stand => CslType::Article, - Self::Stat => CslType::Legislation, - Self::Std => CslType::Article, - Self::Thes => CslType::Thesis, - Self::Unpb => CslType::Article, - Self::Video => CslType::MotionPicture, - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)] -pub struct RisReference { - pub id: Option<String>, - pub ty: Option<RisType>, - pub editors: Vec<String>, - pub tertiary_authors: Vec<String>, - pub subsidiary_authors: Vec<String>, - pub abstrct: Option<String>, - pub author_address: Option<String>, - pub accession_number: Option<String>, - pub authors: Vec<String>, - pub book_or_conference: Option<String>, - pub custom1: Option<String>, - pub custom2: Option<String>, - pub custom3: Option<String>, - pub custom4: Option<String>, - pub custom5: Option<String>, - pub custom6: Option<String>, - pub custom7: Option<String>, - pub custom8: Option<String>, - pub caption: Option<String>, - pub call_number: Option<String>, - pub place: Option<String>, - pub date: Option<String>, - pub name_of_database: Option<String>, - pub doi: Option<String>, - pub database_provider: Option<String>, - pub end_page: Option<String>, - pub edition: Option<String>, - pub issue: Option<String>, - pub journal: Option<String>, - pub keywords: Vec<String>, - pub file_attachments: Vec<String>, - pub figure: Option<String>, - pub language: Option<String>, - pub label: Option<String>, - pub number: Option<String>, - pub type_of_work: Option<String>, - pub notes: Vec<String>, - pub number_of_volumes: Option<String>, - pub original_publication: Option<String>, - pub publisher: Option<String>, - pub year: Option<String>, - pub reviewed_item: Option<String>, - pub research_notes: Option<String>, - pub reprint_edition: Option<String>, - pub section: Option<String>, - pub isbn_or_issn: Option<String>, - pub start_page: Option<String>, - pub short_title: Option<String>, - pub primary_title: Option<String>, - pub secondardy_title: Option<String>, - pub tertiary_title: Option<String>, - pub translated_authors: Vec<String>, - pub title: Option<String>, - pub translated_title: Option<String>, - pub url: Option<String>, - pub volume: Option<String>, - pub access_date: Option<String>, -} - -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct RisLibrary { - pub references: Vec<RisReference>, -} - -impl RisLibrary { - pub fn parse<'a, I: Iterator<Item = &'a str>>(lines: I) -> Self { - let mut library = Self { - references: Vec::new(), - }; - - let mut reference: RisReference = RisReference::default(); - for line in lines { - let line = line.trim(); - if line == "" { - continue; - } - - if line == "ER -" { - library.references.push(reference); - reference = RisReference::default(); - continue; - } - - let chars: Vec<_> = line.chars().collect(); - if chars.len() < 7 || chars[4] != '-' { - continue; - } - - let key: String = (&chars[..2]).iter().collect(); - let value: String = (&chars[6..]).iter().collect(); - match key.to_uppercase().as_str() { - "TY" => reference.ty = RisType::parse(&value), - "A2" => reference.editors.push(value), - "A3" => reference.tertiary_authors.push(value), - "A4" => reference.subsidiary_authors.push(value), - "AB" => reference.abstrct = Some(value), - "AD" => reference.author_address = Some(value), - "AN" => reference.accession_number = Some(value), - "AU" => reference.authors.push(value), - "BT" => reference.book_or_conference = Some(value), - "C1" => reference.custom1 = Some(value), - "C2" => reference.custom2 = Some(value), - "C3" => reference.custom3 = Some(value), - "C4" => reference.custom4 = Some(value), - "C5" => reference.custom5 = Some(value), - "C6" => reference.custom6 = Some(value), - "C7" => reference.custom7 = Some(value), - "C8" => reference.custom8 = Some(value), - "CA" => reference.caption = Some(value), - "CN" => reference.call_number = Some(value), - "CY" => reference.place = Some(value), - "DA" => reference.date = Some(value), - "DB" => reference.name_of_database = Some(value), - "DO" => reference.doi = Some(value), - "DP" => reference.database_provider = Some(value), - "ED" => reference.editors.push(value), - "EP" => reference.end_page = Some(value), - "ET" => reference.edition = Some(value), - "ID" => reference.id = Some(value), - "IS" => reference.issue = Some(value), - "JO" => reference.journal = Some(value), - "J2" => reference.journal = Some(value), - "KW" => reference.keywords.push(value), - "L1" => reference.file_attachments.push(value), - "L4" => reference.figure = Some(value), - "LA" => reference.language = Some(value), - "LB" => reference.label = Some(value), - "M1" => reference.number = Some(value), - "M3" => reference.type_of_work = Some(value), - "N1" => reference.notes.push(value), - "N2" => reference.abstrct = Some(value), - "NV" => reference.number_of_volumes = Some(value), - "OP" => reference.original_publication = Some(value), - "PB" => reference.publisher = Some(value), - "PY" => reference.year = Some(value), - "RI" => reference.reviewed_item = Some(value), - "RN" => reference.research_notes = Some(value), - "RP" => reference.reprint_edition = Some(value), - "SE" => reference.section = Some(value), - "SN" => reference.isbn_or_issn = Some(value), - "SP" => reference.start_page = Some(value), - "ST" => reference.short_title = Some(value), - "T1" => reference.primary_title = Some(value), - "T2" => reference.secondardy_title = Some(value), - "T3" => reference.tertiary_title = Some(value), - "TA" => reference.translated_authors.push(value), - "TI" => reference.title = Some(value), - "TT" => reference.translated_title = Some(value), - "U1" => reference.type_of_work = Some(value), - "UR" => reference.url = Some(value), - "VL" => reference.volume = Some(value), - "Y2" => reference.access_date = Some(value), - _ => (), - } - } - library - } -} - -impl Into<Reference> for RisReference { - fn into(self) -> Reference { - let csl_type = self.ty.expect("RIS type is missing").csl(); - let mut date: FnvHashMap<DateVariable, DateOrRange> = FnvHashMap::default(); - let mut name: FnvHashMap<NameVariable, Vec<Name>> = FnvHashMap::default(); - let mut number: FnvHashMap<NumberVariable, NumberLike> = FnvHashMap::default(); - let mut ordinary: FnvHashMap<Variable, String> = FnvHashMap::default(); - - if let Some(access_date) = self.access_date { - date.insert(DateVariable::Accessed, parse_date_or_range(access_date)); - } - - name.insert(NameVariable::Author, parse_authors(self.authors)); - name.insert(NameVariable::Editor, parse_authors(self.editors)); - - if let Some(container_title) = self - .journal - .or(self.name_of_database) - .or(self.book_or_conference) - { - ordinary.insert(Variable::ContainerTitle, container_title.clone()); - ordinary.insert(Variable::CollectionTitle, container_title); - } - - if let Some(value) = self.date.or(self.year) { - let value = parse_date_or_range(value); - date.insert(DateVariable::Issued, value.clone()); - date.insert(DateVariable::EventDate, value); - } - - if let Some(url) = self.url { - ordinary.insert(Variable::URL, url); - } - - let notes = self.notes; - ordinary.insert( - Variable::Note, - self.research_notes.unwrap_or_else(|| notes.join("\n")), - ); - - if let Some(issue) = self.issue { - number.insert(NumberVariable::Issue, parse_number(issue)); - } - - if let Some(num) = self.number { - number.insert(NumberVariable::Number, parse_number(num)); - } - - if let Some(place) = self.place { - ordinary.insert(Variable::EventPlace, place.clone()); - ordinary.insert(Variable::PublisherPlace, place); - } - - if let Some(abstrct) = self.abstrct { - ordinary.insert(Variable::Abstract, abstrct); - } - - if let Some(call_number) = self.call_number { - ordinary.insert(Variable::CallNumber, call_number); - } - - if let Some(doi) = self.doi { - ordinary.insert(Variable::DOI, doi); - } - - if let Some(edition) = self.edition { - number.insert(NumberVariable::Edition, parse_number(edition)); - } - - if let Some(isbn_or_issn) = self.isbn_or_issn { - ordinary.insert(Variable::ISBN, isbn_or_issn.clone()); - ordinary.insert(Variable::ISSN, isbn_or_issn); - } - - ordinary.insert(Variable::Keyword, self.keywords.join(", ")); - - if let Some(number_of_volumes) = self.number_of_volumes { - number.insert( - NumberVariable::NumberOfVolumes, - parse_number(number_of_volumes), - ); - } - - if let Some(original_publication) = self.original_publication { - ordinary.insert(Variable::OriginalTitle, original_publication); - } - - match (self.start_page, self.end_page) { - (Some(start_page), Some(end_page)) => { - number.insert( - NumberVariable::Page, - NumberLike::Str(format!("{}-{}", start_page, end_page).into()), - ); - } - (Some(page), None) | (None, Some(page)) => { - number.insert(NumberVariable::Page, parse_number(page)); - } - (None, None) => {} - } - - if let Some(publisher) = self.publisher { - ordinary.insert(Variable::Publisher, publisher); - } - - if let Some(reviewed_item) = self.reviewed_item { - ordinary.insert(Variable::ReviewedTitle, reviewed_item); - } - - if let Some(section) = self.section { - ordinary.insert(Variable::Section, section); - } - - if let Some(short_title) = self.short_title { - ordinary.insert(Variable::TitleShort, short_title); - } - - if let Some(title) = self.title { - ordinary.insert(Variable::Title, title); - } - - if let Some(volume) = self.volume { - number.insert(NumberVariable::Volume, parse_number(volume)); - } - - Reference { - id: Atom::from(self.id.or(self.label).expect("RIS id is missing").as_str()), - csl_type, - language: None, - name, - number, - date, - ordinary, - } - } -} - -fn parse_number(value: String) -> NumberLike { - match value.parse() { - Ok(value) => NumberLike::Num(value), - Err(_) => NumberLike::Str(value.into()), - } -} - -fn parse_authors(authors: Vec<String>) -> Vec<Name> { - authors - .into_iter() - .map(|author| Name::Literal { - is_latin_cyrillic: is_latin_cyrillic(&author), - literal: author.into(), - }) - .collect() -} - -fn parse_date_or_range(value: String) -> DateOrRange { - parse_date(&value) - .map(DateOrRange::Single) - .unwrap_or_else(|| DateOrRange::Literal { - literal: value.into(), - circa: false, - }) -} - -fn parse_date(value: &str) -> Option<Date> { - let mut parts = value.split('/'); - let year: i32 = parts.next()?.parse().ok()?; - match parts.next().and_then(|p| p.parse().ok()) { - Some(month) => match parts.next().and_then(|p| p.parse().ok()) { - Some(day) => Some(Date::new(year, month, day)), - None => Some(Date::new(year, month, 0)), - }, - None => Some(Date::new(year, 0, 0)), - } -} diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__article_aksin_2006.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_aksin_2006.snap new file mode 100644 index 0000000000..4598d47ed9 --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_aksin_2006.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 64 +expression: "render_entry(r#\"\n@string{jomch = {J.~Organomet. Chem.}}\n\n@article{aksin,\n author = {Aks{\\i}n, {\\\"O}zge and T{\\\"u}rkmen, Hayati and Artok, Levent\n and {\\c{C}}etinkaya, Bekir and Ni, Chaoying and\n B{\\\"u}y{\\\"u}kg{\\\"u}ng{\\\"o}r, Orhan and {\\\"O}zkal, Erhan},\n title = {Effect of immobilization on catalytic characteristics of\n saturated {Pd-N}-heterocyclic carbenes in {Mizoroki-Heck}\n reactions},\n journaltitle = jomch,\n date = 2006,\n volume = 691,\n number = 13,\n pages = {3027-3036},\n indextitle = {Effect of immobilization on catalytic characteristics},\n}\"#)" +--- +O. Aksın, H. Türkmen, L. Artok, B. Çetinkaya, C. Ni, O. Büyükgüngör, E. Özkal: "Effect of immobilization on catalytic characteristics of saturated Pd-N-heterocyclic carbenes in Mizoroki-Heck reactions". *J. Organomet. Chem.* 691.13 (2006): 3027-3036. diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__article_betram_1996.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_betram_1996.snap new file mode 100644 index 0000000000..dd857419a5 --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_betram_1996.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 87 +expression: "render_entry(r#\"\n@string{jams = {J.~Amer. Math. Soc.}}\n\n@article{bertram,\n author = {Bertram, Aaron and Wentworth, Richard},\n title = {Gromov invariants for holomorphic maps on {Riemann} surfaces},\n journaltitle = jams,\n date = 1996,\n volume = 9,\n number = 2,\n pages = {529-571},\n langid = {english},\n langidopts = {variant=american},\n shorttitle = {Gromov invariants},\n annotation = {An \\texttt{article} entry with a \\texttt{volume} and a\n \\texttt{number} field},\n}\"#)" +--- +A. Bertram, R. Wentworth: "Gromov invariants for holomorphic maps on Riemann surfaces". *J. Amer. Math. Soc.* 9.2 (1996): 529-571. diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__article_blom_2021.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_blom_2021.snap new file mode 100644 index 0000000000..851b874fff --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_blom_2021.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 138 +expression: "render_entry(r#\"\n@article{DBLP:journals/corr/abs-2107-11903,\n author = {Michelle L. Blom and\n Jurlind Budurushi and\n Ronald L. Rivest and\n Philip B. Stark and\n Peter J. Stuckey and\n Vanessa Teague and\n Damjan Vukcevic},\n title = {Assertion-based Approaches to Auditing Complex Elections, with application\n to party-list proportional elections},\n journal = {CoRR},\n volume = {abs/2107.11903},\n year = {2021},\n url = {https://arxiv.org/abs/2107.11903},\n eprinttype = {arXiv},\n eprint = {2107.11903},\n timestamp = {Thu, 29 Jul 2021 16:14:15 +0200},\n biburl = {https://dblp.org/rec/journals/corr/abs-2107-11903.bib},\n bibsource = {dblp computer science bibliography, https://dblp.org}\n}\"#)" +--- +M. Blom, J. Budurushi, R. Rivest, P. Stark, P. Stuckey, V. Teague, D. Vukcevic: "Assertion-based Approaches to Auditing Complex Elections, with application to party-list proportional elections". *CoRR* abs/2107.11903 (2021): arXiv: [2107.11903](https://arxiv.org/abs/2107.11903). URL: [https://arxiv.org/abs/2107.11903](https://arxiv.org/abs/2107.11903). diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__article_jain_1999.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_jain_1999.snap new file mode 100644 index 0000000000..2eefc3a871 --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_jain_1999.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 39 +expression: "render_entry(r#\"\n@article{10.1145/331499.331504,\n author = {Jain, A. K. and Murty, M. N. and Flynn, P. J.},\n title = {Data Clustering: A Review},\n year = {1999},\n issue_date = {Sept. 1999},\n publisher = {Association for Computing Machinery},\n address = {New York, NY, USA},\n volume = {31},\n number = {3},\n issn = {0360-0300},\n url = {https://doi.org/10.1145/331499.331504},\n doi = {10.1145/331499.331504},\n journal = {ACM Comput. Surv.},\n month = {sep},\n pages = {264-323},\n numpages = {60},\n keywords = {incremental clustering, clustering applications, exploratory data analysis, cluster analysis, similarity indices, unsupervised learning}\n}\"#)" +--- +A. Jain, M. Murty, P. Flynn: "Data Clustering: A Review". *ACM Comput. Surv.* 31.3 (Sep. 1999): 264-323. ISSN: 0360-0300. DOI: [10.1145/331499.331504](https://doi.org/10.1145/331499.331504). URL: [https://doi.org/10.1145/331499.331504](https://doi.org/10.1145/331499.331504). diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__article_kastenholz_2006.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_kastenholz_2006.snap new file mode 100644 index 0000000000..d1cae5db36 --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_kastenholz_2006.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 110 +expression: "render_entry(r#\"\n@string{jchph = {J.~Chem. Phys.}}\n\n@article{kastenholz,\n author = {Kastenholz, M. A. and H{\\\"u}nenberger, Philippe H.},\n title = {Computation of methodology\\hyphen independent ionic solvation\n free energies from molecular simulations},\n journaltitle = jchph,\n date = 2006,\n subtitle = {{I}. {The} electrostatic potential in molecular liquids},\n volume = 124,\n eid = 124106,\n doi = {10.1063/1.2172593},\n langid = {english},\n langidopts = {variant=american},\n indextitle = {Computation of ionic solvation free energies},\n annotation = {An \\texttt{article} entry with an \\texttt{eid} and a\n \\texttt{doi} field. Note that the \\textsc{doi} is transformed\n into a clickable link if \\texttt{hyperref} support has been\n enabled},\n}\n \"#)" +--- +M. Kastenholz, P. Hünenberger: "Computation of methodology- independent ionic solvation free energies from molecular simulations. I. The electrostatic potential in molecular liquids". *J. Chem. Phys.* 124, 124106 (2006): DOI: [10.1063/1.2172593](https://doi.org/10.1063/1.2172593). diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__article_rivest_1978.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_rivest_1978.snap new file mode 100644 index 0000000000..3dfcb2967c --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__article_rivest_1978.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 15 +expression: "render_entry(r#\"\n@article{10.1145/359340.359342,\n author = {Rivest, R. L. and Shamir, A. and Adleman, L.},\n title = {A Method for Obtaining Digital Signatures and Public-Key Cryptosystems},\n year = {1978},\n issue_date = {Feb. 1978},\n publisher = {Association for Computing Machinery},\n address = {New York, NY, USA},\n volume = {21},\n number = {2},\n issn = {0001-0782},\n url = {https://doi.org/10.1145/359340.359342},\n doi = {10.1145/359340.359342},\n journal = {Commun. ACM},\n month = {feb},\n pages = {120-126},\n numpages = {7},\n}\"#)" +--- +R. Rivest, A. Shamir, L. Adleman: "A Method for Obtaining Digital Signatures and Public-Key Cryptosystems". *Commun. ACM* 21.2 (Feb. 1978): 120-126. ISSN: 0001-0782. DOI: [10.1145/359340.359342](https://doi.org/10.1145/359340.359342). URL: [https://doi.org/10.1145/359340.359342](https://doi.org/10.1145/359340.359342). diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__book_aho_2006.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__book_aho_2006.snap new file mode 100644 index 0000000000..34c0aeda3b --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__book_aho_2006.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 165 +expression: "render_entry(r#\"\n@book{10.5555/1177220,\n author = {Aho, Alfred V. and Lam, Monica S. and Sethi, Ravi and Ullman, Jeffrey D.},\n title = {Compilers: Principles, Techniques, and Tools (2nd Edition)},\n year = {2006},\n isbn = {0321486811},\n publisher = {Addison-Wesley Longman Publishing Co., Inc.},\n address = {USA}\n}\"#)" +--- +A. Aho, M. Lam, R. Sethi, J. Ullman: "Compilers: Principles, Techniques, and Tools (2nd Edition)". Addison-Wesley Longman Publishing Co., Inc., 2006. ISBN: 0321486811. diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__book_averroes_1998.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__book_averroes_1998.snap new file mode 100644 index 0000000000..32b8a6ca5f --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__book_averroes_1998.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 180 +expression: "render_entry(r#\"\n@book{averroes/bland,\n author = {Averroes},\n title = {The Epistle on the Possibility of Conjunction with the Active\n Intellect by {Ibn Rushd} with the Commentary of {Moses Narboni}},\n date = 1982,\n editor = {Bland, Kalman P.},\n translator = {Bland, Kalman P.},\n series = {Moreshet: Studies in {Jewish} History, Literature and Thought},\n number = 7,\n publisher = {Jewish Theological Seminary of America},\n location = {New York},\n keywords = {primary},\n langid = {english},\n langidopts = {variant=american},\n indextitle = {Epistle on the Possibility of Conjunction, The},\n shorttitle = {Possibility of Conjunction},\n annotation = {A \\texttt{book} entry with a \\texttt{series} and a\n \\texttt{number}. Note the concatenation of the \\texttt{editor}\n and \\texttt{translator} fields as well as the\n \\texttt{indextitle} field},\n}\"#)" +--- +"The Epistle on the Possibility of Conjunction with the Active Intellect by Ibn Rushd with the Commentary of Moses Narboni". Ed. by K. Bland. Trans. by K. Bland. Moreshet: Studies in Jewish History, Literature and Thought 7. New York: Jewish Theological Seminary of America, 1982. diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__book_knuth_1984.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__book_knuth_1984.snap new file mode 100644 index 0000000000..0276be0589 --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__book_knuth_1984.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 208 +expression: "render_entry(r#\"\n@book{knuth:ct:a,\n author = {Knuth, Donald E.},\n title = {The {\\TeX book}},\n date = 1984,\n maintitle = {Computers \\& Typesetting},\n volume = {A},\n publisher = {Addison-Wesley},\n location = {Reading, Mass.},\n langid = {english},\n langidopts = {variant=american},\n sorttitle = {Computers & Typesetting A},\n indexsorttitle= {The TeXbook},\n indextitle = {\\protect\\TeX book, The},\n shorttitle = {\\TeX book},\n annotation = {The first volume of a five-volume book. Note the\n \\texttt{sorttitle} field. We want this\n volume to be listed after the entry referring to the entire\n five-volume set. Also note the \\texttt{indextitle} and\n \\texttt{indexsorttitle} fields. Indexing packages that don't\n generate robust index entries require some control sequences\n to be protected from expansion},\n}\n \"#)" +--- +D. Knuth: "The TeX book". *Computers & Typesetting*. Vol. A. Reading, Mass.: Addison-Wesley, 1984. diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__collection_matuz_1990.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__collection_matuz_1990.snap new file mode 100644 index 0000000000..9ea78c80cf --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__collection_matuz_1990.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 313 +expression: "render_entry(r#\"\n@collection{matuz:doody,\n editor = {Matuz, Roger},\n title = {Contemporary Literary Criticism},\n year = 1990,\n volume = 61,\n publisher = {Gale},\n location = {Detroit},\n pages = {204-208},\n langid = {english},\n langidopts = {variant=american},\n annotation = {A \\texttt{collection} entry providing the excerpt information\n for the \\texttt{doody} entry. Note the format of the\n \\texttt{pages} field},\n}\"#)" +--- +"Contemporary Literary Criticism". Ed. by R. Matuz. Vol. 61. Detroit: Gale, 1990, 204-208. diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_combi_2004.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_combi_2004.snap new file mode 100644 index 0000000000..9baef38be7 --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_combi_2004.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 290 +expression: "render_entry(r#\"\n@inproceedings{10.1145/967900.968040,\n author = {Combi, Carlo and Pozzi, Giuseppe},\n title = {Architectures for a Temporal Workflow Management System},\n year = {2004},\n isbn = {1581138121},\n publisher = {Association for Computing Machinery},\n address = {New York, NY, USA},\n url = {https://doi.org/10.1145/967900.968040},\n doi = {10.1145/967900.968040},\n booktitle = {Proceedings of the 2004 ACM Symposium on Applied Computing},\n pages = {659-666},\n numpages = {8},\n keywords = {active DBMS, temporal DBMS, workflow management system - WfMS, temporal workflow management system},\n location = {Nicosia, Cyprus},\n series = {SAC '04}\n}\"#)" +--- +C. Combi, G. Pozzi: "Architectures for a Temporal Workflow Management System". *Proceedings of the 2004 ACM Symposium on Applied Computing*. SAC '04. Nicosia, Cyprus: Association for Computing Machinery, 2004, 659-666. ISBN: 1581138121. DOI: [10.1145/967900.968040](https://doi.org/10.1145/967900.968040). URL: [https://doi.org/10.1145/967900.968040](https://doi.org/10.1145/967900.968040). diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_erwin_2007.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_erwin_2007.snap new file mode 100644 index 0000000000..df9ab6569f --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__inproceedings_erwin_2007.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 269 +expression: "render_entry(r#\"\n@inproceedings{10.5555/1386993.1386994,\n author = {Erwin, Alva and Gopalan, Raj P. and Achuthan, N. R.},\n title = {A Bottom-up Projection Based Algorithm for Mining High Utility Itemsets},\n year = {2007},\n isbn = {9781920682651},\n publisher = {Australian Computer Society, Inc.},\n address = {AUS},\n booktitle = {Proceedings of the 2nd International Workshop on Integrating Artificial Intelligence and Data Mining - Volume 84},\n pages = {3-11},\n numpages = {9},\n keywords = {pattern growth, high utility itemset mining},\n location = {Gold Coast, Australia},\n series = {AIDM '07}\n}\"#)" +--- +A. Erwin, R. Gopalan, N. Achuthan: "A Bottom-up Projection Based Algorithm for Mining High Utility Itemsets". *Proceedings of the 2nd International Workshop on Integrating Artificial Intelligence and Data Mining - Volume 84*. AIDM '07. Gold Coast, Australia: Australian Computer Society, Inc., 2007, 3-11. ISBN: 9781920682651. diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__mvbook_nietzsche_1988.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__mvbook_nietzsche_1988.snap new file mode 100644 index 0000000000..5162b3114b --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__mvbook_nietzsche_1988.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 238 +expression: "render_entry(r#\"\n@string{dtv = {Deutscher Taschenbuch-Verlag}}\n\n@mvbook{nietzsche:ksa,\n author = {Nietzsche, Friedrich},\n title = {S{\\\"a}mtliche Werke},\n date = 1988,\n editor = {Colli, Giorgio and Montinari, Mazzino},\n edition = 2,\n volumes = 15,\n publisher = dtv # { and Walter de Gruyter},\n location = {M{\\\"u}nchen and Berlin and New York},\n langid = {german},\n sorttitle = {Werke-00-000},\n indexsorttitle= {Samtliche Werke},\n subtitle = {Kritische Studienausgabe},\n annotation = {The critical edition of Nietzsche's works. This is a\n \\texttt{mvbook} entry referring to a 15-volume work as a\n whole. Note the \\texttt{volumes} field and the format of the\n \\texttt{publisher} and \\texttt{location} fields in the\n database file. Also note the \\texttt{sorttitle} and\n field which is used to fine-tune the\n sorting order of the bibliography. We want this item listed\n first in the bibliography},\n}\"#)" +--- +F. Nietzsche: "Sämtliche Werke. Kritische Studienausgabe". Ed. by G. Colli, M. Montinari. 2nd. München and Berlin and New York: Deutscher Taschenbuch-Verlag and Walter de Gruyter, 1988. diff --git a/support/texlab/src/citation/snapshots/texlab__citation__tests__patent_almendro_1998.snap b/support/texlab/src/citation/snapshots/texlab__citation__tests__patent_almendro_1998.snap new file mode 100644 index 0000000000..6f8c17f059 --- /dev/null +++ b/support/texlab/src/citation/snapshots/texlab__citation__tests__patent_almendro_1998.snap @@ -0,0 +1,6 @@ +--- +source: src/citation/tests.rs +assertion_line: 334 +expression: "render_entry(r#\"\n@patent{almendro,\n author = {Almendro, Jos{\\'e} L. and Mart{\\'i}n, Jacinto and S{\\'a}nchez,\n Alberto and Nozal, Fernando},\n title = {Elektromagnetisches Signalhorn},\n number = {EU-29702195U},\n date = 1998,\n location = {countryfr and countryuk and countryde},\n langid = {german},\n annotation = {This is a \\texttt{patent} entry with a \\texttt{location}\n field. The number is given in the \\texttt{number} field. Note\n the format of the \\texttt{location} field in the database\n file. Compare \\texttt{laufenberg}, \\texttt{sorace}, and\n \\texttt{kowalik}},\n}\"#)" +--- +J. Almendro, J. Martín, A. Sánchez, F. Nozal: "Elektromagnetisches Signalhorn". EU-29702195U (France and United Kingdom and Germany). 1998. diff --git a/support/texlab/src/citation/tests.rs b/support/texlab/src/citation/tests.rs new file mode 100644 index 0000000000..16aefea11d --- /dev/null +++ b/support/texlab/src/citation/tests.rs @@ -0,0 +1,351 @@ +use insta::assert_snapshot; +use rowan::ast::AstNode; + +use crate::syntax::bibtex; + +fn render_entry(input: &str) -> String { + let green = bibtex::parse(input); + let root = bibtex::Root::cast(bibtex::SyntaxNode::new_root(green)).unwrap(); + let entry = root.entries().next().unwrap(); + super::render(&entry).unwrap() +} + +#[test] +fn article_rivest_1978() { + assert_snapshot!(render_entry( + r#" +@article{10.1145/359340.359342, + author = {Rivest, R. L. and Shamir, A. and Adleman, L.}, + title = {A Method for Obtaining Digital Signatures and Public-Key Cryptosystems}, + year = {1978}, + issue_date = {Feb. 1978}, + publisher = {Association for Computing Machinery}, + address = {New York, NY, USA}, + volume = {21}, + number = {2}, + issn = {0001-0782}, + url = {https://doi.org/10.1145/359340.359342}, + doi = {10.1145/359340.359342}, + journal = {Commun. ACM}, + month = {feb}, + pages = {120-126}, + numpages = {7}, +}"# + )); +} + +#[test] +fn article_jain_1999() { + assert_snapshot!(render_entry( + r#" +@article{10.1145/331499.331504, + author = {Jain, A. K. and Murty, M. N. and Flynn, P. J.}, + title = {Data Clustering: A Review}, + year = {1999}, + issue_date = {Sept. 1999}, + publisher = {Association for Computing Machinery}, + address = {New York, NY, USA}, + volume = {31}, + number = {3}, + issn = {0360-0300}, + url = {https://doi.org/10.1145/331499.331504}, + doi = {10.1145/331499.331504}, + journal = {ACM Comput. Surv.}, + month = {sep}, + pages = {264-323}, + numpages = {60}, + keywords = {incremental clustering, clustering applications, exploratory data analysis, cluster analysis, similarity indices, unsupervised learning} +}"# + )); +} + +#[test] +fn article_aksin_2006() { + assert_snapshot!(render_entry( + r#" +@string{jomch = {J.~Organomet. Chem.}} + +@article{aksin, + author = {Aks{\i}n, {\"O}zge and T{\"u}rkmen, Hayati and Artok, Levent + and {\c{C}}etinkaya, Bekir and Ni, Chaoying and + B{\"u}y{\"u}kg{\"u}ng{\"o}r, Orhan and {\"O}zkal, Erhan}, + title = {Effect of immobilization on catalytic characteristics of + saturated {Pd-N}-heterocyclic carbenes in {Mizoroki-Heck} + reactions}, + journaltitle = jomch, + date = 2006, + volume = 691, + number = 13, + pages = {3027-3036}, + indextitle = {Effect of immobilization on catalytic characteristics}, +}"# + )); +} + +#[test] +fn article_betram_1996() { + assert_snapshot!(render_entry( + r#" +@string{jams = {J.~Amer. Math. Soc.}} + +@article{bertram, + author = {Bertram, Aaron and Wentworth, Richard}, + title = {Gromov invariants for holomorphic maps on {Riemann} surfaces}, + journaltitle = jams, + date = 1996, + volume = 9, + number = 2, + pages = {529-571}, + langid = {english}, + langidopts = {variant=american}, + shorttitle = {Gromov invariants}, + annotation = {An \texttt{article} entry with a \texttt{volume} and a + \texttt{number} field}, +}"# + )); +} + +#[test] +fn article_kastenholz_2006() { + assert_snapshot!(render_entry( + r#" +@string{jchph = {J.~Chem. Phys.}} + +@article{kastenholz, + author = {Kastenholz, M. A. and H{\"u}nenberger, Philippe H.}, + title = {Computation of methodology\hyphen independent ionic solvation + free energies from molecular simulations}, + journaltitle = jchph, + date = 2006, + subtitle = {{I}. {The} electrostatic potential in molecular liquids}, + volume = 124, + eid = 124106, + doi = {10.1063/1.2172593}, + langid = {english}, + langidopts = {variant=american}, + indextitle = {Computation of ionic solvation free energies}, + annotation = {An \texttt{article} entry with an \texttt{eid} and a + \texttt{doi} field. Note that the \textsc{doi} is transformed + into a clickable link if \texttt{hyperref} support has been + enabled}, +} + "# + )); +} + +#[test] +fn article_blom_2021() { + assert_snapshot!(render_entry( + r#" +@article{DBLP:journals/corr/abs-2107-11903, + author = {Michelle L. Blom and + Jurlind Budurushi and + Ronald L. Rivest and + Philip B. Stark and + Peter J. Stuckey and + Vanessa Teague and + Damjan Vukcevic}, + title = {Assertion-based Approaches to Auditing Complex Elections, with application + to party-list proportional elections}, + journal = {CoRR}, + volume = {abs/2107.11903}, + year = {2021}, + url = {https://arxiv.org/abs/2107.11903}, + eprinttype = {arXiv}, + eprint = {2107.11903}, + timestamp = {Thu, 29 Jul 2021 16:14:15 +0200}, + biburl = {https://dblp.org/rec/journals/corr/abs-2107-11903.bib}, + bibsource = {dblp computer science bibliography, https://dblp.org} +}"# + )); +} + +#[test] +fn book_aho_2006() { + assert_snapshot!(render_entry( + r#" +@book{10.5555/1177220, + author = {Aho, Alfred V. and Lam, Monica S. and Sethi, Ravi and Ullman, Jeffrey D.}, + title = {Compilers: Principles, Techniques, and Tools (2nd Edition)}, + year = {2006}, + isbn = {0321486811}, + publisher = {Addison-Wesley Longman Publishing Co., Inc.}, + address = {USA} +}"# + )); +} + +#[test] +fn book_averroes_1998() { + assert_snapshot!(render_entry( + r#" +@book{averroes/bland, + author = {Averroes}, + title = {The Epistle on the Possibility of Conjunction with the Active + Intellect by {Ibn Rushd} with the Commentary of {Moses Narboni}}, + date = 1982, + editor = {Bland, Kalman P.}, + translator = {Bland, Kalman P.}, + series = {Moreshet: Studies in {Jewish} History, Literature and Thought}, + number = 7, + publisher = {Jewish Theological Seminary of America}, + location = {New York}, + keywords = {primary}, + langid = {english}, + langidopts = {variant=american}, + indextitle = {Epistle on the Possibility of Conjunction, The}, + shorttitle = {Possibility of Conjunction}, + annotation = {A \texttt{book} entry with a \texttt{series} and a + \texttt{number}. Note the concatenation of the \texttt{editor} + and \texttt{translator} fields as well as the + \texttt{indextitle} field}, +}"# + )); +} + +#[test] +fn book_knuth_1984() { + assert_snapshot!(render_entry( + r#" +@book{knuth:ct:a, + author = {Knuth, Donald E.}, + title = {The {\TeX book}}, + date = 1984, + maintitle = {Computers \& Typesetting}, + volume = {A}, + publisher = {Addison-Wesley}, + location = {Reading, Mass.}, + langid = {english}, + langidopts = {variant=american}, + sorttitle = {Computers & Typesetting A}, + indexsorttitle= {The TeXbook}, + indextitle = {\protect\TeX book, The}, + shorttitle = {\TeX book}, + annotation = {The first volume of a five-volume book. Note the + \texttt{sorttitle} field. We want this + volume to be listed after the entry referring to the entire + five-volume set. Also note the \texttt{indextitle} and + \texttt{indexsorttitle} fields. Indexing packages that don't + generate robust index entries require some control sequences + to be protected from expansion}, +} + "# + )); +} + +#[test] +fn mvbook_nietzsche_1988() { + assert_snapshot!(render_entry( + r#" +@string{dtv = {Deutscher Taschenbuch-Verlag}} + +@mvbook{nietzsche:ksa, + author = {Nietzsche, Friedrich}, + title = {S{\"a}mtliche Werke}, + date = 1988, + editor = {Colli, Giorgio and Montinari, Mazzino}, + edition = 2, + volumes = 15, + publisher = dtv # { and Walter de Gruyter}, + location = {M{\"u}nchen and Berlin and New York}, + langid = {german}, + sorttitle = {Werke-00-000}, + indexsorttitle= {Samtliche Werke}, + subtitle = {Kritische Studienausgabe}, + annotation = {The critical edition of Nietzsche's works. This is a + \texttt{mvbook} entry referring to a 15-volume work as a + whole. Note the \texttt{volumes} field and the format of the + \texttt{publisher} and \texttt{location} fields in the + database file. Also note the \texttt{sorttitle} and + field which is used to fine-tune the + sorting order of the bibliography. We want this item listed + first in the bibliography}, +}"# + )); +} + +#[test] +fn inproceedings_erwin_2007() { + assert_snapshot!(render_entry( + r#" +@inproceedings{10.5555/1386993.1386994, + author = {Erwin, Alva and Gopalan, Raj P. and Achuthan, N. R.}, + title = {A Bottom-up Projection Based Algorithm for Mining High Utility Itemsets}, + year = {2007}, + isbn = {9781920682651}, + publisher = {Australian Computer Society, Inc.}, + address = {AUS}, + booktitle = {Proceedings of the 2nd International Workshop on Integrating Artificial Intelligence and Data Mining - Volume 84}, + pages = {3-11}, + numpages = {9}, + keywords = {pattern growth, high utility itemset mining}, + location = {Gold Coast, Australia}, + series = {AIDM '07} +}"# + )); +} + +#[test] +fn inproceedings_combi_2004() { + assert_snapshot!(render_entry( + r#" +@inproceedings{10.1145/967900.968040, + author = {Combi, Carlo and Pozzi, Giuseppe}, + title = {Architectures for a Temporal Workflow Management System}, + year = {2004}, + isbn = {1581138121}, + publisher = {Association for Computing Machinery}, + address = {New York, NY, USA}, + url = {https://doi.org/10.1145/967900.968040}, + doi = {10.1145/967900.968040}, + booktitle = {Proceedings of the 2004 ACM Symposium on Applied Computing}, + pages = {659-666}, + numpages = {8}, + keywords = {active DBMS, temporal DBMS, workflow management system - WfMS, temporal workflow management system}, + location = {Nicosia, Cyprus}, + series = {SAC '04} +}"# + )); +} + +#[test] +fn collection_matuz_1990() { + assert_snapshot!(render_entry( + r#" +@collection{matuz:doody, + editor = {Matuz, Roger}, + title = {Contemporary Literary Criticism}, + year = 1990, + volume = 61, + publisher = {Gale}, + location = {Detroit}, + pages = {204-208}, + langid = {english}, + langidopts = {variant=american}, + annotation = {A \texttt{collection} entry providing the excerpt information + for the \texttt{doody} entry. Note the format of the + \texttt{pages} field}, +}"# + )); +} + +#[test] +fn patent_almendro_1998() { + assert_snapshot!(render_entry( + r#" +@patent{almendro, + author = {Almendro, Jos{\'e} L. and Mart{\'i}n, Jacinto and S{\'a}nchez, + Alberto and Nozal, Fernando}, + title = {Elektromagnetisches Signalhorn}, + number = {EU-29702195U}, + date = 1998, + location = {countryfr and countryuk and countryde}, + langid = {german}, + annotation = {This is a \texttt{patent} entry with a \texttt{location} + field. The number is given in the \texttt{number} field. Note + the format of the \texttt{location} field in the database + file. Compare \texttt{laufenberg}, \texttt{sorace}, and + \texttt{kowalik}}, +}"# + )); +} |