docdiff 0.6.4 → 0.6.5

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: cde4401e24dbddfd093efe812847c7cc57f12cea257c113bc2625ac2c38361d5
4
- data.tar.gz: aa7fd969d604d090f16817dcdb09f2f4a1e5218c2063d3467e29929305afdd41
3
+ metadata.gz: e6c9449dff2946f72c9da89b1a9cd443ea999368160f1712e6da7c56dc2a094f
4
+ data.tar.gz: 67b4fbff27a464b6605e1a7b255c5de811a1fbdba86c9049f8b4adce8a7159f2
5
5
  SHA512:
6
- metadata.gz: b0f085aa58f164c0b6a0d0cfaec0439df555d37fbe1c8dacd66962ab41084ec93cc6d0d13c3852ca9404d62c3941edc4477261b44f9402d0353b71cd3a3f257c
7
- data.tar.gz: b5b1bbb4a18524f5d9278f0afac27dc732dde0be686d4b526301219499fa3ef5a073b132a27a4e12a8bbc7bf847c8440c81179453243b1d30190d8901ea05770
6
+ metadata.gz: 6ee7f1e478639c73fe75bfe642e37912cbbb9df3bf7a13c287c932440e6c3fed68c99340c06e1411302f0afcadaefbabf442e1b229457d9615fe77a367f62682
7
+ data.tar.gz: f62327185b55508a7a1045e1f479c1e1a79998c005aeede62f759743bd013f6e1b11bbd69343083c38f7587cf29aa069a3a9808b49c78eaae8d71756a4e7f747
data/Makefile CHANGED
@@ -14,12 +14,12 @@ DESTDIR =
14
14
  PREFIX = /usr/local
15
15
  datadir = $(DESTDIR)$(PREFIX)/share
16
16
 
17
- all: $(DOCS)
17
+ all: test
18
18
 
19
19
  test: $(TESTS)
20
20
  $(RUBY) -I./lib -e 'ARGV.map{|a| require_relative "#{a}"}' $^
21
21
 
22
- docs: $(DOCS)
22
+ docs: $(DOCS)
23
23
 
24
24
  %.html: %.md
25
25
  $(MD2HTML) --html-title="$(shell grep '^# .*' $< | head -n 1 | sed 's/^# //')" $< \
@@ -65,8 +65,9 @@ uninstall:
65
65
  -rm -fr $(DESTDIR)/etc/$(PRODUCT)
66
66
  -rm -fr $(datadir)/doc/$(PRODUCT)
67
67
 
68
- dist:
69
- git archive --prefix="$(PRODUCT)-$(VERSION)/" --format=tar HEAD --output="$(PRODUCT)-$(VERSION).tar.gz"
68
+ dist: $(PRODUCT)-$(VERSION).tar.gz
69
+ $(PRODUCT)-$(VERSION).tar.gz:
70
+ git archive --prefix="$(PRODUCT)-$(VERSION)/" --format=tar HEAD --output="$@"
70
71
 
71
72
  gem: $(PRODUCT)-$(VERSION).gem
72
73
  $(PRODUCT)-$(VERSION).gem: $(PRODUCT).gemspec
@@ -74,9 +75,7 @@ $(PRODUCT)-$(VERSION).gem: $(PRODUCT).gemspec
74
75
 
75
76
  clean:
76
77
  -rm -fr $(DOCS)
77
-
78
- distclean: clean
79
78
  -rm -fr $(PRODUCT)-$(VERSION).tar.gz
80
79
  -rm -fr $(PRODUCT)-$(VERSION).gem
81
80
 
82
- .PHONY: all test docs install uninstall dist gem clean distclean
81
+ .PHONY: all test docs install uninstall dist gem clean
data/README.md CHANGED
@@ -85,6 +85,7 @@ $
85
85
  * Runtime requirements:
86
86
  - [Ruby](https://www.ruby-lang.org/) (>= 3.0)
87
87
  * Development requirements:
88
+ - [Test::Unit](https://test-unit.github.io/)
88
89
  - Make ([GNU Make](https://www.gnu.org/software/make/))
89
90
  - [Git](https://git-scm.com/)
90
91
  - [md2html](https://github.com/mity/md4c) (for generating documents)
data/README_ja.md CHANGED
@@ -85,6 +85,7 @@ $
85
85
  * 実行時に必要なソフトウェア:
86
86
  - [Ruby](https://www.ruby-lang.org/) (>= 3.0)
87
87
  * 開発時に必要なソフトウェア:
88
+ - [Test::Unit](https://test-unit.github.io/)
88
89
  - Make ([GNU Make](https://www.gnu.org/software/make/))
89
90
  - [Git](https://git-scm.com/)
90
91
  - [md2html](https://github.com/mity/md4c)(ドキュメント生成用)
data/bin/docdiff CHANGED
@@ -3,212 +3,5 @@
3
3
  # Copyright (C) 2002-2011 Hisashi MORITA
4
4
  # Requirements: Ruby (>= 2.0)
5
5
  require 'docdiff'
6
- require 'optparse'
7
6
 
8
- # do_config_stuff
9
-
10
- default_config = {
11
- :resolution => "word",
12
- :encoding => "auto",
13
- :eol => "auto",
14
- :format => "html",
15
- :cache => true,
16
- :digest => false,
17
- :pager => nil,
18
- :verbose => false
19
- }
20
-
21
- clo = command_line_options = {}
22
-
23
- # if invoked as "worddiff" or "chardiff",
24
- # appropriate resolution is set respectively.
25
- case File.basename($0, ".*")
26
- when "worddiff" then; clo[:resolution] = "word"
27
- when "chardiff" then; clo[:resolution] = "char"
28
- end
29
-
30
- ARGV.options {|o|
31
- o.def_option('--resolution=RESOLUTION',
32
- resolutions = ['line', 'word', 'char'],
33
- 'specify resolution (granularity)',
34
- "#{resolutions.join('|')} (default: word)"
35
- ){|s| clo[:resolution] = (s || "word")}
36
- o.def_option('--line', 'same as --resolution=line'){clo[:resolution] = "line"}
37
- o.def_option('--word', 'same as --resolution=word'){clo[:resolution] = "word"}
38
- o.def_option('--char', 'same as --resolution=char'){clo[:resolution] = "char"}
39
-
40
- o.def_option('--encoding=ENCODING',
41
- encodings = ['ASCII', 'EUC-JP', 'Shift_JIS', 'CP932', 'UTF-8', 'auto'],
42
- "specify character encoding",
43
- "#{encodings.join('|')} (default: auto)",
44
- "(try ASCII for single byte encodings such as ISO-8859)"
45
- ){|s| clo[:encoding] = (s || "auto")}
46
- o.def_option('--ascii', 'same as --encoding=ASCII'){clo[:encoding] = "ASCII"}
47
- o.def_option('--iso8859', 'same as --encoding=ASCII'){clo[:encoding] = "ASCII"}
48
- o.def_option('--iso8859x', 'same as --encoding=ASCII (deprecated)'){clo[:encoding] = "ASCII"}
49
- o.def_option('--eucjp', 'same as --encoding=EUC-JP'){clo[:encoding] = "EUC-JP"}
50
- o.def_option('--sjis', 'same as --encoding=Shift_JIS'){clo[:encoding] = "Shift_JIS"}
51
- o.def_option('--cp932', 'same as --encoding=CP932'){clo[:encoding] = "CP932"}
52
- o.def_option('--utf8', 'same as --encoding=UTF-8'){clo[:encoding] = "UTF-8"}
53
-
54
- o.def_option('--eol=EOL',
55
- eols = ['CR','LF','CRLF','auto'],
56
- 'specify end-of-line character',
57
- "#{eols.join('|')} (default: auto)",
58
- ){|s| clo[:eol] = (s || "auto")}
59
- o.def_option('--cr', 'same as --eol=CR'){clo[:eol] = "CR"}
60
- o.def_option('--lf', 'same as --eol=LF'){clo[:eol] = "LF"}
61
- o.def_option('--crlf', 'same as --eol=CRLF'){clo[:eol] = "CRLF"}
62
-
63
- o.def_option('--format=FORMAT',
64
- formats = ['tty', 'manued', 'html', 'wdiff', 'stat', 'user'],
65
- 'specify output format',
66
- "#{formats.join('|')} (default: html) (stat is deprecated)",
67
- '(user tags can be defined in config file)'
68
- ){|s| clo[:format] = (s || "manued")}
69
- o.def_option('--tty', 'same as --format=tty'){clo[:format] = "tty"}
70
- o.def_option('--manued', 'same as --format=manued'){clo[:format] = "manued"}
71
- o.def_option('--html', 'same as --format=html'){clo[:format] = "html"}
72
- o.def_option('--wdiff', 'same as --format=wdiff'){clo[:format] = "wdiff"}
73
- o.def_option('--stat', 'same as --format=stat (not implemented) (deprecated)'){clo[:format] = "stat"}
74
-
75
- o.def_option('--label LABEL', '-L LABEL',
76
- 'use label instead of file name (not implemented; exists for compatibility with diff)'
77
- ){|s1, s2| clo[:label1], clo[:label2] = s1, s2}
78
-
79
- o.def_option('--digest', 'digest output, do not show all'){clo[:digest] = true}
80
- o.def_option('--summary', 'same as --digest'){clo[:digest] = true}
81
- o.def_option('--display=DISPLAY',
82
- display_types = ['inline', 'block', 'multi'],
83
- 'specify presentation type (effective only with digest; experimental feature)',
84
- "#{display_types.join('|')} (default: inline) (multi is deprecated)",
85
- ){|s| clo[:display] ||= s.downcase}
86
- o.def_option('--cache', 'use file cache (not implemented) (deprecated)'){clo[:cache] = true}
87
- o.def_option('--pager=PAGER', String,
88
- 'specify pager (if available, $DOCDIFF_PAGER is used by default)'
89
- ){|s| clo[:pager] = s}
90
- o.def_option('--no-pager', 'do not use pager'){clo[:pager] = false}
91
- o.def_option('--config-file=FILE', String,
92
- 'specify config file to read'){|s| clo[:config_file] = s}
93
- o.def_option('--no-config-file',
94
- 'do not read config files'){clo[:no_config_file] = true}
95
- o.def_option('--verbose', 'run verbosely (not well-supported) (deprecated)'){clo[:verbose] = true}
96
-
97
- o.def_option('--help', 'show this message'){puts o; exit(0)}
98
- o.def_option('--version', 'show version'){puts DocDiff::AppVersion; exit(0)}
99
- o.def_option('--license', 'show license (deprecated)'){puts DocDiff::License; exit(0)}
100
- o.def_option('--author', 'show author(s) (deprecated)'){puts DocDiff::Author; exit(0)}
101
-
102
- o.on_tail("When invoked as worddiff or chardiff, resolution will be set accordingly.",
103
- "Config files: /etc/docdiff/docdiff.conf, ~/.config/docdiff/docdiff.conf (or ~/etc/docdiff/docdiff.conf (deprecated))")
104
-
105
- o.parse!
106
- } or exit(1)
107
-
108
- docdiff = DocDiff.new()
109
- docdiff.config.update(default_config)
110
- unless clo[:no_config_file] == true # process_commandline_option
111
- message = docdiff.process_config_file(DocDiff::SystemConfigFileName)
112
- if clo[:verbose] == true || docdiff.config[:verbose] == true
113
- STDERR.print message
114
- end
115
- # message = docdiff.process_config_file(DocDiff::UserConfigFileName)
116
- case
117
- when [File.exist?(DocDiff::UserConfigFileName),
118
- File.exist?(DocDiff::AltUserConfigFileName),
119
- File.exist?(DocDiff::XDGUserConfigFileName)].count(true) >= 2
120
- raise <<~EOS
121
- #{DocDiff::UserConfigFileName}, #{DocDiff::AltUserConfigFileName}, and \
122
- #{DocDiff::XDGUserConfigFileName} cannot be used at the same time. \
123
- Keep one and remove or rename the others.
124
- EOS
125
- when File.exist?(DocDiff::UserConfigFileName)
126
- message = docdiff.process_config_file(DocDiff::UserConfigFileName)
127
- when File.exist?(DocDiff::AltUserConfigFileName)
128
- message = docdiff.process_config_file(DocDiff::AltUserConfigFileName)
129
- when File.exist?(DocDiff::XDGUserConfigFileName)
130
- message = docdiff.process_config_file(DocDiff::XDGUserConfigFileName)
131
- end
132
- if clo[:verbose] == true || docdiff.config[:verbose] == true
133
- STDERR.print message
134
- end
135
- end
136
- unless clo[:config_file].nil?
137
- if File.exist?(clo[:config_file])
138
- message = docdiff.process_config_file(clo[:config_file])
139
- else
140
- raise "#{clo[:config_file]} does not exist."
141
- end
142
- if clo[:verbose] == true || docdiff.config[:verbose] == true
143
- STDERR.print message
144
- end
145
- end
146
- docdiff.config.update(clo)
147
-
148
- docdiff.config[:pager] =
149
- if (pager = docdiff.config[:pager]).is_a?(String) && !pager.empty?
150
- pager
151
- elsif (pager = docdiff.config[:pager]) == false
152
- pager
153
- elsif (pager = ENV['DOCDIFF_PAGER']) && !pager.empty?
154
- pager
155
- end
156
-
157
- # config stuff done
158
-
159
- # process the documents
160
-
161
- file1_content = nil
162
- file2_content = nil
163
- raise "Try `#{File.basename($0)} --help' for more information." if ARGV[0].nil?
164
- raise "Specify at least 2 target files." unless ARGV[0] && ARGV[1]
165
- ARGV[0] = "/dev/stdin" if ARGV[0] == "-"
166
- ARGV[1] = "/dev/stdin" if ARGV[1] == "-"
167
- raise "No such file: #{ARGV[0]}." unless FileTest.exist?(ARGV[0])
168
- raise "No such file: #{ARGV[1]}." unless FileTest.exist?(ARGV[1])
169
- raise "#{ARGV[0]} is not readable." unless FileTest.readable?(ARGV[0])
170
- raise "#{ARGV[1]} is not readable." unless FileTest.readable?(ARGV[1])
171
- File.open(ARGV[0], "r"){|f| file1_content = f.read}
172
- File.open(ARGV[1], "r"){|f| file2_content = f.read}
173
-
174
- doc1 = nil
175
- doc2 = nil
176
-
177
- encoding1 = docdiff.config[:encoding]
178
- encoding2 = docdiff.config[:encoding]
179
- eol1 = docdiff.config[:eol]
180
- eol2 = docdiff.config[:eol]
181
-
182
- if docdiff.config[:encoding] == "auto"
183
- encoding1 = DocDiff::CharString.guess_encoding(file1_content)
184
- encoding2 = DocDiff::CharString.guess_encoding(file2_content)
185
- case
186
- when (encoding1 == "UNKNOWN" or encoding2 == "UNKNOWN")
187
- raise "Document encoding unknown (#{encoding1}, #{encoding2})."
188
- when encoding1 != encoding2
189
- raise "Document encoding mismatch (#{encoding1}, #{encoding2})."
190
- end
191
- end
192
-
193
- if docdiff.config[:eol] == "auto"
194
- eol1 = DocDiff::CharString.guess_eol(file1_content)
195
- eol2 = DocDiff::CharString.guess_eol(file2_content)
196
- case
197
- when (eol1.nil? or eol2.nil?)
198
- raise "Document eol is nil (#{eol1.inspect}, #{eol2.inspect}). The document might be empty."
199
- when (eol1 == 'UNKNOWN' or eol2 == 'UNKNOWN')
200
- raise "Document eol unknown (#{eol1.inspect}, #{eol2.inspect})."
201
- when (eol1 != eol2)
202
- raise "Document eol mismatch (#{eol1}, #{eol2})."
203
- end
204
- end
205
-
206
- doc1 = DocDiff::Document.new(file1_content, encoding1, eol1)
207
- doc2 = DocDiff::Document.new(file2_content, encoding2, eol2)
208
-
209
- output = docdiff.run(doc1, doc2,
210
- {:resolution => docdiff.config[:resolution],
211
- :format => docdiff.config[:format],
212
- :digest => docdiff.config[:digest],
213
- :display => docdiff.config[:display]})
214
- docdiff.print_or_write_to_pager(output, docdiff.config[:pager])
7
+ DocDiff::CLI.run
data/doc/news.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # News
2
2
 
3
+ ### 0.6.5 (2025-12-29)
4
+
5
+ * User-visible changes:
6
+ - Fixed label option not accumulating correctly.
7
+ - Fixed CRLF bug ([#57](https://github.com/hisashim/docdiff/issues/57)), where CRLFs in the input text are gobbled and not printed in the output when using `tty`, `wdiff`, and `user` formats. This problem seems to have existed since 0.3.
8
+ * Developer-related changes:
9
+ - Moved CLI-related stuff from `bin/docdiff` to `lib/docdiff/cli.rb`.
10
+ - Miscellaneous fixes and refactoring.
11
+
3
12
  ### 0.6.4 (2025-12-13)
4
13
 
5
14
  * User-visible changes:
data/docdiff.gemspec CHANGED
@@ -21,4 +21,5 @@ Gem::Specification.new do |s|
21
21
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
22
  s.require_paths = ["lib"]
23
23
  s.required_ruby_version = '>= 3.0'
24
+ s.add_development_dependency "test-unit", "~> 3"
24
25
  end
data/lib/doc_diff.rb CHANGED
@@ -4,10 +4,10 @@
4
4
  require 'docdiff/difference'
5
5
  require 'docdiff/document'
6
6
  require 'docdiff/view'
7
+ require 'docdiff/cli'
7
8
 
8
9
  class DocDiff
9
10
 
10
- AppVersion = Docdiff::VERSION
11
11
  Author = "Copyright (C) 2002-2011 Hisashi MORITA.\n" +
12
12
  "diff library originates from Ruby/CVS by TANAKA Akira.\n"
13
13
  License = "This software is licensed under so-called modified BSD license.\n" +
@@ -21,34 +21,22 @@ class DocDiff
21
21
  else
22
22
  File.join(ENV['HOME'], ".config", "docdiff", "docdiff.conf")
23
23
  end
24
+ DEFAULT_CONFIG = {
25
+ :resolution => "word",
26
+ :encoding => "auto",
27
+ :eol => "auto",
28
+ :format => "html",
29
+ :cache => true,
30
+ :digest => false,
31
+ :pager => nil,
32
+ :verbose => false
33
+ }
24
34
 
25
- def initialize()
26
- @config = {}
35
+ def initialize(config: {})
36
+ @config = config
27
37
  end
28
38
  attr_accessor :config
29
39
 
30
- def DocDiff.parse_config_file_content(content)
31
- result = {}
32
- return result if content.size <= 0
33
- lines = content.dup.split(/\r\n|\r|\n/).compact
34
- lines.collect!{|line| line.sub(/#.*$/, '')}
35
- lines.collect!{|line| line.strip}
36
- lines.delete_if{|line| line == ""}
37
- lines.each{|line|
38
- raise 'line does not include " = ".' unless /[\s]+=[\s]+/.match line
39
- name_src, value_src = line.split(/[\s]+=[\s]+/)
40
- raise "Invalid name: #{name_src.inspect}" if (/\s/.match name_src)
41
- raise "Invalid value: #{value_src.inspect}" unless value_src.kind_of?(String)
42
- name = name_src.intern
43
- value = value_src
44
- value = true if ['on','yes','true'].include? value_src.downcase
45
- value = false if ['off','no','false'].include? value_src.downcase
46
- value = value_src.to_i if /^[0-9]+$/.match value_src
47
- result[name] = value
48
- }
49
- result
50
- end
51
-
52
40
  def compare_by_line(doc1, doc2)
53
41
  Difference.new(doc1.split_to_line, doc2.split_to_line)
54
42
  end
@@ -162,26 +150,4 @@ class DocDiff
162
150
  end
163
151
  result.join
164
152
  end
165
-
166
- def process_config_file(filename)
167
- file_content = nil
168
- begin
169
- File.open(filename, "r"){|f| file_content = f.read}
170
- rescue Errno::ENOENT
171
- message = "config file not found so not read."
172
- ensure
173
- if file_content != nil
174
- self.config.update(DocDiff.parse_config_file_content(file_content))
175
- end
176
- end
177
- message
178
- end
179
-
180
- def print_or_write_to_pager(content, pager)
181
- if STDOUT.tty? && pager.is_a?(String) && !pager.empty?
182
- IO.popen(pager, "w"){|f| f.print content}
183
- else
184
- print content
185
- end
186
- end
187
153
  end # class DocDiff
@@ -0,0 +1,281 @@
1
+ require 'optparse'
2
+
3
+ class DocDiff
4
+ module CLI
5
+ def self.parse_options!(args, base_options: {})
6
+ o = base_options.dup
7
+
8
+ option_parser = OptionParser.new do |parser|
9
+ parser.on(
10
+ '--resolution=RESOLUTION',
11
+ resolutions = ['line', 'word', 'char'],
12
+ 'specify resolution (granularity)',
13
+ "#{resolutions.join('|')} (default: word)"
14
+ ){|s| o[:resolution] = (s || "word")}
15
+ parser.on('--line', 'same as --resolution=line'){o[:resolution] = "line"}
16
+ parser.on('--word', 'same as --resolution=word'){o[:resolution] = "word"}
17
+ parser.on('--char', 'same as --resolution=char'){o[:resolution] = "char"}
18
+
19
+ parser.on(
20
+ '--encoding=ENCODING',
21
+ encodings = ['ASCII', 'EUC-JP', 'Shift_JIS', 'CP932', 'UTF-8', 'auto'],
22
+ "specify character encoding",
23
+ "#{encodings.join('|')} (default: auto)",
24
+ "(try ASCII for single byte encodings such as ISO-8859)"
25
+ ){|s| o[:encoding] = (s || "auto")}
26
+ parser.on('--ascii', 'same as --encoding=ASCII'){o[:encoding] = "ASCII"}
27
+ parser.on('--iso8859', 'same as --encoding=ASCII'){o[:encoding] = "ASCII"}
28
+ parser.on('--iso8859x', 'same as --encoding=ASCII (deprecated)'){o[:encoding] = "ASCII"}
29
+ parser.on('--eucjp', 'same as --encoding=EUC-JP'){o[:encoding] = "EUC-JP"}
30
+ parser.on('--sjis', 'same as --encoding=Shift_JIS'){o[:encoding] = "Shift_JIS"}
31
+ parser.on('--cp932', 'same as --encoding=CP932'){o[:encoding] = "CP932"}
32
+ parser.on('--utf8', 'same as --encoding=UTF-8'){o[:encoding] = "UTF-8"}
33
+
34
+ parser.on(
35
+ '--eol=EOL',
36
+ eols = ['CR','LF','CRLF','auto'],
37
+ 'specify end-of-line character',
38
+ "#{eols.join('|')} (default: auto)",
39
+ ){|s| o[:eol] = (s || "auto")}
40
+ parser.on('--cr', 'same as --eol=CR'){o[:eol] = "CR"}
41
+ parser.on('--lf', 'same as --eol=LF'){o[:eol] = "LF"}
42
+ parser.on('--crlf', 'same as --eol=CRLF'){o[:eol] = "CRLF"}
43
+
44
+ parser.on(
45
+ '--format=FORMAT',
46
+ formats = ['tty', 'manued', 'html', 'wdiff', 'stat', 'user'],
47
+ 'specify output format',
48
+ "#{formats.join('|')} (default: html) (stat is deprecated)",
49
+ '(user tags can be defined in config file)'
50
+ ){|s| o[:format] = (s || "manued")}
51
+ parser.on('--tty', 'same as --format=tty'){o[:format] = "tty"}
52
+ parser.on('--manued', 'same as --format=manued'){o[:format] = "manued"}
53
+ parser.on('--html', 'same as --format=html'){o[:format] = "html"}
54
+ parser.on('--wdiff', 'same as --format=wdiff'){o[:format] = "wdiff"}
55
+ parser.on('--stat', 'same as --format=stat (not implemented) (deprecated)'){o[:format] = "stat"}
56
+
57
+ parser.on(
58
+ '--label LABEL', '-L LABEL',
59
+ 'use label instead of file name (not implemented; exists for compatibility with diff)'
60
+ ){|s| o[:label] ||= []; o[:label] << s}
61
+
62
+ parser.on('--digest', 'digest output, do not show all'){o[:digest] = true}
63
+ parser.on('--summary', 'same as --digest'){o[:digest] = true}
64
+
65
+ parser.on(
66
+ '--display=DISPLAY',
67
+ display_types = ['inline', 'block', 'multi'],
68
+ 'specify presentation type (effective only with digest; experimental feature)',
69
+ "#{display_types.join('|')} (default: inline) (multi is deprecated)",
70
+ ){|s| o[:display] ||= s.downcase}
71
+
72
+ parser.on('--cache', 'use file cache (not implemented) (deprecated)'){o[:cache] = true}
73
+ parser.on(
74
+ '--pager=PAGER', String,
75
+ 'specify pager (if available, $DOCDIFF_PAGER is used by default)'
76
+ ){|s| o[:pager] = s}
77
+ parser.on('--no-pager', 'do not use pager'){o[:pager] = false}
78
+ parser.on('--config-file=FILE', String, 'specify config file to read'){|s| o[:config_file] = s}
79
+ parser.on('--no-config-file', 'do not read config files'){o[:no_config_file] = true}
80
+ parser.on('--verbose', 'run verbosely (not well-supported) (deprecated)'){o[:verbose] = true}
81
+
82
+ parser.on('--help', 'show this message'){puts parser; exit(0)}
83
+ parser.on('--version', 'show version'){puts Docdiff::VERSION; exit(0)}
84
+ parser.on('--license', 'show license (deprecated)'){puts DocDiff::License; exit(0)}
85
+ parser.on('--author', 'show author(s) (deprecated)'){puts DocDiff::Author; exit(0)}
86
+
87
+ parser.on_tail(
88
+ "When invoked as worddiff or chardiff, resolution will be set accordingly.",
89
+ "Config files: /etc/docdiff/docdiff.conf, ~/.config/docdiff/docdiff.conf (or ~/etc/docdiff/docdiff.conf (deprecated))"
90
+ )
91
+ end
92
+
93
+ option_parser.parse!(args)
94
+ o
95
+ end
96
+
97
+ def self.parse_config_file_content(content)
98
+ result = {}
99
+ return result if content.size <= 0
100
+ lines = content.dup.split(/\r\n|\r|\n/).compact
101
+ lines.collect!{|line| line.sub(/#.*$/, '')}
102
+ lines.collect!{|line| line.strip}
103
+ lines.delete_if{|line| line == ""}
104
+ lines.each{|line|
105
+ raise 'line does not include " = ".' unless /[\s]+=[\s]+/.match line
106
+ name_src, value_src = line.split(/[\s]+=[\s]+/)
107
+ raise "Invalid name: #{name_src.inspect}" if (/\s/.match name_src)
108
+ raise "Invalid value: #{value_src.inspect}" unless value_src.kind_of?(String)
109
+ name = name_src.intern
110
+ value = value_src
111
+ value = true if ['on','yes','true'].include? value_src.downcase
112
+ value = false if ['off','no','false'].include? value_src.downcase
113
+ value = value_src.to_i if /^[0-9]+$/.match value_src
114
+ result[name] = value
115
+ }
116
+ result
117
+ end
118
+
119
+ def self.read_config_from_file(filename)
120
+ content = nil
121
+ begin
122
+ File.open(filename, "r"){|f| content = f.read}
123
+ rescue => exception
124
+ raise exception
125
+ ensure
126
+ message =
127
+ case exception
128
+ in Errno::ENOENT
129
+ "config file not found: #{filename.inspect}"
130
+ in Errno::EACCES
131
+ "permission denied for reading: #{filename.inspect}"
132
+ else
133
+ "something unexpected happened: #{filename.inspect}"
134
+ end
135
+ if content
136
+ config = parse_config_file_content(content)
137
+ else
138
+ message = "config file empty: #{filename.inspect}"
139
+ end
140
+ end
141
+ [config, message]
142
+ end
143
+
144
+ def self.print_or_write_to_pager(content, pager)
145
+ if STDOUT.tty? && pager.is_a?(String) && !pager.empty?
146
+ IO.popen(pager, "w"){|f| f.print content}
147
+ else
148
+ print content
149
+ end
150
+ end
151
+
152
+ def self.run
153
+ command_line_config = parse_options!(ARGV)
154
+
155
+ system_config =
156
+ unless command_line_config[:no_config_file]
157
+ possible_system_config_file_names = [
158
+ DocDiff::SystemConfigFileName,
159
+ ]
160
+ existing_system_config_file_names =
161
+ possible_system_config_file_names.select{|fn| File.exist? fn}
162
+ if existing_system_config_file_names.size >= 2
163
+ raise <<~EOS
164
+ More than one system config file found, using the first one: \
165
+ #{existing_system_config_file_names.inspect}
166
+ EOS
167
+ end
168
+ filename = existing_system_config_file_names.first
169
+ config, message = read_config_from_file(filename)
170
+ STDERR.print message if command_line_config[:verbose]
171
+ config
172
+ end
173
+
174
+ user_config =
175
+ unless command_line_config[:no_config_file]
176
+ possible_user_config_file_names = [
177
+ DocDiff::UserConfigFileName,
178
+ DocDiff::AltUserConfigFileName,
179
+ DocDiff::XDGUserConfigFileName,
180
+ ]
181
+ existing_user_config_file_names =
182
+ possible_user_config_file_names.select{|fn| File.exist? fn}
183
+ if existing_user_config_file_names.size >= 2
184
+ raise <<~EOS
185
+ Only one user config file can be used at the same time. \
186
+ Keep one and remove or rename the others: \
187
+ #{existing_user_config_file_names.inspect}
188
+ EOS
189
+ end
190
+ filename = existing_user_config_file_names.first
191
+ config, message = read_config_from_file(filename)
192
+ STDERR.print message if command_line_config[:verbose]
193
+ config
194
+ end
195
+
196
+ config_from_specified_file =
197
+ if filename = command_line_config[:config_file]
198
+ config, message = read_config_from_file(filename)
199
+ STDERR.print message if command_line_config[:verbose] == true
200
+ config
201
+ end
202
+
203
+ config_from_program_name =
204
+ case File.basename($PROGRAM_NAME, ".*")
205
+ when "worddiff" then {:resolution => "word"}
206
+ when "chardiff" then {:resolution => "char"}
207
+ end
208
+
209
+ config_from_env_vars = {}
210
+ if (pager = ENV['DOCDIFF_PAGER']) && !pager.empty?
211
+ config_from_env_vars[:pager] = pager
212
+ end
213
+
214
+ config_in_effect = DocDiff::DEFAULT_CONFIG.dup
215
+ config_in_effect.merge!(config_from_program_name) if config_from_program_name
216
+ config_in_effect.merge!(system_config) if system_config
217
+ config_in_effect.merge!(user_config) if user_config
218
+ config_in_effect.merge!(config_from_env_vars) if config_from_env_vars
219
+ config_in_effect.merge!(config_from_specified_file) if config_from_specified_file
220
+ config_in_effect.merge!(command_line_config) if command_line_config
221
+
222
+ docdiff = DocDiff.new(config: config_in_effect)
223
+
224
+ file1_content = nil
225
+ file2_content = nil
226
+ raise "Try `#{File.basename($0)} --help' for more information." if ARGV[0].nil?
227
+ raise "Specify at least 2 target files." unless ARGV[0] && ARGV[1]
228
+ ARGV[0] = "/dev/stdin" if ARGV[0] == "-"
229
+ ARGV[1] = "/dev/stdin" if ARGV[1] == "-"
230
+ raise "No such file: #{ARGV[0]}." unless FileTest.exist?(ARGV[0])
231
+ raise "No such file: #{ARGV[1]}." unless FileTest.exist?(ARGV[1])
232
+ raise "#{ARGV[0]} is not readable." unless FileTest.readable?(ARGV[0])
233
+ raise "#{ARGV[1]} is not readable." unless FileTest.readable?(ARGV[1])
234
+ File.open(ARGV[0], "r"){|f| file1_content = f.read}
235
+ File.open(ARGV[1], "r"){|f| file2_content = f.read}
236
+
237
+ doc1 = nil
238
+ doc2 = nil
239
+
240
+ encoding1 = docdiff.config[:encoding]
241
+ encoding2 = docdiff.config[:encoding]
242
+ eol1 = docdiff.config[:eol]
243
+ eol2 = docdiff.config[:eol]
244
+
245
+ if docdiff.config[:encoding] == "auto"
246
+ encoding1 = DocDiff::CharString.guess_encoding(file1_content)
247
+ encoding2 = DocDiff::CharString.guess_encoding(file2_content)
248
+ case
249
+ when (encoding1 == "UNKNOWN" or encoding2 == "UNKNOWN")
250
+ raise "Document encoding unknown (#{encoding1}, #{encoding2})."
251
+ when encoding1 != encoding2
252
+ raise "Document encoding mismatch (#{encoding1}, #{encoding2})."
253
+ end
254
+ end
255
+
256
+ if docdiff.config[:eol] == "auto"
257
+ eol1 = DocDiff::CharString.guess_eol(file1_content)
258
+ eol2 = DocDiff::CharString.guess_eol(file2_content)
259
+ case
260
+ when (eol1.nil? or eol2.nil?)
261
+ raise "Document eol is nil (#{eol1.inspect}, #{eol2.inspect}). The document might be empty."
262
+ when (eol1 == 'UNKNOWN' or eol2 == 'UNKNOWN')
263
+ raise "Document eol unknown (#{eol1.inspect}, #{eol2.inspect})."
264
+ when (eol1 != eol2)
265
+ raise "Document eol mismatch (#{eol1}, #{eol2})."
266
+ end
267
+ end
268
+
269
+ doc1 = DocDiff::Document.new(file1_content, encoding1, eol1)
270
+ doc2 = DocDiff::Document.new(file2_content, encoding2, eol2)
271
+
272
+ output = docdiff.run(doc1, doc2,
273
+ {:resolution => docdiff.config[:resolution],
274
+ :format => docdiff.config[:format],
275
+ :digest => docdiff.config[:digest],
276
+ :display => docdiff.config[:display]})
277
+
278
+ print_or_write_to_pager(output, docdiff.config[:pager])
279
+ end
280
+ end
281
+ end
@@ -1,3 +1,3 @@
1
1
  module Docdiff
2
- VERSION = "0.6.4"
2
+ VERSION = "0.6.5"
3
3
  end
data/lib/docdiff/view.rb CHANGED
@@ -47,10 +47,22 @@ class View
47
47
  end
48
48
 
49
49
  def escape_inside(str, tags)
50
- str.gsub(tags[:inside_escape_pat]){|m| tags[:inside_escape_dic][m]}
50
+ str.gsub(tags[:inside_escape_pat]){|m|
51
+ if replacement = tags[:inside_escape_dic][m]
52
+ replacement
53
+ else
54
+ m
55
+ end
56
+ }
51
57
  end
52
58
  def escape_outside(str, tags)
53
- str.gsub(tags[:outside_escape_pat]){|m| tags[:outside_escape_dic][m]}
59
+ str.gsub(tags[:outside_escape_pat]){|m|
60
+ if replacement = tags[:outside_escape_dic][m]
61
+ replacement
62
+ else
63
+ m
64
+ end
65
+ }
54
66
  end
55
67
 
56
68
  def apply_style(tags, headfoot = true)
@@ -212,7 +224,7 @@ class View
212
224
  []
213
225
  end
214
226
  TTYEscapeDic = {'ThisRandomString' => 'ThisRandomString'}
215
- TTYEscapePat = /(\r\n|#{TTYEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
227
+ TTYEscapePat = /(#{TTYEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
216
228
  def tty_tags()
217
229
  {:outside_escape_dic => TTYEscapeDic,
218
230
  :outside_escape_pat => TTYEscapePat,
@@ -277,7 +289,7 @@ class View
277
289
  end
278
290
  HTMLEscapeDic = {'<'=>'&lt;', '>'=>'&gt;', '&'=>'&amp;', ' '=>'&nbsp;&nbsp;',
279
291
  "\r\n" => "<br />\r\n", "\r" => "<br />\r", "\n" => "<br />\n"}
280
- HTMLEscapePat = /(\r\n|#{HTMLEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
292
+ HTMLEscapePat = /(#{HTMLEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
281
293
  def html_tags()
282
294
  {:outside_escape_dic => HTMLEscapeDic,
283
295
  :outside_escape_pat => HTMLEscapePat,
@@ -387,7 +399,7 @@ class View
387
399
  []
388
400
  end
389
401
  WDIFFEscapeDic = {'ThisRandomString' => 'ThisRandomString'}
390
- WDIFFEscapePat = /(\r\n|#{WDIFFEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
402
+ WDIFFEscapePat = /(#{WDIFFEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
391
403
  def wdiff_tags()
392
404
  {:outside_escape_dic => WDIFFEscapeDic,
393
405
  :outside_escape_pat => WDIFFEscapePat,
@@ -431,7 +443,7 @@ class View
431
443
  def user_header(); []; end
432
444
  def user_footer(); []; end
433
445
  UserEscapeDic = {'ThisRandomString' => 'ThisRandomString'}
434
- UserEscapePat = /(\r\n|#{UserEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
446
+ UserEscapePat = /(#{UserEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
435
447
  def user_tags()
436
448
  {:outside_escape_dic => UserEscapeDic,
437
449
  :outside_escape_pat => UserEscapePat,
data/test/cli_test.rb ADDED
@@ -0,0 +1,312 @@
1
+ #!/usr/bin/ruby
2
+ # -*- coding: utf-8; -*-
3
+
4
+ require 'test/unit'
5
+ require 'nkf'
6
+ require 'docdiff/cli'
7
+
8
+ class TC_CLI < Test::Unit::TestCase
9
+ def test_parse_options!()
10
+ args = [
11
+ "--resolution=line",
12
+ "--char",
13
+ "--encoding=ASCII",
14
+ "--eucjp",
15
+ "--eol=CR",
16
+ "--crlf",
17
+ "--format=manued",
18
+ "--wdiff",
19
+ "--label=old",
20
+ "--label=new",
21
+ "--digest",
22
+ "--display=block",
23
+ "--pager='less --raw-control-chars'",
24
+ "--no-config-file",
25
+ "--config-file=./docdiff.conf",
26
+ "file1",
27
+ "file2",
28
+ ]
29
+ expected = {
30
+ :resolution => "char",
31
+ :encoding => "EUC-JP",
32
+ :eol => "CRLF",
33
+ :format => "wdiff",
34
+ :digest => true,
35
+ :label => ["old", "new"],
36
+ :display => "block",
37
+ :pager => "'less --raw-control-chars'",
38
+ :no_config_file => true,
39
+ :config_file => "./docdiff.conf",
40
+ }
41
+ assert_equal(expected, DocDiff::CLI.parse_options!(args, base_options: {}))
42
+ end
43
+
44
+ def test_parse_config_file_content()
45
+ content = ["# comment line\n",
46
+ " # comment line with leading space\n",
47
+ "foo1 = bar\n",
48
+ "foo2 = bar baz \n",
49
+ " foo3 = 123 # comment\n",
50
+ "foo4 = no \n",
51
+ "foo1 = tRue\n",
52
+ "\n",
53
+ "",
54
+ nil].join
55
+ expected = {:foo1=>true, :foo2=>"bar baz", :foo3=>123, :foo4=>false}
56
+ assert_equal(expected, DocDiff::CLI.parse_config_file_content(content))
57
+ end
58
+
59
+ def test_read_config_from_file()
60
+ filename = File.join(File.dirname(__FILE__), "fixture/simple.conf")
61
+ expected = {:foo1 => true, :foo2 => "bar baz", :foo3 => 123, :foo4 => false}
62
+ config, _message = DocDiff::CLI.read_config_from_file(filename)
63
+ assert_equal(expected, config)
64
+ end
65
+
66
+ def test_read_config_from_file_raises_exception()
67
+ assert_raise(Errno::ENOENT) do
68
+ config, message = DocDiff::CLI.read_config_from_file("no/such/file")
69
+ end
70
+ end
71
+
72
+ def test_cli_resolution_line()
73
+ expected = <<~EOS.chomp
74
+ [-Hello, my name is Watanabe.
75
+ I am just another Ruby porter.
76
+ -]{+Hello, my name is matz.
77
+ It's me who has created Ruby. I am a Ruby hacker.
78
+ +}
79
+ EOS
80
+ cmd = "ruby -I lib bin/docdiff --resolution=line --format=wdiff" +
81
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
82
+ actual = `#{cmd}`
83
+ assert_equal(expected, actual)
84
+ end
85
+
86
+ def test_cli_resolution_word()
87
+ expected = <<~EOS
88
+ Hello, my name is [-Watanabe.-]{+matz.+}
89
+ {+It's me who has created Ruby. +}I am [-just another -]{+a +}Ruby [-porter.-]{+hacker.+}
90
+ EOS
91
+ cmd = "ruby -I lib bin/docdiff --resolution=word --format=wdiff" +
92
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
93
+ actual = `#{cmd}`
94
+ assert_equal(expected, actual)
95
+ end
96
+
97
+ def test_cli_resolution_char()
98
+ expected = <<~EOS
99
+ Hello, my name is [-W-]{+m+}at[-anabe-]{+z+}.
100
+ {+It's me who has created Ruby. +}I am [-just -]a[-nother-] Ruby [-port-]{+hack+}er.
101
+ EOS
102
+ cmd = "ruby -I lib bin/docdiff --resolution=char --format=wdiff" +
103
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
104
+ actual = `#{cmd}`
105
+ assert_equal(expected, actual)
106
+ end
107
+
108
+ def test_cli_encoding_ascii()
109
+ expected = <<~EOS
110
+ Hello, my name is [-Watanabe.-]{+matz.+}
111
+ {+It's me who has created Ruby. +}I am [-just another -]{+a +}Ruby [-porter.-]{+hacker.+}
112
+ EOS
113
+ cmd = "ruby -I lib bin/docdiff --encoding=ASCII --format=wdiff" +
114
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
115
+ actual = `#{cmd}`
116
+ assert_equal(expected, actual)
117
+ end
118
+
119
+ def test_cli_encoding_euc_jp()
120
+ expected = NKF.nkf("--ic=UTF-8 --oc=EUC-JP", <<~EOS)
121
+ [-こんにちは-]{+こんばんは+}、私の[-名前はわたなべです-]{+名前はまつもとです+}。
122
+ {+Rubyを作ったのは私です。+}私は[-Just Another -]Ruby [-Porter-]{+Hacker+}です。
123
+ EOS
124
+ cmd = "ruby --external-encoding EUC-JP -I lib bin/docdiff --encoding=EUC-JP --format=wdiff" +
125
+ " test/fixture/01_ja_eucjp_lf.txt test/fixture/02_ja_eucjp_lf.txt"
126
+ actual = `#{cmd}`.force_encoding("EUC-JP")
127
+ assert_equal(expected, actual)
128
+ end
129
+
130
+ def test_cli_encoding_shift_jis()
131
+ expected_utf8_cr =
132
+ "[-こんにちは-]{+こんばんは+}、私の[-名前はわたなべです-]{+名前はまつもとです+}。\r" +
133
+ "{+Rubyを作ったのは私です。+}私は[-Just Another -]Ruby [-Porter-]{+Hacker+}です。\r"
134
+ expected = NKF.nkf("--ic=UTF-8 --oc=Shift_JIS", expected_utf8_cr)
135
+ cmd = "ruby --external-encoding Shift_JIS -I lib bin/docdiff --encoding=Shift_JIS --format=wdiff" +
136
+ " test/fixture/01_ja_sjis_cr.txt test/fixture/02_ja_sjis_cr.txt"
137
+ actual = `#{cmd}`.force_encoding("Shift_JIS")
138
+ assert_equal(expected, actual)
139
+ end
140
+
141
+ def test_cli_encoding_utf_8()
142
+ expected = <<~EOS
143
+ [-こんにちは-]{+こんばんは+}、私の[-名前はわたなべです-]{+名前はまつもとです+}。
144
+ {+Rubyを作ったのは私です。+}私は[-Just Another -]Ruby [-Porter-]{+Hacker+}です。
145
+ EOS
146
+ cmd = "ruby -I lib bin/docdiff --encoding=UTF-8 --format=wdiff" +
147
+ " test/fixture/01_ja_utf8_lf.txt test/fixture/02_ja_utf8_lf.txt"
148
+ actual = `#{cmd}`.force_encoding("UTF-8")
149
+ assert_equal(expected, actual)
150
+ end
151
+
152
+ def test_cli_eol_cr()
153
+ expected =
154
+ "Hello, my name is [-Watanabe.-]{+matz.+}\r" +
155
+ "{+It's me who has created Ruby. +}I am [-just another -]{+a +}Ruby [-porter.-]{+hacker.+}\r"
156
+ cmd = "ruby -I lib bin/docdiff --eol=CR --format=wdiff" +
157
+ " test/fixture/01_en_ascii_cr.txt test/fixture/02_en_ascii_cr.txt"
158
+ actual = `#{cmd}`
159
+ assert_equal(expected, actual)
160
+ end
161
+
162
+ def test_cli_eol_lf()
163
+ expected =
164
+ "Hello, my name is [-Watanabe.-]{+matz.+}\n" +
165
+ "{+It's me who has created Ruby. +}I am [-just another -]{+a +}Ruby [-porter.-]{+hacker.+}\n"
166
+ cmd = "ruby -I lib bin/docdiff --eol=LF --format=wdiff" +
167
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
168
+ actual = `#{cmd}`
169
+ assert_equal(expected, actual)
170
+ end
171
+
172
+ def test_cli_eol_crlf()
173
+ expected =
174
+ "Hello, my name is [-Watanabe.-]{+matz.+}\r\n" +
175
+ "{+It's me who has created Ruby. +}I am [-just another -]{+a +}Ruby [-porter.-]{+hacker.+}\r\n"
176
+ cmd = "ruby -I lib bin/docdiff --eol=CRLF --format=wdiff" +
177
+ " test/fixture/01_en_ascii_crlf.txt test/fixture/02_en_ascii_crlf.txt"
178
+ actual = `#{cmd}`
179
+ assert_equal(expected, actual)
180
+ end
181
+
182
+ def test_cli_format_html()
183
+ expected = <<~EOS
184
+ <span class="common">Hello, my name is </span>\
185
+ <span class="before-change"><del>Watanabe.</del></span>\
186
+ <span class="after-change"><ins>matz.</ins></span>\
187
+ <span class="common"><br />
188
+ EOS
189
+ cmd = "ruby -I lib bin/docdiff --format=html" +
190
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
191
+ actual = `#{cmd}`.scan(/^.*?$\n/m)[-4]
192
+ assert_equal(expected, actual)
193
+ end
194
+
195
+ def test_cli_format_manued()
196
+ expected = "Hello, my name is [Watanabe./matz.]\n"
197
+ cmd = "ruby -I lib bin/docdiff --format=manued" +
198
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
199
+ actual = `#{cmd}`.scan(/^.*?$\n/m)[-2]
200
+ assert_equal(expected, actual)
201
+ end
202
+
203
+ def test_cli_format_tty()
204
+ expected = "Hello, my name is \e[7;4;33mWatanabe.\e[0m\e[7;1;32mmatz.\e[0m\n"
205
+ cmd = "ruby -I lib bin/docdiff --format=tty" +
206
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
207
+ actual = `#{cmd}`.scan(/^.*?$\n/m).first
208
+ assert_equal(expected, actual)
209
+ end
210
+
211
+ def test_cli_format_wdiff()
212
+ expected = "Hello, my name is [-Watanabe.-]{+matz.+}\n"
213
+ cmd = "ruby -I lib bin/docdiff --format=wdiff" +
214
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
215
+ actual = `#{cmd}`.scan(/^.*?$\n/m).first
216
+ assert_equal(expected, actual)
217
+ end
218
+
219
+ def test_cli_digest()
220
+ expected = <<~EOS
221
+ ----
222
+ 1,1
223
+ Hello, my name is [-Watanabe.-]{+matz.+}
224
+
225
+ ----
226
+ (2),2
227
+
228
+ {+It's me who has created Ruby. +}I am
229
+ ----
230
+ 2,2
231
+ I am [-just another -]{+a +}Ruby
232
+ ----
233
+ 2,2
234
+ Ruby [-porter.-]{+hacker.+}
235
+
236
+ ----
237
+ EOS
238
+ cmd = "ruby -I lib bin/docdiff --digest --format=wdiff" +
239
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
240
+ actual = `#{cmd}`.force_encoding("UTF-8")
241
+ assert_equal(expected, actual)
242
+ end
243
+
244
+ def test_cli_display_inline()
245
+ expected = <<~EOS
246
+ ----
247
+ 1,1
248
+ Hello, my name is [-Watanabe.-]{+matz.+}
249
+
250
+ ----
251
+ (2),2
252
+
253
+ {+It's me who has created Ruby. +}I am
254
+ ----
255
+ 2,2
256
+ I am [-just another -]{+a +}Ruby
257
+ ----
258
+ 2,2
259
+ Ruby [-porter.-]{+hacker.+}
260
+
261
+ ----
262
+ EOS
263
+ cmd = "ruby -I lib bin/docdiff --digest --display=inline --format=wdiff" +
264
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
265
+ actual = `#{cmd}`.force_encoding("UTF-8")
266
+ assert_equal(expected, actual)
267
+ end
268
+
269
+ def test_cli_display_block()
270
+ expected = <<~EOS
271
+ ----
272
+ 1,1
273
+ Hello, my name is [-Watanabe.-]
274
+
275
+ Hello, my name is {+matz.+}
276
+
277
+ ----
278
+ (2),2
279
+
280
+ I am
281
+
282
+ {+It's me who has created Ruby. +}I am
283
+ ----
284
+ 2,2
285
+ I am [-just another -]Ruby
286
+ I am {+a +}Ruby
287
+ ----
288
+ 2,2
289
+ Ruby [-porter.-]
290
+
291
+ Ruby {+hacker.+}
292
+
293
+ ----
294
+ EOS
295
+ cmd = "ruby -I lib bin/docdiff --digest --display=block --format=wdiff" +
296
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
297
+ actual = `#{cmd}`.force_encoding("UTF-8")
298
+ assert_equal(expected, actual)
299
+ end
300
+
301
+ def test_cli_config_file_format_wdiff()
302
+ config_file_name = File.join(File.dirname(__FILE__), "fixture/format_wdiff.conf")
303
+ expected = <<~EOS
304
+ Hello, my name is [-Watanabe.-]{+matz.+}
305
+ {+It's me who has created Ruby. +}I am [-just another -]{+a +}Ruby [-porter.-]{+hacker.+}
306
+ EOS
307
+ cmd = "ruby -I lib bin/docdiff --config-file=#{config_file_name}" +
308
+ " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
309
+ actual = `#{cmd}`
310
+ assert_equal(expected, actual)
311
+ end
312
+ end
data/test/docdiff_test.rb CHANGED
@@ -117,22 +117,6 @@ class TC_DocDiff < Test::Unit::TestCase
117
117
  assert_equal(expected, docdiff.run(doc1, doc2, {:resolution => "char", :format => "manued", :digest => false}))
118
118
  end
119
119
 
120
- def test_parse_config_file_content()
121
- content = ["# comment line\n",
122
- " # comment line with leading space\n",
123
- "foo1 = bar\n",
124
- "foo2 = bar baz \n",
125
- " foo3 = 123 # comment\n",
126
- "foo4 = no \n",
127
- "foo1 = tRue\n",
128
- "\n",
129
- "",
130
- nil].join
131
- expected = {:foo1=>true, :foo2=>"bar baz", :foo3=>123, :foo4=>false}
132
- assert_equal(expected,
133
- DocDiff.parse_config_file_content(content))
134
- end
135
-
136
120
  def test_run_line_user()
137
121
  doc1 = Document.new("foo bar\nbaz", 'US-ASCII', 'LF')
138
122
  doc2 = Document.new("foo beer\nbaz", 'US-ASCII', 'LF')
@@ -187,13 +171,6 @@ class TC_DocDiff < Test::Unit::TestCase
187
171
  expected = "<=>foo </=><=>b</=><!->a</!-><!+>ee</!+><=>r</=><=>\n</=><=>baz</=>"
188
172
  assert_equal(expected, docdiff.run(doc1, doc2, {:resolution => "char", :format => "user", :digest => false}))
189
173
  end
190
- def test_cli()
191
- expected = "Hello, my name is [-Watanabe.-]{+matz.+}\n"
192
- cmd = "ruby -I lib bin/docdiff --wdiff" +
193
- " test/fixture/01_en_ascii_lf.txt test/fixture/02_en_ascii_lf.txt"
194
- actual = `#{cmd}`.scan(/^.*?$\n/m).first
195
- assert_equal(expected, actual)
196
- end
197
174
 
198
175
  def teardown()
199
176
  #
@@ -0,0 +1 @@
1
+ format = wdiff
@@ -0,0 +1,9 @@
1
+ # comment line
2
+ # comment line with leading space
3
+ foo1 = bar
4
+ foo2 = bar baz
5
+ foo3 = 123 # comment
6
+ foo4 = no
7
+
8
+
9
+ foo1 = tRue
metadata CHANGED
@@ -1,14 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docdiff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.4
4
+ version: 0.6.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hisashi Morita
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-12-13 00:00:00.000000000 Z
11
- dependencies: []
10
+ date: 2025-12-29 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: test-unit
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '3'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '3'
12
26
  description: |-
13
27
  DocDiff compares two text files and shows the
14
28
  difference. It can compare files word by word,
@@ -56,6 +70,7 @@ files:
56
70
  - lib/doc_diff.rb
57
71
  - lib/docdiff.rb
58
72
  - lib/docdiff/charstring.rb
73
+ - lib/docdiff/cli.rb
59
74
  - lib/docdiff/diff.rb
60
75
  - lib/docdiff/diff/contours.rb
61
76
  - lib/docdiff/diff/editscript.rb
@@ -73,6 +88,7 @@ files:
73
88
  - lib/docdiff/version.rb
74
89
  - lib/docdiff/view.rb
75
90
  - test/charstring_test.rb
91
+ - test/cli_test.rb
76
92
  - test/diff_test.rb
77
93
  - test/difference_test.rb
78
94
  - test/docdiff_test.rb
@@ -93,8 +109,10 @@ files:
93
109
  - test/fixture/02_ja_sjis_crlf.txt
94
110
  - test/fixture/02_ja_utf8_crlf.txt
95
111
  - test/fixture/02_ja_utf8_lf.txt
112
+ - test/fixture/format_wdiff.conf
96
113
  - test/fixture/humpty_dumpty01_ascii_lf.txt
97
114
  - test/fixture/humpty_dumpty02_ascii_lf.txt
115
+ - test/fixture/simple.conf
98
116
  - test/test_helper.rb
99
117
  - test/view_test.rb
100
118
  homepage: https://github.com/hisashim/docdiff