Move more logic into sync-client

This commit is contained in:
Andras Schmelczer 2025-08-30 11:02:04 +01:00
parent 3f089bd37e
commit 9177984ff6
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
20 changed files with 68 additions and 143 deletions

View file

@ -0,0 +1,9 @@
export function getRandomColor(name: string): string {
let hash = 0;
for (let i = 0; i < name.length; i++) {
hash = (hash << 5) - hash + name.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
const normalised = hash / 0x7fffffff;
return `oklch(0.58 0.15 ${Math.round(Math.abs(normalised * 360))})`;
}

View file

@ -0,0 +1,44 @@
import { describe, it } from "node:test";
import assert from "node:assert";
import { lineAndColumnToPosition } from "./line-and-column-to-position";
describe("lineAndColumnToPosition", () => {
it("should return the correct position for the first line", () => {
const text = "Hello\nWorld";
const position = lineAndColumnToPosition(text, 0, 3);
assert.strictEqual(position, 3);
});
it("should return the correct position for the second line", () => {
const text = "Hello\nWorld";
const position = lineAndColumnToPosition(text, 1, 2);
assert.strictEqual(position, 8);
});
it("should return the correct position for an empty string", () => {
const text = "";
const position = lineAndColumnToPosition(text, 0, 0);
assert.strictEqual(position, 0);
});
it("with carrige return", () => {
assert.strictEqual(lineAndColumnToPosition("a\nb", 1, 1), 3);
assert.strictEqual(lineAndColumnToPosition("a\r\nb", 1, 1), 3);
});
it("should handle multi-line strings with varying lengths", () => {
const text = "Line1\nLongerLine2\nShort3";
const position = lineAndColumnToPosition(text, 2, 4);
assert.strictEqual(position, 22);
});
it("should throw an error if the line number is out of range", () => {
const text = "Line1\nLine2";
assert.throws(() => lineAndColumnToPosition(text, 3, 0));
});
it("should throw an error if the column number is out of range", () => {
const text = "Line1\nLine2";
assert.throws(() => lineAndColumnToPosition(text, 1, 10));
});
});

View file

@ -0,0 +1,34 @@
/**
* Converts line and column coordinates to an absolute character position in a text string.
*
* @param line - The zero-based line number
* @param column - The zero-based column number
* @param text - The text string to calculate position in
* @returns The absolute character position (zero-based index) in the text string
* @throws Error if line number is out of range
* @throws Error if column number is out of range
*/
export function lineAndColumnToPosition(
text: string,
line: number,
column: number
): number {
const lines = text.replace("\r", "").split("\n");
if (line >= lines.length) {
throw new Error(`Line number ${line} is out of range.`);
}
if (column > lines[line].length) {
throw new Error(`Column number ${column} is out of range.`);
}
let position = 0;
for (let i = 0; i < line; i++) {
position += lines[i].length + 1;
}
position += column;
return position;
}

View file

@ -0,0 +1,66 @@
import { describe, test } from "node:test";
import assert from "node:assert";
import { positionToLineAndColumn } from "./position-to-line-and-column";
describe("positionToLineAndColumn", () => {
test("converts position to line and column in multi-line text", () => {
const text = "ab\ncd\n";
assert.deepStrictEqual(positionToLineAndColumn(text, 0), {
line: 0,
column: 0
});
assert.deepStrictEqual(positionToLineAndColumn(text, 1), {
line: 0,
column: 1
});
assert.deepStrictEqual(positionToLineAndColumn(text, 2), {
line: 0,
column: 2
});
assert.deepStrictEqual(positionToLineAndColumn(text, 3), {
line: 1,
column: 0
});
assert.deepStrictEqual(positionToLineAndColumn(text, 4), {
line: 1,
column: 1
});
assert.deepStrictEqual(positionToLineAndColumn(text, 6), {
line: 2,
column: 0
});
});
test("with carrige returns", () => {
assert.deepStrictEqual(positionToLineAndColumn("a\nb", 3), {
line: 1,
column: 1
});
assert.deepStrictEqual(positionToLineAndColumn("a\r\nb", 3), {
line: 1,
column: 1
});
});
test("handles empty input", () => {
assert.deepStrictEqual(positionToLineAndColumn("", 0), {
line: 0,
column: 0
});
});
test("handles positions at the end of text", () => {
const text = "End";
assert.deepStrictEqual(positionToLineAndColumn(text, 3), {
line: 0,
column: 3
});
});
test("throws error for position out of range", () => {
const text = "Short text";
assert.throws(() => positionToLineAndColumn(text, 15));
assert.throws(() => positionToLineAndColumn(text, -1));
});
});

View file

@ -0,0 +1,36 @@
/**
* Converts a character position in text to line and column numbers.
*
* @param text The text content to analyze
* @param position The character position to convert
* @returns An object containing line and column numbers
* @throws Will throw an error if the position is negative or exceeds the text length
*/
export function positionToLineAndColumn(
text: string,
position: number
): { line: number; column: number } {
if (position < 0) {
throw new Error("Position cannot be negative");
}
text = text.replace("\r", "");
if (
position >
text.length + 1
// +1 to account for the cursor being after last character
) {
throw new Error(
`Position ${position} exceeds text length ${text.length}`
);
}
const textUpToPosition = text.substring(0, position);
const lines = textUpToPosition.split("\n");
const line = lines.length - 1;
const column = lines[lines.length - 1].length;
return { line, column };
}