feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+96
View File
@@ -0,0 +1,96 @@
'use strict';
function isInRange(index, range, inclusive = true) {
if (inclusive)
return range[0] <= index && index <= range[1];
else
return range[0] < index && index < range[1];
}
function isInRanges(index, ranges, inclusive = true) {
return ranges.find((range) => isInRange(index, range, inclusive));
}
function mergeRanges(ranges) {
ranges.sort((a, b) => a[0] - b[0]);
const merged = [];
for (const range of ranges) {
const last = merged[merged.length - 1];
if (last && last[1] >= range[0])
last[1] = Math.max(last[1], range[1]);
else
merged.push(range);
}
return merged;
}
function splitLines(code, preserveEnding = false) {
const parts = code.split(/(\r?\n)/g);
let index = 0;
const lines = [];
for (let i = 0; i < parts.length; i += 2) {
const line = preserveEnding ? parts[i] + (parts[i + 1] || "") : parts[i];
lines.push([line, index]);
index += parts[i].length;
index += parts[i + 1]?.length || 0;
}
return lines;
}
function createPositionConverter(code) {
const lines = splitLines(code, true).map(([line]) => line);
function indexToPos(index) {
let character = index;
let line = 0;
for (const lineText of lines) {
if (character < lineText.length)
break;
character -= lineText.length;
line++;
}
return { line, character };
}
function posToIndex(line, character) {
let index = 0;
for (let i = 0; i < line; i++)
index += lines[i].length;
index += character;
return index;
}
return {
lines,
indexToPos,
posToIndex
};
}
function removeCodeRanges(code, removals, nodes) {
const ranges = mergeRanges(removals).sort((a, b) => b[0] - a[0]);
let outputCode = code;
for (const remove of ranges) {
const removalLength = remove[1] - remove[0];
outputCode = outputCode.slice(0, remove[0]) + outputCode.slice(remove[1]);
nodes?.forEach((node) => {
if (node.start + node.length <= remove[0])
return void 0;
else if (node.start < remove[1])
node.start = -1;
else
node.start -= removalLength;
});
}
return {
code: outputCode,
removals: ranges,
nodes
};
}
function resolveNodePositions(nodes, options) {
const indexToPos = typeof options === "string" ? createPositionConverter(options).indexToPos : options;
const resolved = nodes.filter((node) => node.start >= 0).sort((a, b) => a.start - b.start || a.type.localeCompare(b.type));
resolved.forEach((node) => Object.assign(node, indexToPos(node.start)));
return resolved;
}
exports.createPositionConverter = createPositionConverter;
exports.isInRange = isInRange;
exports.isInRanges = isInRanges;
exports.mergeRanges = mergeRanges;
exports.removeCodeRanges = removeCodeRanges;
exports.resolveNodePositions = resolveNodePositions;
exports.splitLines = splitLines;
+47
View File
@@ -0,0 +1,47 @@
import { Range, Position, NodeStartLength, NodeWithoutPosition, TwoslashNode } from './types.cjs';
export { CompletionEntry, ErrorLevel, NodeBase, NodeCompletion, NodeError, NodeErrorWithoutPosition, NodeHighlight, NodeHover, NodeQuery, NodeTag, TwoslashGenericFunction, TwoslashGenericResult } from './types.cjs';
declare function isInRange(index: number, range: Range, inclusive?: boolean): boolean;
declare function isInRanges(index: number, ranges: Range[], inclusive?: boolean): Range | undefined;
/**
* Merge overlapping ranges
*/
declare function mergeRanges(ranges: Range[]): Range[];
/**
* Slipt a string into lines, each line preserves the line ending.
*/
declare function splitLines(code: string, preserveEnding?: boolean): [string, number][];
/**
* Creates a converter between index and position in a code block.
*/
declare function createPositionConverter(code: string): {
lines: string[];
indexToPos: (index: number) => Position;
posToIndex: (line: number, character: number) => number;
};
/**
* Remove ranages for a string, and update nodes' `start` property accordingly
*
* Note that items in `nodes` will be mutated
*/
declare function removeCodeRanges<T extends NodeStartLength>(code: string, removals: Range[], nodes: T[]): {
code: string;
removals: Range[];
nodes: T[];
};
declare function removeCodeRanges(code: string, removals: Range[]): {
code: string;
removals: Range[];
nodes: undefined;
};
/**
* - Calculate nodes `line` and `character` properties to match the code
* - Remove nodes that has negative `start` property
* - Sort nodes by `start`
*
* Note that the nodes items will be mutated, clone them beforehand if not desired
*/
declare function resolveNodePositions(nodes: NodeWithoutPosition[], code: string): TwoslashNode[];
declare function resolveNodePositions(nodes: NodeWithoutPosition[], indexToPos: (index: number) => Position): TwoslashNode[];
export { NodeStartLength, NodeWithoutPosition, Position, Range, TwoslashNode, createPositionConverter, isInRange, isInRanges, mergeRanges, removeCodeRanges, resolveNodePositions, splitLines };
+47
View File
@@ -0,0 +1,47 @@
import { Range, Position, NodeStartLength, NodeWithoutPosition, TwoslashNode } from './types.mjs';
export { CompletionEntry, ErrorLevel, NodeBase, NodeCompletion, NodeError, NodeErrorWithoutPosition, NodeHighlight, NodeHover, NodeQuery, NodeTag, TwoslashGenericFunction, TwoslashGenericResult } from './types.mjs';
declare function isInRange(index: number, range: Range, inclusive?: boolean): boolean;
declare function isInRanges(index: number, ranges: Range[], inclusive?: boolean): Range | undefined;
/**
* Merge overlapping ranges
*/
declare function mergeRanges(ranges: Range[]): Range[];
/**
* Slipt a string into lines, each line preserves the line ending.
*/
declare function splitLines(code: string, preserveEnding?: boolean): [string, number][];
/**
* Creates a converter between index and position in a code block.
*/
declare function createPositionConverter(code: string): {
lines: string[];
indexToPos: (index: number) => Position;
posToIndex: (line: number, character: number) => number;
};
/**
* Remove ranages for a string, and update nodes' `start` property accordingly
*
* Note that items in `nodes` will be mutated
*/
declare function removeCodeRanges<T extends NodeStartLength>(code: string, removals: Range[], nodes: T[]): {
code: string;
removals: Range[];
nodes: T[];
};
declare function removeCodeRanges(code: string, removals: Range[]): {
code: string;
removals: Range[];
nodes: undefined;
};
/**
* - Calculate nodes `line` and `character` properties to match the code
* - Remove nodes that has negative `start` property
* - Sort nodes by `start`
*
* Note that the nodes items will be mutated, clone them beforehand if not desired
*/
declare function resolveNodePositions(nodes: NodeWithoutPosition[], code: string): TwoslashNode[];
declare function resolveNodePositions(nodes: NodeWithoutPosition[], indexToPos: (index: number) => Position): TwoslashNode[];
export { NodeStartLength, NodeWithoutPosition, Position, Range, TwoslashNode, createPositionConverter, isInRange, isInRanges, mergeRanges, removeCodeRanges, resolveNodePositions, splitLines };
+47
View File
@@ -0,0 +1,47 @@
import { Range, Position, NodeStartLength, NodeWithoutPosition, TwoslashNode } from './types.js';
export { CompletionEntry, ErrorLevel, NodeBase, NodeCompletion, NodeError, NodeErrorWithoutPosition, NodeHighlight, NodeHover, NodeQuery, NodeTag, TwoslashGenericFunction, TwoslashGenericResult } from './types.js';
declare function isInRange(index: number, range: Range, inclusive?: boolean): boolean;
declare function isInRanges(index: number, ranges: Range[], inclusive?: boolean): Range | undefined;
/**
* Merge overlapping ranges
*/
declare function mergeRanges(ranges: Range[]): Range[];
/**
* Slipt a string into lines, each line preserves the line ending.
*/
declare function splitLines(code: string, preserveEnding?: boolean): [string, number][];
/**
* Creates a converter between index and position in a code block.
*/
declare function createPositionConverter(code: string): {
lines: string[];
indexToPos: (index: number) => Position;
posToIndex: (line: number, character: number) => number;
};
/**
* Remove ranages for a string, and update nodes' `start` property accordingly
*
* Note that items in `nodes` will be mutated
*/
declare function removeCodeRanges<T extends NodeStartLength>(code: string, removals: Range[], nodes: T[]): {
code: string;
removals: Range[];
nodes: T[];
};
declare function removeCodeRanges(code: string, removals: Range[]): {
code: string;
removals: Range[];
nodes: undefined;
};
/**
* - Calculate nodes `line` and `character` properties to match the code
* - Remove nodes that has negative `start` property
* - Sort nodes by `start`
*
* Note that the nodes items will be mutated, clone them beforehand if not desired
*/
declare function resolveNodePositions(nodes: NodeWithoutPosition[], code: string): TwoslashNode[];
declare function resolveNodePositions(nodes: NodeWithoutPosition[], indexToPos: (index: number) => Position): TwoslashNode[];
export { NodeStartLength, NodeWithoutPosition, Position, Range, TwoslashNode, createPositionConverter, isInRange, isInRanges, mergeRanges, removeCodeRanges, resolveNodePositions, splitLines };
+88
View File
@@ -0,0 +1,88 @@
function isInRange(index, range, inclusive = true) {
if (inclusive)
return range[0] <= index && index <= range[1];
else
return range[0] < index && index < range[1];
}
function isInRanges(index, ranges, inclusive = true) {
return ranges.find((range) => isInRange(index, range, inclusive));
}
function mergeRanges(ranges) {
ranges.sort((a, b) => a[0] - b[0]);
const merged = [];
for (const range of ranges) {
const last = merged[merged.length - 1];
if (last && last[1] >= range[0])
last[1] = Math.max(last[1], range[1]);
else
merged.push(range);
}
return merged;
}
function splitLines(code, preserveEnding = false) {
const parts = code.split(/(\r?\n)/g);
let index = 0;
const lines = [];
for (let i = 0; i < parts.length; i += 2) {
const line = preserveEnding ? parts[i] + (parts[i + 1] || "") : parts[i];
lines.push([line, index]);
index += parts[i].length;
index += parts[i + 1]?.length || 0;
}
return lines;
}
function createPositionConverter(code) {
const lines = splitLines(code, true).map(([line]) => line);
function indexToPos(index) {
let character = index;
let line = 0;
for (const lineText of lines) {
if (character < lineText.length)
break;
character -= lineText.length;
line++;
}
return { line, character };
}
function posToIndex(line, character) {
let index = 0;
for (let i = 0; i < line; i++)
index += lines[i].length;
index += character;
return index;
}
return {
lines,
indexToPos,
posToIndex
};
}
function removeCodeRanges(code, removals, nodes) {
const ranges = mergeRanges(removals).sort((a, b) => b[0] - a[0]);
let outputCode = code;
for (const remove of ranges) {
const removalLength = remove[1] - remove[0];
outputCode = outputCode.slice(0, remove[0]) + outputCode.slice(remove[1]);
nodes?.forEach((node) => {
if (node.start + node.length <= remove[0])
return void 0;
else if (node.start < remove[1])
node.start = -1;
else
node.start -= removalLength;
});
}
return {
code: outputCode,
removals: ranges,
nodes
};
}
function resolveNodePositions(nodes, options) {
const indexToPos = typeof options === "string" ? createPositionConverter(options).indexToPos : options;
const resolved = nodes.filter((node) => node.start >= 0).sort((a, b) => a.start - b.start || a.type.localeCompare(b.type));
resolved.forEach((node) => Object.assign(node, indexToPos(node.start)));
return resolved;
}
export { createPositionConverter, isInRange, isInRanges, mergeRanges, removeCodeRanges, resolveNodePositions, splitLines };
+2
View File
@@ -0,0 +1,2 @@
'use strict';
+101
View File
@@ -0,0 +1,101 @@
/**
* Basic node with start and length to represent a range in the code
*/
interface NodeStartLength {
/** 0-indexed position of the node in the file */
start: number;
/** The length of the node */
length: number;
}
interface NodeBase extends NodeStartLength, Position {
}
interface NodeHover extends NodeBase {
type: 'hover';
/** The string content of the node this represents (mainly for debugging) */
target: string;
/** The base LSP response (the type) */
text: string;
/** Attached JSDoc info */
docs?: string;
/** JSDoc tags */
tags?: [name: string, text: string | undefined][];
}
interface NodeHighlight extends NodeBase {
type: 'highlight';
/** The annotation message */
text?: string;
}
interface NodeQuery extends Omit<NodeHover, 'type'> {
type: 'query';
}
interface CompletionEntry {
name: string;
kind?: string;
}
interface NodeCompletion extends NodeBase {
type: 'completion';
/** Results for completions at a particular point */
completions: CompletionEntry[];
completionsPrefix: string;
}
type ErrorLevel = 'warning' | 'error' | 'suggestion' | 'message';
interface NodeError extends NodeBase {
type: 'error';
id?: string;
/**
* Error level
* When not provided, defaults to 'error'
*/
level?: ErrorLevel;
/**
* Error code
*/
code?: number | string;
/**
* Error message
*/
text: string;
/**
* The filename of the file the error is in
*/
filename?: string;
}
interface NodeTag extends NodeBase {
type: 'tag';
/** What was the name of the tag */
name: string;
/** What was the text after the `// @tag: ` string (optional because you could do // @tag on it's own line without the ':') */
text?: string;
}
type TwoslashNode = NodeHighlight | NodeHover | NodeQuery | NodeCompletion | NodeError | NodeTag;
type NodeWithoutPosition = Omit<NodeHighlight, keyof Position> | Omit<NodeHover, keyof Position> | Omit<NodeQuery, keyof Position> | Omit<NodeCompletion, keyof Position> | Omit<NodeError, keyof Position> | Omit<NodeTag, keyof Position>;
type NodeErrorWithoutPosition = Omit<NodeError, keyof Position>;
interface Position {
/**
* 0-indexed line number
*/
line: number;
/**
* 0-indexed column number
*/
character: number;
}
type Range = [start: number, end: number];
interface TwoslashGenericResult {
/**
* The output code, could be TypeScript, but could also be a JS/JSON/d.ts
*/
code: string;
/**
* Extension of the output code
*/
extension?: string;
/**
* Nodes containing various bits of information about the code
*/
nodes: TwoslashNode[];
}
type TwoslashGenericFunction<Options = never> = (code: string, filename?: string, options?: Options) => TwoslashGenericResult;
export type { CompletionEntry, ErrorLevel, NodeBase, NodeCompletion, NodeError, NodeErrorWithoutPosition, NodeHighlight, NodeHover, NodeQuery, NodeStartLength, NodeTag, NodeWithoutPosition, Position, Range, TwoslashGenericFunction, TwoslashGenericResult, TwoslashNode };
+101
View File
@@ -0,0 +1,101 @@
/**
* Basic node with start and length to represent a range in the code
*/
interface NodeStartLength {
/** 0-indexed position of the node in the file */
start: number;
/** The length of the node */
length: number;
}
interface NodeBase extends NodeStartLength, Position {
}
interface NodeHover extends NodeBase {
type: 'hover';
/** The string content of the node this represents (mainly for debugging) */
target: string;
/** The base LSP response (the type) */
text: string;
/** Attached JSDoc info */
docs?: string;
/** JSDoc tags */
tags?: [name: string, text: string | undefined][];
}
interface NodeHighlight extends NodeBase {
type: 'highlight';
/** The annotation message */
text?: string;
}
interface NodeQuery extends Omit<NodeHover, 'type'> {
type: 'query';
}
interface CompletionEntry {
name: string;
kind?: string;
}
interface NodeCompletion extends NodeBase {
type: 'completion';
/** Results for completions at a particular point */
completions: CompletionEntry[];
completionsPrefix: string;
}
type ErrorLevel = 'warning' | 'error' | 'suggestion' | 'message';
interface NodeError extends NodeBase {
type: 'error';
id?: string;
/**
* Error level
* When not provided, defaults to 'error'
*/
level?: ErrorLevel;
/**
* Error code
*/
code?: number | string;
/**
* Error message
*/
text: string;
/**
* The filename of the file the error is in
*/
filename?: string;
}
interface NodeTag extends NodeBase {
type: 'tag';
/** What was the name of the tag */
name: string;
/** What was the text after the `// @tag: ` string (optional because you could do // @tag on it's own line without the ':') */
text?: string;
}
type TwoslashNode = NodeHighlight | NodeHover | NodeQuery | NodeCompletion | NodeError | NodeTag;
type NodeWithoutPosition = Omit<NodeHighlight, keyof Position> | Omit<NodeHover, keyof Position> | Omit<NodeQuery, keyof Position> | Omit<NodeCompletion, keyof Position> | Omit<NodeError, keyof Position> | Omit<NodeTag, keyof Position>;
type NodeErrorWithoutPosition = Omit<NodeError, keyof Position>;
interface Position {
/**
* 0-indexed line number
*/
line: number;
/**
* 0-indexed column number
*/
character: number;
}
type Range = [start: number, end: number];
interface TwoslashGenericResult {
/**
* The output code, could be TypeScript, but could also be a JS/JSON/d.ts
*/
code: string;
/**
* Extension of the output code
*/
extension?: string;
/**
* Nodes containing various bits of information about the code
*/
nodes: TwoslashNode[];
}
type TwoslashGenericFunction<Options = never> = (code: string, filename?: string, options?: Options) => TwoslashGenericResult;
export type { CompletionEntry, ErrorLevel, NodeBase, NodeCompletion, NodeError, NodeErrorWithoutPosition, NodeHighlight, NodeHover, NodeQuery, NodeStartLength, NodeTag, NodeWithoutPosition, Position, Range, TwoslashGenericFunction, TwoslashGenericResult, TwoslashNode };
+101
View File
@@ -0,0 +1,101 @@
/**
* Basic node with start and length to represent a range in the code
*/
interface NodeStartLength {
/** 0-indexed position of the node in the file */
start: number;
/** The length of the node */
length: number;
}
interface NodeBase extends NodeStartLength, Position {
}
interface NodeHover extends NodeBase {
type: 'hover';
/** The string content of the node this represents (mainly for debugging) */
target: string;
/** The base LSP response (the type) */
text: string;
/** Attached JSDoc info */
docs?: string;
/** JSDoc tags */
tags?: [name: string, text: string | undefined][];
}
interface NodeHighlight extends NodeBase {
type: 'highlight';
/** The annotation message */
text?: string;
}
interface NodeQuery extends Omit<NodeHover, 'type'> {
type: 'query';
}
interface CompletionEntry {
name: string;
kind?: string;
}
interface NodeCompletion extends NodeBase {
type: 'completion';
/** Results for completions at a particular point */
completions: CompletionEntry[];
completionsPrefix: string;
}
type ErrorLevel = 'warning' | 'error' | 'suggestion' | 'message';
interface NodeError extends NodeBase {
type: 'error';
id?: string;
/**
* Error level
* When not provided, defaults to 'error'
*/
level?: ErrorLevel;
/**
* Error code
*/
code?: number | string;
/**
* Error message
*/
text: string;
/**
* The filename of the file the error is in
*/
filename?: string;
}
interface NodeTag extends NodeBase {
type: 'tag';
/** What was the name of the tag */
name: string;
/** What was the text after the `// @tag: ` string (optional because you could do // @tag on it's own line without the ':') */
text?: string;
}
type TwoslashNode = NodeHighlight | NodeHover | NodeQuery | NodeCompletion | NodeError | NodeTag;
type NodeWithoutPosition = Omit<NodeHighlight, keyof Position> | Omit<NodeHover, keyof Position> | Omit<NodeQuery, keyof Position> | Omit<NodeCompletion, keyof Position> | Omit<NodeError, keyof Position> | Omit<NodeTag, keyof Position>;
type NodeErrorWithoutPosition = Omit<NodeError, keyof Position>;
interface Position {
/**
* 0-indexed line number
*/
line: number;
/**
* 0-indexed column number
*/
character: number;
}
type Range = [start: number, end: number];
interface TwoslashGenericResult {
/**
* The output code, could be TypeScript, but could also be a JS/JSON/d.ts
*/
code: string;
/**
* Extension of the output code
*/
extension?: string;
/**
* Nodes containing various bits of information about the code
*/
nodes: TwoslashNode[];
}
type TwoslashGenericFunction<Options = never> = (code: string, filename?: string, options?: Options) => TwoslashGenericResult;
export type { CompletionEntry, ErrorLevel, NodeBase, NodeCompletion, NodeError, NodeErrorWithoutPosition, NodeHighlight, NodeHover, NodeQuery, NodeStartLength, NodeTag, NodeWithoutPosition, Position, Range, TwoslashGenericFunction, TwoslashGenericResult, TwoslashNode };
+1
View File
@@ -0,0 +1 @@