add settings

This commit is contained in:
Grol Grol 2025-04-11 21:09:29 +03:00
parent c751add5fe
commit 3d1ea00009
6 changed files with 178 additions and 45 deletions

View file

@ -1,25 +1,45 @@
// checkboxUtils.test.ts // checkboxUtils.test.ts
import { CheckboxUtils, CheckboxLineInfo } from '../src/checkboxUtils'; import { CheckboxUtils } from '../src/checkboxUtils';
import { CheckboxSyncPluginSettings } from '../src/types'; // Предполагаем, что типы в types.ts import { CheckboxSyncPluginSettings } from '../src/types'; // Предполагаем, что типы в types.ts
// --- Mock Settings --- // --- Mock Settings ---
const defaultSettings: Readonly<CheckboxSyncPluginSettings> = { const defaultSettings: Readonly<CheckboxSyncPluginSettings> = {
xOnlyMode: false, // По умолчанию: любой не-пробел считается 'checked' xOnlyMode: false, // По умолчанию: любой не-пробел считается 'checked'
enableAutomaticParentState: true,
enableAutomaticChildState: true,
}; };
const xOnlySettings: Readonly<CheckboxSyncPluginSettings> = { const xOnlySettings: Readonly<CheckboxSyncPluginSettings> = {
xOnlyMode: true, // Только 'x' считается 'checked' xOnlyMode: true, // Только 'x' считается 'checked'
enableAutomaticParentState: true,
enableAutomaticChildState: true,
};
const settingsParentSyncDisabled: Readonly<CheckboxSyncPluginSettings> = {
xOnlyMode: false, // или true, если нужно тестировать комбинацию
enableAutomaticParentState: false, // Родитель НЕ обновляется от детей
enableAutomaticChildState: true,
};
const settingsChildSyncDisabled: Readonly<CheckboxSyncPluginSettings> = {
xOnlyMode: false,
enableAutomaticParentState: true, // Оставим true для этого варианта
enableAutomaticChildState: false, // Дети НЕ обновляются от родителя
}; };
// --- Test Suite --- // --- Test Suite ---
describe('CheckboxUtils', () => { describe('CheckboxUtils', () => {
let utilsDefault: CheckboxUtils; let utilsDefault: CheckboxUtils;
let utilsXOnly: CheckboxUtils; let utilsXOnly: CheckboxUtils;
let utilsParentSyncDisabled: CheckboxUtils;
let utilsChildSyncDisabled: CheckboxUtils
beforeEach(() => { beforeEach(() => {
// Создаем новые экземпляры перед каждым тестом для изоляции // Создаем новые экземпляры перед каждым тестом для изоляции
utilsDefault = new CheckboxUtils(defaultSettings); utilsDefault = new CheckboxUtils(defaultSettings);
utilsXOnly = new CheckboxUtils(xOnlySettings); utilsXOnly = new CheckboxUtils(xOnlySettings);
utilsParentSyncDisabled = new CheckboxUtils(settingsParentSyncDisabled);
utilsChildSyncDisabled = new CheckboxUtils(settingsChildSyncDisabled);
}); });
// --- matchCheckboxLine --- // --- matchCheckboxLine ---
@ -453,49 +473,72 @@ describe('CheckboxUtils', () => {
// --- syncText (Integration) --- // --- syncText (Integration) ---
describe('syncText', () => { describe('syncText', () => {
it('should handle user checking parent (down -> up)', () => { describe('When user changes parent state directly', () => {
const textBefore = [ const textBefore = [
'- [ ] Parent', '- [ ] Parent',
' - [ ] Child 1', ' - [ ] Child 1',
' - [ ] Child 2', ' - [ ] Child 2',
].join('\n'); ].join('\n');
const textAfterUserCheck = [ const textAfterUserCheck = [
'- [x] Parent', // User checked this '- [x] Parent', // User checked this
' - [ ] Child 1', ' - [ ] Child 1',
' - [ ] Child 2', ' - [ ] Child 2',
].join('\n'); ].join('\n');
const result = utilsDefault.syncText(textAfterUserCheck, textBefore); it('should propagate down when child sync is ENABLED', () => {
const expected = [ // Down propagation first, then up (which changes nothing here) const result = utilsDefault.syncText(textAfterUserCheck, textBefore);
'- [x] Parent', const expected = [
' - [x] Child 1', '- [x] Parent',
' - [x] Child 2', ' - [x] Child 1', // Propagated down
].join('\n'); ' - [x] Child 2', // Propagated down
expect(result).toBe(expected); ].join('\n');
}); expect(result).toBe(expected);
});
it('should handle user unchecking parent (down -> up)', () => { it('should NOT propagate down when child sync is DISABLED', () => {
const textBefore = [ const result = utilsChildSyncDisabled.syncText(textAfterUserCheck, textBefore);
'- [x] Parent', const expected = [ // Ожидаем, что дети НЕ изменились
' - [x] Child 1', '- [ ] Parent', // propagateStateFromChildren
' - [x] Child 2', ' - [ ] Child 1',
' - [ ] Child 2',
].join('\n');
expect(result).toBe(expected);
});
const textBeforeUncheck = [
'- [x] Parent',
' - [x] Child 1',
' - [x] Child 2',
].join('\n'); ].join('\n');
const textAfterUserUncheck = [ const textAfterUserUncheck = [
'- [ ] Parent', // User unchecked this '- [ ] Parent', // User unchecked this
' - [x] Child 1', ' - [x] Child 1',
' - [x] Child 2', ' - [x] Child 2',
].join('\n'); ].join('\n');
const result = utilsDefault.syncText(textAfterUserUncheck, textBefore); it('should propagate down (uncheck) when child sync is ENABLED', () => {
const expected = [ // Down propagation first, then up const result = utilsDefault.syncText(textAfterUserUncheck, textBeforeUncheck);
'- [ ] Parent', const expected = [
' - [ ] Child 1', '- [ ] Parent',
' - [ ] Child 2', ' - [ ] Child 1', // Propagated down
].join('\n'); ' - [ ] Child 2', // Propagated down
expect(result).toBe(expected); ].join('\n');
}); expect(result).toBe(expected);
});
it('should handle user checking the last child, making parent checked (no down -> up)', () => { it('should NOT propagate down (uncheck) when child sync is DISABLED', () => {
const result = utilsChildSyncDisabled.syncText(textAfterUserUncheck, textBeforeUncheck);
const expected = [ // Ожидаем, что дети НЕ изменились
'- [x] Parent', // propagateStateFromChildren
' - [x] Child 1',
' - [x] Child 2',
].join('\n');
expect(result).toBe(expected);
});
});
it('should update parent when parent sync is ENABLED and user checks last child', () => {
const textBefore = [ const textBefore = [
'- [ ] Parent', '- [ ] Parent',
' - [x] Child 1', ' - [x] Child 1',
@ -517,7 +560,27 @@ describe('CheckboxUtils', () => {
expect(result).toBe(expected); expect(result).toBe(expected);
}); });
it('should handle user unchecking one child, making parent unchecked (no down -> up)', () => { it('should NOT update parent when parent sync is DISABLED and user checks last child', () => {
const textBefore = [
'- [ ] Parent',
' - [x] Child 1',
' - [ ] Child 2', // User will check this
].join('\n');
const textAfterChildCheck = [
'- [ ] Parent', // State before syncText runs propagateFromChildren
' - [x] Child 1',
' - [x] Child 2', // User checked this
].join('\n');
const result = utilsParentSyncDisabled.syncText(textAfterChildCheck, textBefore); // Использует utilsParentSyncDisabled
const expected = [ // Родитель НЕ должен обновиться
'- [ ] Parent',
' - [x] Child 1',
' - [x] Child 2',
].join('\n');
expect(result).toBe(expected);
});
it('should update parent when parent sync is ENABLED and user unchecks child', () => {
const textBefore = [ const textBefore = [
'- [x] Parent', '- [x] Parent',
' - [x] Child 1', // User will uncheck this ' - [x] Child 1', // User will uncheck this
@ -539,7 +602,27 @@ describe('CheckboxUtils', () => {
expect(result).toBe(expected); expect(result).toBe(expected);
}); });
it('should handle multiple changes correctly (no down -> up)', () => { it('should NOT update parent when parent sync is DISABLED and user unchecks child', () => {
const textBefore = [
'- [x] Parent',
' - [x] Child 1', // User will uncheck this
' - [x] Child 2',
].join('\n');
const textAfterChildUncheck = [
'- [x] Parent', // State before syncText runs propagateFromChildren
' - [ ] Child 1', // User unchecked this
' - [x] Child 2',
].join('\n');
const result = utilsParentSyncDisabled.syncText(textAfterChildUncheck, textBefore); // utilsParentSyncDisabled
const expected = [ // Родитель НЕ должен обновиться
'- [x] Parent',
' - [ ] Child 1',
' - [x] Child 2',
].join('\n');
expect(result).toBe(expected);
});
it('should update parent based on children when multiple lines change and parent sync is ENABLED', () => {
const textBefore = [ const textBefore = [
'- [ ] Parent', '- [ ] Parent',
' - [ ] Child 1', ' - [ ] Child 1',
@ -566,5 +649,25 @@ describe('CheckboxUtils', () => {
expect(result).toBe(expected); expect(result).toBe(expected);
}); });
it('should NOT update parent when multiple lines change and parent sync is DISABLED', () => {
const textBefore = [
'- [ ] Parent',
' - [ ] Child 1',
' - [ ] Child 2',
].join('\n');
const textAfterMultipleChanges = [ // User checks Parent and Child 1
'- [x] Parent',
' - [x] Child 1',
' - [ ] Child 2',
].join('\n');
const result = utilsParentSyncDisabled.syncText(textAfterMultipleChanges, textBefore); // utilsParentSyncDisabled
const expected = [ // Родитель НЕ корректируется, остается как есть
'- [x] Parent',
' - [x] Child 1',
' - [ ] Child 2',
].join('\n');
expect(result).toBe(expected);
});
}); });
}); });

View file

@ -5,8 +5,8 @@ describe("CheckboxUtils old", () => {
let checkboxUtilsSpaceOnly: CheckboxUtils; let checkboxUtilsSpaceOnly: CheckboxUtils;
beforeEach(() => { beforeEach(() => {
checkboxUtilsXOnly = new CheckboxUtils({ xOnlyMode: true }); checkboxUtilsXOnly = new CheckboxUtils({ xOnlyMode: true, enableAutomaticParentState: true, enableAutomaticChildState: true, });
checkboxUtilsSpaceOnly = new CheckboxUtils({ xOnlyMode: false }); checkboxUtilsSpaceOnly = new CheckboxUtils({ xOnlyMode: false, enableAutomaticParentState: true, enableAutomaticChildState: true,});
}); });
describe("findCheckboxesLine", () => { describe("findCheckboxesLine", () => {
@ -120,7 +120,7 @@ describe("CheckboxUtils old", () => {
describe("updateSettings", () => { describe("updateSettings", () => {
test("updates xOnlyMode setting correctly", () => { test("updates xOnlyMode setting correctly", () => {
const settings = { xOnlyMode: true }; const settings = { xOnlyMode: true, enableAutomaticParentState: true, enableAutomaticChildState: true,};
const utils = new CheckboxUtils(settings); const utils = new CheckboxUtils(settings);
expect(utils.isCheckedSymbol("-")).toBe(false); expect(utils.isCheckedSymbol("-")).toBe(false);

View file

@ -20,10 +20,32 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
toggle toggle
.setValue(this.plugin.settings.xOnlyMode) .setValue(this.plugin.settings.xOnlyMode)
.onChange(async (value) => { .onChange(async (value) => {
await this.plugin.updateSettings((settings) => { await this.plugin.updateSettings(settings => {
settings.xOnlyMode = value; settings.xOnlyMode = value;
}); });
}) })
); );
new Setting(containerEl)
.setName('Update parent checkbox state automatically')
.setDesc('If enabled, the state of a parent checkbox will be automatically updated based on the state of its children (all children checked = parent checked). If disabled, only manually changing a parent will affect its children.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.enableAutomaticParentState)
.onChange(async (value) => {
await this.plugin.updateSettings(settings => {
settings.enableAutomaticParentState = value;
});
}));
new Setting(containerEl)
.setName('Update child checkbox state automatically')
.setDesc('If enabled, changing the state of a parent checkbox will automatically update the state of all its direct and nested children. If disabled, changing a parent checkbox will not affect its children.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.enableAutomaticChildState)
.onChange(async (value) => {
await this.plugin.updateSettings(settings => {
settings.enableAutomaticChildState = value;
});
}));
} }
} }

View file

@ -100,9 +100,13 @@ export class CheckboxUtils {
} }
syncText(text: string, textBefore: string | undefined): string { syncText(text: string, textBefore: string | undefined): string {
let newText; let newText = text;
newText = this.propagateStateToChildrenFromSingleDiff(text, textBefore); if (this.settings.enableAutomaticChildState) {
newText = this.propagateStateFromChildren(newText); newText = this.propagateStateToChildrenFromSingleDiff(text, textBefore);
}
if (this.settings.enableAutomaticParentState) {
newText = this.propagateStateFromChildren(newText);
}
return newText; return newText;
} }

View file

@ -9,6 +9,8 @@ import { FileChangeEventHandler } from "./events/FileChangeEventHandler";
const DEFAULT_SETTINGS: CheckboxSyncPluginSettings = { const DEFAULT_SETTINGS: CheckboxSyncPluginSettings = {
xOnlyMode: true, xOnlyMode: true,
enableAutomaticParentState: true,
enableAutomaticChildState: true,
}; };
export default class CheckboxSyncPlugin extends Plugin { export default class CheckboxSyncPlugin extends Plugin {

View file

@ -1,3 +1,5 @@
export interface CheckboxSyncPluginSettings { export interface CheckboxSyncPluginSettings {
xOnlyMode: boolean; xOnlyMode: boolean;
enableAutomaticParentState: boolean;
enableAutomaticChildState: boolean;
} }