|
1 | | -import { Editor, MarkdownView, Notice, Plugin, normalizePath } from "obsidian"; |
2 | | -import { escapeInvalidFileNameChars, findFrontmatterEndIndex, trimForFileName } from "./utils"; |
| 1 | +import { MarkdownView, Notice, Plugin, TFile, normalizePath } from "obsidian"; |
| 2 | +import { escapeInvalidFileNameChars, removeFrontmatterBlock, trimForFileName } from "./utils"; |
3 | 3 | import NoteSplitterSettingsTab from "./obsidian/note-splitter-settings-tab"; |
4 | 4 |
|
5 | 5 | interface NoteSplitterSettings { |
@@ -27,102 +27,115 @@ export default class NoteSplitterPlugin extends Plugin { |
27 | 27 | this.addCommand({ |
28 | 28 | id: "split-by-delimiter", |
29 | 29 | name: "Split by delimiter", |
30 | | - editorCallback: async (_editor: Editor, view: MarkdownView) => { |
31 | | - const file = view.file; |
32 | | - if (file === null) return; |
33 | | - |
34 | | - //Obsidian will store `\n`` as `\\n` in the settings |
35 | | - const delimiter = this.settings.delimiter.replace(/\\n/g, "\n"); |
36 | | - |
37 | | - if (delimiter === "") { |
38 | | - new Notice("No delimiter set. Please set a delimiter in the settings."); |
39 | | - return; |
40 | | - } |
41 | | - |
42 | | - const fileData = await this.app.vault.cachedRead(file); |
43 | | - const frontmatterEndIndex = findFrontmatterEndIndex(fileData); |
44 | | - |
45 | | - let dataWithoutFrontmatter = fileData; |
46 | | - //Ignore frontmatter |
47 | | - if (frontmatterEndIndex !== -1) { |
48 | | - dataWithoutFrontmatter = dataWithoutFrontmatter.slice(frontmatterEndIndex + 1); |
49 | | - } |
50 | | - if (dataWithoutFrontmatter === "") { |
51 | | - new Notice("No content to split."); |
| 30 | + callback: async () => { |
| 31 | + const view = this.app.workspace.getActiveViewOfType(MarkdownView); |
| 32 | + if (view === null) { |
| 33 | + new Notice("Please open a markdown note."); |
52 | 34 | return; |
53 | 35 | } |
54 | 36 |
|
55 | | - const splitLines = dataWithoutFrontmatter |
56 | | - .split(delimiter) |
57 | | - .map((line) => line.trim()) |
58 | | - .filter((line) => line !== ""); |
59 | | - |
60 | | - if (splitLines.length === 0) { |
61 | | - new Notice("No content to split."); |
| 37 | + const file = view.file; |
| 38 | + if (file === null) { |
| 39 | + new Notice("No file found for this note."); |
62 | 40 | return; |
63 | 41 | } |
64 | 42 |
|
65 | | - if (splitLines.length === 1) { |
66 | | - new Notice("Only one line found. Nothing to split."); |
| 43 | + if (view.getMode() !== 'source') { |
| 44 | + new Notice("Please switch to editing mode to split the note."); |
67 | 45 | return; |
68 | 46 | } |
69 | 47 |
|
70 | | - const folderPath = |
71 | | - this.settings.saveFolderPath || |
72 | | - file.parent?.path || |
73 | | - this.settings.saveFolderPath; |
74 | | - |
75 | | - try { |
76 | | - await this.app.vault.createFolder(folderPath); |
77 | | - } catch (err) { |
78 | | - //Folder already exists |
79 | | - } |
80 | | - |
81 | | - let filesCreated = 0; |
82 | | - for (const [i, line] of splitLines.entries()) { |
83 | | - let fileName = line; |
84 | | - if (this.settings.useContentAsTitle) { |
85 | | - fileName = escapeInvalidFileNameChars(fileName); |
86 | | - fileName = trimForFileName(fileName, ".md"); |
87 | | - } else { |
88 | | - fileName = `split-note-${Date.now() + i}`; |
89 | | - } |
90 | | - |
91 | | - const filePath = normalizePath(`${folderPath}/${fileName}.md`); |
| 48 | + this.splitNoteByDelimiter(file); |
| 49 | + }, |
| 50 | + }); |
| 51 | + } |
92 | 52 |
|
| 53 | + onunload() { } |
| 54 | + |
| 55 | + private async splitNoteByDelimiter(file: TFile) { |
| 56 | + //Obsidian will store `\n`` as `\\n` in the settings |
| 57 | + const delimiter = this.settings.delimiter.replace(/\\n/g, "\n"); |
| 58 | + |
| 59 | + if (delimiter === "") { |
| 60 | + new Notice("No delimiter set. Please set a delimiter in the settings."); |
| 61 | + return; |
| 62 | + } |
| 63 | + |
| 64 | + const data = await this.app.vault.cachedRead(file); |
| 65 | + |
| 66 | + const dataWithoutFrontmatter = removeFrontmatterBlock(data); |
| 67 | + if (dataWithoutFrontmatter === "") { |
| 68 | + new Notice("No content to split."); |
| 69 | + return; |
| 70 | + } |
| 71 | + |
| 72 | + const splitLines = dataWithoutFrontmatter |
| 73 | + .split(delimiter) |
| 74 | + .map((line) => line.trim()) |
| 75 | + .filter((line) => line !== ""); |
| 76 | + |
| 77 | + if (splitLines.length === 0) { |
| 78 | + new Notice("No content to split."); |
| 79 | + return; |
| 80 | + } |
| 81 | + |
| 82 | + if (splitLines.length === 1) { |
| 83 | + new Notice("Only one line found. Nothing to split."); |
| 84 | + return; |
| 85 | + } |
| 86 | + |
| 87 | + const folderPath = |
| 88 | + this.settings.saveFolderPath || |
| 89 | + file.parent?.path || |
| 90 | + this.settings.saveFolderPath; |
| 91 | + |
| 92 | + try { |
| 93 | + await this.app.vault.createFolder(folderPath); |
| 94 | + } catch (err) { |
| 95 | + //Folder already exists |
| 96 | + } |
| 97 | + |
| 98 | + let filesCreated = 0; |
| 99 | + for (const [i, line] of splitLines.entries()) { |
| 100 | + let fileName = line; |
| 101 | + if (this.settings.useContentAsTitle) { |
| 102 | + fileName = escapeInvalidFileNameChars(fileName); |
| 103 | + fileName = trimForFileName(fileName, ".md"); |
| 104 | + } else { |
| 105 | + fileName = `split-note-${Date.now() + i}`; |
| 106 | + } |
| 107 | + |
| 108 | + const filePath = normalizePath(`${folderPath}/${fileName}.md`); |
| 109 | + |
| 110 | + try { |
| 111 | + await this.app.vault.create(filePath, line); |
| 112 | + filesCreated++; |
| 113 | + } catch (err) { |
| 114 | + if (err.message.includes("already exists")) { |
| 115 | + const newFilePath = `${folderPath}/Split conflict ${crypto.randomUUID()}.md`; |
93 | 116 | try { |
94 | | - await this.app.vault.create(filePath, line); |
| 117 | + await this.app.vault.create(newFilePath, line); |
95 | 118 | filesCreated++; |
96 | 119 | } catch (err) { |
97 | | - if (err.message.includes("already exists")) { |
98 | | - const newFilePath = `${folderPath}/Split conflict ${crypto.randomUUID()}.md`; |
99 | | - try { |
100 | | - await this.app.vault.create(newFilePath, line); |
101 | | - filesCreated++; |
102 | | - } catch (err) { |
103 | | - console.error(err); |
104 | | - new Notice(`Error creating file: ${err.message}`); |
105 | | - } |
106 | | - continue; |
107 | | - } |
| 120 | + console.error(err); |
108 | 121 | new Notice(`Error creating file: ${err.message}`); |
109 | | - console.log(err); |
110 | 122 | } |
| 123 | + continue; |
111 | 124 | } |
112 | | - |
113 | | - if (filesCreated === splitLines.length && this.settings.deleteOriginalNote) { |
114 | | - await this.app.vault.delete(file); |
115 | | - } |
116 | | - |
117 | | - new Notice( |
118 | | - "Split into " + filesCreated + " note" + (filesCreated > 1 ? "s" : "") + ".", |
119 | | - ); |
120 | | - }, |
121 | | - }); |
| 125 | + new Notice(`Error creating file: ${err.message}`); |
| 126 | + console.log(err); |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + if (filesCreated === splitLines.length && this.settings.deleteOriginalNote) { |
| 131 | + await this.app.vault.delete(file); |
| 132 | + } |
| 133 | + |
| 134 | + new Notice( |
| 135 | + "Split into " + filesCreated + " note" + (filesCreated > 1 ? "s" : "") + ".", |
| 136 | + ); |
122 | 137 | } |
123 | 138 |
|
124 | | - onunload() {} |
125 | | - |
126 | 139 | async loadSettings() { |
127 | 140 | this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); |
128 | 141 | } |
|
0 commit comments