mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-02 10:29:02 +08:00
refactor(CodeEditor): support file extensions explicitly (#9707)
This commit is contained in:
parent
1ee57f1385
commit
4dbe5c8055
@ -0,0 +1,43 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { getNormalizedExtension } from '../utils'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getExtensionByLanguage: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('@renderer/utils/code-language', () => ({
|
||||
getExtensionByLanguage: mocks.getExtensionByLanguage
|
||||
}))
|
||||
|
||||
describe('getNormalizedExtension', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should return custom mapping for custom language', async () => {
|
||||
mocks.getExtensionByLanguage.mockReturnValue(undefined)
|
||||
await expect(getNormalizedExtension('svg')).resolves.toBe('xml')
|
||||
await expect(getNormalizedExtension('SVG')).resolves.toBe('xml')
|
||||
})
|
||||
|
||||
it('should prefer custom mapping when both custom and linguist exist', async () => {
|
||||
mocks.getExtensionByLanguage.mockReturnValue('.svg')
|
||||
await expect(getNormalizedExtension('svg')).resolves.toBe('xml')
|
||||
})
|
||||
|
||||
it('should return linguist mapping when available (strip leading dot)', async () => {
|
||||
mocks.getExtensionByLanguage.mockReturnValue('.ts')
|
||||
await expect(getNormalizedExtension('TypeScript')).resolves.toBe('ts')
|
||||
})
|
||||
|
||||
it('should return extension when input already looks like extension (leading dot)', async () => {
|
||||
mocks.getExtensionByLanguage.mockReturnValue(undefined)
|
||||
await expect(getNormalizedExtension('.json')).resolves.toBe('json')
|
||||
})
|
||||
|
||||
it('should return language as-is when no rules matched', async () => {
|
||||
mocks.getExtensionByLanguage.mockReturnValue(undefined)
|
||||
await expect(getNormalizedExtension('unknownLanguage')).resolves.toBe('unknownLanguage')
|
||||
})
|
||||
})
|
||||
@ -8,7 +8,9 @@ import { getNormalizedExtension } from './utils'
|
||||
|
||||
const logger = loggerService.withContext('CodeEditorHooks')
|
||||
|
||||
// 语言对应的 linter 加载器
|
||||
/** 语言对应的 linter 加载器
|
||||
* key: 语言文件扩展名(不包含 `.`)
|
||||
*/
|
||||
const linterLoaders: Record<string, () => Promise<any>> = {
|
||||
json: async () => {
|
||||
const jsonParseLinter = await import('@codemirror/lang-json').then((mod) => mod.jsonParseLinter)
|
||||
@ -64,13 +66,15 @@ async function loadLanguageExtension(language: string): Promise<Extension | null
|
||||
* 加载 linter 扩展
|
||||
*/
|
||||
async function loadLinterExtension(language: string): Promise<Extension | null> {
|
||||
const loader = linterLoaders[language]
|
||||
const fileExt = await getNormalizedExtension(language)
|
||||
|
||||
const loader = linterLoaders[fileExt]
|
||||
if (!loader) return null
|
||||
|
||||
try {
|
||||
return await loader()
|
||||
} catch (error) {
|
||||
logger.debug(`Failed to load linter for ${language}`, error as Error)
|
||||
logger.debug(`Failed to load linter for ${language} (${fileExt})`, error as Error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,13 @@ export interface CodeEditorProps {
|
||||
value: string
|
||||
/** Placeholder when the editor content is empty. */
|
||||
placeholder?: string | HTMLElement
|
||||
/** Code language, supports aliases. */
|
||||
/**
|
||||
* Code language string.
|
||||
* - Case-insensitive.
|
||||
* - Supports common names: javascript, json, python, etc.
|
||||
* - Supports aliases: c#/csharp, objective-c++/obj-c++/objc++, etc.
|
||||
* - Supports file extensions: .cpp/cpp, .js/js, .py/py, etc.
|
||||
*/
|
||||
language: string
|
||||
/** Fired when ref.save() is called or the save shortcut is triggered. */
|
||||
onSave?: (newContent: string) => void
|
||||
|
||||
@ -13,6 +13,7 @@ const _customLanguageExtensions: Record<string, string> = {
|
||||
* 获取语言的扩展名,用于 @uiw/codemirror-extensions-langs
|
||||
* - 先搜索自定义扩展名
|
||||
* - 再搜索 github linguist 扩展名
|
||||
* - 最后假定名称已经是扩展名
|
||||
* @param language 语言名称
|
||||
* @returns 扩展名(不包含 `.`)
|
||||
*/
|
||||
@ -29,6 +30,11 @@ export async function getNormalizedExtension(language: string) {
|
||||
return linguistExt.slice(1)
|
||||
}
|
||||
|
||||
// 如果语言名称像扩展名
|
||||
if (language.startsWith('.') && language.length > 1) {
|
||||
return language.slice(1)
|
||||
}
|
||||
|
||||
// 回退到语言名称
|
||||
return language
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user