@jdeighan/coffee-utils 9.0.3 → 10.0.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jdeighan/coffee-utils",
3
3
  "type": "module",
4
- "version": "9.0.3",
4
+ "version": "10.0.0",
5
5
  "description": "A set of utility functions for CoffeeScript",
6
6
  "main": "coffee_utils.js",
7
7
  "exports": {
@@ -2,18 +2,22 @@
2
2
 
3
3
  import {assert, error, croak} from '@jdeighan/unit-tester/utils'
4
4
  import {
5
- pass, undef, defined, isArray,
5
+ pass, undef, defined, isArray, isEmpty,
6
6
  } from '@jdeighan/coffee-utils'
7
7
  import {arrayToBlock} from '@jdeighan/coffee-utils/block'
8
8
  import {indented} from '@jdeighan/coffee-utils/indent'
9
+ import {debug} from '@jdeighan/coffee-utils/debug'
9
10
 
10
11
  # ---------------------------------------------------------------------------
11
12
 
12
13
  export class Section
13
14
 
14
- constructor: (@name) ->
15
+ constructor: (@name, content=undef) ->
16
+ # --- name can be undef or empty
15
17
 
16
18
  @lParts = []
19
+ if defined(content)
20
+ @lParts.push content
17
21
 
18
22
  # ..........................................................
19
23
 
@@ -23,10 +27,10 @@ export class Section
23
27
 
24
28
  # ..........................................................
25
29
 
26
- indent: (level=1) ->
30
+ indent: (level=1, oneIndent="\t") ->
27
31
 
28
32
  lNewLines = for line in @lParts
29
- indented(line, level)
33
+ indented(line, level, oneIndent)
30
34
  @lParts = lNewLines
31
35
  return
32
36
 
@@ -73,7 +77,11 @@ export class Section
73
77
 
74
78
  getBlock: () ->
75
79
 
80
+ debug "enter Section.getBlock()"
76
81
  if (@lParts.length == 0)
82
+ debug "return undef from Section.getBlock()"
77
83
  return undef
78
84
  else
79
- return arrayToBlock(@lParts)
85
+ result = arrayToBlock(@lParts)
86
+ debug "return from Section.getBlock()", result
87
+ return result
package/src/Section.js CHANGED
@@ -10,7 +10,8 @@ import {
10
10
  pass,
11
11
  undef,
12
12
  defined,
13
- isArray
13
+ isArray,
14
+ isEmpty
14
15
  } from '@jdeighan/coffee-utils';
15
16
 
16
17
  import {
@@ -21,11 +22,19 @@ import {
21
22
  indented
22
23
  } from '@jdeighan/coffee-utils/indent';
23
24
 
25
+ import {
26
+ debug
27
+ } from '@jdeighan/coffee-utils/debug';
28
+
24
29
  // ---------------------------------------------------------------------------
25
30
  export var Section = class Section {
26
- constructor(name) {
31
+ constructor(name, content = undef) {
27
32
  this.name = name;
33
+ // --- name can be undef or empty
28
34
  this.lParts = [];
35
+ if (defined(content)) {
36
+ this.lParts.push(content);
37
+ }
29
38
  }
30
39
 
31
40
  // ..........................................................
@@ -34,7 +43,7 @@ export var Section = class Section {
34
43
  }
35
44
 
36
45
  // ..........................................................
37
- indent(level = 1) {
46
+ indent(level = 1, oneIndent = "\t") {
38
47
  var lNewLines, line;
39
48
  lNewLines = (function() {
40
49
  var i, len, ref, results;
@@ -42,7 +51,7 @@ export var Section = class Section {
42
51
  results = [];
43
52
  for (i = 0, len = ref.length; i < len; i++) {
44
53
  line = ref[i];
45
- results.push(indented(line, level));
54
+ results.push(indented(line, level, oneIndent));
46
55
  }
47
56
  return results;
48
57
  }).call(this);
@@ -88,10 +97,15 @@ export var Section = class Section {
88
97
 
89
98
  // ..........................................................
90
99
  getBlock() {
100
+ var result;
101
+ debug("enter Section.getBlock()");
91
102
  if (this.lParts.length === 0) {
103
+ debug("return undef from Section.getBlock()");
92
104
  return undef;
93
105
  } else {
94
- return arrayToBlock(this.lParts);
106
+ result = arrayToBlock(this.lParts);
107
+ debug("return from Section.getBlock()", result);
108
+ return result;
95
109
  }
96
110
  }
97
111
 
@@ -2,12 +2,13 @@
2
2
 
3
3
  import {assert, error, croak} from '@jdeighan/unit-tester/utils'
4
4
  import {
5
- pass, undef, defined, OL, isEmpty, nonEmpty,
5
+ pass, undef, defined, notdefined, OL, isEmpty, nonEmpty,
6
6
  isString, isHash, isArray, isUniqueTree, isNonEmptyString,
7
7
  isNonEmptyArray,
8
8
  } from '@jdeighan/coffee-utils'
9
9
  import {arrayToBlock} from '@jdeighan/coffee-utils/block'
10
10
  import {indented} from '@jdeighan/coffee-utils/indent'
11
+ import {LOG} from '@jdeighan/coffee-utils/log'
11
12
  import {debug} from '@jdeighan/coffee-utils/debug'
12
13
  import {Section} from '@jdeighan/coffee-utils/section'
13
14
 
@@ -15,147 +16,126 @@ import {Section} from '@jdeighan/coffee-utils/section'
15
16
 
16
17
  isSectionName = (name) ->
17
18
 
18
- return isString(name) && name.match(/^[a-z][a-z0-9]*/)
19
+ return isString(name) && name.match(/^[a-z][a-z0-9_]*/)
19
20
 
20
21
  # ---------------------------------------------------------------------------
21
22
 
22
23
  isSetName = (name) ->
23
24
 
24
- return isString(name) && name.match(/^[A-Z][a-z0-9]*/)
25
+ return isString(name) && name.match(/^[A-Z][a-z0-9_]*/)
25
26
 
26
27
  # ---------------------------------------------------------------------------
27
28
 
28
29
  export class SectionMap
29
30
 
30
- constructor: (lSectionTree) ->
31
- # --- lSectionTree is a tree of section names
31
+ constructor: (@lSectionTree) ->
32
+ # --- lSectionTree is a tree of section/set names
32
33
 
33
- debug "enter SectionMap()", lSectionTree
34
- @lSectionTree = lSectionTree # a tree of section names
35
- @hSets = {}
34
+ debug "enter SectionMap()", @lSectionTree
35
+ assert isArray(@lSectionTree), "not an array"
36
+
37
+ # --- keys are section names, values are Section objects
36
38
  @hSections = {}
37
- @addSections lSectionTree
39
+
40
+ # --- keys are set names, values are subtrees of lSectionTree
41
+ @hSets = {}
42
+
43
+ @build @lSectionTree
38
44
  debug "return from SectionMap()", @hSections
39
45
 
40
46
  # ..........................................................
41
47
 
42
- addSections: (desc) ->
43
- # --- returns a flat array of sections that were added
44
-
45
- if isString(desc)
46
- assert nonEmpty(desc), "empty section name"
47
- assert isSectionName(desc), "bad section name #{OL(desc)}"
48
- assert (@hSections[desc] == undef), "duplicate section #{OL(desc)}"
49
- @hSections[desc] = new Section(desc)
50
- return [desc]
48
+ build: (lTree) ->
49
+
50
+ debug "enter build()", lTree
51
+ assert isArray(lTree), "not an array"
52
+ assert nonEmpty(lTree), "empty array"
53
+
54
+ firstItem = lTree[0]
55
+ if isSetName(firstItem)
56
+ assert (lTree.length >= 2), "set without sections"
57
+ @hSets[firstItem] = lTree
58
+ for item in lTree.slice(1)
59
+ if isArray(item)
60
+ @build item
61
+ else if isSectionName(item)
62
+ @hSections[item] = new Section(item)
63
+ else if ! isString(item) # string would be literal
64
+ croak "Bad section tree: #{OL(@lSectionTree)}"
51
65
  else
52
- assert isArray(desc), "not an array or string #{OL(desc)}"
53
- name = undef
54
- lParts = []
55
- for item,i in desc
56
- if (i==0) && isSetName(item)
57
- name = item
66
+ for item in lTree
67
+ if isArray(item)
68
+ @build item
69
+ else if isSectionName(item)
70
+ @hSections[item] = new Section(item)
58
71
  else
59
- lAdded = @addSections item
60
- for item in lAdded
61
- lParts.push item
62
- if defined(name)
63
- @addSet name, lParts
64
- return lParts
72
+ croak "Bad section tree: #{OL(@lSectionTree)}"
73
+ debug "return from build()", @hSections, @hSets
65
74
  return
66
75
 
67
76
  # ..........................................................
77
+ # --- hProc should be <name> -> <function>
78
+ # <name> can be a section name or a set name
79
+ # <function> should be <block> -> <block>
80
+ # --- desc can be:
81
+ # an array, which may begin with a set name
82
+ # a section name
83
+ # a set name
84
+ # undef (equivalent to being set to @SectionTree)
68
85
 
69
- addSet: (name, lSectionTree) ->
70
-
71
- debug "enter addSet()", name, lSectionTree
72
-
73
- # --- check the name
74
- assert isSetName(name), "not a valid set name #{OL(name)}"
75
-
76
- # --- check lSectionTree
77
- assert isArray(lSectionTree), "arg 2 not an array"
78
- for secName in lSectionTree
79
- assert isNonEmptyString(secName),
80
- "not a non-empty string #{OL(secName)}"
81
- assert defined(@hSections[secName]),
82
- "not a section name #{OL(secName)}"
86
+ getBlock: (desc=undef, hProcs={}) ->
83
87
 
84
- @hSets[name] = lSectionTree
85
- debug 'hSets', @hSets
86
- debug "return from addSet()"
87
- return
88
-
89
- # ..........................................................
90
- # --- sections returned in depth-first order from section tree
91
- # Set names are simply skipped
92
- # yields: [<level>, <section>]
88
+ debug "enter SectionMap.getBlock()", desc, hProcs
93
89
 
94
- allSections: (desc=undef, level=0) ->
95
-
96
- debug "enter allSections()", desc, level
97
- if (desc == undef)
90
+ if notdefined(desc)
98
91
  desc = @lSectionTree
92
+
99
93
  if isArray(desc)
94
+ lBlocks = []
95
+ setName = undef
100
96
  for item in desc
101
- if isSectionName(item)
102
- result = [level, @section(item)]
103
- debug 'yield', result
104
- yield result
97
+ if isArray(item) || isSectionName(item)
98
+ # --- arrayToBlock() will skip undef items
99
+ # so, no need to check for undef block
100
+ lBlocks.push @getBlock(item, hProcs)
105
101
  else if isSetName(item)
106
- pass
102
+ setName = item
103
+ else if isString(item)
104
+ lBlocks.push item # a literal string
107
105
  else
108
- assert isArray(item), "not an array #{OL(item)}"
109
- yield from @allSections(item, level+1)
106
+ croak "Bad item: #{OL(item)}"
107
+ block = arrayToBlock(lBlocks)
110
108
  else if isSectionName(desc)
111
- result = [level, @section(desc)]
112
- debug 'yield', result
113
- yield result
109
+ block = @section(desc).getBlock()
110
+ if defined(proc = hProcs[desc])
111
+ block = proc(block)
114
112
  else if isSetName(desc)
115
- lTree = @hSets[desc]
116
- assert defined(lTree), "Not a Set: #{OL(desc)}"
117
- yield from @allSections(lTree, level)
113
+ # --- pass array to getBlock()
114
+ block = @getBlock(@hSets[desc], hProcs)
115
+ if defined(proc = hProcs[desc])
116
+ block = proc(block)
118
117
  else
119
- croak "Bad item: #{OL(desc)}"
120
- debug "return from allSections()"
121
- return
118
+ croak "Bad 1st arg: #{OL(desc)}"
119
+ debug "return from SectionMap.getBlock()", block
120
+ return block
122
121
 
123
122
  # ..........................................................
124
- # --- procFunc should be (name, block) ->
125
- # return processed block
126
123
 
127
- getBlock: (procFunc=undef, lTree=undef) ->
124
+ isEmpty: () ->
128
125
 
129
- debug "enter getBlock()"
130
- if (lTree == undef)
131
- lTree = @lSectionTree
132
- else
133
- assert isArray(lTree), "not an array #{OL(lTree)}"
134
-
135
- lParts = []
136
- for part in lTree
137
- if isString(part)
138
- text = @section(part).getBlock()
139
- if nonEmpty(text) && defined(procFunc)
140
- text = procFunc(part, text)
141
- else if isNonEmptyArray(part)
142
- if isSectionName(part[0])
143
- text = @getBlock(procFunc, part)
144
- else if isSetName(part[0])
145
- text = @getBlock(procFunc, part.slice(1))
146
- if nonEmpty(text) && defined(procFunc)
147
- text = procFunc(part[0], text)
148
- else
149
- croak "Bad part: #{OL(part)}"
150
- else
151
- croak "Bad part: #{OL(part)}"
152
- if defined(text)
153
- lParts.push text
126
+ for name,sect of @hSections
127
+ if sect.nonEmpty()
128
+ return false
129
+ return true
130
+
131
+ # ..........................................................
132
+
133
+ nonEmpty: () ->
154
134
 
155
- debug 'lParts', lParts
156
- result = arrayToBlock(lParts)
157
- debug "return from getBlock()", result
158
- return result
135
+ for name,sect of @hSections
136
+ if sect.nonEmpty()
137
+ return true
138
+ return false
159
139
 
160
140
  # ..........................................................
161
141
 
@@ -170,50 +150,15 @@ export class SectionMap
170
150
  firstSection: (name) ->
171
151
 
172
152
  assert isSetName(name), "bad set name #{OL(name)}"
173
- lSectionTree = @hSets[name]
174
- assert defined(lSectionTree), "no such set #{OL(name)}"
175
- assert nonEmpty(lSectionTree), "empty section #{OL(name)}"
176
- return @section(lSectionTree[0])
153
+ lSubTree = @hSets[name]
154
+ assert defined(lSubTree), "no such set #{OL(name)}"
155
+ return @section(lSubTree[1])
177
156
 
178
157
  # ..........................................................
179
158
 
180
159
  lastSection: (name) ->
181
160
 
182
161
  assert isSetName(name), "bad set name #{OL(name)}"
183
- lSectionTree = @hSets[name]
184
- assert defined(lSectionTree), "no such set #{OL(name)}"
185
- assert nonEmpty(lSectionTree), "empty section #{OL(name)}"
186
- return @section(lSectionTree[lSectionTree.length - 1])
187
-
188
- # ..........................................................
189
-
190
- length: (desc=undef) ->
191
-
192
- result = 0
193
- for [_, sect] from @allSections(desc)
194
- result += sect.length()
195
- return result
196
-
197
- # ..........................................................
198
-
199
- isEmpty: (desc=undef) ->
200
-
201
- return (@length(desc) == 0)
202
-
203
- # ..........................................................
204
-
205
- nonEmpty: (desc=undef) ->
206
-
207
- return (@length(desc) > 0)
208
-
209
- # ..........................................................
210
-
211
- getShape: () ->
212
-
213
- debug "enter getShape()"
214
- lParts = []
215
- for [level, sect] from @allSections()
216
- lParts.push indented(sect.name, level)
217
- result = arrayToBlock(lParts)
218
- debug "return from getShape()", result
219
- return result
162
+ lSubTree = @hSets[name]
163
+ assert defined(lSubTree), "no such set #{OL(name)}"
164
+ return @section(lSubTree[lSubTree.length - 1])
package/src/SectionMap.js CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  pass,
13
13
  undef,
14
14
  defined,
15
+ notdefined,
15
16
  OL,
16
17
  isEmpty,
17
18
  nonEmpty,
@@ -31,6 +32,10 @@ import {
31
32
  indented
32
33
  } from '@jdeighan/coffee-utils/indent';
33
34
 
35
+ import {
36
+ LOG
37
+ } from '@jdeighan/coffee-utils/log';
38
+
34
39
  import {
35
40
  debug
36
41
  } from '@jdeighan/coffee-utils/debug';
@@ -41,156 +46,140 @@ import {
41
46
 
42
47
  // ---------------------------------------------------------------------------
43
48
  isSectionName = function(name) {
44
- return isString(name) && name.match(/^[a-z][a-z0-9]*/);
49
+ return isString(name) && name.match(/^[a-z][a-z0-9_]*/);
45
50
  };
46
51
 
47
52
  // ---------------------------------------------------------------------------
48
53
  isSetName = function(name) {
49
- return isString(name) && name.match(/^[A-Z][a-z0-9]*/);
54
+ return isString(name) && name.match(/^[A-Z][a-z0-9_]*/);
50
55
  };
51
56
 
52
57
  // ---------------------------------------------------------------------------
53
58
  export var SectionMap = class SectionMap {
54
59
  constructor(lSectionTree) {
55
- // --- lSectionTree is a tree of section names
56
- debug("enter SectionMap()", lSectionTree);
57
- this.lSectionTree = lSectionTree; // a tree of section names
58
- this.hSets = {};
60
+ this.lSectionTree = lSectionTree;
61
+ // --- lSectionTree is a tree of section/set names
62
+ debug("enter SectionMap()", this.lSectionTree);
63
+ assert(isArray(this.lSectionTree), "not an array");
64
+ // --- keys are section names, values are Section objects
59
65
  this.hSections = {};
60
- this.addSections(lSectionTree);
66
+ // --- keys are set names, values are subtrees of lSectionTree
67
+ this.hSets = {};
68
+ this.build(this.lSectionTree);
61
69
  debug("return from SectionMap()", this.hSections);
62
70
  }
63
71
 
64
72
  // ..........................................................
65
- addSections(desc) {
66
- var i, item, j, k, lAdded, lParts, len, len1, name;
67
- // --- returns a flat array of sections that were added
68
- if (isString(desc)) {
69
- assert(nonEmpty(desc), "empty section name");
70
- assert(isSectionName(desc), `bad section name ${OL(desc)}`);
71
- assert(this.hSections[desc] === undef, `duplicate section ${OL(desc)}`);
72
- this.hSections[desc] = new Section(desc);
73
- return [desc];
73
+ build(lTree) {
74
+ var firstItem, i, item, j, len, len1, ref;
75
+ debug("enter build()", lTree);
76
+ assert(isArray(lTree), "not an array");
77
+ assert(nonEmpty(lTree), "empty array");
78
+ firstItem = lTree[0];
79
+ if (isSetName(firstItem)) {
80
+ assert(lTree.length >= 2, "set without sections");
81
+ this.hSets[firstItem] = lTree;
82
+ ref = lTree.slice(1);
83
+ for (i = 0, len = ref.length; i < len; i++) {
84
+ item = ref[i];
85
+ if (isArray(item)) {
86
+ this.build(item);
87
+ } else if (isSectionName(item)) {
88
+ this.hSections[item] = new Section(item);
89
+ } else if (!isString(item)) { // string would be literal
90
+ croak(`Bad section tree: ${OL(this.lSectionTree)}`);
91
+ }
92
+ }
74
93
  } else {
75
- assert(isArray(desc), `not an array or string ${OL(desc)}`);
76
- name = undef;
77
- lParts = [];
78
- for (i = j = 0, len = desc.length; j < len; i = ++j) {
79
- item = desc[i];
80
- if ((i === 0) && isSetName(item)) {
81
- name = item;
94
+ for (j = 0, len1 = lTree.length; j < len1; j++) {
95
+ item = lTree[j];
96
+ if (isArray(item)) {
97
+ this.build(item);
98
+ } else if (isSectionName(item)) {
99
+ this.hSections[item] = new Section(item);
82
100
  } else {
83
- lAdded = this.addSections(item);
84
- for (k = 0, len1 = lAdded.length; k < len1; k++) {
85
- item = lAdded[k];
86
- lParts.push(item);
87
- }
101
+ croak(`Bad section tree: ${OL(this.lSectionTree)}`);
88
102
  }
89
103
  }
90
- if (defined(name)) {
91
- this.addSet(name, lParts);
92
- }
93
- return lParts;
94
104
  }
105
+ debug("return from build()", this.hSections, this.hSets);
95
106
  }
96
107
 
97
108
  // ..........................................................
98
- addSet(name, lSectionTree) {
99
- var j, len, secName;
100
- debug("enter addSet()", name, lSectionTree);
101
- // --- check the name
102
- assert(isSetName(name), `not a valid set name ${OL(name)}`);
103
- // --- check lSectionTree
104
- assert(isArray(lSectionTree), "arg 2 not an array");
105
- for (j = 0, len = lSectionTree.length; j < len; j++) {
106
- secName = lSectionTree[j];
107
- assert(isNonEmptyString(secName), `not a non-empty string ${OL(secName)}`);
108
- assert(defined(this.hSections[secName]), `not a section name ${OL(secName)}`);
109
- }
110
- this.hSets[name] = lSectionTree;
111
- debug('hSets', this.hSets);
112
- debug("return from addSet()");
113
- }
114
-
115
- // ..........................................................
116
- // --- sections returned in depth-first order from section tree
117
- // Set names are simply skipped
118
- // yields: [<level>, <section>]
119
- * allSections(desc = undef, level = 0) {
120
- var item, j, lTree, len, result;
121
- debug("enter allSections()", desc, level);
122
- if (desc === undef) {
109
+ // --- hProc should be <name> -> <function>
110
+ // <name> can be a section name or a set name
111
+ // <function> should be <block> -> <block>
112
+ // --- desc can be:
113
+ // an array, which may begin with a set name
114
+ // a section name
115
+ // a set name
116
+ // undef (equivalent to being set to @SectionTree)
117
+ getBlock(desc = undef, hProcs = {}) {
118
+ var block, i, item, lBlocks, len, proc, setName;
119
+ debug("enter SectionMap.getBlock()", desc, hProcs);
120
+ if (notdefined(desc)) {
123
121
  desc = this.lSectionTree;
124
122
  }
125
123
  if (isArray(desc)) {
126
- for (j = 0, len = desc.length; j < len; j++) {
127
- item = desc[j];
128
- if (isSectionName(item)) {
129
- result = [level, this.section(item)];
130
- debug('yield', result);
131
- yield result;
124
+ lBlocks = [];
125
+ setName = undef;
126
+ for (i = 0, len = desc.length; i < len; i++) {
127
+ item = desc[i];
128
+ if (isArray(item) || isSectionName(item)) {
129
+ // --- arrayToBlock() will skip undef items
130
+ // so, no need to check for undef block
131
+ lBlocks.push(this.getBlock(item, hProcs));
132
132
  } else if (isSetName(item)) {
133
- pass;
133
+ setName = item;
134
+ } else if (isString(item)) {
135
+ lBlocks.push(item); // a literal string
134
136
  } else {
135
- assert(isArray(item), `not an array ${OL(item)}`);
136
- yield* this.allSections(item, level + 1);
137
+ croak(`Bad item: ${OL(item)}`);
137
138
  }
138
139
  }
140
+ block = arrayToBlock(lBlocks);
139
141
  } else if (isSectionName(desc)) {
140
- result = [level, this.section(desc)];
141
- debug('yield', result);
142
- yield result;
142
+ block = this.section(desc).getBlock();
143
+ if (defined(proc = hProcs[desc])) {
144
+ block = proc(block);
145
+ }
143
146
  } else if (isSetName(desc)) {
144
- lTree = this.hSets[desc];
145
- assert(defined(lTree), `Not a Set: ${OL(desc)}`);
146
- yield* this.allSections(lTree, level);
147
+ // --- pass array to getBlock()
148
+ block = this.getBlock(this.hSets[desc], hProcs);
149
+ if (defined(proc = hProcs[desc])) {
150
+ block = proc(block);
151
+ }
147
152
  } else {
148
- croak(`Bad item: ${OL(desc)}`);
153
+ croak(`Bad 1st arg: ${OL(desc)}`);
149
154
  }
150
- debug("return from allSections()");
155
+ debug("return from SectionMap.getBlock()", block);
156
+ return block;
151
157
  }
152
158
 
153
159
  // ..........................................................
154
- // --- procFunc should be (name, block) ->
155
- // return processed block
156
- getBlock(procFunc = undef, lTree = undef) {
157
- var j, lParts, len, part, result, text;
158
- debug("enter getBlock()");
159
- if (lTree === undef) {
160
- lTree = this.lSectionTree;
161
- } else {
162
- assert(isArray(lTree), `not an array ${OL(lTree)}`);
163
- }
164
- lParts = [];
165
- for (j = 0, len = lTree.length; j < len; j++) {
166
- part = lTree[j];
167
- if (isString(part)) {
168
- text = this.section(part).getBlock();
169
- if (nonEmpty(text) && defined(procFunc)) {
170
- text = procFunc(part, text);
171
- }
172
- } else if (isNonEmptyArray(part)) {
173
- if (isSectionName(part[0])) {
174
- text = this.getBlock(procFunc, part);
175
- } else if (isSetName(part[0])) {
176
- text = this.getBlock(procFunc, part.slice(1));
177
- if (nonEmpty(text) && defined(procFunc)) {
178
- text = procFunc(part[0], text);
179
- }
180
- } else {
181
- croak(`Bad part: ${OL(part)}`);
182
- }
183
- } else {
184
- croak(`Bad part: ${OL(part)}`);
160
+ isEmpty() {
161
+ var name, ref, sect;
162
+ ref = this.hSections;
163
+ for (name in ref) {
164
+ sect = ref[name];
165
+ if (sect.nonEmpty()) {
166
+ return false;
185
167
  }
186
- if (defined(text)) {
187
- lParts.push(text);
168
+ }
169
+ return true;
170
+ }
171
+
172
+ // ..........................................................
173
+ nonEmpty() {
174
+ var name, ref, sect;
175
+ ref = this.hSections;
176
+ for (name in ref) {
177
+ sect = ref[name];
178
+ if (sect.nonEmpty()) {
179
+ return true;
188
180
  }
189
181
  }
190
- debug('lParts', lParts);
191
- result = arrayToBlock(lParts);
192
- debug("return from getBlock()", result);
193
- return result;
182
+ return false;
194
183
  }
195
184
 
196
185
  // ..........................................................
@@ -203,59 +192,20 @@ export var SectionMap = class SectionMap {
203
192
 
204
193
  // ..........................................................
205
194
  firstSection(name) {
206
- var lSectionTree;
195
+ var lSubTree;
207
196
  assert(isSetName(name), `bad set name ${OL(name)}`);
208
- lSectionTree = this.hSets[name];
209
- assert(defined(lSectionTree), `no such set ${OL(name)}`);
210
- assert(nonEmpty(lSectionTree), `empty section ${OL(name)}`);
211
- return this.section(lSectionTree[0]);
197
+ lSubTree = this.hSets[name];
198
+ assert(defined(lSubTree), `no such set ${OL(name)}`);
199
+ return this.section(lSubTree[1]);
212
200
  }
213
201
 
214
202
  // ..........................................................
215
203
  lastSection(name) {
216
- var lSectionTree;
204
+ var lSubTree;
217
205
  assert(isSetName(name), `bad set name ${OL(name)}`);
218
- lSectionTree = this.hSets[name];
219
- assert(defined(lSectionTree), `no such set ${OL(name)}`);
220
- assert(nonEmpty(lSectionTree), `empty section ${OL(name)}`);
221
- return this.section(lSectionTree[lSectionTree.length - 1]);
222
- }
223
-
224
- // ..........................................................
225
- length(desc = undef) {
226
- var _, ref, result, sect, x;
227
- result = 0;
228
- ref = this.allSections(desc);
229
- for (x of ref) {
230
- [_, sect] = x;
231
- result += sect.length();
232
- }
233
- return result;
234
- }
235
-
236
- // ..........................................................
237
- isEmpty(desc = undef) {
238
- return this.length(desc) === 0;
239
- }
240
-
241
- // ..........................................................
242
- nonEmpty(desc = undef) {
243
- return this.length(desc) > 0;
244
- }
245
-
246
- // ..........................................................
247
- getShape() {
248
- var lParts, level, ref, result, sect, x;
249
- debug("enter getShape()");
250
- lParts = [];
251
- ref = this.allSections();
252
- for (x of ref) {
253
- [level, sect] = x;
254
- lParts.push(indented(sect.name, level));
255
- }
256
- result = arrayToBlock(lParts);
257
- debug("return from getShape()", result);
258
- return result;
206
+ lSubTree = this.hSets[name];
207
+ assert(defined(lSubTree), `no such set ${OL(name)}`);
208
+ return this.section(lSubTree[lSubTree.length - 1]);
259
209
  }
260
210
 
261
211
  };
@@ -25,6 +25,23 @@ export blockToArray = (block) ->
25
25
  len -= 1
26
26
  return lLines
27
27
 
28
+ # ---------------------------------------------------------------------------
29
+ # toArray - split a block or array into lines w/o newlines
30
+
31
+ export toArray = (item) ->
32
+
33
+ if isString(item)
34
+ return item.split(/\r?\n/)
35
+ else if isArray(item)
36
+ # --- We still need to ensure that no strings contain newlines
37
+ lLines = []
38
+ for str in item
39
+ for substr in toArray(str)
40
+ lLines.push substr
41
+ return lLines
42
+ else
43
+ croak "Not a string or array"
44
+
28
45
  # ---------------------------------------------------------------------------
29
46
  # arrayToBlock - block will have no trailing whitespace
30
47
 
@@ -39,6 +56,21 @@ export arrayToBlock = (lLines) ->
39
56
  else
40
57
  return rtrim(lLines.join('\n'))
41
58
 
59
+ # ---------------------------------------------------------------------------
60
+ # toBlock - block may have trailing whitespace
61
+ # but undef items are ignored
62
+
63
+ export toBlock = (lLines) ->
64
+
65
+ if (lLines == undef)
66
+ return undef
67
+ assert isArray(lLines), "lLines is not an array"
68
+ lLines = lLines.filter((line) => defined(line));
69
+ if lLines.length == 0
70
+ return undef
71
+ else
72
+ return lLines.join('\n')
73
+
42
74
  # ---------------------------------------------------------------------------
43
75
 
44
76
  export splitBlock = (block) ->
@@ -37,6 +37,29 @@ export var blockToArray = function(block) {
37
37
  }
38
38
  };
39
39
 
40
+ // ---------------------------------------------------------------------------
41
+ // toArray - split a block or array into lines w/o newlines
42
+ export var toArray = function(item) {
43
+ var i, j, lLines, len1, len2, ref, str, substr;
44
+ if (isString(item)) {
45
+ return item.split(/\r?\n/);
46
+ } else if (isArray(item)) {
47
+ // --- We still need to ensure that no strings contain newlines
48
+ lLines = [];
49
+ for (i = 0, len1 = item.length; i < len1; i++) {
50
+ str = item[i];
51
+ ref = toArray(str);
52
+ for (j = 0, len2 = ref.length; j < len2; j++) {
53
+ substr = ref[j];
54
+ lLines.push(substr);
55
+ }
56
+ }
57
+ return lLines;
58
+ } else {
59
+ return croak("Not a string or array");
60
+ }
61
+ };
62
+
40
63
  // ---------------------------------------------------------------------------
41
64
  // arrayToBlock - block will have no trailing whitespace
42
65
  export var arrayToBlock = function(lLines) {
@@ -54,6 +77,24 @@ export var arrayToBlock = function(lLines) {
54
77
  }
55
78
  };
56
79
 
80
+ // ---------------------------------------------------------------------------
81
+ // toBlock - block may have trailing whitespace
82
+ // but undef items are ignored
83
+ export var toBlock = function(lLines) {
84
+ if (lLines === undef) {
85
+ return undef;
86
+ }
87
+ assert(isArray(lLines), "lLines is not an array");
88
+ lLines = lLines.filter((line) => {
89
+ return defined(line);
90
+ });
91
+ if (lLines.length === 0) {
92
+ return undef;
93
+ } else {
94
+ return lLines.join('\n');
95
+ }
96
+ };
97
+
57
98
  // ---------------------------------------------------------------------------
58
99
  export var splitBlock = function(block) {
59
100
  var pos;
@@ -51,6 +51,17 @@ export patchStr = (bigstr, pos, str) ->
51
51
 
52
52
  # ---------------------------------------------------------------------------
53
53
 
54
+ export charCount = (str, ch) ->
55
+
56
+ count = 0
57
+ pos = str.indexOf(ch, 0)
58
+ while (pos >= 0)
59
+ count += 1
60
+ pos = str.indexOf(ch, pos+1)
61
+ return count
62
+
63
+ # ---------------------------------------------------------------------------
64
+
54
65
  export isString = (x) ->
55
66
 
56
67
  return (typeof x == 'string') || (x instanceof String)
@@ -56,6 +56,18 @@ export var patchStr = function(bigstr, pos, str) {
56
56
  }
57
57
  };
58
58
 
59
+ // ---------------------------------------------------------------------------
60
+ export var charCount = function(str, ch) {
61
+ var count, pos;
62
+ count = 0;
63
+ pos = str.indexOf(ch, 0);
64
+ while (pos >= 0) {
65
+ count += 1;
66
+ pos = str.indexOf(ch, pos + 1);
67
+ }
68
+ return count;
69
+ };
70
+
59
71
  // ---------------------------------------------------------------------------
60
72
  export var isString = function(x) {
61
73
  return (typeof x === 'string') || (x instanceof String);
@@ -5,7 +5,9 @@ import {
5
5
  undef, escapeStr, defined,
6
6
  OL, isInteger, isString, isArray, isEmpty, rtrim,
7
7
  } from '@jdeighan/coffee-utils'
8
- import {arrayToBlock, blockToArray} from '@jdeighan/coffee-utils/block'
8
+ import {
9
+ arrayToBlock, blockToArray, toArray, toBlock,
10
+ } from '@jdeighan/coffee-utils/block'
9
11
 
10
12
  # ---------------------------------------------------------------------------
11
13
 
@@ -71,14 +73,11 @@ export isUndented = (line) ->
71
73
  export indented = (input, level=1, oneIndent="\t") ->
72
74
 
73
75
  assert (level >= 0), "indented(): negative level"
74
- if level == 0
76
+ if (level == 0)
75
77
  return input
76
78
 
77
79
  toAdd = indentation(level, oneIndent)
78
- if isArray(input)
79
- lInputLines = input
80
- else
81
- lInputLines = blockToArray(input)
80
+ lInputLines = toArray(input)
82
81
 
83
82
  lLines = for line in lInputLines
84
83
  if isEmpty(line)
@@ -93,27 +92,18 @@ export indented = (input, level=1, oneIndent="\t") ->
93
92
  # indentation is removed
94
93
  # - returns same type as text, i.e. either string or array
95
94
 
96
- export undented = (text, level=undef, oneIndent="\t") ->
95
+ export undented = (input, level=undef, oneIndent="\t") ->
97
96
 
98
97
  if defined(level) && (level==0)
99
- return text
100
-
101
- if isString(text)
102
- lLines = blockToArray(text)
103
- if (lLines.length == 0)
104
- return ''
105
- else if isArray(text)
106
- lLines = text
107
- for line in lLines
108
- assert isString(line), "undented(): array not all strings"
109
- if (lLines.length == 0)
110
- return []
111
- else
112
- error "undented(): Not an array or string: #{OL(text)}"
98
+ return input
99
+
100
+ lLines = toArray(input)
101
+ if (lLines.length == 0)
102
+ return ''
113
103
 
114
104
  # --- determine what to remove from beginning of each line
115
105
  if defined(level)
116
- assert isInteger(level), "undented(): level must be an integer"
106
+ assert isInteger(level), "level must be an integer"
117
107
  toRemove = indentation(level, oneIndent)
118
108
  else
119
109
  lMatches = lLines[0].match(/^\s*/)
@@ -129,8 +119,8 @@ export undented = (text, level=undef, oneIndent="\t") ->
129
119
  throw new Error("remove #{OL(toRemove)} from #{OL(text)}")
130
120
  lNewLines.push(line.substr(nToRemove))
131
121
 
132
- if isString(text)
133
- return arrayToBlock(lNewLines)
122
+ if isString(input)
123
+ return toBlock(lNewLines)
134
124
  else
135
125
  return lNewLines
136
126
 
@@ -167,6 +157,21 @@ export untabify = (str, numSpaces=3) ->
167
157
  # ---------------------------------------------------------------------------
168
158
  # enclose - indent text, surround with pre and post
169
159
 
170
- export enclose = (text, pre, post) ->
160
+ export enclose = (text, pre, post, oneIndent="\t") ->
161
+
162
+ return arrayToBlock([
163
+ pre
164
+ indented(text, 1, oneIndent)
165
+ post
166
+ ])
167
+
168
+ # ---------------------------------------------------------------------------
169
+ # elem - indent text, surround with HTML tags
170
+
171
+ export elem = (text, tag, oneIndent="\t") ->
171
172
 
172
- return pre + "\n" + indented(text) + "\n" + post
173
+ return arrayToBlock([
174
+ "<#{tag}>"
175
+ indented(text, 1, oneIndent)
176
+ "</#{tag}>"
177
+ ])
@@ -19,7 +19,9 @@ import {
19
19
 
20
20
  import {
21
21
  arrayToBlock,
22
- blockToArray
22
+ blockToArray,
23
+ toArray,
24
+ toBlock
23
25
  } from '@jdeighan/coffee-utils/block';
24
26
 
25
27
  // ---------------------------------------------------------------------------
@@ -87,11 +89,7 @@ export var indented = function(input, level = 1, oneIndent = "\t") {
87
89
  return input;
88
90
  }
89
91
  toAdd = indentation(level, oneIndent);
90
- if (isArray(input)) {
91
- lInputLines = input;
92
- } else {
93
- lInputLines = blockToArray(input);
94
- }
92
+ lInputLines = toArray(input);
95
93
  lLines = (function() {
96
94
  var i, len1, results;
97
95
  results = [];
@@ -113,31 +111,18 @@ export var indented = function(input, level = 1, oneIndent = "\t") {
113
111
  // - unless level is set, in which case exactly that
114
112
  // indentation is removed
115
113
  // - returns same type as text, i.e. either string or array
116
- export var undented = function(text, level = undef, oneIndent = "\t") {
117
- var i, j, lLines, lMatches, lNewLines, len1, len2, line, nToRemove, toRemove;
114
+ export var undented = function(input, level = undef, oneIndent = "\t") {
115
+ var i, lLines, lMatches, lNewLines, len1, line, nToRemove, toRemove;
118
116
  if (defined(level) && (level === 0)) {
119
- return text;
117
+ return input;
120
118
  }
121
- if (isString(text)) {
122
- lLines = blockToArray(text);
123
- if (lLines.length === 0) {
124
- return '';
125
- }
126
- } else if (isArray(text)) {
127
- lLines = text;
128
- for (i = 0, len1 = lLines.length; i < len1; i++) {
129
- line = lLines[i];
130
- assert(isString(line), "undented(): array not all strings");
131
- }
132
- if (lLines.length === 0) {
133
- return [];
134
- }
135
- } else {
136
- error(`undented(): Not an array or string: ${OL(text)}`);
119
+ lLines = toArray(input);
120
+ if (lLines.length === 0) {
121
+ return '';
137
122
  }
138
123
  // --- determine what to remove from beginning of each line
139
124
  if (defined(level)) {
140
- assert(isInteger(level), "undented(): level must be an integer");
125
+ assert(isInteger(level), "level must be an integer");
141
126
  toRemove = indentation(level, oneIndent);
142
127
  } else {
143
128
  lMatches = lLines[0].match(/^\s*/);
@@ -145,8 +130,8 @@ export var undented = function(text, level = undef, oneIndent = "\t") {
145
130
  }
146
131
  nToRemove = indentLevel(toRemove);
147
132
  lNewLines = [];
148
- for (j = 0, len2 = lLines.length; j < len2; j++) {
149
- line = lLines[j];
133
+ for (i = 0, len1 = lLines.length; i < len1; i++) {
134
+ line = lLines[i];
150
135
  if (isEmpty(line)) {
151
136
  lNewLines.push('');
152
137
  } else {
@@ -156,8 +141,8 @@ export var undented = function(text, level = undef, oneIndent = "\t") {
156
141
  lNewLines.push(line.substr(nToRemove));
157
142
  }
158
143
  }
159
- if (isString(text)) {
160
- return arrayToBlock(lNewLines);
144
+ if (isString(input)) {
145
+ return toBlock(lNewLines);
161
146
  } else {
162
147
  return lNewLines;
163
148
  }
@@ -199,6 +184,12 @@ export var untabify = function(str, numSpaces = 3) {
199
184
 
200
185
  // ---------------------------------------------------------------------------
201
186
  // enclose - indent text, surround with pre and post
202
- export var enclose = function(text, pre, post) {
203
- return pre + "\n" + indented(text) + "\n" + post;
187
+ export var enclose = function(text, pre, post, oneIndent = "\t") {
188
+ return arrayToBlock([pre, indented(text, 1, oneIndent), post]);
189
+ };
190
+
191
+ // ---------------------------------------------------------------------------
192
+ // elem - indent text, surround with HTML tags
193
+ export var elem = function(text, tag, oneIndent = "\t") {
194
+ return arrayToBlock([`<${tag}>`, indented(text, 1, oneIndent), `</${tag}>`]);
204
195
  };