How can I align code in code listings in HTML?

345 views Asked by At

I would like to have a code listing with alignment in HTML. This is an example of how it is supposed to look:

code listing in a proportional font with alignment

In this example, the beginning of the second line is aligned with the beginning of the last line, the parentheses before “url”, “method” and “parameters” are aligned, and the columns of symbols starting with colons are aligned. The distance from “url” to “:reader” and the like is supposed to be one text space. The distance between the two parentheses at the beginning of the second line is supposed to be zero, but one text space is acceptable.

How can I have that in HTML? Here are my ideas:

  • Tables seem obvious here, but the HTML code would be hard to read if they were used because they would be nested and inline.

  • Custom tabstops would solve it, but I don't know that HTML would have such thing. Does HTML have custom tabstops, that I can mark a position in the text, and all tabs associated with it would horizontally extend to that mark? It would be like tabstops in Word or a similar text processor, but with the tabstop positions calculated based on the text, not manually placed. This could be hacked somehow, like that a dummy element would be placed at the point of alignment, and the alignment would be done with an element with the width calculated by some Javascript as the difference in the coordinates X of that element and the dummy element.

  • The code is in a proportional typeface, so alignment with spaces isn't viable.

I tried to do it with tables. The following snippet is the best that I could do. The code is ugly and the vertical alignment of the parenthesis on the second line is off.

body {
  font-family: sans-serif;
}
pre {
  font-family: sans-serif;
  display: inline;
}
table {
  display: inline-table;
}
td {
  vertical-align: top;
}
.indent {
  width: 1.5em; display: inline-block;
}
<!doctype html><html>
<body>
  (defclass request ()<br>
    <span class="indent"></span><table><tr><td>(</td><td><table><tr><td>(url</td><td>:reader request-url</td></tr>
          <tr><td></td><td>:initarg :url</td></tr>
          <tr><td></td><td>:type string</td></tr>
          <tr><td></td><td>:documentation "<pre>Request URL.</pre>")</td></tr></table></td>
        <tr><td></td><td><table><tr><td>(method</td><td>:reader request-method</td></tr>
          <tr><td></td><td>:initarg :method</td></tr>
          <tr><td></td><td>:initform :get</td></tr>
          <tr><td></td><td>:type keyword</td></tr>
          <tr><td></td><td>:documentation "<pre>Request method, e.g :get, :post.</pre>")</td></tr></table></td></tr>
        <tr><td></td><td><table><tr><td>(parameters</td><td>:reader request-parameters</td></tr>
          <tr><td></td><td>:initarg :parameters</td></tr>
          <tr><td></td><td>:initform nil</td></tr>
          <tr><td></td><td>:type association-list</td></tr>
          <tr><td></td><td>:documentation "<pre>The request parameters, as an association list.</pre>"))</td></tr></table></td></tr>
    </table><br>
    <span class="indent"></span>(:documentation "<pre>A general HTTP request.</pre>"))
</body>
</html>

Which solution is the most viable? is there a cleaner solution that I missed?

1

There are 1 answers

0
matj1 On BEST ANSWER

I implemented custom tabstops (the second idea) how I described it in the question.

The HTML code has tags marking alignment anchors (<align-anchor>). Then, it has tags for elements having an id as the attribute anchor (<align->); they are supposed to extend to the left border of the element with the given id. The id could be of any element, but I use only <align-anchor> for clarity. There are also <indent->; they are supposed to indent text from the beginning of the line, and they have a fixed width.

Questions about how to make this better:

How can I have custom self-closing HTML tags? all tags described in the previous paragraph are empty, so they don't need a closing tag, and not having it would make it much cleaner.

How can it be done so that the code is aligned first and then displayed? If I load the page, the code is not aligned for a short time, and then gets aligned. I'd like the code to be aligned as soon as it's displayed.

The code

function main() {
  let tabs = document.getElementsByTagName("align-");
  for (let tab of tabs) {
    let x = tab.offsetLeft;
    let tabstop = document.getElementById(tab.getAttribute("anchor"));
    let tabstopX = tabstop.offsetLeft;
    let width = tabstopX - x;
    tab.style.width = `${width}px`;
  }
}

window.onload = main
code-block {
  white-space: pre-line;
  font-family: sans-serif;
}
pre {
  font-family: sans-serif;
}
code-block pre {
  display: inline;
}
indent- {
  display: inline-block;
  width: 1.5em;
}
align- {
  display: inline-block;
}
<!doctype html>
<html>
<body>
  <code-block>
    (defclass request ()
      <indent-></indent->(<align-anchor id=1></align-anchor>(url <align-anchor id=2></align-anchor>:reader request-url
          <align- anchor=2></align->:initarg :url
          <align- anchor=2></align->:type string
          <align- anchor=2></align->:documentation "<pre>Request URL.</pre>")
        <align- anchor=1></align->(method <align-anchor id=3></align-anchor>:reader request-method
          <align- anchor=3></align->:initarg :method
          <align- anchor=3></align->:initform :get
          <align- anchor=3></align->:type keyword
          <align- anchor=3></align->:documentation "<pre>Request method, e.g :get, :post.</pre>")
        <align- anchor=1></align->(parameters <align-anchor id=4></align-anchor>:reader request-parameters
          <align- anchor=4></align->:initarg :parameters
          <align- anchor=4></align->:initform nil
          <align- anchor=4></align->:type association-list
          <align- anchor=4></align->:documentation "<pre>The request parameters, as an association list.</pre>"))
      <indent-></indent->(:documentation "<pre>A general HTTP request.</pre>"))
  </code-block>
</body>
</html>