mirror of
https://github.com/voideditor/void
synced 2026-05-23 17:38:23 +00:00
prepare fast apply
This commit is contained in:
parent
e4694256eb
commit
d698e6f3c1
5 changed files with 365 additions and 23 deletions
|
|
@ -25,7 +25,7 @@ import * as dom from '../../../../base/browser/dom.js';
|
|||
import { Widget } from '../../../../base/browser/ui/widget.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { IConsistentEditorItemService, IConsistentItemService } from './helperServices/consistentItemService.js';
|
||||
import { voidPrefixAndSuffix, ctrlKStream_userMessage, ctrlKStream_systemMessage, fastApply_userMessage, fastApply_systemMessage, defaultFimTags } from './prompt/prompts.js';
|
||||
import { voidPrefixAndSuffix, ctrlKStream_userMessage, ctrlKStream_systemMessage, fastApply_rewritewholething_userMessage, fastApply_rewritewholething_systemMessage, defaultFimTags, fastApply_searchreplace_systemMessage, fastApply_searchreplace_userMessage } from './prompt/prompts.js';
|
||||
import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js';
|
||||
|
||||
import { mountCtrlK } from '../browser/react/out/quick-edit-tsx/index.js'
|
||||
|
|
@ -107,6 +107,7 @@ export type StartApplyingOpts = {
|
|||
} | {
|
||||
from: 'Chat';
|
||||
applyStr: string;
|
||||
applyBoxId: string;
|
||||
} | {
|
||||
from: 'Autocomplete';
|
||||
range: IRange;
|
||||
|
|
@ -1206,6 +1207,216 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
return null
|
||||
}
|
||||
|
||||
private _generateSearchAndReplaceBlocks({ filename, applyStr }: { filename: URI, applyStr: string }): DiffZone | undefined {
|
||||
|
||||
// call LLM to generate search and replace blocks (outputs something like [{search: 'this is my code', replace: 'this is m'}, ... ])
|
||||
|
||||
// 1a output search block
|
||||
|
||||
let uri: URI
|
||||
|
||||
const uri_ = this._getActiveEditorURI()
|
||||
if (!uri_) return
|
||||
uri = uri_
|
||||
|
||||
// reject all diffZones on this URI, adding to history (there can't possibly be overlap after this)
|
||||
this.removeDiffAreas({ uri, behavior: 'reject', removeCtrlKs: true })
|
||||
|
||||
// in ctrl+L the start and end lines are the full document
|
||||
|
||||
const numLines = this._getNumLines(uri)
|
||||
if (numLines === null) return
|
||||
|
||||
let startLine: number
|
||||
let endLine: number
|
||||
|
||||
startLine = 1
|
||||
endLine = numLines
|
||||
|
||||
const currentFileStr = this._readURI(uri)
|
||||
if (currentFileStr === null) return
|
||||
const originalCode = currentFileStr.split('\n').slice((startLine - 1), (endLine - 1) + 1).join('\n')
|
||||
|
||||
|
||||
|
||||
// 1b find the start and end line that the search block lives on (if can't find it, retry 1a)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
let streamRequestIdRef: { current: string | null } = { current: null }
|
||||
|
||||
|
||||
// add to history
|
||||
const { onFinishEdit } = this._addToHistory(uri)
|
||||
|
||||
// __TODO__ let users customize modelFimTags
|
||||
const isOllamaFIM = false // this._voidSettingsService.state.modelSelectionOfFeature['Ctrl+K']?.providerName === 'ollama'
|
||||
const modelFimTags = defaultFimTags
|
||||
|
||||
const adding: Omit<DiffZone, 'diffareaid'> = {
|
||||
type: 'DiffZone',
|
||||
originalCode,
|
||||
startLine,
|
||||
endLine,
|
||||
_URI: uri,
|
||||
_streamState: {
|
||||
isStreaming: true,
|
||||
streamRequestIdRef,
|
||||
line: startLine,
|
||||
},
|
||||
_diffOfId: {}, // added later
|
||||
_removeStylesFns: new Set(),
|
||||
}
|
||||
const diffZone = this._addDiffArea(adding)
|
||||
this._onDidChangeStreaming.fire({ uri, diffareaid: diffZone.diffareaid })
|
||||
this._onDidAddOrDeleteDiffZones.fire({ uri })
|
||||
|
||||
if (from === 'QuickEdit') {
|
||||
const { diffareaid } = opts
|
||||
const ctrlKZone = this.diffAreaOfId[diffareaid]
|
||||
if (ctrlKZone.type !== 'CtrlKZone') return
|
||||
|
||||
ctrlKZone._linkedStreamingDiffZone = diffZone.diffareaid
|
||||
}
|
||||
|
||||
// now handle messages
|
||||
let messages: LLMChatMessage[]
|
||||
|
||||
if (from === 'Chat') {
|
||||
const userContent = fastApply_searchreplace_userMessage({ originalCode, applyStr: opts.applyStr, uri })
|
||||
messages = [
|
||||
{ role: 'system', content: fastApply_rewritewholething_systemMessage, },
|
||||
{ role: 'user', content: userContent, }
|
||||
]
|
||||
}
|
||||
else if (from === 'QuickEdit') {
|
||||
const { diffareaid } = opts
|
||||
const ctrlKZone = this.diffAreaOfId[diffareaid]
|
||||
if (ctrlKZone.type !== 'CtrlKZone') return
|
||||
const { _mountInfo } = ctrlKZone
|
||||
const instructions = _mountInfo?.textAreaRef.current?.value ?? ''
|
||||
|
||||
// __TODO__ use Ollama's FIM api, if (isOllamaFIM) {...} else:
|
||||
const { prefix, suffix } = voidPrefixAndSuffix({ fullFileStr: currentFileStr, startLine, endLine })
|
||||
// if (isOllamaFIM) {
|
||||
// messages = {
|
||||
// type: 'ollamaFIM',
|
||||
// prefix,
|
||||
// suffix,
|
||||
// }
|
||||
|
||||
// }
|
||||
// else {
|
||||
const language = filenameToVscodeLanguage(uri.fsPath) ?? ''
|
||||
const userContent = ctrlKStream_userMessage({ selection: originalCode, instructions: instructions, prefix, suffix, isOllamaFIM: false, fimTags: modelFimTags, language })
|
||||
// type: 'messages',
|
||||
messages = [
|
||||
{ role: 'system', content: ctrlKStream_systemMessage({ fimTags: modelFimTags }), },
|
||||
{ role: 'user', content: userContent, }
|
||||
]
|
||||
// }
|
||||
}
|
||||
else { throw new Error(`featureName ${from} is invalid`) }
|
||||
|
||||
|
||||
const onDone = (hadError: boolean) => {
|
||||
diffZone._streamState = { isStreaming: false, }
|
||||
this._onDidChangeStreaming.fire({ uri, diffareaid: diffZone.diffareaid })
|
||||
|
||||
if (from === 'QuickEdit') {
|
||||
const ctrlKZone = this.diffAreaOfId[opts.diffareaid] as CtrlKZone
|
||||
|
||||
ctrlKZone._linkedStreamingDiffZone = null
|
||||
this._deleteCtrlKZone(ctrlKZone)
|
||||
}
|
||||
this._refreshStylesAndDiffsInURI(uri)
|
||||
onFinishEdit()
|
||||
|
||||
// if had error, revert!
|
||||
if (hadError) {
|
||||
this._undoHistory(diffZone._URI)
|
||||
}
|
||||
}
|
||||
|
||||
// refresh now in case onText takes a while to get 1st message
|
||||
this._refreshStylesAndDiffsInURI(uri)
|
||||
|
||||
|
||||
const extractText = (fullText: string, recentlyAddedTextLen: number) => {
|
||||
if (from === 'QuickEdit') {
|
||||
if (isOllamaFIM) return fullText
|
||||
return extractCodeFromFIM({ text: fullText, recentlyAddedTextLen, midTag: modelFimTags.midTag })
|
||||
}
|
||||
else if (from === 'Chat') {
|
||||
return extractCodeFromRegular({ text: fullText, recentlyAddedTextLen })
|
||||
}
|
||||
throw 1
|
||||
}
|
||||
|
||||
const latestStreamInfo = { line: diffZone.startLine, addedSplitYet: false, col: 1, originalCodeStartLine: 1 }
|
||||
|
||||
|
||||
// state used in onText:
|
||||
let fullText = ''
|
||||
let prevIgnoredSuffix = ''
|
||||
|
||||
streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({
|
||||
messagesType: 'chatMessages',
|
||||
useProviderFor: opts.from === 'Chat' ? 'FastApply' : 'Ctrl+K',
|
||||
logging: { loggingName: `startApplying - ${from}` },
|
||||
messages,
|
||||
onText: ({ newText: newText_ }) => {
|
||||
|
||||
const newText = prevIgnoredSuffix + newText_ // add the previously ignored suffix because it's no longer the suffix!
|
||||
fullText += prevIgnoredSuffix + newText
|
||||
|
||||
const [text, deltaText, ignoredSuffix] = extractText(fullText, newText.length)
|
||||
this._writeStreamedDiffZoneLLMText(diffZone, text, deltaText, latestStreamInfo)
|
||||
this._refreshStylesAndDiffsInURI(uri)
|
||||
|
||||
prevIgnoredSuffix = ignoredSuffix
|
||||
},
|
||||
onFinalMessage: ({ fullText }) => {
|
||||
// console.log('DONE! FULL TEXT\n', extractText(fullText), diffZone.startLine, diffZone.endLine)
|
||||
// at the end, re-write whole thing to make sure no sync errors
|
||||
const [text, _] = extractText(fullText, 0)
|
||||
this._writeText(uri, text,
|
||||
{ startLineNumber: diffZone.startLine, startColumn: 1, endLineNumber: diffZone.endLine, endColumn: Number.MAX_SAFE_INTEGER }, // 1-indexed
|
||||
{ shouldRealignDiffAreas: true }
|
||||
)
|
||||
onDone(false)
|
||||
},
|
||||
onError: (e) => {
|
||||
const details = errorDetails(e.fullError)
|
||||
this._notificationService.notify({
|
||||
severity: Severity.Warning,
|
||||
message: `Void Error: ${e.message}`,
|
||||
actions: {
|
||||
secondary: [{
|
||||
id: 'void.onerror.opensettings',
|
||||
enabled: true,
|
||||
label: 'Open Void settings',
|
||||
tooltip: '',
|
||||
class: undefined,
|
||||
run: () => { this._commandService.executeCommand(VOID_OPEN_SETTINGS_ACTION_ID) }
|
||||
}]
|
||||
},
|
||||
source: details ? `(Hold ${isMacintosh ? 'Option' : 'Alt'} to hover) - ${details}` : undefined
|
||||
})
|
||||
onDone(true)
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
return diffZone
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private _initializeStartApplying(opts: StartApplyingOpts): DiffZone | undefined {
|
||||
|
||||
|
|
@ -1290,9 +1501,9 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
let messages: LLMChatMessage[]
|
||||
|
||||
if (from === 'Chat') {
|
||||
const userContent = fastApply_userMessage({ originalCode, applyStr: opts.applyStr, uri })
|
||||
const userContent = fastApply_rewritewholething_userMessage({ originalCode, applyStr: opts.applyStr, uri })
|
||||
messages = [
|
||||
{ role: 'system', content: fastApply_systemMessage, },
|
||||
{ role: 'system', content: fastApply_rewritewholething_systemMessage, },
|
||||
{ role: 'user', content: userContent, }
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ export const chat_userMessage = async (instructions: string, selections: Staging
|
|||
|
||||
|
||||
|
||||
export const fastApply_systemMessage = `\
|
||||
export const fastApply_rewritewholething_systemMessage = `\
|
||||
You are a coding assistant that re-writes an entire file to make a change. You are given the original file \`ORIGINAL_FILE\` and a change \`CHANGE\`.
|
||||
|
||||
Directions:
|
||||
|
|
@ -195,7 +195,7 @@ Directions:
|
|||
|
||||
|
||||
|
||||
export const fastApply_userMessage = ({ originalCode, applyStr, uri }: { originalCode: string, applyStr: string, uri: URI }) => {
|
||||
export const fastApply_rewritewholething_userMessage = ({ originalCode, applyStr, uri }: { originalCode: string, applyStr: string, uri: URI }) => {
|
||||
|
||||
const language = filenameToVscodeLanguage(uri.fsPath) ?? ''
|
||||
|
||||
|
|
@ -218,6 +218,42 @@ Please finish writing the new file by applying the change to the original file.
|
|||
|
||||
|
||||
|
||||
export const fastApply_searchreplace_systemMessage = `\
|
||||
You are a coding assistant that re-writes an entire file to make a change. You are given the original file \`ORIGINAL_FILE\` and a change \`CHANGE\`.
|
||||
|
||||
Directions:
|
||||
1. Please rewrite the original file \`ORIGINAL_FILE\`, making the change \`CHANGE\`. You must completely re-write the whole file.
|
||||
2. Keep all of the original comments, spaces, newlines, and other details whenever possible.
|
||||
3. ONLY output the full new file. Do not add any other explanations or text.
|
||||
`
|
||||
|
||||
|
||||
export const fastApply_searchreplace_userMessage = ({ originalCode, applyStr, uri }: { originalCode: string, applyStr: string, uri: URI }) => {
|
||||
|
||||
const language = filenameToVscodeLanguage(uri.fsPath) ?? ''
|
||||
|
||||
return `\
|
||||
ORIGINAL_FILE
|
||||
\`\`\`${language}
|
||||
${originalCode}
|
||||
\`\`\`
|
||||
|
||||
CHANGE
|
||||
\`\`\`
|
||||
${applyStr}
|
||||
\`\`\`
|
||||
|
||||
INSTRUCTIONS
|
||||
Please finish writing the new file by applying the change to the original file. Return ONLY the completion of the file, without any explanation.
|
||||
`
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const voidPrefixAndSuffix = ({ fullFileStr, startLine, endLine }: { fullFileStr: string, startLine: number, endLine: number }) => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
import React, { JSX, useCallback, useEffect, useState } from 'react'
|
||||
import { marked, MarkedToken, Token } from 'marked'
|
||||
import { BlockCode } from './BlockCode.js'
|
||||
import { useAccessor } from '../util/services.js'
|
||||
import { useAccessor, useChatThreadsState, useChatThreadsStreamState } from '../util/services.js'
|
||||
import { ChatLocation, getApplyBoxId, } from '../../../searchAndReplaceService.js'
|
||||
import { nameToVscodeLanguage } from '../../../helpers/detectLanguage.js'
|
||||
|
||||
|
||||
|
|
@ -18,7 +19,7 @@ enum CopyButtonState {
|
|||
|
||||
const COPY_FEEDBACK_TIMEOUT = 1000 // amount of time to say 'Copied!'
|
||||
|
||||
const CodeButtonsOnHover = ({ text }: { text: string }) => {
|
||||
const ApplyButtonsOnHover = ({ applyStr, applyBoxId }: { applyStr: string, applyBoxId: string }) => {
|
||||
const accessor = useAccessor()
|
||||
|
||||
const [copyButtonState, setCopyButtonState] = useState(CopyButtonState.Copy)
|
||||
|
|
@ -36,22 +37,24 @@ const CodeButtonsOnHover = ({ text }: { text: string }) => {
|
|||
}, [copyButtonState])
|
||||
|
||||
const onCopy = useCallback(() => {
|
||||
clipboardService.writeText(text)
|
||||
clipboardService.writeText(applyStr)
|
||||
.then(() => { setCopyButtonState(CopyButtonState.Copied) })
|
||||
.catch(() => { setCopyButtonState(CopyButtonState.Error) })
|
||||
metricsService.capture('Copy Code', { length: text.length }) // capture the length only
|
||||
metricsService.capture('Copy Code', { length: applyStr.length }) // capture the length only
|
||||
|
||||
}, [metricsService, clipboardService, text])
|
||||
}, [metricsService, clipboardService, applyStr])
|
||||
|
||||
const onApply = useCallback(() => {
|
||||
|
||||
inlineDiffService.startApplying({
|
||||
from: 'Chat',
|
||||
applyStr: text,
|
||||
applyStr,
|
||||
applyBoxId,
|
||||
})
|
||||
metricsService.capture('Apply Code', { length: text.length }) // capture the length only
|
||||
}, [metricsService, inlineDiffService, text])
|
||||
metricsService.capture('Apply Code', { length: applyStr.length }) // capture the length only
|
||||
}, [metricsService, inlineDiffService, applyStr])
|
||||
|
||||
const isSingleLine = !text.includes('\n')
|
||||
const isSingleLine = !applyStr.includes('\n')
|
||||
|
||||
return <>
|
||||
<button
|
||||
|
|
@ -84,20 +87,30 @@ export const CodeSpan = ({ children, className }: { children: React.ReactNode, c
|
|||
</code>
|
||||
}
|
||||
|
||||
const RenderToken = ({ token, nested = false, noSpace = false }: { token: Token | string, nested?: boolean, noSpace?: boolean }): JSX.Element => {
|
||||
const RenderToken = ({ token, nested = false, noSpace = false, chatLocation, tokenId = '', }: { token: Token | string, nested?: boolean, noSpace?: boolean, chatLocation?: ChatLocation, tokenId?: string, }): JSX.Element => {
|
||||
|
||||
|
||||
// deal with built-in tokens first (assume marked token)
|
||||
const t = token as MarkedToken
|
||||
console.log(t.raw)
|
||||
|
||||
if (t.type === "space") {
|
||||
return <span>{t.raw}</span>
|
||||
}
|
||||
|
||||
if (t.type === "code") {
|
||||
const isCodeblockClosed = t.raw?.startsWith('```') && t.raw?.endsWith('```');
|
||||
|
||||
const applyBoxId = getApplyBoxId({
|
||||
threadId: chatLocation!.threadId,
|
||||
messageIdx: chatLocation!.messageIdx,
|
||||
codeblockId: tokenId,
|
||||
})
|
||||
|
||||
return <BlockCode
|
||||
initValue={t.text}
|
||||
language={t.lang === undefined ? undefined : nameToVscodeLanguage[t.lang]} // use vscode to detect language
|
||||
buttonsOnHover={<CodeButtonsOnHover text={t.text} />}
|
||||
language={t.lang === undefined ? undefined : nameToVscodeLanguage[t.lang]}
|
||||
buttonsOnHover={<ApplyButtonsOnHover applyStr={t.text} applyBoxId={applyBoxId} />}
|
||||
/>
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +196,7 @@ const RenderToken = ({ token, nested = false, noSpace = false }: { token: Token
|
|||
if (t.type === "paragraph") {
|
||||
const contents = <>
|
||||
{t.tokens.map((token, index) => (
|
||||
<RenderToken key={index} token={token} />
|
||||
<RenderToken key={index} token={token} tokenId={`${tokenId}-${index}`} /> // assign a unique tokenId to nested components
|
||||
))}
|
||||
</>
|
||||
if (nested) return contents
|
||||
|
|
@ -266,12 +279,12 @@ const RenderToken = ({ token, nested = false, noSpace = false }: { token: Token
|
|||
)
|
||||
}
|
||||
|
||||
export const ChatMarkdownRender = ({ string, nested = false, noSpace }: { string: string, nested?: boolean, noSpace?: boolean }) => {
|
||||
export const ChatMarkdownRender = ({ string, nested = false, noSpace, chatLocation }: { string: string, nested?: boolean, noSpace?: boolean, chatLocation?: ChatLocation }) => {
|
||||
const tokens = marked.lexer(string); // https://marked.js.org/using_pro#renderer
|
||||
return (
|
||||
<>
|
||||
{tokens.map((token, index) => (
|
||||
<RenderToken key={index} token={token} nested={nested} noSpace={noSpace} />
|
||||
<RenderToken key={index} token={token} nested={nested} noSpace={noSpace} chatLocation={chatLocation} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js';
|
|||
import { Pencil, X } from 'lucide-react';
|
||||
import { FeatureName, isFeatureNameDisabled } from '../../../../../../../platform/void/common/voidSettingsTypes.js';
|
||||
import { WarningBox } from '../void-settings-tsx/WarningBox.js';
|
||||
import { ChatLocation } from '../../../searchAndReplaceService.js';
|
||||
|
||||
|
||||
|
||||
|
|
@ -578,9 +579,7 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: { chatMessage: ChatM
|
|||
}
|
||||
|
||||
}, [role, mode, _justEnabledEdit, textAreaRefState, textAreaFnsRef.current, _justEnabledEdit.current, _mustInitialize.current])
|
||||
|
||||
const EditSymbol = mode === 'display' ? Pencil : X
|
||||
|
||||
const onOpenEdit = () => {
|
||||
setStaging({ ...staging, isBeingEdited: true })
|
||||
chatThreadsService.setFocusedMessageIdx(messageIdx)
|
||||
|
|
@ -674,7 +673,14 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: { chatMessage: ChatM
|
|||
}
|
||||
}
|
||||
else if (role === 'assistant') {
|
||||
chatbubbleContents = <ChatMarkdownRender string={chatMessage.displayContent ?? ''} />
|
||||
const thread = chatThreadsService.getCurrentThread()
|
||||
|
||||
const chatLocation: ChatLocation = {
|
||||
threadId: thread.id,
|
||||
messageIdx: messageIdx!,
|
||||
}
|
||||
|
||||
chatbubbleContents = <ChatMarkdownRender string={chatMessage.displayContent ?? ''} chatLocation={chatLocation} />
|
||||
}
|
||||
|
||||
return <div
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
/*--------------------------------------------------------------------------------------
|
||||
* 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 { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
|
||||
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
|
||||
|
||||
|
||||
export type ChatLocation = {
|
||||
threadId: string;
|
||||
messageIdx: number;
|
||||
}
|
||||
|
||||
export type ApplyBoxLocation = ChatLocation & { codeblockId: string }
|
||||
|
||||
export const getApplyBoxId = ({ threadId, messageIdx, codeblockId }: ApplyBoxLocation) => {
|
||||
return `${threadId}-${messageIdx}-${codeblockId}}`
|
||||
}
|
||||
|
||||
export type SearchAndReplaceBlock = {
|
||||
search: string;
|
||||
replace: string;
|
||||
}
|
||||
|
||||
// service that manages state
|
||||
export type ApplyState = {
|
||||
[applyBoxId: string]: {
|
||||
searchAndReplaceBlocks: SearchAndReplaceBlock;
|
||||
}
|
||||
}
|
||||
|
||||
// the purpose of this service is to generate search and replace blocks for a given codeblock `codeblockId` and on a file `fileName` and version `fileVersion`
|
||||
|
||||
export interface IFastApplyService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
// readonly state: ApplyState; // readonly to the user
|
||||
// setState(newState: Partial<ApplyState>): void;
|
||||
// onDidChangeState: Event<void>;
|
||||
}
|
||||
|
||||
export const IVoidFastApplyService = createDecorator<IFastApplyService>('voidFastApplyService');
|
||||
class VoidFastApplyService extends Disposable implements IFastApplyService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
static readonly ID = 'voidFastApplyService';
|
||||
|
||||
private readonly _onDidChangeState = new Emitter<void>();
|
||||
readonly onDidChangeState: Event<void> = this._onDidChangeState.event;
|
||||
|
||||
|
||||
// state
|
||||
// state: ApplyState
|
||||
|
||||
constructor(
|
||||
) {
|
||||
super()
|
||||
|
||||
// initial state
|
||||
// this.state = { currentUri: undefined }
|
||||
}
|
||||
|
||||
setState(newState: Partial<ApplyState>) {
|
||||
|
||||
// this.state = { ...this.state, ...newState }
|
||||
this._onDidChangeState.fire()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
registerSingleton(IVoidFastApplyService, VoidFastApplyService, InstantiationType.Eager);
|
||||
Loading…
Reference in a new issue