15
\$\begingroup\$

Challenge:

Some ascii-art is a pain to make, but makes code comments easier to read, especially when the code is dense. The challenge is to make a simple tool that converts comments to simple ascii-art with arrows. The comments to modify are delimited by empty comments.

For example, supposing Haskell comment syntax, convert this:

--
-- Here's a thing
-- Here's another thing
-- The most important thing
-- *    *     *
--
f x=x+1*x*1*1*0

To this:

-- /------------< Here's a thing
-- |    /-------< Here's another thing
-- |    |     /-< The most important thing
-- |    |     |
-- v    v     v
f x=x+1*x*1*1*0

Rules:

  • Your answer may be either a function or full program
  • You may pick the language this is made to work with, replacing the "--" with two or more characters that delimit a comment in some language
  • If using another comment format that requires beginning and ending delimiters, each line of the reformatted sections must be a proper comment
  • The sections to be reformatted are delimited by the empty comment "\n--\n"
  • Besides adding newlines, the program must not change any of the input except the delimited sections
  • A comment filled with an arbitrary number of spaces may come immediately before a properly formatted section of output
  • Standard loopholes are disallowed

Additional Examples:

(input)
--
--
(output)
nothing


(input)
[Code Here]
--
-- important
--    *
--
(output)
[Code Here]
--    /-< important
--    |
--    v


(input)
--
-- Do
-- Re
-- Mi
-- Fa
-- So
-- *****
--
(output)
-- /-----< Do
-- |/----< Re
-- ||/---< Mi
-- |||/--< Fa
-- ||||/-< So
-- |||||
-- vvvvv

Scoring:

  • Fewest bytes wins
  • Submissions without explanations or non-trivial example input/output will not be considered (though I will leave a grace period to allow time to add such)
\$\endgroup\$
8
  • 2
    \$\begingroup\$ What if only one character is needed to delimit a comment? \$\endgroup\$
    – Adám
    Jan 14, 2016 at 22:19
  • \$\begingroup\$ As long as it's a valid comment in the language, it's fine \$\endgroup\$ Jan 14, 2016 at 22:21
  • \$\begingroup\$ We can assume that each comment section which is subject to reformatting will contain exactly one line of position marker asterisks, right? Will that line be always the last one? \$\endgroup\$
    – manatwork
    Jan 15, 2016 at 11:09
  • \$\begingroup\$ Yup, exactly one and always last (comment before end delimiter) \$\endgroup\$ Jan 15, 2016 at 12:27
  • \$\begingroup\$ And the amount of asterisks will be equal with the number of preceding lines in that section, right? \$\endgroup\$
    – manatwork
    Jan 15, 2016 at 12:43

4 Answers 4

5
\$\begingroup\$

JavaScript(ES6), 418, 237, 233, 236 bytes

f=(s)=>(d='\n//',s.split(d+'\n').map((x,y)=>y%2?'//'+(l=x.slice(2).split(d),t=l.pop().split('*'),l.map((i,j)=>t.map((k,m)=>m==j?k+'/':m<j?k+'|':k.replace(/ /g,'-')+'-').join('')+'<'+i).join(d)+d+t.join('|')+d+t.join('v')):x).join('\n'))

Whew, this is my first submission on CG. Took, I think, a totally different tack from Washington Guedes. Ended up 54 bytes shorter than his first pass. Minifying all this by hand was grueling. My one regret is not being able to eliminate the while loop yet, which would also let me cut the return.

Total rewrite, taking partial inspiration from a couple of the other answers. I got to close the whole thing in maps, making returning much better. The code snippet contains the commented version.

Took a few more bytes off, and made the example operate on itself. (You're gonna need a bigger monitor.) :)

Forgot an entire letter in the spec! Fortunately, adding the leading '<' was a tiny, trivial fix.

var comments = `
//
// New function, declare the comment delimiter
// Chop the string by the fancy comment delimiter
// Iterate over the array.
// Because of how it's delimited, and how JS splits strings, only the even elements will contain fancy comments.
// Add the original comment delimiter to the front of the first line, because join won't.
// we've got a single fancy comment, split it into lines.
// Take the last line, our asterisk template, and split it on the asterisks themselves. We were only gonna replace them anyway.
// Now we get to the real work. Iterate the remaining lines.
// For each chunk of whitespace in the template array, toss a different character on the end based on where we are in the series of lines.
// if we're at the matching asterisk, insert a slash
// Otherwise, if we're at a prior asterisk, add a pipe.
// Finally, replace anything afer the matching asterisk with dashes, join the template, and add it to the line text.
// Join the lines with the delimiter
// Add the pipe line...
// And the v line...
// For odd chunks, just return the chunk.
// Join all the code chunks and autoreturn.
//      *        *               *          *   *     *                     *                    *            *            *          *         *                                        *           *             *          *  *  
//
`
f=(s)=>(d='\n//',s.split(d+'\n').map((x,y)=>y%2?'//'+(l=x.slice(2).split(d),t=l.pop().split('*'),l.map((i,j)=>t.map((k,m)=>m==j?k+'/':m<j?k+'|':k.replace(/ /g,'-')+'-').join('')+'<'+i).join(d)+d+t.join('|')+d+t.join('v')):x).join('\n'))

str = `
//
// This is a test
// with two lines
// actually three
// *  *       *
//
var myCode = function () {

}
// This doesn't cause any trouble.

moreCode(stuff--)

//
// another test?
//  *
//

`
document.getElementById('before').textContent = str;
document.getElementById('after').textContent = f(str);
document.getElementById('self').textContent = f(comments)+'f='+f.toString();
Test cases before:
<pre id="before" style="background:#eef"></pre>
Test cases after:
<pre id="after" style="background:#eef"></pre>
Self-annotated:
<pre id="self"></pre>

\$\endgroup\$
4
\$\begingroup\$

Ruby, 160 characters

->c{c.gsub(/^--$(.+?)^--$/m){*t,a=$&.lines[1..-2]
a&&a.chop!&&(t.map{|l|a[?*]=?/
l[0,2]=a.gsub(/(?<=\/).*/){?-*$&.size}+'-<'
a[?/]=?|
l}<<a+$/+a.tr(?|,?v))*''}}

Sample run:

2.1.5 :001 > puts ->c{c.gsub(/^--$(.+?)^--$/m){*t,a=$&.lines[1..-2];a&&a.chop!&&(t.map{|l|a[?*]=?/;l[0,2]=a.gsub(/(?<=\/).*/){?-*$&.size}+'-<';a[?/]=?|;l}<<a+$/+a.tr(?|,?v))*''}}["
2.1.5 :002"> --
2.1.5 :003"> -- Here's a thing
2.1.5 :004"> -- Here's another thing
2.1.5 :005"> -- The most important thing
2.1.5 :006"> -- *    *     *
2.1.5 :007"> --
2.1.5 :008"> f x=x+1*x*1*1*0
2.1.5 :009"> "]

-- /------------< Here's a thing
-- |    /-------< Here's another thing
-- |    |     /-< The most important thing
-- |    |     |
-- v    v     v
f x=x+1*x*1*1*0
 => nil 

Brief description:

.lines splits the section to array items ─────────╮
                                                  ▽

.gsub extracts ⎧   --                             0         
these sections ⎪   -- Here's a thing              1   t[0]   
for processing ⎨   -- Here's another thing        2   t[1]   
and replaces   ⎪   -- The most important thing    ⋮   t[2]   
them with the  ⎪   -- *    *     *               -2   a      
pretty version ⎩   --                            -1          
rest untouched —   f x=x+1*x*1*1*0
                                                      △
only the needed lines get into variables ─────────────╯



a = "-- *    *     *" + "-<"           inside .gsub's block
        ↓↓                             the first 2 characters
t[0] = "-- Here's a thing"             of t's each item are
t[1] = "-- Here's another thing"       replaced with a's value
t[2] = "-- The most important thing"   and the the separator



not only t's items are transformed inside .gsub's block,
but a's value also gets changed in multiple small steps

                       change a's value    change the value    change a's value
   a's initial value   before insertion   being inserted now   after insertion
   ╭───────────────╮   ╭───────────────╮   ╭───────────────╮   ╭───────────────╮

0  "-- *    *     *" → "-- /    *     *" → "-- /-----------" → "-- |    *     *"
1  "-- |    *     *" → "-- |    /     *" → "-- |    /------" → "-- |    |     *"
2  "-- |    |     *" → "-- |    |     /" → "-- |    |     /" → "-- |    |     |"

                       ╰───────────────╯   ╰───────────────╯   ╰───────────────╯
                      change first * to /  change everything  change first / to |
                                          after / with string
                                          of - of same length
\$\endgroup\$
3
\$\begingroup\$

Python 2, 299 bytes

Expects a trailing newline in the input

i=input().split('--\n')
a=0
for j in i:
 a+=1
 if a%2:print j,;continue
 if''==j:continue
 l=j.split('\n');n=l[-2];r=l[:-2];R=[n.replace('*','v'),n.replace('*','|')];L=R[1]
 for x in range(len(l)-2)[::-1]:L=L[:L.rfind('|')]+'/';R+=[L.ljust(n.rfind('*')+2,'-')+'< '+r[x][3:]]
 print'\n'.join(R[::-1])

Explanation/Example

Input:

[Code Here]
--
-- important
--    *
--

Splits the input by --\n. Every second string is a delimited comment block.

['[Code Here]\n',
'-- important\n-- stuff\n--    *  *\n',
'']

Runs through each string. If the string is not a comment, then just prints the string. Otherwise:

Splits each line in the comment block.

['-- important', '-- stuff', '--    *  *', '']

Makes the bottom two lines by replacing the lines of *s with v and |.

['--    v  v', '--    |  |']

For each line of comments(backwards) remove rightmost column, add /, pad with - and add comment.

'--    |  /'
'--    /'
'--    /----< important'

Print Everything

--    /----< important
--    |  /-< stuff
--    |  |
--    v  v

Less golfed:

i=input().split('--\n')
a=0
for j in i:
 a+=1
 if a%2:print j,;continue # Not commment
 if''==j:continue # Empty comment
 l=j.split('\n') # Split comment into lines
 r=l[:-2]
 # Replace line of *s with v and | respectively
 R=[l[-2].replace('*','v'),l[-2].replace('*','|')]
 L=R[1][3:] # line of |
 for x in range(len(l)-2)[::-1]: # For each comment line
  L=L[:L.rfind('|')]+'/' #Remove rightmost column
  # Add a line with '-- ',columns, and comment
  R+=['-- '+L.ljust(n.rfind('*')-1,'-')+'< '+r[x][3:]]
 print'\n'.join(R[::-1]) #Print all comment lines
\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES6), 253

As an anonymous function, with the code to format as a string parameter and returning the formatted code.

Notes

  1. The pair of marker comments must enclose the right text (comment lines, then stars)
  2. ... or the pair must enclose nothing (additional example 1)
t=>(t=t.split`
`,t.map((r,i)=>r=='--'?(c++&&l.map((r,j)=>(p+=q[j],z+=~q[j].length,t[i-n+j]=p+`/${'-'.repeat(z+1)}<`+r.slice(3),p+=`|`),q=l.pop(c=p=``)||p,z=q.length,q=q.split`*`,t[i]=p+q.join`v`,t[i-1]=p+q.join`|`),l=[]):n=l.push(r),c=0,l=[]),t.join`
`)

Less golfed

f=t=>{
  t = t.split`\n`; // string to array of lines
  l = []; // special coment text
  c = 0; // counter of marker comment '--'
  t.forEach((r,i)=>{ // for each line of t - r: current line, i: index
    if (r == '--') // if marker comment
    {
       ++ c; // increment marker counter
       if (c > 1) // this is a closing marker
       {
          c = 0; // reset marker counter
          if (n > 0) // n is the length of array l
             q = l.pop(); // get last line from l, have to be the star line
          else
             q = ''; // no text comment, no star line 
          p = '';  // prefix for drawing the tree
          z = q.length; // length of star line, used to draw the tree horiz lines
          q = q.split('*'); // split to get star count and position
          // each element in q is the spaces between stars
          // modifiy the current and previous text line 
          t[i] = p + q.join`v`; // current row was '--', becomes the V line
          t[i-1] = p + q.join`|`; // previous row was the star line, becomes the last tree line
          l.forEach((r,j)=>{ // for each line in l, r: current line, j: index
             // each line in tree is: prefix("-- |  |"...) + ... "---< " + text
             p = p + q[j]; // adjust prefix
             z = z - q[j].length - 1 // adjust length of '---'
             // modify text in t
             t[i-n+j] = p // prefix
                + '/' + '-'.repeat(z+1) + '<'  // horiz line and <
                + r.slice(3); // text, removed '-- '
             p = p + '|'; // add vertical bar to prefix
          });
       } // end if closing comment
       l = []; // reset l
    }  
    else // not a special comment marker
       n = l.push(r) // add current line to l, set n to array size
  });
  return t.join`\n` // join to a single string
}

Test

f=t=>(
  t=t.split`\n`,
  t.map((r,i)=>
    r=='--'
    ?(
      c++&&l.map((r,j)=>(
        p+=q[j],z+=~q[j].length,t[i-n+j]=p+`/${'-'.repeat(z+1)}<`+r.slice(3),p+=`|`),
        q=l.pop(c=p=``)||p,z=q.length,q=q.split`*`,t[i]=p+q.join`v`,t[i-1]=p+q.join`|`
      ),l=[]
    ):n=l.push(r)
    ,c=0,l=[]
  ),
  t.join`\n` 
)
        
function update(){O.textContent=f(C.value)}

update()
textarea, pre { width:50%; height: 200px }
Input<br>
<textarea id=C>
code line 1
--
-- Do
-- Re
-- Mi
-- Fa
-- So
-- *****
--
code to explain
--
--
code line n
code line n+1
--
-- important
--    *
--

</textarea><br>
Output <button onclick="update()">Update</button><br>
<pre id=O></pre>

\$\endgroup\$
2
  • \$\begingroup\$ This misses the second comment block for me on chrome 47 without erroring. Also, crap, I didn't see before that you could use any comment syntax with any language. \$\endgroup\$
    – Emmett R.
    Jan 15, 2016 at 14:54
  • \$\begingroup\$ Uh, yes, you are right. @EmmettR. thanks. I'll try to fix it \$\endgroup\$
    – edc65
    Jan 15, 2016 at 15:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.