From 7505c0a1e8aca24a8a0390a1b93770744206ff60 Mon Sep 17 00:00:00 2001 From: James Lynch Date: Mon, 18 Sep 2023 23:32:15 +0100 Subject: [PATCH] Creates separate files for duplicate headings #84 --- src/file.ts | 12 ++++++++++ src/main.ts | 13 +++++----- tests/file.test.ts | 59 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/file.ts b/src/file.ts index 90e9faa..0121d4c 100644 --- a/src/file.ts +++ b/src/file.ts @@ -3,6 +3,7 @@ import { NoteRefactorSettings } from './settings'; import MomentDateRegex from './moment-date-regex' export default class NRFile { + private settings: NoteRefactorSettings; private momentDateRegex: MomentDateRegex; @@ -21,4 +22,15 @@ export default class NRFile { fileNamePrefix(): string { return this.settings.fileNamePrefix ? this.momentDateRegex.replace(this.settings.fileNamePrefix) : ''; } + + ensureUniqueFileNames(headingNotes: string[][]): string[] { + const fileNames:string[] = []; + const deduped = headingNotes.map((hn) => { + const fileName = this.sanitisedFileName(hn[0]); + const duplicates = fileNames.filter(fn => fn == fileName); + fileNames.push(fileName); + return duplicates.length >= 1 ? `${fileName}${duplicates.length + 1}` : fileName; + }); + return deduped; + } } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index e896c68..54673c1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -116,7 +116,8 @@ export default class NoteRefactor extends Plugin { const mdView = this.app.workspace.activeLeaf.view as MarkdownView; const doc = mdView.editor; const headingNotes = this.NRDoc.contentSplitByHeading(doc, headingLevel); - headingNotes.forEach(hn => this.createNoteWithFirstLineAsFileName(hn, mdView, doc, 'replace-headings', true)); + const dedupedFileNames = this.file.ensureUniqueFileNames(headingNotes); + headingNotes.forEach((hn, i) => this.createNoteWithFirstLineAsFileName(dedupedFileNames[i], hn, mdView, doc, 'replace-headings', true)); } async extractSelectionFirstLine(mode: ReplaceMode): Promise { @@ -127,7 +128,7 @@ export default class NoteRefactor extends Plugin { const selectedContent = mode === 'split' ? this.NRDoc.noteRemainder(doc) : this.NRDoc.selectedContent(doc); if(selectedContent.length <= 0) { return } - await this.createNoteWithFirstLineAsFileName(selectedContent, mdView, doc, mode, false); + await this.createNoteWithFirstLineAsFileName(selectedContent[0], selectedContent, mdView, doc, mode, false); } async extractSelectionAutogenerate(mode: ReplaceMode): Promise { @@ -162,11 +163,11 @@ export default class NoteRefactor extends Plugin { } } - private async createNoteWithFirstLineAsFileName(selectedContent: string[], mdView: MarkdownView, doc: Editor, mode: ReplaceMode, isMultiple: boolean) { - const [header, ...contentArr] = selectedContent; + private async createNoteWithFirstLineAsFileName(dedupedHeader: string, selectedContent: string[], mdView: MarkdownView, doc: Editor, mode: ReplaceMode, isMultiple: boolean) { + const [originalHeader, ...contentArr] = selectedContent; - const fileName = this.file.sanitisedFileName(header); - const originalNote = this.NRDoc.noteContent(header, contentArr); + const fileName = this.file.sanitisedFileName(dedupedHeader); + const originalNote = this.NRDoc.noteContent(originalHeader, contentArr); let note = originalNote; const filePath = await this.obsFile.createOrAppendFile(fileName, ''); diff --git a/tests/file.test.ts b/tests/file.test.ts index b829b6a..1091401 100644 --- a/tests/file.test.ts +++ b/tests/file.test.ts @@ -1,5 +1,5 @@ -import {describe, expect, beforeAll, afterAll} from '@jest/globals'; -import { mockDate} from './mocks/date'; +import { describe, expect, beforeAll, afterAll } from '@jest/globals'; +import { mockDate } from './mocks/date'; import NRFile from '../src/file'; import { NoteRefactorSettings } from '../src/settings'; @@ -8,9 +8,9 @@ const date = new Date(2020, 11, 25, 11, 17, 52); describe("File Name Prefix", () => { - let resetDateMock:() => void; + let resetDateMock: () => void; let settings = new NoteRefactorSettings(); - + beforeAll(async () => { file = new NRFile(settings); resetDateMock = mockDate(date); @@ -21,7 +21,7 @@ describe("File Name Prefix", () => { const prefix = file.fileNamePrefix(); expect(prefix).toBe('202012251117-'); }); - + it("Correct prefix for YYYYMMDDHHmmss", () => { settings.fileNamePrefix = '{{date:YYYYMMDDHHmmss}}-'; const prefix = file.fileNamePrefix(); @@ -59,7 +59,7 @@ describe("File Name Prefix", () => { describe("File Name Sanitisation", () => { let fileName = ''; - let resetDateMock:() => void; + let resetDateMock: () => void; let settings = new NoteRefactorSettings(); beforeAll(async () => { @@ -92,7 +92,7 @@ describe("File Name Sanitisation", () => { }); it("Illegal file path character sanitisation (*\"\/<>:|?", () => { - fileName = '**This has**\\/ "a lot" of : |characters??|' ; + fileName = '**This has**\\/ "a lot" of : |characters??|'; const sanitised = file.sanitisedFileName(fileName); expect(sanitised).toBe('This has a lot of illegal characters'); }); @@ -117,4 +117,49 @@ describe("File Name Sanitisation", () => { afterAll(() => { resetDateMock(); }); +}); + +describe("Regression - Issue #84 - File Name Duplication Protection", () => { + let fileNames = [ + ["## Duplicate Heading", "Some text"], + ["#### Some other heading", "Paragraph text", "", "Another paragraph"], + ["### Test", "", "Sentence 7"], + ["### Another test", "", "Sentence text"], + ["## Duplicate Heading", "", "More text under heading"], + ["# Test", "", "Testing"], + ]; + let resetDateMock: () => void; + let settings = new NoteRefactorSettings(); + + beforeAll(async () => { + file = new NRFile(settings); + resetDateMock = mockDate(date); + }); + + it("Should return expected count", () => { + const deduped = file.ensureUniqueFileNames(fileNames); + expect(deduped.length).toBe(6); + }); + + it("Should sanitised filenames", () => { + const deduped = file.ensureUniqueFileNames(fileNames); + const includingHash = deduped.filter(d => d[0].includes("#")); + expect(includingHash.length).toBe(0); + }); + + it("First duplicate should have unchanged filename", () => { + const deduped = file.ensureUniqueFileNames(fileNames); + expect(deduped[0]).toBe("Duplicate Heading"); + }); + + it("Second duplicate should have filename with incremented number", () => { + const deduped = file.ensureUniqueFileNames(fileNames); + expect(deduped[4]).toBe("Duplicate Heading2"); + }); + + it("Duplicate should have filename with incremented number regardless of heading level", () => { + const deduped = file.ensureUniqueFileNames(fileNames); + expect(deduped[5]).toBe("Test2"); + }); + }); \ No newline at end of file