Skip to content

Commit b5d7c0c

Browse files
committed
feat(sfc): sfc works in readonly mode
feat(sfc): support more features for sfc
1 parent d26bce4 commit b5d7c0c

File tree

17 files changed

+258
-166
lines changed

17 files changed

+258
-166
lines changed

examples/sfc/src/App.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
</div>
1212
</template>
1313

14-
<i18n locale='en'>
14+
<i18n>
1515
{
1616
"en": {
1717
"hello": "hello world!"

src/commands/keyManipulations.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as path from 'path'
22
import { window, commands, workspace, Selection, TextEditorRevealType, env } from 'vscode'
3-
import { Global, Commands, LocaleRecord, Node, Config } from '../core'
3+
import { Global, Commands, LocaleRecord, Node, Config, CurrentFile } from '../core'
44
import { ExtensionModule } from '../modules'
55
import { decorateLocale, Log } from '../utils'
66
import { LocaleTreeItem } from '../views/LocalesTreeView'
@@ -18,7 +18,7 @@ function getNode (item?: LocaleTreeItem | CommandOptions) {
1818

1919
if (item instanceof LocaleTreeItem)
2020
return item.node
21-
return Global.loader.getRecordByKey(item.keypath, item.locale, true)
21+
return CurrentFile.loader.getRecordByKey(item.keypath, item.locale, true)
2222
}
2323

2424
async function getRecordFromNode (node: Node, defaultLocale?: string) {
@@ -29,7 +29,7 @@ async function getRecordFromNode (node: Node, defaultLocale?: string) {
2929
return node
3030

3131
if (node.type === 'node') {
32-
const locales = Global.loader.getShadowLocales(node)
32+
const locales = CurrentFile.loader.getShadowLocales(node)
3333
const locale = defaultLocale || await window.showQuickPick(
3434
Global.visibleLocales,
3535
{ placeHolder: i18n.t('prompt.choice_locale') }
@@ -61,7 +61,7 @@ const m: ExtensionModule = (ctx) => {
6161
const from = (item && !(item instanceof LocaleTreeItem) && item.from) || Config.sourceLanguage
6262

6363
try {
64-
await Global.loader.translator.MachineTranslate(node, from)
64+
await Global.loader.translator.MachineTranslate(node, from) // TODO:sfc
6565
}
6666
catch (err) {
6767
Log.error(err.toString())
@@ -114,7 +114,7 @@ const m: ExtensionModule = (ctx) => {
114114
let node: Node | undefined
115115

116116
if (typeof item === 'string')
117-
node = Global.loader.getTreeNodeByKey(item)
117+
node = CurrentFile.loader.getTreeNodeByKey(item)
118118
else
119119
node = item.node
120120

@@ -131,7 +131,7 @@ const m: ExtensionModule = (ctx) => {
131131
if (!newkeypath)
132132
return
133133

134-
const edit = await Global.loader.renameKey(oldkeypath, newkeypath)
134+
const edit = await Global.loader.renameKey(oldkeypath, newkeypath) // TODO:sfc
135135
await workspace.applyEdit(edit)
136136
}
137137
catch (err) {
@@ -163,7 +163,7 @@ const m: ExtensionModule = (ctx) => {
163163
})
164164

165165
if (newvalue !== undefined && newvalue !== node.value) {
166-
await Global.loader.writeToFile({
166+
await Global.loader.writeToFile({ // TODO:sfc
167167
value: newvalue,
168168
keypath: node.keypath,
169169
filepath: node.filepath,
@@ -190,7 +190,7 @@ const m: ExtensionModule = (ctx) => {
190190
records = Object.values(node.locales)
191191

192192
try {
193-
await Global.loader.writeToFile(records
193+
await Global.loader.writeToFile(records // TODO:sfc
194194
.filter(record => !record.shadow)
195195
.map(record => ({
196196
value: undefined,

src/core/CurrentFile.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,28 @@ import { workspace, ExtensionContext, Uri, window } from 'vscode'
22
import { ComposedLoader } from './loaders/ComposedLoader'
33
import { Global } from './Global'
44
import { SFCLoader } from './loaders/SfcLoader'
5+
import { Config } from './Config'
56
import { Loader } from '.'
67

78
export class CurrentFile {
89
static _sfc_loader: SFCLoader | null = null
10+
static _composed_loader = new ComposedLoader()
911

1012
static watch (ctx: ExtensionContext) {
1113
ctx.subscriptions.push(workspace.onDidSaveTextDocument(e => this.update(e.uri)))
1214
ctx.subscriptions.push(window.onDidChangeActiveTextEditor(e => this.update(e && e.document.uri)))
13-
15+
ctx.subscriptions.push(Global.onDidChangeLoader(() => this.updateLoaders()))
1416
this.update(window.activeTextEditor && window.activeTextEditor.document.uri)
17+
if (!Config.sfc)
18+
this.updateLoaders()
1519
}
1620

1721
static update (uri?: Uri) {
1822
if (!Global.enabled)
1923
return
24+
if (!Config.sfc)
25+
return
26+
2027
if (this._sfc_loader) {
2128
if (uri && this._sfc_loader.uri.path === uri.path) {
2229
this._sfc_loader.load()
@@ -29,13 +36,18 @@ export class CurrentFile {
2936
}
3037
if (uri && uri.fsPath.endsWith('.vue'))
3138
this._sfc_loader = new SFCLoader(uri)
39+
40+
this.updateLoaders()
3241
}
3342

34-
static get loader () {
43+
static updateLoaders () {
3544
const loaders: Loader[] = [Global.loader]
3645
if (this._sfc_loader)
3746
loaders.push(this._sfc_loader)
47+
this._composed_loader.loaders = loaders
48+
}
3849

39-
return new ComposedLoader(loaders)
50+
static get loader () {
51+
return this._composed_loader
4052
}
4153
}

src/core/Global.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { isVueI18nProject } from '../utils/utils'
55
import { ConfigLocalesGuide } from '../commands/configLocales'
66
import { PARSERS } from '../parsers'
77
import { Log } from '../utils'
8+
import { CurrentFile } from './CurrentFile'
89
import { LocaleLoader, Config } from '.'
910

1011
export type KeyStyle = 'auto' | 'nested' | 'flat'
@@ -165,7 +166,7 @@ export class Global {
165166
}
166167

167168
static get allLocales () {
168-
return this.loader.locales
169+
return CurrentFile.loader.locales
169170
}
170171

171172
static get visibleLocales () {

src/core/loaders/ComposedLoader.ts

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
11
import _ from 'lodash'
2+
import { Disposable } from 'vscode'
23
import { LocaleNode, LocaleTree } from '../types'
34
import { Loader } from './Loader'
45

56
export class ComposedLoader extends Loader {
6-
constructor (
7-
public loaders: Loader[] = []
8-
) {
7+
constructor () {
98
super('[Composed]')
109
}
1110

11+
_loaders: Loader[] = []
12+
_watchers: Disposable[] = []
13+
14+
get loaders () {
15+
return this._loaders
16+
}
17+
18+
set loaders (value: Loader[]) {
19+
this._watchers.forEach(d => d.dispose())
20+
this._loaders = value
21+
this._watchers = this.loaders.map(loader =>
22+
loader.onDidChange(() => this._onDidChange.fire())
23+
)
24+
this._onDidChange.fire()
25+
}
26+
1227
get root (): LocaleTree {
1328
const children: Record<string | number, LocaleTree | LocaleNode> = {}
14-
for (const loader of this.loaders) {
29+
for (const loader of this._loaders) {
1530
const loaderChildren = loader.root.children
1631
for (const key of Object.keys(loaderChildren))
1732
children[key] = loaderChildren[key]
@@ -20,25 +35,43 @@ export class ComposedLoader extends Loader {
2035
}
2136

2237
get locales (): string[] {
23-
return _(this.loaders)
38+
return _(this._loaders)
2439
.flatMap(l => l.locales)
2540
.uniq()
2641
.value()
2742
}
2843

2944
getShadowFilePath (keypath: string, locale: string) {
30-
for (const loader of this.loaders.reverse()) {
45+
for (const loader of this._loaders.reverse()) {
3146
const value = loader.getShadowFilePath(keypath, locale)
3247
if (value)
3348
return value
3449
}
3550
}
3651

3752
getTreeNodeByKey (keypath: string, tree?: LocaleTree) {
38-
for (const loader of this.loaders.reverse()) {
53+
for (const loader of this._loaders.reverse()) {
3954
const value = loader.getTreeNodeByKey(keypath, tree)
4055
if (value)
4156
return value
4257
}
4358
}
59+
60+
getFilepathByKey (keypath: string, locale?: string) {
61+
for (const loader of this._loaders.reverse()) {
62+
const value = loader.getFilepathByKey(keypath, locale)
63+
if (value)
64+
return value
65+
}
66+
}
67+
68+
getCoverage (locale: string, keys?: string[]) {
69+
for (const loader of this._loaders.reverse()) {
70+
const value = loader.getCoverage(locale, keys)
71+
if (value)
72+
return value
73+
}
74+
}
75+
76+
// TODO:sfc merge tree nodes
4477
}

src/core/loaders/Loader.ts

Lines changed: 116 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,125 @@
1-
import { Disposable } from 'vscode'
1+
import { Disposable, EventEmitter } from 'vscode'
2+
import _ from 'lodash'
23
import { Log } from '../../utils'
3-
import { LocaleTree, LocaleNode, LocaleRecord } from '../types'
4+
import { LocaleTree, LocaleNode, LocaleRecord, FlattenLocaleTree, Coverage } from '../types'
45
import { Config, Global } from '..'
56

7+
export interface NodeOptions{
8+
locale: string
9+
readonly?: boolean
10+
filepath: string
11+
sfc?: boolean
12+
}
13+
614
export abstract class Loader extends Disposable {
715
protected _disposables: Disposable[] = []
16+
protected _onDidChange = new EventEmitter<undefined>()
17+
readonly onDidChange = this._onDidChange.event
18+
19+
protected _flattenLocaleTree: FlattenLocaleTree = {}
20+
protected _localeTree: LocaleTree = new LocaleTree({ keypath: '' })
821

922
constructor (
1023
public readonly name: string
1124
) {
1225
super(() => this.onDispose())
1326
}
1427

15-
protected onDispose () {
16-
Log.info(`🗑 Disposing loader "${this.name}"`)
17-
this._disposables.forEach(d => d.dispose())
18-
this._disposables = []
19-
}
20-
21-
abstract get root(): LocaleTree
2228
abstract get locales(): string[]
2329

2430
abstract getShadowFilePath(keypath: string, locale: string): string | undefined
2531

32+
get root () {
33+
return this._localeTree
34+
}
35+
36+
get flattenLocaleTree () {
37+
return this._flattenLocaleTree
38+
}
39+
2640
splitKeypath (keypath: string): string[] {
2741
return keypath.replace(/\[(.*?)\]/g, '.$1').split('.')
2842
}
2943

44+
getCoverage (locale: string, keys?: string[]): Coverage | undefined {
45+
keys = keys || Object.keys(this._flattenLocaleTree)
46+
const total = keys.length
47+
const translated = keys.filter((key) => {
48+
return this._flattenLocaleTree[key] && this._flattenLocaleTree[key].getValue(locale)
49+
})
50+
return {
51+
locale,
52+
total,
53+
translated: translated.length,
54+
keys,
55+
}
56+
}
57+
58+
protected updateTree (tree: LocaleTree | undefined, data: any, keypath: string, keyname: string, options: NodeOptions, isCollection = false) {
59+
tree = tree || new LocaleTree({
60+
keypath,
61+
keyname,
62+
isCollection,
63+
sfc: options.sfc,
64+
})
65+
tree.values[options.locale] = data
66+
for (const [key, value] of Object.entries(data)) {
67+
const newKeyPath = keypath
68+
? (isCollection
69+
? `${keypath}[${key}]`
70+
: `${keypath}.${key}`)
71+
: (isCollection
72+
? `[${key}]`
73+
: key)
74+
75+
// should go nested
76+
if (_.isArray(value)) {
77+
let subtree: LocaleTree|undefined
78+
if (tree.getChild(key) && tree.getChild(key).type === 'tree')
79+
subtree = tree.getChild(key) as LocaleTree
80+
81+
tree.setChild(key, this.updateTree(subtree, value, newKeyPath, key, options, true))
82+
continue
83+
}
84+
85+
if (_.isObject(value)) {
86+
let subtree: LocaleTree|undefined
87+
if (tree.getChild(key) && tree.getChild(key).type === 'tree')
88+
subtree = tree.getChild(key) as LocaleTree
89+
90+
tree.setChild(key, this.updateTree(subtree, value, newKeyPath, key, options))
91+
continue
92+
}
93+
94+
// init node
95+
if (!tree.getChild(key)) {
96+
const node = new LocaleNode({
97+
keypath: newKeyPath,
98+
keyname: key,
99+
readonly: options.readonly,
100+
sfc: options.sfc,
101+
})
102+
tree.setChild(key, node)
103+
this._flattenLocaleTree[node.keypath] = node
104+
}
105+
106+
// add locales to exitsing node
107+
const node = tree.getChild(key)
108+
if (node.type === 'node') {
109+
node.locales[options.locale] = new LocaleRecord({
110+
keypath: newKeyPath,
111+
keyname: key,
112+
value: `${value}`,
113+
locale: options.locale,
114+
filepath: options.filepath,
115+
sfc: options.sfc,
116+
readonly: options.readonly,
117+
})
118+
}
119+
}
120+
return tree
121+
}
122+
30123
getTreeNodeByKey (keypath: string, tree?: LocaleTree): LocaleNode | LocaleTree | undefined {
31124
const root = !tree
32125
tree = tree || this.root
@@ -50,6 +143,14 @@ export abstract class Loader extends Disposable {
50143
return undefined
51144
}
52145

146+
getFilepathByKey (key: string, locale?: string) {
147+
locale = locale || Config.displayLanguage
148+
const record = this.getRecordByKey(key, locale)
149+
if (record && record.filepath)
150+
return record.filepath
151+
return undefined
152+
}
153+
53154
getValueByKey (keypath: string, locale?: string, clamp: boolean = true, stringifySpace?: number) {
54155
locale = locale || Config.displayLanguage
55156

@@ -135,4 +236,10 @@ export abstract class Loader extends Disposable {
135236
})
136237
return locales
137238
}
239+
240+
protected onDispose () {
241+
Log.info(`🗑 Disposing loader "${this.name}"`)
242+
this._disposables.forEach(d => d.dispose())
243+
this._disposables = []
244+
}
138245
}

0 commit comments

Comments
 (0)