diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 1ad8a594c78c5..016a22fe9903e 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -5350,13 +5350,13 @@ namespace ts.projectSystem { }); // send outlining spans request (normal priority) - session.executeCommandSeq({ + session.executeCommandSeq({ command: "outliningSpans", arguments: { file: f1.path } }); // ensure the outlining spans request can be canceled - verifyExecuteCommandSeqIsCancellable({ + verifyExecuteCommandSeqIsCancellable({ command: "outliningSpans", arguments: { file: f1.path } }); diff --git a/src/server/client.ts b/src/server/client.ts index 542f89d50fce9..578cbe507bc00 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -522,8 +522,16 @@ namespace ts.server { })); } - getOutliningSpans(_fileName: string): OutliningSpan[] { - return notImplemented(); + getOutliningSpans(file: string): OutliningSpan[] { + const request = this.processRequest(CommandNames.GetOutliningSpans, { file }); + const response = this.processResponse(request); + + return response.body.map(item => ({ + textSpan: this.decodeSpan(item.textSpan, file), + hintSpan: this.decodeSpan(item.hintSpan, file), + bannerText: item.bannerText, + autoCollapse: item.autoCollapse + })); } getTodoComments(_fileName: string, _descriptors: TodoCommentDescriptor[]): TodoComment[] { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index ea312196d0bee..47c607966006b 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -91,8 +91,9 @@ namespace ts.server.protocol { EncodedSemanticClassificationsFull = "encodedSemanticClassifications-full", /* @internal */ Cleanup = "cleanup", + GetOutliningSpans = "getOutliningSpans", /* @internal */ - OutliningSpans = "outliningSpans", + GetOutliningSpansFull = "outliningSpans", // Full command name is different for backward compatibility purposes TodoComments = "todoComments", Indentation = "indentation", DocCommentTemplate = "docCommentTemplate", @@ -303,19 +304,50 @@ namespace ts.server.protocol { /** * Request to obtain outlining spans in file. */ - /* @internal */ export interface OutliningSpansRequest extends FileRequest { - command: CommandTypes.OutliningSpans; + command: CommandTypes.GetOutliningSpans; + } + + export interface OutliningSpan { + /** The span of the document to actually collapse. */ + textSpan: TextSpan; + + /** The span of the document to display when the user hovers over the collapsed span. */ + hintSpan: TextSpan; + + /** The text to display in the editor for the collapsed region. */ + bannerText: string; + + /** + * Whether or not this region should be automatically collapsed when + * the 'Collapse to Definitions' command is invoked. + */ + autoCollapse: boolean; } /** * Response to OutliningSpansRequest request. */ - /* @internal */ export interface OutliningSpansResponse extends Response { body?: OutliningSpan[]; } + /** + * Request to obtain outlining spans in file. + */ + /* @internal */ + export interface OutliningSpansRequestFull extends FileRequest { + command: CommandTypes.GetOutliningSpansFull; + } + + /** + * Response to OutliningSpansRequest request. + */ + /* @internal */ + export interface OutliningSpansResponseFull extends Response { + body?: ts.OutliningSpan[]; + } + /** * A request to get indentation for a location in file */ diff --git a/src/server/session.ts b/src/server/session.ts index 8e5770a4f66fb..6c3069ed0c160 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1087,9 +1087,21 @@ namespace ts.server { return { file, project }; } - private getOutliningSpans(args: protocol.FileRequestArgs) { + private getOutliningSpans(args: protocol.FileRequestArgs, simplifiedResult: boolean): protocol.OutliningSpan[] | OutliningSpan[] { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); - return languageService.getOutliningSpans(file); + const spans = languageService.getOutliningSpans(file); + if (simplifiedResult) { + const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file); + return spans.map(s => ({ + textSpan: this.toLocationTextSpan(s.textSpan, scriptInfo), + hintSpan: this.toLocationTextSpan(s.hintSpan, scriptInfo), + bannerText: s.bannerText, + autoCollapse: s.autoCollapse + })); + } + else { + return spans; + } } private getTodoComments(args: protocol.TodoCommentRequestArgs) { @@ -1893,8 +1905,11 @@ namespace ts.server { [CommandNames.QuickinfoFull]: (request: protocol.QuickInfoRequest) => { return this.requiredResponse(this.getQuickInfoWorker(request.arguments, /*simplifiedResult*/ false)); }, - [CommandNames.OutliningSpans]: (request: protocol.FileRequest) => { - return this.requiredResponse(this.getOutliningSpans(request.arguments)); + [CommandNames.GetOutliningSpans]: (request: protocol.FileRequest) => { + return this.requiredResponse(this.getOutliningSpans(request.arguments, /*simplifiedResult*/ true)); + }, + [CommandNames.GetOutliningSpansFull]: (request: protocol.FileRequest) => { + return this.requiredResponse(this.getOutliningSpans(request.arguments, /*simplifiedResult*/ false)); }, [CommandNames.TodoComments]: (request: protocol.TodoCommentRequest) => { return this.requiredResponse(this.getTodoComments(request.arguments)); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 385828ef73330..6a5f5f003aadd 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5067,6 +5067,7 @@ declare namespace ts.server.protocol { OpenExternalProject = "openExternalProject", OpenExternalProjects = "openExternalProjects", CloseExternalProject = "closeExternalProject", + GetOutliningSpans = "getOutliningSpans", TodoComments = "todoComments", Indentation = "indentation", DocCommentTemplate = "docCommentTemplate", @@ -5225,6 +5226,31 @@ declare namespace ts.server.protocol { */ onlyMultiLine: boolean; } + /** + * Request to obtain outlining spans in file. + */ + interface OutliningSpansRequest extends FileRequest { + command: CommandTypes.GetOutliningSpans; + } + interface OutliningSpan { + /** The span of the document to actually collapse. */ + textSpan: TextSpan; + /** The span of the document to display when the user hovers over the collapsed span. */ + hintSpan: TextSpan; + /** The text to display in the editor for the collapsed region. */ + bannerText: string; + /** + * Whether or not this region should be automatically collapsed when + * the 'Collapse to Definitions' command is invoked. + */ + autoCollapse: boolean; + } + /** + * Response to OutliningSpansRequest request. + */ + interface OutliningSpansResponse extends Response { + body?: OutliningSpan[]; + } /** * A request to get indentation for a location in file */ @@ -7276,7 +7302,7 @@ declare namespace ts.server { private getFileAndProject(args); private getFileAndLanguageServiceForSyntacticOperation(args); private getFileAndProjectWorker(uncheckedFileName, projectFileName); - private getOutliningSpans(args); + private getOutliningSpans(args, simplifiedResult); private getTodoComments(args); private getDocCommentTemplate(args); private getSpanOfEnclosingComment(args); diff --git a/tests/cases/fourslash/server/getOutliningSpansForRegions.ts b/tests/cases/fourslash/server/getOutliningSpansForRegions.ts new file mode 100644 index 0000000000000..24f515fb8a99c --- /dev/null +++ b/tests/cases/fourslash/server/getOutliningSpansForRegions.ts @@ -0,0 +1,51 @@ +/// + +////// region without label +////[|// #region +//// +////// #endregion|] +//// +////// region without label with trailing spaces +////[|// #region +//// +////// #endregion|] +//// +////// region with label +////[|// #region label1 +//// +////// #endregion|] +//// +////// region with extra whitespace in all valid locations +//// [|// #region label2 label3 +//// +//// // #endregion|] +//// +////// No space before directive +////[|//#region label4 +//// +//////#endregion|] +//// +////// Nested regions +////[|// #region outer +//// +////[|// #region inner +//// +////// #endregion inner|] +//// +////// #endregion outer|] +//// +////// region delimiters not valid when there is preceding text on line +//// test // #region invalid1 +//// +////test // #endregion +//// +////// region delimiters not valid when in multiline comment +/////* +////// #region invalid2 +////*/ +//// +/////* +////// #endregion +////*/ + +verify.outliningSpansInCurrentFile(test.ranges());