Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preformatted text loses its formatting #23

Closed
aral opened this issue Mar 11, 2023 · 12 comments
Closed

Preformatted text loses its formatting #23

aral opened this issue Mar 11, 2023 · 12 comments

Comments

@aral
Copy link
Contributor

aral commented Mar 11, 2023

The spacing in preformatted text should be maintained.

To reproduce

import xhtm from 'xhtm'
import vhtml from 'vhtml' 

const html = xhtm.bind(vhtml)
const markup = html`
  <pre>
         ███                              ███      
       ███░                              ░░░███    
     ███░    ████████  ████████   ██████   ░░░███  
   ███░     ░░███░░███░░███░░███ ███░░███    ░░░███
  ░░░███     ░███ ░███ ░███ ░░░ ░███████      ███░ 
    ░░░███   ░███ ░███ ░███     ░███░░░     ███░   
      ░░░███ ░███████  █████    ░░██████  ███░     
        ░░░  ░███░░░  ░░░░░      ░░░░░░  ░░░       
             ░███                                  
             █████                                 
            ░░░░░                                    
</pre>
`

console.log(markup)

What should render

<pre>
       ███                              ███      
     ███░                              ░░░███    
   ███░    ████████  ████████   ██████   ░░░███  
 ███░     ░░███░░███░░███░░███ ███░░███    ░░░███
░░░███     ░███ ░███ ░███ ░░░ ░███████      ███░ 
  ░░░███   ░███ ░███ ░███     ░███░░░     ███░   
    ░░░███ ░███████  █████    ░░██████  ███░     
      ░░░  ░███░░░  ░░░░░      ░░░░░░  ░░░       
           ░███                                  
           █████                                 
          ░░░░░                                    
</pre>

What actually renders

███ ██████░ ░░░██████░ ████████ ████████ ██████ ░░░██████░ ░░███░░███░░███░░███ ███░░███ ░░░███░░░███ ░███ ░███ ░███ ░░░ ░███████ ███░░░░███ ░███ ░███ ░███ ░███░░░ ███░░░░███ ░███████ █████ ░░██████ ███░░░░ ░███░░░ ░░░░░ ░░░░░░ ░░░░████████░░░░░

Looks like this is due to the indentation fix at https://github.com/dy/xhtm/blob/master/htm.js#L90 but that seems to set expectations for the parsing in other places as merely removing it leads to other issues.

@dy
Copy link
Owner

dy commented Mar 11, 2023

Interesting point! I suspect JSX/HTM just ignores that. You can insert ASCII art as <pre>${'...'}</pre> I suspect, wonder if there are other elements that persist input text formatting, like <pre>, so that's worthy of generalizing and including.

@aral
Copy link
Contributor Author

aral commented Mar 11, 2023

I wonder if we can preprocess preformatted sections where we’re preprocessing comments, etc., to replace whitespace with tokens like we do for fields, etc., and then replace them back at the end. (Or am I missing something simpler?)

@aral
Copy link
Contributor Author

aral commented Mar 11, 2023

PS. My use case is code listings included in preformatted tags (I just thought the ASCII art was more fun for the issue) ;)

@aral
Copy link
Contributor Author

aral commented Mar 11, 2023

I suspect, wonder if there are other elements that persist input text formatting, like <pre>, so that's worthy of generalizing and including.

Just had a quick look on MDN and at the spec and couldn’t see any other tags referenced with preformatted content. I did learn, however, that there’s apparently a tag called <samp> for sample program output. Who knew? :)

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/samp

@dy
Copy link
Owner

dy commented Mar 11, 2023

I think problem can get unsolvable if we provide style="white-space: pre"

@dy
Copy link
Owner

dy commented Mar 11, 2023

Ok, fixed in 1.6.0, added htm.pre with list of preformatted tags. Not absolute solution, but good enough.

@dy dy closed this as completed Mar 11, 2023
@aral
Copy link
Contributor Author

aral commented Mar 11, 2023

I think problem can get unsolvable if we provide style="white-space: pre"

Ah, I hadn’t thought of that. Yes, indeed.

Ok, fixed in 1.6.0, added htm.pre with list of preformatted tags. Not absolute solution, but good enough.

Cool, will try it out now; thanks :)

@aral
Copy link
Contributor Author

aral commented Mar 11, 2023

OK, I was wrong, we can just remove https://github.com/dy/xhtm/blob/master/htm.js#L90 and it doesn’t have any ill effect on real-world rendering. Some tests are failing in xhtm when you remove it but it would mean that `style='white-space: pre" would also work.

Preparing a pull request now with updated tests in case it’s useful and you decide to go with it :)

@dy
Copy link
Owner

dy commented Mar 11, 2023

That line makes xhtm compatible with htm, not sure if it'd be ok removing it fully

@aral
Copy link
Contributor Author

aral commented Mar 11, 2023

@dy Sure, that makes sense and it can lead to other problems due to the creation of extra nodes.

I don’t know if there’s a better way to fix this in just xhtm alone. For Kitten, since HTML rendering is core to what it does, what I ended up doing a few days ago was to inline both xhtm and vhtml into a single source file in the project itself. This has been great for me as I can now iterate and test much more easily. (I added an additional conditional tag to Kitten’s templating language which I couldn’t have done otherwise, for example.)

Regarding the preformatted text issue, I was able to fix it in Kitten but, like the implementation of the <if> tag, that fix requires changes to both the xhtm and vhtml sides.

Here‘s the patch of what I ended up doing in Kitten, in case it helps and/or is interesting:

diff --git a/src/lib/html.js b/src/lib/html.js
index 4528788..bc69f2f 100644
--- a/src/lib/html.js
+++ b/src/lib/html.js
@@ -33,7 +33,7 @@ export const html = htm.bind(h)
 // xhtm
 ////////////////////////////////////////////////////////////////////////////////

-const FIELD = '\ue000', QUOTES = '\ue001'
+const FIELD = '\ue000', QUOTES = '\ue001', SPACES = '\ue002', TABS = '\ue003', NEWLINES = '\ue004'

 const map = { '&':'amp', '<':'lt', '>':'gt', '"':'quot', "'":'apos' }
 const escape = str => String(str).replace(/[&<>"']/g, s => `&${map[s]};`)
@@ -74,7 +74,13 @@ function htm (statics) {
     .replace(/<!--[^]*?-->/g, '')
     .replace(/<!\[CDATA\[[^]*\]\]>/g, '')
     .replace(/('|")[^\1]*?\1/g, match => (quotes.push(match), QUOTES))
-
+
+    .replace(/<pre>(.*?)<\/pre>/gs, match => match
+      .replace(/ /g, SPACES)
+      .replace(/\t/g, TABS)
+      .replace(/\n/g, NEWLINES)
+    )
+
     // ...>text<... sequence
     str.replace(/(?:^|>)([^<]*)(?:$|<)/g, (match, text, idx, str) => {
       let tag, close
@@ -235,6 +241,18 @@ function h(name, attrs) {
     return name(attrs)
   }

+  if (name === 'pre') {
+    // Replace the whitespace in pre tags.
+    stack = stack.map(line => {
+      const fixedLine = line
+        .replace(/\ue002/g, ' ')
+        .replace(/\ue003/g, '\t')
+        .replace(/\ue004/g, '\n')
+      console.log('fixed', fixedLine)
+      return fixedLine
+    })
+  }
+
   // Kitten HTML extension: <if> conditional tag (and optional <then> and <else> tags).
   if (name === 'if') {
     // This is a conditional in the form

@dy
Copy link
Owner

dy commented Mar 11, 2023

Hm, ok. Does it work with pre's children? You can try 1.6.1 - it must do what you need too.

@aral
Copy link
Contributor Author

aral commented Mar 12, 2023

@dy 1.6.1 does it far more elegantly; just implemented your patch and will be using that; thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants