From b0fa9e1dfeaa42c472ff8d980006dfcd5d29a6cc Mon Sep 17 00:00:00 2001 From: MuntasirSZN Date: Mon, 13 Oct 2025 17:17:20 +0600 Subject: [PATCH 1/3] feat: add typeScript syntax highlighting support for markdown code blocks - Added TypeScript/ts language support to Markdown.sublime-syntax.patch - Code blocks with ```typescript or ```ts will now use TypeScript syntax highlighting - Updated patch hunk headers to reflect added lines --- CHANGELOG.md | 1 + assets/patches/Markdown.sublime-syntax.patch | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c386a294..35df9a16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Support negative relative line ranges, e.g. `bat -r :-10` / `bat -r='-10:'`, see #3068 (@ajesipow) - Support context in line ranges, e.g. `bat -r 30::5` / `bat -r 30:40:5`, see #3345 (@cavanaug) - Add built-in 'minus' pager, e.g. `bat --pager=builtin` see PR #3402 (@academician) +- Syntax highlighting for typescript code blocks within Markdown files, see #3435 (@MuntasirSZN) ## Bugfixes diff --git a/assets/patches/Markdown.sublime-syntax.patch b/assets/patches/Markdown.sublime-syntax.patch index a295334c..9ac0f633 100644 --- a/assets/patches/Markdown.sublime-syntax.patch +++ b/assets/patches/Markdown.sublime-syntax.patch @@ -166,7 +166,7 @@ index 19dc685d..3a45ea05 100644 - match: ^\s*$\n? scope: invalid.illegal.non-terminated.bold-italic.markdown pop: true -@@ -1073,6 +1031,21 @@ contexts: +@@ -1073,6 +1031,36 @@ contexts: escape: '{{code_fence_escape}}' escape_captures: 0: meta.code-fence.definition.end.python.markdown-gfm @@ -185,10 +185,25 @@ index 19dc685d..3a45ea05 100644 + escape: '{{code_fence_escape}}' + escape_captures: + 0: meta.code-fence.definition.end.puppet.markdown-gfm ++ 1: punctuation.definition.raw.code-fence.end.markdown ++ - match: |- ++ (?x) ++ {{fenced_code_block_start}} ++ ((?i:typescript|ts)) ++ {{fenced_code_block_trailing_infostring_characters}} ++ captures: ++ 0: meta.code-fence.definition.begin.typescript.markdown-gfm ++ 2: punctuation.definition.raw.code-fence.begin.markdown ++ 5: constant.other.language-name.markdown ++ embed: scope:source.ts ++ embed_scope: markup.raw.code-fence.typescript.markdown-gfm ++ escape: '{{code_fence_escape}}' ++ escape_captures: ++ 0: meta.code-fence.definition.end.typescript.markdown-gfm 1: punctuation.definition.raw.code-fence.end.markdown - match: |- (?x) -@@ -1152,7 +1125,7 @@ contexts: +@@ -1152,7 +1155,7 @@ contexts: - match: |- (?x) {{fenced_code_block_start}} From 0139c9d9aeb604809c73d300dc18802845330bf7 Mon Sep 17 00:00:00 2001 From: MuntasirSZN Date: Tue, 14 Oct 2025 15:19:49 +0600 Subject: [PATCH 2/3] chore: add a test typescript file --- .../highlighted/Markdown/typescript.md | 57 +++++++++++++++++++ .../source/Markdown/typescript.md | 57 +++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 tests/syntax-tests/highlighted/Markdown/typescript.md create mode 100644 tests/syntax-tests/source/Markdown/typescript.md diff --git a/tests/syntax-tests/highlighted/Markdown/typescript.md b/tests/syntax-tests/highlighted/Markdown/typescript.md new file mode 100644 index 00000000..46239d39 --- /dev/null +++ b/tests/syntax-tests/highlighted/Markdown/typescript.md @@ -0,0 +1,57 @@ +# Typescript test + +```typescript +enum Status { + Pending, + InProgress, + Completed, +} + +interface Task { + id: number; + title: string; + status: Status; + assignee?: string; +} + +class TaskManager { + private tasks: T[] = []; + + addTask(task: T): void { + this.tasks.push(task); + } + + getTasksByStatus(status: Status): T[] { + return this.tasks.filter(task => task.status === status); + } + + async fetchTasks(): Promise { + // Simulate async fetch + return new Promise(resolve => setTimeout(() => resolve(this.tasks), 500)); + } +} + +// Type guard +function isTask(obj: any): obj is Task { + return typeof obj.id === 'number' && typeof obj.title === 'string'; +} + +// Usage +const manager = new TaskManager(); +manager.addTask({ id: 1, title: "Write docs", status: Status.Pending }); +manager.addTask({ id: 2, title: "Review PR", status: Status.InProgress, assignee: "Alice" }); + +(async () => { + const allTasks = await manager.fetchTasks(); + allTasks.forEach(task => { + if (isTask(task)) { + console.log(`Task #${task.id}: ${task.title} [${Status[task.status]}]`); + } + }); +})(); + +// Type assertion +const unknownValue: unknown = { id: 3, title: "Test", status: Status.Completed }; +const assertedTask = unknownValue as Task; +console.log(assertedTask.title); +``` diff --git a/tests/syntax-tests/source/Markdown/typescript.md b/tests/syntax-tests/source/Markdown/typescript.md new file mode 100644 index 00000000..e9efd63b --- /dev/null +++ b/tests/syntax-tests/source/Markdown/typescript.md @@ -0,0 +1,57 @@ +# Typescript test + +```typescript +enum Status { + Pending, + InProgress, + Completed, +} + +interface Task { + id: number; + title: string; + status: Status; + assignee?: string; +} + +class TaskManager { + private tasks: T[] = []; + + addTask(task: T): void { + this.tasks.push(task); + } + + getTasksByStatus(status: Status): T[] { + return this.tasks.filter(task => task.status === status); + } + + async fetchTasks(): Promise { + // Simulate async fetch + return new Promise(resolve => setTimeout(() => resolve(this.tasks), 500)); + } +} + +// Type guard +function isTask(obj: any): obj is Task { + return typeof obj.id === 'number' && typeof obj.title === 'string'; +} + +// Usage +const manager = new TaskManager(); +manager.addTask({ id: 1, title: "Write docs", status: Status.Pending }); +manager.addTask({ id: 2, title: "Review PR", status: Status.InProgress, assignee: "Alice" }); + +(async () => { + const allTasks = await manager.fetchTasks(); + allTasks.forEach(task => { + if (isTask(task)) { + console.log(`Task #${task.id}: ${task.title} [${Status[task.status]}]`); + } + }); +})(); + +// Type assertion +const unknownValue: unknown = { id: 3, title: "Test", status: Status.Completed }; +const assertedTask = unknownValue as Task; +console.log(assertedTask.title); +``` From 8920c738c5d3e2bcffc3b677486509af9557efe2 Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Tue, 14 Oct 2025 22:02:16 +0300 Subject: [PATCH 3/3] chore: update highlighted test Markdown with embedded Typescript file --- .../highlighted/Markdown/typescript.md | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/tests/syntax-tests/highlighted/Markdown/typescript.md b/tests/syntax-tests/highlighted/Markdown/typescript.md index 46239d39..90ce87ae 100644 --- a/tests/syntax-tests/highlighted/Markdown/typescript.md +++ b/tests/syntax-tests/highlighted/Markdown/typescript.md @@ -1,57 +1,57 @@ # Typescript test ```typescript -enum Status { - Pending, - InProgress, - Completed, +enum Status { + Pending, + InProgress, + Completed, } -interface Task { - id: number; - title: string; - status: Status; - assignee?: string; +interface Task { + id: number; + title: string; + status: Status; + assignee?: string; } -class TaskManager { - private tasks: T[] = []; +class TaskManager<T extends Task> { + private tasks: T[] = []; - addTask(task: T): void { - this.tasks.push(task); - } + addTask(task: T): void { + this.tasks.push(task); + } - getTasksByStatus(status: Status): T[] { - return this.tasks.filter(task => task.status === status); - } + getTasksByStatus(status: Status): T[] { + return this.tasks.filter(task => task.status === status); + } - async fetchTasks(): Promise { - // Simulate async fetch - return new Promise(resolve => setTimeout(() => resolve(this.tasks), 500)); - } + async fetchTasks(): Promise<T[]> { + // Simulate async fetch + return new Promise(resolve => setTimeout(() => resolve(this.tasks), 500)); + } } -// Type guard -function isTask(obj: any): obj is Task { - return typeof obj.id === 'number' && typeof obj.title === 'string'; +// Type guard +function isTask(obj: any): obj is Task { + return typeof obj.id === 'number' && typeof obj.title === 'string'; } -// Usage -const manager = new TaskManager(); -manager.addTask({ id: 1, title: "Write docs", status: Status.Pending }); -manager.addTask({ id: 2, title: "Review PR", status: Status.InProgress, assignee: "Alice" }); +// Usage +const manager = new TaskManager<Task>(); +manager.addTask({ id: 1, title: "Write docs", status: Status.Pending }); +manager.addTask({ id: 2, title: "Review PR", status: Status.InProgress, assignee: "Alice" }); -(async () => { - const allTasks = await manager.fetchTasks(); - allTasks.forEach(task => { - if (isTask(task)) { - console.log(`Task #${task.id}: ${task.title} [${Status[task.status]}]`); - } - }); -})(); +(async () => { + const allTasks = await manager.fetchTasks(); + allTasks.forEach(task => { + if (isTask(task)) { + console.log(`Task #${task.id}: ${task.title} [${Status[task.status]}]`); + } + }); +})(); -// Type assertion -const unknownValue: unknown = { id: 3, title: "Test", status: Status.Completed }; -const assertedTask = unknownValue as Task; -console.log(assertedTask.title); +// Type assertion +const unknownValue: unknown = { id: 3, title: "Test", status: Status.Completed }; +const assertedTask = unknownValue as Task; +console.log(assertedTask.title); ```