Skip to content

Commit 08c0971

Browse files
committed
Merge remote-tracking branch 'origin/master' into add-text-based-reference-hovering
# Conflicts: # premake5.files.lua
2 parents 61b74b0 + 0adefac commit 08c0971

555 files changed

Lines changed: 172603 additions & 15771 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*.cov
88
out.txt
99
out/
10+
agent-tools/
1011
mupdf/generated/
1112
.vs/
1213
.vscode/db/
@@ -33,11 +34,11 @@ docs/scratch.txt
3334
bin/zopflipng.exe
3435

3536
docs-www/
36-
*.pdf
37-
*.cbr
38-
*.cbz
3937
translations/translations.txt.lzsa
4038
log.txt
4139
sumlog.txt
4240
vs2022/enc_temp_folder
4341
docs/md/.obsidian
42+
43+
# temporary / scratch output written by tests (see tests/util.ts tmpPath())
44+
tests/tmp/

AUTHORS

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,8 @@ SyncTeX ext/.../synctex_parser.c https://itexmac.sourceforge.net/Sy
5050
zlib ext/zlib/zlib.h https://www.zlib.net/
5151
lzma ext/lzma/lzma.txt https://www.7-zip.org/sdk.html
5252
libwebp ext/libwebp/COPYING BSD https://developers.google.com/speed/webp/
53-
unarr ext/unarr/COPYING LGPL https://github.com/zeniko/unarr
5453

55-
CMap Resources mupdf/.../cmaps/README https://sourceforge.net/adobe/cmap/home/Home/
54+
CMap Resources mupdf/.../cmaps/README https://sourceforge.net/adobe/cmap/home/Home/
5655
XySSL mupdf/.../crypt-aes.c BSD https://en.wikipedia.org/wiki/PolarSSL#History
5756
Arcfour mupdf/.../crypt-arc4.c https://tools.ietf.org/html/draft-kaukonen-cipher-arcfour-01
5857
RSA's MD5 mupdf/.../crypt-md5.c https://tools.ietf.org/html/rfc1321

agents.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ To debug run: `windbgx -Q -o -g ./out/dbg64/SumatraPDF-dll.exe`
1414

1515
When launching SumatraPDF.exe for ad-hoc testing, always pass the `-for-testing` cmd-line flag. It starts a new instance (won't interfere with an already running SumatraPDF), doesn't restore the previous session (only loads files given on the cmd-line) and doesn't save settings (won't overwrite the settings of the user).
1616

17-
After making a change to .cpp, .c or .h file (and before running build.ts), run clang-format on those files to reformat them in place
17+
After making a change to a .cpp, .c or .h file under `src/` (and before running build.ts), run clang-format on those files to reformat them in place. Do **not** clang-format third-party / vendored code (`mupdf/`, `ext/`, etc.) — keep edits there minimal and match the existing local style.
1818

1919
Never commit changes automatically. Always wait for explicit command to commit changes.
2020

21+
When committing a fix for a GitHub issue, include `(fixes #<issue-no>)` at the end of the commit message.
22+
2123
## Adding a new advanced setting
2224

2325
To add a new advanced setting:
@@ -40,6 +42,42 @@ To add a new cmd-line flag:
4042
- implement handling in Flags.cpp
4143
- document in docs/md/Version-history.md in **next** section
4244

45+
## Bug reproduction / test files
46+
47+
We keep test and reproduction files for bugs in `C:\Users\kjk\OneDrive\!sumatra\bugs\`, named after the GitHub issue number:
48+
- a single file is `bug-<bug-no><rest>` (e.g. `bug-534.pdf`)
49+
- if a repro needs more than one file, use a directory `bug-<bug-no><rest>\`
50+
51+
When fixing a bug, look there first for an existing repro file for that issue and use it. When you create a test/repro file while working on a bug, save it there (using the naming above) after fixing the bug.
52+
53+
## Writing tests
54+
55+
Tests live in tests/ and are run with bun (e.g. `bun tests/issue-5633.ts`). Naming convention, keyed by the GitHub issue number being tested:
56+
- the test script is `tests/issue-<number>.ts`
57+
- if it needs a small number (one or two) of extra files, name them `tests/issue-<number>.<rest>`
58+
- if it needs more files, or a file must live in a directory, put them in `tests/issue-<number>-data/`
59+
60+
Structure of each test (so they compose in tests/all.ts):
61+
- each `tests/issue-<number>.ts` exports `export async function testit(): Promise<void>` that runs the test logic and THROWS on failure (returns normally on success). It must NOT call `process.exit` or build the app itself.
62+
- end the file with a standalone runner so it can still be run directly:
63+
```ts
64+
if (import.meta.main) {
65+
await runStandalone(testit);
66+
}
67+
```
68+
`runStandalone` (from `tests/util.ts`) builds the app (unless `--no-build`), runs `testit()`, and exits 0 on pass / 1 on failure.
69+
- shared helpers (`EXE` path, `buildApp`, `runStandalone`) live in `tests/util.ts` — use them instead of re-implementing per file.
70+
- register every new test in `tests/all.ts` (import its `testit` and add it to the `tests` array). `bun tests/all.ts` builds once and runs them all in order, stopping at the first failure.
71+
72+
Guidelines for test scripts:
73+
- build the app the same way cmd/build.ts does (via `buildApp`/`runStandalone` in tests/util.ts) and test the resulting out/dbg64/SumatraPDF-dll.exe
74+
- if a needed external tool (e.g. MiKTeX) isn't installed, don't fail the test: print a clear message (with instructions to install it) and skip that part, returning normally so `tests/all.ts` continues
75+
- a good test fails when the fix is reverted (verify this) — not just passes with the fix present
76+
- bun has FFI; if you need to call Windows APIs, put reusable wrappers in tests/winapi.ts
77+
- prefer driving the app via cmd-line flags that write a machine-readable result (see `-test-synctex`) over GUI automation
78+
- never write runtime scratch / result files directly into `tests/` — that leaves the repo dirty. Write them under `tests/tmp/` (gitignored), using `tmpPath("name")` from `tests/util.ts` (it creates the dir on demand); the OS temp dir (`os.tmpdir()`) is also fine if you clean up after
79+
- if a binary test fixture (e.g. a .pdf) is generated from source (LaTeX, a script, etc.), commit the source alongside it (e.g. `tests/issue-<number>.tex` next to `tests/issue-<number>.pdf`) with a comment on how to regenerate it, so the fixture can be modified later
80+
4381
## Windows Shell Safety
4482

4583
The Bash tool runs under Git Bash (MSYS2), **not** cmd.exe. This causes critical issues with Windows-style commands:

cmd/build-asan.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { copyFileSync, existsSync, readdirSync } from "node:fs";
2+
import { join } from "node:path";
3+
import { detectVisualStudio2026, runLogged } from "./util";
4+
import { clearDirPreserveSettings } from "./clean";
5+
6+
const asanDllName = "clang_rt.asan_dynamic-x86_64.dll";
7+
8+
function findAsanDll(vsRoot: string): string {
9+
const candidates = [
10+
join(vsRoot, String.raw`VC\Tools\MSVC`),
11+
join(vsRoot, String.raw`VC\Tools\Llvm\x64\lib\clang`),
12+
];
13+
for (const base of candidates) {
14+
if (!existsSync(base)) continue;
15+
const walk = (dir: string): string | undefined => {
16+
for (const name of readdirSync(dir, { withFileTypes: true })) {
17+
const p = join(dir, name.name);
18+
if (name.isDirectory()) {
19+
const hit = walk(p);
20+
if (hit) return hit;
21+
} else if (name.name === asanDllName) {
22+
return p;
23+
}
24+
}
25+
return;
26+
};
27+
const hit = walk(base);
28+
if (hit) return hit;
29+
}
30+
throw new Error(`could not find ${asanDllName} under ${vsRoot}`);
31+
}
32+
33+
function copyAsanRuntime(vsRoot: string, outDir: string): void {
34+
const src = findAsanDll(vsRoot);
35+
const dst = join(outDir, asanDllName);
36+
copyFileSync(src, dst);
37+
console.log(`copied ${asanDllName}`);
38+
}
39+
40+
// Build SumatraPDF.exe (static) with MSVC AddressSanitizer (x64_asan).
41+
// Output: out/dbg64_asan/SumatraPDF.exe or out/rel64_asan/SumatraPDF.exe
42+
//
43+
// Usage:
44+
// bun cmd/build-asan.ts # Debug ASan build
45+
// bun cmd/build-asan.ts --release # Release ASan build
46+
// bun cmd/build-asan.ts --clean
47+
48+
const isRelease = process.argv.includes("--release");
49+
const clean = process.argv.includes("--clean");
50+
const config = isRelease ? "Release" : "Debug";
51+
const outDir = isRelease ? join("out", "rel64_asan") : join("out", "dbg64_asan");
52+
53+
async function main() {
54+
const timeStart = performance.now();
55+
console.log(`${config} ASan build (SumatraPDF.exe, x64_asan)`);
56+
57+
if (clean) {
58+
clearDirPreserveSettings(outDir);
59+
}
60+
61+
await runLogged(join("bin", "premake5.exe"), ["vs2022"]);
62+
63+
const { msbuildPath, vsRoot } = detectVisualStudio2026();
64+
const sln = String.raw`vs2022\SumatraPDF.sln`;
65+
const t = `/t:SumatraPDF`;
66+
const p = `/p:Configuration=${config};Platform=x64_asan`;
67+
await runLogged(msbuildPath, [sln, t, p, `/m`]);
68+
copyAsanRuntime(vsRoot, outDir);
69+
70+
const elapsed = ((performance.now() - timeStart) / 1000).toFixed(1);
71+
console.log(`build took ${elapsed}s`);
72+
console.log(`exe: ${join(outDir, "SumatraPDF.exe")}`);
73+
}
74+
75+
await main();

cmd/build-with-mingw.ts

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -379,43 +379,6 @@ const chm: LibDef = {
379379
files: [{ dir: "ext/CHMLib", patterns: ["chm_lib.c", "lzx.c"] }],
380380
};
381381

382-
const unarrlib: LibDef = {
383-
name: "unarrlib",
384-
alwaysOptimize: true,
385-
defines: ["HAVE_ZLIB", "HAVE_BZIP2", "HAVE_7Z", "BZ_NO_STDIO", "_7ZIP_PPMD_SUPPPORT"],
386-
includes: ["ext/zlib", "ext/bzip2", "ext/lzma/C"],
387-
files: [
388-
{ dir: "ext/unarr/common", patterns: ["*.c"] },
389-
{ dir: "ext/unarr/rar", patterns: ["*.c"] },
390-
{ dir: "ext/unarr/zip", patterns: ["*.c"] },
391-
{ dir: "ext/unarr/tar", patterns: ["*.c"] },
392-
{ dir: "ext/unarr/_7z", patterns: ["*.c"] },
393-
{ dir: "ext/bzip2", patterns: ["bzip_all.c"] },
394-
{
395-
dir: "ext/unarr/lzmasdk",
396-
patterns: ["CpuArch.c", "Ppmd7.c", "Ppmd7Dec.c", "Ppmd8.c", "Ppmd8Dec.c"],
397-
},
398-
{
399-
dir: "ext/lzma/C",
400-
patterns: [
401-
"LzmaDec.c",
402-
"Bra86.c",
403-
"LzmaEnc.c",
404-
"LzFind.c",
405-
"LzFindMt.c",
406-
"Threads.c",
407-
"7zBuf.c",
408-
"7zDec.c",
409-
"7zIn.c",
410-
"7zStream.c",
411-
"Bcj2.c",
412-
"Bra.c",
413-
"Lzma2Dec.c",
414-
],
415-
},
416-
],
417-
};
418-
419382
const libwebp: LibDef = {
420383
name: "libwebp",
421384
alwaysOptimize: true,
@@ -1225,7 +1188,6 @@ const utils: LibDef = {
12251188
"ext/libheif/libheif/api",
12261189
"ext/libwebp/src",
12271190
"ext/dav1d/include",
1228-
"ext/unarr",
12291191
"mupdf/include",
12301192
"ext/zlib",
12311193
],
@@ -1752,7 +1714,7 @@ namespace _com_util {
17521714

17531715
// Order: libraries that have no deps first, then dependents.
17541716
// The link order for archives is: most-dependent first, least-dependent last.
1755-
const ALL_LIBS: LibDef[] = [zlib, unrar, libdjvu, chm, unarrlib, libwebp, dav1d, libheif, mupdfLibs, mupdf, utils];
1717+
const ALL_LIBS: LibDef[] = [zlib, unrar, libdjvu, chm, libwebp, dav1d, libheif, mupdfLibs, mupdf, utils];
17561718

17571719
async function build(isRelease: boolean, clean: boolean): Promise<void> {
17581720
const config = isRelease ? "release" : "debug";

cmd/bun.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/clang-tidy.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ const includes = [
2121
"-I",
2222
"ext/synctex",
2323
"-I",
24-
"ext/unarr",
25-
"-I",
2624
"ext/lzma/C",
2725
"-I",
2826
"ext/libwebp/src",

cmd/gen-commands.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export const commands = [
8181
"CmdZoomFitPage", "Zoom: Fit Page",
8282
"CmdZoomActualSize", "Zoom: Actual Size",
8383
"CmdZoomFitWidth", "Zoom: Fit Width",
84+
"CmdZoomFitByOrientation", "Zoom: Fit Page or Width by Orientation",
8485
"CmdZoom6400", "Zoom: 6400%",
8586
"CmdZoom3200", "Zoom: 3200%",
8687
"CmdZoom1600", "Zoom: 1600%",
@@ -231,6 +232,12 @@ export const commands = [
231232
"CmdPauseReadAloud", "Pause Read Aloud",
232233
"CmdContinueReadAloud", "Continue Read Aloud",
233234
"CmdToggleHoverPreview", "Toggle Hover Preview",
235+
"CmdRemoveDeletedFilesFromHistory", "Remove Deleted Files From History",
236+
"CmdCommandPaletteTOC", "Command Palette: Table Of Contents",
237+
"CmdDebugToggleRenderInfo", "Debug: Toggle Render Queue Info",
238+
"CmdConvertImageToPdf", "Convert Image To PDF",
239+
"CmdExpandToCurrentPage", "Expand TOC to Current Page",
240+
"CmdStartAutoScroll", "Start Auto-Scroll",
234241
"CmdNone", "Do nothing",
235242
];
236243

cmd/gen-docs-website.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ async function main() {
125125
}
126126

127127
// copy CSS and JS files
128-
const htmlFiles = ["sumatra.css", "gen_toc.js", "favicon.ico"];
128+
const htmlFiles = ["sumatra.css", "gen_toc.js", "gen_code_copy.js", "favicon.ico"];
129129
for (const name of htmlFiles) {
130130
const srcPath = join("docs", name);
131131
const dstPath = join(websiteDir, "www", name);

cmd/gen-docs.ts

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// vendored in cmd/markdown-it.min.js from https://cdn.jsdelivr.net/npm/markdown-it@14.1.0/dist/markdown-it.min.js
33
// @ts-ignore
44
import MarkdownIt from "./markdown-it.min.js";
5+
import hljs from "highlight.js/lib/core";
6+
import javascript from "highlight.js/lib/languages/javascript";
57
import {
68
readFileSync,
79
writeFileSync,
@@ -27,6 +29,42 @@ const searchJS = `<script>${readFileSync(join(docsDir, "gen_docs.search.js"), "u
2729
const searchHTML = readFileSync(join(docsDir, "gen_docs.search.html"), "utf-8");
2830
const tmplManual = readFileSync(join(docsDir, "manual.tmpl.html"), "utf-8");
2931

32+
hljs.registerLanguage("javascript", javascript);
33+
34+
function highlightJsCode(code: string): string {
35+
return hljs.highlight(code, { language: "javascript" }).value;
36+
}
37+
38+
function isMultiLineCode(content: string): boolean {
39+
return content.replace(/\r\n/g, "\n").trimEnd().includes("\n");
40+
}
41+
42+
function genPlainCodeBlockHTML(codeInnerHtml: string, codeClass = ""): string {
43+
const cls = codeClass ? ` class="${codeClass}"` : "";
44+
return `<pre><code${cls}>${codeInnerHtml}</code></pre>\n`;
45+
}
46+
47+
function genCodeBlockHTML(codeInnerHtml: string, codeClass = ""): string {
48+
const cls = codeClass ? ` class="${codeClass}"` : "";
49+
return (
50+
`<div class="code-block">` +
51+
`<button type="button" class="sum-code-copy-btn" title="Copy to clipboard">Copy</button>` +
52+
`<pre><code${cls}>${codeInnerHtml}</code></pre>` +
53+
`</div>\n`
54+
);
55+
}
56+
57+
function renderFenceCodeBlock(
58+
content: string,
59+
codeInnerHtml: string,
60+
codeClass = "",
61+
): string {
62+
if (!isMultiLineCode(content)) {
63+
return genPlainCodeBlockHTML(codeInnerHtml, codeClass);
64+
}
65+
return genCodeBlockHTML(codeInnerHtml, codeClass);
66+
}
67+
3068
function buildTocHTML(currentPage: string): string {
3169
const text = readFileSync(
3270
join(mdDir, "SumatraPDF-documentation.md"),
@@ -191,12 +229,21 @@ function mdToHTML(name: string): string {
191229
md.renderer.rules.paragraph_open = () => "<div>";
192230
md.renderer.rules.paragraph_close = () => "</div>\n";
193231

194-
// render ```commands fenced blocks as CSV tables
232+
// render ```commands fenced blocks as CSV tables; highlight ```js blocks
195233
md.renderer.rules.fence = (tokens: MarkdownIt.Token[], idx: number) => {
196234
const t = tokens[idx];
197-
if (t.info.trim() === "commands")
235+
const lang = t.info.trim().split(/\s+/)[0];
236+
if (lang === "commands")
198237
return genCsvTableHTML(parseCsv(t.content));
199-
return `<pre><code>${md.utils.escapeHtml(t.content)}</code></pre>\n`;
238+
if (lang === "js" || lang === "javascript") {
239+
const highlighted = highlightJsCode(t.content);
240+
return renderFenceCodeBlock(
241+
t.content,
242+
highlighted,
243+
"hljs language-javascript",
244+
);
245+
}
246+
return renderFenceCodeBlock(t.content, md.utils.escapeHtml(t.content));
200247
};
201248

202249
md.renderer.rules.heading_open = (
@@ -344,7 +391,7 @@ function writeDocsHtmlFiles(): void {
344391
}
345392

346393
copyDirRecursive(join(wwwOutDir, "img"), join(mdDir, "img"));
347-
const htmlFiles = ["sumatra.css", "gen_toc.js", "favicon.ico"];
394+
const htmlFiles = ["sumatra.css", "gen_toc.js", "gen_code_copy.js", "favicon.ico"];
348395
for (const name of htmlFiles) {
349396
const srcPath = join("docs", name);
350397
const dstPath = join(wwwOutDir, name);

0 commit comments

Comments
 (0)