pure_link_helpers.ex (2532B)
1 defmodule EarmarkParser.Helpers.PureLinkHelpers do 2 @moduledoc false 3 4 import EarmarkParser.Helpers.AstHelpers, only: [render_link: 2] 5 6 @pure_link_rgx ~r{ 7 \A 8 (\s*) 9 ( 10 (?:https?://|www\.) 11 [^\s<>]* 12 [^\s<>?!.,:*_~] 13 ) 14 }ux 15 16 def convert_pure_link(src) do 17 case Regex.run(@pure_link_rgx, src) do 18 [_match, spaces, link_text] -> 19 if String.ends_with?(link_text, ")") do 20 remove_trailing_closing_parens(spaces, link_text) 21 else 22 make_result(spaces, link_text) 23 end 24 25 _ -> 26 nil 27 end 28 end 29 30 @split_at_ending_parens ~r{ (.*?) (\)*) \z}x 31 defp remove_trailing_closing_parens(leading_spaces, link_text) do 32 [_, link_text, trailing_parens] = Regex.run(@split_at_ending_parens, link_text) 33 trailing_paren_count = String.length(trailing_parens) 34 35 # try to balance parens from the rhs 36 unbalanced_count = balance_parens(String.reverse(link_text), trailing_paren_count) 37 balanced_parens = String.slice(trailing_parens, 0, trailing_paren_count - unbalanced_count) 38 39 make_result(leading_spaces, link_text <> balanced_parens) 40 end 41 42 defp make_result(leading_spaces, link_text) do 43 link = 44 if String.starts_with?(link_text, "www.") do 45 render_link("http://" <> link_text, link_text) 46 else 47 render_link(link_text, link_text) 48 end 49 50 if leading_spaces == "" do 51 {link, String.length(link_text)} 52 else 53 {[leading_spaces, link], String.length(leading_spaces) + String.length(link_text)} 54 end 55 end 56 57 # balance parens and return unbalanced *trailing* paren count 58 defp balance_parens(reverse_text, trailing_count, non_trailing_count \\ 0) 59 60 defp balance_parens(<<>>, trailing_paren_count, _non_trailing_count), do: trailing_paren_count 61 62 defp balance_parens(_reverse_text, 0, _non_trailing_count), do: 0 63 64 defp balance_parens(")" <> rest, trailing_paren_count, non_trailing_count) do 65 balance_parens(rest, trailing_paren_count, non_trailing_count + 1) 66 end 67 68 defp balance_parens("(" <> rest, trailing_paren_count, non_trailing_count) do 69 # non-trailing paren must be balanced before trailing paren 70 if non_trailing_count > 0 do 71 balance_parens(rest, trailing_paren_count, non_trailing_count - 1) 72 else 73 balance_parens(rest, trailing_paren_count - 1, non_trailing_count) 74 end 75 end 76 77 defp balance_parens(<<_::utf8,rest::binary>>, trailing_paren_count, non_trailing_count) do 78 balance_parens(rest, trailing_paren_count, non_trailing_count) 79 end 80 end