diff --git a/Packages/Shared/CmuxSyntaxHighlighting/Sources/CmuxSyntaxHighlighting/LanguageCatalog.swift b/Packages/Shared/CmuxSyntaxHighlighting/Sources/CmuxSyntaxHighlighting/LanguageCatalog.swift index febb0cadbe6e..c005b4e248e3 100644 --- a/Packages/Shared/CmuxSyntaxHighlighting/Sources/CmuxSyntaxHighlighting/LanguageCatalog.swift +++ b/Packages/Shared/CmuxSyntaxHighlighting/Sources/CmuxSyntaxHighlighting/LanguageCatalog.swift @@ -43,6 +43,10 @@ public struct LanguageCatalog: Sendable { "go": "go", "rs": "rust", "rb": "ruby", + "ex": "elixir", + "exs": "elixir", + "erl": "erlang", + "hrl": "erlang", "java": "java", "kt": "kotlin", "kts": "kotlin", diff --git a/Packages/Shared/CmuxSyntaxHighlighting/Tests/CmuxSyntaxHighlightingTests/LanguageCatalogTests.swift b/Packages/Shared/CmuxSyntaxHighlighting/Tests/CmuxSyntaxHighlightingTests/LanguageCatalogTests.swift index f3d4d3cb4156..d807e0217913 100644 --- a/Packages/Shared/CmuxSyntaxHighlighting/Tests/CmuxSyntaxHighlightingTests/LanguageCatalogTests.swift +++ b/Packages/Shared/CmuxSyntaxHighlighting/Tests/CmuxSyntaxHighlightingTests/LanguageCatalogTests.swift @@ -16,6 +16,14 @@ struct LanguageCatalogTests { #expect(catalog.language(forExtension: ".py") == "python") } + @Test("Maps BEAM source extensions to highlight.js ids") + func mapsBeamExtensions() { + #expect(catalog.language(forExtension: "ex") == "elixir") + #expect(catalog.language(forExtension: "exs") == "elixir") + #expect(catalog.language(forExtension: "erl") == "erlang") + #expect(catalog.language(forExtension: "hrl") == "erlang") + } + @Test("Unknown extensions return nil") func unknownExtensionIsNil() { #expect(catalog.language(forExtension: "bin") == nil) diff --git a/Sources/Panels/FilePreviewPanel.swift b/Sources/Panels/FilePreviewPanel.swift index 1f5923558425..24c9163daa25 100644 --- a/Sources/Panels/FilePreviewPanel.swift +++ b/Sources/Panels/FilePreviewPanel.swift @@ -943,7 +943,8 @@ enum FilePreviewKindResolver { private static let textExtensions: Set = [ "bash", "c", "cc", "cfg", "conf", "cpp", "cs", "css", "csv", "cts", "env", - "fish", "go", "h", "hpp", "htm", "html", "ini", "java", "js", "json", + "erl", "ex", "exs", "fish", "go", "h", "hpp", "hrl", "htm", "html", "ini", + "java", "js", "json", "jsx", "kt", "log", "m", "markdown", "md", "mdx", "mm", "mts", "plist", "py", "rb", "rs", "sh", "sql", "swift", "toml", "ts", "tsx", "tsv", "txt", "xml", "yaml", "yml", "zsh" diff --git a/cmuxTests/FilePreviewKindResolverTests.swift b/cmuxTests/FilePreviewKindResolverTests.swift index c97940547159..78714fc53be0 100644 --- a/cmuxTests/FilePreviewKindResolverTests.swift +++ b/cmuxTests/FilePreviewKindResolverTests.swift @@ -26,6 +26,25 @@ struct FilePreviewKindResolverTests { } } + @Test("Elixir and Erlang source files route directly to text preview") + func beamSourceFilesRouteDirectlyToTextPreview() throws { + // .exs collides with the Logic sampler UTI (com.apple.logic.exs), which + // conforms to audiovisualContent and would otherwise open as media. + for fileExtension in ["ex", "exs", "erl", "hrl"] { + let url = try temporaryFile( + extension: fileExtension, + contents: "defmodule Greeter do\n def hello, do: :world\nend\n" + ) + defer { try? FileManager.default.removeItem(at: url) } + + #expect( + FilePreviewKindResolver.initialMode(for: url) == .text, + "Expected .\(fileExtension) to avoid the QuickLook/media backend before async resolution." + ) + #expect(FilePreviewKindResolver.mode(for: url) == .text) + } + } + @Test("Movie file extensions keep media preview") func movieFileExtensionsKeepMediaPreview() throws { for fileExtension in ["mov", "mp4"] {