From b141df541708f74fe6f7b0c9a3d38aee5239f7f9 Mon Sep 17 00:00:00 2001 From: Oleksii Shmalko Date: Sun, 19 May 2024 03:47:51 +0300 Subject: [PATCH] feat: support export-snippet --- .changeset/nine-jeans-pay.md | 12 +++++ .../src/__snapshots__/parser.spec.ts.snap | 48 +++++++++++++++++++ packages/uniorg-parse/src/parser.spec.ts | 8 ++++ packages/uniorg-parse/src/parser.ts | 23 +++++++++ .../__snapshots__/org-to-hast.spec.ts.snap | 6 +++ .../uniorg-rehype/src/org-to-hast.spec.ts | 2 + packages/uniorg-rehype/src/org-to-hast.ts | 5 ++ .../src/__snapshots__/stringify.spec.ts.snap | 5 ++ .../uniorg-stringify/src/stringify.spec.ts | 2 + packages/uniorg-stringify/src/stringify.ts | 4 +- packages/uniorg/src/index.ts | 7 +++ 11 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 .changeset/nine-jeans-pay.md diff --git a/.changeset/nine-jeans-pay.md b/.changeset/nine-jeans-pay.md new file mode 100644 index 0000000..b891a06 --- /dev/null +++ b/.changeset/nine-jeans-pay.md @@ -0,0 +1,12 @@ +--- +'uniorg-parse': major +'uniorg-stringify': minor +'uniorg-rehype': minor +'uniorg': minor +--- + +Support `export-snippet` in uniorg, uniorg-parse, uniorg-rehype, and uniorg-stringify. + +`export-snippet` has the following form: `@@backend:value@@`. Example: `@@html:@@some text@@html:`. + +This is a breaking change for uniorg-parse as it may output nodes unknown to downstream users (uniorg-rehype and uniorg-stringify). If you upgrade uniorg-parse, you should also upgrade uniorg-rehype and uniorg-stringify to the corresponding versions. diff --git a/packages/uniorg-parse/src/__snapshots__/parser.spec.ts.snap b/packages/uniorg-parse/src/__snapshots__/parser.spec.ts.snap index 71ec1bf..0021453 100644 --- a/packages/uniorg-parse/src/__snapshots__/parser.spec.ts.snap +++ b/packages/uniorg-parse/src/__snapshots__/parser.spec.ts.snap @@ -1280,6 +1280,54 @@ children: value: ")" `; +exports[`org/parser export-snippet fake export snippet, missing backend 1`] = ` +type: "org-data" +contentsBegin: 0 +contentsEnd: 7 +children: + - type: "paragraph" + affiliated: {} + contentsBegin: 0 + contentsEnd: 7 + children: + - type: "text" + value: "@@@@" +`; + +exports[`org/parser export-snippet html export snippet 1`] = ` +type: "org-data" +contentsBegin: 0 +contentsEnd: 38 +children: + - type: "paragraph" + affiliated: {} + contentsBegin: 0 + contentsEnd: 38 + children: + - type: "export-snippet" + backEnd: "html" + value: "" + - type: "text" + value: "hello, world!" + - type: "export-snippet" + backEnd: "html" + value: "" +`; + +exports[`org/parser export-snippet incomplete export snippet 1`] = ` +type: "org-data" +contentsBegin: 0 +contentsEnd: 27 +children: + - type: "paragraph" + affiliated: {} + contentsBegin: 0 + contentsEnd: 27 + children: + - type: "text" + value: "@@html:fake export snippet@" +`; + exports[`org/parser fake keyword 1`] = ` type: "org-data" contentsBegin: 0 diff --git a/packages/uniorg-parse/src/parser.spec.ts b/packages/uniorg-parse/src/parser.spec.ts index 5f41d27..837b8aa 100644 --- a/packages/uniorg-parse/src/parser.spec.ts +++ b/packages/uniorg-parse/src/parser.spec.ts @@ -1098,4 +1098,12 @@ more text itParses('reference suffix', `[cite:@hello p.13]`); }); + + describe('export-snippet', () => { + itParses('html export snippet', '@@html:@@hello, world!@@html:@@'); + + itParses('incomplete export snippet', '@@html:fake export snippet@'); + + itParses('fake export snippet, missing backend', '@@@@'); + }); }); diff --git a/packages/uniorg-parse/src/parser.ts b/packages/uniorg-parse/src/parser.ts index 4e93e3a..b281ced 100644 --- a/packages/uniorg-parse/src/parser.ts +++ b/packages/uniorg-parse/src/parser.ts @@ -52,6 +52,7 @@ import { CitationKey, CitationSuffix, CitationCommonSuffix, + ExportSnippet, } from 'uniorg'; import { getOrgEntity } from './entities.js'; @@ -611,6 +612,11 @@ class Parser { return this.parseStrikeThrough(); } break; + case '@': + if (restriction.has('export-snippet')) { + return this.parseExportSnippet(); + } + break; case '$': if (restriction.has('latex-fragment')) { return this.parseLatexFragment(); @@ -1630,6 +1636,23 @@ class Parser { return u('entity', { useBrackets: hasBrackets, ...value }); } + private parseExportSnippet(): ExportSnippet | null { + const m = this.r.advance(this.r.lookingAt(/@@([-A-Za-z0-9]+):/)); + if (!m) return null; + + const backEnd = m[1]; + const contentsBegin = this.r.offset(); + + const mend = this.r.advance(this.r.match(/@@/)); + if (!mend) return null; + + const contentsEnd = this.r.offset() - 2; // exclude @@ + + const value = this.r.substring(contentsBegin, contentsEnd); + + return u('export-snippet', { backEnd, value }); + } + private parseLatexFragment(): LatexFragment | null { const begin = this.r.offset(); const prefix = this.r.peek(2); diff --git a/packages/uniorg-rehype/src/__snapshots__/org-to-hast.spec.ts.snap b/packages/uniorg-rehype/src/__snapshots__/org-to-hast.spec.ts.snap index ce23f9a..72305c0 100644 --- a/packages/uniorg-rehype/src/__snapshots__/org-to-hast.spec.ts.snap +++ b/packages/uniorg-rehype/src/__snapshots__/org-to-hast.spec.ts.snap @@ -183,6 +183,12 @@ exports[`org/org-to-hast export html keyword 1`] = ` `; +exports[`org/org-to-hast export snippet 1`] = ` + +

Hello, world!

+ +`; + exports[`org/org-to-hast fixed-width 1`] = `
hello
diff --git a/packages/uniorg-rehype/src/org-to-hast.spec.ts b/packages/uniorg-rehype/src/org-to-hast.spec.ts index 67c1c0d..5fee29b 100644 --- a/packages/uniorg-rehype/src/org-to-hast.spec.ts +++ b/packages/uniorg-rehype/src/org-to-hast.spec.ts @@ -260,6 +260,8 @@ export #+HTML:

html tag

` ); + hastTest('export snippet', `@@html:@@Hello, world!@@html:@@`); + hastTest( 'special block', `#+begin_blah diff --git a/packages/uniorg-rehype/src/org-to-hast.ts b/packages/uniorg-rehype/src/org-to-hast.ts index bd73b4e..765f5ed 100644 --- a/packages/uniorg-rehype/src/org-to-hast.ts +++ b/packages/uniorg-rehype/src/org-to-hast.ts @@ -79,6 +79,11 @@ const defaultHandlers: Handlers = { 'citation-key': function (org) { return this.h(org, 'a', { href: 'cite:' + org.key }, ['cite:' + org.key]); }, + + 'export-snippet': function (org) { + if (org.backEnd !== 'html') return null; + return u('raw', org.value) as any; + }, }; function renderAsChildren( diff --git a/packages/uniorg-stringify/src/__snapshots__/stringify.spec.ts.snap b/packages/uniorg-stringify/src/__snapshots__/stringify.spec.ts.snap index d452422..10d4eef 100644 --- a/packages/uniorg-stringify/src/__snapshots__/stringify.spec.ts.snap +++ b/packages/uniorg-stringify/src/__snapshots__/stringify.spec.ts.snap @@ -211,6 +211,11 @@ exports[`stringify footnotes starting on next line 1`] = ` " `; +exports[`stringify handle export-snippet 1`] = ` +"@@backend:custom value@@ +" +`; + exports[`stringify handlers allow handlers to return empty string to suppress output 1`] = ` "hello, world " diff --git a/packages/uniorg-stringify/src/stringify.spec.ts b/packages/uniorg-stringify/src/stringify.spec.ts index 1a70f46..e971ea7 100644 --- a/packages/uniorg-stringify/src/stringify.spec.ts +++ b/packages/uniorg-stringify/src/stringify.spec.ts @@ -512,4 +512,6 @@ some text { handlers: { 'strike-through': () => null } } ); }); + + test('handle export-snippet', `@@backend:custom value@@`); }); diff --git a/packages/uniorg-stringify/src/stringify.ts b/packages/uniorg-stringify/src/stringify.ts index 1ff7ea3..93a1b31 100644 --- a/packages/uniorg-stringify/src/stringify.ts +++ b/packages/uniorg-stringify/src/stringify.ts @@ -16,7 +16,9 @@ export type StringifyOptions = { }; const defaultOptions: StringifyOptions = { - handlers: {}, + handlers: { + 'export-snippet': (org) => `@@${org.backEnd}:${org.value}@@`, + }, }; function normalizeOptions( diff --git a/packages/uniorg/src/index.ts b/packages/uniorg/src/index.ts index eaaf316..665bc12 100644 --- a/packages/uniorg/src/index.ts +++ b/packages/uniorg/src/index.ts @@ -76,6 +76,7 @@ export type ObjectType = | FootnoteReference | LatexFragment | Entity + | ExportSnippet | TableCell; export type OrgNode = GreaterElementType | ElementType | ObjectType; @@ -190,6 +191,12 @@ export interface Entity extends Node { utf8: string; } +export interface ExportSnippet extends Node { + type: 'export-snippet'; + backEnd: string; + value: string; +} + export interface List extends GreaterElement { type: 'plain-list'; listType: 'ordered' | 'unordered' | 'descriptive';