@boperators/plugin-ts-language-server 0.2.1 → 0.3.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.
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findOverloadEdits = findOverloadEdits;
4
+ exports.simplifyTypeName = simplifyTypeName;
5
+ exports.getOverloadHoverInfo = getOverloadHoverInfo;
6
+ const boperators_1 = require("boperators");
7
+ // ----- Internal helpers -----
8
+ /**
9
+ * Recursively resolve the effective type of an expression, accounting for
10
+ * operator overloads. For sub-expressions that match a registered overload,
11
+ * uses the overload's declared return type instead of what TypeScript infers
12
+ * (since TS doesn't know about operator overloading).
13
+ */
14
+ function resolveOverloadedType(node, overloadStore) {
15
+ if (boperators_1.Node.isParenthesizedExpression(node)) {
16
+ return resolveOverloadedType(node.getExpression(), overloadStore);
17
+ }
18
+ if (boperators_1.Node.isBinaryExpression(node)) {
19
+ const operatorKind = node.getOperatorToken().getKind();
20
+ if ((0, boperators_1.isOperatorSyntaxKind)(operatorKind)) {
21
+ const leftType = resolveOverloadedType(node.getLeft(), overloadStore);
22
+ const rightType = resolveOverloadedType(node.getRight(), overloadStore);
23
+ const overload = overloadStore.findOverload(operatorKind, leftType, rightType);
24
+ if (overload)
25
+ return overload.returnType;
26
+ }
27
+ }
28
+ if (boperators_1.Node.isPrefixUnaryExpression(node)) {
29
+ const operatorKind = node.getOperatorToken();
30
+ if ((0, boperators_1.isPrefixUnaryOperatorSyntaxKind)(operatorKind)) {
31
+ const operandType = resolveOverloadedType(node.getOperand(), overloadStore);
32
+ const overload = overloadStore.findPrefixUnaryOverload(operatorKind, operandType);
33
+ if (overload)
34
+ return overload.returnType;
35
+ }
36
+ }
37
+ if (boperators_1.Node.isPostfixUnaryExpression(node)) {
38
+ const operatorKind = node.getOperatorToken();
39
+ if ((0, boperators_1.isPostfixUnaryOperatorSyntaxKind)(operatorKind)) {
40
+ const operandType = resolveOverloadedType(node.getOperand(), overloadStore);
41
+ const overload = overloadStore.findPostfixUnaryOverload(operatorKind, operandType);
42
+ if (overload)
43
+ return overload.returnType;
44
+ }
45
+ }
46
+ return (0, boperators_1.resolveExpressionType)(node);
47
+ }
48
+ // ----- Exported helpers -----
49
+ /**
50
+ * Before transformation, find all expressions (binary, prefix unary, postfix unary)
51
+ * that match registered overloads and record their operator token positions.
52
+ * This is used to provide hover info for overloaded operators.
53
+ */
54
+ function findOverloadEdits(sourceFile, overloadStore) {
55
+ const edits = [];
56
+ const binaryExpressions = sourceFile.getDescendantsOfKind(boperators_1.SyntaxKind.BinaryExpression);
57
+ for (const expression of binaryExpressions) {
58
+ const operatorToken = expression.getOperatorToken();
59
+ const operatorKind = operatorToken.getKind();
60
+ if (!(0, boperators_1.isOperatorSyntaxKind)(operatorKind))
61
+ continue;
62
+ const leftType = resolveOverloadedType(expression.getLeft(), overloadStore);
63
+ const rightType = resolveOverloadedType(expression.getRight(), overloadStore);
64
+ const overloadDesc = overloadStore.findOverload(operatorKind, leftType, rightType);
65
+ if (!overloadDesc)
66
+ continue;
67
+ edits.push({
68
+ operatorStart: operatorToken.getStart(),
69
+ operatorEnd: operatorToken.getEnd(),
70
+ hoverStart: expression.getLeft().getEnd(),
71
+ hoverEnd: expression.getRight().getStart(),
72
+ exprStart: expression.getStart(),
73
+ exprEnd: expression.getEnd(),
74
+ className: overloadDesc.className,
75
+ classFilePath: overloadDesc.classFilePath,
76
+ operatorString: overloadDesc.operatorString,
77
+ returnType: overloadDesc.returnType,
78
+ lhsType: leftType,
79
+ rhsType: rightType,
80
+ isStatic: overloadDesc.isStatic,
81
+ kind: "binary",
82
+ });
83
+ }
84
+ // Scan prefix unary expressions
85
+ const prefixExpressions = sourceFile.getDescendantsOfKind(boperators_1.SyntaxKind.PrefixUnaryExpression);
86
+ for (const expression of prefixExpressions) {
87
+ const operatorKind = expression.getOperatorToken();
88
+ if (!(0, boperators_1.isPrefixUnaryOperatorSyntaxKind)(operatorKind))
89
+ continue;
90
+ const operandType = resolveOverloadedType(expression.getOperand(), overloadStore);
91
+ const overloadDesc = overloadStore.findPrefixUnaryOverload(operatorKind, operandType);
92
+ if (!overloadDesc)
93
+ continue;
94
+ const exprStart = expression.getStart();
95
+ const operand = expression.getOperand();
96
+ edits.push({
97
+ operatorStart: exprStart,
98
+ operatorEnd: operand.getStart(),
99
+ hoverStart: exprStart,
100
+ hoverEnd: operand.getStart(),
101
+ exprStart,
102
+ exprEnd: expression.getEnd(),
103
+ className: overloadDesc.className,
104
+ classFilePath: overloadDesc.classFilePath,
105
+ operatorString: overloadDesc.operatorString,
106
+ returnType: overloadDesc.returnType,
107
+ operandType: operandType,
108
+ isStatic: overloadDesc.isStatic,
109
+ kind: "prefixUnary",
110
+ });
111
+ }
112
+ // Scan postfix unary expressions
113
+ const postfixExpressions = sourceFile.getDescendantsOfKind(boperators_1.SyntaxKind.PostfixUnaryExpression);
114
+ for (const expression of postfixExpressions) {
115
+ const operatorKind = expression.getOperatorToken();
116
+ if (!(0, boperators_1.isPostfixUnaryOperatorSyntaxKind)(operatorKind))
117
+ continue;
118
+ const operandType = resolveOverloadedType(expression.getOperand(), overloadStore);
119
+ const overloadDesc = overloadStore.findPostfixUnaryOverload(operatorKind, operandType);
120
+ if (!overloadDesc)
121
+ continue;
122
+ const operand = expression.getOperand();
123
+ const operatorStart = operand.getEnd();
124
+ edits.push({
125
+ operatorStart,
126
+ operatorEnd: expression.getEnd(),
127
+ hoverStart: operatorStart,
128
+ hoverEnd: expression.getEnd(),
129
+ exprStart: expression.getStart(),
130
+ exprEnd: expression.getEnd(),
131
+ className: overloadDesc.className,
132
+ classFilePath: overloadDesc.classFilePath,
133
+ operatorString: overloadDesc.operatorString,
134
+ returnType: overloadDesc.returnType,
135
+ operandType: operandType,
136
+ isStatic: overloadDesc.isStatic,
137
+ kind: "postfixUnary",
138
+ });
139
+ }
140
+ return edits;
141
+ }
142
+ /**
143
+ * Strip fully-qualified import paths from a type name so that
144
+ * `import("/path/to/Vec2").Vec2` is displayed as just `Vec2`.
145
+ */
146
+ function simplifyTypeName(typeName) {
147
+ return typeName.replace(/\bimport\("[^"]*"\)\./g, "");
148
+ }
149
+ /**
150
+ * Build a QuickInfo response for hovering over an operator token
151
+ * that corresponds to an overloaded operator. Extracts the function
152
+ * signature and JSDoc from the overload definition.
153
+ */
154
+ function getOverloadHoverInfo(ts, project, edit) {
155
+ var _a, _b, _c, _d;
156
+ try {
157
+ // Extract JSDoc from the method declaration (or its first overload signature).
158
+ let docText;
159
+ const classSourceFile = project.getSourceFile(edit.classFilePath);
160
+ if (classSourceFile) {
161
+ const classDecl = classSourceFile.getClass(edit.className);
162
+ if (classDecl) {
163
+ const method = classDecl
164
+ .getMethods()
165
+ .find((m) => (0, boperators_1.getOperatorStringFromMethod)(m) === edit.operatorString);
166
+ if (method) {
167
+ const overloads = method.getOverloads();
168
+ const source = overloads.length > 0 ? overloads[0] : method;
169
+ const jsDocs = source.getJsDocs();
170
+ if (jsDocs.length > 0) {
171
+ const raw = jsDocs[0].getText();
172
+ docText = raw
173
+ .replace(/^\/\*\*\s*/, "")
174
+ .replace(/\s*\*\/$/, "")
175
+ .replace(/^\s*\* ?/gm, "")
176
+ .trim();
177
+ }
178
+ }
179
+ }
180
+ }
181
+ // Build display signature parts based on overload kind.
182
+ // Types are sourced from the resolved expression types stored at scan time.
183
+ const returnTypeName = simplifyTypeName(edit.returnType);
184
+ const displayParts = [];
185
+ if (edit.kind === "prefixUnary") {
186
+ // Prefix unary: "-Vec2 = Vec2"
187
+ displayParts.push({ text: edit.operatorString, kind: "operator" });
188
+ displayParts.push({
189
+ text: simplifyTypeName((_a = edit.operandType) !== null && _a !== void 0 ? _a : edit.className),
190
+ kind: "className",
191
+ });
192
+ if (returnTypeName !== "void") {
193
+ displayParts.push({ text: " = ", kind: "punctuation" });
194
+ displayParts.push({ text: returnTypeName, kind: "className" });
195
+ }
196
+ }
197
+ else if (edit.kind === "postfixUnary") {
198
+ // Postfix unary: "Vec2++"
199
+ displayParts.push({ text: edit.className, kind: "className" });
200
+ displayParts.push({ text: edit.operatorString, kind: "operator" });
201
+ }
202
+ else if (edit.isStatic) {
203
+ // Binary static: "LhsType + RhsType = ReturnType"
204
+ displayParts.push({
205
+ text: simplifyTypeName((_b = edit.lhsType) !== null && _b !== void 0 ? _b : edit.className),
206
+ kind: "className",
207
+ });
208
+ displayParts.push({ text: " ", kind: "space" });
209
+ displayParts.push({ text: edit.operatorString, kind: "operator" });
210
+ displayParts.push({ text: " ", kind: "space" });
211
+ displayParts.push({
212
+ text: simplifyTypeName((_c = edit.rhsType) !== null && _c !== void 0 ? _c : edit.className),
213
+ kind: "className",
214
+ });
215
+ if (returnTypeName !== "void") {
216
+ displayParts.push({ text: " = ", kind: "punctuation" });
217
+ displayParts.push({ text: returnTypeName, kind: "className" });
218
+ }
219
+ }
220
+ else {
221
+ // Binary instance: "ClassName += RhsType"
222
+ displayParts.push({ text: edit.className, kind: "className" });
223
+ displayParts.push({ text: " ", kind: "space" });
224
+ displayParts.push({ text: edit.operatorString, kind: "operator" });
225
+ displayParts.push({ text: " ", kind: "space" });
226
+ displayParts.push({
227
+ text: simplifyTypeName((_d = edit.rhsType) !== null && _d !== void 0 ? _d : "unknown"),
228
+ kind: "className",
229
+ });
230
+ if (returnTypeName !== "void") {
231
+ displayParts.push({ text: " = ", kind: "punctuation" });
232
+ displayParts.push({ text: returnTypeName, kind: "className" });
233
+ }
234
+ }
235
+ return {
236
+ kind: ts.ScriptElementKind.functionElement,
237
+ kindModifiers: edit.isStatic ? "static" : "",
238
+ textSpan: {
239
+ start: edit.operatorStart,
240
+ length: edit.operatorEnd - edit.operatorStart,
241
+ },
242
+ displayParts,
243
+ documentation: docText ? [{ text: docText, kind: "text" }] : undefined,
244
+ tags: [],
245
+ };
246
+ }
247
+ catch (_e) {
248
+ return undefined;
249
+ }
250
+ }
package/dist/index.js CHANGED
@@ -1,291 +1,7 @@
1
1
  "use strict";
2
2
  const boperators_1 = require("boperators");
3
+ const helpers_1 = require("./helpers");
3
4
  const SourceMap_1 = require("./SourceMap");
4
- // ----- Overload edit scanner -----
5
- /**
6
- * Before transformation, find all expressions (binary, prefix unary, postfix unary)
7
- * that match registered overloads and record their operator token positions.
8
- * This is used to provide hover info for overloaded operators.
9
- */
10
- /**
11
- * Recursively resolve the effective type of an expression, accounting for
12
- * operator overloads. For sub-expressions that match a registered overload,
13
- * uses the overload's declared return type instead of what TypeScript infers
14
- * (since TS doesn't know about operator overloading).
15
- */
16
- function resolveOverloadedType(node, overloadStore) {
17
- if (boperators_1.Node.isParenthesizedExpression(node)) {
18
- return resolveOverloadedType(node.getExpression(), overloadStore);
19
- }
20
- if (boperators_1.Node.isBinaryExpression(node)) {
21
- const operatorKind = node.getOperatorToken().getKind();
22
- if ((0, boperators_1.isOperatorSyntaxKind)(operatorKind)) {
23
- const leftType = resolveOverloadedType(node.getLeft(), overloadStore);
24
- const rightType = resolveOverloadedType(node.getRight(), overloadStore);
25
- const overload = overloadStore.findOverload(operatorKind, leftType, rightType);
26
- if (overload)
27
- return overload.returnType;
28
- }
29
- }
30
- if (boperators_1.Node.isPrefixUnaryExpression(node)) {
31
- const operatorKind = node.getOperatorToken();
32
- if ((0, boperators_1.isPrefixUnaryOperatorSyntaxKind)(operatorKind)) {
33
- const operandType = resolveOverloadedType(node.getOperand(), overloadStore);
34
- const overload = overloadStore.findPrefixUnaryOverload(operatorKind, operandType);
35
- if (overload)
36
- return overload.returnType;
37
- }
38
- }
39
- if (boperators_1.Node.isPostfixUnaryExpression(node)) {
40
- const operatorKind = node.getOperatorToken();
41
- if ((0, boperators_1.isPostfixUnaryOperatorSyntaxKind)(operatorKind)) {
42
- const operandType = resolveOverloadedType(node.getOperand(), overloadStore);
43
- const overload = overloadStore.findPostfixUnaryOverload(operatorKind, operandType);
44
- if (overload)
45
- return overload.returnType;
46
- }
47
- }
48
- return (0, boperators_1.resolveExpressionType)(node);
49
- }
50
- function findOverloadEdits(sourceFile, overloadStore) {
51
- const edits = [];
52
- const binaryExpressions = sourceFile.getDescendantsOfKind(boperators_1.SyntaxKind.BinaryExpression);
53
- for (const expression of binaryExpressions) {
54
- const operatorToken = expression.getOperatorToken();
55
- const operatorKind = operatorToken.getKind();
56
- if (!(0, boperators_1.isOperatorSyntaxKind)(operatorKind))
57
- continue;
58
- const leftType = resolveOverloadedType(expression.getLeft(), overloadStore);
59
- const rightType = resolveOverloadedType(expression.getRight(), overloadStore);
60
- const overloadDesc = overloadStore.findOverload(operatorKind, leftType, rightType);
61
- if (!overloadDesc)
62
- continue;
63
- edits.push({
64
- operatorStart: operatorToken.getStart(),
65
- operatorEnd: operatorToken.getEnd(),
66
- hoverStart: expression.getLeft().getEnd(),
67
- hoverEnd: expression.getRight().getStart(),
68
- exprStart: expression.getStart(),
69
- exprEnd: expression.getEnd(),
70
- className: overloadDesc.className,
71
- classFilePath: overloadDesc.classFilePath,
72
- operatorString: overloadDesc.operatorString,
73
- index: overloadDesc.index,
74
- isStatic: overloadDesc.isStatic,
75
- kind: "binary",
76
- });
77
- }
78
- // Scan prefix unary expressions
79
- const prefixExpressions = sourceFile.getDescendantsOfKind(boperators_1.SyntaxKind.PrefixUnaryExpression);
80
- for (const expression of prefixExpressions) {
81
- const operatorKind = expression.getOperatorToken();
82
- if (!(0, boperators_1.isPrefixUnaryOperatorSyntaxKind)(operatorKind))
83
- continue;
84
- const operandType = resolveOverloadedType(expression.getOperand(), overloadStore);
85
- const overloadDesc = overloadStore.findPrefixUnaryOverload(operatorKind, operandType);
86
- if (!overloadDesc)
87
- continue;
88
- const exprStart = expression.getStart();
89
- const operand = expression.getOperand();
90
- edits.push({
91
- operatorStart: exprStart,
92
- operatorEnd: operand.getStart(),
93
- hoverStart: exprStart,
94
- hoverEnd: operand.getStart(),
95
- exprStart,
96
- exprEnd: expression.getEnd(),
97
- className: overloadDesc.className,
98
- classFilePath: overloadDesc.classFilePath,
99
- operatorString: overloadDesc.operatorString,
100
- index: overloadDesc.index,
101
- isStatic: overloadDesc.isStatic,
102
- kind: "prefixUnary",
103
- });
104
- }
105
- // Scan postfix unary expressions
106
- const postfixExpressions = sourceFile.getDescendantsOfKind(boperators_1.SyntaxKind.PostfixUnaryExpression);
107
- for (const expression of postfixExpressions) {
108
- const operatorKind = expression.getOperatorToken();
109
- if (!(0, boperators_1.isPostfixUnaryOperatorSyntaxKind)(operatorKind))
110
- continue;
111
- const operandType = resolveOverloadedType(expression.getOperand(), overloadStore);
112
- const overloadDesc = overloadStore.findPostfixUnaryOverload(operatorKind, operandType);
113
- if (!overloadDesc)
114
- continue;
115
- const operand = expression.getOperand();
116
- const operatorStart = operand.getEnd();
117
- edits.push({
118
- operatorStart,
119
- operatorEnd: expression.getEnd(),
120
- hoverStart: operatorStart,
121
- hoverEnd: expression.getEnd(),
122
- exprStart: expression.getStart(),
123
- exprEnd: expression.getEnd(),
124
- className: overloadDesc.className,
125
- classFilePath: overloadDesc.classFilePath,
126
- operatorString: overloadDesc.operatorString,
127
- index: overloadDesc.index,
128
- isStatic: overloadDesc.isStatic,
129
- kind: "postfixUnary",
130
- });
131
- }
132
- return edits;
133
- }
134
- // ----- Overload hover info -----
135
- /**
136
- * Build a QuickInfo response for hovering over an operator token
137
- * that corresponds to an overloaded operator. Extracts the function
138
- * signature and JSDoc from the overload definition.
139
- */
140
- function getOverloadHoverInfo(ts, project, edit) {
141
- try {
142
- const classSourceFile = project.getSourceFile(edit.classFilePath);
143
- if (!classSourceFile)
144
- return undefined;
145
- const classDecl = classSourceFile.getClass(edit.className);
146
- if (!classDecl)
147
- return undefined;
148
- // Find the property with the matching operator string
149
- const prop = classDecl.getProperties().find((p) => {
150
- if (!boperators_1.Node.isPropertyDeclaration(p))
151
- return false;
152
- return (0, boperators_1.getOperatorStringFromProperty)(p) === edit.operatorString;
153
- });
154
- if (!prop || !boperators_1.Node.isPropertyDeclaration(prop))
155
- return undefined;
156
- // Extract param types and return type from either the initializer (regular
157
- // .ts files) or the type annotation (.d.ts files where initializers are
158
- // stripped by TypeScript's declaration emit).
159
- let params = [];
160
- let returnTypeName;
161
- let docText;
162
- const initializer = (0, boperators_1.unwrapInitializer)(prop.getInitializer());
163
- if (initializer && boperators_1.Node.isArrayLiteralExpression(initializer)) {
164
- const element = initializer.getElements()[edit.index];
165
- if (!element ||
166
- (!boperators_1.Node.isFunctionExpression(element) && !boperators_1.Node.isArrowFunction(element)))
167
- return undefined;
168
- const nonThisParams = element
169
- .getParameters()
170
- .filter((p) => p.getName() !== "this");
171
- params = nonThisParams.map((p) => ({
172
- typeName: p.getType().getText(element),
173
- }));
174
- returnTypeName = element.getReturnType().getText(element);
175
- const jsDocs = element.getJsDocs();
176
- if (jsDocs.length > 0) {
177
- const raw = jsDocs[0].getText();
178
- docText = raw
179
- .replace(/^\/\*\*\s*/, "")
180
- .replace(/\s*\*\/$/, "")
181
- .replace(/^\s*\* ?/gm, "")
182
- .trim();
183
- }
184
- }
185
- else {
186
- // Type-annotation fallback for .d.ts files
187
- const propertyType = prop.getType();
188
- if (!propertyType.isTuple())
189
- return undefined;
190
- const tupleElements = propertyType.getTupleElements();
191
- if (edit.index >= tupleElements.length)
192
- return undefined;
193
- const elementType = tupleElements[edit.index];
194
- const callSigs = elementType.getCallSignatures();
195
- if (callSigs.length === 0)
196
- return undefined;
197
- const sig = callSigs[0];
198
- for (const sym of sig.getParameters()) {
199
- if (sym.getName() === "this")
200
- continue;
201
- const decl = sym.getValueDeclaration();
202
- if (!decl)
203
- continue;
204
- params.push({ typeName: decl.getType().getText(prop) });
205
- }
206
- returnTypeName = sig.getReturnType().getText(prop);
207
- }
208
- // Build display signature parts based on overload kind
209
- const displayParts = [];
210
- if (edit.kind === "prefixUnary") {
211
- // Prefix unary: "-Vector3 = Vector3"
212
- displayParts.push({
213
- text: edit.operatorString,
214
- kind: "operator",
215
- });
216
- const operandType = params.length >= 1 ? params[0].typeName : edit.className;
217
- displayParts.push({ text: operandType, kind: "className" });
218
- if (returnTypeName !== "void") {
219
- displayParts.push({ text: " = ", kind: "punctuation" });
220
- displayParts.push({
221
- text: returnTypeName,
222
- kind: "className",
223
- });
224
- }
225
- }
226
- else if (edit.kind === "postfixUnary") {
227
- // Postfix unary: "Vector3++"
228
- displayParts.push({ text: edit.className, kind: "className" });
229
- displayParts.push({
230
- text: edit.operatorString,
231
- kind: "operator",
232
- });
233
- }
234
- else if (edit.isStatic && params.length >= 2) {
235
- // Binary static: "LhsType + RhsType = ReturnType"
236
- const lhsType = params[0].typeName;
237
- const rhsType = params[1].typeName;
238
- displayParts.push({ text: lhsType, kind: "className" });
239
- displayParts.push({ text: " ", kind: "space" });
240
- displayParts.push({
241
- text: edit.operatorString,
242
- kind: "operator",
243
- });
244
- displayParts.push({ text: " ", kind: "space" });
245
- displayParts.push({ text: rhsType, kind: "className" });
246
- if (returnTypeName !== "void") {
247
- displayParts.push({ text: " = ", kind: "punctuation" });
248
- displayParts.push({
249
- text: returnTypeName,
250
- kind: "className",
251
- });
252
- }
253
- }
254
- else {
255
- // Binary instance: "ClassName += RhsType"
256
- const rhsType = params.length >= 1 ? params[0].typeName : "unknown";
257
- displayParts.push({ text: edit.className, kind: "className" });
258
- displayParts.push({ text: " ", kind: "space" });
259
- displayParts.push({
260
- text: edit.operatorString,
261
- kind: "operator",
262
- });
263
- displayParts.push({ text: " ", kind: "space" });
264
- displayParts.push({ text: rhsType, kind: "className" });
265
- if (returnTypeName !== "void") {
266
- displayParts.push({ text: " = ", kind: "punctuation" });
267
- displayParts.push({
268
- text: returnTypeName,
269
- kind: "className",
270
- });
271
- }
272
- }
273
- return {
274
- kind: ts.ScriptElementKind.functionElement,
275
- kindModifiers: edit.isStatic ? "static" : "",
276
- textSpan: {
277
- start: edit.operatorStart,
278
- length: edit.operatorEnd - edit.operatorStart,
279
- },
280
- displayParts,
281
- documentation: docText ? [{ text: docText, kind: "text" }] : undefined,
282
- tags: [],
283
- };
284
- }
285
- catch (_a) {
286
- return undefined;
287
- }
288
- }
289
5
  // ----- LanguageService proxy -----
290
6
  function getSourceMapForFile(cache, fileName) {
291
7
  const entry = cache.get(fileName);
@@ -293,15 +9,14 @@ function getSourceMapForFile(cache, fileName) {
293
9
  return undefined;
294
10
  return entry.sourceMap;
295
11
  }
296
- function remapDiagnosticSpan(diag, sourceMap) {
297
- if (diag.start !== undefined && diag.length !== undefined) {
298
- const remapped = sourceMap.remapSpan({
299
- start: diag.start,
300
- length: diag.length,
301
- });
302
- diag.start = remapped.start;
303
- diag.length = remapped.length;
304
- }
12
+ function withRemappedSpan(diag, sourceMap) {
13
+ if (diag.start === undefined || diag.length === undefined)
14
+ return diag;
15
+ const remapped = sourceMap.remapSpan({
16
+ start: diag.start,
17
+ length: diag.length,
18
+ });
19
+ return Object.assign(Object.assign({}, diag), { start: remapped.start, length: remapped.length });
305
20
  }
306
21
  function createProxy(ts, ls, cache, project) {
307
22
  // Copy all methods from the underlying language service
@@ -320,55 +35,37 @@ function createProxy(ts, ls, cache, project) {
320
35
  }
321
36
  return false;
322
37
  };
38
+ function remapDiagnostics(result, entry) {
39
+ const sourceMap = (entry === null || entry === void 0 ? void 0 : entry.sourceMap.isEmpty) === false ? entry.sourceMap : undefined;
40
+ if (!sourceMap)
41
+ return result;
42
+ return result.map((diag) => {
43
+ var _a;
44
+ const remapped = withRemappedSpan(diag, sourceMap);
45
+ if (!((_a = remapped.relatedInformation) === null || _a === void 0 ? void 0 : _a.length))
46
+ return remapped;
47
+ return Object.assign(Object.assign({}, remapped), { relatedInformation: remapped.relatedInformation.map((related) => {
48
+ const relatedMap = related.file
49
+ ? getSourceMapForFile(cache, related.file.fileName)
50
+ : undefined;
51
+ return relatedMap ? withRemappedSpan(related, relatedMap) : related;
52
+ }) });
53
+ });
54
+ }
323
55
  proxy.getSemanticDiagnostics = (fileName) => {
324
56
  const result = ls.getSemanticDiagnostics(fileName);
325
57
  const entry = cache.get(fileName);
326
- const sourceMap = (entry === null || entry === void 0 ? void 0 : entry.sourceMap.isEmpty) === false ? entry.sourceMap : undefined;
327
- if (sourceMap) {
328
- for (const diag of result) {
329
- remapDiagnosticSpan(diag, sourceMap);
330
- if (diag.relatedInformation) {
331
- for (const related of diag.relatedInformation) {
332
- const relatedMap = related.file
333
- ? getSourceMapForFile(cache, related.file.fileName)
334
- : undefined;
335
- if (relatedMap)
336
- remapDiagnosticSpan(related, relatedMap);
337
- }
338
- }
339
- }
340
- }
341
- return result.filter((diag) => !isOverloadSuppressed(diag.code, diag.start, entry));
58
+ return remapDiagnostics(result, entry).filter((diag) => !isOverloadSuppressed(diag.code, diag.start, entry));
342
59
  };
343
60
  proxy.getSyntacticDiagnostics = (fileName) => {
344
61
  const result = ls.getSyntacticDiagnostics(fileName);
345
62
  const entry = cache.get(fileName);
346
- const sourceMap = (entry === null || entry === void 0 ? void 0 : entry.sourceMap.isEmpty) === false ? entry.sourceMap : undefined;
347
- if (sourceMap) {
348
- for (const diag of result) {
349
- remapDiagnosticSpan(diag, sourceMap);
350
- if (diag.relatedInformation) {
351
- for (const related of diag.relatedInformation) {
352
- const relatedMap = related.file
353
- ? getSourceMapForFile(cache, related.file.fileName)
354
- : undefined;
355
- if (relatedMap)
356
- remapDiagnosticSpan(related, relatedMap);
357
- }
358
- }
359
- }
360
- }
361
- return result.filter((diag) => !isOverloadSuppressed(diag.code, diag.start, entry));
63
+ return remapDiagnostics(result, entry).filter((diag) => !isOverloadSuppressed(diag.code, diag.start, entry));
362
64
  };
363
65
  proxy.getSuggestionDiagnostics = (fileName) => {
364
66
  const result = ls.getSuggestionDiagnostics(fileName);
365
- const sourceMap = getSourceMapForFile(cache, fileName);
366
- if (!sourceMap)
367
- return result;
368
- for (const diag of result) {
369
- remapDiagnosticSpan(diag, sourceMap);
370
- }
371
- return result;
67
+ const entry = cache.get(fileName);
68
+ return remapDiagnostics(result, entry);
372
69
  };
373
70
  // --- Hover: remap input position + output span, custom operator hover ---
374
71
  proxy.getQuickInfoAtPosition = (fileName, position) => {
@@ -377,7 +74,7 @@ function createProxy(ts, ls, cache, project) {
377
74
  if (entry) {
378
75
  const operatorEdit = entry.overloadEdits.find((e) => position >= e.hoverStart && position < e.hoverEnd);
379
76
  if (operatorEdit) {
380
- const hoverInfo = getOverloadHoverInfo(ts, project, operatorEdit);
77
+ const hoverInfo = (0, helpers_1.getOverloadHoverInfo)(ts, project, operatorEdit);
381
78
  if (hoverInfo)
382
79
  return hoverInfo;
383
80
  }
@@ -666,7 +363,7 @@ module.exports = function init(modules) {
666
363
  // Before transforming, scan for overloaded expressions
667
364
  // so we can record their operator positions for hover info.
668
365
  const sourceFile = project.getSourceFileOrThrow(fileName);
669
- const overloadEdits = findOverloadEdits(sourceFile, overloadStore);
366
+ const overloadEdits = (0, helpers_1.findOverloadEdits)(sourceFile, overloadStore);
670
367
  // Transform expressions (returns text + source map)
671
368
  const result = overloadInjector.overloadFile(fileName);
672
369
  cache.set(fileName, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boperators/plugin-ts-language-server",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "license": "MIT",
5
5
  "description": "TypeScript Language Server plugin for boperators - IDE support with source mapping.",
6
6
  "repository": {
@@ -27,6 +27,7 @@
27
27
  "scripts": {
28
28
  "build": "tsc",
29
29
  "watch": "tsc --watch",
30
+ "test": "bun test",
30
31
  "prepublish": "bun run build"
31
32
  },
32
33
  "files": [
@@ -36,7 +37,7 @@
36
37
  "dist"
37
38
  ],
38
39
  "peerDependencies": {
39
- "boperators": "0.2.1",
40
+ "boperators": "0.3.0",
40
41
  "typescript": ">=5.0.0 <5.10.0"
41
42
  },
42
43
  "devDependencies": {