Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,14 @@ These are the variables that have a specific purpose other than being used in te
- Both built-in and custom variables can be used while defining these variables.
- The values of these variables can be used in the template body just like built-in and custom variables.

Currently there are two special variables.
Currently there are four special variables.

| Variable | Purpose | Example |
| --- | --- | --- |
| `template_title` | Title of the note/to-do created using this template. | template_title: Standup - {{ date }} |
| `template_tags` | Comma separated tags to be applied to the note/to-do created using this template. | template_tags: spec, {{ project }} |
| `template_notebook` | The ID of the target notebook for this template. Whenever a new note/to-do will be created by this template, it will be created in this target notebook. | template_notebook: 82d2384b025f44588e4d3851a1237028 |
| `template_auto_incremented_prefix` | Adds an automatically incremented ID with "prefix string-incremented id: " format to the the title as a prefix. | template_auto_incremented_prefix: "prefix string" |

**Points to note**
- If `template_title` is not provided, the title of the template will be used as a fallback value.
Expand Down
36 changes: 35 additions & 1 deletion src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,18 @@ export interface NewNote {
const NOTE_TITLE_VARIABLE_NAME = "template_title";
const NOTE_TAGS_VARIABLE_NAME = "template_tags";
const NOTE_FOLDER_VARIABLE_NAME = "template_notebook";
const AUTO_INCREMENTED_PREFIX_VARIABLE_NAME = "template_auto_incremented_prefix";

export class Parser {
private utils: DateAndTimeUtils;
private dialog: string;
private logger: Logger;
private specialVariableNames = [NOTE_TITLE_VARIABLE_NAME, NOTE_TAGS_VARIABLE_NAME, NOTE_FOLDER_VARIABLE_NAME];
private specialVariableNames = [
NOTE_TITLE_VARIABLE_NAME,
NOTE_TAGS_VARIABLE_NAME,
NOTE_FOLDER_VARIABLE_NAME,
AUTO_INCREMENTED_PREFIX_VARIABLE_NAME
];

constructor(dateAndTimeUtils: DateAndTimeUtils, dialogViewHandle: string, logger: Logger) {
this.utils = dateAndTimeUtils;
Expand Down Expand Up @@ -138,6 +144,34 @@ export class Parser {
meta.title = parsedSpecialVariables[NOTE_TITLE_VARIABLE_NAME];
}

if (AUTO_INCREMENTED_PREFIX_VARIABLE_NAME in parsedSpecialVariables) {
const prefix = parsedSpecialVariables[AUTO_INCREMENTED_PREFIX_VARIABLE_NAME];
const prefixRegexp = new RegExp(`^${prefix}-([0-9]+): `);

let maximum = 0;
let pageNumber = 0;
let response: { items: { id: string, title: string }[], has_more: boolean };
do {
response = await joplin.data.get(["search"], {
query: `title:"^${prefix}"`,
fields: ["id", "title"],
page: pageNumber++,
});

for (const item of response.items ?? []) {
const match = item.title.match(prefixRegexp);
if (match) {
const id = parseInt(match[1]);
if (!Number.isNaN(id) && maximum < id) {
maximum = id;
}
}
}
} while (response && response.has_more);

meta.title = `${prefix}-${maximum + 1}: ${meta.title}`;
}

if (NOTE_TAGS_VARIABLE_NAME in parsedSpecialVariables) {
meta.tags = parsedSpecialVariables[NOTE_TAGS_VARIABLE_NAME].split(",").map(t => t.trim()).filter(t => t !== "");
}
Expand Down
6 changes: 6 additions & 0 deletions tests/mock-joplin-api.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Path } from "api/types";

export default {
commands: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
execute: async (cmd: string, props: unknown): Promise<unknown> => { return ""; }
},
data: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
get: async (path: Path, query?: unknown): Promise<unknown> => { return { items: [], has_more: false }; }
},
views: {
dialogs: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down
42 changes: 42 additions & 0 deletions tests/parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,48 @@ describe("Template parser", () => {
`);
});

test("should parse auto incremented prefix special variables correctly", async () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
jest.spyOn(joplin.data, "get").mockImplementation(async (path, query) => {
return { items: [], has_more: false };
});
const parsedTemplate = await parser.parseTemplate({
id: "note-id",
title: "Some template",
body: dedent`
---
template_auto_incremented_prefix: "TEST"

---
`
});
expect(parsedTemplate.title).toEqual("TEST-1: Some template");
});

test("should parse auto incremented prefix special variables and increment title correctly", async () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
jest.spyOn(joplin.data, "get").mockImplementation(async (path, query) => {
return {
items: [{
id: "notebook id",
title: "TEST-100: Some template",
}],
has_more: false
};
});
const parsedTemplate = await parser.parseTemplate({
id: "note-id",
title: "Some template",
body: dedent`
---
template_auto_incremented_prefix: "TEST"

---
`
});
expect(parsedTemplate.title).toEqual("TEST-101: Some template");
});

test("should show an error message if value of a special variable is not a string", async () => {
const template = {
id: "note-id",
Expand Down