@termdock/twlint 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +185 -0
- package/TRADEMARK.md +39 -0
- package/bin/twlint +6 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +142 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/config-loader.d.ts +3 -0
- package/dist/core/config-loader.d.ts.map +1 -0
- package/dist/core/config-loader.js +54 -0
- package/dist/core/config-loader.js.map +1 -0
- package/dist/core/config-schema.d.ts +12 -0
- package/dist/core/config-schema.d.ts.map +1 -0
- package/dist/core/config-schema.js +64 -0
- package/dist/core/config-schema.js.map +1 -0
- package/dist/core/dictionary-manager.d.ts +14 -0
- package/dist/core/dictionary-manager.d.ts.map +1 -0
- package/dist/core/dictionary-manager.js +94 -0
- package/dist/core/dictionary-manager.js.map +1 -0
- package/dist/core/linter.d.ts +31 -0
- package/dist/core/linter.d.ts.map +1 -0
- package/dist/core/linter.js +182 -0
- package/dist/core/linter.js.map +1 -0
- package/dist/core/matching/match-strategies.d.ts +26 -0
- package/dist/core/matching/match-strategies.d.ts.map +1 -0
- package/dist/core/matching/match-strategies.js +170 -0
- package/dist/core/matching/match-strategies.js.map +1 -0
- package/dist/core/position-mapper.d.ts +31 -0
- package/dist/core/position-mapper.d.ts.map +1 -0
- package/dist/core/position-mapper.js +101 -0
- package/dist/core/position-mapper.js.map +1 -0
- package/dist/core/rules/context-sensitive.d.ts +17 -0
- package/dist/core/rules/context-sensitive.d.ts.map +1 -0
- package/dist/core/rules/context-sensitive.js +154 -0
- package/dist/core/rules/context-sensitive.js.map +1 -0
- package/dist/core/rules/mainland-terms.d.ts +11 -0
- package/dist/core/rules/mainland-terms.d.ts.map +1 -0
- package/dist/core/rules/mainland-terms.js +45 -0
- package/dist/core/rules/mainland-terms.js.map +1 -0
- package/dist/core/rules/simplified-chars.d.ts +10 -0
- package/dist/core/rules/simplified-chars.d.ts.map +1 -0
- package/dist/core/rules/simplified-chars.js +51 -0
- package/dist/core/rules/simplified-chars.js.map +1 -0
- package/dist/dictionaries/academic.json +141 -0
- package/dist/dictionaries/core.json +1491 -0
- package/dist/dictionaries/extended.json +141 -0
- package/dist/dictionaries/index.json +21 -0
- package/dist/formatters/json.d.ts +5 -0
- package/dist/formatters/json.d.ts.map +1 -0
- package/dist/formatters/json.js +18 -0
- package/dist/formatters/json.js.map +1 -0
- package/dist/formatters/stylish.d.ts +5 -0
- package/dist/formatters/stylish.d.ts.map +1 -0
- package/dist/formatters/stylish.js +45 -0
- package/dist/formatters/stylish.js.map +1 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface TextPosition {
|
|
2
|
+
line: number;
|
|
3
|
+
column: number;
|
|
4
|
+
offset: number;
|
|
5
|
+
}
|
|
6
|
+
export interface PositionMapping {
|
|
7
|
+
originalPosition: TextPosition;
|
|
8
|
+
convertedPosition: TextPosition;
|
|
9
|
+
}
|
|
10
|
+
export declare class PositionMapper {
|
|
11
|
+
private mappings;
|
|
12
|
+
private originalText;
|
|
13
|
+
private convertedText;
|
|
14
|
+
constructor(originalText: string, convertedText: string);
|
|
15
|
+
private buildMappings;
|
|
16
|
+
/**
|
|
17
|
+
* Map position from converted text back to original text
|
|
18
|
+
*/
|
|
19
|
+
mapToOriginal(convertedLine: number, convertedColumn: number): TextPosition;
|
|
20
|
+
/**
|
|
21
|
+
* Map position from original text to converted text
|
|
22
|
+
*/
|
|
23
|
+
mapToConverted(originalLine: number, originalColumn: number): TextPosition;
|
|
24
|
+
private getOffsetFromPosition;
|
|
25
|
+
/**
|
|
26
|
+
* Simple mapping for cases where text lengths are similar
|
|
27
|
+
* Falls back to this when detailed mapping is not needed
|
|
28
|
+
*/
|
|
29
|
+
static simpleMap(originalLine: number, originalColumn: number): TextPosition;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=position-mapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"position-mapper.d.ts","sourceRoot":"","sources":["../../src/core/position-mapper.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,YAAY,CAAA;IAC9B,iBAAiB,EAAE,YAAY,CAAA;CAChC;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,aAAa,CAAQ;gBAEjB,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAMvD,OAAO,CAAC,aAAa;IAwCrB;;OAEG;IACH,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,YAAY;IAmB3E;;OAEG;IACH,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,YAAY;IAkB1E,OAAO,CAAC,qBAAqB;IAe7B;;;OAGG;IACH,MAAM,CAAC,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,YAAY;CAO7E"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export class PositionMapper {
|
|
2
|
+
mappings = [];
|
|
3
|
+
originalText;
|
|
4
|
+
convertedText;
|
|
5
|
+
constructor(originalText, convertedText) {
|
|
6
|
+
this.originalText = originalText;
|
|
7
|
+
this.convertedText = convertedText;
|
|
8
|
+
this.buildMappings();
|
|
9
|
+
}
|
|
10
|
+
buildMappings() {
|
|
11
|
+
// Build character-level mapping between original and converted text
|
|
12
|
+
// This handles cases where character conversion changes text length
|
|
13
|
+
const originalLines = this.originalText.split('\n');
|
|
14
|
+
const convertedLines = this.convertedText.split('\n');
|
|
15
|
+
let originalOffset = 0;
|
|
16
|
+
let convertedOffset = 0;
|
|
17
|
+
for (let lineIndex = 0; lineIndex < Math.max(originalLines.length, convertedLines.length); lineIndex++) {
|
|
18
|
+
const originalLine = originalLines[lineIndex] || '';
|
|
19
|
+
const convertedLine = convertedLines[lineIndex] || '';
|
|
20
|
+
// Map each character position in the line
|
|
21
|
+
const maxLength = Math.max(originalLine.length, convertedLine.length);
|
|
22
|
+
for (let charIndex = 0; charIndex < maxLength; charIndex++) {
|
|
23
|
+
if (charIndex < originalLine.length && charIndex < convertedLine.length) {
|
|
24
|
+
this.mappings.push({
|
|
25
|
+
originalPosition: {
|
|
26
|
+
line: lineIndex + 1,
|
|
27
|
+
column: charIndex + 1,
|
|
28
|
+
offset: originalOffset + charIndex
|
|
29
|
+
},
|
|
30
|
+
convertedPosition: {
|
|
31
|
+
line: lineIndex + 1,
|
|
32
|
+
column: charIndex + 1,
|
|
33
|
+
offset: convertedOffset + charIndex
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Account for line breaks
|
|
39
|
+
originalOffset += originalLine.length + 1; // +1 for \n
|
|
40
|
+
convertedOffset += convertedLine.length + 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Map position from converted text back to original text
|
|
45
|
+
*/
|
|
46
|
+
mapToOriginal(convertedLine, convertedColumn) {
|
|
47
|
+
// Find the best matching position in original text
|
|
48
|
+
const convertedOffset = this.getOffsetFromPosition(this.convertedText, convertedLine, convertedColumn);
|
|
49
|
+
// Find closest mapping
|
|
50
|
+
let bestMapping = this.mappings[0];
|
|
51
|
+
let minDistance = Math.abs(bestMapping.convertedPosition.offset - convertedOffset);
|
|
52
|
+
for (const mapping of this.mappings) {
|
|
53
|
+
const distance = Math.abs(mapping.convertedPosition.offset - convertedOffset);
|
|
54
|
+
if (distance < minDistance) {
|
|
55
|
+
minDistance = distance;
|
|
56
|
+
bestMapping = mapping;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return bestMapping.originalPosition;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Map position from original text to converted text
|
|
63
|
+
*/
|
|
64
|
+
mapToConverted(originalLine, originalColumn) {
|
|
65
|
+
const originalOffset = this.getOffsetFromPosition(this.originalText, originalLine, originalColumn);
|
|
66
|
+
// Find closest mapping
|
|
67
|
+
let bestMapping = this.mappings[0];
|
|
68
|
+
let minDistance = Math.abs(bestMapping.originalPosition.offset - originalOffset);
|
|
69
|
+
for (const mapping of this.mappings) {
|
|
70
|
+
const distance = Math.abs(mapping.originalPosition.offset - originalOffset);
|
|
71
|
+
if (distance < minDistance) {
|
|
72
|
+
minDistance = distance;
|
|
73
|
+
bestMapping = mapping;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return bestMapping.convertedPosition;
|
|
77
|
+
}
|
|
78
|
+
getOffsetFromPosition(text, line, column) {
|
|
79
|
+
const lines = text.split('\n');
|
|
80
|
+
let offset = 0;
|
|
81
|
+
// Add lengths of previous lines
|
|
82
|
+
for (let i = 0; i < line - 1 && i < lines.length; i++) {
|
|
83
|
+
offset += lines[i].length + 1; // +1 for \n
|
|
84
|
+
}
|
|
85
|
+
// Add column position in current line
|
|
86
|
+
offset += Math.min(column - 1, lines[line - 1]?.length || 0);
|
|
87
|
+
return offset;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Simple mapping for cases where text lengths are similar
|
|
91
|
+
* Falls back to this when detailed mapping is not needed
|
|
92
|
+
*/
|
|
93
|
+
static simpleMap(originalLine, originalColumn) {
|
|
94
|
+
return {
|
|
95
|
+
line: originalLine,
|
|
96
|
+
column: originalColumn,
|
|
97
|
+
offset: 0 // Not computed in simple mapping
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=position-mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"position-mapper.js","sourceRoot":"","sources":["../../src/core/position-mapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Rule, Issue } from '../../types.js';
|
|
2
|
+
import type { DictionaryManager } from '../dictionary-manager.js';
|
|
3
|
+
export declare class ContextSensitiveRule implements Rule {
|
|
4
|
+
readonly name = "context-sensitive";
|
|
5
|
+
private dictManager;
|
|
6
|
+
constructor(dictManager: DictionaryManager);
|
|
7
|
+
check(text: string): Promise<Issue[]>;
|
|
8
|
+
fix(text: string): Promise<string>;
|
|
9
|
+
private getContextSensitiveWords;
|
|
10
|
+
private analyzeSimpleContext;
|
|
11
|
+
private tokenize;
|
|
12
|
+
private calculateBM25Score;
|
|
13
|
+
private extractContext;
|
|
14
|
+
private findTermPositions;
|
|
15
|
+
private escapeRegExp;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=context-sensitive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-sensitive.d.ts","sourceRoot":"","sources":["../../../src/core/rules/context-sensitive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAEjE,qBAAa,oBAAqB,YAAW,IAAI;IAC/C,QAAQ,CAAC,IAAI,uBAAsB;IACnC,OAAO,CAAC,WAAW,CAAmB;gBAE1B,WAAW,EAAE,iBAAiB;IAIpC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAmCrC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmBxC,OAAO,CAAC,wBAAwB;IAwChC,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,kBAAkB;IA8B1B,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,YAAY;CAGrB"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
export class ContextSensitiveRule {
|
|
2
|
+
name = 'context-sensitive';
|
|
3
|
+
dictManager;
|
|
4
|
+
constructor(dictManager) {
|
|
5
|
+
this.dictManager = dictManager;
|
|
6
|
+
}
|
|
7
|
+
async check(text) {
|
|
8
|
+
const issues = [];
|
|
9
|
+
const lines = text.split('\n');
|
|
10
|
+
// 語境敏感詞彙列表(從詞庫中標記為 context-sensitive 的條目)
|
|
11
|
+
const contextSensitiveWords = this.getContextSensitiveWords();
|
|
12
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
13
|
+
const line = lines[lineIndex];
|
|
14
|
+
for (const word of contextSensitiveWords) {
|
|
15
|
+
const positions = this.findTermPositions(line, word.source);
|
|
16
|
+
for (const position of positions) {
|
|
17
|
+
// 提取上下文(前後各50個字元)
|
|
18
|
+
const context = this.extractContext(text, word.source);
|
|
19
|
+
const contextResult = this.analyzeSimpleContext(context, word);
|
|
20
|
+
if (contextResult.confidence > 0.3) { // 使用適當的信心度門檻
|
|
21
|
+
issues.push({
|
|
22
|
+
line: lineIndex + 1,
|
|
23
|
+
column: position + 1,
|
|
24
|
+
message: `'${word.source}' 在${contextResult.domain}語境建議使用 '${contextResult.suggestion}'`,
|
|
25
|
+
severity: contextResult.confidence > 0.8 ? 'warning' : 'info',
|
|
26
|
+
rule: this.name,
|
|
27
|
+
suggestions: [contextResult.suggestion],
|
|
28
|
+
fixable: contextResult.confidence > 0.8
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return issues;
|
|
35
|
+
}
|
|
36
|
+
async fix(text) {
|
|
37
|
+
let fixedText = text;
|
|
38
|
+
const contextSensitiveWords = this.getContextSensitiveWords();
|
|
39
|
+
for (const word of contextSensitiveWords) {
|
|
40
|
+
if (text.includes(word.source)) {
|
|
41
|
+
const context = this.extractContext(text, word.source);
|
|
42
|
+
const result = this.analyzeSimpleContext(context, word);
|
|
43
|
+
if (result.confidence > 0.8) {
|
|
44
|
+
const regex = new RegExp(this.escapeRegExp(word.source), 'g');
|
|
45
|
+
fixedText = fixedText.replace(regex, result.suggestion);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return fixedText;
|
|
50
|
+
}
|
|
51
|
+
getContextSensitiveWords() {
|
|
52
|
+
// 硬編碼常見語境敏感詞彙,避免過度複雜化
|
|
53
|
+
return [
|
|
54
|
+
{
|
|
55
|
+
source: '質量', // 使用簡體
|
|
56
|
+
candidates: [
|
|
57
|
+
{
|
|
58
|
+
suggestion: '品質',
|
|
59
|
+
domain: '商業',
|
|
60
|
+
keywords: ['產品', '產品', '控制', '管理', '標準', '標準', '檢驗', '檢驗', '服務', '服務', '客戶', '客戶', '品管'],
|
|
61
|
+
confidence: 0.85
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
suggestion: '質量',
|
|
65
|
+
domain: '物理',
|
|
66
|
+
keywords: ['密度', '重量', '公式', '物理', '質點', '質點', '慣性', '慣性', '守恆', '守恆', '定律'],
|
|
67
|
+
confidence: 0.90
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
source: '土豆',
|
|
73
|
+
candidates: [
|
|
74
|
+
{
|
|
75
|
+
suggestion: '馬鈴薯',
|
|
76
|
+
domain: '料理',
|
|
77
|
+
keywords: ['烹飪', '料理', '炒', '煮', '蒸', '炸', '調味', '食材', '菜'],
|
|
78
|
+
confidence: 0.85
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
suggestion: '花生',
|
|
82
|
+
domain: '農業',
|
|
83
|
+
keywords: ['農業', '種植', '花生油', '作物', '收成', '田地', '油料'],
|
|
84
|
+
confidence: 0.80
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
analyzeSimpleContext(context, word) {
|
|
91
|
+
const contextWords = this.tokenize(context);
|
|
92
|
+
const scores = word.candidates.map(candidate => {
|
|
93
|
+
// TF-IDF 風格計算:關鍵詞在上下文中的重要性
|
|
94
|
+
const relevanceScore = this.calculateBM25Score(contextWords, candidate.keywords);
|
|
95
|
+
return {
|
|
96
|
+
suggestion: candidate.suggestion,
|
|
97
|
+
domain: candidate.domain,
|
|
98
|
+
confidence: candidate.confidence * relevanceScore,
|
|
99
|
+
reason: `${candidate.domain}語境相關度: ${(relevanceScore * 100).toFixed(1)}%`
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
// 返回信心度最高的建議
|
|
103
|
+
return scores.reduce((best, current) => current.confidence > best.confidence ? current : best);
|
|
104
|
+
}
|
|
105
|
+
tokenize(text) {
|
|
106
|
+
// 簡單中文分詞:去除標點符號,分割為字元
|
|
107
|
+
return text.replace(/[。,!?;:、""''()【】]/g, '').split('')
|
|
108
|
+
.filter(char => char.trim() && !char.match(/\s/));
|
|
109
|
+
}
|
|
110
|
+
calculateBM25Score(contextWords, domainKeywords) {
|
|
111
|
+
const context = contextWords.join('');
|
|
112
|
+
const docLength = context.length;
|
|
113
|
+
const avgDocLength = 100; // 假設平均文件長度
|
|
114
|
+
const k1 = 1.5; // BM25 參數
|
|
115
|
+
const b = 0.75; // BM25 參數
|
|
116
|
+
let score = 0;
|
|
117
|
+
for (const keyword of domainKeywords) {
|
|
118
|
+
if (context.includes(keyword)) {
|
|
119
|
+
// 計算詞頻 (TF)
|
|
120
|
+
const tf = (context.match(new RegExp(this.escapeRegExp(keyword), 'g')) || []).length;
|
|
121
|
+
// 計算 IDF
|
|
122
|
+
const totalDocs = domainKeywords.length;
|
|
123
|
+
const docsWithTerm = 1; // 簡化:假設只在當前候選中出現
|
|
124
|
+
const idf = Math.log((totalDocs - docsWithTerm + 0.5) / (docsWithTerm + 0.5));
|
|
125
|
+
// BM25 的 TF 正規化
|
|
126
|
+
const normalizedTF = (tf * (k1 + 1)) /
|
|
127
|
+
(tf + k1 * (1 - b + b * (docLength / avgDocLength)));
|
|
128
|
+
score += idf * normalizedTF;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return Math.max(0, score);
|
|
132
|
+
}
|
|
133
|
+
extractContext(text, word) {
|
|
134
|
+
const index = text.indexOf(word);
|
|
135
|
+
if (index === -1)
|
|
136
|
+
return '';
|
|
137
|
+
const start = Math.max(0, index - 50);
|
|
138
|
+
const end = Math.min(text.length, index + word.length + 50);
|
|
139
|
+
return text.slice(start, end);
|
|
140
|
+
}
|
|
141
|
+
findTermPositions(text, term) {
|
|
142
|
+
const positions = [];
|
|
143
|
+
let index = text.indexOf(term);
|
|
144
|
+
while (index !== -1) {
|
|
145
|
+
positions.push(index);
|
|
146
|
+
index = text.indexOf(term, index + 1);
|
|
147
|
+
}
|
|
148
|
+
return positions;
|
|
149
|
+
}
|
|
150
|
+
escapeRegExp(string) {
|
|
151
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=context-sensitive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-sensitive.js","sourceRoot":"","sources":["../../../src/core/rules/context-sensitive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Rule, Issue } from '../../types.js';
|
|
2
|
+
import type { DictionaryManager } from '../dictionary-manager.js';
|
|
3
|
+
export declare class MainlandTermsRule implements Rule {
|
|
4
|
+
readonly name = "mainland-terms";
|
|
5
|
+
private dictManager;
|
|
6
|
+
constructor(dictManager: DictionaryManager);
|
|
7
|
+
check(text: string): Promise<Issue[]>;
|
|
8
|
+
fix(text: string): Promise<string>;
|
|
9
|
+
private getLineInfo;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=mainland-terms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mainland-terms.d.ts","sourceRoot":"","sources":["../../../src/core/rules/mainland-terms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAEjE,qBAAa,iBAAkB,YAAW,IAAI;IAC5C,QAAQ,CAAC,IAAI,oBAAmB;IAChC,OAAO,CAAC,WAAW,CAAmB;gBAE1B,WAAW,EAAE,iBAAiB;IAIpC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAqBrC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBxC,OAAO,CAAC,WAAW;CAOpB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export class MainlandTermsRule {
|
|
2
|
+
name = 'mainland-terms';
|
|
3
|
+
dictManager;
|
|
4
|
+
constructor(dictManager) {
|
|
5
|
+
this.dictManager = dictManager;
|
|
6
|
+
}
|
|
7
|
+
async check(text) {
|
|
8
|
+
const issues = [];
|
|
9
|
+
const matches = this.dictManager.findMatches(text);
|
|
10
|
+
for (const match of matches) {
|
|
11
|
+
const lineInfo = this.getLineInfo(text, match.start);
|
|
12
|
+
issues.push({
|
|
13
|
+
line: lineInfo.line,
|
|
14
|
+
column: lineInfo.column,
|
|
15
|
+
message: `大陸用語 '${match.term}' 建議使用臺灣用語 '${match.replacement}'`,
|
|
16
|
+
severity: 'warning',
|
|
17
|
+
rule: this.name,
|
|
18
|
+
suggestions: [match.replacement],
|
|
19
|
+
fixable: true
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return issues;
|
|
23
|
+
}
|
|
24
|
+
async fix(text) {
|
|
25
|
+
let fixedText = text;
|
|
26
|
+
const matches = this.dictManager.findMatches(text);
|
|
27
|
+
// Sort by position (from end to start) to avoid offset issues
|
|
28
|
+
const sortedMatches = matches.sort((a, b) => b.start - a.start);
|
|
29
|
+
for (const match of sortedMatches) {
|
|
30
|
+
// Replace using precise position information
|
|
31
|
+
fixedText = fixedText.substring(0, match.start) +
|
|
32
|
+
match.replacement +
|
|
33
|
+
fixedText.substring(match.end);
|
|
34
|
+
}
|
|
35
|
+
return fixedText;
|
|
36
|
+
}
|
|
37
|
+
getLineInfo(text, offset) {
|
|
38
|
+
const lines = text.substring(0, offset).split('\n');
|
|
39
|
+
return {
|
|
40
|
+
line: lines.length,
|
|
41
|
+
column: lines[lines.length - 1].length + 1
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=mainland-terms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mainland-terms.js","sourceRoot":"","sources":["../../../src/core/rules/mainland-terms.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,iBAAiB;IACnB,IAAI,GAAG,gBAAgB,CAAA;IACxB,WAAW,CAAmB;IAEtC,YAAY,WAA8B;QACxC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;IAChC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,MAAM,MAAM,GAAY,EAAE,CAAA;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;QAElD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;YAEpD,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,OAAO,EAAE,SAAS,KAAK,CAAC,IAAI,eAAe,KAAK,CAAC,WAAW,GAAG;gBAC/D,QAAQ,EAAE,SAAS;gBACnB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,WAAW,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC;gBAChC,OAAO,EAAE,IAAI;aACd,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAY;QACpB,IAAI,SAAS,GAAG,IAAI,CAAA;QACpB,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;QAElD,8DAA8D;QAC9D,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAA;QAE/D,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;YAClC,6CAA6C;YAC7C,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;gBACnC,KAAK,CAAC,WAAW;gBACjB,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5C,CAAC;QAED,OAAO,SAAS,CAAA;IAClB,CAAC;IAEO,WAAW,CAAC,IAAY,EAAE,MAAc;QAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACnD,OAAO;YACL,IAAI,EAAE,KAAK,CAAC,MAAM;YAClB,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;SAC3C,CAAA;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Rule, Issue } from '../../types.js';
|
|
2
|
+
export declare class SimplifiedCharsRule implements Rule {
|
|
3
|
+
readonly name = "simplified-chars";
|
|
4
|
+
private converter;
|
|
5
|
+
constructor();
|
|
6
|
+
check(text: string): Promise<Issue[]>;
|
|
7
|
+
fix(text: string): Promise<string>;
|
|
8
|
+
private findDifferences;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=simplified-chars.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simplified-chars.d.ts","sourceRoot":"","sources":["../../../src/core/rules/simplified-chars.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAEjD,qBAAa,mBAAoB,YAAW,IAAI;IAC9C,QAAQ,CAAC,IAAI,sBAAqB;IAClC,OAAO,CAAC,SAAS,CAA8B;;IAOzC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IA6BrC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKxC,OAAO,CAAC,eAAe;CAkBxB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Converter } from 'opencc-js';
|
|
2
|
+
export class SimplifiedCharsRule {
|
|
3
|
+
name = 'simplified-chars';
|
|
4
|
+
converter;
|
|
5
|
+
constructor() {
|
|
6
|
+
// 簡體到繁體的轉換器
|
|
7
|
+
this.converter = Converter({ from: 'cn', to: 'tw' });
|
|
8
|
+
}
|
|
9
|
+
async check(text) {
|
|
10
|
+
const issues = [];
|
|
11
|
+
const lines = text.split('\n');
|
|
12
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
13
|
+
const line = lines[lineIndex];
|
|
14
|
+
const convertedLine = this.converter(line);
|
|
15
|
+
// 如果轉換後的內容不同,表示原文包含簡體字
|
|
16
|
+
if (line !== convertedLine) {
|
|
17
|
+
const differences = this.findDifferences(line, convertedLine);
|
|
18
|
+
for (const diff of differences) {
|
|
19
|
+
issues.push({
|
|
20
|
+
line: lineIndex + 1,
|
|
21
|
+
column: diff.column + 1,
|
|
22
|
+
message: `簡體字 '${diff.simplified}' 建議使用繁體字 '${diff.traditional}'`,
|
|
23
|
+
severity: 'error',
|
|
24
|
+
rule: this.name,
|
|
25
|
+
suggestions: [diff.traditional],
|
|
26
|
+
fixable: true
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return issues;
|
|
32
|
+
}
|
|
33
|
+
async fix(text) {
|
|
34
|
+
// 使用 opencc 進行簡繁轉換
|
|
35
|
+
return this.converter(text);
|
|
36
|
+
}
|
|
37
|
+
findDifferences(original, converted) {
|
|
38
|
+
const differences = [];
|
|
39
|
+
for (let i = 0; i < Math.min(original.length, converted.length); i++) {
|
|
40
|
+
if (original[i] !== converted[i]) {
|
|
41
|
+
differences.push({
|
|
42
|
+
simplified: original[i],
|
|
43
|
+
traditional: converted[i],
|
|
44
|
+
column: i
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return differences;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=simplified-chars.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simplified-chars.js","sourceRoot":"","sources":["../../../src/core/rules/simplified-chars.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAGrC,MAAM,OAAO,mBAAmB;IACrB,IAAI,GAAG,kBAAkB,CAAA;IAC1B,SAAS,CAA8B;IAE/C;QACE,YAAY;QACZ,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;IACtD,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,MAAM,MAAM,GAAY,EAAE,CAAA;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAE9B,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;YAC9D,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAA;YAC7B,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YAE1C,uBAAuB;YACvB,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;gBAC3B,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;gBAE7D,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;oBAC/B,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,SAAS,GAAG,CAAC;wBACnB,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC;wBACvB,OAAO,EAAE,QAAQ,IAAI,CAAC,UAAU,cAAc,IAAI,CAAC,WAAW,GAAG;wBACjE,QAAQ,EAAE,OAAO;wBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,WAAW,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC;wBAC/B,OAAO,EAAE,IAAI;qBACd,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAY;QACpB,mBAAmB;QACnB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;IAC7B,CAAC;IAEO,eAAe,CACrB,QAAgB,EAChB,SAAiB;QAEjB,MAAM,WAAW,GAAuE,EAAE,CAAA;QAE1F,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACrE,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjC,WAAW,CAAC,IAAI,CAAC;oBACf,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;oBACvB,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;oBACzB,MAAM,EAAE,CAAC;iBACV,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAED,OAAO,WAAW,CAAA;IACpB,CAAC;CACF"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
{
|
|
2
|
+
"metadata": {
|
|
3
|
+
"name": "academic",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"entries": 12
|
|
6
|
+
},
|
|
7
|
+
"lookup": {
|
|
8
|
+
"算法": {
|
|
9
|
+
"taiwan": "演算法",
|
|
10
|
+
"confidence": 0.9,
|
|
11
|
+
"category": "mainland-term",
|
|
12
|
+
"reason": "台灣學術標準用語"
|
|
13
|
+
},
|
|
14
|
+
"数据结构": {
|
|
15
|
+
"taiwan": "資料結構",
|
|
16
|
+
"confidence": 0.95,
|
|
17
|
+
"category": "mainland-term",
|
|
18
|
+
"reason": "台灣計算機科學標準"
|
|
19
|
+
},
|
|
20
|
+
"數據結構": {
|
|
21
|
+
"taiwan": "資料結構",
|
|
22
|
+
"confidence": 0.95,
|
|
23
|
+
"category": "mainland-term",
|
|
24
|
+
"reason": "台灣計算機科學標準"
|
|
25
|
+
},
|
|
26
|
+
"计算机科学": {
|
|
27
|
+
"taiwan": "計算機科學",
|
|
28
|
+
"confidence": 0.85,
|
|
29
|
+
"category": "mainland-term",
|
|
30
|
+
"reason": "台灣學術慣用語"
|
|
31
|
+
},
|
|
32
|
+
"計算機科學": {
|
|
33
|
+
"taiwan": "計算機科學",
|
|
34
|
+
"confidence": 0.85,
|
|
35
|
+
"category": "mainland-term",
|
|
36
|
+
"reason": "台灣學術慣用語"
|
|
37
|
+
},
|
|
38
|
+
"人工智能": {
|
|
39
|
+
"taiwan": "人工智慧",
|
|
40
|
+
"confidence": 0.9,
|
|
41
|
+
"category": "mainland-term",
|
|
42
|
+
"reason": "台灣AI領域標準"
|
|
43
|
+
},
|
|
44
|
+
"机器学习": {
|
|
45
|
+
"taiwan": "機器學習",
|
|
46
|
+
"confidence": 0.8,
|
|
47
|
+
"category": "mainland-term",
|
|
48
|
+
"reason": "台灣ML領域用語"
|
|
49
|
+
},
|
|
50
|
+
"機器學習": {
|
|
51
|
+
"taiwan": "機器學習",
|
|
52
|
+
"confidence": 0.8,
|
|
53
|
+
"category": "mainland-term",
|
|
54
|
+
"reason": "台灣ML領域用語"
|
|
55
|
+
},
|
|
56
|
+
"数据库系统": {
|
|
57
|
+
"taiwan": "資料庫系統",
|
|
58
|
+
"confidence": 0.95,
|
|
59
|
+
"category": "mainland-term",
|
|
60
|
+
"reason": "台灣資訊科學標準"
|
|
61
|
+
},
|
|
62
|
+
"數據庫系統": {
|
|
63
|
+
"taiwan": "資料庫系統",
|
|
64
|
+
"confidence": 0.95,
|
|
65
|
+
"category": "mainland-term",
|
|
66
|
+
"reason": "台灣資訊科學標準"
|
|
67
|
+
},
|
|
68
|
+
"操作系统": {
|
|
69
|
+
"taiwan": "作業系統",
|
|
70
|
+
"confidence": 0.9,
|
|
71
|
+
"category": "mainland-term",
|
|
72
|
+
"reason": "台灣系統軟體標準"
|
|
73
|
+
},
|
|
74
|
+
"操作系統": {
|
|
75
|
+
"taiwan": "作業系統",
|
|
76
|
+
"confidence": 0.9,
|
|
77
|
+
"category": "mainland-term",
|
|
78
|
+
"reason": "台灣系統軟體標準"
|
|
79
|
+
},
|
|
80
|
+
"用户界面": {
|
|
81
|
+
"taiwan": "使用者介面",
|
|
82
|
+
"confidence": 0.85,
|
|
83
|
+
"category": "mainland-term",
|
|
84
|
+
"reason": "台灣HCI標準用語"
|
|
85
|
+
},
|
|
86
|
+
"用戶界面": {
|
|
87
|
+
"taiwan": "使用者介面",
|
|
88
|
+
"confidence": 0.85,
|
|
89
|
+
"category": "mainland-term",
|
|
90
|
+
"reason": "台灣HCI標準用語"
|
|
91
|
+
},
|
|
92
|
+
"面向对象": {
|
|
93
|
+
"taiwan": "物件導向",
|
|
94
|
+
"confidence": 0.8,
|
|
95
|
+
"category": "mainland-term",
|
|
96
|
+
"reason": "台灣程式設計標準"
|
|
97
|
+
},
|
|
98
|
+
"面向對象": {
|
|
99
|
+
"taiwan": "物件導向",
|
|
100
|
+
"confidence": 0.8,
|
|
101
|
+
"category": "mainland-term",
|
|
102
|
+
"reason": "台灣程式設計標準"
|
|
103
|
+
},
|
|
104
|
+
"函数式编程": {
|
|
105
|
+
"taiwan": "函數式程式設計",
|
|
106
|
+
"confidence": 0.75,
|
|
107
|
+
"category": "mainland-term",
|
|
108
|
+
"reason": "台灣程式語言理論"
|
|
109
|
+
},
|
|
110
|
+
"函數式編程": {
|
|
111
|
+
"taiwan": "函數式程式設計",
|
|
112
|
+
"confidence": 0.75,
|
|
113
|
+
"category": "mainland-term",
|
|
114
|
+
"reason": "台灣程式語言理論"
|
|
115
|
+
},
|
|
116
|
+
"计算机视觉": {
|
|
117
|
+
"taiwan": "電腦視覺",
|
|
118
|
+
"confidence": 0.85,
|
|
119
|
+
"category": "mainland-term",
|
|
120
|
+
"reason": "台灣影像處理標準"
|
|
121
|
+
},
|
|
122
|
+
"計算機視覺": {
|
|
123
|
+
"taiwan": "電腦視覺",
|
|
124
|
+
"confidence": 0.85,
|
|
125
|
+
"category": "mainland-term",
|
|
126
|
+
"reason": "台灣影像處理標準"
|
|
127
|
+
},
|
|
128
|
+
"自然语言处理": {
|
|
129
|
+
"taiwan": "自然語言處理",
|
|
130
|
+
"confidence": 0.9,
|
|
131
|
+
"category": "mainland-term",
|
|
132
|
+
"reason": "台灣NLP標準用語"
|
|
133
|
+
},
|
|
134
|
+
"自然語言處理": {
|
|
135
|
+
"taiwan": "自然語言處理",
|
|
136
|
+
"confidence": 0.9,
|
|
137
|
+
"category": "mainland-term",
|
|
138
|
+
"reason": "台灣NLP標準用語"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|