Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
a2006ec
much simpler, faster profiler, no analysis, just collection
cscheid Mar 29, 2023
6b3cdc3
new profiler:
cscheid Mar 29, 2023
6d17499
convert to tracing json in lua
cscheid Mar 29, 2023
4becc26
keep track of active filter in category
cscheid Mar 30, 2023
21500fc
revive old coarse, joint ts+lua profiler
cscheid Mar 30, 2023
1108875
remove print
cscheid Mar 30, 2023
e5ee48c
garbage
cscheid Apr 3, 2023
b3a268e
Merge branch 'main' into feature/fast-profiler
cscheid Apr 3, 2023
7ffa1a1
using the interrupt-based lua profiler
cscheid Apr 4, 2023
9de2080
Merge branch 'main' into feature/fast-profiler
cscheid Apr 4, 2023
d25df0e
profile using more accurate line information
cscheid Apr 7, 2023
c5a298c
add tool to convert to perfetto's json format
cscheid Apr 7, 2023
ec75e32
Merge branch 'main' into feature/fast-profiler
cscheid Apr 7, 2023
d94b676
properly nested filter category, trim useless frames
cscheid Apr 7, 2023
619e45d
performance: skip empty filters, don't walk pure-pandoc filters
cscheid Apr 7, 2023
74cec4a
performance: don't call walk on meta-only filter
cscheid Apr 7, 2023
8342fcf
only merge when necessary
cscheid Apr 7, 2023
e872370
Merge branch 'main' into feature/fast-profiler
cscheid Apr 7, 2023
fe9891c
partial work towards shortcode parsing at frontend
cscheid Apr 9, 2023
431cd05
breakquartomd mystery solved
cscheid Apr 10, 2023
9d9f465
partial work towards parsing shortcodes: blocks and inlines
cscheid Apr 10, 2023
2184911
shortcodes: code (block/inline/raw), images/links
cscheid Apr 10, 2023
9428b2d
undo bad combineFilters, add more fixes for shortcode params
cscheid Apr 11, 2023
5d6c2fa
temporarily switch to raw-text in shortcodes
cscheid Apr 12, 2023
7b85eb4
partial work on shortcodes
cscheid Apr 12, 2023
de07929
unshortcode fixes
cscheid Apr 12, 2023
08c01ec
continue unshortcodeing after bracket
cscheid Apr 12, 2023
69866eb
be more robust if shortcode is not found
cscheid Apr 12, 2023
d1f63c9
hack around pipeline with shortcode ids
cscheid Apr 12, 2023
be3e8dc
revert bad perf change
cscheid Apr 13, 2023
fb9c2b3
make execution conditional
cscheid Apr 14, 2023
40bdea4
use quarto.ast.walk
cscheid Apr 14, 2023
7d9e114
don't run if code-annotation: false
cscheid Apr 14, 2023
b1ee9c2
Merge branch 'main' into feature/fast-profiler
cscheid Apr 14, 2023
3494b60
compute index to prevent calling unnecessary filters
cscheid Apr 14, 2023
ea316ec
missing file
cscheid Apr 14, 2023
6d38c61
missing file
cscheid Apr 14, 2023
952c776
rm print
cscheid Apr 14, 2023
4f733fa
represent index checks explicitly
cscheid Apr 14, 2023
08f7b63
dont import unused file
cscheid Apr 14, 2023
aa8d262
indices: crossref preprocess
cscheid Apr 14, 2023
089bd9a
crossref initialization outside of preprocess for skipping well
cscheid Apr 14, 2023
d162e74
preprocess: theorems
cscheid Apr 14, 2023
88429e0
cites and layout
cscheid Apr 15, 2023
06e0c3a
move crossref init earlier, fix cites
cscheid Apr 15, 2023
ea4525c
bad conditional
cscheid Apr 15, 2023
a7f2ef3
panels and extended figures
cscheid Apr 15, 2023
a197eb9
fix conditional
cscheid Apr 16, 2023
93d2d20
Merge branch 'main' into feature/fast-profiler
cscheid Apr 17, 2023
78fc949
typo
cscheid Apr 17, 2023
72645a7
Merge branch 'main' into feature/fast-profiler
cscheid Apr 17, 2023
21473d0
fix typos, update indices on figure change
cscheid Apr 17, 2023
8069483
table-captions fix
cscheid Apr 17, 2023
c3bb2d2
grab file information from right location
cscheid Apr 17, 2023
ff27ba9
more fixes. tests pass.
cscheid Apr 18, 2023
0e476b6
Merge branch 'main' into feature/fast-profiler
cscheid Apr 19, 2023
29bea9e
memoize kernelspecs
cscheid Apr 21, 2023
e5cb322
don't repeatedly reconstruct parse
cscheid Apr 21, 2023
042fc66
perf: cache translations file
cscheid Apr 21, 2023
ff10c10
cache project index results
cscheid Apr 21, 2023
e63c89a
perf metrics
cscheid Apr 21, 2023
23ecb1c
clone options optionally
cscheid Apr 21, 2023
5883b67
Merge branch 'main' into feature/fast-profiler
cscheid Apr 26, 2023
2727de7
QUARTO_TS_PROFILE
cscheid Apr 26, 2023
763fa8e
indices -> flags
cscheid Apr 27, 2023
3920b67
indices -> flags
cscheid Apr 27, 2023
57295e3
indices -> flags
cscheid Apr 27, 2023
ee087e4
smoke test
cscheid Apr 27, 2023
ed9f9a8
remove comments, move lpeg calculation back inside filter
cscheid Apr 27, 2023
4ba2261
actually use flags
cscheid Apr 27, 2023
528dade
Merge branch 'main' into feature/fast-profiler
cscheid Apr 27, 2023
d65dc6b
merge
cscheid May 1, 2023
dd593bf
refactor: use names that respect lua's naming convention
cscheid May 1, 2023
b7fe690
chore: naming convention
cscheid May 2, 2023
89177fd
chore: naming conventions
cscheid May 2, 2023
5e951c6
chore: lua naming conventions
cscheid May 2, 2023
26fef77
chore: lua naming conventions
cscheid May 2, 2023
48e5a55
chore: lua naming conventions
cscheid May 2, 2023
8727eed
chore: lua naming conventions
cscheid May 3, 2023
4299f6e
Merge branch 'main' into feature/fast-profiler
cscheid May 3, 2023
d7bd93a
chore: lua naming conventions
cscheid May 3, 2023
6acc63d
trim spaces at the end of raw parameters
cscheid May 3, 2023
53ce143
use is_format consistently
cscheid May 3, 2023
05359c6
chore: lua naming conventions
cscheid May 3, 2023
ecc81cf
Merge branch 'main' into feature/fast-profiler
cscheid May 3, 2023
89022af
Fix bundling issue
dragonstyle May 4, 2023
5a91371
chore: lua naming conventions
cscheid May 4, 2023
b097010
remove unneeded files
cscheid May 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion package/scripts/common/quarto
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,9 @@ export DENO_NO_UPDATE_CHECK=1
# Be sure to include any already defined QUARTO_DENO_OPTIONS
QUARTO_DENO_OPTIONS="--unstable --no-config ${QUARTO_CACHE_OPTIONS} --allow-read --allow-write --allow-run --allow-env --allow-net --allow-ffi ${QUARTO_DENO_OPTIONS}"

"${QUARTO_DENO}" ${QUARTO_ACTION} ${QUARTO_DENO_OPTIONS} ${QUARTO_DENO_EXTRA_OPTIONS} ${QUARTO_IMPORT_ARGMAP} "${QUARTO_TARGET}" "$@"
if [ "$QUARTO_TS_PROFILE" != "" ]; then
QUARTO_DENO_EXTRA_OPTIONS="--inspect-brk ${QUARTO_DENO_EXTRA_OPTIONS}"
QUARTO_TS_PROFILE=true "${QUARTO_DENO}" ${QUARTO_ACTION} ${QUARTO_DENO_OPTIONS} ${QUARTO_DENO_EXTRA_OPTIONS} "${QUARTO_IMPORT_ARGMAP}" "${QUARTO_TARGET}" "$@"
else
"${QUARTO_DENO}" ${QUARTO_ACTION} ${QUARTO_DENO_OPTIONS} ${QUARTO_DENO_EXTRA_OPTIONS} ${QUARTO_IMPORT_ARGMAP} "${QUARTO_TARGET}" "$@"
fi
10 changes: 8 additions & 2 deletions src/command/render/render-contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,15 @@ export async function renderContexts(
options: RenderOptions,
forExecute: boolean,
project?: ProjectContext,
cloneOptions: boolean = true,
): Promise<Record<string, RenderContext>> {
// clone options (b/c we will modify them)
options = ld.cloneDeep(options) as RenderOptions;
if (cloneOptions) {
// clone options (b/c we will modify them)
// we make it optional because some of the callers have
// actually just cloned it themselves and don't need to preserve
// the original
options = ld.cloneDeep(options) as RenderOptions;
}

const { engine, target } = await fileExecutionEngineAndTarget(
file.path,
Expand Down
1 change: 1 addition & 0 deletions src/command/render/render-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ export async function renderFiles(
options,
true,
project,
false,
);
} catch (e) {
// bad YAML can cause failure before validation. We
Expand Down
29 changes: 29 additions & 0 deletions src/core/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* cache.ts
*
* provides a simple cache for expensive operations
*
* Copyright (C) 2023 Posit Software, PBC
*/

export function cache<T>(f: () => Promise<T>): () => Promise<T> {
let value: T | undefined = undefined;
return async () => {
if (value === undefined) {
value = await f();
}
return value;
};
}

export function cacheMap<K, V>(
f: (key: K) => Promise<V>,
): (key: K) => Promise<V> {
const map = new Map<K, V>();
return async (key: K) => {
if (!map.has(key)) {
map.set(key, await f(key));
}
return map.get(key)!;
};
}
20 changes: 15 additions & 5 deletions src/core/jupyter/kernels.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/*
* kerenels.ts
*
* Copyright (C) 2020-2022 Posit Software, PBC
*
*/
* kernels.ts
*
* Copyright (C) 2020-2022 Posit Software, PBC
*/

import { basename, join } from "path/mod.ts";
import { existsSync, walkSync } from "fs/mod.ts";
Expand Down Expand Up @@ -43,8 +42,19 @@ export async function jupyterKernelspec(
return kernelspecs.get(name);
}

let kJupyterKernelspecs: Map<string, JupyterKernelspec> | undefined = undefined;
export async function jupyterKernelspecs(): Promise<
Map<string, JupyterKernelspec>
> {
if (kJupyterKernelspecs) {
return kJupyterKernelspecs;
}
kJupyterKernelspecs = await computeJupyterKernelspecs();
return kJupyterKernelspecs;
}

async function computeJupyterKernelspecs(): Promise<
Map<string, JupyterKernelspec>
> {
try {
const result = await execProcess(
Expand Down
65 changes: 45 additions & 20 deletions src/core/language.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/*
* language.ts
*
* Copyright (C) 2020-2022 Posit Software, PBC
*
*/
* language.ts
*
* Copyright (C) 2020-2022 Posit Software, PBC
*/

import { existsSync, expandGlobSync } from "fs/mod.ts";
import { extname, join } from "path/mod.ts";
Expand All @@ -20,31 +19,50 @@ import { mergeConfigs } from "./config.ts";
import { readAndValidateYamlFromFile } from "./schema/validated-yaml.ts";
import { RenderFlags } from "../command/render/types.ts";
import { getSchemaDefinition } from "./lib/yaml-validation/schema.ts";
import { cacheMap } from "./cache.ts";

export async function readLanguageTranslations(
translationFile: string,
lang?: string,
): Promise<{ language: FormatLanguage; files: string[] }> {
// read and parse yaml if it exists (track files read)
const files: string[] = [];
const maybeReadYaml = async (file: string) => {
type TranslationCacheValue = {
file?: string;
result: FormatLanguage;
};

const translationCache = cacheMap(
async (file: string) => {
if (existsSync(file)) {
files.push(normalizePath(file));
const errMsg = "Validation of format language object failed.";
const formatLanguageSchema = getSchemaDefinition("format-language");
const result = await readAndValidateYamlFromFile(
file,
formatLanguageSchema,
errMsg,
);
return result as FormatLanguage;
return {
file: normalizePath(file),
language: result as FormatLanguage,
};
} else {
return {} as FormatLanguage;
return {
language: {} as FormatLanguage,
};
}
};
},
);

export async function readLanguageTranslations(
translationFile: string,
lang?: string,
): Promise<{ language: FormatLanguage; files: string[] }> {
// read and parse yaml if it exists (track files read)
const files: string[] = [];

// read the original file
const language = await maybeReadYaml(translationFile);
const {
file,
language,
} = await translationCache(translationFile);
if (file) {
files.push(file);
}

// determine additional variations to read
const ext = extname(translationFile);
Expand Down Expand Up @@ -78,9 +96,16 @@ export async function readLanguageTranslations(

// read the variations
for (const variation of variations) {
const translations = await maybeReadYaml(
join(dir, stem + "-" + variation + ext),
);
const {
file: variationFile,
language: translations,
} = await translationCache(join(dir, stem + "-" + variation + ext));
if (variationFile) {
files.push(variationFile);
}
// const translations = await maybeReadYaml(
// join(dir, stem + "-" + variation + ext),
// );
Object.keys(translations).forEach((key) => {
// top level entries use the variation key
if (kLanguageDefaultsKeys.includes(key)) {
Expand Down
15 changes: 6 additions & 9 deletions src/core/lib/yaml-intelligence/annotated-yaml.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/*
* annotated-yaml.ts
*
* Copyright (C) 2021-2022 Posit Software, PBC
*
*/
* annotated-yaml.ts
*
* Copyright (C) 2021-2022 Posit Software, PBC
*/

import { lineColToIndex } from "../text.ts";
import { AnnotatedParse, JSONValue } from "../yaml-schema/types.ts";
Expand Down Expand Up @@ -48,10 +47,8 @@ function postProcessAnnotation(parse: AnnotatedParse): AnnotatedParse {
) {
return postProcessAnnotation(parse.components[0]);
} else {
return {
...parse,
components: parse.components.map(postProcessAnnotation),
};
parse.components = parse.components.map(postProcessAnnotation);
return parse;
}
}

Expand Down
53 changes: 53 additions & 0 deletions src/core/performance/metrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* metrics.ts
*
* Copyright (C) 2020-2023 Posit Software, PBC
*/

import { inputTargetIndexCacheMetrics } from "../../project/project-index.ts";

export function quartoPerformanceMetrics() {
return {
inputTargetIndexCache: inputTargetIndexCacheMetrics,
};
}

export function reportPeformanceMetrics() {
console.log("---");
console.log("Performance metrics");
console.log("Quarto:");
console.log(JSON.stringify(quartoPerformanceMetrics(), null, 2));
console.log();
// denoMetrics is some kind of fancy object that doesn't respond
// to a bunch of the normal methods. So we have to do this
// the JSON-round-trip way.
console.log("Deno:");
const denoMetrics = JSON.parse(JSON.stringify(Deno.metrics() as any));
denoMetrics.ops = Object.fromEntries(
Object.entries(denoMetrics.ops).map(
([key, opMetrics]: any) => {
for (const key of Object.keys(opMetrics)) {
if (opMetrics[key] === 0) {
delete opMetrics[key];
}
}
return [key, opMetrics];
},
).filter(([_key, opMetrics]: any) => Object.keys(opMetrics).length > 0)
.map(([key, opMetrics]: any) => {
if (
(opMetrics.opsDispatched === opMetrics.opsDispatchedSync &&
opMetrics.opsDispatched === opMetrics.opsCompleted &&
opMetrics.opsDispatched === opMetrics.opsCompletedSync) ||
(opMetrics.opsDispatched === opMetrics.opsDispatchedAsync &&
opMetrics.opsDispatched === opMetrics.opsCompleted &&
opMetrics.opsDispatched === opMetrics.opsCompletedAsync)
) {
return [key, opMetrics.opsDispatched];
} else {
return [key, opMetrics];
}
}),
);
console.log(JSON.stringify(denoMetrics, null, 2));
}
40 changes: 34 additions & 6 deletions src/project/project-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,20 +179,48 @@ export function inputTargetIsEmpty(index: InputTargetIndex) {
return true;
}

const inputTargetIndexCache = new Map<string, InputTargetIndex>();
export const inputTargetIndexCacheMetrics = {
hits: 0,
misses: 0,
invalidations: 0,
};

function readInputTargetIndexIfStillCurrent(projectDir: string, input: string) {
const inputFile = join(projectDir, input);
const indexFile = inputTargetIndexFile(projectDir, input);
if (existsSync(indexFile)) {
try {
const inputMod = Deno.statSync(inputFile).mtime;
const indexMod = Deno.statSync(indexFile).mtime;
if (
inputMod && indexMod && (indexMod >= inputMod)
inputMod && indexMod
) {
try {
return JSON.parse(Deno.readTextFileSync(indexFile)) as InputTargetIndex;
} catch {
return undefined;
if (inputMod > indexMod) {
inputTargetIndexCacheMetrics.invalidations++;
inputTargetIndexCache.delete(indexFile);
}

if (inputTargetIndexCache.has(indexFile)) {
inputTargetIndexCacheMetrics.hits++;
return inputTargetIndexCache.get(indexFile);
} else {
inputTargetIndexCacheMetrics.misses++;
try {
const result = JSON.parse(
Deno.readTextFileSync(indexFile),
) as InputTargetIndex;
inputTargetIndexCache.set(indexFile, result);
return result;
} catch {
return undefined;
}
}
}
} catch (e) {
if (e instanceof Deno.errors.NotFound) {
return undefined;
} else {
throw e;
}
}
}
Expand Down
14 changes: 6 additions & 8 deletions src/quarto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,12 @@ if (import.meta.main) {

await cleanupLogger();

// print out metrics
// reportPeformanceMetrics();
// console.log(
// "Done running, sleeping for 10 seconds... Stop your profiler now!",
// );
// await Deno.run({
// cmd: ["sleep", "10"],
// }).status();
// if profiling, wait for 10 seconds before quitting
if (Deno.env.get("QUARTO_TS_PROFILE") !== undefined) {
console.log("Program finished. Turn off the Chrome profiler now!");
console.log("Waiting for 10 seconds ...");
await new Promise((resolve) => setTimeout(resolve, 10000));
}

// exit
exitWithCleanup(0);
Expand Down
2 changes: 1 addition & 1 deletion src/resources/extensions/quarto/kbd/kbd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ return {
return pandoc.RawInline('html', '<kbd ' .. kwargs_str .. '>' .. default_arg_str .. '</kbd>')
elseif quarto.doc.isFormat("asciidoc") and args and #args == 1 then
-- get the 'first' kbd shortcut as we can only produce on shortcut in asciidoc
local shortcutText = pandoc.utils.stringify(args[1][1]):gsub('-', '+')
local shortcutText = pandoc.utils.stringify(args[1]):gsub('-', '+')
return pandoc.RawInline("asciidoc", "kbd:[" .. shortcutText .. "]")
else
-- example shortcodes
Expand Down
3 changes: 2 additions & 1 deletion src/resources/extensions/quarto/video/video.lua
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ return {
end

if isEmpty(srcValue) then

if #raw_args > 0 then
srcValue = pandoc.utils.stringify(raw_args[1])
else
Expand All @@ -298,7 +299,7 @@ return {

if quarto.doc.is_format("html:js") then
return htmlVideo(srcValue, heightValue, widthValue, titleValue, startValue, aspectRatio)
elseif quarto.doc.isFormat("asciidoc") then
elseif quarto.doc.is_format("asciidoc") then
return asciidocVideo(srcValue, heightValue, widthValue, titleValue, startValue, aspectRatio)
else
-- Fall-back to a link of the source
Expand Down
Loading