codeblock 1.0
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 +7 -0
- data/README.md +191 -0
- data/lib/codeblock.rb +606 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 583c7a3edfb276f54463bc3c9a04ff035d9c7597b8f353fdc18b1076cfdf1b33
|
4
|
+
data.tar.gz: b6d7010884b4fc6a0347896d9a6029ce6c8e01cbc11ef2f0c0e3e8dd4441d395
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b001b7883f3075a41638aa92f66935eb4b52ad3a7eb90b182d1b3ebc2950f11312787e4df387b63d69de4297fb4e472495093547781da1d8edaf5acaf5b551c4
|
7
|
+
data.tar.gz: 759e75ff240f9a12b4d06d67fd7af5ce536c0b0c896443ffbc30f7891d00d786147b180c7a914f7130494f82b3f44827fa61733a16283e92716e210716e5e7a1
|
data/README.md
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
# CodeBlock
|
2
|
+
|
3
|
+
CodeBlock assists in documenting code by importing working code for display in
|
4
|
+
documentation. CodeBlock allows you to write working examples of code, then
|
5
|
+
import that code into documentation without any copying or pasting. Features
|
6
|
+
include:
|
7
|
+
|
8
|
+
- import all of a file or just parts of it
|
9
|
+
- strip out lines that are not relevent to the documentation
|
10
|
+
- reference lines by name instead of line number
|
11
|
+
|
12
|
+
CodeBlock itself is not a documentation system. It is intended for use in other
|
13
|
+
documentation systems.
|
14
|
+
|
15
|
+
## Basic usage
|
16
|
+
|
17
|
+
Consider an example of a code file that I have for development of a DBMS system
|
18
|
+
I'm developing called "Audrey". The code file looks like this:
|
19
|
+
|
20
|
+
#!/usr/bin/ruby -w
|
21
|
+
require 'audrey'
|
22
|
+
|
23
|
+
# connect to database, creating it if necessary
|
24
|
+
Audrey.connect('/tmp/my.db', 'rw') do |db|
|
25
|
+
db['hero'] = 'Thor'
|
26
|
+
db['antagonist'] = 'Loki'
|
27
|
+
end
|
28
|
+
|
29
|
+
# done
|
30
|
+
puts '[done]'
|
31
|
+
|
32
|
+
In its simplest use, a new CodeBlock object simply slurps in a code file. The
|
33
|
+
contents of the file are accessible in the object's to_s method. So, we might
|
34
|
+
use CodeBlock like this:
|
35
|
+
|
36
|
+
require 'codeblock'
|
37
|
+
src_path = 'samples/plain.rb'
|
38
|
+
block = CodeBlock.new(File.read src_path)
|
39
|
+
puts block.to_s # => outputs the exact contents of the file
|
40
|
+
|
41
|
+
So far that's not very useful. We could accomplish the same thing by simply
|
42
|
+
slurping in the file contents. Let's look at a more advanced usage.
|
43
|
+
|
44
|
+
Suppose you only want to display a certain block of code in your documentation.
|
45
|
+
Now, you could just copy and paste the relevent block into your documentation,
|
46
|
+
but that's redundant information. Your document doesn't get updated if you
|
47
|
+
change the working example. That's where CodeBlock gets useful. With CodeBlock,
|
48
|
+
you can name specific lines to import. Let's look at the same working code as
|
49
|
+
above, but with a few additions.
|
50
|
+
|
51
|
+
#!/usr/bin/ruby -w
|
52
|
+
require 'audrey'
|
53
|
+
|
54
|
+
# connect to database, creating it if necessary
|
55
|
+
Audrey.connect('/tmp/my.db', 'rw') do |db| ## name=begin
|
56
|
+
db['hero'] = 'Thor'
|
57
|
+
db['antagonist'] = 'Loki'
|
58
|
+
end ## name=finish
|
59
|
+
|
60
|
+
# done
|
61
|
+
puts '[done]'
|
62
|
+
|
63
|
+
That's the same working code... all we've done is added some comments that
|
64
|
+
CodeBlock can interpret. Each line to which we want to add information has `##`
|
65
|
+
followed by name=value pairs. In the above example, the lines are givens the
|
66
|
+
names "begin" and "finish".
|
67
|
+
|
68
|
+
To extract the block of code that starts with "begin" and ends with "finish",
|
69
|
+
we set the CodeBlock#start and CodeBlock#end properties, like this:
|
70
|
+
|
71
|
+
src_path = 'samples/block.rb'
|
72
|
+
block = CodeBlock.new(File.read src_path)
|
73
|
+
block.start = 'begin'
|
74
|
+
block.end = 'finish'
|
75
|
+
block.to_s
|
76
|
+
|
77
|
+
That retrieves just the lines indicated. Notice that `##` and everything after
|
78
|
+
it is stripped from the lines:
|
79
|
+
|
80
|
+
Audrey.connect('/tmp/my.db', 'rw') do |db|
|
81
|
+
db['hero'] = 'Thor'
|
82
|
+
db['antagonist'] = 'Loki'
|
83
|
+
end
|
84
|
+
|
85
|
+
## Skip lines
|
86
|
+
|
87
|
+
You might find that in some situations its helpful to have code in your working
|
88
|
+
file that isn't relevent to the documentation. In those case, you can indicate
|
89
|
+
that a line should be skipped with skip=true:
|
90
|
+
|
91
|
+
Audrey.connect('/tmp/my.db', 'rw') do |db| ## name=start
|
92
|
+
db['hero'] = 'Thor'
|
93
|
+
puts db['hero'] ## skip=true
|
94
|
+
end ## name=end
|
95
|
+
|
96
|
+
That will results in a code sample that doesn't include that line
|
97
|
+
|
98
|
+
Audrey.connect('/tmp/my.db', 'rw') do |db|
|
99
|
+
db['hero'] = 'Thor'
|
100
|
+
end
|
101
|
+
|
102
|
+
## Line numbers
|
103
|
+
|
104
|
+
You might want to include line numbers in your documentation. There are two ways
|
105
|
+
to do this.
|
106
|
+
|
107
|
+
First, you might want a string just of line numbers. Doing so would allow you to
|
108
|
+
put the line numbers into an HTML <pre> tag alongside the code itself. To get
|
109
|
+
line numbers simply call CodeBlock#line_nums_to_s. Doing so outputs something
|
110
|
+
like as follows. Notice that the numbers are right justified.
|
111
|
+
|
112
|
+
1.
|
113
|
+
2.
|
114
|
+
3.
|
115
|
+
4.
|
116
|
+
5.
|
117
|
+
6.
|
118
|
+
7.
|
119
|
+
8.
|
120
|
+
9.
|
121
|
+
10.
|
122
|
+
11.
|
123
|
+
12.
|
124
|
+
|
125
|
+
The line numbers match up with the lines that have been selected, not the line
|
126
|
+
numbers in the file. That allows you to display code lines and line numbers that
|
127
|
+
match up.
|
128
|
+
|
129
|
+
To output the entire code block with line numbers, set the block's `line_nums`
|
130
|
+
property to true:
|
131
|
+
|
132
|
+
block.line_nums = true
|
133
|
+
puts block.to_s
|
134
|
+
|
135
|
+
That will output something like this:
|
136
|
+
|
137
|
+
1. # connect to database, creating it if necessary
|
138
|
+
2. Audrey.connect('/tmp/my.db', 'rw') do |db|
|
139
|
+
3. db['hero'] = 'Thor'
|
140
|
+
4. db['antagonist'] = 'Loki'
|
141
|
+
5. end
|
142
|
+
6.
|
143
|
+
7. # read
|
144
|
+
8. Audrey.connect('/tmp/my.db', 'rw') do |db|
|
145
|
+
9. puts db['hero']
|
146
|
+
10. puts db['antagonist']
|
147
|
+
11. end
|
148
|
+
12.
|
149
|
+
13. # done
|
150
|
+
14. puts '[done]'
|
151
|
+
|
152
|
+
## Named lines
|
153
|
+
|
154
|
+
Lines can be referenced by their names to get their line numbers. This allows
|
155
|
+
you to give the number of a line in a block without having to change the line
|
156
|
+
number when you modify the block. Access the line in the block's `named`
|
157
|
+
property.
|
158
|
+
|
159
|
+
In this example, we name one of the lines "Thor":
|
160
|
+
|
161
|
+
Audrey.connect('/tmp/my.db', 'rw') do |db| ## name=start
|
162
|
+
db['hero'] = 'Thor' ## name=Thor
|
163
|
+
end ## name=end
|
164
|
+
|
165
|
+
Then, depending on how your documentation system works, you could reference the
|
166
|
+
line number like this:
|
167
|
+
|
168
|
+
src_path = 'samples/named-lines.rb'
|
169
|
+
block = CodeBlock.new(File.read(src_path), 'se'=>true)
|
170
|
+
puts "In line number #{block.named['Thor'].number} we set a value."
|
171
|
+
|
172
|
+
This will output as follows:
|
173
|
+
|
174
|
+
In line number 2 we set a value.
|
175
|
+
|
176
|
+
## Install
|
177
|
+
|
178
|
+
```
|
179
|
+
gem install codeblock
|
180
|
+
```
|
181
|
+
|
182
|
+
## Author
|
183
|
+
|
184
|
+
Mike O'Sullivan
|
185
|
+
mike@idocs.com
|
186
|
+
|
187
|
+
## History
|
188
|
+
|
189
|
+
| version | date | notes |
|
190
|
+
|---------|---------------|-------------------------------------------------------|
|
191
|
+
| 1.0 | June 18, 2020 | Initial upload. |
|
data/lib/codeblock.rb
ADDED
@@ -0,0 +1,606 @@
|
|
1
|
+
require 'attparser'
|
2
|
+
|
3
|
+
|
4
|
+
#===============================================================================
|
5
|
+
# CodeBlock
|
6
|
+
#
|
7
|
+
class CodeBlock
|
8
|
+
# Version
|
9
|
+
VERSION = '1.0'
|
10
|
+
|
11
|
+
#---------------------------------------------------------------------------
|
12
|
+
# initialize
|
13
|
+
#
|
14
|
+
|
15
|
+
# First param should be the code that should be parsed. The remaining
|
16
|
+
# options follow the names of the various properties that can be set. So,
|
17
|
+
# for example, to create a block with named, starting and ending lines,
|
18
|
+
# you could do this:
|
19
|
+
#
|
20
|
+
# block = CodeBlock.new(src, 'start'=>'a', 'end'=>'b')
|
21
|
+
#
|
22
|
+
# You might find that you are often naming the starting line "start" and the
|
23
|
+
# ending line "end". To simplify setting those common names, you can use the
|
24
|
+
# se ("start/end") option:
|
25
|
+
#
|
26
|
+
# block = CodeBlock.new(src, 'se'=>true)
|
27
|
+
#
|
28
|
+
# That is exactly equivelant to:
|
29
|
+
#
|
30
|
+
# block = CodeBlock.new(src, 'start'=>'start', 'end'=>'end')
|
31
|
+
|
32
|
+
def initialize(raw, opts={})
|
33
|
+
@lines_org = raw.lines
|
34
|
+
@keep_meta = opts['keep_meta'] || false
|
35
|
+
@tab = opts['tab'] || ' '
|
36
|
+
@indent = opts['indent']
|
37
|
+
@line_nums = opts['line_nums']
|
38
|
+
|
39
|
+
# if 'se' param, set start and end to "start" and "end"
|
40
|
+
if opts['se']
|
41
|
+
@start = 'start'
|
42
|
+
@end = 'end'
|
43
|
+
else
|
44
|
+
@start = opts['start']
|
45
|
+
@end = opts['end']
|
46
|
+
end
|
47
|
+
|
48
|
+
# if collapse is defined
|
49
|
+
if opts.has_key?('collapse')
|
50
|
+
@collapse = opts['collapse']
|
51
|
+
else
|
52
|
+
@collapse = true
|
53
|
+
end
|
54
|
+
|
55
|
+
# if skip_skips is defined
|
56
|
+
if opts.has_key?('skip_skips')
|
57
|
+
@skip_skips = opts['skip_skips']
|
58
|
+
else
|
59
|
+
@skip_skips = true
|
60
|
+
end
|
61
|
+
|
62
|
+
# initialize block object
|
63
|
+
@block_ob = nil
|
64
|
+
end
|
65
|
+
#
|
66
|
+
# initialize
|
67
|
+
#---------------------------------------------------------------------------
|
68
|
+
|
69
|
+
|
70
|
+
#---------------------------------------------------------------------------
|
71
|
+
# attribute readers
|
72
|
+
#
|
73
|
+
|
74
|
+
# The name of the line that starts the block. Leave nil to start at the
|
75
|
+
# beginning of the file. If an ending line is given but that line is not
|
76
|
+
# found then an exception is raised.
|
77
|
+
attr_reader :start
|
78
|
+
|
79
|
+
# The name of the line that ends the block. Leave nil for no ending line.
|
80
|
+
# If an ending line is given but that line is not found then an exception is
|
81
|
+
# raised.
|
82
|
+
attr_reader :end
|
83
|
+
|
84
|
+
# The string to convert tab characters to. Defaults to four spaces.
|
85
|
+
attr_reader :tab
|
86
|
+
|
87
|
+
# If set, indicates a string to put at the beginning of each line. For
|
88
|
+
# Markdown, a value of four spaces is a good choice. Defaults to nil.
|
89
|
+
attr_reader :indent
|
90
|
+
|
91
|
+
# If true, then contiguous lines that contain just spaces are collapsed to a
|
92
|
+
# single space-only line.
|
93
|
+
attr_reader :collapse
|
94
|
+
|
95
|
+
# If true, the line meta information (i.e. the ## and everything after it)
|
96
|
+
# is not stripped from the output lines. Defaults to false.
|
97
|
+
attr_reader :keep_meta
|
98
|
+
|
99
|
+
|
100
|
+
# If true, then lines that are marked to be skipped are not skipped.
|
101
|
+
# Defaults to false.
|
102
|
+
attr_reader :skip_skips
|
103
|
+
|
104
|
+
# If true, line numbers are output with the block of code. Defaults to
|
105
|
+
# false.
|
106
|
+
attr_reader :line_nums
|
107
|
+
|
108
|
+
# The full array of lines in the code.
|
109
|
+
attr_reader :lines_full
|
110
|
+
|
111
|
+
#
|
112
|
+
# attribute readers
|
113
|
+
#---------------------------------------------------------------------------
|
114
|
+
|
115
|
+
|
116
|
+
#---------------------------------------------------------------------------
|
117
|
+
# start=, end=, tab=
|
118
|
+
#
|
119
|
+
|
120
|
+
# Sets the start property.
|
121
|
+
def start=(new_val)
|
122
|
+
@block_ob = nil
|
123
|
+
@start = new_val
|
124
|
+
end
|
125
|
+
|
126
|
+
# Sets the end property.
|
127
|
+
def end=(new_val)
|
128
|
+
@block_ob = nil
|
129
|
+
@end = new_val
|
130
|
+
end
|
131
|
+
|
132
|
+
# Sets the tab property.
|
133
|
+
def tab=(new_val)
|
134
|
+
@block_ob = nil
|
135
|
+
@tab = new_val
|
136
|
+
end
|
137
|
+
|
138
|
+
# Sets the indent property.
|
139
|
+
def indent=(new_val)
|
140
|
+
@block_ob = nil
|
141
|
+
@indent = new_val
|
142
|
+
end
|
143
|
+
|
144
|
+
# Sets the collapse property.
|
145
|
+
def collapse=(new_val)
|
146
|
+
@block_ob = nil
|
147
|
+
@collapse = new_val
|
148
|
+
end
|
149
|
+
|
150
|
+
# Sets the keep_meta property.
|
151
|
+
def keep_meta=(new_val)
|
152
|
+
@block_ob = nil
|
153
|
+
@keep_meta = new_val
|
154
|
+
end
|
155
|
+
|
156
|
+
# Sets the skip_skips property.
|
157
|
+
def skip_skips=(new_val)
|
158
|
+
@block_ob = nil
|
159
|
+
@skip_skips = new_val
|
160
|
+
end
|
161
|
+
|
162
|
+
# Sets the line_nums property.
|
163
|
+
def line_nums=(new_val)
|
164
|
+
@block_ob = nil
|
165
|
+
@line_nums = new_val
|
166
|
+
end
|
167
|
+
|
168
|
+
#
|
169
|
+
# start=, end=, tab=
|
170
|
+
#---------------------------------------------------------------------------
|
171
|
+
|
172
|
+
|
173
|
+
#---------------------------------------------------------------------------
|
174
|
+
# sort-of delegate some methods to block()
|
175
|
+
# NOTE: I can't get delegate() to work for delegating to a method. Not sure
|
176
|
+
# what I'm doing wrong. Feel free to fix it.
|
177
|
+
#
|
178
|
+
|
179
|
+
# Returns an array of the lines in the code block. Each element of the array
|
180
|
+
# is a CodeBlock::Line object.
|
181
|
+
def lines
|
182
|
+
return block.lines
|
183
|
+
end
|
184
|
+
|
185
|
+
# Returns a string of the code block.
|
186
|
+
def to_s
|
187
|
+
return block.to_s
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns a string of the line numbers, one number per line.
|
191
|
+
def line_nums_to_s
|
192
|
+
return block.line_nums_to_s
|
193
|
+
end
|
194
|
+
|
195
|
+
# Returns a string of the notes for each each line. Notes are set with the
|
196
|
+
# `notes` attribute in the meta section of the line, like this:
|
197
|
+
#
|
198
|
+
# db['hero'] = 'Thor' ## notes="Set the value in the database"
|
199
|
+
#
|
200
|
+
# There will be as many lines in the string as lines in the code block.
|
201
|
+
# Lines with no notes will be empty lines.
|
202
|
+
def notes_to_s
|
203
|
+
return block.notes_to_s
|
204
|
+
end
|
205
|
+
|
206
|
+
# A hash of named lines.
|
207
|
+
def named
|
208
|
+
return block.named
|
209
|
+
end
|
210
|
+
|
211
|
+
#
|
212
|
+
# sort-of delegate some methods to block()
|
213
|
+
#---------------------------------------------------------------------------
|
214
|
+
|
215
|
+
|
216
|
+
# private
|
217
|
+
private
|
218
|
+
|
219
|
+
|
220
|
+
#---------------------------------------------------------------------------
|
221
|
+
# block
|
222
|
+
#
|
223
|
+
def block()
|
224
|
+
# build block if necessary
|
225
|
+
if not @block_ob
|
226
|
+
@lines_full = @lines_org.map {|line| CodeBlock::Line.new(self, line)}
|
227
|
+
opts = {}
|
228
|
+
opts['start'] = @start
|
229
|
+
opts['end'] = @end
|
230
|
+
opts['tab'] = @tab
|
231
|
+
opts['collapse'] = @collapse
|
232
|
+
@block_ob = CodeBlock::Block.new(self, opts)
|
233
|
+
end
|
234
|
+
|
235
|
+
# return
|
236
|
+
return @block_ob
|
237
|
+
end
|
238
|
+
#
|
239
|
+
# block
|
240
|
+
#---------------------------------------------------------------------------
|
241
|
+
end
|
242
|
+
#
|
243
|
+
# CodeBlock
|
244
|
+
#===============================================================================
|
245
|
+
|
246
|
+
|
247
|
+
#===============================================================================
|
248
|
+
# CodeBlock::Line
|
249
|
+
#
|
250
|
+
|
251
|
+
# A CodeBlock::Line represents a single line in the block of code. Generally you
|
252
|
+
# will not need to instantiate a CodeBlock::Line object yourself.
|
253
|
+
class CodeBlock::Line
|
254
|
+
#---------------------------------------------------------------------------
|
255
|
+
# initialize
|
256
|
+
#
|
257
|
+
|
258
|
+
# Accepts a CodeBlock object and the raw string from the code.
|
259
|
+
def initialize(code, raw)
|
260
|
+
@code = code
|
261
|
+
@raw = raw
|
262
|
+
@number = nil
|
263
|
+
@display = @raw
|
264
|
+
|
265
|
+
# unless keep meta, else strip ## and everything after
|
266
|
+
if @code.keep_meta
|
267
|
+
@display = @raw
|
268
|
+
else
|
269
|
+
@display = @raw.sub(/\s*\#\#.*/mu, '')
|
270
|
+
end
|
271
|
+
|
272
|
+
# remove trailing spaces
|
273
|
+
@display = @display.rstrip
|
274
|
+
|
275
|
+
# attributes
|
276
|
+
if @raw.match(/\#\#/mu)
|
277
|
+
raw_atts = @raw.sub(/\A.*\#\#\s*/mu, '')
|
278
|
+
raw_atts = raw_atts.rstrip
|
279
|
+
@atts = AttParser.parse(raw_atts, 'infer'=>true)
|
280
|
+
else
|
281
|
+
@atts = {}
|
282
|
+
end
|
283
|
+
end
|
284
|
+
#
|
285
|
+
# initialize
|
286
|
+
#---------------------------------------------------------------------------
|
287
|
+
|
288
|
+
|
289
|
+
#---------------------------------------------------------------------------
|
290
|
+
# readers and accessors
|
291
|
+
#
|
292
|
+
|
293
|
+
# Returns a hash of the name=value pairs set in the line's meta section.
|
294
|
+
attr_reader :atts
|
295
|
+
|
296
|
+
# Returns the line's number in the code block.
|
297
|
+
attr_accessor :number
|
298
|
+
|
299
|
+
#
|
300
|
+
# readers and accessors
|
301
|
+
#---------------------------------------------------------------------------
|
302
|
+
|
303
|
+
|
304
|
+
#---------------------------------------------------------------------------
|
305
|
+
# convenience
|
306
|
+
#
|
307
|
+
|
308
|
+
# Returns the value of 'name' in the line's attributes. Returns nil if no
|
309
|
+
# name was set.
|
310
|
+
def name
|
311
|
+
return @atts['name']
|
312
|
+
end
|
313
|
+
|
314
|
+
# Returns the value of 'skip' in the line's attributes. Returns nil if no
|
315
|
+
# skip was set.
|
316
|
+
def skip
|
317
|
+
return @atts['skip']
|
318
|
+
end
|
319
|
+
|
320
|
+
# Returns the value of 'notes' in the line's attributes. Returns nil if no
|
321
|
+
# notes were set.
|
322
|
+
def notes
|
323
|
+
return @atts['notes']
|
324
|
+
end
|
325
|
+
|
326
|
+
#
|
327
|
+
# convenience
|
328
|
+
#---------------------------------------------------------------------------
|
329
|
+
|
330
|
+
|
331
|
+
#---------------------------------------------------------------------------
|
332
|
+
# to_s
|
333
|
+
#
|
334
|
+
|
335
|
+
# Returns the line as it should be displayed, taking into account tabs,
|
336
|
+
# indentation, line number, etc. If the line is to be skipped then nil is
|
337
|
+
# returned.
|
338
|
+
def to_s
|
339
|
+
if @atts['skip'] and (not @code.skip_skips)
|
340
|
+
return nil
|
341
|
+
else
|
342
|
+
return @display
|
343
|
+
end
|
344
|
+
end
|
345
|
+
#
|
346
|
+
# to_s
|
347
|
+
#---------------------------------------------------------------------------
|
348
|
+
|
349
|
+
|
350
|
+
#---------------------------------------------------------------------------
|
351
|
+
# has_content?
|
352
|
+
#
|
353
|
+
|
354
|
+
# Returns true if the line has any non-space characters.
|
355
|
+
def has_content?
|
356
|
+
return @display.match(/\S/mu)
|
357
|
+
end
|
358
|
+
|
359
|
+
# Returns false if the line has any non-space characters.
|
360
|
+
def no_content?
|
361
|
+
return !has_content?
|
362
|
+
end
|
363
|
+
|
364
|
+
#
|
365
|
+
# has_content?
|
366
|
+
#---------------------------------------------------------------------------
|
367
|
+
end
|
368
|
+
#
|
369
|
+
# CodeBlock::Line
|
370
|
+
#===============================================================================
|
371
|
+
|
372
|
+
|
373
|
+
|
374
|
+
#===============================================================================
|
375
|
+
# CodeBlock::Block
|
376
|
+
#
|
377
|
+
|
378
|
+
# This is the class that does the work of filtering which lines should be
|
379
|
+
# displayed and which shouldn't. Generally you won't have to work directly with
|
380
|
+
# this class.
|
381
|
+
class CodeBlock::Block
|
382
|
+
#---------------------------------------------------------------------------
|
383
|
+
# initialize
|
384
|
+
#
|
385
|
+
def initialize(p_code, opts={})
|
386
|
+
@code = p_code
|
387
|
+
@collapse = opts['collapse']
|
388
|
+
@tab = opts['tab'] || ' '
|
389
|
+
@start = opts['start']
|
390
|
+
@end = opts['end']
|
391
|
+
|
392
|
+
# build lines array
|
393
|
+
build_lines()
|
394
|
+
end
|
395
|
+
#
|
396
|
+
# initialize
|
397
|
+
#---------------------------------------------------------------------------
|
398
|
+
|
399
|
+
|
400
|
+
#---------------------------------------------------------------------------
|
401
|
+
# attribute readers
|
402
|
+
#
|
403
|
+
|
404
|
+
# The array of CodeBlock::Line objects.
|
405
|
+
attr_reader :lines
|
406
|
+
|
407
|
+
# A hash of named lines.
|
408
|
+
attr_reader :named
|
409
|
+
|
410
|
+
#
|
411
|
+
# attribute readers
|
412
|
+
#---------------------------------------------------------------------------
|
413
|
+
|
414
|
+
|
415
|
+
|
416
|
+
#---------------------------------------------------------------------------
|
417
|
+
# to_s
|
418
|
+
#
|
419
|
+
|
420
|
+
# Builds the string that is returned by CodeBlock#to_s.
|
421
|
+
def to_s()
|
422
|
+
# $tm.hrm
|
423
|
+
rv = []
|
424
|
+
indent = @code.indent || ''
|
425
|
+
line_nums = @code.line_nums
|
426
|
+
|
427
|
+
# get maximum width
|
428
|
+
if line_nums
|
429
|
+
max_width = @lines[-1].number.to_s.length
|
430
|
+
end
|
431
|
+
|
432
|
+
# build return array
|
433
|
+
@lines.each do |line|
|
434
|
+
# get string
|
435
|
+
v = line.to_s
|
436
|
+
|
437
|
+
# substitute tabs
|
438
|
+
if @tab
|
439
|
+
v = v.gsub(/\t/mu, @tab)
|
440
|
+
end
|
441
|
+
|
442
|
+
# line numbers
|
443
|
+
if line_nums
|
444
|
+
v = line.number.to_s.rjust(max_width) + '. ' + v
|
445
|
+
end
|
446
|
+
|
447
|
+
# indent
|
448
|
+
v = indent + v
|
449
|
+
|
450
|
+
# add to return array
|
451
|
+
rv.push v
|
452
|
+
end
|
453
|
+
|
454
|
+
# return
|
455
|
+
return rv.join("\n")
|
456
|
+
end
|
457
|
+
#
|
458
|
+
# to_s
|
459
|
+
#---------------------------------------------------------------------------
|
460
|
+
|
461
|
+
|
462
|
+
#---------------------------------------------------------------------------
|
463
|
+
# line_nums_to_s
|
464
|
+
#
|
465
|
+
|
466
|
+
# Builds the string that is returned by CodeBlock#line_nums_to_s.
|
467
|
+
def line_nums_to_s
|
468
|
+
# $tm.hrm
|
469
|
+
rv = []
|
470
|
+
|
471
|
+
# get maximum width
|
472
|
+
width = @lines[-1].number.to_s.length
|
473
|
+
|
474
|
+
# loop through lines
|
475
|
+
@lines.each do |line|
|
476
|
+
rv.push line.number.to_s.rjust(width) + '.'
|
477
|
+
end
|
478
|
+
|
479
|
+
# return
|
480
|
+
return rv.join("\n")
|
481
|
+
end
|
482
|
+
#
|
483
|
+
# line_nums_to_s
|
484
|
+
#---------------------------------------------------------------------------
|
485
|
+
|
486
|
+
|
487
|
+
#---------------------------------------------------------------------------
|
488
|
+
# notes_to_s
|
489
|
+
#
|
490
|
+
|
491
|
+
# Builds the string that is returned by CodeBlock#notes_to_s.
|
492
|
+
def notes_to_s
|
493
|
+
rv = []
|
494
|
+
|
495
|
+
# loop through lines
|
496
|
+
@lines.each do |line|
|
497
|
+
rv.push line.notes || ''
|
498
|
+
end
|
499
|
+
|
500
|
+
# return
|
501
|
+
return rv.join("\n")
|
502
|
+
end
|
503
|
+
#
|
504
|
+
# notes_to_s
|
505
|
+
#---------------------------------------------------------------------------
|
506
|
+
|
507
|
+
|
508
|
+
# private
|
509
|
+
private
|
510
|
+
|
511
|
+
|
512
|
+
#---------------------------------------------------------------------------
|
513
|
+
# build_lines
|
514
|
+
#
|
515
|
+
def build_lines
|
516
|
+
# $tm.hrm
|
517
|
+
@lines = []
|
518
|
+
|
519
|
+
# starting line name
|
520
|
+
started = @start ? false : true
|
521
|
+
ended = false
|
522
|
+
|
523
|
+
# get visible lines
|
524
|
+
@code.lines_full.each do |line|
|
525
|
+
# start if necessary
|
526
|
+
if (not started) and @start
|
527
|
+
started = line.atts['name'] == @start
|
528
|
+
end
|
529
|
+
|
530
|
+
# add line
|
531
|
+
if started and (not ended)
|
532
|
+
if line.to_s
|
533
|
+
@lines.push line.clone
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
# if ending line
|
538
|
+
if (not ended) and @end
|
539
|
+
ended = line.atts['name'] == @end
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
# strip leading empty lines
|
544
|
+
while @lines.any? and (not @lines[0].to_s.match(/\S/mu))
|
545
|
+
@lines.shift
|
546
|
+
end
|
547
|
+
|
548
|
+
# strip trailing empty lines
|
549
|
+
while @lines.any? and (not @lines[-1].to_s.match(/\S/mu))
|
550
|
+
@lines.pop
|
551
|
+
end
|
552
|
+
|
553
|
+
# collapse
|
554
|
+
if @collapse
|
555
|
+
while collapse_find()
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
# number lines
|
560
|
+
@lines.each_with_index() do |line, idx|
|
561
|
+
line.number = idx + 1
|
562
|
+
end
|
563
|
+
|
564
|
+
# build hash of named lines
|
565
|
+
@named = {}
|
566
|
+
|
567
|
+
# get named lines
|
568
|
+
@lines.each do |line|
|
569
|
+
if line.name
|
570
|
+
if @named[line.name]
|
571
|
+
raise 'https://motley.uno/messages/code/init/redundant-name: ' + line.name
|
572
|
+
end
|
573
|
+
|
574
|
+
@named[line.name] = line
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
#
|
579
|
+
# build_lines
|
580
|
+
#---------------------------------------------------------------------------
|
581
|
+
|
582
|
+
|
583
|
+
#---------------------------------------------------------------------------
|
584
|
+
# collapse_find
|
585
|
+
#
|
586
|
+
def collapse_find()
|
587
|
+
# loop through lines
|
588
|
+
@lines.each_with_index do|line, idx|
|
589
|
+
if idx > 0
|
590
|
+
if line.no_content? and @lines[idx-1].no_content?
|
591
|
+
@lines.delete_at(idx-1)
|
592
|
+
return true
|
593
|
+
end
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
# didn't find anymore lines to collapse
|
598
|
+
return false
|
599
|
+
end
|
600
|
+
#
|
601
|
+
# collapse_find
|
602
|
+
#---------------------------------------------------------------------------
|
603
|
+
end
|
604
|
+
#
|
605
|
+
# CodeBlock::Block
|
606
|
+
#===============================================================================
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: codeblock
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike O'Sullivan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-06-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: attparser
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
description: Read a block of code from a working code file and format it.
|
28
|
+
email: mike@idocs.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- README.md
|
34
|
+
- lib/codeblock.rb
|
35
|
+
homepage: https://rubygems.org/gems/codeblock
|
36
|
+
licenses:
|
37
|
+
- MIT
|
38
|
+
metadata: {}
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
requirements: []
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 2.7.6
|
56
|
+
signing_key:
|
57
|
+
specification_version: 4
|
58
|
+
summary: Document a block of code
|
59
|
+
test_files: []
|