difftastic 0.2.0 → 0.3.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/README.md +103 -0
- data/lib/difftastic/differ.rb +18 -5
- data/lib/difftastic/version.rb +1 -1
- data/lib/difftastic.rb +51 -19
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 207e850cc699486c73de8d32b74bd42cb3d13f2a67027038bef770483d24b0e7
|
4
|
+
data.tar.gz: 43ade182f9cedb9203a02afaab34f814ea4f38f6c38f3bbadefca756a23674d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 786f0f2ad6ca34147d93a21d12df4bcf084f8468b82052ea047206cbe3be69c6814f3c7911aabba1d8b17b27fc2b9b5e8f3e6a9a3f0c60d359c60eb0e4f2b624
|
7
|
+
data.tar.gz: bae8870b2892c9398eca3a196446c208fe581f65c0ef58e1f271f41bb1a46aa87279ad96b2ead45d4b97093ae8865d878fa766a2830f2bdc6b5a51bebec37446
|
data/README.md
CHANGED
@@ -1 +1,104 @@
|
|
1
1
|
# Difftastic Ruby
|
2
|
+
|
3
|
+
A Ruby interface and wrapper for the wonderful [Difftastic](https://difftastic.wilfred.me.uk) CLI tool.
|
4
|
+
|
5
|
+
## Creating a Differ
|
6
|
+
|
7
|
+
First, create a differ with your configuration:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
MY_DIFFER = Difftastic::Differ.new(
|
11
|
+
background: :dark,
|
12
|
+
color: :always,
|
13
|
+
left_label: "Expected",
|
14
|
+
right_label: "Actual"
|
15
|
+
)
|
16
|
+
```
|
17
|
+
|
18
|
+
## Diffing Objects
|
19
|
+
|
20
|
+
You can diff objects with different configurations:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
a = { foo: 1, bar: [2, 3, 4] }
|
24
|
+
b = { foo: 1, bar: [2, 4, 3] }
|
25
|
+
|
26
|
+
puts MY_DIFFER.diff_objects(a, b)
|
27
|
+
```
|
28
|
+
|
29
|
+
## Diffing Ruby Code
|
30
|
+
|
31
|
+
You can diff Ruby code:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
a = <<~RUBY
|
35
|
+
def hello
|
36
|
+
puts "Hello, world!"
|
37
|
+
end
|
38
|
+
RUBY
|
39
|
+
|
40
|
+
b = <<~RUBY
|
41
|
+
def hello
|
42
|
+
puts "Goodbye, world!"
|
43
|
+
end
|
44
|
+
RUBY
|
45
|
+
|
46
|
+
puts MY_DIFFER.diff_ruby(a, b)
|
47
|
+
```
|
48
|
+
|
49
|
+
## Additional File Type Methods
|
50
|
+
|
51
|
+
You can also diff other file types using the following methods:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
a = "<html>\n\t<body>\n\t\t<h1>Hello, world!</h1>\n\t</body>\n</html>"
|
55
|
+
b = "<html>\n\t<body>\n\t\t<h1>Goodbye, world!</h1>\n\t</body>\n</html>"
|
56
|
+
|
57
|
+
puts MY_DIFFER.diff_html(a, b)
|
58
|
+
|
59
|
+
a = '{ "foo": 1, "bar": 2 }'
|
60
|
+
b = '{ "foo": 1, "bar": 3 }'
|
61
|
+
|
62
|
+
puts MY_DIFFER.diff_json(a, b)
|
63
|
+
|
64
|
+
a = "body { color: red; }"
|
65
|
+
b = "body { color: blue; }"
|
66
|
+
|
67
|
+
puts MY_DIFFER.diff_css(a, b)
|
68
|
+
|
69
|
+
a = "<note><to>Tove</to><from>Jani</from></note>"
|
70
|
+
b = "<note><to>Tove</to><from>John</from></note>"
|
71
|
+
|
72
|
+
puts MY_DIFFER.diff_xml(a, b)
|
73
|
+
|
74
|
+
a = "foo: 1\nbar: 2"
|
75
|
+
b = "foo: 1\nbar: 3"
|
76
|
+
|
77
|
+
puts MY_DIFFER.diff_yaml(a, b)
|
78
|
+
```
|
79
|
+
|
80
|
+
## Configuring Difftastic::Differ
|
81
|
+
|
82
|
+
You can configure the `Difftastic::Differ` instance with various options:
|
83
|
+
|
84
|
+
- `background`: Set the background color (`:dark` or `:light`).
|
85
|
+
- `color`: Set the color mode (`:always`, `:never`, or `:auto`).
|
86
|
+
- `syntax_highlight`: Enable or disable syntax highlighting (`:on` or `:off`).
|
87
|
+
- `context`: Set the number of context lines to display.
|
88
|
+
- `width`: Use this many columns when calculating line wrapping. If not specified, difftastic will detect the terminal width.
|
89
|
+
- `tab_width`: Set the tab width for indentation.
|
90
|
+
- `parse_error_limit`: Set the limit for parse errors.
|
91
|
+
- `underline_highlights`: Enable or disable underlining highlights (`true` or `false`).
|
92
|
+
- `left_label`: Set the label for the left side of the diff.
|
93
|
+
- `right_label`: Set the label for the right side of the diff.
|
94
|
+
- `display`: Set the display mode (`"side-by-side-show-both"`, `"side-by-side"`, or `"inline"`).
|
95
|
+
|
96
|
+
## Pretty Method
|
97
|
+
|
98
|
+
The `Difftastic` module includes a `pretty` method for formatting objects:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
object = { foo: 1, bar: [2, 3, 4] }
|
102
|
+
formatted_object = Difftastic.pretty(object)
|
103
|
+
puts formatted_object
|
104
|
+
```
|
data/lib/difftastic/differ.rb
CHANGED
@@ -1,22 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Difftastic::Differ
|
4
|
-
|
4
|
+
DEFAULT_TAB_WIDTH = 2
|
5
|
+
|
6
|
+
def initialize(background: nil, color: nil, syntax_highlight: nil, context: nil, width: nil, tab_width: nil, parse_error_limit: nil, underline_highlights: true, left_label: nil, right_label: nil, display: "side-by-side-show-both")
|
5
7
|
@show_paths = false
|
6
8
|
@background = background => :dark | :light | nil
|
7
9
|
@color = color => :always | :never | :auto | nil
|
8
10
|
@syntax_highlight = syntax_highlight => :on | :off | nil
|
9
11
|
@context = context => Integer | nil
|
12
|
+
@width = width => Integer | nil
|
10
13
|
@tab_width = tab_width => Integer | nil
|
11
14
|
@parse_error_limit = parse_error_limit => Integer | nil
|
12
15
|
@underline_highlights = underline_highlights => true | false
|
13
16
|
@left_label = left_label => String | nil
|
14
17
|
@right_label = right_label => String | nil
|
18
|
+
@display = display
|
15
19
|
end
|
16
20
|
|
17
21
|
def diff_objects(old, new)
|
18
|
-
|
19
|
-
|
22
|
+
tab_width = @tab_width || DEFAULT_TAB_WIDTH
|
23
|
+
|
24
|
+
old = Difftastic.pretty(old, tab_width:)
|
25
|
+
new = Difftastic.pretty(new, tab_width:)
|
20
26
|
|
21
27
|
diff_strings(old, new, file_extension: "rb")
|
22
28
|
end
|
@@ -290,6 +296,8 @@ class Difftastic::Differ
|
|
290
296
|
("--background=#{@background}" if @background),
|
291
297
|
("--syntax-highlight=#{@syntax_highlight}" if @syntax_highlight),
|
292
298
|
("--tab-width=#{@tab_width}" if @tab_width),
|
299
|
+
("--display=#{@display}" if @display),
|
300
|
+
("--width=#{@width}" if @width),
|
293
301
|
].compact!
|
294
302
|
|
295
303
|
result = Difftastic.execute(options.join(" "))
|
@@ -341,10 +349,15 @@ class Difftastic::Differ
|
|
341
349
|
private
|
342
350
|
|
343
351
|
def right_label_offset(line)
|
352
|
+
tab_width = @tab_width || DEFAULT_TAB_WIDTH
|
344
353
|
stripped_line = ::Difftastic::ANSI.strip_formatting(line)
|
345
|
-
_lhs, rhs = stripped_line.split(/\s{#{
|
354
|
+
_lhs, rhs = stripped_line.split(/\s{#{tab_width},}/, 2)
|
355
|
+
|
356
|
+
index = stripped_line.index("#{' ' * tab_width}#{rhs}")
|
357
|
+
index = @width / 2 if @width && index.nil?
|
358
|
+
index = 0 if index.nil?
|
346
359
|
|
347
|
-
offset =
|
360
|
+
offset = index + tab_width
|
348
361
|
minimum_offset = 29
|
349
362
|
|
350
363
|
[minimum_offset, offset].max
|
data/lib/difftastic/version.rb
CHANGED
data/lib/difftastic.rb
CHANGED
@@ -11,6 +11,9 @@ module Difftastic
|
|
11
11
|
GEM_NAME = "difftastic"
|
12
12
|
DEFAULT_DIR = File.expand_path(File.join(__dir__, "..", "exe"))
|
13
13
|
|
14
|
+
class ExecutableNotFoundException < StandardError
|
15
|
+
end
|
16
|
+
|
14
17
|
def self.execute(command)
|
15
18
|
`#{executable} #{command}`
|
16
19
|
end
|
@@ -71,33 +74,44 @@ module Difftastic
|
|
71
74
|
exe_file
|
72
75
|
end
|
73
76
|
|
74
|
-
def self.pretty(object, indent: 0,
|
77
|
+
def self.pretty(object, indent: 0, tab_width: 2, max_width: 60, max_depth: 5, max_instance_variables: 10, original_object: nil)
|
78
|
+
return "self" if object && object == original_object
|
79
|
+
|
80
|
+
original_object ||= object
|
81
|
+
|
75
82
|
case object
|
76
83
|
when Hash
|
84
|
+
return "{}" if object.empty?
|
85
|
+
|
77
86
|
buffer = +"{\n"
|
78
87
|
indent += 1
|
79
88
|
object.each do |key, value|
|
80
|
-
buffer << ("
|
81
|
-
|
82
|
-
|
83
|
-
|
89
|
+
buffer << ("\t" * indent)
|
90
|
+
case key
|
91
|
+
when Symbol
|
92
|
+
buffer << "#{key.name}: "
|
93
|
+
else
|
94
|
+
buffer << pretty(key, indent:, original_object:)
|
95
|
+
buffer << " => "
|
96
|
+
end
|
97
|
+
buffer << pretty(value, indent:, original_object:)
|
84
98
|
buffer << ",\n"
|
85
99
|
end
|
86
100
|
indent -= 1
|
87
|
-
buffer << ("
|
101
|
+
buffer << ("\t" * indent)
|
88
102
|
buffer << "}"
|
89
103
|
when Array
|
90
104
|
new_lines = false
|
91
105
|
length = 0
|
92
106
|
items = object.map do |item|
|
93
|
-
pretty_item = pretty(item, indent: indent + 1)
|
107
|
+
pretty_item = pretty(item, indent: indent + 1, original_object:)
|
94
108
|
new_lines = true if pretty_item.include?("\n")
|
95
109
|
length += pretty_item.bytesize
|
96
110
|
pretty_item
|
97
111
|
end
|
98
112
|
|
99
|
-
if new_lines || length > max_width - (indent *
|
100
|
-
"[\n#{
|
113
|
+
if new_lines || length > max_width - (indent * tab_width)
|
114
|
+
"[\n#{"\t" * (indent + 1)}#{items.join(",\n#{"\t" * (indent + 1)}")},\n#{"\t" * indent}]"
|
101
115
|
else
|
102
116
|
"[#{items.join(', ')}]"
|
103
117
|
end
|
@@ -105,36 +119,54 @@ module Difftastic
|
|
105
119
|
new_lines = false
|
106
120
|
length = 0
|
107
121
|
items = object.to_a.sort!.map do |item|
|
108
|
-
pretty_item = pretty(item, indent: indent + 1)
|
122
|
+
pretty_item = pretty(item, indent: indent + 1, original_object:)
|
109
123
|
new_lines = true if pretty_item.include?("\n")
|
110
124
|
length += pretty_item.bytesize
|
111
125
|
pretty_item
|
112
126
|
end
|
113
127
|
|
114
|
-
if new_lines || length > max_width - (indent *
|
115
|
-
"Set[\n#{
|
128
|
+
if new_lines || length > max_width - (indent * tab_width)
|
129
|
+
"Set[\n#{"\t" * (indent + 1)}#{items.join(",\n#{"\t" * (indent + 1)}")},\n#{"\t" * indent}]"
|
116
130
|
else
|
117
131
|
"Set[#{items.join(', ')}]"
|
118
132
|
end
|
119
133
|
when Module
|
120
134
|
object.name
|
135
|
+
when Pathname
|
136
|
+
%(Pathname("#{object.to_path}"))
|
121
137
|
when Symbol, String, Integer, Float, Regexp, Range, Rational, Complex, true, false, nil
|
122
138
|
object.inspect
|
123
139
|
else
|
124
140
|
buffer = +""
|
125
141
|
instance_variables = object.instance_variables
|
126
|
-
if instance_variables.length > 0
|
142
|
+
if instance_variables.length > 0 && indent < max_depth
|
127
143
|
buffer << "#{object.class.name}(\n"
|
128
144
|
indent += 1
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
145
|
+
|
146
|
+
if indent < max_depth
|
147
|
+
object.instance_variables.take(max_instance_variables).each do |name|
|
148
|
+
buffer << ("\t" * indent)
|
149
|
+
buffer << name.name
|
150
|
+
buffer << " = "
|
151
|
+
|
152
|
+
buffer << pretty(object.instance_variable_get(name), indent:, original_object:)
|
153
|
+
buffer << ",\n"
|
154
|
+
end
|
155
|
+
|
156
|
+
if object.instance_variables.count > max_instance_variables
|
157
|
+
buffer << ("\t" * indent)
|
158
|
+
buffer << "...\n"
|
159
|
+
end
|
160
|
+
else
|
161
|
+
buffer << ("\t" * indent)
|
162
|
+
buffer << "...\n"
|
134
163
|
end
|
164
|
+
|
135
165
|
indent -= 1
|
136
|
-
buffer << ("
|
166
|
+
buffer << ("\t" * indent)
|
137
167
|
buffer << ")"
|
168
|
+
elsif indent >= max_depth
|
169
|
+
buffer << "#{object.class.name}(...)"
|
138
170
|
else
|
139
171
|
buffer << "#{object.class.name}()"
|
140
172
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: difftastic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Drapper
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-01-
|
10
|
+
date: 2025-01-30 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
email:
|
13
13
|
- joel@drapper.me
|