diff --git a/src/server/client.ts b/src/server/client.ts index 60fbd31243e..07b5d81d8c0 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -488,7 +488,24 @@ module ts.server { } getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[] { - throw new Error("Not Implemented Yet."); + var lineCol = this.positionToOneBasedLineCol(fileName, position); + var args: ServerProtocol.CodeLocationRequestArgs = { + file: fileName, + line: lineCol.line, + col: lineCol.col, + }; + + var request = this.processRequest(CommandNames.Brace, args); + var response = this.processResponse(request); + + return response.body.map(entry => { + var start = this.lineColToPosition(fileName, entry.start); + var end = this.lineColToPosition(fileName, entry.end); + return { + start: start, + length: end - start, + }; + }); } getIndentationAtPosition(fileName: string, position: number, options: EditorOptions): number { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index b4ffddd25ed..6187d8e6140 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -333,7 +333,7 @@ module ts.server { return true; } - module CommandNames { + export module CommandNames { export var Abbrev = "abbrev"; export var Change = "change"; export var Close = "close"; @@ -350,6 +350,7 @@ module ts.server { export var Rename = "rename"; export var Saveto = "saveto"; export var Type = "type"; + export var Brace = "brace"; export var Unknown = "unknown"; } @@ -1056,6 +1057,38 @@ module ts.server { } } + getMatchingBrace(line: number, col: number, rawfile: string, reqSeq = 0) { + var file = ts.normalizePath(rawfile); + var project = this.projectService.getProjectForFile(file); + if (project) { + var compilerService = project.compilerService; + var pos = compilerService.host.lineColToPosition(file, line, col); + var spans: ts.TextSpan[]; + try { + spans = compilerService.languageService.getBraceMatchingAtPosition(file, pos); + } + catch (err) { + this.logError(err, CommandNames.Brace); + spans = undefined; + } + if (spans) { + var bakedSpans: ServerProtocol.TextSpan[] = spans.map(span => ({ + start: span && + compilerService.host.positionToLineCol(file, span.start), + end: span && + compilerService.host.positionToLineCol(file, span.start + span.length) + })); + this.output(bakedSpans, CommandNames.Brace, reqSeq); + } + else { + this.output(undefined, CommandNames.Brace, reqSeq, "no matching braces"); + } + } + else { + this.output(undefined, CommandNames.Brace, reqSeq, "no matching braces"); + } + } + executeJSONcmd(cmd: string) { try { var req = JSON.parse(cmd); @@ -1143,6 +1176,11 @@ module ts.server { this.sendAbbrev(); break; } + case CommandNames.Brace: { + var defArgs = req.arguments; + this.getMatchingBrace(defArgs.line, defArgs.col, defArgs.file, req.seq); + break; + } default: { this.projectService.log("Unrecognized JSON command: " + cmd); break; diff --git a/src/server/protodef.d.ts b/src/server/protodef.d.ts index 75a41cb81ce..73c39404154 100644 --- a/src/server/protodef.d.ts +++ b/src/server/protodef.d.ts @@ -89,15 +89,22 @@ declare module ServerProtocol { Object found in response messages defining a span of text in source code. */ - export interface CodeSpan { - /** File containing the definition */ - file: string; + export interface TextSpan { /** First character of the definition */ start: LineCol; /** One character past last character of the definition */ end: LineCol; } + /** + Object found in response messages defining a span of text in + a specific source file. + */ + export interface CodeSpan extends TextSpan { + /** File containing the definition */ + file: string; + } + /** Definition response message. Gives text range for definition. */ @@ -582,6 +589,21 @@ declare module ServerProtocol { [fullString: string]: string; } } + + + /** Response to "brace" request. */ + export interface BraceResponse extends Response { + body?: TextSpan[]; + } + + /** + Brace matching request; value of command field is "brace". + Return response giving the code locations of matching braces + found in file at location line, col. + */ + export interface BraceRequest extends CodeLocationRequest { + } + } diff --git a/tests/cases/fourslash/server/brace.ts b/tests/cases/fourslash/server/brace.ts new file mode 100644 index 00000000000..fc8a71197db --- /dev/null +++ b/tests/cases/fourslash/server/brace.ts @@ -0,0 +1,43 @@ +/// + +//////curly braces +////module Foo [|{ +//// class Bar [|{ +//// private f() [|{ +//// }|] +//// +//// private f2() [|{ +//// if (true) [|{ }|] [|{ }|]; +//// }|] +//// }|] +////}|] +//// +//////parenthesis +////class FooBar { +//// private f[|()|] { +//// return [|([|(1 + 1)|])|]; +//// } +//// +//// private f2[|()|] { +//// if [|(true)|] { } +//// } +////} +//// +//////square brackets +////class Baz { +//// private f() { +//// var a: any[|[]|] = [|[[|[1, 2]|], [|[3, 4]|], 5]|]; +//// } +////} +//// +////// angular brackets +////class TemplateTest [||] { +//// public foo(a, b) { +//// return [||] a; +//// } +////} + +test.ranges().forEach((range) => { + verify.matchingBracePositionInCurrentFile(range.start, range.end - 1); + verify.matchingBracePositionInCurrentFile(range.end - 1, range.start); +}); \ No newline at end of file