0

I know this has been asked (Python Markdown nl2br extension, etc) but none of those answers is doing it for me.

I would like to render markdown so that linebreaks occuring within a <p> element will be rendered as <br>. Example: they type

Here is line one.
And line two.

New paragraph.

should render as

<p>Here is line one.<br>And line two.</p>
<p>New paragraph.</p>

I know that if you want that, you should type two spaces at the end of the line you want to <br>. I am trying to make it so my users don't have to do that, but rather, enter text as though they were using a typewriter (for those who know what that is). One hard return, new line; two hard returns, new paragraph.

I've been working with https://parsedown.org/ and have also experimented with https://commonmark.thephpleague.com; also the Python markdown module with nl2br extension (tried their example verbatim, did not work for me). Whatever I do, I end up with either too many or not enough linebreaks, depending.

I have tried what I thought would be clever and elegant: style my markdown's <p> with white-space: "pre" (also tried pre-line). That works, unless the user has done it "right" with two spaces, in which case you get the unwanted double <br> effect.

Also tried nl2br($markdown) with likewise unreliable results.

I want non-technical users to be able to use some basic formatting as easily as possible, and markdown seems just the thing, but for this detail. I don't want to write a CMS just to work around this. For example, I've thought of adding a boolean markdown property on the entity and letting them choose, yadda yadda... don't wanna go there. I've thought of doing some string-replacement or regexp magic, either at database-write time or just before rendering. But again, hoping to avoid getting too complicated. (To make it a little more challenging, I will also have to import a few thousand legacy records that are non-markdown, and potentially deal with issues around old ones versus new.)

Maybe I'm overlooking a simple, sane way out. Any thoughts as to the best strategy?

Update: by popular demand, code examples of what does not work. It's a Zend MVC application that involves Doctrine entities I call MOTD and MOTW (Message Of The Day and Message Of The Week, respectively); these have a string property called content. Generically I think of these entities as Notes and they implement a NoteInterface. When I retrieve these from the database (via a NotesService class that internally uses a custom Doctrine repository class), it's time to render the content as markdown before the controller assigns it to the view:

// from NotesService.php

use Parsedown; 

// stuff omitted...
/**
 * gets MOT(D|W) by date
 *
 * @param  DateTime $date
 * @param  string   $type
 * @param boolean $render_markdown
 * @return NoteInterface|null
 */
public function getNoteByDate(DateTime $date, string $type, bool $render_markdown = true) :? NoteInterface
{
    $entity = $this->getRepository()->findByDate($date,$type);
    if ($entity && $render_markdown) {
        $content = $entity->getContent();
        $entity->setContent($this->parsedown($content));
    }

    return $entity;
}

The point of the boolean $render_markdown is for when we want raw markdown, i.e., when it's going to populate a textarea element of a form.

And the parsedown() method, quite simply:

public function parsedown(string $content) : string
{
    if (! $this->parseDown) {
        $this->parseDown = new Parsedown();
    }
    // nope...
    // return nl2br($this->parseDown->text($content));
    return $this->parseDown->text($content);

}

Inside a viewscript, I just go, e.g.,

if ($this->notes['motd']):
    // echo nl2br($this->notes['motd']->getContent());
    echo $this->notes['motd']->getContent();
else:
  ?><p class="font-italic no-note">no MOTD for this date</p><?php
endif;

Now, if in the editing form they input this as content:

here is a line 
and here is another

now, new paragraph.

and then we save it in the database, when you select it back out and run it through $parsedown->text($content), you get this HTML:

<p>here is a line
and here is another</p>
<p>now, new paragraph.</p>

Please note, the example input above does not have any space characters preceding the linebreaks. When you do type two spaces before the linebreaks, yeah, it works great. But I don't think my users want to think about that. So using nl2br() helps, except when it results in too many consecutive <br>s in the HTML.

My latest thinking is, use a CSS solution and an input filter that strips <space><space> at the end of lines. When it works, I'll add the story to my memoir. :-)

David
  • 815
  • 8
  • 18
  • 1
    You mention a number of things you have tried and state they did not work. However, you have not provided what you tried. Therefore, we can only guess at what you might have done wrong. I suggest editing your question to include a minimal reproducible example of what you tried for your preferred tool. Show your input, the code you used to call Markdown, the actual output and how that differs from your desired output. – Waylan Oct 24 '19 at 17:30
  • Fair enough. Thanks for the constructive criticism. I hesitate to make the post even more verbose, but since you asked :-) -- more code examples, above. – David Oct 24 '19 at 18:40

1 Answers1

0

There may be some more desirable way to achieve this, but finally I decided to

(1) filter the input (at create|update time) with regexp pattern substition to remove trailing ' ' (two consecutive space characters) from lines. I happen to be using ZendFramework's Zend\Filter\PregReplace but it's a de facto wrapper for preg_replace('/( {2,})(\R)/m',$2).

(2) Use CSS to make newlines act like <br> when I display these entities, e.g.,

#motd .card-body p { white-space: pre-line }

Seems to be working for me.

David
  • 815
  • 8
  • 18