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}} diff --git a/tests/syntax-tests/highlighted/Markdown/typescript.md b/tests/syntax-tests/highlighted/Markdown/typescript.md new file mode 100644 index 00000000..90ce87ae --- /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<T extends Task> { + 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<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'; +} + +// 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]}]`); + } + }); +})(); + +// 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); +```