Skip to content

Commit 3d6ab2f

Browse files
Add break opportunities in long names for mobile
When wrapping text, Safari and Chrome do not consider `/` nor `:` as word break characters. Firefox considers `/` as a word break character but not `:`. This can lead to horizontal overflow issues with long file paths and module names on mobile. This commit adds a `full_name` helper to insert word-break opportunities into file paths and module names. It also revises the `link_to` helper to do the same when given an `RDoc::CodeObject`. The helpers insert `<wbr>` tags which tell the browser that the text can be wrapped at that point.
1 parent f676ccc commit 3d6ab2f

File tree

5 files changed

+103
-30
lines changed

5 files changed

+103
-30
lines changed

lib/rdoc/generator/template/rails/_context.rhtml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<div class="sectiontitle">Required Files</div>
1515
<ul>
1616
<% context.requires.each do |req| %>
17-
<li><%= h req.name %></li>
17+
<li><%= full_name req.name %></li>
1818
<% end %>
1919
</ul>
2020
<% end %>
@@ -58,9 +58,9 @@
5858
<% context.includes.each do |inc| %>
5959
<li>
6060
<% if inc.module.is_a?(String) %>
61-
<%= h inc.name %>
61+
<%= full_name inc.name %>
6262
<% else %>
63-
<%= link_to inc.module.full_name, inc.module %>
63+
<%= link_to inc.module %>
6464
<% end %>
6565
</li>
6666
<% end %>
@@ -144,7 +144,7 @@
144144
<p class="aka">
145145
Also aliased as:
146146
<%# Sometimes a parent cannot be determined. See ruby/rdoc@85ebfe13dc. %>
147-
<%= method.aliases.map { |aka| link_to aka.name, (aka if aka.parent) }.join(", ") %>.
147+
<%= method.aliases.map { |aka| link_to_if aka.parent, aka.name, aka }.join(", ") %>.
148148
</p>
149149
<% end %>
150150

@@ -191,7 +191,7 @@
191191
<% (context.modules.sort + context.classes.sort).each do |mod| %>
192192
<li>
193193
<span class="type"><%= mod.type.upcase %></span>
194-
<%= link_to mod.full_name, mod %>
194+
<%= link_to mod %>
195195
</li>
196196
<% end %>
197197
</ul>

lib/rdoc/generator/template/rails/class.rhtml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,10 @@
2828

2929
<h2>
3030
<span class="type"><%= klass.module? ? 'Module' : 'Class' %></span>
31-
<%= h klass.full_name %>
32-
<% if klass.type == "class" && klass.superclass %>
33-
<span class="parent">&lt;
34-
<% if klass.superclass.is_a?(String) %>
35-
<%= klass.superclass %>
36-
<% else %>
37-
<%= link_to klass.superclass.full_name, klass.superclass %>
38-
<% end %>
31+
<%= full_name klass %>
32+
<% if klass.type == "class" && superclass = klass.superclass %>
33+
<span class="parent">
34+
&lt; <%= superclass.is_a?(String) ? full_name(superclass) : link_to(superclass) %>
3935
</span>
4036
<% end %>
4137
</h2>
@@ -51,7 +47,7 @@
5147
<summary class="sectiontitle">Appears in</summary>
5248
<ul class="files">
5349
<% klass.in_files.each do |file| %>
54-
<li><%= link_to file.absolute_name, file %></li>
50+
<li><%= link_to file %></li>
5551
<% end %>
5652
</ul>
5753
</details>

lib/rdoc/generator/template/rails/file.rhtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
</h2>
2929
<ul class="files">
3030
<li>
31-
<%= h file.relative_name %>
31+
<%= full_name file %>
3232
<% if github = github_url(file.relative_name) %>
3333
<a href="<%= github %>" target="_blank" class="github_url">on GitHub</a>
3434
<% end %>

lib/sdoc/helpers.rb

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@ module SDoc::Helpers
22
require_relative "helpers/git"
33
include SDoc::Helpers::Git
44

5-
def link_to(text, url, html_attributes = {})
6-
return h(text) if url.nil?
7-
8-
url = "/#{url.path}" if url.is_a?(RDoc::CodeObject)
5+
def link_to(text, url = nil, html_attributes = {})
6+
url, html_attributes = nil, url if url.is_a?(Hash)
7+
url ||= text
98
attribute_string = html_attributes.map { |name, value| %( #{name}="#{h value}") }.join
109

11-
%(<a href="#{h url}"#{attribute_string}>#{h text}</a>)
10+
%(<a href="#{_link_url url}"#{attribute_string}>#{_link_body text}</a>)
11+
end
12+
13+
def _link_url(url)
14+
h(url.is_a?(RDoc::CodeObject) ? "/#{url.path}" : url)
15+
end
16+
17+
def _link_body(text)
18+
text.is_a?(RDoc::CodeObject) ? full_name(text) : h(text)
19+
end
20+
21+
def link_to_if(condition, text, *args)
22+
condition ? link_to(text, *args) : _link_body(text)
1223
end
1324

1425
def link_to_external(text, url, html_attributes = {})
@@ -19,6 +30,11 @@ def link_to_external(text, url, html_attributes = {})
1930
link_to(text, url, html_attributes)
2031
end
2132

33+
def full_name(named)
34+
named = named.full_name if named.is_a?(RDoc::CodeObject)
35+
named.split(%r"(?<=./|.::)").map { |part| h part }.join("<wbr>")
36+
end
37+
2238
def base_tag_for_context(context)
2339
relative_root = "../" * context.path.count("/") if context
2440
%(<base href="./#{relative_root}" data-current-path="#{context&.path}">)

spec/helpers_spec.rb

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,24 +91,61 @@
9191
must_equal %(<a href="qux" title="Foo &gt; Bar">Bar &lt; Foo</a>)
9292
end
9393

94-
it "returns the escaped text when URL argument is nil" do
95-
_(@helpers.link_to("Bar < Foo", nil, title: "Foo > Bar")).
96-
must_equal %(Bar &lt; Foo)
94+
it "uses the first argument as the URL when no URL is specified" do
95+
_(@helpers.link_to("foo/bar/qux.html")).
96+
must_equal %(<a href="foo/bar/qux.html">foo/bar/qux.html</a>)
97+
98+
_(@helpers.link_to("foo/bar/qux.html", "data-hoge": "fuga")).
99+
must_equal %(<a href="foo/bar/qux.html" data-hoge="fuga">foo/bar/qux.html</a>)
97100
end
98101

99-
it "returns an appropriate link when URL argument is an RDoc::CodeObject that responds to #path" do
102+
it "uses #full_name when the text argument is an RDoc::CodeObject" do
100103
top_level = rdoc_top_level_for <<~RUBY
101104
module Foo; class Bar; def qux; end; end; end
102105
RUBY
103106

104-
_(@helpers.link_to("perma", top_level.find_module_named("Foo"))).
105-
must_equal %(<a href="/classes/Foo.html">perma</a>)
107+
[
108+
top_level,
109+
top_level.find_module_named("Foo"),
110+
top_level.find_module_named("Foo::Bar"),
111+
top_level.find_module_named("Foo::Bar").find_method("qux", false),
112+
].each do |code_object|
113+
_(@helpers.link_to(code_object, "url")).
114+
must_equal %(<a href="url">#{@helpers.full_name(code_object)}</a>)
115+
end
116+
end
117+
118+
it "uses RDoc::CodeObject#path as the URL when URL argument is an RDoc::CodeObject" do
119+
top_level = rdoc_top_level_for <<~RUBY
120+
module Foo; class Bar; def qux; end; end; end
121+
RUBY
122+
123+
[
124+
top_level,
125+
top_level.find_module_named("Foo"),
126+
top_level.find_module_named("Foo::Bar"),
127+
top_level.find_module_named("Foo::Bar").find_method("qux", false),
128+
].each do |code_object|
129+
_(@helpers.link_to("text", code_object)).
130+
must_equal %(<a href="/#{code_object.path}">text</a>)
131+
end
132+
end
133+
end
134+
135+
describe "#link_to_if" do
136+
it "returns the link's HTML when the condition is true" do
137+
args = ["Bar < Foo", "qux", title: "Foo > Bar"]
138+
_(@helpers.link_to_if(true, *args)).must_equal @helpers.link_to(*args)
139+
end
140+
141+
it "returns the link's inner HTML when the condition is false" do
142+
_(@helpers.link_to_if(false, "Bar < Foo", "url")).must_equal ERB::Util.h("Bar < Foo")
106143

107-
_(@helpers.link_to("perma", top_level.find_module_named("Foo::Bar"))).
108-
must_equal %(<a href="/classes/Foo/Bar.html">perma</a>)
144+
rdoc_module = rdoc_top_level_for(<<~RUBY).find_module_named("Foo::Bar")
145+
module Foo; class Bar; end; end
146+
RUBY
109147

110-
_(@helpers.link_to("perma", top_level.find_module_named("Foo::Bar").find_method("qux", false))).
111-
must_equal %(<a href="/classes/Foo/Bar.html#method-i-qux">perma</a>)
148+
_(@helpers.link_to_if(false, rdoc_module, "url")).must_equal @helpers.full_name(rdoc_module)
112149
end
113150
end
114151

@@ -134,6 +171,30 @@ module Foo; class Bar; def qux; end; end; end
134171
end
135172
end
136173

174+
describe "#full_name" do
175+
it "inserts word-break opportunities into module names" do
176+
_(@helpers.full_name("Foo::Bar::Qux")).must_equal "Foo::<wbr>Bar::<wbr>Qux"
177+
_(@helpers.full_name("::Foo::Bar::Qux")).must_equal "::Foo::<wbr>Bar::<wbr>Qux"
178+
end
179+
180+
it "inserts word-break opportunities into file paths" do
181+
_(@helpers.full_name("path/to/file.rb")).must_equal "path/<wbr>to/<wbr>file.rb"
182+
_(@helpers.full_name("/path/to/file.rb")).must_equal "/path/<wbr>to/<wbr>file.rb"
183+
end
184+
185+
it "escapes name parts" do
186+
_(@helpers.full_name("ruby & rails/file.rb")).must_equal "ruby &amp; rails/<wbr>file.rb"
187+
end
188+
189+
it "uses RDoc::CodeObject#full_name when argument is an RDoc::CodeObject" do
190+
rdoc_module = rdoc_top_level_for(<<~RUBY).find_module_named("Foo::Bar::Qux")
191+
module Foo; module Bar; class Qux; end; end; end
192+
RUBY
193+
194+
_(@helpers.full_name(rdoc_module)).must_equal "Foo::<wbr>Bar::<wbr>Qux"
195+
end
196+
end
197+
137198
describe "#base_tag_for_context" do
138199
it "returns an idempotent <base> tag for nil context" do
139200
_(@helpers.base_tag_for_context(nil)).

0 commit comments

Comments
 (0)