Skip to content

Commit

Permalink
Generate uniform <h1> headings for all modules
Browse files Browse the repository at this point in the history
In #223, we began generating default `<h1>` headings for modules.
However, these headings were only generated for modules that had a
description.  If a module had no comment to begin with, it would not get
a generated heading.

Furthermore, since #280, each module's full name has been prominently
displayed at the top of its page.  These names are visually redundant
with generated headings, which also use the full name.

To unify the design and ensure that all modules have an `<h1>`, this
commit converts each module's full name into an `<h1>` heading inside an
`<hgroup>`, and uses postprocessing to pull any comment-based `<h1>`
into the `<hgroup>`.

This commit also renames the "Included Modules" section to "Inherits
From", and moves any listed base class from the top of the page to the
top of that section.  This change focuses the `<h1>`, both visually and
from an SEO perspective.
  • Loading branch information
jonathanhefner committed Aug 20, 2023
1 parent 947abf9 commit cf5b68e
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 88 deletions.
37 changes: 16 additions & 21 deletions lib/rdoc/generator/template/rails/_context.rhtml
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
<div id="context">
<% unless (description = context.description).empty? %>
<% unless context.comment_title %>
<h1><%= context.title %></h1>
<% end %>
<div class="description">
<%= description %>
</div>
<% end %>
<%# File only: requires %>
<% unless context.requires.empty? %>
<!-- File only: requires -->
<div class="content__divider">Required Files</div>
<ul>
<% context.requires.each do |req| %>
Expand All @@ -20,6 +17,20 @@
<% end %>
<%# Module only: ancestors %>
<% unless context.is_a?(RDoc::TopLevel) || (ancestors = module_ancestors(context)).empty? %>
<div class="content__divider">Inherits From</div>
<ul>
<% ancestors.each do |kind, ancestor| %>
<li>
<span class="kind"><%= kind %></span>
<%= ancestor.is_a?(String) ? full_name(ancestor) : link_to(ancestor) %>
</li>
<% end %>
</ul>
<% end %>
<% sections = context.sections.select { |s| s.title }.sort_by{ |s| s.title.to_s } %>
<% unless sections.empty? then %>
<!-- Sections -->
Expand Down Expand Up @@ -51,22 +62,6 @@
</dl>
<% end %>
<% unless context.includes.empty? %>
<!-- Includes -->
<div class="content__divider">Included Modules</div>
<ul>
<% context.includes.each do |inc| %>
<li>
<% if inc.module.is_a?(String) %>
<%= full_name inc.name %>
<% else %>
<%= link_to inc.module %>
<% end %>
</li>
<% end %>
</ul>
<% end %>
<% context.each_section do |section, constants, attributes| %>
Expand Down Expand Up @@ -185,7 +180,7 @@
<ul>
<% (context.modules.sort + context.classes.sort).each do |mod| %>
<li>
<span class="type"><%= mod.type.upcase %></span>
<span class="kind"><%= mod.type %></span>
<%= link_to mod %>
</li>
<% end %>
Expand Down
11 changes: 3 additions & 8 deletions lib/rdoc/generator/template/rails/class.rhtml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,9 @@
<%= include_template '_panel.rhtml' %>

<main id="content">
<div class="content__full-name">
<span class="qualifier"><%= klass.type %></span>
<%= module_breadcrumbs klass %>
<% if !klass.module? && superclass = klass.superclass %>
<span class="qualifier">&lt;</span>
<%= superclass.is_a?(String) ? full_name(superclass) : link_to(superclass) %>
<% end %>
</div>
<hgroup class="content__title">
<h1><span class="kind"><%= klass.type %></span> <%= module_breadcrumbs klass %></h1>
</hgroup>

<%= include_template '_context.rhtml', {:context => klass} %>

Expand Down
7 changes: 3 additions & 4 deletions lib/rdoc/generator/template/rails/file.rhtml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@
<%= include_template '_panel.rhtml' %>

<main id="content">
<h1 class="content__full-name">
<span class="qualifier">File</span>
<%= full_name file %>
</h1>
<hgroup class="content__title">
<h1><span class="kind">File</span> <%= full_name file %></h1>
</hgroup>

<% if source_url = github_url(file.relative_name) %>
<p><%= link_to_external "View on GitHub", source_url %></p>
Expand Down
32 changes: 23 additions & 9 deletions lib/rdoc/generator/template/rails/resources/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,6 @@ a {
a:has(> code:only-child) {
text-decoration: none;
}
code a {
text-decoration: none;
}

/* TODO: Remove this hack when Firefox supports `:has()` */
a code {
Expand All @@ -74,6 +71,15 @@ a code {
display: none;
}

.kind {
font-family: monospace;
font-weight: bold;
}

.kind::after {
content: " "; /* Ensure trailing space has width of 1 monospace char */
}

table {
border-collapse: collapse;
}
Expand Down Expand Up @@ -380,18 +386,22 @@ html {
font-style: normal;
}

.content__full-name {
font-family: monospace;
font-size: 1.4em;
.content__title :is(h1, p) {
font-size: 1.6em;
line-height: 1.25;
padding: 0.125em 0;
}

.content__title h1 {
font-weight: normal;

margin-left: 1em;
text-indent: -1em;
}

.content__full-name .qualifier {
font-weight: bold;
.content__title p {
font-style: italic;

margin-top: 0;
}

.content__section-title {
Expand Down Expand Up @@ -513,6 +523,10 @@ html {
* Description of method or module
*/

#context > .description {
margin-top: var(--space-lg);
}

.description :is(h1, h2, h3, h4, h5, h6) {
line-height: 1.25;
padding: 0.125em 0;
Expand Down
10 changes: 0 additions & 10 deletions lib/sdoc/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,6 @@ class RDoc::Options
attr_accessor :search_index
end

module RDoc::Generator::Markup
def comment_title
@comment_title ||= @comment.to_s.match(/\A[=#] (.*)$/) {|match| match[1] }
end

def title
comment_title || full_name
end
end

class RDoc::Generator::SDoc
RDoc::RDoc.add_generator self

Expand Down
11 changes: 11 additions & 0 deletions lib/sdoc/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,17 @@ def module_breadcrumbs(rdoc_module)
"<code>#{crumbs.join("::<wbr>")}</code>"
end

def module_ancestors(rdoc_module)
ancestors = rdoc_module.includes.map { |inc| ["module", inc.module] }

if !rdoc_module.module? && superclass = rdoc_module.superclass
superclass_name = superclass.is_a?(String) ? superclass : superclass.full_name
ancestors.unshift(["class", superclass]) unless superclass_name == "Object"
end

ancestors
end

def method_signature(rdoc_method)
if rdoc_method.call_seq
rdoc_method.call_seq.split(/\n+/).map do |line|
Expand Down
10 changes: 10 additions & 0 deletions lib/sdoc/postprocessor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def process(rendered)
version_rails_guides_urls!(document)
unlink_unintentional_ref_links!(document)
style_ref_links!(document)
unify_h1_headings!(document)
highlight_code_blocks!(document)

document.to_s
Expand Down Expand Up @@ -88,6 +89,15 @@ def style_ref_links!(document)
end
end

def unify_h1_headings!(document)
if h1 = document.at_css("#context > .description h1:first-child")
if hgroup = document.at_css("#content > hgroup")
h1.remove
hgroup.add_child(%(<p>#{h1.inner_html}</p>))
end
end
end

def highlight_code_blocks!(document)
document.css(".description pre > code, .method__source pre > code").each do |element|
code = element.inner_text
Expand Down
38 changes: 38 additions & 0 deletions spec/helpers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,44 @@ module Foo; module Bar; module Qux; end; end; end
end
end

describe "#module_ancestors" do
it "returns a list with the base class (if applicable) and included modules" do
# RDoc chokes on ";" when parsing includes, so replace with "\n".
top_level = rdoc_top_level_for <<~RUBY.gsub(";", "\n")
module M1; end
module M2; end
class C1; end
module Foo; include M1; include M2; end
class Bar < C1; include M2; include M1; end
class Qux < Cx; include Foo; include Mx; end
RUBY

m1, m2, c1, foo, bar, qux = %w[M1 M2 C1 Foo Bar Qux].map { |name| top_level.find_module_named(name) }

_(@helpers.module_ancestors(foo)).must_equal [["module", m1], ["module", m2]]
_(@helpers.module_ancestors(bar)).must_equal [["class", c1], ["module", m2], ["module", m1]]
_(@helpers.module_ancestors(qux)).must_equal [["class", "Cx"], ["module", foo], ["module", "Mx"]]
end

it "excludes the default base class (Object) from the result" do
# RDoc chokes on ";" when parsing includes, so replace with "\n".
top_level = rdoc_top_level_for <<~RUBY.gsub(";", "\n")
class Object; end
class Foo; include M1; end
RUBY

_(@helpers.module_ancestors(top_level.find_module_named("Object"))).must_equal [["class", "BasicObject"]]
_(@helpers.module_ancestors(top_level.find_module_named("Foo"))).must_equal [["module", "M1"]]

top_level = rdoc_top_level_for <<~RUBY.gsub(";", "\n")
class Foo; include M1; end
RUBY

_(@helpers.module_ancestors(top_level.find_module_named("Foo"))).must_equal [["module", "M1"]]
end
end

describe "#method_signature" do
it "returns the method signature wrapped in <code>" do
method = rdoc_top_level_for(<<~RUBY).find_module_named("Foo").find_method("bar", false)
Expand Down
53 changes: 53 additions & 0 deletions spec/postprocessor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,59 @@
_(SDoc::Postprocessor.process(rendered)).must_include expected
end

it "unifies <h1> headings for a context" do
rendered = <<~HTML
<div id="content">
<hgroup><h1>module Foo</h1></hgroup>
<div id="context">
<div class="description"><h1>The Foo</h1><p>Lorem ipsum.</p></div>
</div>
</div>
HTML

expected = <<~HTML
<div id="content">
<hgroup><h1>module Foo</h1><p>The Foo</p></hgroup>
<div id="context">
<div class="description"><p>Lorem ipsum.</p></div>
</div>
</div>
HTML

_(SDoc::Postprocessor.process(rendered)).must_include expected
end

it "does not relocate non-leading <h1> headings" do
rendered = <<~HTML
<div id="content">
<hgroup><h1>module Foo</h1></hgroup>
<div id="context">
<div class="description"><p>Lorem ipsum.</p><h1>Red Herring</h1></div>
<div class="method">
<div class="description"><h1>Red Herring</h1></div>
</div>
</div>
</div>
HTML

_(SDoc::Postprocessor.process(rendered)).must_include rendered
end

it "does not relocate <h1> headings when <hgroup> is not present" do
rendered = <<~HTML
<div id="content">
<div id="context">
<div class="description"><h1>Main Page</h1></div>
</div>
</div>
HTML

_(SDoc::Postprocessor.process(rendered)).must_include rendered
end

it "highlights code blocks" do
rendered = <<~HTML
<div class="description">
Expand Down
36 changes: 0 additions & 36 deletions spec/rdoc_generator_markup_spec.rb

This file was deleted.

0 comments on commit cf5b68e

Please sign in to comment.