marked-conductor 1.0.20 → 1.0.22

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c1f949d9bf655cb035b9bc2ed9f023e49c475effab65533f30b86c5392fa9908
4
- data.tar.gz: de2824955f1c84608c3c32e5c9ef43b9b28a9e9623f454723ca7ddff3cede011
3
+ metadata.gz: 4dd28de6ae0aaf52e49579cb6d42e7d5ae46c5e74fa9016c661f983330ba1163
4
+ data.tar.gz: 4a376f1832720b796dca464b62e3ee4bc47217892658b043fd0eeaf53417cf13
5
5
  SHA512:
6
- metadata.gz: 0f9c7c098a2e714ddca8673eda552d912319822c3a3c711fbfdba1a816e3f26dc8e42162532a68ccd1fb478d0452304b72e2a0d19c2a3025766ee9e0b3800db2
7
- data.tar.gz: ae32f776ecbd94a2eba88b92fbdb3f30c016ace349ded8f8b64ecf1fb688125732d790f6d5ad31152ec74570aab16ef859be9bb43b0df49219086d64c0f52169
6
+ metadata.gz: bdfe7d8bfd2834f5bd21ef4c2294f5b1295e26c0e065154f671bcde34ac81af5c79b2beb9405f8b71a69156a69143ead64609ee327459f41f37bfbf7fe0ad739
7
+ data.tar.gz: 5aece7af60fd7a296cc21ab28ea64d8cd5fe8fd22e2dd93c6fe1b7579080aba8bc6aca61fb945c015ecb3899114a2663b4ff98c69b7eadb24c24f79987fc3568
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ### 1.0.22
2
+
3
+ 2024-07-16 12:30
4
+
5
+ #### IMPROVED
6
+
7
+ - When injecting CSS or JS paths, URL encode the path
8
+
9
+ #### FIXED
10
+
11
+ - Shell escape environment variables
12
+
13
+ ### 1.0.21
14
+
15
+ 2024-07-10 12:18
16
+
17
+ #### NEW
18
+
19
+ - New filter `fixHeaders` will adapt all headlines in the document to be in semantic order
20
+
1
21
  ### 1.0.20
2
22
 
3
23
  2024-07-04 12:18
data/README.md CHANGED
@@ -163,6 +163,7 @@ The action can be `script`, `command`, or `filter`.
163
163
  | `prepend/appendCode(path)` | insert a file as a code block at beginning or end of content |
164
164
  | `insertCSS(path)` | insert custom CSS into document |
165
165
  | `autoLink()` | Turn bare URLs into \<self-linked\> urls |
166
+ | `fixHeaders()` | Reorganize headline levels to semantic order |
166
167
 
167
168
  For `replace` and `replaceAll`: If *search* is surrounded with forward slashes followed by optional flags (*i* for case-insensitive, *m* to make dot match newlines), e.g. `/contribut(ing)?/i`, it will be interpreted as a regular expression. The *replace* value can include numeric capture groups, e.g. `Follow$2`.
168
169
 
@@ -176,7 +177,9 @@ If the path for `insertScript` or `insertCSS` is a URL instead of a filename, th
176
177
 
177
178
  For all of the prepend/append file filters, you can store files in `~/.config/conductor/files` and reference them with just a filename. Otherwise a full path will be assumed.
178
179
 
179
- For `autoLink`, any URL that's not contained in parenthesis or following a `[]: url` pattern will be autolinked (surrounded by angle brackets). URLs must contain `//` to be recognized, but any protocol will work, e.g. `x-marked://refresh`.
180
+ For `autoLink`, any URL that's not contained in parenthesis or following a `[]: url` pattern will be autolinked (surrounded by angle brackets). URLs must contain `//` to be recognized, but any protocol will work, e.g. `x-marked://refresh`. Must be run on Markdown (prior to any postprocessor HTML conversion).
181
+
182
+ For `fixHeaders`, it will be ensured that the document has an h1, and all header levels will be adapted to never jump more than one header level when increasing. If no H1 exists in the document, the first header of the lowest existing level will be turned into an H1 and all other headers will be decremented to fit the hierarchy. It's not perfect, but it does a pretty good job. When saving the document as Markdown from Marked, the new headers will be applied. Must be run on Markdown (prior to any postprocessor HTML conversion).
180
183
 
181
184
  **Note:** successive filters in a sequence that insert or prepend will always insert content before/above the result of the previous insert filter. So if you have an `insertTitle` filter followed by an `insertCSS` filter, the CSS will appear above the inserted title. If you want elements inserted in reverse order, reverse the order of the inserts in the sequence.
182
185
 
data/buildnotes.md ADDED
@@ -0,0 +1,41 @@
1
+ template: git, gem, project
2
+ project: conductor
3
+ readme: src/_README.md
4
+
5
+ # marked-conductor
6
+
7
+ Train conductor for Marked
8
+
9
+ ## File Structure
10
+
11
+ Standard gem structure.
12
+
13
+ ## Deploy
14
+
15
+ You no longer need to manually bump the version, it will be incremented when this task runs.
16
+
17
+ ```run Update Changelog
18
+ #!/bin/bash
19
+
20
+ changelog -u
21
+ ```
22
+
23
+ @include(project:Update GitHub README)
24
+
25
+ ```run Commit with changelog
26
+ #!/bin/bash
27
+
28
+ changelog | git commit -a -F -
29
+ git pull
30
+ git push
31
+ ```
32
+
33
+ @include(gem:Release Gem) Release Gem
34
+ @include(project:Update Blog Project) Update Blog Project
35
+ @run(rake bump[patch]) Bump Version
36
+
37
+ @run(git commit -am 'Version bump')
38
+
39
+ @after
40
+ Don't forget to publish the website!
41
+ @end
data/lib/conductor/env.rb CHANGED
@@ -61,7 +61,7 @@ module Conductor
61
61
  "MARKED_PHASE" => @env[:phase],
62
62
  "OUTLINE" => @env[:outline],
63
63
  "PATH" => @env[:path]
64
- }.map { |k, v| %(#{k}="#{v}") }.join(" ")
64
+ }.map { |k, v| %(#{k}="#{Shellwords.escape(v)}") }.join(" ")
65
65
  end
66
66
  end
67
67
  end
@@ -69,12 +69,35 @@ class ::String
69
69
  first
70
70
  end
71
71
 
72
- def shift_headers(amt = 1)
73
- gsub(/^#/, "#{"#" * amt}#")
72
+ ##
73
+ ## Count the characters in a string
74
+ ##
75
+ ## @return [Integer] number of characters
76
+ ##
77
+ def chars
78
+ split(//).count
74
79
  end
75
80
 
76
- def shift_headers!(amt = 1)
77
- replace shift_headers(amt)
81
+ def decrease_headers(amt = 1)
82
+ gsub(/^(\#{1,6})(?!=#)/) do
83
+ m = Regexp.last_match
84
+ level = m[1].chars
85
+ level -= amt
86
+ level = 1 if level < 1
87
+ "#{"#" * level}"
88
+ end
89
+ end
90
+
91
+ def decrease_headers!(amt = 1)
92
+ replace decrease_headers(amt)
93
+ end
94
+
95
+ def increase_headers(amt = 1)
96
+ gsub(/^#/, "#{"#" * amt}#").gsub(/^\#{7,}/, '######')
97
+ end
98
+
99
+ def increase_headers!(amt = 1)
100
+ replace increase_headers(amt)
78
101
  end
79
102
 
80
103
  def insert_toc(max = nil, after = :h1)
@@ -101,7 +124,7 @@ class ::String
101
124
  end
102
125
 
103
126
  def insert_stylesheet(path)
104
- inject_after_meta(%(<link rel="stylesheet" href="#{path.strip}">))
127
+ inject_after_meta(%(<link rel="stylesheet" href="#{ERB::Util.url_encode(path.strip)}">))
105
128
  end
106
129
 
107
130
  def insert_css(path)
@@ -177,7 +200,7 @@ class ::String
177
200
  end
178
201
 
179
202
  def insert_javascript(path)
180
- %(#{self}\n<script type="javascript" src="#{path.strip}"></script>\n)
203
+ %(#{self}\n<script type="javascript" src="#{ERB::Util.url_encode(path.strip)}"></script>\n)
181
204
  end
182
205
 
183
206
  def insert_raw_javascript(content)
@@ -245,7 +268,7 @@ class ::String
245
268
  def insert_title(shift: 0)
246
269
  content = dup
247
270
  title = get_title
248
- content.shift_headers!(shift) if shift.positive?
271
+ content.increase_headers!(shift) if shift.positive?
249
272
  lines = content.split(/\n/)
250
273
  insert_point = content.meta_insert_point
251
274
  insert_at = insert_point.positive? ? insert_point + 1 : 0
@@ -342,6 +365,121 @@ class ::String
342
365
  '<\1>')
343
366
  end
344
367
 
368
+ ##
369
+ ## Count the number of h1 headers in the document
370
+ ##
371
+ ## @return Number of h1s.
372
+ ##
373
+ def count_h1s
374
+ scan(/^#[^#]/).count
375
+ end
376
+
377
+ ##
378
+ ## Normalize Setext headers to ATX
379
+ ##
380
+ ## @return [String] content with headers updated
381
+ ##
382
+ def normalize_headers
383
+ gsub(/^(\S.*)\n([=-]+)\n/) do
384
+ m = Regexp.last_match
385
+ case m[2]
386
+ when /\=/
387
+ "# #{m[1]}\n\n"
388
+ else
389
+ "## #{m[1]}\n\n"
390
+ end
391
+ end
392
+ end
393
+
394
+ def normalize_headers!
395
+ replace normalize_headers
396
+ end
397
+
398
+ ##
399
+ ## Ensure there's at least 1 h1 in the document
400
+ ##
401
+ ## If no h1 is found, converts the lowest level header (first one) into an h1
402
+ ##
403
+ ## @return [String] content with at least 1 h1
404
+ ##
405
+ def ensure_h1
406
+ headers = to_enum(:scan, /(\#{1,6})([^#].*?)$/m).map { Regexp.last_match }
407
+ return self if headers.select { |h| h[1].chars == 1 }.count.positive?
408
+
409
+ lowest_header = headers.min_by { |h| h[1].chars }
410
+ level = lowest_header[1].chars
411
+
412
+ sub(/#{Regexp.escape(lowest_header[0])}/, "# #{lowest_header[2].strip}").decrease_headers(level)
413
+ end
414
+
415
+ def ensure_h1!
416
+ replace ensure_h1
417
+ end
418
+
419
+ ##
420
+ ## Bump all headers except for first H1
421
+ ##
422
+ ## @return Content with adjusted headers
423
+ ##
424
+ def fix_headers
425
+ return self if count_h1s == 1
426
+
427
+ first_h1 = true
428
+
429
+ gsub(%r/^(\#{1,6})([^#].*?)$/m) do
430
+ m = Regexp.last_match
431
+ level = m[1].chars
432
+ content = m[2].strip
433
+ if level == 1 && first_h1
434
+ first_h1 = false
435
+ m[0]
436
+ else
437
+ level += 1 if level < 6
438
+
439
+ "#{"#" * level} #{content}"
440
+ end
441
+ end
442
+ end
443
+
444
+ def fix_headers!
445
+ replace fix_headers
446
+ end
447
+
448
+ ##
449
+ ## Adjust header levels so there's no jump greater than 1
450
+ ##
451
+ ## @return Content with adjusted headers
452
+ ##
453
+ def fix_hierarchy
454
+ normalize_headers!
455
+ ensure_h1!
456
+ fix_headers!
457
+ headers = to_enum(:scan, /(\#{1,6})([^#].*?)$/m).map { Regexp.last_match }
458
+ content = dup
459
+ last = 1
460
+ headers.each do |h|
461
+ level = h[1].chars
462
+ if level <= last + 1
463
+ last = level
464
+ next
465
+ end
466
+
467
+ level = last + 1
468
+ content.sub!(/#{Regexp.escape(h[0])}/, "#{"#" * level} #{h[2].strip}")
469
+ end
470
+
471
+ content
472
+ end
473
+
474
+ ##
475
+ ## Convert a string to a regular expression
476
+ ##
477
+ ## If the string matches /xxx/, it will be interpreted
478
+ ## directly as a regex. Otherwise it will be escaped and
479
+ ## converted to regex.
480
+ ##
481
+ ## @return [Regexp] Regexp representation of the string.
482
+ ##
345
483
  def to_rx
346
484
  if self =~ %r{^/(.*?)/([im]+)?$}
347
485
  m = Regexp.last_match
@@ -353,6 +491,11 @@ class ::String
353
491
  end
354
492
  end
355
493
 
494
+ ##
495
+ ## Convert a string containing $1, $2 to a Regexp replace pattern
496
+ ##
497
+ ## @return [String] Pattern representation of the object.
498
+ ##
356
499
  def to_pattern
357
500
  gsub(/\$(\d+)/, '\\\\\1').gsub(/(^["']|["']$)/, "")
358
501
  end
@@ -445,6 +588,8 @@ class Filter < String
445
588
  content.replace_one(@params[0], @params[1])
446
589
  when /(auto|self)link/
447
590
  content.autolink
591
+ when /fix(head(lines|ers)|hierarchy)/
592
+ content.fix_hierarchy
448
593
  end
449
594
  end
450
595
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Conductor
4
- VERSION = '1.0.20'
4
+ VERSION = '1.0.22'
5
5
  end
data/lib/conductor.rb CHANGED
@@ -7,6 +7,7 @@ require "fcntl"
7
7
  require "time"
8
8
  require "chronic"
9
9
  require "fileutils"
10
+ require "erb"
10
11
  require_relative "conductor/version"
11
12
  require_relative "conductor/env"
12
13
  require_relative "conductor/config"
data/src/_README.md CHANGED
@@ -163,6 +163,7 @@ The action can be `script`, `command`, or `filter`.
163
163
  | `prepend/appendCode(path)` | insert a file as a code block at beginning or end of content |
164
164
  | `insertCSS(path)` | insert custom CSS into document |
165
165
  | `autoLink()` | Turn bare URLs into \<self-linked\> urls |
166
+ | `fixHeaders()` | Reorganize headline levels to semantic order |
166
167
 
167
168
  For `replace` and `replaceAll`: If *search* is surrounded with forward slashes followed by optional flags (*i* for case-insensitive, *m* to make dot match newlines), e.g. `/contribut(ing)?/i`, it will be interpreted as a regular expression. The *replace* value can include numeric capture groups, e.g. `Follow$2`.
168
169
 
@@ -176,7 +177,9 @@ If the path for `insertScript` or `insertCSS` is a URL instead of a filename, th
176
177
 
177
178
  For all of the prepend/append file filters, you can store files in `~/.config/conductor/files` and reference them with just a filename. Otherwise a full path will be assumed.
178
179
 
179
- For `autoLink`, any URL that's not contained in parenthesis or following a `[]: url` pattern will be autolinked (surrounded by angle brackets). URLs must contain `//` to be recognized, but any protocol will work, e.g. `x-marked://refresh`.
180
+ For `autoLink`, any URL that's not contained in parenthesis or following a `[]: url` pattern will be autolinked (surrounded by angle brackets). URLs must contain `//` to be recognized, but any protocol will work, e.g. `x-marked://refresh`. Must be run on Markdown (prior to any postprocessor HTML conversion).
181
+
182
+ For `fixHeaders`, it will be ensured that the document has an h1, and all header levels will be adapted to never jump more than one header level when increasing. If no H1 exists in the document, the first header of the lowest existing level will be turned into an H1 and all other headers will be decremented to fit the hierarchy. It's not perfect, but it does a pretty good job. When saving the document as Markdown from Marked, the new headers will be applied. Must be run on Markdown (prior to any postprocessor HTML conversion).
180
183
 
181
184
  **Note:** successive filters in a sequence that insert or prepend will always insert content before/above the result of the previous insert filter. So if you have an `insertTitle` filter followed by an `insertCSS` filter, the CSS will appear above the inserted title. If you want elements inserted in reverse order, reverse the order of the inserts in the sequence.
182
185
 
data/test.sh CHANGED
@@ -49,7 +49,7 @@ if [[ -z $1 ]]; then
49
49
  exit 1
50
50
  fi
51
51
 
52
- FILE=$(realpath $1)
52
+ FILE=$(realpath "$1")
53
53
  FILENAME=$(basename -- "$FILE")
54
54
  EXTENSION="${FILENAME##*.}"
55
55
  PHASE=$(echo $PHASE | tr [a-z] [A-Z])
@@ -67,7 +67,7 @@ export MARKED_EXT="$EXTENSION"
67
67
  export MARKED_CSS_PATH="/Applications/Marked 2.app/Contents/Resources/swiss.css"
68
68
  export PATH="$PATH:$(dirname "$FILE")"
69
69
  export MARKED_PATH="$FILE"
70
- export MARKED_INCLUDES=""
70
+ export MARKED_INCLUDES="/Applications/Marked 2.app/Contents/Resources/tocstyle.css"
71
71
  export MARKED_PHASE="$PHASE"
72
72
 
73
73
  if [[ $STD =~ ^(STD)?E ]]; then
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marked-conductor
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.20
4
+ version: 1.0.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-04 00:00:00.000000000 Z
11
+ date: 2024-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_print
@@ -204,6 +204,7 @@ files:
204
204
  - README.rdoc
205
205
  - Rakefile
206
206
  - bin/conductor
207
+ - buildnotes.md
207
208
  - images/preferences.jpg
208
209
  - lib/conductor.rb
209
210
  - lib/conductor/array.rb
@@ -243,7 +244,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
243
244
  - !ruby/object:Gem::Version
244
245
  version: '0'
245
246
  requirements: []
246
- rubygems_version: 3.2.16
247
+ rubygems_version: 3.2.15
247
248
  signing_key:
248
249
  specification_version: 4
249
250
  summary: A custom processor manager for Marked 2 (Mac)