I have a regular expression that is supposed to allow me to annotate pieces of code within markdown documents. Basically it looks for content between /*HLS*/
and /*HLE*/
comments, and wraps that in a span
. It even allows for a small explanation that'll become the title of the span.
import Foundation
let content = """
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return /*HLS Explanation here!*/viewModel.books.value.count/*HLE*/
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let book = /*HLS*/viewModel.books.value[indexPath.row]/*HLE*/
let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell") as! BookCell
cell.configure(with: book)
return cell
}
}
"""
let regex = try NSRegularExpression(pattern: #"(?s)\/\*HLS\W?(.*?)\*\/(.*?)\/\*HLE\*\/"#)
let range = NSRange(content.startIndex..<content.endIndex, in: content)
let newContent = regex.stringByReplacingMatches(in: content, options: [], range: range, withTemplate: #"<span class="highlight" title="$1">$2</span>"#)
print(newContent)
The result:
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return <span class="highlight" title="Explanation here!">viewModel.books.value.count</span>
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let book = <span class="highlight" title="">viewModel.books.value[indexPath.row]</span>
let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell") as! BookCell
cell.configure(with: book)
return cell
}
}
This is exactly how it is supposed to work
However, when I remove that Explanation here!
from the first comment, the regex is too greedy.
import Foundation
let content = """
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return /*HLS*/viewModel.books.value.count/*HLE*/
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let book = /*HLS*/viewModel.books.value[indexPath.row]/*HLE*/
let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell") as! BookCell
cell.configure(with: book)
return cell
}
}
"""
let regex = try NSRegularExpression(pattern: #"(?s)\/\*HLS\W?(.*?)\*\/(.*?)\/\*HLE\*\/"#)
let range = NSRange(content.startIndex..<content.endIndex, in: content)
let newContent = regex.stringByReplacingMatches(in: content, options: [], range: range, withTemplate: #"<span class="highlight" title="$1">$2</span>"#)
print(newContent)
Result:
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return <span class="highlight" title="/viewModel.books.value.count/*HLE">
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let book = /*HLS*/viewModel.books.value[indexPath.row]</span>
let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell") as! BookCell
cell.configure(with: book)
return cell
}
}
As you can see, viewModel.books.value.count/*HLE
becomes the title, and then everything until the second /*HLE*/
gets wrapped. The regex should match the title capture group until that very first */
it encounters, but it's not - it goes until the second one. Why is that? The regex should match (.*?)
until it encounters \*\/
, right?
When I remove the (?s)
flag everything works as expected again, but I want to be able to wrap multiple lines between /*HLS*/
and /*HLE*/
.