mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-20 12:00:24 -04:00
Merge from vscode 6e530127a1bb8ffbd1bfb77dc680c321dc0d71f5 (#6844)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user