mirror of
https://github.com/anthropics/claude-code.git
synced 2026-09-28 05:03:10 +08:00
diff: the first edit opens the pane only from the main loop with checkpointing on, and an open the engine leaves waiting is withdrawn
This commit is contained in:
+14
-10
@@ -13,12 +13,15 @@ built-in's list keys (`ctrl+up`/`ctrl+down`, `opt+up`/`opt+down`), and
|
||||
through Buttons that declare the engine's own actions. The pane refreshes
|
||||
as Claude edits and runs shell commands, and while it is open it polls
|
||||
the repository's HEAD so a commit or checkout made elsewhere shows too.
|
||||
The first successful edit of a session opens the pane by itself where the
|
||||
The main loop's first successful edit of a session opens the pane by
|
||||
itself, as the built-in panel opens on its first checkpoint: where the
|
||||
layout docks it beside the transcript (the fullscreen layout, which each
|
||||
drawing's `viewport` says) and the terminal is wide enough (144 columns
|
||||
when the person never chose, 110 when they kept it open before; a person
|
||||
who closed it is left alone); where the surface does not say, nothing
|
||||
opens by itself.
|
||||
drawing's `viewport` says), the terminal is wide enough (144 columns when
|
||||
the person never chose, 110 when they kept it open before; a person who
|
||||
closed it is left alone) and file checkpointing is on; a subagent's edit
|
||||
opens nothing, and where the surface does not say, nothing opens by
|
||||
itself. An open the engine leaves waiting undrawn is withdrawn, so no
|
||||
later resize seats it, and the next edit asks again.
|
||||
|
||||
Under the fullscreen layout a terminal under 110 columns gets the
|
||||
built-in's line asking for a wider one and nothing opens. Without that
|
||||
@@ -68,16 +71,17 @@ moved file by.
|
||||
| `ui.scroll` of the pane | Docked, moves the hunks under the pinned header and list (three rows a wheel tick, a page a page key), or the list when the wheel is over it, and keeps the engine's window still. |
|
||||
| `ui.focus` in the pane | In the dialog's list, selects the file the ring lands on, re-centres the five rows on it, and lands the ring where that row now sits. |
|
||||
| `command.run` of `clear`, `resume` | Closes the pane and forgets the session's state, the pinned repository with it. |
|
||||
| `tool.call` of `Edit`, `Write`, `NotebookEdit` | After an edit that landed (not refused, not failed), refreshes an open pane; the session's first such edit opens it, pinning the repository then if the terminal has the room. |
|
||||
| `tool.call` of `Edit`, `Write`, `NotebookEdit` | After an edit that landed (not refused, not failed), refreshes an open pane; the main loop's first such edit opens it, pinning the repository then if the terminal has the room and checkpointing is on. |
|
||||
| `tool.call` of `Bash`, `PowerShell` | After a command that was not refused, failed and interrupted ones too, refreshes an open pane. |
|
||||
| `prompt.submit` | Adds the armed file's hunks to the prompt's context and disarms. |
|
||||
|
||||
## What it calls on `$`
|
||||
|
||||
`clock.after`, `clock.every`, `clock.now`, `command.register`, `fs.list`,
|
||||
`fs.read`, `fs.stat`, `process.run` (git, read-only), `session.messages`,
|
||||
`store.get`, `store.set`, `telemetry.log`, `telemetry.mark`, `ui.close`,
|
||||
`ui.invalidate`, `ui.log`, `ui.open`, `ui.resolve`, `ui.status`.
|
||||
`clock.after`, `clock.every`, `clock.now`, `command.register`, `env.get`
|
||||
(`CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING`), `fs.list`, `fs.read`, `fs.stat`,
|
||||
`process.run` (git, read-only), `session.id`, `session.messages`,
|
||||
`settings.read`, `store.get`, `store.set`, `telemetry.log`, `telemetry.mark`,
|
||||
`ui.close`, `ui.invalidate`, `ui.log`, `ui.open`, `ui.resolve`, `ui.status`.
|
||||
|
||||
`$.telemetry` is the telemetry plugin's noun; where it is absent the rows
|
||||
are dropped and nothing else changes.
|
||||
|
||||
@@ -64,6 +64,12 @@ export type Host = {
|
||||
*/
|
||||
storeSet: (key: string, value: unknown) => Promise<void>
|
||||
|
||||
/**
|
||||
* Whether the session checkpoints edits (`$.settings.read`, `$.env.get`):
|
||||
* the built-in panel opens on an edit only while it does.
|
||||
*/
|
||||
isCheckpointing: () => Promise<boolean>
|
||||
|
||||
/**
|
||||
* `$.session.messages`.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@ export * from './drawn-files-of'
|
||||
export * from './entry-kinds-of'
|
||||
export * from './git'
|
||||
export * from './host'
|
||||
export * from './is-checkpointing'
|
||||
export * from './is-on-pane-surface'
|
||||
export * from './is-record'
|
||||
export * from './kept-of'
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './is-checkpointing.js'
|
||||
|
||||
export * as default from '.'
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { Settings } from 'claude-code'
|
||||
|
||||
/**
|
||||
* Whether the session checkpoints Claude's edits, read as the built-in
|
||||
* reads it: the setting on unless set false, the variable unset or falsy.
|
||||
*
|
||||
* The built-in panel opens on an edit only through a checkpoint, so with
|
||||
* checkpointing off its first-edit open never happens; `/diff` still opens.
|
||||
*
|
||||
* @param settings the merged settings (`$.settings.read()`)
|
||||
* @param disabling `CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING` as `$.env.get`
|
||||
* answers it
|
||||
* @returns false when either turns checkpointing off
|
||||
*/
|
||||
export const isCheckpointing = (
|
||||
settings: Settings,
|
||||
disabling: string | undefined,
|
||||
): boolean =>
|
||||
settings.fileCheckpointingEnabled !== false &&
|
||||
!['1', 'true', 'yes', 'on'].includes((disabling ?? '').trim().toLowerCase())
|
||||
+43
-10
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
Args,
|
||||
On,
|
||||
PaneOpenArgs,
|
||||
ResultOf,
|
||||
@@ -13,7 +14,9 @@ import { drawnFilesOf } from './drawn-files-of'
|
||||
import { entryKindsOf } from './entry-kinds-of'
|
||||
import type Git from './git'
|
||||
import type { Host } from './host'
|
||||
import { isCheckpointing } from './is-checkpointing'
|
||||
import { isOnPaneSurface } from './is-on-pane-surface'
|
||||
import { isRecord } from './is-record'
|
||||
import Limits from './limits'
|
||||
import { mapLimited } from './map-limited'
|
||||
import { messageOf } from './message-of'
|
||||
@@ -30,8 +33,8 @@ import Views from './views'
|
||||
* pane's drawing and refresh, its opening on Claude's first edit, the ask.
|
||||
*
|
||||
* Git runs when the built-in's would: `session.start` binds the host and
|
||||
* registers `/diff`; `/diff` or the first edit with room pins the backend
|
||||
* where the session started, until `/clear`; an open pane alone fetches.
|
||||
* registers `/diff`; `/diff` or the main loop's first checkpointed edit with
|
||||
* room pins the backend, until `/clear`; only a placed, open pane fetches.
|
||||
*
|
||||
* @param on the engine's registrar
|
||||
*/
|
||||
@@ -394,7 +397,7 @@ export function register(on: On) {
|
||||
async function openPane(
|
||||
engine: Host,
|
||||
trigger: (typeof Record.SHOWN_TRIGGERS)[number],
|
||||
): Promise<void> {
|
||||
): Promise<boolean> {
|
||||
const isDialog = model.isFullscreen === false
|
||||
|
||||
model = {
|
||||
@@ -406,12 +409,20 @@ export function register(on: On) {
|
||||
|
||||
dialogRows = isDialog ? Views.dialogRowsOf(model) : null
|
||||
|
||||
await engine.openPane(
|
||||
const opened = await engine.openPane(
|
||||
isDialog
|
||||
? { ...dialogPane(), focus: true }
|
||||
: { id: Names.PANE_ID, title: Names.PANE_TITLE, holdToasts: true },
|
||||
)
|
||||
|
||||
const isWaiting = isRecord(opened) && opened.isPlaced === false
|
||||
|
||||
if (isWaiting) {
|
||||
await engine.closePane({ id: Names.PANE_ID }).catch(() => undefined)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
isPaneOpen = true
|
||||
|
||||
const sessionId = await engine.sessionId().catch(() => null)
|
||||
@@ -422,6 +433,8 @@ export function register(on: On) {
|
||||
}
|
||||
|
||||
void refresh(engine)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async function closePane(engine: Host): Promise<void> {
|
||||
@@ -460,6 +473,12 @@ export function register(on: On) {
|
||||
return
|
||||
}
|
||||
|
||||
const isCheckpointed = await engine.isCheckpointing().catch(() => true)
|
||||
|
||||
if (!isCheckpointed || isTaken()) {
|
||||
return
|
||||
}
|
||||
|
||||
await pinBackend(engine)
|
||||
|
||||
if (!backend || isTaken()) {
|
||||
@@ -467,7 +486,7 @@ export function register(on: On) {
|
||||
}
|
||||
|
||||
hasAutoOpened = true
|
||||
await openPane(engine, 'auto_open')
|
||||
hasAutoOpened = await openPane(engine, 'auto_open')
|
||||
}
|
||||
|
||||
function disarm(engine: Host) {
|
||||
@@ -602,6 +621,11 @@ export function register(on: On) {
|
||||
readFile: path => $.fs.read(path),
|
||||
storeGet: key => $.store.get(key),
|
||||
storeSet: (key, value) => $.store.set(key, value),
|
||||
isCheckpointing: async () =>
|
||||
isCheckpointing(
|
||||
await $.settings.read(),
|
||||
await $.env.get('CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING'),
|
||||
),
|
||||
messages: () => $.session.messages(),
|
||||
invalidate: () => $.ui.invalidate('ui.render'),
|
||||
status: text => $.ui.status(text),
|
||||
@@ -699,7 +723,14 @@ export function register(on: On) {
|
||||
}
|
||||
|
||||
const isOpening = toggle === 'open'
|
||||
await (isOpening ? openPane(host, 'manual') : closePane(host))
|
||||
|
||||
const isDone = isOpening
|
||||
? await openPane(host, 'manual')
|
||||
: await closePane(host).then(() => true)
|
||||
|
||||
if (!isDone) {
|
||||
return { text: Names.RESIZE_TERMINAL_TEXT }
|
||||
}
|
||||
|
||||
if (!isFullscreen) {
|
||||
return isOpening ? {} : { text: Names.DIALOG_DISMISSED_TEXT }
|
||||
@@ -818,10 +849,10 @@ export function register(on: On) {
|
||||
|
||||
function afterTool(
|
||||
engine: Host,
|
||||
tool: string,
|
||||
e: Args<'tool.call'>,
|
||||
result: ResultOf['tool.call'] | undefined,
|
||||
) {
|
||||
const isEdit = Tools.EDITING_TOOLS.some(name => name === tool)
|
||||
const isEdit = Tools.EDITING_TOOLS.some(name => name === e.tool)
|
||||
|
||||
const hasEdited =
|
||||
isEdit &&
|
||||
@@ -837,7 +868,9 @@ export function register(on: On) {
|
||||
scheduleRefresh(engine)
|
||||
}
|
||||
|
||||
if (hasEdited) {
|
||||
const isMainLoopEdit = hasEdited && e.agentId === undefined
|
||||
|
||||
if (isMainLoopEdit) {
|
||||
void openOnFirstEdit(engine).catch(() => undefined)
|
||||
}
|
||||
}
|
||||
@@ -854,7 +887,7 @@ export function register(on: On) {
|
||||
return result
|
||||
} finally {
|
||||
if (host) {
|
||||
afterTool(host, e.tool, result)
|
||||
afterTool(host, e, result)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import type { Settings } from 'claude-code'
|
||||
|
||||
/**
|
||||
* What the world beneath a repository session holds and answers besides
|
||||
* git: the store, the settings, the environment, whether a pane is seated.
|
||||
*/
|
||||
export type Beneath = {
|
||||
/**
|
||||
* What the plugin's store holds at the start; nothing when not given.
|
||||
*/
|
||||
stored?: Readonly<Record<string, unknown>>
|
||||
|
||||
/**
|
||||
* What `$.settings.read` answers; empty when not given.
|
||||
*/
|
||||
settings?: Settings
|
||||
|
||||
/**
|
||||
* The variables `$.env.get` answers from; none set when not given.
|
||||
*/
|
||||
env?: Readonly<Record<string, string>>
|
||||
|
||||
/**
|
||||
* Whether an open made now is left waiting undrawn (LEFT_WAITING), as an
|
||||
* engine leaves an unasked open on a narrow terminal; placed when not given.
|
||||
*/
|
||||
isLeftWaiting?: () => boolean
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export type * from './beneath.js'
|
||||
export * from './left-waiting.js'
|
||||
|
||||
export * as default from '.'
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { ResultOf } from 'claude-code'
|
||||
|
||||
/**
|
||||
* What an engine answers an open it leaves waiting undrawn: the plugin
|
||||
* opened unasked on a terminal narrower than an unrequested pane is given.
|
||||
*
|
||||
* Typed through `never` so it compiles against declarations that predate the
|
||||
* answer, where `ui.open` resolves nothing.
|
||||
*/
|
||||
export const LEFT_WAITING: ResultOf['ui.open'] = {
|
||||
value: {
|
||||
isPlaced: false,
|
||||
reason:
|
||||
'unasked below 144 columns (120 now): placed when the person opens ' +
|
||||
'it, or when the terminal is widened to 144 columns',
|
||||
} as never,
|
||||
}
|
||||
+25
-9
@@ -1,6 +1,7 @@
|
||||
import type { Args, On } from 'claude-code'
|
||||
import { mock } from 'claude-code/testing'
|
||||
|
||||
import Beneath from './beneath'
|
||||
import { gitIn } from './git-in.js'
|
||||
import { HINT_DRAWN } from './hint-drawn.js'
|
||||
import { keeping } from './keeping.js'
|
||||
@@ -11,24 +12,25 @@ import { startsSession } from './starts-session.js'
|
||||
* A session in a repository git answers for from a script (REPOSITORY, in
|
||||
* /work, when none is given), keeping what the plugin does there.
|
||||
*
|
||||
* Kept: each git run, ring move, pane opened or closed, status line. The clock
|
||||
* starts at 0 and the engine draws the hint. A test that rewrites the
|
||||
* script between calls changes what git answers next.
|
||||
* Kept: each git run, ring move, pane opened, left waiting or closed, status
|
||||
* line. The clock starts at 0 and the engine draws the hint; the rest of the
|
||||
* world is the test's (Beneath). Rewriting the script changes git's answers.
|
||||
*
|
||||
* @param on the test's `on`
|
||||
* @param script git's output for each invocation whose line holds the key
|
||||
* @param stored what the plugin's store holds at the start
|
||||
* @returns the runs, the ring's moves, the panes opened and closed, the
|
||||
* statuses, the clock
|
||||
* @param beneath the store, settings, environment, whether an open is seated
|
||||
* @returns the runs, the ring's moves, the panes opened, left waiting and
|
||||
* closed, the statuses, the clock
|
||||
*/
|
||||
export function inRepository(
|
||||
on: On,
|
||||
script: Readonly<Record<string, string>> = REPOSITORY,
|
||||
stored: Readonly<Record<string, unknown>> = {},
|
||||
beneath: Beneath.Beneath = {},
|
||||
) {
|
||||
const runs: Args<'process.run'>[] = []
|
||||
const focused: Args<'ui.focus'>[] = []
|
||||
const statuses: (string | undefined)[] = []
|
||||
const waiting: Args<'ui.open'>[] = []
|
||||
const opened = keeping<Args<'ui.open'>>()
|
||||
const closed = keeping<Args<'ui.close'>>()
|
||||
const clock = startsSession(on)
|
||||
@@ -51,17 +53,31 @@ export function inRepository(
|
||||
return { value: undefined }
|
||||
})
|
||||
|
||||
on('ui.open', opened.hook)
|
||||
on('ui.open', (engine, e) => {
|
||||
const isWaiting = beneath.isLeftWaiting?.() === true
|
||||
|
||||
if (!isWaiting) {
|
||||
return opened.hook(engine, e)
|
||||
}
|
||||
|
||||
waiting.push(e)
|
||||
|
||||
return Beneath.LEFT_WAITING
|
||||
})
|
||||
|
||||
on('ui.close', closed.hook)
|
||||
on('ui.invalidate', () => ({ value: undefined }))
|
||||
on('ui.render', { component: 'PromptHint' }, () => HINT_DRAWN)
|
||||
on('session.messages', () => ({ value: [] }))
|
||||
mock.store(on, stored)
|
||||
on('settings.read', () => ({ value: beneath.settings ?? {} }))
|
||||
mock.store(on, beneath.stored ?? {})
|
||||
mock.env(on, beneath.env ?? {})
|
||||
|
||||
return {
|
||||
runs,
|
||||
focused,
|
||||
opened: opened.kept,
|
||||
waiting,
|
||||
closed: closed.kept,
|
||||
statuses,
|
||||
clock,
|
||||
|
||||
Vendored
+1
@@ -1,6 +1,7 @@
|
||||
export * from './answers-of.js'
|
||||
export * from './ask'
|
||||
export * from './backend-of'
|
||||
export * from './beneath'
|
||||
export * from './big-lines.js'
|
||||
export * from './branch-base-of'
|
||||
export * from './builtin-holds.js'
|
||||
|
||||
@@ -448,6 +448,180 @@ describe('register', () => {
|
||||
).toEqual(['diff'])
|
||||
})
|
||||
|
||||
test("only the main loop's edit opens the pane", async ($, on) => {
|
||||
const world = Fixtures.inRepository(on)
|
||||
|
||||
const edit = (agentId?: string) =>
|
||||
$.tool.call({
|
||||
tool: 'Write',
|
||||
file_path: '/work/app.ts',
|
||||
content: '2',
|
||||
...(agentId !== undefined && { agentId }),
|
||||
})
|
||||
|
||||
on('tool.call', () => ({ result: 'written' }))
|
||||
|
||||
await $.session.start(Fixtures.SESSION)
|
||||
await $.ui.render(Fixtures.HINT)
|
||||
await edit('a-explorer')
|
||||
await world.clock.advance(Fixtures.SETTLE_MS)
|
||||
|
||||
expect(world.opened, 'the built-in checkpoints no subagent edit').toEqual(
|
||||
[],
|
||||
)
|
||||
|
||||
expect(world.runs, 'so nothing was asked of the repository').toEqual([])
|
||||
|
||||
await edit()
|
||||
await world.clock.advance(Fixtures.SETTLE_MS)
|
||||
|
||||
expect(
|
||||
world.opened.map(pane => pane.id),
|
||||
"the main loop's",
|
||||
).toEqual(['diff'])
|
||||
})
|
||||
|
||||
test('checkpointing off in settings: edits open nothing', async ($, on) => {
|
||||
const world = Fixtures.inRepository(on, Fixtures.REPOSITORY, {
|
||||
settings: { fileCheckpointingEnabled: false },
|
||||
})
|
||||
|
||||
on('tool.call', () => ({ result: 'edited' }))
|
||||
|
||||
await $.session.start(Fixtures.SESSION)
|
||||
await $.ui.render(Fixtures.HINT)
|
||||
|
||||
await $.tool.call({
|
||||
tool: 'Edit',
|
||||
file_path: '/work/app.ts',
|
||||
old_string: '1',
|
||||
new_string: '2',
|
||||
})
|
||||
|
||||
await world.clock.advance(Fixtures.SETTLE_MS)
|
||||
|
||||
expect(world.opened, 'the built-in opens on a checkpoint alone').toEqual([])
|
||||
|
||||
expect(world.runs, 'so nothing was asked of the repository').toEqual([])
|
||||
|
||||
expect(await $.command.run(Fixtures.DIFF)).toEqual({
|
||||
text: 'Diff panel shown',
|
||||
})
|
||||
})
|
||||
|
||||
test('checkpointing off by variable: edits open nothing', async ($, on) => {
|
||||
const world = Fixtures.inRepository(on, Fixtures.REPOSITORY, {
|
||||
env: { CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING: 'true' },
|
||||
})
|
||||
|
||||
on('tool.call', () => ({ result: 'edited' }))
|
||||
|
||||
await $.session.start(Fixtures.SESSION)
|
||||
await $.ui.render(Fixtures.HINT)
|
||||
|
||||
await $.tool.call({
|
||||
tool: 'Edit',
|
||||
file_path: '/work/app.ts',
|
||||
old_string: '1',
|
||||
new_string: '2',
|
||||
})
|
||||
|
||||
await world.clock.advance(Fixtures.SETTLE_MS)
|
||||
|
||||
expect(world.opened).toEqual([])
|
||||
})
|
||||
|
||||
test('an open left waiting is withdrawn; /diff opens it', async ($, on) => {
|
||||
let isNarrow = true
|
||||
|
||||
const world = Fixtures.inRepository(on, Fixtures.REPOSITORY, {
|
||||
stored: { [Names.STORE_OPEN_KEY]: true },
|
||||
isLeftWaiting: () => isNarrow,
|
||||
})
|
||||
|
||||
const edit = () =>
|
||||
$.tool.call({
|
||||
tool: 'Edit',
|
||||
file_path: '/work/app.ts',
|
||||
old_string: '1',
|
||||
new_string: '2',
|
||||
})
|
||||
|
||||
on('tool.call', () => ({ result: 'edited' }))
|
||||
|
||||
await $.session.start(Fixtures.SESSION)
|
||||
await $.ui.render(Fixtures.hintAt(Limits.AUTO_OPEN_MIN_COLUMNS - 1))
|
||||
await edit()
|
||||
await world.clock.advance(Fixtures.SETTLE_MS)
|
||||
|
||||
expect(
|
||||
world.waiting.map(pane => pane.id),
|
||||
'the edit opened',
|
||||
).toEqual(['diff'])
|
||||
|
||||
expect(
|
||||
world.closed.map(pane => pane.id),
|
||||
'and withdrew the pane the engine left waiting, so no resize seats it',
|
||||
).toEqual(['diff'])
|
||||
|
||||
expect(
|
||||
world.runs.map(run => Fixtures.gitWordOf(run.argv)),
|
||||
'the repository was found, nothing fetched for a pane no one sees',
|
||||
).toEqual(['rev-parse --show-toplevel'])
|
||||
|
||||
isNarrow = false
|
||||
|
||||
expect(
|
||||
await $.command.run(Fixtures.diffAt(Limits.AUTO_OPEN_MIN_COLUMNS - 1)),
|
||||
'/diff opens it, where a pane believed open would have been hidden',
|
||||
).toEqual({ text: 'Diff panel shown' })
|
||||
|
||||
await $.command.run(Fixtures.diffAt(Limits.AUTO_OPEN_MIN_COLUMNS - 1))
|
||||
await edit()
|
||||
await world.clock.advance(Fixtures.SETTLE_MS)
|
||||
|
||||
expect(
|
||||
world.opened.map(pane => pane.id),
|
||||
'closed by hand, no edit opens it again',
|
||||
).toEqual(['diff'])
|
||||
})
|
||||
|
||||
test('an unplaced open is tried again at the next edit', async ($, on) => {
|
||||
let isNarrow = true
|
||||
|
||||
const world = Fixtures.inRepository(on, Fixtures.REPOSITORY, {
|
||||
stored: { [Names.STORE_OPEN_KEY]: true },
|
||||
isLeftWaiting: () => isNarrow,
|
||||
})
|
||||
|
||||
const edit = () =>
|
||||
$.tool.call({
|
||||
tool: 'Edit',
|
||||
file_path: '/work/app.ts',
|
||||
old_string: '1',
|
||||
new_string: '2',
|
||||
})
|
||||
|
||||
on('tool.call', () => ({ result: 'edited' }))
|
||||
|
||||
await $.session.start(Fixtures.SESSION)
|
||||
await $.ui.render(Fixtures.hintAt(Limits.AUTO_OPEN_MIN_COLUMNS - 1))
|
||||
await edit()
|
||||
await world.clock.advance(Fixtures.SETTLE_MS)
|
||||
|
||||
expect(world.opened, 'the first try waited and was withdrawn').toEqual([])
|
||||
|
||||
isNarrow = false
|
||||
|
||||
await edit()
|
||||
await world.clock.advance(Fixtures.SETTLE_MS)
|
||||
|
||||
expect(
|
||||
world.opened.map(pane => pane.id),
|
||||
'the next edit opened it, as the built-in reads the width again then',
|
||||
).toEqual(['diff'])
|
||||
})
|
||||
|
||||
test('/clear closes the pane it finds open', async ($, on) => {
|
||||
const world = Fixtures.inRepository(on)
|
||||
|
||||
@@ -528,7 +702,7 @@ describe('register', () => {
|
||||
|
||||
test('a worktree opens on the base it kept', async ($, on) => {
|
||||
const world = Fixtures.inRepository(on, Fixtures.oneSecret(), {
|
||||
'base:/main/wt': 'uncommitted',
|
||||
stored: { 'base:/main/wt': 'uncommitted' },
|
||||
})
|
||||
|
||||
await $.session.start(Fixtures.WORKTREE_SESSION)
|
||||
|
||||
Reference in New Issue
Block a user