Search code examples
htmlcss

What CSS is needed for <pre> to wrap long lines, but only be the minimum width needed?


I'm trying to configure a <pre> element so that it will wrap long lines mid-word if it has to, but will also reduce size the element itself to the width of the longest line if that's less than the page width.

I've figured out how to do either one of these things, but I can't find a way to make them play nice together.

The use case for this is I'm trying to build a pretty error page for a web application which will include the raw stack trace pretty-printed in a <pre> element, but I want it to correctly handle if a very long string with no white space is included in the stack trace.

For getting mid-word wrap to work I was able to do that using the CSS declarations word-break: break-all; and white-space: break-spaces;. Separately, I could get the <pre> element itself to shrink down to the line width using the declaration width: min-content;. However when I apply both to the same <pre> block it will shrink the block down to fit a single character.

This behavior does makes sense to me given what these declarations are supposed to do, but if there is an alternative way to accomplish this that meets both goals then I am unable to figure out what it is.

Anybody have any suggestions on what I should use instead to get the desired results? Sample code below.

pre {
  background-color: silver;
  border-radius: 1em;
  padding: 2em;
}
.wrap {
  max-width: 3em;
  white-space: break-spaces;
  word-break: break-all;
}
.shrink {
  width: min-content;
}
<h3 class="caption">none</h3>
<pre>0123456789</pre>
<h3 class="caption">wrap</h3>
<pre class="wrap">0123456789</pre>
<h3 class="caption">shrink</h3>
<pre class="shrink">0123456789</pre>
<h3 class="caption">both</h3>
<pre class="shrink wrap">0123456789</pre>


Solution

  • I would advise against allowing line wrapping in a stack trace. That would make it more difficult to read. Instead, just style the pre element with overflow: auto and allow the user to scroll horizontally.

    pre {
      overflow: auto;
      background: lightskyblue;
      padding: 1em;
      border-radius: 0.5em;
    }
    <h1>Here is a scrollable stack trace</h1>
    <pre>
    javax.servlet.ServletException: Something bad happened
        at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:60)
        at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
        at com.example.myproject.ExceptionHandlerFilter.doFilter(ExceptionHandlerFilter.java:28)
        at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
        at com.example.myproject.OutputBufferFilter.doFilter(OutputBufferFilter.java:33)
        at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
        at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
        at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
        at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
        at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
        at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
        at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
        at org.mortbay.jetty.Server.handle(Server.java:326)
        at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
        at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:943)
        at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
        at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
        at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
        at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
        at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
    Caused by: com.example.myproject.MyProjectServletException
        at com.example.myproject.MyServlet.doPost(MyServlet.java:169)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
        at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
        at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
        at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:30)
        ... 27 more
    Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [com.example.myproject.MyEntity]
        at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
        at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
        at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:64)
        at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2329)
        at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2822)
        at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:71)
        at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:268)
        at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:321)
        at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
        at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
        at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
        at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56)
        at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
        at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50)
        at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
        at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:705)
        at org.hibernate.impl.SessionImpl.save(SessionImpl.java:693)
        at org.hibernate.impl.SessionImpl.save(SessionImpl.java:689)
        at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:344)
        at $Proxy19.save(Unknown Source)
        at com.example.myproject.MyEntityService.save(MyEntityService.java:59) <-- relevant call (see notes below)
        at com.example.myproject.MyServlet.doPost(MyServlet.java:164)
        ... 32 more
    Caused by: java.sql.SQLException: Violation of unique constraint MY_ENTITY_UK_1: duplicate value(s) for column(s) MY_COLUMN in statement [...]
        at org.hsqldb.jdbc.Util.throwError(Unknown Source)
        at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
        at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
        at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:57)
        ... 54 more
    </pre>