@@ -6,12 +6,15 @@ import { getConfig } from './utils'
6
6
import { soundPlayer } from './sound'
7
7
import { voicePlayer } from './resource/voice'
8
8
import PluginState from './utils/PluginState'
9
+ import * as fs from 'fs' ;
10
+ import * as path from 'path' ;
9
11
10
12
const PLAY_VOICE_COMMAND = 'qwerty-learner.playVoice'
11
13
const PREV_WORD_COMMAND = 'qwerty-learner.prevWord'
12
14
const NEXT_WORD_COMMAND = 'qwerty-learner.nextWord'
13
15
const TOGGLE_TRANSLATION_COMMAND = 'qwerty-learner.toggleTranslation'
14
16
const TOGGLE_DIC_NAME_COMMAND = 'qwerty-learner.toggleDicName'
17
+ const OPEN_WRONG_WORDS_COMMAND = 'qwerty-learner.openWrongWords'
15
18
16
19
export function activate ( context : vscode . ExtensionContext ) {
17
20
const pluginState = new PluginState ( context )
@@ -22,6 +25,7 @@ export function activate(context: vscode.ExtensionContext) {
22
25
const translationBar = vscode . window . createStatusBarItem ( vscode . StatusBarAlignment . Left , - 103 )
23
26
const prevWord = vscode . window . createStatusBarItem ( vscode . StatusBarAlignment . Left , - 104 )
24
27
const nextWord = vscode . window . createStatusBarItem ( vscode . StatusBarAlignment . Left , - 105 )
28
+ const wrongWords = vscode . window . createStatusBarItem ( vscode . StatusBarAlignment . Left , - 106 )
25
29
prevWord . text = '<'
26
30
prevWord . tooltip = '切换上一个单词'
27
31
prevWord . command = PREV_WORD_COMMAND
@@ -34,54 +38,108 @@ export function activate(context: vscode.ExtensionContext) {
34
38
translationBar . command = TOGGLE_TRANSLATION_COMMAND
35
39
wordBar . command = TOGGLE_DIC_NAME_COMMAND
36
40
wordBar . tooltip = '隐藏/显示字典名称'
41
+ wrongWords . text = '错词本'
42
+ wrongWords . tooltip = '点击打开错词本'
43
+ wrongWords . command = OPEN_WRONG_WORDS_COMMAND
37
44
38
- vscode . workspace . onDidChangeTextDocument ( ( e ) => {
39
- if ( ! pluginState . isStart ) {
40
- return
41
- }
45
+ function logWrongWord ( word : string , translation : string ) {
46
+ const filePath = path . join ( __dirname , 'wrong_words.txt' ) ;
42
47
43
- if ( pluginState . readOnlyMode ) {
44
- return
48
+ let fileContent = '' ;
49
+ try {
50
+ fileContent = fs . readFileSync ( filePath , 'utf8' ) ;
51
+ } catch ( err ) {
52
+ if ( ( err as NodeJS . ErrnoException ) . code !== 'ENOENT' ) throw err ;
45
53
}
46
54
47
- const { uri } = e . document
48
- // 避免破坏配置文件
49
- if ( uri . scheme . indexOf ( 'vscode' ) !== - 1 ) {
50
- return
51
- }
55
+ const lines = fileContent . split ( '\n' ) . filter ( line => line . trim ( ) !== '' ) ;
56
+ const wordMap : { [ key : string ] : { translation : string , count : number } } = { } ;
57
+ lines . forEach ( line => {
58
+ const parts = line . split ( ' ' ) ;
59
+ const count = parseInt ( parts . pop ( ) ! , 10 ) ;
60
+ const translation = parts . pop ( ) ! ;
61
+ const word = parts . join ( ' ' ) ;
62
+ wordMap [ word ] = { translation, count } ;
63
+ } ) ;
52
64
53
- const { range, text, rangeLength } = e . contentChanges [ 0 ]
54
- if ( ! ( text !== '' && text . length === 1 ) ) {
55
- return
56
- }
57
- // 删除用户输入的字符
58
- const newRange = new vscode . Range ( range . start . line , range . start . character , range . end . line , range . end . character + 1 )
59
- const editAction = new vscode . WorkspaceEdit ( )
60
- editAction . delete ( uri , newRange )
61
- vscode . workspace . applyEdit ( editAction )
62
- if ( pluginState . hasWrong ) {
63
- return
65
+ if ( wordMap [ word ] ) {
66
+ wordMap [ word ] . count += 1 ;
67
+ } else {
68
+ wordMap [ word ] = { translation, count : 1 } ;
64
69
}
65
- soundPlayer ( 'click' )
66
- inputBar . text = pluginState . getCurrentInputBarContent ( text )
67
-
68
- const compareResult = pluginState . compareResult
69
- if ( compareResult === - 2 ) {
70
- // 用户完成单词输入
71
- soundPlayer ( 'success' )
72
- pluginState . finishWord ( )
73
- initializeBar ( )
74
- } else if ( compareResult >= 0 ) {
75
- pluginState . wrongInput ( )
76
- inputBar . color = pluginState . highlightWrongColor
77
- soundPlayer ( 'wrong' )
70
+
71
+ const newLines = Object . keys ( wordMap )
72
+ . map ( word => `${ word } ${ wordMap [ word ] . translation } ${ wordMap [ word ] . count } ` )
73
+ . sort ( ( a , b ) => {
74
+ const countA = parseInt ( a . split ( ' ' ) . pop ( ) ! , 10 ) ;
75
+ const countB = parseInt ( b . split ( ' ' ) . pop ( ) ! , 10 ) ;
76
+ return countB - countA ;
77
+ } ) ;
78
+
79
+ fs . writeFileSync ( filePath , newLines . join ( '\n' ) , 'utf8' ) ;
80
+ console . log ( `单词 "${ word } " 和翻译 "${ translation } " 已记录.` ) ;
81
+ }
82
+
83
+
84
+
85
+
86
+ vscode . workspace . onDidChangeTextDocument ( ( e ) => {
87
+ if ( ! pluginState . isStart ) {
88
+ return ;
89
+ }
90
+
91
+ if ( pluginState . readOnlyMode ) {
92
+ return ;
93
+ }
94
+
95
+ const { uri } = e . document ;
96
+
97
+ if ( uri . scheme . indexOf ( 'vscode' ) !== - 1 ) {
98
+ return ;
99
+ }
100
+
101
+ const { range, text, rangeLength } = e . contentChanges [ 0 ] ;
102
+
103
+ if ( ! ( text !== '' && text . length === 1 ) ) {
104
+ return ;
105
+ }
106
+
107
+ const newRange = new vscode . Range ( range . start . line , range . start . character , range . end . line , range . end . character + 1 ) ;
108
+ const editAction = new vscode . WorkspaceEdit ( ) ;
109
+ editAction . delete ( uri , newRange ) ;
110
+ vscode . workspace . applyEdit ( editAction ) ;
111
+
112
+ if ( pluginState . hasWrong ) {
113
+ return ;
114
+ }
115
+
116
+ soundPlayer ( 'click' ) ;
117
+ inputBar . text = pluginState . getCurrentInputBarContent ( text ) ;
118
+
119
+ const compareResult = pluginState . compareResult ;
120
+
121
+ if ( compareResult === - 2 ) {
122
+ soundPlayer ( 'success' ) ;
123
+ pluginState . finishWord ( ) ;
124
+ initializeBar ( ) ;
125
+ } else if ( compareResult >= 0 ) {
126
+ pluginState . wrongInput ( ) ;
127
+ inputBar . color = pluginState . highlightWrongColor ;
128
+ soundPlayer ( 'wrong' ) ;
129
+
130
+ // 调用记录正确单词和翻译的函数
131
+ const currentWord = pluginState . getCurrentWord ( ) ;
132
+ const translation = pluginState . getCurrentTranslation ( )
133
+ logWrongWord ( currentWord , translation ) ;
134
+
78
135
setTimeout ( ( ) => {
79
- pluginState . clearWrong ( )
80
- inputBar . color = undefined
81
- initializeBar ( )
82
- } , pluginState . highlightWrongDelay )
83
- }
84
- } )
136
+ pluginState . clearWrong ( ) ;
137
+ inputBar . color = undefined ;
138
+ initializeBar ( ) ;
139
+ } , pluginState . highlightWrongDelay ) ;
140
+ } e
141
+ } ) ;
142
+
85
143
86
144
vscode . workspace . onDidChangeConfiguration ( ( event ) => {
87
145
if ( event . affectsConfiguration ( 'qwerty-learner.placeholder' ) ) {
@@ -108,6 +166,7 @@ export function activate(context: vscode.ExtensionContext) {
108
166
prevWord . show ( )
109
167
nextWord . show ( )
110
168
translationBar . show ( )
169
+ wrongWords . show ( )
111
170
if ( pluginState . readOnlyMode ) {
112
171
setUpReadOnlyInterval ( )
113
172
}
@@ -118,6 +177,7 @@ export function activate(context: vscode.ExtensionContext) {
118
177
prevWord . hide ( )
119
178
nextWord . hide ( )
120
179
translationBar . hide ( )
180
+ wrongWords . hide ( )
121
181
removeReadOnlyInterval ( )
122
182
}
123
183
} ) ,
@@ -179,6 +239,19 @@ export function activate(context: vscode.ExtensionContext) {
179
239
vscode . window . showInformationMessage ( '章节循环模式已关闭' )
180
240
}
181
241
} ) ,
242
+ // 注册打开错词本的命令
243
+ vscode . commands . registerCommand ( OPEN_WRONG_WORDS_COMMAND , ( ) => {
244
+ const filePath = path . join ( __dirname , 'wrong_words.txt' ) ;
245
+ fs . exists ( filePath , ( exists ) => {
246
+ if ( ! exists ) {
247
+ fs . writeFile ( filePath , '' , ( error ) => { } )
248
+ }
249
+ } ) ;
250
+
251
+ vscode . workspace . openTextDocument ( filePath ) . then ( doc => {
252
+ vscode . window . showTextDocument ( doc ) ;
253
+ } ) ;
254
+ } )
182
255
] ,
183
256
)
184
257
@@ -196,6 +269,7 @@ export function activate(context: vscode.ExtensionContext) {
196
269
} )
197
270
}
198
271
}
272
+
199
273
function setUpWordBar ( ) {
200
274
wordBar . text = pluginState . getInitialWordBarContent ( )
201
275
playVoice ( )
0 commit comments