Skip to content

Instantly share code, notes, and snippets.

@FeralFlora
Last active February 28, 2025 14:27
Show Gist options
  • Save FeralFlora/405404e2cfa1fc75053a2b2674b4de42 to your computer and use it in GitHub Desktop.
Save FeralFlora/405404e2cfa1fc75053a2b2674b4de42 to your computer and use it in GitHub Desktop.
Task toggling function for Obsidian tasks. Useful for bulk editing tasks to toggle status, change tags and the scheduled date.
/**
* Toggles a task in a file based on the given Dataview task action and the given status or scheduled date.
*
* @param {Object} action - The Dataview task object containing the task details.
* @param {string} status - The status to toggle the task to.
* @param {string} [date=null] - The date to schedule the task, if applicable.
* @return {Promise<void>} A promise that resolves when the task is toggled.
*/
async function processFile(file, action, toggledTask) {
const cachedData = await app.vault.cachedRead(file);
return app.vault.process(file, (data) => {
if (cachedData !== data) {
data = cachedData;
}
let lines = data.split("\n");
lines[action.line] = toggledTask;
data = lines.join("\n");
return data;
});
}
function taskStale(action, lines) {
return lines[action.line] !== action.text;
}
async function toggleTask(action, status, date = null, newTag = null) {
const dv = app.plugins.plugins["dataview"].api;
const { DateTime} = dv.luxon;
const file = app.vault.getFileByPath(action.path);
const data = await app.vault.cachedRead(file);
const lines = data.split("\n");
const tasksAPI = app.plugins.plugins["obsidian-tasks-plugin"].apiV1;
let taskText = "";
if (taskStale(action, data)) {
taskText = lines[action.line];
} else {
taskText = `- [ ] ${action.text}`;
}
// Regular expressions for task checkboxes, tags and scheduled dates
const checkboxRegex = /\-\s{1}\[.\]/g;
const todoTagRegex = /(\#todo\/\S+)/m;
const scheduledDateRegex = /⏳\s{1}(\d{4}-\d{2}-\d{2})/gu;
const path = action.path;
let toggledTask = "";
switch(status) {
case "done":
toggledTask = tasksAPI.executeToggleTaskDoneCommand(taskText, path);
break;
case "dropped":
toggledTask = taskText.replace(checkboxRegex, "- [-]").replace(todoTagRegex, "");
break;
case "half-done":
toggledTask = taskText.replace(checkboxRegex, "- [/]");
break;
case "schedule":
const scheduledDate = date;
// I had to add .c after scheduled for luxon to be able to parse the date.
const scheduled = action.scheduled ? true : false;
if (scheduled) {
const oldScheduledDate = DateTime.fromObject(action.scheduled.c).toFormat("yyyy-MM-dd");
if (scheduledDate === oldScheduledDate) {
new Notice("Task already scheduled today");
return;
}
}
const regexMatch = taskText.match(scheduledDateRegex);
const caretIndex = taskText.indexOf("^");
if (regexMatch) {
toggledTask = taskText.replace(scheduledDateRegex, (match, p1) => {
return match.replace(p1, scheduledDate);
});
} else if (caretIndex !== -1) {
const beforeCaret = taskText.slice(0, caretIndex);
const afterCaret = taskText.slice(caretIndex);
toggledTask = `${beforeCaret}⏳ ${scheduledDate} ${afterCaret}`;
} else {
toggledTask = taskText + `⏳ ${scheduledDate}`;
}
break;
case "re-tag":
if (!newTag.startsWith("#")) {
newTag = `#${newTag}`;
}
toggledTask = taskText.replace(todoTagRegex, newTag);
break;
default:
new Notice("Status not recognised");
return;
}
await processFile(file, action, toggledTask);
}
module.exports = toggleTask;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment