Simple markdown rendering

20

2

There are several ways to create headers on posts on the Stack Exchange network. The format that's most commonly1 used on PPCG seems to be:

# Level one header
## Level two header
### Level three header

Note the space after the hash marks. Also, note that trailing hash marks are not included.

Challenge:

Take a (possibly multiline) string as input, and output the string on the following format:

  • If the header is level 1, then output each letter 4-by-4 times
  • If the header is level 2, then output each letter 3-by-3 times
  • If the header is level 3, then output each letter 2-by-2 times
  • If a line is not a header then output it as it is.

To illustrate:

--- Level 1 ---
# Hello
--- Output---
HHHHeeeelllllllloooo    
HHHHeeeelllllllloooo
HHHHeeeelllllllloooo
HHHHeeeelllllllloooo

--- Level 2 ---
## A B C def
--- Output ---
AAA   BBB   CCC   dddeeefff
AAA   BBB   CCC   dddeeefff
AAA   BBB   CCC   dddeeefff

--- Level 3 ---
### PPCG!
--- Output---
PPPPCCGG!!
PPPPCCGG!!

Simple as that!


Rules:

  • You must support input over multiple lines. Using \n etc. for newlines is OK.
    • There won't be lines containing only a # followed by a single space
  • The output must be presented over multiple lines. You may not output \n instead of literal newlines.
    • Trailing spaces and newlines are OK.

Test cases:

Input and output are separated by a line of ....

# This is a text
with two different
### headers!
........................................................    
TTTThhhhiiiissss    iiiissss    aaaa    tttteeeexxxxtttt
TTTThhhhiiiissss    iiiissss    aaaa    tttteeeexxxxtttt
TTTThhhhiiiissss    iiiissss    aaaa    tttteeeexxxxtttt
TTTThhhhiiiissss    iiiissss    aaaa    tttteeeexxxxtttt
with two different
hheeaaddeerrss!!
hheeaaddeerrss!!

This input has
## trailing hash marks ##
#and a hash mark without a space after it.
........................................................    
This input has
tttrrraaaiiillliiinnnggg   hhhaaassshhh   mmmaaarrrkkksss   ######
tttrrraaaiiillliiinnnggg   hhhaaassshhh   mmmaaarrrkkksss   ######
tttrrraaaiiillliiinnnggg   hhhaaassshhh   mmmaaarrrkkksss   ######
#and hash marks without a space after it.

# This ## is ### strange
#### ###
........................................................
TTTThhhhiiiissss    ########    iiiissss    ############    ssssttttrrrraaaannnnggggeeee
TTTThhhhiiiissss    ########    iiiissss    ############    ssssttttrrrraaaannnnggggeeee
TTTThhhhiiiissss    ########    iiiissss    ############    ssssttttrrrraaaannnnggggeeee
TTTThhhhiiiissss    ########    iiiissss    ############    ssssttttrrrraaaannnnggggeeee
#### ###

Multiple


### newlines! # 
:)
........................................................    
Multiple


nneewwlliinneess!!  ##
nneewwlliinneess!!  ##
:)

Line with only a hash mark:
#
### ^ Like that!
........................................................    
Line with only a hash mark:
#
^^  LLiikkee  tthhaatt!!
^^  LLiikkee  tthhaatt!!

1: I haven't really checked, but I think it's true.

Stewie Griffin

Posted 2017-11-16T20:47:53.213

Reputation: 43 471

May we take input as a string array? – Ian H. – 2017-11-16T21:56:12.383

Answers

7

Stacked, 51 50 bytes

Saved 1 byte thanks to @RickHitchcock - golfed regex

['^(##?#?) (.+)'[\#'5\-@k CS k*k rep LF#`]3/mrepl]

Try it online!

Anonymous function that takes input from the stack and leaves it on the stack.

Explanation

['^(##?#?) (.+)'[\#'5\-@k CS k*k rep LF#`]3/mrepl]
[                                            mrepl]   perform multiline replacement
 '^(##?#?) (.+)'                                     regex matching headers
                [                        ]3/         on each match:
                 \#'                                   count number of hashes
                    5\-                                5 - (^)
                       @k                              set k to number of repetitions
                          CS                           convert the header to a char string
                             k*                        repeat each char `k` times
                               k rep                   repeat said string `k` times
                                     LF#`              join by linefeeds

Conor O'Brien

Posted 2017-11-16T20:47:53.213

Reputation: 36 228

3

JavaScript (ES6), 111 105 bytes

Saved 6 bytes thanks to @Shaggy

s=>s.replace(/^(##?#?) (.+)/gm,(_,a,b)=>`
${b.replace(/./g,e=>e.repeat(l=5-a.length))}`.repeat(l).trim())

Matches 1-3 hashes at the beginning of the string or preceded by a new line, then repeats each character in the match along with the match itself, based on the length of the hashes.

Test Cases:

let f=

s=>s.replace(/^(##?#?) (.+)/gm,(_,a,b)=>`
${b.replace(/./g,e=>e.repeat(l=5-a.length))}`.repeat(l).trim())

console.log(f('# This is a text\nwith two different\n### headers!'));
console.log('______________________________________________');
console.log(f('This input has\n## trailing hash marks ##\n#and a hash mark without a space after it.'));
console.log('______________________________________________');
console.log(f('# This ## is ### strange\n#### ###'));
console.log('______________________________________________');
console.log(f('Multiple\n\n\n### newlines! # \n:)'));
console.log('______________________________________________');
console.log(f('Line with only a hash mark:\n#\n### ^ Like that!'));

Rick Hitchcock

Posted 2017-11-16T20:47:53.213

Reputation: 2 461

2

Retina, 125 104 bytes

m(`(?<=^# .*).
$0$0$0$0
(?<=^## .*).
$0$0$0
(?<=^### .*).
$0$0
^# 
$%'¶$%'¶$%'¶
^## 
$%'¶$%'¶
^### 
$%'¶

Try it online

Saved 21 bytes thanks to Neil.

mbomb007

Posted 2017-11-16T20:47:53.213

Reputation: 21 944

Save 3 bytes by using %) on the third stage which allows you to remove the %s on the first two stages. Also one normally puts the G after the (s (of which you'll now need two) in the header. – Neil – 2017-11-17T02:02:34.417

Better still, you can use m) or m( which now saves 9 bytes because you can then remove all of the other ms. – Neil – 2017-11-17T02:04:53.537

The header turned out to be unnecessary. Also, I saved another 12 bytes: Try it online!

– Neil – 2017-11-17T02:09:36.573

Oh, yeah I was just used to using the header for multiple test cases. – mbomb007 – 2017-11-17T04:14:51.927

2

MATL, 43 42 40 bytes

1 byte removed thanks to Rick Hitchcock!

`j[]y'^##?#? 'XXgn:(2M4:QP&mt~+t&Y"0YcDT

This outputs a trailing space in each line (allowed by the challenge), and exits with an error (allowed by default) after producing the ouput.

Try it online!

Explanation

`            % Do...while loop
  j          %   Input a line as unevaluated string
  []         %   Push empty array
  y          %   Duplicate from below: push input line again
  '^##?#? '  %   Push string for regexp pattern
  XX         %   Regexp. Returns cell array with the matched substrings
  g          %   Get cell array contents: a string, possibly empty
  n          %   Length, say k. This is the title level plus 1, or 0 if no title
  :(         %   Assign the empty array to the first k entries in the input line
             %   This removing those entries from the input
  2M         %   Push k again
  4:QP       %   [1 2 3 4], add 1 , flip: pushes [5 4 3 2]
  &m         %   Push index of k in that array, or 0 if not present. This gives
             %   4 for k=2 (title level 1), 3 for k=3 (tile level 2), 2 for k=2
             %   (title level 1), and 0 for k=0 (no title). The entry 5 in the
             %   array is only used as placeholder to get the desired result.
  t~+        %   Duplicate, negate, add. This transforms 0 into 1
  t&Y"       %   Repeat each character that many times in the two dimensions
  0Yc        %   Postpend a column of char 0 (displayed as space). This is 
             %   needed in case the input line was empty, as MATL doesn't
             %   display empty lines
  D          %   Display now. This is needed because the program will end with
             %   an error, and so implicit display won't apply
  T          %   True. This is used as loop condition, to make the loop infinite
             % End (implicit)

Luis Mendo

Posted 2017-11-16T20:47:53.213

Reputation: 87 464

I was wondering what the best way to do this in MATLAB was... Kronecker product was of course the best way to do it :) Nice! – Stewie Griffin – 2017-11-17T10:04:15.480

@StewieGriffin When I saw the challenge I immediately thought of Kronecker product. But I just found a way that is 2 bytes shorter using repelem (Y" in MATL). kron is still probably the shortest way in MATLAB – Luis Mendo – 2017-11-17T10:10:20.517

2

Perl 5, 47 +1 (-p) bytes

s/^##?#? //;$.=6-("@+"||5);$_=s/./$&x$./ger x$.

try it online

Nahuel Fouilleul

Posted 2017-11-16T20:47:53.213

Reputation: 5 582

1

Charcoal, 46 bytes

FN«Sι≔⊕⌕E³…⁺×#κι⁴### θF⎇θ✂ι⁻⁵θLι¹ι«G↓→↑⊕θκ→»D⎚

Try it online! Link is to verbose version of code. Charcoal doesn't really do string array input, so I've had to add the array length as an input. Explanation:

FN«Sι

Loop over the appropriate number of input strings.

≔⊕⌕E³…⁺×#κι⁴### θ

Create an array of strings by taking the input and prefixing up to 2 #s, then truncating to length 4, then try to find ### in the array, then convert to 1-indexing. This results in a number which is one less than the letter zoom.

F⎇θ✂ι⁻⁵θLι¹ι«

If the letter zoom is 1 then loop over the entire string otherwise loop over the appropriate suffix (which is unreasonably hard to extract in Charcoal).

G↓→↑⊕θκ→

Draw a polygon filled with the letter ending at the top right corner, and then move right ready for the next letter.

»D⎚

Print the output and reset ready for the next input string.

Neil

Posted 2017-11-16T20:47:53.213

Reputation: 95 035

1

SOGL V0.12, 31 28 bytes

¶Θ{■^##?#? øβlF⁄κ6κ5%:GI*∑∙P

Try it Here! - extra code added because the code is a function and takes input on stack (SOGL can't take multiline input otherwise :/) - inputs.value” - push that string, - evaluate as JS, F - call that function

Explanation:

¶Θ                            split on newlines
  {                           for each item
   ■^##?#?                      push "^##?#? "
           øβ                   replace that as regex with nothing
             l                  get the new strings length
              F⁄                get the original strings length
                κ               and subtract from the original length the new strings length
                 6κ             from 6 subtract that
                   5%           and modulo that by 5 - `6κ5%` together transforms 0;2;3;4 - the match length to 1;4;3;2 - the size
                     :          duplicate that number
                      G         and get the modified string ontop
                       I        rotate it clockwise - e.g. "hello" -> [["h"],["e"],["l"],["l"],["o"]]
                        *       multiply horizontally by one copy of the size numbers - e.g. 2: [["hh"],["ee"],["ll"],["ll"],["oo"]]
                         ∑      join that array together - "hheelllloo"
                          ∙     and multiply vertiaclly by the other copy of the size number: ["hheelllloo","hheelllloo"]
                           P    print, implicitly joining by newlines

dzaima

Posted 2017-11-16T20:47:53.213

Reputation: 19 048

0

Proton, 130 bytes

x=>for l:x.split("\n"){L=l.find(" ")print(L>3or L+len(l.lstrip("\#"))-len(l)?l:"\n".join(["".join(c*(5-L)for c:l[L+1to])]*(5-L)))}

Try it online!

HyperNeutrino

Posted 2017-11-16T20:47:53.213

Reputation: 26 575

I think you are not allowed to receive and return a list of lines, the rules are quite strict: *You must support input over multiple lines.*, *The output must be presented over multiple lines. You may not output \n instead of literal newlines.*. – Mr. Xcoder – 2017-11-16T21:17:24.543

@Mr.Xcoder Whoops, my bad. Fixing. – HyperNeutrino – 2017-11-16T21:19:54.847

Note: It's OK if the input has \n, but the output should be shown with literal newlines. – Stewie Griffin – 2017-11-16T21:31:17.727

@mbomb007 Whoops I forgot to put the 5- in there. Sorry – HyperNeutrino – 2017-11-16T21:40:33.667

@mbomb007 fixed – HyperNeutrino – 2017-11-16T22:22:38.623

0

Python 3, 147 bytes

def f(x):
	for l in x.split("\n"):L=l.find(" ");print(L>3or L+len(l.lstrip("#"))-len(l)and l or"\n".join(["".join(c*(5-L)for c in l[L+1:])]*(5-L)))

Try it online!

-1 byte thanks to Mr. Xcoder

HyperNeutrino

Posted 2017-11-16T20:47:53.213

Reputation: 26 575

@mbomb007 Whoops I forgot to put the 5- in there. Sorry – HyperNeutrino – 2017-11-16T21:40:35.603

0

Python 3, 131 bytes

from re import*
print(sub("^(#+) (.*?)$",lambda x:((sub('(.)',r'\1'*(5-len(x[1])),x[2])+'\n')*(5-len(x[1])))[:-1],input(),flags=M))

Try it online!

I used Python 3 in order to use [] with regex.

Neil

Posted 2017-11-16T20:47:53.213

Reputation: 2 417

0

C# (.NET Core), 268 + 18 bytes

n=>{var r="";for(int l=0,c;l<n.Length;l++){var m=n[l];var s=m.Split(' ');var y=s[0];if(!y.All(x=>x==35)|y.Length>3|s.Length<2)r+=m+'\n';else for(int i=0,k=y.Length;i<5-k;i++){for(c=1;c<m.Length-k;)r+=new string(m.Substring(k,m.Length-k)[c++],5-k);r+='\n';}}return r;};

Try it online!

Ian H.

Posted 2017-11-16T20:47:53.213

Reputation: 2 431

0

JavaScript, 112 bytes

x=>x.replace(/^(##?#?) (.*)/mg,(_,n,w)=>(t=>Array(t).fill(w.replace(/./g,c=>c.repeat(t))).join`
`)(5-n.length))

f=

x=>x.replace(/^(##?#?) (.*)/mg,(_,n,w)=>(t=>Array(t).fill(w.replace(/./g,c=>c.repeat(t))).join`
`)(5-n.length))
<textarea id=i style=display:block oninput=o.value=f(i.value)></textarea>
<output id=o style=display:block;white-space:pre></output>

tsh

Posted 2017-11-16T20:47:53.213

Reputation: 13 072

I don't think this works for #### ##. – Rick Hitchcock – 2017-11-17T14:20:34.777

@RickHitchcock fixed – tsh – 2017-11-18T08:35:06.130

0

PHP, 122+1 bytes

for($y=$z=" "==$s[$i=strspn($s=$argn,"#")]&&$i?5-$i++:1+$i=0;$y--;print"
")for($k=$i;~$c=$s[$k++];)echo str_pad($c,$z,$c);

Run as pipe with -nR (will work on one input line after another) or try it online.

Titus

Posted 2017-11-16T20:47:53.213

Reputation: 13 814

0

J, 55 bytes

([:{:@,'^##?#? 'rxmatch])((1 1 4 3 2{~[)([:|:[$"0#)}.)]

I don't know how to make TIO work with J regex, so I can't provide a working link.

Here's how to test it in the J interpreter (tested with J804)

   f=.([:{:@,'^##?#? 'rxmatch])((1 1 4 3 2{~[)([:|:[$"0#)}.)]
   txt=.'# Hello'; '## A B C def'; '### PPCG!'; '#and a hash mark without a space after it.'; '##### ###'
   ; f each txt

HHHHeeeelllllllloooo                      
HHHHeeeelllllllloooo                      
HHHHeeeelllllllloooo                      
HHHHeeeelllllllloooo                      
AAA   BBB   CCC   dddeeefff               
AAA   BBB   CCC   dddeeefff               
AAA   BBB   CCC   dddeeefff               
PPPPCCGG!!                                
PPPPCCGG!!                                
#and a hash mark without a space after it.
##### ###

I simulate a multiline string through a list of boxed strings.

Galen Ivanov

Posted 2017-11-16T20:47:53.213

Reputation: 13 815

0

Python 2, 126 124 117 bytes

while 1:l=raw_input();i=l.find(' ');v=5-i*(l[:i]in'###');exec"print[l,''.join(c*v for c in l[i+1:])][v<5];"*(v>4or v)

Try it online!

or

while 1:l=raw_input();i=l.find(' ');t=''<l[:i]in'###';exec"print[l,''.join(c*(5-i)for c in l[i+1:])][t];"*(t<1or 5-i)

Try it online!

ovs

Posted 2017-11-16T20:47:53.213

Reputation: 21 408

0

C# 4.5 158 Bytes

Where i is the input in the form of a string.

int l,m,t,s=0;while(i[s]=='#'){s++;};t=s>0?4-s+1:1;for(l=0;l<t;l++){foreach(char c in i.Skip(s>0?s+1:0))for(m=0;m<t;m++)Console.Write(c);Console.WriteLine();}

supermeerkat

Posted 2017-11-16T20:47:53.213

Reputation: 113