difftastic 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|