@jdeighan/coffee-utils 15.0.0 → 16.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jdeighan/coffee-utils",
3
3
  "type": "module",
4
- "version": "15.0.0",
4
+ "version": "16.0.0",
5
5
  "description": "A set of utility functions for CoffeeScript",
6
6
  "main": "coffee_utils.js",
7
7
  "exports": {
package/src/indent.coffee CHANGED
@@ -34,15 +34,6 @@ export splitLine = (line, oneIndent=undef) =>
34
34
  [prefix, str] = splitPrefix(line)
35
35
  return [indentLevel(prefix, oneIndent), str]
36
36
 
37
- # ---------------------------------------------------------------------------
38
- # indentation - return appropriate indentation string for given level
39
- # export only to allow unit testing
40
-
41
- export indentation = (level, oneIndent="\t") =>
42
-
43
- assert (level >= 0), "indentation(): negative level"
44
- return oneIndent.repeat(level)
45
-
46
37
  # ---------------------------------------------------------------------------
47
38
  # indentLevel - determine indent level of a string
48
39
  # it's OK if the string is ONLY indentation
@@ -59,28 +50,56 @@ export indentLevel = (line, oneIndent=undef) =>
59
50
  if (prefixLen == 0)
60
51
  return 0
61
52
 
62
- if defined(oneIndent)
63
- # --- prefix must be some multiple of oneIndent
64
- len = oneIndent.length
65
- assert (prefixLen % len == 0),
66
- "prefix #{OL(prefix)} not a mult of #{OL(oneIndent)}"
67
-
68
- level = prefixLen / len
53
+ # --- Match \t* followed by \x20* (error if no match)
54
+ if lMatches = prefix.match(/(\t*)(\x20*)/)
55
+ nTabs = lMatches[1].length
56
+ nSpaces = lMatches[2].length
69
57
  else
70
- ch = prefix.substring(0, 1)
71
- if (ch == "\t")
72
- oneIndent = "\t"
73
- level = prefixLen
74
- else if (ch == ' ')
75
- oneIndent = ' '.repeat(prefixLen)
76
- level = 1
58
+ croak "Invalid mix of TABs and spaces"
59
+
60
+ # --- oneIndent must be one of:
61
+ # undef
62
+ # a single TAB character
63
+ # some number of space characters
64
+
65
+ switch oneIndent
66
+ when undef
67
+ if (nTabs > 0)
68
+ level = nTabs # there may also be spaces, but we ignore them
69
+ oneIndent = "\t" # may be used at end
70
+ else
71
+ assert (nSpaces > 0), "There must be TABS or spaces"
72
+ level = 1
73
+ oneIndent = ' '.repeat(nSpaces) # may be used at end
74
+ when "\t"
75
+ assert (nTabs > 0), "Expecting TAB indentation, found spaces"
76
+ # --- NOTE: there may be spaces, but they're not indentation
77
+ level = nTabs
77
78
  else
78
- croak "Bad Indentation in #{OL(line)}"
79
-
80
- assert (prefix == oneIndent.repeat(level)),
81
- "prefix #{OL(prefix)} not a mult of #{OL(oneIndent)}"
79
+ # --- oneIndent must be all space chars
80
+ assert (nTabs == 0),
81
+ "Indentation has TABs but oneIndent = #{OL(oneIndent)}"
82
+ assert (nSpaces % oneIndent.length == 0),
83
+ "prefix #{OL(prefix)} not a mult of #{OL(oneIndent)}"
84
+ level = nSpaces / oneIndent.length
85
+
86
+ # --- If a block, i.e. multi-line string, then all lines must be
87
+ # at least at this level
88
+ if (line.indexOf("\n") >= 0)
89
+ for str in toArray(line)
90
+ assert (indentLevel(str, oneIndent) >= level),
91
+ "indentLevel of #{OL(line)} can't be found"
82
92
  return level
83
93
 
94
+ # ---------------------------------------------------------------------------
95
+ # indentation - return appropriate indentation string for given level
96
+ # export only to allow unit testing
97
+
98
+ export indentation = (level, oneIndent="\t") =>
99
+
100
+ assert (level >= 0), "indentation(): negative level"
101
+ return oneIndent.repeat(level)
102
+
84
103
  # ---------------------------------------------------------------------------
85
104
  # isUndented - true iff indentLevel(line) == 0
86
105
 
@@ -127,27 +146,22 @@ export indented = (input, level=1, oneIndent="\t") =>
127
146
  # ---------------------------------------------------------------------------
128
147
  # undented - string with 1st line indentation removed for each line
129
148
  # - ignore leading empty lines
130
- # - unless level is set, in which case exactly that
131
- # indentation is removed
132
149
  # - returns same type as text, i.e. either string or array
133
150
 
134
- export undented = (input, level=undef, oneIndent="\t") =>
135
-
136
- if defined(level) && (level==0)
137
- return input
151
+ export undented = (input) =>
138
152
 
139
- # --- Remove any leading blank lines, set lLines
153
+ # --- If a string, convert to an array
140
154
  if isString(input)
141
- if lMatches = input.match(///^ [\r\n]+ (.*) $///s)
142
- input = lMatches[1]
143
155
  lLines = toArray(input)
144
156
  else if isArray(input)
145
157
  lLines = input
146
- while (lLines.length > 0) && isEmpty(lLines[0])
147
- lLines.shift()
148
158
  else
149
159
  croak "input not a string or array"
150
160
 
161
+ # --- Remove leading blank lines
162
+ while (lLines.length > 0) && isEmpty(lLines[0])
163
+ lLines.shift() # remove
164
+
151
165
  if (lLines.length == 0)
152
166
  if isString(input)
153
167
  return ''
@@ -155,27 +169,23 @@ export undented = (input, level=undef, oneIndent="\t") =>
155
169
  return []
156
170
 
157
171
  # --- determine what to remove from beginning of each line
158
- if defined(level)
159
- assert isInteger(level), "level must be an integer"
160
- toRemove = indentation(level, oneIndent)
161
- else
162
- lMatches = lLines[0].match(/^\s*/)
163
- toRemove = lMatches[0]
164
- nToRemove = indentLevel(toRemove)
165
-
166
- lNewLines = []
167
- for line in lLines
168
- if isEmpty(line)
169
- lNewLines.push('')
170
- else
171
- if (line.indexOf(toRemove) != 0)
172
- throw new Error("remove #{OL(toRemove)} from #{OL(line)}")
173
- lNewLines.push(line.substr(nToRemove))
172
+ lMatches = lLines[0].match(/^\s*/)
173
+ toRemove = lMatches[0]
174
+ nToRemove = toRemove.length
175
+ if (nToRemove > 0)
176
+ lLines = lLines.map( (line) =>
177
+ if isEmpty(line)
178
+ return ''
179
+ else
180
+ assert (line.indexOf(toRemove)==0),
181
+ "can't remove #{OL(toRemove)} from #{OL(line)}"
182
+ return line.substr(nToRemove)
183
+ )
174
184
 
175
185
  if isString(input)
176
- return toBlock(lNewLines)
186
+ return toBlock(lLines)
177
187
  else
178
- return lNewLines
188
+ return lLines
179
189
 
180
190
  # ---------------------------------------------------------------------------
181
191
  # enclose - indent text, surround with pre and post
package/src/indent.js CHANGED
@@ -48,19 +48,11 @@ export var splitLine = (line, oneIndent = undef) => {
48
48
  return [indentLevel(prefix, oneIndent), str];
49
49
  };
50
50
 
51
- // ---------------------------------------------------------------------------
52
- // indentation - return appropriate indentation string for given level
53
- // export only to allow unit testing
54
- export var indentation = (level, oneIndent = "\t") => {
55
- assert(level >= 0, "indentation(): negative level");
56
- return oneIndent.repeat(level);
57
- };
58
-
59
51
  // ---------------------------------------------------------------------------
60
52
  // indentLevel - determine indent level of a string
61
53
  // it's OK if the string is ONLY indentation
62
54
  export var indentLevel = (line, oneIndent = undef) => {
63
- var ch, lMatches, len, level, prefix, prefixLen;
55
+ var i, lMatches, len, level, nSpaces, nTabs, prefix, prefixLen, ref, str;
64
56
  assert(isString(line), "not a string");
65
57
  // --- This will always match, and it's greedy
66
58
  if (lMatches = line.match(/^\s*/)) {
@@ -70,27 +62,59 @@ export var indentLevel = (line, oneIndent = undef) => {
70
62
  if (prefixLen === 0) {
71
63
  return 0;
72
64
  }
73
- if (defined(oneIndent)) {
74
- // --- prefix must be some multiple of oneIndent
75
- len = oneIndent.length;
76
- assert(prefixLen % len === 0, `prefix ${OL(prefix)} not a mult of ${OL(oneIndent)}`);
77
- level = prefixLen / len;
65
+ // --- Match \t* followed by \x20* (error if no match)
66
+ if (lMatches = prefix.match(/(\t*)(\x20*)/)) {
67
+ nTabs = lMatches[1].length;
68
+ nSpaces = lMatches[2].length;
78
69
  } else {
79
- ch = prefix.substring(0, 1);
80
- if (ch === "\t") {
81
- oneIndent = "\t";
82
- level = prefixLen;
83
- } else if (ch === ' ') {
84
- oneIndent = ' '.repeat(prefixLen);
85
- level = 1;
86
- } else {
87
- croak(`Bad Indentation in ${OL(line)}`);
70
+ croak("Invalid mix of TABs and spaces");
71
+ }
72
+ // --- oneIndent must be one of:
73
+ // undef
74
+ // a single TAB character
75
+ // some number of space characters
76
+ switch (oneIndent) {
77
+ case undef:
78
+ if (nTabs > 0) {
79
+ level = nTabs; // there may also be spaces, but we ignore them
80
+ oneIndent = "\t"; // may be used at end
81
+ } else {
82
+ assert(nSpaces > 0, "There must be TABS or spaces");
83
+ level = 1;
84
+ oneIndent = ' '.repeat(nSpaces); // may be used at end
85
+ }
86
+ break;
87
+ case "\t":
88
+ assert(nTabs > 0, "Expecting TAB indentation, found spaces");
89
+ // --- NOTE: there may be spaces, but they're not indentation
90
+ level = nTabs;
91
+ break;
92
+ default:
93
+ // --- oneIndent must be all space chars
94
+ assert(nTabs === 0, `Indentation has TABs but oneIndent = ${OL(oneIndent)}`);
95
+ assert(nSpaces % oneIndent.length === 0, `prefix ${OL(prefix)} not a mult of ${OL(oneIndent)}`);
96
+ level = nSpaces / oneIndent.length;
97
+ }
98
+ // --- If a block, i.e. multi-line string, then all lines must be
99
+ // at least at this level
100
+ if (line.indexOf("\n") >= 0) {
101
+ ref = toArray(line);
102
+ for (i = 0, len = ref.length; i < len; i++) {
103
+ str = ref[i];
104
+ assert(indentLevel(str, oneIndent) >= level, `indentLevel of ${OL(line)} can't be found`);
88
105
  }
89
106
  }
90
- assert(prefix === oneIndent.repeat(level), `prefix ${OL(prefix)} not a mult of ${OL(oneIndent)}`);
91
107
  return level;
92
108
  };
93
109
 
110
+ // ---------------------------------------------------------------------------
111
+ // indentation - return appropriate indentation string for given level
112
+ // export only to allow unit testing
113
+ export var indentation = (level, oneIndent = "\t") => {
114
+ assert(level >= 0, "indentation(): negative level");
115
+ return oneIndent.repeat(level);
116
+ };
117
+
94
118
  // ---------------------------------------------------------------------------
95
119
  // isUndented - true iff indentLevel(line) == 0
96
120
  export var isUndented = (line) => {
@@ -102,7 +126,7 @@ export var isUndented = (line) => {
102
126
  // indented - add indentation to each string in a block or array
103
127
  // - returns the same type as input, i.e. array or string
104
128
  export var indented = (input, level = 1, oneIndent = "\t") => {
105
- var i, lLines, len1, line, ref, toAdd;
129
+ var i, lLines, len, line, ref, toAdd;
106
130
  // --- level can be a string, in which case it is
107
131
  // pre-pended to each line of input
108
132
  if (isString(level)) {
@@ -123,7 +147,7 @@ export var indented = (input, level = 1, oneIndent = "\t") => {
123
147
  // else it splits the string into an array of lines
124
148
  lLines = [];
125
149
  ref = toArray(input);
126
- for (i = 0, len1 = ref.length; i < len1; i++) {
150
+ for (i = 0, len = ref.length; i < len; i++) {
127
151
  line = ref[i];
128
152
  if (isEmpty(line)) {
129
153
  lLines.push('');
@@ -142,28 +166,21 @@ export var indented = (input, level = 1, oneIndent = "\t") => {
142
166
  // ---------------------------------------------------------------------------
143
167
  // undented - string with 1st line indentation removed for each line
144
168
  // - ignore leading empty lines
145
- // - unless level is set, in which case exactly that
146
- // indentation is removed
147
169
  // - returns same type as text, i.e. either string or array
148
- export var undented = (input, level = undef, oneIndent = "\t") => {
149
- var i, lLines, lMatches, lNewLines, len1, line, nToRemove, toRemove;
150
- if (defined(level) && (level === 0)) {
151
- return input;
152
- }
153
- // --- Remove any leading blank lines, set lLines
170
+ export var undented = (input) => {
171
+ var lLines, lMatches, nToRemove, toRemove;
172
+ // --- If a string, convert to an array
154
173
  if (isString(input)) {
155
- if (lMatches = input.match(/^[\r\n]+(.*)$/s)) {
156
- input = lMatches[1];
157
- }
158
174
  lLines = toArray(input);
159
175
  } else if (isArray(input)) {
160
176
  lLines = input;
161
- while ((lLines.length > 0) && isEmpty(lLines[0])) {
162
- lLines.shift();
163
- }
164
177
  } else {
165
178
  croak("input not a string or array");
166
179
  }
180
+ // --- Remove leading blank lines
181
+ while ((lLines.length > 0) && isEmpty(lLines[0])) {
182
+ lLines.shift(); // remove
183
+ }
167
184
  if (lLines.length === 0) {
168
185
  if (isString(input)) {
169
186
  return '';
@@ -172,30 +189,23 @@ export var undented = (input, level = undef, oneIndent = "\t") => {
172
189
  }
173
190
  }
174
191
  // --- determine what to remove from beginning of each line
175
- if (defined(level)) {
176
- assert(isInteger(level), "level must be an integer");
177
- toRemove = indentation(level, oneIndent);
178
- } else {
179
- lMatches = lLines[0].match(/^\s*/);
180
- toRemove = lMatches[0];
181
- }
182
- nToRemove = indentLevel(toRemove);
183
- lNewLines = [];
184
- for (i = 0, len1 = lLines.length; i < len1; i++) {
185
- line = lLines[i];
186
- if (isEmpty(line)) {
187
- lNewLines.push('');
188
- } else {
189
- if (line.indexOf(toRemove) !== 0) {
190
- throw new Error(`remove ${OL(toRemove)} from ${OL(line)}`);
192
+ lMatches = lLines[0].match(/^\s*/);
193
+ toRemove = lMatches[0];
194
+ nToRemove = toRemove.length;
195
+ if (nToRemove > 0) {
196
+ lLines = lLines.map((line) => {
197
+ if (isEmpty(line)) {
198
+ return '';
199
+ } else {
200
+ assert(line.indexOf(toRemove) === 0, `can't remove ${OL(toRemove)} from ${OL(line)}`);
201
+ return line.substr(nToRemove);
191
202
  }
192
- lNewLines.push(line.substr(nToRemove));
193
- }
203
+ });
194
204
  }
195
205
  if (isString(input)) {
196
- return toBlock(lNewLines);
206
+ return toBlock(lLines);
197
207
  } else {
198
- return lNewLines;
208
+ return lLines;
199
209
  }
200
210
  };
201
211