From 2754b7bc1229fb99e724af27fd987097132dbc80 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Jul 2025 06:00:19 +0000 Subject: [PATCH 1/6] Initial plan From 1b55d1adf3cd29b5c862f097a68583a99ea32d18 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Jul 2025 06:09:04 +0000 Subject: [PATCH 2/6] Fix syntax highlighting for React/TypeScript files by adding language detection Co-authored-by: dwjohnston <2467377+dwjohnston@users.noreply.github.com> --- .../GithubPermalink/GithubPermalinkBase.tsx | 6 +- .../SyntaxHighlight/SyntaxHighlight.tsx | 5 +- src/library/utils/getLanguageFromPath.test.ts | 69 +++++++++++++++ src/library/utils/getLanguageFromPath.ts | 88 +++++++++++++++++++ 4 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 src/library/utils/getLanguageFromPath.test.ts create mode 100644 src/library/utils/getLanguageFromPath.ts diff --git a/src/library/GithubPermalink/GithubPermalinkBase.tsx b/src/library/GithubPermalink/GithubPermalinkBase.tsx index 2529e0a..9798e8e 100644 --- a/src/library/GithubPermalink/GithubPermalinkBase.tsx +++ b/src/library/GithubPermalink/GithubPermalinkBase.tsx @@ -6,6 +6,7 @@ import { SyntaxHighlight } from "../SyntaxHighlight/SyntaxHighlight"; import { formatForLineExclusions } from "./formatLineExclusions"; import { CopySvg } from "../images/CopySvg"; import { CopyButton } from "../common/CopyButton/CopyButton"; +import { getLanguageFromPath } from "../utils/getLanguageFromPath"; export type GithubPermalinkBaseProps = { className?: string; @@ -26,6 +27,7 @@ export function GithubPermalinkBase(props: GithubPermalinkBaseProps) { if (data.status === "ok") { const formatedLineExclusions = formatForLineExclusions(data, excludeLines); + const language = getLanguageFromPath(data.path); const clipboard = formatedLineExclusions.reduce((acc, cur) => { if (cur.isExclude) { @@ -41,11 +43,11 @@ export function GithubPermalinkBase(props: GithubPermalinkBaseProps) { {formatedLineExclusions.map((v) => { if (v.isExclude) { - return + return } - return + return })} diff --git a/src/library/SyntaxHighlight/SyntaxHighlight.tsx b/src/library/SyntaxHighlight/SyntaxHighlight.tsx index c2ebe3f..c1ab539 100644 --- a/src/library/SyntaxHighlight/SyntaxHighlight.tsx +++ b/src/library/SyntaxHighlight/SyntaxHighlight.tsx @@ -126,9 +126,10 @@ export function SyntaxHighlight(props: { text: string; startingLineNumber?: number; className?: string; + language?: string; }) { - const { startingLineNumber, text, className } = props; + const { startingLineNumber, text, className, language = "javascript" } = props; const isDarkMode = useMediaQuery({ query: "(prefers-color-scheme: dark)" }) @@ -141,6 +142,6 @@ export function SyntaxHighlight(props: { }, []) - return {text} + return {text} } diff --git a/src/library/utils/getLanguageFromPath.test.ts b/src/library/utils/getLanguageFromPath.test.ts new file mode 100644 index 0000000..125600f --- /dev/null +++ b/src/library/utils/getLanguageFromPath.test.ts @@ -0,0 +1,69 @@ +import { describe, it, expect } from 'vitest'; +import { getLanguageFromPath } from './getLanguageFromPath'; + +describe('getLanguageFromPath', () => { + it('should return typescript for .tsx files', () => { + expect(getLanguageFromPath('ReactRenders3.tsx')).toBe('typescript'); + }); + + it('should return typescript for .ts files', () => { + expect(getLanguageFromPath('component.ts')).toBe('typescript'); + }); + + it('should return javascript for .js files', () => { + expect(getLanguageFromPath('script.js')).toBe('javascript'); + }); + + it('should return javascript for .jsx files', () => { + expect(getLanguageFromPath('component.jsx')).toBe('javascript'); + }); + + it('should return python for .py files', () => { + expect(getLanguageFromPath('script.py')).toBe('python'); + }); + + it('should return go for .go files', () => { + expect(getLanguageFromPath('main.go')).toBe('go'); + }); + + it('should return html for .html files', () => { + expect(getLanguageFromPath('index.html')).toBe('html'); + }); + + it('should return css for .css files', () => { + expect(getLanguageFromPath('styles.css')).toBe('css'); + }); + + it('should return json for .json files', () => { + expect(getLanguageFromPath('package.json')).toBe('json'); + }); + + it('should return markdown for .md files', () => { + expect(getLanguageFromPath('README.md')).toBe('markdown'); + }); + + it('should return yaml for .yaml files', () => { + expect(getLanguageFromPath('config.yaml')).toBe('yaml'); + }); + + it('should return yaml for .yml files', () => { + expect(getLanguageFromPath('config.yml')).toBe('yaml'); + }); + + it('should return javascript as fallback for unknown extensions', () => { + expect(getLanguageFromPath('file.unknown')).toBe('javascript'); + }); + + it('should return javascript as fallback for files without extension', () => { + expect(getLanguageFromPath('README')).toBe('javascript'); + }); + + it('should handle paths with multiple dots', () => { + expect(getLanguageFromPath('src/components/MyComponent.tsx')).toBe('typescript'); + }); + + it('should handle case insensitive extensions', () => { + expect(getLanguageFromPath('Component.TSX')).toBe('typescript'); + expect(getLanguageFromPath('Script.JS')).toBe('javascript'); + }); +}); \ No newline at end of file diff --git a/src/library/utils/getLanguageFromPath.ts b/src/library/utils/getLanguageFromPath.ts new file mode 100644 index 0000000..9fd8210 --- /dev/null +++ b/src/library/utils/getLanguageFromPath.ts @@ -0,0 +1,88 @@ +/** + * Maps file extensions to syntax highlighting languages supported by react-syntax-highlighter + */ +export function getLanguageFromPath(filePath: string): string { + const extension = filePath.split('.').pop()?.toLowerCase(); + + switch (extension) { + case 'js': + case 'jsx': + case 'mjs': + case 'cjs': + return 'javascript'; + case 'ts': + case 'tsx': + return 'typescript'; + case 'py': + case 'python': + return 'python'; + case 'java': + return 'java'; + case 'cpp': + case 'cc': + case 'cxx': + case 'c++': + return 'cpp'; + case 'c': + case 'h': + return 'c'; + case 'cs': + return 'csharp'; + case 'php': + return 'php'; + case 'rb': + case 'ruby': + return 'ruby'; + case 'go': + return 'go'; + case 'rs': + return 'rust'; + case 'swift': + return 'swift'; + case 'kt': + case 'kts': + return 'kotlin'; + case 'scala': + return 'scala'; + case 'sh': + case 'bash': + return 'bash'; + case 'ps1': + return 'powershell'; + case 'sql': + return 'sql'; + case 'html': + case 'htm': + return 'html'; + case 'css': + return 'css'; + case 'scss': + case 'sass': + return 'scss'; + case 'json': + return 'json'; + case 'xml': + return 'xml'; + case 'yaml': + case 'yml': + return 'yaml'; + case 'md': + case 'markdown': + return 'markdown'; + case 'dockerfile': + return 'dockerfile'; + case 'r': + return 'r'; + case 'dart': + return 'dart'; + case 'lua': + return 'lua'; + case 'perl': + case 'pl': + return 'perl'; + case 'vim': + return 'vim'; + default: + return 'javascript'; // fallback to javascript for unknown extensions + } +} \ No newline at end of file From 08b446f6680b80743c7f654d195691cd20f48054 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Jul 2025 06:13:44 +0000 Subject: [PATCH 3/6] Add React TSX syntax highlighting demo story and verify fix works Co-authored-by: dwjohnston <2467377+dwjohnston@users.noreply.github.com> --- .../GithubPermalinkBase.stories.tsx | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/library/GithubPermalink/GithubPermalinkBase.stories.tsx b/src/library/GithubPermalink/GithubPermalinkBase.stories.tsx index 795f525..69ddfd3 100644 --- a/src/library/GithubPermalink/GithubPermalinkBase.stories.tsx +++ b/src/library/GithubPermalink/GithubPermalinkBase.stories.tsx @@ -89,6 +89,30 @@ schemas.forEach(function (schema) { }) ` +const reactTsxCode = ` +export function ChildrenStyleOne() { + const [value, setValue] = React.useState(0) + return
+ + {/* ò Here we declare the RenderTracker directly in the component */} + +
+} + +export function ChildrenStyleTwo(props: React.PropsWithChildren) { + const [value, setValue] = React.useState(0) + return
+ + {/* ò Here, it is passed from the parent via the 'children' prop */} + {props.children} +
+} +` + export const WithLineExclusionsRealCode: Story = { render: () => ( + ), +}; + +export const ReactTsxSyntaxHighlighting: Story = { + render: () => ( + ), }; \ No newline at end of file From 242681a9df549adcc9a7fca0d8421d98b7e312c4 Mon Sep 17 00:00:00 2001 From: David Johnston Date: Fri, 26 Sep 2025 11:36:17 +1000 Subject: [PATCH 4/6] Sample tsx --- sample_files/sample1.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 sample_files/sample1.tsx diff --git a/sample_files/sample1.tsx b/sample_files/sample1.tsx new file mode 100644 index 0000000..5a79915 --- /dev/null +++ b/sample_files/sample1.tsx @@ -0,0 +1,11 @@ +function Foo(props: { name: string }) { + return
{props.name}
+ +} + +export function Demo() { + return
+ + Hello world! +
+} \ No newline at end of file From 286cac615d484a089bc01cd71d54525f3866c435 Mon Sep 17 00:00:00 2001 From: David Johnston Date: Fri, 26 Sep 2025 11:44:15 +1000 Subject: [PATCH 5/6] Just don't set the language --- .../GithubPermalink/GithubPermalink.stories.tsx | 17 +++++++++++++++++ src/library/SyntaxHighlight/SyntaxHighlight.tsx | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/library/GithubPermalink/GithubPermalink.stories.tsx b/src/library/GithubPermalink/GithubPermalink.stories.tsx index e2a07fe..095dfaa 100644 --- a/src/library/GithubPermalink/GithubPermalink.stories.tsx +++ b/src/library/GithubPermalink/GithubPermalink.stories.tsx @@ -23,6 +23,23 @@ export const Primary: Story = { ), }; +export const DifferentLanguages: Story = { + render: () => (
+

Go

+ + +

JavaScript

+ + +

SQL

+ + +

TSX

+ +
+ ), +}; + export const WithBackground: Story = { render: () => (
diff --git a/src/library/SyntaxHighlight/SyntaxHighlight.tsx b/src/library/SyntaxHighlight/SyntaxHighlight.tsx index c1ab539..166b7ea 100644 --- a/src/library/SyntaxHighlight/SyntaxHighlight.tsx +++ b/src/library/SyntaxHighlight/SyntaxHighlight.tsx @@ -125,7 +125,7 @@ const noTheme = { export function SyntaxHighlight(props: { text: string; startingLineNumber?: number; - className?: string; + className?: string; language?: string; }) { @@ -142,6 +142,6 @@ export function SyntaxHighlight(props: { }, []) - return {text} + return {text} } From f412338f00f17691ff20aea844ee1307e37e8742 Mon Sep 17 00:00:00 2001 From: David Johnston Date: Fri, 26 Sep 2025 11:47:19 +1000 Subject: [PATCH 6/6] Get rid of 'getLanguageFromPath' stuff --- .../GithubPermalinkBase.stories.tsx | 43 --------- .../GithubPermalink/GithubPermalinkBase.tsx | 6 +- .../SyntaxHighlight/SyntaxHighlight.tsx | 2 +- src/library/utils/getLanguageFromPath.test.ts | 69 --------------- src/library/utils/getLanguageFromPath.ts | 88 ------------------- 5 files changed, 3 insertions(+), 205 deletions(-) delete mode 100644 src/library/utils/getLanguageFromPath.test.ts delete mode 100644 src/library/utils/getLanguageFromPath.ts diff --git a/src/library/GithubPermalink/GithubPermalinkBase.stories.tsx b/src/library/GithubPermalink/GithubPermalinkBase.stories.tsx index 69ddfd3..795f525 100644 --- a/src/library/GithubPermalink/GithubPermalinkBase.stories.tsx +++ b/src/library/GithubPermalink/GithubPermalinkBase.stories.tsx @@ -89,30 +89,6 @@ schemas.forEach(function (schema) { }) ` -const reactTsxCode = ` -export function ChildrenStyleOne() { - const [value, setValue] = React.useState(0) - return
- - {/* ò Here we declare the RenderTracker directly in the component */} - -
-} - -export function ChildrenStyleTwo(props: React.PropsWithChildren) { - const [value, setValue] = React.useState(0) - return
- - {/* ò Here, it is passed from the parent via the 'children' prop */} - {props.children} -
-} -` - export const WithLineExclusionsRealCode: Story = { render: () => ( - ), -}; - -export const ReactTsxSyntaxHighlighting: Story = { - render: () => ( - ), }; \ No newline at end of file diff --git a/src/library/GithubPermalink/GithubPermalinkBase.tsx b/src/library/GithubPermalink/GithubPermalinkBase.tsx index 9798e8e..e88963f 100644 --- a/src/library/GithubPermalink/GithubPermalinkBase.tsx +++ b/src/library/GithubPermalink/GithubPermalinkBase.tsx @@ -6,7 +6,6 @@ import { SyntaxHighlight } from "../SyntaxHighlight/SyntaxHighlight"; import { formatForLineExclusions } from "./formatLineExclusions"; import { CopySvg } from "../images/CopySvg"; import { CopyButton } from "../common/CopyButton/CopyButton"; -import { getLanguageFromPath } from "../utils/getLanguageFromPath"; export type GithubPermalinkBaseProps = { className?: string; @@ -27,7 +26,6 @@ export function GithubPermalinkBase(props: GithubPermalinkBaseProps) { if (data.status === "ok") { const formatedLineExclusions = formatForLineExclusions(data, excludeLines); - const language = getLanguageFromPath(data.path); const clipboard = formatedLineExclusions.reduce((acc, cur) => { if (cur.isExclude) { @@ -43,11 +41,11 @@ export function GithubPermalinkBase(props: GithubPermalinkBaseProps) { {formatedLineExclusions.map((v) => { if (v.isExclude) { - return + return } - return + return })} diff --git a/src/library/SyntaxHighlight/SyntaxHighlight.tsx b/src/library/SyntaxHighlight/SyntaxHighlight.tsx index 166b7ea..a7575b0 100644 --- a/src/library/SyntaxHighlight/SyntaxHighlight.tsx +++ b/src/library/SyntaxHighlight/SyntaxHighlight.tsx @@ -129,7 +129,7 @@ export function SyntaxHighlight(props: { language?: string; }) { - const { startingLineNumber, text, className, language = "javascript" } = props; + const { startingLineNumber, text, className } = props; const isDarkMode = useMediaQuery({ query: "(prefers-color-scheme: dark)" }) diff --git a/src/library/utils/getLanguageFromPath.test.ts b/src/library/utils/getLanguageFromPath.test.ts deleted file mode 100644 index 125600f..0000000 --- a/src/library/utils/getLanguageFromPath.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { getLanguageFromPath } from './getLanguageFromPath'; - -describe('getLanguageFromPath', () => { - it('should return typescript for .tsx files', () => { - expect(getLanguageFromPath('ReactRenders3.tsx')).toBe('typescript'); - }); - - it('should return typescript for .ts files', () => { - expect(getLanguageFromPath('component.ts')).toBe('typescript'); - }); - - it('should return javascript for .js files', () => { - expect(getLanguageFromPath('script.js')).toBe('javascript'); - }); - - it('should return javascript for .jsx files', () => { - expect(getLanguageFromPath('component.jsx')).toBe('javascript'); - }); - - it('should return python for .py files', () => { - expect(getLanguageFromPath('script.py')).toBe('python'); - }); - - it('should return go for .go files', () => { - expect(getLanguageFromPath('main.go')).toBe('go'); - }); - - it('should return html for .html files', () => { - expect(getLanguageFromPath('index.html')).toBe('html'); - }); - - it('should return css for .css files', () => { - expect(getLanguageFromPath('styles.css')).toBe('css'); - }); - - it('should return json for .json files', () => { - expect(getLanguageFromPath('package.json')).toBe('json'); - }); - - it('should return markdown for .md files', () => { - expect(getLanguageFromPath('README.md')).toBe('markdown'); - }); - - it('should return yaml for .yaml files', () => { - expect(getLanguageFromPath('config.yaml')).toBe('yaml'); - }); - - it('should return yaml for .yml files', () => { - expect(getLanguageFromPath('config.yml')).toBe('yaml'); - }); - - it('should return javascript as fallback for unknown extensions', () => { - expect(getLanguageFromPath('file.unknown')).toBe('javascript'); - }); - - it('should return javascript as fallback for files without extension', () => { - expect(getLanguageFromPath('README')).toBe('javascript'); - }); - - it('should handle paths with multiple dots', () => { - expect(getLanguageFromPath('src/components/MyComponent.tsx')).toBe('typescript'); - }); - - it('should handle case insensitive extensions', () => { - expect(getLanguageFromPath('Component.TSX')).toBe('typescript'); - expect(getLanguageFromPath('Script.JS')).toBe('javascript'); - }); -}); \ No newline at end of file diff --git a/src/library/utils/getLanguageFromPath.ts b/src/library/utils/getLanguageFromPath.ts deleted file mode 100644 index 9fd8210..0000000 --- a/src/library/utils/getLanguageFromPath.ts +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Maps file extensions to syntax highlighting languages supported by react-syntax-highlighter - */ -export function getLanguageFromPath(filePath: string): string { - const extension = filePath.split('.').pop()?.toLowerCase(); - - switch (extension) { - case 'js': - case 'jsx': - case 'mjs': - case 'cjs': - return 'javascript'; - case 'ts': - case 'tsx': - return 'typescript'; - case 'py': - case 'python': - return 'python'; - case 'java': - return 'java'; - case 'cpp': - case 'cc': - case 'cxx': - case 'c++': - return 'cpp'; - case 'c': - case 'h': - return 'c'; - case 'cs': - return 'csharp'; - case 'php': - return 'php'; - case 'rb': - case 'ruby': - return 'ruby'; - case 'go': - return 'go'; - case 'rs': - return 'rust'; - case 'swift': - return 'swift'; - case 'kt': - case 'kts': - return 'kotlin'; - case 'scala': - return 'scala'; - case 'sh': - case 'bash': - return 'bash'; - case 'ps1': - return 'powershell'; - case 'sql': - return 'sql'; - case 'html': - case 'htm': - return 'html'; - case 'css': - return 'css'; - case 'scss': - case 'sass': - return 'scss'; - case 'json': - return 'json'; - case 'xml': - return 'xml'; - case 'yaml': - case 'yml': - return 'yaml'; - case 'md': - case 'markdown': - return 'markdown'; - case 'dockerfile': - return 'dockerfile'; - case 'r': - return 'r'; - case 'dart': - return 'dart'; - case 'lua': - return 'lua'; - case 'perl': - case 'pl': - return 'perl'; - case 'vim': - return 'vim'; - default: - return 'javascript'; // fallback to javascript for unknown extensions - } -} \ No newline at end of file