@pieceful/ravel-projection 0.2.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.
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/package.json +40 -0
- package/src/builder.js +533 -0
- package/src/context.js +108 -0
- package/src/index.d.ts +331 -0
- package/src/index.js +68 -0
- package/src/internal.js +163 -0
- package/src/line-index.js +130 -0
- package/src/mapping.js +369 -0
- package/src/service.js +384 -0
- package/src/transforms.d.ts +29 -0
- package/src/transforms.js +563 -0
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
export type SnapshotId = string;
|
|
2
|
+
export type ProjectionId = string;
|
|
3
|
+
export type ArtifactId = string;
|
|
4
|
+
export type TargetId = string;
|
|
5
|
+
export type PieceId = string;
|
|
6
|
+
export type OccurrenceId = string;
|
|
7
|
+
export type ProjectionStage = "authoring" | "assembled" | "transformed" | "emitted";
|
|
8
|
+
export type MappingKind = "exact" | "anchored" | "transformed" | "opaque" | "synthetic";
|
|
9
|
+
export type MappingQuality = MappingKind;
|
|
10
|
+
export type Affinity = "left" | "right" | "none";
|
|
11
|
+
export type PositionEncoding = "utf-8" | "utf-16" | "utf-32";
|
|
12
|
+
|
|
13
|
+
export interface OffsetRange { start: number; end: number; }
|
|
14
|
+
export interface SourcePosition { line: number; column: number; offset: number; }
|
|
15
|
+
export interface SourceRange { start: SourcePosition; end: SourcePosition; }
|
|
16
|
+
export interface SourceLocation { uri: string; range: SourceRange; }
|
|
17
|
+
export interface ProjectionCapabilities {
|
|
18
|
+
navigation: boolean;
|
|
19
|
+
diagnostics: boolean;
|
|
20
|
+
completion: boolean;
|
|
21
|
+
writableEdits: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface TransformStep {
|
|
24
|
+
kind?: "transform";
|
|
25
|
+
name?: string;
|
|
26
|
+
phase?: number;
|
|
27
|
+
source?: SourceLocation;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ProjectionSegment {
|
|
31
|
+
readonly virtual: OffsetRange;
|
|
32
|
+
readonly source?: SourceLocation;
|
|
33
|
+
readonly pieceId?: PieceId;
|
|
34
|
+
readonly occurrenceId?: OccurrenceId;
|
|
35
|
+
readonly expansionPath: readonly PieceId[];
|
|
36
|
+
readonly kind: MappingKind;
|
|
37
|
+
readonly role?: string;
|
|
38
|
+
readonly startAffinity?: "left" | "right";
|
|
39
|
+
readonly endAffinity?: "left" | "right";
|
|
40
|
+
readonly transformChain?: readonly TransformStep[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ExpansionOccurrence {
|
|
44
|
+
readonly id: OccurrenceId;
|
|
45
|
+
readonly pieceId: PieceId;
|
|
46
|
+
readonly projectionId: ProjectionId;
|
|
47
|
+
readonly virtual: OffsetRange;
|
|
48
|
+
readonly invocationSource?: SourceLocation;
|
|
49
|
+
readonly definitionSource?: SourceLocation;
|
|
50
|
+
readonly expansionPath: readonly PieceId[];
|
|
51
|
+
readonly parentOccurrenceId?: OccurrenceId;
|
|
52
|
+
readonly childOccurrenceIds: readonly OccurrenceId[];
|
|
53
|
+
readonly implicit?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface LineIndex {
|
|
57
|
+
readonly text: string;
|
|
58
|
+
readonly textLength: number;
|
|
59
|
+
readonly lineStarts: readonly number[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ProjectionIndexes {
|
|
63
|
+
readonly virtual: readonly number[];
|
|
64
|
+
readonly virtualMaxEnds: readonly number[];
|
|
65
|
+
readonly source: Readonly<Record<string, readonly number[]>>;
|
|
66
|
+
readonly sourceMaxEnds: Readonly<Record<string, readonly number[]>>;
|
|
67
|
+
readonly occurrenceById: Readonly<Record<string, number>>;
|
|
68
|
+
readonly children: Readonly<Record<string, readonly number[]>>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface ProjectionDiagnostic {
|
|
72
|
+
code: string;
|
|
73
|
+
severity: "error" | "warning" | "info";
|
|
74
|
+
message: string;
|
|
75
|
+
source: SourceLocation;
|
|
76
|
+
related?: SourceLocation[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface VirtualDocument {
|
|
80
|
+
readonly id: ProjectionId;
|
|
81
|
+
readonly uri: string;
|
|
82
|
+
readonly snapshotId: SnapshotId;
|
|
83
|
+
readonly sourceVersions: Readonly<Record<string, number>>;
|
|
84
|
+
readonly version: number;
|
|
85
|
+
readonly workspaceId: string;
|
|
86
|
+
readonly artifactId: ArtifactId;
|
|
87
|
+
readonly targetId: TargetId;
|
|
88
|
+
readonly stage: ProjectionStage;
|
|
89
|
+
readonly languageId: string;
|
|
90
|
+
readonly text: string;
|
|
91
|
+
readonly mappings: readonly ProjectionSegment[];
|
|
92
|
+
readonly occurrences: readonly ExpansionOccurrence[];
|
|
93
|
+
readonly lineIndex: LineIndex;
|
|
94
|
+
readonly sourceLineIndexes: Readonly<Record<string, LineIndex>>;
|
|
95
|
+
readonly indexes: ProjectionIndexes;
|
|
96
|
+
readonly contentHash: string;
|
|
97
|
+
readonly inputHash: string;
|
|
98
|
+
readonly artifactSource?: SourceLocation;
|
|
99
|
+
readonly projectionDiagnostics: readonly ProjectionDiagnostic[];
|
|
100
|
+
readonly capabilities: ProjectionCapabilities;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ProjectionBuildOptions {
|
|
104
|
+
artifactId?: ArtifactId;
|
|
105
|
+
targetId?: TargetId;
|
|
106
|
+
workspaceId?: string;
|
|
107
|
+
stage?: ProjectionStage;
|
|
108
|
+
languageId?: string;
|
|
109
|
+
projectionId?: ProjectionId;
|
|
110
|
+
uri?: string;
|
|
111
|
+
path?: string;
|
|
112
|
+
snapshotId?: SnapshotId;
|
|
113
|
+
sourceVersions?: Record<string, number>;
|
|
114
|
+
sourceTexts?: Map<string, string> | Record<string, string>;
|
|
115
|
+
version?: number;
|
|
116
|
+
prefix?: string;
|
|
117
|
+
suffix?: string;
|
|
118
|
+
rootPieceId?: PieceId;
|
|
119
|
+
capabilities?: Partial<ProjectionCapabilities>;
|
|
120
|
+
signal?: AbortSignal;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface ProjectionSelection {
|
|
124
|
+
projectionId?: ProjectionId;
|
|
125
|
+
targetId?: TargetId;
|
|
126
|
+
artifactId?: ArtifactId;
|
|
127
|
+
stage?: ProjectionStage;
|
|
128
|
+
occurrenceId?: OccurrenceId;
|
|
129
|
+
affinity?: Affinity;
|
|
130
|
+
projectionVersion?: number;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface ProjectionMappingMatch {
|
|
134
|
+
projectionId: ProjectionId;
|
|
135
|
+
uri: string;
|
|
136
|
+
projectionVersion: number;
|
|
137
|
+
snapshotId: SnapshotId;
|
|
138
|
+
virtual: OffsetRange;
|
|
139
|
+
virtualOffset?: number;
|
|
140
|
+
relatedVirtual?: OffsetRange;
|
|
141
|
+
source?: SourceLocation;
|
|
142
|
+
sourceOffset?: number;
|
|
143
|
+
sourceOverlap?: OffsetRange;
|
|
144
|
+
pieceId?: PieceId;
|
|
145
|
+
occurrenceId?: OccurrenceId;
|
|
146
|
+
quality: MappingQuality;
|
|
147
|
+
role: string;
|
|
148
|
+
affinity?: Affinity;
|
|
149
|
+
writable: boolean;
|
|
150
|
+
segmentIndex: number;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export type MappingQueryResult =
|
|
154
|
+
| { readonly ok: true; readonly matches: readonly ProjectionMappingMatch[] }
|
|
155
|
+
| { readonly ok: false; readonly reason: string; readonly matches: readonly [] };
|
|
156
|
+
|
|
157
|
+
export interface GeneratedHighlight {
|
|
158
|
+
range: OffsetRange;
|
|
159
|
+
kind: string;
|
|
160
|
+
categories: readonly string[];
|
|
161
|
+
mappingKind: MappingKind;
|
|
162
|
+
pieceId?: PieceId;
|
|
163
|
+
occurrenceId?: OccurrenceId;
|
|
164
|
+
}
|
|
165
|
+
export interface ExpansionBreadcrumbItem {
|
|
166
|
+
occurrenceId: OccurrenceId;
|
|
167
|
+
pieceId: PieceId;
|
|
168
|
+
label: string;
|
|
169
|
+
virtual: OffsetRange;
|
|
170
|
+
invocationSource?: SourceLocation;
|
|
171
|
+
}
|
|
172
|
+
export interface OccurrenceSummary {
|
|
173
|
+
occurrenceId: OccurrenceId;
|
|
174
|
+
pieceId: PieceId;
|
|
175
|
+
targetId: TargetId;
|
|
176
|
+
artifactId: ArtifactId;
|
|
177
|
+
stage: ProjectionStage;
|
|
178
|
+
virtual: OffsetRange;
|
|
179
|
+
pathLabel: string;
|
|
180
|
+
}
|
|
181
|
+
export interface GeneratedContext {
|
|
182
|
+
readonly ok: true;
|
|
183
|
+
readonly projection: VirtualDocument;
|
|
184
|
+
readonly projectionVersion: number;
|
|
185
|
+
readonly selectedOccurrenceId: OccurrenceId;
|
|
186
|
+
readonly visibleRange: OffsetRange;
|
|
187
|
+
readonly highlights: readonly GeneratedHighlight[];
|
|
188
|
+
readonly breadcrumb: readonly ExpansionBreadcrumbItem[];
|
|
189
|
+
readonly siblings: readonly OccurrenceSummary[];
|
|
190
|
+
}
|
|
191
|
+
export type GeneratedContextResult = GeneratedContext | { readonly ok: false; readonly reason: string };
|
|
192
|
+
|
|
193
|
+
export interface ProjectionTextEdit { range: OffsetRange; text: string; }
|
|
194
|
+
export interface ProjectionTextChange {
|
|
195
|
+
kind: "none" | "incremental" | "full";
|
|
196
|
+
changes: readonly ProjectionTextEdit[];
|
|
197
|
+
}
|
|
198
|
+
export interface ProjectionDelta {
|
|
199
|
+
readonly snapshotId: SnapshotId;
|
|
200
|
+
readonly sourceVersions: Readonly<Record<string, number>>;
|
|
201
|
+
readonly opened: readonly VirtualDocument[];
|
|
202
|
+
readonly changed: readonly VirtualDocument[];
|
|
203
|
+
readonly unchanged: readonly VirtualDocument[];
|
|
204
|
+
readonly closed: readonly VirtualDocument[];
|
|
205
|
+
readonly textChanges: Readonly<Record<ProjectionId, ProjectionTextChange>>;
|
|
206
|
+
readonly projectionDiagnostics: readonly ProjectionDiagnostic[];
|
|
207
|
+
}
|
|
208
|
+
export interface ProjectionSnapshot {
|
|
209
|
+
readonly id?: SnapshotId;
|
|
210
|
+
readonly snapshotId?: SnapshotId;
|
|
211
|
+
readonly version?: number;
|
|
212
|
+
readonly program: Record<string, any>;
|
|
213
|
+
readonly sourceVersions?: Readonly<Record<string, number>>;
|
|
214
|
+
readonly sourceTexts?: Readonly<Record<string, string>>;
|
|
215
|
+
readonly projections?: readonly ProjectionBuildOptions[] | ((snapshot: ProjectionSnapshot) => readonly ProjectionBuildOptions[]);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface ProjectionServiceOptions {
|
|
219
|
+
workspaceId?: string;
|
|
220
|
+
targetId?: TargetId;
|
|
221
|
+
stage?: ProjectionStage;
|
|
222
|
+
projections?: readonly ProjectionBuildOptions[] | ((snapshot: ProjectionSnapshot) => readonly ProjectionBuildOptions[]);
|
|
223
|
+
yieldEvery?: number;
|
|
224
|
+
maxRetainedSnapshots?: number;
|
|
225
|
+
backgroundDebounceMs?: number;
|
|
226
|
+
scheduler?: () => Promise<void>;
|
|
227
|
+
trace?: (event: Readonly<Record<string, unknown>>) => void;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export class ProjectionService {
|
|
231
|
+
constructor(options?: ProjectionServiceOptions);
|
|
232
|
+
update(snapshot: ProjectionSnapshot, signal?: AbortSignal): Promise<ProjectionDelta>;
|
|
233
|
+
scheduleUpdate(snapshot: ProjectionSnapshot, options?: { signal?: AbortSignal; priority?: "background" | "interactive" }): Promise<ProjectionDelta>;
|
|
234
|
+
getProjection(id: ProjectionId): VirtualDocument | undefined;
|
|
235
|
+
getProjectionByUri(uri: string): VirtualDocument | undefined;
|
|
236
|
+
listProjections(): readonly VirtualDocument[];
|
|
237
|
+
listProjectionsForSource(source: SourceLocation): readonly Array<{ projectionId: ProjectionId; uri: string; artifactId: ArtifactId; targetId: TargetId; stage: ProjectionStage; version: number }>;
|
|
238
|
+
listOccurrences(pieceId: PieceId, targetId?: TargetId): readonly ExpansionOccurrence[];
|
|
239
|
+
toVirtual(source: SourceLocation | { uri: string; offset: number }, selection?: ProjectionSelection): readonly ProjectionMappingMatch[];
|
|
240
|
+
toSource(projectionId: ProjectionId, virtual: OffsetRange | number, options?: ProjectionSelection): readonly ProjectionMappingMatch[];
|
|
241
|
+
generatedContext(occurrenceId: OccurrenceId, options?: GeneratedContextOptions): GeneratedContextResult;
|
|
242
|
+
getStats(): Readonly<{ built: number; reused: number; cancelled: number; updates: number; retainedSnapshots: number; currentProjections: number }>;
|
|
243
|
+
dispose(): void;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export interface GeneratedContextOptions {
|
|
247
|
+
surroundingLines?: number;
|
|
248
|
+
projectionVersion?: number;
|
|
249
|
+
sourceSelection?: SourceLocation;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function createProjectionId(options?: ProjectionBuildOptions): ProjectionId;
|
|
253
|
+
export function createVirtualUri(options?: ProjectionBuildOptions & { path?: string }): string;
|
|
254
|
+
export function buildVirtualDocument(program: Record<string, any>, options?: ProjectionBuildOptions): VirtualDocument;
|
|
255
|
+
export function projectionInputHash(program: Record<string, any>, options?: ProjectionBuildOptions): string;
|
|
256
|
+
export function createLineIndex(text?: string): LineIndex;
|
|
257
|
+
export function positionAt(index: LineIndex, offset: number, encoding?: PositionEncoding): { ok: true; position: { line: number; character: number; offset: number } } | { ok: false; reason: string };
|
|
258
|
+
export function offsetAt(index: LineIndex, position: { line: number; character: number }, encoding?: PositionEncoding): { ok: true; offset: number } | { ok: false; reason: string };
|
|
259
|
+
export function lineRangeAt(index: LineIndex, line: number): { ok: true; range: OffsetRange } | { ok: false; reason: string };
|
|
260
|
+
export function lineWindow(index: LineIndex, range: OffsetRange, surroundingLines?: number): { ok: true; range: OffsetRange } | { ok: false; reason: string };
|
|
261
|
+
export function buildProjectionIndexes(segments?: readonly ProjectionSegment[], occurrences?: readonly ExpansionOccurrence[]): ProjectionIndexes;
|
|
262
|
+
export function coalesceProjectionSegments(segments?: readonly ProjectionSegment[]): readonly ProjectionSegment[];
|
|
263
|
+
export function mapVirtualOffset(document: VirtualDocument, offset: number, options?: ProjectionSelection): MappingQueryResult;
|
|
264
|
+
export function mapVirtualRange(document: VirtualDocument, range: OffsetRange, options?: ProjectionSelection): MappingQueryResult;
|
|
265
|
+
export function mapSourceOffset(document: VirtualDocument, uri: string, offset: number, options?: ProjectionSelection): MappingQueryResult;
|
|
266
|
+
export function mapSourceRange(document: VirtualDocument, uri: string, range: OffsetRange, options?: ProjectionSelection): MappingQueryResult;
|
|
267
|
+
export function validateProjectionSegments(text: string, segments?: readonly ProjectionSegment[]): readonly Array<{ code: string; message: string }>;
|
|
268
|
+
export function sameProjectionMapping(left: ProjectionSegment, right: ProjectionSegment): boolean;
|
|
269
|
+
export function generatedContext(document: VirtualDocument, occurrenceId: OccurrenceId, options?: GeneratedContextOptions): GeneratedContextResult;
|
|
270
|
+
export function navigateGeneratedSelection(document: VirtualDocument, range: OffsetRange, options?: ProjectionSelection): MappingQueryResult;
|
|
271
|
+
export function createProjectionService(options?: ProjectionServiceOptions): ProjectionService;
|
|
272
|
+
export function createProjectionTextChange(previousText: string, nextText: string, options?: { minimumReuseRatio?: number }): ProjectionTextChange;
|
|
273
|
+
|
|
274
|
+
export type TransformSpanMode = "copy" | "mapped" | "inserted" | "removed";
|
|
275
|
+
export interface TransformOffsetSpan { input: OffsetRange; output: OffsetRange; mode: TransformSpanMode; }
|
|
276
|
+
export interface OffsetMap {
|
|
277
|
+
kind: "offset";
|
|
278
|
+
name: string;
|
|
279
|
+
inputLength: number;
|
|
280
|
+
outputLength: number;
|
|
281
|
+
spans: readonly TransformOffsetSpan[];
|
|
282
|
+
}
|
|
283
|
+
export interface SourceMapPoint {
|
|
284
|
+
generated: { line: number; column: number };
|
|
285
|
+
source?: string;
|
|
286
|
+
original?: { line: number; column: number };
|
|
287
|
+
name?: string;
|
|
288
|
+
}
|
|
289
|
+
export interface NormalizedSourceMap {
|
|
290
|
+
kind: "source-map";
|
|
291
|
+
sourceRoot?: string;
|
|
292
|
+
sources: readonly string[];
|
|
293
|
+
sourcesContent: readonly (string | null)[];
|
|
294
|
+
entries: readonly SourceMapPoint[];
|
|
295
|
+
}
|
|
296
|
+
export type TransformMappingCapability =
|
|
297
|
+
| { kind: "identity" }
|
|
298
|
+
| OffsetMap
|
|
299
|
+
| { kind: "source-map"; entries: readonly SourceMapPoint[] }
|
|
300
|
+
| { kind: "opaque"; anchor?: SourceLocation };
|
|
301
|
+
export interface OffsetTransformResult { ok: true; text: string; map: OffsetMap; }
|
|
302
|
+
|
|
303
|
+
export function identityTransformMap(input: string | number): OffsetMap | { kind: "invalid"; reason: string };
|
|
304
|
+
export function createIndentOffsetMap(input: string, count?: number): OffsetTransformResult | { ok: false; reason: string };
|
|
305
|
+
export function createDedentOffsetMap(input: string): (OffsetTransformResult & { amount: number }) | { ok: false; reason: string };
|
|
306
|
+
export function createEolOffsetMap(input: string, eol?: "\n" | "\r\n"): OffsetTransformResult | { ok: false; reason: string };
|
|
307
|
+
export function validateTransformMapping(mapping: TransformMappingCapability): { ok: boolean; reason?: string; index?: number };
|
|
308
|
+
export function composeOffsetMaps(...maps: Array<OffsetMap | readonly OffsetMap[]>): { ok: true; map: OffsetMap } | { ok: false; reason: string };
|
|
309
|
+
export function mapTransformOffset(mapping: OffsetMap, offset: number, options?: { direction?: "output-to-input" | "input-to-output"; affinity?: Affinity }): { ok: boolean; reason?: string; matches: readonly number[] };
|
|
310
|
+
export function opaqueTransformMap(anchor?: SourceLocation): { kind: "opaque"; anchor?: SourceLocation };
|
|
311
|
+
export function stageCapabilities(stage: ProjectionStage, overrides?: Partial<ProjectionCapabilities>): ProjectionCapabilities;
|
|
312
|
+
export function validateAnalysisTransform(descriptor: { pure?: boolean; effect?: unknown; effects?: readonly unknown[]; authorities?: readonly unknown[]; mapping?: TransformMappingCapability; map?: TransformMappingCapability }): { ok: boolean; reason?: string };
|
|
313
|
+
export function normalizeSourceMap(sourceMap: Record<string, any>): { ok: true; map: NormalizedSourceMap } | { ok: false; reason: string };
|
|
314
|
+
export function applyTransformMap(document: VirtualDocument, outputText: string, capability: TransformMappingCapability | { map: TransformMappingCapability }, options?: {
|
|
315
|
+
signal?: AbortSignal;
|
|
316
|
+
inputSource?: string;
|
|
317
|
+
transformSource?: SourceLocation;
|
|
318
|
+
transformChain?: readonly TransformStep[];
|
|
319
|
+
name?: string;
|
|
320
|
+
stage?: ProjectionStage;
|
|
321
|
+
languageId?: string;
|
|
322
|
+
projectionId?: ProjectionId;
|
|
323
|
+
uri?: string;
|
|
324
|
+
path?: string;
|
|
325
|
+
version?: number;
|
|
326
|
+
capabilities?: Partial<ProjectionCapabilities>;
|
|
327
|
+
}): { ok: true; document: VirtualDocument } | { ok: false; reason: string };
|
|
328
|
+
|
|
329
|
+
export const MAPPING_KINDS: readonly MappingKind[];
|
|
330
|
+
export const PROJECTION_STAGES: readonly ProjectionStage[];
|
|
331
|
+
export const POSITION_ENCODINGS: readonly PositionEncoding[];
|
package/src/index.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createProjectionId,
|
|
3
|
+
createVirtualUri,
|
|
4
|
+
buildVirtualDocument,
|
|
5
|
+
projectionInputHash
|
|
6
|
+
} from "./builder.js";
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
createLineIndex,
|
|
10
|
+
positionAt,
|
|
11
|
+
offsetAt,
|
|
12
|
+
lineRangeAt,
|
|
13
|
+
lineWindow
|
|
14
|
+
} from "./line-index.js";
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
buildProjectionIndexes,
|
|
18
|
+
coalesceProjectionSegments,
|
|
19
|
+
mapVirtualOffset,
|
|
20
|
+
mapVirtualRange,
|
|
21
|
+
mapSourceOffset,
|
|
22
|
+
mapSourceRange,
|
|
23
|
+
validateProjectionSegments,
|
|
24
|
+
sameProjectionMapping
|
|
25
|
+
} from "./mapping.js";
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
generatedContext,
|
|
29
|
+
navigateGeneratedSelection
|
|
30
|
+
} from "./context.js";
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
ProjectionService,
|
|
34
|
+
createProjectionService,
|
|
35
|
+
createProjectionTextChange
|
|
36
|
+
} from "./service.js";
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
identityTransformMap,
|
|
40
|
+
createIndentOffsetMap,
|
|
41
|
+
createDedentOffsetMap,
|
|
42
|
+
createEolOffsetMap,
|
|
43
|
+
validateTransformMapping,
|
|
44
|
+
composeOffsetMaps,
|
|
45
|
+
mapTransformOffset,
|
|
46
|
+
opaqueTransformMap,
|
|
47
|
+
normalizeSourceMap,
|
|
48
|
+
applyTransformMap,
|
|
49
|
+
stageCapabilities,
|
|
50
|
+
validateAnalysisTransform
|
|
51
|
+
} from "./transforms.js";
|
|
52
|
+
|
|
53
|
+
export const MAPPING_KINDS = Object.freeze([
|
|
54
|
+
"exact",
|
|
55
|
+
"anchored",
|
|
56
|
+
"transformed",
|
|
57
|
+
"opaque",
|
|
58
|
+
"synthetic"
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
export const PROJECTION_STAGES = Object.freeze([
|
|
62
|
+
"authoring",
|
|
63
|
+
"assembled",
|
|
64
|
+
"transformed",
|
|
65
|
+
"emitted"
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
export const POSITION_ENCODINGS = Object.freeze(["utf-8", "utf-16", "utf-32"]);
|
package/src/internal.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
export const mappingKinds = new Set([
|
|
2
|
+
"exact",
|
|
3
|
+
"anchored",
|
|
4
|
+
"transformed",
|
|
5
|
+
"opaque",
|
|
6
|
+
"synthetic"
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
export const projectionStages = new Set([
|
|
10
|
+
"authoring",
|
|
11
|
+
"assembled",
|
|
12
|
+
"transformed",
|
|
13
|
+
"emitted"
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
export const affinities = new Set(["left", "right", "none"]);
|
|
17
|
+
|
|
18
|
+
export const isInteger = (value) => Number.isInteger(value);
|
|
19
|
+
|
|
20
|
+
export const validOffsetRange = (range, { allowEmpty = true } = {}) =>
|
|
21
|
+
range !== null && typeof range === "object" &&
|
|
22
|
+
isInteger(range.start) && range.start >= 0 &&
|
|
23
|
+
isInteger(range.end) && range.end >= range.start &&
|
|
24
|
+
(allowEmpty || range.end > range.start);
|
|
25
|
+
|
|
26
|
+
export const clone = (value) => {
|
|
27
|
+
if (value === undefined || value === null || typeof value !== "object") return value;
|
|
28
|
+
if (Array.isArray(value)) return value.map(clone);
|
|
29
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, clone(child)]));
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const deepFreeze = (value, seen = new Set()) => {
|
|
33
|
+
if (value === null || typeof value !== "object" || seen.has(value)) return value;
|
|
34
|
+
seen.add(value);
|
|
35
|
+
for (const child of Object.values(value)) deepFreeze(child, seen);
|
|
36
|
+
return Object.freeze(value);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const compareText = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
40
|
+
|
|
41
|
+
export const stableStringify = (value) => {
|
|
42
|
+
const visit = (entry) => {
|
|
43
|
+
if (entry === null || typeof entry !== "object") return JSON.stringify(entry);
|
|
44
|
+
if (Array.isArray(entry)) return "[" + entry.map(visit).join(",") + "]";
|
|
45
|
+
return "{" + Object.keys(entry).sort(compareText)
|
|
46
|
+
.map((key) => JSON.stringify(key) + ":" + visit(entry[key]))
|
|
47
|
+
.join(",") + "}";
|
|
48
|
+
};
|
|
49
|
+
return visit(value);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** A deterministic, portable content hash. This is an identity checksum, not cryptography. */
|
|
53
|
+
export const stableHash = (value) => {
|
|
54
|
+
const text = typeof value === "string" ? value : stableStringify(value);
|
|
55
|
+
let first = 2166136261;
|
|
56
|
+
let second = 2246822519;
|
|
57
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
58
|
+
const code = text.charCodeAt(index);
|
|
59
|
+
first ^= code;
|
|
60
|
+
first = Math.imul(first, 16777619);
|
|
61
|
+
second ^= code + (index & 255);
|
|
62
|
+
second = Math.imul(second, 3266489917);
|
|
63
|
+
}
|
|
64
|
+
return (first >>> 0).toString(16).padStart(8, "0") +
|
|
65
|
+
(second >>> 0).toString(16).padStart(8, "0");
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const abortError = (message = "Projection operation was aborted.") => {
|
|
69
|
+
const error = new Error(message);
|
|
70
|
+
error.name = "AbortError";
|
|
71
|
+
return error;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const throwIfAborted = (signal) => {
|
|
75
|
+
if (signal?.aborted) throw signal.reason?.name === "AbortError"
|
|
76
|
+
? signal.reason
|
|
77
|
+
: abortError(typeof signal.reason === "string" ? signal.reason : undefined);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const sourceOffset = (position) => isInteger(position) ? position : position?.offset;
|
|
81
|
+
|
|
82
|
+
export const sourceRangeOffsets = (source) => {
|
|
83
|
+
const range = source?.range ?? source;
|
|
84
|
+
const start = sourceOffset(range?.start);
|
|
85
|
+
const end = sourceOffset(range?.end);
|
|
86
|
+
return isInteger(start) && isInteger(end) && start >= 0 && end >= start
|
|
87
|
+
? { start, end }
|
|
88
|
+
: null;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const sourceLocation = (source) => {
|
|
92
|
+
if (!source || typeof source.uri !== "string") return undefined;
|
|
93
|
+
const offsets = sourceRangeOffsets(source);
|
|
94
|
+
if (!offsets) return undefined;
|
|
95
|
+
const start = source.range?.start ?? { line: 0, column: 0, offset: offsets.start };
|
|
96
|
+
const end = source.range?.end ?? { line: 0, column: 0, offset: offsets.end };
|
|
97
|
+
return clone({
|
|
98
|
+
uri: source.uri,
|
|
99
|
+
range: {
|
|
100
|
+
start: {
|
|
101
|
+
line: isInteger(start.line) ? start.line : 0,
|
|
102
|
+
column: isInteger(start.column) ? start.column : 0,
|
|
103
|
+
offset: offsets.start
|
|
104
|
+
},
|
|
105
|
+
end: {
|
|
106
|
+
line: isInteger(end.line) ? end.line : 0,
|
|
107
|
+
column: isInteger(end.column) ? end.column : 0,
|
|
108
|
+
offset: offsets.end
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const advancePosition = (start, text, count) => {
|
|
115
|
+
let line = start.line;
|
|
116
|
+
let column = start.column;
|
|
117
|
+
for (let index = 0; index < count; index += 1) {
|
|
118
|
+
if (text[index] === "\n") {
|
|
119
|
+
line += 1;
|
|
120
|
+
column = 0;
|
|
121
|
+
} else {
|
|
122
|
+
column += 1;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { line, column, offset: start.offset + count };
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export const sliceSourceLocation = (source, text, start, end) => {
|
|
129
|
+
const normalized = sourceLocation(source);
|
|
130
|
+
if (!normalized) return undefined;
|
|
131
|
+
return {
|
|
132
|
+
uri: normalized.uri,
|
|
133
|
+
range: {
|
|
134
|
+
start: advancePosition(normalized.range.start, text, start),
|
|
135
|
+
end: advancePosition(normalized.range.start, text, end)
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export const sameSourceLocation = (left, right) => stableStringify(left) === stableStringify(right);
|
|
141
|
+
|
|
142
|
+
export const rangesOverlap = (left, right, { includeBoundary = false } = {}) =>
|
|
143
|
+
includeBoundary
|
|
144
|
+
? left.start <= right.end && right.start <= left.end
|
|
145
|
+
: left.start < right.end && right.start < left.end;
|
|
146
|
+
|
|
147
|
+
export const containsOffset = (range, offset, affinity = "none") => {
|
|
148
|
+
if (range.start === range.end) return offset === range.start;
|
|
149
|
+
if (offset > range.start && offset < range.end) return true;
|
|
150
|
+
if (offset === range.start) return affinity !== "left";
|
|
151
|
+
if (offset === range.end) return affinity !== "right";
|
|
152
|
+
return false;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export const encodeUriPart = (value) => encodeURIComponent(String(value));
|
|
156
|
+
|
|
157
|
+
export const encodeVirtualPath = (value) => String(value).split("/")
|
|
158
|
+
.map((part) => encodeURIComponent(part))
|
|
159
|
+
.join("/");
|
|
160
|
+
|
|
161
|
+
export const defaultCapabilities = (stage) => deepFreeze(stage === "emitted"
|
|
162
|
+
? { navigation: true, diagnostics: true, completion: false, writableEdits: false }
|
|
163
|
+
: { navigation: true, diagnostics: true, completion: true, writableEdits: true });
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { deepFreeze, isInteger } from "./internal.js";
|
|
2
|
+
|
|
3
|
+
const encodings = new Set(["utf-8", "utf-16", "utf-32"]);
|
|
4
|
+
|
|
5
|
+
const unitsFor = (text, encoding) => {
|
|
6
|
+
if (encoding === "utf-16") return text.length;
|
|
7
|
+
let units = 0;
|
|
8
|
+
for (const character of text) {
|
|
9
|
+
if (encoding === "utf-32") units += 1;
|
|
10
|
+
else {
|
|
11
|
+
const code = character.codePointAt(0);
|
|
12
|
+
units += code <= 0x7f ? 1 : code <= 0x7ff ? 2 : code <= 0xffff ? 3 : 4;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return units;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const contentEnd = (index, line) => {
|
|
19
|
+
const start = index.lineStarts[line];
|
|
20
|
+
const next = index.lineStarts[line + 1] ?? index.text.length;
|
|
21
|
+
let end = next;
|
|
22
|
+
if (end > start && index.text[end - 1] === "\n") end -= 1;
|
|
23
|
+
if (end > start && index.text[end - 1] === "\r") end -= 1;
|
|
24
|
+
return end;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const createLineIndex = (text = "") => {
|
|
28
|
+
if (typeof text !== "string") text = String(text ?? "");
|
|
29
|
+
const lineStarts = [0];
|
|
30
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
31
|
+
if (text[index] === "\n") lineStarts.push(index + 1);
|
|
32
|
+
}
|
|
33
|
+
return deepFreeze({ text, textLength: text.length, lineStarts });
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const lineAt = (index, offset) => {
|
|
37
|
+
let low = 0;
|
|
38
|
+
let high = index.lineStarts.length;
|
|
39
|
+
while (low + 1 < high) {
|
|
40
|
+
const middle = (low + high) >>> 1;
|
|
41
|
+
if (index.lineStarts[middle] <= offset) low = middle;
|
|
42
|
+
else high = middle;
|
|
43
|
+
}
|
|
44
|
+
return low;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const positionAt = (index, offset, encoding = "utf-16") => {
|
|
48
|
+
if (!index || typeof index.text !== "string" || !isInteger(offset) ||
|
|
49
|
+
offset < 0 || offset > index.text.length || !encodings.has(encoding)) {
|
|
50
|
+
return { ok: false, reason: "invalid-position" };
|
|
51
|
+
}
|
|
52
|
+
const line = lineAt(index, offset);
|
|
53
|
+
const end = contentEnd(index, line);
|
|
54
|
+
const safeOffset = Math.min(offset, end);
|
|
55
|
+
return {
|
|
56
|
+
ok: true,
|
|
57
|
+
position: {
|
|
58
|
+
line,
|
|
59
|
+
character: unitsFor(index.text.slice(index.lineStarts[line], safeOffset), encoding),
|
|
60
|
+
offset
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const offsetAt = (index, position, encoding = "utf-16") => {
|
|
66
|
+
if (!index || typeof index.text !== "string" || !position ||
|
|
67
|
+
!isInteger(position.line) || position.line < 0 ||
|
|
68
|
+
!isInteger(position.character) || position.character < 0 ||
|
|
69
|
+
position.line >= index.lineStarts.length || !encodings.has(encoding)) {
|
|
70
|
+
return { ok: false, reason: "invalid-position" };
|
|
71
|
+
}
|
|
72
|
+
const start = index.lineStarts[position.line];
|
|
73
|
+
const end = contentEnd(index, position.line);
|
|
74
|
+
if (position.character === 0) return { ok: true, offset: start };
|
|
75
|
+
if (encoding === "utf-16") {
|
|
76
|
+
if (position.character > end - start) return { ok: false, reason: "character-out-of-range" };
|
|
77
|
+
const offset = start + position.character;
|
|
78
|
+
if (offset > start && offset < end) {
|
|
79
|
+
const previous = index.text.charCodeAt(offset - 1);
|
|
80
|
+
const current = index.text.charCodeAt(offset);
|
|
81
|
+
if (previous >= 0xd800 && previous <= 0xdbff && current >= 0xdc00 && current <= 0xdfff) {
|
|
82
|
+
return { ok: false, reason: "split-character" };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { ok: true, offset };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let units = 0;
|
|
89
|
+
let offset = start;
|
|
90
|
+
for (const character of index.text.slice(start, end)) {
|
|
91
|
+
const width = character.length;
|
|
92
|
+
const next = unitsFor(character, encoding);
|
|
93
|
+
if (units + next > position.character) return { ok: false, reason: "split-character" };
|
|
94
|
+
units += next;
|
|
95
|
+
offset += width;
|
|
96
|
+
if (units === position.character) return { ok: true, offset };
|
|
97
|
+
}
|
|
98
|
+
return { ok: false, reason: "character-out-of-range" };
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const lineRangeAt = (index, line) => {
|
|
102
|
+
if (!index || !isInteger(line) || line < 0 || line >= index.lineStarts.length) {
|
|
103
|
+
return { ok: false, reason: "invalid-line" };
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
ok: true,
|
|
107
|
+
range: {
|
|
108
|
+
start: index.lineStarts[line],
|
|
109
|
+
end: index.lineStarts[line + 1] ?? index.text.length
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const lineWindow = (index, range, surroundingLines = 3) => {
|
|
115
|
+
if (!index || !range || !isInteger(range.start) || !isInteger(range.end) ||
|
|
116
|
+
range.start < 0 || range.end < range.start || range.end > index.text.length) {
|
|
117
|
+
return { ok: false, reason: "invalid-range" };
|
|
118
|
+
}
|
|
119
|
+
const count = isInteger(surroundingLines) && surroundingLines >= 0 ? surroundingLines : 3;
|
|
120
|
+
const first = Math.max(0, lineAt(index, range.start) - count);
|
|
121
|
+
const lastOffset = range.end > range.start ? range.end - 1 : range.end;
|
|
122
|
+
const last = Math.min(index.lineStarts.length - 1, lineAt(index, lastOffset) + count);
|
|
123
|
+
return {
|
|
124
|
+
ok: true,
|
|
125
|
+
range: {
|
|
126
|
+
start: index.lineStarts[first],
|
|
127
|
+
end: index.lineStarts[last + 1] ?? index.text.length
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
};
|