@jdeighan/coffee-utils 9.0.3 → 10.0.0

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.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
  };