surshar / claude-code
Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
Built by Anthropic · github.com/anthropics
| Mmods/diff/hooks/drawn-files-of/drawn-files-of.ts |
| Mmods/diff/hooks/register.ts |
| Mmods/diff/tests/views.test.ts |
| @@ -1,25 +1,30 @@ | ||
| 1 | 1 | import type Git from '../git' |
| 2 | 2 | import Limits from '../limits' |
| 3 | 3 | import PaneState from '../pane-state' |
| 4 | +import Views from '../views' | |
| 4 | 5 | |
| 5 | 6 | /** |
| 6 | - * The files whose bodies the pane draws now, in drawing order: the listed | |
| 7 | - * session rows, then the pre-session rows while their section is open. | |
| 7 | + * The files whose bodies the pane draws or a press opens: every row its | |
| 8 | + * seat lists now. | |
| 8 | 9 | * |
| 9 | - * Past PRE_SESSION_BODY_CAP pre-session files only their legend shows, so | |
| 10 | - * none of their bodies is wanted. | |
| 10 | + * Docked, the listed session rows, then the pre-session rows while their | |
| 11 | + * section is open, none past PRE_SESSION_BODY_CAP. Inline, the rows in the | |
| 12 | + * dialog's window, none for a turn, whose rows carry their own bodies. | |
| 11 | 13 | * |
| 12 | - * @param model the pane's state: its fetch and its two toggles | |
| 14 | + * @param model the pane's state: its fetch, its seat, its toggles, its pick | |
| 13 | 15 | * @returns the rows, none without a fetch |
| 14 | 16 | */ |
| 15 | 17 | export function drawnFilesOf( |
| 16 | - model: Pick< | |
| 17 | - PaneState.PaneModel, | |
| 18 | - 'data' | 'isNoiseShown' | 'isPreSessionShown' | |
| 19 | - >, | |
| 18 | + model: PaneState.PaneModel, | |
| 20 | 19 | ): readonly Git.FileStat[] { |
| 20 | + const files = model.data?.files ?? [] | |
| 21 | + const listed = Views.dialogEntriesOf(model).map(entry => entry.path) | |
| 22 | + const start = Views.dialogWindowOf(model, listed) | |
| 23 | + const windowed = listed.slice(start, start + Limits.MAX_VISIBLE_FILES) | |
| 24 | + const isTurn = PaneState.pickedTurnOf(model) !== undefined | |
| 25 | + | |
| 21 | 26 | const partition = PaneState.partitionOf( |
| 22 | - model.data?.files ?? [], | |
| 27 | + files, | |
| 23 | 28 | model.isNoiseShown ? 'shown' : 'hidden', |
| 24 | 29 | ) |
| 25 | 30 | |
| @@ -27,5 +32,8 @@ | ||
| 27 | 32 | model.isPreSessionShown && |
| 28 | 33 | partition.preSession.length <= Limits.PRE_SESSION_BODY_CAP |
| 29 | 34 | |
| 30 | - return [...partition.shown, ...(isBodied ? partition.preSession : [])] | |
| 35 | + const docked = [...partition.shown, ...(isBodied ? partition.preSession : [])] | |
| 36 | + const inDialog = files.filter(file => !isTurn && windowed.includes(file.path)) | |
| 37 | + | |
| 38 | + return model.placement === 'inline' ? inDialog : docked | |
| 31 | 39 | } |
| @@ -544,6 +544,7 @@ | ||
| 544 | 544 | place: isDocked ? Views.placeAtFile(model, path) : model.place, |
| 545 | 545 | } |
| 546 | 546 | |
| 547 | + void loadBodies(engine) | |
| 547 | 548 | redraw(engine) |
| 548 | 549 | }, |
| 549 | 550 | scrollList: delta => { |
| @@ -727,6 +728,8 @@ | ||
| 727 | 728 | |
| 728 | 729 | columns = e.viewport?.columns ?? columns |
| 729 | 730 | |
| 731 | + const isReseated = e.props.placement !== model.placement | |
| 732 | + | |
| 730 | 733 | model = { |
| 731 | 734 | ...model, |
| 732 | 735 | placement: e.props.placement, |
| @@ -740,6 +743,10 @@ | ||
| 740 | 743 | }, |
| 741 | 744 | } |
| 742 | 745 | |
| 746 | + if (isReseated) { | |
| 747 | + void loadBodies(host) | |
| 748 | + } | |
| 749 | + | |
| 743 | 750 | return Views.paneView( |
| 744 | 751 | { |
| 745 | 752 | ui: { Box, Text, Button, Select, Code }, |
| @@ -830,13 +837,9 @@ | ||
| 830 | 837 | isPaneOpen = false |
| 831 | 838 | } |
| 832 | 839 | |
| 833 | - const isDialog = model.isFullscreen === false | |
| 840 | + const isDocking = model.isFullscreen !== false | |
| 834 | 841 | |
| 835 | - if (isPersons && host && isDialog) { | |
| 836 | - host.uiLog(Names.DIALOG_DISMISSED_TEXT) | |
| 837 | - } | |
| 838 | - | |
| 839 | - if (isPersons && host && !isDialog) { | |
| 842 | + if (isPersons && host && isDocking) { | |
| 840 | 843 | markTabSwitch(host, 'convo') |
| 841 | 844 | await host.storeSet(Names.STORE_OPEN_KEY, false).catch(() => undefined) |
| 842 | 845 | } |
| @@ -859,6 +862,7 @@ | ||
| 859 | 862 | } |
| 860 | 863 | |
| 861 | 864 | model = { ...model, selectedPath: focus.selectedPath } |
| 865 | + void loadBodies(host) | |
| 862 | 866 | fitDialog(host) |
| 863 | 867 | host.invalidate() |
| 864 | 868 |
| @@ -92,6 +92,38 @@ | ||
| 92 | 92 | expect(drawn).toContain('\u2191/\u2193 to scroll \u00b7 Esc to back') |
| 93 | 93 | }) |
| 94 | 94 | |
| 95 | + test('inline, a file edited before the session opens too', async ($, on) => { | |
| 96 | + const world = Fixtures.inRepository(on, Fixtures.MOVED_IN) | |
| 97 | + | |
| 98 | + on('session.usage', () => ({ value: Fixtures.usageAt(Fixtures.SETTLE_MS) })) | |
| 99 | + Fixtures.oldFiles(on) | |
| 100 | + | |
| 101 | + await $.session.start(Fixtures.SESSION) | |
| 102 | + await $.command.run(Fixtures.DIALOG_DIFF) | |
| 103 | + await world.clock.advance(Fixtures.SETTLE_MS) | |
| 104 | + | |
| 105 | + expect( | |
| 106 | + Fixtures.textOf(await $.ui.render(Fixtures.INLINE_PANE)), | |
| 107 | + "the dialog lists it beside the session's file", | |
| 108 | + ).toContain('old.ts') | |
| 109 | + | |
| 110 | + expect(await $.ui.press({ plugin: 'diff', key: 'file:old.ts' })).toEqual({ | |
| 111 | + element: 'file:old.ts', | |
| 112 | + }) | |
| 113 | + | |
| 114 | + await world.clock.advance(Fixtures.SETTLE_MS) | |
| 115 | + | |
| 116 | + const drawn = Fixtures.textOf(await $.ui.render(Fixtures.INLINE_PANE)) | |
| 117 | + | |
| 118 | + expect( | |
| 119 | + drawn, | |
| 120 | + 'a listed row can be pressed, so its hunks were read', | |
| 121 | + ).not.toContain('Loading diff') | |
| 122 | + | |
| 123 | + expect(drawn).toContain('+b') | |
| 124 | + expect(drawn).not.toContain('+d') | |
| 125 | + }) | |
| 126 | + | |
| 95 | 127 | test('docked, a wheel tick moves the body, not the list', async ($, on) => { |
| 96 | 128 | const world = Fixtures.inRepository(on, Fixtures.MANY_FILES) |
| 97 | 129 |