diff options
author | Austin Ziegler <austin@zieglers.ca> | 2020-07-03 12:41:48 -0400 |
---|---|---|
committer | Austin Ziegler <austin@zieglers.ca> | 2020-07-03 12:41:48 -0400 |
commit | 476b27efa27d89831ecb9f33e070ce8efa4f5d5e (patch) | |
tree | d24be368ab9f4b9e84b851eb219d4bd353a08d9d /lib/diff/lcs | |
parent | ba11b5923a0007a950bfedef6bd3596baf200f2b (diff) | |
download | diff-lcs-sorbet.tar.gz |
Attempt to sorbetize #1sorbet
Diffstat (limited to 'lib/diff/lcs')
-rw-r--r-- | lib/diff/lcs/array.rb | 1 | ||||
-rw-r--r-- | lib/diff/lcs/backports.rb | 1 | ||||
-rw-r--r-- | lib/diff/lcs/block.rb | 30 | ||||
-rw-r--r-- | lib/diff/lcs/callbacks.rb | 1 | ||||
-rw-r--r-- | lib/diff/lcs/change.rb | 155 | ||||
-rw-r--r-- | lib/diff/lcs/htmldiff.rb | 5 | ||||
-rw-r--r-- | lib/diff/lcs/hunk.rb | 42 | ||||
-rw-r--r-- | lib/diff/lcs/internals.rb | 29 | ||||
-rw-r--r-- | lib/diff/lcs/ldiff.rb | 1 | ||||
-rw-r--r-- | lib/diff/lcs/string.rb | 1 |
10 files changed, 183 insertions, 83 deletions
diff --git a/lib/diff/lcs/array.rb b/lib/diff/lcs/array.rb index 88035ae..80a5c6e 100644 --- a/lib/diff/lcs/array.rb +++ b/lib/diff/lcs/array.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# typed: strict require 'diff/lcs' diff --git a/lib/diff/lcs/backports.rb b/lib/diff/lcs/backports.rb index 642fc9c..7109368 100644 --- a/lib/diff/lcs/backports.rb +++ b/lib/diff/lcs/backports.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# typed: true unless 0.respond_to?(:positive?) class Fixnum # rubocop:disable Lint/UnifiedInteger, Style/Documentation diff --git a/lib/diff/lcs/block.rb b/lib/diff/lcs/block.rb index 430702d..d31a4d3 100644 --- a/lib/diff/lcs/block.rb +++ b/lib/diff/lcs/block.rb @@ -1,15 +1,31 @@ # frozen_string_literal: true +# typed: strict # A block is an operation removing, adding, or changing a group of items. # Basically, this is just a list of changes, where each change adds or # deletes a single item. Used by bin/ldiff. class Diff::LCS::Block - attr_reader :changes, :insert, :remove + extend T::Sig + ArrayOfChange = T.type_alias { T::Array[Diff::LCS::Change] } + + # The full set of changes in the block. + sig { returns(ArrayOfChange) } + attr_reader :changes + + # The insertions in the block. + sig { returns(ArrayOfChange) } + attr_reader :insert + + # The deletions in the block. + sig { returns(ArrayOfChange) } + attr_reader :remove + + sig { params(chunk: ArrayOfChange).void } def initialize(chunk) - @changes = [] - @insert = [] - @remove = [] + @changes = T.let([], ArrayOfChange) + @insert = T.let([], ArrayOfChange) + @remove = T.let([], ArrayOfChange) chunk.each do |item| @changes << item @@ -18,12 +34,14 @@ class Diff::LCS::Block end end + sig { returns(Integer) } def diff_size - @insert.size - @remove.size + insert.size - remove.size end + sig { returns(String) } def op - case [@remove.empty?, @insert.empty?] + case [remove.empty?, insert.empty?] when [false, false] '!' when [false, true] diff --git a/lib/diff/lcs/callbacks.rb b/lib/diff/lcs/callbacks.rb index a86f296..f3565a8 100644 --- a/lib/diff/lcs/callbacks.rb +++ b/lib/diff/lcs/callbacks.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# typed: true require 'diff/lcs/change' diff --git a/lib/diff/lcs/change.rb b/lib/diff/lcs/change.rb index 7977be5..520aeda 100644 --- a/lib/diff/lcs/change.rb +++ b/lib/diff/lcs/change.rb @@ -1,107 +1,133 @@ # frozen_string_literal: true +# typed: strict # Represents a simplistic (non-contextual) change. Represents the removal or # addition of an element from either the old or the new sequenced # enumerable. class Diff::LCS::Change - INTEGER = 1.class # Fixnum is deprecated in Ruby 2.4 + extend T::Sig + + ArrayT = T.type_alias{ + T.any( + [String, Integer, T.untyped], + [String, Integer, T.untyped, Integer, T.untyped], + [String, [Integer, T.untyped], [Integer, T.untyped]] + ) + } + + ChangeT = T.type_alias { T.any(Diff::LCS::Change, Diff::LCS::ContextChange) } # The only actions valid for changes are '+' (add), '-' (delete), '=' # (no change), '!' (changed), '<' (tail changes from first sequence), or # '>' (tail changes from second sequence). The last two ('<>') are only # found with Diff::LCS::diff and Diff::LCS::sdiff. - VALID_ACTIONS = %w(+ - = ! > <).freeze + VALID_ACTIONS = T.let(%w(+ - = ! > <).freeze, T::Array[String]) + sig { params(action: T.any(String, Diff::LCS::Change)).returns(T::Boolean) } def self.valid_action?(action) - VALID_ACTIONS.include? action + return false unless action.kind_of?(String) + + VALID_ACTIONS.include?(action) end # Returns the action this Change represents. + sig { returns(String) } attr_reader :action + # Returns the position of the Change. + sig { returns(Integer) } attr_reader :position + # Returns the sequence element of the Change. + sig { returns(T.untyped) } attr_reader :element - def initialize(*args) - @action, @position, @element = *args + sig { + params( + action: String, + position: Integer, + element: T.untyped, + _: T.untyped + ).void + } + def initialize(action, position, element, *_) + @action = action + @position = position + @element = element fail "Invalid Change Action '#{@action}'" unless Diff::LCS::Change.valid_action?(@action) - fail 'Invalid Position Type' unless @position.kind_of? INTEGER + fail 'Invalid Position Type' unless @position.kind_of? Integer end + sig { params(_args: T.untyped).returns(String) } def inspect(*_args) "#<#{self.class}: #{to_a.inspect}>" end + sig { returns(ArrayT) } def to_a [@action, @position, @element] end alias to_ary to_a + sig { params(arr: ArrayT).returns(ChangeT) } def self.from_a(arr) arr = arr.flatten(1) case arr.size when 5 - Diff::LCS::ContextChange.new(*(arr[0...5])) + Diff::LCS::ContextChange.new(arr[0], arr[1], arr[2], arr[3], arr[4]) when 3 - Diff::LCS::Change.new(*(arr[0...3])) + Diff::LCS::Change.new(arr[0], arr[1], arr[2]) else fail 'Invalid change array format provided.' end end - def to_change(action) - args = - case action - when '-' - [action, old_position, old_element] - when '+' - [action, new_position, new_element] - else - fail 'Invalid action for creating a change' - end - - Diff::LCS::Change.new(*args) - end - include Comparable + sig { params(other: ChangeT).returns(T::Boolean) } def ==(other) - (self.class == other.class) and - (action == other.action) and - (position == other.position) and - (element == other.element) + self.class == other.class && + action == other.action && + position == other.position && + element == other.element end + sig { params(other: Diff::LCS::Change).returns(Integer) } def <=>(other) r = action <=> other.action - r = position <=> other.position if r.zero? - r = element <=> other.element if r.zero? + r = position <=> other.position if T.must(r).zero? + r = element <=> other.element if T.must(r).zero? r end + sig { returns(T::Boolean) } def adding? @action == '+' end + sig { returns(T::Boolean) } def deleting? @action == '-' end + sig { returns(T::Boolean) } def unchanged? @action == '=' end + sig { returns(T::Boolean) } def changed? @action == '!' end + sig { returns(T::Boolean) } def finished_a? @action == '>' end + sig { returns(T::Boolean) } def finished_b? @action == '<' end @@ -111,27 +137,51 @@ end # elements in the old and the new sequenced enumerables as well as the action # taken. class Diff::LCS::ContextChange < Diff::LCS::Change + extend T::Sig + # We don't need these two values. undef :position undef :element # Returns the old position being changed. + sig { returns(Integer) } attr_reader :old_position + # Returns the new position being changed. + sig { returns(Integer) } attr_reader :new_position + # Returns the old element being changed. + sig { returns(T.untyped) } attr_reader :old_element + # Returns the new element being changed. + sig { returns(T.untyped) } attr_reader :new_element - def initialize(*args) - @action, @old_position, @old_element, @new_position, @new_element = *args + sig { + params( + action: String, + old_position: Integer, + old_element: T.untyped, + new_position: Integer, + new_element: T.untyped, + _: T.untyped + ).void + } + def initialize(action, old_position, old_element, new_position, new_element, *_) + @action = action + @old_position = old_position + @old_element = old_element + @new_position = new_position + @new_element = new_element fail "Invalid Change Action '#{@action}'" unless Diff::LCS::Change.valid_action?(@action) - fail 'Invalid (Old) Position Type' unless @old_position.nil? or @old_position.kind_of? INTEGER - fail 'Invalid (New) Position Type' unless @new_position.nil? or @new_position.kind_of? INTEGER + fail 'Invalid (Old) Position Type' unless @old_position.nil? or @old_position.kind_of? Integer + fail 'Invalid (New) Position Type' unless @new_position.nil? or @new_position.kind_of? Integer end + sig { returns(ArrayT) } def to_a [ @action, @@ -142,12 +192,26 @@ class Diff::LCS::ContextChange < Diff::LCS::Change alias to_ary to_a + sig { params(action: String).returns(Diff::LCS::Change) } + def to_change(action) + case action + when '-' + Diff::LCS::Change.new(action, old_position, old_element) + when '+' + Diff::LCS::Change.new(action, new_position, new_element) + else + fail 'Invalid action for creating a change' + end + end + + sig { params(arr: ArrayT).returns(ChangeT) } def self.from_a(arr) Diff::LCS::Change.from_a(arr) end # Simplifies a context change for use in some diff callbacks. '<' actions # are converted to '-' and '>' actions are converted to '+'. + sig { returns(T.any(Diff::LCS::Change, Diff::LCS::ContextChange)) } def simplify args = case action @@ -164,24 +228,33 @@ class Diff::LCS::ContextChange < Diff::LCS::Change # Simplifies a context change for use in some diff callbacks. '<' actions # are converted to '-' and '>' actions are converted to '+'. + sig { + params(event: Diff::LCS::ContextChange).returns(T.any( + Diff::LCS::Change, + Diff::LCS::ContextChange + )) + } def self.simplify(event) event.simplify end + sig { params(other: ChangeT).returns(T::Boolean) } def ==(other) - (self.class == other.class) and - (@action == other.action) and - (@old_position == other.old_position) and - (@new_position == other.new_position) and - (@old_element == other.old_element) and - (@new_element == other.new_element) + return false unless other.kind_of?(Diff::LCS::ContextChange) + + @action == other.action && + @old_position == other.old_position && + @new_position == other.new_position && + @old_element == other.old_element && + @new_element == other.new_element end + sig { params(other: Diff::LCS::ContextChange).returns(Integer) } def <=>(other) r = @action <=> other.action - r = @old_position <=> other.old_position if r.zero? - r = @new_position <=> other.new_position if r.zero? - r = @old_element <=> other.old_element if r.zero? + r = @old_position <=> other.old_position if T.must(r).zero? + r = @new_position <=> other.new_position if T.must(r).zero? + r = @old_element <=> other.old_element if T.must(r).zero? r = @new_element <=> other.new_element if r.zero? r end diff --git a/lib/diff/lcs/htmldiff.rb b/lib/diff/lcs/htmldiff.rb index f12220b..54ce9f4 100644 --- a/lib/diff/lcs/htmldiff.rb +++ b/lib/diff/lcs/htmldiff.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# typed: false require 'cgi' @@ -112,8 +113,8 @@ h1 { margin-left: 2em; } def run verify_options - if @options[:expand_tabs].positive? && self.class.can_expand_tabs - formatter = Text::Format.new + if @options[:expand_tabs].positive? && self.class.can_expand_tabs && defined? ::Text::Format + formatter = ::Text::Format.new formatter.tabstop = @options[:expand_tabs] @left.map! do |line| formatter.expand(line.chomp) end diff --git a/lib/diff/lcs/hunk.rb b/lib/diff/lcs/hunk.rb index fc5a035..ba16e11 100644 --- a/lib/diff/lcs/hunk.rb +++ b/lib/diff/lcs/hunk.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# typed: true require 'diff/lcs/block' @@ -15,9 +16,10 @@ class Diff::LCS::Hunk # piece of data. def initialize(data_old, data_new, piece, flag_context, file_length_difference) # At first, a hunk will have just one Block in it - @blocks = [Diff::LCS::Block.new(piece)] + block = Diff::LCS::Block.new(piece) + @blocks = T.let([block], T::Array[Diff::LCS::Block]) - if @blocks[0].remove.empty? && @blocks[0].insert.empty? + if block.remove.empty? && block.insert.empty? fail "Cannot build a hunk from #{piece.inspect}; has no add or remove actions" end @@ -29,7 +31,7 @@ class Diff::LCS::Hunk @data_new = data_new before = after = file_length_difference - after += @blocks[0].diff_size + after += block.diff_size @file_length_difference = after # The caller must get this manually @max_diff_size = @blocks.map { |e| e.diff_size.abs }.max @@ -37,24 +39,24 @@ class Diff::LCS::Hunk # we're only adding items in this block), then figure out the line number # based on the line number of the other file and the current difference in # file lengths. - if @blocks[0].remove.empty? + if block.remove.empty? a1 = a2 = nil else - a1 = @blocks[0].remove[0].position - a2 = @blocks[0].remove[-1].position + a1 = T.must(block.remove[0]).position + a2 = T.must(block.remove[-1]).position end - if @blocks[0].insert.empty? + if block.insert.empty? b1 = b2 = nil else - b1 = @blocks[0].insert[0].position - b2 = @blocks[0].insert[-1].position + b1 = T.must(block.insert[0]).position + b2 = T.must(block.insert[-1]).position end - @start_old = a1 || (b1 - before) - @start_new = b1 || (a1 + before) - @end_old = a2 || (b2 - after) - @end_new = b2 || (a2 + after) + @start_old = a1 || (T.must(b1) - before) + @start_new = b1 || (T.must(a1) + before) + @end_old = a2 || (T.must(b2) - after) + @end_new = b2 || (T.must(a2) + after) self.flag_context = flag_context end @@ -134,7 +136,7 @@ class Diff::LCS::Hunk def old_diff(_last = false) warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1 - block = @blocks[0] + block = T.must(@blocks[0]) # Calculate item number range. Old diff range is just like a context # diff range, except the ranges are on one line with the action between @@ -142,13 +144,13 @@ class Diff::LCS::Hunk s = encode("#{context_range(:old, ',')}#{OLD_DIFF_OP_ACTION[block.op]}#{context_range(:new, ',')}\n") # If removing anything, just print out all the remove lines in the hunk # which is just all the remove lines in the block. - unless block.remove.empty? + unless T.must(block).remove.empty? @data_old[@start_old..@end_old].each { |e| s << encode('< ') + e.chomp + encode("\n") } end s << encode("---\n") if block.op == '!' - unless block.insert.empty? + unless T.must(block).insert.empty? @data_new[@start_new..@end_new].each { |e| s << encode('> ') + e.chomp + encode("\n") } end @@ -270,14 +272,16 @@ class Diff::LCS::Hunk def ed_diff(format, _last = false) warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1 + block = T.must(@blocks[0]) + s = if format == :reverse_ed - encode("#{ED_DIFF_OP_ACTION[@blocks[0].op]}#{context_range(:old, ',')}\n") + encode("#{ED_DIFF_OP_ACTION[block.op]}#{context_range(:old, ',')}\n") else - encode("#{context_range(:old, ' ')}#{ED_DIFF_OP_ACTION[@blocks[0].op]}\n") + encode("#{context_range(:old, ' ')}#{ED_DIFF_OP_ACTION[block.op]}\n") end - unless @blocks[0].insert.empty? + unless block.insert.empty? @data_new[@start_new..@end_new].each do |e| s << e.chomp + encode("\n") end diff --git a/lib/diff/lcs/internals.rb b/lib/diff/lcs/internals.rb index 60027f2..f73ba52 100644 --- a/lib/diff/lcs/internals.rb +++ b/lib/diff/lcs/internals.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true +# typed: true -class << Diff::LCS - def diff_traversal(method, seq1, seq2, callbacks, &block) +module Diff::LCS # rubocop:disable Style/Documentation + def self.diff_traversal(method, seq1, seq2, callbacks, &block) callbacks = callbacks_for(callbacks) case method when :diff @@ -26,10 +27,7 @@ class << Diff::LCS private :diff_traversal end -module Diff::LCS::Internals # :nodoc: -end - -class << Diff::LCS::Internals +module Diff::LCS::Internals # :nodoc: rubocop:disable Style/Documentation # Compute the longest common subsequence between the sequenced # Enumerables +a+ and +b+. The result is an array whose contents is such # that @@ -38,7 +36,7 @@ class << Diff::LCS::Internals # result.each_with_index do |e, i| # assert_equal(a[i], b[e]) unless e.nil? # end - def lcs(a, b) + def self.lcs(a, b) a_start = b_start = 0 a_finish = a.size - 1 b_finish = b.size - 1 @@ -69,7 +67,8 @@ class << Diff::LCS::Internals (a_start..a_finish).each do |i| ai = string ? a[i, 1] : a[i] bm = b_matches[ai] - k = nil + k = T.let(nil, T.nilable(Integer)) + bm.reverse_each do |j| if k and (thresh[k] > j) and (thresh[k - 1] < j) thresh[k] = j @@ -95,10 +94,10 @@ class << Diff::LCS::Internals # normalization (conversion of the array form of Diff::LCS::Change objects to # the object form of same) and detection of whether the patchset represents # changes to be made. - def analyze_patchset(patchset, depth = 0) + def self.analyze_patchset(patchset, depth = 0) fail 'Patchset too complex' if depth > 1 - has_changes = false + has_changes = T.let(false, T::Boolean) new_patchset = [] # Format: @@ -116,7 +115,7 @@ class << Diff::LCS::Internals new_patchset << hunk when Array # Detect if the 'hunk' is actually an array-format change object. - if Diff::LCS::Change.valid_action? hunk[0] + if Diff::LCS::Change.valid_action?(hunk[0]) hunk = Diff::LCS::Change.from_a(hunk) has_changes ||= !hunk.unchanged? new_patchset << hunk @@ -140,7 +139,7 @@ class << Diff::LCS::Internals # some time. This also works better with Diff::LCS::ContextChange or # Diff::LCS::Change as its source, as an array will cause the creation # of one of the above. - def intuit_diff_direction(src, patchset, limit = nil) + def self.intuit_diff_direction(src, patchset, limit = nil) string = src.kind_of?(String) count = left_match = left_miss = right_match = right_miss = 0 @@ -245,7 +244,7 @@ enumerable as either source or destination value." # are numeric. # # This operation preserves the sort order. - def replace_next_larger(enum, value, last_index = nil) + def self.replace_next_larger(enum, value, last_index = nil) # Off the end? if enum.empty? or (value > enum[-1]) enum << value @@ -279,7 +278,7 @@ enumerable as either source or destination value." # If +vector+ maps the matching elements of another collection onto this # Enumerable, compute the inverse of +vector+ that maps this Enumerable # onto the collection. (Currently unused.) - def inverse_vector(a, vector) + def self.inverse_vector(a, vector) inverse = a.dup (0...vector.size).each do |i| inverse[vector[i]] = i unless vector[i].nil? @@ -291,7 +290,7 @@ enumerable as either source or destination value." # Returns a hash mapping each element of an Enumerable to the set of # positions it occupies in the Enumerable, optionally restricted to the # elements specified in the range of indexes specified by +interval+. - def position_hash(enum, interval) + def self.position_hash(enum, interval) string = enum.kind_of?(String) hash = Hash.new { |h, k| h[k] = [] } interval.each do |i| diff --git a/lib/diff/lcs/ldiff.rb b/lib/diff/lcs/ldiff.rb index 17b374c..d81e392 100644 --- a/lib/diff/lcs/ldiff.rb +++ b/lib/diff/lcs/ldiff.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# typed: false require 'optparse' require 'ostruct' diff --git a/lib/diff/lcs/string.rb b/lib/diff/lcs/string.rb index 6bcdc74..1ee7a3c 100644 --- a/lib/diff/lcs/string.rb +++ b/lib/diff/lcs/string.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# typed: strict warn 'diff/lcs/string: Automatically extending String with Diff::LCS is deprecated' |