zed-local

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