Top Level Namespace

Defined Under Namespace

Modules: Mandown

Instance Method Summary collapse

Instance Method Details

#all_chars?(line, char) ⇒ Boolean

Given a string, return true if all the characters are equal to char, or false otherwise. Note that if the line ends with a newline, this will be ignored.

Returns:

  • (Boolean)


148
149
150
151
152
153
154
# File 'lib/secdown_lib.rb', line 148

def all_chars?(line, char)
  return false if line.nil?
  line = line.strip
  chars_array = line.split('').uniq
  return true if chars_array.length == 1 && chars_array[0] == char
  return false
end

#atx_header?(line) ⇒ Boolean

Return true if the string contains an atx-style header and false otherwise.

Returns:

  • (Boolean)


127
128
129
130
131
132
133
# File 'lib/secdown_lib.rb', line 127

def atx_header?(line)
  if /#+ \w*/.match(line).nil?
    false
  else
    true
  end
end

#get_atx_nesting_level(atx_header) ⇒ Object

Given an atx-style header string of the form ‘# Something …’ or ‘## Something …’, return an integer that indicates the nesting level; ‘#’ would be level 1, ‘##’ would be level 2, etc. Returns 0 if the text is not an atx-style header.



139
140
141
142
143
# File 'lib/secdown_lib.rb', line 139

def get_atx_nesting_level(atx_header)
  # Check that we have an atx header.
  return 0 if !atx_header?(atx_header)
  atx_header.split[0].length # Return the number of leading # characters.
end

#get_heading_level_string(current_nesting, previous_level) ⇒ Object

Given the nesting level of a header (e.g. 2, as returned by get_atx_nesting_level) and a string which specifies the previous heading nesting level (e.g. ‘1.2.1’), return the heading nesting level string for the header (e.g. ‘1.3’, in this example).



186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/secdown_lib.rb', line 186

def get_heading_level_string(current_nesting, previous_level)
  if previous_level.split('.').length < current_nesting
    # The case where we need to move to a deeper nesting level.
    previous_level + '.1'
  else
    # The case where we need to remain at the current, or move up, a level.
    t = previous_level.split('.')
    t = t[0..current_nesting-1]
    t[-1] = (t[-1].to_i + 1).to_s
    t.join('.')
  end
end

#get_next_level_string(previous, header) ⇒ Object

Given a string that specifies the previous heading nesting level string (e.g. ‘1.3.1’) and an atx-style header (e.g. ‘## Header’), return the next heading nesting level string (e.g. ‘## 1.4’, in this example) and return it.



202
203
204
205
206
207
208
209
# File 'lib/secdown_lib.rb', line 202

def get_next_level_string(previous, header)
  # Get the level and the string to insert.
  if previous == ''
    '1'
  else
    get_heading_level_string(get_atx_nesting_level(header), previous)
  end
end

#get_section_number_from_header(header) ⇒ Object

Take a header string that contains a leading section number and return that number.



244
245
246
247
248
249
250
251
# File 'lib/secdown_lib.rb', line 244

def get_section_number_from_header(header)
  tmp = header.scan(/(\d+((\.\d)+))/)
  if tmp.empty?
    header.scan(/(\d+)/)[0][0]
  else
    tmp[0][0]
  end
end

#insert_header_levels(lines, ignore_bibliography = true) ⇒ Object

Given an array of lines of text, insert appropriate leading section numbers, and return the resulting array. By default, ignore the bibliography line.



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/secdown_lib.rb', line 223

def insert_header_levels(lines, ignore_bibliography = true)
  lines = setx_to_atx(lines) # Convert setx-style headers to atx-style.
  
  current_level_string = '0'
  ret_val = []
  lines.each do |line|
    if atx_header?(line) && /# Bibliography/.match(line) && ignore_bibliography
      ret_val.push(line)
    elsif atx_header?(line)
      current_level_string = get_next_level_string(current_level_string, line)
      ret_val.push(
        insert_level_to_atx_header(current_level_string, line))
    else
      ret_val.push(line)
    end
  end
  ret_val
end

#insert_level_to_atx_header(level_string, header) ⇒ Object

Given a string that specifies the level of a heading (e.g. ‘1.2.3’ such as returned by get_next_level_string, for example), and an atx-style header string, insert the level string into the header and return it.



215
216
217
218
# File 'lib/secdown_lib.rb', line 215

def insert_level_to_atx_header(level_string, header)
  level = get_atx_nesting_level(header)
  header[0..level-1] + ' ' + level_string + header[level..-1]  
end

#replace_section_references(lines) ⇒ Object

Take a manuscript, as an array of lines, that has been processed by insert_header_levels, and replace isnstances of ‘sec:some-label’ with the section number that the the label was defined in using ‘label:some-label’. Remove lines that contain the label definition and return the result.



258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/secdown_lib.rb', line 258

def replace_section_references(lines)
  # First parse the lines to get a mapping between the label names and the
  # section numbers they appear in.
  current_section_number = nil
  label_section_map = {} # Will map labels to section numbers.
  ret_val = []
  lines.each do |line|
    # Get the label:some-label string, if it is on this line, as an array.
    label_string_arr = line.scan(/^label:\w+[-_\.]*\w*/)
    
    if atx_header?(line)
      current_section_number = get_section_number_from_header(line)
    elsif !label_string_arr.empty?
      label = label_string_arr[0].split('label:')[1] # Get the label.
      label_section_map[label] = current_section_number
      line = nil # Kill off the label.
    else
      # Do nothing.
    end
    ret_val.push(line) unless line.nil?
  end

  # Now process ret_val to replace instances of 'sec:some-label' with 'section
  # 3.2' or whatever.
  ret_val.each_index do |i|
    label_section_map.each_pair do |key, value|
      ret_val[i].gsub!("sec:#{key}", "section #{value}")
    end
    p ret_val[i]
  end
  
  ret_val
end

#secdown_documentationObject

Define the text that is displayed when the user asks for documentation



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/secdown_lib.rb', line 21

def secdown_documentation
<<END


  secdown --- part of the Mandown set of tools.
  
  secdown processes standard input and:
    * inserts section numbers in titles;
    * allows references to other sections to be made.
    
  Copyright © 2008 Chris Rose. 
  Distributed according to the GNU General Public License.
  
  Usage:
  ------
  cat infile.txt | secdown > outfile.txt
  
  Syntax:
  -------
  
  Standard Markdown provides two ways to indicate headings: the first style uses 
  '=' or '-' characters placed under the title (for first and second level 
  headings respectively); the second uses leading '#' characters, where the 
  number of '#' characters indicates the nesting level.
  
  ------------------------------- Example -------------------------------------

  This is a level 1 heading
  =========================
  
  This is a level 2 heading
  -------------------------
  
  # This is also a level 1 heading
  
  ## This is also a level 2 heading
  
  ## So is this
  
  ### This is a level 3 heading

  ### So is this

  ------------------------------- End of example ------------------------------

  secdown inserts heading numbers into headings. For example, the above example 
  would be given the following heading numbers (the actual result is not shown, 
  but this indicates the resulting numbering):
  
  1 This is a level 1 heading
    1.1 This is a level 2 heading
  2 This is also a level 1 heading
    2.1 This is also a level 2 heading
    2.2 So is this
      2.2.1 This is a level 3 heading
      2.2.2 So is this
  
  secdown outputs all headings in the '#'-based style, irrespective of the input 
  style.
  
  secdown does not insert a heading number for '# Bibliography'.
  
  secdown provides a way to reference other sections. To label a section (or 
  subsection, etc.), insert a line containing a label of the form 
  'label:some-label' somehwere in the section you want to reference. To 
  reference the section, use the syntax 'sec:some-label'. secdown will remove 
  the label definitions and replace the section references with something like 
  'section 1.2'.
  
  ------------------------------- Example -------------------------------------

  The following text

      # This is a heading  
      label:first-section
  
      This is a reference to the second section, sec:second-section.
  
      # This is another heading
      label:second-section
      
      This is a reference to the first section, sec:first-section.
  
  Would be turned into
  
      # 1 This is a heading
      
      This is a reference to the second section, section 2.
      
      # 2 This is another heading
      
      This is a reference to the first section, section 1.  
  
  ------------------------------- End of example ------------------------------

  See also:
  ---------
  
  Markdown -- http://daringfireball.net/projects/markdown/
  Pandoc   -- http://johnmacfarlane.net/pandoc/
  
END
end

#secdown_mainObject

Read the manuscript from standard input and insert appropriately nested level numbering (e.g. ‘2.1’) and send the result to standard output.



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/secdown_lib.rb', line 295

def secdown_main
  # Handle request for documentation.
  if ARGV.length > 0 && ARGV[0] == '--help'
    puts secdown_documentation
    exit
  end
  
  # Read the file from standard input.
  lines = STDIN.readlines
  
  # Insert the level numbers.
  manuscript = insert_header_levels(lines)
  
  # Send the processed manuscript to standard output.
  STDOUT.puts(manuscript)
end

#setx_to_atx(lines) ⇒ Object

Given an array of strings, where the (i+1)-th string is the line that appears after the i-th line, convert setx-style headings (those that use underlining to indicate nesting level) to atx style (where a given number of # character are used). Return the resulting array of lines.



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/secdown_lib.rb', line 160

def setx_to_atx(lines)
  ret_val = []
  lines.each_index do |i|
    # Get the next two lines, if there are, else quit looking.
    if lines.length > i-2
      this_line = lines[i]
      next_line = lines[i+1]

      if all_chars?(next_line, '=')
        ret_val.push('# ' + this_line)
      elsif all_chars?(next_line, '-')
        ret_val.push('## ' + this_line)
      elsif all_chars?(this_line, '-') || all_chars?(this_line, '=')
        # Do nothing, but keep this condition in!
      else
        ret_val.push(this_line)
      end
    end
  end
  ret_val
end