Skip to content

Commit 17f19b2

Browse files
committed
Initial checkin for server code
1 parent 27a9084 commit 17f19b2

File tree

6 files changed

+4953
-0
lines changed

6 files changed

+4953
-0
lines changed

src/server/client.ts

+392
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
/// <reference path="protocol.ts" />
2+
3+
module ts.server {
4+
5+
export interface SessionClientHost extends LanguageServiceHost {
6+
lineColToPosition(fileName: string, line: number, col: number): number;
7+
positionToZeroBasedLineCol(fileName: string, position: number): ts.LineAndCharacter;
8+
}
9+
10+
export class SessionClient implements LanguageService {
11+
private session: Session;
12+
private sequence: number;
13+
private lastReply: string;
14+
15+
constructor(private host: SessionClientHost) {
16+
this.sequence = 0;
17+
18+
this.session = new Session({
19+
args: [],
20+
newLine: host.getNewLine(),
21+
useCaseSensitiveFileNames: true,
22+
write: (s) => this.lastReply = s,
23+
readFile: (fileName): string => {
24+
var snapshot = host.getScriptSnapshot(fileName);
25+
return snapshot && snapshot.getText(0, snapshot.getLength());
26+
},
27+
writeFile: (name, text, writeByteOrderMark) => {
28+
},
29+
resolvePath: (path) => path,
30+
fileExists: (path) => !!host.getScriptSnapshot(path),
31+
directoryExists: (path) => false,
32+
getExecutingFilePath: () => "",
33+
exit: (exitCode) => { },
34+
createDirectory: (directoryName: string) => { },
35+
36+
getCurrentDirectory: () => host.getCurrentDirectory(),
37+
readDirectory: (path: string, extension?: string) => [],
38+
getModififedTime: (fileName) => new Date(),
39+
stat: (path, callback) => { throw new Error("Not implemented Yet."); },
40+
}, {
41+
close: () => { },
42+
info: (m) => this.host.log(m),
43+
msg: (m) => this.host.log(m),
44+
endGroup: () => { },
45+
perftrc: (m) => this.host.log(m),
46+
startGroup: () => { }
47+
}, /* useProtocol */ true, /*prettyJSON*/ true);
48+
}
49+
50+
private lineColToPosition(fileName: string, lineCol: ServerProtocol.LineCol): number {
51+
return this.host.lineColToPosition(fileName, lineCol.line, lineCol.col);
52+
}
53+
54+
private getFileLength(fileName: string): number {
55+
return this.host.getScriptSnapshot(fileName).getLength();
56+
}
57+
58+
private positionToOneBasedLineCol(fileName: string, position: number): ServerProtocol.LineCol {
59+
var lineCol = this.host.positionToZeroBasedLineCol(fileName, position);
60+
return {
61+
line: lineCol.line + 1,
62+
col: lineCol.character + 1
63+
};
64+
}
65+
66+
private convertCodeEditsToTextChange(fileName: string, codeEdit: ServerProtocol.CodeEdit): ts.TextChange {
67+
var start = this.lineColToPosition(fileName, codeEdit.start);
68+
var end = this.lineColToPosition(fileName, codeEdit.end);
69+
70+
return {
71+
span: ts.createTextSpanFromBounds(start, end),
72+
newText: codeEdit.newText
73+
};
74+
}
75+
76+
private processRequest(command: "open", arguments: ServerProtocol.FileRequestArgs): ServerProtocol.OpenRequest;
77+
private processRequest(command: "close", arguments: ServerProtocol.FileRequestArgs): ServerProtocol.CloseRequest;
78+
private processRequest(command: "change", arguments: ServerProtocol.ChangeRequestArgs): ServerProtocol.ChangeRequest;
79+
private processRequest(command: "quickinfo", arguments: ServerProtocol.FileRequestArgs): ServerProtocol.QuickInfoRequest;
80+
private processRequest(command: "format", arguments: ServerProtocol.FormatRequestArgs): ServerProtocol.FormatRequest;
81+
private processRequest(command: "formatonkey", arguments: ServerProtocol.FormatOnKeyRequestArgs): ServerProtocol.FormatRequest;
82+
private processRequest(command: "definition", arguments: ServerProtocol.FileRequestArgs): ServerProtocol.DefinitionRequest;
83+
private processRequest(command: "references", arguments: ServerProtocol.FileRequestArgs): ServerProtocol.ReferencesRequest;
84+
private processRequest(command: "completions", arguments: ServerProtocol.CompletionsRequestArgs): ServerProtocol.CompletionsRequest;
85+
private processRequest(command: "navto", arguments: ServerProtocol.NavtoRequestArgs): ServerProtocol.NavtoRequest;
86+
private processRequest(command: "saveto", arguments: ServerProtocol.SavetoRequestArgs): ServerProtocol.SavetoRequest;
87+
private processRequest(command: string, arguments: any): ServerProtocol.Request;
88+
private processRequest(command: string, arguments: any): ServerProtocol.Request {
89+
var request: ServerProtocol.Request = {
90+
seq: this.sequence++,
91+
type: "request",
92+
command: command,
93+
arguments: arguments
94+
};
95+
96+
this.session.executeJSONcmd(JSON.stringify(request));
97+
98+
return request;
99+
}
100+
101+
private processResponse<T extends ServerProtocol.Response>(request: ServerProtocol.Request): T {
102+
debugger;
103+
104+
// Read the content length
105+
var contentLengthPrefix = "Content-Length: ";
106+
var lines = this.lastReply.split("\r\n");
107+
Debug.assert(lines.length >= 2, "Malformed response: Expected 3 lines in the response.");
108+
109+
var contentLengthText = lines[0];
110+
Debug.assert(contentLengthText.indexOf(contentLengthPrefix) === 0, "Malformed response: Response text did not contain content-length header.");
111+
var contentLength = parseInt(contentLengthText.substring(contentLengthPrefix.length));
112+
113+
// Read the body
114+
var responseBody = lines[2];
115+
116+
// Verify content length
117+
Debug.assert(responseBody.length + 1 === contentLength, "Malformed response: Content length did not match the response's body length.");
118+
119+
try {
120+
var response: T = JSON.parse(responseBody);
121+
}
122+
catch (e) {
123+
throw new Error("Malformed response: Failed to parse server response: " + this.lastReply + ". \r\n Error detailes: " + e.message);
124+
}
125+
126+
// verify the sequence numbers
127+
Debug.assert(response.request_seq === request.seq, "Malformed response: response sequance number did not match request sequence number.");
128+
129+
// unmarshal errors
130+
if (!response.success) {
131+
throw new Error("Error " + response.message);
132+
}
133+
134+
Debug.assert(!!response.body, "Malformed response: Unexpected empty response body.");
135+
136+
return response;
137+
}
138+
139+
openFile(fileName: string): void {
140+
this.processRequest("open", {
141+
file: fileName
142+
});
143+
}
144+
145+
closeFile(fileName: string): void {
146+
this.processRequest("close", {
147+
file: fileName
148+
});
149+
}
150+
151+
changeFile(fileName: string, start: number, end: number, newText: string): void {
152+
var lineCol = this.positionToOneBasedLineCol(fileName, start);
153+
154+
this.processRequest("change", {
155+
file: fileName,
156+
line: lineCol.line,
157+
col: lineCol.col,
158+
insertLen: end - start,
159+
deleteLen: end - start,
160+
insertString: newText
161+
});
162+
}
163+
164+
getQuickInfoAtPosition(fileName: string, position: number): QuickInfo {
165+
var request = this.processRequest("quickinfo", {
166+
file: fileName,
167+
line: 0,
168+
col: 1
169+
});
170+
171+
var response = this.processResponse<ServerProtocol.QuickInfoResponse>(request);
172+
173+
var start = this.lineColToPosition(fileName, response.body.start);
174+
var end = this.lineColToPosition(fileName, response.body.end);
175+
176+
return {
177+
kind: response.body.kind,
178+
kindModifiers: response.body.kindModifiers,
179+
textSpan: ts.createTextSpanFromBounds(start, end),
180+
displayParts: undefined,
181+
documentation: undefined,
182+
documentationString: response.body.documentation,
183+
displayString: response.body.displayString
184+
};
185+
}
186+
187+
getCompletionsAtPosition(fileName: string, position: number): CompletionInfo {
188+
var lineCol = this.positionToOneBasedLineCol(fileName, position);
189+
var request = this.processRequest("completions", {
190+
file: fileName,
191+
line: lineCol.line,
192+
col: lineCol.col,
193+
});
194+
195+
var response = this.processResponse<ServerProtocol.CompletionsResponse>(request);
196+
197+
return {
198+
isMemberCompletion: false,
199+
isNewIdentifierLocation: false,
200+
entries: response.body.map(entry => ({ kind: entry.kind, kindModifiers: entry.kindModifiers, name: entry.name }))
201+
};
202+
}
203+
204+
getNavigateToItems(seatchTerm: string): NavigateToItem[] {
205+
var request = this.processRequest("navto", { seatchTerm });
206+
207+
var response = this.processResponse<ServerProtocol.NavtoResponse>(request);
208+
return response.body.map(entry => {
209+
var start = this.lineColToPosition(entry.file.toString(), entry.start);
210+
var end = this.lineColToPosition(entry.file.toString(), entry.end);
211+
return {
212+
name: entry.name,
213+
containerName: entry.containerName,
214+
containerKind: entry.containerKind,
215+
kind: entry.kind,
216+
kindModifiers: entry.kindModifiers,
217+
matchKind: entry.matchKind,
218+
fileName: entry.file.toString(),
219+
textSpan: ts.createTextSpanFromBounds(start, end)
220+
};
221+
});
222+
}
223+
224+
getFormattingEditsForRange(fileName: string, start: number, end: number, options: ts.FormatCodeOptions): ts.TextChange[] {
225+
var startLineCol = this.positionToOneBasedLineCol(fileName, start);
226+
var endLineCol = this.positionToOneBasedLineCol(fileName, end);
227+
// TODO: handle FormatCodeOptions
228+
var request = this.processRequest("format", {
229+
file: fileName,
230+
line: startLineCol.line,
231+
col: startLineCol.col,
232+
endLine: endLineCol.line,
233+
endCol: endLineCol.col,
234+
});
235+
236+
var response = this.processResponse<ServerProtocol.FormatResponse>(request);
237+
238+
return response.body.map(entry=> this.convertCodeEditsToTextChange(fileName, entry));
239+
}
240+
241+
getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): ts.TextChange[] {
242+
return this.getFormattingEditsForRange(fileName, 0, this.getFileLength(fileName), options);
243+
}
244+
245+
getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): ts.TextChange[] {
246+
var lineCol = this.positionToOneBasedLineCol(fileName, position);
247+
// TODO: handle FormatCodeOptions
248+
var request = this.processRequest("formatonkey", {
249+
file: fileName,
250+
line: lineCol.line,
251+
col: lineCol.col,
252+
key: key
253+
});
254+
255+
var response = this.processResponse<ServerProtocol.FormatResponse>(request);
256+
return response.body.map(entry=> this.convertCodeEditsToTextChange(fileName, entry));
257+
}
258+
259+
getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
260+
var lineCol = this.positionToOneBasedLineCol(fileName, position);
261+
var request = this.processRequest("definition", {
262+
file: fileName,
263+
line: lineCol.line,
264+
col: lineCol.col,
265+
});
266+
267+
var response = this.processResponse<ServerProtocol.DefinitionResponse>(request);
268+
return response.body.map(entry => {
269+
var start = this.lineColToPosition(fileName, entry.start);
270+
var end = this.lineColToPosition(fileName, entry.end);
271+
return {
272+
containerKind: "",
273+
containerName: "",
274+
fileName: entry.file,
275+
textSpan: ts.createTextSpanFromBounds(start, end),
276+
kind: "",
277+
name: ""
278+
};
279+
});
280+
}
281+
282+
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
283+
var lineCol = this.positionToOneBasedLineCol(fileName, position);
284+
var request = this.processRequest("references", {
285+
file: fileName,
286+
line: lineCol.line,
287+
col: lineCol.col,
288+
});
289+
290+
var response = this.processResponse<ServerProtocol.ReferencesResponse>(request);
291+
292+
return response.body.refs.map(entry => {
293+
var start = this.lineColToPosition(fileName, entry.start);
294+
var end = this.lineColToPosition(fileName, entry.end);
295+
return {
296+
fileName: entry.file,
297+
textSpan: ts.createTextSpanFromBounds(start, end),
298+
isWriteAccess: false,
299+
};
300+
});
301+
}
302+
303+
getEmitOutput(fileName: string): EmitOutput {
304+
throw new Error("Not Implemented Yet.");
305+
}
306+
307+
getSyntacticDiagnostics(fileName: string): Diagnostic[] {
308+
throw new Error("Not Implemented Yet.");
309+
}
310+
311+
getSemanticDiagnostics(fileName: string): Diagnostic[] {
312+
throw new Error("Not Implemented Yet.");
313+
}
314+
315+
getCompilerOptionsDiagnostics(): Diagnostic[] {
316+
throw new Error("Not Implemented Yet.");
317+
}
318+
319+
getRenameInfo(fileName: string, position: number): RenameInfo {
320+
throw new Error("Not Implemented Yet.");
321+
}
322+
323+
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[] {
324+
throw new Error("Not Implemented Yet.");
325+
}
326+
327+
getNavigationBarItems(fileName: string): NavigationBarItem[] {
328+
throw new Error("Not Implemented Yet.");
329+
}
330+
331+
getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan {
332+
throw new Error("Not Implemented Yet.");
333+
}
334+
335+
getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan {
336+
throw new Error("Not Implemented Yet.");
337+
}
338+
339+
getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems {
340+
throw new Error("Not Implemented Yet.");
341+
}
342+
343+
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
344+
throw new Error("Not Implemented Yet.");
345+
}
346+
347+
getOutliningSpans(fileName: string): OutliningSpan[] {
348+
throw new Error("Not Implemented Yet.");
349+
}
350+
351+
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[] {
352+
throw new Error("Not Implemented Yet.");
353+
}
354+
355+
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[] {
356+
throw new Error("Not Implemented Yet.");
357+
}
358+
359+
getIndentationAtPosition(fileName: string, position: number, options: EditorOptions): number {
360+
throw new Error("Not Implemented Yet.");
361+
}
362+
363+
getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] {
364+
throw new Error("Not Implemented Yet.");
365+
}
366+
367+
getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] {
368+
throw new Error("Not Implemented Yet.");
369+
}
370+
371+
getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
372+
throw new Error("Not Implemented Yet.");
373+
}
374+
375+
376+
getProgram(): Program {
377+
throw new Error("SourceFile objects are not serializable through the server protocol.");
378+
}
379+
380+
getSourceFile(fileName: string): SourceFile {
381+
throw new Error("SourceFile objects are not serializable through the server protocol.");
382+
}
383+
384+
cleanupSemanticCache(): void {
385+
throw new Error("cleanupSemanticCache is not available through the server layer.");
386+
}
387+
388+
dispose(): void {
389+
throw new Error("dispose is not available through the server layer.");
390+
}
391+
}
392+
}

0 commit comments

Comments
 (0)