for-custom-new-page
gitに移動したあとのコードで使っている
2023-02-09
06:47:50 切り出したタスクはindentを下げて書き込む
本当はyyyy-MM-ddにぶら下げたいところだけど、
makeUpdater
でfrom
...が挿入されるしなあ
共通の処理にするのをやめて、もっと細かくカスタマイズできるようにしようかな
作業ログっぽい見た目にしたい
$ deno check --remote -r=https://scrapbox.io https://scrapbox.io/api/code/takker/for-custom-new-page/mod.ts
mod.tsimport { getEachLatestDate } from "../getEachLatestDate/mod.ts";
import type {
NewPageHook,
Updater
} from "../custom-new-page/mod.ts";
import { parse, toString, isTask } from "../takker99%2Ftakker-scheduler/deps.ts";
import type { Interval } from "../takker99%2Ftakker-scheduler/deps.ts";
import { getIndentCount, toTitleLc } from "../scrapbox-userscript-std/text.ts";
import { lightFormat } from "../date-fns/mod.ts";
import { parse as parseTaskLink } from "../takker99%2Ftakker-scheduler/workflow.ts";
mod.tsconst foodType = {
"あさごはん": "#log-breakfast",
"ひるごはん": "#log-lunch",
"よるごはん": "#log-dinner",
} as Record<string, `#${string}`>;
/** task lineの配下に書いたものを新しいページに切り出す
*
* 食べたもの記録のときは若干Templateを変える
*/
export const taskLineHook: NewPageHook = (
text, { title, projectTo, mode },
) => {
const [taskLine, ...lines] = text.split("\n");
const parsed = parse(taskLine);
// task lineでない場合は対象外
if (!parsed) return;
// 箇条書きが無い時は何も切り出さない
if (lines.length === 0) return { text, pages: [] };
const { title: taskName, base, record, ...rest } = parsed;
const newTitle = makeTaskTitle(taskName, base, record);
const newTaskLine = toString({
title: `[${newTitle}]`,
base,
record,
...rest,
});
// 余計なインデントを削る
const minIndentNum = Math.min(...lines.map((line) => getIndentCount(line)));
const newLines = [
// タスク名にリンクが入っていたときはそれを使う
// ただし、ページタイトルと同名のときは挿入しない
...(/[\[\]]/.test(taskName) && taskName !== `[${newTitle}]` ? [taskName] : []),
...((title in foodType) ? [
...lines.map(
(line) => line.slice(minIndentNum)
),
"",
foodType[title],
'#log-eatenfood'
] : [
...lines.map(
(line) => ` ${line.slice(minIndentNum)}`
),
"",
]
),
];
return {
text: newTaskLine,
pages: [{
project: projectTo,
title: newTitle,
lines: makeUpdater(title, newLines, [record.start ?? base]),
mode,
}],
};
};
taskLineHook.hookName = "task-hook";
切り出したページのタイトル
タスクリンクもしくは
yyyy-MM-dd
で終わるタイトルの場合はそれをそのまま使う
mod.tsconst makeTaskTitle = (title: string, base: Date, record: Interval) => {
const date = lightFormat(record.start ?? base, 'yyyy-MM-dd');
if (title in foodType) {
return `${date} ${title}`;
}
// タスクリンクの場合は、それをそのまま使う
if (parseTaskLink(title.slice(1, -1))) {
return title.slice(1, -1);
}
const first = title.replace(/[\[\]]/g, '').trim();
return `${first}${first.endsWith(date) ? "" : ` ${date}`}`;
};
/^takker-/
にマッチするprojectでは
日付タグを末尾に挿入する
切り出し範囲の行の更新日時を日付タグにして挿入する
mod.tsexport const newPageHook: NewPageHook = (
text, { title: titleFrom, projectTo, mode, lines: metaLines },
) => {
const [rawTitle, ...lines] = text.split("\n");
// 複数行の切り出しのみ対象
if (lines.length === 0) return;
const title = rawTitle.replace(/[\[\]]/g, "").trim();
// 余計なインデントを削る
const minIndentNum = Math.min(...lines.map((line) => getIndentCount(line)));
// 行の更新日時と現在日時から日付タグを作る
// "takker"で始まるproject以外ではoffにする
const dates = projectTo.startsWith("takker") ? getEachLatestDate([
new Date(),
...metaLines.map(({ updated }) => new Date(updated * 1000)),
])
.sort((a, b) => b.getTime() - a.getTime())
: [];
const newLines = [
"",
// タイトルにリンクが入っていたときはそれを使う
...(/[\[\]]/.test(rawTitle) ? [rawTitle.trimStart()] : []),
...lines.map((line) => line.slice(minIndentNum)),
];
return {
text: `${" ".repeat(getIndentCount(rawTitle))}[${title}]`,
pages: [{
project: projectTo,
title,
lines: makeUpdater(titleFrom, newLines, dates),
mode,
}],
};
};
newPageHook.hookName = "new-page-hook";
mod.tsexport const splittedLinkHook: NewPageHook = (
text, { projectTo, mode },
) => {
// リンクを含む単一行のみ対象
if (text.includes("\n") || !/\[[^\]]+\]/.test(text)) return;
const [indent, str] = text.match(/^(\s*)(.*)$/)?.slice?.(1) ??
["", text];
const linksLcInText = [...str.matchAll(/\[([^\]])\]/g)]
.map((match) => toTitleLc(match[1]));
const title = str.replaceAll("[", "").replaceAll("]", "");
return {
text: `${indent}[${title}]`,
pages: [{
project: projectTo,
title,
lines: (prev, { links }) =>{
// すでに同じリンクが書き込まれているときは何もしない
const linksLc = links.map((link) => toTitleLc(link));
if (linksLcInText.every(
(linkLc) => linksLc.includes(linkLc)
)) return;
return [
...prev.map((line) => line.text),
str,
];
},
mode,
}],
};
};
splittedLinkHook.hookName = "splitted-link-hook";
utilities
以下のルールに沿って書き込む
切り出し先ページに日付タグが挿入されている場合は、その前に切り出した文章を挿入する
切り出し先ページにすでにある日付タグは挿入しない
mod.tsconst makeUpdater = (titleFrom: string, lines: Iterable<string>, dates: Date[]): Updater =>
(prev, { links }) => {
const linksLc = links.map((link) => toTitleLc(link));
const fromLine = linksLc.includes(toTitleLc(titleFrom)) ?
[] :
[`from [${titleFrom}]`];
const dateTags = dates.flatMap(
(date) => {
const yyyyMMdd = lightFormat(date, "yyyy-MM-dd");
if (linksLc.includes(yyyyMMdd)) return [];
const HHmmss = lightFormat(date, "HH:mm:ss");
return [`#${yyyyMMdd} ${HHmmss}`];
}
);
const prevLines = prev.map((line) => line.text);
/** the index where the first dateTag starts */
const index = prevLines.findIndex(
(line) => /^#\d{4}-\d{2}-\d{2}/.test(line)
);
const insertBefore = index < 0 ? prevLines.length : index;
return [
...prevLines.slice(0, insertBefore),
...fromLine,
...lines,
"",
...dateTags,
...prevLines.slice(insertBefore),
];
};