Obsidian タスクの開始・終了
タスクシュートでは、タスクの開始時と終了時にボタンを押して、開始時刻と終了時刻を打刻していきます。これによって、ログが残り、実績時間も確認できるようになります。
この処理を、Obsidianでもやりたいと思って、「Templater」というプラグインを使って、コードを実行するようにしています。
基本機能
開始処理
→ - [ ] hh:mm
- [ ] タスク名 → - [ ] hh:mm タスク名
終了処理
- [ ] hh:mm- → - [ ] hh:mm-hh:mm
完了タスクをコピーして開始
- [x] hh:mm-hh:mm タスク名 → - [ ] タスク名
プロパティブロックが設定されていない場合、エラーになる
code:js
<%*
const now = tp.date.now("HH:mm");
const fullNow = tp.date.now("HH:mm:ss");
// 現在のカーソル位置を取得
const initialCursorPos = this.app.workspace.activeEditor.editor.getCursor();
const currentLineText = this.app.workspace.activeEditor.editor.getLine(initialCursorPos.line).trim();
// 行が空白の場合、新しいタスクを作成
if (!currentLineText) {
const newTask = - [ ] ${now}- ;
this.app.workspace.activeEditor.editor.replaceRange(newTask, {line: initialCursorPos.line, ch: 0}, {line: initialCursorPos.line, ch: currentLineText.length});
this.app.workspace.activeEditor.editor.setCursor({line: initialCursorPos.line, ch: newTask.length});
return;
}
// ファイル全体のテキストを取得
let fileContent = tp.file.content;
// タスクのプロパティが存在するかチェック
const propertyRegex = /^---\n(\s\S+?)\n---/s; let propertiesMatch = fileContent.match(propertyRegex);
if (propertiesMatch) {
// プロパティブロックが存在する場合
let properties = propertiesMatch1; let updatedProperties;
if (/startTime:\s*.+/g.test(properties)) {
// "startTime" プロパティを更新
updatedProperties = properties.replace(/startTime:\s*.*/g, startTime: ${fullNow});
} else {
// "startTime" プロパティがない場合、追加
updatedProperties = properties + \nstartTime: ${fullNow};
}
// プロパティ部分のみを置換してテキストを更新
const fileLines = fileContent.split("\n");
let startLine = 0, endLine = 0;
let inPropertiesBlock = false;
for (let i = 0; i < fileLines.length; i++) {
if (fileLinesi.startsWith("---")) { if (!inPropertiesBlock) {
startLine = i;
inPropertiesBlock = true;
} else {
endLine = i;
break;
}
}
}
this.app.workspace.activeEditor.editor.replaceRange(
---\n${updatedProperties}\n---,
{line: startLine, ch: 0},
{line: endLine, ch: fileLinesendLine.length} );
} else {
// プロパティブロックがない場合、新しいプロパティブロックを追加
const newProperties = ---\nstartTime: ${fullNow}\n---\n;
fileContent = newProperties + fileContent;
await tp.file.write(fileContent);
}
// カーソル位置を取得し、行のテキストを処理
let newCursorPos = initialCursorPos;
// 1. のみの場合: 開始時刻を追加
if (/^- \ \ (?!\d{2}:\d{2}-)/.test(currentLineText)) { const updatedLine = currentLineText.replace("- ", - [ ] ${now}- );
this.app.workspace.activeEditor.editor.replaceRange(updatedLine, {line: initialCursorPos.line, ch: 0}, {line: initialCursorPos.line, ch: currentLineText.length});
// カーソルをカーソル行の末尾に移動
newCursorPos.ch = updatedLine.length;
}
// 2. hh:mm- の場合: 終了時刻を追加して、完了状態にする
else if (/^- \ \ \d{2}:\d{2}-/.test(currentLineText)) { const updatedLine = currentLineText.replace(/^-\s\ \\s(\d{2}:\d{2})-/, - [x] $1-${now}); this.app.workspace.activeEditor.editor.replaceRange(updatedLine, {line: initialCursorPos.line, ch: 0}, {line: initialCursorPos.line, ch: currentLineText.length});
// カーソルを次の行に設定
newCursorPos.line += 1;
newCursorPos.ch = 0;
}
// 3. x hh:mm-hh:mm の場合: 行を複製し、複製した行を未完了状態にして、hh:mm-hh:mm の部分を消す else if (/^- \x\ \d{2}:\d{2}-\d{2}:\d{2}/.test(currentLineText)) { const timeRangeEnd = currentLineText.indexOf(" ", 16);
const taskDescription = currentLineText.slice(timeRangeEnd).trim();
const newLine = - [ ] ${taskDescription};
// 現在の行を保持し、複製した行を追加
this.app.workspace.activeEditor.editor.replaceRange(currentLineText, {line: initialCursorPos.line, ch: 0}, {line: initialCursorPos.line, ch: currentLineText.length});
this.app.workspace.activeEditor.editor.replaceRange(newLine + "\n", {line: initialCursorPos.line + 1, ch: 0});
// カーソルを次の行の末尾に設定
newCursorPos.line += 1;
newCursorPos.ch = newLine.length;
}
// 処理完了後、カーソル位置を設定
this.app.workspace.activeEditor.editor.setCursor(newCursorPos);
%>