mirror of
https://github.com/groldsf/obsidian_check_plugin.git
synced 2026-07-22 05:37:48 +00:00
рабочая версия
This commit is contained in:
parent
a7f33e676d
commit
96727c61f5
25 changed files with 1496 additions and 307 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -22,3 +22,4 @@ data.json
|
|||
.DS_Store
|
||||
|
||||
coverage/
|
||||
test-report.html
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { CheckboxUtils } from "src/checkboxUtils";
|
||||
import { CheckboxUtils } from "src/core/checkboxUtils";
|
||||
import { FileFilter } from "src/FileFilter";
|
||||
import TextSyncPipeline from "src/TextSyncPipeline";
|
||||
import { CheckboxSyncPluginSettings, DEFAULT_SETTINGS } from "src/types";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { CheckboxLineInfo, CheckboxUtils } from '../src/checkboxUtils'; // Путь к вашему файлу
|
||||
import { CheckboxLineInfo, CheckboxUtils } from '../src/core/checkboxUtils'; // Путь к вашему файлу
|
||||
import { CheckboxState, CheckboxSyncPluginSettings, DEFAULT_SETTINGS } from '../src/types'; // Путь к вашему файлу типов
|
||||
|
||||
// Вспомогательная функция для создания настроек с переопределениями
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// checkboxUtils.plainLists.test.ts
|
||||
|
||||
import { CheckboxUtils } from '../src/checkboxUtils';
|
||||
import { CheckboxUtils } from '../src/core/checkboxUtils';
|
||||
import { CheckboxState, CheckboxSyncPluginSettings, DEFAULT_SETTINGS } from '../src/types';
|
||||
|
||||
// Вспомогательная функция для создания настроек с переопределениями
|
||||
|
|
|
|||
312
__tests__/core/CheckboxUtils2.test.ts
Normal file
312
__tests__/core/CheckboxUtils2.test.ts
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
// import { CheckboxLineInfo, CheckboxUtils } from '../src/checkboxUtils'; // Путь к вашему файлу
|
||||
import { CheckboxUtils2 } from 'src/core/CheckboxUtils2';
|
||||
import { CheckboxSyncPluginSettings, DEFAULT_SETTINGS } from 'src/types'; // Путь к вашему файлу типов
|
||||
|
||||
// Вспомогательная функция для создания настроек с переопределениями
|
||||
const createSettings = (overrides: Partial<CheckboxSyncPluginSettings> = {}): Readonly<CheckboxSyncPluginSettings> => {
|
||||
return { ...DEFAULT_SETTINGS, ...overrides } as Readonly<CheckboxSyncPluginSettings>;
|
||||
};
|
||||
|
||||
describe('CheckboxUtils2', () => {
|
||||
// --- Тесты для syncText ---
|
||||
describe('syncText', () => {
|
||||
it('should propagate down only if child sync enabled and diff matches', () => {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: true,
|
||||
enableAutomaticParentState: false, // Parent sync off
|
||||
}));
|
||||
const textBefore = [
|
||||
'- [ ] Parent',
|
||||
' - [ ] Child',
|
||||
].join('\n');
|
||||
const textAfterParentChecked = [ // Simulate user checking parent
|
||||
'- [x] Parent',
|
||||
' - [ ] Child',
|
||||
].join('\n');
|
||||
const expected = [
|
||||
'- [x] Parent',
|
||||
' - [x] Child', // Propagated down
|
||||
].join('\n');
|
||||
expect(utils.syncText(textAfterParentChecked, textBefore)).toBe(expected);
|
||||
});
|
||||
|
||||
it('should propagate up only if parent sync enabled', () => {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: false, // Child sync off
|
||||
enableAutomaticParentState: true,
|
||||
}));
|
||||
const textBefore = [
|
||||
'- [ ] Parent',
|
||||
' - [ ] Child',
|
||||
].join('\n');
|
||||
const textAfterChildChecked = [ // Simulate user checking child
|
||||
'- [ ] Parent',
|
||||
' - [x] Child',
|
||||
].join('\n');
|
||||
const expected = [
|
||||
'- [x] Parent', // Propagated up
|
||||
' - [x] Child',
|
||||
].join('\n');
|
||||
// syncText calls propagateFromChildren unconditionally if enabled
|
||||
expect(utils.syncText(textAfterChildChecked, textBefore)).toBe(expected);
|
||||
});
|
||||
|
||||
it('should propagate down then up if both enabled', () => {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: true,
|
||||
enableAutomaticParentState: true,
|
||||
}));
|
||||
const textBefore = [
|
||||
'- [ ] Parent',
|
||||
' - [ ] Child',
|
||||
].join('\n');
|
||||
const textAfterParentChecked = [ // Simulate user checking parent
|
||||
'- [x] Parent',
|
||||
' - [ ] Child',
|
||||
].join('\n');
|
||||
// 1. User checks parent: '- [x] Parent', ' - [ ] Child'
|
||||
// 2. Propagate down: '- [x] Parent', ' - [x] Child'
|
||||
// 3. Propagate up: (no change needed as parent is already checked)
|
||||
const expectedDown = [
|
||||
'- [x] Parent',
|
||||
' - [x] Child', // Propagated down
|
||||
].join('\n');
|
||||
expect(utils.syncText(textAfterParentChecked, textBefore)).toBe(expectedDown);
|
||||
|
||||
const textAfterChildChecked = [ // Simulate user checking child
|
||||
'- [ ] Parent',
|
||||
' - [x] Child',
|
||||
].join('\n');
|
||||
// 1. User checks child: '- [ ] Parent', ' - [x] Child'
|
||||
// 2. Propagate down: (no change, as only one line changed, and it wasn't the parent)
|
||||
// 3. Propagate up: '- [x] Parent', ' - [x] Child'
|
||||
const expectedUp = [
|
||||
'- [x] Parent', // Propagated up
|
||||
' - [x] Child',
|
||||
].join('\n');
|
||||
expect(utils.syncText(textAfterChildChecked, textBefore)).toBe(expectedUp);
|
||||
});
|
||||
|
||||
it('should do nothing if both syncs disabled', () => {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: false,
|
||||
enableAutomaticParentState: false,
|
||||
}));
|
||||
const textBefore = [
|
||||
'- [ ] Parent',
|
||||
' - [ ] Child',
|
||||
].join('\n');
|
||||
const textAfterParentChecked = [ // Simulate user checking parent
|
||||
'- [x] Parent',
|
||||
' - [ ] Child',
|
||||
].join('\n');
|
||||
const textAfterChildChecked = [ // Simulate user checking child
|
||||
'- [ ] Parent',
|
||||
' - [x] Child',
|
||||
].join('\n');
|
||||
expect(utils.syncText(textAfterParentChecked, textBefore)).toBe(textAfterParentChecked);
|
||||
expect(utils.syncText(textAfterChildChecked, textBefore)).toBe(textAfterChildChecked);
|
||||
});
|
||||
|
||||
it('should not propagate down if diff is not a single checkbox state change', () => {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: true, // Down propagation enabled
|
||||
enableAutomaticParentState: false,
|
||||
}));
|
||||
const textBefore = [
|
||||
'- [ ] Parent',
|
||||
' - [ ] Child',
|
||||
].join('\n');
|
||||
const textMultipleChanges = [
|
||||
'- [x] Parent Changed', // Text changed too
|
||||
' - [x] Child also changed',
|
||||
].join('\n');
|
||||
const textIndentChange = [
|
||||
' - [x] Parent', // Indent changed
|
||||
' - [ ] Child',
|
||||
].join('\n');
|
||||
|
||||
// Multiple lines changed
|
||||
expect(utils.syncText(textMultipleChanges, textBefore)).toBe(textMultipleChanges);
|
||||
// Indent changed (diff > 1 line index)
|
||||
expect(utils.syncText(textIndentChange, textBefore)).toBe(textIndentChange);
|
||||
});
|
||||
|
||||
it('equals texts', () => {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: true, // Down propagation enabled
|
||||
enableAutomaticParentState: true,
|
||||
}));
|
||||
const textBefore = [
|
||||
'- [ ] Parent',
|
||||
' - [ ] Child',
|
||||
].join('\n');
|
||||
|
||||
// Multiple lines changed
|
||||
expect(utils.syncText(textBefore, textBefore)).toBe(textBefore);
|
||||
});
|
||||
|
||||
it('checkbox, list', ()=> {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: true,
|
||||
enableAutomaticParentState: true,
|
||||
}));
|
||||
const textBefore = [
|
||||
'- [ ] Parent',
|
||||
' - Child1',
|
||||
' - [ ] Child2',
|
||||
].join('\n');
|
||||
const actualText = [
|
||||
'- [ ] Parent',
|
||||
' - Child1',
|
||||
' - [x] Child2',
|
||||
].join('\n');
|
||||
const expectedText = [
|
||||
'- [x] Parent',
|
||||
' - Child1',
|
||||
' - [x] Child2',
|
||||
].join('\n');
|
||||
|
||||
// Multiple lines changed
|
||||
expect(utils.syncText(actualText, textBefore)).toBe(expectedText);
|
||||
});
|
||||
|
||||
it('checkbox, plain text', ()=> {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: true,
|
||||
enableAutomaticParentState: true,
|
||||
}));
|
||||
const textBefore = [
|
||||
'- [ ] Parent',
|
||||
' Child1',
|
||||
' - [ ] Child2',
|
||||
].join('\n');
|
||||
const actualText = [
|
||||
'- [ ] Parent',
|
||||
' Child1',
|
||||
' - [x] Child2',
|
||||
].join('\n');
|
||||
const expectedText = [
|
||||
'- [x] Parent',
|
||||
' Child1',
|
||||
' - [x] Child2',
|
||||
].join('\n');
|
||||
|
||||
// Multiple lines changed
|
||||
expect(utils.syncText(actualText, textBefore)).toBe(expectedText);
|
||||
});
|
||||
|
||||
describe('without textBefore', () => {
|
||||
it('done from child to parent', () => {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: true,
|
||||
enableAutomaticParentState: true,
|
||||
}));
|
||||
const actualText = [
|
||||
'- [ ] Parent',
|
||||
' - [x] Child',
|
||||
].join('\n');
|
||||
const expected = [
|
||||
'- [x] Parent',
|
||||
' - [x] Child',
|
||||
].join('\n');
|
||||
expect(utils.syncText(actualText, undefined)).toBe(expected);
|
||||
});
|
||||
it('todo from child to parent', () => {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: true,
|
||||
enableAutomaticParentState: true,
|
||||
}));
|
||||
const actualText = [
|
||||
'- [x] Parent',
|
||||
' - [ ] Child',
|
||||
].join('\n');
|
||||
const expected = [
|
||||
'- [ ] Parent',
|
||||
' - [ ] Child',
|
||||
].join('\n');
|
||||
expect(utils.syncText(actualText, undefined)).toBe(expected);
|
||||
});
|
||||
});
|
||||
describe('many roots', () => {
|
||||
it('two roots witout text before', () => {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: true,
|
||||
enableAutomaticParentState: true,
|
||||
}));
|
||||
const actualText = [
|
||||
'- [x] Parent1',
|
||||
' - [ ] Child1',
|
||||
'- [ ] Parent2',
|
||||
' - [x] Child2',
|
||||
].join('\n');
|
||||
const expected = [
|
||||
'- [ ] Parent1',
|
||||
' - [ ] Child1',
|
||||
'- [x] Parent2',
|
||||
' - [x] Child2',
|
||||
].join('\n');
|
||||
expect(utils.syncText(actualText, undefined)).toBe(expected);
|
||||
});
|
||||
|
||||
it('two roots', () => {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: true,
|
||||
enableAutomaticParentState: true,
|
||||
}));
|
||||
const textBefore = [
|
||||
'- [x] Parent1',
|
||||
' - [x] Child1',
|
||||
'- [ ] Parent2',
|
||||
' - [ ] Child2',
|
||||
].join('\n');
|
||||
const actualText = [
|
||||
'- [x] Parent1',
|
||||
' - [ ] Child1',
|
||||
'- [ ] Parent2',
|
||||
' - [x] Child2',
|
||||
].join('\n');
|
||||
const expected = [
|
||||
'- [ ] Parent1',
|
||||
' - [ ] Child1',
|
||||
'- [x] Parent2',
|
||||
' - [x] Child2',
|
||||
].join('\n');
|
||||
expect(utils.syncText(actualText, textBefore)).toBe(expected);
|
||||
});
|
||||
|
||||
it('all type roots', () => {
|
||||
const utils = new CheckboxUtils2(createSettings({
|
||||
enableAutomaticChildState: true,
|
||||
enableAutomaticParentState: true,
|
||||
}));
|
||||
const textBefore = [
|
||||
'- [x] Parent1',
|
||||
' - [x] Child1',
|
||||
'- Parent2',
|
||||
' - Child2',
|
||||
'Parent3',
|
||||
' Child3',
|
||||
].join('\n');
|
||||
const actualText = [
|
||||
'- [x] Parent1',
|
||||
' - [ ] Child1',
|
||||
'- Parent2',
|
||||
' - Child2',
|
||||
'Parent3',
|
||||
' Child3',
|
||||
].join('\n');
|
||||
const expected = [
|
||||
'- [ ] Parent1',
|
||||
' - [ ] Child1',
|
||||
'- Parent2',
|
||||
' - Child2',
|
||||
'Parent3',
|
||||
' Child3',
|
||||
].join('\n');
|
||||
expect(utils.syncText(actualText, textBefore)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -8,13 +8,13 @@ export class InMemoryFilePathStateHolder implements IFilePathStateHolder {
|
|||
}
|
||||
|
||||
public setByPath(filePath: string, text: string): void {
|
||||
console.log(`InMemoryFileStateHolder: Setting state for "${filePath}"`);
|
||||
// console.log(`InMemoryFileStateHolder: Setting state for "${filePath}"`);
|
||||
this.states.set(filePath, text);
|
||||
}
|
||||
|
||||
public getByPath(filePath: string): string | undefined {
|
||||
const state = this.states.get(filePath);
|
||||
console.log(`InMemoryFileStateHolder: Getting state for "${filePath}". Found: ${!!state}`);
|
||||
// console.log(`InMemoryFileStateHolder: Getting state for "${filePath}". Found: ${!!state}`);
|
||||
return state;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,18 @@
|
|||
* https://jestjs.io/docs/configuration
|
||||
*/
|
||||
|
||||
import type {Config} from 'jest';
|
||||
import type { Config } from 'jest';
|
||||
|
||||
const config: Config = {
|
||||
|
||||
reporters: [
|
||||
'default',
|
||||
['jest-html-reporter', {
|
||||
pageTitle: 'Test Report',
|
||||
outputPath: './test-report.html',
|
||||
}]
|
||||
],
|
||||
|
||||
// Automatically clear mock calls, instances, contexts and results before every test
|
||||
clearMocks: true,
|
||||
|
||||
|
|
@ -18,14 +26,14 @@ const config: Config = {
|
|||
|
||||
// Indicates which provider should be used to instrument code for coverage
|
||||
// coverageProvider: "v8",
|
||||
coverageProvider: "babel",
|
||||
coverageProvider: "babel",
|
||||
|
||||
// An array of file extensions your modules use
|
||||
moduleFileExtensions: [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"jsx",
|
||||
"jsx",
|
||||
"json",
|
||||
"node"
|
||||
],
|
||||
|
|
@ -43,11 +51,11 @@ const config: Config = {
|
|||
'^src/(.*)$': '<rootDir>/src/$1',
|
||||
},
|
||||
|
||||
testPathIgnorePatterns: [
|
||||
testPathIgnorePatterns: [
|
||||
"/node_modules/", // Хорошо иметь это явно, хотя Jest часто делает это по умолчанию
|
||||
"/__tests__/fakes/" // Исключаем папку fakes внутри __tests__
|
||||
],
|
||||
|
||||
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
|
|||
1024
package-lock.json
generated
1024
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -22,6 +22,7 @@
|
|||
"builtin-modules": "3.3.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-html-reporter": "^4.3.0",
|
||||
"obsidian": "^1.7.2",
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-node": "^10.9.2",
|
||||
|
|
|
|||
|
|
@ -1,18 +1,15 @@
|
|||
import { Mutex } from "async-mutex";
|
||||
import { Editor, EditorChange, MarkdownFileInfo, MarkdownView, TFile, Vault } from "obsidian";
|
||||
import { CheckboxUtils } from "./checkboxUtils";
|
||||
import TextSyncPipeline from "./TextSyncPipeline";
|
||||
|
||||
export default class SyncController {
|
||||
private vault: Vault;
|
||||
private checkboxUtils: CheckboxUtils;
|
||||
textSyncPipeline: TextSyncPipeline
|
||||
|
||||
private mutex: Mutex;
|
||||
|
||||
constructor(vault: Vault, checkboxUtils: CheckboxUtils, textSyncPipeline: TextSyncPipeline) {
|
||||
constructor(vault: Vault, textSyncPipeline: TextSyncPipeline) {
|
||||
this.vault = vault;
|
||||
this.checkboxUtils = checkboxUtils;
|
||||
this.textSyncPipeline = textSyncPipeline;
|
||||
this.mutex = new Mutex();
|
||||
}
|
||||
|
|
@ -47,7 +44,7 @@ export default class SyncController {
|
|||
const newLines = newText.split("\n");
|
||||
const oldLines = oldText.split("\n");
|
||||
|
||||
const diffIndexes = this.checkboxUtils.findDifferentLineIndexes(oldLines, newLines);
|
||||
const diffIndexes = this.findDifferentLineIndexes(oldLines, newLines);
|
||||
|
||||
const changes: EditorChange[] = [];
|
||||
|
||||
|
|
@ -73,4 +70,19 @@ export default class SyncController {
|
|||
}
|
||||
}
|
||||
|
||||
private findDifferentLineIndexes(lines1: string[], lines2: string[]): number[] {
|
||||
if (lines1.length !== lines2.length) {
|
||||
throw new Error("the length of the lines must be equal");
|
||||
}
|
||||
|
||||
const length = lines1.length;
|
||||
const result: number[] = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (lines1[i] !== lines2[i]) {
|
||||
result.push(i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { CheckboxUtils } from "./checkboxUtils";
|
||||
import { ICheckboxUtils } from "./core/interface/ICheckboxUtils";
|
||||
import { FileFilter } from "./FileFilter";
|
||||
import { IFilePathStateHolder } from "./IFilePathStateHolder";
|
||||
|
||||
export default class TextSyncPipeline {
|
||||
private checkboxUtils: CheckboxUtils;
|
||||
private checkboxUtils: ICheckboxUtils;
|
||||
private fileStateHolder: IFilePathStateHolder;
|
||||
private fileFilter: FileFilter;
|
||||
|
||||
|
||||
constructor(checkboxUtils: CheckboxUtils, fileStateHolder: IFilePathStateHolder, fileFilter: FileFilter) {
|
||||
constructor(checkboxUtils: ICheckboxUtils, fileStateHolder: IFilePathStateHolder, fileFilter: FileFilter) {
|
||||
this.checkboxUtils = checkboxUtils;
|
||||
this.fileStateHolder = fileStateHolder;
|
||||
this.fileFilter = fileFilter;
|
||||
|
|
|
|||
|
|
@ -1,26 +1,18 @@
|
|||
import { CheckboxState, CheckboxSyncPluginSettings } from "src/types";
|
||||
import { CheckboxSyncPluginSettings } from "src/types";
|
||||
import { ICheckboxUtils } from "./interface/ICheckboxUtils";
|
||||
import { ContextFactory } from "./model/ContextFactory";
|
||||
import { PropagateStateToChildrenProcess } from "./process/PropagateStateToChildrenProcess";
|
||||
import { PropagateStateToParentProcess } from "./process/PropagateStateToParentProcess";
|
||||
|
||||
export interface CheckboxLineInfo {
|
||||
indent: number;
|
||||
marker: string;
|
||||
checkChar?: string;
|
||||
checkboxCharPosition?: number;
|
||||
checkboxState: CheckboxState;
|
||||
isChecked?: boolean | undefined;
|
||||
listItemText?: string;
|
||||
}
|
||||
|
||||
export class CheckboxUtils2 implements ICheckboxUtils {
|
||||
private settings: Readonly<CheckboxSyncPluginSettings>;
|
||||
private propagateStateToChildrenProcces: PropagateStateToChildrenProcess;
|
||||
private propagateStateToParentProcces: PropagateStateToParentProcess;
|
||||
private propagateStateToChildrenProcess: PropagateStateToChildrenProcess;
|
||||
private propagateStateToParentProcess: PropagateStateToParentProcess;
|
||||
|
||||
constructor(settings: Readonly<CheckboxSyncPluginSettings>) {
|
||||
this.settings = settings;
|
||||
this.propagateStateToChildrenProcess = new PropagateStateToChildrenProcess();
|
||||
this.propagateStateToParentProcess = new PropagateStateToParentProcess();
|
||||
}
|
||||
|
||||
syncText(text: string, textBefore: string | undefined): string {
|
||||
|
|
@ -29,8 +21,8 @@ export class CheckboxUtils2 implements ICheckboxUtils {
|
|||
}
|
||||
const context = ContextFactory.createContext(text, textBefore, this.settings);
|
||||
|
||||
this.propagateStateToChildrenProcces.process(context);
|
||||
this.propagateStateToParentProcces.process(context);
|
||||
this.propagateStateToChildrenProcess.process(context);
|
||||
this.propagateStateToParentProcess.process(context);
|
||||
|
||||
return context.getResultText();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { CheckboxState, CheckboxSyncPluginSettings } from "./types";
|
||||
import { CheckboxState, CheckboxSyncPluginSettings } from "src/types";
|
||||
import { ICheckboxUtils } from "./interface/ICheckboxUtils";
|
||||
|
||||
|
||||
export interface CheckboxLineInfo {
|
||||
indent: number;
|
||||
|
|
@ -10,7 +12,7 @@ export interface CheckboxLineInfo {
|
|||
listItemText?: string;
|
||||
}
|
||||
|
||||
export class CheckboxUtils {
|
||||
export class CheckboxUtils implements ICheckboxUtils {
|
||||
private settings: Readonly<CheckboxSyncPluginSettings>;
|
||||
constructor(settings: Readonly<CheckboxSyncPluginSettings>) {
|
||||
this.settings = settings;
|
||||
|
|
@ -3,66 +3,42 @@ import { Context } from "./Context";
|
|||
import { TreeNode } from "./TreeNode";
|
||||
import { Line } from "./Line";
|
||||
import { View } from "./View";
|
||||
import { CheckboxLine } from "./line/CheckboxLine";
|
||||
import { ListLine } from "./line/ListLine";
|
||||
import { TextLine } from "./line/TextLine";
|
||||
|
||||
export class ContextFactory {
|
||||
|
||||
static readonly CHECKBOX_REGEXP = /^(\s*)([*+-]|\d+\.) \[(.)\]\s(.*)$/;
|
||||
static readonly LIST_ITEM_REGEXP = /^(\s*)([*+-]|\d+\.)\s+(?!\[.?\])(.*)$/;
|
||||
static readonly TEXT_REGEXP = /^(\s*)(.*)$/;
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
public static createContext(text: string, textBefore: string | undefined, settings: Readonly<CheckboxSyncPluginSettings>): Context {
|
||||
static createContext(text: string, textBefore: string | undefined, settings: Readonly<CheckboxSyncPluginSettings>): Context {
|
||||
return new Context(text, textBefore, settings);
|
||||
}
|
||||
|
||||
static createLines(context: Context): Line[] {
|
||||
const settings = context.getSettings();
|
||||
const textLines = context.getTextLines();
|
||||
const lines = this.createLinesFromTextLines(context.getTextLines(), settings);
|
||||
if (context.textBeforeChangeIsPresent()) {
|
||||
const textBeforeLines = context.getTextBeforeChangeLines()!;
|
||||
if (textLines.length === textBeforeLines.length) {
|
||||
const diffIndex = this.findSingleDiffLineIndex(textLines, textBeforeLines);
|
||||
if (diffIndex) {
|
||||
const oldLine = new Line(textBeforeLines[diffIndex], settings);
|
||||
const actualLine = lines[diffIndex];
|
||||
|
||||
// если новая и старая строка чекбокс
|
||||
// если текст остался старым
|
||||
// если изменилось состояние чекбокса
|
||||
if (oldLine.isCheckbox() &&
|
||||
actualLine.isCheckbox() &&
|
||||
oldLine.getText() === actualLine.getText() &&
|
||||
oldLine.getState() !== actualLine.getState()
|
||||
) {
|
||||
actualLine.setChange(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
static createView(context: Context) {
|
||||
const settings = context.getSettings();
|
||||
|
||||
const lines = context.getLines();
|
||||
|
||||
// построить дерево
|
||||
const treeNodes = this.createNodesFromLines(lines);
|
||||
|
||||
// проверить ноды на modify
|
||||
let isModify = false;
|
||||
for (const treeNode of treeNodes) {
|
||||
isModify = isModify || treeNode.isModify();
|
||||
}
|
||||
|
||||
return new View(treeNodes, isModify);
|
||||
const treeNodes = this.createRootTreeNodesFromLines(lines);
|
||||
return new View(treeNodes);
|
||||
}
|
||||
|
||||
private static createNodesFromLines(textLines: Line[]): TreeNode[] {
|
||||
// создаёт TreeNode из Line
|
||||
// возвращает только корневые узлы
|
||||
private static createRootTreeNodesFromLines(lines: Line[]): TreeNode[] {
|
||||
const flatTreeNodes = this.createFlatNodesFromLines(lines);
|
||||
return flatTreeNodes.filter(node => !node.getParent());
|
||||
}
|
||||
|
||||
// создаёт TreeNode из Line
|
||||
// осторожно, возвращает ВСЕ ноды в порядке линий
|
||||
private static createFlatNodesFromLines(lines: Line[]): TreeNode[] {
|
||||
const nodes: TreeNode[] = [];
|
||||
const stack: TreeNode[] = [];
|
||||
for (let index = 0; index < textLines.length; index++) {
|
||||
const line = textLines[index];
|
||||
for (const line of lines) {
|
||||
const node = new TreeNode(line);
|
||||
|
||||
// найти родителя
|
||||
|
|
@ -86,15 +62,83 @@ export class ContextFactory {
|
|||
return nodes;
|
||||
}
|
||||
|
||||
static createLines(context: Context): Line[] {
|
||||
const settings = context.getSettings();
|
||||
const lines = this.createLinesFromTextLines(context.getTextLines(), settings);
|
||||
this.markChangeIfNeeded(lines, context);
|
||||
return lines;
|
||||
}
|
||||
|
||||
// помечает line как изменённую при необходимости
|
||||
static markChangeIfNeeded(lines: Line[], context: Context) {
|
||||
if (!context.textBeforeChangeIsPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const textLines = context.getTextLines();
|
||||
const textBeforeLines = context.getTextBeforeChangeLines()!;
|
||||
if (textLines.length !== textBeforeLines.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const diffIndex = this.findSingleDiffLineIndex(textLines, textBeforeLines);
|
||||
if (diffIndex === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldLine = this.createLineFromTextLine(textBeforeLines[diffIndex], context.getSettings());
|
||||
const actualLine = lines[diffIndex];
|
||||
|
||||
// если новая и старая строка чекбокс
|
||||
if (oldLine instanceof CheckboxLine && actualLine instanceof CheckboxLine) {
|
||||
// если текст остался старым
|
||||
// если изменилось состояние чекбокса
|
||||
if (
|
||||
oldLine.getText() === actualLine.getText() &&
|
||||
oldLine.getState() !== actualLine.getState()
|
||||
) {
|
||||
actualLine.setChange(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// создаёт Line[] из текста
|
||||
private static createLinesFromTextLines(textLines: string[], settings: Readonly<CheckboxSyncPluginSettings>): Line[] {
|
||||
return textLines.map(line => {
|
||||
return new Line(line, settings);
|
||||
return this.createLineFromTextLine(line, settings);
|
||||
});
|
||||
}
|
||||
|
||||
// создаёт объект из одного из классов, кто реализует Line в зависимости от строки.
|
||||
static createLineFromTextLine(textLine: string, settings: Readonly<CheckboxSyncPluginSettings>): Line {
|
||||
const checkboxMatch = textLine.match(this.CHECKBOX_REGEXP);
|
||||
if (checkboxMatch) {
|
||||
const indentString = checkboxMatch[1];
|
||||
const marker = checkboxMatch[2];
|
||||
const checkChar = checkboxMatch[3];
|
||||
|
||||
const listItemText = checkboxMatch[4].trimStart();
|
||||
|
||||
return new CheckboxLine(indentString, marker, checkChar, listItemText, settings);
|
||||
}
|
||||
const listItemMatch = textLine.match(this.LIST_ITEM_REGEXP);
|
||||
if (listItemMatch) {
|
||||
const indentString = listItemMatch[1];
|
||||
const marker = listItemMatch[2];
|
||||
const listItemText = listItemMatch[3].trimStart();
|
||||
|
||||
return new ListLine(indentString, marker, listItemText, settings.tabSize);
|
||||
}
|
||||
|
||||
const textLineMatch = textLine.match(this.TEXT_REGEXP)!;
|
||||
const indentString = textLineMatch[1];
|
||||
const itemText = textLineMatch[2];
|
||||
return new TextLine(indentString, itemText, settings.tabSize);
|
||||
|
||||
}
|
||||
|
||||
private static findSingleDiffLineIndex(lines1: string[], lines2: string[]): number | undefined {
|
||||
const diffLines = this.findDifferentLineIndexes(lines1, lines2);
|
||||
if (diffLines.length == 1) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { CheckboxState } from "src/types";
|
||||
|
||||
export interface Line {
|
||||
getIndent(): number;
|
||||
|
||||
|
|
@ -5,4 +7,6 @@ export interface Line {
|
|||
|
||||
toResultText(): string;
|
||||
|
||||
getState(): CheckboxState;
|
||||
|
||||
}
|
||||
|
|
@ -1,15 +1,17 @@
|
|||
import { Line } from "./Line";
|
||||
import { CheckboxLine } from "./line/CheckboxLine";
|
||||
|
||||
export class TreeNode {
|
||||
private parent?: TreeNode;
|
||||
private childrens: TreeNode[];
|
||||
private childrens: TreeNode[] = [];
|
||||
|
||||
private line: Line;
|
||||
// метка того, что Единичное изменение произошло в этой ноде или дочерних нодах
|
||||
private modify = false;
|
||||
|
||||
constructor(line: Line) {
|
||||
this.line = line;
|
||||
if (line.isChange()) {
|
||||
if (line instanceof CheckboxLine && line.isChange()) {
|
||||
this.modify = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,16 @@ import { TreeNode } from "./TreeNode";
|
|||
|
||||
export class View {
|
||||
private treeNodes: TreeNode[];
|
||||
// метка того, что Единичное изменение произошло в этой View
|
||||
private modify: boolean;
|
||||
|
||||
constructor(treeNodes: TreeNode[], isModify: boolean) {
|
||||
constructor(treeNodes: TreeNode[]) {
|
||||
this.treeNodes = treeNodes;
|
||||
// проверить ноды на modify
|
||||
let isModify = false;
|
||||
for (const treeNode of treeNodes) {
|
||||
isModify = isModify || treeNode.isModify();
|
||||
}
|
||||
this.modify = isModify;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,33 @@
|
|||
import { CheckboxState } from "src/types";
|
||||
import { Line } from "../Line";
|
||||
|
||||
export abstract class AbstractLine implements Line {
|
||||
|
||||
protected indentString: string;
|
||||
protected tabSize: number;
|
||||
// длина отступа
|
||||
protected indent: number;
|
||||
// текст после чекбокса
|
||||
protected listText: string;
|
||||
protected listText: string;
|
||||
|
||||
constructor(indent:number, lineText: string){
|
||||
this.indent = indent;
|
||||
constructor(indentString: string, lineText: string, tabSize: number) {
|
||||
this.indentString = indentString;
|
||||
this.listText = lineText;
|
||||
|
||||
this.tabSize = tabSize;
|
||||
|
||||
this.indent = this.getIndentFromString(indentString, tabSize);
|
||||
}
|
||||
|
||||
private getIndentFromString(indentString: string, tabSize: number): number {
|
||||
let indent = 0;
|
||||
for (const char of indentString) {
|
||||
if (char === '\t') {
|
||||
indent += tabSize;
|
||||
} else if (char === ' ') {
|
||||
indent += 1;
|
||||
}
|
||||
}
|
||||
return indent;
|
||||
}
|
||||
|
||||
getIndent(): number {
|
||||
|
|
@ -21,9 +38,7 @@ export abstract class AbstractLine implements Line {
|
|||
return this.listText;
|
||||
}
|
||||
|
||||
setText(lineText: string){
|
||||
this.listText = lineText;
|
||||
}
|
||||
|
||||
abstract toResultText(): string;
|
||||
|
||||
abstract getState(): CheckboxState;
|
||||
}
|
||||
|
|
@ -2,32 +2,31 @@ import { CheckboxState, CheckboxSyncPluginSettings } from "src/types";
|
|||
import { AbstractLine } from "./AbstractLine";
|
||||
|
||||
export class CheckboxLine extends AbstractLine {
|
||||
|
||||
|
||||
// символы перед чекбоксом
|
||||
private marker: string;
|
||||
// символ в чекбоксе
|
||||
private checkChar: string;
|
||||
// позиция символа чекбокса в исходной строке
|
||||
private checkboxCharPosition: number;
|
||||
// private checkboxCharPosition: number;
|
||||
// интерпретация символа чекбокса(или его отсутствия)
|
||||
private checkboxState: CheckboxState;
|
||||
// интерпретация состояния чекбокса(или его отсутствия)
|
||||
// private isChecked?: boolean | undefined;
|
||||
|
||||
|
||||
private hasChange = false;
|
||||
// метка того, что Единичное изменение произошло в этой строке
|
||||
private hasChange = false;
|
||||
|
||||
protected settings: Readonly<CheckboxSyncPluginSettings>;
|
||||
|
||||
constructor(indent: number, marker: string, checkChar: string, lineText: string, settings: Readonly<CheckboxSyncPluginSettings>) {
|
||||
super(indent, lineText);
|
||||
constructor(indentString:string, marker: string, checkChar: string, listItemText: string, settings: Readonly<CheckboxSyncPluginSettings>) {
|
||||
super(indentString, listItemText, settings.tabSize);
|
||||
this.marker = marker;
|
||||
this.checkChar = checkChar;
|
||||
this.settings = settings;
|
||||
|
||||
this.checkboxCharPosition = ;
|
||||
this.checkboxState = ;
|
||||
throw new Error("Constructor not implemented.");
|
||||
// this.checkboxCharPosition = indent + marker.length + 2;
|
||||
this.checkboxState = this.getCheckboxState(checkChar);
|
||||
}
|
||||
|
||||
isChange(): boolean {
|
||||
|
|
@ -38,36 +37,12 @@ export class CheckboxLine extends AbstractLine {
|
|||
this.hasChange = hasChange;
|
||||
}
|
||||
|
||||
|
||||
|
||||
isCheckbox(): boolean {
|
||||
return this.checkboxState !== CheckboxState.NoCheckbox;
|
||||
}
|
||||
|
||||
setState(state: CheckboxState): void {
|
||||
// обновить checkboxState
|
||||
this.checkboxState = state;
|
||||
// обновить checkChar
|
||||
switch (state) {
|
||||
case CheckboxState.Checked:
|
||||
this.checkChar = this.settings.checkedSymbols.length > 0 ? this.settings.checkedSymbols[0] : 'x'; // 'x' как дефолт
|
||||
break;
|
||||
case CheckboxState.Unchecked:
|
||||
this.checkChar = this.settings.uncheckedSymbols.length > 0 ? this.settings.uncheckedSymbols[0] : ' '; // ' ' как дефолт
|
||||
break;
|
||||
case CheckboxState.Ignore:
|
||||
if (this.settings.ignoreSymbols.length > 0) {
|
||||
this.checkChar = this.settings.ignoreSymbols[0];
|
||||
} else {
|
||||
throw new Error("Not found ignore char.");
|
||||
}
|
||||
break;
|
||||
case CheckboxState.NoCheckbox:
|
||||
this.checkChar = undefined;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unexpected value for parameter [state] = ${state}.`);
|
||||
}
|
||||
this.checkChar = this.getCharFromState(state);
|
||||
}
|
||||
|
||||
setStateIfNotEquals(newState: CheckboxState) {
|
||||
|
|
@ -80,5 +55,42 @@ export class CheckboxLine extends AbstractLine {
|
|||
return this.checkboxState;
|
||||
}
|
||||
|
||||
|
||||
toResultText(): string {
|
||||
// const spaces = ' '.repeat(this.indent);
|
||||
const resultText = this.indentString + this.marker + " [" + this.checkChar + "] " + this.listText;
|
||||
return resultText;
|
||||
}
|
||||
|
||||
private getCheckboxState(text: string): CheckboxState {
|
||||
if (this.settings.checkedSymbols.includes(text)) {
|
||||
return CheckboxState.Checked;
|
||||
}
|
||||
if (this.settings.uncheckedSymbols.includes(text)) {
|
||||
return CheckboxState.Unchecked;
|
||||
}
|
||||
if (this.settings.ignoreSymbols.includes(text)) {
|
||||
return CheckboxState.Ignore;
|
||||
}
|
||||
return this.settings.unknownSymbolPolicy;
|
||||
}
|
||||
|
||||
private getCharFromState(state: CheckboxState): string {
|
||||
switch (state) {
|
||||
case CheckboxState.Checked:
|
||||
return this.settings.checkedSymbols.length > 0 ? this.settings.checkedSymbols[0] : 'x'; // 'x' как дефолт
|
||||
case CheckboxState.Unchecked:
|
||||
return this.settings.uncheckedSymbols.length > 0 ? this.settings.uncheckedSymbols[0] : ' '; // ' ' как дефолт
|
||||
case CheckboxState.Ignore:
|
||||
if (this.settings.ignoreSymbols.length > 0) {
|
||||
return this.settings.ignoreSymbols[0];
|
||||
} else {
|
||||
throw new Error("Not found ignore char.");
|
||||
}
|
||||
case CheckboxState.NoCheckbox:
|
||||
throw new Error(`Unexpected value for parameter [state] = ${state}. "NoCheckbox".`);
|
||||
default:
|
||||
throw new Error(`Unexpected value for parameter [state] = ${state}.`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { CheckboxState } from "src/types";
|
||||
import { AbstractLine } from "./AbstractLine";
|
||||
|
||||
export class ListLine extends AbstractLine {
|
||||
|
|
@ -5,8 +6,8 @@ export class ListLine extends AbstractLine {
|
|||
// символы перед текстом
|
||||
private marker: string;
|
||||
|
||||
constructor(indent: number, marker: string, listText: string) {
|
||||
super(indent, listText);
|
||||
constructor(indentString: string, marker: string, listText: string, tabSize: number) {
|
||||
super(indentString, listText, tabSize);
|
||||
this.marker = marker;
|
||||
}
|
||||
|
||||
|
|
@ -19,9 +20,12 @@ export class ListLine extends AbstractLine {
|
|||
}
|
||||
|
||||
toResultText(): string {
|
||||
const spaces = ' '.repeat(this.indent);
|
||||
|
||||
const resultText = spaces + this.marker + " " + this.listText;
|
||||
// const spaces = ' '.repeat(this.indent);
|
||||
const resultText = this.indentString + this.marker + " " + this.listText;
|
||||
return resultText;
|
||||
}
|
||||
|
||||
getState(): CheckboxState {
|
||||
return CheckboxState.NoCheckbox;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
import { CheckboxState } from "src/types";
|
||||
import { AbstractLine } from "./AbstractLine";
|
||||
|
||||
export class TextLine extends AbstractLine {
|
||||
toResultText(): string {
|
||||
const spaces = ' '.repeat(this.indent);
|
||||
const resultText = spaces + this.listText;
|
||||
// const spaces = ' '.repeat(this.indent);
|
||||
const resultText = this.indentString + this.listText;
|
||||
return resultText;
|
||||
}
|
||||
|
||||
getState(): CheckboxState {
|
||||
return CheckboxState.NoCheckbox;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ import { CheckboxState } from "src/types";
|
|||
import { CheckboxProcess } from "../interface/CheckboxProcess";
|
||||
import { Context } from "../model/Context";
|
||||
import { TreeNode } from "../model/TreeNode";
|
||||
import { CheckboxLine } from "../model/line/CheckboxLine";
|
||||
|
||||
export class PropagateStateToChildrenProcess implements CheckboxProcess {
|
||||
|
||||
|
|
@ -21,46 +22,51 @@ export class PropagateStateToChildrenProcess implements CheckboxProcess {
|
|||
|
||||
const nodes = view.getTreeNodes();
|
||||
for (const node of nodes) {
|
||||
this.propageteStateToChildrenFromChangeLineNode(node);
|
||||
this.propagateStateToChildrenFromChangeLineNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
// находит изменённую Line, если она существует и вызывает от неё propagateStateToChildren
|
||||
propageteStateToChildrenFromChangeLineNode(node: TreeNode) {
|
||||
propagateStateToChildrenFromChangeLineNode(node: TreeNode) {
|
||||
if (!node.isModify()) {
|
||||
return;
|
||||
}
|
||||
const line = node.getLine();
|
||||
// если line изменён
|
||||
if (line.isChange()) {
|
||||
if (line instanceof CheckboxLine && line.isChange()) {
|
||||
// значит мы нашли нужную ноду
|
||||
this.propagateStateToChildren(node);
|
||||
this.propagateStateToChildrenFromNode(node);
|
||||
} else {
|
||||
// иначе ищем вглубь среди детей
|
||||
const childrens = node.getChildrenNodes();
|
||||
for (const children of childrens) {
|
||||
this.propageteStateToChildrenFromChangeLineNode(children);
|
||||
this.propagateStateToChildrenFromChangeLineNode(children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
propagateStateToChildren(modifiedNode: TreeNode) {
|
||||
const state = modifiedNode.getLine().getState();
|
||||
propagateStateToChildrenFromNode(modifiedNode: TreeNode) {
|
||||
const line = modifiedNode.getLine();
|
||||
if (!(line instanceof CheckboxLine)) {
|
||||
console.warn(`propagateStateToChildren from !CheckboxLine.`);
|
||||
return;
|
||||
}
|
||||
const state = line.getState();
|
||||
if (state === CheckboxState.Ignore || state === CheckboxState.NoCheckbox) {
|
||||
console.warn(`propagateStateToChildren ${state.toString()}`);
|
||||
return;
|
||||
}
|
||||
this.propagateToChildren(modifiedNode, state);
|
||||
this.propagateStateToChildren(modifiedNode, state);
|
||||
}
|
||||
|
||||
propagateToChildren(node: TreeNode, state: CheckboxState) {
|
||||
propagateStateToChildren(node: TreeNode, state: CheckboxState) {
|
||||
const line = node.getLine();
|
||||
if (line.isCheckbox()) {
|
||||
if (line instanceof CheckboxLine) {
|
||||
line.setStateIfNotEquals(state);
|
||||
}
|
||||
for (const childrenNode of node.getChildrenNodes()) {
|
||||
this.propagateToChildren(childrenNode, state);
|
||||
this.propagateStateToChildren(childrenNode, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { CheckboxState } from "src/types";
|
|||
import { CheckboxProcess } from "../interface/CheckboxProcess";
|
||||
import { Context } from "../model/Context";
|
||||
import { TreeNode } from "../model/TreeNode";
|
||||
import { CheckboxLine } from "../model/line/CheckboxLine";
|
||||
|
||||
export class PropagateStateToParentProcess implements CheckboxProcess {
|
||||
|
||||
|
|
@ -16,24 +17,42 @@ export class PropagateStateToParentProcess implements CheckboxProcess {
|
|||
const nodes = view.getTreeNodes();
|
||||
|
||||
for (const node of nodes) {
|
||||
this.propagateStateFromChildrens(node);
|
||||
this.propagateStateToParent(node);
|
||||
}
|
||||
}
|
||||
|
||||
// возврат значения нужен для того, чтобы правильно обрабатывать случаи с "не чекбоксам", чтобы они передавали значения детей
|
||||
propagateStateFromChildrens(node: TreeNode): CheckboxState {
|
||||
propagateStateToParent(node: TreeNode): CheckboxState {
|
||||
const line = node.getLine();
|
||||
|
||||
if (!node.hasChildren()) {
|
||||
return line.getState();
|
||||
// обходим всех детей
|
||||
const childrenStates: CheckboxState[] = [];
|
||||
for (const childrenNode of node.getChildrenNodes()) {
|
||||
const childrenState = this.propagateStateToParent(childrenNode);
|
||||
childrenStates.push(childrenState);
|
||||
}
|
||||
|
||||
const newState = this.getNewState(line.getState(), childrenStates);
|
||||
|
||||
if (line instanceof CheckboxLine){
|
||||
line.setStateIfNotEquals(newState);
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
getNewState(actualState: CheckboxState, childrenStates: CheckboxState[]): CheckboxState {
|
||||
if (actualState === CheckboxState.Ignore) {
|
||||
return actualState;
|
||||
}
|
||||
|
||||
// обходим всех детей
|
||||
// если есть релевантные дети, то помечаем это и обновляем "состояние" Line
|
||||
// "состояние" - потому что даже не чекбоксы могут иметь состояние через своих детей
|
||||
let resultIfHasRelevantChildren = CheckboxState.Checked;
|
||||
let hasRelevantChildren = false;
|
||||
for (const childrenNode of node.getChildrenNodes()) {
|
||||
const childrenState = this.propagateStateFromChildrens(childrenNode);
|
||||
|
||||
if (childrenState === CheckboxState.Checked || childrenState === CheckboxState.Unchecked) {
|
||||
for (const childrenState of childrenStates) {
|
||||
if (childrenState !== CheckboxState.Ignore && childrenState !== CheckboxState.NoCheckbox) {
|
||||
hasRelevantChildren = true;
|
||||
}
|
||||
|
||||
|
|
@ -41,17 +60,12 @@ export class PropagateStateToParentProcess implements CheckboxProcess {
|
|||
resultIfHasRelevantChildren = CheckboxState.Unchecked;
|
||||
}
|
||||
}
|
||||
// делаем эту проверку только после обработки детей
|
||||
if (line.getState() == CheckboxState.Ignore) {
|
||||
return line.getState();
|
||||
}
|
||||
// проверка для нод, все дети которых не релевантны
|
||||
if (!hasRelevantChildren) {
|
||||
return line.getState();
|
||||
}
|
||||
|
||||
line.setStateIfNotEquals(resultIfHasRelevantChildren);
|
||||
|
||||
return resultIfHasRelevantChildren;
|
||||
if (hasRelevantChildren) {
|
||||
return resultIfHasRelevantChildren;
|
||||
} else {
|
||||
return actualState;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Plugin, TFile } from "obsidian";
|
||||
import { CheckboxUtils } from "./checkboxUtils";
|
||||
import { FileChangeEventHandler } from "./events/FileChangeEventHandler";
|
||||
import { FileLoadEventHandler } from "./events/FileLoadEventHandler";
|
||||
import FileStateHolder from "./FileStateHolder";
|
||||
|
|
@ -8,6 +7,8 @@ import SyncController from "./SyncController";
|
|||
import { CheckboxSyncPluginSettings, DEFAULT_SETTINGS } from "./types";
|
||||
import { FileFilter } from "./FileFilter";
|
||||
import TextSyncPipeline from "./TextSyncPipeline";
|
||||
import { ICheckboxUtils } from "./core/interface/ICheckboxUtils";
|
||||
import { CheckboxUtils2 } from "./core/CheckboxUtils2";
|
||||
|
||||
const DEBUG_FLAG_NAME = 'CHECKBOX_SYNC_DEBUG';
|
||||
|
||||
|
|
@ -23,7 +24,7 @@ export default class CheckboxSyncPlugin extends Plugin {
|
|||
private _settings: CheckboxSyncPluginSettings;
|
||||
|
||||
private syncController: SyncController;
|
||||
private checkboxUtils: CheckboxUtils;
|
||||
private checkboxUtils: ICheckboxUtils;
|
||||
private fileStateHolder: FileStateHolder;
|
||||
private fileLoadEventHandler: FileLoadEventHandler;
|
||||
private fileChangeEventHandler: FileChangeEventHandler;
|
||||
|
|
@ -35,9 +36,9 @@ export default class CheckboxSyncPlugin extends Plugin {
|
|||
|
||||
this.fileFilter = new FileFilter(this.settings);
|
||||
this.fileStateHolder = new FileStateHolder(this.app.vault);
|
||||
this.checkboxUtils = new CheckboxUtils(this.settings);
|
||||
this.checkboxUtils = new CheckboxUtils2(this.settings);
|
||||
this.textSyncPipeline = new TextSyncPipeline(this.checkboxUtils,this.fileStateHolder, this.fileFilter);
|
||||
this.syncController = new SyncController(this.app.vault, this.checkboxUtils, this.textSyncPipeline);
|
||||
this.syncController = new SyncController(this.app.vault, this.textSyncPipeline);
|
||||
this.fileLoadEventHandler = new FileLoadEventHandler(this, this.app, this.syncController, this.fileStateHolder);
|
||||
this.fileChangeEventHandler = new FileChangeEventHandler(this, this.app, this.syncController, this.fileStateHolder);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
export interface CheckboxSyncPluginSettings {
|
||||
tabSize: number,
|
||||
enableAutomaticParentState: boolean;
|
||||
enableAutomaticChildState: boolean;
|
||||
checkedSymbols: string[];
|
||||
|
|
@ -18,6 +19,7 @@ export enum CheckboxState {
|
|||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: CheckboxSyncPluginSettings = {
|
||||
tabSize: 4,
|
||||
enableAutomaticParentState: true,
|
||||
enableAutomaticChildState: true,
|
||||
checkedSymbols: ['x'],
|
||||
|
|
|
|||
Loading…
Reference in a new issue