@ripple-ts/language-server 0.2.178 → 0.2.179

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@ripple-ts/language-server",
3
- "version": "0.2.178",
3
+ "version": "0.2.179",
4
4
  "description": "Language Server Protocol implementation for Ripple",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -19,7 +19,7 @@
19
19
  "volar-service-typescript": "0.0.65",
20
20
  "vscode-languageserver-textdocument": "^1.0.12",
21
21
  "vscode-uri": "^3.1.0",
22
- "@ripple-ts/typescript-plugin": "0.2.178"
22
+ "@ripple-ts/typescript-plugin": "0.2.179"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "typescript": "^5.9.2"
@@ -1,18 +1,8 @@
1
1
  /** @import { LanguageServicePlugin } from '@volar/language-server' */
2
- /** @import { RippleVirtualCode } from '@ripple-ts/typescript-plugin/src/language.js') */
3
2
 
4
- const { URI } = require('vscode-uri');
3
+ const { getVirtualCode, createLogging } = require('./utils.js');
5
4
 
6
- const DEBUG = process.env.RIPPLE_DEBUG === 'true';
7
-
8
- /**
9
- * @param {...unknown} args
10
- */
11
- function log(...args) {
12
- if (DEBUG) {
13
- console.log('[Ripple Auto-Insert]', ...args);
14
- }
15
- }
5
+ const { log } = createLogging('[Ripple Auto-Insert Plugin]');
16
6
 
17
7
  /**
18
8
  * List of HTML void/self-closing elements that don't need closing tags
@@ -74,18 +64,7 @@ function createAutoInsertPlugin() {
74
64
  return null;
75
65
  }
76
66
 
77
- const uri = URI.parse(document.uri);
78
- const decoded = context.decodeEmbeddedDocumentUri(uri);
79
-
80
- if (!decoded) {
81
- return null;
82
- }
83
-
84
- const [sourceUri, virtualCodeId] = decoded;
85
- const sourceScript = context.language.scripts.get(sourceUri);
86
- const virtualCode = /** @type {RippleVirtualCode } */ (
87
- sourceScript?.generated?.embeddedCodes.get(virtualCodeId)
88
- );
67
+ const virtualCode = getVirtualCode(document, context);
89
68
 
90
69
  // Map position back to source
91
70
  const offset = document.offsetAt(position);
@@ -1,32 +1,12 @@
1
- const { RippleVirtualCode } = require('@ripple-ts/typescript-plugin/src/language.js');
2
- const { URI } = require('vscode-uri');
3
-
4
1
  /**
5
- * @typedef {import('@volar/language-server').LanguageServicePlugin} LanguageServicePlugin
6
- * @typedef {import('@volar/language-server').LanguageServiceContext} LanguageServiceContext
7
- * @typedef {import('@volar/language-server').Diagnostic} Diagnostic
8
- * @typedef {import('@volar/language-server').DiagnosticSeverity} DiagnosticSeverity
9
- * @typedef {import('@volar/language-server').Position} Position
10
- * @typedef {import('vscode-languageserver-textdocument').TextDocument} TextDocument
2
+ * @import {Diagnostic, LanguageServicePlugin, LanguageServiceContext} from '@volar/language-server'
3
+ * @import {TextDocument} from 'vscode-languageserver-textdocument'
11
4
  */
12
5
 
13
- const DEBUG = process.env.RIPPLE_DEBUG === 'true';
6
+ const { getVirtualCode, createLogging } = require('./utils.js');
14
7
 
15
- /**
16
- * @param {...unknown} args
17
- */
18
- function log(...args) {
19
- if (DEBUG) {
20
- console.log('[Ripple Language]', ...args);
21
- }
22
- }
23
-
24
- /**
25
- * @param {...unknown} args
26
- */
27
- function logError(...args) {
28
- console.error('[Ripple Language ERROR]', ...args);
29
- }
8
+ const { log, logError } = createLogging('[Ripple Compile Error Diagnostic Plugin]');
9
+ const { DiagnosticSeverity } = require('@volar/language-server');
30
10
 
31
11
  /**
32
12
  * @returns {LanguageServicePlugin}
@@ -44,22 +24,16 @@ function createCompileErrorDiagnosticPlugin() {
44
24
  },
45
25
  create(/** @type {LanguageServiceContext} */ context) {
46
26
  return {
47
- /**
48
- * @param {TextDocument} document
49
- * @param {import('@volar/language-server').CancellationToken} _token
50
- * @returns {import('@volar/language-server').NullableProviderResult<Diagnostic[]>}
51
- */
52
27
  provideDiagnostics(document, _token) {
53
28
  try {
54
29
  log('Providing Ripple diagnostics for:', document.uri);
55
30
 
56
- const info = getEmbeddedInfo(context, document);
31
+ const virtualCode = getVirtualCode(document, context);
57
32
 
58
- if (!info || !info.virtualCode.errors || info.virtualCode.errors.length === 0) {
33
+ if (!virtualCode || !virtualCode.errors || virtualCode.errors.length === 0) {
59
34
  return [];
60
35
  }
61
36
 
62
- const virtualCode = info.virtualCode;
63
37
  const diagnostics = [];
64
38
 
65
39
  log('Processing', virtualCode.errors.length, 'errors');
@@ -127,7 +101,7 @@ function parseCompilationErrorWithDocument(error, fallbackFileName, sourceText,
127
101
  const zeroBasedEndColumn = Math.max(0, endColumn);
128
102
 
129
103
  return {
130
- severity: 1, // DiagnosticSeverity.Error
104
+ severity: DiagnosticSeverity.Error,
131
105
  range: {
132
106
  start: { line: zeroBasedStartLine, character: zeroBasedStartColumn },
133
107
  end: { line: zeroBasedEndLine, character: zeroBasedEndColumn },
@@ -160,7 +134,7 @@ function parseCompilationErrorWithDocument(error, fallbackFileName, sourceText,
160
134
  let length = Math.min(1, sourceText.split('\n')[zeroBasedLine]?.length - actualColumn || 1);
161
135
 
162
136
  return {
163
- severity: 1, // DiagnosticSeverity.Error
137
+ severity: DiagnosticSeverity.Error,
164
138
  range: {
165
139
  start: { line: zeroBasedLine, character: actualColumn },
166
140
  end: { line: zeroBasedLine, character: actualColumn + length },
@@ -175,7 +149,7 @@ function parseCompilationErrorWithDocument(error, fallbackFileName, sourceText,
175
149
  const endPosition = document.positionAt(Math.min(1, sourceText.length));
176
150
 
177
151
  return {
178
- severity: 1, // DiagnosticSeverity.Error
152
+ severity: DiagnosticSeverity.Error,
179
153
  range: {
180
154
  start: startPosition,
181
155
  end: endPosition,
@@ -189,7 +163,7 @@ function parseCompilationErrorWithDocument(error, fallbackFileName, sourceText,
189
163
  logError('Error parsing compilation error:', parseError);
190
164
 
191
165
  return {
192
- severity: 1,
166
+ severity: DiagnosticSeverity.Error,
193
167
  range: {
194
168
  start: { line: 0, character: 0 },
195
169
  end: { line: 0, character: 1 },
@@ -201,40 +175,6 @@ function parseCompilationErrorWithDocument(error, fallbackFileName, sourceText,
201
175
  }
202
176
  }
203
177
 
204
- /**
205
- * @param {LanguageServiceContext} context
206
- * @param {TextDocument} document
207
- */
208
- function getEmbeddedInfo(context, document) {
209
- try {
210
- const uri = URI.parse(document.uri);
211
- const decoded = context.decodeEmbeddedDocumentUri(uri);
212
- if (!decoded) {
213
- return;
214
- }
215
-
216
- const [documentUri, embeddedCodeId] = decoded;
217
-
218
- const sourceScript = context.language.scripts.get(documentUri);
219
- if (!sourceScript?.generated) {
220
- return;
221
- }
222
-
223
- const virtualCode = sourceScript.generated.embeddedCodes.get(embeddedCodeId);
224
- if (!(virtualCode instanceof RippleVirtualCode)) {
225
- return;
226
- }
227
-
228
- return {
229
- sourceScript: sourceScript,
230
- virtualCode,
231
- };
232
- } catch (err) {
233
- logError('Failed to get embedded info:', err);
234
- return null;
235
- }
236
- }
237
-
238
178
  module.exports = {
239
179
  createCompileErrorDiagnosticPlugin,
240
180
  };
@@ -1,20 +1,11 @@
1
1
  /**
2
- * @typedef {import('@volar/language-server').LanguageServicePlugin} LanguageServicePlugin
2
+ * @import {LanguageServicePlugin} from '@volar/language-server'
3
3
  */
4
4
 
5
5
  const { CompletionItemKind, InsertTextFormat } = require('@volar/language-server');
6
- const { URI } = require('vscode-uri');
6
+ const { getVirtualCode, createLogging } = require('./utils.js');
7
7
 
8
- const DEBUG = process.env.RIPPLE_DEBUG === 'true';
9
-
10
- /**
11
- * @param {...unknown} args
12
- */
13
- function log(...args) {
14
- if (DEBUG) {
15
- console.log('[Ripple Completion]', ...args);
16
- }
17
- }
8
+ const { log } = createLogging('[Ripple Completion Plugin]');
18
9
 
19
10
  /**
20
11
  * Ripple-specific completion enhancements
@@ -143,6 +134,15 @@ const RIPPLE_SNIPPETS = [
143
134
  insertTextFormat: InsertTextFormat.Snippet,
144
135
  sortText: '0-untrack',
145
136
  },
137
+ {
138
+ label: 'try-pending',
139
+ kind: CompletionItemKind.Snippet,
140
+ detail: 'try...pending block',
141
+ documentation: 'Handle async content with loading fallback',
142
+ insertText: "try {\n\t$1\n} pending {\n\t<div>{'Loading...'}</div>\n}",
143
+ insertTextFormat: InsertTextFormat.Snippet,
144
+ sortText: '0-try-pending',
145
+ },
146
146
  ];
147
147
 
148
148
  /**
@@ -217,22 +217,13 @@ function createCompletionPlugin() {
217
217
  return { items: [], isIncomplete: false };
218
218
  }
219
219
 
220
+ const virtualCode = getVirtualCode(document, context);
221
+
220
222
  // Check if we're inside an embedded code (like CSS in <style> blocks)
221
223
  // If so, don't provide Ripple snippets - let CSS completions take priority
222
- const uri = URI.parse(document.uri);
223
- const decoded = context.decodeEmbeddedDocumentUri(uri);
224
- if (decoded) {
225
- const [documentUri, embeddedCodeId] = decoded;
226
- const sourceScript = context.language.scripts.get(documentUri);
227
-
228
- if (sourceScript?.generated) {
229
- const virtualCode = sourceScript.generated.embeddedCodes.get(embeddedCodeId);
230
- // If we're in a CSS embedded code (from <style> blocks), skip Ripple completions
231
- if (virtualCode && virtualCode.languageId === 'css') {
232
- log('Skipping Ripple completions in CSS context');
233
- return { items: [], isIncomplete: false };
234
- }
235
- }
224
+ if (virtualCode && virtualCode.languageId === 'css') {
225
+ log('Skipping Ripple completions in CSS context');
226
+ return { items: [], isIncomplete: false };
236
227
  }
237
228
 
238
229
  const line = document.getText({
@@ -296,26 +287,6 @@ function createCompletionPlugin() {
296
287
  };
297
288
  }
298
289
 
299
- /**
300
- * Heuristic to detect if we're inside a component template
301
- * @param {string} text
302
- * @returns {boolean}
303
- */
304
- function isLikelyInTemplate(text) {
305
- // Simple heuristic: inside component body after opening brace
306
- const componentMatch = text.match(/component\s+\w+\([^)]*\)\s*\{/);
307
- if (!componentMatch || componentMatch.index === undefined) return false;
308
-
309
- // Count braces to see if we're inside
310
- let braceCount = 0;
311
- for (let i = componentMatch.index + componentMatch[0].length; i < text.length; i++) {
312
- if (text[i] === '{') braceCount++;
313
- if (text[i] === '}') braceCount--;
314
- }
315
-
316
- return braceCount > 0;
317
- }
318
-
319
290
  module.exports = {
320
291
  createCompletionPlugin,
321
292
  };
@@ -1,18 +1,8 @@
1
1
  /** @import { LanguageServicePlugin } from '@volar/language-server' */
2
- /** @import { RippleVirtualCode } from '@ripple-ts/typescript-plugin/src/language.js') */
3
2
 
4
- const { URI } = require('vscode-uri');
3
+ const { getVirtualCode, createLogging } = require('./utils.js');
5
4
 
6
- const DEBUG = process.env.RIPPLE_DEBUG === 'true';
7
-
8
- /**
9
- * @param {...unknown} args
10
- */
11
- function log(...args) {
12
- if (DEBUG) {
13
- console.log('[Ripple Definition]', ...args);
14
- }
15
- }
5
+ const { log } = createLogging('[Ripple Definition Plugin]');
16
6
 
17
7
  /**
18
8
  * @returns {LanguageServicePlugin}
@@ -26,9 +16,6 @@ function createDefinitionPlugin() {
26
16
  create(context) {
27
17
  return {
28
18
  async provideDefinition(document, position, token) {
29
- const uri = URI.parse(document.uri);
30
- const decoded = context.decodeEmbeddedDocumentUri(uri);
31
-
32
19
  // Get TypeScript definition from typescript-semantic service
33
20
  let tsDefinitions = [];
34
21
  for (const [plugin, instance] of context.plugins) {
@@ -47,17 +34,6 @@ function createDefinitionPlugin() {
47
34
  return;
48
35
  }
49
36
 
50
- // If not in a Ripple embedded context, just return TypeScript results
51
- if (!decoded) {
52
- return tsDefinitions;
53
- }
54
-
55
- const [sourceUri, virtualCodeId] = decoded;
56
- const sourceScript = context.language.scripts.get(sourceUri);
57
- const virtualCode = /** @type {RippleVirtualCode } */ (
58
- sourceScript?.generated?.embeddedCodes.get(virtualCodeId)
59
- );
60
-
61
37
  // Get the range from TypeScript's definition to find the exact token
62
38
  // This gives us the precise start and end of the token (e.g., "function")
63
39
  const firstDefinition = tsDefinitions[0];
@@ -69,6 +45,8 @@ function createDefinitionPlugin() {
69
45
  const rangeStart = document.offsetAt(range.start);
70
46
  const rangeEnd = document.offsetAt(range.end);
71
47
 
48
+ const virtualCode = getVirtualCode(document, context);
49
+
72
50
  // Find the mapping using the exact token range for O(1) lookup
73
51
  const mapping = virtualCode.findMappingByGeneratedRange(rangeStart, rangeEnd);
74
52
 
@@ -0,0 +1,121 @@
1
+ /** @import { LanguageServicePlugin } from '@volar/language-server' */
2
+ /** @import { LanguageServicePluginInstance } from '@volar/language-server' */
3
+
4
+ const { getVirtualCode, getWordFromPosition, createLogging } = require('./utils.js');
5
+ const { log } = createLogging('[Ripple Document Highlight Plugin]');
6
+
7
+ /**
8
+ * Document Highlight plugin for Ripple
9
+ * Provides word highlighting (grey background) for custom Ripple keywords like 'pending'
10
+ * @returns {LanguageServicePlugin}
11
+ */
12
+ function createDocumentHighlightPlugin() {
13
+ return {
14
+ name: 'ripple-document-highlight',
15
+ capabilities: {
16
+ documentHighlightProvider: true,
17
+ },
18
+ create(context) {
19
+ /** @type {LanguageServicePluginInstance['provideDocumentHighlights']} */
20
+ let originalProvideDocumentHighlights;
21
+ /** @type {LanguageServicePluginInstance} */
22
+ let originalInstance;
23
+
24
+ // Get TypeScript's document highlights provider
25
+ for (const [plugin, instance] of context.plugins) {
26
+ if (plugin.name === 'typescript-semantic' && instance.provideDocumentHighlights) {
27
+ originalInstance = instance;
28
+ originalProvideDocumentHighlights = instance.provideDocumentHighlights;
29
+ instance.provideDocumentHighlights = undefined;
30
+ break;
31
+ }
32
+ }
33
+
34
+ if (!originalProvideDocumentHighlights) {
35
+ log(
36
+ "'typescript-semantic plugin' was not found or has no 'provideDocumentHighlights'. \
37
+ Document highlights will be limited to custom Ripple keywords only.",
38
+ );
39
+ }
40
+
41
+ return {
42
+ async provideDocumentHighlights(document, position, token) {
43
+ if (!originalProvideDocumentHighlights) {
44
+ return null;
45
+ }
46
+
47
+ let tsHighlights = await originalProvideDocumentHighlights.call(
48
+ originalInstance,
49
+ document,
50
+ position,
51
+ token,
52
+ );
53
+
54
+ if (!tsHighlights || tsHighlights.length > 0) {
55
+ // If TypeScript recognized tokens and provided highlights, return them
56
+ return tsHighlights;
57
+ }
58
+
59
+ const virtualCode = getVirtualCode(document, context);
60
+
61
+ if (!virtualCode) {
62
+ return tsHighlights;
63
+ }
64
+
65
+ // Check if we're on a custom Ripple keyword
66
+ const offset = document.offsetAt(position);
67
+ const text = document.getText();
68
+
69
+ // Find word boundaries
70
+ const { word } = getWordFromPosition(text, offset);
71
+
72
+ // If the word is a Ripple keyword, find all occurrences in the document
73
+
74
+ const regex = new RegExp(`\\b${word}\\b`, 'g');
75
+ let match;
76
+
77
+ while ((match = regex.exec(text)) !== null) {
78
+ const start = match.index;
79
+ const end = match.index + word.length;
80
+ const mapping = virtualCode.findMappingByGeneratedRange(start, end);
81
+
82
+ if (!mapping) {
83
+ // If no mapping, skip all others as well
84
+ // This shouldn't happen as TS handles only mapped ranges
85
+ return tsHighlights;
86
+ }
87
+
88
+ if (!mapping.data.customData?.wordHighlight?.kind) {
89
+ // Skip if we didn't define word highlighting in segments
90
+ continue;
91
+ }
92
+
93
+ if (!tsHighlights) {
94
+ tsHighlights = [];
95
+ }
96
+
97
+ tsHighlights.push({
98
+ range: {
99
+ start: document.positionAt(start),
100
+ end: document.positionAt(end),
101
+ },
102
+
103
+ kind: mapping.data.customData.wordHighlight.kind,
104
+ });
105
+ }
106
+
107
+ if (tsHighlights.length > 0) {
108
+ log(`Found ${tsHighlights.length} occurrences of '${word}'`);
109
+ }
110
+
111
+ // Return TypeScript highlights if no custom keyword was found
112
+ return [...tsHighlights];
113
+ },
114
+ };
115
+ },
116
+ };
117
+ }
118
+
119
+ module.exports = {
120
+ createDocumentHighlightPlugin,
121
+ };
@@ -1,26 +1,9 @@
1
1
  /** @import { LanguageServicePlugin } from '@volar/language-server' */
2
- /** @import { RippleVirtualCode } from '@ripple-ts/typescript-plugin/src/language.js') */
3
2
  /** @import { LanguageServicePluginInstance } from '@volar/language-server' */
4
3
 
5
- const { URI } = require('vscode-uri');
4
+ const { getVirtualCode, createLogging } = require('./utils.js');
6
5
 
7
- const DEBUG = process.env.RIPPLE_DEBUG === 'true';
8
-
9
- /**
10
- * @param {...unknown} args
11
- */
12
- function log(...args) {
13
- if (DEBUG) {
14
- console.log('[Ripple Hover]', ...args);
15
- }
16
- }
17
-
18
- /**
19
- * @param {...unknown} args
20
- */
21
- function logError(...args) {
22
- console.error('[Ripple Hover]', ...args);
23
- }
6
+ const { log, logError } = createLogging('[Ripple Hover Plugin]');
24
7
 
25
8
  /**
26
9
  * @returns {LanguageServicePlugin}
@@ -55,9 +38,6 @@ function createHoverPlugin() {
55
38
  }
56
39
  return {
57
40
  async provideHover(document, position, token) {
58
- const uri = URI.parse(document.uri);
59
- const decoded = context.decodeEmbeddedDocumentUri(uri);
60
-
61
41
  // Get TypeScript hover from typescript-semantic service
62
42
  let tsHover = null;
63
43
  if (originalProvideHover) {
@@ -65,23 +45,13 @@ function createHoverPlugin() {
65
45
  }
66
46
 
67
47
  // If no TypeScript hover, nothing to modify
68
- if (!tsHover) {
48
+ if (!tsHover || !tsHover.range) {
69
49
  return;
70
50
  }
71
51
 
72
- // If not in a Ripple embedded context, just return TypeScript results
73
- if (!decoded) {
74
- return tsHover;
75
- }
76
-
77
- const [sourceUri, virtualCodeId] = decoded;
78
- const sourceScript = context.language.scripts.get(sourceUri);
79
- const virtualCode = /** @type {RippleVirtualCode } */ (
80
- sourceScript?.generated?.embeddedCodes.get(virtualCodeId)
81
- );
52
+ const virtualCode = getVirtualCode(document, context);
82
53
 
83
- // If there's no range to adjust, return as-is
84
- if (!tsHover.range) {
54
+ if (!virtualCode) {
85
55
  return tsHover;
86
56
  }
87
57
 
package/src/server.js CHANGED
@@ -1,4 +1,7 @@
1
- const {
1
+ /** @import {CompilerOptions} from 'typescript' */
2
+
3
+ const { createLogging } = require('./utils.js');
4
+ const {
2
5
  createConnection,
3
6
  createServer,
4
7
  createTypeScriptProject,
@@ -9,6 +12,7 @@ const { createHoverPlugin } = require('./hoverPlugin.js');
9
12
  const { createCompletionPlugin } = require('./completionPlugin.js');
10
13
  const { createAutoInsertPlugin } = require('./autoInsertPlugin.js');
11
14
  const { createTypeScriptDiagnosticFilterPlugin } = require('./typescriptDiagnosticPlugin.js');
15
+ const { createDocumentHighlightPlugin } = require('./documentHighlightPlugin.js');
12
16
  const {
13
17
  getRippleLanguagePlugin,
14
18
  resolveConfig,
@@ -16,25 +20,7 @@ const {
16
20
  const { createTypeScriptServices } = require('./typescriptService.js');
17
21
  const { create: createCssService } = require('volar-service-css');
18
22
 
19
- const DEBUG = process.env.RIPPLE_DEBUG === 'true';
20
-
21
- /** @typedef {import('typescript').CompilerOptions} CompilerOptions */
22
-
23
- /**
24
- * @param {...unknown} args
25
- */
26
- function log(...args) {
27
- if (DEBUG) {
28
- console.log('[Ripple Server]', ...args);
29
- }
30
- }
31
-
32
- /**
33
- * @param {...unknown} args
34
- */
35
- function logError(...args) {
36
- console.error('[Ripple Server ERROR]', ...args);
37
- }
23
+ const { log, logError } = createLogging('[Ripple Language Server]');
38
24
 
39
25
  function createRippleLanguageServer() {
40
26
  const connection = createConnection();
@@ -115,11 +101,12 @@ function createRippleLanguageServer() {
115
101
  createDefinitionPlugin(),
116
102
  createCssService(),
117
103
  ...createTypeScriptServices(ts),
118
- // !IMPORTANT 'createTypeScriptDiagnosticFilterPlugin' and 'createHoverPlugin'
119
- // must come after TypeScript services
104
+ // !IMPORTANT 'createTypeScriptDiagnosticFilterPlugin', 'createHoverPlugin',
105
+ // and 'createDocumentHighlightPlugin' must come after TypeScript services
120
106
  // to intercept volar's and vscode default providers
121
107
  createTypeScriptDiagnosticFilterPlugin(),
122
108
  createHoverPlugin(),
109
+ createDocumentHighlightPlugin(),
123
110
  ],
124
111
  );
125
112
 
@@ -1,31 +1,11 @@
1
1
  /** @import { LanguageServicePlugin } from '@volar/language-server' */
2
2
  /** @import { LanguageServicePluginInstance } from '@volar/language-server' */
3
3
 
4
- // @ts-expect-error type-only import from ESM module into CJS is fine
5
- /** @import { CodeMapping } from 'ripple/compiler' */
4
+ const { getVirtualCode, createLogging } = require('./utils.js');
6
5
 
7
- const { getVirtualCode } = require('./utils.js');
8
-
9
- const DEBUG = process.env.RIPPLE_DEBUG === 'true';
10
-
11
- /**
12
- * @param {...unknown} args
13
- */
14
- function log(...args) {
15
- if (DEBUG) {
16
- console.log('[Ripple Typescript Diagnostics]', ...args);
17
- }
18
- }
6
+ const { log, logError } = createLogging('[Ripple TypeScript Diagnostic Plugin]');
19
7
 
20
8
  /**
21
- * @param {...unknown} args
22
- */
23
- function logError(...args) {
24
- console.error('[Ripple Typescript Diagnostics]', ...args);
25
- }
26
-
27
- /**
28
- * Create a plugin to filter TypeScript diagnostics based on customData in mappings
29
9
  * @returns {LanguageServicePlugin}
30
10
  */
31
11
  function createTypeScriptDiagnosticFilterPlugin() {
@@ -45,7 +25,7 @@ function createTypeScriptDiagnosticFilterPlugin() {
45
25
  /** @type {LanguageServicePluginInstance} */
46
26
  let originalInstance;
47
27
 
48
- // Disable typescript-semantic's provideHover so it doesn't merge with ours
28
+ // Disable typescript-semantic's provideDiagnostics so it doesn't merge with ours
49
29
  for (const [plugin, instance] of context.plugins) {
50
30
  if (plugin.name === 'typescript-semantic') {
51
31
  originalInstance = instance;
@@ -118,54 +98,6 @@ function createTypeScriptDiagnosticFilterPlugin() {
118
98
  };
119
99
  }
120
100
 
121
- /**
122
- * Get the mapping at a specific position in the document
123
- * @param {import('@volar/language-server').LanguageServiceContext} context
124
- * @param {import('vscode-languageserver-textdocument').TextDocument} document
125
- * @param {import('@volar/language-server').Position} position
126
- * @returns {CodeMapping | null}
127
- */
128
- function getMappingAtPosition(context, document, position) {
129
- try {
130
- const { URI } = require('vscode-uri');
131
- const { RippleVirtualCode } = require('@ripple-ts/typescript-plugin/src/language.js');
132
-
133
- const uri = URI.parse(document.uri);
134
- const decoded = context.decodeEmbeddedDocumentUri(uri);
135
- if (!decoded) {
136
- return null;
137
- }
138
-
139
- const [sourceUri, virtualCodeId] = decoded;
140
- const sourceScript = context.language.scripts.get(sourceUri);
141
- const virtualCode = /** @type {RippleVirtualCode} */ (
142
- sourceScript?.generated?.embeddedCodes.get(virtualCodeId)
143
- );
144
-
145
- if (!virtualCode?.mappings) {
146
- return null;
147
- }
148
-
149
- // Convert position to offset in the virtual document
150
- const offset = document.offsetAt(position);
151
-
152
- // Find mapping that contains this offset
153
- for (const mapping of virtualCode.mappings) {
154
- const genStart = mapping.generatedOffsets[0];
155
- const genEnd = genStart + mapping.lengths[0];
156
-
157
- if (offset >= genStart && offset < genEnd) {
158
- return mapping;
159
- }
160
- }
161
-
162
- return null;
163
- } catch (err) {
164
- log('Error getting mapping at position:', err);
165
- return null;
166
- }
167
- }
168
-
169
101
  module.exports = {
170
102
  createTypeScriptDiagnosticFilterPlugin,
171
103
  };
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @typedef {import('@volar/language-server').LanguageServiceContext} LanguageServiceContext
3
- * @typedef {import('vscode-languageserver-textdocument').TextDocument} TextDocument
2
+ * @import {LanguageServiceContext} from '@volar/language-server'
3
+ * @import {TextDocument} from 'vscode-languageserver-textdocument'
4
4
  */
5
5
 
6
6
  // Monkey-patch getUserPreferences before requiring the main module
package/src/utils.js CHANGED
@@ -3,6 +3,7 @@
3
3
  /** @import {RippleVirtualCode} from '@ripple-ts/typescript-plugin/src/language.js' */
4
4
 
5
5
  const { URI } = require('vscode-uri');
6
+ const { createLogging, DEBUG } = require('@ripple-ts/typescript-plugin/src/utils.js');
6
7
 
7
8
  /**
8
9
  * Get virtual code from the encoded document URI
@@ -24,6 +25,34 @@ function getVirtualCode(document, context) {
24
25
  return virtualCode;
25
26
  }
26
27
 
28
+ /**
29
+ * Get the word at a specific position in the text
30
+ * @param {string} text
31
+ * @param {number} start
32
+ * @returns {{word: string, start: number, end: number}}
33
+ */
34
+ function getWordFromPosition(text, start) {
35
+ let wordStart = start;
36
+ let wordEnd = start;
37
+ while (wordStart > 0 && /\w/.test(text[wordStart - 1])) {
38
+ wordStart--;
39
+ }
40
+ while (wordEnd < text.length && /\w/.test(text[wordEnd])) {
41
+ wordEnd++;
42
+ }
43
+
44
+ const word = text.substring(wordStart, wordEnd);
45
+
46
+ return {
47
+ word,
48
+ start: wordStart,
49
+ end: wordEnd,
50
+ };
51
+ }
52
+
27
53
  module.exports = {
28
54
  getVirtualCode,
55
+ getWordFromPosition,
56
+ createLogging,
57
+ DEBUG,
29
58
  };