Skip to content

Commit 261ce4f

Browse files
committed
feat: configure for custom refactor template, close #574
1 parent ab25966 commit 261ce4f

27 files changed

+177
-95
lines changed

examples/by-features/extractions/.vscode/settings.json

+6
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,10 @@
44
"i18n-ally.keystyle": "nested",
55
"i18n-ally.extract.autoDetect": true,
66
"i18n-ally.translate.saveAsCandidates": true,
7+
"i18n-ally.refactor.templates": [
8+
{
9+
"source": "html-attribute",
10+
"template": "z('{key}'{args})"
11+
}
12+
]
713
}

examples/by-features/extractions/locales/en.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@
1616
"and": "good"
1717
}
1818
},
19-
"yad": "asd"
20-
}
19+
"yad": "asd",
20+
"saved-boards": "Saved boards"
21+
}

package.json

+34-1
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,40 @@
899899
"i18n-ally.refactor.templates": {
900900
"type": "array",
901901
"items": {
902-
"type": "string"
902+
"type": "object",
903+
"properties": {
904+
"source": {
905+
"type": "string",
906+
"enum": [
907+
"html-attribute",
908+
"html-inline",
909+
"js-string",
910+
"js-template",
911+
"jsx-text"
912+
]
913+
},
914+
"template": {
915+
"type": "string"
916+
},
917+
"templates": {
918+
"type": "array",
919+
"items": {
920+
"type": "string"
921+
}
922+
},
923+
"include": {
924+
"type": "array",
925+
"items": {
926+
"type": "string"
927+
}
928+
},
929+
"exclude": {
930+
"type": "array",
931+
"items": {
932+
"type": "string"
933+
}
934+
}
935+
}
903936
},
904937
"description": "%config.refactor_templates%"
905938
},

src/commands/batchHardStringsExtract.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ export async function BatchHardStringExtraction() {
2020
const options = DetectionResultToExtraction(i, document)
2121

2222
if (options.rawText && !options.text) {
23-
const result = parseHardString(options.rawText, options.languageId, options.isDynamic)
23+
const result = parseHardString(options.rawText, options.document.languageId, options.isDynamic)
2424
options.text = result?.text || ''
2525
options.args = result?.args
2626
}
2727

28-
const { filepath, rawText, text, range, args, languageId } = options
29-
28+
const { rawText, text, range, args } = options
29+
const filepath = document.uri.fsPath
3030
const keypath = generateKeyFromText(rawText || text, filepath)
31-
const templates = Global.refactorTemplates(keypath, args, languageId, i).filter(Boolean)
31+
const templates = Global.interpretRefactorTemplates(keypath, args, document, i).filter(Boolean)
3232

3333
return {
3434
range,

src/commands/detectHardStrings.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { commands, window } from 'vscode'
22
import { ExtensionModule } from '~/modules'
3-
import { Global } from '~/core'
3+
import { Global, DetectionResult } from '~/core'
44
import { Commands } from '~/commands'
5-
import { DetectionResult, trimDetection } from '~/extraction'
5+
import { trimDetection } from '~/extraction'
66
import i18n from '~/i18n'
77

88
export async function DetectHardStrings() {

src/commands/extractText.ts

+11-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { commands, window, workspace, QuickPickItem, Range } from 'vscode'
1+
import { commands, window, QuickPickItem, Range, TextDocument } from 'vscode'
22
import { trim } from 'lodash'
33
import { overrideConfirm } from './overrideConfirm'
44
import { keypathValidate } from './keypathValidate'
@@ -8,21 +8,20 @@ import { extractHardStrings, generateKeyFromText, Config, CurrentFile } from '~/
88
import i18n from '~/i18n'
99
import { Log, promptTemplates } from '~/utils'
1010
import { parseHardString } from '~/extraction/parseHardString'
11-
import { DetectionResult } from '~/extraction'
11+
import { DetectionResult } from '~/core/types'
1212

1313
interface QuickPickItemWithKey extends QuickPickItem {
1414
keypath: string
1515
type: 'tree' | 'node' | 'new' | 'existed'
1616
}
1717

1818
export interface ExtractTextOptions {
19-
filepath: string
2019
text: string
2120
rawText?: string
2221
args?: string[]
2322
range: Range
2423
isDynamic?: boolean
25-
languageId?: string
24+
document: TextDocument
2625
isInsert?: boolean
2726
}
2827

@@ -35,16 +34,15 @@ async function ExtractOrInsertCommnad(options?: ExtractTextOptions, detection?:
3534
if (!options) {
3635
// execute from command palette, get from active document
3736
const editor = window.activeTextEditor
38-
const document = editor?.document
39-
if (!editor || !document)
37+
const currentDoc = editor?.document
38+
if (!editor || !currentDoc)
4039
return
4140

4241
options = {
43-
filepath: document.uri.fsPath,
4442
text: '',
45-
rawText: trim(document.getText(editor.selection), '\'"` '),
43+
rawText: trim(currentDoc.getText(editor.selection), '\'"` '),
4644
range: editor.selection,
47-
languageId: document.languageId,
45+
document: currentDoc,
4846
isInsert: editor.selection.start.isEqual(editor.selection.end),
4947
}
5048
}
@@ -53,12 +51,13 @@ async function ExtractOrInsertCommnad(options?: ExtractTextOptions, detection?:
5351
const loader = CurrentFile.loader
5452

5553
if (options.rawText && !options.text) {
56-
const result = parseHardString(options.rawText, options.languageId, options.isDynamic)
54+
const result = parseHardString(options.rawText, options.document?.languageId, options.isDynamic)
5755
options.text = result?.text || ''
5856
options.args = result?.args
5957
}
6058

61-
const { filepath, text, rawText, range, args, languageId, isInsert } = options
59+
const { text, rawText, range, args, document, isInsert } = options
60+
const filepath = document.uri.fsPath
6261

6362
const default_keypath = generateKeyFromText(rawText || text, filepath)
6463

@@ -147,15 +146,13 @@ async function ExtractOrInsertCommnad(options?: ExtractTextOptions, detection?:
147146
return
148147
}
149148

150-
const replacer = await promptTemplates(keypath, args, languageId, detection)
149+
const replacer = await promptTemplates(keypath, args, document, detection)
151150

152151
if (!replacer) {
153152
window.showWarningMessage(i18n.t('prompt.extraction_canceled'))
154153
return
155154
}
156155

157-
const document = await workspace.openTextDocument(filepath)
158-
159156
await extractHardStrings(document, [{
160157
range,
161158
replaceTo: replacer,

src/commands/manipulations/insertKey.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export async function InsertKey() {
1414
if (!keypath)
1515
return
1616

17-
const replacer = await promptTemplates(keypath, [], document.languageId)
17+
const replacer = await promptTemplates(keypath, [], document)
1818

1919
if (!replacer)
2020
return

src/core/Config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { KeyStyle, DirStructureAuto, TargetPickingStrategy } from '.'
88
import i18n from '~/i18n'
99
import { CaseStyles } from '~/utils/changeCase'
1010
import { ExtractionHTMLOptions } from '~/extraction/parsers/options'
11+
import { resolveRefactorTemplate } from '~/utils/resolveRefactorTemplate'
1112

1213
export class Config {
1314
static readonly reloadConfigs = [
@@ -319,7 +320,7 @@ export class Config {
319320
}
320321

321322
static get refactorTemplates() {
322-
return this.getConfig<string[]>('refactor.templates') || []
323+
return resolveRefactorTemplate(this.getConfig<string[]>('refactor.templates') || [])
323324
}
324325

325326
static get disablePathParsing() {

src/core/CurrentFile.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Global } from './Global'
44
import { VueSfcLoader } from './loaders/VueSfcLoader'
55
import { Loader, Analyst } from '.'
66
import { DetectHardStrings } from '~/commands/detectHardStrings'
7-
import { DetectionResult } from '~/extraction'
7+
import { DetectionResult } from '~/core/types'
88

99
export class CurrentFile {
1010
static _vue_sfc_loader: VueSfcLoader | null = null

src/core/Extract.ts

+3-10
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
import { basename, extname } from 'path'
2-
import { Range, TextDocument, window } from 'vscode'
2+
import { TextDocument, window } from 'vscode'
33
import { nanoid } from 'nanoid'
44
import limax from 'limax'
55
import { Config } from '../extension'
6-
import { CurrentFile } from '.'
6+
import { ExtractInfo } from './types'
7+
import { CurrentFile } from './CurrentFile'
78
import { changeCase } from '~/utils/changeCase'
89

9-
export interface ExtractInfo {
10-
range: Range
11-
replaceTo: string
12-
keypath?: string
13-
message?: string
14-
locale?: string
15-
}
16-
1710
export function generateKeyFromText(text: string, filepath?: string) {
1811
let key: string
1912

src/core/Global.ts

+25-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { extname } from 'path'
2-
import { workspace, commands, window, EventEmitter, Event, ExtensionContext, ConfigurationChangeEvent } from 'vscode'
2+
import { workspace, commands, window, EventEmitter, Event, ExtensionContext, ConfigurationChangeEvent, TextDocument } from 'vscode'
33
import { uniq } from 'lodash'
44
import { ParsePathMatcher } from '../utils/PathMatcher'
55
import { EXT_NAMESPACE } from '../meta'
@@ -16,7 +16,7 @@ import { LocaleLoader } from './loaders/LocaleLoader'
1616
import { Analyst } from './Analyst'
1717
import i18n from '~/i18n'
1818
import { Log, getExtOfLanguageId, normalizeUsageMatchRegex } from '~/utils'
19-
import { DetectionResult } from '~/extraction'
19+
import { DetectionResult } from '~/core/types'
2020

2121
export class Global {
2222
private static _loaders: Record<string, LocaleLoader> = {}
@@ -108,10 +108,30 @@ export class Global {
108108
return result.value as KeyStyle
109109
}
110110

111-
static refactorTemplates(keypath: string, args?: string[], languageId?: string, detection?: DetectionResult) {
111+
static interpretRefactorTemplates(keypath: string, args?: string[], document?: TextDocument, detection?: DetectionResult) {
112+
// const path = document?.uri.fsPath
113+
const customTemplates = Config.refactorTemplates
114+
.filter((i) => {
115+
if (i.source && i.source !== detection?.source)
116+
return false
117+
// TODO: include / exclude
118+
return true
119+
})
120+
const argsString = args?.length ? `,${args?.join(',')}` : ''
121+
122+
const customReplacers = customTemplates
123+
.flatMap(i => i.templates)
124+
.map(i => i
125+
.replace(/{key}/, keypath)
126+
.replace(/{args}/, argsString),
127+
)
128+
129+
const frameworkReplacers = this.enabledFrameworks
130+
.flatMap(f => f.refactorTemplates(keypath, args, document, detection))
131+
112132
return uniq([
113-
...Config.refactorTemplates.map(i => i.replace(/{key}/, keypath)),
114-
...this.enabledFrameworks.flatMap(f => f.refactorTemplates(keypath, args, languageId, detection)),
133+
...customReplacers,
134+
...frameworkReplacers,
115135
])
116136
}
117137

src/core/types.ts

+32
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Range, TextDocument, TextEditor } from 'vscode'
2+
13
export interface OptionalFeatures {
24
VueSfc?: boolean
35
LinkedMessages?: boolean
@@ -150,3 +152,33 @@ export enum TargetPickingStrategy {
150152
FilePrevious ='file-previous',
151153
GlobalPrevious = 'global-previous',
152154
}
155+
156+
export type DetectionSource = 'html-attribute' | 'html-inline' | 'js-string' | 'js-template' | 'jsx-text'
157+
158+
export interface DetectionResult {
159+
text: string
160+
start: number
161+
end: number
162+
document?: TextDocument
163+
editor?: TextEditor
164+
isDynamic?: boolean
165+
fullText?: string
166+
fullStart?: number
167+
fullEnd?: number
168+
source: DetectionSource
169+
}
170+
171+
export interface ExtractInfo {
172+
range: Range
173+
replaceTo: string
174+
keypath?: string
175+
message?: string
176+
locale?: string
177+
}
178+
179+
export interface CustomRefactorTemplate {
180+
source?: DetectionSource
181+
templates: string[]
182+
include?: string[]
183+
exclude?: string[]
184+
}

src/editor/extract.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ import { Commands } from '~/commands'
66
import i18n from '~/i18n'
77
import { parseHardString } from '~/extraction/parseHardString'
88
import { ExtractTextOptions } from '~/commands/extractText'
9-
import { DetectionResult } from '~/extraction'
9+
import { DetectionResult } from '~/core/types'
1010

1111
export function DetectionResultToExtraction(detection: DetectionResult, document: TextDocument): ExtractTextOptions {
1212
return {
1313
isDynamic: detection.isDynamic,
14-
languageId: document.languageId,
15-
filepath: document.fileName,
14+
document,
1615
text: '',
1716
rawText: detection.text.trim(),
1817
isInsert: false,
@@ -77,7 +76,7 @@ class ExtractProvider implements CodeActionProvider {
7776
description: CurrentFile.loader.getValueByKey(key, Config.displayLanguage, 30),
7877
}))
7978
.filter(labelDescription => labelDescription.description === text)
80-
.flatMap(t => Global.refactorTemplates(t.label, args, document.languageId, diagnostic?.detection))
79+
.flatMap(t => Global.interpretRefactorTemplates(t.label, args, document, diagnostic?.detection))
8180
.map(t => ({
8281
command: Commands.replace_with,
8382
title: i18n.t('refactor.replace_with', t),

src/editor/problems.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { ExtensionContext, languages, DiagnosticCollection, window, TextDocument, Diagnostic, DiagnosticSeverity, Range, workspace, Uri } from 'vscode'
22
import { EXT_NAMESPACE } from '../meta'
33
import { ExtensionModule } from '../modules'
4-
import { Global, KeyDetector, Config, Loader, CurrentFile } from '~/core'
4+
import { Global, KeyDetector, Config, Loader, CurrentFile, DetectionResult } from '~/core'
55
import i18n from '~/i18n'
6-
import { DetectionResult } from '~/extraction'
76

87
export const PROBLEM_CODE_HARD_STRING = 'i18n-ally-hard-string'
98
export const PROBLEM_KEY_MISSING = 'i18n-ally-key-missing'

src/extraction/parsers/babel.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { parse } from '@babel/parser'
33
import traverse from '@babel/traverse'
44
import { DefaultDynamicExtractionsRules, DefaultExtractionRules, ExtractionRule } from '../rules'
55
import { shouldExtract } from '../shouldExtract'
6-
import { DetectionResult } from './types'
6+
import { DetectionResult } from '~/core/types'
77

88
export function detect(
99
input: string,
@@ -21,7 +21,7 @@ export function detect(
2121
],
2222
})
2323

24-
function handlePath(path: any, type: DetectionResult['type']) {
24+
function handlePath(path: any, type: DetectionResult['source']) {
2525
if (!path.node.start || !path.node.end)
2626
return
2727

@@ -47,7 +47,7 @@ export function detect(
4747
start: fullStart,
4848
end: fullEnd,
4949
text,
50-
type,
50+
source: type,
5151
})
5252
}
5353

0 commit comments

Comments
 (0)