@jdeighan/coffee-utils 9.0.4 → 10.0.1

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jdeighan/coffee-utils",
3
3
  "type": "module",
4
- "version": "9.0.4",
4
+ "version": "10.0.1",
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
 
@@ -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
  // ..........................................................
@@ -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,149 +16,145 @@ 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)}"
83
-
84
- @hSets[name] = lSectionTree
85
- debug 'hSets', @hSets
86
- debug "return from addSet()"
87
- return
86
+ getBlock: (desc=undef, hReplacers={}) ->
88
87
 
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, hReplacers
93
89
 
94
- allSections: (desc=undef, level=0) ->
90
+ # --- desc can only be a string or an array
91
+ # so, if it's a hash, then it's really the hReplacers
92
+ # and the real desc is undef
95
93
 
96
- debug "enter allSections()", desc, level
97
- if (desc == undef)
94
+ if isHash(desc)
95
+ debug "arg 1 is hReplacers, no desc"
96
+ assert isEmpty(hReplacers), "invalid parms"
97
+ hReplacers = desc
98
98
  desc = @lSectionTree
99
+ else if notdefined(desc)
100
+ desc = @lSectionTree
101
+
99
102
  if isArray(desc)
103
+ lBlocks = []
104
+ setName = undef
100
105
  for item in desc
101
- if isSectionName(item)
102
- result = [level, @section(item)]
103
- debug 'yield', result
104
- yield result
106
+ subBlock = undef
107
+ if isArray(item)
108
+ subBlock = @getBlock(item, hReplacers)
109
+ else if isSectionName(item)
110
+ subBlock = @getBlock(item, hReplacers)
105
111
  else if isSetName(item)
106
- pass
112
+ setName = item
113
+ else if isString(item)
114
+ subBlock = item # a literal string
115
+ else
116
+ croak "Bad item: #{OL(item)}"
117
+ if defined(subBlock)
118
+ lBlocks.push subBlock
119
+ block = arrayToBlock(lBlocks)
120
+ if defined(setName)
121
+ if defined(proc = hReplacers[setName])
122
+ debug "REPLACE #{setName}"
123
+ block = proc(block)
107
124
  else
108
- assert isArray(item), "not an array #{OL(item)}"
109
- yield from @allSections(item, level+1)
125
+ debug "NO REPLACER for #{setName}"
110
126
  else if isSectionName(desc)
111
- result = [level, @section(desc)]
112
- debug 'yield', result
113
- yield result
127
+ block = @section(desc).getBlock()
128
+ if defined(proc = hReplacers[desc])
129
+ debug "REPLACE #{desc}"
130
+ block = proc(block)
131
+ else
132
+ debug "NO REPLACER for #{desc}"
114
133
  else if isSetName(desc)
115
- lTree = @hSets[desc]
116
- assert defined(lTree), "Not a Set: #{OL(desc)}"
117
- yield from @allSections(lTree, level)
134
+ # --- pass array to getBlock()
135
+ block = @getBlock(@hSets[desc], hReplacers)
118
136
  else
119
- croak "Bad item: #{OL(desc)}"
120
- debug "return from allSections()"
121
- return
137
+ croak "Bad 1st arg: #{OL(desc)}"
138
+ debug "return from SectionMap.getBlock()", block
139
+ return block
122
140
 
123
141
  # ..........................................................
124
- # --- hProc should be <name> -> <function>
125
- # <function> should be <block> -> <block>
126
- # --- lTree allows you to get just a section
127
142
 
128
- getBlock: (hProc={}, lTree=undef) ->
143
+ isEmpty: () ->
129
144
 
130
- debug "enter getBlock()"
131
- if (lTree == undef)
132
- lTree = @lSectionTree
133
- else
134
- assert isArray(lTree), "not an array #{OL(lTree)}"
135
-
136
- lParts = []
137
- for part in lTree
138
- if isString(part)
139
- block = @section(part).getBlock()
140
- if defined(hProc[part])
141
- # --- called even if block is empty
142
- block = hProc[part](block)
143
- else if isNonEmptyArray(part)
144
- if isSectionName(part[0])
145
- block = @getBlock(hProc, part)
146
- else if isSetName(part[0])
147
- block = @getBlock(hProc, part.slice(1))
148
- if defined(hProc[part[0]])
149
- block = hProc[part[0]](block)
150
- else
151
- croak "Bad part: #{OL(part)}"
152
- else
153
- croak "Bad part: #{OL(part)}"
154
- if defined(block)
155
- lParts.push block
145
+ for name,sect of @hSections
146
+ if sect.nonEmpty()
147
+ return false
148
+ return true
149
+
150
+ # ..........................................................
151
+
152
+ nonEmpty: () ->
156
153
 
157
- debug 'lParts', lParts
158
- result = arrayToBlock(lParts)
159
- debug "return from getBlock()", result
160
- return result
154
+ for name,sect of @hSections
155
+ if sect.nonEmpty()
156
+ return true
157
+ return false
161
158
 
162
159
  # ..........................................................
163
160
 
@@ -172,50 +169,15 @@ export class SectionMap
172
169
  firstSection: (name) ->
173
170
 
174
171
  assert isSetName(name), "bad set name #{OL(name)}"
175
- lSectionTree = @hSets[name]
176
- assert defined(lSectionTree), "no such set #{OL(name)}"
177
- assert nonEmpty(lSectionTree), "empty section #{OL(name)}"
178
- return @section(lSectionTree[0])
172
+ lSubTree = @hSets[name]
173
+ assert defined(lSubTree), "no such set #{OL(name)}"
174
+ return @section(lSubTree[1])
179
175
 
180
176
  # ..........................................................
181
177
 
182
178
  lastSection: (name) ->
183
179
 
184
180
  assert isSetName(name), "bad set name #{OL(name)}"
185
- lSectionTree = @hSets[name]
186
- assert defined(lSectionTree), "no such set #{OL(name)}"
187
- assert nonEmpty(lSectionTree), "empty section #{OL(name)}"
188
- return @section(lSectionTree[lSectionTree.length - 1])
189
-
190
- # ..........................................................
191
-
192
- length: (desc=undef) ->
193
-
194
- result = 0
195
- for [_, sect] from @allSections(desc)
196
- result += sect.length()
197
- return result
198
-
199
- # ..........................................................
200
-
201
- isEmpty: (desc=undef) ->
202
-
203
- return (@length(desc) == 0)
204
-
205
- # ..........................................................
206
-
207
- nonEmpty: (desc=undef) ->
208
-
209
- return (@length(desc) > 0)
210
-
211
- # ..........................................................
212
-
213
- getShape: () ->
214
-
215
- debug "enter getShape()"
216
- lParts = []
217
- for [level, sect] from @allSections()
218
- lParts.push indented(sect.name, level)
219
- result = arrayToBlock(lParts)
220
- debug "return from getShape()", result
221
- return result
181
+ lSubTree = @hSets[name]
182
+ assert defined(lSubTree), "no such set #{OL(name)}"
183
+ 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,158 +46,160 @@ 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
- }
95
- }
96
-
97
- // ..........................................................
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
104
  }
110
- this.hSets[name] = lSectionTree;
111
- debug('hSets', this.hSets);
112
- debug("return from addSet()");
105
+ debug("return from build()", this.hSections, this.hSets);
113
106
  }
114
107
 
115
108
  // ..........................................................
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, hReplacers = {}) {
118
+ var block, i, item, lBlocks, len, proc, setName, subBlock;
119
+ debug("enter SectionMap.getBlock()", desc, hReplacers);
120
+ // --- desc can only be a string or an array
121
+ // so, if it's a hash, then it's really the hReplacers
122
+ // and the real desc is undef
123
+ if (isHash(desc)) {
124
+ debug("arg 1 is hReplacers, no desc");
125
+ assert(isEmpty(hReplacers), "invalid parms");
126
+ hReplacers = desc;
127
+ desc = this.lSectionTree;
128
+ } else if (notdefined(desc)) {
123
129
  desc = this.lSectionTree;
124
130
  }
125
131
  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;
132
+ lBlocks = [];
133
+ setName = undef;
134
+ for (i = 0, len = desc.length; i < len; i++) {
135
+ item = desc[i];
136
+ subBlock = undef;
137
+ if (isArray(item)) {
138
+ subBlock = this.getBlock(item, hReplacers);
139
+ } else if (isSectionName(item)) {
140
+ subBlock = this.getBlock(item, hReplacers);
132
141
  } else if (isSetName(item)) {
133
- pass;
142
+ setName = item;
143
+ } else if (isString(item)) {
144
+ subBlock = item; // a literal string
145
+ } else {
146
+ croak(`Bad item: ${OL(item)}`);
147
+ }
148
+ if (defined(subBlock)) {
149
+ lBlocks.push(subBlock);
150
+ }
151
+ }
152
+ block = arrayToBlock(lBlocks);
153
+ if (defined(setName)) {
154
+ if (defined(proc = hReplacers[setName])) {
155
+ debug(`REPLACE ${setName}`);
156
+ block = proc(block);
134
157
  } else {
135
- assert(isArray(item), `not an array ${OL(item)}`);
136
- yield* this.allSections(item, level + 1);
158
+ debug(`NO REPLACER for ${setName}`);
137
159
  }
138
160
  }
139
161
  } else if (isSectionName(desc)) {
140
- result = [level, this.section(desc)];
141
- debug('yield', result);
142
- yield result;
162
+ block = this.section(desc).getBlock();
163
+ if (defined(proc = hReplacers[desc])) {
164
+ debug(`REPLACE ${desc}`);
165
+ block = proc(block);
166
+ } else {
167
+ debug(`NO REPLACER for ${desc}`);
168
+ }
143
169
  } else if (isSetName(desc)) {
144
- lTree = this.hSets[desc];
145
- assert(defined(lTree), `Not a Set: ${OL(desc)}`);
146
- yield* this.allSections(lTree, level);
170
+ // --- pass array to getBlock()
171
+ block = this.getBlock(this.hSets[desc], hReplacers);
147
172
  } else {
148
- croak(`Bad item: ${OL(desc)}`);
173
+ croak(`Bad 1st arg: ${OL(desc)}`);
149
174
  }
150
- debug("return from allSections()");
175
+ debug("return from SectionMap.getBlock()", block);
176
+ return block;
151
177
  }
152
178
 
153
179
  // ..........................................................
154
- // --- hProc should be <name> -> <function>
155
- // <function> should be <block> -> <block>
156
- // --- lTree allows you to get just a section
157
- getBlock(hProc = {}, lTree = undef) {
158
- var block, j, lParts, len, part, result;
159
- debug("enter getBlock()");
160
- if (lTree === undef) {
161
- lTree = this.lSectionTree;
162
- } else {
163
- assert(isArray(lTree), `not an array ${OL(lTree)}`);
164
- }
165
- lParts = [];
166
- for (j = 0, len = lTree.length; j < len; j++) {
167
- part = lTree[j];
168
- if (isString(part)) {
169
- block = this.section(part).getBlock();
170
- if (defined(hProc[part])) {
171
- // --- called even if block is empty
172
- block = hProc[part](block);
173
- }
174
- } else if (isNonEmptyArray(part)) {
175
- if (isSectionName(part[0])) {
176
- block = this.getBlock(hProc, part);
177
- } else if (isSetName(part[0])) {
178
- block = this.getBlock(hProc, part.slice(1));
179
- if (defined(hProc[part[0]])) {
180
- block = hProc[part[0]](block);
181
- }
182
- } else {
183
- croak(`Bad part: ${OL(part)}`);
184
- }
185
- } else {
186
- croak(`Bad part: ${OL(part)}`);
180
+ isEmpty() {
181
+ var name, ref, sect;
182
+ ref = this.hSections;
183
+ for (name in ref) {
184
+ sect = ref[name];
185
+ if (sect.nonEmpty()) {
186
+ return false;
187
187
  }
188
- if (defined(block)) {
189
- lParts.push(block);
188
+ }
189
+ return true;
190
+ }
191
+
192
+ // ..........................................................
193
+ nonEmpty() {
194
+ var name, ref, sect;
195
+ ref = this.hSections;
196
+ for (name in ref) {
197
+ sect = ref[name];
198
+ if (sect.nonEmpty()) {
199
+ return true;
190
200
  }
191
201
  }
192
- debug('lParts', lParts);
193
- result = arrayToBlock(lParts);
194
- debug("return from getBlock()", result);
195
- return result;
202
+ return false;
196
203
  }
197
204
 
198
205
  // ..........................................................
@@ -205,59 +212,20 @@ export var SectionMap = class SectionMap {
205
212
 
206
213
  // ..........................................................
207
214
  firstSection(name) {
208
- var lSectionTree;
215
+ var lSubTree;
209
216
  assert(isSetName(name), `bad set name ${OL(name)}`);
210
- lSectionTree = this.hSets[name];
211
- assert(defined(lSectionTree), `no such set ${OL(name)}`);
212
- assert(nonEmpty(lSectionTree), `empty section ${OL(name)}`);
213
- return this.section(lSectionTree[0]);
217
+ lSubTree = this.hSets[name];
218
+ assert(defined(lSubTree), `no such set ${OL(name)}`);
219
+ return this.section(lSubTree[1]);
214
220
  }
215
221
 
216
222
  // ..........................................................
217
223
  lastSection(name) {
218
- var lSectionTree;
224
+ var lSubTree;
219
225
  assert(isSetName(name), `bad set name ${OL(name)}`);
220
- lSectionTree = this.hSets[name];
221
- assert(defined(lSectionTree), `no such set ${OL(name)}`);
222
- assert(nonEmpty(lSectionTree), `empty section ${OL(name)}`);
223
- return this.section(lSectionTree[lSectionTree.length - 1]);
224
- }
225
-
226
- // ..........................................................
227
- length(desc = undef) {
228
- var _, ref, result, sect, x;
229
- result = 0;
230
- ref = this.allSections(desc);
231
- for (x of ref) {
232
- [_, sect] = x;
233
- result += sect.length();
234
- }
235
- return result;
236
- }
237
-
238
- // ..........................................................
239
- isEmpty(desc = undef) {
240
- return this.length(desc) === 0;
241
- }
242
-
243
- // ..........................................................
244
- nonEmpty(desc = undef) {
245
- return this.length(desc) > 0;
246
- }
247
-
248
- // ..........................................................
249
- getShape() {
250
- var lParts, level, ref, result, sect, x;
251
- debug("enter getShape()");
252
- lParts = [];
253
- ref = this.allSections();
254
- for (x of ref) {
255
- [level, sect] = x;
256
- lParts.push(indented(sect.name, level));
257
- }
258
- result = arrayToBlock(lParts);
259
- debug("return from getShape()", result);
260
- return result;
226
+ lSubTree = this.hSets[name];
227
+ assert(defined(lSubTree), `no such set ${OL(name)}`);
228
+ return this.section(lSubTree[lSubTree.length - 1]);
261
229
  }
262
230
 
263
231
  };
@@ -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
  };