最終更新日 2025-02-18

簡易サーバーを立てて、レンダリングペインのHTML側から編集テキスト内容を取得

簡易サーバーを立てることで、レンダリングペインやブラウザペインなど、
HTML側から通信経由で「秀丸マクロのjsmode内の情報」へとアクセスしやすくなるため、
やれることが広がります。
(CPU負荷的にも「多コア・かつ非同期」に分散されて優れた作りになりやすい)

レンダリングペインのHTML側から秀丸エディタの編集エリアの内容を取得し、
その内容をマークダウンとして解釈してレンダリングしてみましょう。

1秒に1度情報を取得し、リアルタイムに反映します。

HttpServerオブジェクトを使いましょう。

HmTextUpdateServer.mac
jsmode "WebView2\\" + currentmacrofilename;
 
js {
debuginfo(2);
 
const renderPaneName = "HmTextUpdateServer";
 
 
// 前回マクロを実行した際の、tickがまだ継続しているなら、クリアする。
if (typeof(intervalHandle) != "undefined") {
    hidemaru.clearInterval(intervalHandle);
}
 
 
var intervalHandle; // 宣言だけ。js{ }から脱出しても変数を次回マクロ実行時にも残すため、var にする。
 
 
 
if (typeof(server) != "undefined") {
    server.close();
}
  
var server = hidemaru.createHttpServer(async (req, res) => {
  
    let url = req.url;
    // ここはパッチだと思って!!
    if (typeof (url) != "string") {
        url = await req.url;
    }
  
    if (url == "/text") {
        res.writeHead(200); // OK
        // 空っぽを返すとfetch側でエラーが出てしまうので「HmTextUpdateServerIsEmpty」を返し、HTML側で識別する
        let text = gettotaltext() || "HmTextUpdateServerIsEmpty";
        res.write(text);
        res.end("");
    } else {
        res.writeHead(404); // Not found
        res.end("");
    }
});
  
server.listen(0); //ランダムなポート
let port = server.port;
  
if (port == 0 ) {
    console.log("サーバー構築失敗");
}
 
 
 
// メッセージをアウトプット枠へと出力
function writeLineOutputPane(msg) {
    let dll = loaddll("HmOutputPane.dll");
    dll.dllFuncW.OutputW(hidemaru.getCurrentWindowHandle(), msg + "\r\n");
}
 
function makeUrl(htmlFullPath, port) {
    let absoluteUrl = new URL(htmlFullPath);
    let params = new URLSearchParams();
    params.set("port", String(port));
    absoluteUrl.search = new URLSearchParams(params).toString();
    return absoluteUrl;
}
 
// メインの処理
function main() {
 
    let hrefURL = makeUrl(currentmacrodirectory() + "\\HmTextUpdateServer.html", port);
 
    // 指定のパラメータでレンダーペインを開く。browserpanecommand にして、targetを "_each" にしてもほぼ同じこと
    renderpanecommand ( 
    {
        target: renderPaneName,
        url : hrefURL,
        watch: 0,
        watchsave: 0,
        initialize: "async",
        show: 1,
        size : 800
    }
    );
 
    const intervalTickTime = 1000; // hidemaru.setIntervalの間隔。この間隔は担保されないので注意。
                                   // (実行の予約は概ね正確にこの間隔となるが、関数の実行は全く違う)
    let latestUpdateCount = 0;
 
    // 指定の間隔で
    intervalHandle = hidemaru.setInterval(() => {
 
        // ----------------------------------------------------------------
        // テキストが変化したか?
        let updateCount = updatecount();
        if (latestUpdateCount != updateCount) {
            latestUpdateCount = updateCount;
            // レンダリングペイン側から、通信経由で秀丸マクロ上のマクロ情報へとアクセスする
            renderpanecommand ( {
                target: renderPaneName,
                url: "javascript: HmTextUpdateServer_Update()",
            }
            );
        }
 
    }, intervalTickTime);
}
 
main();
 
}

HTML 側

受信した内容を、テキストとしてそのまま表示してみましょう。

HmTextUpdateServer.html
<!DOCTYPE html>
<html>
<head>
<title>HmTextUpdateServer</title>
</head>
<body>
<div id='output'>
</div>
<script>
// ファイルURLからポート番号を取得
let urlParams = new URLSearchParams(window.location.search);
let port = Number(urlParams.get('port'));
 
async function HmTextUpdateServer_Update() {

    if (!port) {
        output.innerText = "ポート番号が指定されていません。";
        return;
    }
    const url = `http://localhost:${port}/text`;
 
    fetch(url)
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            return response.text();
        })
        .then(data => {
            if (data == "HmTextUpdateServerIsEmpty") {
                output.innerText = "";
            } else {
                output.innerText = data;
            }
        })
        .catch(error => {
            output.innerText = "エラーが発生しました: " + error.message + error.stack;
        });
}
</script>
</body>
</html>

HTML 改変版

それでは、これを単にテキストとして解釈するのではなく、マークダウンとして解釈してHTMLとして表示するように改変してみましょう。

HmTextUpdateServer.html
<!DOCTYPE html>
<html>
<head>
<title>HmTextUpdateServer</title>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
</head>
<body>
<div id='output'>
</div>
<script>
// ファイルURLからポート番号を取得
let urlParams = new URLSearchParams(window.location.search);
let port = Number(urlParams.get('port'));
 
async function HmTextUpdateServer_Update() {

    if (!port) {
        output.innerText = "ポート番号が指定されていません。";
        return;
    }
    const url = `http://localhost:${port}/text`;
 
    fetch(url)
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            return response.text();
        })
        .then(data => {
            if (data == "HmTextUpdateServerIsEmpty") {
                output.innerText = "";
            } else {
                // 受信したテキストをマークダウンと解釈してHTMLに
                const html = marked.parse(data);
                output.innerHTML = html;
            }
        })
        .catch(error => {
            output.innerText = "エラーが発生しました: " + error.message + error.stack;
        });
}
</script>
</body>
</html>