mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-24 04:09:28 +00:00
Remove too many folding regions notification (#163854)
* Remove too many folding regions notification * remove duplicate folding limit item for JSON/JSONC * polish * fix test
This commit is contained in:
committed by
GitHub
parent
fb5b553316
commit
c83eff40dd
@@ -90,6 +90,10 @@
|
|||||||
"name": "vs/workbench/contrib/files",
|
"name": "vs/workbench/contrib/files",
|
||||||
"project": "vscode-workbench"
|
"project": "vscode-workbench"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "vs/workbench/contrib/folding",
|
||||||
|
"project": "vscode-workbench"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "vs/workbench/contrib/html",
|
"name": "vs/workbench/contrib/html",
|
||||||
"project": "vscode-workbench"
|
"project": "vscode-workbench"
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
|
|
||||||
|
|
||||||
import { hash } from './utils/hash';
|
import { hash } from './utils/hash';
|
||||||
import { createDocumentColorsLimitItem, createDocumentSymbolsLimitItem, createFoldingRangeLimitItem, createLanguageStatusItem, createLimitStatusItem } from './languageStatus';
|
import { createDocumentColorsLimitItem, createDocumentSymbolsLimitItem, createLanguageStatusItem, createLimitStatusItem } from './languageStatus';
|
||||||
|
|
||||||
namespace VSCodeContentRequest {
|
namespace VSCodeContentRequest {
|
||||||
export const type: RequestType<string, string, any> = new RequestType('vscode/content');
|
export const type: RequestType<string, string, any> = new RequestType('vscode/content');
|
||||||
@@ -60,6 +60,8 @@ type Settings = {
|
|||||||
keepLines?: { enable?: boolean };
|
keepLines?: { enable?: boolean };
|
||||||
validate?: { enable?: boolean };
|
validate?: { enable?: boolean };
|
||||||
resultLimit?: number;
|
resultLimit?: number;
|
||||||
|
jsonFoldingLimit?: number;
|
||||||
|
jsoncFoldingLimit?: number;
|
||||||
};
|
};
|
||||||
http?: {
|
http?: {
|
||||||
proxy?: string;
|
proxy?: string;
|
||||||
@@ -79,6 +81,9 @@ export namespace SettingIds {
|
|||||||
export const enableValidation = 'json.validate.enable';
|
export const enableValidation = 'json.validate.enable';
|
||||||
export const enableSchemaDownload = 'json.schemaDownload.enable';
|
export const enableSchemaDownload = 'json.schemaDownload.enable';
|
||||||
export const maxItemsComputed = 'json.maxItemsComputed';
|
export const maxItemsComputed = 'json.maxItemsComputed';
|
||||||
|
|
||||||
|
export const editorSection = 'editor';
|
||||||
|
export const foldingMaximumRegions = 'foldingMaximumRegions';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TelemetryReporter {
|
export interface TelemetryReporter {
|
||||||
@@ -104,6 +109,8 @@ export interface SchemaRequestService {
|
|||||||
export const languageServerDescription = localize('jsonserver.name', 'JSON Language Server');
|
export const languageServerDescription = localize('jsonserver.name', 'JSON Language Server');
|
||||||
|
|
||||||
let resultLimit = 5000;
|
let resultLimit = 5000;
|
||||||
|
let jsonFoldingLimit = 5000;
|
||||||
|
let jsoncFoldingLimit = 5000;
|
||||||
|
|
||||||
export async function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise<BaseLanguageClient> {
|
export async function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise<BaseLanguageClient> {
|
||||||
|
|
||||||
@@ -123,10 +130,9 @@ export async function startClient(context: ExtensionContext, newLanguageClient:
|
|||||||
|
|
||||||
let isClientReady = false;
|
let isClientReady = false;
|
||||||
|
|
||||||
const foldingRangeLimitStatusBarItem = createLimitStatusItem((limit: number) => createFoldingRangeLimitItem(documentSelector, SettingIds.maxItemsComputed, limit));
|
|
||||||
const documentSymbolsLimitStatusbarItem = createLimitStatusItem((limit: number) => createDocumentSymbolsLimitItem(documentSelector, SettingIds.maxItemsComputed, limit));
|
const documentSymbolsLimitStatusbarItem = createLimitStatusItem((limit: number) => createDocumentSymbolsLimitItem(documentSelector, SettingIds.maxItemsComputed, limit));
|
||||||
const documentColorsLimitStatusbarItem = createLimitStatusItem((limit: number) => createDocumentColorsLimitItem(documentSelector, SettingIds.maxItemsComputed, limit));
|
const documentColorsLimitStatusbarItem = createLimitStatusItem((limit: number) => createDocumentColorsLimitItem(documentSelector, SettingIds.maxItemsComputed, limit));
|
||||||
toDispose.push(foldingRangeLimitStatusBarItem, documentSymbolsLimitStatusbarItem, documentColorsLimitStatusbarItem);
|
toDispose.push(documentSymbolsLimitStatusbarItem, documentColorsLimitStatusbarItem);
|
||||||
|
|
||||||
toDispose.push(commands.registerCommand('json.clearCache', async () => {
|
toDispose.push(commands.registerCommand('json.clearCache', async () => {
|
||||||
if (isClientReady && runtime.schemaRequests.clearCache) {
|
if (isClientReady && runtime.schemaRequests.clearCache) {
|
||||||
@@ -214,20 +220,11 @@ export async function startClient(context: ExtensionContext, newLanguageClient:
|
|||||||
return updateHover(r);
|
return updateHover(r);
|
||||||
},
|
},
|
||||||
provideFoldingRanges(document: TextDocument, context: FoldingContext, token: CancellationToken, next: ProvideFoldingRangeSignature) {
|
provideFoldingRanges(document: TextDocument, context: FoldingContext, token: CancellationToken, next: ProvideFoldingRangeSignature) {
|
||||||
function checkLimit(r: FoldingRange[] | null | undefined): FoldingRange[] | null | undefined {
|
|
||||||
if (Array.isArray(r) && r.length > resultLimit) {
|
|
||||||
r.length = resultLimit; // truncate
|
|
||||||
foldingRangeLimitStatusBarItem.update(document, resultLimit);
|
|
||||||
} else {
|
|
||||||
foldingRangeLimitStatusBarItem.update(document, false);
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
const r = next(document, context, token);
|
const r = next(document, context, token);
|
||||||
if (isThenable<FoldingRange[] | null | undefined>(r)) {
|
if (isThenable<FoldingRange[] | null | undefined>(r)) {
|
||||||
return r.then(checkLimit);
|
return r;
|
||||||
}
|
}
|
||||||
return checkLimit(r);
|
return r;
|
||||||
},
|
},
|
||||||
provideDocumentColors(document: TextDocument, token: CancellationToken, next: ProvideDocumentColorsSignature) {
|
provideDocumentColors(document: TextDocument, token: CancellationToken, next: ProvideDocumentColorsSignature) {
|
||||||
function checkLimit(r: ColorInformation[] | null | undefined): ColorInformation[] | null | undefined {
|
function checkLimit(r: ColorInformation[] | null | undefined): ColorInformation[] | null | undefined {
|
||||||
@@ -472,7 +469,11 @@ function getSettings(): Settings {
|
|||||||
const configuration = workspace.getConfiguration();
|
const configuration = workspace.getConfiguration();
|
||||||
const httpSettings = workspace.getConfiguration('http');
|
const httpSettings = workspace.getConfiguration('http');
|
||||||
|
|
||||||
resultLimit = Math.trunc(Math.max(0, Number(workspace.getConfiguration().get(SettingIds.maxItemsComputed)))) || 5000;
|
const normalizeLimit = (settingValue: any) => Math.trunc(Math.max(0, Number(settingValue))) || 5000;
|
||||||
|
|
||||||
|
resultLimit = normalizeLimit(workspace.getConfiguration().get(SettingIds.maxItemsComputed));
|
||||||
|
jsonFoldingLimit = normalizeLimit(workspace.getConfiguration(SettingIds.editorSection, { languageId: 'json' }).get(SettingIds.foldingMaximumRegions));
|
||||||
|
jsoncFoldingLimit = normalizeLimit(workspace.getConfiguration(SettingIds.editorSection, { languageId: 'jsonc' }).get(SettingIds.foldingMaximumRegions));
|
||||||
|
|
||||||
const settings: Settings = {
|
const settings: Settings = {
|
||||||
http: {
|
http: {
|
||||||
@@ -484,7 +485,9 @@ function getSettings(): Settings {
|
|||||||
format: { enable: configuration.get(SettingIds.enableFormatter) },
|
format: { enable: configuration.get(SettingIds.enableFormatter) },
|
||||||
keepLines: { enable: configuration.get(SettingIds.enableKeepLines) },
|
keepLines: { enable: configuration.get(SettingIds.enableKeepLines) },
|
||||||
schemas: [],
|
schemas: [],
|
||||||
resultLimit: resultLimit + 1 // ask for one more so we can detect if the limit has been exceeded
|
resultLimit: resultLimit + 1, // ask for one more so we can detect if the limit has been exceeded
|
||||||
|
jsonFoldingLimit: jsonFoldingLimit + 1,
|
||||||
|
jsoncFoldingLimit: jsoncFoldingLimit + 1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null);
|
const schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null);
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ export function createLanguageStatusItem(documentSelector: string[], statusReque
|
|||||||
|
|
||||||
async function updateLanguageStatus() {
|
async function updateLanguageStatus() {
|
||||||
const document = window.activeTextEditor?.document;
|
const document = window.activeTextEditor?.document;
|
||||||
if (document && documentSelector.indexOf(document.languageId) !== -1) {
|
if (document) {
|
||||||
try {
|
try {
|
||||||
statusItem.text = '$(loading~spin)';
|
statusItem.text = '$(loading~spin)';
|
||||||
statusItem.detail = localize('pending.detail', 'Loading JSON info');
|
statusItem.detail = localize('pending.detail', 'Loading JSON info');
|
||||||
@@ -205,12 +205,12 @@ export function createLanguageStatusItem(documentSelector: string[], statusReque
|
|||||||
arguments: [{ schemas, uri: document.uri.toString() } as ShowSchemasInput]
|
arguments: [{ schemas, uri: document.uri.toString() } as ShowSchemasInput]
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
statusItem.text = localize('status.error', 'Unable to compute used schemas');
|
statusItem.text = localize('status.error1', 'Unable to compute used schemas: {0}', e.message);
|
||||||
statusItem.detail = undefined;
|
statusItem.detail = undefined;
|
||||||
statusItem.command = undefined;
|
statusItem.command = undefined;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
statusItem.text = localize('status.notJSON', 'Not a JSON editor');
|
statusItem.text = localize('status.error2', 'Unable to compute used schemas: No document');
|
||||||
statusItem.detail = undefined;
|
statusItem.detail = undefined;
|
||||||
statusItem.command = undefined;
|
statusItem.command = undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,8 +68,9 @@ The server supports the following settings:
|
|||||||
- `fileMatch`: an array of file names or paths (separated by `/`). `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern.
|
- `fileMatch`: an array of file names or paths (separated by `/`). `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern.
|
||||||
- `url`: The URL of the schema, optional when also a schema is provided.
|
- `url`: The URL of the schema, optional when also a schema is provided.
|
||||||
- `schema`: The schema content.
|
- `schema`: The schema content.
|
||||||
- `resultLimit`: The max number folding ranges and outline symbols to be computed (for performance reasons)
|
- `resultLimit`: The max number of color decorators and outline symbols to be computed (for performance reasons)
|
||||||
|
- `jsonFoldingLimit`: The max number of folding ranges to be computed for json documents (for performance reasons)
|
||||||
|
- `jsoncFoldingLimit`: The max number of folding ranges to be computed for jsonc documents (for performance reasons)
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"http": {
|
"http": {
|
||||||
@@ -187,7 +188,8 @@ Notification:
|
|||||||
|
|
||||||
### Item Limit
|
### Item Limit
|
||||||
|
|
||||||
If the setting `resultLimit` is set, the JSON language server will limit the number of folding ranges and document symbols computed.
|
If the setting `resultLimit` is set, the JSON language server will limit the number of color symbols and document symbols computed.
|
||||||
|
If the setting `jsonFoldingLimit` or `jsoncFoldingLimit` is set, the JSON language server will limit the number of folding ranges computed.
|
||||||
|
|
||||||
## Try
|
## Try
|
||||||
|
|
||||||
|
|||||||
@@ -106,8 +106,9 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
|||||||
let hierarchicalDocumentSymbolSupport = false;
|
let hierarchicalDocumentSymbolSupport = false;
|
||||||
|
|
||||||
let foldingRangeLimitDefault = Number.MAX_VALUE;
|
let foldingRangeLimitDefault = Number.MAX_VALUE;
|
||||||
let foldingRangeLimit = Number.MAX_VALUE;
|
|
||||||
let resultLimit = Number.MAX_VALUE;
|
let resultLimit = Number.MAX_VALUE;
|
||||||
|
let jsonFoldingRangeLimit = Number.MAX_VALUE;
|
||||||
|
let jsoncFoldingRangeLimit = Number.MAX_VALUE;
|
||||||
let formatterMaxNumberOfEdits = Number.MAX_VALUE;
|
let formatterMaxNumberOfEdits = Number.MAX_VALUE;
|
||||||
|
|
||||||
let diagnosticsSupport: DiagnosticsSupport | undefined;
|
let diagnosticsSupport: DiagnosticsSupport | undefined;
|
||||||
@@ -187,6 +188,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
|||||||
keepLines?: { enable?: boolean };
|
keepLines?: { enable?: boolean };
|
||||||
validate?: { enable?: boolean };
|
validate?: { enable?: boolean };
|
||||||
resultLimit?: number;
|
resultLimit?: number;
|
||||||
|
jsonFoldingLimit?: number;
|
||||||
|
jsoncFoldingLimit?: number;
|
||||||
};
|
};
|
||||||
http?: {
|
http?: {
|
||||||
proxy?: string;
|
proxy?: string;
|
||||||
@@ -217,8 +220,10 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
|||||||
keepLinesEnabled = settings.json?.keepLines?.enable || false;
|
keepLinesEnabled = settings.json?.keepLines?.enable || false;
|
||||||
updateConfiguration();
|
updateConfiguration();
|
||||||
|
|
||||||
foldingRangeLimit = Math.trunc(Math.max(settings.json?.resultLimit || foldingRangeLimitDefault, 0));
|
const sanitizeLimitSetting = (settingValue: any) => Math.trunc(Math.max(settingValue, 0));
|
||||||
resultLimit = Math.trunc(Math.max(settings.json?.resultLimit || Number.MAX_VALUE, 0));
|
resultLimit = sanitizeLimitSetting(settings.json?.resultLimit || Number.MAX_VALUE);
|
||||||
|
jsonFoldingRangeLimit = sanitizeLimitSetting(settings.json?.jsonFoldingLimit || foldingRangeLimitDefault);
|
||||||
|
jsoncFoldingRangeLimit = sanitizeLimitSetting(settings.json?.jsoncFoldingLimit || foldingRangeLimitDefault);
|
||||||
|
|
||||||
// dynamically enable & disable the formatter
|
// dynamically enable & disable the formatter
|
||||||
if (dynamicFormatterRegistration) {
|
if (dynamicFormatterRegistration) {
|
||||||
@@ -437,7 +442,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
|||||||
return runSafe(runtime, () => {
|
return runSafe(runtime, () => {
|
||||||
const document = documents.get(params.textDocument.uri);
|
const document = documents.get(params.textDocument.uri);
|
||||||
if (document) {
|
if (document) {
|
||||||
return languageService.getFoldingRanges(document, { rangeLimit: foldingRangeLimit });
|
const rangeLimit = document.languageId === 'jsonc' ? jsoncFoldingRangeLimit : jsonFoldingRangeLimit;
|
||||||
|
return languageService.getFoldingRanges(document, { rangeLimit });
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token);
|
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token);
|
||||||
|
|||||||
@@ -35,17 +35,16 @@ import { foldingCollapsedIcon, FoldingDecorationProvider, foldingExpandedIcon, f
|
|||||||
import { FoldingRegion, FoldingRegions, FoldRange, FoldSource, ILineRange } from './foldingRanges';
|
import { FoldingRegion, FoldingRegions, FoldRange, FoldSource, ILineRange } from './foldingRanges';
|
||||||
import { SyntaxRangeProvider } from './syntaxRangeProvider';
|
import { SyntaxRangeProvider } from './syntaxRangeProvider';
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
import Severity from 'vs/base/common/severity';
|
|
||||||
import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce';
|
import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce';
|
||||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||||
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
|
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
|
||||||
|
import { Emitter, Event } from 'vs/base/common/event';
|
||||||
|
|
||||||
const CONTEXT_FOLDING_ENABLED = new RawContextKey<boolean>('foldingEnabled', false);
|
const CONTEXT_FOLDING_ENABLED = new RawContextKey<boolean>('foldingEnabled', false);
|
||||||
|
|
||||||
export interface RangeProvider {
|
export interface RangeProvider {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
compute(cancelationToken: CancellationToken, notifyTooMany: (max: number) => void): Promise<FoldingRegions | null>;
|
compute(cancelationToken: CancellationToken): Promise<FoldingRegions | null>;
|
||||||
dispose(): void;
|
dispose(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,6 +55,16 @@ interface FoldingStateMemento {
|
|||||||
foldedImports?: boolean;
|
foldedImports?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FoldingLimitReporter {
|
||||||
|
readonly limit: number;
|
||||||
|
report(limitInfo: FoldingLimitInfo): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FoldingLimitInfo {
|
||||||
|
computed: number;
|
||||||
|
limited: number | false;
|
||||||
|
}
|
||||||
|
|
||||||
export class FoldingController extends Disposable implements IEditorContribution {
|
export class FoldingController extends Disposable implements IEditorContribution {
|
||||||
|
|
||||||
public static readonly ID = 'editor.contrib.folding';
|
public static readonly ID = 'editor.contrib.folding';
|
||||||
@@ -71,9 +80,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||||||
private _restoringViewState: boolean;
|
private _restoringViewState: boolean;
|
||||||
private _foldingImportsByDefault: boolean;
|
private _foldingImportsByDefault: boolean;
|
||||||
private _currentModelHasFoldedImports: boolean;
|
private _currentModelHasFoldedImports: boolean;
|
||||||
private _maxFoldingRegions: number;
|
private _foldingLimitReporter: FoldingLimitReporter;
|
||||||
private _notifyTooManyRegions: (m: number) => void;
|
|
||||||
private _tooManyRegionsNotified = false;
|
|
||||||
|
|
||||||
private readonly foldingDecorationProvider: FoldingDecorationProvider;
|
private readonly foldingDecorationProvider: FoldingDecorationProvider;
|
||||||
|
|
||||||
@@ -93,6 +100,14 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||||||
private readonly localToDispose = this._register(new DisposableStore());
|
private readonly localToDispose = this._register(new DisposableStore());
|
||||||
private mouseDownInfo: { lineNumber: number; iconClicked: boolean } | null;
|
private mouseDownInfo: { lineNumber: number; iconClicked: boolean } | null;
|
||||||
|
|
||||||
|
private _onDidChangeFoldingLimit = new Emitter<FoldingLimitInfo>();
|
||||||
|
public readonly onDidChangeFoldingLimit: Event<FoldingLimitInfo> = this._onDidChangeFoldingLimit.event;
|
||||||
|
|
||||||
|
private _foldingLimitInfo: FoldingLimitInfo | undefined;
|
||||||
|
public get foldingLimitInfo() {
|
||||||
|
return this._foldingLimitInfo;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
editor: ICodeEditor,
|
editor: ICodeEditor,
|
||||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||||
@@ -110,7 +125,17 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||||||
this._restoringViewState = false;
|
this._restoringViewState = false;
|
||||||
this._currentModelHasFoldedImports = false;
|
this._currentModelHasFoldedImports = false;
|
||||||
this._foldingImportsByDefault = options.get(EditorOption.foldingImportsByDefault);
|
this._foldingImportsByDefault = options.get(EditorOption.foldingImportsByDefault);
|
||||||
this._maxFoldingRegions = options.get(EditorOption.foldingMaximumRegions);
|
this._foldingLimitReporter = {
|
||||||
|
get limit() {
|
||||||
|
return editor.getOptions().get(EditorOption.foldingMaximumRegions);
|
||||||
|
},
|
||||||
|
report: (info: FoldingLimitInfo) => {
|
||||||
|
if (!this._foldingLimitInfo || (info.limited !== this._foldingLimitInfo.limited)) {
|
||||||
|
this._foldingLimitInfo = info;
|
||||||
|
this._onDidChangeFoldingLimit.fire(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
this.updateDebounceInfo = languageFeatureDebounceService.for(languageFeaturesService.foldingRangeProvider, 'Folding', { min: 200 });
|
this.updateDebounceInfo = languageFeatureDebounceService.for(languageFeaturesService.foldingRangeProvider, 'Folding', { min: 200 });
|
||||||
|
|
||||||
this.foldingModel = null;
|
this.foldingModel = null;
|
||||||
@@ -128,18 +153,6 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||||||
this.foldingEnabled = CONTEXT_FOLDING_ENABLED.bindTo(this.contextKeyService);
|
this.foldingEnabled = CONTEXT_FOLDING_ENABLED.bindTo(this.contextKeyService);
|
||||||
this.foldingEnabled.set(this._isEnabled);
|
this.foldingEnabled.set(this._isEnabled);
|
||||||
|
|
||||||
this._notifyTooManyRegions = (maxFoldingRegions: number) => {
|
|
||||||
// Message will display once per time vscode runs. Once per file would be tricky.
|
|
||||||
if (!this._tooManyRegionsNotified) {
|
|
||||||
notificationService.notify({
|
|
||||||
severity: Severity.Warning,
|
|
||||||
sticky: true,
|
|
||||||
message: nls.localize('maximum fold ranges', "The number of foldable regions is limited to a maximum of {0}. Increase configuration option ['Folding Maximum Regions'](command:workbench.action.openSettings?[\"editor.foldingMaximumRegions\"]) to enable more.", maxFoldingRegions)
|
|
||||||
});
|
|
||||||
this._tooManyRegionsNotified = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this._register(this.editor.onDidChangeModel(() => this.onModelChanged()));
|
this._register(this.editor.onDidChangeModel(() => this.onModelChanged()));
|
||||||
|
|
||||||
this._register(this.editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => {
|
this._register(this.editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => {
|
||||||
@@ -149,8 +162,6 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||||||
this.onModelChanged();
|
this.onModelChanged();
|
||||||
}
|
}
|
||||||
if (e.hasChanged(EditorOption.foldingMaximumRegions)) {
|
if (e.hasChanged(EditorOption.foldingMaximumRegions)) {
|
||||||
this._maxFoldingRegions = this.editor.getOptions().get(EditorOption.foldingMaximumRegions);
|
|
||||||
this._tooManyRegionsNotified = false;
|
|
||||||
this.onModelChanged();
|
this.onModelChanged();
|
||||||
}
|
}
|
||||||
if (e.hasChanged(EditorOption.showFoldingControls) || e.hasChanged(EditorOption.foldingHighlight)) {
|
if (e.hasChanged(EditorOption.showFoldingControls) || e.hasChanged(EditorOption.foldingHighlight)) {
|
||||||
@@ -268,17 +279,17 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||||||
if (this.rangeProvider) {
|
if (this.rangeProvider) {
|
||||||
return this.rangeProvider;
|
return this.rangeProvider;
|
||||||
}
|
}
|
||||||
this.rangeProvider = new IndentRangeProvider(editorModel, this.languageConfigurationService, this._maxFoldingRegions); // fallback
|
this.rangeProvider = new IndentRangeProvider(editorModel, this.languageConfigurationService, this._foldingLimitReporter); // fallback
|
||||||
if (this._useFoldingProviders && this.foldingModel) {
|
if (this._useFoldingProviders && this.foldingModel) {
|
||||||
const foldingProviders = this.languageFeaturesService.foldingRangeProvider.ordered(this.foldingModel.textModel);
|
const foldingProviders = this.languageFeaturesService.foldingRangeProvider.ordered(this.foldingModel.textModel);
|
||||||
if (foldingProviders.length > 0) {
|
if (foldingProviders.length > 0) {
|
||||||
this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders, () => this.triggerFoldingModelChanged(), this._maxFoldingRegions);
|
this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders, () => this.triggerFoldingModelChanged(), this._foldingLimitReporter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.rangeProvider;
|
return this.rangeProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getFoldingModel() {
|
public getFoldingModel(): Promise<FoldingModel | null> | null {
|
||||||
return this.foldingModelPromise;
|
return this.foldingModelPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +298,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||||||
this.triggerFoldingModelChanged();
|
this.triggerFoldingModelChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public triggerFoldingModelChanged() {
|
public triggerFoldingModelChanged() {
|
||||||
if (this.updateScheduler) {
|
if (this.updateScheduler) {
|
||||||
if (this.foldingRegionPromise) {
|
if (this.foldingRegionPromise) {
|
||||||
@@ -300,7 +312,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
|||||||
}
|
}
|
||||||
const sw = new StopWatch(true);
|
const sw = new StopWatch(true);
|
||||||
const provider = this.getRangeProvider(foldingModel.textModel);
|
const provider = this.getRangeProvider(foldingModel.textModel);
|
||||||
const foldingRegionPromise = this.foldingRegionPromise = createCancelablePromise(token => provider.compute(token, this._notifyTooManyRegions));
|
const foldingRegionPromise = this.foldingRegionPromise = createCancelablePromise(token => provider.compute(token));
|
||||||
return foldingRegionPromise.then(foldingRanges => {
|
return foldingRegionPromise.then(foldingRanges => {
|
||||||
if (foldingRanges && foldingRegionPromise === this.foldingRegionPromise) { // new request or cancelled in the meantime?
|
if (foldingRanges && foldingRegionPromise === this.foldingRegionPromise) { // new request or cancelled in the meantime?
|
||||||
let scrollState: StableEditorScrollState | undefined;
|
let scrollState: StableEditorScrollState | undefined;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { computeIndentLevel } from 'vs/editor/common/model/utils';
|
|||||||
import { FoldingMarkers } from 'vs/editor/common/languages/languageConfiguration';
|
import { FoldingMarkers } from 'vs/editor/common/languages/languageConfiguration';
|
||||||
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
|
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
|
||||||
import { FoldingRegions, MAX_LINE_NUMBER } from 'vs/editor/contrib/folding/browser/foldingRanges';
|
import { FoldingRegions, MAX_LINE_NUMBER } from 'vs/editor/contrib/folding/browser/foldingRanges';
|
||||||
import { RangeProvider } from './folding';
|
import { FoldingLimitReporter, RangeProvider } from './folding';
|
||||||
|
|
||||||
const MAX_FOLDING_REGIONS_FOR_INDENT_DEFAULT = 5000;
|
const MAX_FOLDING_REGIONS_FOR_INDENT_DEFAULT = 5000;
|
||||||
|
|
||||||
@@ -21,16 +21,16 @@ export class IndentRangeProvider implements RangeProvider {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly editorModel: ITextModel,
|
private readonly editorModel: ITextModel,
|
||||||
private readonly languageConfigurationService: ILanguageConfigurationService,
|
private readonly languageConfigurationService: ILanguageConfigurationService,
|
||||||
private readonly maxFoldingRegions: number
|
private readonly foldingRangesLimit: FoldingLimitReporter
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
dispose() { }
|
dispose() { }
|
||||||
|
|
||||||
compute(cancelationToken: CancellationToken, notifyTooManyRegions: (maxRegions: number) => void): Promise<FoldingRegions> {
|
compute(cancelationToken: CancellationToken,): Promise<FoldingRegions> {
|
||||||
const foldingRules = this.languageConfigurationService.getLanguageConfiguration(this.editorModel.getLanguageId()).foldingRules;
|
const foldingRules = this.languageConfigurationService.getLanguageConfiguration(this.editorModel.getLanguageId()).foldingRules;
|
||||||
const offSide = foldingRules && !!foldingRules.offSide;
|
const offSide = foldingRules && !!foldingRules.offSide;
|
||||||
const markers = foldingRules && foldingRules.markers;
|
const markers = foldingRules && foldingRules.markers;
|
||||||
return Promise.resolve(computeRanges(this.editorModel, offSide, markers, this.maxFoldingRegions, notifyTooManyRegions));
|
return Promise.resolve(computeRanges(this.editorModel, offSide, markers, this.foldingRangesLimit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,9 +40,9 @@ export class RangesCollector {
|
|||||||
private readonly _endIndexes: number[];
|
private readonly _endIndexes: number[];
|
||||||
private readonly _indentOccurrences: number[];
|
private readonly _indentOccurrences: number[];
|
||||||
private _length: number;
|
private _length: number;
|
||||||
private readonly _foldingRangesLimit: number;
|
private readonly _foldingRangesLimit: FoldingLimitReporter;
|
||||||
|
|
||||||
constructor(foldingRangesLimit: number, private readonly _notifyTooManyRegions?: (maxRegions: number) => void) {
|
constructor(foldingRangesLimit: FoldingLimitReporter) {
|
||||||
this._startIndexes = [];
|
this._startIndexes = [];
|
||||||
this._endIndexes = [];
|
this._endIndexes = [];
|
||||||
this._indentOccurrences = [];
|
this._indentOccurrences = [];
|
||||||
@@ -64,7 +64,10 @@ export class RangesCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public toIndentRanges(model: ITextModel) {
|
public toIndentRanges(model: ITextModel) {
|
||||||
if (this._length <= this._foldingRangesLimit) {
|
const limit = this._foldingRangesLimit.limit;
|
||||||
|
if (this._length <= limit) {
|
||||||
|
this._foldingRangesLimit.report({ limited: false, computed: this._length });
|
||||||
|
|
||||||
// reverse and create arrays of the exact length
|
// reverse and create arrays of the exact length
|
||||||
const startIndexes = new Uint32Array(this._length);
|
const startIndexes = new Uint32Array(this._length);
|
||||||
const endIndexes = new Uint32Array(this._length);
|
const endIndexes = new Uint32Array(this._length);
|
||||||
@@ -74,13 +77,14 @@ export class RangesCollector {
|
|||||||
}
|
}
|
||||||
return new FoldingRegions(startIndexes, endIndexes);
|
return new FoldingRegions(startIndexes, endIndexes);
|
||||||
} else {
|
} else {
|
||||||
this._notifyTooManyRegions?.(this._foldingRangesLimit);
|
this._foldingRangesLimit.report({ limited: limit, computed: this._length });
|
||||||
|
|
||||||
let entries = 0;
|
let entries = 0;
|
||||||
let maxIndent = this._indentOccurrences.length;
|
let maxIndent = this._indentOccurrences.length;
|
||||||
for (let i = 0; i < this._indentOccurrences.length; i++) {
|
for (let i = 0; i < this._indentOccurrences.length; i++) {
|
||||||
const n = this._indentOccurrences[i];
|
const n = this._indentOccurrences[i];
|
||||||
if (n) {
|
if (n) {
|
||||||
if (n + entries > this._foldingRangesLimit) {
|
if (n + entries > limit) {
|
||||||
maxIndent = i;
|
maxIndent = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -89,13 +93,13 @@ export class RangesCollector {
|
|||||||
}
|
}
|
||||||
const tabSize = model.getOptions().tabSize;
|
const tabSize = model.getOptions().tabSize;
|
||||||
// reverse and create arrays of the exact length
|
// reverse and create arrays of the exact length
|
||||||
const startIndexes = new Uint32Array(this._foldingRangesLimit);
|
const startIndexes = new Uint32Array(limit);
|
||||||
const endIndexes = new Uint32Array(this._foldingRangesLimit);
|
const endIndexes = new Uint32Array(limit);
|
||||||
for (let i = this._length - 1, k = 0; i >= 0; i--) {
|
for (let i = this._length - 1, k = 0; i >= 0; i--) {
|
||||||
const startIndex = this._startIndexes[i];
|
const startIndex = this._startIndexes[i];
|
||||||
const lineContent = model.getLineContent(startIndex);
|
const lineContent = model.getLineContent(startIndex);
|
||||||
const indent = computeIndentLevel(lineContent, tabSize);
|
const indent = computeIndentLevel(lineContent, tabSize);
|
||||||
if (indent < maxIndent || (indent === maxIndent && entries++ < this._foldingRangesLimit)) {
|
if (indent < maxIndent || (indent === maxIndent && entries++ < limit)) {
|
||||||
startIndexes[k] = startIndex;
|
startIndexes[k] = startIndex;
|
||||||
endIndexes[k] = this._endIndexes[i];
|
endIndexes[k] = this._endIndexes[i];
|
||||||
k++;
|
k++;
|
||||||
@@ -114,10 +118,14 @@ interface PreviousRegion {
|
|||||||
line: number; // start line of the region. Only used for marker regions.
|
line: number; // start line of the region. Only used for marker regions.
|
||||||
}
|
}
|
||||||
|
|
||||||
export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, foldingRangesLimit?: number, notifyTooManyRegions?: (maxRegions: number) => void): FoldingRegions {
|
const foldingRangesLimitDefault: FoldingLimitReporter = {
|
||||||
|
limit: MAX_FOLDING_REGIONS_FOR_INDENT_DEFAULT,
|
||||||
|
report: () => { }
|
||||||
|
};
|
||||||
|
|
||||||
|
export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, foldingRangesLimit: FoldingLimitReporter = foldingRangesLimitDefault): FoldingRegions {
|
||||||
const tabSize = model.getOptions().tabSize;
|
const tabSize = model.getOptions().tabSize;
|
||||||
foldingRangesLimit = foldingRangesLimit ?? MAX_FOLDING_REGIONS_FOR_INDENT_DEFAULT;
|
const result = new RangesCollector(foldingRangesLimit);
|
||||||
const result = new RangesCollector(foldingRangesLimit, notifyTooManyRegions);
|
|
||||||
|
|
||||||
let pattern: RegExp | undefined = undefined;
|
let pattern: RegExp | undefined = undefined;
|
||||||
if (markers) {
|
if (markers) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { onUnexpectedExternalError } from 'vs/base/common/errors';
|
|||||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||||
import { ITextModel } from 'vs/editor/common/model';
|
import { ITextModel } from 'vs/editor/common/model';
|
||||||
import { FoldingContext, FoldingRange, FoldingRangeProvider } from 'vs/editor/common/languages';
|
import { FoldingContext, FoldingRange, FoldingRangeProvider } from 'vs/editor/common/languages';
|
||||||
import { RangeProvider } from './folding';
|
import { FoldingLimitReporter, RangeProvider } from './folding';
|
||||||
import { FoldingRegions, MAX_LINE_NUMBER } from './foldingRanges';
|
import { FoldingRegions, MAX_LINE_NUMBER } from './foldingRanges';
|
||||||
|
|
||||||
export interface IFoldingRangeData extends FoldingRange {
|
export interface IFoldingRangeData extends FoldingRange {
|
||||||
@@ -26,7 +26,12 @@ export class SyntaxRangeProvider implements RangeProvider {
|
|||||||
|
|
||||||
readonly disposables: DisposableStore | undefined;
|
readonly disposables: DisposableStore | undefined;
|
||||||
|
|
||||||
constructor(private readonly editorModel: ITextModel, private providers: FoldingRangeProvider[], handleFoldingRangesChange: () => void, private limit: number) {
|
constructor(
|
||||||
|
private readonly editorModel: ITextModel,
|
||||||
|
private readonly providers: FoldingRangeProvider[],
|
||||||
|
readonly handleFoldingRangesChange: () => void,
|
||||||
|
private readonly foldingRangesLimit: FoldingLimitReporter
|
||||||
|
) {
|
||||||
for (const provider of providers) {
|
for (const provider of providers) {
|
||||||
if (typeof provider.onDidChange === 'function') {
|
if (typeof provider.onDidChange === 'function') {
|
||||||
if (!this.disposables) {
|
if (!this.disposables) {
|
||||||
@@ -37,10 +42,10 @@ export class SyntaxRangeProvider implements RangeProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compute(cancellationToken: CancellationToken, notifyTooManyRegions?: (maxRegions: number) => void): Promise<FoldingRegions | null> {
|
compute(cancellationToken: CancellationToken): Promise<FoldingRegions | null> {
|
||||||
return collectSyntaxRanges(this.providers, this.editorModel, cancellationToken).then(ranges => {
|
return collectSyntaxRanges(this.providers, this.editorModel, cancellationToken).then(ranges => {
|
||||||
if (ranges) {
|
if (ranges) {
|
||||||
const res = sanitizeRanges(ranges, this.limit, notifyTooManyRegions);
|
const res = sanitizeRanges(ranges, this.foldingRangesLimit);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -84,9 +89,9 @@ export class RangesCollector {
|
|||||||
private readonly _nestingLevelCounts: number[];
|
private readonly _nestingLevelCounts: number[];
|
||||||
private readonly _types: Array<string | undefined>;
|
private readonly _types: Array<string | undefined>;
|
||||||
private _length: number;
|
private _length: number;
|
||||||
private readonly _foldingRangesLimit: number;
|
private readonly _foldingRangesLimit: FoldingLimitReporter;
|
||||||
|
|
||||||
constructor(foldingRangesLimit: number, private readonly _notifyTooManyRegions?: (maxRegions: number) => void) {
|
constructor(foldingRangesLimit: FoldingLimitReporter) {
|
||||||
this._startIndexes = [];
|
this._startIndexes = [];
|
||||||
this._endIndexes = [];
|
this._endIndexes = [];
|
||||||
this._nestingLevels = [];
|
this._nestingLevels = [];
|
||||||
@@ -112,7 +117,10 @@ export class RangesCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public toIndentRanges() {
|
public toIndentRanges() {
|
||||||
if (this._length <= this._foldingRangesLimit) {
|
const limit = this._foldingRangesLimit.limit;
|
||||||
|
if (this._length <= limit) {
|
||||||
|
this._foldingRangesLimit.report({ limited: false, computed: this._length });
|
||||||
|
|
||||||
const startIndexes = new Uint32Array(this._length);
|
const startIndexes = new Uint32Array(this._length);
|
||||||
const endIndexes = new Uint32Array(this._length);
|
const endIndexes = new Uint32Array(this._length);
|
||||||
for (let i = 0; i < this._length; i++) {
|
for (let i = 0; i < this._length; i++) {
|
||||||
@@ -121,13 +129,14 @@ export class RangesCollector {
|
|||||||
}
|
}
|
||||||
return new FoldingRegions(startIndexes, endIndexes, this._types);
|
return new FoldingRegions(startIndexes, endIndexes, this._types);
|
||||||
} else {
|
} else {
|
||||||
this._notifyTooManyRegions?.(this._foldingRangesLimit);
|
this._foldingRangesLimit.report({ limited: limit, computed: this._length });
|
||||||
|
|
||||||
let entries = 0;
|
let entries = 0;
|
||||||
let maxLevel = this._nestingLevelCounts.length;
|
let maxLevel = this._nestingLevelCounts.length;
|
||||||
for (let i = 0; i < this._nestingLevelCounts.length; i++) {
|
for (let i = 0; i < this._nestingLevelCounts.length; i++) {
|
||||||
const n = this._nestingLevelCounts[i];
|
const n = this._nestingLevelCounts[i];
|
||||||
if (n) {
|
if (n) {
|
||||||
if (n + entries > this._foldingRangesLimit) {
|
if (n + entries > limit) {
|
||||||
maxLevel = i;
|
maxLevel = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -135,12 +144,12 @@ export class RangesCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const startIndexes = new Uint32Array(this._foldingRangesLimit);
|
const startIndexes = new Uint32Array(limit);
|
||||||
const endIndexes = new Uint32Array(this._foldingRangesLimit);
|
const endIndexes = new Uint32Array(limit);
|
||||||
const types: Array<string | undefined> = [];
|
const types: Array<string | undefined> = [];
|
||||||
for (let i = 0, k = 0; i < this._length; i++) {
|
for (let i = 0, k = 0; i < this._length; i++) {
|
||||||
const level = this._nestingLevels[i];
|
const level = this._nestingLevels[i];
|
||||||
if (level < maxLevel || (level === maxLevel && entries++ < this._foldingRangesLimit)) {
|
if (level < maxLevel || (level === maxLevel && entries++ < limit)) {
|
||||||
startIndexes[k] = this._startIndexes[i];
|
startIndexes[k] = this._startIndexes[i];
|
||||||
endIndexes[k] = this._endIndexes[i];
|
endIndexes[k] = this._endIndexes[i];
|
||||||
types[k] = this._types[i];
|
types[k] = this._types[i];
|
||||||
@@ -154,7 +163,7 @@ export class RangesCollector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sanitizeRanges(rangeData: IFoldingRangeData[], limit: number, notifyTooManyRegions?: (maxRegions: number) => void): FoldingRegions {
|
export function sanitizeRanges(rangeData: IFoldingRangeData[], foldingRangesLimit: FoldingLimitReporter): FoldingRegions {
|
||||||
const sorted = rangeData.sort((d1, d2) => {
|
const sorted = rangeData.sort((d1, d2) => {
|
||||||
let diff = d1.start - d2.start;
|
let diff = d1.start - d2.start;
|
||||||
if (diff === 0) {
|
if (diff === 0) {
|
||||||
@@ -162,7 +171,7 @@ export function sanitizeRanges(rangeData: IFoldingRangeData[], limit: number, no
|
|||||||
}
|
}
|
||||||
return diff;
|
return diff;
|
||||||
});
|
});
|
||||||
const collector = new RangesCollector(limit, notifyTooManyRegions);
|
const collector = new RangesCollector(foldingRangesLimit);
|
||||||
|
|
||||||
let top: IFoldingRangeData | undefined = undefined;
|
let top: IFoldingRangeData | undefined = undefined;
|
||||||
const previous: IFoldingRangeData[] = [];
|
const previous: IFoldingRangeData[] = [];
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ suite('FoldingRanges', () => {
|
|||||||
lines.push('#endregion');
|
lines.push('#endregion');
|
||||||
}
|
}
|
||||||
const model = createTextModel(lines.join('\n'));
|
const model = createTextModel(lines.join('\n'));
|
||||||
const actual = computeRanges(model, false, markers, MAX_FOLDING_REGIONS);
|
const actual = computeRanges(model, false, markers, { limit: MAX_FOLDING_REGIONS, report: () => { } });
|
||||||
assert.strictEqual(actual.length, nRegions, 'len');
|
assert.strictEqual(actual.length, nRegions, 'len');
|
||||||
for (let i = 0; i < nRegions; i++) {
|
for (let i = 0; i < nRegions; i++) {
|
||||||
assert.strictEqual(actual.getStartLineNumber(i), i + 1, 'start' + i);
|
assert.strictEqual(actual.getStartLineNumber(i), i + 1, 'start' + i);
|
||||||
@@ -108,7 +108,7 @@ suite('FoldingRanges', () => {
|
|||||||
lines.push('#endregion');
|
lines.push('#endregion');
|
||||||
}
|
}
|
||||||
const model = createTextModel(lines.join('\n'));
|
const model = createTextModel(lines.join('\n'));
|
||||||
const actual = computeRanges(model, false, markers, MAX_FOLDING_REGIONS);
|
const actual = computeRanges(model, false, markers);
|
||||||
assert.strictEqual(actual.length, nRegions, 'len');
|
assert.strictEqual(actual.length, nRegions, 'len');
|
||||||
for (let i = 0; i < nRegions; i++) {
|
for (let i = 0; i < nRegions; i++) {
|
||||||
actual.setCollapsed(i, i % 3 === 0);
|
actual.setCollapsed(i, i % 3 === 0);
|
||||||
|
|||||||
@@ -50,13 +50,15 @@ suite('Indentation Folding', () => {
|
|||||||
const model = createTextModel(lines.join('\n'));
|
const model = createTextModel(lines.join('\n'));
|
||||||
|
|
||||||
function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) {
|
function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) {
|
||||||
const indentRanges = computeRanges(model, true, undefined, maxEntries);
|
let reported: number | false = false;
|
||||||
|
const indentRanges = computeRanges(model, true, undefined, { limit: maxEntries, report: r => reported = r.limited });
|
||||||
assert.ok(indentRanges.length <= maxEntries, 'max ' + message);
|
assert.ok(indentRanges.length <= maxEntries, 'max ' + message);
|
||||||
const actual: IndentRange[] = [];
|
const actual: IndentRange[] = [];
|
||||||
for (let i = 0; i < indentRanges.length; i++) {
|
for (let i = 0; i < indentRanges.length; i++) {
|
||||||
actual.push({ start: indentRanges.getStartLineNumber(i), end: indentRanges.getEndLineNumber(i) });
|
actual.push({ start: indentRanges.getStartLineNumber(i), end: indentRanges.getEndLineNumber(i) });
|
||||||
}
|
}
|
||||||
assert.deepStrictEqual(actual, expectedRanges, message);
|
assert.deepStrictEqual(actual, expectedRanges, message);
|
||||||
|
assert.equal(reported, 9 <= maxEntries ? false : maxEntries, 'limited');
|
||||||
}
|
}
|
||||||
|
|
||||||
assertLimit(1000, [r1, r2, r3, r4, r5, r6, r7, r8, r9], '1000');
|
assertLimit(1000, [r1, r2, r3, r4, r5, r6, r7, r8, r9], '1000');
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { ITextModel } from 'vs/editor/common/model';
|
|||||||
import { FoldingContext, FoldingRange, FoldingRangeProvider, ProviderResult } from 'vs/editor/common/languages';
|
import { FoldingContext, FoldingRange, FoldingRangeProvider, ProviderResult } from 'vs/editor/common/languages';
|
||||||
import { SyntaxRangeProvider } from 'vs/editor/contrib/folding/browser/syntaxRangeProvider';
|
import { SyntaxRangeProvider } from 'vs/editor/contrib/folding/browser/syntaxRangeProvider';
|
||||||
import { createTextModel } from 'vs/editor/test/common/testTextModel';
|
import { createTextModel } from 'vs/editor/test/common/testTextModel';
|
||||||
|
import { FoldingLimitReporter } from 'vs/editor/contrib/folding/browser/folding';
|
||||||
|
|
||||||
interface IndentRange {
|
interface IndentRange {
|
||||||
start: number;
|
start: number;
|
||||||
@@ -74,14 +75,18 @@ suite('Syntax folding', () => {
|
|||||||
const providers = [new TestFoldingRangeProvider(model, ranges)];
|
const providers = [new TestFoldingRangeProvider(model, ranges)];
|
||||||
|
|
||||||
async function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) {
|
async function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) {
|
||||||
const indentRanges = await new SyntaxRangeProvider(model, providers, () => { }, maxEntries).compute(CancellationToken.None);
|
let reported: number | false = false;
|
||||||
|
const foldingRangesLimit: FoldingLimitReporter = { limit: maxEntries, report: r => reported = r.limited };
|
||||||
|
const indentRanges = await new SyntaxRangeProvider(model, providers, () => { }, foldingRangesLimit).compute(CancellationToken.None);
|
||||||
const actual: IndentRange[] = [];
|
const actual: IndentRange[] = [];
|
||||||
if (indentRanges) {
|
if (indentRanges) {
|
||||||
for (let i = 0; i < indentRanges.length; i++) {
|
for (let i = 0; i < indentRanges.length; i++) {
|
||||||
actual.push({ start: indentRanges.getStartLineNumber(i), end: indentRanges.getEndLineNumber(i) });
|
actual.push({ start: indentRanges.getStartLineNumber(i), end: indentRanges.getEndLineNumber(i) });
|
||||||
}
|
}
|
||||||
|
assert.equal(reported, 9 <= maxEntries ? false : maxEntries, 'limited');
|
||||||
}
|
}
|
||||||
assert.deepStrictEqual(actual, expectedRanges, message);
|
assert.deepStrictEqual(actual, expectedRanges, message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await assertLimit(1000, [r1, r2, r3, r4, r5, r6, r7, r8, r9], '1000');
|
await assertLimit(1000, [r1, r2, r3, r4, r5, r6, r7, r8, r9], '1000');
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||||
|
import Severity from 'vs/base/common/severity';
|
||||||
|
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||||
|
import { FoldingController, FoldingLimitInfo } from 'vs/editor/contrib/folding/browser/folding';
|
||||||
|
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||||
|
import { ILanguageStatus, ILanguageStatusService } from 'vs/workbench/services/languageStatus/common/languageStatusService';
|
||||||
|
import * as nls from 'vs/nls';
|
||||||
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
|
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||||
|
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||||
|
|
||||||
|
const openSettingsCommand = 'workbench.action.openSettings';
|
||||||
|
const configureSettingsLabel = nls.localize('status.button.configure', "Configure");
|
||||||
|
|
||||||
|
const foldingMaximumRegionsSettingsId = 'editor.foldingMaximumRegions';
|
||||||
|
|
||||||
|
export class FoldingLimitIndicatorContribution extends Disposable implements IWorkbenchContribution {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@IEditorService private readonly editorService: IEditorService,
|
||||||
|
@ILanguageStatusService private readonly languageStatusService: ILanguageStatusService
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
let changeListener: IDisposable | undefined;
|
||||||
|
let control: any;
|
||||||
|
|
||||||
|
const onActiveEditorChanged = () => {
|
||||||
|
const activeControl = editorService.activeTextEditorControl;
|
||||||
|
if (activeControl === control) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
control = undefined;
|
||||||
|
if (changeListener) {
|
||||||
|
changeListener.dispose();
|
||||||
|
changeListener = undefined;
|
||||||
|
}
|
||||||
|
const editor = getCodeEditor(activeControl);
|
||||||
|
if (editor) {
|
||||||
|
const controller = FoldingController.get(editor);
|
||||||
|
if (controller) {
|
||||||
|
const info = controller.foldingLimitInfo;
|
||||||
|
this.updateLimitInfo(info);
|
||||||
|
control = activeControl;
|
||||||
|
changeListener = controller.onDidChangeFoldingLimit(info => {
|
||||||
|
this.updateLimitInfo(info);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.updateLimitInfo(undefined);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.updateLimitInfo(undefined);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this._register(this.editorService.onDidActiveEditorChange(onActiveEditorChanged));
|
||||||
|
|
||||||
|
onActiveEditorChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _limitStatusItem: IDisposable | undefined;
|
||||||
|
|
||||||
|
private updateLimitInfo(info: FoldingLimitInfo | undefined) {
|
||||||
|
if (this._limitStatusItem) {
|
||||||
|
this._limitStatusItem.dispose();
|
||||||
|
this._limitStatusItem = undefined;
|
||||||
|
}
|
||||||
|
if (info && info.limited !== false) {
|
||||||
|
const status: ILanguageStatus = {
|
||||||
|
id: 'foldingLimitInfo',
|
||||||
|
selector: '*',
|
||||||
|
name: nls.localize('foldingRangesStatusItem.name', 'Folding Status'),
|
||||||
|
severity: Severity.Warning,
|
||||||
|
label: nls.localize('status.limitedFoldingRanges.short', 'Folding Ranges Limited'),
|
||||||
|
detail: nls.localize('status.limitedFoldingRanges.details', 'only {0} folding ranges shown for performance reasons', info.limited),
|
||||||
|
command: { id: openSettingsCommand, arguments: [foldingMaximumRegionsSettingsId], title: configureSettingsLabel },
|
||||||
|
accessibilityInfo: undefined,
|
||||||
|
source: nls.localize('foldingRangesStatusItem.source', 'Folding'),
|
||||||
|
busy: false
|
||||||
|
};
|
||||||
|
this._limitStatusItem = this.languageStatusService.addStatus(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(
|
||||||
|
FoldingLimitIndicatorContribution,
|
||||||
|
LifecyclePhase.Restored
|
||||||
|
);
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
import { Emitter, Event } from 'vs/base/common/event';
|
import { Emitter, Event } from 'vs/base/common/event';
|
||||||
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { TrackedRangeStickiness } from 'vs/editor/common/model';
|
import { TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||||
|
import { FoldingLimitReporter } from 'vs/editor/contrib/folding/browser/folding';
|
||||||
import { FoldingRegion, FoldingRegions } from 'vs/editor/contrib/folding/browser/foldingRanges';
|
import { FoldingRegion, FoldingRegions } from 'vs/editor/contrib/folding/browser/foldingRanges';
|
||||||
import { IFoldingRangeData, sanitizeRanges } from 'vs/editor/contrib/folding/browser/syntaxRangeProvider';
|
import { IFoldingRangeData, sanitizeRanges } from 'vs/editor/contrib/folding/browser/syntaxRangeProvider';
|
||||||
import { INotebookViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
import { INotebookViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||||
@@ -15,6 +16,10 @@ import { cellRangesToIndexes, ICellRange } from 'vs/workbench/contrib/notebook/c
|
|||||||
type RegionFilter = (r: FoldingRegion) => boolean;
|
type RegionFilter = (r: FoldingRegion) => boolean;
|
||||||
type RegionFilterWithLevel = (r: FoldingRegion, level: number) => boolean;
|
type RegionFilterWithLevel = (r: FoldingRegion, level: number) => boolean;
|
||||||
|
|
||||||
|
const foldingRangeLimit: FoldingLimitReporter = {
|
||||||
|
limit: 5000,
|
||||||
|
report: () => { }
|
||||||
|
};
|
||||||
|
|
||||||
export class FoldingModel implements IDisposable {
|
export class FoldingModel implements IDisposable {
|
||||||
private _viewModel: INotebookViewModel | null = null;
|
private _viewModel: INotebookViewModel | null = null;
|
||||||
@@ -200,7 +205,7 @@ export class FoldingModel implements IDisposable {
|
|||||||
};
|
};
|
||||||
}).filter(range => range.start !== range.end);
|
}).filter(range => range.start !== range.end);
|
||||||
|
|
||||||
const newRegions = sanitizeRanges(rawFoldingRanges, 5000);
|
const newRegions = sanitizeRanges(rawFoldingRanges, foldingRangeLimit);
|
||||||
|
|
||||||
// restore collased state
|
// restore collased state
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|||||||
@@ -270,6 +270,9 @@ import 'vs/workbench/contrib/snippets/browser/snippets.contribution';
|
|||||||
// Formatter Help
|
// Formatter Help
|
||||||
import 'vs/workbench/contrib/format/browser/format.contribution';
|
import 'vs/workbench/contrib/format/browser/format.contribution';
|
||||||
|
|
||||||
|
// Folding Limit Indicator
|
||||||
|
import 'vs/workbench/contrib/folding/browser/folding.contribution';
|
||||||
|
|
||||||
// Inlay Hint Accessibility
|
// Inlay Hint Accessibility
|
||||||
import 'vs/workbench/contrib/inlayHints/browser/inlayHintsAccessibilty';
|
import 'vs/workbench/contrib/inlayHints/browser/inlayHintsAccessibilty';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user