g | x | w | all
Bytes Lang Time Link
023Uiua240307T154345Znoodle p
084JavaScript Node.js231008T003938Znoodle p
126Swift 5.9240308T132609ZmacOSist
026250406T141211Znoodle p
013Vyxal 3250218T194448Zpacman25
031Perl 5 + lF\| M5.10.0240309T152258ZDom Hast
050Perl 5 pl240308T220345ZXcali
043GolfScript231011T034444Znoodle p
074Ruby231008T211935Znoodle p
136Java 11160909T073150ZKevin Cr
096Haskell231010T111316ZLaikoni
079JavaScript Node.js231008T012743Zl4m2
925Vyxal j231008T130631Zlyxal
016Japt R231008T123934Znoodle p
017Japt R230823T210555ZShaggy
209Go230822T160933Zbigyihsu
025Thunno 2 N230822T143303ZThe Thon
028APL Dyalog Unicode160909T124900ZAdá
016Stax210609T163237ZRazetime
103AWK210607T195254Zcnamejj
099Python 3.8 prerelease210608T174514Zkur0dama
108Python 3210607T184046ZKinuTheD
02905AB1E180605T095432ZKevin Cr
010Jelly180605T130936ZErik the
089Funky171113T230525ZATaco
085GolfScript160910T155339ZFedeWar
035Turtlèd160921T031908ZDestruct
032CJam160913T163211ZNeorej
063Perl160912T091310ZTon Hosp
154C#160910T195016Zpinkfloy
094Floroid160910T140208ZYytsi
099Python 2160909T100830ZElPedro
041Jelly160911T022541ZJonathan
132Python 2160909T060519ZJonathan
038Vim160909T221944ZJordan
030Pyth160910T001307ZTheBikin
086GNU sed 85 + 1 for r160909T153541ZRiley
023Pyth160910T024413ZMaltysen
080Ruby160909T071207ZJordan
031MATL160909T121732ZLuis Men
099PowerShell v2+160909T134230ZAdmBorkB
034Retina160909T083702ZMartin E
111Groovy160909T082253Znorganos
024Pyke160909T124546ZBlue
123Scala160909T112434ZAmazingD
109PHP160909T090720ZTitus
135Haskell160909T081851Zsudee
092JavaScript ES6160909T083923ZNeil

Uiua, 42 36 33 26 23 bytes

≡⊂≡↻₁⬚@ \◇(⌝↘⧻)°/$"_|_"

Try it: Uiua pad

Revisiting one of my first Uiua solutions over a year later, I was able to shave off quite a few more bytes, in part thanks to the addition of ⌝↘ (anti-drop) to prepend a given number of spaces to a string, as well as changes made to filled \ (scan).

JavaScript (Node.js), 84 79 84 bytes

c=>s=>s.split`|`.map(l=>z="".padEnd(z.length)+l,z="").map(l=>l.padEnd(z.length+1)+c)

Attempt This Online!

After a year and a half I realized this was bugged all along :) fixed!

Swift 5.9, 153 152 126 bytes

{var x={{String.init}()(" ",$0)},i=0
for c in($0+"").split{$0=="|"}{print(x(i)+c+(i+=c.count,x($0.count{$0 != "|"}-i)).1,$1)}}

Try it on SwiftFiddle!

This is an almost direct port of Kevin Cruijssen's Java answer.

☾, 30 26 chars

󷺹󷹌␛|󰈳␛|Ϝ󷺹🃌⋅𝘀+󷺽→⍉􋋎⍉ᴍ⨁󱖔􍀉𝘀𝗻⨝󷺽

try it

30 → 26 with help from Ganer

explanation

Vyxal 3, 13 bytes

'|÷:σ«ʈ»␣+$+”

Vyxal It Online!

basically a port of Vyxal 2, but really wants the "append string with space before" because that saves 3 bytes

Perl 5 + -lF\| -M5.10.0, 31 bytes

A different approach to both the existing Perl answers.

$,=v12;say@F," .[A@{[<>]}.."x@F

Try it online!

Explanation

Sets $, (which is the record separator, automatically printed between each item of a list) to v12 (which is chr(12), or form feed - which moves the cursor down a line), then we say @F (which is the input, split on |), followed by a raw string consisting of space, the ANSI escape sequence for "up one line", the comment char from STDIN (<>), which is embedded to the string using @{[...]}, and two backspace characters, which is repeated the same number of times as there are items in the input (using the list of input, @F, in a scalar context yield the list length).

Perl 5 -pl, 50 bytes

$f=<>;s%\||$%$"x$'=~y/|//c." $f
".$"x$`=~y/|//c%ge

Try it online!

GolfScript, 44 43 bytes

~:~;"|"/[""\{\," "*\+.}/,):l;]{l" "*+l<~n}/

Try it online!

This was my first GolfScript submission, and I’ve improved quite a lot since I first tried this task.

Explanation:

~:~;

Eval STDIN as GolfScript. Assign the comment character to the ~ variable, and pop out of the stack, so the code is on top.

Using a symbol instead of a letter as a command name let me remove a whitespace later on.

"|"/

Split the code on |s.

[""\{\," "*\+.}/

This is equivalent to a scan (reduce, keeping in-between values) by repeating spaces to the length of the previous value, and prepending that to the current value.

GolfScript doesn’t have scan built-in, so this is implemented somewhat cumbersomely, using / (each) and duplicating the result each iteration.

,):l;]

The previous step left an extra copy of the last value. We take its length, increment that, and assign it to l.

{l" "*+l<~n}/

For each line ({ … }/): append l spaces (l" "*+), take the first l characters (l<), then push ~ (the comment character) and n (newline).

The entire stack is printed when the program ends.

Original solution:

GolfScript, 64 63 bytes

~n+:c;0:a;."|"/""*,):b;{.124={;b" "*c" "a*}{a):a;b(:b;}if}%" "c

Try it online!

Full program, takes input like "abc|def|g" "#" on STDIN.

I’ve always thought GolfScript seemed interesting but have never actually tried it. After using quite a few more modern golfing languages with hundreds of built-ins, the terse but very readable syntax of GolfScript is really refreshing. I had a lot of fun trying to get this to work, so I’m glad I could beat the 75 byte solution posted in 2017.

I had a 50-or so byte version that used stack manipulation instead of variables, but couldn’t get it to work.

I hope you like the smileys ): (: :b :c ={

Explanation

Eval the input; this leaves two strings on the stack. Append a newline to the top one, store it in c as the comment character, and pop it off the stack. The variable a is initialized to 0. The variable b is initialized to the length of splitting the input on | and joining it back.

The top of the stack is now the |-separated string. Map each character of the input string to:

Finally, push a space and c to the stack. The whole stack is implicitly output at the end.

Ruby, 80 74 bytes

->i,c{a=0;(i=i.split ?|).map{" "*a+_1+" "*(1+i.join.size-(a+=_1.size))+c}}

Attempt This Online!

I managed to golf a non-regex based solution to beat the other Ruby. The solution previously tied but I found a slightly different method that is 6 bytes shorter.

Explanation:

->i,c{…} Proc (lambda function) taking arguments i (code) and c (comment)

Java 11, 189 159 136 bytes

s->c->{int p=0;for(var a:s.split("\\|"))System.out.println(" ".repeat(p)+a+" ".repeat(s.replace("|","").length()-(p+=a.length())+1)+c);}

-30 bytes converting Java 7 to Java 10 and optimizing the loops.
-23 bytes converting Java 10 to Java 11, so String#repeat builtin can be used.

Try it online.

Explanation:

s->c->{                     // Method with String & char parameters and no return-type
  int p=0;                  //  Position-integer, starting at 0
  for(var a:s.split("\\|")) //  Loop over the parts split by "|":
    System.out.println(     //   Print with trailing newline:
      " ".repeat(p)         //    `p` amount of spaces
      +a                    //    plus the current part
      " ".repeat(s.replace("|","").length()-(p+=a.length())+1)
                            //    plus the correct trailing amount of spaces
                            //    (and add the length of the current part to `p` at the
                            //    same time)
      +c);}                 //    plus the current comment character

Haskell, 96 bytes

f"".lines.map r
r '|'='\n'
r c=c
w=(' '<$)
f l(a:s)c=(w l++a++(w=<<c:s)++c):f(l++a)s c
f l s c=s

Attempt This Online!

Eplanation

The function r is defined to map pipe characters to newlines (r '|'='\n') and every other char to itself (r c=c). lines.map r thus first replaces all pipes in the input string with new lines (map r), and then splits the string at the newlines with the build-in lines function. The main function f iterates over this list of strings and constructs the output line by line with the expression w l++a++(w=<<c:s)++c where

JavaScript (Node.js), 79 bytes

c=>s=>s.split`|`.map((_,i,a)=>a.map(y=>i--?y.replace(/./g,' '):y).join``+' '+c)

Attempt This Online!

+4 to add an extra space before #

Vyxal j, 74 bitsv2, 9.25 bytes

\|/:¦↳:t↲Ḋ

Try it Online!

Bitstring:

01101010100010101110000101000000110001011111111110001111111100110011110001

Explained

\|/:¦↳:t↲Ḋ­⁡​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁢⁢‏‏​⁡⁠⁡‌⁤​‎‎⁡⁠⁢⁣‏⁠‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁣⁢‏‏​⁡⁠⁡‌⁢⁢​‎‏​⁢⁠⁢‌­
\|/         # ‎⁡Split on "|"s
   :¦       # ‎⁢Push the cumulative sums without popping. This acts as the space length before each section
     ↳      # ‎⁣Pad each item right with spaces to length in ^
      :t↲   # ‎⁤And pad each item left with spaces to length of tail of ^
         Ḋ  # ‎⁢⁡Append " {comment char}" to each
‎⁢⁢Implicitly join on newlines
💎

Created with the help of Luminespire and not this challenge.

Japt -R, 16 bytes

q| åÏiXîÃú m+S+V

Try it

The boilerplate is similar to @Shaggy's 17 byte Japt submission.

Explanation:

q|                 split on |
   åÏ              cumulatively reduce each X, Y to:
     i               prepend to Y
      Xî             spaces to the length of X
        Ã          end cumulative reduce
         ú         right-pad each to the longest line
           m+      to each, append
             S+V     space (S) and comment char (V)

flag -R            join the final result on newlines

Japt -R, 17 bytes

There's a byte to be saved here, at least, I just can't quite see it.

q| ËùT±DÊÃú Ë+ViS

Try it

q| ËùT±DÊÃú Ë+ViS     :Implicit input of strings U=programme & V=character
q|                    :Split U on |
   Ë                  :Map each D
    ù                 :  Left pad with spaces to length
     T±               :    Increment T (initially 0) by
       DÊ             :    Length of D
         Ã            :End map
          ú           :Right pad each with spaces to length of longest
            Ë         :Map
             +        :  Append
              Vi      :    V prepended with
                S     :      Space
                      :Implicit output joined with newlines

Go, 209 bytes

import."strings"
func f(C,c string)string{L,Q,S:=Split(C,"|"),[]string{},0
for _,l:=range L{q:=len(l)
if q<1{continue}
Q=append(Q,Repeat(" ",S)+l+Repeat(" ",len(Join(L,""))-q+1-S)+c)
S+=q}
return Join(Q,"\n")}

Attempt This Online!

Explanation

import."strings"
func f(C,c string)string{
// split C into substrings, and init the output slice and space count
L,Q,S:=Split(C,"|"),[]string{},0
// for each substring...
for _,l:=range L{
// if it's empty, skip it
q:=len(l)
if q<1{continue}
// add to the output slice...
Q=append(Q,
    // initial spaces
    Repeat(" ",S)+
    // the substring
    l+
    // the following spaces 
    Repeat(" ",len(Join(L,""))-q+1-S)
    // and the comment    
    +c)
// increment the space count
S+=q}
// join together with newlines
return Join(Q,"\n")}

Thunno 2 N, 25 bytes

'|/DJl⁺X0Yıyṣs+xðĿnlẏ+ƥ¹+

Try it online!

Explanation

(Yes, this is in the exact same format as the outputs for this challenge :P)

'|/DJl⁺X0Yıyṣs+xðĿnlẏ+ƥ¹+  '# Implicit input
'|/                        '# Split the input on "|"
   DJl                      # Duplicate, join, and get length
      ⁺X                    # Increment this and store it in x
        0Y                  # Store 0 in y
          ı                 # Map over the list:
           yṣ               #  Push y spaces
             s+             #  Prepend this to the input
               xðĿ          #  Pad with spaces to length x
                  nlẏ+ƥ     #  Add the length to y
                       ¹+   #  Append the second input
                            # Implicit output, joined on newlines

APL (Dyalog Unicode), 28 bytes

Full program. Prompts for code, then for comment character.

⍞,⍤1⍨⎕FMT⎕TC[2]@('|'∘=)⍞,' '

Try it online!

⍞,' ' append a space the to prompted-for code

⎕TC[2]@() place the 2nd Terminal Control character (Line Feed) at the positions indicated by:

'|'∘= the mask indicating where the pipe symbol is equal to the characters

⎕FMT ForMaT as a character matrix, evaluating terminal control characters (Line Feed moves the "cursor" down one line)

⍞,⍤1⍨ prompt for comment character and append it to each line (lit. to each 1D sub-array)

Stax, 16 bytes

ù3l╛CrΣ╝▀↕┘╣óétº

Run and debug it

AWK, 107 104 103 bytes

w=2-(n=split($1,p,"|"))+gsub(t=".",FS,$1){for(;s++<n;gsub(t,FS,b))y=y substr((b=b p[s])$1,1,w)$2RS}$0=y

Try it online!

Thanks to Pedro Maimere for helping shave off more 3 chars

One interesting thing about this challenge... I found a quirk about the TIO AWK setup. I can't figure out way to specify input where a positional argument can contain a space. Meaning setup a TIO test that behaves like this commandline,

echo "this is the first parameter" second | gawk '{ print $1 }'

In TIO, the first parameter will be "this not the whole string. That means while it works in a terminal window, I can't show that it handle the second test for this challenge in TIO.

Here's how the code works... The test associated with the codeblock is always truthy, so the code always runs. It's only a test (rather then code) to avoid needing a ; character. It does a couple of things by using AWK's willingness to combine multiple expressions into one "line" of code.

w=2-(n=split($1,p,"|"))+gsub(t=".",FS,$1)

First is splits the first parameter into in array of strings using the pipe character as the delimeter with n=split($1,p,"|"). It also saves the number of substrings generated in n. Then it changes all the characters in the first parameter to spaces with gsub(t=".",FS,$1) setting convenience constants t and n in the process.

Finally the value of w is set to the number of non-pipe characters in the first parameter by subtracting the number of sub-strings from the total number of characters in the original.

The associated code block is one statement, a for loop, that processes each substring.

for(;s++<n;gsub(t,FS,b))y=y substr((b=b p[s])$1,1,w)$2RS

The iteration check is simple s++<n. The end of loop statement converts all the characters in the previous output line to blanks gsub(t,FS,b), which turns it into the prefix for the next line. The body of the loop constructs and appends the next line of output to an accumulator y. That string is a concatenation of the previous string, plus the current string (code fragment) with a bunch of blanks appended (meaning $1) (b=b p[s])$1. That mess is truncated to the length we want w and the comment delimiter and LF are appended with y=y substr(...,w)$2RS.

Once all the code substrings have been processes, assigning $0 to the accumulator takes care if printing out the results.

$0=y

Python 3.8 (pre-release), 99 bytes

Different approach:

def f(c,d,i=0):
 for y in(x:=c.split('|')):z=[' '*len(e)for e in x];z[i]=y;print(''.join(z),d);i+=1

Try it online!

Ungolfed with explanation:

def f(c,d,i=0):
 x=c.split('|')              # break string by '|'
 for y in x:                 # for each element of list
  z=[' '*len(e) for e in x]  # create a list of blank strings
  z[i]=y                     # insert element we care about into otherwise blank list
  print(''.join(z),d)        # print line
  i+=1                       # increment

Python 3, 108 bytes

s,c=input().split("|"),input()
i,S=0,len("".join(s))
for l in s:L=len(l);print(" "*i+l+" "*(S-L-i+1)+c);i+=L

Try it online! Simple solution -- keep track of how many spaces we have (i) and accumulate.

05AB1E, 29 38 31 29 bytes

'|„ǝʒ:'ǝ¡'ʒмεD®>úsg®+©s}.Bεð²J,

Can definitely be golfed, but at least its working now..
+9 bytes because ¡ (split) removes empty items automatically, so I had to add '|„ǝʒ:'ǝ¡'ʒм..
-2 bytes thanks to @MagicOctopusUrn by changing '|„ǝʒ:'ǝ¡'ʒм to '|¶:.BεðÜ} (current solution doesn't work on items with trailing spaces, but I've assumed that's allowed according to the test cases).

Try it online.

Explanation:

'|¶:           # Take the first input, and replace every "|" with "¶"
               #  i.e. "abc|d|e||fg" → "abc¶d¶e¶¶fg" (¶ are new-lines in 05AB1E)
    .B         # Box all the items (appending trailing whitespace to make it a rectangle)
               #  i.e. "abc¶d¶e¶¶fg" → ['abc','d  ','e  ','   ','fg ']
      εðÜ}     # Remove all trailing spaces from each item
               #  i.e. ['abc','d  ','e  ','   ','fg '] → ['abc','d,'e','','fg']
               #  NOTE: `'|¡` would have resulted in ['abc','d','e','fd'], hence the use of
               #        Box which implicitly splits on new-lines to keep empty items
ε              # For-each:
 D             #  Duplicate the current item
  ®>ú          #  Prepend global_variable + 1 amount of spaces
               #  (+1 because the global_variable is -1 by default)
               #   i.e. "e" and 3+1 → "    e"
 sg            #  Swap so the duplicated item is at the top, and take its length
   ®+          #  Sum it with the global_variable
               #   i.e. "e" (→ 1) and 4 → 5
     ©         #  And store it as new global_variable
      s        #  Then swap so the space appended item is at the end again
       }       # And end the for-each loop
.B             # Box all the items (appending the appropriate amount of spaces)
               #  i.e. ['abc','   d','    e','     ','     fg']
               #   → ['abc    ','   d   ','    e  ','       ','     fg']
ε              # For-each again:
 ð             #  A space character
  I            #  The second input-character
   J           #  Join both together with the current item
    ,          #  And print the current row with trailing new-line

Jelly, 10 bytes

ṣ”|⁶ṁ;ɗ\pG

Try it online!

Funky, 89 bytes

s=>c=>{y=0forn inl=s::split"|"print((v=" "::rep)(y)+l[n]+v((#l::reduce@++1)-y+=#l[n])+c)}

Try it online!

GolfScript, 85 bytes

{(;);}:r;", "%(r\(r n+:c;;.,\'|'%.,@\-)):l;0:m;{.,0>}{" "m*\(.,m+:m l\-" "\*+c@}while

Try it online

2017 Update - GolfScript - 71 bytes

', '/~~:c;~1/.,\.{'|'=},,@\-):l;['|']/0:i;{.,i+:i l i-' '*c+\' '*"
"\}/

Explanation

', '/~~:c;~1/        # Parses input
.,\.{'|'=},,@\-):l;  # Computes string length without '|'
['|']/               # Splits the array
0:i;                 # Counter
{.,                  # Length of the substring
i+:i                 # Counter update
l i-' '*c+\          # Adds spaces after the substring 
' '*"\n"\            # Adds spaces before the next substring
}/                   # ...For each substring

Turtlèd, 35 bytes (noncompeting)

Takes one input, the last character is the comment character. Does not work with comment character as space, but I assume that isn't necessary.

!' [*.+(|' dl)r_]' r[*+.(|u)_][ .d]

Explanation:

!                                  take input into string variable
 '                                 write space over current cell
   [*           ]                  while cell is not *
     .+                            write pointed char of string, stringpointer+1 (starts 0)
       (|    )                     if current cell is |
         ' dl                      write space (over |), move down, left
              r_                   move right, write * if pointed char is
                                   last char, else space

                 ' r               write space, move right
                    [*       ]     while cell is not *
                      +.           increment pointer and write pointed char
                        (|u)       if cell is |, move up
                            _      write * if the pointed char is the last char

                              [   ] while cell is not space
                                .d  write the pointed char from string, move down 

CJam, 32 bytes

l'|/_s,)L@{1$,S*\+}%@f{Se]}lN+f+

Explanation

l                                  get code string
 '|/                               split into code parts
    _s,)                           length of all the parts +1
        L@{1$,S*\+}%               left pad spaces to every part for the length of the previous parts
                    @f{Se]}        right pad spaces
                           lN+f+   add comment character and newline

Try it online

Perl, 63 bytes

Includes +5 for -Xpi

Run with input on STDIN and comment character after -i:

perl -Xpi% esolang.pl <<< "Ab|Cd||ef"

esolang.pl:

s/
/|/;s%(.*?)\|%$"x$`=~y/|//c.$1.$"x$'=~y/|//c." $^I
"%eg

Totally boring straightforward solution

C# 176 167 154 bytes

string f(string s,char x){var c=s.Split('|');var d="";int i=0;foreach(var b in c)d+=b.PadLeft(i+=b.Length).PadRight(s.Length+2-c.Length)+x+"\n";return d;}

UnGolfed

string f(string s, char x)
{
    var c = s.Split('|');
    var d = "";
    int i = 0;
    foreach (var b in c)
        d += b.PadLeft(i += b.Length).PadRight(s.Length + 2 - c.Length) + x + "\n";
    return d;
}

A LINQ solution would have been 146 but needed using System.Linq; bringing it back up to 164:

string f(string s,char x){var c=s.Split('|');int i=0;return c.Aggregate("",(g,b)=>g+b.PadLeft(i+=b.Length).PadRight(s.Length+2-c.Length)+x+"\n");}

Old solutions:

167 bytes:

string f(string s,char x){var c=s.Split('|');var d="";int i=0;foreach(var b in c){d+=b.PadLeft(i+b.Length).PadRight(s.Length+2-c.Length)+x+"\n";i+=b.Length;}return d;}

176 bytes using string interpolation

string f(string s,char x){var c=s.Split('|');var d="";int i=0;foreach(var b in c){d+=string.Format($"{{1,{i}}}{{0,-{s.Length+2-c.Length-i}}}{x}\n",b,"");i+=b.Length;}return d;}

Floroid - 94 bytes

Ah(a,b):c=a.fn("|");z(" "+b+"\n".y(' '*Z("".y(c[:j]))+l+" "*Z("".y(c[j+1:]))Kj,lIai(c))+' '+b)

Uses an approach similar to @JonathanAllans' Python solution.

Testcases

Call: h("a|bc|d|e|fgh|ij|k|l|mn|op", "#")
Output: 
a                #
 bc              #
   d             #
    e            #
     fgh         #
        ij       #
          k      #
           l     #
            mn   #
              op #

Python 2, 107 105 102 99 bytes

Tested with all test cases above

EDIT Golfed off 2 bytes by changing d=a.split("|");i=0 to d,i=a.split("|"),0 Not sure how I missed that one. Thanks @Oliver Ni

Another 3 bytes gone. Thanks again.

Suggestion from @Jonathan actually saves 3 bytes and takes it down to the magic 99. Thanks.

def c(a,b):
 d,i=a.split("|"),0
 for e in d:j=i+len(e);print" "*i+e+" "*(len("".join(d))-j+1)+b;i=j

Jelly, 41 bytes

This looks like it has far to many increments, and probably too many links...

ṫø⁹‘‘µFL‘⁶ẋ
‘ị
ḣFL⁶ẋ$;ç@;1ŀ
J’ç@€
ṣ”|Ç;€Y

Test it at TryItOnline

How?

ṫø⁹‘‘µFL‘⁶ẋ  - link 1: get the spaces for after the code, dyadic(split string, index)
 ø           - next chain as a nilad
  ⁹‘‘        - right argument incremented twice (the index we actually want)
ṫ            - tail (get the rest of the split string)
     µ       - next chain as a monad
      FL‘    - flatten, get its length and increment
         ⁶   - a space character " "
          ẋ  - repeat the space character that many times

‘ị           - Link 2: get the code for a line dyadic(index, split string)
‘            - increment the index
 ị           - retrieve the string at that index

ḣFL⁶ẋ$;ç@;1ŀ - Link 3: get the code and join with spaces, dyadic (index, split string)
ḣ            - head: split string[index:]
 FL          - flatten and get its length
     $       - previous two atoms as a monad
   ⁶         - a space character, " "
    ẋ        - repeat the space that many times
      ;      - concatenate with
       ç@    - the result of the previous link (the code) - reverse inputs
         ;   - concatenate with
          1ŀ - the result of Link 1 (the spaces after the code)

J’ç@€        - Link 3: a for loop, monadic(split string)
J’           - [0,...,Len(split string)-1]
  ç@€        - the result of the previous link, with revered inputs, for each

ṣ”|Ç;€Y      - Main Link: dyadic(code string, comment character)
ṣ”|          - split on "|"
   Ç         - the result of the previous link
    ;€       - concatenate each with the comment character
      Y      - join with line feeds
             - implicit print

Python 2, 125 124 132 bytes

-1 byte thanks to @TuukkaX (missed golfing the space from i, v)

def g(s,c):x=s.split('|');print((' '+c+'\n').join(' '*len(''.join(x[:i]))+v+' '*len(''.join(x[i+1:]))for i,v in enumerate(x))+' '+c)

All test cases on ideone

Vim, 39 38 keystrokes

-1 byte thanks to DJMcMayhem

Expects as input a buffer (e.g. a file) whose first character is the comment delimiter, followed by the code, e.g. #foo|bar|baz.

"cxqaf|m`Yp<Ctrl+o>v$r jv0r x@aq@a$p<Ctrl+v>gg$C <Ctrl+r>c<Esc>

Explanation

("_" denotes a literal space.)

"cx          " Delete the first character (the comment delimiter) and store in register 'c'
qa           " Start recording macro 'a'
f|m`         " Advance to the first '|' on the line and set mark
Yp<Ctrl+o>   " Duplicate this line and return to mark
v$r_         " Replace everything after the cursor on this line (inclusive) with spaces
jv0r_x       " Go down a line and replace everything before the cursor on this line (inclusive) with
             "   spaces, then delete one space
@a           " Call macro recursively
q@a          " Stop recording and immediately call the macro
$p           " Paste the deleted space at the end of the last line
<Ctrl+v>gg$       " Highlight the column where the comment delimiters will go and all trailing spaces
C_<Ctrl+r>c<Esc>  " Replace the highlighted text on each line with a space and the contents of
                  "   register 'c' (the comment delimiter)

Pyth, 30 bytes

VJcE\|s[*ZdN*h--lsJZlNdQ)=+ZlN

or

jm+dQ.t.t+MC,.u*l+NYdJc+Ed\|kJ

Both are full programs that take input on STDIN of the comment string, and then the program string, newline-separated.

Try the first version online

Try the second version online

How they work

VJcE\|s[*ZdN*h--lsJZlNdQ)=+ZlN  Program. Inputs: E, Q
  cE\|                          Split E on "|"
 J                              Assign to J
                                Implicit Z=0
V                               For N in that:
       [                )        Create a list with elements:
        *Zd                       Z spaces
           N                      N
               -lsJZ              len(concatenate(J))-Z
              -     lN             -len(N)
             h                     +1
            *         d            spaces
                       Q          Q
      s                          Concatenate the list
                                 Implicitly print
                        =+ZlN    Z=Z+len(N)

jm+dQ.t.t+MC,.u*l+NYdJc+Ed\|kJ  Program. Inputs: E, Q
                       +Ed      Add a trailing space to E
                      c   \|    Split that on "|"
                     J          Assign to J
             .u                 Cumulatively reduce J with:
                            k    starting value empty string and
                                 function N, Y ->
                l+NY              len(N+Y)
               *    d             spaces
            ,                J  Two-element list of that and J
           C                    Transpose
         +M                     Map concatenation over that
       .t                       Transpose, padding with spaces
     .t                         Transpose again
 m+dQ                           Map concatenation with Q over that
j                               Join on newlines
                                Implicitly print

GNU sed (85 + 1 for -r) 86

:s;h;:;s,\|( *)[^ \|](.),|\1 \2,;t;s,\|,,g
p;g;:l;s,^( *)[^ \|],\1 ,;tl;s,\|,,;/\S/bs

The inputs are strings separated by a space.

Tests:
input.txt:

a|b|c|d|e|f|g ,
abcdefg :
4|8|15|16|23|42 %
E|ac|h s|ecti|on is| one c|haracte|r longer| than the| last! !
This|Code|has||empty||sections @

Output:

$ cat input.txt | sed -rf template
a       ,
 b      ,
  c     ,
   d    ,
    e   ,
     f  ,
      g ,

abcdefg :

4          %
 8         %
  15       %
    16     %
      23   %
        42 %

E                                                   !
 ac                                                 !
   h s                                              !
      ecti                                          !
          on is                                     !
                one c                               !
                     haracte                        !
                            r longer                !
                                     than the       !
                                              last! !

This                     @
    Code                 @
        has              @
                         @
           empty         @
                         @
                sections @

Pyth - 28 27 24 23 bytes

Might be able to golf a little off. A lot off, apparently!

jt+R+;zC.t.u+*lNdYcQ\|k

Try it online here.

Ruby, 96 80 bytes

->s,c{s.gsub(/(^|\|)([^|]*)/){" "*$`.count(t="^|")+$2+" "*(1+$'.count(t))+c+$/}}

See it on eval.in: https://eval.in/639012

I really ought to just learn Retina.

MATL, 33 31 bytes

'\|'0'|'hYXo8M&YbY:&YdtaZ)0ihYc

Try it online!

Explanation

The builtin function Yd (blkdiag), which builds a block-diagonal matrix from its inputs, does most of the work. The fill values in the matrix are 0, and char 0 is treated as a space for displaying purposes. The code would simply split on |, build a matrix from the resulting blocks, convert to char, and append two columns with space and comment symbol.

However, the possibility of empty sections in the input string complicates makes the problem more interesting: the resulting block would be empty and thus wouldn't show in the resulting matrix.

To solve this, we introduce a char 0 before each |, so no block will be empty; and then in the resulting char matrix we remove columns that are formed by char 0 only. A non-empty code section will have some printable ASCII char, and thus the columns it spans will survive. An empty section will contribute a row, but won't introduce an extra column.

'\|'    % Push this string: source for regexp matching. It's just | escaped
0'|'h   % Push a string formed by char 0 followed by | (no escaping needed)
YX      % Input string implicitly. Replace first of the above string by the second
o       % Convert from chars to code points. Gives a numeric vector
8M      % Push '|' again
&Yb     % Split numeric vector at occurences of | (the latter is automatically
        % converted  to its code point). This gives a cell array of numeric vectors
Y:      % Unbox cell array: pushes the numeric vectors it contains
&Yd     % Form a block-diagonal matrix from those vectors
ta      % Duplicate. Compute vector that equals true for columns that have some
        % nonzero value
Z)      % Used that as a logical index (mask) for the columns of the matrix.
        % This removes columns that contain only zeros
0ih     % Input comment symbol and prepend char 0 (which will be displayed as space)
Yc      % Append that to each row of the matrix. The matrix is automatically 
        % converted from code points to chars
        % Display implicitly

PowerShell v2+, 103 99 bytes

param($a,$b)$a-split'\|'|%{" "*$l+$_+" "*(($a-replace'\|').length+1-$_.length-$l)+$b;$l+=$_.Length}

Takes input as two strings, -splits the first on literal pipe (since split uses regex syntax), and feeds the elements into a loop |%{...}.

Each iteration, we construct a string as being a number of spaces defined by $l concatenated with the current element. For the first loop, $l initializes to $null, which gets evaluate here as 0.

That string is further concatenated with another number of spaces (defined by how long $a would be if we -replaced every pipe with nothing, plus 1 for the additional padding between code and comments, minus the .length of the current element, minus $l which is how many spaces we padded left on this iteration), concatenated with our comment character $b. That's left on the pipeline.

We then update $l for the next iteration.

The resultant strings are all left on the pipeline, and output via implicit Write-Output happens at program execution, with a newline between them by default.

Examples

PS C:\Tools\Scripts\golfing> .\esolang-comment-template-generator.ps1 "This|Code|has||empty||sections" "@"
This                     @
    Code                 @
        has              @
                         @
           empty         @
                         @
                sections @

PS C:\Tools\Scripts\golfing> .\esolang-comment-template-generator.ps1 "a|bc|def|ghi|h" "|"
a          |
 bc        |
   def     |
      ghi  |
         h |

Retina, 35 34 bytes

Byte count assumes ISO 8859-1 encoding.

\|
·$'¶$`±
T0-2`·±|p`___ `.+±.|·.+

The two input strings are separated by a space (which is unambiguous since we know that the comment delimiter is always a single character).

Try it online!

Groovy, 120 113 111 Bytes

def m(s,c){s.split(/\|/).inject(0,{e,t->println((' '*e+t).padRight(s.replace('|','').size()+1)+c);e+t.size()})}

ungolfed*

def m(s,c){
  s.split(/\|/).inject(0, { e, t ->
    println((' '*e+t).padRight(s.replace('|','').size())+' '+c)
    e+t.size()
  })
}

(First Draft with 120 Bytes)

def m(s,c){def l=0;s.split(/\|/).collect{l+=it.size();it.padLeft(l).padRight(s.replace('|','').size())+' '+c}.join('\n')}

ungolfed*

def m(s,c){
  def l=0 // minimized version needs a semicolon here
  s.split(/\|/).collect{
    l+=it.size() // minimized version needs a semicolon here
    it.padLeft(l).padRight(s.replace('|','').size())+' '+c
  }.join('\n')
}

Tests

%> m('a|bc|d|e|fgh|ij|k|l|mn|op', '#')
a                #
 bc              #
   d             #
    e            #
     fgh         #
        ij       #
          k      #
           l     #
            mn   #
              op #

%> m('Hello|World', '/')
Hello      /
     World /

%> m('a|b|c|d|e|f|g', ',')
a       ,
 b      ,
  c     ,
   d    ,
    e   ,
     f  ,
      g ,

%> m('abcdefg', ':')
abcdefg :

%> m('4|8|15|16|23|42', '%')
4          %
 8         %
  15       %
    16     %
      23   %
        42 %

%> m('E|ac|h s|ecti|on is| one c|haracte|r longer| than the| last!', '!')
E                                                   !
 ac                                                 !
   h s                                              !
      ecti                                          !
          on is                                     !
                one c                               !
                     haracte                        !
                            r longer                !
                                     than the       !
                                              last! !

%> m('This|Code|has||empty||sections', '@')
This                     @
    Code                 @
        has              @
                         @
           empty         @
                         @
                sections @

Pyke, 31 28 24 bytes

\|cDslF2l-hd*Q+Zi:il?+ZK

Try it here!

Scala, 123 bytes

def?(i:String,c:String)={var b=0
i.split('|').map{s=>println(" "*b+s+" "*(i.replace("|","").size-b-s.size+1)+c)
b+=s.size}}

Test code + Output:

?("a|b|c|d|e|f|g", ",")
a       ,
 b      ,
  c     ,
   d    ,
    e   ,
     f  ,
      g ,

?("abcdefg", ":")
abcdefg :

?("4|8|15|16|23|42", "%")
4          %
 8         %
  15       %
    16     %
      23   %
        42 %

?("E|ac|h s|ecti|on is| one c|haracte|r longer| than the| last!", "!")
E                                                   !
 ac                                                 !
   h s                                              !
      ecti                                          !
          on is                                     !
                one c                               !
                     haracte                        !
                            r longer                !
                                     than the       !
                                              last! !

?("This|Code|has||empty||sections", "@")
This                     @
    Code                 @
        has              @
                         @
           empty         @
                         @
                sections @

PHP, 120 117 116 110 109 bytes

foreach($a=split('\|',$argv[1])as$i=>$t){$c=preg_replace('#.#',' ',$a);$c[$i]=$t;echo join($c)," $argv[2]
";}

or

foreach($a=split('\|',$argv[1])as$t){$c=preg_replace('#.#',' ',$a);$c[$i++|0]=$t;echo join($c)," $argv[2]
";}

Haskell, 139 135 bytes

s#p=j$foldl g("",0)s where g(a,n)c|c=='|'=(j(a,n)++"\n"++q n,n)|1>0=(a++[c],n+1);q m=' '<$[1..m];j(a,n)=a++q(sum[1|c<-s,c/='|']-n+1)++p

Saved 4 bytes by inlining a definition.

Ungolfed:

template :: String -> String -> String
template code comment = format $ foldl g ("", 0) code
    where g (acc, n) c
            | c == '|' = (format (acc, n) ++ "\n" ++ spaces n, n)
            | otherwise = (acc ++ [c], n+1)
          l = length $ filter (/= '|') code
          spaces n = replicate n ' '
          format (acc, n) = acc ++ spaces (l-n+1) ++ comment

JavaScript (ES6), 92 bytes

f=
(s,c)=>s.split`|`.map((_,i,a)=>a.map((e,j)=>i-j?e.replace(/./g,` `):e).join``+` `+c).join`
`
;
<div oninput=o.textContent=f(s.value,c.value)><input id=s placeholder=Code><input id=c size=1 maxlength=1 value=#><pre id=o>