rangu 0.3.0 → 1.0.0
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/CHANGELOG.md +29 -0
- data/README.md +81 -21
- data/exe/rangu +7 -0
- data/lib/rangu/cli.rb +104 -0
- data/lib/rangu/version.rb +3 -1
- data/lib/rangu.rb +114 -44
- metadata +16 -63
- data/.gitignore +0 -12
- data/.rspec +0 -3
- data/.rubocop.yml +0 -650
- data/.travis.yml +0 -10
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -51
- data/Rakefile +0 -6
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/rangu.gemspec +0 -27
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '09dd594057ccb584243d53f15ad5506e4dcb0ca155423f97c5e1b08f56981596'
|
|
4
|
+
data.tar.gz: e1591c9cb6a351994aa7eea1f3c22b06e7325871081eaaecd21c001ebe8eefa3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a9052b246105a9455ddb18ecd6a376667e3bbd5897ffade8ca37ffa72b13826c7b5e4df9e010502cab98d051daa05e144e84862907544ca847fef31ad89f34d7
|
|
7
|
+
data.tar.gz: 8b1400f450b06cfe71b5118b07de1b78f19051acc193c77b6a23aa785adebfdc2bee900e2de96634e90ba4846cf533f013d34b23745a1e02701ea70e08318fc6
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Rangu are documented in this file.
|
|
4
|
+
|
|
5
|
+
## 1.0.0 - 2026-07-19
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added the `rangu` command-line interface with text, file, and standard-input modes.
|
|
10
|
+
- Added GitHub Actions coverage for Ruby 3.3, 3.4, and 4.0.
|
|
11
|
+
- Added grouped monthly Dependabot updates for Bundler and GitHub Actions.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Updated Rangu's spacing rules to match pangu.py 4.0.6.1 and added corresponding Minitest coverage.
|
|
16
|
+
- Made `Rangu.spacing` and `Rangu.spacing_text` immutable text-only APIs.
|
|
17
|
+
- Made `Rangu.spacing_file` the only file-reading API and preserved file contents.
|
|
18
|
+
- Replaced the RSpec suite with Minitest 6 and modernized the development toolchain to Bundler 4, Rake 13, and
|
|
19
|
+
RuboCop 1.88.
|
|
20
|
+
- Raised the minimum supported Ruby version to 3.3.
|
|
21
|
+
- Enabled MFA-required RubyGems releases and restricted the gem push host to RubyGems.org.
|
|
22
|
+
|
|
23
|
+
### Breaking changes from 0.3.0
|
|
24
|
+
|
|
25
|
+
- CJK-adjacent ASCII punctuation is now normalized to full-width punctuation, and middle-dot variants are normalized
|
|
26
|
+
to `・`.
|
|
27
|
+
- `Rangu.spacing(existing_path)` no longer reads the file at that path; call `Rangu.spacing_file(path)` instead.
|
|
28
|
+
- Input strings are no longer modified in place.
|
|
29
|
+
- Ruby versions older than 3.3 are no longer supported.
|
data/README.md
CHANGED
|
@@ -1,47 +1,107 @@
|
|
|
1
1
|
# Rangu
|
|
2
2
|
|
|
3
|
-
[](https://github.com/bugtender/rangu/actions/workflows/ci.yml)
|
|
4
|
+
[](https://rubygems.org/gems/rangu)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
Rangu inserts readable spacing between CJK characters and half-width letters, numbers, and symbols. It provides an
|
|
7
|
+
immutable Ruby API and a command-line interface.
|
|
6
8
|
|
|
7
9
|
This is the Ruby copy version from [Vinta](https://github.com/vinta)'s [pangu.py](https://github.com/vinta/pangu.py).
|
|
8
10
|
|
|
11
|
+
Rangu requires Ruby 3.3 or newer and has no runtime dependencies.
|
|
12
|
+
|
|
9
13
|
## Installation
|
|
10
14
|
|
|
11
|
-
Add
|
|
15
|
+
Add Rangu to your bundle:
|
|
12
16
|
|
|
13
|
-
```
|
|
14
|
-
|
|
17
|
+
```shell
|
|
18
|
+
bundle add rangu
|
|
15
19
|
```
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
$ bundle
|
|
21
|
+
Or install the gem directly:
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
```shell
|
|
24
|
+
gem install rangu
|
|
25
|
+
```
|
|
24
26
|
|
|
25
|
-
##
|
|
27
|
+
## Ruby API
|
|
26
28
|
|
|
27
29
|
```ruby
|
|
28
30
|
require "rangu"
|
|
29
31
|
|
|
30
32
|
Rangu.spacing("當你凝視著bug,bug也凝視著你")
|
|
31
|
-
=> "當你凝視著 bug,bug 也凝視著你"
|
|
33
|
+
# => "當你凝視著 bug,bug 也凝視著你"
|
|
34
|
+
|
|
35
|
+
Rangu.spacing_text("Ruby中文")
|
|
36
|
+
# => "Ruby 中文"
|
|
32
37
|
|
|
33
|
-
Rangu.
|
|
34
|
-
=>
|
|
38
|
+
Rangu.spacing_file("notes.txt")
|
|
39
|
+
# => the spaced UTF-8 contents of notes.txt
|
|
35
40
|
```
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
`Rangu.spacing` is the primary text API, and `Rangu.spacing_text` is an alias. Both always treat their argument as
|
|
43
|
+
text, leave the original string unchanged, accept frozen strings, and return a distinct string. `Rangu.spacing_file`
|
|
44
|
+
is the only file-reading API; it reads UTF-8 and never writes the file. File and encoding errors are passed through to
|
|
45
|
+
the caller.
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
When CJK text is present, Rangu also normalizes adjacent ASCII punctuation such as `~!;:,.?` to full-width forms and
|
|
48
|
+
normalizes `·`, `•`, and `‧` to `・`. Leading and trailing whitespace is removed from CJK results.
|
|
40
49
|
|
|
41
|
-
##
|
|
50
|
+
## Command-line interface
|
|
51
|
+
|
|
52
|
+
```shell
|
|
53
|
+
rangu "當你凝視著bug,bug也凝視著你"
|
|
54
|
+
rangu --text "Ruby中文"
|
|
55
|
+
rangu --file notes.txt
|
|
56
|
+
printf "中文Ruby" | rangu
|
|
57
|
+
rangu --version
|
|
58
|
+
rangu --help
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Text, file, and positional modes are mutually exclusive. Explicit input takes priority over standard input, and stdin
|
|
62
|
+
is read only when no explicit input is provided. Results go to stdout; usage, option, and file errors go to stderr with
|
|
63
|
+
a non-zero exit status. The CLI never edits a file in place.
|
|
64
|
+
|
|
65
|
+
## Upgrading from 0.3.0
|
|
66
|
+
|
|
67
|
+
Rangu 1.0.0 has four intentional breaking changes:
|
|
42
68
|
|
|
43
|
-
|
|
69
|
+
- ASCII punctuation adjacent to CJK text is now normalized to full-width punctuation, including middle-dot conversion.
|
|
70
|
+
- `Rangu.spacing(existing_path)` no longer detects or reads files. Use `Rangu.spacing_file(path)` explicitly.
|
|
71
|
+
- Spacing APIs no longer modify the input string and always return a separate string.
|
|
72
|
+
- The minimum supported Ruby version is now 3.3.
|
|
44
73
|
|
|
45
|
-
##
|
|
74
|
+
## Development
|
|
75
|
+
|
|
76
|
+
Install Ruby 3.3 or newer and Bundler 4, then run:
|
|
77
|
+
|
|
78
|
+
```shell
|
|
79
|
+
bin/setup
|
|
80
|
+
bundle exec rake
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The default Rake task runs the Minitest suite and RuboCop. Individual checks and the package build are also available:
|
|
84
|
+
|
|
85
|
+
```shell
|
|
86
|
+
bundle exec rake test
|
|
87
|
+
bundle exec rubocop
|
|
88
|
+
bundle exec rake build
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Bug reports and pull requests are welcome in the [issue tracker](https://github.com/bugtender/rangu/issues). By
|
|
92
|
+
participating, you agree to follow the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
93
|
+
|
|
94
|
+
## Manual release
|
|
95
|
+
|
|
96
|
+
1. Update `Rangu::VERSION` and `CHANGELOG.md` in a release commit.
|
|
97
|
+
2. Run `bundle exec rake` and `bundle exec rake build`.
|
|
98
|
+
3. Inspect `pkg/rangu-VERSION.gem`, then install it into a temporary `GEM_HOME` for a smoke test.
|
|
99
|
+
4. Create and push an annotated `vVERSION` tag.
|
|
100
|
+
5. Publish with `gem push --host https://rubygems.org --otp OTP pkg/rangu-VERSION.gem`.
|
|
101
|
+
|
|
102
|
+
RubyGems publishing is restricted to `https://rubygems.org`, and all owners must have multi-factor authentication
|
|
103
|
+
enabled. Releases are manual; CI does not publish gems.
|
|
104
|
+
|
|
105
|
+
## License
|
|
46
106
|
|
|
47
|
-
|
|
107
|
+
Rangu is available under the [MIT License](LICENSE).
|
data/exe/rangu
ADDED
data/lib/rangu/cli.rb
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
require "rangu"
|
|
5
|
+
|
|
6
|
+
module Rangu
|
|
7
|
+
class CLI
|
|
8
|
+
SUCCESS = 0
|
|
9
|
+
ERROR = 1
|
|
10
|
+
USAGE_ERROR = 2
|
|
11
|
+
|
|
12
|
+
def self.run(arguments, stdin: $stdin, stdout: $stdout, stderr: $stderr)
|
|
13
|
+
new(stdin:, stdout:, stderr:).run(arguments)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(stdin:, stdout:, stderr:)
|
|
17
|
+
@stdin = stdin
|
|
18
|
+
@stdout = stdout
|
|
19
|
+
@stderr = stderr
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run(arguments)
|
|
23
|
+
options = {}
|
|
24
|
+
parser = option_parser(options)
|
|
25
|
+
positional = parser.parse(arguments.dup)
|
|
26
|
+
return print_help(parser) if options[:help]
|
|
27
|
+
return print_version if options[:version]
|
|
28
|
+
|
|
29
|
+
source = resolve_source(options, positional, parser)
|
|
30
|
+
source ? execute(source) : USAGE_ERROR
|
|
31
|
+
rescue OptionParser::ParseError => e
|
|
32
|
+
report_usage(e.message, parser)
|
|
33
|
+
USAGE_ERROR
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def option_parser(options)
|
|
39
|
+
OptionParser.new do |parser|
|
|
40
|
+
parser.banner = "Usage: rangu [TEXT] | --text TEXT | --file PATH"
|
|
41
|
+
parser.summary_width = 24
|
|
42
|
+
|
|
43
|
+
parser.on("-t", "--text TEXT", "space the provided text") { |text| options[:text] = text }
|
|
44
|
+
parser.on("-f", "--file PATH", "read and space a UTF-8 file") { |path| options[:file] = path }
|
|
45
|
+
parser.on("-v", "--version", "print the version") { options[:version] = true }
|
|
46
|
+
parser.on("-h", "--help", "show this help") { options[:help] = true }
|
|
47
|
+
|
|
48
|
+
parser.separator("")
|
|
49
|
+
parser.separator("With no explicit input, rangu reads standard input. Files are never modified.")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def resolve_source(options, positional, parser)
|
|
54
|
+
return report_usage("expected at most one TEXT argument", parser) if positional.length > 1
|
|
55
|
+
|
|
56
|
+
sources = explicit_sources(options, positional)
|
|
57
|
+
return report_usage("text and file inputs are mutually exclusive", parser) if sources.length > 1
|
|
58
|
+
return sources.first unless sources.empty?
|
|
59
|
+
|
|
60
|
+
standard_input_source(parser)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def explicit_sources(options, positional)
|
|
64
|
+
sources = []
|
|
65
|
+
sources << [:text, options[:text]] if options.key?(:text)
|
|
66
|
+
sources << [:file, options[:file]] if options.key?(:file)
|
|
67
|
+
sources << [:text, positional.first] unless positional.empty?
|
|
68
|
+
sources
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def standard_input_source(parser)
|
|
72
|
+
input = @stdin.read
|
|
73
|
+
return report_usage("no input provided", parser) if input.empty?
|
|
74
|
+
|
|
75
|
+
[:text, input]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def execute(source)
|
|
79
|
+
mode, value = source
|
|
80
|
+
result = mode == :file ? Rangu.spacing_file(value) : Rangu.spacing(value)
|
|
81
|
+
@stdout.puts(result)
|
|
82
|
+
SUCCESS
|
|
83
|
+
rescue SystemCallError, IOError, EncodingError, ArgumentError => e
|
|
84
|
+
@stderr.puts("rangu: #{e.message}")
|
|
85
|
+
ERROR
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def print_help(parser)
|
|
89
|
+
@stdout.puts(parser)
|
|
90
|
+
SUCCESS
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def print_version
|
|
94
|
+
@stdout.puts(VERSION)
|
|
95
|
+
SUCCESS
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def report_usage(message, parser)
|
|
99
|
+
@stderr.puts("rangu: #{message}")
|
|
100
|
+
@stderr.puts(parser.banner)
|
|
101
|
+
nil
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
data/lib/rangu/version.rb
CHANGED
data/lib/rangu.rb
CHANGED
|
@@ -1,68 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "rangu/version"
|
|
2
4
|
|
|
3
5
|
module Rangu
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
# Spacing rules adapted from pangu.py 4.0.6.1.
|
|
7
|
+
CJK = "\\u2e80-\\u2eff\\u2f00-\\u2fdf\\u3040-\\u309f\\u30a0-\\u30fa\\u30fc-\\u30ff" \
|
|
8
|
+
"\\u3100-\\u312f\\u3200-\\u32ff\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff"
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
FIX_SINGLE_QUOTE = /([\u2e80-\u2eff\u2f00-\u2fdf\u3040-\u309f\u30a0-\u30ff\u3100-\u312f\u3200-\u32ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff])( )(\')([A-Za-zΑ-Ωα-ω])/i
|
|
10
|
+
ANY_CJK = /[#{CJK}]/
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
CONVERT_TO_FULLWIDTH_CJK_SYMBOLS_CJK = /([#{CJK}])( *(?::+|\.) *)([#{CJK}])/
|
|
13
|
+
CONVERT_TO_FULLWIDTH_CJK_SYMBOLS = /([#{CJK}]) *([~!;,?]+) */
|
|
14
|
+
DOTS_CJK = /(\.{2,}|…)([#{CJK}])/
|
|
15
|
+
FIX_CJK_COLON_ANS = /([#{CJK}]):([A-Z0-9()])/
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
CJK_QUOTE = /([#{CJK}])([`"״])/
|
|
18
|
+
QUOTE_CJK = /([`"״])([#{CJK}])/
|
|
19
|
+
FIX_QUOTE_ANY_QUOTE = /([`"״]+)(\s*)(.+?)(\s*)([`"״]+)/
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
FIX_BRACKET = /([\(\[\{<\u201c]+)(\s*)(.+?)(\s*)([\)\]\}>\u201d]+)/i
|
|
21
|
+
CJK_SINGLE_QUOTE_BUT_POSSESSIVE = /([#{CJK}])('[^s])/
|
|
22
|
+
SINGLE_QUOTE_CJK = /(')([#{CJK}])/
|
|
23
|
+
FIX_POSSESSIVE_SINGLE_QUOTE = /([#{CJK}A-Za-z0-9])( )('s)/
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
HASH_ANS_CJK_HASH = /([#{CJK}])(#)([#{CJK}]+)(#)([#{CJK}])/
|
|
26
|
+
CJK_HASH = /([#{CJK}])(#([^ ]))/
|
|
27
|
+
HASH_CJK = /(([^ ])#)([#{CJK}])/
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
CJK_OPERATOR_ANS = %r{([#{CJK}])([+\-*/=&|<>])([A-Za-z0-9])}
|
|
30
|
+
ANS_OPERATOR_CJK = %r{([A-Za-z0-9])([+\-*/=&|<>])([#{CJK}])}
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
text.gsub!(QUOTE_CJK, "\\1 \\2")
|
|
32
|
+
FIX_SLASH_AS = %r{(/) ([a-z\-_./]+)}
|
|
33
|
+
FIX_SLASH_AS_SLASH = %r{([/.])([A-Za-z\-_./]+) (/)}
|
|
29
34
|
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
CJK_LEFT_BRACKET = /([#{CJK}])([\(\[\{<>“])/
|
|
36
|
+
RIGHT_BRACKET_CJK = /([)\]}<>”])([#{CJK}])/
|
|
37
|
+
FIX_LEFT_BRACKET_ANY_RIGHT_BRACKET = /([\(\[\{<“]+)(\s*)(.+?)(\s*)([)\]}>”]+)/
|
|
38
|
+
ANS_CJK_LEFT_BRACKET_ANY_RIGHT_BRACKET = /([A-Za-z0-9#{CJK}]) *(“)([A-Za-z0-9#{CJK}\-_ ]+)(”)/
|
|
39
|
+
LEFT_BRACKET_ANY_RIGHT_BRACKET_ANS_CJK = /(“)([A-Za-z0-9#{CJK}\-_ ]+)(”) *([A-Za-z0-9#{CJK}])/
|
|
32
40
|
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
AN_LEFT_BRACKET = /([A-Za-z0-9])([\(\[\{])/
|
|
42
|
+
RIGHT_BRACKET_AN = /([)\]}])([A-Za-z0-9])/
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
CJK_ANS = %r{([#{CJK}])([A-Za-z\u0370-\u03ff0-9@$%\^&*\-+\\=|/\u00a1-\u00ff\u2150-\u218f\u2700—\u27bf])}
|
|
45
|
+
ANS_CJK = %r{([A-Za-z\u0370-\u03ff0-9~!$%\^&*\-+\\=|;:,./?\u00a1-\u00ff\u2150-\u218f\u2700—\u27bf])([#{CJK}])}
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if old_text == text
|
|
42
|
-
text.gsub!(CJK_BRACKET, "\\1 \\2")
|
|
43
|
-
text.gsub!(BRACKET_CJK, "\\1 \\2")
|
|
44
|
-
end
|
|
45
|
-
text.gsub!(FIX_BRACKET, "\\1\\3\\5")
|
|
47
|
+
S_A = /(%)([A-Za-z])/
|
|
48
|
+
MIDDLE_DOT = /( *)([·•‧])( *)/
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
TILDES = /~+/
|
|
51
|
+
EXCLAMATION_MARKS = /!+/
|
|
52
|
+
SEMICOLONS = /;+/
|
|
53
|
+
COLONS = /:+/
|
|
54
|
+
COMMAS = /,+/
|
|
55
|
+
PERIODS = /\.+/
|
|
56
|
+
QUESTION_MARKS = /\?+/
|
|
48
57
|
|
|
49
|
-
|
|
50
|
-
text.
|
|
51
|
-
|
|
58
|
+
def self.spacing(text)
|
|
59
|
+
return text.dup if text.length <= 1 || !ANY_CJK.match?(text)
|
|
60
|
+
|
|
61
|
+
new_text = normalize_fullwidth_symbols_between_cjk(text.dup)
|
|
62
|
+
new_text = normalize_fullwidth_symbols_after_cjk(new_text)
|
|
63
|
+
|
|
64
|
+
new_text = new_text.gsub(DOTS_CJK, "\\1 \\2")
|
|
65
|
+
new_text = new_text.gsub(FIX_CJK_COLON_ANS, "\\1:\\2")
|
|
66
|
+
|
|
67
|
+
new_text = new_text.gsub(CJK_QUOTE, "\\1 \\2")
|
|
68
|
+
new_text = new_text.gsub(QUOTE_CJK, "\\1 \\2")
|
|
69
|
+
new_text = new_text.gsub(FIX_QUOTE_ANY_QUOTE, "\\1\\3\\5")
|
|
70
|
+
|
|
71
|
+
new_text = new_text.gsub(CJK_SINGLE_QUOTE_BUT_POSSESSIVE, "\\1 \\2")
|
|
72
|
+
new_text = new_text.gsub(SINGLE_QUOTE_CJK, "\\1 \\2")
|
|
73
|
+
new_text = new_text.gsub(FIX_POSSESSIVE_SINGLE_QUOTE, "\\1's")
|
|
74
|
+
|
|
75
|
+
new_text = new_text.gsub(HASH_ANS_CJK_HASH, "\\1 \\2\\3\\4 \\5")
|
|
76
|
+
new_text = new_text.gsub(CJK_HASH, "\\1 \\2")
|
|
77
|
+
new_text = new_text.gsub(HASH_CJK, "\\1 \\3")
|
|
78
|
+
|
|
79
|
+
new_text = new_text.gsub(CJK_OPERATOR_ANS, "\\1 \\2 \\3")
|
|
80
|
+
new_text = new_text.gsub(ANS_OPERATOR_CJK, "\\1 \\2 \\3")
|
|
81
|
+
|
|
82
|
+
new_text = new_text.gsub(FIX_SLASH_AS, "\\1\\2")
|
|
83
|
+
new_text = new_text.gsub(FIX_SLASH_AS_SLASH, "\\1\\2\\3")
|
|
84
|
+
|
|
85
|
+
new_text = new_text.gsub(CJK_LEFT_BRACKET, "\\1 \\2")
|
|
86
|
+
new_text = new_text.gsub(RIGHT_BRACKET_CJK, "\\1 \\2")
|
|
87
|
+
new_text = new_text.gsub(FIX_LEFT_BRACKET_ANY_RIGHT_BRACKET, "\\1\\3\\5")
|
|
88
|
+
new_text = new_text.gsub(ANS_CJK_LEFT_BRACKET_ANY_RIGHT_BRACKET, "\\1 \\2\\3\\4")
|
|
89
|
+
new_text = new_text.gsub(LEFT_BRACKET_ANY_RIGHT_BRACKET_ANS_CJK, "\\1\\2\\3 \\4")
|
|
90
|
+
|
|
91
|
+
new_text = new_text.gsub(AN_LEFT_BRACKET, "\\1 \\2")
|
|
92
|
+
new_text = new_text.gsub(RIGHT_BRACKET_AN, "\\1 \\2")
|
|
93
|
+
|
|
94
|
+
new_text = new_text.gsub(CJK_ANS, "\\1 \\2")
|
|
95
|
+
new_text = new_text.gsub(ANS_CJK, "\\1 \\2")
|
|
96
|
+
new_text = new_text.gsub(S_A, "\\1 \\2")
|
|
97
|
+
new_text.gsub(MIDDLE_DOT, "・").strip
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class << self
|
|
101
|
+
alias spacing_text spacing
|
|
52
102
|
end
|
|
53
103
|
|
|
54
104
|
def self.spacing_file(path)
|
|
55
|
-
|
|
56
|
-
data = file.read
|
|
57
|
-
file.close
|
|
58
|
-
spacing_text(data)
|
|
105
|
+
spacing(File.read(path, encoding: Encoding::UTF_8))
|
|
59
106
|
end
|
|
60
107
|
|
|
61
|
-
def self.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
else
|
|
65
|
-
spacing_text(text_or_path)
|
|
108
|
+
def self.normalize_fullwidth_symbols_between_cjk(text)
|
|
109
|
+
while (matched = CONVERT_TO_FULLWIDTH_CJK_SYMBOLS_CJK.match(text))
|
|
110
|
+
text[matched.begin(0)...matched.end(0)] = "#{matched[1]}#{convert_to_fullwidth(matched[2])}#{matched[3]}"
|
|
66
111
|
end
|
|
112
|
+
|
|
113
|
+
text
|
|
114
|
+
end
|
|
115
|
+
private_class_method :normalize_fullwidth_symbols_between_cjk
|
|
116
|
+
|
|
117
|
+
def self.normalize_fullwidth_symbols_after_cjk(text)
|
|
118
|
+
while (matched = CONVERT_TO_FULLWIDTH_CJK_SYMBOLS.match(text))
|
|
119
|
+
prefix = text[0, matched.begin(0) + 1].strip
|
|
120
|
+
suffix = text[matched.end(0)..].strip
|
|
121
|
+
text = "#{prefix}#{convert_to_fullwidth(matched[2])}#{suffix}"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
text
|
|
125
|
+
end
|
|
126
|
+
private_class_method :normalize_fullwidth_symbols_after_cjk
|
|
127
|
+
|
|
128
|
+
def self.convert_to_fullwidth(symbols)
|
|
129
|
+
symbols = symbols.gsub(TILDES, "~")
|
|
130
|
+
symbols = symbols.gsub(EXCLAMATION_MARKS, "!")
|
|
131
|
+
symbols = symbols.gsub(SEMICOLONS, ";")
|
|
132
|
+
symbols = symbols.gsub(COLONS, ":")
|
|
133
|
+
symbols = symbols.gsub(COMMAS, ",")
|
|
134
|
+
symbols = symbols.gsub(PERIODS, "。")
|
|
135
|
+
symbols.gsub(QUESTION_MARKS, "?").strip
|
|
67
136
|
end
|
|
137
|
+
private_class_method :convert_to_fullwidth
|
|
68
138
|
end
|
metadata
CHANGED
|
@@ -1,84 +1,39 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rangu
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vincent Lin
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
-
dependencies:
|
|
13
|
-
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: bundler
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - "~>"
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '2.0'
|
|
20
|
-
type: :development
|
|
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: rake
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - "~>"
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '10.0'
|
|
34
|
-
type: :development
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - "~>"
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '10.0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: rspec
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - "~>"
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '3.2'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - "~>"
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '3.2'
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
55
12
|
description: Insert a white space between FullWidth and HalfWidth characters.
|
|
56
13
|
email:
|
|
57
14
|
- bugtender@gmail.com
|
|
58
|
-
executables:
|
|
15
|
+
executables:
|
|
16
|
+
- rangu
|
|
59
17
|
extensions: []
|
|
60
18
|
extra_rdoc_files: []
|
|
61
19
|
files:
|
|
62
|
-
-
|
|
63
|
-
- ".rspec"
|
|
64
|
-
- ".rubocop.yml"
|
|
65
|
-
- ".travis.yml"
|
|
20
|
+
- CHANGELOG.md
|
|
66
21
|
- CODE_OF_CONDUCT.md
|
|
67
|
-
- Gemfile
|
|
68
|
-
- Gemfile.lock
|
|
69
22
|
- LICENSE
|
|
70
23
|
- README.md
|
|
71
|
-
-
|
|
72
|
-
- bin/console
|
|
73
|
-
- bin/setup
|
|
24
|
+
- exe/rangu
|
|
74
25
|
- lib/rangu.rb
|
|
26
|
+
- lib/rangu/cli.rb
|
|
75
27
|
- lib/rangu/version.rb
|
|
76
|
-
- rangu.gemspec
|
|
77
28
|
homepage: https://github.com/bugtender/rangu
|
|
78
29
|
licenses:
|
|
79
30
|
- MIT
|
|
80
|
-
metadata:
|
|
81
|
-
|
|
31
|
+
metadata:
|
|
32
|
+
allowed_push_host: https://rubygems.org
|
|
33
|
+
bug_tracker_uri: https://github.com/bugtender/rangu/issues
|
|
34
|
+
changelog_uri: https://github.com/bugtender/rangu/blob/master/CHANGELOG.md
|
|
35
|
+
rubygems_mfa_required: 'true'
|
|
36
|
+
source_code_uri: https://github.com/bugtender/rangu
|
|
82
37
|
rdoc_options: []
|
|
83
38
|
require_paths:
|
|
84
39
|
- lib
|
|
@@ -86,16 +41,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
86
41
|
requirements:
|
|
87
42
|
- - ">="
|
|
88
43
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '
|
|
44
|
+
version: '3.3'
|
|
90
45
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
46
|
requirements:
|
|
92
47
|
- - ">="
|
|
93
48
|
- !ruby/object:Gem::Version
|
|
94
49
|
version: '0'
|
|
95
50
|
requirements: []
|
|
96
|
-
|
|
97
|
-
rubygems_version: 2.7.3
|
|
98
|
-
signing_key:
|
|
51
|
+
rubygems_version: 3.6.9
|
|
99
52
|
specification_version: 4
|
|
100
53
|
summary: Paranoid text spacing in Ruby
|
|
101
54
|
test_files: []
|
data/.gitignore
DELETED
data/.rspec
DELETED