NeedsFixingPage

tcmts.studio.NeedsFixingPage · #ap3y7dr3 · term · runs on the server

is an async component

What needs fixing on the branch, as tcm todo says, each with what fixes it: a merge waiting apart (what it left, as merged, and Drop the merge); conflicted names, each definition with its side (Edit merges them into a file, Keep ours, theirs or one by hash settles it); definitions that stopped typechecking, with why (Edit writes each against what replaced what it used); and code using a version nothing names. On a branch only read, nothing to press.

import { at, codeHref, servedCodebase, shareNow } from "#codebase/tcmts/share";
import {
  AbortMergeButton, ConflictButtons, EditButton, fixesOf, Live, mergeRuns, scratchFiles, StudioFrame, studioShare,
} from "#codebase/tcmts/studio";

export async function NeedsFixingPage() {
  studioShare();
  const s = shareNow();
  if (!s.signedIn)
    return (
      <StudioFrame tab="fix" title="Sign in">
        {null}
      </StudioFrame>
    );
  const own = s.cb === servedCodebase().cb;
  const list = await fixesOf(s.cb);
  const running = mergeRuns.some((r) => r.ended === undefined);
  const name = (n: string | undefined, hash: string) =>
    n ? (
      <a className="mono" href={codeHref(n)}>
        {n}
      </a>
    ) : (
      <code>#{hash.slice(0, 8)}</code>
    );
  const merged = list.merging
    ? scratchFiles(s.cb).filter((f) => list.conflicts.some((c) => f.file.startsWith(`${c.path}.`)))
    : [];
  const nothing = !list.conflicts.length && !list.broken.length && !list.unnamed.length;
  return (
    <StudioFrame tab="fix" title="Needs fixing">
      {(running || list.merging) && <Live every={4000} />}
      <div className="pagehead">
        <h2 className="title">Needs fixing</h2>
        <span className="muted">
          {nothing
            ? ""
            : [
                list.conflicts.length && `${list.conflicts.length} conflicted`,
                list.broken.length && `${list.broken.length} no longer typecheck${list.broken.length === 1 ? "s" : ""}`,
                list.unnamed.length && `${list.unnamed.length} use${list.unnamed.length === 1 ? "s" : ""} an unnamed version`,
              ]
                .filter(Boolean)
                .join(" · ")}
        </span>
      </div>
      {list.merging && (
        <div className="notice mergewait">
          <div>
            <b>{`Merging ${list.merging} into ${list.branch} isn't finished.`}</b>{" "}
            {`It waits apart: ${list.branch} stays as it was until what's below is settled, and then has the merge. What's here is as merged.`}
            {merged.length > 0 && (
              <>
                {" Its conflicts are written out, to settle: "}
                {merged.map((f, i) => (
                  <span key={f.file}>
                    {i ? ", " : ""}
                    <a className="mono" href={at(`/scratch/${encodeURIComponent(f.file)}`)}>
                      {f.file}
                    </a>
                  </span>
                ))}
                .
              </>
            )}
          </div>
          {own && <AbortMergeButton theirs={list.merging} into={list.branch} />}
        </div>
      )}
      {nothing && (
        <div className="box">
          <div className="empty">
            {list.merging
              ? "Nothing's left to settle: the next change on the branch finishes the merge."
              : "Nothing needs fixing: everything typechecks, and no name is conflicted."}
          </div>
        </div>
      )}
      {list.conflicts.length > 0 && (
        <div className="box">
          <div className="boxhead">
            <b>Conflicted</b>
            <span className="muted">two definitions or more under one name: keep one, or merge them in a file</span>
          </div>
          {list.conflicts.map((c) => (
            <div className="failrow" key={c.key}>
              <div className="lrow">
                <span>
                  {name(c.path, c.sides[0]?.hash ?? "")}
                  {c.key !== c.path && <span className="muted"> (its type)</span>}
                </span>
                {own && <ConflictButtons name={c.path} sides={c.sides} />}
              </div>
              <div className="conflictsides muted">
                {c.sides.map((x) => (
                  <span key={x.hash}>
                    <code>#{x.hash.slice(0, 8)}</code>
                    {x.side ? ` ${x.side}: ${x.label}` : ""}
                  </span>
                ))}
              </div>
            </div>
          ))}
        </div>
      )}
      {list.broken.length > 0 && (
        <div className="box">
          <div className="boxhead">
            <b>No longer typechecks</b>
            <span className="muted">
              what it used changed; Edit writes it with what replaced that, to fix (fixing the first often fixes the
              rest)
            </span>
          </div>
          {list.broken.map((b) => (
            <div className="failrow" key={b.hash}>
              <div className="lrow">
                <span>{name(b.names[0], b.hash)}</span>
                {own && b.names[0] && (
                  <span className="actions">
                    <EditButton name={b.names[0]} />
                  </span>
                )}
              </div>
              <pre className="code errmsg">{b.message.split("\n").slice(0, 6).join("\n")}</pre>
            </div>
          ))}
        </div>
      )}
      {list.unnamed.length > 0 && (
        <div className="box">
          <div className="boxhead">
            <b>Uses an unnamed version</b>
            <span className="muted">
              not wrong: from a change it couldn't follow, or a name removed with Delete; Edit imports what replaced it,
              where something names that
            </span>
          </div>
          {list.unnamed.map((u) => (
            <div className="lrow" key={`${u.path} ${u.uses}`}>
              <span>
                {name(u.path, u.uses)}{" "}
                <span className="muted">
                  uses <code>#{u.uses.slice(0, 8)}</code>, made as <span className="mono">{u.madeAs}</span>
                </span>
              </span>
              {own && (
                <span className="actions">
                  <EditButton name={u.path} />
                </span>
              )}
            </div>
          ))}
        </div>
      )}
    </StudioFrame>
  );
}

uses

used by