はじめに
今回は、MarkdownにPyhtonコード実行機能を改善しました。SmarMemoでは、Markdownの中にPyhtonコードを記述し、ブラウザ上で実行できるようにしています。
今回取り組んだのは、主に以下の内容です。
- コードブロックごとの実行結果を管理
- Pythonの実行結果をMarkdownへ保存する
- PythonのエラーもMarkdownへ保存する
- エラー結果をプレビュー上で分かりやすく表示する
単純に実行するというところから、実行した結果もメモとして残せるところまで改善しました。
コードブロックごとの実行結果を管理する
最初に取り込んだのは、複数のPythonコードブロックを実行したときの実行結果の管理です。
たとえば、Markdownの中に複数のPythonコードがある場合
print("HelloWorld~")
for i in range(1,10):
print(i)
それぞれのコードに対応した実行結果を保存する必要があります。
そこで、実行されたPythonコードからMarkdown上のコードブロックを作成し、indexOf()を使って現在のMarkdown内の位置を検索するようにしました。
const currentContent = editor.getValue();
const codeBlock =
"```python\n" +
code.trim() +
"\n```";
const position = currentContent.indexOf(codeBlock);
コードブロックが見つからない場合は処理を終了します。
if (position === -1){
return;
}
これによって、実行されたコードブロックを基準に、その直後の実行結果を管理できるようにしました。
PythonのエラーもMarkdownへ保存する
次に、Python実行時のエラーについて改善しました。
PyodideでPythonを実行してエラーが発生した場合、エラー内容を取得して表示します。
}catch (err){
outputDiv.classList.remove('text-light');
outputDiv.classList.add('text-danger');
outputDiv.textContent =
`❌ ${err.name || 'Error'}: ${err.message}\n`;
}
例えば、
❌ ZeroDivisionError: division by zero
のように表示されます。
しかし、画面に表示するだけではページを更新したときに消えてしまいます。
そこで、エラーの場合も updateOutputBlock() を呼び出して、Markdownへ保存するようにしました。
updateOutputBlock(
editor,
block.textContent,
outputDiv.textContent,
true
);
textとerrorを区別
実行結果とエラーを区別するため、Markdownのコードフェンスを分けることにしました。
const fenceLabel = isError ? 'error' : 'text';
const outputBlock =
"\n\n```" + fenceLabel + "\n" +
output.trim() +
"\n```\n";
正常な実行結果の場合は、
Hello
エラーの場合は、
❌ ZeroDivisionError: division by zero
として保存されます。
Markdownそのものには文字色を指定していません。
text と error という種類だけをMarkdownに保存し、表示方法はCSS側で管理するという考え方にしました。
CSSでエラーを赤く表示する
Markdownでは標準的に文字色を変更する機能はありません。
そこで、marked.js が生成するコードブロックのクラスを利用しました。
error はHTMLに変換されると、
<code class="language-error">
のようになります。
そこでCSSに、
#preview pre code.language-error {
color: #ff6b6b;
background-color: #2b1414;
display: block;
padding: 0.5rem;
border-radius: 4px;
white-space: pre-wrap;
}
を追加しました。
これによって、通常の実行結果は通常のコードブロックとして表示し、エラーだけを赤文字で表示できるようになりました。
実装してみて
今回の改善によって、SmartMemoのPython実行機能が少しずつ「実行するだけ」の機能から、「実行した内容を記録する」機能へ変わってきました。
現在は、
Markdown
↓
Pythonコードを実行
↓
実行結果を取得
↓
コードブロックを特定
↓
既存の結果を置き換え
↓
Markdownへ保存
↓
プレビューに反映
という流れになっています。また、エラーの場合は、
Pythonエラー
↓
errorブロックとして保存
↓
language-error
↓
CSSで赤く表示
という流れにしています。
Markdown、JavaScript、Pyodide、CSSを組み合わせることで、単純なメモアプリとは少し違った使い方ができるようになってきました。
今後について
次回はUI改善に取り組む予定です。
現在エディタとプレビューのサイズが小さく固定されているため、幅を変更して見やすく作りたいと考えています。
