Skip to content

Commit

Permalink
feat: support latex
Browse files Browse the repository at this point in the history
uses `prettier-plugin-latex`
  • Loading branch information
Sec-ant committed Nov 28, 2023
1 parent de97dbd commit 9847156
Show file tree
Hide file tree
Showing 7 changed files with 607 additions and 0 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,14 @@ Formatting embedded HTML code doesn't require other plugins and uses the parsers

Formatting embedded INI code requires [`prettier-plugin-ini`](https://github.com/kddnewton/prettier-plugin-ini) to be loaded as well. And [options](https://github.com/kddnewton/prettier-plugin-ini#configuration) supported by `prettier-plugin-ini` can therefore be used to further control the formatting behavior.

#### LaTeX

| Option | Default | Description |
| :------------------------: | :----------------------------------------: | --------------------------------------------------------------------------------------------------- |
| `embeddedLatexIdentifiers` | [`[...]`](./src/embedded/latex/options.ts) | Tag or comment identifiers that make their subsequent template literals be identified as LaTeX code |

Formatting embedded LaTeX code requires [`prettier-plugin-latex`](https://github.com/siefkenj/prettier-plugin-latex) to be loaded as well.

#### Markdown

| Option | Default | Description |
Expand Down
446 changes: 446 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"prettier": "^3.0.0",
"prettier-plugin-glsl": ">=0.1.2 <1",
"prettier-plugin-ini": "^1.1.0",
"prettier-plugin-latex": "^2.0.1",
"prettier-plugin-organize-imports": "^3.2.4",
"prettier-plugin-sql": ">=0.15.0 <1",
"prettier-plugin-toml": "^2.0.1",
Expand Down
7 changes: 7 additions & 0 deletions src/embedded/latex/embedded-language.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const embeddedLanguage = "embeddedLatex";

declare module "../types.js" {
interface EmbeddedLanguagesHolder {
[embeddedLanguage]: void;
}
}
88 changes: 88 additions & 0 deletions src/embedded/latex/embedder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import type { Options } from "prettier";
import { builders } from "prettier/doc";
import type { Embedder } from "../../types.js";
import {
preparePlaceholder,
printTemplateExpressions,
simpleRehydrateDoc,
throwIfPluginIsNotFound,
} from "../utils.js";
import { embeddedLanguage } from "./embedded-language.js";

const { line, group, indent, softline } = builders;

export const embedder: Embedder<Options> = async (
textToDoc,
print,
path,
options,
{ identifier, embeddedOverrideOptions },
) => {
throwIfPluginIsNotFound("prettier-plugin-latex", options, identifier);

options = {
...options,
...embeddedOverrideOptions,
};

const { node } = path;

const { createPlaceholder, placeholderRegex } = preparePlaceholder();

const text = node.quasis
.map((quasi, index, { length }) =>
index === length - 1
? quasi.value.cooked
: quasi.value.cooked + createPlaceholder(index),
)
.join("");

const leadingWhitespaces = text.match(/^\s+/)?.[0] ?? "";
const trailingWhitespaces = text.match(/\s+$/)?.[0] ?? "";

const trimmedText = text.slice(
leadingWhitespaces.length,
-trailingWhitespaces.length || undefined,
);

const expressionDocs = printTemplateExpressions(path, print);

const doc = await textToDoc(trimmedText, {
...options,
parser: "latex-parser",
});

const contentDoc = simpleRehydrateDoc(doc, placeholderRegex, expressionDocs);

if (options.preserveEmbeddedExteriorWhitespaces?.includes(identifier)) {
// TODO: should we label the doc with { hug: false } ?
// https://github.com/prettier/prettier/blob/5cfb76ee50cf286cab267cf3cb7a26e749c995f7/src/language-js/embed/html.js#L88
return group([
"`",
leadingWhitespaces,
options.noEmbeddedMultiLineIndentation?.includes(identifier)
? [group(contentDoc)]
: indent([group(contentDoc)]),
trailingWhitespaces,
"`",
]);
}

const leadingLineBreak = leadingWhitespaces.length ? line : softline;
const trailingLineBreak = trailingWhitespaces.length ? line : softline;

return group([
"`",
options.noEmbeddedMultiLineIndentation?.includes(identifier)
? [leadingLineBreak, group(contentDoc)]
: indent([leadingLineBreak, group(contentDoc)]),
trailingLineBreak,
"`",
]);
};

declare module "../types.js" {
interface EmbeddedEmbedders {
[embeddedLanguage]: typeof embedder;
}
}
3 changes: 3 additions & 0 deletions src/embedded/latex/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./embedded-language.js";
export * from "./embedder.js";
export * from "./options.js";
54 changes: 54 additions & 0 deletions src/embedded/latex/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { CoreCategoryType, SupportOptions } from "prettier";
import {
makeIdentifiersOptionName,
type AutocompleteStringList,
type StringListToInterfaceKey,
} from "../utils.js";
import { embeddedLanguage } from "./embedded-language.js";

/** References:
* - https://github.com/github-linguist/linguist/blob/7ca3799b8b5f1acde1dd7a8dfb7ae849d3dfb4cd/lib/linguist/languages.yml#L6994
*/
const DEFAULT_IDENTIFIERS = [
"latex",
"tex",
"aux",
"cls",
"bbl",
"bib",
"toc",
"sty",
] as const;
type Identifiers = AutocompleteStringList<typeof DEFAULT_IDENTIFIERS>;
type DefaultIdentifiersHolder = StringListToInterfaceKey<
typeof DEFAULT_IDENTIFIERS
>;

const embeddedLanguageIdentifiers = makeIdentifiersOptionName(embeddedLanguage);

export interface PrettierPluginDepsOptions {}

export const options = {
[embeddedLanguageIdentifiers]: {
category: "Global",
type: "string",
array: true,
default: [{ value: [...DEFAULT_IDENTIFIERS] }],
description:
'Specify embedded LaTeX language identifiers. This requires "prettier-plugin-latex".',
},
} satisfies SupportOptions & Record<string, { category: CoreCategoryType }>;

type Options = typeof options;

declare module "../types.js" {
interface EmbeddedOptions extends Options {}
interface EmbeddedDefaultIdentifiersHolder extends DefaultIdentifiersHolder {}
interface PrettierPluginEmbedOptions {
[embeddedLanguageIdentifiers]?: Identifiers;
}
}

declare module "prettier" {
export interface Options extends PrettierPluginDepsOptions {}
}

0 comments on commit 9847156

Please sign in to comment.