diff --git a/aastex/_aastex.py b/aastex/_aastex.py index be07d45..9dd9add 100644 --- a/aastex/_aastex.py +++ b/aastex/_aastex.py @@ -140,11 +140,39 @@ def affiliations(self) -> list[Affiliation]: @dataclasses.dataclass class Acronym(pylatex.base_classes.LatexObject): + r""" + An acronym which is expanded on first use and abbreviated thereafter. + + Defining an acronym also defines LaTeX commands for using it: + ``\NASA`` expands it on first use and abbreviates it afterwards, and + ``\NASACapital`` does the same with the first letter capitalized, for a + sentence which begins with the acronym. + The capitalized form matters for an instrument whose name reads as + ``"the Multi-slit Solar Explorer"``, since a sentence should open with + "The" rather than "the". + ``\NASAs`` and ``\NASACapitals`` are the plural forms, and ``\NASAShort`` + is the abbreviation whether or not it has been used before. + """ + acronym: str + """The abbreviated form of this acronym.""" + name_full: str + """ + The expanded form of this acronym. + + Include a leading article, as in ``"the Multi-slit Solar Explorer"``, + for a name which needs one. + """ + name_short: None | str = None + """The abbreviation to display, if it differs from :attr:`acronym`.""" + plural: bool = False + """Whether to define the plural forms of this acronym.""" + short: bool = False + """Whether to define a command which always gives the abbreviation.""" def __post_init__(self): self.packages.append(pylatex.Package("acronym")) @@ -187,6 +215,21 @@ def dumps(self): pylatex.NoEscape(rf"\acs{{{self.acronym}}}"), ], ).dumps() + command += pylatex.Command( + command="newcommand", + arguments=[ + pylatex.NoEscape(rf"\{self.acronym}Capital"), + pylatex.NoEscape(rf"\Ac{{{self.acronym}}}"), + ], + ).dumps() + if self.plural: + command += pylatex.Command( + command="newcommand", + arguments=[ + pylatex.NoEscape(rf"\{self.acronym}Capitals"), + pylatex.NoEscape(rf"\Acp{{{self.acronym}}}"), + ], + ).dumps() return command diff --git a/aastex/_tests/test_aastex.py b/aastex/_tests/test_aastex.py index 93d4b04..f1ba785 100644 --- a/aastex/_tests/test_aastex.py +++ b/aastex/_tests/test_aastex.py @@ -144,6 +144,52 @@ def test_short(self, a: aastex.Acronym): def test_dumps(self, a: aastex.Title): assert isinstance(a.dumps(), str) + def test_capital(self, a: aastex.Acronym): + """A capitalized command is defined for a sentence starting with the acronym.""" + dumps = a.dumps() + assert rf"\{a.acronym}Capital" in dumps + assert rf"\Ac{{{a.acronym}}}" in dumps + if a.plural: + assert rf"\{a.acronym}Capitals" in dumps + assert rf"\Acp{{{a.acronym}}}" in dumps + + +@pytest.mark.skipif( + shutil.which("latexmk") is None, + reason="requires a LaTeX installation", +) +def test_acronym_capital_compiles(tmp_path: pathlib.Path): + """ + The capitalized command raises the article of a name which carries one. + + An instrument named "the Multi-slit Solar Explorer" should open a sentence + with "The", which is what the underlying `acronym` package provides. + """ + doc = aastex.Document(document_options="twocolumn", linenumbers=False) + doc.preamble.append(aastex.Acronym("MUSE", "the Multi-slit Solar Explorer")) + doc.append(aastex.Title("Acronyms")) + doc += [ + aastex.Author( + name="Jane Doe", + affiliation=aastex.Affiliation("Fancy University"), + ) + ] + section = aastex.Section("Introduction") + section.append(pylatex.NoEscape(r"\MUSECapital\ observes the Sun. \MUSE\ again.")) + doc.append(section) + + path = tmp_path / "acronyms" + doc.generate_pdf(path, clean_tex=False) + + text = subprocess.run( + args=["pdftotext", str(path.with_suffix(".pdf")), "-"], + capture_output=True, + text=True, + ).stdout + + assert "The Multi-slit Solar Explorer (MUSE)" in text + assert "MUSE again" in text + @pytest.mark.parametrize( argnames="a",