昨日から、SmarMemoでPythonコードをブラウザ上で実行できるようにしたい。実行結果もMarkdownノートに保存したい。ことをしたがったので、時間をかけて作りました。
実装内容
- まずは最初は複雑のをするよりはPyodideを導入
- プレビュー画面のPythonコードブロックに「▶Run」ボタンを追加
- 実行結果をリアルタイム表示
- updateOutBlock()を作成し、実行したコードブロックの直後に```textブロックを自動追加・更新するようにしました。
完成した動作
print("Hello")
を実行すると
print("Hello")
Hello
苦労した点
- 最初はMarkdown全体の最後に実行していました。
- コードブロックが複数になると、どの実行結果なのか分からなくなっていました。
- substring()やreplace()を使って対応しょうとしましたが、複雑になってしまいました。
function updateOutputBlock(editor,code,output){
//コードブロックごとに実行管理
const codeText = code.trim();
const currentContent = editor.getValue();
//実行したPythonコードをMarkdownの中で探すため作成
const codeBlock =
"```python\n" +
codeText +
"\n```";
//実行したPythonコードがMarkdownの何文字目にあるか確認
const position = currentContent.indexOf(codeBlock);
const outputBlock =
"\n\n```text\n" +
output.trim() +
"\n```\n";
//positionが見つかったどうか確認
if (position === -1){
return;
}
//見つからなかったら終了
const insertPosition = position + codeBlock.length;
//コードブロックより後ろのMarkdownを取り出す
const afterCode = currentContent.substring(insertPosition);
//ここから実行結果(textブロック)があるか確認
const trimmedAfterCode = afterCode.trimStart();
if(trimmedAfterCode.startsWith("```text")){
const endIndex = trimmedAfterCode.indexOf("```",7);
if(endIndex !== -1){
const rest = trimmedAfterCode.substring(endIndex + 3);
const newContent =
currentContent.substring(0, insertPosition) +
outputBlock +
rest;
editor.setValue(newContent);
}
}
else{
editor.setValue(
currentContent.substring(0, insertPosition) +
outputBlock +
currentContent.substring(insertPosition)
);
}
}
最終的に「実行したコードブロックの直後だけを更新する」というシンプルなアルゴリズムに作り直しました。
//コードブロックごとに実行管理
function updateOutputBlock(editor,code,output){
//エディタ全体のMarkdownを取得
const currentContent = editor.getValue();
//実行したPyhtonコードブロックを作成
const codeBlock =
"```python\n" +
code.trim() +
"\n```";
//実行結果(textブロック)を作成
const outputBlock =
"\n\n```text\n" +
output.trim() +
"\n```\n";
//実行したコードブロックの位置を検索
const position = currentContent.indexOf(codeBlock);
//コードが見つからなければ終了
if (position === -1){
return;
}
//コードブロックまで内容を取得
const before = currentContent.slice(
0,
position + codeBlock.length
);
// コードブロック以降の内容を取得
let after = currentContent.slice(
position + codeBlock.length
);
//既存の実行結果(textブロック)を削除
after = after.replace(
/^\s*```text[\s\S]*?```\s*/,
""
);
//新しい実行結果を導入
const newContent =
before +
outputBlock +
after;
//エディタを更新
editor.setValue(newContent);
}
今後の課題
- エラー表示改善
- Notebook風UIへの発展