259 lines · 10.5 KB
import * as Y from "yjs";
import { HttpError } from "../errors";
import { withStore } from "../git/store";
import type { GitStore } from "../git/store";
import { MAX_POINTER_BYTES, parseLfsPointer } from "../lfs/pointer";
import { loadRepoById } from "../repos";
import { readBounded } from "./blob-read";
import { FILES_MAP, textKey } from "~/lib/editor-protocol";
import type { ChangeStatus, FileKind, FileMeta } from "~/lib/editor-protocol";

export const MAX_ROOM_FILES = 300;
export const MAX_TEXT_BYTES = 1024 * 1024;
const EVICT_TARGET = 0.8;
const decoder = new TextDecoder();

export type RoomSql = SqlStorage;

export type HeadFile = { kind: FileKind; size: number; text: string | null } | null;

export function filesMap(doc: Y.Doc): Y.Map<FileMeta> {
  return doc.getMap<FileMeta>(FILES_MAP);
}

export function textOf(doc: Y.Doc, path: string): Y.Text {
  return doc.getText(textKey(path));
}

/** Rewrites a Y.Text to `next` touching only the differing middle, so remote cursors stay put. */
export function replaceText(text: Y.Text, next: string): void {
  const current = text.toString();
  if (current === next) return;
  let start = 0;
  while (start < current.length && start < next.length && current[start] === next[start]) start++;
  let end = 0;
  while (
    end < current.length - start &&
    end < next.length - start &&
    current[current.length - 1 - end] === next[next.length - 1 - end]
  ) {
    end++;