A minified file is shown plain instead of highlighted for seconds f157bcea0e32
syntect's time grows much faster than a line's length: the file page of assets/vendor/editor.js (860 KB in 31 lines) took 6.2 s to highlight, and 41 such requests were slow tonight, since every restart empties the highlight cache. A text with a line over 4 000 bytes is now shown plain (escaped), as other forges do. Test: a long-line file renders plain at once, a normal one is still highlighted. Refs #228
- Author
- Andy McCall @andymccall <[email protected]>
- via
- agent
Claude Code <[email protected]> · delegate for changes, until 2026-11-13 19:00Z · key ea5f78160088
- When
- · 2026-10-17 04:40 UTC
- Signature
- ✔ signed by Claude Code
- Hash
f157bcea0e329a44ac78d59b88fb11028b05d17aa820106d94ba128d777df124- Depends on
- 93be9ce4653e d23464c0ac60 f5c443378333
- Depended on by
- 8eae2f0e255c
- In bookmarks
- api-unknown-fields cancel-landed-runs diffusion-pin-bookmark draft-ready main oh-comment-body-file proposal-list-fast
- In tags
- v0.6.0 v0.6.1 v0.7.0 v0.8.0 v0.9.0 v0.9.1 v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.2 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.3 v0.9.4 v0.9.5 v0.9.6 v0.9.7 v0.9.8 v0.9.9
- Edits
- 5 atoms, 0 tree operations, 2 files
Browse the tree at this change
modified crates/hub/src/render/highlight.rs +21 −0
| … | 45 unchanged lines |
||
| 46 | 46 | cache().get_or(key, || lines_uncached(path, text)).as_ref().clone() |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | + | /// The longest line highlighted. syntect's time grows much faster than a |
|
| 50 | + | /// line's length: a minified 860 KB script of 31 lines took 6.2 s to |
|
| 51 | + | /// highlight, every time the hub had restarted, for a page nobody reads as |
|
| 52 | + | /// code. A text with a longer line is shown plain, as other forges do. |
|
| 53 | + | pub const MAX_HIGHLIGHTED_LINE: usize = 4_000; |
|
| 54 | + | ||
| 49 | 55 | fn lines_uncached(path: &str, text: &str) -> Vec<String> { |
|
| 56 | + | if text.lines().any(|l| l.len() > MAX_HIGHLIGHTED_LINE) { |
|
| 57 | + | return text.lines().map(super::escape).collect(); |
|
| 58 | + | } |
|
| 50 | 59 | let set = syntaxes(); |
|
| 51 | 60 | let ext = path.rsplit('.').next().unwrap_or(""); |
|
| 52 | 61 | let name = path.rsplit('/').next().unwrap_or(path); |
|
| … | 66 unchanged lines |
||
| 119 | 128 | assert_eq!(language("a/b/lib.rs"), Some("Rust")); |
|
| 120 | 129 | assert_eq!(language("Makefile"), Some("Makefile")); |
|
| 121 | 130 | assert_eq!(language("blob.bin"), None); |
|
| 131 | + | } |
|
| 132 | + | ||
| 133 | + | /// A minified file is shown plain, at once, its lines still escaped. |
|
| 134 | + | #[test] |
|
| 135 | + | fn a_text_with_a_very_long_line_is_shown_plain() { |
|
| 136 | + | let long = format!("var a=\"<\";{}\nb();\n", "x+".repeat(MAX_HIGHLIGHTED_LINE)); |
|
| 137 | + | let started = std::time::Instant::now(); |
|
| 138 | + | let l = lines("app.min.js", &long); |
|
| 139 | + | assert!(started.elapsed() < std::time::Duration::from_millis(500), "{:?}", started.elapsed()); |
|
| 140 | + | assert_eq!(l.len(), 2); |
|
| 141 | + | assert!(l[0].starts_with("var a="<";") && !l[0].contains("<span"), "{}", &l[0][..40]); |
|
| 142 | + | assert!(lines("app.js", "var a = 1;\n")[0].contains("<span"), "a normal file is highlighted"); |
|
| 122 | 143 | } |
|
| 123 | 144 | } |
modified docs/spec.md +1 −1
| … | 259 unchanged lines |
||
| 260 | 260 | ||
| 261 | 261 | ### 6.6 File view and blame |
|
| 262 | 262 | ||
| 263 | - | Text files render with syntect line highlighting, line numbers as anchors (`#L12`, `#L12-L20` selects a range), a "raw", "history", "blame", "edit" toolbar, and the file's last change. Markdown renders by default with a toggle to source. Images and PDFs render inline; other binaries offer download. Files above `render_limit` (default 2 MiB) show the first part and a download link. |
|
| 263 | + | Text files render with syntect line highlighting (a text with a line over 4 000 bytes, a minified file, is shown plain: syntect's time grows much faster than a line), line numbers as anchors (`#L12`, `#L12-L20` selects a range), a "raw", "history", "blame", "edit" toolbar, and the file's last change. Markdown renders by default with a toggle to source. Images and PDFs render inline; other binaries offer download. Files above `render_limit` (default 2 MiB) show the first part and a download link. |
|
| 264 | 264 | ||
| 265 | 265 | Blame uses `Repo::blame`: every line is exact, because each line is an atom with one authoring change. The gutter shows the change's short hash, author and age, grouped into runs, with the change's signature glyph. Blame is exact through rebase and squash, so there is no "ignore revs" file. |
|
| 266 | 266 | ||
| … | 278 unchanged lines |