///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
module ts {
export interface Node {
getSourceFile(): SourceFile;
getChildCount(sourceFile?: SourceFile): number;
getChildAt(index: number, sourceFile?: SourceFile): Node;
getChildren(sourceFile?: SourceFile): Node[];
getStart(sourceFile?: SourceFile): number;
getFullStart(): number;
getEnd(): number;
getWidth(sourceFile?: SourceFile): number;
getFullWidth(): number;
getLeadingTriviaWidth(sourceFile?: SourceFile): number;
getFullText(sourceFile?: SourceFile): string;
getFirstToken(sourceFile?: SourceFile): Node;
getLastToken(sourceFile?: SourceFile): Node;
}
export interface Symbol {
getFlags(): SymbolFlags;
getName(): string;
getDeclarations(): Declaration[];
}
export interface Type {
getFlags(): TypeFlags;
getSymbol(): Symbol;
getProperties(): Symbol[];
getProperty(propertyName: string): Symbol;
getApparentProperties(): Symbol[];
getCallSignatures(): Signature[];
getConstructSignatures(): Signature[];
getStringIndexType(): Type;
getNumberIndexType(): Type;
}
export interface Signature {
getDeclaration(): SignatureDeclaration;
getTypeParameters(): Type[];
getParameters(): Symbol[];
getReturnType(): Type;
}
export interface SourceFile {
getSourceUnit(): TypeScript.SourceUnitSyntax;
getSyntaxTree(): TypeScript.SyntaxTree;
getScriptSnapshot(): TypeScript.IScriptSnapshot;
getNamedDeclarations(): Declaration[];
update(scriptSnapshot: TypeScript.IScriptSnapshot, version: string, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): SourceFile;
}
var scanner: Scanner = createScanner(ScriptTarget.ES5, /*skipTrivia*/ true);
var emptyArray: any[] = [];
function createNode(kind: SyntaxKind, pos: number, end: number, flags: NodeFlags, parent?: Node): NodeObject {
var node = new (getNodeConstructor(kind))();
node.pos = pos;
node.end = end;
node.flags = flags;
node.parent = parent;
return node;
}
class NodeObject implements Node {
public kind: SyntaxKind;
public pos: number;
public end: number;
public flags: NodeFlags;
public parent: Node;
private _children: Node[];
public getSourceFile(): SourceFile {
var node: Node = this;
while (node.kind !== SyntaxKind.SourceFile) node = node.parent;
return node;
}
public getStart(sourceFile?: SourceFile): number {
return getTokenPosOfNode(this, sourceFile);
}
public getFullStart(): number {
return this.pos;
}
public getEnd(): number {
return this.end;
}
public getWidth(sourceFile?: SourceFile): number {
return this.getEnd() - this.getStart(sourceFile);
}
public getFullWidth(): number {
return this.end - this.getFullStart();
}
public getLeadingTriviaWidth(sourceFile?: SourceFile): number {
return this.getStart(sourceFile) - this.pos;
}
public getFullText(sourceFile?: SourceFile): string {
return (sourceFile || this.getSourceFile()).text.substring(this.pos, this.end);
}
private addSyntheticNodes(nodes: Node[], pos: number, end: number): number {
scanner.setTextPos(pos);
while (pos < end) {
var token = scanner.scan();
var textPos = scanner.getTextPos();
var node = nodes.push(createNode(token, pos, textPos, NodeFlags.Synthetic, this));
pos = textPos;
}
return pos;
}
private createSyntaxList(nodes: NodeArray): Node {
var list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, NodeFlags.Synthetic, this);
list._children = [];
var pos = nodes.pos;
for (var i = 0, len = nodes.length; i < len; i++) {
var node = nodes[i];
if (pos < node.pos) {
pos = this.addSyntheticNodes(list._children, pos, node.pos);
}
list._children.push(node);
pos = node.end;
}
if (pos < nodes.end) {
this.addSyntheticNodes(list._children, pos, nodes.end);
}
return list;
}
private createChildren(sourceFile?: SourceFile) {
if (this.kind > SyntaxKind.Missing) {
scanner.setText((sourceFile || this.getSourceFile()).text);
var children: Node[] = [];
var pos = this.pos;
var processNode = (node: Node) => {
if (pos < node.pos) {
pos = this.addSyntheticNodes(children, pos, node.pos);
}
children.push(node);
pos = node.end;
};
var processNodes = (nodes: NodeArray) => {
if (pos < nodes.pos) {
pos = this.addSyntheticNodes(children, pos, nodes.pos);
}
children.push(this.createSyntaxList(>nodes));
pos = nodes.end;
};
forEachChild(this, processNode, processNodes);
if (pos < this.end) {
this.addSyntheticNodes(children, pos, this.end);
}
scanner.setText(undefined);
}
this._children = children || emptyArray;
}
public getChildCount(sourceFile?: SourceFile): number {
if (!this._children) this.createChildren(sourceFile);
return this._children.length;
}
public getChildAt(index: number, sourceFile?: SourceFile): Node {
if (!this._children) this.createChildren(sourceFile);
return this._children[index];
}
public getChildren(sourceFile?: SourceFile): Node[] {
if (!this._children) this.createChildren(sourceFile);
return this._children;
}
public getFirstToken(sourceFile?: SourceFile): Node {
var children = this.getChildren(sourceFile);
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.kind < SyntaxKind.Missing) return child;
if (child.kind > SyntaxKind.Missing) return child.getFirstToken(sourceFile);
}
}
public getLastToken(sourceFile?: SourceFile): Node {
var children = this.getChildren(sourceFile);
for (var i = children.length - 1; i >= 0; i--) {
var child = children[i];
if (child.kind < SyntaxKind.Missing) return child;
if (child.kind > SyntaxKind.Missing) return child.getLastToken(sourceFile);
}
}
}
class SymbolObject implements Symbol {
flags: SymbolFlags;
name: string;
declarations: Declaration[];
constructor(flags: SymbolFlags, name: string) {
this.flags = flags;
this.name = name;
}
getFlags(): SymbolFlags {
return this.flags;
}
getName(): string {
return this.name;
}
getDeclarations(): Declaration[] {
return this.declarations;
}
}
class TypeObject implements Type {
checker: TypeChecker;
flags: TypeFlags;
id: number;
symbol: Symbol;
constructor(checker: TypeChecker, flags: TypeFlags) {
this.checker = checker;
this.flags = flags;
}
getFlags(): TypeFlags {
return this.flags;
}
getSymbol(): Symbol {
return this.symbol;
}
getProperties(): Symbol[] {
return this.checker.getPropertiesOfType(this);
}
getProperty(propertyName: string): Symbol {
return this.checker.getPropertyOfType(this, propertyName);
}
getApparentProperties(): Symbol[] {
return this.checker.getAugmentedPropertiesOfApparentType(this);
}
getCallSignatures(): Signature[] {
return this.checker.getSignaturesOfType(this, SignatureKind.Call);
}
getConstructSignatures(): Signature[] {
return this.checker.getSignaturesOfType(this, SignatureKind.Construct);
}
getStringIndexType(): Type {
return this.checker.getIndexTypeOfType(this, IndexKind.String);
}
getNumberIndexType(): Type {
return this.checker.getIndexTypeOfType(this, IndexKind.Number);
}
}
class SignatureObject implements Signature {
checker: TypeChecker;
declaration: SignatureDeclaration;
typeParameters: TypeParameter[];
parameters: Symbol[];
resolvedReturnType: Type;
minArgumentCount: number;
hasRestParameter: boolean;
hasStringLiterals: boolean;
constructor(checker: TypeChecker) {
this.checker = checker;
}
getDeclaration(): SignatureDeclaration {
return this.declaration;
}
getTypeParameters(): Type[] {
return this.typeParameters;
}
getParameters(): Symbol[] {
return this.parameters;
}
getReturnType(): Type {
return this.checker.getReturnTypeOfSignature(this);
}
}
var incrementalParse: IncrementalParse = TypeScript.IncrementalParser.parse;
class SourceFileObject extends NodeObject implements SourceFile {
public filename: string;
public text: string;
public getLineAndCharacterFromPosition(position: number): { line: number; character: number } { return null; }
public getPositionFromLineAndCharacter(line: number, character: number): number { return -1; }
public amdDependencies: string[];
public referencedFiles: FileReference[];
public syntacticErrors: Diagnostic[];
public semanticErrors: Diagnostic[];
public hasNoDefaultLib: boolean;
public externalModuleIndicator: Node; // The first node that causes this file to be an external module
public nodeCount: number;
public identifierCount: number;
public symbolCount: number;
public statements: NodeArray;
public version: string;
public isOpen: boolean;
public languageVersion: ScriptTarget;
public identifiers: Map;
private syntaxTree: TypeScript.SyntaxTree;
private scriptSnapshot: TypeScript.IScriptSnapshot;
private namedDeclarations: Declaration[];
public getSourceUnit(): TypeScript.SourceUnitSyntax {
// If we don't have a script, create one from our parse tree.
return this.getSyntaxTree().sourceUnit();
}
public getScriptSnapshot(): TypeScript.IScriptSnapshot {
return this.scriptSnapshot;
}
public getLineMap(): TypeScript.LineMap {
return this.getSyntaxTree().lineMap();
}
public getNamedDeclarations() {
if (!this.namedDeclarations) {
var sourceFile = this;
var namedDeclarations: Declaration[] = [];
var isExternalModule = ts.isExternalModule(sourceFile);
forEachChild(sourceFile, function visit(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ImportDeclaration:
case SyntaxKind.Method:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.TypeLiteral:
if ((node).name) {
namedDeclarations.push(node);
}
forEachChild(node, visit);
break;
case SyntaxKind.VariableStatement:
case SyntaxKind.ModuleBlock:
case SyntaxKind.FunctionBlock:
forEachChild(node, visit);
break;
case SyntaxKind.Parameter:
if (!(node.flags & NodeFlags.AccessibilityModifier)) {
// Only consider properties defined as constructor parameters
break;
}
case SyntaxKind.VariableDeclaration:
case SyntaxKind.EnumMember:
case SyntaxKind.Property:
namedDeclarations.push(node);
break;
}
// do not go any deeper
return undefined;
});
this.namedDeclarations = namedDeclarations;
}
return this.namedDeclarations;
}
public getSyntaxTree(): TypeScript.SyntaxTree {
if (!this.syntaxTree) {
var start = new Date().getTime();
this.syntaxTree = TypeScript.Parser.parse(
this.filename, TypeScript.SimpleText.fromScriptSnapshot(this.scriptSnapshot), this.languageVersion, this.isDeclareFile());
var time = new Date().getTime() - start;
//TypeScript.syntaxTreeParseTime += time;
}
return this.syntaxTree;
}
private isDeclareFile(): boolean {
return TypeScript.isDTSFile(this.filename);
}
public update(scriptSnapshot: TypeScript.IScriptSnapshot, version: string, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): SourceFile {
// See if we are currently holding onto a syntax tree. We may not be because we're
// either a closed file, or we've just been lazy and haven't had to create the syntax
// tree yet. Access the field instead of the method so we don't accidentally realize
// the old syntax tree.
var oldSyntaxTree = this.syntaxTree;
if (textChangeRange && Debug.shouldAssert(AssertionLevel.Normal)) {
var oldText = this.scriptSnapshot;
var newText = scriptSnapshot;
TypeScript.Debug.assert((oldText.getLength() - textChangeRange.span().length() + textChangeRange.newLength()) === newText.getLength());
if (Debug.shouldAssert(AssertionLevel.VeryAggressive)) {
var oldTextPrefix = oldText.getText(0, textChangeRange.span().start());
var newTextPrefix = newText.getText(0, textChangeRange.span().start());
TypeScript.Debug.assert(oldTextPrefix === newTextPrefix);
var oldTextSuffix = oldText.getText(textChangeRange.span().end(), oldText.getLength());
var newTextSuffix = newText.getText(textChangeRange.newSpan().end(), newText.getLength());
TypeScript.Debug.assert(oldTextSuffix === newTextSuffix);
}
}
var text = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot);
// If we don't have a text change, or we don't have an old syntax tree, then do a full
// parse. Otherwise, do an incremental parse.
var newSyntaxTree = !textChangeRange || !oldSyntaxTree
? TypeScript.Parser.parse(this.filename, text, this.languageVersion, TypeScript.isDTSFile(this.filename))
: TypeScript.IncrementalParser.parse(oldSyntaxTree, textChangeRange, text);
return SourceFileObject.createSourceFileObject(this.filename, scriptSnapshot, this.languageVersion, version, isOpen, newSyntaxTree);
}
public static createSourceFileObject(filename: string, scriptSnapshot: TypeScript.IScriptSnapshot, languageVersion: ScriptTarget, version: string, isOpen: boolean, syntaxTree?: TypeScript.SyntaxTree) {
var newSourceFile = createSourceFile(filename, scriptSnapshot.getText(0, scriptSnapshot.getLength()), languageVersion, version, isOpen);
newSourceFile.scriptSnapshot = scriptSnapshot;
newSourceFile.syntaxTree = syntaxTree;
return newSourceFile;
}
}
export interface Logger {
log(s: string): void;
}
//
// Public interface of the host of a language service instance.
//
export interface LanguageServiceHost extends Logger {
getCompilationSettings(): CompilerOptions;
getScriptFileNames(): string[];
getScriptVersion(fileName: string): string;
getScriptIsOpen(fileName: string): boolean;
getScriptSnapshot(fileName: string): TypeScript.IScriptSnapshot;
getLocalizedDiagnosticMessages(): any;
getCancellationToken(): CancellationToken;
getCurrentDirectory(): string;
getDefaultLibFilename(): string;
}
//
// Public services of a language service instance associated
// with a language service host instance
//
export interface LanguageService {
cleanupSemanticCache(): void;
getSyntacticDiagnostics(fileName: string): Diagnostic[];
getSemanticDiagnostics(fileName: string): Diagnostic[];
getCompilerOptionsDiagnostics(): Diagnostic[];
getSyntacticClassifications(fileName: string, span: TypeScript.TextSpan): ClassifiedSpan[];
getSemanticClassifications(fileName: string, span: TypeScript.TextSpan): ClassifiedSpan[];
getCompletionsAtPosition(fileName: string, position: number, isMemberCompletion: boolean): CompletionInfo;
getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails;
getTypeAtPosition(fileName: string, position: number): TypeInfo;
getQuickInfoAtPosition(fileName: string, position: number): QuickInfo;
getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TypeScript.TextSpan;
getBreakpointStatementAtPosition(fileName: string, position: number): TypeScript.TextSpan;
getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems;
getSignatureHelpCurrentArgumentState(fileName: string, position: number, applicableSpanStart: number): SignatureHelpState;
getRenameInfo(fileName: string, position: number): RenameInfo;
getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getImplementorsAtPosition(fileName: string, position: number): ReferenceEntry[];
getNavigateToItems(searchValue: string): NavigateToItem[];
getNavigationBarItems(fileName: string): NavigationBarItem[];
getOutliningSpans(fileName: string): OutliningSpan[];
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];
getBraceMatchingAtPosition(fileName: string, position: number): TypeScript.TextSpan[];
getIndentationAtPosition(fileName: string, position: number, options: EditorOptions): number;
getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions): TextChange[];
getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions): TextChange[];
getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): TextChange[];
getEmitOutput(fileName: string): EmitOutput;
//getSyntaxTree(fileName: string): TypeScript.SyntaxTree;
dispose(): void;
}
export class ClassificationTypeNames {
public static comment = "comment";
public static identifier = "identifier";
public static keyword = "keyword";
public static numericLiteral = "number";
public static operator = "operator";
public static stringLiteral = "string";
public static whiteSpace = "whitespace";
public static text = "text";
public static punctuation = "punctuation";
public static className = "class name";
public static enumName = "enum name";
public static interfaceName = "interface name";
public static moduleName = "module name";
public static typeParameterName = "type parameter name";
}
export class ClassifiedSpan {
constructor(public textSpan: TypeScript.TextSpan,
public classificationType: string) {
}
}
export class NavigationBarItem {
constructor(public text: string,
public kind: string,
public kindModifiers: string,
public spans: TypeScript.TextSpan[],
public childItems: NavigationBarItem[] = null,
public indent = 0,
public bolded = false,
public grayed = false) {
}
}
export class TodoCommentDescriptor {
constructor(public text: string,
public priority: number) {
}
}
export class TodoComment {
constructor(public descriptor: TodoCommentDescriptor,
public message: string,
public position: number) {
}
}
export class TextChange {
constructor(public span: TypeScript.TextSpan, public newText: string) {
}
static createInsert(pos: number, newText: string): TextChange {
return new TextChange(new TypeScript.TextSpan(pos, 0), newText);
}
static createDelete(start: number, end: number): TextChange {
return new TextChange(TypeScript.TextSpan.fromBounds(start, end), "");
}
static createReplace(start: number, end: number, newText: string): TextChange {
return new TextChange(TypeScript.TextSpan.fromBounds(start, end), newText);
}
}
export class ReferenceEntry {
public fileName: string = "";
public textSpan: TypeScript.TextSpan;
public isWriteAccess: boolean = false;
constructor(fileName: string, textSpan: TypeScript.TextSpan, isWriteAccess: boolean) {
this.fileName = fileName;
this.textSpan = textSpan;
this.isWriteAccess = isWriteAccess;
}
}
export class NavigateToItem {
constructor(public name: string,
public kind: string,
public kindModifiers: string,
public matchKind: string,
public fileName: string,
public textSpan: TypeScript.TextSpan,
public containerName: string,
public containerKind: string) {
}
}
export interface EditorOptions {
IndentSize: number;
TabSize: number;
NewLineCharacter: string;
ConvertTabsToSpaces: boolean;
}
export interface FormatCodeOptions extends EditorOptions {
InsertSpaceAfterCommaDelimiter: boolean;
InsertSpaceAfterSemicolonInForStatements: boolean;
InsertSpaceBeforeAndAfterBinaryOperators: boolean;
InsertSpaceAfterKeywordsInControlFlowStatements: boolean;
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: boolean;
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: boolean;
PlaceOpenBraceOnNewLineForFunctions: boolean;
PlaceOpenBraceOnNewLineForControlBlocks: boolean;
}
export class DefinitionInfo {
constructor(public fileName: string,
public textSpan: TypeScript.TextSpan,
public kind: string,
public name: string,
public containerKind: string,
public containerName: string) {
}
}
export interface MemberName {
prefix: string;
suffix: string;
text: string;
}
export class QuickInfo {
constructor(public kind: string,
public kindModifiers: string,
public textSpan: TypeScript.TextSpan,
public displayParts: SymbolDisplayPart[]) {
}
public toJSON() {
return {
kind: this.kind,
kindModifiers: this.kindModifiers,
textSpan: this.textSpan,
displayParts: this.displayParts.map(d => {
return {
text: d.text,
kind: SymbolDisplayPartKind[d.kind]
};
})
};
}
}
export class TypeInfo {
constructor(
public memberName: TypeScript.MemberName,
public docComment: string,
public fullSymbolName: string,
public kind: string,
public textSpan: TypeScript.TextSpan) {
}
}
export class RenameInfo {
constructor(public canRename: boolean,
public localizedErrorMessage: string,
public displayName: string,
public fullDisplayName: string,
public kind: string,
public kindModifiers: string,
public triggerSpan: TypeScript.TextSpan) {
}
public static CreateError(localizedErrorMessage: string) {
return new RenameInfo(/*canRename:*/ false, localizedErrorMessage,
/*displayName:*/ null, /*fullDisplayName:*/ null,
/*kind:*/ null, /*kindModifiers:*/ null, /*triggerSpan:*/ null);
}
public static Create(displayName: string,
fullDisplayName: string,
kind: string,
kindModifiers: string,
triggerSpan: TypeScript.TextSpan) {
return new RenameInfo(/*canRename:*/ true, /*localizedErrorMessage:*/ null, displayName, fullDisplayName, kind, kindModifiers, triggerSpan);
}
}
export class SignatureHelpParameter {
constructor(public name: string,
public documentation: string,
public display: string,
public isOptional: boolean) {
}
}
/**
* Represents a single signature to show in signature help.
* The id is used for subsequent calls into the language service to ask questions about the
* signature help item in the context of any documents that have been updated. i.e. after
* an edit has happened, while signature help is still active, the host can ask important
* questions like 'what parameter is the user currently contained within?'.
*/
export class SignatureHelpItem {
constructor(public isVariadic: boolean,
public prefix: string,
public suffix: string,
public separator: string,
public parameters: SignatureHelpParameter[],
public documentation: string) {
}
}
/**
* Represents a set of signature help items, and the preferred item that should be selected.
*/
export class SignatureHelpItems {
constructor(public items: SignatureHelpItem[],
public applicableSpan: TypeScript.TextSpan,
public selectedItemIndex: number) {
}
}
export class SignatureHelpState {
constructor(public argumentIndex: number,
public argumentCount: number) {
}
}
export interface CompletionInfo {
isMemberCompletion: boolean;
entries: CompletionEntry[];
}
export interface CompletionEntry {
name: string;
kind: string; // see ScriptElementKind
kindModifiers: string; // see ScriptElementKindModifier, comma separated
}
export interface CompletionEntryDetails {
name: string;
kind: string; // see ScriptElementKind
kindModifiers: string; // see ScriptElementKindModifier, comma separated
type: string;
fullSymbolName: string;
docComment: string;
}
export interface EmitOutput {
outputFiles: OutputFile[];
emitOutputStatus: EmitReturnStatus;
}
export enum OutputFileType {
JavaScript,
SourceMap,
Declaration
}
export interface OutputFile {
name: string;
writeByteOrderMark: boolean;
text: string;
}
export enum EndOfLineState {
Start,
InMultiLineCommentTrivia,
InSingleQuoteStringLiteral,
InDoubleQuoteStringLiteral,
}
export enum TokenClass {
Punctuation,
Keyword,
Operator,
Comment,
Whitespace,
Identifier,
NumberLiteral,
StringLiteral,
RegExpLiteral,
}
export interface ClassificationResult {
finalLexState: EndOfLineState;
entries: ClassificationInfo[];
}
export interface ClassificationInfo {
length: number;
classification: TokenClass;
}
export interface Classifier {
getClassificationsForLine(text: string, lexState: EndOfLineState): ClassificationResult;
}
export interface DocumentRegistry {
acquireDocument(
filename: string,
compilationSettings: CompilerOptions,
scriptSnapshot: TypeScript.IScriptSnapshot,
version: string,
isOpen: boolean): SourceFile;
updateDocument(
sourceFile: SourceFile,
filename: string,
compilationSettings: CompilerOptions,
scriptSnapshot: TypeScript.IScriptSnapshot,
version: string,
isOpen: boolean,
textChangeRange: TypeScript.TextChangeRange
): SourceFile;
releaseDocument(filename: string, compilationSettings: CompilerOptions): void
}
// TODO: move these to enums
export class ScriptElementKind {
static unknown = "";
// predefined type (void) or keyword (class)
static keyword = "keyword";
// top level script node
static scriptElement = "script";
// module foo {}
static moduleElement = "module";
// class X {}
static classElement = "class";
// interface Y {}
static interfaceElement = "interface";
// enum E
static enumElement = "enum";
// Inside module and script only
// var v = ..
static variableElement = "var";
// Inside function
static localVariableElement = "local var";
// Inside module and script only
// function f() { }
static functionElement = "function";
// Inside function
static localFunctionElement = "local function";
// class X { [public|private]* foo() {} }
static memberFunctionElement = "method";
// class X { [public|private]* [get|set] foo:number; }
static memberGetAccessorElement = "getter";
static memberSetAccessorElement = "setter";
// class X { [public|private]* foo:number; }
// interface Y { foo:number; }
static memberVariableElement = "property";
// class X { constructor() { } }
static constructorImplementationElement = "constructor";
// interface Y { ():number; }
static callSignatureElement = "call";
// interface Y { []:number; }
static indexSignatureElement = "index";
// interface Y { new():Y; }
static constructSignatureElement = "construct";
// function foo(*Y*: string)
static parameterElement = "parameter";
static typeParameterElement = "type parameter";
static primitiveType = "primitive type";
static label = "label";
}
export class ScriptElementKindModifier {
static none = "";
static publicMemberModifier = "public";
static privateMemberModifier = "private";
static exportedModifier = "export";
static ambientModifier = "declare";
static staticModifier = "static";
}
enum MatchKind {
none = 0,
exact = 1,
substring = 2,
prefix = 3
}
interface IncrementalParse {
(oldSyntaxTree: TypeScript.SyntaxTree, textChangeRange: TypeScript.TextChangeRange, newText: TypeScript.ISimpleText): TypeScript.SyntaxTree
}
/// Language Service
interface CompletionSession {
filename: string; // the file where the completion was requested
position: number; // position in the file where the completion was requested
entries: CompletionEntry[]; // entries for this completion
symbols: Map; // symbols by entry name map
location: Node; // the node where the completion was requested
typeChecker: TypeChecker;// the typeChecker used to generate this completion
}
interface FormattingOptions {
useTabs: boolean;
spacesPerTab: number;
indentSpaces: number;
newLineCharacter: string;
}
// Information about a specific host file.
interface HostFileInformation {
filename: string;
version: string;
isOpen: boolean;
sourceText?: TypeScript.IScriptSnapshot;
}
interface DocumentRegistryEntry {
sourceFile: SourceFile;
refCount: number;
owners: string[];
}
export function getDefaultCompilerOptions(): CompilerOptions {
// Set "ES5" target by default for language service
return {
target: ScriptTarget.ES5,
module: ModuleKind.None,
};
}
export function compareDataObjects(dst: any, src: any): boolean {
for (var e in dst) {
if (typeof dst[e] === "object") {
if (!compareDataObjects(dst[e], src[e]))
return false;
}
else if (typeof dst[e] !== "function") {
if (dst[e] !== src[e])
return false;
}
}
return true;
}
export class OperationCanceledException { }
class CancellationTokenObject {
public static None: CancellationTokenObject = new CancellationTokenObject(null)
constructor(private cancellationToken: CancellationToken) {
}
public isCancellationRequested() {
return this.cancellationToken && this.cancellationToken.isCancellationRequested();
}
public throwIfCancellationRequested(): void {
if (this.isCancellationRequested()) {
throw new OperationCanceledException();
}
}
}
// Cache host information about scrip Should be refreshed
// at each language service public entry point, since we don't know when
// set of scripts handled by the host changes.
class HostCache {
private filenameToEntry: Map;
private _compilationSettings: CompilerOptions;
constructor(private host: LanguageServiceHost) {
// script id => script index
this.filenameToEntry = {};
var filenames = host.getScriptFileNames();
for (var i = 0, n = filenames.length; i < n; i++) {
var filename = filenames[i];
this.filenameToEntry[TypeScript.switchToForwardSlashes(filename)] = {
filename: filename,
version: host.getScriptVersion(filename),
isOpen: host.getScriptIsOpen(filename)
};
}
this._compilationSettings = host.getCompilationSettings() || getDefaultCompilerOptions();
}
public compilationSettings() {
return this._compilationSettings;
}
public getEntry(filename: string): HostFileInformation {
filename = TypeScript.switchToForwardSlashes(filename);
return lookUp(this.filenameToEntry, filename);
}
public contains(filename: string): boolean {
return !!this.getEntry(filename);
}
public getHostfilename(filename: string) {
var hostCacheEntry = this.getEntry(filename);
if (hostCacheEntry) {
return hostCacheEntry.filename;
}
return filename;
}
public getFilenames(): string[] {
var fileNames: string[] = [];
forEachKey(this.filenameToEntry, key => {
if (hasProperty(this.filenameToEntry, key))
fileNames.push(key);
});
return fileNames;
}
public getVersion(filename: string): string {
return this.getEntry(filename).version;
}
public isOpen(filename: string): boolean {
return this.getEntry(filename).isOpen;
}
public getScriptSnapshot(filename: string): TypeScript.IScriptSnapshot {
var file = this.getEntry(filename);
if (!file.sourceText) {
file.sourceText = this.host.getScriptSnapshot(file.filename);
}
return file.sourceText;
}
public getChangeRange(filename: string, lastKnownVersion: string, oldScriptSnapshot: TypeScript.IScriptSnapshot): TypeScript.TextChangeRange {
var currentVersion = this.getVersion(filename);
if (lastKnownVersion === currentVersion) {
return TypeScript.TextChangeRange.unchanged; // "No changes"
}
var scriptSnapshot = this.getScriptSnapshot(filename);
return scriptSnapshot.getChangeRange(oldScriptSnapshot);
}
}
class SyntaxTreeCache {
private hostCache: HostCache;
// For our syntactic only features, we also keep a cache of the syntax tree for the
// currently edited file.
private currentFilename: string = "";
private currentFileVersion: string = null;
private currentSourceFile: SourceFile = null;
private currentFileSyntaxTree: TypeScript.SyntaxTree = null;
constructor(private host: LanguageServiceHost) {
this.hostCache = new HostCache(host);
}
private initialize(filename: string) {
// ensure that both source file and syntax tree are either initialized or not initialized
Debug.assert(!!this.currentFileSyntaxTree === !!this.currentSourceFile);
this.hostCache = new HostCache(this.host);
var version = this.hostCache.getVersion(filename);
var syntaxTree: TypeScript.SyntaxTree = null;
var sourceFile: SourceFile;
if (this.currentFileSyntaxTree === null || this.currentFilename !== filename) {
var scriptSnapshot = this.hostCache.getScriptSnapshot(filename);
syntaxTree = this.createSyntaxTree(filename, scriptSnapshot);
sourceFile = createSourceFileFromScriptSnapshot(filename, scriptSnapshot, getDefaultCompilerOptions(), version, /*isOpen*/ true);
fixupParentReferences(sourceFile);
}
else if (this.currentFileVersion !== version) {
var scriptSnapshot = this.hostCache.getScriptSnapshot(filename);
syntaxTree = this.updateSyntaxTree(filename, scriptSnapshot,
this.currentSourceFile.getScriptSnapshot(), this.currentFileSyntaxTree, this.currentFileVersion);
var editRange = this.hostCache.getChangeRange(filename, this.currentFileVersion, this.currentSourceFile.getScriptSnapshot());
sourceFile = !editRange
? createSourceFileFromScriptSnapshot(filename, scriptSnapshot, getDefaultCompilerOptions(), version, /*isOpen*/ true)
: this.currentSourceFile.update(scriptSnapshot, version, /*isOpen*/ true, editRange);
fixupParentReferences(sourceFile);
}
if (syntaxTree !== null) {
Debug.assert(sourceFile);
// All done, ensure state is up to date
this.currentFileVersion = version;
this.currentFilename = filename;
this.currentFileSyntaxTree = syntaxTree;
this.currentSourceFile = sourceFile;
}
function fixupParentReferences(sourceFile: SourceFile) {
// normally parent references are set during binding.
// however here SourceFile data is used only for syntactic features so running the whole binding process is an overhead.
// walk over the nodes and set parent references
var parent: Node = sourceFile;
function walk(n: Node): void {
n.parent = parent;
var saveParent = parent;
parent = n;
forEachChild(n, walk);
parent = saveParent;
}
forEachChild(sourceFile, walk);
}
}
public getCurrentFileSyntaxTree(filename: string): TypeScript.SyntaxTree {
this.initialize(filename);
return this.currentFileSyntaxTree;
}
public getCurrentSourceFile(filename: string): SourceFile {
this.initialize(filename);
return this.currentSourceFile;
}
public getCurrentScriptSnapshot(filename: string): TypeScript.IScriptSnapshot {
// update currentFileScriptSnapshot as a part of 'getCurrentFileSyntaxTree' call
this.getCurrentFileSyntaxTree(filename);
return this.getCurrentSourceFile(filename).getScriptSnapshot();
}
private createSyntaxTree(filename: string, scriptSnapshot: TypeScript.IScriptSnapshot): TypeScript.SyntaxTree {
var text = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot);
// For the purposes of features that use this syntax tree, we can just use the default
// compilation settings. The features only use the syntax (and not the diagnostics),
// and the syntax isn't affected by the compilation settings.
var syntaxTree = TypeScript.Parser.parse(filename, text, getDefaultCompilerOptions().target, TypeScript.isDTSFile(filename));
return syntaxTree;
}
private updateSyntaxTree(filename: string, scriptSnapshot: TypeScript.IScriptSnapshot, previousScriptSnapshot: TypeScript.IScriptSnapshot, previousSyntaxTree: TypeScript.SyntaxTree, previousFileVersion: string): TypeScript.SyntaxTree {
var editRange = this.hostCache.getChangeRange(filename, previousFileVersion, previousScriptSnapshot);
// Debug.assert(newLength >= 0);
// The host considers the entire buffer changed. So parse a completely new tree.
if (editRange === null) {
return this.createSyntaxTree(filename, scriptSnapshot);
}
var nextSyntaxTree = TypeScript.IncrementalParser.parse(
previousSyntaxTree, editRange, TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot));
this.ensureInvariants(filename, editRange, nextSyntaxTree, previousScriptSnapshot, scriptSnapshot);
return nextSyntaxTree;
}
private ensureInvariants(filename: string, editRange: TypeScript.TextChangeRange, incrementalTree: TypeScript.SyntaxTree, oldScriptSnapshot: TypeScript.IScriptSnapshot, newScriptSnapshot: TypeScript.IScriptSnapshot) {
// First, verify that the edit range and the script snapshots make sense.
// If this fires, then the edit range is completely bogus. Somehow the lengths of the
// old snapshot, the change range and the new snapshot aren't in sync. This is very
// bad.
var expectedNewLength = oldScriptSnapshot.getLength() - editRange.span().length() + editRange.newLength();
var actualNewLength = newScriptSnapshot.getLength();
function provideMoreDebugInfo() {
var debugInformation = ["expected length:", expectedNewLength, "and actual length:", actualNewLength, "are not equal\r\n"];
var oldSpan = editRange.span();
function prettyPrintString(s: string): string {
return '"' + s.replace(/\r/g, '\\r').replace(/\n/g, '\\n') + '"';
}
debugInformation.push('Edit range (old text) (start: ' + oldSpan.start() + ', end: ' + oldSpan.end() + ') \r\n');
debugInformation.push('Old text edit range contents: ' + prettyPrintString(oldScriptSnapshot.getText(oldSpan.start(), oldSpan.end())));
var newSpan = editRange.newSpan();
debugInformation.push('Edit range (new text) (start: ' + newSpan.start() + ', end: ' + newSpan.end() + ') \r\n');
debugInformation.push('New text edit range contents: ' + prettyPrintString(newScriptSnapshot.getText(newSpan.start(), newSpan.end())));
return debugInformation.join(' ');
}
Debug.assert(
expectedNewLength === actualNewLength,
"Expected length is different from actual!",
provideMoreDebugInfo);
if (Debug.shouldAssert(AssertionLevel.VeryAggressive)) {
// If this fires, the text change range is bogus. It says the change starts at point
// 'X', but we can see a text difference *before* that point.
var oldPrefixText = oldScriptSnapshot.getText(0, editRange.span().start());
var newPrefixText = newScriptSnapshot.getText(0, editRange.span().start());
Debug.assert(oldPrefixText === newPrefixText, 'Expected equal prefix texts!');
// If this fires, the text change range is bogus. It says the change goes only up to
// point 'X', but we can see a text difference *after* that point.
var oldSuffixText = oldScriptSnapshot.getText(editRange.span().end(), oldScriptSnapshot.getLength());
var newSuffixText = newScriptSnapshot.getText(editRange.newSpan().end(), newScriptSnapshot.getLength());
Debug.assert(oldSuffixText === newSuffixText, 'Expected equal suffix texts!');
// Ok, text change range and script snapshots look ok. Let's verify that our
// incremental parsing worked properly.
//var normalTree = this.createSyntaxTree(filename, newScriptSnapshot);
//Debug.assert(normalTree.structuralEquals(incrementalTree), 'Expected equal incremental and normal trees');
// Ok, the trees looked good. So at least our incremental parser agrees with the
// normal parser. Now, verify that the incremental tree matches the contents of the
// script snapshot.
var incrementalTreeText = TypeScript.fullText(incrementalTree.sourceUnit());
var actualSnapshotText = newScriptSnapshot.getText(0, newScriptSnapshot.getLength());
Debug.assert(incrementalTreeText === actualSnapshotText, 'Expected full texts to be equal');
}
}
}
function createSourceFileFromScriptSnapshot(filename: string, scriptSnapshot: TypeScript.IScriptSnapshot, settings: CompilerOptions, version: string, isOpen: boolean) {
return SourceFileObject.createSourceFileObject(filename, scriptSnapshot, settings.target, version, isOpen);
}
export function createDocumentRegistry(): DocumentRegistry {
var buckets: Map