Summary
PasteDestinationManager currently owns VS Code context key management directly — it calls this.vscodeAdapter.executeCommand('setContext', CONTEXT_IS_BOUND, true/false) at three points (constructor init line 103, after bind in setBoundDestination line 628, and on clear in clearBoundDestination line 639). This is a SOLID violation. Context key lifecycle is a distinct concern that should be encapsulated in its own module. Separating it also enables the existing wireActiveTerminalBindabilityContext function to be absorbed into the same service, consolidating all when-expression context key management into one place. A unit test file already exists at src/tests/destinations/ContextKeyService.test.ts awaiting the implementation.
Changes
1. Create src/destinations/ContextKeyService.ts
Implement a ContextKeyService class that owns all context key management for the extension's when-expression system. Its constructor accepts VscodeAdapter and the PasteDestinationManager (for reading bound destination state and subscribing to onDidChangeBoundDestination). On construction it evaluates and sets all three context keys: rangelink.isBound (from PasteDestinationManager.getBoundDestination()), rangelink.isActiveTerminalBindable (from VscodeAdapter.activeTerminal via classifyTerminalForBinding), and rangelink.isActiveTerminalPasteDestination (true when bound destination matches active terminal). It subscribes to VscodeAdapter terminal lifecycle events (onDidOpenTerminal, onDidCloseTerminal, onDidChangeActiveTerminal) and PasteDestinationManager.onDidChangeBoundDestination to re-evaluate all three keys on change. It implements vscode.Disposable to clean up all subscriptions. The service does not take a Logger dependency — it delegates logging to the calling subscriber. Add a CONTEXT_IS_ACTIVE_TERMINAL_PASTE_DESTINATION constant to src/constants/contextKeys.ts for the rangelink.isActiveTerminalPasteDestination key.
2. Remove setContext calls from PasteDestinationManager
Delete the three executeCommand('setContext', CONTEXT_IS_BOUND, ...) calls at lines 103, 628, and 639 of PasteDestinationManager.ts. Remove the CONTEXT_IS_BOUND import from PasteDestinationManager. PasteDestinationManager no longer owns context key state — it only manages the bound destination reference and fires onDidChangeBoundDestination events.
3. Replace wireActiveTerminalBindabilityContext with ContextKeyService in wireSubscriptions.ts
In src/wireSubscriptions.ts, replace the wireActiveTerminalBindabilityContext(ideAdapter, logger) call (line 115) with new ContextKeyService(ideAdapter, destinationManager) and pushDisposable the service. Remove the wireActiveTerminalBindabilityContext import. This function was already consumed exclusively via wireSubscriptions, so there are no other call sites to update. The wireActiveTerminalBindabilityContext module and its test file can be deleted after removal, provided all context key behaviors are covered by the ContextKeyService tests.
4. Update unit tests
The existing ContextKeyService.test.ts already covers the full expected interface. Update PasteDestinationManager.test.ts to remove assertions that PasteDestinationManager calls setContext (it no longer does). The existing wireActiveTerminalBindabilityContext.test.ts will be deleted when that module is removed. Integration tests (contextMenuTerminal.test.ts, contextMenuEditorTab.test.ts, contextMenuExplorer.test.ts, contextMenuEditorContent.test.ts, unbind.test.ts) that assert setContext logged behavior via assertSetContextLogged and assertNoSetContextLogged should continue to pass because the setContext calls still happen — they are just driven by ContextKeyService reacting to onDidChangeBoundDestination events instead of by PasteDestinationManager directly.
5. Update integration test helpers if needed
The logBasedUiAssertions.ts helper provides assertSetContextLogged and assertNoSetContextLogged. These match setContext calls in captured log output. Since the setContext calls still fire (just from a different origin), the log-based assertions should still work. Verify this holds during testing — no changes expected unless the log message format changes.
6. Delete obsolete files after migration
Delete src/destinations/wireActiveTerminalBindabilityContext.ts (its functionality is absorbed into ContextKeyService) and its test file src/tests/destinations/wireActiveTerminalBindabilityContext.test.ts. Remove the export from src/destinations/index.ts. Do not remove the wireSubscriptions.test.ts reference yet — it will be updated in step 3 to import ContextKeyService instead.
Acceptance Criteria
- ContextKeyService.ts is created and passes the existing ContextKeyService.test.ts unit tests (all 9 tests: constructor with isBound=true/false, isActiveTerminalBindable=true/false for no-terminal/shell/pty, isActiveTerminalPasteDestination=true/false, subscription registration, re-evaluation on active-terminal change, re-evaluation on bound-destination change, dispose cleanup).
- PasteDestinationManager.ts no longer imports CONTEXT_IS_BOUND or calls executeCommand('setContext', ...).
- wireSubscriptions.ts wires ContextKeyService instead of wireActiveTerminalBindabilityContext, and the wireSubscriptions.test.ts mock is updated accordingly.
- wireActiveTerminalBindabilityContext.ts and its test file are deleted.
- All existing unit tests pass (pnpm test).
- All existing integration tests pass (pnpm test:release:automated), specifically the setContext log assertions in contextMenuTerminal, contextMenuEditorTab, contextMenuExplorer, contextMenuEditorContent, and unbind test suites.
- A CONTEXT_IS_ACTIVE_TERMINAL_PASTE_DESTINATION constant is added to src/constants/contextKeys.ts.
- The extraction does not change any user-visible behavior — context keys are set with the same values at the same logical times (construction, bind, unbind, terminal lifecycle events).
Labels
enhancement
Dependencies
none (extraction from current code)
Blocked by
nothing
Blocks
BoundDestinationLifecycle extraction (touches the same file PasteDestinationManager.ts, so the smaller ContextKeyService extraction should land first to avoid merge conflicts)
Summary
PasteDestinationManager currently owns VS Code context key management directly — it calls
this.vscodeAdapter.executeCommand('setContext', CONTEXT_IS_BOUND, true/false)at three points (constructor init line 103, after bind in setBoundDestination line 628, and on clear in clearBoundDestination line 639). This is a SOLID violation. Context key lifecycle is a distinct concern that should be encapsulated in its own module. Separating it also enables the existing wireActiveTerminalBindabilityContext function to be absorbed into the same service, consolidating all when-expression context key management into one place. A unit test file already exists at src/tests/destinations/ContextKeyService.test.ts awaiting the implementation.Changes
1. Create src/destinations/ContextKeyService.ts
Implement a ContextKeyService class that owns all context key management for the extension's when-expression system. Its constructor accepts VscodeAdapter and the PasteDestinationManager (for reading bound destination state and subscribing to onDidChangeBoundDestination). On construction it evaluates and sets all three context keys: rangelink.isBound (from PasteDestinationManager.getBoundDestination()), rangelink.isActiveTerminalBindable (from VscodeAdapter.activeTerminal via classifyTerminalForBinding), and rangelink.isActiveTerminalPasteDestination (true when bound destination matches active terminal). It subscribes to VscodeAdapter terminal lifecycle events (onDidOpenTerminal, onDidCloseTerminal, onDidChangeActiveTerminal) and PasteDestinationManager.onDidChangeBoundDestination to re-evaluate all three keys on change. It implements vscode.Disposable to clean up all subscriptions. The service does not take a Logger dependency — it delegates logging to the calling subscriber. Add a CONTEXT_IS_ACTIVE_TERMINAL_PASTE_DESTINATION constant to src/constants/contextKeys.ts for the rangelink.isActiveTerminalPasteDestination key.
2. Remove setContext calls from PasteDestinationManager
Delete the three executeCommand('setContext', CONTEXT_IS_BOUND, ...) calls at lines 103, 628, and 639 of PasteDestinationManager.ts. Remove the CONTEXT_IS_BOUND import from PasteDestinationManager. PasteDestinationManager no longer owns context key state — it only manages the bound destination reference and fires onDidChangeBoundDestination events.
3. Replace wireActiveTerminalBindabilityContext with ContextKeyService in wireSubscriptions.ts
In src/wireSubscriptions.ts, replace the wireActiveTerminalBindabilityContext(ideAdapter, logger) call (line 115) with new ContextKeyService(ideAdapter, destinationManager) and pushDisposable the service. Remove the wireActiveTerminalBindabilityContext import. This function was already consumed exclusively via wireSubscriptions, so there are no other call sites to update. The wireActiveTerminalBindabilityContext module and its test file can be deleted after removal, provided all context key behaviors are covered by the ContextKeyService tests.
4. Update unit tests
The existing ContextKeyService.test.ts already covers the full expected interface. Update PasteDestinationManager.test.ts to remove assertions that PasteDestinationManager calls setContext (it no longer does). The existing wireActiveTerminalBindabilityContext.test.ts will be deleted when that module is removed. Integration tests (contextMenuTerminal.test.ts, contextMenuEditorTab.test.ts, contextMenuExplorer.test.ts, contextMenuEditorContent.test.ts, unbind.test.ts) that assert setContext logged behavior via assertSetContextLogged and assertNoSetContextLogged should continue to pass because the setContext calls still happen — they are just driven by ContextKeyService reacting to onDidChangeBoundDestination events instead of by PasteDestinationManager directly.
5. Update integration test helpers if needed
The logBasedUiAssertions.ts helper provides assertSetContextLogged and assertNoSetContextLogged. These match setContext calls in captured log output. Since the setContext calls still fire (just from a different origin), the log-based assertions should still work. Verify this holds during testing — no changes expected unless the log message format changes.
6. Delete obsolete files after migration
Delete src/destinations/wireActiveTerminalBindabilityContext.ts (its functionality is absorbed into ContextKeyService) and its test file src/tests/destinations/wireActiveTerminalBindabilityContext.test.ts. Remove the export from src/destinations/index.ts. Do not remove the wireSubscriptions.test.ts reference yet — it will be updated in step 3 to import ContextKeyService instead.
Acceptance Criteria
Labels
enhancement
Dependencies
none (extraction from current code)
Blocked by
nothing
Blocks
BoundDestinationLifecycle extraction (touches the same file PasteDestinationManager.ts, so the smaller ContextKeyService extraction should land first to avoid merge conflicts)