html-renderer 0.0.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gemspec +5 -4
- data/.gitignore +1 -0
- data/README.md +23 -5
- data/Rakefile +9 -4
- data/TODO.md +14 -0
- data/VERSION +1 -1
- data/lib/html-renderer.rb +5 -0
- data/{examples/ansi_renderer.rb → lib/html-renderer/ansi.rb} +20 -14
- data/lib/html-renderer/base.rb +30 -29
- data/lib/html-renderer/debug.rb +9 -0
- data/lib/html-renderer/html_parser.rb +105 -0
- data/{examples/plain_text_renderer.rb → lib/html-renderer/text.rb} +3 -7
- data/{examples → test}/test.html +0 -0
- data/test/test.rb +15 -0
- metadata +34 -17
- data/examples/debug_renderer.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3c17a79e5d484dfafbdf2161683d28c8ee9d24b3ec099c7e392236fe167f547
|
4
|
+
data.tar.gz: 4c0cc61cc44691ab2e35b2c289f352fe17aec1d499cd01d86b6a01f4a3a9dab6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52f7781a488c72b42398dad8a8a10393a3ccc1eca7147696e94398d28633d57956133a05c026364c8125b5dfcdf333a6772ec44ee786ced02ceb130e2c6c1ce2
|
7
|
+
data.tar.gz: 228891adf55ef0e77c958399071c0eb2a4acbc2620e3681aa59b65b56bd47dc308e5cee5b968043603388cd92e44443b7906acf93963c69513d94cceafa3f0b4
|
data/.gemspec
CHANGED
@@ -12,9 +12,10 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.email = "chris@ill-logic.com"
|
13
13
|
s.authors = ["epitron"]
|
14
14
|
|
15
|
-
s.files
|
15
|
+
s.files = `git ls`.lines.map(&:strip)
|
16
16
|
s.extra_rdoc_files = ["README.md", "LICENSE"]
|
17
|
-
|
18
|
-
s.add_dependency "oga"
|
19
|
-
s.
|
17
|
+
|
18
|
+
s.add_dependency "oga"
|
19
|
+
s.add_dependency "terminal-table", "~> 1.8"
|
20
|
+
s.add_dependency "term-ansicolor", "~> 1.7"
|
20
21
|
end
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/pkg/
|
data/README.md
CHANGED
@@ -2,11 +2,29 @@
|
|
2
2
|
|
3
3
|
## Overview
|
4
4
|
|
5
|
-
|
5
|
+
An extensible HTML renderer.
|
6
6
|
|
7
|
-
|
7
|
+
Comes with two built-in renderers:
|
8
|
+
* `HTMLRenderer::ANSI` (outputs colored text to the termnial)
|
9
|
+
* `HTMLRenderer::Text` (outputs plain text)
|
8
10
|
|
9
|
-
##
|
11
|
+
## Usage
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
Render to ANSI:
|
14
|
+
```
|
15
|
+
ansi_text = HTMLRenderer::ANSI.render("<b>hello html</b>")
|
16
|
+
ansi_text = HTMLRenderer::ANSI.render(open("file.html"))
|
17
|
+
```
|
18
|
+
|
19
|
+
Render to plain text:
|
20
|
+
```
|
21
|
+
plain_text = HTMLRenderer::Text.render(open("file.html"))
|
22
|
+
```
|
23
|
+
|
24
|
+
## Extending it
|
25
|
+
|
26
|
+
The API design uses the same philosophy as [RedCarpet](https://github.com/vmg/redcarpet).
|
27
|
+
|
28
|
+
To create a new renderer, subclass `HTMLRenderer::Base`, then add a method to handle each type of element. Whatever the method returns is output by the renderer.
|
29
|
+
|
30
|
+
Example renderer: [HTMLRenderer::ANSI](https://github.com/epitron/html-renderer/blob/master/lib/html-renderer/ansi.rb)
|
data/Rakefile
CHANGED
@@ -1,14 +1,13 @@
|
|
1
|
-
|
2
|
-
gem_version = File.read("VERSION").strip
|
1
|
+
gem = eval(File.read '.gemspec')
|
3
2
|
|
4
|
-
gemfile = "#{
|
3
|
+
gemfile = "#{gem.name}-#{gem.version}.gem"
|
5
4
|
|
6
5
|
task :build do
|
7
6
|
system "gem build .gemspec"
|
8
7
|
system "mkdir pkg/" unless File.directory? "pkg"
|
9
8
|
system "mv #{gemfile} pkg/"
|
10
9
|
end
|
11
|
-
|
10
|
+
|
12
11
|
task :release => :build do
|
13
12
|
system "gem push pkg/#{gemfile}"
|
14
13
|
end
|
@@ -18,3 +17,9 @@ task :gem => :build
|
|
18
17
|
task :install => :build do
|
19
18
|
system "gem install pkg/#{gemfile}"
|
20
19
|
end
|
20
|
+
|
21
|
+
task :test do
|
22
|
+
Dir.chdir "test"
|
23
|
+
$: << "../lib"
|
24
|
+
load "test.rb"
|
25
|
+
end
|
data/TODO.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# TODO list
|
2
|
+
|
3
|
+
## Base class
|
4
|
+
|
5
|
+
! add HTMLRenderer.reflow_paragraph
|
6
|
+
* #render takes options (and passes them to subclasses)
|
7
|
+
* :debug option (hide parse errors unless 'true')
|
8
|
+
|
9
|
+
## ANSI renderer
|
10
|
+
|
11
|
+
* use HTMLRenderer.reflow_paragraph
|
12
|
+
* :wrap option (with optional terminal width)
|
13
|
+
* Better table renderer (with line-drawing)
|
14
|
+
* Follow `<table>` style attributes when drawing tables (eg: borders)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.3
|
data/lib/html-renderer.rb
CHANGED
@@ -1,16 +1,24 @@
|
|
1
1
|
require 'html-renderer'
|
2
2
|
require 'ansi/mixin'
|
3
3
|
require 'terminal-table'
|
4
|
-
require 'coderay'
|
5
4
|
|
6
|
-
|
7
|
-
include ANSI::Mixin
|
5
|
+
###########################################################################
|
8
6
|
|
9
|
-
|
7
|
+
module ANSI::Mixin
|
8
|
+
def grey; ANSI::Code.bold { ANSI::Code.black { to_s } }; end
|
10
9
|
end
|
11
10
|
|
11
|
+
module HTMLRenderer::ANSIStrings
|
12
|
+
refine String do
|
13
|
+
include ANSI::Mixin
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
###########################################################################
|
18
|
+
|
19
|
+
class HTMLRenderer::ANSI < HTMLRenderer::Base
|
12
20
|
|
13
|
-
|
21
|
+
using HTMLRenderer::ANSIStrings
|
14
22
|
|
15
23
|
private
|
16
24
|
|
@@ -32,6 +40,9 @@ private
|
|
32
40
|
s&.downcase&.scan(/\w+/)&.join
|
33
41
|
end
|
34
42
|
|
43
|
+
def subscript(s)
|
44
|
+
"[#{s}]"
|
45
|
+
end
|
35
46
|
|
36
47
|
public
|
37
48
|
|
@@ -78,13 +89,7 @@ public
|
|
78
89
|
end
|
79
90
|
|
80
91
|
def block_code(code, language)
|
81
|
-
|
82
|
-
|
83
|
-
language = language[1..-1] if language[0] == "." # strip leading "."
|
84
|
-
language = :cpp if language == "C++"
|
85
|
-
|
86
|
-
require 'coderay'
|
87
|
-
"#{indent CodeRay.scan(code, language).term, 4}\n"
|
92
|
+
code.bold.cyan + "\n"
|
88
93
|
end
|
89
94
|
|
90
95
|
def block_quote(text)
|
@@ -102,7 +107,7 @@ public
|
|
102
107
|
when 1 then title.bold.yellow
|
103
108
|
when 2 then title.bold.cyan
|
104
109
|
when 3 then title.bold.blue
|
105
|
-
else title.
|
110
|
+
else title.magenta
|
106
111
|
end
|
107
112
|
|
108
113
|
"#{bar}\n #{title}\n#{bar}\n\n"
|
@@ -174,7 +179,8 @@ public
|
|
174
179
|
|
175
180
|
end
|
176
181
|
|
182
|
+
###########################################################################
|
177
183
|
|
178
184
|
if __FILE__ == $0
|
179
|
-
puts
|
185
|
+
puts HTMLRenderer::ANSI.render(open(ARGV.first || "test.html"))
|
180
186
|
end
|
data/lib/html-renderer/base.rb
CHANGED
@@ -1,30 +1,25 @@
|
|
1
1
|
######################################################################################
|
2
2
|
require 'oga'
|
3
3
|
######################################################################################
|
4
|
-
#
|
5
|
-
# TODOs:
|
6
|
-
# - Streaming output (yield every paragraph/div/header)
|
7
|
-
# - Embed into 'c' tool (for rendering raw HTML blocks)
|
8
|
-
#
|
9
|
-
######################################################################################
|
10
|
-
|
11
|
-
class String
|
12
4
|
|
13
|
-
|
14
|
-
gsub(/\s+/, ' ').strip
|
15
|
-
end
|
5
|
+
module HTMLRenderer
|
16
6
|
|
17
|
-
|
7
|
+
module STDLIBRefinements
|
8
|
+
refine String do
|
9
|
+
def tighten
|
10
|
+
gsub(/\s+/, ' ').strip
|
11
|
+
end
|
18
12
|
|
19
|
-
end
|
13
|
+
def blank?; !!self[/[^\s]/]; end
|
14
|
+
end
|
20
15
|
|
21
|
-
|
22
|
-
|
23
|
-
end
|
16
|
+
refine NilClass do
|
17
|
+
def blank?; true; end
|
18
|
+
end
|
19
|
+
end
|
24
20
|
|
25
|
-
|
21
|
+
using STDLIBRefinements
|
26
22
|
|
27
|
-
module HTMLRenderer
|
28
23
|
|
29
24
|
class State
|
30
25
|
attr_accessor :list_order
|
@@ -65,14 +60,16 @@ private
|
|
65
60
|
content.blank? ? normal_text(content) : nil
|
66
61
|
|
67
62
|
when Oga::XML::Element
|
68
|
-
case node.name
|
63
|
+
case node.name.downcase
|
69
64
|
when "a"
|
70
65
|
url = node["href"]
|
71
66
|
title = node["title"]
|
72
67
|
name = node["name"]
|
73
68
|
content = render_children(node, state)
|
74
69
|
|
75
|
-
if
|
70
|
+
if title.blank? and url.blank? and name.blank?
|
71
|
+
content
|
72
|
+
elsif name and not url
|
76
73
|
anchor(name, title, content)
|
77
74
|
else
|
78
75
|
link(url, title, content)
|
@@ -96,6 +93,8 @@ private
|
|
96
93
|
emphasis(render_children(node, state))
|
97
94
|
when "sup"
|
98
95
|
superscript(render_children(node, state))
|
96
|
+
when "sub"
|
97
|
+
subscript(render_children(node, state))
|
99
98
|
when "u"
|
100
99
|
underline(render_children(node, state))
|
101
100
|
when "br"
|
@@ -118,7 +117,7 @@ private
|
|
118
117
|
list_item(render_children(node, state), state.list_order)
|
119
118
|
|
120
119
|
when "code"
|
121
|
-
block_code(render_children(node, state))
|
120
|
+
block_code(render_children(node, state), nil)
|
122
121
|
when "blockquote"
|
123
122
|
block_quote(render_children(node, state))
|
124
123
|
|
@@ -149,20 +148,22 @@ private
|
|
149
148
|
table(header, rows)
|
150
149
|
|
151
150
|
when "html", "body", "nav", "span", "form", "label", "input", "button", "section", "fieldset",
|
152
|
-
"menu", "article", "header", "time", "aside", "footer", "nobr", "wbr",
|
153
|
-
"table", "tr", "td", "th", "thead", "tbody", "noscript", "select",
|
154
|
-
"address"
|
151
|
+
"pre", "menu", "article", "header", "time", "aside", "footer", "nobr", "wbr",
|
152
|
+
"table", "tr", "td", "th", "tt", "thead", "tbody", "noscript", "select",
|
153
|
+
"address", "center", "small"
|
155
154
|
render_children(node, state)
|
156
155
|
|
157
156
|
when "head", "script", "link", "style"
|
158
|
-
#
|
159
|
-
|
157
|
+
#
|
158
|
+
# don't render anything
|
159
|
+
#
|
160
160
|
else
|
161
|
-
raise "Unrecognized HTML tag: #{node.name} -> #{node.inspect}"
|
161
|
+
# raise "Unrecognized HTML tag: #{node.name} -> #{node.inspect}"
|
162
|
+
$stderr.puts "Unrecognized HTML tag: #{node.name} -> #{node.inspect}"
|
163
|
+
render_children(node, state)
|
162
164
|
end
|
163
165
|
|
164
|
-
when Oga::XML::Comment
|
165
|
-
# skip it
|
166
|
+
when Oga::XML::Comment, Oga::XML::Cdata
|
166
167
|
|
167
168
|
else
|
168
169
|
raise "Unhandled Oga node type: #{node.class}"
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
class Stack < Array
|
4
|
+
alias_method :top, :last
|
5
|
+
alias_method :peek, :last
|
6
|
+
end
|
7
|
+
|
8
|
+
class String
|
9
|
+
def recursive_inspect(depth)
|
10
|
+
(" "*depth)+inspect
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class HTMLParser
|
15
|
+
|
16
|
+
OPEN_TAG_RE = %r{<([^>]+)>}
|
17
|
+
CLOSE_TAG_RE = %r{</([^>]+)>}
|
18
|
+
TEXT_RE = %r{[^<]+}
|
19
|
+
ATTR_RE = %r{(\w+)=(?:"([^"]+)"|'([^']+)'|(\w+))}
|
20
|
+
|
21
|
+
class Tag
|
22
|
+
|
23
|
+
attr_accessor :name, :attrs, :children
|
24
|
+
|
25
|
+
def self.from_str(s)
|
26
|
+
name, rest = s.split(/\s+/, 2)
|
27
|
+
|
28
|
+
if rest
|
29
|
+
attrs = rest.scan(HTMLParser::ATTR_RE).flatten.compact.each_slice(2).to_h
|
30
|
+
else
|
31
|
+
attrs = {}
|
32
|
+
end
|
33
|
+
new(name, attrs)
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(name, attrs={}, children=[])
|
37
|
+
@name = name
|
38
|
+
@attrs = attrs
|
39
|
+
@children = children
|
40
|
+
end
|
41
|
+
|
42
|
+
def recursive_inspect(depth=0)
|
43
|
+
curdent = " "*depth
|
44
|
+
indent = " "*(depth+1)
|
45
|
+
"#{curdent}<#{name} #{attrs}>\n#{indent}#{children.map{|c| c.recursive_inspect(depth+1)}}\n#{curdent}</#{name}>"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize(html)
|
51
|
+
@s = StringScanner.new(html)
|
52
|
+
# @s = html
|
53
|
+
end
|
54
|
+
|
55
|
+
def each_tag
|
56
|
+
until @s.eos?
|
57
|
+
if @s.scan(CLOSE_TAG_RE)
|
58
|
+
yield [:close_tag, @s.captures.first]
|
59
|
+
elsif @s.scan(OPEN_TAG_RE)
|
60
|
+
tag = Tag.from_str(@s.captures.first)
|
61
|
+
yield [:open_tag, tag]
|
62
|
+
elsif @s.scan(TEXT_RE)
|
63
|
+
yield [:text, @s.matched]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def as_tree
|
69
|
+
tree.map { |e| e.recursive_inspect }
|
70
|
+
end
|
71
|
+
|
72
|
+
def tree
|
73
|
+
stack = Stack.new
|
74
|
+
stack.push Tag.new("root")
|
75
|
+
|
76
|
+
each_tag do |type, elem|
|
77
|
+
case type
|
78
|
+
when :text
|
79
|
+
text = elem.strip
|
80
|
+
stack.top.children << text unless text.empty?
|
81
|
+
when :open_tag
|
82
|
+
stack.top.children << elem
|
83
|
+
stack.push elem
|
84
|
+
when :close_tag
|
85
|
+
stack.pop
|
86
|
+
else
|
87
|
+
raise "wat"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
stack
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
unless file = ARGV.first
|
97
|
+
file = "test.html"
|
98
|
+
end
|
99
|
+
|
100
|
+
html = File.read(file)
|
101
|
+
|
102
|
+
r = HTMLParser.new(html)
|
103
|
+
r.each_tag{|t| p t}
|
104
|
+
|
105
|
+
# puts r.as_tree
|
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'html-renderer'
|
2
2
|
|
3
3
|
#
|
4
|
-
#
|
4
|
+
# Strips out everything but the plain text.
|
5
5
|
#
|
6
|
-
class
|
6
|
+
class HTMLRenderer::Text < HTMLRenderer::Base
|
7
|
+
|
7
8
|
# Methods where the first argument is the text content
|
8
9
|
[
|
9
10
|
# block-level calls
|
@@ -65,8 +66,3 @@ class PlainTextRenderer < HTMLRenderer::Base
|
|
65
66
|
content + "\t"
|
66
67
|
end
|
67
68
|
end
|
68
|
-
|
69
|
-
|
70
|
-
if __FILE__ == $0
|
71
|
-
puts PlainTextRenderer.render(open("test.html"))
|
72
|
-
end
|
data/{examples → test}/test.html
RENAMED
File without changes
|
data/test/test.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'html-renderer'
|
2
|
+
require 'html-renderer/debug'
|
3
|
+
|
4
|
+
[
|
5
|
+
HTMLRenderer::ANSI,
|
6
|
+
HTMLRenderer::Text,
|
7
|
+
HTMLRenderer::DebugRenderer,
|
8
|
+
].each do |renderer|
|
9
|
+
puts
|
10
|
+
puts "=== #{renderer} =============================="
|
11
|
+
puts
|
12
|
+
puts renderer.render(open("test.html"))
|
13
|
+
puts
|
14
|
+
puts
|
15
|
+
end
|
metadata
CHANGED
@@ -1,43 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: html-renderer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- epitron
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oga
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: terminal-table
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - "~>"
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
33
|
+
version: '1.8'
|
20
34
|
type: :runtime
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
40
|
+
version: '1.8'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
42
|
+
name: term-ansicolor
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1'
|
34
|
-
type: :
|
47
|
+
version: '1.7'
|
48
|
+
type: :runtime
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1'
|
54
|
+
version: '1.7'
|
41
55
|
description: Easily implement an HTML renderer by creating a subclass and adding some
|
42
56
|
methods, similar to RedCarpet. (Examples are included for rendering HTML to ANSI
|
43
57
|
and plain text.)
|
@@ -49,21 +63,25 @@ extra_rdoc_files:
|
|
49
63
|
- LICENSE
|
50
64
|
files:
|
51
65
|
- ".gemspec"
|
66
|
+
- ".gitignore"
|
52
67
|
- LICENSE
|
53
68
|
- README.md
|
54
69
|
- Rakefile
|
70
|
+
- TODO.md
|
55
71
|
- VERSION
|
56
|
-
- examples/ansi_renderer.rb
|
57
|
-
- examples/debug_renderer.rb
|
58
|
-
- examples/plain_text_renderer.rb
|
59
|
-
- examples/test.html
|
60
72
|
- lib/html-renderer.rb
|
73
|
+
- lib/html-renderer/ansi.rb
|
61
74
|
- lib/html-renderer/base.rb
|
75
|
+
- lib/html-renderer/debug.rb
|
76
|
+
- lib/html-renderer/html_parser.rb
|
77
|
+
- lib/html-renderer/text.rb
|
78
|
+
- test/test.html
|
79
|
+
- test/test.rb
|
62
80
|
homepage: http://github.com/epitron/html-renderer/
|
63
81
|
licenses:
|
64
82
|
- WTFPL
|
65
83
|
metadata: {}
|
66
|
-
post_install_message:
|
84
|
+
post_install_message:
|
67
85
|
rdoc_options: []
|
68
86
|
require_paths:
|
69
87
|
- lib
|
@@ -78,9 +96,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
96
|
- !ruby/object:Gem::Version
|
79
97
|
version: '0'
|
80
98
|
requirements: []
|
81
|
-
|
82
|
-
|
83
|
-
signing_key:
|
99
|
+
rubygems_version: 3.1.3
|
100
|
+
signing_key:
|
84
101
|
specification_version: 4
|
85
102
|
summary: HTML Renderer
|
86
103
|
test_files: []
|
data/examples/debug_renderer.rb
DELETED