Implement LSP code folding
Change-Id: I2b076b1792c229cbbce610d7a195fa03eb29e6bd
Reviewed-on: https://dart-review.googlesource.com/c/89503
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index f784b71..7dd292f 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -239,4 +239,13 @@
.getResult(path, sendCachedToStream: sendCachedToStream)
.catchError((_) => null);
}
+
+ /// Return the unresolved unit for the file with the given [path].
+ ParsedUnitResult getParsedUnit(String path) {
+ if (!AnalysisEngine.isDartFileName(path)) {
+ return null;
+ }
+
+ return getAnalysisDriver(path)?.currentSession?.getParsedUnit(path);
+ }
}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/organize_imports.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/organize_imports.dart
index 4161f20..674201f 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/organize_imports.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/organize_imports.dart
@@ -33,7 +33,7 @@
final path = arguments.single;
final docIdentifier = server.getVersionedDocumentIdentifier(path);
- final result = await requireUnit(path);
+ final result = await requireResolvedUnit(path);
return result.mapResult((result) {
final code = result.content;
final unit = result.unit;
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
index 41d3014..3788e2c 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
@@ -43,7 +43,7 @@
capabilities?.codeActionLiteralSupport?.codeActionKind?.valueSet ?? []);
final path = pathOfDoc(params.textDocument);
- final unit = await path.mapResult(requireUnit);
+ final unit = await path.mapResult(requireResolvedUnit);
return unit.mapResult((unit) {
final startOffset = toOffset(unit.lineInfo, params.range.start);
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index bc81078..1f845ef 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -60,7 +60,7 @@
final pos = params.position;
final path = pathOfDoc(params.textDocument);
- final unit = await path.mapResult(requireUnit);
+ final unit = await path.mapResult(requireResolvedUnit);
final offset = await unit.mapResult((unit) => toOffset(unit.lineInfo, pos));
return offset.mapResult((offset) => _getItems(
completionCapabilities,
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
index c3e9246..90912c6 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
@@ -26,7 +26,7 @@
TextDocumentPositionParams params) async {
final pos = params.position;
final path = pathOfDoc(params.textDocument);
- final unit = await path.mapResult(requireUnit);
+ final unit = await path.mapResult(requireResolvedUnit);
final offset = await unit.mapResult((unit) => toOffset(unit.lineInfo, pos));
return offset.mapResult((offset) {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_highlights.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_highlights.dart
index 9f6822e..cc21c40 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_highlights.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_highlights.dart
@@ -25,7 +25,7 @@
TextDocumentPositionParams params) async {
final pos = params.position;
final path = pathOfDoc(params.textDocument);
- final unit = await path.mapResult(requireUnit);
+ final unit = await path.mapResult(requireResolvedUnit);
final offset = await unit.mapResult((unit) => toOffset(unit.lineInfo, pos));
return offset.mapResult((requestedOffset) {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_symbols.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_symbols.dart
index e1fda79..7810b7f 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_symbols.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_symbols.dart
@@ -61,7 +61,7 @@
symbolCapabilities?.hierarchicalDocumentSymbolSupport ?? false;
final path = pathOfDoc(params.textDocument);
- final unit = await path.mapResult(requireUnit);
+ final unit = await path.mapResult(requireResolvedUnit);
return unit.mapResult((unit) => _getSymbols(clientSupportedSymbolKinds,
clientSupportsDocumentSymbol, path.result, unit));
}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_folding.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_folding.dart
new file mode 100644
index 0000000..96e1a4a
--- /dev/null
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_folding.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/computer/computer_folding.dart';
+import 'package:analysis_server/src/lsp/handlers/handlers.dart';
+import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
+import 'package:analysis_server/src/lsp/mapping.dart';
+
+class FoldingHandler
+ extends MessageHandler<FoldingRangeParams, List<FoldingRange>> {
+ FoldingHandler(LspAnalysisServer server) : super(server);
+ Method get handlesMessage => Method.textDocument_foldingRange;
+
+ @override
+ FoldingRangeParams convertParams(Map<String, dynamic> json) =>
+ FoldingRangeParams.fromJson(json);
+
+ Future<ErrorOr<List<FoldingRange>>> handle(FoldingRangeParams params) async {
+ final path = pathOfDoc(params.textDocument);
+ final unit = await path.mapResult(requireUnresolvedUnit);
+
+ return unit.mapResult((unit) {
+ final lineInfo = unit.lineInfo;
+ final regions =
+ new DartUnitFoldingComputer(lineInfo, unit.unit).compute();
+
+ return success(
+ regions.map((region) => toFoldingRange(lineInfo, region)).toList(),
+ );
+ });
+ }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
index 6fb1c2f..a2a1218 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
@@ -31,7 +31,7 @@
Future<ErrorOr<List<TextEdit>>> handle(
DocumentOnTypeFormattingParams params) async {
final path = pathOfDoc(params.textDocument);
- final unit = await path.mapResult(requireUnit);
+ final unit = await path.mapResult(requireResolvedUnit);
return unit.mapResult((unit) => formatFile(path.result, unit));
}
}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
index e226bc4..0d4cb66 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
@@ -31,7 +31,7 @@
Future<ErrorOr<List<TextEdit>>> handle(
DocumentFormattingParams params) async {
final path = pathOfDoc(params.textDocument);
- final unit = await path.mapResult(requireUnit);
+ final unit = await path.mapResult(requireResolvedUnit);
return unit.mapResult((unit) => formatFile(path.result, unit));
}
}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart
index a5396e5..d9302ae 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart
@@ -26,7 +26,7 @@
Future<ErrorOr<Hover>> handle(TextDocumentPositionParams params) async {
final pos = params.position;
final path = pathOfDoc(params.textDocument);
- final unit = await path.mapResult(requireUnit);
+ final unit = await path.mapResult(requireResolvedUnit);
final offset = await unit.mapResult((unit) => toOffset(unit.lineInfo, pos));
return offset.mapResult((offset) => _getHover(unit.result, offset));
}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
index e1a72f6..5b47079 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
@@ -94,7 +94,7 @@
: Either2<bool, RenameOptions>.t1(true),
null,
null,
- null,
+ Either3<bool, FoldingRangeProviderOptions, dynamic>.t1(true),
new ExecuteCommandOptions(Commands.serverSupportedCommands),
new ServerCapabilitiesWorkspace(
new ServerCapabilitiesWorkspaceFolders(true, true)),
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart
index f6f803f..5f3fe45 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart
@@ -31,7 +31,7 @@
Future<ErrorOr<List<Location>>> handle(ReferenceParams params) async {
final pos = params.position;
final path = pathOfDoc(params.textDocument);
- final unit = await path.mapResult(requireUnit);
+ final unit = await path.mapResult(requireResolvedUnit);
final offset = await unit.mapResult((unit) => toOffset(unit.lineInfo, pos));
return offset.mapResult(
(offset) => _getRefererences(path.result, offset, params, unit.result));
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
index 3a9075e..5433077 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
@@ -24,7 +24,7 @@
TextDocumentPositionParams params) async {
final pos = params.position;
final path = pathOfDoc(params.textDocument);
- final unit = await path.mapResult(requireUnit);
+ final unit = await path.mapResult(requireResolvedUnit);
final offset = await unit.mapResult((unit) => toOffset(unit.lineInfo, pos));
return offset.mapResult((offset) async {
@@ -84,7 +84,7 @@
params.textDocument is VersionedTextDocumentIdentifier
? params.textDocument
: server.getVersionedDocumentIdentifier(path)));
- final unit = await path.mapResult(requireUnit);
+ final unit = await path.mapResult(requireResolvedUnit);
final offset = await unit.mapResult((unit) => toOffset(unit.lineInfo, pos));
return offset.mapResult((offset) async {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
index 2fe40ef..3f3e195 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
@@ -24,7 +24,7 @@
TextDocumentPositionParams params) async {
final pos = params.position;
final path = pathOfDoc(params.textDocument);
- final unit = await path.mapResult(requireUnit);
+ final unit = await path.mapResult(requireResolvedUnit);
final offset = await unit.mapResult((unit) => toOffset(unit.lineInfo, pos));
return offset.mapResult((offset) {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
index 9375b8b..ead989c 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart
@@ -13,6 +13,7 @@
import 'package:analysis_server/src/lsp/handlers/handler_document_highlights.dart';
import 'package:analysis_server/src/lsp/handlers/handler_document_symbols.dart';
import 'package:analysis_server/src/lsp/handlers/handler_execute_command.dart';
+import 'package:analysis_server/src/lsp/handlers/handler_folding.dart';
import 'package:analysis_server/src/lsp/handlers/handler_format_on_type.dart';
import 'package:analysis_server/src/lsp/handlers/handler_formatting.dart';
import 'package:analysis_server/src/lsp/handlers/handler_hover.dart';
@@ -64,6 +65,7 @@
registerHandler(new WorkspaceFoldersHandler(server));
registerHandler(new PrepareRenameHandler(server));
registerHandler(new RenameHandler(server));
+ registerHandler(new FoldingHandler(server));
}
}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart b/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart
index 9fd98d8..5125008 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart
@@ -38,7 +38,7 @@
ErrorOr<R> failure<R>(ErrorOr<dynamic> error) =>
new ErrorOr<R>.error(error.error);
- Future<ErrorOr<ResolvedUnitResult>> requireUnit(String path) async {
+ Future<ErrorOr<ResolvedUnitResult>> requireResolvedUnit(String path) async {
final result = await server.getResolvedUnit(path);
if (result?.state != ResultState.VALID) {
return error(ServerErrorCodes.InvalidFilePath, 'Invalid file path', path);
@@ -46,6 +46,14 @@
return success(result);
}
+ ErrorOr<ParsedUnitResult> requireUnresolvedUnit(String path) {
+ final result = server.getParsedUnit(path);
+ if (result?.state != ResultState.VALID) {
+ return error(ServerErrorCodes.InvalidFilePath, 'Invalid file path', path);
+ }
+ return success(result);
+ }
+
ErrorOr<R> success<R>([R t]) => new ErrorOr<R>.success(t);
}
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index d69d15d..3f1e394 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -417,6 +417,28 @@
}
}
+lsp.FoldingRange toFoldingRange(
+ server.LineInfo lineInfo, server.FoldingRegion region) {
+ final range = toRange(lineInfo, region.offset, region.length);
+ return new lsp.FoldingRange(range.start.line, range.start.character,
+ range.end.line, range.end.character, toFoldingRangeKind(region.kind));
+}
+
+lsp.FoldingRangeKind toFoldingRangeKind(server.FoldingKind kind) {
+ switch (kind) {
+ case server.FoldingKind.DOCUMENTATION_COMMENT:
+ case server.FoldingKind.FILE_HEADER:
+ return lsp.FoldingRangeKind.Comment;
+ case server.FoldingKind.DIRECTIVES:
+ return lsp.FoldingRangeKind.Imports;
+ default:
+ // null (actually undefined in LSP, the toJson() takes care of that) is
+ // valid, and actually the value used for the majority of folds
+ // (class/functions/etc.).
+ return null;
+ }
+}
+
List<lsp.DocumentHighlight> toHighlights(
server.LineInfo lineInfo, server.Occurrences occurrences) {
return occurrences.offsets
diff --git a/pkg/analysis_server/test/lsp/folding_test.dart b/pkg/analysis_server/test/lsp/folding_test.dart
new file mode 100644
index 0000000..0b41612
--- /dev/null
+++ b/pkg/analysis_server/test/lsp/folding_test.dart
@@ -0,0 +1,113 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'server_abstract.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(FoldingTest);
+ });
+}
+
+@reflectiveTest
+class FoldingTest extends AbstractLspAnalysisServerTest {
+ test_class() async {
+ final content = '''
+ class MyClass2 {[[
+ // Class content
+ ]]}
+ ''';
+
+ final range1 = rangeFromMarkers(content);
+ final expectedRegions = [
+ new FoldingRange(
+ range1.start.line,
+ range1.start.character,
+ range1.end.line,
+ range1.end.character,
+ null,
+ )
+ ];
+
+ await initialize();
+ await openFile(mainFileUri, withoutMarkers(content));
+
+ final regions = await getFoldingRegions(mainFileUri);
+ expect(regions, unorderedEquals(expectedRegions));
+ }
+
+ test_comments() async {
+ final content = '''
+ [[/// This is a comment
+ /// that spans many lines]]
+ class MyClass2 {}
+ ''';
+
+ final range1 = rangeFromMarkers(content);
+ final expectedRegions = [
+ new FoldingRange(
+ range1.start.line,
+ range1.start.character,
+ range1.end.line,
+ range1.end.character,
+ FoldingRangeKind.Comment,
+ )
+ ];
+
+ await initialize();
+ await openFile(mainFileUri, withoutMarkers(content));
+
+ final regions = await getFoldingRegions(mainFileUri);
+ expect(regions, unorderedEquals(expectedRegions));
+ }
+
+ test_headersImportsComments() async {
+ // TODO(dantup): Review why the file header and the method comment ranges
+ // are different... one spans only the range to collapse, but the other
+ // just starts at the logical block.
+ // The LSP spec doesn't give any guidance on whether the first part of
+ // the surrounded content should be visible or not after folding
+ // so we'll need to revisit this once there's clarification:
+ // https://github.com/Microsoft/language-server-protocol/issues/659
+ final content = '''
+ // Copyright some year by some people[[
+ // See LICENCE etc.]]
+
+ import[[ 'dart:io';
+ import 'dart:async';]]
+
+ [[/// This is not the file header
+ /// It's just a comment]]
+ main() {}
+ ''';
+
+ final ranges = rangesFromMarkers(content);
+
+ final expectedRegions = [
+ _toFoldingRange(ranges[0], FoldingRangeKind.Comment),
+ _toFoldingRange(ranges[1], FoldingRangeKind.Imports),
+ _toFoldingRange(ranges[2], FoldingRangeKind.Comment),
+ ];
+
+ await initialize();
+ await openFile(mainFileUri, withoutMarkers(content));
+
+ final regions = await getFoldingRegions(mainFileUri);
+ expect(regions, unorderedEquals(expectedRegions));
+ }
+
+ FoldingRange _toFoldingRange(Range range, FoldingRangeKind kind) {
+ return new FoldingRange(
+ range.start.line,
+ range.start.character,
+ range.end.line,
+ range.end.character,
+ kind,
+ );
+ }
+}
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index ecae12a..d4a1e2f 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -72,45 +72,6 @@
);
}
- /// Validates the document versions for a set of edits match the versions in
- /// the supplied map.
- void expectDocumentVersions(
- Either2<List<TextDocumentEdit>,
- List<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>>
- documentChanges,
- Map<String, int> expectedVersions,
- ) {
- documentChanges.map(
- // Validate versions on simple doc edits
- (edits) => edits
- .forEach((edit) => expectDocumentVersion(edit, expectedVersions)),
- // For resource changes, we only need to validate changes since
- // creates/renames/deletes do not supply versions.
- (changes) => changes.forEach((change) {
- change.map(
- (edit) => expectDocumentVersion(edit, expectedVersions),
- (create) => {},
- (rename) {},
- (delete) {},
- );
- }),
- );
- }
-
- void expectDocumentVersion(
- TextDocumentEdit edit,
- Map<String, int> expectedVersions,
- ) {
- final path = Uri.parse(edit.textDocument.uri).toFilePath();
- final expectedVersion = expectedVersions[path];
-
- if (edit.textDocument is VersionedTextDocumentIdentifier) {
- expect(edit.textDocument.version, equals(expectedVersion));
- } else {
- throw 'Document identifier for $path was not versioned (expected version $expectedVersion)';
- }
- }
-
void applyResourceChanges(
Map<String, String> oldFileContent,
List<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>> changes,
@@ -234,6 +195,45 @@
return expectSuccessfulResponseTo(request);
}
+ void expectDocumentVersion(
+ TextDocumentEdit edit,
+ Map<String, int> expectedVersions,
+ ) {
+ final path = Uri.parse(edit.textDocument.uri).toFilePath();
+ final expectedVersion = expectedVersions[path];
+
+ if (edit.textDocument is VersionedTextDocumentIdentifier) {
+ expect(edit.textDocument.version, equals(expectedVersion));
+ } else {
+ throw 'Document identifier for $path was not versioned (expected version $expectedVersion)';
+ }
+ }
+
+ /// Validates the document versions for a set of edits match the versions in
+ /// the supplied map.
+ void expectDocumentVersions(
+ Either2<List<TextDocumentEdit>,
+ List<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>>
+ documentChanges,
+ Map<String, int> expectedVersions,
+ ) {
+ documentChanges.map(
+ // Validate versions on simple doc edits
+ (edits) => edits
+ .forEach((edit) => expectDocumentVersion(edit, expectedVersions)),
+ // For resource changes, we only need to validate changes since
+ // creates/renames/deletes do not supply versions.
+ (changes) => changes.forEach((change) {
+ change.map(
+ (edit) => expectDocumentVersion(edit, expectedVersions),
+ (create) => {},
+ (rename) {},
+ (delete) {},
+ );
+ }),
+ );
+ }
+
Future<T> expectErrorNotification<T>(
FutureOr<void> f(), {
Duration timeout = const Duration(seconds: 5),
@@ -363,6 +363,14 @@
return expectSuccessfulResponseTo(request);
}
+ Future<List<FoldingRange>> getFoldingRegions(Uri uri) {
+ final request = makeRequest(
+ Method.textDocument_foldingRange,
+ new FoldingRangeParams(new TextDocumentIdentifier(uri.toString())),
+ );
+ return expectSuccessfulResponseTo<List<FoldingRange>>(request);
+ }
+
Future<Hover> getHover(Uri uri, Position pos) {
final request = makeRequest(
Method.textDocument_hover,
diff --git a/pkg/analysis_server/test/lsp/test_all.dart b/pkg/analysis_server/test/lsp/test_all.dart
index d1e29ad..2745a6b 100644
--- a/pkg/analysis_server/test/lsp/test_all.dart
+++ b/pkg/analysis_server/test/lsp/test_all.dart
@@ -23,6 +23,7 @@
import 'rename_test.dart' as rename_test;
import 'server_test.dart' as server_test;
import 'signature_help_test.dart' as signature_help_test;
+import 'folding_test.dart' as folding_test;
main() {
defineReflectiveSuite(() {
@@ -44,5 +45,6 @@
assists_code_action_tests.main();
packet_transformer_tests.main();
rename_test.main();
+ folding_test.main();
}, name: 'lsp');
}
diff --git a/pkg/analysis_server/tool/lsp_spec/README.md b/pkg/analysis_server/tool/lsp_spec/README.md
index 7eb9e39..b696b00 100644
--- a/pkg/analysis_server/tool/lsp_spec/README.md
+++ b/pkg/analysis_server/tool/lsp_spec/README.md
@@ -77,6 +77,6 @@
| textDocument/onTypeFormatting | ✅ | ✅ | ✅ | ✅ |
| textDocument/rename | ✅ | ✅ | ✅ | ✅ |
| textDocument/prepareRename | | | | |
-| textDocument/foldingRange | | | | |
+| textDocument/foldingRange | ✅ | ✅ | ✅ | ✅ |