Skip to content

Commit 6535141

Browse files
committed
feat: enhance configuration validation and update workspace settings handling
1 parent a105252 commit 6535141

3 files changed

Lines changed: 84 additions & 36 deletions

File tree

src/configuration.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ export class ConfigurationManager {
228228
const config = resource
229229
? vscode.workspace.getConfiguration(ConfigurationManager.SECTION, resource)
230230
: this.getConfig();
231-
await config.update(key, value, target || vscode.ConfigurationTarget.Global);
231+
await config.update(key, value, target || vscode.ConfigurationTarget.Workspace);
232232
}
233233

234234
/**
@@ -262,6 +262,7 @@ export class ConfigurationManager {
262262
const config = this.getConfig();
263263
for (const key of ConfigurationManager.RESETTABLE_KEYS) {
264264
await config.update(key, undefined, vscode.ConfigurationTarget.Global);
265+
await config.update(key, undefined, vscode.ConfigurationTarget.Workspace);
265266
}
266267
}
267268

@@ -284,6 +285,44 @@ export class ConfigurationManager {
284285
}
285286
}
286287

288+
// Validate numeric bounds (mirrors package.json constraints)
289+
const imageMinSize = this.getImageMinSize();
290+
if (imageMinSize < 1) {
291+
errors.push(`imageMinSize must be >= 1, got ${imageMinSize}`);
292+
}
293+
294+
const threshold = this.getDocumentSplittingThreshold();
295+
if (threshold < 100000 || threshold > 2000000) {
296+
errors.push(`documentSplittingThreshold must be between 100000 and 2000000, got ${threshold}`);
297+
}
298+
299+
const batchWindow = this.getBatchDetectionWindow();
300+
if (batchWindow < 1000 || batchWindow > 10000) {
301+
errors.push(`batchDetectionWindow must be between 1000 and 10000, got ${batchWindow}`);
302+
}
303+
304+
// Validate non-empty string settings
305+
const subdirectoryName = this.getMarkdownSubdirectoryName();
306+
if (typeof subdirectoryName !== 'string' || !subdirectoryName.trim()) {
307+
errors.push('markdownSubdirectoryName must be a non-empty string');
308+
}
309+
310+
const imageFolder = this.getImageOutputFolder();
311+
if (typeof imageFolder !== 'string' || !imageFolder.trim()) {
312+
errors.push('imageOutputFolder must be a non-empty string');
313+
}
314+
315+
// Validate enum values
316+
const validBatchBehaviors: BatchConversionBehavior[] = ['askForEach', 'askOnce', 'convertAll', 'skipAll'];
317+
if (!validBatchBehaviors.includes(this.getBatchConversionBehavior())) {
318+
errors.push(`Invalid batchConversionBehavior value`);
319+
}
320+
321+
const validDeleteBehaviors: DeleteGeneratedOutputsBehavior[] = ['ask', 'delete', 'keep'];
322+
if (!validDeleteBehaviors.includes(this.getDeleteGeneratedOutputsBehavior())) {
323+
errors.push(`Invalid deleteGeneratedOutputsBehavior value`);
324+
}
325+
287326
return {
288327
isValid: errors.length === 0,
289328
errors

src/fileWatcher.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ export class FileWatcher implements vscode.Disposable {
3131
this.projectManager = projectManager;
3232
this.initializeWatchers();
3333

34-
// Listen for configuration changes
34+
// Listen for configuration changes - only rebuild watchers when watcher-relevant settings change
3535
this.configChangeDisposable = vscode.workspace.onDidChangeConfiguration(e => {
36-
if (e.affectsConfiguration('documentConverter')) {
36+
if (this.affectsWatcherPattern(e)) {
3737
this.reinitializeWatchers();
3838
}
3939
});
@@ -130,6 +130,9 @@ export class FileWatcher implements vscode.Disposable {
130130
return;
131131
}
132132

133+
// If a create event is already queued for this path, skip the change event.
134+
// The pending flush will read the file at processing time, capturing any
135+
// modifications made between create and flush.
133136
if (eventType === 'changed' && this.pendingCreatedFiles.has(filePath)) {
134137
console.log(`Skipping change event because create event is already queued: ${filePath}`);
135138
return;
@@ -250,6 +253,16 @@ export class FileWatcher implements vscode.Disposable {
250253
this.initializeWatchers();
251254
}
252255

256+
/**
257+
* Check if a configuration change affects the watcher glob pattern.
258+
* Only settings that change which files are watched require a rebuild.
259+
*/
260+
private affectsWatcherPattern(e: vscode.ConfigurationChangeEvent): boolean {
261+
return e.affectsConfiguration('documentConverter.copyTextFiles')
262+
|| e.affectsConfiguration('documentConverter.organizeInSubdirectory')
263+
|| e.affectsConfiguration('documentConverter.markdownSubdirectoryName');
264+
}
265+
253266
private disposeWatchers(): void {
254267
this.watchers.forEach(watcher => watcher.dispose());
255268
this.watchers = [];
@@ -410,6 +423,7 @@ export class FileWatcher implements vscode.Disposable {
410423
return;
411424
}
412425

426+
// 'askOnce': batch confirmation when multiple files, single confirmation when one file
413427
if (behavior === 'askOnce' && existingFiles.length > 1) {
414428
const shouldConvert = await this.askForBatchConversionConfirmation(existingFiles);
415429
if (shouldConvert) {
@@ -418,6 +432,7 @@ export class FileWatcher implements vscode.Disposable {
418432
return;
419433
}
420434

435+
// 'askForEach': confirm each file individually (also handles 'askOnce' with a single file)
421436
await this.processCreatedFilesIndividually(existingFiles);
422437
}
423438

src/projectManager.ts

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as vscode from 'vscode';
22
import * as fs from 'fs';
33
import * as path from 'path';
44
import { localize } from './i18n';
5+
import { SUPPORTED_CONVERT_EXTENSIONS } from './constants';
56

67
export interface ProjectConfig {
78
enabled: boolean;
@@ -116,7 +117,7 @@ export class ProjectManager {
116117
}
117118

118119
/**
119-
* 检查项目中是否有可转换的文档文件
120+
* 检查项目中是否有可转换的文档文件(扫描根目录及一级子目录)
120121
*/
121122
hasConvertibleFiles(rootPath?: string): boolean {
122123
const workspaceFolders = vscode.workspace.workspaceFolders;
@@ -125,14 +126,31 @@ export class ProjectManager {
125126
}
126127

127128
const targetPath = rootPath || workspaceFolders[0].uri.fsPath;
128-
const supportedExtensions = ['.docx', '.xlsx', '.pptx', '.pdf'];
129-
129+
130130
try {
131-
const files = fs.readdirSync(targetPath);
132-
return files.some(file => {
133-
const ext = path.extname(file).toLowerCase();
134-
return supportedExtensions.includes(ext);
135-
});
131+
const entries = fs.readdirSync(targetPath, { withFileTypes: true });
132+
for (const entry of entries) {
133+
if (entry.isFile()) {
134+
const ext = path.extname(entry.name).toLowerCase();
135+
if (SUPPORTED_CONVERT_EXTENSIONS.includes(ext)) {
136+
return true;
137+
}
138+
} else if (entry.isDirectory() && !entry.name.startsWith('.')) {
139+
// Scan one level of subdirectories, skipping hidden folders
140+
try {
141+
const subEntries = fs.readdirSync(path.join(targetPath, entry.name));
142+
for (const subEntry of subEntries) {
143+
const ext = path.extname(subEntry).toLowerCase();
144+
if (SUPPORTED_CONVERT_EXTENSIONS.includes(ext)) {
145+
return true;
146+
}
147+
}
148+
} catch {
149+
// Skip directories we can't read
150+
}
151+
}
152+
}
153+
return false;
136154
} catch (error) {
137155
console.error('Error checking for convertible files:', error);
138156
return false;
@@ -177,16 +195,8 @@ export class ProjectManager {
177195
);
178196

179197
if (choice === convertNowLabel) {
180-
// 触发文件夹转换命令
181198
await vscode.commands.executeCommand('documentConverter.convertFolder', vscode.Uri.file(rootPath));
182199
}
183-
} else if (!showConvertPrompt) {
184-
// 当 showConvertPrompt 为 false 时,不显示任何消息,由调用方处理
185-
// 这避免了重复的消息提示
186-
} else {
187-
vscode.window.showInformationMessage(
188-
localize('project.info.enabledReady')
189-
);
190200
}
191201

192202
return true;
@@ -297,23 +307,7 @@ export class ProjectManager {
297307

298308
switch (choice) {
299309
case enableLabel:
300-
// 用户已经在第一个弹窗中表达了启用意图,直接启用并自动转换,无需再次询问
301-
const enabled = await this.enableForProject(undefined, false);
302-
if (enabled) {
303-
const workspaceFolders = vscode.workspace.workspaceFolders;
304-
if (workspaceFolders && this.hasConvertibleFiles(workspaceFolders[0].uri.fsPath)) {
305-
// 直接执行转换,不再询问
306-
await vscode.commands.executeCommand('documentConverter.convertFolder', workspaceFolders[0].uri);
307-
vscode.window.showInformationMessage(
308-
localize('project.info.conversionStarted')
309-
);
310-
} else {
311-
vscode.window.showInformationMessage(
312-
localize('project.info.enabledReady')
313-
);
314-
}
315-
}
316-
return enabled;
310+
return await this.enableForProject(undefined, true);
317311
case dontAskAgainLabel:
318312
await this.saveWorkspaceProjectConfig(
319313
vscode.workspace.workspaceFolders![0].uri.fsPath,

0 commit comments

Comments
 (0)