Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81169 views
1
var assert = require("assert");
2
var types = require("./types");
3
var isString = types.builtInTypes.string;
4
var isNumber = types.builtInTypes.number;
5
var SourceLocation = types.namedTypes.SourceLocation;
6
var Position = types.namedTypes.Position;
7
var linesModule = require("./lines");
8
var comparePos = require("./util").comparePos;
9
10
function Mapping(sourceLines, sourceLoc, targetLoc) {
11
assert.ok(this instanceof Mapping);
12
assert.ok(sourceLines instanceof linesModule.Lines);
13
SourceLocation.assert(sourceLoc);
14
15
if (targetLoc) {
16
// In certain cases it's possible for targetLoc.{start,end}.column
17
// values to be negative, which technically makes them no longer
18
// valid SourceLocation nodes, so we need to be more forgiving.
19
assert.ok(
20
isNumber.check(targetLoc.start.line) &&
21
isNumber.check(targetLoc.start.column) &&
22
isNumber.check(targetLoc.end.line) &&
23
isNumber.check(targetLoc.end.column)
24
);
25
} else {
26
// Assume identity mapping if no targetLoc specified.
27
targetLoc = sourceLoc;
28
}
29
30
Object.defineProperties(this, {
31
sourceLines: { value: sourceLines },
32
sourceLoc: { value: sourceLoc },
33
targetLoc: { value: targetLoc }
34
});
35
}
36
37
var Mp = Mapping.prototype;
38
module.exports = Mapping;
39
40
Mp.slice = function(lines, start, end) {
41
assert.ok(lines instanceof linesModule.Lines);
42
Position.assert(start);
43
44
if (end) {
45
Position.assert(end);
46
} else {
47
end = lines.lastPos();
48
}
49
50
var sourceLines = this.sourceLines;
51
var sourceLoc = this.sourceLoc;
52
var targetLoc = this.targetLoc;
53
54
function skip(name) {
55
var sourceFromPos = sourceLoc[name];
56
var targetFromPos = targetLoc[name];
57
var targetToPos = start;
58
59
if (name === "end") {
60
targetToPos = end;
61
} else {
62
assert.strictEqual(name, "start");
63
}
64
65
return skipChars(
66
sourceLines, sourceFromPos,
67
lines, targetFromPos, targetToPos
68
);
69
}
70
71
if (comparePos(start, targetLoc.start) <= 0) {
72
if (comparePos(targetLoc.end, end) <= 0) {
73
targetLoc = {
74
start: subtractPos(targetLoc.start, start.line, start.column),
75
end: subtractPos(targetLoc.end, start.line, start.column)
76
};
77
78
// The sourceLoc can stay the same because the contents of the
79
// targetLoc have not changed.
80
81
} else if (comparePos(end, targetLoc.start) <= 0) {
82
return null;
83
84
} else {
85
sourceLoc = {
86
start: sourceLoc.start,
87
end: skip("end")
88
};
89
90
targetLoc = {
91
start: subtractPos(targetLoc.start, start.line, start.column),
92
end: subtractPos(end, start.line, start.column)
93
};
94
}
95
96
} else {
97
if (comparePos(targetLoc.end, start) <= 0) {
98
return null;
99
}
100
101
if (comparePos(targetLoc.end, end) <= 0) {
102
sourceLoc = {
103
start: skip("start"),
104
end: sourceLoc.end
105
};
106
107
targetLoc = {
108
// Same as subtractPos(start, start.line, start.column):
109
start: { line: 1, column: 0 },
110
end: subtractPos(targetLoc.end, start.line, start.column)
111
};
112
113
} else {
114
sourceLoc = {
115
start: skip("start"),
116
end: skip("end")
117
};
118
119
targetLoc = {
120
// Same as subtractPos(start, start.line, start.column):
121
start: { line: 1, column: 0 },
122
end: subtractPos(end, start.line, start.column)
123
};
124
}
125
}
126
127
return new Mapping(this.sourceLines, sourceLoc, targetLoc);
128
};
129
130
Mp.add = function(line, column) {
131
return new Mapping(this.sourceLines, this.sourceLoc, {
132
start: addPos(this.targetLoc.start, line, column),
133
end: addPos(this.targetLoc.end, line, column)
134
});
135
};
136
137
function addPos(toPos, line, column) {
138
return {
139
line: toPos.line + line - 1,
140
column: (toPos.line === 1)
141
? toPos.column + column
142
: toPos.column
143
};
144
}
145
146
Mp.subtract = function(line, column) {
147
return new Mapping(this.sourceLines, this.sourceLoc, {
148
start: subtractPos(this.targetLoc.start, line, column),
149
end: subtractPos(this.targetLoc.end, line, column)
150
});
151
};
152
153
function subtractPos(fromPos, line, column) {
154
return {
155
line: fromPos.line - line + 1,
156
column: (fromPos.line === line)
157
? fromPos.column - column
158
: fromPos.column
159
};
160
}
161
162
Mp.indent = function(by, skipFirstLine, noNegativeColumns) {
163
if (by === 0) {
164
return this;
165
}
166
167
var targetLoc = this.targetLoc;
168
var startLine = targetLoc.start.line;
169
var endLine = targetLoc.end.line;
170
171
if (skipFirstLine && startLine === 1 && endLine === 1) {
172
return this;
173
}
174
175
targetLoc = {
176
start: targetLoc.start,
177
end: targetLoc.end
178
};
179
180
if (!skipFirstLine || startLine > 1) {
181
var startColumn = targetLoc.start.column + by;
182
targetLoc.start = {
183
line: startLine,
184
column: noNegativeColumns
185
? Math.max(0, startColumn)
186
: startColumn
187
};
188
}
189
190
if (!skipFirstLine || endLine > 1) {
191
var endColumn = targetLoc.end.column + by;
192
targetLoc.end = {
193
line: endLine,
194
column: noNegativeColumns
195
? Math.max(0, endColumn)
196
: endColumn
197
};
198
}
199
200
return new Mapping(this.sourceLines, this.sourceLoc, targetLoc);
201
};
202
203
function skipChars(
204
sourceLines, sourceFromPos,
205
targetLines, targetFromPos, targetToPos
206
) {
207
assert.ok(sourceLines instanceof linesModule.Lines);
208
assert.ok(targetLines instanceof linesModule.Lines);
209
Position.assert(sourceFromPos);
210
Position.assert(targetFromPos);
211
Position.assert(targetToPos);
212
213
var targetComparison = comparePos(targetFromPos, targetToPos);
214
if (targetComparison === 0) {
215
// Trivial case: no characters to skip.
216
return sourceFromPos;
217
}
218
219
if (targetComparison < 0) {
220
// Skipping forward.
221
222
var sourceCursor = sourceLines.skipSpaces(sourceFromPos);
223
var targetCursor = targetLines.skipSpaces(targetFromPos);
224
225
var lineDiff = targetToPos.line - targetCursor.line;
226
sourceCursor.line += lineDiff;
227
targetCursor.line += lineDiff;
228
229
if (lineDiff > 0) {
230
// If jumping to later lines, reset columns to the beginnings
231
// of those lines.
232
sourceCursor.column = 0;
233
targetCursor.column = 0;
234
} else {
235
assert.strictEqual(lineDiff, 0);
236
}
237
238
while (comparePos(targetCursor, targetToPos) < 0 &&
239
targetLines.nextPos(targetCursor, true)) {
240
assert.ok(sourceLines.nextPos(sourceCursor, true));
241
assert.strictEqual(
242
sourceLines.charAt(sourceCursor),
243
targetLines.charAt(targetCursor)
244
);
245
}
246
247
} else {
248
// Skipping backward.
249
250
var sourceCursor = sourceLines.skipSpaces(sourceFromPos, true);
251
var targetCursor = targetLines.skipSpaces(targetFromPos, true);
252
253
var lineDiff = targetToPos.line - targetCursor.line;
254
sourceCursor.line += lineDiff;
255
targetCursor.line += lineDiff;
256
257
if (lineDiff < 0) {
258
// If jumping to earlier lines, reset columns to the ends of
259
// those lines.
260
sourceCursor.column = sourceLines.getLineLength(sourceCursor.line);
261
targetCursor.column = targetLines.getLineLength(targetCursor.line);
262
} else {
263
assert.strictEqual(lineDiff, 0);
264
}
265
266
while (comparePos(targetToPos, targetCursor) < 0 &&
267
targetLines.prevPos(targetCursor, true)) {
268
assert.ok(sourceLines.prevPos(sourceCursor, true));
269
assert.strictEqual(
270
sourceLines.charAt(sourceCursor),
271
targetLines.charAt(targetCursor)
272
);
273
}
274
}
275
276
return sourceCursor;
277
}
278
279