Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 52 additions & 17 deletions scripts/buildProtocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,27 @@ function endsWith(s: string, suffix: string) {
class DeclarationsWalker {
private visitedTypes: ts.Type[] = [];
private text = "";
private removedTypes: ts.Type[] = [];

private constructor(private typeChecker: ts.TypeChecker, private protocolFile: ts.SourceFile) {
}

static getExtraDeclarations(typeChecker: ts.TypeChecker, protocolFile: ts.SourceFile): string {
let text = "declare namespace ts.server.protocol {\n";
var walker = new DeclarationsWalker(typeChecker, protocolFile);
walker.visitTypeNodes(protocolFile);
return walker.text
text = walker.text
? `declare namespace ts.server.protocol {\n${walker.text}}`
: "";
if (walker.removedTypes) {
text += "\ndeclare namespace ts {\n";
text += " // these types are empty stubs for types from services and should not be used directly\n"
for (const type of walker.removedTypes) {
text += ` export type ${type.symbol.name} = never;\n`;
}
text += "}"
}
return text;
}

private processType(type: ts.Type): void {
Expand All @@ -41,19 +52,18 @@ class DeclarationsWalker {
if (sourceFile === this.protocolFile || path.basename(sourceFile.fileName) === "lib.d.ts") {
return;
}
// splice declaration in final d.ts file
let text = decl.getFullText();
if (decl.kind === ts.SyntaxKind.EnumDeclaration && !(decl.flags & ts.NodeFlags.Const)) {
// patch enum declaration to make them constan
const declStart = decl.getStart() - decl.getFullStart();
const prefix = text.substring(0, declStart);
const suffix = text.substring(declStart + "enum".length, decl.getEnd() - decl.getFullStart());
text = prefix + "const enum" + suffix;
if (decl.kind === ts.SyntaxKind.EnumDeclaration) {
this.removedTypes.push(type);
return;
}
this.text += `${text}\n`;
else {
// splice declaration in final d.ts file
let text = decl.getFullText();
this.text += `${text}\n`;
// recursively pull all dependencies into result dts file

// recursively pull all dependencies into result dts file
this.visitTypeNodes(decl);
this.visitTypeNodes(decl);
}
}
}
}
Expand All @@ -69,15 +79,37 @@ class DeclarationsWalker {
case ts.SyntaxKind.Parameter:
case ts.SyntaxKind.IndexSignature:
if (((<ts.VariableDeclaration | ts.MethodDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration | ts.PropertySignature | ts.MethodSignature | ts.IndexSignatureDeclaration>node.parent).type) === node) {
const type = this.typeChecker.getTypeAtLocation(node);
if (type && !(type.flags & ts.TypeFlags.TypeParameter)) {
this.processType(type);
}
this.processTypeOfNode(node);
}
break;
case ts.SyntaxKind.InterfaceDeclaration:
const heritageClauses = (<ts.InterfaceDeclaration>node.parent).heritageClauses;
if (heritageClauses) {
if (heritageClauses[0].token !== ts.SyntaxKind.ExtendsKeyword) {
throw new Error(`Unexpected kind of heritage clause: ${ts.SyntaxKind[heritageClauses[0].kind]}`);
}
for (const type of heritageClauses[0].types) {
this.processTypeOfNode(type);
}
}
break;
}
}
ts.forEachChild(node, n => this.visitTypeNodes(n));
}

private processTypeOfNode(node: ts.Node): void {
if (node.kind === ts.SyntaxKind.UnionType) {
for (const t of (<ts.UnionTypeNode>node).types) {
this.processTypeOfNode(t);
}
}
else {
const type = this.typeChecker.getTypeAtLocation(node);
if (type && !(type.flags & (ts.TypeFlags.TypeParameter))) {
this.processType(type);
}
}
}
}

Expand Down Expand Up @@ -128,9 +160,12 @@ function generateProtocolFile(protocolTs: string, typeScriptServicesDts: string)
if (extraDeclarations) {
protocolDts += extraDeclarations;
}
protocolDts += "\nimport protocol = ts.server.protocol;";
protocolDts += "\nexport = protocol;";
protocolDts += "\nexport as namespace protocol;";
// do sanity check and try to compile generated text as standalone program
const sanityCheckProgram = getProgramWithProtocolText(protocolDts, /*includeTypeScriptServices*/ false);
const diagnostics = [...program.getSyntacticDiagnostics(), ...program.getSemanticDiagnostics(), ...program.getGlobalDiagnostics()];
const diagnostics = [...sanityCheckProgram.getSyntacticDiagnostics(), ...sanityCheckProgram.getSemanticDiagnostics(), ...sanityCheckProgram.getGlobalDiagnostics()];
if (diagnostics.length) {
const flattenedDiagnostics = diagnostics.map(d => ts.flattenDiagnosticMessageText(d.messageText, "\n")).join("\n");
throw new Error(`Unexpected errors during sanity check: ${flattenedDiagnostics}`);
Expand Down
69 changes: 58 additions & 11 deletions src/harness/unittests/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,18 @@ namespace ts.server {
getLogFileName: (): string => undefined
};

class TestSession extends Session {
getProjectService() {
return this.projectService;
}
}

describe("the Session class", () => {
let session: Session;
let session: TestSession;
let lastSent: protocol.Message;

beforeEach(() => {
session = new Session(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true);
session = new TestSession(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true);
session.send = (msg: protocol.Message) => {
lastSent = msg;
};
Expand All @@ -55,7 +61,7 @@ namespace ts.server {
const req: protocol.FileRequest = {
command: CommandNames.Open,
seq: 0,
type: "command",
type: "request",
arguments: {
file: undefined
}
Expand All @@ -67,7 +73,7 @@ namespace ts.server {
const req: protocol.Request = {
command: "foobar",
seq: 0,
type: "command"
type: "request"
};

session.executeCommand(req);
Expand All @@ -85,7 +91,7 @@ namespace ts.server {
const req: protocol.ConfigureRequest = {
command: CommandNames.Configure,
seq: 0,
type: "command",
type: "request",
arguments: {
hostInfo: "unit test",
formatOptions: {
Expand All @@ -106,6 +112,47 @@ namespace ts.server {
body: undefined
});
});
it ("should handle literal types in request", () => {
const configureRequest: protocol.ConfigureRequest = {
command: CommandNames.Configure,
seq: 0,
type: "request",
arguments: {
formatOptions: {
indentStyle: "Block"
}
}
};

session.onMessage(JSON.stringify(configureRequest));

assert.equal(session.getProjectService().getFormatCodeOptions().indentStyle, IndentStyle.Block);

const setOptionsRequest: protocol.SetCompilerOptionsForInferredProjectsRequest = {
command: CommandNames.CompilerOptionsForInferredProjects,
seq: 1,
type: "request",
arguments: {
options: {
module: "System",
target: "ES5",
jsx: "React",
newLine: "Lf",
moduleResolution: "Node"
}
}
};
session.onMessage(JSON.stringify(setOptionsRequest));
assert.deepEqual(
session.getProjectService().getCompilerOptionsForInferredProjects(),
<CompilerOptions>{
module: ModuleKind.System,
target: ScriptTarget.ES5,
jsx: JsxEmit.React,
newLine: NewLineKind.LineFeed,
moduleResolution: ModuleResolutionKind.NodeJs
});
});
});

describe("onMessage", () => {
Expand All @@ -118,7 +165,7 @@ namespace ts.server {
const req: protocol.Request = {
command: name,
seq: i,
type: "command"
type: "request"
};
i++;
session.onMessage(JSON.stringify(req));
Expand Down Expand Up @@ -151,7 +198,7 @@ namespace ts.server {
const req: protocol.ConfigureRequest = {
command: CommandNames.Configure,
seq: 0,
type: "command",
type: "request",
arguments: {
hostInfo: "unit test",
formatOptions: {
Expand All @@ -175,7 +222,7 @@ namespace ts.server {

describe("send", () => {
it("is an overrideable handle which sends protocol messages over the wire", () => {
const msg = { seq: 0, type: "none" };
const msg: server.protocol.Request = { seq: 0, type: "request", command: "" };
const strmsg = JSON.stringify(msg);
const len = 1 + Utils.byteLength(strmsg, "utf8");
const resultMsg = `Content-Length: ${len}\r\n\r\n${strmsg}\n`;
Expand Down Expand Up @@ -203,7 +250,7 @@ namespace ts.server {
expect(session.executeCommand({
command,
seq: 0,
type: "command"
type: "request"
})).to.deep.equal(result);
});
it("throws when a duplicate handler is passed", () => {
Expand Down Expand Up @@ -304,7 +351,7 @@ namespace ts.server {

expect(session.executeCommand({
seq: 0,
type: "command",
type: "request",
command: session.customHandler
})).to.deep.equal({
response: undefined,
Expand Down Expand Up @@ -405,7 +452,7 @@ namespace ts.server {
this.seq++;
this.server.enqueue({
seq: this.seq,
type: "command",
type: "request",
command,
arguments: args
});
Expand Down
Loading