mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
prospective file adding
This commit is contained in:
parent
9756ab6d16
commit
e4fd6d05a4
4 changed files with 213 additions and 107 deletions
|
|
@ -6,7 +6,7 @@
|
||||||
import React, { ButtonHTMLAttributes, FormEvent, FormHTMLAttributes, Fragment, useCallback, useEffect, useRef, useState } from 'react';
|
import React, { ButtonHTMLAttributes, FormEvent, FormHTMLAttributes, Fragment, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
|
||||||
import { useAccessor, useSidebarState, useChatThreadsState, useChatThreadsStreamState } from '../util/services.js';
|
import { useAccessor, useSidebarState, useChatThreadsState, useChatThreadsStreamState, useUriState } from '../util/services.js';
|
||||||
import { ChatMessage, CodeSelection, CodeStagingSelection } from '../../../chatThreadService.js';
|
import { ChatMessage, CodeSelection, CodeStagingSelection } from '../../../chatThreadService.js';
|
||||||
|
|
||||||
import { BlockCode } from '../markdown/BlockCode.js';
|
import { BlockCode } from '../markdown/BlockCode.js';
|
||||||
|
|
@ -259,9 +259,9 @@ const getBasename = (pathStr: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SelectedFiles = (
|
export const SelectedFiles = (
|
||||||
{ type, selections, setStaging }:
|
{ type, selections, setSelections }:
|
||||||
| { type: 'past', selections: CodeSelection[] | null; setStaging?: undefined }
|
| { type: 'past', selections: CodeSelection[]; setSelections?: undefined }
|
||||||
| { type: 'staging', selections: CodeStagingSelection[] | null; setStaging: ((files: CodeStagingSelection[]) => void) }
|
| { type: 'staging', selections: CodeStagingSelection[]; setSelections: ((newSelections: CodeStagingSelection[]) => void) }
|
||||||
) => {
|
) => {
|
||||||
|
|
||||||
// index -> isOpened
|
// index -> isOpened
|
||||||
|
|
@ -273,85 +273,104 @@ export const SelectedFiles = (
|
||||||
const accessor = useAccessor()
|
const accessor = useAccessor()
|
||||||
const commandService = accessor.get('ICommandService')
|
const commandService = accessor.get('ICommandService')
|
||||||
|
|
||||||
|
const { currentUri } = useUriState()
|
||||||
|
|
||||||
|
let prospectiveSelections: CodeStagingSelection[] = []
|
||||||
|
if ( // add a prospective file if type === 'staging' and if the user is in a file, and if the file is not selected yet
|
||||||
|
type === 'staging'
|
||||||
|
&& currentUri
|
||||||
|
&& !selections.find(s => s.range === null && s.fileURI.fsPath === currentUri.fsPath)
|
||||||
|
) {
|
||||||
|
prospectiveSelections = [{
|
||||||
|
type: 'File',
|
||||||
|
fileURI: currentUri,
|
||||||
|
selectionStr: null,
|
||||||
|
range: null,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
const allSelections = [...selections, ...prospectiveSelections]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
!!selections && selections.length !== 0 && (
|
<div className='flex items-center flex-wrap gap-0.5 text-left relative'>
|
||||||
<div
|
|
||||||
className='flex items-center flex-wrap gap-0.5 text-left relative'
|
|
||||||
>
|
|
||||||
{selections.map((selection, i) => {
|
|
||||||
|
|
||||||
const isThisSelectionOpened = !!(selection.selectionStr && selectionIsOpened[i])
|
{allSelections.map((selection, i) => {
|
||||||
const isThisSelectionAFile = selection.selectionStr === null
|
|
||||||
|
|
||||||
return <div key={i} // container for `selectionSummary` and `selectionText`
|
const isThisSelectionOpened = !!(selection.selectionStr && selectionIsOpened[i])
|
||||||
className={`${isThisSelectionOpened ? 'w-full' : ''}`}
|
const isThisSelectionAFile = selection.selectionStr === null
|
||||||
|
const isThisSelectionProspective = i > selections.length - 1
|
||||||
|
|
||||||
|
return <div key={i} // container for `selectionSummary` and `selectionText`
|
||||||
|
className={`${isThisSelectionOpened ? 'w-full' : ''}`}
|
||||||
|
>
|
||||||
|
{/* selection summary */}
|
||||||
|
<div // container for delete button
|
||||||
|
className='flex items-center gap-0.5'
|
||||||
>
|
>
|
||||||
{/* selection summary */}
|
<div // styled summary box
|
||||||
<div // container for delete button
|
className={`flex items-center gap-0.5 relative
|
||||||
className='flex items-center gap-0.5'
|
|
||||||
>
|
|
||||||
<div // styled summary box
|
|
||||||
className={`flex items-center gap-0.5 relative
|
|
||||||
rounded-md px-1
|
rounded-md px-1
|
||||||
w-fit h-fit
|
w-fit h-fit
|
||||||
select-none
|
select-none
|
||||||
bg-void-bg-3 hover:brightness-95
|
${isThisSelectionProspective ? 'bg-void-1' : 'bg-void-bg-3 hover:brightness-95'}
|
||||||
text-void-fg-1 text-xs text-nowrap
|
text-void-fg-1 text-xs text-nowrap
|
||||||
border rounded-xs ${isClearHovered ? 'border-void-border-1' : 'border-void-border-2'} hover:border-void-border-1
|
border rounded-xs ${isClearHovered ? 'border-void-border-1' : 'border-void-border-2'} hover:border-void-border-1
|
||||||
transition-all duration-150`}
|
transition-all duration-150`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// open the file if it is a file
|
if (isThisSelectionProspective) { // add prospective selection to selections
|
||||||
if (isThisSelectionAFile) {
|
if (type !== 'staging') return; // (never)
|
||||||
commandService.executeCommand('vscode.open', selection.fileURI, {
|
setSelections([...selections, selection as CodeStagingSelection])
|
||||||
preview: true,
|
|
||||||
// preserveFocus: false,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// open the selection if it is a text-selection
|
|
||||||
setSelectionIsOpened(s => {
|
|
||||||
const newS = [...s]
|
|
||||||
newS[i] = !newS[i]
|
|
||||||
return newS
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
{/* file name */}
|
|
||||||
{getBasename(selection.fileURI.fsPath)}
|
|
||||||
{/* selection range */}
|
|
||||||
{!isThisSelectionAFile ? ` (${selection.range.startLineNumber}-${selection.range.endLineNumber})` : ''}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{/* X button */}
|
} else if (isThisSelectionAFile) { // open files
|
||||||
{type === 'staging' &&
|
commandService.executeCommand('vscode.open', selection.fileURI, {
|
||||||
<span
|
preview: true,
|
||||||
className='cursor-pointer hover:brightness-95 rounded-md z-1'
|
// preserveFocus: false,
|
||||||
onClick={(e) => {
|
});
|
||||||
e.stopPropagation(); // don't open/close selection
|
} else { // show text
|
||||||
if (type !== 'staging') return;
|
setSelectionIsOpened(s => {
|
||||||
setStaging([...selections.slice(0, i), ...selections.slice(i + 1)])
|
const newS = [...s]
|
||||||
setSelectionIsOpened(o => [...o.slice(0, i), ...o.slice(i + 1)])
|
newS[i] = !newS[i]
|
||||||
}}
|
return newS
|
||||||
>
|
});
|
||||||
<IconX size={16} className="p-[2px] stroke-[3]" />
|
}
|
||||||
</span>}
|
}}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{/* file name */}
|
||||||
|
{getBasename(selection.fileURI.fsPath)}
|
||||||
|
{/* selection range */}
|
||||||
|
{!isThisSelectionAFile ? ` (${selection.range.startLineNumber}-${selection.range.endLineNumber})` : ''}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{/* X button */}
|
||||||
|
{type === 'staging' &&
|
||||||
|
<span
|
||||||
|
className='cursor-pointer hover:brightness-95 rounded-md z-1'
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation(); // don't open/close selection
|
||||||
|
if (type !== 'staging') return;
|
||||||
|
setSelections([...selections.slice(0, i), ...selections.slice(i + 1)])
|
||||||
|
setSelectionIsOpened(o => [...o.slice(0, i), ...o.slice(i + 1)])
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconX size={16} className="p-[2px] stroke-[3]" />
|
||||||
|
</span>}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* clear all selections button */}
|
{/* clear all selections button */}
|
||||||
{type !== 'staging' || selections.length === 0 || i !== selections.length - 1
|
{type !== 'staging' || allSelections.length === 0 || i !== allSelections.length - 1
|
||||||
? null
|
? null
|
||||||
: <div key={i} className={`flex items-center gap-0.5 ${isThisSelectionOpened ? 'w-full' : ''}`}>
|
: <div key={i} className={`flex items-center gap-0.5 ${isThisSelectionOpened ? 'w-full' : ''}`}>
|
||||||
<div
|
<div
|
||||||
className='rounded-md'
|
className='rounded-md'
|
||||||
onMouseEnter={() => setIsClearHovered(true)}
|
onMouseEnter={() => setIsClearHovered(true)}
|
||||||
onMouseLeave={() => setIsClearHovered(false)}
|
onMouseLeave={() => setIsClearHovered(false)}
|
||||||
>
|
>
|
||||||
<Delete
|
<Delete
|
||||||
size={16}
|
size={16}
|
||||||
className={`stroke-[1]
|
className={`stroke-[1]
|
||||||
stroke-void-fg-1
|
stroke-void-fg-1
|
||||||
fill-void-bg-3
|
fill-void-bg-3
|
||||||
opacity-40
|
opacity-40
|
||||||
|
|
@ -359,35 +378,35 @@ export const SelectedFiles = (
|
||||||
transition-all duration-150
|
transition-all duration-150
|
||||||
cursor-pointer
|
cursor-pointer
|
||||||
`}
|
`}
|
||||||
onClick={() => { setStaging([]) }}
|
onClick={() => { setSelections([]) }}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
</div>
|
|
||||||
{/* selection text */}
|
|
||||||
{isThisSelectionOpened &&
|
|
||||||
<div
|
|
||||||
className='w-full px-1 rounded-sm border-vscode-editor-border'
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation(); // don't focus input box
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<BlockCode
|
|
||||||
initValue={selection.selectionStr!}
|
|
||||||
language={filenameToVscodeLanguage(selection.fileURI.path)}
|
|
||||||
maxHeight={200}
|
|
||||||
showScrollbars={true}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
{/* selection text */}
|
||||||
|
{isThisSelectionOpened &&
|
||||||
|
<div
|
||||||
|
className='w-full px-1 rounded-sm border-vscode-editor-border'
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation(); // don't focus input box
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<BlockCode
|
||||||
|
initValue={selection.selectionStr!}
|
||||||
|
language={filenameToVscodeLanguage(selection.fileURI.path)}
|
||||||
|
maxHeight={200}
|
||||||
|
showScrollbars={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -407,7 +426,7 @@ const ChatBubble = ({ chatMessage, isLoading }: {
|
||||||
|
|
||||||
if (role === 'user') {
|
if (role === 'user') {
|
||||||
chatbubbleContents = <>
|
chatbubbleContents = <>
|
||||||
<SelectedFiles type='past' selections={chatMessage.selections} />
|
<SelectedFiles type='past' selections={chatMessage.selections || []} />
|
||||||
{chatMessage.displayContent}
|
{chatMessage.displayContent}
|
||||||
|
|
||||||
{/* {!isEditMode ? chatMessage.displayContent : <></>} */}
|
{/* {!isEditMode ? chatMessage.displayContent : <></>} */}
|
||||||
|
|
@ -604,9 +623,8 @@ export const SidebarChat = () => {
|
||||||
{/* top row */}
|
{/* top row */}
|
||||||
<>
|
<>
|
||||||
{/* selections */}
|
{/* selections */}
|
||||||
{(selections && selections.length !== 0) &&
|
<SelectedFiles type='staging' selections={selections || []} setSelections={chatThreadsService.setStaging.bind(chatThreadsService)} />
|
||||||
<SelectedFiles type='staging' selections={selections} setStaging={chatThreadsService.setStaging.bind(chatThreadsService)} />
|
|
||||||
}
|
|
||||||
|
|
||||||
{/* error message */}
|
{/* error message */}
|
||||||
{latestError === undefined ? null :
|
{latestError === undefined ? null :
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { IDisposable } from '../../../../../../../base/common/lifecycle.js'
|
||||||
import { VoidSidebarState } from '../../../sidebarStateService.js'
|
import { VoidSidebarState } from '../../../sidebarStateService.js'
|
||||||
import { VoidSettingsState } from '../../../../../../../platform/void/common/voidSettingsService.js'
|
import { VoidSettingsState } from '../../../../../../../platform/void/common/voidSettingsService.js'
|
||||||
import { ColorScheme } from '../../../../../../../platform/theme/common/theme.js'
|
import { ColorScheme } from '../../../../../../../platform/theme/common/theme.js'
|
||||||
|
import { VoidUriState } from '../../../voidUriStateService.js';
|
||||||
import { VoidQuickEditState } from '../../../quickEditStateService.js'
|
import { VoidQuickEditState } from '../../../quickEditStateService.js'
|
||||||
import { RefreshModelStateOfProvider } from '../../../../../../../platform/void/common/refreshModelService.js'
|
import { RefreshModelStateOfProvider } from '../../../../../../../platform/void/common/refreshModelService.js'
|
||||||
|
|
||||||
|
|
@ -28,6 +29,7 @@ import { ILLMMessageService } from '../../../../../../../platform/void/common/ll
|
||||||
import { IRefreshModelService } from '../../../../../../../platform/void/common/refreshModelService.js';
|
import { IRefreshModelService } from '../../../../../../../platform/void/common/refreshModelService.js';
|
||||||
import { IVoidSettingsService } from '../../../../../../../platform/void/common/voidSettingsService.js';
|
import { IVoidSettingsService } from '../../../../../../../platform/void/common/voidSettingsService.js';
|
||||||
import { IInlineDiffsService } from '../../../inlineDiffsService.js';
|
import { IInlineDiffsService } from '../../../inlineDiffsService.js';
|
||||||
|
import { IVoidUriStateService } from '../../../voidUriStateService.js';
|
||||||
import { IQuickEditStateService } from '../../../quickEditStateService.js';
|
import { IQuickEditStateService } from '../../../quickEditStateService.js';
|
||||||
import { ISidebarStateService } from '../../../sidebarStateService.js';
|
import { ISidebarStateService } from '../../../sidebarStateService.js';
|
||||||
import { IChatThreadService } from '../../../chatThreadService.js';
|
import { IChatThreadService } from '../../../chatThreadService.js';
|
||||||
|
|
@ -47,10 +49,14 @@ import { IPathService } from '../../../../../../../workbench/services/path/commo
|
||||||
import { IMetricsService } from '../../../../../../../platform/void/common/metricsService.js'
|
import { IMetricsService } from '../../../../../../../platform/void/common/metricsService.js'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// normally to do this you'd use a useEffect that calls .onDidChangeState(), but useEffect mounts too late and misses initial state changes
|
// normally to do this you'd use a useEffect that calls .onDidChangeState(), but useEffect mounts too late and misses initial state changes
|
||||||
|
|
||||||
// even if React hasn't mounted yet, the variables are always updated to the latest state.
|
// even if React hasn't mounted yet, the variables are always updated to the latest state.
|
||||||
// React listens by adding a setState function to these listeners.
|
// React listens by adding a setState function to these listeners.
|
||||||
|
let uriState: VoidUriState
|
||||||
|
const uriStateListeners: Set<(s: VoidUriState) => void> = new Set()
|
||||||
|
|
||||||
let quickEditState: VoidQuickEditState
|
let quickEditState: VoidQuickEditState
|
||||||
const quickEditStateListeners: Set<(s: VoidQuickEditState) => void> = new Set()
|
const quickEditStateListeners: Set<(s: VoidQuickEditState) => void> = new Set()
|
||||||
|
|
||||||
|
|
@ -90,6 +96,7 @@ export const _registerServices = (accessor: ServicesAccessor) => {
|
||||||
_registerAccessor(accessor)
|
_registerAccessor(accessor)
|
||||||
|
|
||||||
const stateServices = {
|
const stateServices = {
|
||||||
|
uriStateService: accessor.get(IVoidUriStateService),
|
||||||
quickEditStateService: accessor.get(IQuickEditStateService),
|
quickEditStateService: accessor.get(IQuickEditStateService),
|
||||||
sidebarStateService: accessor.get(ISidebarStateService),
|
sidebarStateService: accessor.get(ISidebarStateService),
|
||||||
chatThreadsStateService: accessor.get(IChatThreadService),
|
chatThreadsStateService: accessor.get(IChatThreadService),
|
||||||
|
|
@ -99,7 +106,15 @@ export const _registerServices = (accessor: ServicesAccessor) => {
|
||||||
inlineDiffsService: accessor.get(IInlineDiffsService),
|
inlineDiffsService: accessor.get(IInlineDiffsService),
|
||||||
}
|
}
|
||||||
|
|
||||||
const { sidebarStateService, quickEditStateService, settingsStateService, chatThreadsStateService, refreshModelService, themeService, inlineDiffsService } = stateServices
|
const { uriStateService, sidebarStateService, quickEditStateService, settingsStateService, chatThreadsStateService, refreshModelService, themeService, inlineDiffsService } = stateServices
|
||||||
|
|
||||||
|
uriState = uriStateService.state
|
||||||
|
disposables.push(
|
||||||
|
uriStateService.onDidChangeState(() => {
|
||||||
|
uriState = uriStateService.state
|
||||||
|
uriStateListeners.forEach(l => l(uriState))
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
quickEditState = quickEditStateService.state
|
quickEditState = quickEditStateService.state
|
||||||
disposables.push(
|
disposables.push(
|
||||||
|
|
@ -178,6 +193,7 @@ const getReactAccessor = (accessor: ServicesAccessor) => {
|
||||||
IRefreshModelService: accessor.get(IRefreshModelService),
|
IRefreshModelService: accessor.get(IRefreshModelService),
|
||||||
IVoidSettingsService: accessor.get(IVoidSettingsService),
|
IVoidSettingsService: accessor.get(IVoidSettingsService),
|
||||||
IInlineDiffsService: accessor.get(IInlineDiffsService),
|
IInlineDiffsService: accessor.get(IInlineDiffsService),
|
||||||
|
IVoidUriStateService: accessor.get(IVoidUriStateService),
|
||||||
IQuickEditStateService: accessor.get(IQuickEditStateService),
|
IQuickEditStateService: accessor.get(IQuickEditStateService),
|
||||||
ISidebarStateService: accessor.get(ISidebarStateService),
|
ISidebarStateService: accessor.get(ISidebarStateService),
|
||||||
IChatThreadService: accessor.get(IChatThreadService),
|
IChatThreadService: accessor.get(IChatThreadService),
|
||||||
|
|
@ -224,6 +240,16 @@ export const useAccessor = () => {
|
||||||
|
|
||||||
// -- state of services --
|
// -- state of services --
|
||||||
|
|
||||||
|
export const useUriState = () => {
|
||||||
|
const [s, ss] = useState(uriState)
|
||||||
|
useEffect(() => {
|
||||||
|
ss(uriState)
|
||||||
|
uriStateListeners.add(ss)
|
||||||
|
return () => { uriStateListeners.delete(ss) }
|
||||||
|
}, [ss])
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
export const useQuickEditState = () => {
|
export const useQuickEditState = () => {
|
||||||
const [s, ss] = useState(quickEditState)
|
const [s, ss] = useState(quickEditState)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,9 @@ import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase
|
||||||
import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';
|
import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';
|
||||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||||
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
|
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
|
||||||
import { URI } from '../../../../base/common/uri.js';
|
|
||||||
import { localize2 } from '../../../../nls.js';
|
import { localize2 } from '../../../../nls.js';
|
||||||
import { IViewsService } from '../../../services/views/common/viewsService.js';
|
import { IViewsService } from '../../../services/views/common/viewsService.js';
|
||||||
|
import { IVoidUriStateService } from './voidUriStateService.js';
|
||||||
|
|
||||||
// ---------- Register commands and keybindings ----------
|
// ---------- Register commands and keybindings ----------
|
||||||
|
|
||||||
|
|
@ -241,7 +240,7 @@ registerAction2(class extends Action2 {
|
||||||
export class TabSwitchListener extends Disposable {
|
export class TabSwitchListener extends Disposable {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
onSwitchTab: (uri: URI) => void,
|
onSwitchTab: () => void,
|
||||||
@ICodeEditorService private readonly _editorService: ICodeEditorService,
|
@ICodeEditorService private readonly _editorService: ICodeEditorService,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
@ -250,7 +249,7 @@ export class TabSwitchListener extends Disposable {
|
||||||
const addTabSwitchListeners = (editor: ICodeEditor) => {
|
const addTabSwitchListeners = (editor: ICodeEditor) => {
|
||||||
this._register(editor.onDidChangeModel(e => {
|
this._register(editor.onDidChangeModel(e => {
|
||||||
if (e.newModelUrl?.scheme !== 'file') return
|
if (e.newModelUrl?.scheme !== 'file') return
|
||||||
onSwitchTab(e.newModelUrl)
|
onSwitchTab()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,8 +269,10 @@ class TabSwitchContribution extends Disposable implements IWorkbenchContribution
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||||
@ICommandService private readonly commandService: ICommandService,
|
|
||||||
@IViewsService private readonly viewsService: IViewsService,
|
@IViewsService private readonly viewsService: IViewsService,
|
||||||
|
@IVoidUriStateService private readonly uriStateService: IVoidUriStateService,
|
||||||
|
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
|
||||||
|
// @ICommandService private readonly commandService: ICommandService,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
|
@ -281,18 +282,22 @@ class TabSwitchContribution extends Disposable implements IWorkbenchContribution
|
||||||
sidebarIsVisible = e.visible
|
sidebarIsVisible = e.visible
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const addCurrentFileIfVisible = () => {
|
const onSwitchTab = () => { // update state
|
||||||
if (sidebarIsVisible)
|
if (sidebarIsVisible) {
|
||||||
this.commandService.executeCommand(VOID_ADD_SELECTION_TO_SIDEBAR_ACTION_ID)
|
const currentUri = this.codeEditorService.getActiveCodeEditor()?.getModel()?.uri
|
||||||
|
if (!currentUri) return;
|
||||||
|
this.uriStateService.setState({ currentUri })
|
||||||
|
// this.commandService.executeCommand(VOID_ADD_SELECTION_TO_SIDEBAR_ACTION_ID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// when sidebar becomes visible, add current file
|
// when sidebar becomes visible, add current file
|
||||||
this._register(this.viewsService.onDidChangeViewVisibility(e => { sidebarIsVisible = e.visible }))
|
this._register(this.viewsService.onDidChangeViewVisibility(e => { sidebarIsVisible = e.visible }))
|
||||||
|
|
||||||
// run on current tab if it exists, and listen for tab switches and visibility changes
|
// run on current tab if it exists, and listen for tab switches and visibility changes
|
||||||
addCurrentFileIfVisible()
|
onSwitchTab()
|
||||||
this._register(this.viewsService.onDidChangeViewVisibility(() => { addCurrentFileIfVisible() }))
|
this._register(this.viewsService.onDidChangeViewVisibility(() => { onSwitchTab() }))
|
||||||
this._register(this.instantiationService.createInstance(TabSwitchListener, () => { addCurrentFileIfVisible() }))
|
this._register(this.instantiationService.createInstance(TabSwitchListener, () => { onSwitchTab() }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
57
src/vs/workbench/contrib/void/browser/voidUriStateService.ts
Normal file
57
src/vs/workbench/contrib/void/browser/voidUriStateService.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*--------------------------------------------------------------------------------------
|
||||||
|
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
|
||||||
|
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||||
|
*--------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { Emitter, Event } from '../../../../base/common/event.js';
|
||||||
|
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||||
|
import { URI } from '../../../../base/common/uri.js';
|
||||||
|
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
|
||||||
|
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// service that manages state
|
||||||
|
export type VoidUriState = {
|
||||||
|
currentUri?: URI
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVoidUriStateService {
|
||||||
|
readonly _serviceBrand: undefined;
|
||||||
|
|
||||||
|
readonly state: VoidUriState; // readonly to the user
|
||||||
|
setState(newState: Partial<VoidUriState>): void;
|
||||||
|
onDidChangeState: Event<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IVoidUriStateService = createDecorator<IVoidUriStateService>('voidUriStateService');
|
||||||
|
class VoidUriStateService extends Disposable implements IVoidUriStateService {
|
||||||
|
_serviceBrand: undefined;
|
||||||
|
|
||||||
|
static readonly ID = 'voidUriStateService';
|
||||||
|
|
||||||
|
private readonly _onDidChangeState = new Emitter<void>();
|
||||||
|
readonly onDidChangeState: Event<void> = this._onDidChangeState.event;
|
||||||
|
|
||||||
|
|
||||||
|
// state
|
||||||
|
state: VoidUriState
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
// initial state
|
||||||
|
this.state = { currentUri: undefined }
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(newState: Partial<VoidUriState>) {
|
||||||
|
|
||||||
|
this.state = { ...this.state, ...newState }
|
||||||
|
this._onDidChangeState.fire()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
registerSingleton(IVoidUriStateService, VoidUriStateService, InstantiationType.Eager);
|
||||||
Loading…
Reference in a new issue