folding provider: first version
This commit is contained in:
parent
c2a227b77e
commit
0faa5d117c
19 changed files with 689 additions and 65 deletions
|
@ -8,8 +8,8 @@ import * as path from 'path';
|
|||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration } from 'vscode';
|
||||
import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification } from 'vscode-languageclient';
|
||||
import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration, TextDocument, FoldingRangeList as VSFoldingRangeList, FoldingRange as VSFoldingRange } from 'vscode';
|
||||
import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, TextDocumentIdentifier } from 'vscode-languageclient';
|
||||
import TelemetryReporter from 'vscode-extension-telemetry';
|
||||
|
||||
import { hash } from './utils/hash';
|
||||
|
@ -30,6 +30,57 @@ namespace SchemaAssociationNotification {
|
|||
export const type: NotificationType<ISchemaAssociations, any> = new NotificationType('json/schemaAssociations');
|
||||
}
|
||||
|
||||
interface FoldingRangeList {
|
||||
/**
|
||||
* The folding ranges.
|
||||
*/
|
||||
ranges: FoldingRange[];
|
||||
}
|
||||
|
||||
export enum FoldingRangeType {
|
||||
/**
|
||||
* Folding range for a comment
|
||||
*/
|
||||
Comment = 'comment',
|
||||
/**
|
||||
* Folding range for a imports or includes
|
||||
*/
|
||||
Imports = 'imports',
|
||||
/**
|
||||
* Folding range for a region (e.g. `#region`)
|
||||
*/
|
||||
Region = 'region'
|
||||
}
|
||||
|
||||
interface FoldingRange {
|
||||
|
||||
/**
|
||||
* The start line number
|
||||
*/
|
||||
startLine: number;
|
||||
|
||||
/**
|
||||
* The end line number
|
||||
*/
|
||||
endLine: number;
|
||||
|
||||
/**
|
||||
* The actual color value for this color range.
|
||||
*/
|
||||
type?: FoldingRangeType;
|
||||
}
|
||||
|
||||
interface FoldingRangeRequest {
|
||||
/**
|
||||
* The text document.
|
||||
*/
|
||||
textDocument: TextDocumentIdentifier;
|
||||
}
|
||||
|
||||
namespace FoldingRangesRequest {
|
||||
export const type: RequestType<FoldingRangeRequest, FoldingRangeList | null, any, any> = new RequestType('textDocument/foldingRanges');
|
||||
}
|
||||
|
||||
interface IPackageInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
|
@ -124,6 +175,17 @@ export function activate(context: ExtensionContext) {
|
|||
toDispose.push(workspace.onDidCloseTextDocument(d => handleContentChange(d.uri)));
|
||||
|
||||
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context));
|
||||
|
||||
languages.registerFoldingProvider(documentSelector, {
|
||||
provideFoldingRanges(document: TextDocument) {
|
||||
return client.sendRequest(FoldingRangesRequest.type, { textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) }).then(res => {
|
||||
if (res && Array.isArray(res.ranges)) {
|
||||
return new VSFoldingRangeList(res.ranges.map(r => new VSFoldingRange(r.startLine, r.endLine, r.type)));
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let languageConfiguration: LanguageConfiguration = {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import {
|
||||
createConnection, IConnection,
|
||||
TextDocuments, TextDocument, InitializeParams, InitializeResult, NotificationType, RequestType,
|
||||
DocumentRangeFormattingRequest, Disposable, ServerCapabilities
|
||||
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentIdentifier
|
||||
} from 'vscode-languageserver';
|
||||
|
||||
import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities, ColorPresentationRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed';
|
||||
|
@ -20,6 +20,7 @@ import Strings = require('./utils/strings');
|
|||
import { formatError, runSafe, runSafeAsync } from './utils/errors';
|
||||
import { JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration } from 'vscode-json-languageservice';
|
||||
import { getLanguageModelCache } from './languageModelCache';
|
||||
import { createScanner, SyntaxKind } from 'jsonc-parser';
|
||||
|
||||
interface ISchemaAssociations {
|
||||
[pattern: string]: string[];
|
||||
|
@ -37,6 +38,57 @@ namespace SchemaContentChangeNotification {
|
|||
export const type: NotificationType<string, any> = new NotificationType('json/schemaContent');
|
||||
}
|
||||
|
||||
interface FoldingRangeList {
|
||||
/**
|
||||
* The folding ranges.
|
||||
*/
|
||||
ranges: FoldingRange[];
|
||||
}
|
||||
|
||||
export enum FoldingRangeType {
|
||||
/**
|
||||
* Folding range for a comment
|
||||
*/
|
||||
Comment = 'comment',
|
||||
/**
|
||||
* Folding range for a imports or includes
|
||||
*/
|
||||
Imports = 'imports',
|
||||
/**
|
||||
* Folding range for a region (e.g. `#region`)
|
||||
*/
|
||||
Region = 'region'
|
||||
}
|
||||
|
||||
interface FoldingRange {
|
||||
|
||||
/**
|
||||
* The start line number
|
||||
*/
|
||||
startLine: number;
|
||||
|
||||
/**
|
||||
* The end line number
|
||||
*/
|
||||
endLine: number;
|
||||
|
||||
/**
|
||||
* The actual color value for this color range.
|
||||
*/
|
||||
type?: FoldingRangeType | string;
|
||||
}
|
||||
|
||||
interface FoldingRangeRequest {
|
||||
/**
|
||||
* The text document.
|
||||
*/
|
||||
textDocument: TextDocumentIdentifier;
|
||||
}
|
||||
|
||||
namespace FoldingRangesRequest {
|
||||
export const type: RequestType<FoldingRangeRequest, FoldingRangeList | null, any, any> = new RequestType('textDocument/foldingRanges');
|
||||
}
|
||||
|
||||
// Create a connection for the server
|
||||
let connection: IConnection = createConnection();
|
||||
|
||||
|
@ -347,7 +399,83 @@ connection.onRequest(ColorPresentationRequest.type, params => {
|
|||
return languageService.getColorPresentations(document, jsonDocument, params.color, params.range);
|
||||
}
|
||||
return [];
|
||||
}, [], `Error while computing color presentationsd for ${params.textDocument.uri}`);
|
||||
}, [], `Error while computing color presentations for ${params.textDocument.uri}`);
|
||||
});
|
||||
|
||||
connection.onRequest(FoldingRangesRequest.type, params => {
|
||||
return runSafe(() => {
|
||||
let document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
let ranges: FoldingRange[] = [];
|
||||
let stack: FoldingRange[] = [];
|
||||
let prevStart = -1;
|
||||
let scanner = createScanner(document.getText(), false);
|
||||
let token = scanner.scan();
|
||||
while (token !== SyntaxKind.EOF) {
|
||||
switch (token) {
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
case SyntaxKind.OpenBracketToken: {
|
||||
let startLine = document.positionAt(scanner.getTokenOffset()).line;
|
||||
let range = { startLine, endLine: startLine, type: token === SyntaxKind.OpenBraceToken ? 'object' : 'array' };
|
||||
stack.push(range);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.CloseBraceToken:
|
||||
case SyntaxKind.CloseBracketToken: {
|
||||
let type = token === SyntaxKind.CloseBraceToken ? 'object' : 'array';
|
||||
if (stack.length > 0 && stack[stack.length - 1].type === type) {
|
||||
let range = stack.pop();
|
||||
let line = document.positionAt(scanner.getTokenOffset()).line;
|
||||
if (range && line > range.startLine + 1 && prevStart !== range.startLine) {
|
||||
range.endLine = line - 1;
|
||||
ranges.push(range);
|
||||
prevStart = range.startLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.BlockCommentTrivia: {
|
||||
let startLine = document.positionAt(scanner.getTokenOffset()).line;
|
||||
let endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
|
||||
if (startLine < endLine) {
|
||||
ranges.push({ startLine, endLine, type: FoldingRangeType.Comment });
|
||||
prevStart = startLine;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.LineCommentTrivia: {
|
||||
let text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength());
|
||||
let m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/);
|
||||
if (m) {
|
||||
let line = document.positionAt(scanner.getTokenOffset()).line;
|
||||
if (m[1]) { // start pattern match
|
||||
let range = { startLine: line, endLine: line, type: FoldingRangeType.Region };
|
||||
stack.push(range);
|
||||
} else {
|
||||
let i = stack.length - 1;
|
||||
while (i >= 0 && stack[i].type !== FoldingRangeType.Region) {
|
||||
i--;
|
||||
}
|
||||
if (i >= 0) {
|
||||
let range = stack[i];
|
||||
stack.length = i;
|
||||
if (line > range.startLine && prevStart !== range.startLine) {
|
||||
range.endLine = line;
|
||||
ranges.push(range);
|
||||
prevStart = range.startLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
token = scanner.scan();
|
||||
}
|
||||
return <FoldingRangeList>{ ranges };
|
||||
}
|
||||
return null;
|
||||
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`);
|
||||
});
|
||||
|
||||
// Listen on the connection
|
||||
|
|
|
@ -826,6 +826,67 @@ export interface DocumentColorProvider {
|
|||
provideColorPresentations(model: model.ITextModel, colorInfo: IColorInformation, token: CancellationToken): IColorPresentation[] | Thenable<IColorPresentation[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A provider of colors for editor models.
|
||||
*/
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface FoldingProvider {
|
||||
/**
|
||||
* Provides the color ranges for a specific model.
|
||||
*/
|
||||
provideFoldingRanges(model: model.ITextModel, token: CancellationToken): IFoldingRangeList | Thenable<IFoldingRangeList>;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface IFoldingRangeList {
|
||||
|
||||
ranges: IFoldingRange[];
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface IFoldingRange {
|
||||
|
||||
/**
|
||||
* The start line number
|
||||
*/
|
||||
startLineNumber: number;
|
||||
|
||||
/**
|
||||
* The end line number
|
||||
*/
|
||||
endLineNumber: number;
|
||||
|
||||
/**
|
||||
* The optional type of the folding range
|
||||
*/
|
||||
type?: FoldingRangeType | string;
|
||||
|
||||
// auto-collapse
|
||||
// header span
|
||||
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export enum FoldingRangeType {
|
||||
/**
|
||||
* Folding range for a comment
|
||||
*/
|
||||
Comment = 'comment',
|
||||
/**
|
||||
* Folding range for a imports or includes
|
||||
*/
|
||||
Imports = 'imports',
|
||||
/**
|
||||
* Folding range for a region (e.g. `#region`)
|
||||
*/
|
||||
Region = 'region'
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
@ -971,6 +1032,11 @@ export const LinkProviderRegistry = new LanguageFeatureRegistry<LinkProvider>();
|
|||
*/
|
||||
export const ColorProviderRegistry = new LanguageFeatureRegistry<DocumentColorProvider>();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const FoldingProviderRegistry = new LanguageFeatureRegistry<FoldingProvider>();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
|
|
@ -19,17 +19,24 @@ import { registerEditorAction, registerEditorContribution, ServicesAccessor, Edi
|
|||
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines } from 'vs/editor/contrib/folding/foldingModel';
|
||||
import { FoldingDecorationProvider } from './foldingDecorations';
|
||||
import { FoldingRegions } from './foldingRanges';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
|
||||
import { IMarginData, IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
import { HiddenRangeModel } from 'vs/editor/contrib/folding/hiddenRangeModel';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { computeRanges as computeIndentRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
|
||||
import { IndentRangeProvider } from 'vs/editor/contrib/folding/indentRangeProvider';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { FoldingProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { SyntaxRangeProvider } from './syntaxRangeProvider';
|
||||
|
||||
export const ID = 'editor.contrib.folding';
|
||||
|
||||
export interface RangeProvider {
|
||||
compute(editorModel: ITextModel): TPromise<FoldingRegions>;
|
||||
}
|
||||
|
||||
export class FoldingController implements IEditorContribution {
|
||||
|
||||
static MAX_FOLDING_REGIONS = 5000;
|
||||
|
@ -48,6 +55,8 @@ export class FoldingController implements IEditorContribution {
|
|||
private foldingModel: FoldingModel;
|
||||
private hiddenRangeModel: HiddenRangeModel;
|
||||
|
||||
private rangeProvider: RangeProvider;
|
||||
|
||||
private foldingModelPromise: TPromise<FoldingModel>;
|
||||
private updateScheduler: Delayer<FoldingModel>;
|
||||
|
||||
|
@ -69,6 +78,7 @@ export class FoldingController implements IEditorContribution {
|
|||
this.foldingDecorationProvider.autoHideFoldingControls = this._autoHideFoldingControls;
|
||||
|
||||
this.globalToDispose.push(this.editor.onDidChangeModel(() => this.onModelChanged()));
|
||||
this.globalToDispose.push(FoldingProviderRegistry.onDidChange(() => this.onModelChanged()));
|
||||
|
||||
this.globalToDispose.push(this.editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => {
|
||||
if (e.contribInfo) {
|
||||
|
@ -167,17 +177,22 @@ export class FoldingController implements IEditorContribution {
|
|||
this.foldingModelPromise = null;
|
||||
this.hiddenRangeModel = null;
|
||||
this.cursorChangedScheduler = null;
|
||||
this.rangeProvider = null;
|
||||
}
|
||||
});
|
||||
this.onModelContentChanged();
|
||||
}
|
||||
|
||||
private computeRanges(editorModel: ITextModel) {
|
||||
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id);
|
||||
let offSide = foldingRules && foldingRules.offSide;
|
||||
let markers = foldingRules && foldingRules.markers;
|
||||
let ranges = computeIndentRanges(editorModel, offSide, markers);
|
||||
return ranges;
|
||||
private getRangeProvider(): RangeProvider {
|
||||
if (!this.rangeProvider) {
|
||||
let foldingProviders = FoldingProviderRegistry.ordered(this.foldingModel.textModel);
|
||||
if (foldingProviders.length) {
|
||||
this.rangeProvider = new SyntaxRangeProvider(foldingProviders);
|
||||
} else {
|
||||
this.rangeProvider = new IndentRangeProvider();
|
||||
}
|
||||
}
|
||||
return this.rangeProvider;
|
||||
}
|
||||
|
||||
public getFoldingModel() {
|
||||
|
@ -191,9 +206,12 @@ export class FoldingController implements IEditorContribution {
|
|||
// some cursors might have moved into hidden regions, make sure they are in expanded regions
|
||||
let selections = this.editor.getSelections();
|
||||
let selectionLineNumbers = selections ? selections.map(s => s.startLineNumber) : [];
|
||||
this.foldingModel.update(this.computeRanges(this.foldingModel.textModel), selectionLineNumbers);
|
||||
return this.getRangeProvider().compute(this.foldingModel.textModel).then(foldingRanges => {
|
||||
this.foldingModel.update(foldingRanges, selectionLineNumbers);
|
||||
return this.foldingModel;
|
||||
});
|
||||
}
|
||||
return this.foldingModel;
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { ITextModel, IModelDecorationOptions, IModelDeltaDecoration, IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { FoldingRanges, ILineRange, FoldingRegion } from './foldingRanges';
|
||||
import { FoldingRegions, ILineRange, FoldingRegion } from './foldingRanges';
|
||||
|
||||
export interface IDecorationProvider {
|
||||
getDecorationOption(isCollapsed: boolean): IModelDecorationOptions;
|
||||
|
@ -24,13 +24,13 @@ export class FoldingModel {
|
|||
private _textModel: ITextModel;
|
||||
private _decorationProvider: IDecorationProvider;
|
||||
|
||||
private _ranges: FoldingRanges;
|
||||
private _regions: FoldingRegions;
|
||||
private _editorDecorationIds: string[];
|
||||
private _isInitialized: boolean;
|
||||
|
||||
private _updateEventEmitter = new Emitter<FoldingModelChangeEvent>();
|
||||
|
||||
public get ranges(): FoldingRanges { return this._ranges; }
|
||||
public get regions(): FoldingRegions { return this._regions; }
|
||||
public get onDidChange(): Event<FoldingModelChangeEvent> { return this._updateEventEmitter.event; }
|
||||
public get textModel() { return this._textModel; }
|
||||
public get isInitialized() { return this._isInitialized; }
|
||||
|
@ -38,7 +38,7 @@ export class FoldingModel {
|
|||
constructor(textModel: ITextModel, decorationProvider: IDecorationProvider) {
|
||||
this._textModel = textModel;
|
||||
this._decorationProvider = decorationProvider;
|
||||
this._ranges = new FoldingRanges(new Uint32Array(0), new Uint32Array(0));
|
||||
this._regions = new FoldingRegions(new Uint32Array(0), new Uint32Array(0));
|
||||
this._editorDecorationIds = [];
|
||||
this._isInitialized = false;
|
||||
}
|
||||
|
@ -54,8 +54,8 @@ export class FoldingModel {
|
|||
let editorDecorationId = this._editorDecorationIds[index];
|
||||
if (editorDecorationId && !processed[editorDecorationId]) {
|
||||
processed[editorDecorationId] = true;
|
||||
let newCollapseState = !this._ranges.isCollapsed(index);
|
||||
this._ranges.setCollapsed(index, newCollapseState);
|
||||
let newCollapseState = !this._regions.isCollapsed(index);
|
||||
this._regions.setCollapsed(index, newCollapseState);
|
||||
accessor.changeDecorationOptions(editorDecorationId, this._decorationProvider.getDecorationOption(newCollapseState));
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ export class FoldingModel {
|
|||
this._updateEventEmitter.fire({ model: this, collapseStateChanged: regions });
|
||||
}
|
||||
|
||||
public update(newRanges: FoldingRanges, blockedLineNumers: number[] = []): void {
|
||||
public update(newRegions: FoldingRegions, blockedLineNumers: number[] = []): void {
|
||||
let newEditorDecorations = [];
|
||||
|
||||
let isBlocked = (startLineNumber, endLineNumber) => {
|
||||
|
@ -76,11 +76,11 @@ export class FoldingModel {
|
|||
};
|
||||
|
||||
let initRange = (index: number, isCollapsed: boolean) => {
|
||||
let startLineNumber = newRanges.getStartLineNumber(index);
|
||||
if (isCollapsed && isBlocked(startLineNumber, newRanges.getEndLineNumber(index))) {
|
||||
let startLineNumber = newRegions.getStartLineNumber(index);
|
||||
if (isCollapsed && isBlocked(startLineNumber, newRegions.getEndLineNumber(index))) {
|
||||
isCollapsed = false;
|
||||
}
|
||||
newRanges.setCollapsed(index, isCollapsed);
|
||||
newRegions.setCollapsed(index, isCollapsed);
|
||||
let maxColumn = this._textModel.getLineMaxColumn(startLineNumber);
|
||||
let decorationRange = {
|
||||
startLineNumber: startLineNumber,
|
||||
|
@ -93,8 +93,8 @@ export class FoldingModel {
|
|||
|
||||
let i = 0;
|
||||
let nextCollapsed = () => {
|
||||
while (i < this._ranges.length) {
|
||||
let isCollapsed = this._ranges.isCollapsed(i);
|
||||
while (i < this._regions.length) {
|
||||
let isCollapsed = this._regions.isCollapsed(i);
|
||||
i++;
|
||||
if (isCollapsed) {
|
||||
return i - 1;
|
||||
|
@ -105,14 +105,14 @@ export class FoldingModel {
|
|||
|
||||
let k = 0;
|
||||
let collapsedIndex = nextCollapsed();
|
||||
while (collapsedIndex !== -1 && k < newRanges.length) {
|
||||
while (collapsedIndex !== -1 && k < newRegions.length) {
|
||||
// get the latest range
|
||||
let decRange = this._textModel.getDecorationRange(this._editorDecorationIds[collapsedIndex]);
|
||||
if (decRange) {
|
||||
let collapsedStartLineNumber = decRange.startLineNumber;
|
||||
if (this._textModel.getLineMaxColumn(collapsedStartLineNumber) === decRange.startColumn) { // test that the decoration is still at the end otherwise it got deleted
|
||||
while (k < newRanges.length) {
|
||||
let startLineNumber = newRanges.getStartLineNumber(k);
|
||||
while (k < newRegions.length) {
|
||||
let startLineNumber = newRegions.getStartLineNumber(k);
|
||||
if (collapsedStartLineNumber >= startLineNumber) {
|
||||
initRange(k, collapsedStartLineNumber === startLineNumber);
|
||||
k++;
|
||||
|
@ -124,13 +124,13 @@ export class FoldingModel {
|
|||
}
|
||||
collapsedIndex = nextCollapsed();
|
||||
}
|
||||
while (k < newRanges.length) {
|
||||
while (k < newRegions.length) {
|
||||
initRange(k, false);
|
||||
k++;
|
||||
}
|
||||
|
||||
this._editorDecorationIds = this._decorationProvider.deltaDecorations(this._editorDecorationIds, newEditorDecorations);
|
||||
this._ranges = newRanges;
|
||||
this._regions = newRegions;
|
||||
this._isInitialized = true;
|
||||
this._updateEventEmitter.fire({ model: this });
|
||||
}
|
||||
|
@ -140,12 +140,12 @@ export class FoldingModel {
|
|||
*/
|
||||
public getMemento(): CollapseMemento {
|
||||
let collapsedRanges: ILineRange[] = [];
|
||||
for (let i = 0; i < this._ranges.length; i++) {
|
||||
if (this._ranges.isCollapsed(i)) {
|
||||
for (let i = 0; i < this._regions.length; i++) {
|
||||
if (this._regions.isCollapsed(i)) {
|
||||
let range = this._textModel.getDecorationRange(this._editorDecorationIds[i]);
|
||||
if (range) {
|
||||
let startLineNumber = range.startLineNumber;
|
||||
let endLineNumber = range.endLineNumber + this._ranges.getEndLineNumber(i) - this._ranges.getStartLineNumber(i);
|
||||
let endLineNumber = range.endLineNumber + this._regions.getEndLineNumber(i) - this._regions.getStartLineNumber(i);
|
||||
collapsedRanges.push({ startLineNumber, endLineNumber });
|
||||
}
|
||||
}
|
||||
|
@ -179,11 +179,11 @@ export class FoldingModel {
|
|||
|
||||
getAllRegionsAtLine(lineNumber: number, filter?: (r: FoldingRegion, level: number) => boolean): FoldingRegion[] {
|
||||
let result: FoldingRegion[] = [];
|
||||
if (this._ranges) {
|
||||
let index = this._ranges.findRange(lineNumber);
|
||||
if (this._regions) {
|
||||
let index = this._regions.findRange(lineNumber);
|
||||
let level = 1;
|
||||
while (index >= 0) {
|
||||
let current = this._ranges.toRegion(index);
|
||||
let current = this._regions.toRegion(index);
|
||||
if (!filter || filter(current, level)) {
|
||||
result.push(current);
|
||||
}
|
||||
|
@ -195,10 +195,10 @@ export class FoldingModel {
|
|||
}
|
||||
|
||||
getRegionAtLine(lineNumber: number): FoldingRegion {
|
||||
if (this._ranges) {
|
||||
let index = this._ranges.findRange(lineNumber);
|
||||
if (this._regions) {
|
||||
let index = this._regions.findRange(lineNumber);
|
||||
if (index >= 0) {
|
||||
return this._ranges.toRegion(index);
|
||||
return this._regions.toRegion(index);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -210,9 +210,9 @@ export class FoldingModel {
|
|||
let levelStack: FoldingRegion[] = trackLevel ? [] : null;
|
||||
let index = region ? region.regionIndex + 1 : 0;
|
||||
let endLineNumber = region ? region.endLineNumber : Number.MAX_VALUE;
|
||||
for (let i = index, len = this._ranges.length; i < len; i++) {
|
||||
let current = this._ranges.toRegion(i);
|
||||
if (this._ranges.getStartLineNumber(i) < endLineNumber) {
|
||||
for (let i = index, len = this._regions.length; i < len; i++) {
|
||||
let current = this._regions.toRegion(i);
|
||||
if (this._regions.getStartLineNumber(i) < endLineNumber) {
|
||||
if (trackLevel) {
|
||||
while (levelStack.length > 0 && !current.containedBy(levelStack[levelStack.length - 1])) {
|
||||
levelStack.pop();
|
||||
|
@ -296,13 +296,13 @@ export function setCollapseStateAtLevel(foldingModel: FoldingModel, foldLevel: n
|
|||
*/
|
||||
export function setCollapseStateForMatchingLines(foldingModel: FoldingModel, regExp: RegExp, doCollapse: boolean): void {
|
||||
let editorModel = foldingModel.textModel;
|
||||
let ranges = foldingModel.ranges;
|
||||
let regions = foldingModel.regions;
|
||||
let toToggle = [];
|
||||
for (let i = ranges.length - 1; i >= 0; i--) {
|
||||
if (doCollapse !== ranges.isCollapsed(i)) {
|
||||
let startLineNumber = ranges.getStartLineNumber(i);
|
||||
for (let i = regions.length - 1; i >= 0; i--) {
|
||||
if (doCollapse !== regions.isCollapsed(i)) {
|
||||
let startLineNumber = regions.getStartLineNumber(i);
|
||||
if (regExp.test(editorModel.getLineContent(startLineNumber))) {
|
||||
toToggle.push(ranges.toRegion(i));
|
||||
toToggle.push(regions.toRegion(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ export const MAX_LINE_NUMBER = 0xFFFFFF;
|
|||
|
||||
const MASK_INDENT = 0xFF000000;
|
||||
|
||||
export class FoldingRanges {
|
||||
export class FoldingRegions {
|
||||
private _startIndexes: Uint32Array;
|
||||
private _endIndexes: Uint32Array;
|
||||
private _collapseStates: Uint32Array;
|
||||
|
@ -138,7 +138,7 @@ export class FoldingRanges {
|
|||
|
||||
export class FoldingRegion {
|
||||
|
||||
constructor(private ranges: FoldingRanges, private index: number) {
|
||||
constructor(private ranges: FoldingRegions, private index: number) {
|
||||
}
|
||||
|
||||
public get startLineNumber() {
|
||||
|
|
|
@ -23,7 +23,7 @@ export class HiddenRangeModel {
|
|||
this._foldingModel = model;
|
||||
this._foldingModelListener = model.onDidChange(_ => this.updateHiddenRanges());
|
||||
this._hiddenRanges = [];
|
||||
if (model.ranges.length) {
|
||||
if (model.regions.length) {
|
||||
this.updateHiddenRanges();
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ export class HiddenRangeModel {
|
|||
let lastCollapsedStart = Number.MAX_VALUE;
|
||||
let lastCollapsedEnd = -1;
|
||||
|
||||
let ranges = this._foldingModel.ranges;
|
||||
let ranges = this._foldingModel.regions;
|
||||
for (; i < ranges.length; i++) {
|
||||
if (!ranges.isCollapsed(i)) {
|
||||
continue;
|
||||
|
|
|
@ -7,25 +7,37 @@
|
|||
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { FoldingRanges, MAX_LINE_NUMBER } from 'vs/editor/contrib/folding/foldingRanges';
|
||||
import { FoldingRegions, MAX_LINE_NUMBER } from 'vs/editor/contrib/folding/foldingRanges';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { RangeProvider } from './folding';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
|
||||
const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000;
|
||||
|
||||
export class IndentRangeProvider implements RangeProvider {
|
||||
compute(editorModel: ITextModel): TPromise<FoldingRegions> {
|
||||
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id);
|
||||
let offSide = foldingRules && foldingRules.offSide;
|
||||
let markers = foldingRules && foldingRules.markers;
|
||||
return TPromise.as(computeRanges(editorModel, offSide, markers));
|
||||
}
|
||||
}
|
||||
|
||||
// public only for testing
|
||||
export class RangesCollector {
|
||||
private _startIndexes: number[];
|
||||
private _endIndexes: number[];
|
||||
private _indentOccurrences: number[];
|
||||
private _length: number;
|
||||
private _FoldingRangesLimit: number;
|
||||
private _foldingRangesLimit: number;
|
||||
|
||||
constructor(FoldingRangesLimit: number) {
|
||||
constructor(foldingRangesLimit: number) {
|
||||
this._startIndexes = [];
|
||||
this._endIndexes = [];
|
||||
this._indentOccurrences = [];
|
||||
this._length = 0;
|
||||
this._FoldingRangesLimit = FoldingRangesLimit;
|
||||
this._foldingRangesLimit = foldingRangesLimit;
|
||||
}
|
||||
|
||||
public insertFirst(startLineNumber: number, endLineNumber: number, indent: number) {
|
||||
|
@ -42,7 +54,7 @@ export class RangesCollector {
|
|||
}
|
||||
|
||||
public toIndentRanges(model: ITextModel) {
|
||||
if (this._length <= this._FoldingRangesLimit) {
|
||||
if (this._length <= this._foldingRangesLimit) {
|
||||
// reverse and create arrays of the exact length
|
||||
let startIndexes = new Uint32Array(this._length);
|
||||
let endIndexes = new Uint32Array(this._length);
|
||||
|
@ -50,14 +62,14 @@ export class RangesCollector {
|
|||
startIndexes[k] = this._startIndexes[i];
|
||||
endIndexes[k] = this._endIndexes[i];
|
||||
}
|
||||
return new FoldingRanges(startIndexes, endIndexes);
|
||||
return new FoldingRegions(startIndexes, endIndexes);
|
||||
} else {
|
||||
let entries = 0;
|
||||
let maxIndent = this._indentOccurrences.length;
|
||||
for (let i = 0; i < this._indentOccurrences.length; i++) {
|
||||
let n = this._indentOccurrences[i];
|
||||
if (n) {
|
||||
if (n + entries > this._FoldingRangesLimit) {
|
||||
if (n + entries > this._foldingRangesLimit) {
|
||||
maxIndent = i;
|
||||
break;
|
||||
}
|
||||
|
@ -78,7 +90,7 @@ export class RangesCollector {
|
|||
k++;
|
||||
}
|
||||
}
|
||||
return new FoldingRanges(startIndexes, endIndexes);
|
||||
return new FoldingRegions(startIndexes, endIndexes);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -87,9 +99,9 @@ export class RangesCollector {
|
|||
|
||||
interface PreviousRegion { indent: number; line: number; marker: boolean; }
|
||||
|
||||
export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, FoldingRangesLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT): FoldingRanges {
|
||||
export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, foldingRangesLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT): FoldingRegions {
|
||||
const tabSize = model.getOptions().tabSize;
|
||||
let result = new RangesCollector(FoldingRangesLimit);
|
||||
let result = new RangesCollector(foldingRangesLimit);
|
||||
|
||||
let pattern = void 0;
|
||||
if (markers) {
|
||||
|
|
154
src/vs/editor/contrib/folding/syntaxRangeProvider.ts
Normal file
154
src/vs/editor/contrib/folding/syntaxRangeProvider.ts
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { FoldingProvider, IFoldingRange } from 'vs/editor/common/modes';
|
||||
import { onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { asWinJsPromise } from 'vs/base/common/async';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { RangeProvider } from './folding';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { MAX_LINE_NUMBER, FoldingRegions } from './foldingRanges';
|
||||
|
||||
const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000;
|
||||
|
||||
export interface IFoldingRangeData extends IFoldingRange {
|
||||
rank: number;
|
||||
}
|
||||
|
||||
export class SyntaxRangeProvider implements RangeProvider {
|
||||
|
||||
constructor(private providers: FoldingProvider[]) {
|
||||
}
|
||||
|
||||
compute(model: ITextModel): TPromise<FoldingRegions> {
|
||||
return collectSyntaxRanges(this.providers, model).then(ranges => {
|
||||
return sanitizeRanges(ranges);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function collectSyntaxRanges(providers: FoldingProvider[], model: ITextModel): TPromise<IFoldingRangeData[]> {
|
||||
const rangeData: IFoldingRangeData[] = [];
|
||||
let promises = providers.map((provider, rank) => asWinJsPromise(token => provider.provideFoldingRanges(model, token)).then(list => {
|
||||
if (list && Array.isArray(list.ranges)) {
|
||||
for (let r of list.ranges) {
|
||||
rangeData.push({ startLineNumber: r.startLineNumber, endLineNumber: r.endLineNumber, rank, type: r.type });
|
||||
}
|
||||
}
|
||||
}, onUnexpectedExternalError));
|
||||
|
||||
return TPromise.join(promises).then(() => {
|
||||
return rangeData;
|
||||
});
|
||||
}
|
||||
|
||||
export class RangesCollector {
|
||||
private _startIndexes: number[];
|
||||
private _endIndexes: number[];
|
||||
private _nestingLevels: number[];
|
||||
private _nestingLevelCounts: number[];
|
||||
private _length: number;
|
||||
private _foldingRangesLimit: number;
|
||||
|
||||
constructor(foldingRangesLimit: number) {
|
||||
this._startIndexes = [];
|
||||
this._endIndexes = [];
|
||||
this._nestingLevels = [];
|
||||
this._nestingLevelCounts = [];
|
||||
this._length = 0;
|
||||
this._foldingRangesLimit = foldingRangesLimit;
|
||||
}
|
||||
|
||||
public add(startLineNumber: number, endLineNumber: number, nestingLevel: number) {
|
||||
if (startLineNumber > MAX_LINE_NUMBER || endLineNumber > MAX_LINE_NUMBER) {
|
||||
return;
|
||||
}
|
||||
let index = this._length;
|
||||
this._startIndexes[index] = startLineNumber;
|
||||
this._endIndexes[index] = endLineNumber;
|
||||
this._nestingLevels[index] = nestingLevel;
|
||||
this._length++;
|
||||
if (nestingLevel < 30) {
|
||||
this._nestingLevelCounts[nestingLevel] = (this._nestingLevelCounts[nestingLevel] || 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public toIndentRanges() {
|
||||
if (this._length <= this._foldingRangesLimit) {
|
||||
let startIndexes = new Uint32Array(this._length);
|
||||
let endIndexes = new Uint32Array(this._length);
|
||||
for (let i = 0; i < this._length; i++) {
|
||||
startIndexes[i] = this._startIndexes[i];
|
||||
endIndexes[i] = this._endIndexes[i];
|
||||
}
|
||||
return new FoldingRegions(startIndexes, endIndexes);
|
||||
} else {
|
||||
let entries = 0;
|
||||
let maxLevel = this._nestingLevelCounts.length;
|
||||
for (let i = 0; i < this._nestingLevelCounts.length; i++) {
|
||||
let n = this._nestingLevelCounts[i];
|
||||
if (n) {
|
||||
if (n + entries > this._foldingRangesLimit) {
|
||||
maxLevel = i;
|
||||
break;
|
||||
}
|
||||
entries += n;
|
||||
}
|
||||
}
|
||||
let startIndexes = new Uint32Array(entries);
|
||||
let endIndexes = new Uint32Array(entries);
|
||||
for (let i = 0, k = 0; i < this._length; i++) {
|
||||
let level = this._nestingLevels[i];
|
||||
if (level < maxLevel) {
|
||||
startIndexes[k] = this._startIndexes[i];
|
||||
endIndexes[k] = this._endIndexes[i];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
return new FoldingRegions(startIndexes, endIndexes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export function sanitizeRanges(rangeData: IFoldingRangeData[]): FoldingRegions {
|
||||
|
||||
let sorted = rangeData.sort((d1, d2) => {
|
||||
let diff = d1.startLineNumber - d2.startLineNumber;
|
||||
if (diff === 0) {
|
||||
diff = d1.rank - d2.rank;
|
||||
}
|
||||
return diff;
|
||||
});
|
||||
let collector = new RangesCollector(MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT);
|
||||
|
||||
let top: IFoldingRangeData = null;
|
||||
let previous = [];
|
||||
for (let entry of sorted) {
|
||||
if (!top) {
|
||||
top = entry;
|
||||
collector.add(entry.startLineNumber, entry.endLineNumber, previous.length);
|
||||
} else {
|
||||
if (entry.startLineNumber > top.startLineNumber) {
|
||||
if (entry.endLineNumber <= top.endLineNumber) {
|
||||
previous.push(top);
|
||||
top = entry;
|
||||
collector.add(entry.startLineNumber, entry.endLineNumber, previous.length);
|
||||
} else if (entry.startLineNumber > top.endLineNumber) {
|
||||
do {
|
||||
top = previous.pop();
|
||||
} while (top && entry.startLineNumber > top.endLineNumber);
|
||||
previous.push(top);
|
||||
top = entry;
|
||||
collector.add(entry.startLineNumber, entry.endLineNumber, previous.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return collector.toIndentRanges();
|
||||
}
|
|
@ -61,7 +61,7 @@ suite('Folding Model', () => {
|
|||
|
||||
function assertFoldedRanges(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) {
|
||||
let actualRanges = [];
|
||||
let actual = foldingModel.ranges;
|
||||
let actual = foldingModel.regions;
|
||||
for (let i = 0; i < actual.length; i++) {
|
||||
if (actual.isCollapsed(i)) {
|
||||
actualRanges.push(r(actual.getStartLineNumber(i), actual.getEndLineNumber(i)));
|
||||
|
@ -72,7 +72,7 @@ suite('Folding Model', () => {
|
|||
|
||||
function assertRanges(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) {
|
||||
let actualRanges = [];
|
||||
let actual = foldingModel.ranges;
|
||||
let actual = foldingModel.regions;
|
||||
for (let i = 0; i < actual.length; i++) {
|
||||
actualRanges.push(r(actual.getStartLineNumber(i), actual.getEndLineNumber(i), actual.isCollapsed(i)));
|
||||
}
|
||||
|
|
|
@ -389,6 +389,14 @@ export function registerColorProvider(languageId: string, provider: modes.Docume
|
|||
return modes.ColorProviderRegistry.register(languageId, provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a folding provider
|
||||
*/
|
||||
/*export function registerFoldingProvider(languageId: string, provider: modes.FoldingProvider): IDisposable {
|
||||
return modes.FoldingProviderRegistry.register(languageId, provider);
|
||||
}*/
|
||||
|
||||
|
||||
/**
|
||||
* Contains additional diagnostic information about the context in which
|
||||
* a [code action](#CodeActionProvider.provideCodeActions) is run.
|
||||
|
|
3
src/vs/monaco.d.ts
vendored
3
src/vs/monaco.d.ts
vendored
|
@ -4047,6 +4047,9 @@ declare module monaco.languages {
|
|||
*/
|
||||
export function registerColorProvider(languageId: string, provider: DocumentColorProvider): IDisposable;
|
||||
|
||||
/**
|
||||
* Register a folding provider
|
||||
*/
|
||||
/**
|
||||
* Contains additional diagnostic information about the context in which
|
||||
* a [code action](#CodeActionProvider.provideCodeActions) is run.
|
||||
|
|
74
src/vs/vscode.proposed.d.ts
vendored
74
src/vs/vscode.proposed.d.ts
vendored
|
@ -7,6 +7,63 @@
|
|||
|
||||
declare module 'vscode' {
|
||||
|
||||
export class FoldingRangeList {
|
||||
|
||||
/**
|
||||
* The folding ranges.
|
||||
*/
|
||||
ranges: FoldingRange[];
|
||||
|
||||
/**
|
||||
* Creates mew folding range list.
|
||||
*
|
||||
* @param ranges The folding ranges
|
||||
*/
|
||||
constructor(ranges: FoldingRange[]);
|
||||
}
|
||||
|
||||
|
||||
export class FoldingRange {
|
||||
|
||||
/**
|
||||
* The start line number (0-based)
|
||||
*/
|
||||
startLine: number;
|
||||
|
||||
/**
|
||||
* The end line number (0-based)
|
||||
*/
|
||||
endLine: number;
|
||||
|
||||
/**
|
||||
* The actual color value for this color range.
|
||||
*/
|
||||
type?: FoldingRangeType | string;
|
||||
|
||||
/**
|
||||
* Creates a new folding range.
|
||||
*
|
||||
* @param startLineNumber The first line of the fold
|
||||
* @param type The last line of the fold
|
||||
*/
|
||||
constructor(startLineNumber: number, endLineNumber: number, type?: FoldingRangeType);
|
||||
}
|
||||
|
||||
export enum FoldingRangeType {
|
||||
/**
|
||||
* Folding range for a comment
|
||||
*/
|
||||
Comment = 'comment',
|
||||
/**
|
||||
* Folding range for a imports or includes
|
||||
*/
|
||||
Imports = 'imports',
|
||||
/**
|
||||
* Folding range for a region (e.g. `#region`)
|
||||
*/
|
||||
Region = 'region'
|
||||
}
|
||||
|
||||
// export enum FileErrorCodes {
|
||||
// /**
|
||||
// * Not owner.
|
||||
|
@ -340,10 +397,27 @@ declare module 'vscode' {
|
|||
}
|
||||
|
||||
export namespace languages {
|
||||
|
||||
/**
|
||||
* Register a folding provider.
|
||||
*
|
||||
* Multiple folding can be registered for a language. In that case providers are sorted
|
||||
* by their [score](#languages.match) and the best-matching provider is used. Failure
|
||||
* of the selected provider will cause a failure of the whole operation.
|
||||
*
|
||||
* @param selector A selector that defines the documents this provider is applicable to.
|
||||
* @param provider A folding provider.
|
||||
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
|
||||
*/
|
||||
export function registerFoldingProvider(selector: DocumentSelector, provider: FoldingProvider): Disposable;
|
||||
|
||||
export interface RenameProvider2 extends RenameProvider {
|
||||
resolveInitialRenameValue?(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<RenameInitialValue>;
|
||||
}
|
||||
}
|
||||
export interface FoldingProvider {
|
||||
provideFoldingRanges(document: TextDocument, token: CancellationToken): ProviderResult<FoldingRangeList>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the validation type of the Source Control input.
|
||||
|
|
|
@ -346,6 +346,17 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
|||
});
|
||||
}
|
||||
|
||||
// --- folding
|
||||
|
||||
$registerFoldingProvider(handle: number, selector: vscode.DocumentSelector): void {
|
||||
const proxy = this._proxy;
|
||||
this._registrations[handle] = modes.FoldingProviderRegistry.register(toLanguageSelector(selector), <modes.FoldingProvider>{
|
||||
provideFoldingRanges: (model, token) => {
|
||||
return wireCancellationToken(token, proxy.$provideFoldingRanges(handle, model.uri));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- configuration
|
||||
|
||||
private static _reviveRegExp(regExp: ISerializedRegExp): RegExp {
|
||||
|
|
|
@ -292,6 +292,9 @@ export function createApiFactory(
|
|||
registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerColorProvider(selector, provider);
|
||||
},
|
||||
registerFoldingProvider: proposedApiFunction(extension, (selector: vscode.DocumentSelector, provider: vscode.FoldingProvider): vscode.Disposable => {
|
||||
return extHostLanguageFeatures.registerFoldingProvider(selector, provider);
|
||||
}),
|
||||
setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => {
|
||||
return extHostLanguageFeatures.setLanguageConfiguration(language, configuration);
|
||||
}
|
||||
|
@ -634,7 +637,10 @@ export function createApiFactory(
|
|||
RelativePattern: extHostTypes.RelativePattern,
|
||||
|
||||
FileChangeType: extHostTypes.FileChangeType,
|
||||
FileType: extHostTypes.FileType
|
||||
FileType: extHostTypes.FileType,
|
||||
FoldingRangeList: extHostTypes.FoldingRangeList,
|
||||
FoldingRange: extHostTypes.FoldingRange,
|
||||
FoldingRangeType: extHostTypes.FoldingRangeType
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -282,6 +282,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
|
|||
$registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): void;
|
||||
$registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): void;
|
||||
$registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): void;
|
||||
$registerFoldingProvider(handle: number, selector: vscode.DocumentSelector): void;
|
||||
$setLanguageConfiguration(handle: number, languageId: string, configuration: ISerializedLanguageConfiguration): void;
|
||||
}
|
||||
|
||||
|
@ -692,6 +693,7 @@ export interface ExtHostLanguageFeaturesShape {
|
|||
$resolveDocumentLink(handle: number, link: modes.ILink): TPromise<modes.ILink>;
|
||||
$provideDocumentColors(handle: number, resource: UriComponents): TPromise<IRawColorInfo[]>;
|
||||
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo): TPromise<modes.IColorPresentation[]>;
|
||||
$provideFoldingRanges(handle: number, resource: UriComponents): TPromise<modes.IFoldingRangeList>;
|
||||
}
|
||||
|
||||
export interface ExtHostQuickOpenShape {
|
||||
|
|
|
@ -804,10 +804,29 @@ class ColorProviderAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
class FoldingProviderAdapter {
|
||||
|
||||
constructor(
|
||||
private _documents: ExtHostDocuments,
|
||||
private _provider: vscode.FoldingProvider
|
||||
) { }
|
||||
|
||||
provideFoldingRanges(resource: URI): TPromise<modes.IFoldingRangeList> {
|
||||
const doc = this._documents.getDocumentData(resource).document;
|
||||
return asWinJsPromise(token => this._provider.provideFoldingRanges(doc, token)).then(list => {
|
||||
if (!Array.isArray(list.ranges)) {
|
||||
return void 0;
|
||||
}
|
||||
return TypeConverters.FoldingRangeList.from(list);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type Adapter = OutlineAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter
|
||||
| DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter
|
||||
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
|
||||
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter;
|
||||
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter
|
||||
| ColorProviderAdapter | FoldingProviderAdapter;
|
||||
|
||||
export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
|
||||
|
||||
|
@ -1108,6 +1127,16 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
|
|||
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo));
|
||||
}
|
||||
|
||||
registerFoldingProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingProvider): vscode.Disposable {
|
||||
const handle = this._addNewAdapter(new FoldingProviderAdapter(this._documents, provider));
|
||||
this._proxy.$registerFoldingProvider(handle, selector);
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
||||
$provideFoldingRanges(handle: number, resource: UriComponents): TPromise<modes.IFoldingRangeList> {
|
||||
return this._withAdapter(handle, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource)));
|
||||
}
|
||||
|
||||
// --- configuration
|
||||
|
||||
private static _serializeRegExp(regExp: RegExp): ISerializedRegExp {
|
||||
|
|
|
@ -586,6 +586,14 @@ export namespace ProgressLocation {
|
|||
}
|
||||
}
|
||||
|
||||
export namespace FoldingRangeList {
|
||||
export function from(rangeList: vscode.FoldingRangeList): modes.IFoldingRangeList {
|
||||
return {
|
||||
ranges: rangeList.ranges.map(r => ({ startLineNumber: r.startLine + 1, endLineNumber: r.endLine + 1 }))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function toTextEditorOptions(options?: vscode.TextDocumentShowOptions): ITextEditorOptions {
|
||||
if (options) {
|
||||
return {
|
||||
|
|
|
@ -1659,3 +1659,46 @@ export enum FileType {
|
|||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region folding api
|
||||
|
||||
export class FoldingRangeList {
|
||||
|
||||
ranges: FoldingRange[];
|
||||
|
||||
constructor(ranges: FoldingRange[]) {
|
||||
this.ranges = ranges;
|
||||
}
|
||||
}
|
||||
|
||||
export class FoldingRange {
|
||||
|
||||
startLine: number;
|
||||
|
||||
endLine: number;
|
||||
|
||||
type?: FoldingRangeType;
|
||||
|
||||
constructor(startLine: number, endLine: number, type?: FoldingRangeType) {
|
||||
this.startLine = startLine;
|
||||
this.endLine = endLine;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export enum FoldingRangeType {
|
||||
/**
|
||||
* Folding range for a comment
|
||||
*/
|
||||
Comment = 'comment',
|
||||
/**
|
||||
* Folding range for a imports or includes
|
||||
*/
|
||||
Imports = 'imports',
|
||||
/**
|
||||
* Folding range for a region (e.g. `#region`)
|
||||
*/
|
||||
Region = 'region'
|
||||
}
|
||||
|
||||
//#endregion
|
Loading…
Reference in a new issue