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.
- checksums.yaml +5 -5
- data/.gitignore +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +34 -0
- data/LICENSE +21 -0
- data/README.md +59 -0
- data/Rakefile +11 -0
- data/TUTORIAL.md +243 -0
- data/bin/console +9 -0
- data/bin/setup +6 -0
- data/exe/leg +6 -0
- data/leg.gemspec +31 -0
- data/lib/leg.rb +27 -0
- data/lib/leg/cli.rb +42 -0
- data/lib/leg/commands.rb +19 -0
- data/lib/leg/commands/amend.rb +49 -0
- data/lib/leg/commands/base_command.rb +99 -0
- data/lib/leg/commands/build.rb +126 -0
- data/lib/leg/commands/commit.rb +48 -0
- data/lib/leg/commands/diff.rb +29 -0
- data/lib/leg/commands/help.rb +43 -0
- data/lib/leg/commands/init.rb +46 -0
- data/lib/leg/commands/reset.rb +26 -0
- data/lib/leg/commands/resolve.rb +31 -0
- data/lib/leg/commands/save.rb +31 -0
- data/lib/leg/commands/status.rb +54 -0
- data/lib/leg/commands/step.rb +31 -0
- data/lib/leg/config.rb +42 -0
- data/lib/leg/default_templates.rb +340 -0
- data/lib/leg/diff.rb +151 -0
- data/lib/leg/diff_transformers.rb +11 -0
- data/lib/leg/diff_transformers/base_transformer.rb +13 -0
- data/lib/leg/diff_transformers/fold_sections.rb +89 -0
- data/lib/leg/diff_transformers/omit_adjacent_removals.rb +38 -0
- data/lib/leg/diff_transformers/syntax_highlight.rb +32 -0
- data/lib/leg/diff_transformers/trim_blank_lines.rb +25 -0
- data/lib/leg/line.rb +83 -0
- data/lib/leg/markdown.rb +20 -0
- data/lib/leg/page.rb +27 -0
- data/lib/leg/representations.rb +9 -0
- data/lib/leg/representations/base_representation.rb +42 -0
- data/lib/leg/representations/git.rb +388 -0
- data/lib/leg/representations/litdiff.rb +85 -0
- data/lib/leg/step.rb +16 -0
- data/lib/leg/template.rb +95 -0
- data/lib/leg/tutorial.rb +49 -0
- data/lib/leg/version.rb +3 -0
- metadata +112 -38
- data/bin/leg +0 -9
- data/lib/snaptoken.rb +0 -24
- data/lib/snaptoken/cli.rb +0 -61
- data/lib/snaptoken/commands.rb +0 -13
- data/lib/snaptoken/commands/amend.rb +0 -27
- data/lib/snaptoken/commands/base_command.rb +0 -92
- data/lib/snaptoken/commands/build.rb +0 -107
- data/lib/snaptoken/commands/commit.rb +0 -27
- data/lib/snaptoken/commands/help.rb +0 -38
- data/lib/snaptoken/commands/resolve.rb +0 -27
- data/lib/snaptoken/commands/status.rb +0 -21
- data/lib/snaptoken/commands/step.rb +0 -35
- data/lib/snaptoken/default_templates.rb +0 -287
- data/lib/snaptoken/diff.rb +0 -180
- data/lib/snaptoken/diff_line.rb +0 -54
- data/lib/snaptoken/diff_transformers.rb +0 -9
- data/lib/snaptoken/diff_transformers/base_transformer.rb +0 -9
- data/lib/snaptoken/diff_transformers/fold_sections.rb +0 -85
- data/lib/snaptoken/diff_transformers/omit_adjacent_removals.rb +0 -28
- data/lib/snaptoken/diff_transformers/trim_blank_lines.rb +0 -21
- data/lib/snaptoken/markdown.rb +0 -18
- data/lib/snaptoken/page.rb +0 -64
- data/lib/snaptoken/representations.rb +0 -8
- data/lib/snaptoken/representations/base_representation.rb +0 -38
- data/lib/snaptoken/representations/git.rb +0 -262
- data/lib/snaptoken/representations/litdiff.rb +0 -81
- data/lib/snaptoken/step.rb +0 -27
- data/lib/snaptoken/template.rb +0 -53
- data/lib/snaptoken/tutorial.rb +0 -64
data/lib/leg/diff.rb
ADDED
@@ -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,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
|
data/lib/leg/line.rb
ADDED
@@ -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
|