From cb1686d84e272de9bc6952c47a31f0d445e7549b Mon Sep 17 00:00:00 2001 From: Jose Farias Date: Tue, 17 Dec 2024 18:02:42 -0600 Subject: [PATCH] Use house ssr --- Gemfile | 2 + Gemfile.lock | 10 ++ config/initializers/markdown.rb | 2 +- lib/markdown_renderer.rb | 75 +++++---- lib/markdown_renderer/markup.rb | 117 ------------- lib/markdown_renderer/parsing.rb | 136 --------------- lib/rails_ext/action_text_markdown.rb | 26 +-- test/lib/markdown_renderer_test.rb | 230 -------------------------- test/models/bubble_test.rb | 2 +- 9 files changed, 62 insertions(+), 538 deletions(-) delete mode 100644 lib/markdown_renderer/markup.rb delete mode 100644 lib/markdown_renderer/parsing.rb delete mode 100644 test/lib/markdown_renderer_test.rb diff --git a/Gemfile b/Gemfile index 1699e8a74..d4349c7cd 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,5 @@ source "https://rubygems.org" +git_source(:bc) { |repo| "https://github.com/basecamp/#{repo}" } ruby file: ".ruby-version" gem "rails", github: "rails/rails", branch: "main" @@ -9,6 +10,7 @@ gem "propshaft" gem "stimulus-rails" gem "turbo-rails" gem "hotwire_combobox", github: "josefarias/hotwire_combobox", branch: :main +gem "house_md", bc: "house", branch: :renderer # Deployment and drivers gem "bootsnap", require: false diff --git a/Gemfile.lock b/Gemfile.lock index 4b63b69ab..1c26d38e1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,12 @@ +GIT + remote: https://github.com/basecamp/house + revision: a8b19e70bbb95a7a0d27e38985a99c2d697a6650 + branch: renderer + specs: + house_md (1.0.0) + rails + zeitwerk + GIT remote: https://github.com/josefarias/hotwire_combobox.git revision: 57ee6c9087320fab9383a07fa919fe75e0ea72ba @@ -346,6 +355,7 @@ DEPENDENCIES capybara debug hotwire_combobox! + house_md! importmap-rails jbuilder propshaft diff --git a/config/initializers/markdown.rb b/config/initializers/markdown.rb index 57e7c4384..2d3a879a3 100644 --- a/config/initializers/markdown.rb +++ b/config/initializers/markdown.rb @@ -1,4 +1,4 @@ ActiveSupport.on_load :action_text_markdown do require "markdown_renderer" - ActionText::Markdown.renderer = -> { MarkdownRenderer.new } + ActionText::Markdown.html_renderer = -> { MarkdownRenderer.new } end diff --git a/lib/markdown_renderer.rb b/lib/markdown_renderer.rb index 1205e6d6d..9117b28d0 100644 --- a/lib/markdown_renderer.rb +++ b/lib/markdown_renderer.rb @@ -1,40 +1,47 @@ -class MarkdownRenderer - require_relative "markdown_renderer/parsing" - require_relative "markdown_renderer/markup" +require "rouge/plugins/redcarpet" - include Parsing, Markup +class MarkdownRenderer < HouseMd::Renderer + class Generator < HouseMd::Generator::Html + include Rouge::Plugins::Redcarpet - def initialize - @id_counts = Hash.new(0) - end + def initialize + @id_counts = Hash.new 0 + end - def render(content) - content - .then { |c| parse_paragraphs(c) } - .then { |c| parse_bold_italics(c) } - .then { |c| parse_bold(c) } - .then { |c| parse_italics(c) } - .then { |c| parse_strikethrough(c) } - .then { |c| parse_highlight(c) } - .then { |c| parse_headers(c) } - .then { |c| parse_tables(c) } - .then { |c| parse_ordered_lists(c) } - .then { |c| parse_unordered_lists(c) } - .then { |c| parse_block_quotes(c) } - .then { |c| parse_horizontal_rules(c) } - .then { |c| parse_images(c) } - .then { |c| parse_links(c) } - .then { |c| parse_code_blocks(c) } - .then { |c| parse_code_spans(c) } - end - - private - attr_reader :id_counts - - def unique_id(text) - text.parameterize.then do |base_id| - id_counts[base_id] += 1 - id_counts[base_id] > 1 ? "#{base_id}-#{id_counts[base_id]}" : base_id + def header(text, header_level) + unique_id(text).then do |id| + <<~HTML.chomp + + #{text} + + HTML end end + + def image(url, alt_text) + <<~HTML.chomp + + #{alt_text} + + HTML + end + + def code_block(code, language) + block_code(code, language) # call Rouge Redcarpet plugin + end + + private + attr_reader :id_counts + + def unique_id(text) + text.parameterize.then do |base_id| + id_counts[base_id] += 1 + id_counts[base_id] > 1 ? "#{base_id}-#{id_counts[base_id]}" : base_id + end + end + end + + def initialize + @generator = Generator.new + end end diff --git a/lib/markdown_renderer/markup.rb b/lib/markdown_renderer/markup.rb deleted file mode 100644 index 2e2753c5e..000000000 --- a/lib/markdown_renderer/markup.rb +++ /dev/null @@ -1,117 +0,0 @@ -require "rouge/plugins/redcarpet" - -module MarkdownRenderer::Markup - include Rouge::Plugins::Redcarpet - - TABLE_ROW_INDENT = " " * 4 - LIST_ITEM_INDENT = " " * 2 - BLOCK_QUOTE_INDENT = " " * 2 - TABLE_HEADER_INDENT = " " * 6 - - # FIXME: the attributes suggest this should be an app-level override instead - def header(text, header_level) - unique_id(text).then do |id| - <<~HTML.chomp - - #{text} - - HTML - end - end - - def bold(text) - "#{text}" - end - - def italics(text) - "#{text}" - end - - def strikethrough(text) - "#{text}" - end - - def highlight(text) - "#{text}" - end - - def table(headers, rows) - <<~HTML - - - - #{headers.map { |header| "" }.join("\n" + TABLE_HEADER_INDENT)} - - - - #{rows.map { |row| "#{row.map { |cell| "" }.join}" }.join("\n" + TABLE_ROW_INDENT)} - -
#{header}
#{cell}
- HTML - end - - def ordered_list(contents) - <<~HTML -
    - #{contents.split("\n").join("\n" + LIST_ITEM_INDENT)} -
- HTML - end - - def unordered_list(contents) - <<~HTML - - HTML - end - - def list_item(text) - "
  • #{text}
  • " - end - - def block_quote(text) - <<~HTML -
    - #{text.gsub(/^>\s?/, '').split("\n").join("\n" + BLOCK_QUOTE_INDENT)} -
    - HTML - end - - def horizontal_rule - "
    " - end - - def link(text, href) - "#{text}" - end - - def code_block(code, language) - block_code(code, language) # call Rouge Redcarpet plugin - end - - def code_span(text) - "#{text}" - end - - # FIXME: the attributes suggest this should be an app-level override instead - def image(url, alt_text) - <<~HTML.chomp - - #{alt_text} - - HTML - end - - def paragraph(text) - "

    #{text}

    " - end - - def soft_line_break - "\n#{line_break}\n" - end - - def line_break - "
    " - end -end diff --git a/lib/markdown_renderer/parsing.rb b/lib/markdown_renderer/parsing.rb deleted file mode 100644 index 3069251cb..000000000 --- a/lib/markdown_renderer/parsing.rb +++ /dev/null @@ -1,136 +0,0 @@ -module MarkdownRenderer::Parsing - HR = /^---$/ - INLINE_HTML_BLOCK_START = /^\s?/ - BLOCK_QUOTE_CONTENT = /#{BLOCK_QUOTE_START}[^\n]*#{NEWLINE_OR_EOF}/ - - TABLE_START = /^\|/ - TABLE_HEADER_ROW = /^\|(.+)\|/ - TABLE_SEPARATOR = /^\|[-|]+\|/ - TABLE_DATA_ROW = /^\|.+\|/ - TABLE = /#{TABLE_HEADER_ROW}\r?\n#{TABLE_SEPARATOR}\r?\n((?:#{TABLE_DATA_ROW}\r?\n)*)/ - - BLOCK_STARTERS = Regexp.union [ - HR, - TABLE_START, - HEADING_START, - CODE_BLOCK_START, - BLOCK_QUOTE_START, - NUMBERED_LIST_START, - UNORDERED_LIST_START, - INLINE_HTML_BLOCK_START - ] - - private - def parse_paragraphs(content) - content.split(/\n\n+/).map do |text| - if text.match?(BLOCK_STARTERS) - text - else - paragraph text.gsub(SOFT_LINE_BREAK, soft_line_break) - end - end.join("\n\n") - end - - def parse_bold_italics(content) - transform = ->(match) { bold(italics(match)) } - - content - .gsub(/(?(match) { bold(match) } - - content - .gsub(/(?(match) { italics(match) } - - content - .gsub(/(? - Welcome to My Document - - -

    This is an introduction paragraph with some text.

    - -
    - -

    Hello, world! Hello, world! Hello, world! Hello, world! Hello, world!

    - -

    Hello, world! -
    - Hello, world! -
    - Hello, world! -
    - Hello, world! -
    - Hello, world!

    - -

    - Getting Started -

    - -

    Here’s what you need to know:

    - -
      -
    1. First important point
    2. -
    3. Second crucial thing
    4. -
    5. Don’t forget this
    6. -
    - -

    - Key Features -

    - -

    Our product offers:

    - - - -

    Get Basecamp

    - -

    - Important Note -

    - -

    Here’s some -
    - multiline text. -
    - It has line breaks -
    - in-between.

    - -
    - Please read this carefully - It contains vital information - That you shouldn’t miss -
    - -

    - Placeholder image -

    - -

    - Important Note -

    - -

    - Placeholder image -

    - -
      -
    1. Remember to save
    2. -
    3. Back up your work
    4. -
    - -

    Get Basecamp

    - - - - - - - - - - - - -
    Table 1 Header 1Table 1 Header 2
    Table 1 Data 1Table 1 Data 2
    Table 1 Data 3Table 1 Data 4
    - -

    Some content between tables...

    - - - - - - - - - - - - -
    Table 2 Header 1Table 2 Header 2
    Table 2 Data 1Table 2 Data 2
    Table 2 Data 3Table 2 Data 4
    - -
    class Post < ApplicationRecord
    -          def title
    -            "foo"
    -          end
    -        end
    -        
    - -

    puts "Hello, world!"

    - -

    Thanks for reading!

    - -
    - Summary - Details -
    - HTML - end - - def markdown_content - <<~MARKDOWN - # Welcome to My Document - - This is an introduction paragraph with some text. - - --- - - ***Hello, world!*** **Hello, world!** *Hello, world!* ~~Hello, world!~~ ==Hello, world!== - - ***Hello, world!*** - **Hello, world!** - *Hello, world!* - ~~Hello, world!~~ - ==Hello, world!== - - ## Getting Started - - Here’s what you need to know: - - 1. First important point - 2. Second crucial thing - 3. Don’t forget this - - ## Key Features - - Our product offers: - - - Simple interface - - Powerful features - - Great documentation - - [Get Basecamp](https://basecamp.com) - - ### Important Note - - Here’s some - multiline text. - It has line breaks - in-between. - - > Please read this carefully - > It contains vital information - > That you shouldn’t miss - - ![Placeholder image](https://placehold.co/600x400) - - ## Important Note - - ![Placeholder image](https://placehold.co/400x400) - - 1. Remember to save - 2. Back up your work - - [Get Basecamp](https://basecamp.com) - - | Table 1 Header 1 | Table 1 Header 2 | - |-|-| - | Table 1 Data 1 | Table 1 Data 2 | - | Table 1 Data 3 | Table 1 Data 4 | - - Some content between tables... - - | Table 2 Header 1 | Table 2 Header 2 | - |------------------|------------------| - | Table 2 Data 1 | Table 2 Data 2 | - | Table 2 Data 3 | Table 2 Data 4 | - - ```rb - class Post < ApplicationRecord - def title - "foo" - end - end - ``` - - `puts "Hello, world!"` - - Thanks for reading! - -
    - Summary - Details -
    - MARKDOWN - end -end diff --git a/test/models/bubble_test.rb b/test/models/bubble_test.rb index fdfd65c82..d890ee19a 100644 --- a/test/models/bubble_test.rb +++ b/test/models/bubble_test.rb @@ -10,7 +10,7 @@ class BubbleTest < ActiveSupport::TestCase bubbles(:logo).capture Comment.new(body: "Agreed.") end - assert_equal "Agreed.\n", bubbles(:logo).messages.last.messageable.body.to_plain_text + assert_equal "Agreed.", bubbles(:logo).messages.last.messageable.body.to_plain_text end test "boosting" do