5e8020aa38
Editor previews (colour swatches, arrows, embeds), the 18 obsidian-basics tasks (find and replace with regex, back/forward, auto-pair, footnotes, folding, search operators, split view, PDF/audio/ video embeds, spelling, drag and drop, pinned tabs, settings) and round 2: ```query blocks, stacked tabs, pop-out windows, tasks ticked inside embeds, text around an embed, embeds that follow edits, block menu shortcut. New crates: notescraft-regex (linear-time engine) and notescraft-syntax. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
111 lines
3.6 KiB
Rust
111 lines
3.6 KiB
Rust
//! The embedded TOML and TypeScript grammars: real samples highlight every expected
|
|
//! kind, hostile bodies return quickly without panicking, and known labels get no
|
|
//! suggestion.
|
|
|
|
use std::time::Instant;
|
|
|
|
use notescraft_syntax::{Highlighter, TokenKind, suggest_language};
|
|
|
|
/// A realistic TOML document: comments, bare keys, strings of every kind, numbers,
|
|
/// booleans, dates, tables, arrays and an inline table.
|
|
const TOML: &str = r#"# NotesCraft configuration
|
|
title = "NotesCraft"
|
|
port = 8080
|
|
enabled = true
|
|
ratio = 0.75
|
|
big = 1_000_000
|
|
hex = 0xDEAD_beef
|
|
label = 'literal string'
|
|
story = """
|
|
A multi-line
|
|
basic string."""
|
|
updated = 1979-05-27T07:32:00Z
|
|
tags = ["notes", "craft", 42]
|
|
|
|
[server]
|
|
host = "127.0.0.1"
|
|
timeout = 12.5
|
|
|
|
[[plugins]]
|
|
name = "graph"
|
|
|
|
inline = { a = 1, b = "two" }
|
|
"#;
|
|
|
|
/// A realistic TypeScript module: comments, strings, a template literal, numbers,
|
|
/// keywords, types and functions.
|
|
const TS: &str = r#"// NotesCraft engine demo
|
|
/* Block comment
|
|
spanning lines */
|
|
interface Command {
|
|
id: string;
|
|
run(): boolean;
|
|
}
|
|
enum Kind { Note, Canvas }
|
|
class Engine implements Command {
|
|
id = "engine";
|
|
run(): boolean {
|
|
const count = 42;
|
|
let name: string = `demo ${this.id}`;
|
|
if (count > 0) return true;
|
|
return false;
|
|
}
|
|
}
|
|
function boot(): void {
|
|
const engine = new Engine();
|
|
console.log(engine.run());
|
|
}
|
|
"#;
|
|
|
|
/// The kinds that appear anywhere in `body` highlighted as `language`.
|
|
fn kinds(language: &str, body: &str) -> Vec<TokenKind> {
|
|
let Some(mut highlighter) = Highlighter::new(language, body) else { panic!("{language} should be known") };
|
|
let lines = body.split_inclusive('\n').count();
|
|
highlighter.lines(0..lines).lines.iter().flatten().map(|span| span.kind).collect()
|
|
}
|
|
|
|
#[test]
|
|
fn toml_sample_shows_comment_string_number_keyword_and_property() {
|
|
let found = kinds("toml", TOML);
|
|
for want in [TokenKind::Comment, TokenKind::String, TokenKind::Number, TokenKind::Keyword, TokenKind::Property] {
|
|
assert!(found.contains(&want), "{want:?} missing from {found:?}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn typescript_sample_shows_comment_string_number_keyword_type_and_function() {
|
|
let found = kinds("ts", TS);
|
|
for want in [TokenKind::Comment, TokenKind::String, TokenKind::Number, TokenKind::Keyword, TokenKind::Type, TokenKind::Function] {
|
|
assert!(found.contains(&want), "{want:?} missing from {found:?}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn hostile_bodies_return_without_panicking_within_three_seconds() {
|
|
let start = Instant::now();
|
|
let bracket_flood = "[".repeat(50_000);
|
|
let megabyte_line = "[".repeat(1024 * 1024);
|
|
let unterminated_multiline = format!("story = \"\"\"\n{}", "still a string\n".repeat(40));
|
|
let unterminated_template = format!("const s = `open\n{}\n", "${ expr } and ${ `nested".repeat(20));
|
|
for (language, body) in [
|
|
("toml", bracket_flood.as_str()),
|
|
("ts", bracket_flood.as_str()),
|
|
("toml", megabyte_line.as_str()),
|
|
("ts", megabyte_line.as_str()),
|
|
("toml", unterminated_multiline.as_str()),
|
|
("ts", unterminated_template.as_str()),
|
|
] {
|
|
let Some(mut highlighter) = Highlighter::new(language, body) else { panic!("{language} should accept a hostile body") };
|
|
let lines = body.split_inclusive('\n').count();
|
|
let _ = highlighter.lines(0..lines);
|
|
}
|
|
assert!(start.elapsed().as_secs() < 3, "hostile bodies took {:?}", start.elapsed());
|
|
}
|
|
|
|
#[test]
|
|
fn the_embedded_labels_are_known_so_nothing_is_suggested() {
|
|
assert_eq!(suggest_language("toml"), None);
|
|
assert_eq!(suggest_language("ts"), None);
|
|
assert_eq!(suggest_language("typescript"), None);
|
|
}
|