3

I want to reformat my Swift documentation from

/**
  Lorem Ipsum is simply dummy text of the printing and typesetting industry. 
  Lorem Ipsum has been the industry's standard dummy text ever since the 
  1500s, when an unknown printer took a galley of type and scrambled it to 
  make a type specimen book. It has survived not only five centuries, but 
  also the leap into electronic typesetting, remaining essentially 
  unchanged. It was popularised in the 1960s with the release of Letraset 
  sheets containing Lorem Ipsum passages, and more recently with desktop 
  publishing software like Aldus PageMaker including versions of Lorem Ipsum.
  */

to

/// Lorem Ipsum is simply dummy text of the printing and typesetting industry. 
/// Lorem Ipsum has been the industry's standard dummy text ever since the 
/// 1500s, when an unknown printer took a galley of type and scrambled it to 
/// make a type specimen book. It has survived not only five centuries, but 
/// also the leap into electronic typesetting, remaining essentially 
/// unchanged. It was popularised in the 1960s with the release of Letraset 
/// sheets containing Lorem Ipsum passages, and more recently with desktop 
/// publishing software like Aldus PageMaker including versions of Lorem Ipsum.

A regular expression find and replace edit: , using Xcode's find/replace regex engine, would be preferred because God only knows how many doc notes I have in my library of over 200 different classes. The expression to find these blocks is simple, but I do not know how to make my replace expression so that I am able to add a prefix to each line, well.

Current Search Expression: (?s:/\*\*(.*?)\*/) - matches all text in between /** */

Current Replace Expression: /// $1

Obviously, the expressions above do not achieve exactly what I am looking for. I appreciate any help, in advance! Thanks.

rolling_codes
  • 15,174
  • 22
  • 76
  • 112

3 Answers3

2

This is the nicest solution I could come up with, but it relies on some specialized PCRE anchors (most importantly \G, but I also went with \A and \K) so it may not work in your flavor of regex. This is also a two step solution, but I don't think it'd be possible to get it down to one -- would love to see someone prove me wrong here!


First, you want to match every line starting with whitespace in between /** and */ and replace the whitespace with ///.

Find:

~(?<=/[*]{2}|(?<!\A)\G)\n\K^\s*+(?![*]/)(.*)$~gm

Replace:

/// $1

Demo.


Then we want to take the result of that replacement and remove the left over lines denoting the old comment, /** and */.

Find:

~^.*(?:/[*]{2}|[*]/).*$\n?~gm

Replace:

[null]

Demo.


Most of the above is straightforward, or at least should be assuming you have a basic understanding of regex...which it appears as though you do. The obvious one standing out is that first one. Let's break it down (yay!)...

(?<=       # start a zero-length lookahead
  /[*]{2}  # look for the start of a comment
 |         # or...
  (?<!\A)  # negate this part if we're at the beginning of the string
  \G       # start at the end of the last match (or beginning of string)
)          # end that lookahead and move on to each line
\n\K       # find the newline and then reset the match for clarity
^\s*+      # match whitespace at the beginning of the line
(?![*]/)   # negate this match if we're at the end of the comment
(.*)$      # capture everything up until the end of the line

The only thing in the above that may need an extra explanation is the (?<!\A)\G) "hack" I use. \G lets us start a match at the end of the last match, which is quite necessary (for an all-encompassing solution such as this) in a repeating problem like we have here. However, \G also matches the beginning of the string which we don't want (we deal with that in the first half of the lookahead where we match the start of the comment) so we negate matching the beginning of the string with (?<!\A). Boom!

\K isn't necessary, but makes for a cleaner expression when we get this complicated. Without it, the \n is part of the match and we would need to manually replace this with \n\\\ $1 instead.

Sam
  • 20,096
  • 2
  • 45
  • 71
  • 2
    @NoodleofDeath thanks. When you love writing regex, you love writing regex. I expanded/explained the main expression in an updated. Feel free to ask questions...hopefully it works in Xcode, lol. – Sam Sep 08 '16 at 16:23
  • 1
    Trust me, I know how it is. The more I write in regex the more I become obsessed. Thanks for the detailed explanation. Wish I could up vote this more than once. – rolling_codes Sep 08 '16 at 16:24
  • Yea, I've needed to use them a lot in my job and so I often "practice" on SO. You can always award bounty ;) But, I'm content with the +25 and anything else that slowly trickles in. Enjoy! – Sam Sep 08 '16 at 16:29
1

If you have a lot of files, write a little script that does the following (do your self a favor, backup your data):

  1. Find the comments with the regex (?s:/\*\*(.*?)\*/)
  2. Remove /** and */ parts
  3. Replace the indentation with "/// " (without the quotes)
  4. Put back comments with the new style.
  5. Voilà
Thomas Ayoub
  • 29,063
  • 15
  • 95
  • 142
0

Despite the magnificent expression work in Sam's answer I ended up doing the following ugly brute-force method, in case anyone was wondering:

Step 1

Find: /\*\*\n
Replace: "" - nothing

Step 2

Find: ^\s*-\s*(?=parameter|returns|note|important|precondition|postcondition)
Replace: ///

Step 3 Find: (?<=/// .{0,80}?\n)(\s*)(?!(\s*(case|let|convenience|enum|struct|init|required|override|weak|public|private|static|class|var|func|lazy|@|///))) Replace: $1///

Step 4 Find: ^\s*\*/\n(?=case|let|convenience|enum|struct|init|required|override|weak|public|private|static|class|var|func|lazy)
Replace: "" - nothing

Community
  • 1
  • 1
rolling_codes
  • 15,174
  • 22
  • 76
  • 112