Skip to content

Commit 63fcd10

Browse files
committed
refector into a more final form
1 parent 83a1af5 commit 63fcd10

File tree

5 files changed

+197
-118
lines changed

5 files changed

+197
-118
lines changed

src/command/render/render-contexts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ export async function renderContexts(
250250
format: context.format,
251251
markdown: context.target.markdown,
252252
context,
253+
flags: options.flags || {} as RenderFlags,
253254
stage: "pre-engine",
254255
};
255256

src/command/render/render-files.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import {
5353
RenderExecuteOptions,
5454
RenderFile,
5555
RenderFilesResult,
56+
RenderFlags,
5657
RenderOptions,
5758
} from "./types.ts";
5859
import { error, info, warning } from "log/mod.ts";
@@ -443,6 +444,7 @@ export async function renderFiles(
443444
format: recipe.format,
444445
markdown: mappedMarkdown!,
445446
context,
447+
flags: options.flags || {} as RenderFlags,
446448
stage: "post-engine",
447449
};
448450

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* include-notebook.ts
3+
*
4+
* Copyright (C) 2022 by RStudio, PBC
5+
*
6+
*/
7+
8+
import { resourcePath } from "../resources.ts";
9+
10+
import { languages } from "./base.ts";
11+
12+
import { jupyterFromFile, jupyterToMarkdown } from "../jupyter/jupyter.ts";
13+
14+
import {
15+
kFigDpi,
16+
kFigFormat,
17+
kFigPos,
18+
kKeepHidden,
19+
} from "../../config/constants.ts";
20+
import {
21+
isHtmlCompatible,
22+
isIpynbOutput,
23+
isLatexOutput,
24+
isMarkdownOutput,
25+
isPresentationOutput,
26+
} from "../../config/format.ts";
27+
import { resolveParams } from "../../command/render/flags.ts";
28+
import { RenderContext, RenderFlags } from "../../command/render/types.ts";
29+
import {
30+
JupyterAssets,
31+
JupyterCell,
32+
JupyterNotebook,
33+
} from "../jupyter/types.ts";
34+
35+
import { dirname, extname } from "path/mod.ts";
36+
37+
export interface NotebookInclude {
38+
path: string;
39+
cellIds: string[] | undefined;
40+
params: Record<string, string>;
41+
}
42+
43+
const resolveCellIds = (hash: string) => {
44+
if (hash.indexOf(",") > 0) {
45+
return hash.split(",");
46+
} else {
47+
return hash;
48+
}
49+
};
50+
51+
// If the path is a notebook path, then process it separately.
52+
const kPlaceholderProtocol = "ipynb://";
53+
export function parseNotebookPath(path: string) {
54+
if (!path.startsWith(kPlaceholderProtocol)) {
55+
path = `${kPlaceholderProtocol}/${path}`;
56+
}
57+
const url = new URL(path);
58+
const pathname = url.pathname;
59+
if (extname(pathname) === ".ipynb") {
60+
const hash = url.hash;
61+
const cellIds = resolveCellIds(hash);
62+
return {
63+
path: pathname,
64+
cellIds,
65+
} as NotebookInclude;
66+
} else {
67+
return undefined;
68+
}
69+
}
70+
71+
export function notebookForInclude(
72+
nbInclude: NotebookInclude,
73+
context: RenderContext,
74+
) {
75+
const nb = jupyterFromFile(nbInclude.path);
76+
const cells: JupyterCell[] = [];
77+
78+
// If cellIds are present, filter the notebook to only include
79+
// those cells
80+
if (nbInclude.cellIds) {
81+
for (const cell of nb.cells) {
82+
// cellId can either by a literal cell Id, or a tag with that value
83+
const hasId = cell.id ? nbInclude.cellIds.includes(cell.id) : false;
84+
if (hasId) {
85+
// It's an ID
86+
cells.push(cell);
87+
} else {
88+
// Check tags
89+
const hasTag = cell.metadata.tags
90+
? cell.metadata.tags.find((tag) =>
91+
nbInclude.cellIds?.includes(tag)
92+
) !==
93+
undefined
94+
: false;
95+
if (hasTag) {
96+
cells.push(cell);
97+
}
98+
}
99+
}
100+
nb.cells = cells;
101+
}
102+
103+
// Read any notebook metadata
104+
const notebookMetadata = context.format.metadata["notebook"] as Record<
105+
string,
106+
unknown
107+
>;
108+
109+
// Resolve any execution options from the notebook metadata
110+
nb.cells = nb.cells.map((cell) => {
111+
cell.metadata = {
112+
"echo": false,
113+
"warning": false,
114+
...notebookMetadata,
115+
...cell.metadata,
116+
};
117+
return cell;
118+
});
119+
120+
return nb;
121+
}
122+
123+
export async function notebookMarkdown(
124+
notebook: JupyterNotebook,
125+
assets: JupyterAssets,
126+
context: RenderContext,
127+
flags: RenderFlags,
128+
) {
129+
const format = context.format;
130+
const executeOptions = {
131+
target: context.target,
132+
resourceDir: resourcePath(),
133+
tempDir: context.options.services.temp.createDir(),
134+
dependencies: true,
135+
libDir: context.libDir,
136+
format: context.format,
137+
projectDir: context.project?.dir,
138+
cwd: flags.executeDir ||
139+
dirname(Deno.realPathSync(context.target.source)),
140+
params: resolveParams(flags.params, flags.paramsFile),
141+
quiet: flags.quiet,
142+
previewServer: context.options.previewServer,
143+
handledLanguages: languages(),
144+
};
145+
const result = await jupyterToMarkdown(
146+
notebook,
147+
{
148+
executeOptions,
149+
language: notebook.metadata.kernelspec.language.toLowerCase(),
150+
assets,
151+
execute: format.execute,
152+
keepHidden: format.render[kKeepHidden],
153+
toHtml: isHtmlCompatible(format),
154+
toLatex: isLatexOutput(format.pandoc),
155+
toMarkdown: isMarkdownOutput(format.pandoc),
156+
toIpynb: isIpynbOutput(format.pandoc),
157+
toPresentation: isPresentationOutput(format.pandoc),
158+
figFormat: format.execute[kFigFormat],
159+
figDpi: format.execute[kFigDpi],
160+
figPos: format.render[kFigPos],
161+
},
162+
);
163+
if (result) {
164+
return result.markdown;
165+
} else {
166+
return undefined;
167+
}
168+
}

src/core/handlers/include.ts

Lines changed: 23 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
import { LanguageCellHandlerContext, LanguageHandler } from "./types.ts";
9-
import { baseHandler, install, languages } from "./base.ts";
9+
import { baseHandler, install } from "./base.ts";
1010
import {
1111
asMappedString,
1212
EitherString,
@@ -18,32 +18,13 @@ import {
1818
import { rangedLines } from "../lib/ranged-text.ts";
1919
import { isBlockShortcode } from "../lib/parse-shortcode.ts";
2020
import { DirectiveCell } from "../lib/break-quarto-md-types.ts";
21-
import {
22-
jupyterAssets,
23-
jupyterFromFile,
24-
jupyterToMarkdown,
25-
} from "../jupyter/jupyter.ts";
21+
import { jupyterAssets } from "../jupyter/jupyter.ts";
2622

27-
import { dirname, extname } from "path/mod.ts";
28-
import {
29-
kFigDpi,
30-
kFigFormat,
31-
kFigPos,
32-
kKeepHidden,
33-
} from "../../config/constants.ts";
3423
import {
35-
isHtmlCompatible,
36-
isIpynbOutput,
37-
isLatexOutput,
38-
isMarkdownOutput,
39-
isPresentationOutput,
40-
} from "../../config/format.ts";
41-
import { resourcePath } from "../resources.ts";
42-
import { resolveParams } from "../../command/render/flags.ts";
43-
import { RenderFlags } from "../../command/render/types.ts";
44-
import { callbackify } from "https://deno.land/[email protected]/node/util.ts";
45-
import { translationsForLang } from "../language.ts";
46-
import { JupyterCell } from "../jupyter/types.ts";
24+
notebookForInclude,
25+
notebookMarkdown,
26+
parseNotebookPath,
27+
} from "./include-notebook.ts";
4728

4829
const includeHandler: LanguageHandler = {
4930
...baseHandler,
@@ -71,105 +52,30 @@ const includeHandler: LanguageHandler = {
7152
);
7253
}
7354

74-
// If the path is a notebook path, then process it separately.
75-
const parseNotebookPath = (path: string) => {
76-
if (path.includes("#")) {
77-
const pathParts = path.split("#");
78-
const filePath = pathParts[0];
79-
const cellId = pathParts.slice(1);
80-
return {
81-
path: filePath,
82-
cellId,
83-
};
84-
} else {
85-
const ext = extname(path);
86-
if (ext === ".ipynb") {
87-
return {
88-
path,
89-
};
90-
} else {
91-
return undefined;
92-
}
93-
}
94-
};
95-
96-
const notebookPath = parseNotebookPath(path);
97-
if (notebookPath) {
98-
const nb = jupyterFromFile(notebookPath.path);
55+
// Handle notebooks directly by extracting the items
56+
// from the notebook
57+
const notebookInclude = parseNotebookPath(path);
58+
if (notebookInclude) {
59+
// This is a notebook include, so read the notebook (including only
60+
// the cells that are specified in the include and include them)
61+
const nb = notebookForInclude(
62+
notebookInclude,
63+
handlerContext.options.context,
64+
);
9965
const assets = jupyterAssets(
10066
source,
10167
handlerContext.options.context.format.pandoc.to,
10268
);
10369

104-
const cells: JupyterCell[] = [];
105-
106-
// filter the notebook
107-
if (notebookPath.cellId) {
108-
for (const cell of nb.cells) {
109-
const hasId = cell.id
110-
? notebookPath.cellId.includes(cell.id)
111-
: false;
112-
if (hasId) {
113-
cells.push(cell);
114-
} else {
115-
const hasTag = cell.metadata.tags
116-
? cell.metadata.tags.find((tag) =>
117-
notebookPath.cellId.includes(tag)
118-
) !== undefined
119-
: false;
120-
if (hasTag) {
121-
cells.push(cell);
122-
}
123-
}
124-
}
125-
}
126-
nb.cells = cells.map((cell) => {
127-
cell.metadata = {
128-
...cell.metadata,
129-
"echo": false,
130-
};
131-
return cell;
132-
});
133-
134-
const options = handlerContext.options;
135-
const context = options.context;
136-
const flags: RenderFlags = {};
137-
138-
const executeOptions = {
139-
target: context.target,
140-
resourceDir: resourcePath(),
141-
tempDir: context.options.services.temp.createDir(),
142-
dependencies: true,
143-
libDir: context.libDir,
144-
format: context.format,
145-
projectDir: context.project?.dir,
146-
cwd: flags.executeDir ||
147-
dirname(Deno.realPathSync(context.target.source)),
148-
params: resolveParams(flags.params, flags.paramsFile),
149-
quiet: flags.quiet,
150-
previewServer: context.options.previewServer,
151-
handledLanguages: languages(),
152-
};
153-
const result = await jupyterToMarkdown(
70+
// Render the notebook markdown and inject it
71+
const markdown = await notebookMarkdown(
15472
nb,
155-
{
156-
executeOptions,
157-
language: nb.metadata.kernelspec.language.toLowerCase(),
158-
assets,
159-
execute: options.format.execute,
160-
keepHidden: options.format.render[kKeepHidden],
161-
toHtml: isHtmlCompatible(options.format),
162-
toLatex: isLatexOutput(options.format.pandoc),
163-
toMarkdown: isMarkdownOutput(options.format.pandoc),
164-
toIpynb: isIpynbOutput(options.format.pandoc),
165-
toPresentation: isPresentationOutput(options.format.pandoc),
166-
figFormat: options.format.execute[kFigFormat],
167-
figDpi: options.format.execute[kFigDpi],
168-
figPos: options.format.render[kFigPos],
169-
},
73+
assets,
74+
handlerContext.options.context,
75+
handlerContext.options.flags,
17076
);
171-
if (result) {
172-
textFragments.push(result.markdown);
77+
if (markdown) {
78+
textFragments.push(markdown);
17379
}
17480
} else {
17581
let includeSrc;

src/core/handlers/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { RenderContext } from "../../command/render/types.ts";
1+
import { RenderContext, RenderFlags } from "../../command/render/types.ts";
22
import {
33
kIncludeAfterBody,
44
kIncludeBeforeBody,
@@ -32,6 +32,8 @@ export interface LanguageCellHandlerOptions {
3232

3333
context: RenderContext;
3434

35+
flags: RenderFlags;
36+
3537
// per-document state, so that different handlers can share state as needed.
3638
state?: Record<string, Record<string, unknown>>;
3739
}

0 commit comments

Comments
 (0)