jay_flavored_markdown 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,391 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2016 Nomura Laboratory
5
+ #
6
+ # This file is NOT part of kramdown and is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ require 'kramdown/parser'
11
+ require 'kramdown/converter'
12
+ require 'kramdown/utils'
13
+
14
+ # block 間の明示的な改行は, :blank エレメントとしてパーズされる
15
+ # span 中の改行は, :text エレメント中に残り,かつ,:br が挟まる
16
+ #
17
+ # + :blank は,そのまま反映する
18
+ # + span 中の改行と :br は全て削る
19
+ # + block の後には改行を1つ入れるが,block がネストしている場合は,改行が続くので,1つに集約する
20
+ #
21
+ # block は,通常インデントする必要はない.
22
+ # :root, :blank, :p, :header, :hr, :table, :tr, :td
23
+ #
24
+ # 以下のブロックは,インデントをする
25
+ # :blockquote, :codeblock
26
+ #
27
+ # :ul, :ol,:li, :dl はインデントする
28
+ #
29
+ # dt (term), dd (definition)
30
+ #
31
+ # li は,中のブロック
32
+ # <ul> や <p> のように 実体のない (transparent) ブロックは,何もしない
33
+ # <li> のように,ぶら下げるブロックはインデントしない
34
+
35
+ module Kramdown
36
+ module Converter
37
+
38
+ # Converts a Kramdown::Document to ASCII Plain Text.
39
+ #
40
+ # You can customize this converter by sub-classing it and overriding the +convert_NAME+
41
+ # methods. Each such method takes the following parameters:
42
+ #
43
+ # [+el+] The element of type +NAME+ to be converted.
44
+ #
45
+ # [+indent+] A number representing the current amount of spaces for indent (only used for
46
+ # block-level elements).
47
+ #
48
+ # The return value of such a method has to be a string containing the element +el+ formatted as
49
+ # HTML element.
50
+ class Ascii < Base
51
+
52
+ MAX_COLUMN = 80
53
+
54
+ include ::Kramdown::Utils::Html
55
+ include ::Kramdown::Parser::Html::Constants
56
+
57
+ # The amount of indentation used when nesting HTML tags.
58
+ attr_accessor :indent
59
+
60
+ # Initialize the ASCII converter with the given Kramdown document +doc+.
61
+ def initialize(root, options)
62
+ super
63
+ @indent = 2
64
+ @stack = []
65
+ @xref_table = {}
66
+ ref_visitor = ReferenceVisitor.new
67
+ @root = ref_visitor.traverse(@root)
68
+ @xref_table = ref_visitor.xref_table
69
+ @item_table = ref_visitor.item_table
70
+ @section_table = ref_visitor.section_table
71
+ debug_dump_tree(@root) if $JAY_DEBUG
72
+ @root
73
+ end
74
+
75
+ # Dispatch the conversion of the element +el+ to a +convert_TYPE+ method using the +type+ of
76
+ # the element.
77
+ def convert(el, indent = 0)
78
+ send(DISPATCHER[el.type], el, indent)
79
+ end
80
+
81
+ # The mapping of element type to conversion method.
82
+ DISPATCHER = Hash.new {|h,k| h[k] = "convert_#{k}"}
83
+
84
+ ################################################################
85
+ private
86
+
87
+ # Format the given element as span text.
88
+ def format_as_span(name, attr, body)
89
+ return "<SPAN:#{name}>#{body}</SPAN:#{name}>" if $JAY_DEBUG
90
+ return body.to_s.gsub(/\n */, "")
91
+ end
92
+
93
+ # indent を付加した span の列を作る
94
+ # 前提として span 内には block はない
95
+ # span は,行頭にある (block に直接内包される)か,改行を含むものしか indent されないので注意すること.
96
+ def render_span(el, indent)
97
+ el.children.each do |child|
98
+ body << send(DISPATCHER[child.type], child, indent)
99
+ end
100
+ # XXX
101
+ end
102
+
103
+ # Format the given element as block text.
104
+ # current_indent は自身のインデント幅で,ブロック内の
105
+ #
106
+ # render_block: block エレメントをレンダリングする.
107
+ #
108
+ # 前提: span の子供には span しか入っていない (block は,来ない)
109
+ # span の子供が block になるような記述ができるのか不明 (tree をチェックして waring を出すほうがいいかも)
110
+ #
111
+ # 自分(block) について,子供に span があったら,つなげて indent する
112
+ #
113
+ # DISPATCHER を通して作った str は,indent だけのインデントを持つブロックを返すという前提
114
+ # str = send(DISPATCHER[inner_el.type], inner_el, indent)
115
+ def render_block(el, current_indent, add_indent = 0, bullet = nil)
116
+ body = ""
117
+ span = ""
118
+
119
+ orig_indent = current_indent
120
+ current_indent = [(add_indent + current_indent), 0].max
121
+
122
+ el.children.each do |inner_el|
123
+ str = send(DISPATCHER[inner_el.type], inner_el, current_indent)
124
+
125
+ if el.ancestor?(:blockquote)
126
+ body << str # no wrap
127
+ elsif Element.category(inner_el) == :span
128
+ span << str
129
+ else
130
+ # body << wrap_block(span, current_indent, 60) if span.length > 0
131
+ body << span
132
+ body << str
133
+ span = ""
134
+ end
135
+ end
136
+ if span.length > 0
137
+ # body << wrap_block(span, current_indent, 60)
138
+ body << span
139
+ span = ""
140
+ end
141
+
142
+ body = add_bullet_to_block(bullet, body, orig_indent) if bullet
143
+ body = add_indent_to_block(add_indent, body) if add_indent > 0
144
+ # body = remove_indent(body, 2) if bullet && bullet.length > 2 && ancestor?(el, :li)
145
+ body = body.sub(/[\s]*\Z/, "") + "\n"
146
+
147
+ return "<BLOCK:#{el.type}>#{body}</BLOCK:#{el.type}>\n" if $JAY_DEBUG
148
+ return "#{body}"
149
+ end
150
+
151
+ # XXX この中で span に indent を付けるのはおかしい
152
+ #
153
+ def wrap_block(body, indent, max_columns)
154
+ # puts "WRAP_BLOCK: #{body}, #{indent}"
155
+ body = remove_indent(body, indent)
156
+ body = wrap(body, max_columns - indent)
157
+ body = add_indent_to_block(indent, body)
158
+ # puts "WRAPed_BLOCK: #{body}, #{indent}"
159
+ body
160
+ end
161
+
162
+ def remove_indent(body, indent)
163
+ body.gsub(/^#{" "*indent}/, "")
164
+ end
165
+
166
+ def wrap(body, width)
167
+ body = body.gsub(/[\r\n]/, "")
168
+ string, length = "", 0
169
+ body.each_char.map do |c|
170
+ string << c
171
+ length += (c.bytesize == 1 ? 1 : 2)
172
+ if length > width
173
+ string << "\n"
174
+ length = 0
175
+ end
176
+ end
177
+ string
178
+ end
179
+
180
+ ################################################################
181
+ # conver each element
182
+
183
+ def convert_blank(el, indent)
184
+ render_block(el, indent)
185
+ end
186
+
187
+ def convert_text(el, indent)
188
+ format_as_span("text", nil, el.value)
189
+ end
190
+
191
+ def convert_p(el, indent)
192
+ render_block(el, indent)
193
+ end
194
+
195
+ def convert_codeblock(el, indent)
196
+ "-----------------------\n" + el.value.to_s + "-----------------------\n"
197
+ end
198
+
199
+ def convert_blockquote(el, indent)
200
+ "-----------------------\n" + render_block(el, indent, 4) + "-----------------------"
201
+ end
202
+
203
+ def convert_header(el, indent)
204
+ render_block(el, indent, 0, "#{el.value.full_mark}")
205
+ end
206
+
207
+ def convert_hr(el, indent)
208
+ "-" * MAX_COLUMN
209
+ end
210
+
211
+ def convert_ul(el, indent)
212
+ render_block(el, indent)
213
+ end
214
+
215
+ def convert_dl(el, indent)
216
+ format_as_block("dl", nil, render_block(el, indent), indent)
217
+ end
218
+
219
+ def convert_li(el, indent)
220
+ output = ''
221
+
222
+ bullet = el.value ? "(#{el.value.mark})" : "*"
223
+
224
+ output << "<BLOCK:li>" if $JAY_DEBUG
225
+ output << render_block(el, indent, 0, bullet)
226
+ output << "</BLOCK:li>" if $JAY_DEBUG
227
+ output
228
+ end
229
+
230
+ def add_bullet_to_block(bullet, body, indent)
231
+ hang = 0
232
+ bullet_offset = " " * (bullet.size + 1)
233
+ indent_string = " " * indent
234
+ hang_string = " " * hang
235
+
236
+ body = body.sub(/^#{indent_string}/, "#{indent_string}#{bullet} ")
237
+ body = body.gsub(/\n/, "\n" + bullet_offset)
238
+ body = body.gsub(/^#{hang_string}/, "") if hang > 0
239
+ body
240
+ end
241
+
242
+ def add_indent_to_block(indent, body)
243
+ spc = " " * indent
244
+ body = "#{spc}#{body}".gsub(/\n/, "\n" + spc)
245
+ end
246
+
247
+ def convert_dt(el, indent)
248
+ render_block(el, indent)
249
+ end
250
+
251
+ def convert_html_element(el, indent)
252
+ ""
253
+ end
254
+
255
+ def convert_xml_comment(el, indent)
256
+ ""
257
+ end
258
+
259
+ def convert_table(el, indent)
260
+ render_block(el, indent)
261
+ end
262
+
263
+ def convert_td(el, indent)
264
+ render_block(el, indent)
265
+ end
266
+
267
+ def convert_comment(el, indent)
268
+ render_block(el, indent)
269
+ end
270
+
271
+ def convert_br(el, indent)
272
+ "\n" # "\n"
273
+ end
274
+
275
+ def convert_a(el, indent)
276
+ if (c = el.children.first) && c.type == :text && c.value
277
+ "[" + c.value + "]"
278
+ else
279
+ el.attr["href"].to_s
280
+ end
281
+ end
282
+
283
+ def convert_img(el, indent)
284
+ el.attr["href"].to_s
285
+ end
286
+
287
+ def convert_codespan(el, indent)
288
+ "-----------------------\n" + el.value.to_s + "-----------------------"
289
+ end
290
+
291
+ def convert_footnote(el, indent)
292
+ ""
293
+ end
294
+
295
+ def convert_raw(el, indent)
296
+ el.value + (el.options[:category] == :block ? "\n" : '')
297
+ end
298
+
299
+ def convert_em(el, indent)
300
+ format_as_span(el.type, el.attr, render_block(el, indent))
301
+ end
302
+
303
+ # ;gt
304
+ def convert_entity(el, indent)
305
+ format_as_span(el.type, el.attr, render_block(el, indent))
306
+ end
307
+
308
+ def convert_typographic_sym(el, indent)
309
+ {
310
+ :mdash => "---",
311
+ :ndash => "--",
312
+ :hellip => "...",
313
+ :laquo_space => "<<",
314
+ :raquo_space => ">>",
315
+ :laquo => "<< ",
316
+ :raquo => " >>",
317
+ }[el.value]
318
+ end
319
+
320
+ def convert_smart_quote(el, indent)
321
+ {
322
+ :lsquo => "'",
323
+ :rsquo => "'",
324
+ :ldquo => '"',
325
+ :rdquo => '"',
326
+ }[el.value]
327
+ end
328
+
329
+ def convert_math(el, indent)
330
+ format_as_span(el.type, el.attr, render_block(el, indent))
331
+ end
332
+
333
+ def convert_abbreviation(el, indent)
334
+ title = @root.options[:abbrev_defs][el.value]
335
+ attr = @root.options[:abbrev_attr][el.value].dup
336
+ attr['title'] = title unless title.empty?
337
+ format_as_span("abbr", attr, el.value)
338
+ end
339
+
340
+ def convert_root(el, indent)
341
+ render_block(el, indent)
342
+ end
343
+
344
+ alias :convert_ol :convert_ul
345
+ alias :convert_dd :convert_li
346
+ alias :convert_xml_pi :convert_xml_comment
347
+ alias :convert_thead :convert_table
348
+ alias :convert_tbody :convert_table
349
+ alias :convert_tfoot :convert_table
350
+ alias :convert_tr :convert_table
351
+ alias :convert_strong :convert_em
352
+
353
+ ################################################################
354
+
355
+ def convert_ref(el, indent)
356
+ if @xref_table[el.value]
357
+ return "(#{@xref_table[el.value].full_mark})"
358
+ elsif el.value =~ /^(\++|-+)$/
359
+ parent = el.find_first_ancestor(:header) || el.find_first_ancestor(:li)
360
+ table = parent.type == :li ? @item_table : @section_table
361
+ rel_pos = ($1.include?("+") ? 1 : -1) * $1.length
362
+ idx = parent.options[:relative_position] + rel_pos
363
+ ref_el = idx >= 0 ? table[idx] : nil
364
+ return "(#{ref_el.value.full_mark})" if ref_el
365
+ end
366
+ "(???)"
367
+ end
368
+
369
+ def convert_label(el, indent)
370
+ ""
371
+ end
372
+
373
+ def convert_action_item(el, indent)
374
+ "-->(#{el.options[:assignee]})"
375
+ end
376
+
377
+ def convert_issue_link(el, indent)
378
+ el.options[:match]
379
+ end
380
+
381
+ def debug_dump_tree(tree, indent = 0)
382
+ STDERR.print " " * indent
383
+ STDERR.print "#{tree.type}(#{Element.category(tree)}) <<#{tree.value.to_s.gsub("\n", '\n')}>>\n"
384
+ tree.children.each do |c|
385
+ debug_dump_tree(c, indent + 2)
386
+ end
387
+ end
388
+
389
+ end # class Ascii
390
+ end # module Converter
391
+ end # module Kramdown
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JayFlavoredMarkdown
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "jay_flavored_markdown/version"
4
+ require_relative "jay_flavored_markdown/markdown_converter"
5
+ require_relative "jay_flavored_markdown/markdown_to_ascii"
6
+
7
+ module JayFlavoredMarkdown
8
+ class Error < StandardError; end
9
+ # Your code goes here...
10
+ end
metadata ADDED
@@ -0,0 +1,219 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jay_flavored_markdown
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nomura Laboratory
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-09-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: kramdown
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: kramdown-parser-gfm
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: html-pipeline
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.14.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.14.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rinku
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: gemoji
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sanitize
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rouge
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: activesupport
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 6.1.4
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 6.1.4
125
+ - !ruby/object:Gem::Dependency
126
+ name: rake
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '13.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '13.0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: minitest
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '5.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '5.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '1.7'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '1.7'
167
+ description: JayFlavoredMarkdown Converter
168
+ email:
169
+ - ''
170
+ executables:
171
+ - jay_flavored_markdown
172
+ extensions: []
173
+ extra_rdoc_files: []
174
+ files:
175
+ - ".github/workflows/main.yml"
176
+ - ".gitignore"
177
+ - ".rubocop.yml"
178
+ - CHANGELOG.md
179
+ - CODE_OF_CONDUCT.md
180
+ - Gemfile
181
+ - LICENSE.txt
182
+ - README.md
183
+ - Rakefile
184
+ - bin/console
185
+ - bin/setup
186
+ - exe/jay_flavored_markdown
187
+ - jay_flavored_markdown.gemspec
188
+ - lib/jay_flavored_markdown.rb
189
+ - lib/jay_flavored_markdown/markdown_converter.rb
190
+ - lib/jay_flavored_markdown/markdown_to_ascii.rb
191
+ - lib/jay_flavored_markdown/version.rb
192
+ homepage: https://github.com/nomlab/jay_flavored_markdown
193
+ licenses:
194
+ - MIT
195
+ metadata:
196
+ allowed_push_host: https://rubygems.org
197
+ homepage_uri: https://github.com/nomlab/jay_flavored_markdown
198
+ source_code_uri: https://github.com/nomlab/jay_flavored_markdown
199
+ changelog_uri: https://github.com/nomlab/jay_flavored_markdown/blob/master/CHANGELOG.md
200
+ post_install_message:
201
+ rdoc_options: []
202
+ require_paths:
203
+ - lib
204
+ required_ruby_version: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: 2.4.0
209
+ required_rubygems_version: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - ">="
212
+ - !ruby/object:Gem::Version
213
+ version: '0'
214
+ requirements: []
215
+ rubygems_version: 3.2.3
216
+ signing_key:
217
+ specification_version: 4
218
+ summary: JayFlavoredMarkdown Converter
219
+ test_files: []