Skip to content

Commit acafb50

Browse files
committed
Merge branch 'master' of https://github.com/Microsoft/TypeScript into feature/eslint
2 parents 049f5da + 111b73a commit acafb50

21 files changed

+549
-184
lines changed

src/harness/client.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,13 @@ namespace ts.server {
134134
this.processRequest(CommandNames.Close, args);
135135
}
136136

137-
changeFile(fileName: string, start: number, end: number, insertString: string): void {
137+
createChangeFileRequestArgs(fileName: string, start: number, end: number, insertString: string): protocol.ChangeRequestArgs {
138+
return { ...this.createFileLocationRequestArgsWithEndLineAndOffset(fileName, start, end), insertString };
139+
}
140+
141+
changeFile(fileName: string, args: protocol.ChangeRequestArgs): void {
138142
// clear the line map after an edit
139143
this.lineMaps.set(fileName, undefined!); // TODO: GH#18217
140-
141-
const args: protocol.ChangeRequestArgs = { ...this.createFileLocationRequestArgsWithEndLineAndOffset(fileName, start, end), insertString };
142144
this.processRequest(CommandNames.Change, args);
143145
}
144146

src/harness/fourslash.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ namespace FourSlash {
8080
// To add additional option, add property into the testOptMetadataNames, refer the property in either globalMetadataNames or fileMetadataNames
8181
// Add cases into convertGlobalOptionsToCompilationsSettings function for the compiler to acknowledge such option from meta data
8282
const enum MetadataOptionNames {
83-
baselineFile = "BaselineFile",
84-
emitThisFile = "emitThisFile", // This flag is used for testing getEmitOutput feature. It allows test-cases to indicate what file to be output in multiple files project
85-
fileName = "Filename",
86-
resolveReference = "ResolveReference", // This flag is used to specify entry file for resolve file references. The flag is only allow once per test file
87-
symlink = "Symlink",
83+
baselineFile = "baselinefile",
84+
emitThisFile = "emitthisfile", // This flag is used for testing getEmitOutput feature. It allows test-cases to indicate what file to be output in multiple files project
85+
fileName = "filename",
86+
resolveReference = "resolvereference", // This flag is used to specify entry file for resolve file references. The flag is only allow once per test file
87+
symlink = "symlink",
8888
}
8989

9090
// List of allowed metadata names
@@ -150,6 +150,7 @@ namespace FourSlash {
150150
private languageServiceAdapterHost: Harness.LanguageService.LanguageServiceAdapterHost;
151151
private languageService: ts.LanguageService;
152152
private cancellationToken: TestCancellationToken;
153+
private assertTextConsistent: ((fileName: string) => void) | undefined;
153154

154155
// The current caret position in the active file
155156
public currentCaretPosition = 0;
@@ -280,6 +281,9 @@ namespace FourSlash {
280281
const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions);
281282
this.languageServiceAdapterHost = languageServiceAdapter.getHost();
282283
this.languageService = memoWrap(languageServiceAdapter.getLanguageService(), this); // Wrap the LS to cache some expensive operations certain tests call repeatedly
284+
if (this.testType === FourSlashTestType.Server) {
285+
this.assertTextConsistent = fileName => (languageServiceAdapter as Harness.LanguageService.ServerLanguageServiceAdapter).assertTextConsistent(fileName);
286+
}
283287

284288
if (startResolveFileRef) {
285289
// Add the entry-point file itself into the languageServiceShimHost
@@ -1867,6 +1871,9 @@ namespace FourSlash {
18671871

18681872
private editScriptAndUpdateMarkers(fileName: string, editStart: number, editEnd: number, newText: string) {
18691873
this.languageServiceAdapterHost.editScript(fileName, editStart, editEnd, newText);
1874+
if (this.assertTextConsistent) {
1875+
this.assertTextConsistent(fileName);
1876+
}
18701877
for (const marker of this.testData.markers) {
18711878
if (marker.fileName === fileName) {
18721879
marker.position = updatePosition(marker.position, editStart, editEnd, newText);
@@ -3274,7 +3281,7 @@ ${code}
32743281

32753282
function parseTestData(basePath: string, contents: string, fileName: string): FourSlashData {
32763283
// Regex for parsing options in the format "@Alpha: Value of any sort"
3277-
const optionRegex = /^\s*@(\w+): (.*)\s*/;
3284+
const optionRegex = /^\s*@(\w+):\s*(.*)\s*/;
32783285

32793286
// List of all the subfiles we've parsed out
32803287
const files: FourSlashFile[] = [];
@@ -3286,6 +3293,7 @@ ${code}
32863293
// Note: IE JS engine incorrectly handles consecutive delimiters here when using RegExp split, so
32873294
// we have to string-based splitting instead and try to figure out the delimiting chars
32883295
const lines = contents.split("\n");
3296+
let i = 0;
32893297

32903298
const markerPositions = ts.createMap<Marker>();
32913299
const markers: Marker[] = [];
@@ -3314,6 +3322,7 @@ ${code}
33143322
}
33153323

33163324
for (let line of lines) {
3325+
i++;
33173326
if (line.length > 0 && line.charAt(line.length - 1) === "\r") {
33183327
line = line.substr(0, line.length - 1);
33193328
}
@@ -3322,11 +3331,15 @@ ${code}
33223331
const text = line.substr(4);
33233332
currentFileContent = currentFileContent === undefined ? text : currentFileContent + "\n" + text;
33243333
}
3334+
else if (line.substr(0, 3) === "///" && currentFileContent !== undefined) {
3335+
throw new Error("Three-slash line in the middle of four-slash region at line " + i);
3336+
}
33253337
else if (line.substr(0, 2) === "//") {
33263338
// Comment line, check for global/file @options and record them
33273339
const match = optionRegex.exec(line.substr(2));
33283340
if (match) {
3329-
const [key, value] = match.slice(1);
3341+
const key = match[1].toLowerCase();
3342+
const value = match[2];
33303343
if (!ts.contains(fileMetadataNames, key)) {
33313344
// Check if the match is already existed in the global options
33323345
if (globalOptions[key] !== undefined) {

src/harness/harnessLanguageService.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -658,8 +658,9 @@ namespace Harness.LanguageService {
658658
}
659659

660660
editScript(fileName: string, start: number, end: number, newText: string) {
661+
const changeArgs = this.client.createChangeFileRequestArgs(fileName, start, end, newText);
661662
super.editScript(fileName, start, end, newText);
662-
this.client.changeFile(fileName, start, end, newText);
663+
this.client.changeFile(fileName, changeArgs);
663664
}
664665
}
665666

@@ -716,8 +717,8 @@ namespace Harness.LanguageService {
716717
return this.host.getCurrentDirectory();
717718
}
718719

719-
getDirectories(): string[] {
720-
return [];
720+
getDirectories(path: string): string[] {
721+
return this.host.getDirectories(path);
721722
}
722723

723724
getEnvironmentVariable(name: string): string {
@@ -890,9 +891,16 @@ namespace Harness.LanguageService {
890891
}
891892
}
892893

894+
class FourslashSession extends ts.server.Session {
895+
getText(fileName: string) {
896+
return ts.getSnapshotText(this.projectService.getDefaultProjectForFile(ts.server.toNormalizedPath(fileName), /*ensureProject*/ true)!.getScriptSnapshot(fileName)!);
897+
}
898+
}
899+
893900
export class ServerLanguageServiceAdapter implements LanguageServiceAdapter {
894901
private host: SessionClientHost;
895902
private client: ts.server.SessionClient;
903+
private server: FourslashSession;
896904
constructor(cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) {
897905
// This is the main host that tests use to direct tests
898906
const clientHost = new SessionClientHost(cancellationToken, options);
@@ -912,11 +920,12 @@ namespace Harness.LanguageService {
912920
logger: serverHost,
913921
canUseEvents: true
914922
};
915-
const server = new ts.server.Session(opts);
923+
this.server = new FourslashSession(opts);
924+
916925

917926
// Fake the connection between the client and the server
918927
serverHost.writeMessage = client.onMessage.bind(client);
919-
clientHost.writeMessage = server.onMessage.bind(server);
928+
clientHost.writeMessage = this.server.onMessage.bind(this.server);
920929

921930
// Wire the client to the host to get notifications when a file is open
922931
// or edited.
@@ -930,5 +939,20 @@ namespace Harness.LanguageService {
930939
getLanguageService(): ts.LanguageService { return this.client; }
931940
getClassifier(): ts.Classifier { throw new Error("getClassifier is not available using the server interface."); }
932941
getPreProcessedFileInfo(): ts.PreProcessedFileInfo { throw new Error("getPreProcessedFileInfo is not available using the server interface."); }
942+
assertTextConsistent(fileName: string) {
943+
const serverText = this.server.getText(fileName);
944+
const clientText = this.host.readFile(fileName);
945+
ts.Debug.assert(serverText === clientText, [
946+
"Server and client text are inconsistent.",
947+
"",
948+
"\x1b[1mServer\x1b[0m\x1b[31m:",
949+
serverText,
950+
"",
951+
"\x1b[1mClient\x1b[0m\x1b[31m:",
952+
clientText,
953+
"",
954+
"This probably means something is wrong with the fourslash infrastructure, not with the test."
955+
].join(ts.sys.newLine));
956+
}
933957
}
934958
}

src/services/classifier.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,14 @@ namespace ts {
771771
return false;
772772
}
773773

774+
// Limiting classification to exactly the elements and attributes
775+
// defined in `ts.commentPragmas` would be excessive, but we can avoid
776+
// some obvious false positives (e.g. in XML-like doc comments) by
777+
// checking the element name.
778+
if (!match[3] || !(match[3] in commentPragmas)) {
779+
return false;
780+
}
781+
774782
let pos = start;
775783

776784
pushCommentRange(pos, match[1].length); // ///
@@ -779,10 +787,6 @@ namespace ts {
779787
pushClassification(pos, match[2].length, ClassificationType.punctuation); // <
780788
pos += match[2].length;
781789

782-
if (!match[3]) {
783-
return true;
784-
}
785-
786790
pushClassification(pos, match[3].length, ClassificationType.jsxSelfClosingTagName); // element name
787791
pos += match[3].length;
788792

0 commit comments

Comments
 (0)