Skip to content

Commit

Permalink
fix(language-service): formatting range param not converted correctly (
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Feb 17, 2024
1 parent 8b74be8 commit b9da423
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { TextDocument } from 'vscode-languageserver-textdocument';
import { SourceMapWithDocuments } from '../documents';
import type { ServiceContext, ServicePluginInstance } from '../types';
import { NoneCancellationToken } from '../utils/cancellation';
import { isInsideRange, stringToSnapshot } from '../utils/common';
import { isInsideRange, stringToSnapshot, findOverlapCodeRange } from '../utils/common';
import { getEmbeddedFilesByLevel as getEmbeddedCodesByLevel } from '../utils/featureWorkers';

export function register(context: ServiceContext) {
Expand Down Expand Up @@ -96,6 +96,20 @@ export function register(context: ServiceContext) {
);
}
}
else if (range) {
const mapped = findOverlapCodeRange(
docMap.sourceFileDocument.offsetAt(range.start),
docMap.sourceFileDocument.offsetAt(range.end),
docMap.map,
isFormattingEnabled,
);
if (mapped) {
embeddedCodeResult = await tryFormat(docMap.virtualFileDocument, {
start: docMap.virtualFileDocument.positionAt(mapped.start),
end: docMap.virtualFileDocument.positionAt(mapped.end),
});
}
}
else {
embeddedCodeResult = await tryFormat(docMap.virtualFileDocument, {
start: docMap.virtualFileDocument.positionAt(0),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type * as vscode from 'vscode-languageserver-protocol';
import type { SemanticToken, ServiceContext } from '../types';
import { SemanticTokensBuilder } from '../utils/SemanticTokensBuilder';
import { NoneCancellationToken } from '../utils/cancellation';
import { notEmpty } from '../utils/common';
import { notEmpty, findOverlapCodeRange } from '../utils/common';
import { languageFeatureWorker } from '../utils/featureWorkers';
import { isSemanticTokensEnabled } from '@volar/language-core';

Expand Down Expand Up @@ -34,46 +34,16 @@ export function register(context: ServiceContext) {
uri,
() => range!,
function* (map) {

for (const mapped of map.getGeneratedRanges(range!, isSemanticTokensEnabled)) {
yield mapped;
return;
}

let result: {
start: number;
end: number;
} | undefined;

const start = document.offsetAt(range!.start);
const end = document.offsetAt(range!.end);

for (const mapping of map.map.mappings) {
if (isSemanticTokensEnabled(mapping.data)) {
for (let i = 0; i < mapping.sourceOffsets.length; i++) {
if (
mapping.sourceOffsets[i] + mapping.lengths[i] > start
&& mapping.sourceOffsets[i] < end
) {
if (!result) {
result = {
start: mapping.generatedOffsets[i],
end: mapping.generatedOffsets[i] + mapping.lengths[i],
};
}
else {
result.start = Math.min(result.start, mapping.generatedOffsets[i]);
result.end = Math.max(result.end, mapping.generatedOffsets[i] + mapping.lengths[i]);
}
}
}
}
}

if (result) {
yield {
start: map.virtualFileDocument.positionAt(result.start),
end: map.virtualFileDocument.positionAt(result.end),
const mapped = findOverlapCodeRange(
map.sourceFileDocument.offsetAt(range!.start),
map.sourceFileDocument.offsetAt(range!.end),
map.map,
isSemanticTokensEnabled,
);
if (mapped) {
return {
start: map.virtualFileDocument.positionAt(mapped.start),
end: map.virtualFileDocument.positionAt(mapped.end),
};
}
},
Expand Down
55 changes: 55 additions & 0 deletions packages/language-service/lib/utils/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,60 @@
import type * as ts from 'typescript';
import type * as vscode from 'vscode-languageserver-protocol';
import type { CodeInformation, SourceMap } from '@volar/language-core';

export function findOverlapCodeRange(
start: number,
end: number,
map: SourceMap<CodeInformation>,
filter: (data: CodeInformation) => boolean,
) {
let mappedStart: number | undefined;
let mappedEnd: number | undefined;

for (const [mapped, mapping] of map.getGeneratedOffsets(start)) {
if (filter(mapping.data)) {
mappedStart = mapped;
break;
}
}
for (const [mapped, mapping] of map.getGeneratedOffsets(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) {
if (mappedStart === undefined) {
mappedStart = overlap.start + mapping.generatedOffsets[0] - mappingStart;
}
else {
mappedStart = Math.min(mappedStart, overlap.start + mapping.generatedOffsets[0] - mappingStart);
}
if (mappedEnd === undefined) {
mappedEnd = overlap.end + mapping.generatedOffsets[0] - mappingStart;
}
else {
mappedEnd = Math.max(mappedEnd, overlap.end + mapping.generatedOffsets[0] - mappingStart);
}
}
}
}
}

if (mappedStart !== undefined && mappedEnd !== undefined) {
return {
start: mappedStart,
end: mappedEnd,
};
}
}

export function getOverlapRange(
range1Start: number,
Expand Down
75 changes: 75 additions & 0 deletions packages/language-service/tests/findOverlapCodeRange.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { describe, expect, it } from 'vitest';
import { findOverlapCodeRange } from '../lib/utils/common';
import { CodeInformation, Mapping, SourceMap } from '@volar/language-core';

// test code: <html><body><p>Hello</p></body></html>

describe(`Test findOverlapCodeRange()`, () => {

it('signal mapping', () => {
const mappings: Mapping<CodeInformation>[] = [
{
sourceOffsets: [0],
generatedOffsets: [0],
lengths: [38],
data: { verification: true, completion: true, semantic: true, navigation: true, structure: true, format: true },
},
];
const map = new SourceMap(mappings);

expect(findOverlapCodeRange(0, 38, map, () => true)).toEqual({ start: 0, end: 38 });
expect(findOverlapCodeRange(6, 31, map, () => true)).toEqual({ start: 6, end: 31 });
});

it('fallback to valid range', () => {
const mappings: Mapping<CodeInformation>[] = [
{
sourceOffsets: [6],
generatedOffsets: [6],
lengths: [25],
data: { verification: true, completion: true, semantic: true, navigation: true, structure: true, format: true },
},
];
const map = new SourceMap(mappings);

expect(findOverlapCodeRange(5, 32, map, () => true)).toEqual({ start: 6, end: 31 });
expect(findOverlapCodeRange(7, 32, map, () => true)).toEqual({ start: 7, end: 31 });
expect(findOverlapCodeRange(5, 30, map, () => true)).toEqual({ start: 6, end: 30 });
});

it('fallback to valid range (offset)', () => {
const mappings: Mapping<CodeInformation>[] = [
{
sourceOffsets: [6],
generatedOffsets: [7],
lengths: [25],
data: { verification: true, completion: true, semantic: true, navigation: true, structure: true, format: true },
},
];
const map = new SourceMap(mappings);

expect(findOverlapCodeRange(5, 32, map, () => true)).toEqual({ start: 7, end: 32 });
expect(findOverlapCodeRange(7, 32, map, () => true)).toEqual({ start: 8, end: 32 });
expect(findOverlapCodeRange(5, 30, map, () => true)).toEqual({ start: 7, end: 31 });
});

it('mutilple mappings', () => {
const mappings: Mapping<CodeInformation>[] = [
{
sourceOffsets: [6],
generatedOffsets: [6],
lengths: [6],
data: { verification: true, completion: true, semantic: true, navigation: true, structure: true, format: true },
},
{
sourceOffsets: [24],
generatedOffsets: [24],
lengths: [7],
data: { verification: true, completion: true, semantic: true, navigation: true, structure: true, format: true },
},
];
const map = new SourceMap(mappings);

expect(findOverlapCodeRange(0, 38, map, () => true)).toEqual({ start: 6, end: 31 });
});
});

0 comments on commit b9da423

Please sign in to comment.