Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,26 @@

import { DocumentId } from '../../../platform/inlineEdits/common/dataTypes/documentId';
import { Schemas } from '../../../util/vs/base/common/network';
import { isWindows } from '../../../util/vs/base/common/platform';

export function toUniquePath(documentId: DocumentId, workspaceRootPath: string | undefined): string {
const filePath = documentId.path;
// remove prefix from path if defined
const workspaceRootPathWithSlash = workspaceRootPath === undefined ? undefined : (workspaceRootPath.endsWith('/') ? workspaceRootPath : workspaceRootPath + '/');
const documentScheme = documentId.toUri().scheme;
const isWorkspaceRelative = workspaceRootPathWithSlash !== undefined
&& normalizeWindowsDriveLetter(filePath).startsWith(normalizeWindowsDriveLetter(workspaceRootPathWithSlash));

const updatedFilePath = workspaceRootPathWithSlash !== undefined && filePath.startsWith(workspaceRootPathWithSlash)
const updatedFilePath = isWorkspaceRelative
? filePath.substring(workspaceRootPathWithSlash.length)
: filePath;

return documentId.toUri().scheme === Schemas.vscodeNotebookCell ? `${updatedFilePath}#${documentId.fragment}` : updatedFilePath;
return documentScheme === Schemas.vscodeNotebookCell ? `${updatedFilePath}#${documentId.fragment}` : updatedFilePath;
}

function normalizeWindowsDriveLetter(path: string): string {
return isWindows && /^\/[a-zA-Z]:/.test(path) ? `/${path[1].toLowerCase()}${path.substring(2)}` : path;
}

export function countTokensForLines(page: string[], computeTokens: (s: string) => number): number {
return page.reduce((sum, line) => sum + computeTokens(line) + 1 /* \n */, 0);
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { NoNextEditReason, StreamedEdit } from '../../../../platform/inlineEdits
import { TestLogService } from '../../../../platform/testing/common/testLogService';
import { AsyncIterUtils } from '../../../../util/common/asyncIterableUtils';
import { AsyncIterableSource } from '../../../../util/vs/base/common/async';
import { isWindows } from '../../../../util/vs/base/common/platform';
import { URI } from '../../../../util/vs/base/common/uri';
import { LineReplacement } from '../../../../util/vs/editor/common/core/edits/lineEdit';
import { Position } from '../../../../util/vs/editor/common/core/position';
import { LineRange } from '../../../../util/vs/editor/common/core/ranges/lineRange';
Expand Down Expand Up @@ -97,6 +99,43 @@ relative/path/to/another_file.js:42
expect(patches).toEqual(patchText);
});

it.skipIf(!isWindows)('applies a patch for a Windows path whose parent folder contains a space', async () => {
const workspaceRoot = URI.file('C:\\workspace');
const documentId = DocumentId.create(URI.file('C:\\workspace\\space folder\\test.py').toString());
const document = new CurrentDocument(new StringText('def my_function'), new Position(1, 16));
const lines = AsyncIterUtils.fromArray([
'space folder/test.py:0',
'-def my_function',
'+def my_function():',
'+ pass',
]);

const { edits } = await consumeHandleResponse(
lines,
document,
documentId,
workspaceRoot,
undefined,
new TestLogService(),
DuplicateAdditionsMode.Off,
true,
);

expect(edits.map(edit => ({
targetDocument: edit.targetDocument.toString(),
lineRange: edit.edit.lineRange.toString(),
newLines: edit.edit.newLines,
}))).toEqual([{
targetDocument: 'file:///c%3A/workspace/space%20folder/test.py',
lineRange: '[1,2)',
newLines: ['def my_function():'],
}, {
targetDocument: 'file:///c%3A/workspace/space%20folder/test.py',
lineRange: '[2,2)',
newLines: [' pass'],
}]);
});

it('discard a patch if no valid header', async () => {
const patchText = `myFile.ts:
+New line 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import { CancellationToken, CancellationTokenSource } from '../../../../util/vs/
import { Emitter, Event } from '../../../../util/vs/base/common/event';
import { constObservable } from '../../../../util/vs/base/common/observable';
import { DisposableStore } from '../../../../util/vs/base/common/lifecycle';
import { Schemas } from '../../../../util/vs/base/common/network';
import { isWindows } from '../../../../util/vs/base/common/platform';
import { URI } from '../../../../util/vs/base/common/uri';
import { LineEdit, LineReplacement } from '../../../../util/vs/editor/common/core/edits/lineEdit';
import { StringEdit, StringReplacement } from '../../../../util/vs/editor/common/core/edits/stringEdit';
Expand Down Expand Up @@ -525,6 +527,59 @@ describe('getPredictionContents', () => {
expect(result.endsWith(':')).toBe(true);
});

it.skipIf(!isWindows)('preserves spaces in a workspace-relative Windows path', () => {
const lines = ['def my_function'];
const text = new StringText(lines.join('\n'));
const workspaceRoot = URI.file('C:\\workspace');
const doc = new StatelessNextEditDocument(
DocumentId.create(URI.file('C:\\workspace\\space folder\\test.py').toString()),
workspaceRoot,
LanguageId.create('python'),
lines,
LineEdit.empty,
text,
new Edits(StringEdit, []),
);

expect(call(lines, ResponseFormat.CustomDiffPatch, { doc })).toBe('space folder/test.py:');
});

it.skipIf(!isWindows)('normalizes the drive letter for a file-backed notebook cell', () => {
const lines = ['print("hello")'];
const text = new StringText(lines.join('\n'));
const workspaceRoot = URI.file('C:\\workspace');
const cellUri = URI.file('C:\\workspace\\notebook.ipynb').with({ scheme: Schemas.vscodeNotebookCell, fragment: 'ch000001' });
const doc = new StatelessNextEditDocument(
DocumentId.create(cellUri.toString()),
workspaceRoot,
LanguageId.create('python'),
lines,
LineEdit.empty,
text,
new Edits(StringEdit, []),
);

expect(call(lines, ResponseFormat.CustomDiffPatch, { doc })).toBe('notebook.ipynb#ch000001:');
});

it.skipIf(!isWindows)('preserves path casing for a virtual notebook cell', () => {
const lines = ['print("hello")'];
const text = new StringText(lines.join('\n'));
const workspaceRoot = URI.from({ scheme: 'test-notebook', path: '/repo' });
const cellUri = URI.from({ scheme: Schemas.vscodeNotebookCell, path: '/Repo/notebook.ipynb', fragment: 'ch000001' });
const doc = new StatelessNextEditDocument(
DocumentId.create(cellUri.toString()),
workspaceRoot,
LanguageId.create('python'),
lines,
LineEdit.empty,
text,
new Edits(StringEdit, []),
);

expect(call(lines, ResponseFormat.CustomDiffPatch, { doc })).toBe('/Repo/notebook.ipynb#ch000001:');
});

it('returns correct content for CustomDiffPatch without workspace root', () => {
const result = call(editWindowLines, ResponseFormat.CustomDiffPatch);
expect(result.endsWith(':')).toBe(true);
Expand Down
Loading