leg 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +10 -0
  3. data/CODE_OF_CONDUCT.md +74 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +34 -0
  6. data/LICENSE +21 -0
  7. data/README.md +59 -0
  8. data/Rakefile +11 -0
  9. data/TUTORIAL.md +243 -0
  10. data/bin/console +9 -0
  11. data/bin/setup +6 -0
  12. data/exe/leg +6 -0
  13. data/leg.gemspec +31 -0
  14. data/lib/leg.rb +27 -0
  15. data/lib/leg/cli.rb +42 -0
  16. data/lib/leg/commands.rb +19 -0
  17. data/lib/leg/commands/amend.rb +49 -0
  18. data/lib/leg/commands/base_command.rb +99 -0
  19. data/lib/leg/commands/build.rb +126 -0
  20. data/lib/leg/commands/commit.rb +48 -0
  21. data/lib/leg/commands/diff.rb +29 -0
  22. data/lib/leg/commands/help.rb +43 -0
  23. data/lib/leg/commands/init.rb +46 -0
  24. data/lib/leg/commands/reset.rb +26 -0
  25. data/lib/leg/commands/resolve.rb +31 -0
  26. data/lib/leg/commands/save.rb +31 -0
  27. data/lib/leg/commands/status.rb +54 -0
  28. data/lib/leg/commands/step.rb +31 -0
  29. data/lib/leg/config.rb +42 -0
  30. data/lib/leg/default_templates.rb +340 -0
  31. data/lib/leg/diff.rb +151 -0
  32. data/lib/leg/diff_transformers.rb +11 -0
  33. data/lib/leg/diff_transformers/base_transformer.rb +13 -0
  34. data/lib/leg/diff_transformers/fold_sections.rb +89 -0
  35. data/lib/leg/diff_transformers/omit_adjacent_removals.rb +38 -0
  36. data/lib/leg/diff_transformers/syntax_highlight.rb +32 -0
  37. data/lib/leg/diff_transformers/trim_blank_lines.rb +25 -0
  38. data/lib/leg/line.rb +83 -0
  39. data/lib/leg/markdown.rb +20 -0
  40. data/lib/leg/page.rb +27 -0
  41. data/lib/leg/representations.rb +9 -0
  42. data/lib/leg/representations/base_representation.rb +42 -0
  43. data/lib/leg/representations/git.rb +388 -0
  44. data/lib/leg/representations/litdiff.rb +85 -0
  45. data/lib/leg/step.rb +16 -0
  46. data/lib/leg/template.rb +95 -0
  47. data/lib/leg/tutorial.rb +49 -0
  48. data/lib/leg/version.rb +3 -0
  49. metadata +112 -38
  50. data/bin/leg +0 -9
  51. data/lib/snaptoken.rb +0 -24
  52. data/lib/snaptoken/cli.rb +0 -61
  53. data/lib/snaptoken/commands.rb +0 -13
  54. data/lib/snaptoken/commands/amend.rb +0 -27
  55. data/lib/snaptoken/commands/base_command.rb +0 -92
  56. data/lib/snaptoken/commands/build.rb +0 -107
  57. data/lib/snaptoken/commands/commit.rb +0 -27
  58. data/lib/snaptoken/commands/help.rb +0 -38
  59. data/lib/snaptoken/commands/resolve.rb +0 -27
  60. data/lib/snaptoken/commands/status.rb +0 -21
  61. data/lib/snaptoken/commands/step.rb +0 -35
  62. data/lib/snaptoken/default_templates.rb +0 -287
  63. data/lib/snaptoken/diff.rb +0 -180
  64. data/lib/snaptoken/diff_line.rb +0 -54
  65. data/lib/snaptoken/diff_transformers.rb +0 -9
  66. data/lib/snaptoken/diff_transformers/base_transformer.rb +0 -9
  67. data/lib/snaptoken/diff_transformers/fold_sections.rb +0 -85
  68. data/lib/snaptoken/diff_transformers/omit_adjacent_removals.rb +0 -28
  69. data/lib/snaptoken/diff_transformers/trim_blank_lines.rb +0 -21
  70. data/lib/snaptoken/markdown.rb +0 -18
  71. data/lib/snaptoken/page.rb +0 -64
  72. data/lib/snaptoken/representations.rb +0 -8
  73. data/lib/snaptoken/representations/base_representation.rb +0 -38
  74. data/lib/snaptoken/representations/git.rb +0 -262
  75. data/lib/snaptoken/representations/litdiff.rb +0 -81
  76. data/lib/snaptoken/step.rb +0 -27
  77. data/lib/snaptoken/template.rb +0 -53
  78. data/lib/snaptoken/tutorial.rb +0 -64
@@ -0,0 +1,151 @@
1
+ module Leg
2
+ class Diff
3
+ attr_accessor :filename, :is_new_file, :lines
4
+
5
+ def initialize(filename = nil, is_new_file = false, lines = [])
6
+ @filename = filename
7
+ @is_new_file = is_new_file
8
+ @lines = lines
9
+ end
10
+
11
+ def clone
12
+ Leg::Diff.new(@filename.dup, @is_new_file, @lines.map(&:clone))
13
+ end
14
+
15
+ def clone_empty
16
+ Leg::Diff.new(@filename.dup, @is_new_file, [])
17
+ end
18
+
19
+ # Append a Line to the Diff.
20
+ def <<(line)
21
+ unless line.is_a? Leg::Line
22
+ raise ArgumentError, "expected a Line"
23
+ end
24
+ @lines << line
25
+ self
26
+ end
27
+
28
+ def to_patch(options = {})
29
+ patch = ""
30
+ patch += "diff --git a/#{@filename} b/#{@filename}\n" unless options[:strip_git_lines]
31
+ if @is_new_file
32
+ patch += "new file mode 100644\n" unless options[:strip_git_lines]
33
+ patch += "--- /dev/null\n"
34
+ else
35
+ patch += "--- #{'a/' unless options[:strip_git_lines]}#{@filename}\n"
36
+ end
37
+ patch += "+++ #{'b/' unless options[:strip_git_lines]}#{@filename}\n"
38
+
39
+ find_hunks.each do |hunk|
40
+ patch += hunk_header(hunk)
41
+ hunk.each do |line|
42
+ patch += line.to_patch(options)
43
+ end
44
+ end
45
+
46
+ patch
47
+ end
48
+
49
+ # Parse a git diff and return an array of Diff objects, one for each file in
50
+ # the git diff.
51
+ def self.parse(git_diff)
52
+ in_diff = false
53
+ old_line_num = nil
54
+ new_line_num = nil
55
+ cur_diff = nil
56
+ diffs = []
57
+
58
+ git_diff.lines.each do |line|
59
+ if line =~ /^--- (.+)$/
60
+ cur_diff = Leg::Diff.new
61
+ if $1 == '/dev/null'
62
+ cur_diff.is_new_file = true
63
+ end
64
+ diffs << cur_diff
65
+ in_diff = false
66
+ elsif line =~ /^\+\+\+ (.+)$/
67
+ cur_diff.filename = $1.strip.sub(/^b\//, '')
68
+ elsif line =~ /^@@ -(\d+)(,\d+)? \+(\d+)(,\d+)? @@/
69
+ # TODO: somehow preserve function name that comes to the right of the @@ header?
70
+ in_diff = true
71
+ old_line_num = $1.to_i
72
+ new_line_num = $3.to_i
73
+ elsif in_diff && line[0] == '\\'
74
+ # Ignore "".
75
+ elsif in_diff && [' ', '|', '+', '-'].include?(line[0])
76
+ case line[0]
77
+ when ' ', '|'
78
+ line_nums = [old_line_num, new_line_num]
79
+ old_line_num += 1
80
+ new_line_num += 1
81
+ cur_diff << Leg::Line::Unchanged.new(line[1..-1], line_nums)
82
+ when '+'
83
+ line_nums = [nil, new_line_num]
84
+ new_line_num += 1
85
+ cur_diff << Leg::Line::Added.new(line[1..-1], line_nums)
86
+ when '-'
87
+ line_nums = [old_line_num, nil]
88
+ old_line_num += 1
89
+ cur_diff << Leg::Line::Removed.new(line[1..-1], line_nums)
90
+ end
91
+ else
92
+ in_diff = false
93
+ end
94
+ end
95
+
96
+ diffs
97
+ end
98
+
99
+ private
100
+
101
+ # :S
102
+ def hunk_header(hunk)
103
+ old_line, new_line = hunk.first.line_numbers
104
+ old_line ||= 1
105
+ new_line ||= 1
106
+
107
+ old_count = hunk.count { |line| [Leg::Line::Removed, Leg::Line::Unchanged].include? line.class }
108
+ new_count = hunk.count { |line| [Leg::Line::Added, Leg::Line::Unchanged].include? line.class }
109
+
110
+ old_line = 0 if old_count == 0
111
+ new_line = 0 if new_count == 0
112
+
113
+ "@@ -#{old_line},#{old_count} +#{new_line},#{new_count} @@\n"
114
+ end
115
+
116
+ # :(
117
+ def find_hunks
118
+ raise "can't create patch from empty diff" if @lines.empty?
119
+ hunks = []
120
+ cur_hunk = [@lines.first]
121
+ cur_line_nums = @lines.first.line_numbers.dup
122
+ @lines[1..-1].each do |line|
123
+ case line
124
+ when Leg::Line::Unchanged
125
+ cur_line_nums[0] = cur_line_nums[0].nil? ? line.line_numbers[0] : (cur_line_nums[0] + 1)
126
+ cur_line_nums[1] = cur_line_nums[1].nil? ? line.line_numbers[1] : (cur_line_nums[1] + 1)
127
+ when Leg::Line::Added
128
+ cur_line_nums[1] = cur_line_nums[1].nil? ? line.line_numbers[1] : (cur_line_nums[1] + 1)
129
+ when Leg::Line::Removed
130
+ cur_line_nums[0] = cur_line_nums[0].nil? ? line.line_numbers[0] : (cur_line_nums[0] + 1)
131
+ when Leg::Line::Folded
132
+ raise "can't create patch from diff with folded lines"
133
+ end
134
+
135
+ old_match = (line.line_numbers[0].nil? || line.line_numbers[0] == cur_line_nums[0])
136
+ new_match = (line.line_numbers[1].nil? || line.line_numbers[1] == cur_line_nums[1])
137
+
138
+ if !old_match || !new_match
139
+ hunks << cur_hunk
140
+
141
+ cur_hunk = []
142
+ cur_line_nums = line.line_numbers.dup
143
+ end
144
+
145
+ cur_hunk << line
146
+ end
147
+ hunks << cur_hunk
148
+ hunks
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,11 @@
1
+ require 'leg/diff_transformers/base_transformer'
2
+
3
+ require 'leg/diff_transformers/fold_sections'
4
+ require 'leg/diff_transformers/omit_adjacent_removals'
5
+ require 'leg/diff_transformers/syntax_highlight'
6
+ require 'leg/diff_transformers/trim_blank_lines'
7
+
8
+ module Leg
9
+ module DiffTransformers
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Leg
2
+ module DiffTransformers
3
+ class BaseTransformer
4
+ def initialize(options = {})
5
+ @options = options
6
+ end
7
+
8
+ def transform(diff)
9
+ raise NotImplementedError
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,89 @@
1
+ module Leg
2
+ module DiffTransformers
3
+ class FoldSections < BaseTransformer
4
+ def transform(diff)
5
+ sections = @options[:section_types].map { [] }
6
+
7
+ cur_sections = @options[:section_types].map { nil }
8
+ diff.lines.each.with_index do |line, idx|
9
+ @options[:section_types].each.with_index do |section_type, level|
10
+ if line.source =~ Regexp.new(section_type[:start])
11
+ if !section_type[:end] && cur_sections[level]
12
+ cur_sections[level].end_line = idx - 1
13
+ if @options[:unfold_before_new_section]
14
+ cur_sections[level].dirty! if [:added, :removed].include? line.type
15
+ end
16
+ sections[level] << cur_sections[level]
17
+ end
18
+
19
+ cur_sections[level] = Section.new(level, idx)
20
+
21
+ if [:added, :removed].include? line.type
22
+ cur_sections[level].dirty!
23
+ end
24
+ elsif section_type[:end] && line.source =~ Regexp.new(section_type[:end])
25
+ if [:added, :removed].include? line.type
26
+ cur_sections[level].dirty!
27
+ end
28
+
29
+ cur_sections[level].end_line = idx
30
+ sections[level] << cur_sections[level]
31
+ cur_sections[level] = nil
32
+ elsif cur_sections[level]
33
+ if [:added, :removed].include? line.type
34
+ cur_sections[level].dirty!
35
+ end
36
+ end
37
+ end
38
+ end
39
+ cur_sections.each.with_index do |section, level|
40
+ unless section.nil?
41
+ section.end_line = diff.lines.length - 1
42
+ sections[level] << section
43
+ end
44
+ end
45
+
46
+ new_diff = diff.clone
47
+ sections.each.with_index do |level_sections, level|
48
+ level_sections.each do |section|
49
+ if !section.dirty? && !new_diff.lines[section.to_range].any?(&:nil?)
50
+ start_line = new_diff.lines[section.start_line]
51
+ end_line = new_diff.lines[section.end_line]
52
+
53
+ summary_lines = [start_line]
54
+ summary_lines << end_line if @options[:section_types][level][:end]
55
+ summary = summary_lines.map(&:source).join(" … ")
56
+
57
+ line_numbers = [start_line.line_number, end_line.line_number]
58
+
59
+ folded_line = Leg::Line::Folded.new(summary, line_numbers)
60
+
61
+ section.to_range.each do |idx|
62
+ new_diff.lines[idx] = nil
63
+ end
64
+
65
+ new_diff.lines[section.start_line] = folded_line
66
+ end
67
+ end
68
+ end
69
+ new_diff.lines.compact!
70
+ new_diff
71
+ end
72
+
73
+ class Section
74
+ attr_accessor :level, :start_line, :end_line, :dirty
75
+
76
+ def initialize(level, start_line, end_line = nil, dirty = false)
77
+ @level, @start_line, @end_line, @dirty = level, start_line, end_line, dirty
78
+ end
79
+
80
+ def to_range
81
+ start_line..end_line
82
+ end
83
+
84
+ def dirty?; @dirty; end
85
+ def dirty!; @dirty = true; end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,38 @@
1
+ module Leg
2
+ module DiffTransformers
3
+ class OmitAdjacentRemovals < BaseTransformer
4
+ def transform(diff)
5
+ new_diff = diff.clone
6
+
7
+ removed_lines = []
8
+ saw_added_line = false
9
+ new_diff.lines.each.with_index do |line, idx|
10
+ case line.type
11
+ when :unchanged, :folded
12
+ if saw_added_line
13
+ removed_lines.each do |removed_idx|
14
+ new_diff.lines[removed_idx] = nil
15
+ end
16
+ end
17
+
18
+ removed_lines = []
19
+ saw_added_line = false
20
+ when :added
21
+ saw_added_line = true
22
+ when :removed
23
+ removed_lines << idx
24
+ end
25
+ end
26
+
27
+ if saw_added_line
28
+ removed_lines.each do |removed_idx|
29
+ new_diff.lines[removed_idx] = nil
30
+ end
31
+ end
32
+
33
+ new_diff.lines.compact!
34
+ new_diff
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,32 @@
1
+ module Leg
2
+ module DiffTransformers
3
+ class SyntaxHighlight < BaseTransformer
4
+ class HTMLLineByLine < Rouge::Formatter
5
+ def initialize(formatter)
6
+ @formatter = formatter
7
+ end
8
+
9
+ def stream(tokens, &b)
10
+ token_lines(tokens) do |line|
11
+ line.each do |tok, val|
12
+ yield @formatter.span(tok, val)
13
+ end
14
+ yield "\n"
15
+ end
16
+ end
17
+ end
18
+
19
+ SYNTAX_HIGHLIGHTER = HTMLLineByLine.new(Rouge::Formatters::HTML.new)
20
+
21
+ def transform(diff)
22
+ new_diff = diff.clone
23
+ code = new_diff.lines.map(&:source).join("\n") + "\n"
24
+ lexer = Rouge::Lexer.guess(filename: new_diff.filename, source: code)
25
+ SYNTAX_HIGHLIGHTER.format(lexer.lex(code)).lines.each.with_index do |line_hl, idx|
26
+ new_diff.lines[idx].source = line_hl
27
+ end
28
+ new_diff
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ module Leg
2
+ module DiffTransformers
3
+ class TrimBlankLines < BaseTransformer
4
+ def transform(diff)
5
+ new_diff = diff.clone_empty
6
+ diff.lines.each.with_index do |line, idx|
7
+ line = line.clone
8
+ if line.blank? && [:added, :removed].include?(line.type)
9
+ prev_line = idx > 0 ? diff.lines[idx - 1] : nil
10
+ next_line = idx < diff.lines.length - 1 ? diff.lines[idx + 1] : nil
11
+
12
+ prev_changed = prev_line && [:added, :removed].include?(prev_line.type)
13
+ next_changed = next_line && [:added, :removed].include?(next_line.type)
14
+
15
+ if !prev_changed || !next_changed
16
+ line = Leg::Line::Unchanged.new(line.source, line.line_numbers)
17
+ end
18
+ end
19
+ new_diff.lines << line
20
+ end
21
+ new_diff
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,83 @@
1
+ module Leg
2
+ class Line
3
+ attr_accessor :source, :line_numbers
4
+
5
+ def initialize(source, line_numbers)
6
+ @source = source.chomp
7
+ @line_numbers = line_numbers
8
+ end
9
+
10
+ def clone
11
+ self.class.new(@source.dup, @line_numbers.dup)
12
+ end
13
+
14
+ def blank?
15
+ @source.strip.empty?
16
+ end
17
+
18
+ def line_number
19
+ raise NotImplementedError
20
+ end
21
+
22
+ def to_patch(options = {})
23
+ raise NotImplementedError
24
+ end
25
+
26
+ class Added < Line
27
+ def type
28
+ :added
29
+ end
30
+
31
+ def line_number
32
+ @line_numbers[1]
33
+ end
34
+
35
+ def to_patch(options = {})
36
+ "+#{@source}\n"
37
+ end
38
+ end
39
+
40
+ class Removed < Line
41
+ def type
42
+ :removed
43
+ end
44
+
45
+ def line_number
46
+ @line_numbers[0]
47
+ end
48
+
49
+ def to_patch(options = {})
50
+ "-#{@source}\n"
51
+ end
52
+ end
53
+
54
+ class Unchanged < Line
55
+ def type
56
+ :unchanged
57
+ end
58
+
59
+ def line_number
60
+ @line_numbers[1]
61
+ end
62
+
63
+ def to_patch(options = {})
64
+ char = options[:unchanged_char] || " "
65
+ "#{char}#{@source}\n"
66
+ end
67
+ end
68
+
69
+ class Folded < Line
70
+ def type
71
+ :folded
72
+ end
73
+
74
+ def line_number
75
+ @line_numbers[0]
76
+ end
77
+
78
+ def to_patch(options = {})
79
+ raise "can't convert folded line to patch"
80
+ end
81
+ end
82
+ end
83
+ end