From e5abcd66b1913f8a5655d209510981f766b64ccf Mon Sep 17 00:00:00 2001 From: Egor Date: Sat, 23 Feb 2019 12:51:38 +0000 Subject: [PATCH] Improve hashtags and `rel` attribute --- lib/auto_linker/builder.ex | 41 ++++++++++++++++++-------- lib/auto_linker/parser.ex | 3 +- test/auto_linker_test.exs | 59 ++++++++++++++++++++++++++++++++------ 3 files changed, 81 insertions(+), 22 deletions(-) diff --git a/lib/auto_linker/builder.ex b/lib/auto_linker/builder.ex index a46ccf4..cb2d18e 100644 --- a/lib/auto_linker/builder.ex +++ b/lib/auto_linker/builder.ex @@ -7,11 +7,13 @@ defmodule AutoLinker.Builder do Create a link. """ def create_link(url, opts) do + url = add_scheme(url) + [] |> build_attrs(url, opts, :rel) |> build_attrs(url, opts, :target) |> build_attrs(url, opts, :class) - |> build_attrs(url, opts, :scheme) + |> build_attrs(url, opts, :href) |> format_url(url, opts) end @@ -23,6 +25,13 @@ defmodule AutoLinker.Builder do |> format_markdown(text, opts) end + defp build_attrs(attrs, uri, %{rel: get_rel}, :rel) when is_function(get_rel, 1) do + case get_rel.(uri) do + nil -> attrs + rel -> [{:rel, rel} | attrs] + end + end + defp build_attrs(attrs, _, opts, :rel) do if rel = Map.get(opts, :rel, "noopener noreferrer"), do: [{:rel, rel} | attrs], else: attrs end @@ -35,12 +44,14 @@ defmodule AutoLinker.Builder do if cls = Map.get(opts, :class, "auto-linker"), do: [{:class, cls} | attrs], else: attrs end - defp build_attrs(attrs, url, _opts, :scheme) do - if String.starts_with?(url, ["http://", "https://"]), - do: [{:href, url} | attrs], - else: [{:href, "http://" <> url} | attrs] + defp build_attrs(attrs, url, _opts, :href) do + [{:href, url} | attrs] end + defp add_scheme("http://" <> _ = url), do: url + defp add_scheme("https://" <> _ = url), do: url + defp add_scheme(url), do: "http://" <> url + defp format_url(attrs, url, opts) do url = url @@ -117,10 +128,11 @@ defmodule AutoLinker.Builder do url = mention_prefix <> name - [href: url] + [] |> build_attrs(url, opts, :rel) |> build_attrs(url, opts, :target) |> build_attrs(url, opts, :class) + |> build_attrs(url, opts, :href) |> format_mention(name, opts) end @@ -129,43 +141,48 @@ defmodule AutoLinker.Builder do url = hashtag_prefix <> tag - [href: url] + [] |> build_attrs(url, opts, :rel) |> build_attrs(url, opts, :target) |> build_attrs(url, opts, :class) + |> build_attrs(url, opts, :href) |> format_hashtag(tag, opts) end def create_email_link(email, opts) do [] |> build_attrs(email, opts, :class) + |> build_attrs("mailto:#{email}", opts, :href) |> format_email(email, opts) end def create_extra_link(uri, opts) do [] |> build_attrs(uri, opts, :class) + |> build_attrs(uri, opts, :rel) + |> build_attrs(uri, opts, :target) + |> build_attrs(uri, opts, :href) |> format_extra(uri, opts) end def format_mention(attrs, name, _opts) do attrs = format_attrs(attrs) - "@" <> name <> "" + "@#{name}" end def format_hashtag(attrs, tag, _opts) do attrs = format_attrs(attrs) - "#" <> tag <> "" + "##{tag}" end def format_email(attrs, email, _opts) do attrs = format_attrs(attrs) - ~s(#{email}) + ~s(#{email}) end def format_extra(attrs, uri, _opts) do - attrs = format_attributes(attrs) - ~s(#{uri}) + attrs = format_attrs(attrs) + ~s(#{uri}) end defp format_attributes(attrs) do diff --git a/lib/auto_linker/parser.ex b/lib/auto_linker/parser.ex index c675bb1..c37f56c 100644 --- a/lib/auto_linker/parser.ex +++ b/lib/auto_linker/parser.ex @@ -25,7 +25,6 @@ defmodule AutoLinker.Parser do ~s{, work (555) 555-5555} """ - # @invalid_url ~r/\.\.+/ @invalid_url ~r/(\.\.+)|(^(\d+\.){1,2}\d+$)/ @match_url ~r{^[\w\.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+$} @@ -45,7 +44,7 @@ defmodule AutoLinker.Parser do # https://www.w3.org/TR/html5/forms.html#valid-e-mail-address @match_email ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/u - @match_hashtag ~r/^(?\#\w+)/u + @match_hashtag ~r/^(?\#[[:word:]_]*[[:alpha:]_·][[:word:]_·]*)/u @prefix_extra [ "magnet:?", diff --git a/test/auto_linker_test.exs b/test/auto_linker_test.exs index ad26d81..9bd0719 100644 --- a/test/auto_linker_test.exs +++ b/test/auto_linker_test.exs @@ -33,7 +33,7 @@ defmodule AutoLinkerTest do "hello google.com https://ddg.com 888 888-8888 user@email.com [google.com](http://google.com) irc:///mIRC" expected = - "hello google.com ddg.com 888 888-8888 user@email.com google.com irc:///mIRC" + "hello google.com ddg.com 888 888-8888 user@email.com google.com irc:///mIRC" assert AutoLinker.link(text, phone: true, @@ -47,6 +47,22 @@ defmodule AutoLinkerTest do ) == expected end + test "rel as function" do + text = "google.com" + + expected = "google.com" + + custom_rel = fn url -> + url |> String.split(".") |> List.last() + end + + assert AutoLinker.link(text, + class: false, + new_window: false, + rel: custom_rel + ) == expected + end + describe "custom handlers" do test "mentions handler" do text = "hello @user, @valid_user and @invalid_user" @@ -106,7 +122,7 @@ defmodule AutoLinkerTest do end expected = - "Hello again, @@user.<script></script>\nThis is on another :moominmamma: line. #2hu #epic #phantasmagoric" + "Hello again, @@user.<script></script>\nThis is on another :moominmamma: line. #2hu #epic #phantasmagoric" assert AutoLinker.link(text, mention: true, @@ -120,7 +136,7 @@ defmodule AutoLinkerTest do describe "mentions" do test "simple mentions" do expected = - ~s{hello @user and @anotherUser.} + ~s{hello @user and @anotherUser.} assert AutoLinker.link("hello @user and @anotherUser.", mention: true, @@ -132,7 +148,7 @@ defmodule AutoLinkerTest do text = "hey @user@example.com" expected = - "hey @user@example.com" + "hey @user@example.com" assert AutoLinker.link(text, mention: true, @@ -144,7 +160,7 @@ defmodule AutoLinkerTest do describe "hashtag links" do test "hashtag" do expected = - " one #2two three #four." + " one #2two three #four." assert AutoLinker.link(" one #2two three #four.", hashtag: true, @@ -152,6 +168,33 @@ defmodule AutoLinkerTest do ) == expected end + test "must have non-numbers" do + expected = "#1ok #42 #7" + + assert AutoLinker.link("#1ok #42 #7", + hashtag: true, + hashtag_prefix: "/t/", + class: false, + rel: false, + new_window: false + ) == expected + end + + test "support French" do + text = "#administrateur·rice·s #ingénieur·e·s" + + expected = + "#administrateur·rice·s #ingénieur·e·s" + + assert AutoLinker.link(text, + hashtag: true, + hashtag_prefix: "/t/", + class: false, + rel: false, + new_window: false + ) == expected + end + test "do not turn urls with hashes into hashtags" do text = "google.com#test #test google.com/#test #tag" @@ -262,7 +305,7 @@ defmodule AutoLinkerTest do expected = "xmpp:user@example.com" - assert AutoLinker.link(text, extra: true) == expected + assert AutoLinker.link(text, extra: true, new_window: false, rel: false) == expected end test "email" do @@ -278,7 +321,7 @@ defmodule AutoLinkerTest do expected = "magnet:?xt=urn:btih:a4104a9d2f5615601c429fe8bab8177c47c05c84&dn=ubuntu-18.04.1.0-live-server-amd64.iso&tr=http%3A%2F%2Ftorrent.ubuntu.com%3A6969%2Fannounce&tr=http%3A%2F%2Fipv6.torrent.ubuntu.com%3A6969%2Fannounce" - assert AutoLinker.link(text, extra: true) == expected + assert AutoLinker.link(text, extra: true, new_window: false, rel: false) == expected end test "dweb" do @@ -288,7 +331,7 @@ defmodule AutoLinkerTest do expected = "dweb://584faa05d394190ab1a3f0240607f9bf2b7e2bd9968830a11cf77db0cea36a21+v1.0.0/path/to/file.txt" - assert AutoLinker.link(text, extra: true) == expected + assert AutoLinker.link(text, extra: true, new_window: false, rel: false) == expected end end