forked from microsoft/vscode
-
Notifications
You must be signed in to change notification settings - Fork 0
/
folding.ts
96 lines (82 loc) · 3.27 KB
/
folding.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import type * as Proto from '../protocol';
import { ITypeScriptServiceClient } from '../typescriptService';
import API from '../utils/api';
import { coalesce } from '../utils/arrays';
import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration';
import { DocumentSelector } from '../utils/documentSelector';
import * as typeConverters from '../utils/typeConverters';
class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider {
public static readonly minVersion = API.v280;
public constructor(
private readonly client: ITypeScriptServiceClient
) { }
async provideFoldingRanges(
document: vscode.TextDocument,
_context: vscode.FoldingContext,
token: vscode.CancellationToken
): Promise<vscode.FoldingRange[] | undefined> {
const file = this.client.toOpenedFilePath(document);
if (!file) {
return;
}
const args: Proto.FileRequestArgs = { file };
const response = await this.client.execute('getOutliningSpans', args, token);
if (response.type !== 'response' || !response.body) {
return;
}
return coalesce(response.body.map(span => this.convertOutliningSpan(span, document)));
}
private convertOutliningSpan(
span: Proto.OutliningSpan,
document: vscode.TextDocument
): vscode.FoldingRange | undefined {
const range = typeConverters.Range.fromTextSpan(span.textSpan);
const kind = TypeScriptFoldingProvider.getFoldingRangeKind(span);
// Workaround for #49904
if (span.kind === 'comment') {
const line = document.lineAt(range.start.line).text;
if (line.match(/\/\/\s*#endregion/gi)) {
return undefined;
}
}
const start = range.start.line;
const end = this.adjustFoldingEnd(range, document);
return new vscode.FoldingRange(start, end, kind);
}
private static readonly foldEndPairCharacters = ['}', ']', ')', '`'];
private adjustFoldingEnd(range: vscode.Range, document: vscode.TextDocument) {
// workaround for #47240
if (range.end.character > 0) {
const foldEndCharacter = document.getText(new vscode.Range(range.end.translate(0, -1), range.end));
if (TypeScriptFoldingProvider.foldEndPairCharacters.includes(foldEndCharacter)) {
return Math.max(range.end.line - 1, range.start.line);
}
}
return range.end.line;
}
private static getFoldingRangeKind(span: Proto.OutliningSpan): vscode.FoldingRangeKind | undefined {
switch (span.kind) {
case 'comment': return vscode.FoldingRangeKind.Comment;
case 'region': return vscode.FoldingRangeKind.Region;
case 'imports': return vscode.FoldingRangeKind.Imports;
case 'code':
default: return undefined;
}
}
}
export function register(
selector: DocumentSelector,
client: ITypeScriptServiceClient,
): vscode.Disposable {
return conditionalRegistration([
requireMinVersion(client, TypeScriptFoldingProvider.minVersion),
], () => {
return vscode.languages.registerFoldingRangeProvider(selector.syntax,
new TypeScriptFoldingProvider(client));
});
}