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 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