hiki2md 0.2.0 → 0.3.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd9f235518be1faf5230ff7f4739a1fb311727ded398fe7c44f24b459c3ec3a0
4
- data.tar.gz: 47ae584b929f160ae9a680994dfebdacfc1fe12e7c9cb52470ae900fc0d8482a
3
+ metadata.gz: 4fc18ad51b63a8dde11dfe5ea4c04e4e8347e21457670edc8200acdaa125e8e9
4
+ data.tar.gz: f0bf7d53ac1bfd7a71a643ae9354f781fab199a29a07ea8225399831808ab2fb
5
5
  SHA512:
6
- metadata.gz: b0e0d03d869dfd64587ae2b782e56d6bf5fa88e4bb8bd0eba6736b00ff06c5f0b929e41577fdbeb4cdb3a01e272400b94e95ddf3633c2ad7a3be6f7d3e37d8be
7
- data.tar.gz: 98c7f8cda9a0d088cf26c4a4d23e340f5fbcb7b23e3cfe6e47794170166c6511c6c8324f798d9a07e8ea2a2f227c03670670b9f91fce4a6d8c07c58a65994727
6
+ metadata.gz: 147c40644b389860b2f9b0868235571ebd95ab005d6873910a4c54849d6a6f625619bf439e1679da0894a7908c9766fcc05d11f14f571d283492206bb19750b8
7
+ data.tar.gz: 61dd0a68d793fafa4c5320e72c3bd9c4f5f88288e5566686d2abbf56fe9bc734861de8bf75b70556f6d6477bda01ceda9f14a7d7830d4d6e1d5534774f7cc6e3
data/.travis.yml CHANGED
@@ -1,4 +1,6 @@
1
1
  language: ruby
2
+ dist: jammy
2
3
  rvm:
3
- - 2.2.3
4
- before_install: gem install bundler -v 1.10.6
4
+ - 3.1
5
+ - 3.4
6
+ before_install: gem install bundler -v 2.6.8
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hiki2md (0.1.3)
4
+ hiki2md (0.3.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -6,6 +6,8 @@ Converter of Hiki to Markdown.
6
6
 
7
7
  ## Installation
8
8
 
9
+ Hiki2md requires Ruby 2.2 or newer.
10
+
9
11
  Add this line to your application's Gemfile:
10
12
 
11
13
  ```ruby
@@ -22,7 +24,97 @@ Or install it yourself as:
22
24
 
23
25
  ## Usage
24
26
 
25
- TODO: Write usage instructions here
27
+ ### Ruby
28
+
29
+ ```ruby
30
+ require "hiki2md"
31
+
32
+ converter = Hiki2md.new(
33
+ interwiki_map: {
34
+ "Google" => "https://www.google.com/search?q=",
35
+ "Issues" => "https://example.com/issues/$1"
36
+ },
37
+ use_wiki_name: true,
38
+ preserve_plugins: false
39
+ )
40
+
41
+ markdown = converter.convert(hiki_source)
42
+ ```
43
+
44
+ Internal page links receive a `.md` extension. InterWiki values either have
45
+ the encoded page name appended, or can contain `$1` where the page name should
46
+ be inserted.
47
+
48
+ ### Command line
49
+
50
+ ```console
51
+ $ hiki2md [options] input.hiki
52
+ ```
53
+
54
+ Options:
55
+
56
+ - `--interwiki-map PATH`: load InterWiki prefixes from a YAML mapping.
57
+ - `--[no-]wiki-name`: enable or disable automatic WikiName links.
58
+ - `--preserve-plugins`: retain plugins as escaped Markdown text instead of
59
+ removing them.
60
+ - `-E`, `--input-encoding ENCODING`: override the detected source encoding.
61
+
62
+ The command writes UTF-8 Markdown. It automatically recognizes BOM-marked
63
+ Unicode input and the common Hiki encodings UTF-8, EUC-JP, Windows-31J, and
64
+ ISO-2022-JP. Since legacy encodings can be ambiguous, use
65
+ `--input-encoding` to specify the source explicitly when needed:
66
+
67
+ ```console
68
+ $ hiki2md --input-encoding EUC-JP TextFormattingRules.ja
69
+ ```
70
+
71
+ ### Supported Hiki syntax
72
+
73
+ The converter targets Hiki's default style and emits GitHub Flavored Markdown
74
+ (GFM).
75
+
76
+ | Hiki | Output |
77
+ | --- | --- |
78
+ | `!` through `!!!!!` | headings 2 through 6 |
79
+ | `*` / `#` | nested unordered / ordered lists |
80
+ | `''text''` / `'''text'''` | emphasis / strong emphasis |
81
+ | `==text==` / double-backtick text | strikethrough / inline code |
82
+ | `[[Page]]` / `[[label\|target]]` | page, URL, and InterWiki links |
83
+ | image URLs | Markdown images, including basename or labeled alt text |
84
+ | leading space or tab, `<<<` ... `>>>` | fenced code blocks |
85
+ | `""` | recursive blockquotes |
86
+ | `:term:description` | HTML definition lists |
87
+ | `\|\|cell` | raw HTML tables |
88
+ | `----` | horizontal rules |
89
+ | `// comment` | removed comments |
90
+
91
+ Hiki permits tables without a header row, while GFM pipe tables require one.
92
+ All Hiki tables are therefore emitted as raw HTML so ordinary `td` cells,
93
+ explicit `!` header cells, and `^` / `>` row and column spans retain their
94
+ meaning.
95
+
96
+ Hiki plugins are executable Ruby extensions and cannot be converted
97
+ generically. Plugin calls are removed by default. Use `preserve_plugins: true`
98
+ or `--preserve-plugins` to keep them as escaped Markdown text for manual
99
+ migration. Plugin-looking text inside preformatted blocks is always kept
100
+ literally.
101
+
102
+ ### Version 0.3 output changes
103
+
104
+ Version 0.3 favors the rendered meaning of Hiki over preserving the exact
105
+ Markdown source produced by 0.2. In particular, all tables now use raw HTML,
106
+ ordered-list indentation follows CommonMark, and Markdown-looking source text
107
+ is escaped when Hiki treats it as plain text. The legacy public `make_matrix`
108
+ and `make_table` helpers retain their 0.2 behavior.
109
+
110
+ Lists deeper than 100 levels use raw HTML to keep output growth bounded.
111
+ Uniform blockquotes may be arbitrarily deep; blocks containing more than 100
112
+ distinct quote depths raise `ArgumentError` instead of risking stack or CPU
113
+ exhaustion.
114
+
115
+ See Hiki's
116
+ [TextFormattingRules](https://hikiwiki.org/en/TextFormattingRules.html) for
117
+ the source syntax.
26
118
 
27
119
  ## Development
28
120
 
@@ -40,4 +132,3 @@ The gem is available as open source under the terms of the [MIT License](http://
40
132
  ## Inspired by
41
133
 
42
134
  https://github.com/masasuzu/p5-App-hiki2md
43
-
data/exe/hiki2md CHANGED
@@ -3,7 +3,88 @@ require 'hiki2md'
3
3
  require 'optparse'
4
4
  require 'yaml'
5
5
 
6
+ module Hiki2mdCommand
7
+ class InputEncodingError < StandardError; end
8
+
9
+ BOM_ENCODINGS = [
10
+ [[0x00, 0x00, 0xfe, 0xff].pack('C*'), Encoding::UTF_32],
11
+ [[0xff, 0xfe, 0x00, 0x00].pack('C*'), Encoding::UTF_32],
12
+ [[0xfe, 0xff].pack('C*'), Encoding::UTF_16],
13
+ [[0xff, 0xfe].pack('C*'), Encoding::UTF_16],
14
+ [[0xef, 0xbb, 0xbf].pack('C*'), Encoding::UTF_8]
15
+ ].freeze
16
+ AUTO_ENCODINGS = [
17
+ Encoding::UTF_8,
18
+ Encoding::EUC_JP,
19
+ Encoding::Windows_31J
20
+ ].freeze
21
+
22
+ module_function
23
+
24
+ def read_source(path, requested_encoding = nil)
25
+ bytes = File.binread(path)
26
+ encoding = if requested_encoding.nil? || requested_encoding.casecmp('auto').zero?
27
+ detect_encoding(bytes)
28
+ else
29
+ find_encoding(requested_encoding)
30
+ end
31
+
32
+ unless encoding
33
+ raise InputEncodingError,
34
+ "#{path}: unable to detect input encoding; " \
35
+ "use --input-encoding ENCODING"
36
+ end
37
+
38
+ source = bytes.dup.force_encoding(encoding)
39
+ unless source.valid_encoding?
40
+ raise InputEncodingError,
41
+ "#{path}: invalid byte sequence in #{encoding.name}"
42
+ end
43
+
44
+ source.encode(Encoding::UTF_8).sub(/\A\uFEFF/, '')
45
+ rescue Encoding::InvalidByteSequenceError,
46
+ Encoding::UndefinedConversionError => error
47
+ raise InputEncodingError,
48
+ "#{path}: cannot convert #{encoding.name} to UTF-8: #{error.message}"
49
+ end
50
+
51
+ def detect_encoding(bytes)
52
+ bom = BOM_ENCODINGS.find { |marker, _encoding| bytes.start_with?(marker) }
53
+ return bom[1] if bom
54
+
55
+ if iso_2022_jp?(bytes) && decodable_as?(bytes, Encoding::ISO_2022_JP)
56
+ return Encoding::ISO_2022_JP
57
+ end
58
+
59
+ AUTO_ENCODINGS.find { |encoding| decodable_as?(bytes, encoding) }
60
+ end
61
+
62
+ def iso_2022_jp?(bytes)
63
+ bytes.include?("\e$") || bytes.include?("\e(")
64
+ end
65
+
66
+ def decodable_as?(bytes, encoding)
67
+ source = bytes.dup.force_encoding(encoding)
68
+ source.valid_encoding? && begin
69
+ source.encode(Encoding::UTF_8)
70
+ true
71
+ rescue Encoding::InvalidByteSequenceError,
72
+ Encoding::UndefinedConversionError
73
+ false
74
+ end
75
+ end
76
+
77
+ def find_encoding(name)
78
+ Encoding.find(name)
79
+ rescue ArgumentError
80
+ raise InputEncodingError, "unknown input encoding: #{name}"
81
+ end
82
+ end
83
+
6
84
  interwiki_map = {}
85
+ use_wiki_name = true
86
+ preserve_plugins = false
87
+ input_encoding = nil
7
88
 
8
89
  OptionParser.new do |opts|
9
90
  opts.banner = "Usage: hiki2md [options] <file>"
@@ -15,6 +96,22 @@ OptionParser.new do |opts|
15
96
  raise IOError.new("InterWiki map file not found: #{path}")
16
97
  end
17
98
  end
99
+
100
+ opts.on("--[no-]wiki-name", "Enable or disable WikiName autolinking") do |enabled|
101
+ use_wiki_name = enabled
102
+ end
103
+
104
+ opts.on("--preserve-plugins", "Keep Hiki plugins as escaped Markdown text") do
105
+ preserve_plugins = true
106
+ end
107
+
108
+ opts.on(
109
+ "-E",
110
+ "--input-encoding ENCODING",
111
+ "Source encoding (default: auto)"
112
+ ) do |encoding|
113
+ input_encoding = encoding
114
+ end
18
115
  end.parse!
19
116
 
20
117
  if ARGV.size != 1
@@ -27,5 +124,17 @@ if !File.exist?(input_file)
27
124
  raise IOError.new("no exist #{input_file}")
28
125
  end
29
126
 
30
- hiki2md = Hiki2md.new(interwiki_map: interwiki_map)
31
- puts hiki2md.convert(File.read(input_file))
127
+ hiki2md = Hiki2md.new(
128
+ interwiki_map: interwiki_map,
129
+ use_wiki_name: use_wiki_name,
130
+ preserve_plugins: preserve_plugins
131
+ )
132
+
133
+ begin
134
+ source = Hiki2mdCommand.read_source(input_file, input_encoding)
135
+ rescue Hiki2mdCommand::InputEncodingError => error
136
+ warn "hiki2md: #{error.message}"
137
+ exit 1
138
+ end
139
+
140
+ puts hiki2md.convert(source)
data/hiki2md.gemspec CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.summary = %q{Converter of Hiki to Markdown}
13
13
  spec.homepage = "http://github.com/kdmsnr/hiki2md"
14
14
  spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.2.0"
15
16
 
16
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
18
  spec.bindir = "exe"
@@ -1,3 +1,3 @@
1
1
  class Hiki2md
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.1"
3
3
  end