leg 0.0.1 → 0.0.2

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