-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TS Server #2041
TS Server #2041
Changes from all commits
acd4914
e4128af
9735b74
27a9084
17f19b2
7b28f20
abec4f9
54f3250
0963644
ee3ee05
07d37fa
6e94f39
4a44b74
c987ab9
3e86e55
c0b1254
93aa3f1
99373db
a0b557e
dfd8a06
b175045
ce828d0
89267bc
d396ddf
9867e06
11e2460
178e8f7
40d3cb7
2a02655
50ca35a
8944df1
76c7fdf
d9d2e99
f5c1bfb
d081c9c
ca34838
8a9ac8d
bdd0bf3
bbcdb61
54e6756
32e2f4d
d2712dd
cadd57c
3868fb5
4b59083
d364f61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#!/usr/bin/env node | ||
require('./tsserver.js') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
/// <reference path='..\services\services.ts' /> | ||
/// <reference path='..\services\shims.ts' /> | ||
/// <reference path='..\server\client.ts' /> | ||
/// <reference path='harness.ts' /> | ||
|
||
module Harness.LanguageService { | ||
|
@@ -23,18 +24,18 @@ module Harness.LanguageService { | |
this.version++; | ||
} | ||
|
||
public editContent(minChar: number, limChar: number, newText: string): void { | ||
public editContent(start: number, end: number, newText: string): void { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to stop doing |
||
// Apply edits | ||
var prefix = this.content.substring(0, minChar); | ||
var prefix = this.content.substring(0, start); | ||
var middle = newText; | ||
var suffix = this.content.substring(limChar); | ||
var suffix = this.content.substring(end); | ||
this.setContent(prefix + middle + suffix); | ||
|
||
// Store edit range + new length of script | ||
this.editRanges.push({ | ||
length: this.content.length, | ||
textChangeRange: ts.createTextChangeRange( | ||
ts.createTextSpanFromBounds(minChar, limChar), newText.length) | ||
ts.createTextSpanFromBounds(start, end), newText.length) | ||
}); | ||
|
||
// Update version # | ||
|
@@ -145,24 +146,17 @@ module Harness.LanguageService { | |
this.fileNameToScript[fileName] = new ScriptInfo(fileName, content); | ||
} | ||
|
||
public updateScript(fileName: string, content: string) { | ||
public editScript(fileName: string, start: number, end: number, newText: string) { | ||
var script = this.getScriptInfo(fileName); | ||
if (script !== null) { | ||
script.updateContent(content); | ||
script.editContent(start, end, newText); | ||
return; | ||
} | ||
|
||
this.addScript(fileName, content); | ||
throw new Error("No script with name '" + fileName + "'"); | ||
} | ||
|
||
public editScript(fileName: string, minChar: number, limChar: number, newText: string) { | ||
var script = this.getScriptInfo(fileName); | ||
if (script !== null) { | ||
script.editContent(minChar, limChar, newText); | ||
return; | ||
} | ||
|
||
throw new Error("No script with name '" + fileName + "'"); | ||
public openFile(fileName: string): void { | ||
} | ||
|
||
/** | ||
|
@@ -236,8 +230,7 @@ module Harness.LanguageService { | |
getFilenames(): string[] { return this.nativeHost.getFilenames(); } | ||
getScriptInfo(fileName: string): ScriptInfo { return this.nativeHost.getScriptInfo(fileName); } | ||
addScript(fileName: string, content: string): void { this.nativeHost.addScript(fileName, content); } | ||
updateScript(fileName: string, content: string): void { return this.nativeHost.updateScript(fileName, content); } | ||
editScript(fileName: string, minChar: number, limChar: number, newText: string): void { this.nativeHost.editScript(fileName, minChar, limChar, newText); } | ||
editScript(fileName: string, start: number, end: number, newText: string): void { this.nativeHost.editScript(fileName, start, end, newText); } | ||
lineColToPosition(fileName: string, line: number, col: number): number { return this.nativeHost.lineColToPosition(fileName, line, col); } | ||
positionToZeroBasedLineCol(fileName: string, position: number): ts.LineAndCharacter { return this.nativeHost.positionToZeroBasedLineCol(fileName, position); } | ||
|
||
|
@@ -442,5 +435,156 @@ module Harness.LanguageService { | |
return convertResult; | ||
} | ||
} | ||
|
||
// Server adapter | ||
class SessionClientHost extends NativeLanguageServiceHost implements ts.server.SessionClientHost { | ||
private client: ts.server.SessionClient; | ||
|
||
constructor(cancellationToken: ts.CancellationToken, settings: ts.CompilerOptions) { | ||
super(cancellationToken, settings); | ||
} | ||
|
||
onMessage(message: string): void { | ||
|
||
} | ||
|
||
writeMessage(message: string): void { | ||
|
||
} | ||
|
||
setClient(client: ts.server.SessionClient) { | ||
this.client = client; | ||
} | ||
|
||
openFile(fileName: string): void { | ||
super.openFile(fileName); | ||
this.client.openFile(fileName); | ||
} | ||
|
||
editScript(fileName: string, start: number, end: number, newText: string) { | ||
super.editScript(fileName, start, end, newText); | ||
this.client.changeFile(fileName, start, end, newText); | ||
} | ||
} | ||
|
||
class SessionServerHost implements ts.server.ServerHost, ts.server.Logger { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd really like to see all classes documented to explain what purpose they serve. |
||
args: string[] = []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fields should be private. |
||
newLine: string; | ||
useCaseSensitiveFileNames: boolean = false; | ||
|
||
constructor(private host: NativeLanguageServiceHost) { | ||
this.newLine = this.host.getNewLine(); | ||
} | ||
|
||
onMessage(message: string): void { | ||
|
||
} | ||
|
||
writeMessage(message: string): void { | ||
} | ||
|
||
write(message: string): void { | ||
this.writeMessage(message); | ||
} | ||
|
||
readFile(fileName: string): string { | ||
if (fileName.indexOf(Harness.Compiler.defaultLibFileName) >= 0) { | ||
fileName = Harness.Compiler.defaultLibFileName; | ||
} | ||
|
||
var snapshot = this.host.getScriptSnapshot(fileName); | ||
return snapshot && snapshot.getText(0, snapshot.getLength()); | ||
} | ||
|
||
writeFile(name: string, text: string, writeByteOrderMark: boolean): void { | ||
} | ||
|
||
resolvePath(path: string): string { | ||
return path; | ||
} | ||
|
||
fileExists(path: string): boolean { | ||
return !!this.host.getScriptSnapshot(path); | ||
} | ||
|
||
directoryExists(path: string): boolean { | ||
return false; | ||
} | ||
|
||
getExecutingFilePath(): string { | ||
return ""; | ||
} | ||
|
||
exit(exitCode: number): void { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this not also throw? |
||
} | ||
|
||
createDirectory(directoryName: string): void { | ||
throw new Error("Not Implemented Yet."); | ||
} | ||
|
||
getCurrentDirectory(): string { | ||
return this.host.getCurrentDirectory(); | ||
} | ||
|
||
readDirectory(path: string, extension?: string): string[] { | ||
throw new Error("Not implemented Yet."); | ||
} | ||
|
||
watchFile(fileName: string, callback: (fileName: string) => void): ts.FileWatcher { | ||
return { close() { } }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put this on a new line. Also, maybe this should throw. |
||
} | ||
|
||
close(): void { | ||
} | ||
|
||
info(message: string): void { | ||
return this.host.log(message); | ||
} | ||
|
||
msg(message: string) { | ||
return this.host.log(message); | ||
} | ||
|
||
endGroup(): void { | ||
} | ||
|
||
perftrc(message: string): void { | ||
return this.host.log(message); | ||
} | ||
|
||
startGroup(): void { | ||
} | ||
} | ||
|
||
export class ServerLanugageServiceAdapter implements LanguageServiceAdapter { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe |
||
private host: SessionClientHost; | ||
private client: ts.server.SessionClient; | ||
constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) { | ||
// This is the main host that tests use to direct tests | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know what this comment means. |
||
var clientHost = new SessionClientHost(cancellationToken, options); | ||
var client = new ts.server.SessionClient(clientHost); | ||
|
||
// This host is just a proxy for the clientHost, it uses the client | ||
// host to answer server queries about files on disk | ||
var serverHost = new SessionServerHost(clientHost); | ||
var server = new ts.server.Session(serverHost, serverHost); | ||
|
||
// Fake the connection between the client and the server | ||
serverHost.writeMessage = client.onMessage.bind(client); | ||
clientHost.writeMessage = server.onMessage.bind(server); | ||
|
||
// Wire the client to the host to get notifications when a file is open | ||
// or edited. | ||
clientHost.setClient(client); | ||
|
||
// Set the properties | ||
this.client = client; | ||
this.host = clientHost; | ||
} | ||
getHost() { return this.host; } | ||
getLanguageService(): ts.LanguageService { return this.client; } | ||
getClassifier(): ts.Classifier { throw new Error("getClassifier is not available using the server interface."); } | ||
getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo { throw new Error("getPreProcessedFileInfo is not available using the server interface."); } | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It woudl be great if you documented. Explain that fourslash always tests the LS api. And, we can take advantage of that to test the shim and server layers by wrapping them so tehy expose the LS api. etc. etc.