@jdeighan/coffee-utils 7.0.49 → 7.0.52

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.
@@ -1,6 +1,6 @@
1
1
  // Generated by CoffeeScript 2.7.0
2
2
  // debug_utils.coffee
3
- var adjustStack, doDebugDebug, reMethod, resetDebugging, shouldLogFunc, shouldLogString, stack;
3
+ var callStack, doDebugDebug, reMethod, resetDebugging;
4
4
 
5
5
  import {
6
6
  assert,
@@ -8,9 +8,11 @@ import {
8
8
  error,
9
9
  croak,
10
10
  warn,
11
+ defined,
11
12
  isString,
12
13
  isFunction,
13
14
  isBoolean,
15
+ sep_dash,
14
16
  OL,
15
17
  escapeStr,
16
18
  isNumber,
@@ -35,28 +37,170 @@ import {
35
37
  CallStack
36
38
  } from '@jdeighan/coffee-utils/stack';
37
39
 
40
+ import {
41
+ getPrefix,
42
+ addArrow,
43
+ removeLastVbar
44
+ } from '@jdeighan/coffee-utils/arrow';
45
+
38
46
  import {
39
47
  log,
40
48
  logItem,
41
49
  LOG,
42
- setStringifier,
43
- orderedStringify
50
+ shortEnough
44
51
  } from '@jdeighan/coffee-utils/log';
45
52
 
46
- // --- These are saved/restored on the call stack
47
- export var debugging = false;
53
+ callStack = new CallStack();
48
54
 
49
- shouldLogFunc = function(func) {
50
- return debugging;
51
- };
55
+ doDebugDebug = false;
56
+
57
+ export var shouldLog = undef; // set in resetDebugging() and setDebugging()
58
+
59
+ export var lFuncList = [];
52
60
 
53
- shouldLogString = function(str) {
54
- return debugging;
61
+ // ---------------------------------------------------------------------------
62
+ export var debug = function(label, ...lObjects) {
63
+ var doLog, funcName, i, j, k, l, len1, len2, len3, level, nObjects, obj, prefix, type;
64
+ assert(isString(label), `1st arg ${OL(label)} should be a string`);
65
+ // --- We want to allow objects to be undef. Therefore, we need to
66
+ // distinguish between 1 arg sent vs. 2 or more args sent
67
+ nObjects = lObjects.length;
68
+ // --- funcName is only set for types 'enter' and 'return'
69
+ [type, funcName] = getType(label, nObjects);
70
+ if (doDebugDebug) {
71
+ LOG(`debug(): type = ${OL(type)}`);
72
+ LOG(`debug(): funcName = ${OL(funcName)}`);
73
+ }
74
+ switch (type) {
75
+ case 'enter':
76
+ callStack.enter(funcName);
77
+ label = shouldLog(label, type, funcName, callStack);
78
+ break;
79
+ case 'return':
80
+ label = shouldLog(label, type, funcName, callStack);
81
+ break;
82
+ case 'string':
83
+ label = shouldLog(label, type, undef, callStack);
84
+ assert(nObjects === 0, `multiple objects only not allowed for ${OL(type)}`);
85
+ break;
86
+ case 'objects':
87
+ label = shouldLog(label, type, undef, callStack);
88
+ assert(nObjects > 0, `multiple objects only not allowed for ${OL(type)}`);
89
+ }
90
+ doLog = defined(label);
91
+ if (doDebugDebug) {
92
+ if (nObjects === 0) {
93
+ LOG(`debug(${OL(label)}) - 1 arg`);
94
+ } else {
95
+ LOG(`debug(${OL(label)}), ${nObjects} args`);
96
+ }
97
+ LOG(`doLog = ${OL(doLog)}`);
98
+ }
99
+ if (doLog) {
100
+ level = callStack.getLevel();
101
+ prefix = getPrefix(level);
102
+ if (doDebugDebug) {
103
+ LOG("callStack", callStack);
104
+ LOG(`level = ${OL(level)}`);
105
+ LOG(`prefix = ${OL(prefix)}`);
106
+ }
107
+ switch (type) {
108
+ case 'enter':
109
+ log(label, {prefix});
110
+ for (i = j = 0, len1 = lObjects.length; j < len1; i = ++j) {
111
+ obj = lObjects[i];
112
+ if (i > 0) {
113
+ log(sep_dash, {
114
+ prefix: removeLastVbar(prefix)
115
+ });
116
+ }
117
+ logItem(undef, obj, {
118
+ prefix: removeLastVbar(prefix)
119
+ });
120
+ }
121
+ break;
122
+ case 'return':
123
+ log(label, {
124
+ prefix: addArrow(prefix)
125
+ });
126
+ for (i = k = 0, len2 = lObjects.length; k < len2; i = ++k) {
127
+ obj = lObjects[i];
128
+ if (i > 0) {
129
+ log(sep_dash, {
130
+ prefix: removeLastVbar(prefix)
131
+ });
132
+ }
133
+ logItem(undef, obj, {
134
+ prefix: removeLastVbar(prefix)
135
+ });
136
+ }
137
+ break;
138
+ case 'string':
139
+ log(label, {prefix});
140
+ break;
141
+ case 'objects':
142
+ if ((nObjects === 1) && shortEnough(label, lObjects[0])) {
143
+ logItem(label, lObjects[0], {prefix});
144
+ } else {
145
+ if (label.indexOf(':') !== label.length - 1) {
146
+ label += ':';
147
+ }
148
+ log(label, {prefix});
149
+ for (l = 0, len3 = lObjects.length; l < len3; l++) {
150
+ obj = lObjects[l];
151
+ logItem(undef, obj, {prefix});
152
+ }
153
+ }
154
+ }
155
+ }
156
+ if ((type === 'enter') && doLog) {
157
+ callStack.logCurFunc();
158
+ } else if (type === 'return') {
159
+ callStack.returnFrom(funcName);
160
+ }
161
+ return true; // allow use in boolean expressions
55
162
  };
56
163
 
57
- stack = new CallStack();
58
164
 
59
- doDebugDebug = false;
165
+ // ---------------------------------------------------------------------------
166
+ export var stdShouldLog = function(label, type, funcName, stack) {
167
+ // --- if type is 'enter', then funcName won't be on the stack yet
168
+ // returns the (possibly modified) label to log
169
+
170
+ // --- If we're logging now,
171
+ // but we won't be logging when funcName is activated
172
+ // then change 'enter' to 'call'
173
+ assert(isString(label), `label ${OL(label)} not a string`);
174
+ assert(isString(type), `type ${OL(type)} not a string`);
175
+ if ((type === 'enter') || (type === 'return')) {
176
+ assert(isString(funcName), `func name ${OL(funcName)} not a string`);
177
+ } else {
178
+ assert(funcName === undef, `func name ${OL(funcName)} not undef`);
179
+ }
180
+ assert(stack instanceof CallStack, "not a call stack object");
181
+ if (doDebugDebug) {
182
+ LOG(`stdShouldLog(${OL(label)}, ${OL(type)}, ${OL(funcName)}, stack)`);
183
+ LOG("stack", stack);
184
+ LOG("lFuncList", lFuncList);
185
+ }
186
+ switch (type) {
187
+ case 'enter':
188
+ if (funcMatch(stack, lFuncList)) {
189
+ return label;
190
+ // --- As a special case, if we enter a function where we will not
191
+ // be logging, but we were logging in the calling function,
192
+ // we'll log out the call itself
193
+ } else if (stack.isLoggingPrev()) {
194
+ return label.replace('enter', 'call');
195
+ }
196
+ break;
197
+ default:
198
+ if (funcMatch(stack, lFuncList)) {
199
+ return label;
200
+ }
201
+ }
202
+ return undef;
203
+ };
60
204
 
61
205
  // ---------------------------------------------------------------------------
62
206
  export var debugDebug = function(flag = true) {
@@ -68,174 +212,137 @@ export var debugDebug = function(flag = true) {
68
212
 
69
213
  // ---------------------------------------------------------------------------
70
214
  resetDebugging = function() {
71
- debugging = false;
72
215
  if (doDebugDebug) {
73
- LOG("resetDebugging() - debugging = false");
216
+ LOG("resetDebugging()");
74
217
  }
75
- stack.reset();
76
- shouldLogFunc = function(func) {
77
- return debugging;
78
- };
79
- shouldLogString = function(str) {
80
- return debugging;
218
+ callStack.reset();
219
+ shouldLog = function(label, type, funcName, stack) {
220
+ return undef;
81
221
  };
82
222
  };
83
223
 
84
224
  // ---------------------------------------------------------------------------
85
- export var setDebugging = function(funcDoDebug = undef, funcDoLog = undef) {
86
- var lFuncNames;
225
+ export var setDebugging = function(option) {
87
226
  resetDebugging();
88
- if (isBoolean(funcDoDebug)) {
89
- debugging = funcDoDebug;
227
+ if (isBoolean(option)) {
228
+ if (option) {
229
+ shouldLog = function(label, type, funcName, stack) {
230
+ return label;
231
+ };
232
+ } else {
233
+ shouldLog = function(label, type, funcName, stack) {
234
+ return undef;
235
+ };
236
+ }
90
237
  if (doDebugDebug) {
91
- LOG(`setDebugging(): debugging = ${funcDoDebug}`);
238
+ LOG(`setDebugging = ${option}`);
92
239
  }
93
- } else if (isString(funcDoDebug)) {
94
- debugging = false;
95
- lFuncNames = words(funcDoDebug);
96
- assert(isArray(lFuncNames), `words('${funcDoDebug}') returned non-array`);
97
- shouldLogFunc = function(funcName) {
98
- return funcMatch(funcName, lFuncNames);
99
- };
240
+ } else if (isString(option)) {
241
+ lFuncList = getFuncList(option);
242
+ shouldLog = stdShouldLog;
100
243
  if (doDebugDebug) {
101
- LOG(`setDebugging FUNCS: ${lFuncNames.join(',')}, debugging = false`);
244
+ LOG(`setDebugging FUNCS: ${option}`);
245
+ LOG('lFuncList', lFuncList);
102
246
  }
103
- } else if (isFunction(funcDoDebug)) {
104
- shouldLogFunc = funcDoDebug;
247
+ } else if (isFunction(option)) {
248
+ shouldLog = option;
105
249
  if (doDebugDebug) {
106
250
  LOG("setDebugging to custom func");
107
251
  }
108
252
  } else {
109
- croak(`setDebugging(): bad parameter ${OL(funcDoDebug)}`);
110
- }
111
- if (funcDoLog) {
112
- assert(isFunction(funcDoLog), "setDebugging: arg 2 not a function");
113
- shouldLogString = funcDoLog;
253
+ croak(`bad parameter ${OL(option)}`);
114
254
  }
115
255
  };
116
256
 
117
257
  // ---------------------------------------------------------------------------
118
258
  // --- export only to allow unit tests
119
- export var funcMatch = function(curFunc, lFuncNames) {
120
- var _, cls, lMatches, meth;
121
- assert(isString(curFunc), "funcMatch(): not a string");
122
- assert(isArray(lFuncNames), `funcMatch(): bad array ${lFuncNames}`);
123
- if (lFuncNames.includes(curFunc)) {
124
- return true;
125
- } else if ((lMatches = curFunc.match(reMethod)) && ([_, cls, meth] = lMatches) && lFuncNames.includes(meth)) {
126
- return true;
127
- } else {
128
- return false;
129
- }
130
- };
131
-
132
- // ---------------------------------------------------------------------------
133
- // 1. adjust call stack on 'enter' or 'return from'
134
- // 2. adjust debugging flag
135
- // 3. return [mainPrefix, auxPrefix, hEnv] - hEnv can be undef
136
- // 4. disable logging by setting mainPrefix to undef
137
- adjustStack = function(str) {
138
- var _, auxPre, curFunc, hEnv, lMatches, mainPre, trans;
139
- if ((lMatches = str.match(/^\s*enter\s+([A-Za-z_][A-Za-z0-9_\.]*)/))) {
140
- curFunc = lMatches[1];
141
- hEnv = {debugging, shouldLogFunc, shouldLogString};
142
- debugging = shouldLogFunc(curFunc);
143
- if (doDebugDebug) {
144
- trans = `${hEnv.debugging} => ${debugging}`;
145
- LOG(` ENTER ${curFunc}, debugging: ${trans}`);
146
- }
147
- [mainPre, auxPre, _] = stack.call(curFunc, hEnv, debugging);
148
- return [mainPre, auxPre, undef, shouldLogFunc(curFunc) ? 'enter' : undef];
149
- } else if ((lMatches = str.match(/^\s*return.+from\s+([A-Za-z_][A-Za-z0-9_\.]*)/))) {
150
- curFunc = lMatches[1];
151
- [mainPre, auxPre, hEnv] = stack.returnFrom(curFunc);
152
- if (doDebugDebug) {
153
- LOG(` RETURN FROM ${curFunc}`);
259
+ export var getFuncList = function(str) {
260
+ var _, ident1, ident2, j, lMatches, len1, plus, ref, word;
261
+ lFuncList = [];
262
+ ref = words(str);
263
+ for (j = 0, len1 = ref.length; j < len1; j++) {
264
+ word = ref[j];
265
+ if (lMatches = word.match(/^([A-Za-z_][A-Za-z0-9_]*)(?:\.([A-Za-z_][A-Za-z0-9_]*))?(\+)?$/)) {
266
+ [_, ident1, ident2, plus] = lMatches;
267
+ if (ident2) {
268
+ lFuncList.push({
269
+ name: ident2,
270
+ object: ident1,
271
+ plus: plus === '+'
272
+ });
273
+ } else {
274
+ lFuncList.push({
275
+ name: ident1,
276
+ plus: plus === '+'
277
+ });
278
+ }
279
+ } else {
280
+ croak(`Bad word in func list: ${OL(word)}`);
154
281
  }
155
- return [mainPre, auxPre, hEnv, shouldLogFunc(curFunc) ? 'return' : undef];
156
- } else {
157
- [mainPre, auxPre, _] = stack.logStr();
158
- return [mainPre, auxPre, undef, shouldLogString(str) ? 'string' : undef];
159
282
  }
283
+ return lFuncList;
160
284
  };
161
285
 
162
286
  // ---------------------------------------------------------------------------
163
- export var debug = function(...lArgs) {
164
- var auxPre, hEnv, hOptions, item, lResult, label, mainPre, nArgs, orgDebugging, trans, type;
165
- // --- We want to allow item to be undef. Therefore, we need to
166
- // distinguish between 1 arg sent vs. 2 args sent
167
- nArgs = lArgs.length;
168
- assert((nArgs === 1) || (nArgs === 2), `debug(): ${nArgs} args`);
169
- [label, item] = lArgs;
170
- assert(isString(label), `debug(): 1st arg ${OL(label)} should be a string`);
171
- if (doDebugDebug) {
172
- if (nArgs === 1) {
173
- LOG(`debug('${escapeStr(label)}') - 1 arg`);
174
- } else {
175
- LOG(`debug('${escapeStr(label)}', ${typeof item}) - 2 args`);
176
- }
177
- LOG(`debugging flag = ${OL(debugging)}`);
178
- }
179
- // --- We always need to manipulate the stack when we encounter
180
- // either "enter X" or "return from X", so we can't short-circuit
181
- // when debugging is off
182
- lResult = adjustStack(label);
287
+ // --- export only to allow unit tests
288
+ export var funcMatch = function(stack, lFuncList) {
289
+ var curFunc, h, j, len1, name, object, plus;
290
+ assert(isArray(lFuncList), `not an array ${OL(lFuncList)}`);
291
+ curFunc = stack.curFunc();
183
292
  if (doDebugDebug) {
184
- LOG('lResult', lResult);
293
+ LOG(`funcMatch(): curFunc = ${OL(curFunc)}`);
294
+ stack.dump(' ');
295
+ LOG('lFuncList', lFuncList);
185
296
  }
186
- [mainPre, auxPre, hEnv, type] = lResult;
187
- if (doDebugDebug && (type === undef)) {
188
- LOG("type is undef - NOT LOGGING");
189
- }
190
- hOptions = {
191
- prefix: mainPre,
192
- itemPrefix: auxPre
193
- };
194
- switch (type) {
195
- case 'enter':
196
- log(label, hOptions);
197
- if (nArgs === 2) {
198
- // --- don't repeat the label
199
- logItem(undef, item, hOptions);
200
- }
201
- break;
202
- case 'return':
203
- log(label, hOptions);
204
- if (nArgs === 2) {
205
- // --- don't repeat the label
206
- logItem(undef, item, hOptions);
297
+ for (j = 0, len1 = lFuncList.length; j < len1; j++) {
298
+ h = lFuncList[j];
299
+ ({name, object, plus} = h);
300
+ if (name === curFunc) {
301
+ if (doDebugDebug) {
302
+ LOG(" curFunc in lFuncList - match successful");
207
303
  }
208
- break;
209
- case 'string':
210
- if (nArgs === 2) {
211
- logItem(label, item, hOptions);
212
- } else {
213
- log(label, hOptions);
304
+ return true;
305
+ }
306
+ if (plus && stack.isActive(name)) {
307
+ if (doDebugDebug) {
308
+ LOG(` func ${OL(name)} is active - match successful`);
214
309
  }
215
- }
216
- if (hEnv) {
217
- orgDebugging = debugging;
218
- ({debugging, shouldLogFunc, shouldLogString} = hEnv);
219
- if (doDebugDebug) {
220
- trans = `${orgDebugging} => ${debugging}`;
221
- LOG(` Restore hEnv: debugging: ${trans}`);
310
+ return true;
222
311
  }
223
312
  }
224
- return true; // allow use in boolean expressions
313
+ if (doDebugDebug) {
314
+ LOG(" - no match");
315
+ }
316
+ return false;
225
317
  };
226
318
 
319
+ // ---------------------------------------------------------------------------
320
+ // --- type is one of: 'enter', 'return', 'string', 'object'
321
+ export var getType = function(str, nObjects) {
322
+ var lMatches;
323
+ if (lMatches = str.match(/^\s*enter\s+([A-Za-z_][A-Za-z0-9_\.]*)/)) {
324
+ // --- We are entering function curFunc
325
+ return ['enter', lMatches[1]];
326
+ } else if (lMatches = str.match(/^\s*return.+from\s+([A-Za-z_][A-Za-z0-9_\.]*)/)) {
327
+ return ['return', lMatches[1]];
328
+ } else if (nObjects > 0) {
329
+ return ['objects', undef];
330
+ } else {
331
+ return ['string', undef];
332
+ }
333
+ };
227
334
 
228
335
  // ---------------------------------------------------------------------------
229
336
  reMethod = /^([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)$/;
230
337
 
231
338
  // ---------------------------------------------------------------------------
232
339
  export var checkTrace = function(block) {
233
- var funcName, i, lMatches, lStack, len, len1, line, ref;
340
+ var funcName, j, lMatches, lStack, len, len1, line, ref;
234
341
  // --- export only to allow unit tests
235
342
  lStack = [];
236
343
  ref = blockToArray(block);
237
- for (i = 0, len1 = ref.length; i < len1; i++) {
238
- line = ref[i];
344
+ for (j = 0, len1 = ref.length; j < len1; j++) {
345
+ line = ref[j];
239
346
  if (lMatches = line.match(/enter\s+([A-Za-z_][A-Za-z0-9_\.]*)/)) {
240
347
  funcName = lMatches[1];
241
348
  lStack.push(funcName);
@@ -4,15 +4,28 @@ import yaml from 'js-yaml'
4
4
 
5
5
  import {
6
6
  assert, undef, isNumber, isInteger, isString, isHash, isFunction,
7
- escapeStr, sep_eq, sep_dash, pass
7
+ escapeStr, sep_eq, sep_dash, pass, OL,
8
8
  } from '@jdeighan/coffee-utils'
9
9
  import {blockToArray} from '@jdeighan/coffee-utils/block'
10
- import {tabify, untabify, indentation} from '@jdeighan/coffee-utils/indent'
10
+ import {
11
+ tabify, untabify, indentation, indented,
12
+ } from '@jdeighan/coffee-utils/indent'
11
13
 
12
14
  # --- This logger only ever gets passed a single string argument
13
15
  putstr = undef
16
+ doDebugLog = false
14
17
 
15
18
  export stringify = undef
19
+ fourSpaces = ' '
20
+
21
+ # ---------------------------------------------------------------------------
22
+
23
+ export debugLog = (flag=true) ->
24
+
25
+ doDebugLog = flag
26
+ if doDebugLog
27
+ LOG "doDebugLog = #{flag}"
28
+ return
16
29
 
17
30
  # ---------------------------------------------------------------------------
18
31
  # This is useful for debugging
@@ -23,9 +36,9 @@ export LOG = (lArgs...) ->
23
36
  if lArgs.length > 1
24
37
  # --- There's both a label and an item
25
38
  if (item == undef)
26
- console.log "#{label}: UNDEFINED"
39
+ console.log "#{label} = undef"
27
40
  else if (item == null)
28
- console.log "#{label}: NULL"
41
+ console.log "#{label} = null"
29
42
  else
30
43
  console.log sep_dash
31
44
  console.log "#{label}:"
@@ -112,19 +125,6 @@ maxOneLine = 32
112
125
 
113
126
  # ---------------------------------------------------------------------------
114
127
 
115
- fixStr = (str) ->
116
-
117
- if !str
118
- return ''
119
-
120
- # --- If putstr is console.log, we'll convert TAB char to 3 spaces
121
- if putstr == console.log
122
- return untabify(str)
123
- else
124
- return str
125
-
126
- # ---------------------------------------------------------------------------
127
-
128
128
  export log = (str, hOptions={}) ->
129
129
  # --- valid options:
130
130
  # prefix
@@ -132,8 +132,11 @@ export log = (str, hOptions={}) ->
132
132
  assert isFunction(putstr), "putstr not properly set"
133
133
  assert isString(str), "log(): not a string"
134
134
  assert isHash(hOptions), "log(): arg 2 not a hash"
135
+ prefix = fixForTerminal(hOptions.prefix)
136
+
137
+ if doDebugLog
138
+ LOG "CALL log(#{OL(str)}), prefix = #{OL(prefix)}"
135
139
 
136
- prefix = fixStr(hOptions.prefix)
137
140
  putstr "#{prefix}#{str}"
138
141
  return true # to allow use in boolean expressions
139
142
 
@@ -141,15 +144,20 @@ export log = (str, hOptions={}) ->
141
144
 
142
145
  export logItem = (label, item, hOptions={}) ->
143
146
  # --- valid options:
144
- # prefix - not used
145
- # itemPrefix - always used
147
+ # prefix
146
148
 
147
149
  assert isFunction(putstr), "putstr not properly set"
148
150
  assert !label || isString(label), "label a non-string"
149
151
  assert isHash(hOptions), "arg 3 not a hash"
150
152
 
151
- label = fixStr(label)
152
- prefix = fixStr(hOptions.itemPrefix || hOptions.prefix)
153
+ label = fixForTerminal(label)
154
+ prefix = fixForTerminal(hOptions.prefix)
155
+ assert prefix.indexOf("\t") == -1, "prefix has TAB"
156
+
157
+ if doDebugLog
158
+ LOG "CALL logItem(#{OL(label)}, #{OL(item)})"
159
+ LOG "prefix = #{OL(prefix)}"
160
+
153
161
  labelStr = if label then "#{label} = " else ""
154
162
 
155
163
  if (item == undef)
@@ -162,21 +170,36 @@ export logItem = (label, item, hOptions={}) ->
162
170
  else
163
171
  if label
164
172
  putstr "#{prefix}#{label}:"
165
- putBlock item, prefix
173
+ putBlock item, prefix + fourSpaces
166
174
  else if isNumber(item)
167
175
  putstr "#{prefix}#{labelStr}#{item}"
168
176
  else
169
- putstr "#{prefix}#{sep_dash}"
170
177
  if label
171
178
  putstr "#{prefix}#{label}:"
172
179
  for str in blockToArray(stringify(item, true)) # escape special chars
173
- putstr "#{prefix}#{indentation(1)}#{fixStr(str)}"
174
- putstr "#{prefix}#{sep_dash}"
180
+ putstr "#{prefix + fourSpaces}#{fixForTerminal(str)}"
175
181
 
176
182
  return true
177
183
 
178
184
  # ---------------------------------------------------------------------------
179
185
 
186
+ export shortEnough = (label, value) ->
187
+
188
+ return (value == undef)
189
+
190
+ # ---------------------------------------------------------------------------
191
+ # --- needed because Windows Terminal handles TAB chars badly
192
+
193
+ fixForTerminal = (str) ->
194
+
195
+ if !str
196
+ return ''
197
+
198
+ # --- convert TAB char to 4 spaces
199
+ return str.replace(/\t/g, fourSpaces)
200
+
201
+ # ---------------------------------------------------------------------------
202
+
180
203
  putBlock = (item, prefix='') ->
181
204
 
182
205
  putstr "#{prefix}#{sep_eq}"