Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreadman committed Sep 18, 2023
2 parents 7f91e33 + e6b8e03 commit e8068a0
Show file tree
Hide file tree
Showing 48 changed files with 975 additions and 198 deletions.
2 changes: 1 addition & 1 deletion extensions/css/cgmanifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"git": {
"name": "microsoft/vscode-css",
"repositoryUrl": "https://github.com/microsoft/vscode-css",
"commitHash": "3bd00206f6b0d16eb2eba53fb886462eb8c58baa"
"commitHash": "c216f777497265700ff336f739328e5197e012cd"
}
},
"licenseDetail": [
Expand Down
6 changes: 3 additions & 3 deletions extensions/css/syntaxes/css.tmLanguage.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/vs/base/common/arrays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ export function forEachAdjacent<T>(arr: T[], f: (item1: T | undefined, item2: T
}
}

export function forEachWithNeighbors<T>(arr: T[], f: (before: T | undefined, element: T, after: T | undefined) => void): void {
for (let i = 0; i < arr.length; i++) {
f(i === 0 ? undefined : arr[i - 1], arr[i], i + 1 === arr.length ? undefined : arr[i + 1]);
}
}

interface IMutableSplice<T> extends ISplice<T> {
readonly toInsert: T[];
deleteCount: number;
Expand Down
6 changes: 6 additions & 0 deletions src/vs/editor/common/core/offsetRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ export class OffsetRange {
return undefined;
}

public intersectsOrTouches(other: OffsetRange): boolean {
const start = Math.max(this.start, other.start);
const end = Math.min(this.endExclusive, other.endExclusive);
return start <= end;
}

public slice<T>(arr: T[]): T[] {
return arr.slice(this.start, this.endExclusive);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ export interface IDiffAlgorithm {

export class DiffAlgorithmResult {
static trivial(seq1: ISequence, seq2: ISequence): DiffAlgorithmResult {
return new DiffAlgorithmResult([new SequenceDiff(new OffsetRange(0, seq1.length), new OffsetRange(0, seq2.length))], false);
return new DiffAlgorithmResult([new SequenceDiff(OffsetRange.ofLength(seq1.length), OffsetRange.ofLength(seq2.length))], false);
}

static trivialTimedOut(seq1: ISequence, seq2: ISequence): DiffAlgorithmResult {
return new DiffAlgorithmResult([new SequenceDiff(new OffsetRange(0, seq1.length), new OffsetRange(0, seq2.length))], true);
return new DiffAlgorithmResult([new SequenceDiff(OffsetRange.ofLength(seq1.length), OffsetRange.ofLength(seq2.length))], true);
}

constructor(
Expand All @@ -36,21 +36,22 @@ export class DiffAlgorithmResult {
export class SequenceDiff {
public static invert(sequenceDiffs: SequenceDiff[], doc1Length: number): SequenceDiff[] {
const result: SequenceDiff[] = [];

forEachAdjacent(sequenceDiffs, (a, b) => {
const seq1Start = a ? a.seq1Range.endExclusive : 0;
const seq2Start = a ? a.seq2Range.endExclusive : 0;
const seq1EndEx = b ? b.seq1Range.start : doc1Length;
const seq2EndEx = b ? b.seq2Range.start : (a ? a.seq2Range.endExclusive - a.seq1Range.endExclusive : 0) + doc1Length;
result.push(new SequenceDiff(
new OffsetRange(seq1Start, seq1EndEx),
new OffsetRange(seq2Start, seq2EndEx),
result.push(SequenceDiff.fromOffsetPairs(
a ? a.getEndExclusives() : OffsetPair.zero,
b ? b.getStarts() : new OffsetPair(doc1Length, (a ? a.seq2Range.endExclusive - a.seq1Range.endExclusive : 0) + doc1Length)
));
});

return result;
}

public static fromOffsetPairs(start: OffsetPair, endExclusive: OffsetPair): SequenceDiff {
return new SequenceDiff(
new OffsetRange(start.offset1, endExclusive.offset1),
new OffsetRange(start.offset2, endExclusive.offset2),
);
}

constructor(
public readonly seq1Range: OffsetRange,
public readonly seq2Range: OffsetRange,
Expand All @@ -74,6 +75,56 @@ export class SequenceDiff {
}
return new SequenceDiff(this.seq1Range.delta(offset), this.seq2Range.delta(offset));
}

public deltaStart(offset: number): SequenceDiff {
if (offset === 0) {
return this;
}
return new SequenceDiff(this.seq1Range.deltaStart(offset), this.seq2Range.deltaStart(offset));
}

public deltaEnd(offset: number): SequenceDiff {
if (offset === 0) {
return this;
}
return new SequenceDiff(this.seq1Range.deltaEnd(offset), this.seq2Range.deltaEnd(offset));
}

public intersectsOrTouches(other: SequenceDiff): boolean {
return this.seq1Range.intersectsOrTouches(other.seq1Range) || this.seq2Range.intersectsOrTouches(other.seq2Range);
}

public intersect(other: SequenceDiff): SequenceDiff | undefined {
const i1 = this.seq1Range.intersect(other.seq1Range);
const i2 = this.seq2Range.intersect(other.seq2Range);
if (!i1 || !i2) {
return undefined;
}
return new SequenceDiff(i1, i2);
}

public getStarts(): OffsetPair {
return new OffsetPair(this.seq1Range.start, this.seq2Range.start);
}

public getEndExclusives(): OffsetPair {
return new OffsetPair(this.seq1Range.endExclusive, this.seq2Range.endExclusive);
}
}

export class OffsetPair {
public static readonly zero = new OffsetPair(0, 0);
public static readonly max = new OffsetPair(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);

constructor(
public readonly offset1: number,
public readonly offset2: number,
) {
}

public toString(): string {
return `${this.offset1} <-> ${this.offset2}`;
}
}

export interface ISequence {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ function computeUnchangedMoves(
for (extendToTop = 0; extendToTop < linesAbove; extendToTop++) {
const origLine = move.original.startLineNumber - extendToTop - 1;
const modLine = move.modified.startLineNumber - extendToTop - 1;
if (origLine > originalLines.length || modLine > modifiedLines.length) {
break;
}
if (modifiedSet.contains(modLine) || originalSet.contains(origLine)) {
break;
}
Expand All @@ -213,6 +216,9 @@ function computeUnchangedMoves(
for (extendToBottom = 0; extendToBottom < linesBelow; extendToBottom++) {
const origLine = move.original.endLineNumberExclusive + extendToBottom;
const modLine = move.modified.endLineNumberExclusive + extendToBottom;
if (origLine > originalLines.length || modLine > modifiedLines.length) {
break;
}
if (modifiedSet.contains(modLine) || originalSet.contains(origLine)) {
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { forEachWithNeighbors } from 'vs/base/common/arrays';
import { OffsetRange } from 'vs/editor/common/core/offsetRange';
import { ISequence, SequenceDiff } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm';
import { ISequence, OffsetPair, SequenceDiff } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm';
import { LineSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/lineSequence';
import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence';

Expand Down Expand Up @@ -425,28 +426,33 @@ export function removeVeryShortMatchingTextBetweenLongDiffs(sequence1: LinesSlic
diffs = result;
} while (counter++ < 10 && shouldRepeat);

const newDiffs: SequenceDiff[] = [];

// Remove short suffixes/prefixes
for (let i = 0; i < diffs.length; i++) {
const cur = diffs[i];
forEachWithNeighbors(diffs, (prev, cur, next) => {
let newDiff = cur;

let range1 = cur.seq1Range;
let range2 = cur.seq2Range;
function shouldMarkAsChanged(text: string): boolean {
return text.length > 0 && text.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 100;
}

const fullRange1 = sequence1.extendToFullLines(cur.seq1Range);
const prefix = sequence1.getText(new OffsetRange(fullRange1.start, cur.seq1Range.start));
if (prefix.length > 0 && prefix.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 100) {
range1 = cur.seq1Range.deltaStart(-prefix.length);
range2 = cur.seq2Range.deltaStart(-prefix.length);
if (shouldMarkAsChanged(prefix)) {
newDiff = newDiff.deltaStart(-prefix.length);
}

const suffix = sequence1.getText(new OffsetRange(cur.seq1Range.endExclusive, fullRange1.endExclusive));
if (suffix.length > 0 && (suffix.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 150)) {
range1 = range1.deltaEnd(suffix.length);
range2 = range2.deltaEnd(suffix.length);
if (shouldMarkAsChanged(suffix)) {
newDiff = newDiff.deltaEnd(suffix.length);
}

diffs[i] = new SequenceDiff(range1, range2);
}
const availableSpace = SequenceDiff.fromOffsetPairs(
prev ? prev.getEndExclusives() : OffsetPair.zero,
next ? next.getStarts() : OffsetPair.max,
);
const result = newDiff.intersect(availableSpace)!;
newDiffs.push(result);
});

return diffs;
return newDiffs;
}
2 changes: 1 addition & 1 deletion src/vs/editor/common/services/editorSimpleWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
return result;
}

public async computeHumanReadableDiff(modelUrl: string, edits: TextEdit[], options: ILinesDiffComputerOptions): Promise<TextEdit[]> {
public computeHumanReadableDiff(modelUrl: string, edits: TextEdit[], options: ILinesDiffComputerOptions): TextEdit[] {
const model = this._getModel(modelUrl);
if (!model) {
return edits;
Expand Down
10 changes: 8 additions & 2 deletions src/vs/editor/contrib/codeAction/browser/codeAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import { coalesce, equals, isNonEmptyArray } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { illegalArgument, isCancellationError, onUnexpectedExternalError } from 'vs/base/common/errors';
Expand All @@ -18,7 +19,6 @@ import { ITextModel } from 'vs/editor/common/model';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { IModelService } from 'vs/editor/common/services/model';
import { TextModelCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState';
import * as nls from 'vs/nls';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService } from 'vs/platform/notification/common/notification';
Expand Down Expand Up @@ -89,14 +89,20 @@ export async function getCodeActions(
token: CancellationToken,
): Promise<CodeActionSet> {
const filter = trigger.filter || {};
const notebookFilter: CodeActionFilter = {
...filter,
excludes: [...(filter.excludes || []), CodeActionKind.Notebook],
};

const codeActionContext: languages.CodeActionContext = {
only: filter.include?.value,
trigger: trigger.type,
};

const cts = new TextModelCancellationTokenSource(model, token);
const providers = getCodeActionProviders(registry, model, filter);
// if the trigger is auto (autosave, lightbulb, etc), we should exclude notebook codeActions
const excludeNotebookCodeActions = (trigger.type === languages.CodeActionTriggerType.Auto);
const providers = getCodeActionProviders(registry, model, (excludeNotebookCodeActions) ? notebookFilter : filter);

const disposables = new DisposableStore();
const promises = providers.map(async provider => {
Expand Down
1 change: 1 addition & 0 deletions src/vs/editor/contrib/codeAction/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export class CodeActionKind {
public static readonly RefactorInline = CodeActionKind.Refactor.append('inline');
public static readonly RefactorMove = CodeActionKind.Refactor.append('move');
public static readonly RefactorRewrite = CodeActionKind.Refactor.append('rewrite');
public static readonly Notebook = new CodeActionKind('notebook');
public static readonly Source = new CodeActionKind('source');
public static readonly SourceOrganizeImports = CodeActionKind.Source.append('organizeImports');
public static readonly SourceFixAll = CodeActionKind.Source.append('fixAll');
Expand Down
8 changes: 4 additions & 4 deletions src/vs/editor/test/common/services/editorSimpleWorker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ suite('EditorSimpleWorker', () => {
);
});

test.skip('[Bug] Getting Message "Overlapping ranges are not allowed" and nothing happens with Inline-Chat ', async function () {
test('[Bug] Getting Message "Overlapping ranges are not allowed" and nothing happens with Inline-Chat ', async function () {
await testEdits(("const API = require('../src/api');\n\ndescribe('API', () => {\n let api;\n let database;\n\n beforeAll(() => {\n database = {\n getAllBooks: jest.fn(),\n getBooksByAuthor: jest.fn(),\n getBooksByTitle: jest.fn(),\n };\n api = new API(database);\n });\n\n describe('GET /books', () => {\n it('should return all books', async () => {\n const mockBooks = [{ title: 'Book 1' }, { title: 'Book 2' }];\n database.getAllBooks.mockResolvedValue(mockBooks);\n\n const req = {};\n const res = {\n json: jest.fn(),\n };\n\n await api.register({\n get: (path, handler) => {\n if (path === '/books') {\n handler(req, res);\n }\n },\n });\n\n expect(database.getAllBooks).toHaveBeenCalled();\n expect(res.json).toHaveBeenCalledWith(mockBooks);\n });\n });\n\n describe('GET /books/author/:author', () => {\n it('should return books by author', async () => {\n const mockAuthor = 'John Doe';\n const mockBooks = [{ title: 'Book 1', author: mockAuthor }, { title: 'Book 2', author: mockAuthor }];\n database.getBooksByAuthor.mockResolvedValue(mockBooks);\n\n const req = {\n params: {\n author: mockAuthor,\n },\n };\n const res = {\n json: jest.fn(),\n };\n\n await api.register({\n get: (path, handler) => {\n if (path === `/books/author/${mockAuthor}`) {\n handler(req, res);\n }\n },\n });\n\n expect(database.getBooksByAuthor).toHaveBeenCalledWith(mockAuthor);\n expect(res.json).toHaveBeenCalledWith(mockBooks);\n });\n });\n\n describe('GET /books/title/:title', () => {\n it('should return books by title', async () => {\n const mockTitle = 'Book 1';\n const mockBooks = [{ title: mockTitle, author: 'John Doe' }];\n database.getBooksByTitle.mockResolvedValue(mockBooks);\n\n const req = {\n params: {\n title: mockTitle,\n },\n };\n const res = {\n json: jest.fn(),\n };\n\n await api.register({\n get: (path, handler) => {\n if (path === `/books/title/${mockTitle}`) {\n handler(req, res);\n }\n },\n });\n\n expect(database.getBooksByTitle).toHaveBeenCalledWith(mockTitle);\n expect(res.json).toHaveBeenCalledWith(mockBooks);\n });\n });\n});\n").split('\n'),
[{
range: { startLineNumber: 1, startColumn: 1, endLineNumber: 96, endColumn: 1 },
Expand Down Expand Up @@ -337,7 +337,7 @@ function applyEdits(text: string, edits: { range: IRange; text: string }[]): str
class PositionOffsetTransformer {
private readonly lineStartOffsetByLineIdx: number[];

constructor(text: string) {
constructor(private readonly text: string) {
this.lineStartOffsetByLineIdx = [];
this.lineStartOffsetByLineIdx.push(0);
for (let i = 0; i < text.length; i++) {
Expand All @@ -349,7 +349,7 @@ class PositionOffsetTransformer {
}

getOffset(position: Position): number {
const nextLineOffset = this.lineStartOffsetByLineIdx[position.lineNumber];
return Math.min(this.lineStartOffsetByLineIdx[position.lineNumber - 1] + position.column - 1, nextLineOffset - 1);
const maxLineOffset = position.lineNumber >= this.lineStartOffsetByLineIdx.length ? this.text.length : (this.lineStartOffsetByLineIdx[position.lineNumber] - 1);
return Math.min(this.lineStartOffsetByLineIdx[position.lineNumber - 1] + position.column - 1, maxLineOffset);
}
}
94 changes: 94 additions & 0 deletions src/vs/editor/test/node/diffing/fixtures/invalid-diff-bug/1.tst
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const API = require('../src/api');

describe('API', () => {
let api;
let database;

beforeAll(() => {
database = {
getAllBooks: jest.fn(),
getBooksByAuthor: jest.fn(),
getBooksByTitle: jest.fn(),
};
api = new API(database);
});

describe('GET /books', () => {
it('should return all books', async () => {
const mockBooks = [{ title: 'Book 1' }, { title: 'Book 2' }];
database.getAllBooks.mockResolvedValue(mockBooks);

const req = {};
const res = {
json: jest.fn(),
};

await api.register({
get: (path, handler) => {
if (path === '/books') {
handler(req, res);
}
},
});

expect(database.getAllBooks).toHaveBeenCalled();
expect(res.json).toHaveBeenCalledWith(mockBooks);
});
});

describe('GET /books/author/:author', () => {
it('should return books by author', async () => {
const mockAuthor = 'John Doe';
const mockBooks = [{ title: 'Book 1', author: mockAuthor }, { title: 'Book 2', author: mockAuthor }];
database.getBooksByAuthor.mockResolvedValue(mockBooks);

const req = {
params: {
author: mockAuthor,
},
};
const res = {
json: jest.fn(),
};

await api.register({
get: (path, handler) => {
if (path === `/books/author/${mockAuthor}`) {
handler(req, res);
}
},
});

expect(database.getBooksByAuthor).toHaveBeenCalledWith(mockAuthor);
expect(res.json).toHaveBeenCalledWith(mockBooks);
});
});

describe('GET /books/title/:title', () => {
it('should return books by title', async () => {
const mockTitle = 'Book 1';
const mockBooks = [{ title: mockTitle, author: 'John Doe' }];
database.getBooksByTitle.mockResolvedValue(mockBooks);

const req = {
params: {
title: mockTitle,
},
};
const res = {
json: jest.fn(),
};

await api.register({
get: (path, handler) => {
if (path === `/books/title/${mockTitle}`) {
handler(req, res);
}
},
});

expect(database.getBooksByTitle).toHaveBeenCalledWith(mockTitle);
expect(res.json).toHaveBeenCalledWith(mockBooks);
});
});
});
Loading

0 comments on commit e8068a0

Please sign in to comment.