Summary
Redesign lifecycle event registration in BoundDestinationLifecycle from hard-coded per-type listener methods to a LifecycleEvent interface / hook-based abstraction that destination types implement. Currently each lifecycle event (terminal close, document close) is wired as a separate private method with destination-type branching. A generic LifecycleEvent interface with hooks (e.g., onClose, onDelete) that destination types implement would make adding new destination types and new lifecycle events trivial without modifying the lifecycle module itself.
Motivation
The initial BoundDestinationLifecycle extraction (see separate issue) is a pure extraction — moving existing listener logic out of PasteDestinationManager without changing behavior. The current design has each listener as a separate method (setupTerminalCloseListener, setupDocumentCloseListener) with switch-on-type logic inside. Adding a new destination type (e.g., file-picker output channel) or a new lifecycle event (e.g., file deletion) requires adding a new method and new branching. A hook-based design means you just implement the relevant interface on the destination class.
Proposed Design
- Define a
LifecycleEvent interface (or set of interfaces) that exposes hooks like onClose(lifecycleContext): Disposable, onDelete(lifecycleContext): Disposable, etc.
- Destination types (TerminalDestination, TextEditorDestination) implement the hooks relevant to their lifecycle. Terminal implements
onClose. TextEditor implements onClose and can optionally implement onDelete.
- BoundDestinationLifecycle iterates registered destinations and collects their hooks at bind time. When an event fires (e.g., VS Code
onDidCloseTerminal), the lifecycle module dispatches to the relevant hook implementations instead of running hard-coded per-type listeners.
- The
lifecycleContext parameter provides each hook with the unbind function, logger, and VS Code adapter so hooks can perform their cleanup without importing PasteDestinationManager directly.
Relevant Code
- Terminal close listener: PasteDestinationManager.ts lines 382-404 — checks
isTerminalDestination, compares terminal identity, calls unbind
- Document close listener: PasteDestinationManager.ts lines 658-697 — checks
isEditorDestination, compares URI, handles isClosed=false for language-mode transitions, calls unbind
- Both follow the same structural pattern: guard → compare identity → handle edge case → log → unbind → status bar message
Backward Compatibility
All existing behavior must be preserved:
- Terminal auto-unbind on terminal close (silent unbind + status bar message)
- Text editor auto-unbind on document close (with isClosed=false language-mode transition guard)
- Lazy unbind strategy (unbind only on actual close, not tab switch)
- Status bar messages (STATUS_BAR_DESTINATION_UNBOUND_TERMINAL_CLOSED, STATUS_BAR_DESTINATION_UNBOUND_EDITOR_CLOSED)
Tasks
- Design and define
LifecycleEvent interface(s) with hook signatures
- Implement hooks on TerminalDestination and TextEditorDestination
- Refactor BoundDestinationLifecycle to collect hooks at bind time and dispatch events
- Remove the two private listener setup methods after migration
- Add unit tests for: hook registration, event dispatch routing, edge cases (language-mode transition, unmatched terminal/editor), and the contract that adding a new destination type with hooks works without modifying BoundaryDestinationLifecycle
- Verify backward compatibility via existing integration tests
Dependencies
Depends on: BoundDestinationLifecycle extraction (#610). This redesign should not begin until that extraction is merged, as it builds on the extracted module.
Non-goals
- No new lifecycle events beyond what currently exists (close is the only one); the interface should be extensible but this issue only refactors existing events
- No changes to PasteDestinationManager's bind/unbind flow
- No changes to status bar messages or logging format
Summary
Redesign lifecycle event registration in BoundDestinationLifecycle from hard-coded per-type listener methods to a
LifecycleEventinterface / hook-based abstraction that destination types implement. Currently each lifecycle event (terminal close, document close) is wired as a separate private method with destination-type branching. A genericLifecycleEventinterface with hooks (e.g.,onClose,onDelete) that destination types implement would make adding new destination types and new lifecycle events trivial without modifying the lifecycle module itself.Motivation
The initial BoundDestinationLifecycle extraction (see separate issue) is a pure extraction — moving existing listener logic out of PasteDestinationManager without changing behavior. The current design has each listener as a separate method (
setupTerminalCloseListener,setupDocumentCloseListener) with switch-on-type logic inside. Adding a new destination type (e.g., file-picker output channel) or a new lifecycle event (e.g., file deletion) requires adding a new method and new branching. A hook-based design means you just implement the relevant interface on the destination class.Proposed Design
LifecycleEventinterface (or set of interfaces) that exposes hooks likeonClose(lifecycleContext): Disposable,onDelete(lifecycleContext): Disposable, etc.onClose. TextEditor implementsonCloseand can optionally implementonDelete.onDidCloseTerminal), the lifecycle module dispatches to the relevant hook implementations instead of running hard-coded per-type listeners.lifecycleContextparameter provides each hook with the unbind function, logger, and VS Code adapter so hooks can perform their cleanup without importing PasteDestinationManager directly.Relevant Code
isTerminalDestination, compares terminal identity, calls unbindisEditorDestination, compares URI, handlesisClosed=falsefor language-mode transitions, calls unbindBackward Compatibility
All existing behavior must be preserved:
Tasks
LifecycleEventinterface(s) with hook signaturesDependencies
Depends on: BoundDestinationLifecycle extraction (#610). This redesign should not begin until that extraction is merged, as it builds on the extracted module.
Non-goals