feat: Implement core logic using flexible symbols and policy

This commit is contained in:
Grol Grol 2025-04-22 02:21:15 +03:00
parent 01ed090ad6
commit 63dac704f5
4 changed files with 59 additions and 30 deletions

View file

@ -1,6 +1,6 @@
import { App, ButtonComponent, DropdownComponent, Notice, PluginSettingTab, Setting, TextComponent, ToggleComponent } from "obsidian";
import CheckboxSyncPlugin from "./main";
import { UnknownSymbolPolicyEnum } from "./types";
import { CheckboxState } from "./types";
export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
plugin: CheckboxSyncPlugin;
@ -116,9 +116,9 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
.addDropdown(dropdown => {
this.unknownPolicyDropdown = dropdown;
dropdown
.addOption(UnknownSymbolPolicyEnum.Checked, 'Treat as Checked')
.addOption(UnknownSymbolPolicyEnum.Unchecked, 'Treat as Unchecked')
.addOption(UnknownSymbolPolicyEnum.Ignore, 'Ignore')
.addOption(CheckboxState.Checked, 'Treat as Checked')
.addOption(CheckboxState.Unchecked, 'Treat as Unchecked')
.addOption(CheckboxState.Ignore, 'Ignore')
.setValue(this.plugin.settings.unknownSymbolPolicy)
.onChange(() => this.applyButton.setDisabled(false))
});
@ -178,7 +178,7 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
try {
const checkedValue = this.checkedSymbolsInput.getValue();
const uncheckedValue = this.uncheckedSymbolsInput.getValue();
const policyValue = this.unknownPolicyDropdown.getValue() as UnknownSymbolPolicyEnum;
const policyValue = this.unknownPolicyDropdown.getValue() as CheckboxState;
const parentValue = this.parentToggle.getValue();
const childValue = this.childToggle.getValue();

View file

@ -1,10 +1,12 @@
import { CheckboxSyncPluginSettings } from "./types";
import { CheckboxState, CheckboxSyncPluginSettings } from "./types";
export interface CheckboxLineInfo {
indent: number;
marker: string;
checkChar: string;
checkboxCharPosition: number;
checkboxState: CheckboxState;
isChecked: boolean | undefined;
}
export class CheckboxUtils {
@ -22,21 +24,33 @@ export class CheckboxUtils {
const marker = match[2];
const checkChar = match[3];
const checkboxCharPosition = indent + marker.length + 2;
const checkboxState = this.getCheckboxState(checkChar);
let isChecked = checkboxState === CheckboxState.Ignore ? undefined : checkboxState === CheckboxState.Checked;
return {
indent,
marker,
checkChar,
checkboxCharPosition
checkboxCharPosition,
checkboxState,
isChecked
};
}
isCheckedSymbol(text: string): boolean {
return this.settings.xOnlyMode ? text === "x" : text !== " ";
getCheckboxState(text: string): CheckboxState {
// Проверяем наличие символа в списке отмеченных
if (this.settings.checkedSymbols.includes(text)) {
return CheckboxState.Checked;
}
// Проверяем наличие символа в списке неотмеченных
if (this.settings.uncheckedSymbols.includes(text)) {
return CheckboxState.Unchecked;
}
return this.settings.unknownSymbolPolicy;
}
updateLineCheckboxStateWithInfo(line: string, shouldBeChecked: boolean, lineInfo: CheckboxLineInfo): string {
const newCheckChar = shouldBeChecked ? "x" : " ";
const newCheckChar = shouldBeChecked ? this.settings.checkedSymbols[0] : this.settings.uncheckedSymbols[0];
const pos = lineInfo.checkboxCharPosition;
if (pos >= 0 && pos < line.length) {
return line.substring(0, pos) + newCheckChar + line.substring(pos + 1);
@ -52,17 +66,24 @@ export class CheckboxUtils {
const parentLineInfo = this.matchCheckboxLine(lines[parentLine]);
if (!parentLineInfo) {
console.log(`checkbox not found in line ${parentLine}`)
console.warn(`checkbox not found in line ${parentLine}`)
return text;
}
const isChecked = this.isCheckedSymbol(parentLineInfo.checkChar);
if (parentLineInfo.checkboxState === CheckboxState.Ignore) {
return text;
}
let parentIsChecked = parentLineInfo.isChecked!;
let j = parentLine + 1;
while (j < lines.length) {
const childLineInfo = this.matchCheckboxLine(lines[j]);
if (!childLineInfo || childLineInfo.indent <= parentLineInfo.indent) break;
lines[j] = this.updateLineCheckboxStateWithInfo(lines[j], isChecked, childLineInfo);
if (childLineInfo.checkboxState !== CheckboxState.Ignore) {
lines[j] = this.updateLineCheckboxStateWithInfo(lines[j], parentIsChecked, childLineInfo);
}
j++;
}
return lines.join("\n");
@ -75,24 +96,32 @@ export class CheckboxUtils {
const parentLineInfo = this.matchCheckboxLine(lines[i]);
if (!parentLineInfo) continue;
const isChecked = this.isCheckedSymbol(parentLineInfo.checkChar);
if (parentLineInfo.checkboxState === CheckboxState.Ignore) {
continue;
}
const parentIsChecked = parentLineInfo.isChecked!;
let allChildrenChecked = true;
let hasChildren = false;
let j = i + 1;
while (j < lines.length) {
for (let j = i + 1; j < lines.length; j++) {
const childLineInfo = this.matchCheckboxLine(lines[j]);
if (!childLineInfo || childLineInfo.indent <= parentLineInfo.indent) break;
if (!childLineInfo) {
continue;
}
if (childLineInfo.indent <= parentLineInfo.indent) break;
if (childLineInfo.checkboxState === CheckboxState.Ignore) {
continue;
}
hasChildren = true;
const childrenIsChecked = this.isCheckedSymbol(childLineInfo.checkChar);
const childrenIsChecked = childLineInfo.isChecked!;
if (!childrenIsChecked) {
allChildrenChecked = false;
break;
}
j++;
}
if (hasChildren && isChecked !== allChildrenChecked) {
if (hasChildren && parentIsChecked !== allChildrenChecked) {
lines[i] = this.updateLineCheckboxStateWithInfo(lines[i], allChildrenChecked, parentLineInfo);
}
}
@ -137,7 +166,7 @@ export class CheckboxUtils {
lineInfoBefore && lineInfoAfter &&
lineInfoBefore.indent === lineInfoAfter.indent &&
lineInfoBefore.marker === lineInfoAfter.marker &&
lineInfoBefore.checkChar !== lineInfoAfter.checkChar
lineInfoBefore.checkboxState !== lineInfoAfter.checkboxState
) {
return this.propagateStateToChildren(text, diffIndexes[0]);
}

View file

@ -3,7 +3,7 @@ import { CheckboxSyncPluginSettingTab } from "./CheckboxSyncPluginSettingTab";
import { CheckboxUtils } from "./checkboxUtils";
import FileStateHolder from "./FileStateHolder";
import SyncController from "./SyncController";
import { CheckboxSyncPluginSettings, UnknownSymbolPolicyEnum } from "./types";
import { CheckboxState, CheckboxSyncPluginSettings } from "./types";
import { FileLoadEventHandler } from "./events/FileLoadEventHandler";
import { FileChangeEventHandler } from "./events/FileChangeEventHandler";
@ -13,7 +13,7 @@ const DEFAULT_SETTINGS: CheckboxSyncPluginSettings = {
enableAutomaticChildState: true,
checkedSymbols: ['x'],
uncheckedSymbols: [' '],
unknownSymbolPolicy: UnknownSymbolPolicyEnum.Checked,
unknownSymbolPolicy: CheckboxState.Checked,
};
export default class CheckboxSyncPlugin extends Plugin {

View file

@ -1,14 +1,14 @@
export enum UnknownSymbolPolicyEnum {
Checked = 'checked',
Unchecked = 'unchecked',
Ignore = 'ignore'
}
export interface CheckboxSyncPluginSettings {
xOnlyMode: boolean;
enableAutomaticParentState: boolean;
enableAutomaticChildState: boolean;
checkedSymbols: string[];
uncheckedSymbols: string[];
unknownSymbolPolicy: UnknownSymbolPolicyEnum;
unknownSymbolPolicy: CheckboxState;
}
export enum CheckboxState {
Checked = 'checked',
Unchecked = 'unchecked',
Ignore = 'ignore'
}