Skip to content

Commit 2971116

Browse files
committed
make it simpler, fix issues
1 parent a177750 commit 2971116

File tree

9 files changed

+985
-660
lines changed

9 files changed

+985
-660
lines changed

src/vs/base/common/buffer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ export function binaryIndexOf(haystack: Uint8Array, needle: Uint8Array, offset =
213213
}
214214

215215
if (needleLen === 1) {
216-
return haystack.indexOf(needle[0]);
216+
return haystack.indexOf(needle[0], offset);
217217
}
218218

219219
if (needleLen > haystackLen - offset) {

src/vs/platform/files/common/inMemoryFilesystemProvider.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,6 @@ export class InMemoryFileSystemProvider extends Disposable implements
147147
if (!entry && !opts.create) {
148148
throw createFileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
149149
}
150-
if (entry && opts.create && !opts.overwrite) {
151-
throw createFileSystemProviderError('file exists already', FileSystemProviderErrorCode.FileExists);
152-
}
153150
if (!entry) {
154151
entry = new File(basename);
155152
parent.entries.set(basename, entry);

src/vs/platform/files/test/node/diskFileService.integrationTest.ts

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2510,6 +2510,181 @@ flakySuite('Disk File Service', function () {
25102510
assert.ok(error);
25112511
});
25122512

2513+
test('appendFile', async () => {
2514+
return testAppendFile();
2515+
});
2516+
2517+
test('appendFile - unbuffered', async () => {
2518+
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);
2519+
2520+
return testAppendFile();
2521+
});
2522+
2523+
test('appendFile - buffered', async () => {
2524+
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);
2525+
2526+
return testAppendFile();
2527+
});
2528+
2529+
async function testAppendFile() {
2530+
let event: FileOperationEvent;
2531+
disposables.add(service.onDidRunOperation(e => event = e));
2532+
2533+
const resource = URI.file(join(testDir, 'small.txt'));
2534+
2535+
const content = readFileSync(resource.fsPath).toString();
2536+
assert.strictEqual(content, 'Small File');
2537+
2538+
const appendContent = ' - Appended!';
2539+
await service.appendFile(resource, VSBuffer.fromString(appendContent));
2540+
2541+
assert.ok(event!);
2542+
assert.strictEqual(event!.resource.fsPath, resource.fsPath);
2543+
assert.strictEqual(event!.operation, FileOperation.WRITE);
2544+
2545+
assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Small File - Appended!');
2546+
}
2547+
2548+
test('appendFile (readable)', async () => {
2549+
return testAppendFileReadable();
2550+
});
2551+
2552+
test('appendFile (readable) - unbuffered', async () => {
2553+
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);
2554+
2555+
return testAppendFileReadable();
2556+
});
2557+
2558+
test('appendFile (readable) - buffered', async () => {
2559+
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);
2560+
2561+
return testAppendFileReadable();
2562+
});
2563+
2564+
async function testAppendFileReadable() {
2565+
const resource = URI.file(join(testDir, 'small.txt'));
2566+
2567+
const content = readFileSync(resource.fsPath).toString();
2568+
assert.strictEqual(content, 'Small File');
2569+
2570+
const appendContent = ' - Appended via readable!';
2571+
await service.appendFile(resource, bufferToReadable(VSBuffer.fromString(appendContent)));
2572+
2573+
assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Small File - Appended via readable!');
2574+
}
2575+
2576+
test('appendFile (stream)', async () => {
2577+
return testAppendFileStream();
2578+
});
2579+
2580+
test('appendFile (stream) - unbuffered', async () => {
2581+
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);
2582+
2583+
return testAppendFileStream();
2584+
});
2585+
2586+
test('appendFile (stream) - buffered', async () => {
2587+
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);
2588+
2589+
return testAppendFileStream();
2590+
});
2591+
2592+
async function testAppendFileStream() {
2593+
const resource = URI.file(join(testDir, 'small.txt'));
2594+
2595+
const content = readFileSync(resource.fsPath).toString();
2596+
assert.strictEqual(content, 'Small File');
2597+
2598+
const appendContent = ' - Appended via stream!';
2599+
await service.appendFile(resource, bufferToStream(VSBuffer.fromString(appendContent)));
2600+
2601+
assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Small File - Appended via stream!');
2602+
}
2603+
2604+
test('appendFile - creates file if not exists', async () => {
2605+
return testAppendFileCreatesFile();
2606+
});
2607+
2608+
test('appendFile - creates file if not exists (unbuffered)', async () => {
2609+
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);
2610+
2611+
return testAppendFileCreatesFile();
2612+
});
2613+
2614+
test('appendFile - creates file if not exists (buffered)', async () => {
2615+
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);
2616+
2617+
return testAppendFileCreatesFile();
2618+
});
2619+
2620+
async function testAppendFileCreatesFile() {
2621+
const resource = URI.file(join(testDir, 'appendfile-new.txt'));
2622+
2623+
assert.strictEqual(existsSync(resource.fsPath), false);
2624+
2625+
const content = 'Initial content via append';
2626+
await service.appendFile(resource, VSBuffer.fromString(content));
2627+
2628+
assert.strictEqual(existsSync(resource.fsPath), true);
2629+
assert.strictEqual(readFileSync(resource.fsPath).toString(), content);
2630+
}
2631+
2632+
test('appendFile - multiple appends', async () => {
2633+
return testAppendFileMultiple();
2634+
});
2635+
2636+
test('appendFile - multiple appends (unbuffered)', async () => {
2637+
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend);
2638+
2639+
return testAppendFileMultiple();
2640+
});
2641+
2642+
test('appendFile - multiple appends (buffered)', async () => {
2643+
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend);
2644+
2645+
return testAppendFileMultiple();
2646+
});
2647+
2648+
async function testAppendFileMultiple() {
2649+
const resource = URI.file(join(testDir, 'appendfile-multiple.txt'));
2650+
2651+
await service.appendFile(resource, VSBuffer.fromString('Line 1\n'));
2652+
await service.appendFile(resource, VSBuffer.fromString('Line 2\n'));
2653+
await service.appendFile(resource, VSBuffer.fromString('Line 3\n'));
2654+
2655+
assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Line 1\nLine 2\nLine 3\n');
2656+
}
2657+
2658+
test('appendFile - fallback when provider does not support append', async () => {
2659+
// Remove FileAppend capability to force fallback path
2660+
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
2661+
2662+
const resource = URI.file(join(testDir, 'small.txt'));
2663+
2664+
const content = readFileSync(resource.fsPath).toString();
2665+
assert.strictEqual(content, 'Small File');
2666+
2667+
const appendContent = ' - Appended via fallback!';
2668+
await service.appendFile(resource, VSBuffer.fromString(appendContent));
2669+
2670+
assert.strictEqual(readFileSync(resource.fsPath).toString(), 'Small File - Appended via fallback!');
2671+
});
2672+
2673+
test('appendFile - fallback creates file if not exists', async () => {
2674+
// Remove FileAppend capability to force fallback path
2675+
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
2676+
2677+
const resource = URI.file(join(testDir, 'appendfile-fallback-new.txt'));
2678+
2679+
assert.strictEqual(existsSync(resource.fsPath), false);
2680+
2681+
const content = 'Initial content via fallback append';
2682+
await service.appendFile(resource, VSBuffer.fromString(content));
2683+
2684+
assert.strictEqual(existsSync(resource.fsPath), true);
2685+
assert.strictEqual(readFileSync(resource.fsPath).toString(), content);
2686+
});
2687+
25132688
test('read - mixed positions', async () => {
25142689
const resource = URI.file(join(testDir, 'lorem.txt'));
25152690

src/vs/workbench/contrib/chat/common/model/chatModel.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import { ILanguageModelChatMetadata, ILanguageModelChatMetadataAndIdentifier } f
3737
import { IChatAgentCommand, IChatAgentData, IChatAgentResult, IChatAgentService, UserSelectedTools, reviveSerializedAgent } from '../participants/chatAgents.js';
3838
import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from '../requestParser/chatParserTypes.js';
3939
import { LocalChatSessionUri } from './chatUri.js';
40+
import { ObjectMutationLog } from './objectMutationLog.js';
4041

4142

4243
export const CHAT_ATTACHABLE_IMAGE_MIME_TYPES: Record<string, string> = {
@@ -210,6 +211,8 @@ export interface IChatResponseModel {
210211
readonly completedAt?: number;
211212
/** The state of this response */
212213
readonly state: ResponseModelState;
214+
/** @internal */
215+
readonly stateT: ResponseModelStateT;
213216
/**
214217
* Adjusted millisecond timestamp that excludes the duration during which
215218
* the model was pending user confirmation. `Date.now() - confirmationAdjustedTimestamp`
@@ -792,7 +795,7 @@ export interface IChatResponseModelParameters {
792795
codeBlockInfos: ICodeBlockInfo[] | undefined;
793796
}
794797

795-
type ResponseModelStateT =
798+
export type ResponseModelStateT =
796799
| { value: ResponseModelState.Pending }
797800
| { value: ResponseModelState.NeedsInput }
798801
| { value: ResponseModelState.Complete | ResponseModelState.Cancelled | ResponseModelState.Failed; completedAt: number };
@@ -869,6 +872,10 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
869872
return state;
870873
}
871874

875+
public get stateT(): ResponseModelStateT {
876+
return this._modelState.get();
877+
}
878+
872879
public get vote(): ChatAgentVoteDirection | undefined {
873880
return this._vote;
874881
}
@@ -1398,9 +1405,7 @@ export interface ISerializableChatModelInputState {
13981405
*/
13991406
export type ISerializableChatData = ISerializableChatData3;
14001407

1401-
export interface IChatDataSerializerLog {
1402-
write(current: IChatModel): { op: 'append' | 'replace'; data: VSBuffer };
1403-
}
1408+
export type IChatDataSerializerLog = ObjectMutationLog<IChatModel, ISerializableChatData>;
14041409

14051410
export interface ISerializedChatDataReference {
14061411
value: ISerializableChatData | IExportableChatData;
@@ -1736,7 +1741,7 @@ export class ChatModel extends Disposable implements IChatModel {
17361741
public dataSerializer?: IChatDataSerializerLog;
17371742

17381743
constructor(
1739-
initialData: ISerializedChatDataReference | undefined,
1744+
dataRef: ISerializedChatDataReference | undefined,
17401745
initialModelProps: { initialLocation: ChatAgentLocation; canUseTools: boolean; inputState?: ISerializableChatModelInputState; resource?: URI; sessionId?: string; disableBackgroundKeepAlive?: boolean },
17411746
@IConfigurationService private readonly configurationService: IConfigurationService,
17421747
@ILogService private readonly logService: ILogService,
@@ -1746,6 +1751,7 @@ export class ChatModel extends Disposable implements IChatModel {
17461751
) {
17471752
super();
17481753

1754+
const initialData = dataRef?.value;
17491755
const isValidExportedData = isExportableSessionData(initialData);
17501756
const isValidFullData = isValidExportedData && isSerializableSessionData(initialData);
17511757
if (initialData && !isValidExportedData) {
@@ -1775,9 +1781,9 @@ export class ChatModel extends Disposable implements IChatModel {
17751781
selections: serializedInputState.selections
17761782
});
17771783

1778-
this.dataSerializer = initialData?.serializer;
1779-
this._initialResponderUsername = initialData?.value.responderUsername;
1780-
this._initialLocation = initialData?.value.initialLocation ?? initialModelProps.initialLocation;
1784+
this.dataSerializer = dataRef?.serializer;
1785+
this._initialResponderUsername = initialData?.responderUsername;
1786+
this._initialLocation = initialData?.initialLocation ?? initialModelProps.initialLocation;
17811787

17821788
this._canUseTools = initialModelProps.canUseTools;
17831789

@@ -1918,6 +1924,11 @@ export class ChatModel extends Disposable implements IChatModel {
19181924
const result = 'responseErrorDetails' in raw ?
19191925
// eslint-disable-next-line local/code-no-dangerous-type-assertions
19201926
{ errorDetails: raw.responseErrorDetails } as IChatAgentResult : raw.result;
1927+
let modelState = raw.modelState || { value: raw.isCanceled ? ResponseModelState.Cancelled : ResponseModelState.Complete, completedAt: Date.now() };
1928+
if (modelState.value === ResponseModelState.Pending || modelState.value === ResponseModelState.NeedsInput) {
1929+
modelState = { value: ResponseModelState.Cancelled, completedAt: Date.now() };
1930+
}
1931+
19211932
request.response = new ChatResponseModel({
19221933
responseContent: raw.response ?? [new MarkdownString(raw.response)],
19231934
session: this,

0 commit comments

Comments
 (0)