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 +4 -4
- data/.travis.yml +4 -2
- data/Gemfile.lock +1 -1
- data/README.md +93 -2
- data/exe/hiki2md +111 -2
- data/hiki2md.gemspec +1 -0
- data/lib/hiki2md/version.rb +1 -1
- data/lib/hiki2md.rb +838 -185
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4fc18ad51b63a8dde11dfe5ea4c04e4e8347e21457670edc8200acdaa125e8e9
|
|
4
|
+
data.tar.gz: f0bf7d53ac1bfd7a71a643ae9354f781fab199a29a07ea8225399831808ab2fb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 147c40644b389860b2f9b0868235571ebd95ab005d6873910a4c54849d6a6f625619bf439e1679da0894a7908c9766fcc05d11f14f571d283492206bb19750b8
|
|
7
|
+
data.tar.gz: 61dd0a68d793fafa4c5320e72c3bd9c4f5f88288e5566686d2abbf56fe9bc734861de8bf75b70556f6d6477bda01ceda9f14a7d7830d4d6e1d5534774f7cc6e3
|
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
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
|
-
|
|
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(
|
|
31
|
-
|
|
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"
|
data/lib/hiki2md/version.rb
CHANGED