Skip to content

Commit f755a59

Browse files
authored
Merge pull request #15 from decaf-dev/dev
1.2.0
2 parents 85e111c + 3596bdb commit f755a59

20 files changed

+502
-138
lines changed

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,40 @@ If disabled, a timestamp will be used as the title e.g. `note-splitter-170259191
7878
> [!NOTE]
7979
> Disabled by default.
8080
81+
### Append to split content
82+
83+
This text will be appended to each section of split content.
84+
85+
**Example:**
86+
87+
Suppose you have two sentences and your delimiter is set to a period (`.`).
88+
89+
```markdown
90+
This is sentence 1. This is sentence 2.
91+
```
92+
93+
The split content would result in:
94+
95+
```markdown
96+
This is sentence 1
97+
```
98+
99+
```markdown
100+
This is sentence 2
101+
```
102+
103+
If you want to retain the period at the end of each sentence, simply add a period into the input field of this setting.
104+
105+
The updated result would be:
106+
107+
```markdown
108+
This is sentence 1.
109+
```
110+
111+
```markdown
112+
This is sentence 2.
113+
```
114+
81115
### Delete original
82116

83117
If enabled, the original note will be deleted after a successful split.

__mocks__/obsidian.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const normalizePath = jest.fn((path) => path);

bun.lockb

98.9 KB
Binary file not shown.

jest.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** @type {import('ts-jest').JestConfigWithTsJest} */
2+
module.exports = {
3+
preset: "ts-jest",
4+
testEnvironment: "node",
5+
moduleNameMapper: {
6+
"^src/(.*)$": "<rootDir>/src/$1",
7+
},
8+
};

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"id": "note-splitter",
33
"name": "Note Splitter",
4-
"version": "1.1.1",
4+
"version": "1.2.0",
55
"minAppVersion": "0.15.0",
66
"description": "Split a note into individual notes based on a delimiter.",
77
"author": "DecafDev",

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
{
22
"name": "obsidian-note-splitter",
3-
"version": "1.1.1",
3+
"version": "1.2.0",
44
"description": "Split notes based on a delimiter",
55
"main": "main.js",
66
"scripts": {
77
"dev": "node esbuild.config.mjs",
88
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
9+
"test": "jest --config jest.config.js",
910
"format": "prettier --write .",
1011
"version": "node version-bump.mjs && git add manifest.json versions.json"
1112
},
1213
"keywords": [],
1314
"author": "DecafDev",
1415
"license": "MIT",
1516
"devDependencies": {
17+
"@types/jest": "^29.5.12",
1618
"@types/node": "^16.11.6",
1719
"@typescript-eslint/eslint-plugin": "5.29.0",
1820
"@typescript-eslint/parser": "5.29.0",
1921
"builtin-modules": "3.3.0",
2022
"esbuild": "0.17.3",
23+
"jest": "^29.7.0",
2124
"obsidian": "latest",
2225
"prettier": "^3.3.2",
26+
"ts-jest": "^29.2.4",
2327
"tslib": "2.4.0",
2428
"typescript": "4.7.4"
2529
}

src/main.ts

Lines changed: 15 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
1-
import { MarkdownView, Notice, Plugin, TFile, normalizePath } from "obsidian";
2-
import { escapeInvalidFileNameChars, removeFrontmatterBlock, trimForFileName } from "./utils";
1+
import { MarkdownView, Notice, Plugin } from "obsidian";
32
import NoteSplitterSettingsTab from "./obsidian/note-splitter-settings-tab";
4-
5-
interface NoteSplitterSettings {
6-
saveFolderPath: string;
7-
useContentAsTitle: boolean;
8-
delimiter: string;
9-
deleteOriginalNote: boolean;
10-
}
3+
import { splitByDelimiter } from "./splitter/split-by-delimiter";
4+
import { NodeFileSystem, NoteSplitterSettings, Notifier } from "./types";
115

126
const DEFAULT_SETTINGS: NoteSplitterSettings = {
137
saveFolderPath: "note-splitter",
148
useContentAsTitle: false,
159
delimiter: "\\n",
10+
appendToSplitContent: "",
1611
deleteOriginalNote: false,
1712
};
1813

@@ -40,101 +35,24 @@ export default class NoteSplitterPlugin extends Plugin {
4035
return;
4136
}
4237

43-
if (view.getMode() !== 'source') {
44-
new Notice("Please switch to editing mode to split the note.");
38+
if (view.getMode() !== "source") {
39+
new Notice("Please switch to editing mode to split this note.");
4540
return;
4641
}
4742

48-
this.splitNoteByDelimiter(file);
43+
const fileSystem: NodeFileSystem = {
44+
create: (filePath, content) => this.app.vault.create(filePath, content),
45+
createFolder: (folderPath) => this.app.vault.createFolder(folderPath),
46+
delete: (file) => this.app.vault.delete(file),
47+
read: (file) => this.app.vault.read(file),
48+
};
49+
const notifier: Notifier = (message: string) => new Notice(message);
50+
await splitByDelimiter(fileSystem, notifier, file, this.settings);
4951
},
5052
});
5153
}
5254

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 splitContent = dataWithoutFrontmatter
73-
.split(delimiter)
74-
.map((content) => content.trim())
75-
.filter((content) => content !== "");
76-
77-
if (splitContent.length === 0) {
78-
new Notice("No content to split.");
79-
return;
80-
}
81-
82-
if (splitContent.length === 1) {
83-
new Notice("Only one piece of content 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, content] of splitContent.entries()) {
100-
let fileName = content.split("\n")[0];
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, content);
112-
filesCreated++;
113-
} catch (err) {
114-
if (err.message.includes("already exists")) {
115-
const newFilePath = `${folderPath}/Split conflict ${crypto.randomUUID()}.md`;
116-
try {
117-
await this.app.vault.create(newFilePath, content);
118-
filesCreated++;
119-
} catch (err) {
120-
console.error(err);
121-
new Notice(`Error creating file: ${err.message}`);
122-
}
123-
continue;
124-
}
125-
new Notice(`Error creating file: ${err.message}`);
126-
console.log(err);
127-
}
128-
}
129-
130-
if (filesCreated === splitContent.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-
);
137-
}
55+
onunload() {}
13856

13957
async loadSettings() {
14058
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());

src/obsidian/note-splitter-settings-tab.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ export default class NoteSplitterSettingsTab extends PluginSettingTab {
4848
}),
4949
);
5050

51+
new Setting(containerEl)
52+
.setName("Append to split content")
53+
.setDesc("Text to append to the split content.")
54+
.addText((text) =>
55+
text.setValue(this.plugin.settings.appendToSplitContent).onChange(async (value) => {
56+
this.plugin.settings.appendToSplitContent = value;
57+
await this.plugin.saveSettings();
58+
}),
59+
);
60+
5161
new Setting(containerEl)
5262
.setName("Delete original")
5363
.setDesc("Delete the original note after a successful split.")
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const removeFrontmatterBlock = (content: string) => {
2+
const FRONTMATTER_REGEX = /^---[\s\S]*?---/;
3+
return content.replace(FRONTMATTER_REGEX, "").trim();
4+
};

src/splitter/sanitize-file-name.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Sanitizes a file name for use in a file system
3+
*/
4+
export const sanitizeFileName = (name: string) => {
5+
// Replace colon with hyphen
6+
name = name.replace(/:/g, "-");
7+
// Replace back slash with space
8+
name = name.replace(/\\/g, " ");
9+
// Replace forward slash with space
10+
name = name.replace(/\//g, " ");
11+
// Replace carrot with nothing
12+
name = name.replace(/\^/g, "");
13+
// Replace left bracket with nothing
14+
name = name.replace(/\[/g, "");
15+
// Replace right bracket with nothing
16+
name = name.replace(/\]/g, "");
17+
// Replace hash tag with nothing
18+
name = name.replace(/#/g, "");
19+
// Replace pipe with nothing
20+
name = name.replace(/\|/g, "");
21+
return name.trim();
22+
};

0 commit comments

Comments
 (0)