Revamping to matrix style

This commit is contained in:
2026-02-16 16:37:35 -05:00
parent 71852ec99a
commit 9d0e3938e4
14958 changed files with 2089572 additions and 114 deletions

View File

@@ -0,0 +1,13 @@
import type { SemanticTokens } from 'vscode-languageserver-protocol';
export declare class SemanticTokensBuilder {
private _id;
private _prevLine;
private _prevChar;
private _data;
private _dataLen;
constructor();
private initialize;
push(line: number, char: number, length: number, tokenType: number, tokenModifiers: number): void;
get id(): string;
build(): SemanticTokens;
}

View File

@@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SemanticTokensBuilder = void 0;
class SemanticTokensBuilder {
constructor() {
this.initialize();
}
initialize() {
this._id = Date.now();
this._prevLine = 0;
this._prevChar = 0;
this._data = [];
this._dataLen = 0;
}
push(line, char, length, tokenType, tokenModifiers) {
let pushLine = line;
let pushChar = char;
if (this._dataLen > 0) {
pushLine -= this._prevLine;
if (pushLine === 0) {
pushChar -= this._prevChar;
}
}
this._data[this._dataLen++] = pushLine;
this._data[this._dataLen++] = pushChar;
this._data[this._dataLen++] = length;
this._data[this._dataLen++] = tokenType;
this._data[this._dataLen++] = tokenModifiers;
this._prevLine = line;
this._prevChar = char;
}
get id() {
return this._id.toString();
}
build() {
return {
resultId: this.id,
data: this._data,
};
}
}
exports.SemanticTokensBuilder = SemanticTokensBuilder;
//# sourceMappingURL=SemanticTokensBuilder.js.map

View File

@@ -0,0 +1,2 @@
import type * as vscode from 'vscode-languageserver-protocol';
export declare const NoneCancellationToken: vscode.CancellationToken;

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NoneCancellationToken = void 0;
exports.NoneCancellationToken = {
isCancellationRequested: false,
onCancellationRequested: () => ({ dispose: () => { } }),
};
//# sourceMappingURL=cancellation.js.map

View File

@@ -0,0 +1,11 @@
import type { CodeInformation, Mapper } from '@volar/language-core';
import type * as ts from 'typescript';
import type * as vscode from 'vscode-languageserver-protocol';
export declare function findOverlapCodeRange(start: number, end: number, map: Mapper, filter: (data: CodeInformation) => boolean): {
start: number;
end: number;
} | undefined;
export declare function isInsideRange(parent: vscode.Range, child: vscode.Range): boolean;
export declare function isEqualRange(a: vscode.Range, b: vscode.Range): boolean;
export declare function stringToSnapshot(str: string): ts.IScriptSnapshot;
export declare function sleep(ms: number): Promise<unknown>;

View File

@@ -0,0 +1,89 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findOverlapCodeRange = findOverlapCodeRange;
exports.isInsideRange = isInsideRange;
exports.isEqualRange = isEqualRange;
exports.stringToSnapshot = stringToSnapshot;
exports.sleep = sleep;
function findOverlapCodeRange(start, end, map, filter) {
let mappedStart;
let mappedEnd;
for (const [mapped, mapping] of map.toGeneratedLocation(start)) {
if (filter(mapping.data)) {
mappedStart = mapped;
break;
}
}
for (const [mapped, mapping] of map.toGeneratedLocation(end)) {
if (filter(mapping.data)) {
mappedEnd = mapped;
break;
}
}
if (mappedStart === undefined || mappedEnd === undefined) {
for (const mapping of map.mappings) {
if (filter(mapping.data)) {
const mappingStart = mapping.sourceOffsets[0];
const mappingEnd = mapping.sourceOffsets[mapping.sourceOffsets.length - 1] + mapping.lengths[mapping.lengths.length - 1];
const overlap = getOverlapRange(start, end, mappingStart, mappingEnd);
if (overlap) {
const curMappedStart = (overlap.start - mappingStart) + mapping.generatedOffsets[0];
const lastGeneratedLength = (mapping.generatedLengths ?? mapping.lengths)[mapping.generatedOffsets.length - 1];
const curMappedEndOffset = Math.min(overlap.end - mapping.sourceOffsets[mapping.sourceOffsets.length - 1], lastGeneratedLength);
const curMappedEnd = mapping.generatedOffsets[mapping.generatedOffsets.length - 1] + curMappedEndOffset;
mappedStart = mappedStart === undefined ? curMappedStart : Math.min(mappedStart, curMappedStart);
mappedEnd = mappedEnd === undefined ? curMappedEnd : Math.max(mappedEnd, curMappedEnd);
}
}
}
}
if (mappedStart !== undefined && mappedEnd !== undefined) {
return {
start: mappedStart,
end: mappedEnd,
};
}
}
function getOverlapRange(range1Start, range1End, range2Start, range2End) {
const start = Math.max(range1Start, range2Start);
const end = Math.min(range1End, range2End);
if (start > end) {
return undefined;
}
return {
start,
end,
};
}
function isInsideRange(parent, child) {
if (child.start.line < parent.start.line) {
return false;
}
if (child.end.line > parent.end.line) {
return false;
}
if (child.start.line === parent.start.line && child.start.character < parent.start.character) {
return false;
}
if (child.end.line === parent.end.line && child.end.character > parent.end.character) {
return false;
}
return true;
}
function isEqualRange(a, b) {
return a.start.line === b.start.line
&& a.start.character === b.start.character
&& a.end.line === b.end.line
&& a.end.character === b.end.character;
}
function stringToSnapshot(str) {
return {
getText: (start, end) => str.substring(start, end),
getLength: () => str.length,
getChangeRange: () => undefined,
};
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
//# sourceMappingURL=common.js.map

View File

@@ -0,0 +1,14 @@
import type * as vscode from 'vscode-languageserver-protocol';
export declare function createLocationSet(): {
add: (item: vscode.Location) => boolean;
has: (item: vscode.Location) => boolean;
};
export declare function withCodeAction<T extends vscode.CodeAction>(items: T[]): T[];
export declare function withTextEdits<T extends vscode.TextEdit>(items: T[]): T[];
export declare function withDocumentChanges(items: NonNullable<vscode.WorkspaceEdit['documentChanges']>): (vscode.CreateFile | vscode.TextDocumentEdit | vscode.RenameFile | vscode.DeleteFile)[];
export declare function withDiagnostics<T extends vscode.Diagnostic>(items: T[]): T[];
export declare function withLocations<T extends vscode.Location>(items: T[]): T[];
export declare function withLocationLinks<T extends vscode.LocationLink>(items: T[]): T[];
export declare function withCallHierarchyIncomingCalls<T extends vscode.CallHierarchyIncomingCall>(items: T[]): T[];
export declare function withCallHierarchyOutgoingCalls<T extends vscode.CallHierarchyOutgoingCall>(items: T[]): T[];
export declare function withRanges<T extends vscode.Range>(items: T[]): T[];

View File

@@ -0,0 +1,120 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createLocationSet = createLocationSet;
exports.withCodeAction = withCodeAction;
exports.withTextEdits = withTextEdits;
exports.withDocumentChanges = withDocumentChanges;
exports.withDiagnostics = withDiagnostics;
exports.withLocations = withLocations;
exports.withLocationLinks = withLocationLinks;
exports.withCallHierarchyIncomingCalls = withCallHierarchyIncomingCalls;
exports.withCallHierarchyOutgoingCalls = withCallHierarchyOutgoingCalls;
exports.withRanges = withRanges;
function createLocationSet() {
const set = new Set();
return {
add,
has,
};
function add(item) {
if (has(item)) {
return false;
}
set.add(getKey(item));
return true;
}
function has(item) {
return set.has(getKey(item));
}
function getKey(item) {
return [
item.uri,
item.range.start.line,
item.range.start.character,
item.range.end.line,
item.range.end.character,
].join(':');
}
}
function withCodeAction(items) {
return dedupe(items, item => [
item.title
].join(':'));
}
function withTextEdits(items) {
return dedupe(items, item => [
item.range.start.line,
item.range.start.character,
item.range.end.line,
item.range.end.character,
item.newText,
].join(':'));
}
function withDocumentChanges(items) {
return dedupe(items, item => JSON.stringify(item)); // TODO: improve this
}
function withDiagnostics(items) {
return dedupe(items, item => [
item.range.start.line,
item.range.start.character,
item.range.end.line,
item.range.end.character,
item.source,
item.code,
item.severity,
item.message,
].join(':'));
}
function withLocations(items) {
return dedupe(items, item => [
item.uri,
item.range.start.line,
item.range.start.character,
item.range.end.line,
item.range.end.character,
].join(':'));
}
function withLocationLinks(items) {
return dedupe(items, item => [
item.targetUri,
item.targetSelectionRange.start.line,
item.targetSelectionRange.start.character,
item.targetSelectionRange.end.line,
item.targetSelectionRange.end.character,
// ignore difference targetRange
].join(':'));
}
function withCallHierarchyIncomingCalls(items) {
return dedupe(items, item => [
item.from.uri,
item.from.range.start.line,
item.from.range.start.character,
item.from.range.end.line,
item.from.range.end.character,
].join(':'));
}
function withCallHierarchyOutgoingCalls(items) {
return dedupe(items, item => [
item.to.uri,
item.to.range.start.line,
item.to.range.start.character,
item.to.range.end.line,
item.to.range.end.character,
].join(':'));
}
function withRanges(items) {
return dedupe(items, item => [
item.start.line,
item.start.character,
item.end.line,
item.end.character,
].join(':'));
}
function dedupe(items, getKey) {
const map = new Map();
for (const item of items.reverse()) {
map.set(getKey(item), item);
}
return [...map.values()];
}
//# sourceMappingURL=dedupe.js.map

View File

@@ -0,0 +1,33 @@
import type { CodeInformation, LinkedCodeMap, Mapper, SourceScript, VirtualCode } from '@volar/language-core';
import type * as vscode from 'vscode-languageserver-protocol';
import type { TextDocument } from 'vscode-languageserver-textdocument';
import type { URI } from 'vscode-uri';
import type { LanguageServiceContext, LanguageServicePlugin, LanguageServicePluginInstance } from '../types';
export type DocumentsAndMap = [
sourceDocument: TextDocument,
embeddedDocument: TextDocument,
map: Mapper
];
export declare function documentFeatureWorker<T>(context: LanguageServiceContext, uri: URI, valid: (info: DocumentsAndMap) => boolean, worker: (plugin: [LanguageServicePlugin, LanguageServicePluginInstance], document: TextDocument) => Thenable<T | null | undefined> | T | null | undefined, transformResult: (result: T, map?: DocumentsAndMap) => T | undefined, combineResult?: (results: T[]) => T): Promise<T | undefined>;
export declare function languageFeatureWorker<T, K>(context: LanguageServiceContext, uri: URI, getRealDocParams: () => K, eachVirtualDocParams: (map: DocumentsAndMap) => Generator<K>, worker: (plugin: [LanguageServicePlugin, LanguageServicePluginInstance], document: TextDocument, params: K, map?: DocumentsAndMap) => Thenable<T | null | undefined> | T | null | undefined, transformResult: (result: T, map?: DocumentsAndMap) => T | undefined, combineResult?: (results: T[]) => T): Promise<T | undefined>;
export declare function safeCall<T>(cb: () => Thenable<T> | T, errorMsg?: string): Promise<T | undefined>;
export declare function forEachEmbeddedDocument(context: LanguageServiceContext, sourceScript: SourceScript<URI>, current: VirtualCode): Generator<DocumentsAndMap>;
export declare function getSourceRange(docs: DocumentsAndMap, range: vscode.Range, filter?: (data: CodeInformation) => boolean): {
start: import("vscode-languageserver-textdocument").Position;
end: import("vscode-languageserver-textdocument").Position;
} | undefined;
export declare function getGeneratedRange(docs: DocumentsAndMap, range: vscode.Range, filter?: (data: CodeInformation) => boolean): {
start: import("vscode-languageserver-textdocument").Position;
end: import("vscode-languageserver-textdocument").Position;
} | undefined;
export declare function getSourceRanges([sourceDocument, embeddedDocument, map]: DocumentsAndMap, range: vscode.Range, filter?: (data: CodeInformation) => boolean): Generator<{
start: import("vscode-languageserver-textdocument").Position;
end: import("vscode-languageserver-textdocument").Position;
}, void, unknown>;
export declare function getGeneratedRanges([sourceDocument, embeddedDocument, map]: DocumentsAndMap, range: vscode.Range, filter?: (data: CodeInformation) => boolean): Generator<{
start: import("vscode-languageserver-textdocument").Position;
end: import("vscode-languageserver-textdocument").Position;
}, void, unknown>;
export declare function getSourcePositions([sourceDocument, embeddedDocument, map]: DocumentsAndMap, position: vscode.Position, filter?: (data: CodeInformation) => boolean): Generator<import("vscode-languageserver-textdocument").Position, void, unknown>;
export declare function getGeneratedPositions([sourceDocument, embeddedDocument, map]: DocumentsAndMap, position: vscode.Position, filter?: (data: CodeInformation) => boolean): Generator<import("vscode-languageserver-textdocument").Position, void, unknown>;
export declare function getLinkedCodePositions(document: TextDocument, linkedMap: LinkedCodeMap, posotion: vscode.Position): Generator<import("vscode-languageserver-textdocument").Position, void, unknown>;

View File

@@ -0,0 +1,168 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.documentFeatureWorker = documentFeatureWorker;
exports.languageFeatureWorker = languageFeatureWorker;
exports.safeCall = safeCall;
exports.forEachEmbeddedDocument = forEachEmbeddedDocument;
exports.getSourceRange = getSourceRange;
exports.getGeneratedRange = getGeneratedRange;
exports.getSourceRanges = getSourceRanges;
exports.getGeneratedRanges = getGeneratedRanges;
exports.getSourcePositions = getSourcePositions;
exports.getGeneratedPositions = getGeneratedPositions;
exports.getLinkedCodePositions = getLinkedCodePositions;
function documentFeatureWorker(context, uri, valid, worker, transformResult, combineResult) {
return languageFeatureWorker(context, uri, () => void 0, function* (map) {
if (valid(map)) {
yield;
}
}, worker, transformResult, combineResult);
}
async function languageFeatureWorker(context, uri, getRealDocParams, eachVirtualDocParams, worker, transformResult, combineResult) {
let sourceScript;
const decoded = context.decodeEmbeddedDocumentUri(uri);
if (decoded) {
sourceScript = context.language.scripts.get(decoded[0]);
}
else {
sourceScript = context.language.scripts.get(uri);
}
if (!sourceScript) {
return;
}
let results = [];
if (decoded) {
const virtualCode = sourceScript.generated?.embeddedCodes.get(decoded[1]);
if (virtualCode) {
const docs = [
context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot),
context.documents.get(uri, virtualCode.languageId, virtualCode.snapshot),
context.language.maps.get(virtualCode, sourceScript),
];
await docsWorker(docs, false);
}
}
else if (sourceScript.generated) {
for (const docs of forEachEmbeddedDocument(context, sourceScript, sourceScript.generated.root)) {
if (results.length && !combineResult) {
continue;
}
await docsWorker(docs, true);
}
}
else {
const document = context.documents.get(uri, sourceScript.languageId, sourceScript.snapshot);
const params = getRealDocParams();
for (const [pluginIndex, plugin] of Object.entries(context.plugins)) {
if (context.disabledServicePlugins.has(plugin[1])) {
continue;
}
const embeddedResult = await safeCall(() => worker(plugin, document, params, undefined), `Language service plugin "${plugin[0].name}" (${pluginIndex}) failed to provide document feature for ${document.uri}.`);
if (!embeddedResult) {
continue;
}
const result = transformResult(embeddedResult, undefined);
if (!result) {
continue;
}
results.push(result);
if (!combineResult) {
break;
}
}
}
if (combineResult && results.length > 0) {
const combined = combineResult(results);
return combined;
}
else if (results.length > 0) {
return results[0];
}
async function docsWorker(docs, transform) {
for (const mappedArg of eachVirtualDocParams(docs)) {
if (results.length && !combineResult) {
continue;
}
for (const [pluginIndex, plugin] of Object.entries(context.plugins)) {
if (context.disabledServicePlugins.has(plugin[1])) {
continue;
}
if (results.length && !combineResult) {
continue;
}
const embeddedResult = await safeCall(() => worker(plugin, docs[1], mappedArg, docs), `Language service plugin "${plugin[0].name}" (${pluginIndex}) failed to provide document feature for ${docs[1].uri}.`);
if (!embeddedResult) {
continue;
}
if (transform) {
const mappedResult = transformResult(embeddedResult, docs);
if (mappedResult) {
results.push(mappedResult);
}
}
else {
results.push(embeddedResult);
}
}
}
}
}
async function safeCall(cb, errorMsg) {
try {
return await cb();
}
catch (err) {
console.warn(errorMsg, err);
}
}
function* forEachEmbeddedDocument(context, sourceScript, current) {
if (current.embeddedCodes) {
for (const embeddedCode of current.embeddedCodes) {
yield* forEachEmbeddedDocument(context, sourceScript, embeddedCode);
}
}
const embeddedDocumentUri = context.encodeEmbeddedDocumentUri(sourceScript.id, current.id);
if (!context.disabledEmbeddedDocumentUris.get(embeddedDocumentUri)) {
yield [
context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot),
context.documents.get(embeddedDocumentUri, current.languageId, current.snapshot),
context.language.maps.get(current, sourceScript),
];
}
}
function getSourceRange(docs, range, filter) {
for (const result of getSourceRanges(docs, range, filter)) {
return result;
}
}
function getGeneratedRange(docs, range, filter) {
for (const result of getGeneratedRanges(docs, range, filter)) {
return result;
}
}
function* getSourceRanges([sourceDocument, embeddedDocument, map], range, filter) {
for (const [mappedStart, mappedEnd] of map.toSourceRange(embeddedDocument.offsetAt(range.start), embeddedDocument.offsetAt(range.end), true, filter)) {
yield { start: sourceDocument.positionAt(mappedStart), end: sourceDocument.positionAt(mappedEnd) };
}
}
function* getGeneratedRanges([sourceDocument, embeddedDocument, map], range, filter) {
for (const [mappedStart, mappedEnd] of map.toGeneratedRange(sourceDocument.offsetAt(range.start), sourceDocument.offsetAt(range.end), true, filter)) {
yield { start: embeddedDocument.positionAt(mappedStart), end: embeddedDocument.positionAt(mappedEnd) };
}
}
function* getSourcePositions([sourceDocument, embeddedDocument, map], position, filter = () => true) {
for (const mapped of map.toSourceLocation(embeddedDocument.offsetAt(position), filter)) {
yield sourceDocument.positionAt(mapped[0]);
}
}
function* getGeneratedPositions([sourceDocument, embeddedDocument, map], position, filter = () => true) {
for (const mapped of map.toGeneratedLocation(sourceDocument.offsetAt(position), filter)) {
yield embeddedDocument.positionAt(mapped[0]);
}
}
function* getLinkedCodePositions(document, linkedMap, posotion) {
for (const linkedPosition of linkedMap.getLinkedOffsets(document.offsetAt(posotion))) {
yield document.positionAt(linkedPosition);
}
}
//# sourceMappingURL=featureWorkers.js.map

View File

@@ -0,0 +1,23 @@
import type * as vscode from 'vscode-languageserver-protocol';
import type { TextDocument } from 'vscode-languageserver-textdocument';
import { URI } from 'vscode-uri';
import type { LanguageServiceContext } from '../types';
export declare function transformDocumentLinkTarget(_target: string, context: LanguageServiceContext): URI;
export declare function transformMarkdown(content: string, context: LanguageServiceContext): string;
export declare function transformCompletionItem<T extends vscode.CompletionItem>(item: T, getOtherRange: (range: vscode.Range) => vscode.Range | undefined, document: vscode.TextDocument, context: LanguageServiceContext): T;
export declare function transformCompletionList<T extends vscode.CompletionList>(completionList: T, getOtherRange: (range: vscode.Range) => vscode.Range | undefined, document: TextDocument, context: LanguageServiceContext): T;
export declare function transformDocumentSymbol(symbol: vscode.DocumentSymbol, getOtherRange: (range: vscode.Range) => vscode.Range | undefined): vscode.DocumentSymbol | undefined;
export declare function transformFoldingRanges(ranges: vscode.FoldingRange[], getOtherRange: (range: vscode.Range) => vscode.Range | undefined): vscode.FoldingRange[];
export declare function transformHover<T extends vscode.Hover>(hover: T, getOtherRange: (range: vscode.Range) => vscode.Range | undefined): T | undefined;
export declare function transformLocation<T extends {
range: vscode.Range;
}>(location: T, getOtherRange: (range: vscode.Range) => vscode.Range | undefined): T | undefined;
export declare function transformLocations<T extends {
range: vscode.Range;
}>(locations: T[], getOtherRange: (range: vscode.Range) => vscode.Range | undefined): T[];
export declare function transformSelectionRange<T extends vscode.SelectionRange>(location: T, getOtherRange: (range: vscode.Range) => vscode.Range | undefined): T | undefined;
export declare function transformSelectionRanges<T extends vscode.SelectionRange>(locations: T[], getOtherRange: (range: vscode.Range) => vscode.Range | undefined): T[];
export declare function transformTextEdit<T extends vscode.TextEdit | vscode.InsertReplaceEdit>(textEdit: T, getOtherRange: (range: vscode.Range) => vscode.Range | undefined, document: vscode.TextDocument): T | undefined;
export declare function transformWorkspaceSymbol(symbol: vscode.WorkspaceSymbol, getOtherLocation: (location: vscode.Location) => vscode.Location | undefined): vscode.WorkspaceSymbol | undefined;
export declare function transformWorkspaceEdit(edit: vscode.WorkspaceEdit, context: LanguageServiceContext, mode: 'fileName' | 'rename' | 'codeAction' | undefined, versions?: Record<string, number>): vscode.WorkspaceEdit | undefined;
export declare function pushEditToDocumentChanges(arr: NonNullable<vscode.WorkspaceEdit['documentChanges']>, item: NonNullable<vscode.WorkspaceEdit['documentChanges']>[number]): void;

View File

@@ -0,0 +1,460 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.transformDocumentLinkTarget = transformDocumentLinkTarget;
exports.transformMarkdown = transformMarkdown;
exports.transformCompletionItem = transformCompletionItem;
exports.transformCompletionList = transformCompletionList;
exports.transformDocumentSymbol = transformDocumentSymbol;
exports.transformFoldingRanges = transformFoldingRanges;
exports.transformHover = transformHover;
exports.transformLocation = transformLocation;
exports.transformLocations = transformLocations;
exports.transformSelectionRange = transformSelectionRange;
exports.transformSelectionRanges = transformSelectionRanges;
exports.transformTextEdit = transformTextEdit;
exports.transformWorkspaceSymbol = transformWorkspaceSymbol;
exports.transformWorkspaceEdit = transformWorkspaceEdit;
exports.pushEditToDocumentChanges = pushEditToDocumentChanges;
const language_core_1 = require("@volar/language-core");
const vscode_uri_1 = require("vscode-uri");
const featureWorkers_1 = require("./featureWorkers");
function transformDocumentLinkTarget(_target, context) {
let target = vscode_uri_1.URI.parse(_target);
const decoded = context.decodeEmbeddedDocumentUri(target);
if (!decoded) {
return target;
}
const embeddedRange = target.fragment.match(/^L(\d+)(,(\d+))?(-L(\d+)(,(\d+))?)?$/);
const sourceScript = context.language.scripts.get(decoded[0]);
const virtualCode = sourceScript?.generated?.embeddedCodes.get(decoded[1]);
target = decoded[0];
if (embeddedRange && sourceScript && virtualCode) {
const embeddedDocument = context.documents.get(context.encodeEmbeddedDocumentUri(sourceScript.id, virtualCode.id), virtualCode.languageId, virtualCode.snapshot);
for (const [sourceScript, map] of context.language.maps.forEach(virtualCode)) {
if (!map.mappings.some(mapping => (0, language_core_1.isDocumentLinkEnabled)(mapping.data))) {
continue;
}
const sourceDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot);
const docs = [sourceDocument, embeddedDocument, map];
const startLine = Number(embeddedRange[1]) - 1;
const startCharacter = Number(embeddedRange[3] ?? 1) - 1;
if (embeddedRange[5] !== undefined) {
const endLine = Number(embeddedRange[5]) - 1;
const endCharacter = Number(embeddedRange[7] ?? 1) - 1;
const sourceRange = (0, featureWorkers_1.getSourceRange)(docs, {
start: { line: startLine, character: startCharacter },
end: { line: endLine, character: endCharacter },
});
if (sourceRange) {
target = target.with({
fragment: 'L' + (sourceRange.start.line + 1) + ',' + (sourceRange.start.character + 1)
+ '-L' + (sourceRange.end.line + 1) + ',' + (sourceRange.end.character + 1),
});
break;
}
}
else {
let mapped = false;
for (const sourcePos of (0, featureWorkers_1.getSourcePositions)(docs, { line: startLine, character: startCharacter })) {
mapped = true;
target = target.with({
fragment: 'L' + (sourcePos.line + 1) + ',' + (sourcePos.character + 1),
});
break;
}
if (mapped) {
break;
}
}
}
}
return target;
}
function transformMarkdown(content, context) {
return content.replace(/(?!\()volar-embedded-content:\/\/\w+\/[^)]+/g, match => {
const segments = match.split('|');
segments[0] = transformDocumentLinkTarget(segments[0], context).toString();
return segments.join('|');
});
}
function transformCompletionItem(item, getOtherRange, document, context) {
return {
...item,
additionalTextEdits: item.additionalTextEdits
?.map(edit => transformTextEdit(edit, getOtherRange, document))
.filter(edit => !!edit),
textEdit: item.textEdit
? transformTextEdit(item.textEdit, getOtherRange, document)
: undefined,
documentation: item.documentation ?
typeof item.documentation === 'string' ? transformMarkdown(item.documentation, context) :
item.documentation.kind === 'markdown' ?
{ kind: 'markdown', value: transformMarkdown(item.documentation.value, context) }
: item.documentation
: undefined
};
}
function transformCompletionList(completionList, getOtherRange, document, context) {
return {
isIncomplete: completionList.isIncomplete,
itemDefaults: completionList.itemDefaults ? {
...completionList.itemDefaults,
editRange: completionList.itemDefaults.editRange
? 'replace' in completionList.itemDefaults.editRange
? {
insert: getOtherRange(completionList.itemDefaults.editRange.insert),
replace: getOtherRange(completionList.itemDefaults.editRange.replace),
}
: getOtherRange(completionList.itemDefaults.editRange)
: undefined,
} : undefined,
items: completionList.items.map(item => transformCompletionItem(item, getOtherRange, document, context)),
};
}
function transformDocumentSymbol(symbol, getOtherRange) {
const range = getOtherRange(symbol.range);
if (!range) {
return;
}
const selectionRange = getOtherRange(symbol.selectionRange);
if (!selectionRange) {
return;
}
return {
...symbol,
range,
selectionRange,
children: symbol.children
?.map(child => transformDocumentSymbol(child, getOtherRange))
.filter(child => !!child),
};
}
function transformFoldingRanges(ranges, getOtherRange) {
const result = [];
for (const range of ranges) {
const otherRange = getOtherRange({
start: { line: range.startLine, character: range.startCharacter ?? 0 },
end: { line: range.endLine, character: range.endCharacter ?? 0 },
});
if (otherRange) {
range.startLine = otherRange.start.line;
range.endLine = otherRange.end.line;
if (range.startCharacter !== undefined) {
range.startCharacter = otherRange.start.character;
}
if (range.endCharacter !== undefined) {
range.endCharacter = otherRange.end.character;
}
result.push(range);
}
}
return result;
}
function transformHover(hover, getOtherRange) {
if (!hover?.range) {
return hover;
}
const range = getOtherRange(hover.range);
if (!range) {
return;
}
return {
...hover,
range,
};
}
function transformLocation(location, getOtherRange) {
const range = getOtherRange(location.range);
if (!range) {
return;
}
return {
...location,
range,
};
}
function transformLocations(locations, getOtherRange) {
return locations
.map(location => transformLocation(location, getOtherRange))
.filter(location => !!location);
}
function transformSelectionRange(location, getOtherRange) {
const range = getOtherRange(location.range);
if (!range) {
return;
}
const parent = location.parent ? transformSelectionRange(location.parent, getOtherRange) : undefined;
return {
range,
parent,
};
}
function transformSelectionRanges(locations, getOtherRange) {
return locations
.map(location => transformSelectionRange(location, getOtherRange))
.filter(location => !!location);
}
function transformTextEdit(textEdit, getOtherRange, document) {
if ('range' in textEdit) {
let range = getOtherRange(textEdit.range);
if (range) {
return {
...textEdit,
range,
};
}
;
const cover = tryRecoverTextEdit(getOtherRange, textEdit.range, textEdit.newText, document);
if (cover) {
return {
...textEdit,
range: cover.range,
newText: cover.newText,
};
}
}
else if ('replace' in textEdit && 'insert' in textEdit) {
const insert = getOtherRange(textEdit.insert);
const replace = insert ? getOtherRange(textEdit.replace) : undefined;
if (insert && replace) {
return {
...textEdit,
insert,
replace,
};
}
const recoverInsert = tryRecoverTextEdit(getOtherRange, textEdit.insert, textEdit.newText, document);
const recoverReplace = recoverInsert ? tryRecoverTextEdit(getOtherRange, textEdit.replace, textEdit.newText, document) : undefined;
if (recoverInsert && recoverReplace && recoverInsert.newText === recoverReplace.newText) {
return {
...textEdit,
insert: recoverInsert.range,
replace: recoverReplace.range,
newText: recoverInsert.newText,
};
}
}
}
/**
* update edit text from ". foo" to " foo"
* fix https://github.com/johnsoncodehk/volar/issues/2155
*/
function tryRecoverTextEdit(getOtherRange, replaceRange, newText, document) {
if (replaceRange.start.line === replaceRange.end.line && replaceRange.end.character > replaceRange.start.character) {
let character = replaceRange.start.character;
while (newText.length && replaceRange.end.character > character) {
const newStart = { line: replaceRange.start.line, character: replaceRange.start.character + 1 };
if (document.getText({ start: replaceRange.start, end: newStart }) === newText[0]) {
newText = newText.slice(1);
character++;
const otherRange = getOtherRange({ start: newStart, end: replaceRange.end });
if (otherRange) {
return {
newText,
range: otherRange,
};
}
}
else {
break;
}
}
}
}
function transformWorkspaceSymbol(symbol, getOtherLocation) {
if (!('range' in symbol.location)) {
return symbol;
}
const loc = getOtherLocation(symbol.location);
if (!loc) {
return;
}
return {
...symbol,
location: loc,
};
}
function transformWorkspaceEdit(edit, context, mode, versions = {}) {
const sourceResult = {};
let hasResult = false;
for (const tsUri in edit.changeAnnotations) {
sourceResult.changeAnnotations ??= {};
const tsAnno = edit.changeAnnotations[tsUri];
const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsUri));
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
if (sourceScript && virtualCode) {
for (const [sourceScript] of context.language.maps.forEach(virtualCode)) {
// TODO: check capability?
const uri = sourceScript.id.toString();
sourceResult.changeAnnotations[uri] = tsAnno;
break;
}
}
else {
sourceResult.changeAnnotations[tsUri] = tsAnno;
}
}
for (const tsUri in edit.changes) {
sourceResult.changes ??= {};
const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsUri));
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
if (sourceScript && virtualCode) {
const embeddedDocument = context.documents.get(context.encodeEmbeddedDocumentUri(sourceScript.id, virtualCode.id), virtualCode.languageId, virtualCode.snapshot);
for (const [sourceScript, map] of context.language.maps.forEach(virtualCode)) {
const sourceDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot);
const docs = [sourceDocument, embeddedDocument, map];
const tsEdits = edit.changes[tsUri];
for (const tsEdit of tsEdits) {
if (mode === 'rename' || mode === 'fileName' || mode === 'codeAction') {
let _data;
const range = (0, featureWorkers_1.getSourceRange)(docs, tsEdit.range, data => {
_data = data;
return (0, language_core_1.isRenameEnabled)(data);
});
if (range) {
sourceResult.changes[sourceDocument.uri] ??= [];
sourceResult.changes[sourceDocument.uri].push({
newText: (0, language_core_1.resolveRenameEditText)(tsEdit.newText, _data),
range,
});
hasResult = true;
}
}
else {
const range = (0, featureWorkers_1.getSourceRange)(docs, tsEdit.range);
if (range) {
sourceResult.changes[sourceDocument.uri] ??= [];
sourceResult.changes[sourceDocument.uri].push({ newText: tsEdit.newText, range });
hasResult = true;
}
}
}
}
}
else {
sourceResult.changes[tsUri] = edit.changes[tsUri];
hasResult = true;
}
}
if (edit.documentChanges) {
for (const tsDocEdit of edit.documentChanges) {
sourceResult.documentChanges ??= [];
let sourceEdit;
if ('textDocument' in tsDocEdit) {
const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsDocEdit.textDocument.uri));
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
if (sourceScript && virtualCode) {
const embeddedDocument = context.documents.get(context.encodeEmbeddedDocumentUri(sourceScript.id, virtualCode.id), virtualCode.languageId, virtualCode.snapshot);
for (const [sourceScript, map] of context.language.maps.forEach(virtualCode)) {
const sourceDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot);
const docs = [sourceDocument, embeddedDocument, map];
sourceEdit = {
textDocument: {
uri: sourceDocument.uri,
version: versions[sourceDocument.uri] ?? null,
},
edits: [],
};
for (const tsEdit of tsDocEdit.edits) {
if (mode === 'rename' || mode === 'fileName' || mode === 'codeAction') {
let _data;
const range = (0, featureWorkers_1.getSourceRange)(docs, tsEdit.range, data => {
_data = data;
// fix https://github.com/johnsoncodehk/volar/issues/1091
return (0, language_core_1.isRenameEnabled)(data);
});
if (range) {
sourceEdit.edits.push({
annotationId: 'annotationId' in tsEdit ? tsEdit.annotationId : undefined,
newText: (0, language_core_1.resolveRenameEditText)(tsEdit.newText, _data),
range,
});
}
}
else {
const range = (0, featureWorkers_1.getSourceRange)(docs, tsEdit.range);
if (range) {
sourceEdit.edits.push({
annotationId: 'annotationId' in tsEdit ? tsEdit.annotationId : undefined,
newText: tsEdit.newText,
range,
});
}
}
}
if (!sourceEdit.edits.length) {
sourceEdit = undefined;
}
}
}
else {
sourceEdit = tsDocEdit;
}
}
else if (tsDocEdit.kind === 'create') {
sourceEdit = tsDocEdit; // TODO: remove .ts?
}
else if (tsDocEdit.kind === 'rename') {
const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsDocEdit.oldUri));
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
if (virtualCode) {
for (const [sourceScript] of context.language.maps.forEach(virtualCode)) {
// TODO: check capability?
sourceEdit = {
kind: 'rename',
oldUri: sourceScript.id.toString(),
newUri: tsDocEdit.newUri /* TODO: remove .ts? */,
options: tsDocEdit.options,
annotationId: tsDocEdit.annotationId,
};
}
}
else {
sourceEdit = tsDocEdit;
}
}
else if (tsDocEdit.kind === 'delete') {
const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsDocEdit.uri));
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
if (virtualCode) {
for (const [sourceScript] of context.language.maps.forEach(virtualCode)) {
// TODO: check capability?
sourceEdit = {
kind: 'delete',
uri: sourceScript.id.toString(),
options: tsDocEdit.options,
annotationId: tsDocEdit.annotationId,
};
}
}
else {
sourceEdit = tsDocEdit;
}
}
if (sourceEdit) {
pushEditToDocumentChanges(sourceResult.documentChanges, sourceEdit);
hasResult = true;
}
}
}
if (hasResult) {
return sourceResult;
}
}
function pushEditToDocumentChanges(arr, item) {
const current = arr.find(edit => 'textDocument' in edit
&& 'textDocument' in item
&& edit.textDocument.uri === item.textDocument.uri);
if (current) {
current.edits.push(...item.edits);
}
else {
arr.push(item);
}
}
//# sourceMappingURL=transform.js.map

View File

@@ -0,0 +1,3 @@
import type { URI } from 'vscode-uri';
export type UriMap<T> = ReturnType<typeof createUriMap<T>>;
export declare function createUriMap<T>(caseSensitive?: boolean): Map<URI, T>;

View File

@@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createUriMap = createUriMap;
function createUriMap(caseSensitive = false) {
const map = new Map();
const rawUriToNormalizedUri = new Map();
const normalizedUriToRawUri = new Map();
return {
get size() {
return map.size;
},
get [Symbol.toStringTag]() {
return 'UriMap';
},
[Symbol.iterator]() {
return this.entries();
},
clear() {
rawUriToNormalizedUri.clear();
normalizedUriToRawUri.clear();
return map.clear();
},
values() {
return map.values();
},
*keys() {
for (const normalizedUri of map.keys()) {
yield normalizedUriToRawUri.get(normalizedUri);
}
return undefined;
},
*entries() {
for (const [normalizedUri, item] of map.entries()) {
yield [normalizedUriToRawUri.get(normalizedUri), item];
}
return undefined;
},
forEach(callbackfn, thisArg) {
for (const [uri, item] of this.entries()) {
callbackfn.call(thisArg, item, uri, this);
}
},
delete(uri) {
return map.delete(toKey(uri));
},
get(uri) {
return map.get(toKey(uri));
},
has(uri) {
return map.has(toKey(uri));
},
set(uri, item) {
map.set(toKey(uri), item);
return this;
},
};
function toKey(uri) {
const rawUri = uri.toString();
if (!rawUriToNormalizedUri.has(rawUri)) {
let normalizedUri = uri.toString();
if (!caseSensitive) {
normalizedUri = normalizedUri.toLowerCase();
}
rawUriToNormalizedUri.set(rawUri, normalizedUri);
normalizedUriToRawUri.set(normalizedUri, uri);
}
return rawUriToNormalizedUri.get(rawUri);
}
}
//# sourceMappingURL=uriMap.js.map