Merge from vscode 6e530127a1bb8ffbd1bfb77dc680c321dc0d71f5 (#6844)

This commit is contained in:
Anthony Dresser
2019-08-20 21:07:47 -07:00
committed by GitHub
parent 1f00249646
commit ecb80f14f0
221 changed files with 3140 additions and 1552 deletions

View File

@@ -202,16 +202,33 @@ export class OutlineGroup extends TreeElement {
}
}
class MovingAverage {
private _n = 1;
private _val = 0;
update(value: number): this {
this._val = this._val + (value - this._val) / this._n;
this._n += 1;
return this;
}
get value(): number {
return this._val;
}
}
export class OutlineModel extends TreeElement {
private static readonly _requestDurations = new LRUCache<string, MovingAverage>(50, 0.7);
private static readonly _requests = new LRUCache<string, { promiseCnt: number, source: CancellationTokenSource, promise: Promise<any>, model: OutlineModel | undefined }>(9, 0.75);
private static readonly _keys = new class {
private _counter = 1;
private _data = new WeakMap<DocumentSymbolProvider, number>();
for(textModel: ITextModel): string {
return `${textModel.id}/${textModel.getVersionId()}/${this._hash(DocumentSymbolProviderRegistry.all(textModel))}`;
for(textModel: ITextModel, version: boolean): string {
return `${textModel.id}/${version ? textModel.getVersionId() : ''}/${this._hash(DocumentSymbolProviderRegistry.all(textModel))}`;
}
private _hash(providers: DocumentSymbolProvider[]): string {
@@ -231,7 +248,7 @@ export class OutlineModel extends TreeElement {
static create(textModel: ITextModel, token: CancellationToken): Promise<OutlineModel> {
let key = this._keys.for(textModel);
let key = this._keys.for(textModel, true);
let data = OutlineModel._requests.get(key);
if (!data) {
@@ -243,6 +260,18 @@ export class OutlineModel extends TreeElement {
model: undefined,
};
OutlineModel._requests.set(key, data);
// keep moving average of request durations
const now = Date.now();
data.promise.then(() => {
let key = this._keys.for(textModel, false);
let avg = this._requestDurations.get(key);
if (!avg) {
avg = new MovingAverage();
this._requestDurations.set(key, avg);
}
avg.update(Date.now() - now);
});
}
if (data!.model) {
@@ -272,7 +301,18 @@ export class OutlineModel extends TreeElement {
});
}
static _create(textModel: ITextModel, token: CancellationToken): Promise<OutlineModel> {
static getRequestDelay(textModel: ITextModel | null): number {
if (!textModel) {
return 350;
}
const avg = this._requestDurations.get(this._keys.for(textModel, false));
if (!avg) {
return 350;
}
return Math.max(350, Math.floor(1.3 * avg.value));
}
private static _create(textModel: ITextModel, token: CancellationToken): Promise<OutlineModel> {
const cts = new CancellationTokenSource(token);
const result = new OutlineModel(textModel);

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { CharCode } from 'vs/base/common/charCode';
import { containsUppercaseCharacter } from 'vs/base/common/strings';
import { buildReplaceStringWithCasePreserved } from 'vs/base/common/search';
const enum ReplacePatternKind {
StaticValue = 0,
@@ -51,17 +51,8 @@ export class ReplacePattern {
public buildReplaceString(matches: string[] | null, preserveCase?: boolean): string {
if (this._state.kind === ReplacePatternKind.StaticValue) {
if (preserveCase && matches && (matches[0] !== '')) {
if (matches[0].toUpperCase() === matches[0]) {
return this._state.staticValue.toUpperCase();
} else if (matches[0].toLowerCase() === matches[0]) {
return this._state.staticValue.toLowerCase();
} else if (containsUppercaseCharacter(matches[0][0])) {
return this._state.staticValue[0].toUpperCase() + this._state.staticValue.substr(1);
} else {
// we don't understand its pattern yet.
return this._state.staticValue;
}
if (preserveCase) {
return buildReplaceStringWithCasePreserved(matches, this._state.staticValue);
} else {
return this._state.staticValue;
}

View File

@@ -5,6 +5,7 @@
import * as assert from 'assert';
import { ReplacePattern, ReplacePiece, parseReplaceString } from 'vs/editor/contrib/find/replacePattern';
import { buildReplaceStringWithCasePreserved } from 'vs/base/common/search';
suite('Replace Pattern test', () => {
@@ -154,6 +155,29 @@ suite('Replace Pattern test', () => {
assert.equal(actual, 'a{}');
});
test('buildReplaceStringWithCasePreserved test', () => {
let replacePattern = 'Def';
let actual: string | string[] = 'abc';
assert.equal(buildReplaceStringWithCasePreserved([actual], replacePattern), 'def');
actual = 'Abc';
assert.equal(buildReplaceStringWithCasePreserved([actual], replacePattern), 'Def');
actual = 'ABC';
assert.equal(buildReplaceStringWithCasePreserved([actual], replacePattern), 'DEF');
actual = ['abc', 'Abc'];
assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'def');
actual = ['Abc', 'abc'];
assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def');
actual = ['ABC', 'abc'];
assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'DEF');
actual = ['AbC'];
assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def');
actual = ['aBC'];
assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def');
});
test('preserve case', () => {
let replacePattern = parseReplaceString('Def');
let actual = replacePattern.buildReplaceString(['abc'], true);

View File

@@ -105,7 +105,11 @@ export class RangesCollector {
}
interface PreviousRegion { indent: number; line: number; marker: boolean; }
interface PreviousRegion {
indent: number; // indent or -2 if a marker
endAbove: number; // end line number for the region above
line: number; // start line of the region. Only used for marker regions.
}
export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, foldingRangesLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT): FoldingRegions {
const tabSize = model.getOptions().tabSize;
@@ -117,16 +121,19 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol
}
let previousRegions: PreviousRegion[] = [];
previousRegions.push({ indent: -1, line: model.getLineCount() + 1, marker: false }); // sentinel, to make sure there's at least one entry
let line = model.getLineCount() + 1;
previousRegions.push({ indent: -1, endAbove: line, line }); // sentinel, to make sure there's at least one entry
for (let line = model.getLineCount(); line > 0; line--) {
let lineContent = model.getLineContent(line);
let indent = TextModel.computeIndentLevel(lineContent, tabSize);
let previous = previousRegions[previousRegions.length - 1];
if (indent === -1) {
if (offSide && !previous.marker) {
// for offSide languages, empty lines are associated to the next block
previous.line = line;
if (offSide) {
// for offSide languages, empty lines are associated to the previous block
// note: the next block is already written to the results, so this only
// impacts the end position of the block before
previous.endAbove = line;
}
continue; // only whitespace
}
@@ -136,7 +143,7 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol
if (m[1]) { // start pattern match
// discard all regions until the folding pattern
let i = previousRegions.length - 1;
while (i > 0 && !previousRegions[i].marker) {
while (i > 0 && previousRegions[i].indent !== -2) {
i--;
}
if (i > 0) {
@@ -145,15 +152,15 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol
// new folding range from pattern, includes the end line
result.insertFirst(line, previous.line, indent);
previous.marker = false;
previous.indent = indent;
previous.line = line;
previous.indent = indent;
previous.endAbove = line;
continue;
} else {
// no end marker found, treat line as a regular line
}
} else { // end pattern match
previousRegions.push({ indent: -2, line, marker: true });
previousRegions.push({ indent: -2, endAbove: line, line });
continue;
}
}
@@ -165,16 +172,16 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol
} while (previous.indent > indent);
// new folding range
let endLineNumber = previous.line - 1;
let endLineNumber = previous.endAbove - 1;
if (endLineNumber - line >= 1) { // needs at east size 1
result.insertFirst(line, endLineNumber, indent);
}
}
if (previous.indent === indent) {
previous.line = line;
previous.endAbove = line;
} else { // previous.indent < indent
// new region with a bigger indent
previousRegions.push({ indent, line, marker: false });
previousRegions.push({ indent, endAbove: line, line });
}
}
return result.toIndentRanges(model);

View File

@@ -316,5 +316,17 @@ suite('Folding with regions', () => {
/* 8*/ '#endregionff',
], [], true, markers);
});
test('Issue 79359', () => {
assertRanges([
/* 1*/ '#region',
/* 2*/ '',
/* 3*/ 'class A',
/* 4*/ ' foo',
/* 5*/ '',
/* 6*/ 'class A',
/* 7*/ ' foo',
/* 8*/ '',
/* 9*/ '#endregion',
], [r(1, 9, -1, true), r(3, 4, 0), r(6, 7, 0)], true, markers);
});
});