zed-local

  1#!/usr/bin/env node
  2
  3const HELP = `
  4USAGE
  5  zed-local  [options]  [zed args]
  6
  7SUMMARY
  8  Runs 1-6 instances of Zed using a locally-running collaboration server.
  9  Each instance of Zed will be signed in as a different user specified in
 10  either \`.admins.json\` or \`.admins.default.json\`.
 11
 12OPTIONS
 13  --help           Print this help message
 14  --release        Build Zed in release mode
 15  -2, -3, -4, ...  Spawn multiple Zed instances, with their windows tiled.
 16  --top            Arrange the Zed windows so they take up the top half of the screen.
 17`.trim();
 18
 19const { spawn, execFileSync } = require("child_process");
 20const assert = require("assert");
 21
 22const users = require(
 23  process.env.SEED_PATH || "../crates/collab/seed.default.json",
 24).admins;
 25
 26const RESOLUTION_REGEX = /(\d+) x (\d+)/;
 27const DIGIT_FLAG_REGEX = /^--?(\d+)$/;
 28
 29let instanceCount = 1;
 30let isReleaseMode = false;
 31let isTop = false;
 32
 33const args = process.argv.slice(2);
 34while (args.length > 0) {
 35  const arg = args[0];
 36
 37  const digitMatch = arg.match(DIGIT_FLAG_REGEX);
 38  if (digitMatch) {
 39    instanceCount = parseInt(digitMatch[1]);
 40  } else if (arg === "--release") {
 41    isReleaseMode = true;
 42  } else if (arg === "--top") {
 43    isTop = true;
 44  } else if (arg === "--help") {
 45    console.log(HELP);
 46    process.exit(0);
 47  } else {
 48    break;
 49  }
 50
 51  args.shift();
 52}
 53
 54// Parse the resolution of the main screen
 55const displayInfo = JSON.parse(
 56  execFileSync("system_profiler", ["SPDisplaysDataType", "-json"], {
 57    encoding: "utf8",
 58  }),
 59);
 60const mainDisplayResolution =
 61  displayInfo?.SPDisplaysDataType[0]?.spdisplays_ndrvs
 62    ?.find((entry) => entry.spdisplays_main === "spdisplays_yes")
 63    ?._spdisplays_resolution?.match(RESOLUTION_REGEX);
 64if (!mainDisplayResolution) {
 65  throw new Error("Could not parse screen resolution");
 66}
 67const titleBarHeight = 24;
 68const screenWidth = parseInt(mainDisplayResolution[1]);
 69let screenHeight = parseInt(mainDisplayResolution[2]) - titleBarHeight;
 70
 71if (isTop) {
 72  screenHeight = Math.floor(screenHeight / 2);
 73}
 74
 75// Determine the window size for each instance
 76let rows;
 77let columns;
 78switch (instanceCount) {
 79  case 1:
 80    [rows, columns] = [1, 1];
 81    break;
 82  case 2:
 83    [rows, columns] = [1, 2];
 84    break;
 85  case 3:
 86  case 4:
 87    [rows, columns] = [2, 2];
 88    break;
 89  case 5:
 90  case 6:
 91    [rows, columns] = [2, 3];
 92    break;
 93}
 94
 95const instanceWidth = Math.floor(screenWidth / columns);
 96const instanceHeight = Math.floor(screenHeight / rows);
 97
 98// If a user is specified, make sure it's first in the list
 99const user = process.env.ZED_IMPERSONATE;
100if (user) {
101  users = [user].concat(users.filter((u) => u !== user));
102}
103
104let buildArgs = ["build"];
105let zedBinary = "target/debug/Zed";
106if (isReleaseMode) {
107  buildArgs.push("--release");
108  zedBinary = "target/release/Zed";
109}
110
111try {
112  execFileSync("cargo", buildArgs, { stdio: "inherit" });
113} catch (e) {
114  process.exit(0);
115}
116
117setTimeout(() => {
118  for (let i = 0; i < instanceCount; i++) {
119    const row = Math.floor(i / columns);
120    const column = i % columns;
121    const position = [
122      column * instanceWidth,
123      row * instanceHeight + titleBarHeight,
124    ].join(",");
125    const size = [instanceWidth, instanceHeight].join(",");
126
127    spawn(zedBinary, i == 0 ? args : [], {
128      stdio: "inherit",
129      env: {
130        ZED_IMPERSONATE: users[i],
131        ZED_WINDOW_POSITION: position,
132        ZED_STATELESS: "1",
133        ZED_ALWAYS_ACTIVE: "1",
134        ZED_SERVER_URL: "http://localhost:3000",
135        ZED_RPC_URL: "http://localhost:8080/rpc",
136        ZED_ADMIN_API_TOKEN: "secret",
137        ZED_WINDOW_SIZE: size,
138        ZED_CLIENT_CHECKSUM_SEED: "development-checksum-seed",
139        PATH: process.env.PATH,
140        RUST_LOG: process.env.RUST_LOG || "info",
141      },
142    });
143  }
144}, 0.1);