@ripple-ts/language-server 0.2.185 → 0.2.187
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 +5 -2
- package/src/autoInsertPlugin.js +1 -1
- package/src/compileErrorDiagnosticPlugin.js +1 -1
- package/src/completionPlugin.js +1 -1
- package/src/definitionPlugin.js +92 -4
- package/src/documentHighlightPlugin.js +1 -1
- package/src/hoverPlugin.js +62 -34
- package/src/typescriptDiagnosticPlugin.js +1 -1
- package/src/utils.js +14 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ripple-ts/language-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.187",
|
|
4
4
|
"description": "Language Server Protocol implementation for Ripple",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -19,7 +19,10 @@
|
|
|
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.
|
|
22
|
+
"@ripple-ts/typescript-plugin": "0.2.187"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"ripple": "0.2.187"
|
|
23
26
|
},
|
|
24
27
|
"peerDependencies": {
|
|
25
28
|
"typescript": "^5.9.2"
|
package/src/autoInsertPlugin.js
CHANGED
|
@@ -64,7 +64,7 @@ function createAutoInsertPlugin() {
|
|
|
64
64
|
return null;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const virtualCode = getVirtualCode(document, context);
|
|
67
|
+
const [virtualCode] = getVirtualCode(document, context);
|
|
68
68
|
|
|
69
69
|
// Map position back to source
|
|
70
70
|
const offset = document.offsetAt(position);
|
|
@@ -28,7 +28,7 @@ function createCompileErrorDiagnosticPlugin() {
|
|
|
28
28
|
try {
|
|
29
29
|
log('Providing Ripple diagnostics for:', document.uri);
|
|
30
30
|
|
|
31
|
-
const virtualCode = getVirtualCode(document, context);
|
|
31
|
+
const [virtualCode] = getVirtualCode(document, context);
|
|
32
32
|
|
|
33
33
|
if (!virtualCode || !virtualCode.errors || virtualCode.errors.length === 0) {
|
|
34
34
|
return [];
|
package/src/completionPlugin.js
CHANGED
|
@@ -217,7 +217,7 @@ function createCompletionPlugin() {
|
|
|
217
217
|
return { items: [], isIncomplete: false };
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
const virtualCode = getVirtualCode(document, context);
|
|
220
|
+
const [virtualCode] = getVirtualCode(document, context);
|
|
221
221
|
|
|
222
222
|
// Check if we're inside an embedded code (like CSS in <style> blocks)
|
|
223
223
|
// If so, don't provide Ripple snippets - let CSS completions take priority
|
package/src/definitionPlugin.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
/** @import { LanguageServicePlugin } from '@volar/language-server' */
|
|
1
|
+
/** @import { LanguageServicePlugin, LocationLink } from '@volar/language-server'; */
|
|
2
|
+
// @ts-expect-error type-only import from ESM module into CJS is fine
|
|
3
|
+
/** @import { DefinitionLocation } from 'ripple/compiler'; */
|
|
2
4
|
|
|
3
|
-
const {
|
|
5
|
+
const { TextDocument } = require('vscode-languageserver-textdocument');
|
|
6
|
+
const { getVirtualCode, createLogging, getWordFromPosition } = require('./utils.js');
|
|
4
7
|
|
|
5
8
|
const { log } = createLogging('[Ripple Definition Plugin]');
|
|
6
9
|
|
|
@@ -17,6 +20,7 @@ function createDefinitionPlugin() {
|
|
|
17
20
|
return {
|
|
18
21
|
async provideDefinition(document, position, token) {
|
|
19
22
|
// Get TypeScript definition from typescript-semantic service
|
|
23
|
+
/** @type {LocationLink[]} */
|
|
20
24
|
let tsDefinitions = [];
|
|
21
25
|
for (const [plugin, instance] of context.plugins) {
|
|
22
26
|
if (plugin.name === 'typescript-semantic' && instance.provideDefinition) {
|
|
@@ -28,6 +32,92 @@ function createDefinitionPlugin() {
|
|
|
28
32
|
}
|
|
29
33
|
}
|
|
30
34
|
|
|
35
|
+
const [virtualCode, sourceUri] = getVirtualCode(document, context);
|
|
36
|
+
|
|
37
|
+
// First check for custom definitions (e.g., CSS class selectors)
|
|
38
|
+
const offset = document.offsetAt(position);
|
|
39
|
+
const text = document.getText();
|
|
40
|
+
// Find word boundaries
|
|
41
|
+
const { word, start, end } = getWordFromPosition(text, offset);
|
|
42
|
+
const customMapping = virtualCode.findMappingByGeneratedRange(start, end);
|
|
43
|
+
|
|
44
|
+
log(`Cursor position in generated code for word '${word}':`, position);
|
|
45
|
+
log(`Cursor offset in generated code for word '${word}':`, offset);
|
|
46
|
+
|
|
47
|
+
// If mapping has custom definition metadata with location, handle it
|
|
48
|
+
if (
|
|
49
|
+
customMapping?.data.customData.definition !== false &&
|
|
50
|
+
customMapping?.data.customData.definition?.location
|
|
51
|
+
) {
|
|
52
|
+
const def = customMapping.data.customData.definition;
|
|
53
|
+
const loc = /** @type {DefinitionLocation} */ (def.location);
|
|
54
|
+
|
|
55
|
+
const embeddedCode = loc.embeddedId
|
|
56
|
+
? virtualCode.embeddedCodes?.find(({ id }) => id === loc.embeddedId)
|
|
57
|
+
: undefined;
|
|
58
|
+
|
|
59
|
+
if (embeddedCode) {
|
|
60
|
+
const embedMapping = embeddedCode.mappings[0];
|
|
61
|
+
|
|
62
|
+
// Calculate the position in the source document
|
|
63
|
+
// CSS offset relative to embedded code start + source offset of CSS region
|
|
64
|
+
const sourceStartOffset = embedMapping.sourceOffsets[0] + loc.start;
|
|
65
|
+
const sourceEndOffset = embedMapping.sourceOffsets[0] + loc.end;
|
|
66
|
+
|
|
67
|
+
log(
|
|
68
|
+
'Source document offsets - start for matching css:',
|
|
69
|
+
sourceStartOffset,
|
|
70
|
+
'end:',
|
|
71
|
+
sourceEndOffset,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Calculate line/column positions using the source document's proper encoding
|
|
75
|
+
// Create a TextDocument from the source code for proper position calculations
|
|
76
|
+
const sourceDocument = TextDocument.create(
|
|
77
|
+
sourceUri.toString(),
|
|
78
|
+
'ripple',
|
|
79
|
+
0,
|
|
80
|
+
virtualCode.originalCode,
|
|
81
|
+
);
|
|
82
|
+
const targetStart = sourceDocument.positionAt(sourceStartOffset);
|
|
83
|
+
const targetEnd = sourceDocument.positionAt(sourceEndOffset);
|
|
84
|
+
|
|
85
|
+
log('Target positions in source - start:', targetStart, 'end:', targetEnd);
|
|
86
|
+
|
|
87
|
+
// The origin selection range should be in the virtual document
|
|
88
|
+
// not in the source document!
|
|
89
|
+
const generatedStart = customMapping.generatedOffsets[0];
|
|
90
|
+
const generatedEnd =
|
|
91
|
+
generatedStart + customMapping.data.customData.generatedLengths[0];
|
|
92
|
+
const originStart = document.positionAt(generatedStart);
|
|
93
|
+
const originEnd = document.positionAt(generatedEnd);
|
|
94
|
+
|
|
95
|
+
log('Origin positions - start:', originStart, 'end:', originEnd);
|
|
96
|
+
|
|
97
|
+
/** @type {LocationLink} */
|
|
98
|
+
tsDefinitions.push({
|
|
99
|
+
targetUri: sourceUri.toString(), // Use the actual source file URI
|
|
100
|
+
targetRange: {
|
|
101
|
+
start: targetStart,
|
|
102
|
+
end: targetEnd,
|
|
103
|
+
},
|
|
104
|
+
targetSelectionRange: {
|
|
105
|
+
start: targetStart,
|
|
106
|
+
end: targetEnd,
|
|
107
|
+
},
|
|
108
|
+
originSelectionRange: {
|
|
109
|
+
start: originStart,
|
|
110
|
+
end: originEnd,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return tsDefinitions;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Below here we handle adjusting TypeScript definitions for transformed tokens
|
|
119
|
+
// specifically, when "component" in Ripple maps to "function" in TS
|
|
120
|
+
|
|
31
121
|
// If no TypeScript definitions, nothing to modify
|
|
32
122
|
// Volar will let the next ts plugin handle it
|
|
33
123
|
if (tsDefinitions.length === 0) {
|
|
@@ -45,8 +135,6 @@ function createDefinitionPlugin() {
|
|
|
45
135
|
const rangeStart = document.offsetAt(range.start);
|
|
46
136
|
const rangeEnd = document.offsetAt(range.end);
|
|
47
137
|
|
|
48
|
-
const virtualCode = getVirtualCode(document, context);
|
|
49
|
-
|
|
50
138
|
// Find the mapping using the exact token range for O(1) lookup
|
|
51
139
|
const mapping = virtualCode.findMappingByGeneratedRange(rangeStart, rangeEnd);
|
|
52
140
|
|
package/src/hoverPlugin.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
@import {
|
|
3
|
+
LanguageServicePlugin,
|
|
4
|
+
LanguageServicePluginInstance,
|
|
5
|
+
MarkupContent,
|
|
6
|
+
} from '@volar/language-server'; */
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
getVirtualCode,
|
|
10
|
+
createLogging,
|
|
11
|
+
getWordFromPosition,
|
|
12
|
+
concatMarkdownContents,
|
|
13
|
+
} = require('./utils.js');
|
|
5
14
|
|
|
6
15
|
const { log, logError } = createLogging('[Ripple Hover Plugin]');
|
|
7
16
|
|
|
@@ -44,22 +53,33 @@ function createHoverPlugin() {
|
|
|
44
53
|
tsHover = await originalProvideHover.call(originalInstance, document, position, token);
|
|
45
54
|
}
|
|
46
55
|
|
|
47
|
-
|
|
48
|
-
if (!tsHover || !tsHover.range) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const virtualCode = getVirtualCode(document, context);
|
|
56
|
+
const [virtualCode] = getVirtualCode(document, context);
|
|
53
57
|
|
|
54
58
|
if (!virtualCode) {
|
|
55
59
|
return tsHover;
|
|
56
60
|
}
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
/** @type {number} */
|
|
63
|
+
let starOffset;
|
|
64
|
+
/** @type {number} */
|
|
65
|
+
let endOffset;
|
|
66
|
+
|
|
67
|
+
if (tsHover && tsHover.range) {
|
|
68
|
+
starOffset = document.offsetAt(tsHover.range.start);
|
|
69
|
+
endOffset = document.offsetAt(tsHover.range.end);
|
|
70
|
+
} else {
|
|
71
|
+
const offset = document.offsetAt(position);
|
|
72
|
+
const text = document.getText();
|
|
73
|
+
// Find word boundaries
|
|
74
|
+
const { word, start, end } = getWordFromPosition(text, offset);
|
|
75
|
+
starOffset = start;
|
|
76
|
+
endOffset = end;
|
|
77
|
+
|
|
78
|
+
log(`Cursor position in generated code for word '${word}':`, position);
|
|
79
|
+
log(`Cursor offset in generated code for word '${word}':`, offset);
|
|
80
|
+
}
|
|
61
81
|
|
|
62
|
-
const mapping = virtualCode.findMappingByGeneratedRange(
|
|
82
|
+
const mapping = virtualCode.findMappingByGeneratedRange(starOffset, endOffset);
|
|
63
83
|
|
|
64
84
|
if (!mapping) {
|
|
65
85
|
return tsHover;
|
|
@@ -67,11 +87,17 @@ function createHoverPlugin() {
|
|
|
67
87
|
|
|
68
88
|
const customHover = mapping?.data?.customData?.hover;
|
|
69
89
|
if (customHover) {
|
|
90
|
+
const contents = tsHover
|
|
91
|
+
? concatMarkdownContents(
|
|
92
|
+
/** @type {MarkupContent} **/ (tsHover.contents).value,
|
|
93
|
+
customHover.contents,
|
|
94
|
+
)
|
|
95
|
+
: customHover.contents;
|
|
70
96
|
log('Found custom hover data in mapping');
|
|
71
97
|
return {
|
|
72
98
|
contents: {
|
|
73
99
|
kind: 'markdown',
|
|
74
|
-
value:
|
|
100
|
+
value: contents,
|
|
75
101
|
},
|
|
76
102
|
range: {
|
|
77
103
|
start: position,
|
|
@@ -80,33 +106,35 @@ function createHoverPlugin() {
|
|
|
80
106
|
};
|
|
81
107
|
} else if (customHover === false) {
|
|
82
108
|
log(
|
|
83
|
-
`Hover explicitly suppressed in mapping at range start: ${
|
|
109
|
+
`Hover explicitly suppressed in mapping at range start: ${starOffset}, end: ${endOffset}`,
|
|
84
110
|
);
|
|
85
111
|
return null;
|
|
86
112
|
}
|
|
87
113
|
|
|
88
|
-
log('Found mapping for hover at range', 'start: ',
|
|
114
|
+
log('Found mapping for hover at range', 'start: ', starOffset, 'end: ', endOffset);
|
|
89
115
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
116
|
+
if (tsHover && tsHover.range) {
|
|
117
|
+
// Check if source length is greater than generated length (component -> function)
|
|
118
|
+
const customData = mapping.data.customData;
|
|
119
|
+
const sourceLength = mapping.lengths[0];
|
|
120
|
+
const generatedLength = customData.generatedLengths[0];
|
|
94
121
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
122
|
+
// If no generatedLengths, or source and generated are same length, no transformation
|
|
123
|
+
if (sourceLength <= generatedLength) {
|
|
124
|
+
return tsHover;
|
|
125
|
+
}
|
|
99
126
|
|
|
100
|
-
|
|
127
|
+
const diffLength = sourceLength - generatedLength;
|
|
101
128
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
129
|
+
// Adjust the hover range to highlight the full "component" keyword
|
|
130
|
+
tsHover.range = {
|
|
131
|
+
start: tsHover.range.start,
|
|
132
|
+
end: {
|
|
133
|
+
line: tsHover.range.end.line,
|
|
134
|
+
character: tsHover.range.end.character + diffLength,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
110
138
|
|
|
111
139
|
return tsHover;
|
|
112
140
|
},
|
|
@@ -56,7 +56,7 @@ function createTypeScriptDiagnosticFilterPlugin() {
|
|
|
56
56
|
|
|
57
57
|
log(`Filtering ${diagnostics.length} TypeScript diagnostics for ${document.uri}`);
|
|
58
58
|
|
|
59
|
-
const virtualCode = getVirtualCode(document, context);
|
|
59
|
+
const [virtualCode] = getVirtualCode(document, context);
|
|
60
60
|
|
|
61
61
|
const filtered = diagnostics.filter((diagnostic) => {
|
|
62
62
|
const range = diagnostic.range;
|
package/src/utils.js
CHANGED
|
@@ -4,12 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
const { URI } = require('vscode-uri');
|
|
6
6
|
const { createLogging, DEBUG } = require('@ripple-ts/typescript-plugin/src/utils.js');
|
|
7
|
+
const wordRegex = /\w/;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {...string} contents
|
|
11
|
+
* @returns string
|
|
12
|
+
*/
|
|
13
|
+
function concatMarkdownContents(...contents) {
|
|
14
|
+
return contents.join('\n\n<br>\n\n---\n\n<br><br>\n\n');
|
|
15
|
+
}
|
|
7
16
|
|
|
8
17
|
/**
|
|
9
18
|
* Get virtual code from the encoded document URI
|
|
10
19
|
* @param {LanguageServiceContext} context
|
|
11
20
|
* @param {TextDocument} document
|
|
12
|
-
* @returns {RippleVirtualCode}
|
|
21
|
+
* @returns {[RippleVirtualCode, URI]}
|
|
13
22
|
*/
|
|
14
23
|
function getVirtualCode(document, context) {
|
|
15
24
|
const uri = URI.parse(document.uri);
|
|
@@ -22,7 +31,7 @@ function getVirtualCode(document, context) {
|
|
|
22
31
|
sourceScript?.generated?.embeddedCodes.get(virtualCodeId)
|
|
23
32
|
);
|
|
24
33
|
|
|
25
|
-
return virtualCode;
|
|
34
|
+
return [virtualCode, sourceUri];
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
/**
|
|
@@ -34,10 +43,10 @@ function getVirtualCode(document, context) {
|
|
|
34
43
|
function getWordFromPosition(text, start) {
|
|
35
44
|
let wordStart = start;
|
|
36
45
|
let wordEnd = start;
|
|
37
|
-
while (wordStart > 0 &&
|
|
46
|
+
while (wordStart > 0 && wordRegex.test(text[wordStart - 1])) {
|
|
38
47
|
wordStart--;
|
|
39
48
|
}
|
|
40
|
-
while (wordEnd < text.length &&
|
|
49
|
+
while (wordEnd < text.length && wordRegex.test(text[wordEnd])) {
|
|
41
50
|
wordEnd++;
|
|
42
51
|
}
|
|
43
52
|
|
|
@@ -54,5 +63,6 @@ module.exports = {
|
|
|
54
63
|
getVirtualCode,
|
|
55
64
|
getWordFromPosition,
|
|
56
65
|
createLogging,
|
|
66
|
+
concatMarkdownContents,
|
|
57
67
|
DEBUG,
|
|
58
68
|
};
|