支持了t方法调用的param相关提示

This commit is contained in:
Pan Qiancheng 2024-11-22 18:56:58 +08:00
parent 848233ed1f
commit 83f9196625
5 changed files with 187 additions and 15 deletions

View File

@ -2,6 +2,7 @@ import {
addKeyToLocale,
addLocaleToData,
getLocaleItem,
getParamsFromItem,
} from './../utils/locales';
import { join } from 'path';
import { getCachedLocaleItemByKey, getLocalesData } from '../utils/locales';
@ -14,13 +15,20 @@ import { getLevel, notIgnore } from '../utils/oakConfig';
const diagnosticCollection =
vscode.languages.createDiagnosticCollection('oakLocales');
const tCallRegex =
/(?<![a-zA-Z])t\([\s]*['"`]([^'"`]*)['"`]\s*(\s*|\,\s*({\s*([a-zA-Z_0-9]+:\s*(['"`][a-zA-Z_0-9]*['"`]|[.a-zA-Z_0-9]+)(|\,)\s*)*}|[.a-zA-Z_0-9]+))\s*\)/g;
const paramRegex =
/{\s*([a-zA-Z_0-9]+:\s*(['"`][a-zA-Z_0-9]*['"`]|[.a-zA-Z_0-9]+)(|\,)\s*)*}/;
class LocaleDocumentLinkProvider implements vscode.DocumentLinkProvider {
async provideDocumentLinks(
document: vscode.TextDocument,
token: vscode.CancellationToken
): Promise<vscode.DocumentLink[]> {
const text = document.getText();
const tCallRegex = /(?<![a-zA-Z])t\([\s]*['"`]([^'"`]*)['"`][\s]*(|,[\s]*{\s*([a-zA-Z_0-9]+:\s*['"`][a-zA-Z_0-9]*['"`](|,)\s*)*})\s*\)/g;
const documentLinks: vscode.DocumentLink[] = [];
const diagnostics: vscode.Diagnostic[] = [];
@ -28,17 +36,18 @@ class LocaleDocumentLinkProvider implements vscode.DocumentLinkProvider {
await waitUntilLocaleLoaded();
}
const match = tCallRegex.exec(text);
let match = tCallRegex.exec(text);
if (!match) {
return [];
}
getLocalesData(join(document.uri.fsPath, '../locales'));
while (match !== null) {
const handler = (match: RegExpExecArray) => {
const key = match[1];
if (key.includes('${')) {
// 忽略动态key
continue;
// continue;
return;
}
const item = getLocaleItem(key);
@ -47,7 +56,7 @@ class LocaleDocumentLinkProvider implements vscode.DocumentLinkProvider {
if (localePath && localePath.path) {
const startPos = document.positionAt(match.index + 2);
const endPos = document.positionAt(
match.index + match[0].length - 1
match.index + match[1].length + 4
);
const range = new vscode.Range(startPos, endPos);
const documentLink = new vscode.DocumentLink(
@ -58,10 +67,130 @@ class LocaleDocumentLinkProvider implements vscode.DocumentLinkProvider {
? `CN: ${localePath.desc}`
: `[未找到中文] 跳转到定义`;
documentLinks.push(documentLink);
// 这边是正常使用i18n的情况还需要判断param的参数
const param = match[2];
// 理想情况是一个object类似这样
// t("hello", { key: 'value' } )
// t("hello", { key: item.name } )
// t("hello", { key: 'value', key2: 'value' })
const paramsGet = getParamsFromItem(item);
if (!param) {
// 没有参数,判断一下是否需要提示
if (!notIgnore('i18n.onParamMissing')) {
// continue;
return;
}
const startPos = document.positionAt(
match.index + 2
);
const endPos = document.positionAt(
match.index + match[0].length - 1
);
const range = new vscode.Range(startPos, endPos);
const diagnostic = new vscode.Diagnostic(
range,
`需要参数: ${paramsGet.join(', ')}`,
getLevel('i18n.onParamMissing')
);
// 添加 code 用于区分错误类型
diagnostic.code = 'missing_param';
diagnostics.push(diagnostic);
return;
}
// 把key都匹配出来
const paramMatch = param.match(paramRegex);
// 如果不匹配,提示无法解读的参数,对这个参数标黄
if (!paramMatch) {
if (!notIgnore('i18n.onParamError')) {
// continue;
return;
}
const startPos = document.positionAt(
match.index + match[1].length + 5
);
const endPos = document.positionAt(
match.index + match[0].length
);
const range = new vscode.Range(startPos, endPos);
const diagnostic = new vscode.Diagnostic(
range,
`无法解读的参数: ${param}`,
getLevel('i18n.onParamError')
);
// 添加 code 用于区分错误类型
diagnostic.code = 'param_error';
diagnostics.push(diagnostic);
// continue;
return;
}
// 这里是已经匹配上了的情况判断一下key是否在locale的属性中存在%{xxx}的定义
const paramKeys = paramMatch[0]
.replace(/{|}/g, '')
.split(',')
.map((item) => item.split(':')[0].trim())
.filter(Boolean);
// 这里需要判断两个情况一个是paramKeys中的key是否在paramsGet中存在另一个是paramsGet中的key是否在paramKeys中存在
// 遍历paramKeys判断是否在paramsGet中存在
paramsGet.forEach((key) => {
if (!paramKeys.includes(key)) {
if (!notIgnore('i18n.onParamMissing')) {
// continue;
return;
}
const startPos = document.positionAt(
match.index + match[1].length + 5
);
const endPos = document.positionAt(
match.index + match[0].length
);
const range = new vscode.Range(startPos, endPos);
const diagnostic = new vscode.Diagnostic(
range,
`缺少参数定义: ${key}`,
getLevel('i18n.onParamMissing')
);
// 添加 code 用于区分错误类型
diagnostic.code = 'missing_param';
diagnostics.push(diagnostic);
}
});
// 遍历paramsGet判断是否在paramKeys中存在
paramKeys.forEach((key) => {
if (!paramsGet.includes(key)) {
if (!notIgnore('i18n.onParamRedundant')) {
// continue;
return;
}
const startPos = document.positionAt(
match.index + match[1].length + 5
);
const endPos = document.positionAt(
match.index + match[0].length
);
const range = new vscode.Range(startPos, endPos);
const diagnostic = new vscode.Diagnostic(
range,
`多余的参数定义: ${key}`,
getLevel('i18n.onParamRedundant')
);
// 添加 code 用于区分错误类型
diagnostic.code = 'redundant_param';
diagnostics.push(diagnostic);
}
});
}
if (item.desc === '') {
if (!notIgnore('i18n.onKeyBlank')) {
continue;
// continue;
return;
}
const startPos = document.positionAt(match.index + 2);
const endPos = document.positionAt(
@ -80,7 +209,8 @@ class LocaleDocumentLinkProvider implements vscode.DocumentLinkProvider {
} else {
// range需要排除掉t(的部分和最后的 ) 部分
if (!notIgnore('i18n.onMissingKey')) {
continue;
// continue;
return;
}
const startPos = document.positionAt(match.index + 2);
const endPos = document.positionAt(
@ -96,6 +226,14 @@ class LocaleDocumentLinkProvider implements vscode.DocumentLinkProvider {
diagnostic.code = 'missing_locale';
diagnostics.push(diagnostic);
}
};
getLocalesData(join(document.uri.fsPath, '../locales'));
handler(match);
while ((match = tCallRegex.exec(text)) !== null) {
handler(match);
}
diagnosticCollection.set(document.uri, diagnostics);

View File

@ -26,6 +26,12 @@ export type OakConfiog = {
onMissingKey?: Level;
// key为空
onKeyBlank?: Level;
// 无法解读的param
onParamError?: Level;
// 缺少param
onParamMissing?: Level;
// 多余的param
onParamRedundant?: Level;
};
// oak组件配置
oakComponent?: {

View File

@ -251,9 +251,15 @@ function readLocales(localesDir: string): LocalesDef {
language.forEach((lang) => {
const localeFile = join(localesDir, `${lang}.json`);
if (fs.existsSync(localeFile)) {
locales[lang] = JSON.parse(
fs.readFileSync(localeFile, 'utf-8')
) as LanguageValue;
try {
locales[lang] = JSON.parse(
fs.readFileSync(localeFile, 'utf-8')
) as LanguageValue;
} catch (e) {
// vscode.window.showWarningMessage(
// `解析语言文件 ${localeFile} 失败: ${e.message}`
// );
}
}
});
return locales;

View File

@ -470,9 +470,26 @@ export const addKeyToLocale = (
assert(false, '不应该走到这里');
};
// 匹配%{}的正则
const paramRegex = /%\{\s*([.a-zA-Z_0-9]+)\s*\}/g;
export const getParamsFromItem = (item: LocaleItem): string[] => {
if (!item.desc) {
return [];
}
const value = item.desc;
const params: string[] = [];
let result: RegExpExecArray | null;
while ((result = paramRegex.exec(value)) !== null) {
params.push(result[1]);
}
return params;
};
// 主动定时刷新缓存
const setRefreshInterval = () => {
let timer: NodeJS.Timeout | null = null;
if (timer) {
@ -481,7 +498,7 @@ const setRefreshInterval = () => {
const refresh = () => {
console.log('定时主动刷新组件locales缓存');
const componentsPath = componentConfig.getAllComponents().map((c) => {
return c.path;
});
@ -491,7 +508,9 @@ const setRefreshInterval = () => {
};
// 获取配置项
const config = vscode.workspace.getConfiguration('oak-assistant').get('localesRefreshInterval');
const config = vscode.workspace
.getConfiguration('oak-assistant')
.get('localesRefreshInterval');
assert(typeof config === 'number', '配置项必须是数字');
@ -515,4 +534,4 @@ subsPath(() => {
setNameSpaceLocales();
});
setRefreshInterval();
setRefreshInterval();

View File

@ -19,6 +19,9 @@ export const defaultConfig: OakConfiog = {
i18n: {
onMissingKey: 'error',
onKeyBlank: 'warn',
onParamError: 'warn',
onParamMissing: 'error',
onParamRedundant: 'warn',
},
oakComponent: {
onInvalidEntity: 'error',