Repository: asciidoctor/asciidoctor (Ruby)

Issue:
Double quotes around URL macro prevent it from being converted
Enclosing a URL or URL macro in double quotes prevents it from being recognized and converted. For example:

```
"https://asciidoctor.org[Asciidoctor]"
```

However, it's acceptable to put double quotes around an explicit link macro or xref macro.

```
"link:https://asciidoctor.org[Asciidoctor]"

"xref:index.adoc[Home]"
```

The behavior should be consistent.


Discussion:
> The behavior should be consistent.

As in, we should allow double quotes around a URL or URL macro?
Yes.
What is the exhaustive list of allowed enclosing characters?
In my opinion, when using an implicit link, we should only support a strict list of prefixes and suffixes:

```
"https://asciidoctor.org" is the project page for AsciiDoc.
<https://asciidoctor.org> is the project page for AsciiDoc.
[https://asciidoctor.org] is the project page for AsciiDoc.
(https://asciidoctor.org) is the project page for AsciiDoc.
'https://asciidoctor.org' is the project page for AsciiDoc.
```
When we detect a valid prefix (i.e. `[`, `(`, `<` or `"` or `'`), we should search for the matching suffix and remove it (if found).

| AsciiDoc | Link (href) | Comments |
| ---- | ---- | ---- |
| `<https://asciidoctor.org>` | `https://asciidoctor.org` | `<` is a valid prefix, <br/> the last character of the target is a valid suffix `>` |
| `[https://asciidoctor.org"` | `https://asciidoctor.org"` | `[` is a valid prefix,  <br/>`]` was expected as a suffix, since `"` does not match, <br/>we consider this character as part of the URL |
| `]https://asciidoctor.org[` | - | `]` is not a valid prefix |

Not sure about the second, maybe we should consider that this is not a valid link since the suffix is not in compliance with the prefix? It's more strict but might avoid some confusions.

Implementation wise, I think it might the right time to fix the following "FIXME", otherwise, the regular expression and the associated code will be hard to grasp. 

```rb
# FIXME revisit! the main issue is we need different rules for implicit vs explicit
```


I'm not interested right now in considering all permutations. I want to stay focused on the inconsistency at hand.
What it probably should be is that the URL protocol as macro name should be treated the same way as the link macro. But there are various reasons why that is not easily supported as the code currently stands. So it may be something we look at in the spec. Until then, just allows the URL protocol to be prefixed with a quote goes a long way.
> Implementation wise, I think it might the right time to fix the following "FIXME", otherwise, the regular expression and the associated code will be hard to grasp.

That needs to wait until at least a minor release (and maybe even a major one).
```
"https://asciidoctor.org"
```

The reason this one is not supported is because of the way substitutions are currently done in AsciiDoc. This would end up catching href attributes returned by partially converted text and would wreck havoc on the result.



Patch:
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index c130654fb8..0521e9a841 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -55,6 +55,7 @@ Improvements::
   * Allow template to be used to override outline by only specifying the outline template (#3491)
   * Upgrade MathJax from 2.7.5 to 2.7.6
   * Skip unused default attribute assigments for embedded document
+  * Allow a URL macro to have a preceding single or double quote (#3376)
 
 Build / Infrastructure::
 
diff --git a/lib/asciidoctor/rx.rb b/lib/asciidoctor/rx.rb
index 72ea69d241..f571e3f79e 100644
--- a/lib/asciidoctor/rx.rb
+++ b/lib/asciidoctor/rx.rb
@@ -514,9 +514,10 @@ module Rx; end
   #   https://github.com[GitHub]
   #   <https://github.com>
   #   link:https://github.com[]
+  #   "https://github.com[]"
   #
   # FIXME revisit! the main issue is we need different rules for implicit vs explicit
-  InlineLinkRx = %r((^|link:|#{CG_BLANK}|&lt;|[>\(\)\[\];])(\\?(?:https?|file|ftp|irc)://[^\s\[\]<]*([^\s.,\[\]<]))(?:\[(|#{CC_ALL}*?[^\\])\])?)m
+  InlineLinkRx = %r((^|link:|#{CG_BLANK}|&lt;|[>\(\)\[\];"'])(\\?(?:https?|file|ftp|irc)://[^\s\[\]<]*([^\s.,\[\]<]))(?:\[(|#{CC_ALL}*?[^\\])\])?)m
 
   # Match a link or e-mail inline macro.
   #
diff --git a/lib/asciidoctor/substitutors.rb b/lib/asciidoctor/substitutors.rb
index 57be07235a..8873f3ab6a 100644
--- a/lib/asciidoctor/substitutors.rb
+++ b/lib/asciidoctor/substitutors.rb
@@ -542,14 +542,17 @@ def sub_macros text
         end
 
         prefix, suffix = $1, ''
-        # NOTE if $4 is set, then we're looking at a formal macro
+        # NOTE if $4 is set, we're looking at a formal macro (e.g., https://example.org[])
         if $4
           prefix = '' if prefix == 'link:'
           text = $4
         else
-          # invalid macro syntax (link: prefix w/o trailing square brackets)
-          # FIXME we probably shouldn't even get here...our regex is doing too much
-          next $& if prefix == 'link:'
+          # invalid macro syntax (link: prefix w/o trailing square brackets or enclosed in double quotes)
+          # FIXME we probably shouldn't even get here when the link: prefix is present; the regex is doing too much
+          case prefix
+          when 'link:', ?", ?'
+            next $&
+          end
           text = ''
           case $3
           when ')'


Test patch:
diff --git a/test/links_test.rb b/test/links_test.rb
index 5c10b89a26..87a133b4dd 100644
--- a/test/links_test.rb
+++ b/test/links_test.rb
@@ -168,6 +168,16 @@
     assert_match(/l&#8217;<a href=/, output)
   end
 
+  test 'should convert qualified url as macro enclosed in double quotes' do
+    output = convert_string_to_embedded('"https://asciidoctor.org[]"')
+    assert_include '"<a href="https://asciidoctor.org" class="bare">https://asciidoctor.org</a>"', output
+  end
+
+  test 'should convert qualified url as macro enclosed in single quotes' do
+    output = convert_string_to_embedded('\'https://asciidoctor.org[]\'')
+    assert_include '\'<a href="https://asciidoctor.org" class="bare">https://asciidoctor.org</a>\'', output
+  end
+
   test 'qualified url using invalid link macro should not create link' do
     assert_xpath '//a', convert_string('link:http://asciidoc.org is the project page for AsciiDoc.'), 0
   end