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,29 @@
1
+ module Leg
2
+ module Commands
3
+ class Diff < BaseCommand
4
+ def self.name
5
+ "diff"
6
+ end
7
+
8
+ def self.summary
9
+ "Compare last step with changes made in step/."
10
+ end
11
+
12
+ def self.usage
13
+ ""
14
+ end
15
+
16
+ def setopts!(o)
17
+ end
18
+
19
+ def run
20
+ needs! :config, :repo
21
+
22
+ @git.copy_step_to_repo!
23
+ FileUtils.cd(@git.repo_path) do
24
+ system("git diff")
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,43 @@
1
+ module Leg
2
+ module Commands
3
+ class Help < BaseCommand
4
+ def self.name
5
+ "help"
6
+ end
7
+
8
+ def self.summary
9
+ "Print out list of commands, or get help\n" +
10
+ "on a specific command."
11
+ end
12
+
13
+ def self.usage
14
+ "[<command>]"
15
+ end
16
+
17
+ def setopts!(o)
18
+ end
19
+
20
+ def run
21
+ if @args.empty?
22
+ puts "<< Hello! I am leg, version #{Leg::VERSION} >>"
23
+ puts
24
+ puts "Usage: leg <command> [args...]"
25
+ puts
26
+ puts "Commands:"
27
+ Leg::Commands::LIST.each do |cmd|
28
+ puts " #{cmd.name} #{cmd.usage}"
29
+ cmd.summary.split("\n").each do |line|
30
+ puts " #{line}"
31
+ end
32
+ end
33
+ puts
34
+ puts "For more help on a specific command, run `leg help <command>`."
35
+ elsif cmd = Leg::Commands::LIST.find { |cmd| cmd.name == @args.first }
36
+ cmd.new(["--help"], @config)
37
+ else
38
+ puts "There is no '#{@args.first}' command."
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,46 @@
1
+ module Leg
2
+ module Commands
3
+ class Init < BaseCommand
4
+ def self.name
5
+ "init"
6
+ end
7
+
8
+ def self.summary
9
+ "Initialize a new leg project."
10
+ end
11
+
12
+ def self.usage
13
+ "[new-dir]"
14
+ end
15
+
16
+ def setopts!(o)
17
+ end
18
+
19
+ def run
20
+ if @config
21
+ puts "You are already in a leg working directory."
22
+ return false
23
+ end
24
+
25
+ if new_dir = @args.first
26
+ if File.exist?(new_dir)
27
+ puts "Error: directory already exists."
28
+ return false
29
+ end
30
+ FileUtils.mkdir(new_dir)
31
+ FileUtils.cd(new_dir)
32
+ end
33
+
34
+ FileUtils.mkdir_p(".leg/repo")
35
+ FileUtils.mkdir_p("step")
36
+ FileUtils.mkdir_p("doc")
37
+ File.write("doc/tutorial.litdiff", "")
38
+ File.write("leg.yml", "---\n")
39
+
40
+ config = Leg::Config.new(FileUtils.pwd)
41
+ git = Leg::Representations::Git.new(config)
42
+ git.init!
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,26 @@
1
+ module Leg
2
+ module Commands
3
+ class Reset < BaseCommand
4
+ def self.name
5
+ "reset"
6
+ end
7
+
8
+ def self.summary
9
+ "Abort any saves in progress and checkout the top-most step."
10
+ end
11
+
12
+ def self.usage
13
+ ""
14
+ end
15
+
16
+ def setopts!(o)
17
+ end
18
+
19
+ def run
20
+ needs! :config, :repo
21
+
22
+ @git.reset!
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ module Leg
2
+ module Commands
3
+ class Resolve < BaseCommand
4
+ def self.name
5
+ "resolve"
6
+ end
7
+
8
+ def self.summary
9
+ "Continue rewriting steps after resolving a merge conflict."
10
+ end
11
+
12
+ def self.usage
13
+ ""
14
+ end
15
+
16
+ def setopts!(o)
17
+ end
18
+
19
+ def run
20
+ needs! :config, :repo
21
+
22
+ if @git.resolve!
23
+ git_to_litdiff!
24
+ output "Success!\n"
25
+ else
26
+ output "Looks like you've got a conflict to resolve!\n"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ module Leg
2
+ module Commands
3
+ class Save < BaseCommand
4
+ def self.name
5
+ "save"
6
+ end
7
+
8
+ def self.summary
9
+ "Save changes to doc/."
10
+ end
11
+
12
+ def self.usage
13
+ ""
14
+ end
15
+
16
+ def setopts!(o)
17
+ end
18
+
19
+ def run
20
+ needs! :config, :repo
21
+
22
+ if @git.rebase_remaining!
23
+ git_to_litdiff!
24
+ output "Success!\n"
25
+ else
26
+ output "Looks like you've got a conflict to resolve!\n"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,54 @@
1
+ module Leg
2
+ module Commands
3
+ class Status < BaseCommand
4
+ def self.name
5
+ "status"
6
+ end
7
+
8
+ def self.summary
9
+ "Show unsaved changes and the state of the step/ folder."
10
+ end
11
+
12
+ def self.usage
13
+ ""
14
+ end
15
+
16
+ def setopts!(o)
17
+ end
18
+
19
+ def run
20
+ needs! :config, :repo
21
+
22
+ state = @git.state
23
+ case state.operation
24
+ when nil
25
+ if state.step_number.nil?
26
+ output "Nothing to report.\n"
27
+ else
28
+ output "Step #{state.step_number} checked out into step/.\n"
29
+ end
30
+ when :commit
31
+ if state.args[1]
32
+ output "Amended step #{state.step_number}. "
33
+ end
34
+ if state.args[0] > 0
35
+ output "Added #{state.args[0]} step#{'s' if state.args[0] != 1} after step #{state.step_number}."
36
+ end
37
+ output "\n"
38
+ else
39
+ raise "unknown operation"
40
+ end
41
+
42
+ if state.conflict
43
+ output "\n"
44
+ output "Currently in a merge conflict. Resolve the conflict in step/ and\n"
45
+ output "run `leg resolve` to continue.\n"
46
+ elsif !state.operation.nil?
47
+ output "\n"
48
+ output "The above change(s) have not been saved yet. Run `leg save` to\n"
49
+ output "save to the doc/ folder.\n"
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,31 @@
1
+ module Leg
2
+ module Commands
3
+ class Step < BaseCommand
4
+ def self.name
5
+ "step"
6
+ end
7
+
8
+ def self.summary
9
+ "Select a step for editing."
10
+ end
11
+
12
+ def self.usage
13
+ "<step-number>"
14
+ end
15
+
16
+ def setopts!(o)
17
+ end
18
+
19
+ def run
20
+ needs! :config, :repo
21
+
22
+ step_number = @args.first.to_i
23
+
24
+ unless @git.checkout!(@args.first.to_i)
25
+ output "Error: Step not found.\n"
26
+ return false
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,42 @@
1
+ module Leg
2
+ class Config
3
+ attr_reader :path, :options
4
+
5
+ def initialize(path)
6
+ @path = path
7
+ end
8
+
9
+ def load!
10
+ @options = YAML.load_file(File.join(@path, "leg.yml"))
11
+ @options = {} unless @options.is_a? Hash
12
+ @options = symbolize_keys(@options)
13
+ end
14
+
15
+ def last_synced_at
16
+ File.mtime(last_synced_path) if File.exist?(last_synced_path)
17
+ end
18
+
19
+ def synced!
20
+ FileUtils.touch(last_synced_path)
21
+ end
22
+
23
+ private
24
+
25
+ def last_synced_path
26
+ File.join(@path, ".leg/last_synced")
27
+ end
28
+
29
+ def symbolize_keys(value)
30
+ case value
31
+ when Hash
32
+ value.map do |k, v|
33
+ [k.to_sym, symbolize_keys(v)]
34
+ end.to_h
35
+ when Array
36
+ value.map { |v| symbolize_keys(v) }
37
+ else
38
+ value
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,340 @@
1
+ module Leg
2
+ module DefaultTemplates
3
+ PAGE = {}
4
+ STEP = {}
5
+
6
+ PAGE["html"] = <<~TEMPLATE
7
+ <!doctype html>
8
+ <html>
9
+ <head>
10
+ <meta charset="utf-8">
11
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
12
+ <title><%= page_number %>. <%= page_title %></title>
13
+ <link href="style.css" rel="stylesheet">
14
+ </head>
15
+ <body>
16
+ <header class="bar">
17
+ <nav>
18
+ <% if prev_page %>
19
+ <a href="<%= prev_page.filename %>.html">&larr; prev</a>
20
+ <% else %>
21
+ <a href="#"></a>
22
+ <% end %>
23
+
24
+ <a href="<%= pages.first.filename %>.html">beginning</a>
25
+
26
+ <% if next_page %>
27
+ <a href="<%= next_page.filename %>.html">next &rarr;</a>
28
+ <% else %>
29
+ <a href="#"></a>
30
+ <% end %>
31
+ </nav>
32
+ </header>
33
+ <div id="container">
34
+ <%= content %>
35
+ </div>
36
+ </body>
37
+ </html>
38
+ TEMPLATE
39
+
40
+ STEP["html"] = <<~TEMPLATE
41
+ <div class="step">
42
+ <div class="step-number">
43
+ <%= number %>
44
+ </div>
45
+
46
+ <% for diff in diffs %>
47
+ <% diff = Leg::DiffTransformers::SyntaxHighlight.new.transform(diff) %>
48
+ <div class="diff">
49
+ <div class="diff-header">
50
+ <div class="diff-summary">
51
+ <%= markdown(summary) %>
52
+ </div>
53
+ <div class="diff-filename">
54
+ <%= diff.filename %>
55
+ </div>
56
+ </div>
57
+ <div class="diff-code">
58
+ <table>
59
+ <% for line in diff.lines %>
60
+ <tr>
61
+ <td class="line-number">
62
+ <%= line.line_number %>
63
+ </td>
64
+ <td class="line <%= diff.is_new_file ? :unchanged : line.type %>">\\
65
+ <% if line.type == :folded %>\\
66
+ <%= line.source.gsub('<span class="err">…</span>', '…') %>\\
67
+ <% else %>\\
68
+ <%= line.source %>\\
69
+ <% end %>\\
70
+ </td>
71
+ </tr>
72
+ <% end %>
73
+ </table>
74
+ </div>
75
+ </div>
76
+ <% end %>
77
+ </div>
78
+ TEMPLATE
79
+
80
+ PAGE["md"] = <<~TEMPLATE
81
+ <%= content %>
82
+ TEMPLATE
83
+
84
+ STEP["md"] = <<~TEMPLATE
85
+ ## <%= number %>. <%= summary %>
86
+
87
+ <% for diff in diffs %>\\
88
+ ```diff
89
+ // <%= diff.filename %>
90
+ <% for line in diff.lines %>\\
91
+ <%= { added: '+', removed: '-', unchanged: ' ', folded: '@' }[line.type] + line.source %>
92
+ <% end %>\\
93
+ ```
94
+ <% end %>
95
+ TEMPLATE
96
+
97
+ CSS = <<~TEMPLATE
98
+ * {
99
+ margin: 0;
100
+ padding: 0;
101
+ box-sizing: border-box;
102
+ }
103
+
104
+ body {
105
+ font-family: Utopia, Georgia, Times, 'Apple Symbols', serif;
106
+ line-height: 140%;
107
+ color: #333;
108
+ font-size: 18px;
109
+ }
110
+
111
+ #container {
112
+ width: 750px;
113
+ margin: 18px auto;
114
+ }
115
+
116
+ .bar {
117
+ display: block;
118
+ width: 100%;
119
+ background-color: #ceb;
120
+ box-shadow: 0px 0px 15px 1px #ddd;
121
+ }
122
+
123
+ .bar > nav {
124
+ display: flex;
125
+ justify-content: space-between;
126
+ width: 700px;
127
+ margin: 0 auto;
128
+ }
129
+
130
+ footer.bar > nav {
131
+ justify-content: center;
132
+ }
133
+
134
+ .bar > nav > a {
135
+ display: block;
136
+ padding: 2px 0 4px 0;
137
+ color: #152;
138
+ }
139
+
140
+ h1, h2, h3, h4, h5, h6 {
141
+ font-family: Futura, Helvetica, Arial, sans-serif;
142
+ color: #222;
143
+ line-height: 100%;
144
+ margin-top: 32px;
145
+ }
146
+
147
+ h2 a, h3 a, h4 a {
148
+ color: inherit;
149
+ text-decoration: none;
150
+ }
151
+
152
+ h2 a::before, h3 a::before, h4 a::before {
153
+ content: '#';
154
+ color: #fff;
155
+ font-weight: normal;
156
+ transition: color 0.15s ease;
157
+ display: block;
158
+ float: left;
159
+ width: 32px;
160
+ margin-left: -32px;
161
+ }
162
+
163
+ h2 a:hover::before, h3 a:hover::before, h4 a:hover::before {
164
+ color: #ccc;
165
+ }
166
+
167
+ h1 {
168
+ margin-top: 0;
169
+ font-size: 38px;
170
+ border-bottom: 3px solid #e7c;
171
+ display: inline-block;
172
+ }
173
+
174
+ h2 {
175
+ font-size: 26px;
176
+ }
177
+
178
+ p {
179
+ margin-top: 18px;
180
+ }
181
+
182
+ ul, ol {
183
+ margin-top: 18px;
184
+ margin-left: 36px;
185
+ }
186
+
187
+ hr {
188
+ border: none;
189
+ border-bottom: 1px solid #888;
190
+ }
191
+
192
+ a {
193
+ color: #26d;
194
+ }
195
+
196
+ code {
197
+ font-family: monospace;
198
+ font-size: inherit;
199
+ white-space: nowrap;
200
+ background-color: #eff4ea;
201
+ padding: 1px 3px;
202
+ }
203
+
204
+ h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
205
+ font-weight: normal;
206
+ }
207
+
208
+ kbd {
209
+ font-family: monospace;
210
+ border-radius: 3px;
211
+ padding: 2px 3px;
212
+ box-shadow: 1px 1px 1px #777;
213
+ margin: 2px;
214
+ font-size: 14px;
215
+ background: #f7f7f7;
216
+ font-weight: 500;
217
+ color: #555;
218
+ white-space: nowrap;
219
+ }
220
+
221
+ h1 kbd, h2 kbd, h3 kbd, h4 kbd, h5 kbd, h6 kbd {
222
+ font-size: 80%;
223
+ }
224
+
225
+ .step {
226
+ margin-top: 18px;
227
+ }
228
+
229
+ .step-number {
230
+ position: absolute;
231
+ margin-top: -6px;
232
+ margin-left: -148px;
233
+ font-size: 48px;
234
+ font-family: Helvetica, sans-serif;
235
+ line-height: 130%;
236
+ width: 128px;
237
+ text-align: right;
238
+ }
239
+
240
+ .diff {
241
+ border: 1px solid #ede7e3;
242
+ border-radius: 3px;
243
+ }
244
+
245
+ .diff .diff-header {
246
+ display: flex;
247
+ justify-content: space-between;
248
+ align-items: center;
249
+ padding: 7px 10px;
250
+ background-color: #fafbfc;
251
+ border-bottom: 1px solid #ede7e3;
252
+ font-size: 16px;
253
+ }
254
+
255
+ .diff .diff-summary {
256
+ color: #666;
257
+ font-size: 18px;
258
+ }
259
+
260
+ .diff .diff-summary p {
261
+ margin-top: 0;
262
+ }
263
+
264
+ .diff .diff-filename {
265
+ color: #666;
266
+ font-weight: bold;
267
+ }
268
+
269
+ .diff table {
270
+ width: 100%;
271
+ border-spacing: 0;
272
+ border-collapse: collapse;
273
+ }
274
+
275
+ .diff tr {
276
+ height: 20px;
277
+ line-height: 20px;
278
+ padding: 0 5px;
279
+ background-color: #fff;
280
+ font-family: monospace;
281
+ font-size: 14px;
282
+ }
283
+
284
+ .diff td.line-number {
285
+ width: 1%;
286
+ min-width: 55px;
287
+ text-align: right;
288
+ padding-right: 15px;
289
+ background-color: #fafbfc;
290
+ color: #ccc;
291
+ }
292
+
293
+ .diff td.line {
294
+ white-space: pre;
295
+ position: relative;
296
+ background-color: inherit;
297
+ padding-left: 5px;
298
+ }
299
+
300
+ .diff td.line.folded {
301
+ background-color: #eef;
302
+ opacity: 0.5;
303
+ }
304
+
305
+ .diff td.line.added {
306
+ background-color: #ffd;
307
+ text-decoration: none;
308
+ }
309
+
310
+ .diff td.line.removed {
311
+ background-color: #fdd;
312
+ text-decoration: line-through;
313
+ }
314
+
315
+ @media screen and (max-width: 700px) {
316
+ #container {
317
+ width: auto;
318
+ margin: 18px 0;
319
+ padding: 0 5px;
320
+ }
321
+
322
+ .bar > nav {
323
+ width: auto;
324
+ margin: 0;
325
+ padding: 0 5px;
326
+ }
327
+
328
+ .diff .diff-code {
329
+ overflow-x: scroll;
330
+ }
331
+
332
+ .diff .table {
333
+ width: 700px;
334
+ }
335
+ }
336
+
337
+ <%= syntax_highlighting_css ".line" %>
338
+ TEMPLATE
339
+ end
340
+ end