@jdeighan/coffee-utils 7.0.50 → 7.0.53

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,
@@ -12,6 +12,7 @@ import {
12
12
  isString,
13
13
  isFunction,
14
14
  isBoolean,
15
+ sep_dash,
15
16
  OL,
16
17
  escapeStr,
17
18
  isNumber,
@@ -36,28 +37,170 @@ import {
36
37
  CallStack
37
38
  } from '@jdeighan/coffee-utils/stack';
38
39
 
40
+ import {
41
+ getPrefix,
42
+ addArrow,
43
+ removeLastVbar
44
+ } from '@jdeighan/coffee-utils/arrow';
45
+
39
46
  import {
40
47
  log,
41
48
  logItem,
42
49
  LOG,
43
- setStringifier,
44
- orderedStringify
50
+ shortEnough
45
51
  } from '@jdeighan/coffee-utils/log';
46
52
 
47
- // --- These are saved/restored on the call stack
48
- export var debugging = false;
53
+ callStack = new CallStack();
49
54
 
50
- shouldLogFunc = function(func) {
51
- return debugging;
52
- };
55
+ doDebugDebug = false;
56
+
57
+ export var shouldLog = undef; // set in resetDebugging() and setDebugging()
58
+
59
+ export var lFuncList = [];
53
60
 
54
- shouldLogString = function(str) {
55
- 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
56
162
  };
57
163
 
58
- stack = new CallStack();
59
164
 
60
- 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
+ };
61
204
 
62
205
  // ---------------------------------------------------------------------------
63
206
  export var debugDebug = function(flag = true) {
@@ -69,177 +212,137 @@ export var debugDebug = function(flag = true) {
69
212
 
70
213
  // ---------------------------------------------------------------------------
71
214
  resetDebugging = function() {
72
- debugging = false;
73
215
  if (doDebugDebug) {
74
- LOG("resetDebugging() - debugging = false");
216
+ LOG("resetDebugging()");
75
217
  }
76
- stack.reset();
77
- shouldLogFunc = function(func) {
78
- return debugging;
79
- };
80
- shouldLogString = function(str) {
81
- return debugging;
218
+ callStack.reset();
219
+ shouldLog = function(label, type, funcName, stack) {
220
+ return undef;
82
221
  };
83
222
  };
84
223
 
85
224
  // ---------------------------------------------------------------------------
86
- export var setDebugging = function(funcDoDebug = undef, funcDoLog = undef) {
87
- var lFuncNames;
225
+ export var setDebugging = function(option) {
88
226
  resetDebugging();
89
- if (isBoolean(funcDoDebug)) {
90
- 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
+ }
91
237
  if (doDebugDebug) {
92
- LOG(`setDebugging(): debugging = ${funcDoDebug}`);
238
+ LOG(`setDebugging = ${option}`);
93
239
  }
94
- } else if (isString(funcDoDebug)) {
95
- debugging = false;
96
- lFuncNames = words(funcDoDebug);
97
- assert(isArray(lFuncNames), `words('${funcDoDebug}') returned non-array`);
98
- shouldLogFunc = function(funcName) {
99
- return funcMatch(funcName, lFuncNames);
100
- };
240
+ } else if (isString(option)) {
241
+ lFuncList = getFuncList(option);
242
+ shouldLog = stdShouldLog;
101
243
  if (doDebugDebug) {
102
- LOG(`setDebugging FUNCS: ${lFuncNames.join(',')}, debugging = false`);
244
+ LOG(`setDebugging FUNCS: ${option}`);
245
+ LOG('lFuncList', lFuncList);
103
246
  }
104
- } else if (isFunction(funcDoDebug)) {
105
- shouldLogFunc = funcDoDebug;
247
+ } else if (isFunction(option)) {
248
+ shouldLog = option;
106
249
  if (doDebugDebug) {
107
250
  LOG("setDebugging to custom func");
108
251
  }
109
252
  } else {
110
- croak(`setDebugging(): bad parameter ${OL(funcDoDebug)}`);
111
- }
112
- if (isFunction(funcDoLog)) {
113
- assert(isFunction(funcDoLog), "setDebugging: arg 2 not a function");
114
- shouldLogString = funcDoLog;
253
+ croak(`bad parameter ${OL(option)}`);
115
254
  }
116
255
  };
117
256
 
118
257
  // ---------------------------------------------------------------------------
119
258
  // --- export only to allow unit tests
120
- export var funcMatch = function(curFunc, lFuncNames) {
121
- var _, cls, lMatches, meth;
122
- assert(isString(curFunc), "funcMatch(): not a string");
123
- assert(isArray(lFuncNames), `funcMatch(): bad array ${lFuncNames}`);
124
- if (lFuncNames.includes(curFunc)) {
125
- return true;
126
- } else if ((lMatches = curFunc.match(reMethod)) && ([_, cls, meth] = lMatches) && lFuncNames.includes(meth)) {
127
- return true;
128
- } else {
129
- return false;
130
- }
131
- };
132
-
133
- // ---------------------------------------------------------------------------
134
- // 1. adjust call stack on 'enter' or 'return from'
135
- // 2. adjust debugging flag
136
- // 3. return [mainPrefix, auxPrefix, hEnv, type] - hEnv can be undef
137
- // 4. disable logging by setting type to undef
138
- adjustStack = function(str) {
139
- var _, auxPre, curFunc, hEnv, lMatches, mainPre, trans;
140
- if ((lMatches = str.match(/^\s*enter\s+([A-Za-z_][A-Za-z0-9_\.]*)/))) {
141
- // --- We are entering function curFunc
142
- curFunc = lMatches[1];
143
- hEnv = {debugging, shouldLogFunc, shouldLogString};
144
- debugging = shouldLogFunc(curFunc);
145
- if (doDebugDebug) {
146
- trans = `${hEnv.debugging} => ${debugging}`;
147
- LOG(` ENTER ${curFunc}, debugging: ${trans}`);
148
- }
149
- [mainPre, auxPre] = stack.doCall(curFunc, hEnv, debugging);
150
- return [mainPre, auxPre, undef, shouldLogFunc(curFunc) ? 'enter' : undef];
151
- } else if ((lMatches = str.match(/^\s*return.+from\s+([A-Za-z_][A-Za-z0-9_\.]*)/))) {
152
- curFunc = lMatches[1];
153
- [mainPre, auxPre, hEnv] = stack.returnFrom(curFunc);
154
- if (doDebugDebug) {
155
- 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)}`);
156
281
  }
157
- return [mainPre, auxPre, hEnv, shouldLogFunc(curFunc) ? 'return' : undef];
158
- } else {
159
- [mainPre, auxPre, _] = stack.logStr();
160
- return [mainPre, auxPre, undef, shouldLogString(str) ? 'string' : undef];
161
282
  }
283
+ return lFuncList;
162
284
  };
163
285
 
164
286
  // ---------------------------------------------------------------------------
165
- export var debug = function(...lArgs) {
166
- var auxPre, hEnv, hOptions, item, lResult, label, mainPre, nArgs, orgDebugging, trans, type;
167
- // --- We want to allow item to be undef. Therefore, we need to
168
- // distinguish between 1 arg sent vs. 2 args sent
169
- nArgs = lArgs.length;
170
- assert((nArgs === 1) || (nArgs === 2), `debug(): ${nArgs} args`);
171
- // --- label must always be there, and be a string
172
- // item is optional
173
- [label, item] = lArgs;
174
- assert(isString(label), `debug(): 1st arg ${OL(label)} should be a string`);
175
- if (doDebugDebug) {
176
- if (nArgs === 1) {
177
- LOG(`debug('${escapeStr(label)}') - 1 arg`);
178
- } else {
179
- LOG(`debug('${escapeStr(label)}', ${typeof item}) - 2 args`);
180
- }
181
- LOG(`debugging flag = ${OL(debugging)}`);
182
- }
183
- // --- We always need to manipulate the stack when we encounter
184
- // either "enter X" or "return from X", so we can't short-circuit
185
- // when debugging is off
186
- 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();
187
292
  if (doDebugDebug) {
188
- LOG('lResult', lResult);
293
+ LOG(`funcMatch(): curFunc = ${OL(curFunc)}`);
294
+ stack.dump(' ');
295
+ LOG('lFuncList', lFuncList);
189
296
  }
190
- [mainPre, auxPre, hEnv, type] = lResult;
191
- if (doDebugDebug && (type === undef)) {
192
- LOG("type is undef - NOT LOGGING");
193
- }
194
- hOptions = {
195
- prefix: mainPre,
196
- itemPrefix: auxPre
197
- };
198
- switch (type) {
199
- case 'enter':
200
- log(label, hOptions);
201
- if (nArgs === 2) {
202
- // --- don't repeat the label
203
- 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");
204
303
  }
205
- break;
206
- case 'return':
207
- log(label, hOptions);
208
- if (nArgs === 2) {
209
- // --- don't repeat the label
210
- logItem(undef, item, hOptions);
211
- }
212
- break;
213
- case 'string':
214
- if (nArgs === 2) {
215
- logItem(label, item, hOptions);
216
- } else {
217
- 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`);
218
309
  }
219
- }
220
- if (hEnv) {
221
- orgDebugging = debugging;
222
- ({debugging, shouldLogFunc, shouldLogString} = hEnv);
223
- if (doDebugDebug) {
224
- trans = `${orgDebugging} => ${debugging}`;
225
- LOG(` Restore hEnv: debugging: ${trans}`);
310
+ return true;
226
311
  }
227
312
  }
228
- return true; // allow use in boolean expressions
313
+ if (doDebugDebug) {
314
+ LOG(" - no match");
315
+ }
316
+ return false;
229
317
  };
230
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
+ };
231
334
 
232
335
  // ---------------------------------------------------------------------------
233
336
  reMethod = /^([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)$/;
234
337
 
235
338
  // ---------------------------------------------------------------------------
236
339
  export var checkTrace = function(block) {
237
- var funcName, i, lMatches, lStack, len, len1, line, ref;
340
+ var funcName, j, lMatches, lStack, len, len1, line, ref;
238
341
  // --- export only to allow unit tests
239
342
  lStack = [];
240
343
  ref = blockToArray(block);
241
- for (i = 0, len1 = ref.length; i < len1; i++) {
242
- line = ref[i];
344
+ for (j = 0, len1 = ref.length; j < len1; j++) {
345
+ line = ref[j];
243
346
  if (lMatches = line.match(/enter\s+([A-Za-z_][A-Za-z0-9_\.]*)/)) {
244
347
  funcName = lMatches[1];
245
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}"