Thursday, March 27, 2025

Showing Source Code


I have a list of about ten things I want to try doing in this debugger extension, now that I'm up and running. I'm not entirely sure if there is a "logical" order, so I'm just going to do them in an order that feels right to me.

But it seems I can't get very far if I don't have the source code. At the end of the last episode, I felt stumped by how all the pieces of the extension fit together, so I went away and read this and that, and I think I now understand that a little better. We'll come back to that in a bit, but for now let's get started.

I'm working on the assumption that this extension has been opened "on the page" which holds a Till program (actually checking if that is true is one of the things I want to try). So it seems reasonable that the URL of the current page refers to a Till server, so if I can get that, I can query a URL. Our current fetch from the service worker has a full URL because a partial URL didn't work.

So the first thing I'm going to do is add a Go endpoint to return a source file by name; then I'm going to add a fetch to obtain that; then I'm going to insert that into the side panel DOM. We'll then refine and refactor.

A Go Endpoint

At the moment, this feels like a little light relief. I know how to write Go endpoints. We are going to add a handler for /src/{resource} which returns the source code file resource from samples.
    srcHandler := NewDirHandler("samples", "text/plain")
    handlers.Handle("/src/{resource}", srcHandler)
    server := &http.Server{Addr: addr, Handler: handlers}
    err := server.ListenAndServe()

CDP_SRC_HANDLER:cdp-till/internal/web/server.go

Yeah, well, I didn't ever say it was hard, did I?

Fetch in the Sidepanel

When I was working through the tutorials last night, I only had one place to put JavaScript (service-worker.js), so I put my fetch there. What I couldn't understand was how to get what I loaded there into the DOM. In "normal" JavaScript, only a script running in the foreground (page) context can access the DOM.

The same is true of extensions.

And, obviously now I think about it, you can put all the JavaScript you want in the side panel by just including it from sidepanel.html. So we can do our fetch there and then we will be in a perfect position to just insert it into the DOM.
<!DOCTYPE html>
<html>
  <head>
    <title>Till Debugger Panel</title>
    <script src="js/sidepanel.js" type="module"></script>
  </head>
  <body>
    <h1>Till Debugger</h1>
    <p>This is the panel for displaying everything</p>
  </body>
</html>

CDP_FETCH_SOURCE:cdp-till/plugin/html/sidepanel.html

fetch("http://localhost:1399/src/cafe.till").then(resp => {
    console.log(resp.status, resp.statusText);
  });
  

CDP_FETCH_SOURCE:cdp-till/plugin/html/js/sidepanel.js

So the extension window now shows me two console files I can look at, and, lo and behold, I can see 200 OK in html/sidepanel.html (after I had made the Go handler CORS compliant!).

Shove it in the DOM

Now that I am back in the world of foreground DOM manipulation, I know just what to do (at least for now). Let's just create a pre-formatted DOM node and attach the file we've just fetched as the innerText.
<!DOCTYPE html>
<html>
  <head>
    <title>Till Debugger Panel</title>
    <script src="js/sidepanel.js" type="module"></script>
  </head>
  <body>
    <h1>Till Debugger</h1>
    <pre id="source-code"></pre>
  </body>
</html>

CDP_SHOW_SOURCE:cdp-till/plugin/html/sidepanel.html

var pre = document.getElementById("source-code");

fetch("http://localhost:1399/src/cafe.till").then(resp => {
    console.log(resp.status, resp.statusText);
    resp.text().then(src => {
        pre.innerText = src;
    });
});
  

CDP_SHOW_SOURCE:cdp-till/plugin/html/js/sidepanel.js

For what it's worth, it seems that in order for my new extension code to take effect, I have to close and re-open the sidepanel. I don't know whether this is a specific feature of extensions, or whether I haven't rigged something up correctly.

Refining this

So that does the absolute minimum. But I think I'm going to find that very hard to work with. So it seems to me that breaking it up into lines and then putting that in a table is a better way to go. Ultimately, we may need a handful of columns (e.g. a column for icons or buttons), but for now, I'm just going to add two: a line number and a line of source code.
<!DOCTYPE html>
<html>
  <head>
    <title>Till Debugger Panel</title>
    <script src="js/sidepanel.js" type="module"></script>
    <link rel="stylesheet" href="css/sidepanel.css" type="text/css">
  </head>
  <body>
    <table class="scroll-table">
      <thead>
        <tr>
          <th>#</th>
          <th>Code</th>
        </tr>
      </thead>
      <tbody id="source-code">
      </tbody>
    </table>
  </body>
</html>

CDP_SOURCE_TABLE:cdp-till/plugin/html/sidepanel.html

var tbody = document.getElementById("source-code");

fetch("http://localhost:1399/src/cafe.till").then(resp => {
    resp.text().then(src => {
        tbody.innerHTML = '';
        var lines = src.split(/\r?\n/g);
        for (var i=0;i<lines.length;i++) {
            var tr = document.createElement("tr");

            var tlineNo = document.createElement("td");
            tlineNo.className = 'line-no'
            tlineNo.appendChild(document.createTextNode(i+1))
            tr.appendChild(tlineNo);

            var tlineText = document.createElement("td");
            tlineText.appendChild(document.createTextNode(lines[i]))
            tr.appendChild(tlineText);

            tbody.appendChild(tr);
        }
    });
});
  

CDP_SOURCE_TABLE:cdp-till/plugin/html/js/sidepanel.js

I think this is all fairly straightforward text and DOM manipulation. Basically we split the input into lines and create a row in the table with two columns, one for the line number and one for the line content.
.scroll-table {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
}

.scroll-table tr th {
    text-align: left;
}

.line-no {
    text-align: right;
}

CDP_SOURCE_TABLE:cdp-till/plugin/html/css/sidepanel.css

My CSS skills are not great, but this is enough to make it look vaguely presentable. This really needs a monospace font and the leading tabs need to appear properly, but that's not important to me right now.

Conclusion

It really wasn't that hard to wire up the sidepanel to show source code once I realized that it was just a plain old HTML window with the ability to attach JavaScript files and manipulate the DOM directly from there.

No comments:

Post a Comment