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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 31125243809498f1920e30f4c8f2dc681e6862a2c06f4c7c9314eb717f30bdfb
4
- data.tar.gz: ae9612238b4267ada429d79611ec0dcbfe694b319d601dac4a402e9212b0c372
3
+ metadata.gz: 207e850cc699486c73de8d32b74bd42cb3d13f2a67027038bef770483d24b0e7
4
+ data.tar.gz: 43ade182f9cedb9203a02afaab34f814ea4f38f6c38f3bbadefca756a23674d6
5
5
  SHA512:
6
- metadata.gz: feef6a85bb995f6678115c0edfafc41a73b19c2927466790eb75151010c526968d1881a9e84bfe02793516028d8325eb41747219de1ac1af1363c7057533fa2a
7
- data.tar.gz: a53103ffbcc0fcbf5e074c13a91b6da0c965d8a50b48304f65a42137dd3334612f90f5b3e42ea0668b37b895af4bcfdc316dcaaf78c8f8f4f6461db85a995791
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
+ ```
@@ -1,22 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Difftastic::Differ
4
- def initialize(background: nil, color: nil, syntax_highlight: nil, context: nil, tab_width: nil, parse_error_limit: nil, underline_highlights: true, left_label: nil, right_label: nil)
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
- old = Difftastic.pretty(old)
19
- new = Difftastic.pretty(new)
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{#{@tab_width},}/, 2)
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 = (stripped_line.index("#{' ' * @tab_width}#{rhs}") || 0) + @tab_width
360
+ offset = index + tab_width
348
361
  minimum_offset = 29
349
362
 
350
363
  [minimum_offset, offset].max
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Difftastic
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
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, indent_width: 2, max_width: 80, indentation_bytes: "\t")
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 << (" " * indent)
81
- buffer << pretty(key, indent:)
82
- buffer << " => "
83
- buffer << pretty(value, indent:)
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 << (" " * indent)
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 * indent_width)
100
- "[\n#{indentation_bytes * (indent + 1)}#{items.join(",\n#{indentation_bytes * (indent + 1)}")},\n#{indentation_bytes * indent}]"
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 * indent_width)
115
- "Set[\n#{indentation_bytes * (indent + 1)}#{items.join(",\n#{indentation_bytes * (indent + 1)}")},\n#{indentation_bytes * indent}]"
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
- object.instance_variables.each do |name|
130
- buffer << (" " * indent)
131
- buffer << ":#{name} => "
132
- buffer << pretty(object.instance_variable_get(name), indent:)
133
- buffer << ",\n"
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 << (" " * indent)
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.2.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-23 00:00:00.000000000 Z
10
+ date: 2025-01-30 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  email:
13
13
  - joel@drapper.me