Recover from git corruption

This commit is contained in:
Mohamed Hegazy 2015-02-14 15:12:06 -08:00
parent 99373dbd89
commit a0b557e1e2
6 changed files with 601 additions and 1167 deletions

View file

@ -128,7 +128,9 @@ module Harness.LanguageService {
protected settings = ts.getDefaultCompilerOptions()) { protected settings = ts.getDefaultCompilerOptions()) {
} }
public getNewLine(): string { return "\r\n"; } public getNewLine(): string {
return "\r\n";
}
public getFilenames(): string[] { public getFilenames(): string[] {
var fileNames: string[] = []; var fileNames: string[] = [];
@ -435,17 +437,26 @@ module Harness.LanguageService {
} }
// Server adapter // Server adapter
class ServerLanguageServiceHost extends NativeLanguageServiceHost { class SessionClientHost extends NativeLanguageServiceHost implements ts.server.SessionClientHost {
private client: ts.server.SessionClient; private client: ts.server.SessionClient;
constructor(cancellationToken: ts.CancellationToken, settings: ts.CompilerOptions) { constructor(cancellationToken: ts.CancellationToken, settings: ts.CompilerOptions) {
super(cancellationToken, settings); super(cancellationToken, settings);
} }
setClient(client: ts.server.SessionClient) { onMessage(message: string): void {
}
writeMessage(message: string): void {
}
setClient(client: ts.server.SessionClient) {
this.client = client; this.client = client;
} }
openFile(fileName: string): void { openFile(fileName: string): void {
super.openFile(fileName); super.openFile(fileName);
this.client.openFile(fileName); this.client.openFile(fileName);
} }
@ -456,15 +467,135 @@ module Harness.LanguageService {
} }
} }
class SessionServerHost implements ts.server.ServerHost, ts.server.Logger {
args: string[] = [];
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 {
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 {
}
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.");
}
getModififedTime(fileName: string): Date {
return new Date();
}
stat(path: string, callback?: (err: any, stats: any) => any) {
return 0;
}
lineColToPosition(fileName: string, line: number, col: number): number {
return this.host.lineColToPosition(fileName, line, col);
}
positionToZeroBasedLineCol(fileName: string, position: number): ts.LineAndCharacter {
return this.host.positionToZeroBasedLineCol(fileName, position);
}
getFileLength(fileName: string): number {
return this.host.getScriptSnapshot(fileName).getLength();
}
getFileNames(): string[] {
return this.host.getScriptFileNames();
}
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 { export class ServerLanugageServiceAdapter implements LanguageServiceAdapter {
private host: ServerLanguageServiceHost; private host: SessionClientHost;
private client: ts.server.SessionClient; private client: ts.server.SessionClient;
constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) { constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) {
debugger; // This is the main host that tests use to direct tests
var clientHost = new SessionClientHost(cancellationToken, options);
var client = new ts.server.SessionClient(clientHost);
this.host = new ServerLanguageServiceHost(cancellationToken, options); // This host is just a proxy for the clientHost, it uses the client
this.client = new ts.server.SessionClient(this.host, /*abbreviate*/ true); // host to answer server queries about files on disk
this.host.setClient(this.client); var serverHost = new SessionServerHost(clientHost);
var server = new ts.server.Session(serverHost, serverHost, /*useProtocol*/ true, /*prettyJSON*/ false);
// 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; } getHost() { return this.host; }
getLanguageService(): ts.LanguageService { return this.client; } getLanguageService(): ts.LanguageService { return this.client; }

View file

@ -1,177 +1,49 @@
/// <reference path="protocol.ts" /> /// <reference path="protocol.ts" />
module ts.server {
export interface SessionClientHost extends LanguageServiceHost {
lineColToPosition(fileName: string, line: number, col: number): number;
positionToZeroBasedLineCol(fileName: string, position: number): ts.LineAndCharacter;
}
class SessionClientHostProxy implements ServerHost, Logger {
args: string[] = [];
newLine: string;
useCaseSensitiveFileNames: boolean = false;
lastReply: string;
constructor(private host: SessionClientHost) {
this.newLine = this.host.getNewLine();
}
write(message: string): void { module ts.server {
this.lastReply = message;
}
readFile(fileName: string): string { export interface SessionClientHost extends LanguageServiceHost {
var snapshot = this.host.getScriptSnapshot(fileName); writeMessage(message: string): void;
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 {
}
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.");
}
getModififedTime(fileName: string): Date {
return new Date();
}
stat(path: string, callback?: (err: any, stats: any) => any) {
return 0;
}
lineColToPosition(fileName: string, line: number, col: number): number {
return this.host.lineColToPosition(fileName, line, col);
}
positionToZeroBasedLineCol(fileName: string, position: number): ts.LineAndCharacter {
return this.host.positionToZeroBasedLineCol(fileName, position);
}
getFileLength(fileName: string): number {
return this.host.getScriptSnapshot(fileName).getLength();
}
getFileNames(): string[] {
return this.host.getScriptFileNames();
}
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 SessionClient implements LanguageService {
private session: Session;
private sequence: number = 0;
private host: SessionClientHostProxy;
private expantionTable: ts.Map<string>;
private fileMapping: ts.Map<string> = {};
constructor(host: SessionClientHost, abbreviate: boolean) {
this.sequence = 0;
this.host = new SessionClientHostProxy(host);
this.session = new Session(this.host, this.host, /* useProtocol */ true, /*prettyJSON*/ false);
// Setup the abbreviation table
if (abbreviate) {
this.setupExpantionTable()
}
}
private setupExpantionTable(): void {
var request = this.processRequest<ServerProtocol.AbbrevRequest>(CommandNames.Abbrev);
var response = this.processResponse<ServerProtocol.AbbrevResponse>(request);
var abbriviationTable = response.body;
Debug.assert(!!abbriviationTable, "Could not setup abbreviation. Abbreviation table was empty."); export class SessionClient implements LanguageService {
private sequence: number = 0;
var expantionTable: ts.Map<string> = {}; private fileMapping: ts.Map<string> = {};
for (var p in abbriviationTable) { private lineMaps: ts.Map<number[]> = {};
if (abbriviationTable.hasOwnProperty(p)) { private messages: string[] = [];
expantionTable[abbriviationTable[p]] = p;
} constructor(private host: SessionClientHost) {
}
this.expantionTable = expantionTable;
}
private expand<T>(obj: T): T {
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
if (typeof (<any>obj)[p] === "object") {
// Expand the property value
(<any>obj)[p] = this.expand((<any>obj)[p]);
}
// Substitute the name if applicaple
var substitution = ts.lookUp(this.expantionTable, p);
if (substitution) {
(<any>obj)[substitution] = (<any>obj)[p];
(<any>obj)[p] = undefined;
}
}
}
return obj;
}
private lineColToPosition(fileName: string, lineCol: ServerProtocol.LineCol): number {
return this.host.lineColToPosition(fileName, lineCol.line, lineCol.col);
} }
private positionToOneBasedLineCol(fileName: string, position: number): ServerProtocol.LineCol { public onMessage(message: string): void {
var lineCol = this.host.positionToZeroBasedLineCol(fileName, position); this.messages.push(message);
}
private writeMessage(message: string): void {
this.host.writeMessage(message);
}
private getLineMap(fileName: string): number[] {
var lineMap = ts.lookUp(this.lineMaps, fileName);
if (!lineMap) {
var scriptSnapshot = this.host.getScriptSnapshot(fileName);
lineMap = this.lineMaps[fileName] = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
}
return lineMap;
}
private lineColToPosition(fileName: string, lineCol: ServerProtocol.LineCol): number {
return ts.computePositionFromLineAndCharacter(this.getLineMap(fileName), lineCol.line, lineCol.col);
}
private positionToOneBasedLineCol(fileName: string, position: number): ServerProtocol.LineCol {
var lineCol = ts.computeLineAndCharacterOfPosition(this.getLineMap(fileName), position);
return { return {
line: lineCol.line + 1, line: lineCol.line,
col: lineCol.character + 1 col: lineCol.character
}; };
} }
private convertCodeEditsToTextChange(fileName: string, codeEdit: ServerProtocol.CodeEdit): ts.TextChange { private convertCodeEditsToTextChange(fileName: string, codeEdit: ServerProtocol.CodeEdit): ts.TextChange {
var start = this.lineColToPosition(fileName, codeEdit.start); var start = this.lineColToPosition(fileName, codeEdit.start);
var end = this.lineColToPosition(fileName, codeEdit.end); var end = this.lineColToPosition(fileName, codeEdit.end);
@ -180,8 +52,8 @@ module ts.server {
span: ts.createTextSpanFromBounds(start, end), span: ts.createTextSpanFromBounds(start, end),
newText: codeEdit.newText newText: codeEdit.newText
}; };
} }
private getFileNameFromEncodedFile(fileId: ServerProtocol.EncodedFile): string { private getFileNameFromEncodedFile(fileId: ServerProtocol.EncodedFile): string {
var fileName: string; var fileName: string;
if (typeof fileId === "object") { if (typeof fileId === "object") {
@ -196,9 +68,9 @@ module ts.server {
Debug.fail("Got unexpedted fileId type."); Debug.fail("Got unexpedted fileId type.");
} }
return fileName; return fileName;
} }
private processRequest<T extends ServerProtocol.Request>(command: string, arguments?: any): T { private processRequest<T extends ServerProtocol.Request>(command: string, arguments?: any): T {
var request: ServerProtocol.Request = { var request: ServerProtocol.Request = {
seq: this.sequence++, seq: this.sequence++,
type: "request", type: "request",
@ -206,34 +78,33 @@ module ts.server {
arguments: arguments arguments: arguments
}; };
this.session.executeJSONcmd(JSON.stringify(request)); this.writeMessage(JSON.stringify(request));
return <T>request; return <T>request;
} }
private processResponse<T extends ServerProtocol.Response>(request: ServerProtocol.Request): T { private processResponse<T extends ServerProtocol.Response>(request: ServerProtocol.Request): T {
debugger; debugger;
var lastMessage = this.host.lastReply; var lastMessage = this.messages.shift();
this.host.lastReply = undefined; Debug.assert(!!lastMessage, "Did not recieve any responses.");
Debug.assert(!!lastMessage, "Did not recieve any responses.");
// Read the content length
// Read the content length var contentLengthPrefix = "Content-Length: ";
var contentLengthPrefix = "Content-Length: "; var lines = lastMessage.split("\r\n");
var lines = lastMessage.split("\r\n"); Debug.assert(lines.length >= 2, "Malformed response: Expected 3 lines in the response.");
Debug.assert(lines.length >= 2, "Malformed response: Expected 3 lines in the response.");
var contentLengthText = lines[0];
var contentLengthText = lines[0]; Debug.assert(contentLengthText.indexOf(contentLengthPrefix) === 0, "Malformed response: Response text did not contain content-length header.");
Debug.assert(contentLengthText.indexOf(contentLengthPrefix) === 0, "Malformed response: Response text did not contain content-length header."); var contentLength = parseInt(contentLengthText.substring(contentLengthPrefix.length));
var contentLength = parseInt(contentLengthText.substring(contentLengthPrefix.length));
// Read the body
// Read the body var responseBody = lines[2];
var responseBody = lines[2];
// Verify content length
// Verify content length Debug.assert(responseBody.length + 1 === contentLength, "Malformed response: Content length did not match the response's body length.");
Debug.assert(responseBody.length + 1 === contentLength, "Malformed response: Content length did not match the response's body length.");
try {
try {
var response: T = JSON.parse(responseBody); var response: T = JSON.parse(responseBody);
} }
catch (e) { catch (e) {
@ -250,13 +121,9 @@ module ts.server {
Debug.assert(!!response.body, "Malformed response: Unexpected empty response body."); Debug.assert(!!response.body, "Malformed response: Unexpected empty response body.");
if (this.expantionTable) {
// Expand the response if abbreviated
return this.expand(response);
}
return response; return response;
} }
openFile(fileName: string): void { openFile(fileName: string): void {
var args: ServerProtocol.FileRequestArgs = { file: fileName }; var args: ServerProtocol.FileRequestArgs = { file: fileName };
this.processRequest(CommandNames.Open, args); this.processRequest(CommandNames.Open, args);
@ -268,6 +135,9 @@ module ts.server {
} }
changeFile(fileName: string, start: number, end: number, newText: string): void { changeFile(fileName: string, start: number, end: number, newText: string): void {
// clear the line map after an edit
this.lineMaps[fileName] = undefined;
var lineCol = this.positionToOneBasedLineCol(fileName, start); var lineCol = this.positionToOneBasedLineCol(fileName, start);
var args: ServerProtocol.ChangeRequestArgs = { var args: ServerProtocol.ChangeRequestArgs = {
file: fileName, file: fileName,
@ -326,7 +196,7 @@ module ts.server {
getNavigateToItems(searchTerm: string): NavigateToItem[] { getNavigateToItems(searchTerm: string): NavigateToItem[] {
var args: ServerProtocol.NavtoRequestArgs = { var args: ServerProtocol.NavtoRequestArgs = {
searchTerm, searchTerm,
file: this.host.getFileNames()[0] file: this.host.getScriptFileNames()[0]
}; };
var request = this.processRequest<ServerProtocol.NavtoRequest>(CommandNames.Navto, args); var request = this.processRequest<ServerProtocol.NavtoRequest>(CommandNames.Navto, args);
@ -357,7 +227,7 @@ module ts.server {
file: fileName, file: fileName,
line: startLineCol.line, line: startLineCol.line,
col: startLineCol.col, col: startLineCol.col,
endLine: endLineCol.line, endLine: endLineCol.line,
endCol: endLineCol.col, endCol: endLineCol.col,
}; };
@ -369,7 +239,7 @@ module ts.server {
} }
getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): ts.TextChange[] { getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): ts.TextChange[] {
return this.getFormattingEditsForRange(fileName, 0, this.host.getFileLength(fileName), options); return this.getFormattingEditsForRange(fileName, 0, this.host.getScriptSnapshot(fileName).getLength(), options);
} }
getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): ts.TextChange[] { getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): ts.TextChange[] {
@ -537,7 +407,7 @@ module ts.server {
} }
dispose(): void { dispose(): void {
throw new Error("dispose is not available through the server layer."); throw new Error("dispose is not available through the server layer.");
} }
} }
} }

View file

@ -425,7 +425,8 @@ module ts.server {
if (projectOptions.compilerOptions) { if (projectOptions.compilerOptions) {
this.compilerService.setCompilerOptions(projectOptions.compilerOptions); this.compilerService.setCompilerOptions(projectOptions.compilerOptions);
} }
// TODO: format code options } // TODO: format code options
}
} }
export interface ProjectOpenResult { export interface ProjectOpenResult {

File diff suppressed because it is too large Load diff

View file

@ -188,18 +188,6 @@ declare module ServerProtocol {
} }
} }
/**
Type request; value of command field is "type". Return response
giving the code locations that define the type of the symbol
found in file at location line, col.
*/
export interface TypeRequest extends CodeLocationRequest {
}
export interface TypeResponse extends Response {
body?: CodeSpan[];
}
/** /**
Open request; value of command field is "open". Notify the Open request; value of command field is "open". Notify the
server that the client has file open. The server will not server that the client has file open. The server will not
@ -483,8 +471,8 @@ declare module ServerProtocol {
kindModifiers?: string; kindModifiers?: string;
/** /**
The file in which the symbol is found; the value of this The file in which the symbol is found; the value of this
field will always be a string unless the client opts-in to field will always be a string, number of a mapping between
file encoding by sending the "abbrev" request. a string and a number.
*/ */
file: EncodedFile; file: EncodedFile;
/** The location within file at which the symbol is found*/ /** The location within file at which the symbol is found*/
@ -528,27 +516,6 @@ declare module ServerProtocol {
arguments: ChangeRequestArgs; arguments: ChangeRequestArgs;
} }
/*
The following messages describe an OPTIONAL compression scheme
that clients can choose to use. If a client does not opt-in to
this scheme by sending an "abbrev" request, the server will not
compress messages.
*/
/**
Abbrev request message; value of command field is "abbrev".
Server returns an array of mappings from common message field
names and common message field string values to shortened
versions of these strings. Once a client opts-in by requesting
the abbreviations, the server may send responses whose field
names are shortened. The server may also send file names as
instances of AssignFileId, or as file ids, if the corresponding
id assignment had been previously returned. Currently, only
responses to the "navto" request can be encoded.
*/
export interface AbbrevRequest extends Request {
}
/** /**
If an object of this type is returned in place of a string as If an object of this type is returned in place of a string as
the value of a file field in a response message, add the the value of a file field in a response message, add the
@ -572,25 +539,6 @@ declare module ServerProtocol {
*/ */
export type EncodedFile = number | IdFile | string; export type EncodedFile = number | IdFile | string;
/**
Response to abbrev opt-in request message. This is a map of
string field names and common string field values to shortened
strings. Once the server sends this response, it will assume
that it can use the shortened field names and field values. In
addition, the server will assume it can assign ids to file
names by returning an AssignFileId instance in place of a file
name. Once an AssignFileId instance is returned, the server
may send the file id (a number) in place of the file name.
*/
export interface AbbrevResponse extends Response {
body?: {
/** Map from full string (either field name or string
field value) to shortened string */
[fullString: string]: string;
}
}
/** Response to "brace" request. */ /** Response to "brace" request. */
export interface BraceResponse extends Response { export interface BraceResponse extends Response {
body?: TextSpan[]; body?: TextSpan[];

View file

@ -1,18 +1,18 @@
/// <reference path="node.d.ts" /> /// <reference path="node.d.ts" />
/// <reference path="protocol.ts" /> /// <reference path="protocol.ts" />
module ts.server { module ts.server {
var nodeproto: typeof NodeJS._debugger = require('_debugger'); var nodeproto: typeof NodeJS._debugger = require('_debugger');
var readline: NodeJS.ReadLine = require('readline'); var readline: NodeJS.ReadLine = require('readline');
var path: NodeJS.Path = require('path'); var path: NodeJS.Path = require('path');
var fs: typeof NodeJS.fs = require('fs'); var fs: typeof NodeJS.fs = require('fs');
var rl = readline.createInterface({ var rl = readline.createInterface({
input: process.stdin, input: process.stdin,
output: process.stdout, output: process.stdout,
terminal: false, terminal: false,
}); });
class Logger implements ts.server.Logger { class Logger implements ts.server.Logger {
fd = -1; fd = -1;
seq = 0; seq = 0;
@ -71,17 +71,17 @@ module ts.server {
} }
} }
} }
class IOSession extends Session { class IOSession extends Session {
protocol: NodeJS._debugger.Protocol; protocol: NodeJS._debugger.Protocol;
constructor(host: ServerHost, logger: ts.server.Logger, useProtocol: boolean, prettyJSON: boolean) { constructor(host: ServerHost, logger: ts.server.Logger, useProtocol: boolean, prettyJSON: boolean) {
super(host, logger, useProtocol, prettyJSON); super(host, logger, useProtocol, prettyJSON);
if (useProtocol) { if (useProtocol) {
this.initProtocol(); this.initProtocol();
} }
} }
initProtocol() { initProtocol() {
this.protocol = new nodeproto.Protocol(); this.protocol = new nodeproto.Protocol();
// note: onResponse was named by nodejs authors; we are re-purposing the Protocol // note: onResponse was named by nodejs authors; we are re-purposing the Protocol
@ -89,25 +89,16 @@ module ts.server {
this.protocol.onResponse = (pkt) => { this.protocol.onResponse = (pkt) => {
this.handleRequest(pkt); this.handleRequest(pkt);
}; };
} }
handleRequest(req: NodeJS._debugger.Packet) { handleRequest(req: NodeJS._debugger.Packet) {
this.projectService.log("Got JSON msg:\n" + req.raw); this.projectService.log("Got JSON msg:\n" + req.raw);
} }
listen() { listen() {
rl.on('line',(input: string) => { rl.on('line',(input: string) => {
var cmd = input.trim(); var message = input.trim();
if (cmd.indexOf("{") == 0) { this.onMessage(message);
// assumption is JSON on single line
// plan is to also carry this protocol
// over tcp, in which case JSON would
// have a Content-Length header
this.executeJSONcmd(cmd);
}
else {
this.executeCmd(cmd);
}
}); });
rl.on('close',() => { rl.on('close',() => {
@ -115,20 +106,13 @@ module ts.server {
this.projectService.log("Exiting..."); this.projectService.log("Exiting...");
process.exit(0); process.exit(0);
}); });
} }
} }
// This places log file in the directory containing editorServices.js // This places log file in the directory containing editorServices.js
// TODO: check that this location is writable // TODO: check that this location is writable
var logger = new Logger(__dirname + "/.log" + process.pid.toString()); var logger = new Logger(__dirname + "/.log" + process.pid.toString());
var host: ServerHost = ts.sys; // Start listening
new IOSession(ts.sys, logger, /* useProtocol */ true, /* prettyJSON */ false).listen();
// Wire the debugging interface
if (!host.getDebuggerClient) {
host.getDebuggerClient = () => new nodeproto.Client();
}
// Start listening
new IOSession(host, logger, /* useProtocol */ true, /* prettyJSON */ false).listen();
} }