-
-
Notifications
You must be signed in to change notification settings - Fork 382
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support grammar injection (#48)
- Loading branch information
Showing
12 changed files
with
344 additions
and
198 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,194 +1,8 @@ | ||
import fs from 'fs-extra' | ||
import { BUNDLED_LANGUAGES, BUNDLED_THEMES } from 'shiki' | ||
import fg from 'fast-glob' | ||
|
||
const allLangFiles = await fg('*.json', { | ||
cwd: './node_modules/shiki/languages', | ||
absolute: true, | ||
onlyFiles: true, | ||
}) | ||
|
||
const comments = ` | ||
/** | ||
* Generated by scripts/prepare.ts | ||
*/ | ||
`.trim() | ||
import { prepareLangs } from './prepare/langs' | ||
import { prepareTheme } from './prepare/themes' | ||
|
||
await fs.ensureDir('./src/assets/langs') | ||
await fs.emptyDir('./src/assets/langs') | ||
|
||
allLangFiles.sort() | ||
for (const file of allLangFiles) { | ||
const content = await fs.readJSON(file) | ||
const lang = BUNDLED_LANGUAGES.find(i => i.id === content.name) | ||
if (!lang) { | ||
console.warn(`unknown ${content.name}`) | ||
continue | ||
} | ||
|
||
const json = { | ||
...content, | ||
name: content.name || lang.id, | ||
scopeName: content.scopeName || lang.scopeName, | ||
displayName: lang.displayName, | ||
aliases: lang.aliases, | ||
embeddedLangs: lang.embeddedLangs, | ||
balancedBracketSelectors: lang.balancedBracketSelectors, | ||
unbalancedBracketSelectors: lang.unbalancedBracketSelectors, | ||
} | ||
|
||
// F# and Markdown has circular dependency | ||
if (lang.id === 'fsharp') | ||
json.embeddedLangs = json.embeddedLangs.filter((i: string) => i !== 'markdown') | ||
|
||
const embedded = (json.embeddedLangs || []) as string[] | ||
|
||
await fs.writeFile(`./src/assets/langs/${lang.id}.ts`, `${comments} | ||
import type { LanguageRegistration } from 'shikiji-core' | ||
${embedded.map(i => `import ${i.replace(/[^\w]/g, '_')} from './${i}'`).join('\n')} | ||
const lang = Object.freeze(${JSON.stringify(json)}) as unknown as LanguageRegistration | ||
export default [ | ||
${[ | ||
...embedded.map(i => ` ...${i.replace(/[^\w]/g, '_')}`), | ||
' lang', | ||
].join(',\n') || ''} | ||
] | ||
`, 'utf-8') | ||
} | ||
|
||
async function writeLanguageBundleIndex(fileName: string, ids: string[]) { | ||
const bundled = ids.map(id => BUNDLED_LANGUAGES.find(i => i.id === id)!) | ||
|
||
const info = bundled.map(i => ({ | ||
id: i.id, | ||
name: i.displayName, | ||
aliases: i.aliases, | ||
import: `__(() => import('./langs/${i.id}')) as DynamicLangReg__`, | ||
}) as const) | ||
.sort((a, b) => a.id.localeCompare(b.id)) | ||
|
||
const type = info.flatMap(i => [...i.aliases || [], i.id]).sort().map(i => `'${i}'`).join(' | ') | ||
|
||
await fs.writeFile( | ||
`src/assets/${fileName}.ts`, | ||
`${comments} | ||
import type { LanguageRegistration } from 'shikiji-core' | ||
type DynamicLangReg = () => Promise<{ default: LanguageRegistration[] }> | ||
export interface BundledLanguageInfo { | ||
id: string | ||
name: string | ||
import: DynamicLangReg | ||
aliases?: string[] | ||
} | ||
export const bundledLanguagesInfo: BundledLanguageInfo[] = ${JSON.stringify(info, null, 2).replace(/"__|__"/g, '').replace(/"/g, '\'')} | ||
export const bundledLanguagesBase = Object.fromEntries(bundledLanguagesInfo.map(i => [i.id, i.import])) | ||
export const bundledLanguagesAlias = Object.fromEntries(bundledLanguagesInfo.flatMap(i => i.aliases?.map(a => [a, i.import]) || [])) | ||
export type BuiltinLanguage = ${type} | ||
export const bundledLanguages = { | ||
...bundledLanguagesBase, | ||
...bundledLanguagesAlias, | ||
} as Record<BuiltinLanguage, DynamicLangReg> | ||
`, | ||
'utf-8', | ||
) | ||
|
||
await fs.writeJSON( | ||
`src/assets/${fileName}.json`, | ||
BUNDLED_LANGUAGES.map(i => ({ | ||
id: i.id, | ||
name: i.displayName, | ||
aliases: i.aliases, | ||
})), | ||
{ spaces: 2 }, | ||
) | ||
} | ||
|
||
await writeLanguageBundleIndex('langs', BUNDLED_LANGUAGES.map(i => i.id)) | ||
// await writeLanguageBundleIndex('langs-common', BundleCommonLangs) | ||
|
||
const themes = BUNDLED_THEMES.sort() | ||
.filter(i => i !== 'css-variables') | ||
.map((id) => { | ||
const theme = fs.readJSONSync(`./node_modules/shiki/themes/${id}.json`) | ||
|
||
return { | ||
id, | ||
name: guessThemeName(id, theme), | ||
type: guessThemeType(id, theme), | ||
import: `__(() => import('shiki/themes/${id}.json')) as unknown as DynamicThemeReg__`, | ||
} | ||
}) | ||
|
||
await fs.writeFile( | ||
'src/assets/themes.ts', | ||
`${comments} | ||
import type { ThemeRegistrationRaw } from 'shikiji-core' | ||
type DynamicThemeReg = () => Promise<{ default: ThemeRegistrationRaw }> | ||
export interface BundledThemeInfo { | ||
id: string | ||
name: string | ||
type: 'light' | 'dark' | ||
import: DynamicThemeReg | ||
} | ||
export const bundledThemesInfo: BundledThemeInfo[] = ${JSON.stringify(themes, null, 2).replace(/"__|__"/g, '')} | ||
export type BuiltinTheme = ${themes.map(i => `'${i.id}'`).join(' | ')} | ||
export const bundledThemes = Object.fromEntries(bundledThemesInfo.map(i => [i.id, i.import])) as Record<BuiltinTheme, DynamicThemeReg> | ||
`, | ||
'utf-8', | ||
) | ||
|
||
await fs.writeJSON( | ||
'src/assets/themes.json', | ||
BUNDLED_THEMES | ||
.filter(i => i !== 'css-variables') | ||
.map(i => ({ | ||
id: i, | ||
})), | ||
{ spaces: 2 }, | ||
) | ||
|
||
function isLightColor(hex: string) { | ||
const [r, g, b] = hex.slice(1).match(/.{2}/g)!.map(i => Number.parseInt(i, 16)) | ||
return (r * 299 + g * 587 + b * 114) / 1000 > 128 | ||
} | ||
|
||
function guessThemeType(id: string, theme: any) { | ||
let color | ||
if (id.includes('dark') || id.includes('dimmed') || id.includes('black')) | ||
color = 'dark' | ||
else if (id.includes('light') || id.includes('white') || id === 'slack-ochin') | ||
color = 'light' | ||
else if (theme.colors.background) | ||
color = isLightColor(theme.colors.background) ? 'light' : 'dark' | ||
else if (theme.colors['editor.background']) | ||
color = isLightColor(theme.colors['editor.background']) ? 'light' : 'dark' | ||
else if (theme.colors.foreground) | ||
color = isLightColor(theme.colors.foreground) ? 'dark' : 'light' | ||
else | ||
color = 'light' | ||
return color | ||
} | ||
|
||
function guessThemeName(id: string, theme: any) { | ||
if (theme.displayName) | ||
return theme.displayName | ||
let name: string = theme.name || id | ||
name = name.split(/[_-]/).map(i => i[0].toUpperCase() + i.slice(1)).join(' ') | ||
name = name.replace(/github/ig, 'GitHub') | ||
return name | ||
} | ||
await prepareLangs() | ||
await prepareTheme() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export const COMMENT_HEAD = ` | ||
/** | ||
* Generated by scripts/prepare.ts | ||
*/ | ||
`.trim() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Download and prepare grammar injections | ||
|
||
import fs from 'fs-extra' | ||
import { fetch } from 'ofetch' | ||
import { COMMENT_HEAD } from './constants' | ||
|
||
interface Injection { | ||
name: string | ||
contents: any[] | ||
/** | ||
* Bundle into a language | ||
*/ | ||
toLang?: string | ||
} | ||
|
||
export async function prepareInjections() { | ||
const injections = (await Promise.all([ | ||
prepareVueInjections(), | ||
])).flat() | ||
|
||
for (const injection of injections) { | ||
await fs.writeFile( | ||
`src/assets/langs/${injection.name}.ts`, | ||
`${COMMENT_HEAD} | ||
import type { LanguageRegistration } from 'shikiji-core' | ||
export default ${JSON.stringify(injection.contents, null, 2)} as unknown as LanguageRegistration[] | ||
`, | ||
'utf-8', | ||
) | ||
} | ||
|
||
return injections | ||
} | ||
|
||
export async function prepareVueInjections(): Promise<Injection> { | ||
const base = 'https://github.com/vuejs/language-tools/blob/master/extensions/vscode/' | ||
const pkgJson = await fetchJson(`${base}package.json?raw=true`) | ||
const grammars = pkgJson.contributes.grammars as any[] | ||
const injections = await Promise.all(grammars | ||
.filter(i => i.injectTo) | ||
.map(async (i) => { | ||
const content = await fetchJson(`${new URL(i.path, base).href}?raw=true`) | ||
return { | ||
...content, | ||
name: i.language, | ||
injectTo: i.injectTo, | ||
} | ||
}), | ||
) | ||
|
||
return { | ||
name: 'vue-injections', | ||
toLang: 'vue', | ||
contents: injections, | ||
} | ||
} | ||
|
||
export function fetchJson(url: string) { | ||
return fetch(url).then(res => res.json()) | ||
} |
Oops, something went wrong.