Merge branch 'master' into taggedSigHelp
This commit is contained in:
commit
4bf023982c
|
@ -9102,7 +9102,12 @@ module ts {
|
|||
globalNumberType = getGlobalType("Number");
|
||||
globalBooleanType = getGlobalType("Boolean");
|
||||
globalRegExpType = getGlobalType("RegExp");
|
||||
globalTemplateStringsArrayType = getGlobalType("TemplateStringsArray");
|
||||
|
||||
// If we're in ES6 mode, load the TemplateStringsArray.
|
||||
// Otherwise, default to 'unknown' for the purposes of type checking in LS scenarios.
|
||||
globalTemplateStringsArrayType = compilerOptions.target >= ScriptTarget.ES6
|
||||
? getGlobalType("TemplateStringsArray")
|
||||
: unknownType;
|
||||
}
|
||||
|
||||
initializeTypeChecker();
|
||||
|
|
|
@ -139,7 +139,7 @@ module ts {
|
|||
function writeLiteral(s: string) {
|
||||
if (s && s.length) {
|
||||
write(s);
|
||||
var lineStartsOfS = getLineStarts(s);
|
||||
var lineStartsOfS = computeLineStarts(s);
|
||||
if (lineStartsOfS.length > 1) {
|
||||
lineCount = lineCount + lineStartsOfS.length - 1;
|
||||
linePos = output.length - s.length + lineStartsOfS[lineStartsOfS.length - 1];
|
||||
|
|
|
@ -957,18 +957,16 @@ module ts {
|
|||
};
|
||||
})();
|
||||
|
||||
function getLineAndCharacterlFromSourcePosition(position: number) {
|
||||
if (!lineStarts) {
|
||||
lineStarts = getLineStarts(sourceText);
|
||||
}
|
||||
return getLineAndCharacterOfPosition(lineStarts, position);
|
||||
function getLineStarts(): number[] {
|
||||
return lineStarts || (lineStarts = computeLineStarts(sourceText));
|
||||
}
|
||||
|
||||
function getLineAndCharacterFromSourcePosition(position: number) {
|
||||
return getLineAndCharacterOfPosition(getLineStarts(), position);
|
||||
}
|
||||
|
||||
function getPositionFromSourceLineAndCharacter(line: number, character: number): number {
|
||||
if (!lineStarts) {
|
||||
lineStarts = getLineStarts(sourceText);
|
||||
}
|
||||
return getPositionFromLineAndCharacter(lineStarts, line, character);
|
||||
return getPositionFromLineAndCharacter(getLineStarts(), line, character);
|
||||
}
|
||||
|
||||
function error(message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void {
|
||||
|
@ -1011,7 +1009,9 @@ module ts {
|
|||
? file.syntacticErrors[file.syntacticErrors.length - 1].start
|
||||
: -1;
|
||||
if (start !== lastErrorPos) {
|
||||
file.syntacticErrors.push(createFileDiagnostic(file, start, length, message, arg0, arg1, arg2));
|
||||
var diagnostic = createFileDiagnostic(file, start, length, message, arg0, arg1, arg2);
|
||||
diagnostic.isParseError = true;
|
||||
file.syntacticErrors.push(diagnostic);
|
||||
}
|
||||
|
||||
if (lookAheadMode === LookAheadMode.NoErrorYet) {
|
||||
|
@ -4282,8 +4282,9 @@ module ts {
|
|||
file = <SourceFile>createRootNode(SyntaxKind.SourceFile, 0, sourceText.length, rootNodeFlags);
|
||||
file.filename = normalizePath(filename);
|
||||
file.text = sourceText;
|
||||
file.getLineAndCharacterFromPosition = getLineAndCharacterlFromSourcePosition;
|
||||
file.getLineAndCharacterFromPosition = getLineAndCharacterFromSourcePosition;
|
||||
file.getPositionFromLineAndCharacter = getPositionFromSourceLineAndCharacter;
|
||||
file.getLineStarts = getLineStarts;
|
||||
file.syntacticErrors = [];
|
||||
file.semanticErrors = [];
|
||||
var referenceComments = processReferenceComments();
|
||||
|
|
|
@ -246,7 +246,7 @@ module ts {
|
|||
return tokenStrings[t];
|
||||
}
|
||||
|
||||
export function getLineStarts(text: string): number[] {
|
||||
export function computeLineStarts(text: string): number[] {
|
||||
var result: number[] = new Array();
|
||||
var pos = 0;
|
||||
var lineStart = 0;
|
||||
|
@ -294,7 +294,7 @@ module ts {
|
|||
}
|
||||
|
||||
export function positionToLineAndCharacter(text: string, pos: number) {
|
||||
var lineStarts = getLineStarts(text);
|
||||
var lineStarts = computeLineStarts(text);
|
||||
return getLineAndCharacterOfPosition(lineStarts, pos);
|
||||
}
|
||||
|
||||
|
|
|
@ -245,7 +245,11 @@ module ts {
|
|||
FirstLiteralToken = NumericLiteral,
|
||||
LastLiteralToken = NoSubstitutionTemplateLiteral,
|
||||
FirstTemplateToken = NoSubstitutionTemplateLiteral,
|
||||
LastTemplateToken = TemplateTail
|
||||
LastTemplateToken = TemplateTail,
|
||||
FirstOperator = SemicolonToken,
|
||||
LastOperator = CaretEqualsToken,
|
||||
FirstBinaryOperator = LessThanToken,
|
||||
LastBinaryOperator = CaretEqualsToken
|
||||
}
|
||||
|
||||
export const enum NodeFlags {
|
||||
|
@ -627,8 +631,9 @@ module ts {
|
|||
export interface SourceFile extends Block {
|
||||
filename: string;
|
||||
text: string;
|
||||
getLineAndCharacterFromPosition(position: number): { line: number; character: number };
|
||||
getLineAndCharacterFromPosition(position: number): LineAndCharacter;
|
||||
getPositionFromLineAndCharacter(line: number, character: number): number;
|
||||
getLineStarts(): number[];
|
||||
amdDependencies: string[];
|
||||
referencedFiles: FileReference[];
|
||||
syntacticErrors: Diagnostic[];
|
||||
|
@ -1121,7 +1126,16 @@ module ts {
|
|||
messageText: string;
|
||||
category: DiagnosticCategory;
|
||||
code: number;
|
||||
/**
|
||||
* Early error - any error (can be produced at parsing\binding\typechecking step) that blocks emit
|
||||
*/
|
||||
isEarly?: boolean;
|
||||
/**
|
||||
* Parse error - error produced by parser when it scanner returns a token
|
||||
* that parser does not understand in its current state
|
||||
* (as opposed to grammar error when parser can interpret the token but interpretation is not legal from the grammar perespective)
|
||||
*/
|
||||
isParseError?: boolean;
|
||||
}
|
||||
|
||||
export enum DiagnosticCategory {
|
||||
|
@ -1343,4 +1357,4 @@ module ts {
|
|||
useCaseSensitiveFileNames(): boolean;
|
||||
getNewLine(): string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1021,7 +1021,7 @@ module FourSlash {
|
|||
var resultString = "SpanInfo: " + JSON.stringify(spanInfo);
|
||||
if (spanInfo) {
|
||||
var spanString = this.activeFile.content.substr(spanInfo.start(), spanInfo.length());
|
||||
var spanLineMap = ts.getLineStarts(spanString);
|
||||
var spanLineMap = ts.computeLineStarts(spanString);
|
||||
for (var i = 0; i < spanLineMap.length; i++) {
|
||||
if (!i) {
|
||||
resultString += "\n";
|
||||
|
@ -1035,7 +1035,7 @@ module FourSlash {
|
|||
}
|
||||
|
||||
private baselineCurrentFileLocations(getSpanAtPos: (pos: number) => TypeScript.TextSpan): string {
|
||||
var fileLineMap = ts.getLineStarts(this.activeFile.content);
|
||||
var fileLineMap = ts.computeLineStarts(this.activeFile.content);
|
||||
var nextLine = 0;
|
||||
var resultString = "";
|
||||
var currentLine: string;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
/// <reference path='..\services\services.ts' />
|
||||
/// <reference path='..\services\shims.ts' />
|
||||
/// <reference path='..\compiler\core.ts' />
|
||||
/// <reference path='..\compiler\sys.ts' />
|
||||
/// <reference path='external\mocha.d.ts'/>
|
||||
/// <reference path='external\chai.d.ts'/>
|
||||
|
@ -957,7 +958,7 @@ module Harness {
|
|||
// Note: IE JS engine incorrectly handles consecutive delimiters here when using RegExp split, so
|
||||
// we have to string-based splitting instead and try to figure out the delimiting chars
|
||||
|
||||
var lineStarts = ts.getLineStarts(inputFile.content);
|
||||
var lineStarts = ts.computeLineStarts(inputFile.content);
|
||||
var lines = inputFile.content.split('\n');
|
||||
lines.forEach((line, lineIndex) => {
|
||||
if (line.length > 0 && line.charAt(line.length - 1) === '\r') {
|
||||
|
@ -1002,8 +1003,12 @@ module Harness {
|
|||
assert.equal(markedErrorCount, fileErrors.length, 'count of errors in ' + inputFile.unitName);
|
||||
});
|
||||
|
||||
var numLibraryDiagnostics = ts.countWhere(diagnostics, diagnostic => {
|
||||
return diagnostic.filename && isLibraryFile(diagnostic.filename);
|
||||
});
|
||||
|
||||
// Verify we didn't miss any errors in total
|
||||
assert.equal(totalErrorsReported, diagnostics.length, 'total number of errors');
|
||||
assert.equal(totalErrorsReported + numLibraryDiagnostics, diagnostics.length, 'total number of errors');
|
||||
|
||||
return minimalDiagnosticsToString(diagnostics) +
|
||||
sys.newLine + sys.newLine + outputLines.join('\r\n');
|
||||
|
|
|
@ -223,7 +223,7 @@ module Harness.SourceMapRecoder {
|
|||
sourceMapNames = sourceMapData.sourceMapNames;
|
||||
|
||||
jsFile = currentJsFile;
|
||||
jsLineMap = ts.getLineStarts(jsFile.code);
|
||||
jsLineMap = ts.computeLineStarts(jsFile.code);
|
||||
|
||||
spansOnSingleLine = [];
|
||||
prevWrittenSourcePos = 0;
|
||||
|
@ -294,7 +294,7 @@ module Harness.SourceMapRecoder {
|
|||
sourceMapRecoder.WriteLine("sourceFile:" + sourceMapSources[spansOnSingleLine[0].sourceMapSpan.sourceIndex]);
|
||||
sourceMapRecoder.WriteLine("-------------------------------------------------------------------");
|
||||
|
||||
tsLineMap = ts.getLineStarts(newSourceFileCode);
|
||||
tsLineMap = ts.computeLineStarts(newSourceFileCode);
|
||||
tsCode = newSourceFileCode;
|
||||
prevWrittenSourcePos = 0;
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ module Harness.SourceMapRecoder {
|
|||
}
|
||||
}
|
||||
|
||||
var tsCodeLineMap = ts.getLineStarts(sourceText);
|
||||
var tsCodeLineMap = ts.computeLineStarts(sourceText);
|
||||
for (var i = 0; i < tsCodeLineMap.length; i++) {
|
||||
writeSourceMapIndent(prevEmittedCol, i == 0 ? markerIds[index] : " >");
|
||||
sourceMapRecoder.Write(getTextOfLine(i, tsCodeLineMap, sourceText));
|
||||
|
|
953
src/services/formatting.ts
Normal file
953
src/services/formatting.ts
Normal file
|
@ -0,0 +1,953 @@
|
|||
///<reference path='services.ts' />
|
||||
///<reference path='formatting\indentation.ts' />
|
||||
///<reference path='formatting\formattingScanner.ts' />
|
||||
///<reference path='formatting\rulesProvider.ts' />
|
||||
|
||||
module ts.formatting {
|
||||
|
||||
export interface TextRangeWithKind extends TextRange {
|
||||
kind: SyntaxKind;
|
||||
}
|
||||
|
||||
export interface TokenInfo {
|
||||
leadingTrivia: TextRangeWithKind[];
|
||||
token: TextRangeWithKind;
|
||||
trailingTrivia: TextRangeWithKind[];
|
||||
}
|
||||
|
||||
const enum Constants {
|
||||
Unknown = -1
|
||||
}
|
||||
|
||||
/*
|
||||
* Indentation for the scope that can be dynamically recomputed.
|
||||
* i.e
|
||||
* while(true)
|
||||
* { var x;
|
||||
* }
|
||||
* Normally indentation is applied only to the first token in line so at glance 'var' should not be touched.
|
||||
* However if some format rule adds new line between '}' and 'var' 'var' will become
|
||||
* the first token in line so it should be indented
|
||||
*/
|
||||
interface DynamicIndentation {
|
||||
getIndentationForToken(tokenLine: number, tokenKind: SyntaxKind): number;
|
||||
getIndentationForComment(owningToken: SyntaxKind): number;
|
||||
/**
|
||||
* Indentation for open and close tokens of the node if it is block or another node that needs special indentation
|
||||
* ... {
|
||||
* .........<child>
|
||||
* ....}
|
||||
* ____ - indentation
|
||||
* ____ - delta
|
||||
**/
|
||||
getIndentation(): number;
|
||||
/**
|
||||
* Prefered relative indentation for child nodes.
|
||||
* Delta is used to carry the indentation info
|
||||
* foo(bar({
|
||||
* $
|
||||
* }))
|
||||
* Both 'foo', 'bar' introduce new indentation with delta = 4, but total indentation in $ is not 8.
|
||||
* foo: { indentation: 0, delta: 4 }
|
||||
* bar: { indentation: foo.indentation + foo.delta = 4, delta: 4} however 'foo' and 'bar' are on the same line
|
||||
* so bar inherits indentation from foo and bar.delta will be 4
|
||||
*
|
||||
*/
|
||||
getDelta(): number;
|
||||
/**
|
||||
* Formatter calls this function when rule adds or deletes new lines from the text
|
||||
* so indentation scope can adjust values of indentation and delta.
|
||||
*/
|
||||
recomputeIndentation(lineAddedByFormatting: boolean): void;
|
||||
}
|
||||
|
||||
interface Indentation {
|
||||
indentation: number;
|
||||
delta: number
|
||||
}
|
||||
|
||||
export function formatOnEnter(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
|
||||
var line = sourceFile.getLineAndCharacterFromPosition(position).line;
|
||||
Debug.assert(line >= 2);
|
||||
// get the span for the previous\current line
|
||||
var span = {
|
||||
// get start position for the previous line
|
||||
pos: getStartPositionOfLine(line - 1, sourceFile),
|
||||
// get end position for the current line (end value is exclusive so add 1 to the result)
|
||||
end: getEndLinePosition(line, sourceFile) + 1
|
||||
}
|
||||
return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnEnter);
|
||||
}
|
||||
|
||||
export function formatOnSemicolon(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
|
||||
return formatOutermostParent(position, SyntaxKind.SemicolonToken, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnSemicolon);
|
||||
}
|
||||
|
||||
export function formatOnClosingCurly(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
|
||||
return formatOutermostParent(position, SyntaxKind.CloseBraceToken, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnClosingCurlyBrace);
|
||||
}
|
||||
|
||||
export function formatDocument(sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
|
||||
var span = {
|
||||
pos: 0,
|
||||
end: sourceFile.text.length
|
||||
};
|
||||
return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatDocument);
|
||||
}
|
||||
|
||||
export function formatSelection(start: number, end: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
|
||||
// format from the beginning of the line
|
||||
var span = {
|
||||
pos: getStartLinePositionForPosition(start, sourceFile),
|
||||
end: end
|
||||
};
|
||||
return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatSelection);
|
||||
}
|
||||
|
||||
function formatOutermostParent(position: number, expectedLastToken: SyntaxKind, sourceFile: SourceFile, options: FormatCodeOptions, rulesProvider: RulesProvider, requestKind: FormattingRequestKind): TextChange[] {
|
||||
var parent = findOutermostParent(position, expectedLastToken, sourceFile);
|
||||
if (!parent) {
|
||||
return [];
|
||||
}
|
||||
var span = {
|
||||
pos: getStartLinePositionForPosition(parent.getStart(sourceFile), sourceFile),
|
||||
end: parent.end
|
||||
};
|
||||
return formatSpan(span, sourceFile, options, rulesProvider, requestKind);
|
||||
}
|
||||
|
||||
function findOutermostParent(position: number, expectedTokenKind: SyntaxKind, sourceFile: SourceFile): Node {
|
||||
var precedingToken = findPrecedingToken(position, sourceFile);
|
||||
if (!precedingToken || precedingToken.kind !== expectedTokenKind) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// walk up and search for the parent node that ends at the same position with precedingToken.
|
||||
// for cases like this
|
||||
//
|
||||
// var x = 1;
|
||||
// while (true) {
|
||||
// }
|
||||
// after typing close curly in while statement we want to reformat just the while statement.
|
||||
// However if we just walk upwards searching for the parent that has the same end value -
|
||||
// we'll end up with the whole source file. isListElement allows to stop on the list element level
|
||||
var current = precedingToken;
|
||||
while (current &&
|
||||
current.parent &&
|
||||
current.parent.end === precedingToken.end &&
|
||||
!isListElement(current.parent, current)) {
|
||||
current = current.parent;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
// Returns true if node is a element in some list in parent
|
||||
// i.e. parent is class declaration with the list of members and node is one of members.
|
||||
function isListElement(parent: Node, node: Node): boolean {
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
return rangeContainsRange((<InterfaceDeclaration>parent).members, node);
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
var body = (<ModuleDeclaration>parent).body;
|
||||
return body && body.kind === SyntaxKind.Block && rangeContainsRange((<Block>body).statements, node);
|
||||
case SyntaxKind.SourceFile:
|
||||
case SyntaxKind.Block:
|
||||
case SyntaxKind.TryBlock:
|
||||
case SyntaxKind.CatchBlock:
|
||||
case SyntaxKind.FinallyBlock:
|
||||
case SyntaxKind.ModuleBlock:
|
||||
return rangeContainsRange((<Block>parent).statements, node)
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** find node that fully contains given text range */
|
||||
function findEnclosingNode(range: TextRange, sourceFile: SourceFile): Node {
|
||||
return find(sourceFile);
|
||||
|
||||
function find(n: Node): Node {
|
||||
var candidate = forEachChild(n, c => startEndContainsRange(c.getStart(sourceFile), c.end, range) && c);
|
||||
if (candidate) {
|
||||
var result = find(candidate);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
/** formatting is not applied to ranges that contain parse errors.
|
||||
* This function will return a predicate that for a given text range will tell
|
||||
* if there are any parse errors that overlap with the range.
|
||||
*/
|
||||
function prepareRangeContainsErrorFunction(errors: Diagnostic[], originalRange: TextRange): (r: TextRange) => boolean {
|
||||
if (!errors.length) {
|
||||
return rangeHasNoErrors;
|
||||
}
|
||||
|
||||
// pick only errors that fall in range
|
||||
var sorted = errors
|
||||
.filter(d => d.isParseError && rangeOverlapsWithStartEnd(originalRange, d.start, d.start + d.length))
|
||||
.sort((e1, e2) => e1.start - e2.start);
|
||||
|
||||
if (!sorted.length) {
|
||||
return rangeHasNoErrors;
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
|
||||
return r => {
|
||||
// in current implementation sequence of arguments [r1, r2...] is monotonically increasing.
|
||||
// 'index' tracks the index of the most recent error that was checked.
|
||||
while (true) {
|
||||
if (index >= sorted.length) {
|
||||
// all errors in the range were already checked -> no error in specified range
|
||||
return false;
|
||||
}
|
||||
|
||||
var error = sorted[index];
|
||||
if (r.end <= error.start) {
|
||||
// specified range ends before the error refered by 'index' - no error in range
|
||||
return false;
|
||||
}
|
||||
|
||||
if (startEndOverlapsWithStartEnd(r.pos, r.end, error.start, error.start + error.length)) {
|
||||
// specified range overlaps with error range
|
||||
return true;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
};
|
||||
|
||||
function rangeHasNoErrors(r: TextRange): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start of the original range might fall inside the comment - scanner will not yield appropriate results
|
||||
* This function will look for token that is located before the start of target range
|
||||
* and return its end as start position for the scanner.
|
||||
*/
|
||||
function getScanStartPosition(enclosingNode: Node, originalRange: TextRange, sourceFile: SourceFile): number {
|
||||
var start = enclosingNode.getStart(sourceFile);
|
||||
if (start === originalRange.pos && enclosingNode.end === originalRange.end) {
|
||||
return start;
|
||||
}
|
||||
|
||||
var precedingToken = findPrecedingToken(enclosingNode.pos, sourceFile);
|
||||
// no preceding token found - start from the beginning of enclosing node
|
||||
return precedingToken ? precedingToken.end : enclosingNode.pos;
|
||||
}
|
||||
|
||||
function formatSpan(originalRange: TextRange,
|
||||
sourceFile: SourceFile,
|
||||
options: FormatCodeOptions,
|
||||
rulesProvider: RulesProvider,
|
||||
requestKind: FormattingRequestKind): TextChange[] {
|
||||
|
||||
var rangeContainsError = prepareRangeContainsErrorFunction(sourceFile.syntacticErrors, originalRange);
|
||||
|
||||
// formatting context is used by rules provider
|
||||
var formattingContext = new FormattingContext(sourceFile, requestKind);
|
||||
|
||||
// find the smallest node that fully wraps the range and compute the initial indentation for the node
|
||||
var enclosingNode = findEnclosingNode(originalRange, sourceFile);
|
||||
|
||||
var formattingScanner = getFormattingScanner(sourceFile, getScanStartPosition(enclosingNode, originalRange, sourceFile), originalRange.end);
|
||||
|
||||
var initialIndentation = SmartIndenter.getIndentationForNode(enclosingNode, originalRange, sourceFile, options);
|
||||
|
||||
var previousRangeHasError: boolean;
|
||||
var previousRange: TextRangeWithKind;
|
||||
var previousParent: Node;
|
||||
var previousRangeStartLine: number;
|
||||
|
||||
var edits: TextChange[] = [];
|
||||
|
||||
formattingScanner.advance();
|
||||
|
||||
if (formattingScanner.isOnToken()) {
|
||||
var startLine = sourceFile.getLineAndCharacterFromPosition(enclosingNode.getStart(sourceFile)).line;
|
||||
var delta = SmartIndenter.shouldIndentChildNode(enclosingNode.kind, SyntaxKind.Unknown) ? options.IndentSize : 0;
|
||||
processNode(enclosingNode, enclosingNode, startLine, initialIndentation, delta);
|
||||
}
|
||||
|
||||
formattingScanner.close();
|
||||
|
||||
return edits;
|
||||
|
||||
// local functions
|
||||
|
||||
/** Tries to compute the indentation for a list element.
|
||||
* If list element is not in range then
|
||||
* function will pick its actual indentation
|
||||
* so it can be pushed downstream as inherited indentation.
|
||||
* If list element is in the range - its indentation will be equal
|
||||
* to inherited indentation from its predecessors.
|
||||
*/
|
||||
function tryComputeIndentationForListItem(startPos: number,
|
||||
endPos: number,
|
||||
parentStartLine: number,
|
||||
range: TextRange,
|
||||
inheritedIndentation: number): number {
|
||||
|
||||
if (rangeOverlapsWithStartEnd(range, startPos, endPos)) {
|
||||
if (inheritedIndentation !== Constants.Unknown) {
|
||||
return inheritedIndentation;
|
||||
}
|
||||
}
|
||||
else {
|
||||
var startLine = sourceFile.getLineAndCharacterFromPosition(startPos).line;
|
||||
var startLinePosition = getStartLinePositionForPosition(startPos, sourceFile);
|
||||
var column = SmartIndenter.findFirstNonWhitespaceColumn(startLinePosition, startPos, sourceFile, options);
|
||||
if (startLine !== parentStartLine || startPos === column) {
|
||||
return column
|
||||
}
|
||||
}
|
||||
|
||||
return Constants.Unknown;
|
||||
}
|
||||
|
||||
function computeIndentation(
|
||||
node: TextRangeWithKind,
|
||||
startLine: number,
|
||||
inheritedIndentation: number,
|
||||
parent: Node,
|
||||
parentDynamicIndentation: DynamicIndentation,
|
||||
effectiveParentStartLine: number): Indentation {
|
||||
|
||||
var indentation = inheritedIndentation;
|
||||
if (indentation === Constants.Unknown) {
|
||||
if (isSomeBlock(node.kind)) {
|
||||
// blocks should be indented in
|
||||
// - other blocks
|
||||
// - source file
|
||||
// - switch\default clauses
|
||||
if (isSomeBlock(parent.kind) ||
|
||||
parent.kind === SyntaxKind.SourceFile ||
|
||||
parent.kind === SyntaxKind.CaseClause ||
|
||||
parent.kind === SyntaxKind.DefaultClause) {
|
||||
|
||||
indentation = parentDynamicIndentation.getIndentation() + parentDynamicIndentation.getDelta();
|
||||
}
|
||||
else {
|
||||
indentation = parentDynamicIndentation.getIndentation();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile)) {
|
||||
indentation = parentDynamicIndentation.getIndentation();
|
||||
}
|
||||
else {
|
||||
indentation = parentDynamicIndentation.getIndentation() + parentDynamicIndentation.getDelta();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var delta = SmartIndenter.shouldIndentChildNode(node.kind, SyntaxKind.Unknown) ? options.IndentSize : 0;
|
||||
|
||||
if (effectiveParentStartLine === startLine) {
|
||||
// if node is located on the same line with the parent
|
||||
// - inherit indentation from the parent
|
||||
// - push children if either parent of node itself has non-zero delta
|
||||
indentation = parentDynamicIndentation.getIndentation();
|
||||
delta = Math.min(options.IndentSize, parentDynamicIndentation.getDelta() + delta);
|
||||
}
|
||||
return {
|
||||
indentation: indentation,
|
||||
delta: delta
|
||||
}
|
||||
}
|
||||
|
||||
function getDynamicIndentation(node: Node, nodeStartLine: number, indentation: number, delta: number): DynamicIndentation {
|
||||
return {
|
||||
getIndentationForComment: kind => {
|
||||
switch (kind) {
|
||||
// preceding comment to the token that closes the indentation scope inherits the indentation from the scope
|
||||
// .. {
|
||||
// // comment
|
||||
// }
|
||||
case SyntaxKind.CloseBraceToken:
|
||||
case SyntaxKind.CloseBracketToken:
|
||||
return indentation + delta;
|
||||
}
|
||||
return indentation;
|
||||
},
|
||||
getIndentationForToken: (line, kind) => {
|
||||
switch (kind) {
|
||||
// open and close brace, 'else' and 'while' (in do statement) tokens has indentation of the parent
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
case SyntaxKind.CloseBraceToken:
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
case SyntaxKind.CloseBracketToken:
|
||||
case SyntaxKind.ElseKeyword:
|
||||
case SyntaxKind.WhileKeyword:
|
||||
return indentation;
|
||||
default:
|
||||
// if token line equals to the line of containing node (this is a first token in the node) - use node indentation
|
||||
return nodeStartLine !== line ? indentation + delta : indentation;
|
||||
}
|
||||
},
|
||||
getIndentation: () => indentation,
|
||||
getDelta: () => delta,
|
||||
recomputeIndentation: lineAdded => {
|
||||
if (node.parent && SmartIndenter.shouldIndentChildNode(node.parent.kind, node.kind)) {
|
||||
if (lineAdded) {
|
||||
indentation += options.IndentSize;
|
||||
}
|
||||
else {
|
||||
indentation -= options.IndentSize;
|
||||
}
|
||||
|
||||
if (SmartIndenter.shouldIndentChildNode(node.kind, SyntaxKind.Unknown)) {
|
||||
delta = options.IndentSize;
|
||||
}
|
||||
else {
|
||||
delta = 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function processNode(node: Node, contextNode: Node, nodeStartLine: number, indentation: number, delta: number) {
|
||||
if (!rangeOverlapsWithStartEnd(originalRange, node.getStart(sourceFile), node.getEnd())) {
|
||||
return;
|
||||
}
|
||||
|
||||
var nodeDynamicIndentation = getDynamicIndentation(node, nodeStartLine, indentation, delta);
|
||||
|
||||
// a useful observations when tracking context node
|
||||
// /
|
||||
// [a]
|
||||
// / | \
|
||||
// [b] [c] [d]
|
||||
// node 'a' is a context node for nodes 'b', 'c', 'd'
|
||||
// except for the leftmost leaf token in [b] - in this case context node ('e') is located somewhere above 'a'
|
||||
// this rule can be applied recursively to child nodes of 'a'.
|
||||
//
|
||||
// context node is set to parent node value after processing every child node
|
||||
// context node is set to parent of the token after processing every token
|
||||
|
||||
var childContextNode = contextNode;
|
||||
|
||||
// if there are any tokens that logically belong to node and interleave child nodes
|
||||
// such tokens will be consumed in processChildNode for for the child that follows them
|
||||
forEachChild(
|
||||
node,
|
||||
child => {
|
||||
processChildNode(child, /*inheritedIndentation*/ Constants.Unknown, node, nodeDynamicIndentation, nodeStartLine, /*isListElement*/ false)
|
||||
},
|
||||
(nodes: NodeArray<Node>) => {
|
||||
processChildNodes(nodes, node, nodeStartLine, nodeDynamicIndentation);
|
||||
});
|
||||
|
||||
// proceed any tokens in the node that are located after child nodes
|
||||
while (formattingScanner.isOnToken()) {
|
||||
var tokenInfo = formattingScanner.readTokenInfo(node);
|
||||
if (tokenInfo.token.end > node.end) {
|
||||
break;
|
||||
}
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, node, nodeDynamicIndentation);
|
||||
}
|
||||
|
||||
function processChildNode(
|
||||
child: Node,
|
||||
inheritedIndentation: number,
|
||||
parent: Node,
|
||||
parentDynamicIndentation: DynamicIndentation,
|
||||
parentStartLine: number,
|
||||
isListItem: boolean): number {
|
||||
|
||||
var childStartPos = child.getStart(sourceFile);
|
||||
|
||||
var childStart = sourceFile.getLineAndCharacterFromPosition(childStartPos);
|
||||
|
||||
// if child is a list item - try to get its indentation
|
||||
var childIndentationAmount = Constants.Unknown;
|
||||
if (isListItem) {
|
||||
childIndentationAmount = tryComputeIndentationForListItem(childStartPos, child.end, parentStartLine, originalRange, inheritedIndentation);
|
||||
if (childIndentationAmount !== Constants.Unknown) {
|
||||
inheritedIndentation = childIndentationAmount;
|
||||
}
|
||||
}
|
||||
|
||||
// child node is outside the target range - do not dive inside
|
||||
if (!rangeOverlapsWithStartEnd(originalRange, child.pos, child.end)) {
|
||||
return inheritedIndentation;
|
||||
}
|
||||
|
||||
if (child.kind === SyntaxKind.Missing) {
|
||||
return inheritedIndentation;
|
||||
}
|
||||
|
||||
while (formattingScanner.isOnToken()) {
|
||||
// proceed any parent tokens that are located prior to child.getStart()
|
||||
var tokenInfo = formattingScanner.readTokenInfo(node);
|
||||
if (tokenInfo.token.end > childStartPos) {
|
||||
// stop when formatting scanner advances past the beginning of the child
|
||||
break;
|
||||
}
|
||||
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, node, parentDynamicIndentation);
|
||||
}
|
||||
|
||||
if (!formattingScanner.isOnToken()) {
|
||||
return inheritedIndentation;
|
||||
}
|
||||
|
||||
if (isToken(child)) {
|
||||
// if child node is a token, it does not impact indentation, proceed it using parent indentation scope rules
|
||||
var tokenInfo = formattingScanner.readTokenInfo(node);
|
||||
Debug.assert(tokenInfo.token.end === child.end);
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, node, parentDynamicIndentation);
|
||||
return inheritedIndentation;
|
||||
}
|
||||
|
||||
var childIndentation = computeIndentation(child, childStart.line, childIndentationAmount, node, parentDynamicIndentation, parentStartLine);
|
||||
|
||||
processNode(child, childContextNode, childStart.line, childIndentation.indentation, childIndentation.delta);
|
||||
|
||||
childContextNode = node;
|
||||
|
||||
return inheritedIndentation;
|
||||
}
|
||||
|
||||
function processChildNodes(nodes: NodeArray<Node>,
|
||||
parent: Node,
|
||||
parentStartLine: number,
|
||||
parentDynamicIndentation: DynamicIndentation): void {
|
||||
|
||||
var listStartToken = getOpenTokenForList(parent, nodes);
|
||||
var listEndToken = getCloseTokenForOpenToken(listStartToken);
|
||||
|
||||
var listDynamicIndentation = parentDynamicIndentation;
|
||||
var startLine = parentStartLine;
|
||||
|
||||
if (listStartToken !== SyntaxKind.Unknown) {
|
||||
// introduce a new indentation scope for lists (including list start and end tokens)
|
||||
while (formattingScanner.isOnToken()) {
|
||||
var tokenInfo = formattingScanner.readTokenInfo(parent);
|
||||
if (tokenInfo.token.end > nodes.pos) {
|
||||
// stop when formatting scanner moves past the beginning of node list
|
||||
break;
|
||||
}
|
||||
else if (tokenInfo.token.kind === listStartToken) {
|
||||
// consume list start token
|
||||
startLine = sourceFile.getLineAndCharacterFromPosition(tokenInfo.token.pos).line;
|
||||
var indentation =
|
||||
computeIndentation(tokenInfo.token, startLine, Constants.Unknown, parent, parentDynamicIndentation, startLine);
|
||||
|
||||
listDynamicIndentation = getDynamicIndentation(parent, parentStartLine, indentation.indentation, indentation.delta);
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, parent, listDynamicIndentation);
|
||||
}
|
||||
else {
|
||||
// consume any tokens that precede the list as child elements of 'node' using its indentation scope
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, parent, parentDynamicIndentation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var inheritedIndentation = Constants.Unknown;
|
||||
for (var i = 0, len = nodes.length; i < len; ++i) {
|
||||
inheritedIndentation = processChildNode(nodes[i], inheritedIndentation, node, listDynamicIndentation, startLine, /*isListElement*/ true)
|
||||
}
|
||||
|
||||
if (listEndToken !== SyntaxKind.Unknown) {
|
||||
if (formattingScanner.isOnToken()) {
|
||||
var tokenInfo = formattingScanner.readTokenInfo(parent);
|
||||
if (tokenInfo.token.kind === listEndToken) {
|
||||
// consume list end token
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, parent, listDynamicIndentation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function consumeTokenAndAdvanceScanner(currentTokenInfo: TokenInfo, parent: Node, dynamicIndentation: DynamicIndentation): void {
|
||||
Debug.assert(rangeContainsRange(parent, currentTokenInfo.token));
|
||||
|
||||
var lastTriviaWasNewLine = formattingScanner.lastTrailingTriviaWasNewLine();
|
||||
var indentToken = false;
|
||||
|
||||
if (currentTokenInfo.leadingTrivia) {
|
||||
processTrivia(currentTokenInfo.leadingTrivia, parent, childContextNode, dynamicIndentation);
|
||||
}
|
||||
|
||||
var lineAdded: boolean;
|
||||
var isTokenInRange = rangeContainsRange(originalRange, currentTokenInfo.token);
|
||||
|
||||
var tokenStart = sourceFile.getLineAndCharacterFromPosition(currentTokenInfo.token.pos);
|
||||
if (isTokenInRange) {
|
||||
// save prevStartLine since processRange will overwrite this value with current ones
|
||||
var prevStartLine = previousRangeStartLine;
|
||||
lineAdded = processRange(currentTokenInfo.token, tokenStart, parent, childContextNode, dynamicIndentation);
|
||||
if (lineAdded !== undefined) {
|
||||
indentToken = lineAdded;
|
||||
}
|
||||
else {
|
||||
indentToken = lastTriviaWasNewLine && tokenStart.line !== prevStartLine;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentTokenInfo.trailingTrivia) {
|
||||
processTrivia(currentTokenInfo.trailingTrivia, parent, childContextNode, dynamicIndentation);
|
||||
}
|
||||
|
||||
if (indentToken) {
|
||||
var indentNextTokenOrTrivia = true;
|
||||
if (currentTokenInfo.leadingTrivia) {
|
||||
for (var i = 0, len = currentTokenInfo.leadingTrivia.length; i < len; ++i) {
|
||||
var triviaItem = currentTokenInfo.leadingTrivia[i];
|
||||
if (!rangeContainsRange(originalRange, triviaItem)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var triviaStartLine = sourceFile.getLineAndCharacterFromPosition(triviaItem.pos).line;
|
||||
switch (triviaItem.kind) {
|
||||
case SyntaxKind.MultiLineCommentTrivia:
|
||||
var commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind);
|
||||
indentMultilineComment(triviaItem, commentIndentation, /*firstLineIsIndented*/ !indentNextTokenOrTrivia);
|
||||
indentNextTokenOrTrivia = false;
|
||||
break;
|
||||
case SyntaxKind.SingleLineCommentTrivia:
|
||||
if (indentNextTokenOrTrivia) {
|
||||
var commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind);
|
||||
insertIndentation(triviaItem.pos, commentIndentation, /*lineAdded*/ false);
|
||||
indentNextTokenOrTrivia = false;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.NewLineTrivia:
|
||||
indentNextTokenOrTrivia = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// indent token only if is it is in target range and does not overlap with any error ranges
|
||||
if (isTokenInRange && !rangeContainsError(currentTokenInfo.token)) {
|
||||
var tokenIndentation = dynamicIndentation.getIndentationForToken(tokenStart.line, currentTokenInfo.token.kind);
|
||||
insertIndentation(currentTokenInfo.token.pos, tokenIndentation, lineAdded);
|
||||
}
|
||||
}
|
||||
|
||||
formattingScanner.advance();
|
||||
|
||||
childContextNode = parent;
|
||||
}
|
||||
}
|
||||
|
||||
function processTrivia(trivia: TextRangeWithKind[], parent: Node, contextNode: Node, dynamicIndentation: DynamicIndentation): void {
|
||||
for (var i = 0, len = trivia.length; i < len; ++i) {
|
||||
var triviaItem = trivia[i];
|
||||
if (isComment(triviaItem.kind) && rangeContainsRange(originalRange, triviaItem)) {
|
||||
var triviaItemStart = sourceFile.getLineAndCharacterFromPosition(triviaItem.pos);
|
||||
processRange(triviaItem, triviaItemStart, parent, contextNode, dynamicIndentation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processRange(range: TextRangeWithKind,
|
||||
rangeStart: LineAndCharacter,
|
||||
parent: Node,
|
||||
contextNode: Node,
|
||||
dynamicIndentation: DynamicIndentation): boolean {
|
||||
|
||||
var rangeHasError = rangeContainsError(range);
|
||||
var lineAdded: boolean;
|
||||
if (!rangeHasError && !previousRangeHasError) {
|
||||
if (!previousRange) {
|
||||
// trim whitespaces starting from the beginning of the span up to the current line
|
||||
var originalStart = sourceFile.getLineAndCharacterFromPosition(originalRange.pos);
|
||||
trimTrailingWhitespacesForLines(originalStart.line, rangeStart.line);
|
||||
}
|
||||
else {
|
||||
lineAdded =
|
||||
processPair(range, rangeStart.line, parent, previousRange, previousRangeStartLine, previousParent, contextNode, dynamicIndentation)
|
||||
}
|
||||
}
|
||||
|
||||
previousRange = range;
|
||||
previousParent = parent;
|
||||
previousRangeStartLine = rangeStart.line;
|
||||
previousRangeHasError = rangeHasError;
|
||||
|
||||
return lineAdded;
|
||||
}
|
||||
|
||||
function processPair(currentItem: TextRangeWithKind,
|
||||
currentStartLine: number,
|
||||
currentParent: Node,
|
||||
previousItem: TextRangeWithKind,
|
||||
previousStartLine: number,
|
||||
previousParent: Node,
|
||||
contextNode: Node,
|
||||
dynamicIndentation: DynamicIndentation): boolean {
|
||||
|
||||
formattingContext.updateContext(previousItem, previousParent, currentItem, currentParent, contextNode);
|
||||
|
||||
var rule = rulesProvider.getRulesMap().GetRule(formattingContext);
|
||||
|
||||
var trimTrailingWhitespaces: boolean;
|
||||
var lineAdded: boolean;
|
||||
if (rule) {
|
||||
applyRuleEdits(rule, previousItem, previousStartLine, currentItem, currentStartLine);
|
||||
|
||||
if (rule.Operation.Action & (RuleAction.Space | RuleAction.Delete) && currentStartLine !== previousStartLine) {
|
||||
// Handle the case where the next line is moved to be the end of this line.
|
||||
// In this case we don't indent the next line in the next pass.
|
||||
if (currentParent.getStart(sourceFile) === currentItem.pos) {
|
||||
lineAdded = false;
|
||||
}
|
||||
}
|
||||
else if (rule.Operation.Action & RuleAction.NewLine && currentStartLine === previousStartLine) {
|
||||
// Handle the case where token2 is moved to the new line.
|
||||
// In this case we indent token2 in the next pass but we set
|
||||
// sameLineIndent flag to notify the indenter that the indentation is within the line.
|
||||
if (currentParent.getStart(sourceFile) === currentItem.pos) {
|
||||
lineAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (lineAdded !== undefined) {
|
||||
dynamicIndentation.recomputeIndentation(lineAdded);
|
||||
}
|
||||
|
||||
// We need to trim trailing whitespace between the tokens if they were on different lines, and no rule was applied to put them on the same line
|
||||
trimTrailingWhitespaces =
|
||||
(rule.Operation.Action & (RuleAction.NewLine | RuleAction.Space)) &&
|
||||
rule.Flag !== RuleFlags.CanDeleteNewLines;
|
||||
}
|
||||
else {
|
||||
trimTrailingWhitespaces = true;
|
||||
}
|
||||
|
||||
if (currentStartLine !== previousStartLine && trimTrailingWhitespaces) {
|
||||
// We need to trim trailing whitespace between the tokens if they were on different lines, and no rule was applied to put them on the same line
|
||||
trimTrailingWhitespacesForLines(previousStartLine, currentStartLine, previousItem);
|
||||
}
|
||||
|
||||
return lineAdded;
|
||||
}
|
||||
|
||||
function insertIndentation(pos: number, indentation: number, lineAdded: boolean): void {
|
||||
var indentationString = getIndentationString(indentation, options);
|
||||
if (lineAdded) {
|
||||
// new line is added before the token by the formatting rules
|
||||
// insert indentation string at the very beginning of the token
|
||||
recordReplace(pos, 0, indentationString);
|
||||
}
|
||||
else {
|
||||
var tokenStart = sourceFile.getLineAndCharacterFromPosition(pos);
|
||||
if (indentation !== tokenStart.character - 1) {
|
||||
var startLinePosition = getStartPositionOfLine(tokenStart.line, sourceFile);
|
||||
recordReplace(startLinePosition, tokenStart.character - 1, indentationString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function indentMultilineComment(commentRange: TextRange, indentation: number, firstLineIsIndented: boolean) {
|
||||
// split comment in lines
|
||||
var startLine = sourceFile.getLineAndCharacterFromPosition(commentRange.pos).line;
|
||||
var endLine = sourceFile.getLineAndCharacterFromPosition(commentRange.end).line;
|
||||
|
||||
if (startLine === endLine) {
|
||||
if (!firstLineIsIndented) {
|
||||
// treat as single line comment
|
||||
insertIndentation(commentRange.pos, indentation, /*lineAdded*/ false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
var parts: TextRange[] = [];
|
||||
var startPos = commentRange.pos;
|
||||
for (var line = startLine; line < endLine; ++line) {
|
||||
var endOfLine = getEndLinePosition(line, sourceFile);
|
||||
parts.push({ pos: startPos, end: endOfLine });
|
||||
startPos = getStartPositionOfLine(line + 1, sourceFile);
|
||||
}
|
||||
|
||||
parts.push({ pos: startPos, end: commentRange.end });
|
||||
}
|
||||
|
||||
var startLinePos = getStartPositionOfLine(startLine, sourceFile);
|
||||
|
||||
var nonWhitespaceColumnInFirstPart =
|
||||
SmartIndenter.findFirstNonWhitespaceColumn(startLinePos, parts[0].pos, sourceFile, options);
|
||||
|
||||
if (indentation === nonWhitespaceColumnInFirstPart) {
|
||||
return;
|
||||
}
|
||||
|
||||
var startIndex = 0;
|
||||
if (firstLineIsIndented) {
|
||||
startIndex = 1;
|
||||
startLine++;
|
||||
}
|
||||
|
||||
// shift all parts on the delta size
|
||||
var delta = indentation - nonWhitespaceColumnInFirstPart;
|
||||
for (var i = startIndex, len = parts.length; i < len; ++i, ++startLine) {
|
||||
var startLinePos = getStartPositionOfLine(startLine, sourceFile);
|
||||
var nonWhitespaceColumn =
|
||||
i === 0
|
||||
? nonWhitespaceColumnInFirstPart
|
||||
: SmartIndenter.findFirstNonWhitespaceColumn(parts[i].pos, parts[i].end, sourceFile, options);
|
||||
|
||||
var newIndentation = nonWhitespaceColumn + delta;
|
||||
if (newIndentation > 0) {
|
||||
var indentationString = getIndentationString(newIndentation, options);
|
||||
recordReplace(startLinePos, nonWhitespaceColumn, indentationString);
|
||||
}
|
||||
else {
|
||||
recordDelete(startLinePos, nonWhitespaceColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function trimTrailingWhitespacesForLines(line1: number, line2: number, range?: TextRangeWithKind) {
|
||||
for (var line = line1; line < line2; ++line) {
|
||||
var lineStartPosition = getStartPositionOfLine(line, sourceFile);
|
||||
var lineEndPosition = getEndLinePosition(line, sourceFile);
|
||||
|
||||
// do not trim whitespaces in comments
|
||||
if (range && isComment(range.kind) && range.pos <= lineEndPosition && range.end > lineEndPosition) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var pos = lineEndPosition;
|
||||
while (pos >= lineStartPosition && isWhiteSpace(sourceFile.text.charCodeAt(pos))) {
|
||||
pos--;
|
||||
}
|
||||
if (pos !== lineEndPosition) {
|
||||
Debug.assert(pos === lineStartPosition || !isWhiteSpace(sourceFile.text.charCodeAt(pos)));
|
||||
recordDelete(pos + 1, lineEndPosition - pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function newTextChange(start: number, len: number, newText: string): TextChange {
|
||||
return { span: new TypeScript.TextSpan(start, len), newText: newText }
|
||||
}
|
||||
|
||||
function recordDelete(start: number, len: number) {
|
||||
if (len) {
|
||||
edits.push(newTextChange(start, len, ""));
|
||||
}
|
||||
}
|
||||
|
||||
function recordReplace(start: number, len: number, newText: string) {
|
||||
if (len || newText) {
|
||||
edits.push(newTextChange(start, len, newText));
|
||||
}
|
||||
}
|
||||
|
||||
function applyRuleEdits(rule: Rule,
|
||||
previousRange: TextRangeWithKind,
|
||||
previousStartLine: number,
|
||||
currentRange: TextRangeWithKind,
|
||||
currentStartLine: number): void {
|
||||
|
||||
var between: TextRange;
|
||||
switch (rule.Operation.Action) {
|
||||
case RuleAction.Ignore:
|
||||
// no action required
|
||||
return;
|
||||
case RuleAction.Delete:
|
||||
if (previousRange.end !== currentRange.pos) {
|
||||
// delete characters starting from t1.end up to t2.pos exclusive
|
||||
recordDelete(previousRange.end, currentRange.pos - previousRange.end);
|
||||
}
|
||||
break;
|
||||
case RuleAction.NewLine:
|
||||
// exit early if we on different lines and rule cannot change number of newlines
|
||||
// if line1 and line2 are on subsequent lines then no edits are required - ok to exit
|
||||
// if line1 and line2 are separated with more than one newline - ok to exit since we cannot delete extra new lines
|
||||
if (rule.Flag !== RuleFlags.CanDeleteNewLines && previousStartLine !== currentStartLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
// edit should not be applied only if we have one line feed between elements
|
||||
var lineDelta = currentStartLine - previousStartLine;
|
||||
if (lineDelta !== 1) {
|
||||
recordReplace(previousRange.end, currentRange.pos - previousRange.end, options.NewLineCharacter);
|
||||
}
|
||||
break;
|
||||
case RuleAction.Space:
|
||||
// exit early if we on different lines and rule cannot change number of newlines
|
||||
if (rule.Flag !== RuleFlags.CanDeleteNewLines && previousStartLine !== currentStartLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
var posDelta = currentRange.pos - previousRange.end;
|
||||
if (posDelta !== 1 || sourceFile.text.charCodeAt(previousRange.end) !== CharacterCodes.space) {
|
||||
recordReplace(previousRange.end, currentRange.pos - previousRange.end, " ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isSomeBlock(kind: SyntaxKind): boolean {
|
||||
switch (kind) {
|
||||
case SyntaxKind.Block:
|
||||
case SyntaxKind.FunctionBlock:
|
||||
case SyntaxKind.TryBlock:
|
||||
case SyntaxKind.CatchBlock:
|
||||
case SyntaxKind.FinallyBlock:
|
||||
case SyntaxKind.ModuleBlock:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getOpenTokenForList(node: Node, list: Node[]) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.Method:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
if ((<FunctionDeclaration>node).typeParameters === list) {
|
||||
return SyntaxKind.LessThanToken;
|
||||
}
|
||||
else if ((<FunctionDeclaration>node).parameters === list) {
|
||||
return SyntaxKind.OpenParenToken;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.NewExpression:
|
||||
if ((<CallExpression>node).typeArguments === list) {
|
||||
return SyntaxKind.LessThanToken;
|
||||
}
|
||||
else if ((<CallExpression>node).arguments === list) {
|
||||
return SyntaxKind.OpenParenToken;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.TypeReference:
|
||||
if ((<TypeReferenceNode>node).typeArguments === list) {
|
||||
return SyntaxKind.LessThanToken;
|
||||
}
|
||||
}
|
||||
|
||||
return SyntaxKind.Unknown;
|
||||
}
|
||||
|
||||
function getCloseTokenForOpenToken(kind: SyntaxKind) {
|
||||
switch (kind) {
|
||||
case SyntaxKind.OpenParenToken:
|
||||
return SyntaxKind.CloseParenToken;
|
||||
case SyntaxKind.LessThanToken:
|
||||
return SyntaxKind.GreaterThanToken;
|
||||
}
|
||||
|
||||
return SyntaxKind.Unknown;
|
||||
}
|
||||
}
|
|
@ -1,315 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export class Formatter extends MultipleTokenIndenter {
|
||||
private previousTokenSpan: TokenSpan = null;
|
||||
private previousTokenParent: IndentationNodeContext = null;
|
||||
|
||||
// TODO: implement it with skipped tokens in Fidelity
|
||||
private scriptHasErrors: boolean = false;
|
||||
|
||||
private rulesProvider: RulesProvider;
|
||||
private formattingRequestKind: FormattingRequestKind;
|
||||
private formattingContext: FormattingContext;
|
||||
|
||||
constructor(textSpan: TextSpan,
|
||||
sourceUnit: SourceUnitSyntax,
|
||||
indentFirstToken: boolean,
|
||||
options: FormattingOptions,
|
||||
snapshot: ITextSnapshot,
|
||||
rulesProvider: RulesProvider,
|
||||
formattingRequestKind: FormattingRequestKind) {
|
||||
|
||||
super(textSpan, sourceUnit, snapshot, indentFirstToken, options);
|
||||
|
||||
this.previousTokenParent = this.parent().clone(this.indentationNodeContextPool());
|
||||
|
||||
this.rulesProvider = rulesProvider;
|
||||
this.formattingRequestKind = formattingRequestKind;
|
||||
this.formattingContext = new FormattingContext(this.snapshot(), this.formattingRequestKind);
|
||||
}
|
||||
|
||||
public static getEdits(textSpan: TextSpan,
|
||||
sourceUnit: SourceUnitSyntax,
|
||||
options: FormattingOptions,
|
||||
indentFirstToken: boolean,
|
||||
snapshot: ITextSnapshot,
|
||||
rulesProvider: RulesProvider,
|
||||
formattingRequestKind: FormattingRequestKind): TextEditInfo[] {
|
||||
var walker = new Formatter(textSpan, sourceUnit, indentFirstToken, options, snapshot, rulesProvider, formattingRequestKind);
|
||||
walker.walk(sourceUnit);
|
||||
return walker.edits();
|
||||
}
|
||||
|
||||
public visitTokenInSpan(token: ISyntaxToken): void {
|
||||
if (token.fullWidth() !== 0) {
|
||||
var tokenSpan = new TextSpan(this.position() + token.leadingTriviaWidth(), width(token));
|
||||
if (this.textSpan().containsTextSpan(tokenSpan)) {
|
||||
this.processToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
// Call the base class to process the token and indent it if needed
|
||||
super.visitTokenInSpan(token);
|
||||
}
|
||||
|
||||
private processToken(token: ISyntaxToken): void {
|
||||
var position = this.position();
|
||||
|
||||
// Extract any leading comments
|
||||
if (token.leadingTriviaWidth() !== 0) {
|
||||
this.processTrivia(token.leadingTrivia(), position);
|
||||
position += token.leadingTriviaWidth();
|
||||
}
|
||||
|
||||
// Push the token
|
||||
var currentTokenSpan = new TokenSpan(token.kind, position, width(token));
|
||||
if (!this.parent().hasSkippedOrMissingTokenChild()) {
|
||||
if (this.previousTokenSpan) {
|
||||
// Note that formatPair calls TrimWhitespaceInLineRange in between the 2 tokens
|
||||
this.formatPair(this.previousTokenSpan, this.previousTokenParent, currentTokenSpan, this.parent());
|
||||
}
|
||||
else {
|
||||
// We still want to trim whitespace even if it is the first trivia of the first token. Trim from the beginning of the span to the trivia
|
||||
this.trimWhitespaceInLineRange(this.getLineNumber(this.textSpan()), this.getLineNumber(currentTokenSpan));
|
||||
}
|
||||
}
|
||||
this.previousTokenSpan = currentTokenSpan;
|
||||
if (this.previousTokenParent) {
|
||||
// Make sure to clear the previous parent before assigning a new value to it
|
||||
this.indentationNodeContextPool().releaseNode(this.previousTokenParent, /* recursive */true);
|
||||
}
|
||||
this.previousTokenParent = this.parent().clone(this.indentationNodeContextPool());
|
||||
position += width(token);
|
||||
}
|
||||
|
||||
private processTrivia(triviaList: ISyntaxTriviaList, fullStart: number) {
|
||||
var position = fullStart;
|
||||
|
||||
for (var i = 0, n = triviaList.count(); i < n ; i++) {
|
||||
var trivia = triviaList.syntaxTriviaAt(i);
|
||||
// For a comment, format it like it is a token. For skipped text, eat it up as a token, but skip the formatting
|
||||
if (trivia.isComment() || trivia.isSkippedToken()) {
|
||||
var currentTokenSpan = new TokenSpan(trivia.kind, position, trivia.fullWidth());
|
||||
if (this.textSpan().containsTextSpan(currentTokenSpan)) {
|
||||
if (trivia.isComment() && this.previousTokenSpan) {
|
||||
// Note that formatPair calls TrimWhitespaceInLineRange in between the 2 tokens
|
||||
this.formatPair(this.previousTokenSpan, this.previousTokenParent, currentTokenSpan, this.parent());
|
||||
}
|
||||
else {
|
||||
// We still want to trim whitespace even if it is the first trivia of the first token. Trim from the beginning of the span to the trivia
|
||||
var startLine = this.getLineNumber(this.previousTokenSpan || this.textSpan());
|
||||
this.trimWhitespaceInLineRange(startLine, this.getLineNumber(currentTokenSpan));
|
||||
}
|
||||
this.previousTokenSpan = currentTokenSpan;
|
||||
if (this.previousTokenParent) {
|
||||
// Make sure to clear the previous parent before assigning a new value to it
|
||||
this.indentationNodeContextPool().releaseNode(this.previousTokenParent, /* recursive */true);
|
||||
}
|
||||
this.previousTokenParent = this.parent().clone(this.indentationNodeContextPool());
|
||||
}
|
||||
}
|
||||
|
||||
position += trivia.fullWidth();
|
||||
}
|
||||
}
|
||||
|
||||
private findCommonParents(parent1: IndentationNodeContext, parent2: IndentationNodeContext): IndentationNodeContext {
|
||||
// TODO: disable debug assert message
|
||||
|
||||
var shallowParent: IndentationNodeContext;
|
||||
var shallowParentDepth: number;
|
||||
var deepParent: IndentationNodeContext;
|
||||
var deepParentDepth: number;
|
||||
|
||||
if (parent1.depth() < parent2.depth()) {
|
||||
shallowParent = parent1;
|
||||
shallowParentDepth = parent1.depth();
|
||||
deepParent = parent2;
|
||||
deepParentDepth = parent2.depth();
|
||||
}
|
||||
else {
|
||||
shallowParent = parent2;
|
||||
shallowParentDepth = parent2.depth();
|
||||
deepParent = parent1;
|
||||
deepParentDepth = parent1.depth();
|
||||
}
|
||||
|
||||
Debug.assert(shallowParentDepth >= 0, "Expected shallowParentDepth >= 0");
|
||||
Debug.assert(deepParentDepth >= 0, "Expected deepParentDepth >= 0");
|
||||
Debug.assert(deepParentDepth >= shallowParentDepth, "Expected deepParentDepth >= shallowParentDepth");
|
||||
|
||||
while (deepParentDepth > shallowParentDepth) {
|
||||
deepParent = <IndentationNodeContext>deepParent.parent();
|
||||
deepParentDepth--;
|
||||
}
|
||||
|
||||
Debug.assert(deepParentDepth === shallowParentDepth, "Expected deepParentDepth === shallowParentDepth");
|
||||
|
||||
while (deepParent.node() && shallowParent.node()) {
|
||||
if (deepParent.node() === shallowParent.node()) {
|
||||
return deepParent;
|
||||
}
|
||||
deepParent = <IndentationNodeContext>deepParent.parent();
|
||||
shallowParent = <IndentationNodeContext>shallowParent.parent();
|
||||
}
|
||||
|
||||
// The root should be the first element in the parent chain, we can not be here unless something wrong
|
||||
// happened along the way
|
||||
throw Errors.invalidOperation();
|
||||
}
|
||||
|
||||
private formatPair(t1: TokenSpan, t1Parent: IndentationNodeContext, t2: TokenSpan, t2Parent: IndentationNodeContext): void {
|
||||
var token1Line = this.getLineNumber(t1);
|
||||
var token2Line = this.getLineNumber(t2);
|
||||
|
||||
// Find common parent
|
||||
var commonParent= this.findCommonParents(t1Parent, t2Parent);
|
||||
|
||||
// Update the context
|
||||
this.formattingContext.updateContext(t1, t1Parent, t2, t2Parent, commonParent);
|
||||
|
||||
// Find rules matching the current context
|
||||
var rule = this.rulesProvider.getRulesMap().GetRule(this.formattingContext);
|
||||
|
||||
if (rule != null) {
|
||||
// Record edits from the rule
|
||||
this.RecordRuleEdits(rule, t1, t2);
|
||||
|
||||
// Handle the case where the next line is moved to be the end of this line.
|
||||
// In this case we don't indent the next line in the next pass.
|
||||
if ((rule.Operation.Action == RuleAction.Space || rule.Operation.Action == RuleAction.Delete) &&
|
||||
token1Line != token2Line) {
|
||||
this.forceSkipIndentingNextToken(t2.start());
|
||||
}
|
||||
|
||||
// Handle the case where token2 is moved to the new line.
|
||||
// In this case we indent token2 in the next pass but we set
|
||||
// sameLineIndent flag to notify the indenter that the indentation is within the line.
|
||||
if (rule.Operation.Action == RuleAction.NewLine && token1Line == token2Line) {
|
||||
this.forceIndentNextToken(t2.start());
|
||||
}
|
||||
}
|
||||
|
||||
// We need to trim trailing whitespace between the tokens if they were on different lines, and no rule was applied to put them on the same line
|
||||
if (token1Line != token2Line && (!rule || (rule.Operation.Action != RuleAction.Delete && rule.Flag != RuleFlags.CanDeleteNewLines))) {
|
||||
this.trimWhitespaceInLineRange(token1Line, token2Line, t1);
|
||||
}
|
||||
}
|
||||
|
||||
private getLineNumber(span: TextSpan): number {
|
||||
return this.snapshot().getLineNumberFromPosition(span.start());
|
||||
}
|
||||
|
||||
private trimWhitespaceInLineRange(startLine: number, endLine: number, token?: TokenSpan): void {
|
||||
for (var lineNumber = startLine; lineNumber < endLine; ++lineNumber) {
|
||||
var line = this.snapshot().getLineFromLineNumber(lineNumber);
|
||||
|
||||
this.trimWhitespace(line, token);
|
||||
}
|
||||
}
|
||||
|
||||
private trimWhitespace(line: ITextSnapshotLine, token?: TokenSpan): void {
|
||||
// Don't remove the trailing spaces inside comments (this includes line comments and block comments)
|
||||
if (token && (token.kind == SyntaxKind.MultiLineCommentTrivia || token.kind == SyntaxKind.SingleLineCommentTrivia) && token.start() <= line.endPosition() && token.end() >= line.endPosition())
|
||||
return;
|
||||
|
||||
var text = line.getText();
|
||||
var index = 0;
|
||||
|
||||
for (index = text.length - 1; index >= 0; --index) {
|
||||
if (!CharacterInfo.isWhitespace(text.charCodeAt(index))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++index;
|
||||
|
||||
if (index < text.length) {
|
||||
this.recordEdit(line.startPosition() + index, line.length() - index, "");
|
||||
}
|
||||
}
|
||||
|
||||
private RecordRuleEdits(rule: Rule, t1: TokenSpan, t2: TokenSpan): void {
|
||||
if (rule.Operation.Action == RuleAction.Ignore) {
|
||||
return;
|
||||
}
|
||||
|
||||
var betweenSpan: TextSpan;
|
||||
|
||||
switch (rule.Operation.Action) {
|
||||
case RuleAction.Delete:
|
||||
{
|
||||
betweenSpan = new TextSpan(t1.end(), t2.start() - t1.end());
|
||||
|
||||
if (betweenSpan.length() > 0) {
|
||||
this.recordEdit(betweenSpan.start(), betweenSpan.length(), "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RuleAction.NewLine:
|
||||
{
|
||||
if (!(rule.Flag == RuleFlags.CanDeleteNewLines || this.getLineNumber(t1) == this.getLineNumber(t2))) {
|
||||
return;
|
||||
}
|
||||
|
||||
betweenSpan = new TextSpan(t1.end(), t2.start() - t1.end());
|
||||
|
||||
var doEdit = false;
|
||||
var betweenText = this.snapshot().getText(betweenSpan);
|
||||
|
||||
var lineFeedLoc = betweenText.indexOf(this.options.newLineCharacter);
|
||||
if (lineFeedLoc < 0) {
|
||||
// no linefeeds, do the edit
|
||||
doEdit = true;
|
||||
}
|
||||
else {
|
||||
// We only require one line feed. If there is another one, do the edit
|
||||
lineFeedLoc = betweenText.indexOf(this.options.newLineCharacter, lineFeedLoc + 1);
|
||||
if (lineFeedLoc >= 0) {
|
||||
doEdit = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (doEdit) {
|
||||
this.recordEdit(betweenSpan.start(), betweenSpan.length(), this.options.newLineCharacter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RuleAction.Space:
|
||||
{
|
||||
if (!(rule.Flag == RuleFlags.CanDeleteNewLines || this.getLineNumber(t1) == this.getLineNumber(t2))) {
|
||||
return;
|
||||
}
|
||||
|
||||
betweenSpan = new TextSpan(t1.end(), t2.start() - t1.end());
|
||||
|
||||
if (betweenSpan.length() > 1 || this.snapshot().getText(betweenSpan) != " ") {
|
||||
this.recordEdit(betweenSpan.start(), betweenSpan.length(), " ");
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,48 +13,48 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
/// <reference path="formatting.ts"/>
|
||||
/// <reference path="references.ts"/>
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
module ts.formatting {
|
||||
export class FormattingContext {
|
||||
public currentTokenSpan: TokenSpan = null;
|
||||
public nextTokenSpan: TokenSpan = null;
|
||||
public contextNode: IndentationNodeContext = null;
|
||||
public currentTokenParent: IndentationNodeContext = null;
|
||||
public nextTokenParent: IndentationNodeContext = null;
|
||||
public currentTokenSpan: TextRangeWithKind;
|
||||
public nextTokenSpan: TextRangeWithKind;
|
||||
public contextNode: Node;
|
||||
public currentTokenParent: Node;
|
||||
public nextTokenParent: Node;
|
||||
|
||||
private contextNodeAllOnSameLine: boolean = null;
|
||||
private nextNodeAllOnSameLine: boolean = null;
|
||||
private tokensAreOnSameLine: boolean = null;
|
||||
private contextNodeBlockIsOnOneLine: boolean = null;
|
||||
private nextNodeBlockIsOnOneLine: boolean = null;
|
||||
private contextNodeAllOnSameLine: boolean;
|
||||
private nextNodeAllOnSameLine: boolean;
|
||||
private tokensAreOnSameLine: boolean;
|
||||
private contextNodeBlockIsOnOneLine: boolean;
|
||||
private nextNodeBlockIsOnOneLine: boolean;
|
||||
|
||||
constructor(private snapshot: ITextSnapshot, public formattingRequestKind: FormattingRequestKind) {
|
||||
Debug.assert(this.snapshot != null, "snapshot is null");
|
||||
constructor(private sourceFile: SourceFile, public formattingRequestKind: FormattingRequestKind) {
|
||||
}
|
||||
|
||||
public updateContext(currentTokenSpan: TokenSpan, currentTokenParent: IndentationNodeContext, nextTokenSpan: TokenSpan, nextTokenParent: IndentationNodeContext, commonParent: IndentationNodeContext) {
|
||||
Debug.assert(currentTokenSpan != null, "currentTokenSpan is null");
|
||||
Debug.assert(currentTokenParent != null, "currentTokenParent is null");
|
||||
Debug.assert(nextTokenSpan != null, "nextTokenSpan is null");
|
||||
Debug.assert(nextTokenParent != null, "nextTokenParent is null");
|
||||
Debug.assert(commonParent != null, "commonParent is null");
|
||||
public updateContext(currentRange: TextRangeWithKind, currentTokenParent: Node, nextRange: TextRangeWithKind, nextTokenParent: Node, commonParent: Node) {
|
||||
Debug.assert(currentRange !== undefined, "currentTokenSpan is null");
|
||||
Debug.assert(currentTokenParent !== undefined, "currentTokenParent is null");
|
||||
Debug.assert(nextRange !== undefined, "nextTokenSpan is null");
|
||||
Debug.assert(nextTokenParent !== undefined, "nextTokenParent is null");
|
||||
Debug.assert(commonParent !== undefined, "commonParent is null");
|
||||
|
||||
this.currentTokenSpan = currentTokenSpan;
|
||||
this.currentTokenSpan = currentRange;
|
||||
this.currentTokenParent = currentTokenParent;
|
||||
this.nextTokenSpan = nextTokenSpan;
|
||||
this.nextTokenSpan = nextRange;
|
||||
this.nextTokenParent = nextTokenParent;
|
||||
this.contextNode = commonParent;
|
||||
|
||||
this.contextNodeAllOnSameLine = null;
|
||||
this.nextNodeAllOnSameLine = null;
|
||||
this.tokensAreOnSameLine = null;
|
||||
this.contextNodeBlockIsOnOneLine = null;
|
||||
this.nextNodeBlockIsOnOneLine = null;
|
||||
// drop cached results
|
||||
this.contextNodeAllOnSameLine = undefined;
|
||||
this.nextNodeAllOnSameLine = undefined;
|
||||
this.tokensAreOnSameLine = undefined;
|
||||
this.contextNodeBlockIsOnOneLine = undefined;
|
||||
this.nextNodeBlockIsOnOneLine = undefined;
|
||||
}
|
||||
|
||||
public ContextNodeAllOnSameLine(): boolean {
|
||||
if (this.contextNodeAllOnSameLine === null) {
|
||||
if (this.contextNodeAllOnSameLine === undefined) {
|
||||
this.contextNodeAllOnSameLine = this.NodeIsOnOneLine(this.contextNode);
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ module TypeScript.Services.Formatting {
|
|||
}
|
||||
|
||||
public NextNodeAllOnSameLine(): boolean {
|
||||
if (this.nextNodeAllOnSameLine === null) {
|
||||
if (this.nextNodeAllOnSameLine === undefined) {
|
||||
this.nextNodeAllOnSameLine = this.NodeIsOnOneLine(this.nextTokenParent);
|
||||
}
|
||||
|
||||
|
@ -70,10 +70,9 @@ module TypeScript.Services.Formatting {
|
|||
}
|
||||
|
||||
public TokensAreOnSameLine(): boolean {
|
||||
if (this.tokensAreOnSameLine === null) {
|
||||
var startLine = this.snapshot.getLineNumberFromPosition(this.currentTokenSpan.start());
|
||||
var endLine = this.snapshot.getLineNumberFromPosition(this.nextTokenSpan.start());
|
||||
|
||||
if (this.tokensAreOnSameLine === undefined) {
|
||||
var startLine = this.sourceFile.getLineAndCharacterFromPosition(this.currentTokenSpan.pos).line;
|
||||
var endLine = this.sourceFile.getLineAndCharacterFromPosition(this.nextTokenSpan.pos).line;
|
||||
this.tokensAreOnSameLine = (startLine == endLine);
|
||||
}
|
||||
|
||||
|
@ -81,7 +80,7 @@ module TypeScript.Services.Formatting {
|
|||
}
|
||||
|
||||
public ContextNodeBlockIsOnOneLine() {
|
||||
if (this.contextNodeBlockIsOnOneLine === null) {
|
||||
if (this.contextNodeBlockIsOnOneLine === undefined) {
|
||||
this.contextNodeBlockIsOnOneLine = this.BlockIsOnOneLine(this.contextNode);
|
||||
}
|
||||
|
||||
|
@ -89,28 +88,28 @@ module TypeScript.Services.Formatting {
|
|||
}
|
||||
|
||||
public NextNodeBlockIsOnOneLine() {
|
||||
if (this.nextNodeBlockIsOnOneLine === null) {
|
||||
if (this.nextNodeBlockIsOnOneLine === undefined) {
|
||||
this.nextNodeBlockIsOnOneLine = this.BlockIsOnOneLine(this.nextTokenParent);
|
||||
}
|
||||
|
||||
return this.nextNodeBlockIsOnOneLine;
|
||||
}
|
||||
|
||||
public NodeIsOnOneLine(node: IndentationNodeContext): boolean {
|
||||
var startLine = this.snapshot.getLineNumberFromPosition(node.start());
|
||||
var endLine = this.snapshot.getLineNumberFromPosition(node.end());
|
||||
|
||||
private NodeIsOnOneLine(node: Node): boolean {
|
||||
var startLine = this.sourceFile.getLineAndCharacterFromPosition(node.getStart(this.sourceFile)).line;
|
||||
var endLine = this.sourceFile.getLineAndCharacterFromPosition(node.getEnd()).line;
|
||||
return startLine == endLine;
|
||||
}
|
||||
|
||||
// Now we know we have a block (or a fake block represented by some other kind of node with an open and close brace as children).
|
||||
// IMPORTANT!!! This relies on the invariant that IsBlockContext must return true ONLY for nodes with open and close braces as immediate children
|
||||
public BlockIsOnOneLine(node: IndentationNodeContext): boolean {
|
||||
var block = <BlockSyntax>node.node();
|
||||
|
||||
// Now check if they are on the same line
|
||||
return this.snapshot.getLineNumberFromPosition(fullEnd(block.openBraceToken)) ===
|
||||
this.snapshot.getLineNumberFromPosition(start(block.closeBraceToken));
|
||||
private BlockIsOnOneLine(node: Node): boolean {
|
||||
var openBrace = findChildOfKind(node, SyntaxKind.OpenBraceToken, this.sourceFile);
|
||||
var closeBrace = findChildOfKind(node, SyntaxKind.CloseBraceToken, this.sourceFile);
|
||||
if (openBrace && closeBrace) {
|
||||
var startLine = this.sourceFile.getLineAndCharacterFromPosition(openBrace.getEnd()).line;
|
||||
var endLine = this.sourceFile.getLineAndCharacterFromPosition(closeBrace.getStart(this.sourceFile)).line;
|
||||
return startLine === endLine;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
/// <reference path="formatting.ts"/>
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export class FormattingManager {
|
||||
private options: FormattingOptions;
|
||||
|
||||
constructor(private syntaxTree: SyntaxTree,
|
||||
private snapshot: ITextSnapshot,
|
||||
private rulesProvider: RulesProvider,
|
||||
editorOptions: ts.EditorOptions) {
|
||||
//
|
||||
// TODO: convert to use FormattingOptions instead of EditorOptions
|
||||
this.options = new FormattingOptions(!editorOptions.ConvertTabsToSpaces, editorOptions.TabSize, editorOptions.IndentSize, editorOptions.NewLineCharacter)
|
||||
}
|
||||
|
||||
public formatSelection(minChar: number, limChar: number): ts.TextChange[] {
|
||||
var span = TextSpan.fromBounds(minChar, limChar);
|
||||
return this.formatSpan(span, FormattingRequestKind.FormatSelection);
|
||||
}
|
||||
|
||||
public formatDocument(): ts.TextChange[] {
|
||||
var span = TextSpan.fromBounds(0, this.snapshot.getLength());
|
||||
return this.formatSpan(span, FormattingRequestKind.FormatDocument);
|
||||
}
|
||||
|
||||
public formatOnSemicolon(caretPosition: number): ts.TextChange[] {
|
||||
var sourceUnit = this.syntaxTree.sourceUnit();
|
||||
var semicolonPositionedToken = findToken(sourceUnit, caretPosition - 1);
|
||||
|
||||
if (semicolonPositionedToken.kind === SyntaxKind.SemicolonToken) {
|
||||
// Find the outer most parent that this semicolon terminates
|
||||
var current: ISyntaxElement = semicolonPositionedToken;
|
||||
while (current.parent !== null &&
|
||||
fullEnd(current.parent) === fullEnd(semicolonPositionedToken) &&
|
||||
current.parent.kind !== SyntaxKind.List) {
|
||||
current = current.parent;
|
||||
}
|
||||
|
||||
// Compute the span
|
||||
var span = new TextSpan(fullStart(current), fullWidth(current));
|
||||
|
||||
// Format the span
|
||||
return this.formatSpan(span, FormattingRequestKind.FormatOnSemicolon);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public formatOnClosingCurlyBrace(caretPosition: number): ts.TextChange[] {
|
||||
var sourceUnit = this.syntaxTree.sourceUnit();
|
||||
var closeBracePositionedToken = findToken(sourceUnit, caretPosition - 1);
|
||||
|
||||
if (closeBracePositionedToken.kind === SyntaxKind.CloseBraceToken) {
|
||||
// Find the outer most parent that this closing brace terminates
|
||||
var current: ISyntaxElement = closeBracePositionedToken;
|
||||
while (current.parent !== null &&
|
||||
fullEnd(current.parent) === fullEnd(closeBracePositionedToken) &&
|
||||
current.parent.kind !== SyntaxKind.List) {
|
||||
current = current.parent;
|
||||
}
|
||||
|
||||
// Compute the span
|
||||
var span = new TextSpan(fullStart(current), fullWidth(current));
|
||||
|
||||
// Format the span
|
||||
return this.formatSpan(span, FormattingRequestKind.FormatOnClosingCurlyBrace);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public formatOnEnter(caretPosition: number): ts.TextChange[] {
|
||||
var lineNumber = this.snapshot.getLineNumberFromPosition(caretPosition);
|
||||
|
||||
if (lineNumber > 0) {
|
||||
// Format both lines
|
||||
var prevLine = this.snapshot.getLineFromLineNumber(lineNumber - 1);
|
||||
var currentLine = this.snapshot.getLineFromLineNumber(lineNumber);
|
||||
var span = TextSpan.fromBounds(prevLine.startPosition(), currentLine.endPosition());
|
||||
|
||||
// Format the span
|
||||
return this.formatSpan(span, FormattingRequestKind.FormatOnEnter);
|
||||
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private formatSpan(span: TextSpan, formattingRequestKind: FormattingRequestKind): ts.TextChange[] {
|
||||
// Always format from the beginning of the line
|
||||
var startLine = this.snapshot.getLineFromPosition(span.start());
|
||||
span = TextSpan.fromBounds(startLine.startPosition(), span.end());
|
||||
|
||||
var result: ts.TextChange[] = [];
|
||||
|
||||
var formattingEdits = Formatter.getEdits(span, this.syntaxTree.sourceUnit(), this.options, true, this.snapshot, this.rulesProvider, formattingRequestKind);
|
||||
|
||||
//
|
||||
// TODO: Change the ILanguageService interface to return TextEditInfo (with start, and length) instead of TextEdit (with minChar and limChar)
|
||||
formattingEdits.forEach(item => {
|
||||
result.push({
|
||||
span: new TextSpan(item.position, item.length),
|
||||
newText: item.replaceWith
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,15 +13,14 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
/// <reference path="formatting.ts"/>
|
||||
/// <reference path="references.ts"/>
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export enum FormattingRequestKind {
|
||||
module ts.formatting {
|
||||
export const enum FormattingRequestKind {
|
||||
FormatDocument,
|
||||
FormatSelection,
|
||||
FormatOnEnter,
|
||||
FormatOnSemicolon,
|
||||
FormatOnClosingCurlyBrace,
|
||||
FormatOnPaste
|
||||
FormatOnClosingCurlyBrace
|
||||
}
|
||||
}
|
210
src/services/formatting/formattingScanner.ts
Normal file
210
src/services/formatting/formattingScanner.ts
Normal file
|
@ -0,0 +1,210 @@
|
|||
/// <reference path="..\..\compiler\scanner.ts"/>
|
||||
|
||||
module ts.formatting {
|
||||
var scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false);
|
||||
|
||||
export interface FormattingScanner {
|
||||
advance(): void;
|
||||
isOnToken(): boolean;
|
||||
readTokenInfo(n: Node): TokenInfo;
|
||||
lastTrailingTriviaWasNewLine(): boolean;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
const enum ScanAction{
|
||||
Scan,
|
||||
RescanGreaterThanToken,
|
||||
RescanSlashToken
|
||||
}
|
||||
|
||||
export function getFormattingScanner(sourceFile: SourceFile, startPos: number, endPos: number): FormattingScanner {
|
||||
|
||||
scanner.setText(sourceFile.text);
|
||||
scanner.setTextPos(startPos);
|
||||
|
||||
var wasNewLine: boolean = true;
|
||||
var leadingTrivia: TextRangeWithKind[];
|
||||
var trailingTrivia: TextRangeWithKind[];
|
||||
|
||||
var savedPos: number;
|
||||
var lastScanAction: ScanAction;
|
||||
var lastTokenInfo: TokenInfo;
|
||||
|
||||
return {
|
||||
advance: advance,
|
||||
readTokenInfo: readTokenInfo,
|
||||
isOnToken: isOnToken,
|
||||
lastTrailingTriviaWasNewLine: () => wasNewLine,
|
||||
close: () => {
|
||||
lastTokenInfo = undefined;
|
||||
scanner.setText(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
function advance(): void {
|
||||
lastTokenInfo = undefined;
|
||||
var isStarted = scanner.getStartPos() !== startPos;
|
||||
|
||||
if (isStarted) {
|
||||
if (trailingTrivia) {
|
||||
Debug.assert(trailingTrivia.length !== 0);
|
||||
wasNewLine = trailingTrivia[trailingTrivia.length - 1].kind === SyntaxKind.NewLineTrivia;
|
||||
}
|
||||
else {
|
||||
wasNewLine = false;
|
||||
}
|
||||
}
|
||||
|
||||
leadingTrivia = undefined;
|
||||
trailingTrivia = undefined;
|
||||
|
||||
if (!isStarted) {
|
||||
scanner.scan();
|
||||
}
|
||||
|
||||
var t: SyntaxKind;
|
||||
var pos = scanner.getStartPos();
|
||||
|
||||
// Read leading trivia and token
|
||||
while (pos < endPos) {
|
||||
var t = scanner.getToken();
|
||||
if (!isTrivia(t)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// consume leading trivia
|
||||
scanner.scan();
|
||||
var item = {
|
||||
pos: pos,
|
||||
end: scanner.getStartPos(),
|
||||
kind: t
|
||||
}
|
||||
|
||||
pos = scanner.getStartPos();
|
||||
|
||||
if (!leadingTrivia) {
|
||||
leadingTrivia = [];
|
||||
}
|
||||
leadingTrivia.push(item);
|
||||
}
|
||||
|
||||
savedPos = scanner.getStartPos();
|
||||
}
|
||||
|
||||
function shouldRescanGreaterThanToken(container: Node): boolean {
|
||||
if (container.kind !== SyntaxKind.BinaryExpression) {
|
||||
return false;
|
||||
}
|
||||
switch ((<BinaryExpression>container).operator) {
|
||||
case SyntaxKind.GreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanToken:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function shouldRescanSlashToken(container: Node): boolean {
|
||||
return container.kind === SyntaxKind.RegularExpressionLiteral;
|
||||
}
|
||||
|
||||
function startsWithSlashToken(t: SyntaxKind): boolean {
|
||||
return t === SyntaxKind.SlashToken || t === SyntaxKind.SlashEqualsToken;
|
||||
}
|
||||
|
||||
function readTokenInfo(n: Node): TokenInfo {
|
||||
if (!isOnToken()) {
|
||||
// scanner is not on the token (either advance was not called yet or scanner is already past the end position)
|
||||
return {
|
||||
leadingTrivia: leadingTrivia,
|
||||
trailingTrivia: undefined,
|
||||
token: undefined
|
||||
};
|
||||
}
|
||||
|
||||
// normally scanner returns the smallest available token
|
||||
// check the kind of context node to determine if scanner should have more greedy behavior and consume more text.
|
||||
var expectedScanAction =
|
||||
shouldRescanGreaterThanToken(n)
|
||||
? ScanAction.RescanGreaterThanToken
|
||||
: shouldRescanSlashToken(n)
|
||||
? ScanAction.RescanSlashToken
|
||||
: ScanAction.Scan
|
||||
|
||||
if (lastTokenInfo && expectedScanAction === lastScanAction) {
|
||||
// readTokenInfo was called before with the same expected scan action.
|
||||
// No need to re-scan text, return existing 'lastTokenInfo'
|
||||
return lastTokenInfo;
|
||||
}
|
||||
|
||||
if (scanner.getStartPos() !== savedPos) {
|
||||
Debug.assert(lastTokenInfo !== undefined);
|
||||
// readTokenInfo was called before but scan action differs - rescan text
|
||||
scanner.setTextPos(savedPos);
|
||||
scanner.scan();
|
||||
}
|
||||
|
||||
var currentToken = scanner.getToken();
|
||||
|
||||
if (expectedScanAction === ScanAction.RescanGreaterThanToken && currentToken === SyntaxKind.GreaterThanToken) {
|
||||
currentToken = scanner.reScanGreaterToken();
|
||||
Debug.assert((<BinaryExpression>n).operator === currentToken);
|
||||
lastScanAction = ScanAction.RescanGreaterThanToken;
|
||||
}
|
||||
else if (expectedScanAction === ScanAction.RescanSlashToken && startsWithSlashToken(currentToken)) {
|
||||
currentToken = scanner.reScanSlashToken();
|
||||
Debug.assert(n.kind === currentToken);
|
||||
lastScanAction = ScanAction.RescanSlashToken;
|
||||
}
|
||||
else {
|
||||
lastScanAction = ScanAction.Scan;
|
||||
}
|
||||
|
||||
var token: TextRangeWithKind = {
|
||||
pos: scanner.getStartPos(),
|
||||
end: scanner.getTextPos(),
|
||||
kind: currentToken
|
||||
}
|
||||
|
||||
// consume trailing trivia
|
||||
while(scanner.getStartPos() < endPos) {
|
||||
currentToken = scanner.scan();
|
||||
if (!isTrivia(currentToken)) {
|
||||
break;
|
||||
}
|
||||
var trivia = {
|
||||
pos: scanner.getStartPos(),
|
||||
end: scanner.getTextPos(),
|
||||
kind: currentToken
|
||||
};
|
||||
|
||||
if (!trailingTrivia) {
|
||||
trailingTrivia = [];
|
||||
}
|
||||
|
||||
trailingTrivia.push(trivia);
|
||||
|
||||
if (currentToken === SyntaxKind.NewLineTrivia) {
|
||||
// move past new line
|
||||
scanner.scan();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lastTokenInfo = {
|
||||
leadingTrivia: leadingTrivia,
|
||||
trailingTrivia: trailingTrivia,
|
||||
token: token
|
||||
}
|
||||
}
|
||||
|
||||
function isOnToken(): boolean {
|
||||
var current = (lastTokenInfo && lastTokenInfo.token.kind) || scanner.getToken();
|
||||
var startPos = (lastTokenInfo && lastTokenInfo.token.pos) || scanner.getStartPos();
|
||||
return startPos < endPos && current !== SyntaxKind.EndOfFileToken && !isTrivia(current);
|
||||
}
|
||||
}
|
||||
}
|
54
src/services/formatting/indentation.ts
Normal file
54
src/services/formatting/indentation.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
module ts.formatting {
|
||||
|
||||
var internedTabsIndentation: string[];
|
||||
var internedSpacesIndentation: string[];
|
||||
|
||||
export function getIndentationString(indentation: number, options: FormatCodeOptions): string {
|
||||
if (!options.ConvertTabsToSpaces) {
|
||||
var tabs = Math.floor(indentation / options.TabSize);
|
||||
var spaces = indentation - tabs * options.TabSize;
|
||||
|
||||
var tabString: string;
|
||||
if (!internedTabsIndentation) {
|
||||
internedTabsIndentation = [];
|
||||
}
|
||||
|
||||
if (internedTabsIndentation[tabs] === undefined) {
|
||||
internedTabsIndentation[tabs] = tabString = repeat('\t', tabs);
|
||||
}
|
||||
else {
|
||||
tabString = internedTabsIndentation[tabs];
|
||||
}
|
||||
|
||||
return spaces ? tabString + repeat(" ", spaces) : tabString;
|
||||
}
|
||||
else {
|
||||
var spacesString: string;
|
||||
var quotient = Math.floor(indentation / options.IndentSize);
|
||||
var remainder = indentation % options.IndentSize;
|
||||
if (!internedSpacesIndentation) {
|
||||
internedSpacesIndentation = [];
|
||||
}
|
||||
|
||||
if (internedSpacesIndentation[quotient] === undefined) {
|
||||
spacesString = repeat(" ", options.IndentSize * quotient);
|
||||
internedSpacesIndentation[quotient] = spacesString;
|
||||
}
|
||||
else {
|
||||
spacesString = internedSpacesIndentation[quotient];
|
||||
}
|
||||
|
||||
|
||||
return remainder ? spacesString + repeat(" ", remainder) : spacesString;
|
||||
}
|
||||
|
||||
function repeat(value: string, count: number): string {
|
||||
var s = "";
|
||||
for (var i = 0; i < count; ++i) {
|
||||
s += value;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export class IndentationNodeContext {
|
||||
private _node: ISyntaxNode;
|
||||
private _parent: IndentationNodeContext;
|
||||
private _fullStart: number;
|
||||
private _indentationAmount: number;
|
||||
private _childIndentationAmountDelta: number;
|
||||
private _depth: number;
|
||||
private _hasSkippedOrMissingTokenChild: boolean;
|
||||
|
||||
constructor(parent: IndentationNodeContext, node: ISyntaxNode, fullStart: number, indentationAmount: number, childIndentationAmountDelta: number) {
|
||||
this.update(parent, node, fullStart, indentationAmount, childIndentationAmountDelta);
|
||||
}
|
||||
|
||||
public parent(): IndentationNodeContext {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
public node(): ISyntaxNode {
|
||||
return this._node;
|
||||
}
|
||||
|
||||
public fullStart(): number {
|
||||
return this._fullStart;
|
||||
}
|
||||
|
||||
public fullWidth(): number {
|
||||
return fullWidth(this._node);
|
||||
}
|
||||
|
||||
public start(): number {
|
||||
return this._fullStart + leadingTriviaWidth(this._node);
|
||||
}
|
||||
|
||||
public end(): number {
|
||||
return this._fullStart + leadingTriviaWidth(this._node) + width(this._node);
|
||||
}
|
||||
|
||||
public indentationAmount(): number {
|
||||
return this._indentationAmount;
|
||||
}
|
||||
|
||||
public childIndentationAmountDelta(): number {
|
||||
return this._childIndentationAmountDelta;
|
||||
}
|
||||
|
||||
public depth(): number {
|
||||
return this._depth;
|
||||
}
|
||||
|
||||
public kind(): SyntaxKind {
|
||||
return this._node.kind;
|
||||
}
|
||||
|
||||
public hasSkippedOrMissingTokenChild(): boolean {
|
||||
if (this._hasSkippedOrMissingTokenChild === null) {
|
||||
this._hasSkippedOrMissingTokenChild = Syntax.nodeHasSkippedOrMissingTokens(this._node);
|
||||
}
|
||||
return this._hasSkippedOrMissingTokenChild;
|
||||
}
|
||||
|
||||
public clone(pool: IndentationNodeContextPool): IndentationNodeContext {
|
||||
var parent: IndentationNodeContext = null;
|
||||
if (this._parent) {
|
||||
parent = this._parent.clone(pool);
|
||||
}
|
||||
return pool.getNode(parent, this._node, this._fullStart, this._indentationAmount, this._childIndentationAmountDelta);
|
||||
}
|
||||
|
||||
public update(parent: IndentationNodeContext, node: ISyntaxNode, fullStart: number, indentationAmount: number, childIndentationAmountDelta: number) {
|
||||
this._parent = parent;
|
||||
this._node = node;
|
||||
this._fullStart = fullStart;
|
||||
this._indentationAmount = indentationAmount;
|
||||
this._childIndentationAmountDelta = childIndentationAmountDelta;
|
||||
this._hasSkippedOrMissingTokenChild = null;
|
||||
|
||||
if (parent) {
|
||||
this._depth = parent.depth() + 1;
|
||||
}
|
||||
else {
|
||||
this._depth = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export class IndentationNodeContextPool {
|
||||
private nodes: IndentationNodeContext[] = [];
|
||||
|
||||
public getNode(parent: IndentationNodeContext, node: ISyntaxNode, fullStart: number, indentationLevel: number, childIndentationLevelDelta: number): IndentationNodeContext {
|
||||
if (this.nodes.length > 0) {
|
||||
var cachedNode = this.nodes.pop();
|
||||
cachedNode.update(parent, node, fullStart, indentationLevel, childIndentationLevelDelta);
|
||||
return cachedNode;
|
||||
}
|
||||
|
||||
return new IndentationNodeContext(parent, node, fullStart, indentationLevel, childIndentationLevelDelta);
|
||||
}
|
||||
|
||||
public releaseNode(node: IndentationNodeContext, recursive: boolean = false): void {
|
||||
this.nodes.push(node);
|
||||
|
||||
if (recursive) {
|
||||
var parent = node.parent();
|
||||
if (parent) {
|
||||
this.releaseNode(parent, recursive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,371 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export class IndentationTrackingWalker {
|
||||
private _position: number = 0;
|
||||
private _parent: IndentationNodeContext = null;
|
||||
private _textSpan: TextSpan;
|
||||
private _snapshot: ITextSnapshot;
|
||||
private _lastTriviaWasNewLine: boolean;
|
||||
private _indentationNodeContextPool: IndentationNodeContextPool;
|
||||
private _text: ISimpleText;
|
||||
|
||||
constructor(textSpan: TextSpan, sourceUnit: SourceUnitSyntax, snapshot: ITextSnapshot, indentFirstToken: boolean, public options: FormattingOptions) {
|
||||
// Create a pool object to manage context nodes while walking the tree
|
||||
this._indentationNodeContextPool = new IndentationNodeContextPool();
|
||||
|
||||
this._textSpan = textSpan;
|
||||
this._text = sourceUnit.syntaxTree.text;
|
||||
this._snapshot = snapshot;
|
||||
this._parent = this._indentationNodeContextPool.getNode(null, sourceUnit, 0, 0, 0);
|
||||
|
||||
// Is the first token in the span at the start of a new line.
|
||||
this._lastTriviaWasNewLine = indentFirstToken;
|
||||
}
|
||||
|
||||
public position(): number {
|
||||
return this._position;
|
||||
}
|
||||
|
||||
public parent(): IndentationNodeContext {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
public textSpan(): TextSpan {
|
||||
return this._textSpan;
|
||||
}
|
||||
|
||||
public snapshot(): ITextSnapshot {
|
||||
return this._snapshot;
|
||||
}
|
||||
|
||||
public indentationNodeContextPool(): IndentationNodeContextPool {
|
||||
return this._indentationNodeContextPool;
|
||||
}
|
||||
|
||||
public forceIndentNextToken(tokenStart: number): void {
|
||||
this._lastTriviaWasNewLine = true;
|
||||
this.forceRecomputeIndentationOfParent(tokenStart, true);
|
||||
}
|
||||
|
||||
public forceSkipIndentingNextToken(tokenStart: number): void {
|
||||
this._lastTriviaWasNewLine = false;
|
||||
this.forceRecomputeIndentationOfParent(tokenStart, false);
|
||||
}
|
||||
|
||||
public indentToken(token: ISyntaxToken, indentationAmount: number, commentIndentationAmount: number): void {
|
||||
throw Errors.abstract();
|
||||
}
|
||||
|
||||
public visitTokenInSpan(token: ISyntaxToken): void {
|
||||
if (this._lastTriviaWasNewLine) {
|
||||
// Compute the indentation level at the current token
|
||||
var indentationAmount = this.getTokenIndentationAmount(token);
|
||||
var commentIndentationAmount = this.getCommentIndentationAmount(token);
|
||||
|
||||
// Process the token
|
||||
this.indentToken(token, indentationAmount, commentIndentationAmount);
|
||||
}
|
||||
}
|
||||
|
||||
public visitToken(token: ISyntaxToken): void {
|
||||
var tokenSpan = new TextSpan(this._position, token.fullWidth());
|
||||
|
||||
if (tokenSpan.intersectsWithTextSpan(this._textSpan)) {
|
||||
this.visitTokenInSpan(token);
|
||||
|
||||
// Only track new lines on tokens within the range. Make sure to check that the last trivia is a newline, and not just one of the trivia
|
||||
var _nextToken = nextToken(token);
|
||||
if (_nextToken && _nextToken.hasLeadingTrivia()) {
|
||||
var trivia = _nextToken.leadingTrivia();
|
||||
this._lastTriviaWasNewLine = trivia.hasNewLine();
|
||||
}
|
||||
else {
|
||||
this._lastTriviaWasNewLine = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the position
|
||||
this._position += token.fullWidth();
|
||||
}
|
||||
|
||||
public walk(element: ISyntaxElement) {
|
||||
if (element) {
|
||||
if (isToken(element)) {
|
||||
this.visitToken(<ISyntaxToken>element);
|
||||
}
|
||||
else if (element.kind === SyntaxKind.List) {
|
||||
for (var i = 0, n = childCount(element); i < n; i++) {
|
||||
this.walk(childAt(element, i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.visitNode(<ISyntaxNode>element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private visitNode(node: ISyntaxNode): void {
|
||||
var nodeSpan = new TextSpan(this._position, fullWidth(node));
|
||||
|
||||
if (nodeSpan.intersectsWithTextSpan(this._textSpan)) {
|
||||
// Update indentation level
|
||||
var indentation = this.getNodeIndentation(node);
|
||||
|
||||
// Update the parent
|
||||
var currentParent = this._parent;
|
||||
this._parent = this._indentationNodeContextPool.getNode(currentParent, node, this._position, indentation.indentationAmount, indentation.indentationAmountDelta);
|
||||
|
||||
// Visit node
|
||||
for (var i = 0, n = childCount(node); i < n; i++) {
|
||||
this.walk(childAt(node, i));
|
||||
}
|
||||
|
||||
// Reset state
|
||||
this._indentationNodeContextPool.releaseNode(this._parent);
|
||||
this._parent = currentParent;
|
||||
}
|
||||
else {
|
||||
// We're skipping the node, so update our position accordingly.
|
||||
this._position += fullWidth(node);
|
||||
}
|
||||
}
|
||||
|
||||
private getTokenIndentationAmount(token: ISyntaxToken): number {
|
||||
// If this is the first token of a node, it should follow the node indentation and not the child indentation;
|
||||
// (e.g.class in a class declaration or module in module declariotion).
|
||||
// Open and close braces should follow the indentation of thier parent as well(e.g.
|
||||
// class {
|
||||
// }
|
||||
// Also in a do-while statement, the while should be indented like the parent.
|
||||
if (firstToken(this._parent.node()) === token ||
|
||||
token.kind === SyntaxKind.OpenBraceToken || token.kind === SyntaxKind.CloseBraceToken ||
|
||||
token.kind === SyntaxKind.OpenBracketToken || token.kind === SyntaxKind.CloseBracketToken ||
|
||||
(token.kind === SyntaxKind.WhileKeyword && this._parent.node().kind == SyntaxKind.DoStatement)) {
|
||||
return this._parent.indentationAmount();
|
||||
}
|
||||
|
||||
return (this._parent.indentationAmount() + this._parent.childIndentationAmountDelta());
|
||||
}
|
||||
|
||||
private getCommentIndentationAmount(token: ISyntaxToken): number {
|
||||
// If this is token terminating an indentation scope, leading comments should be indented to follow the children
|
||||
// indentation level and not the node
|
||||
|
||||
if (token.kind === SyntaxKind.CloseBraceToken || token.kind === SyntaxKind.CloseBracketToken) {
|
||||
return (this._parent.indentationAmount() + this._parent.childIndentationAmountDelta());
|
||||
}
|
||||
return this._parent.indentationAmount();
|
||||
}
|
||||
|
||||
private getNodeIndentation(node: ISyntaxNode, newLineInsertedByFormatting?: boolean): { indentationAmount: number; indentationAmountDelta: number; } {
|
||||
var parent = this._parent;
|
||||
|
||||
// We need to get the parent's indentation, which could be one of 2 things. If first token of the parent is in the span, use the parent's computed indentation.
|
||||
// If the parent was outside the span, use the actual indentation of the parent.
|
||||
var parentIndentationAmount: number;
|
||||
if (this._textSpan.containsPosition(parent.start())) {
|
||||
parentIndentationAmount = parent.indentationAmount();
|
||||
}
|
||||
else {
|
||||
if (parent.kind() === SyntaxKind.Block && !this.shouldIndentBlockInParent(this._parent.parent())) {
|
||||
// Blocks preserve the indentation of their containing node (unless they're a
|
||||
// standalone block in a list). i.e. if you have:
|
||||
//
|
||||
// function foo(
|
||||
// a: number) {
|
||||
//
|
||||
// Then we expect the indentation of the block to be tied to the function, not to
|
||||
// the line that the block is defined on. If we were to do the latter, then the
|
||||
// indentation would be here:
|
||||
//
|
||||
// function foo(
|
||||
// a: number) {
|
||||
// |
|
||||
//
|
||||
// Instead of:
|
||||
//
|
||||
// function foo(
|
||||
// a: number) {
|
||||
// |
|
||||
parent = this._parent.parent();
|
||||
}
|
||||
|
||||
var line = this._snapshot.getLineFromPosition(parent.start()).getText();
|
||||
var firstNonWhiteSpacePosition = Indentation.firstNonWhitespacePosition(line);
|
||||
parentIndentationAmount = Indentation.columnForPositionInString(line, firstNonWhiteSpacePosition, this.options);
|
||||
}
|
||||
var parentIndentationAmountDelta = parent.childIndentationAmountDelta();
|
||||
|
||||
// The indentation level of the node
|
||||
var indentationAmount: number;
|
||||
|
||||
// The delta it adds to its children.
|
||||
var indentationAmountDelta: number;
|
||||
var parentNode = parent.node();
|
||||
|
||||
switch (node.kind) {
|
||||
default:
|
||||
// General case
|
||||
// This node should follow the child indentation set by its parent
|
||||
// This node does not introduce any new indentation scope, indent any decendants of this node (tokens or child nodes)
|
||||
// using the same indentation level
|
||||
indentationAmount = (parentIndentationAmount + parentIndentationAmountDelta);
|
||||
indentationAmountDelta = 0;
|
||||
break;
|
||||
|
||||
// Statements introducing {}
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
case SyntaxKind.ObjectType:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.ConstructorDeclaration:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.MemberFunctionDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.IndexMemberDeclaration:
|
||||
case SyntaxKind.CatchClause:
|
||||
// Statements introducing []
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
case SyntaxKind.ArrayType:
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
case SyntaxKind.IndexSignature:
|
||||
// Other statements
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WithStatement:
|
||||
case SyntaxKind.CaseSwitchClause:
|
||||
case SyntaxKind.DefaultSwitchClause:
|
||||
case SyntaxKind.ReturnStatement:
|
||||
case SyntaxKind.ThrowStatement:
|
||||
case SyntaxKind.SimpleArrowFunctionExpression:
|
||||
case SyntaxKind.ParenthesizedArrowFunctionExpression:
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.ExportAssignment:
|
||||
|
||||
// Expressions which have argument lists or parameter lists
|
||||
case SyntaxKind.InvocationExpression:
|
||||
case SyntaxKind.ObjectCreationExpression:
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
|
||||
// These nodes should follow the child indentation set by its parent;
|
||||
// they introduce a new indenation scope; children should be indented at one level deeper
|
||||
indentationAmount = (parentIndentationAmount + parentIndentationAmountDelta);
|
||||
indentationAmountDelta = this.options.indentSpaces;
|
||||
break;
|
||||
|
||||
case SyntaxKind.IfStatement:
|
||||
if (parent.kind() === SyntaxKind.ElseClause &&
|
||||
!SyntaxUtilities.isLastTokenOnLine((<ElseClauseSyntax>parentNode).elseKeyword, this._text)) {
|
||||
// This is an else if statement with the if on the same line as the else, do not indent the if statmement.
|
||||
// Note: Children indentation has already been set by the parent if statement, so no need to increment
|
||||
indentationAmount = parentIndentationAmount;
|
||||
}
|
||||
else {
|
||||
// Otherwise introduce a new indenation scope; children should be indented at one level deeper
|
||||
indentationAmount = (parentIndentationAmount + parentIndentationAmountDelta);
|
||||
}
|
||||
indentationAmountDelta = this.options.indentSpaces;
|
||||
break;
|
||||
|
||||
case SyntaxKind.ElseClause:
|
||||
// Else should always follow its parent if statement indentation.
|
||||
// Note: Children indentation has already been set by the parent if statement, so no need to increment
|
||||
indentationAmount = parentIndentationAmount;
|
||||
indentationAmountDelta = this.options.indentSpaces;
|
||||
break;
|
||||
|
||||
|
||||
case SyntaxKind.Block:
|
||||
// Check if the block is a member in a list of statements (if the parent is a source unit, module, or block, or switch clause)
|
||||
if (this.shouldIndentBlockInParent(parent)) {
|
||||
indentationAmount = parentIndentationAmount + parentIndentationAmountDelta;
|
||||
}
|
||||
else {
|
||||
indentationAmount = parentIndentationAmount;
|
||||
}
|
||||
|
||||
indentationAmountDelta = this.options.indentSpaces;
|
||||
break;
|
||||
}
|
||||
|
||||
// If the parent happens to start on the same line as this node, then override the current node indenation with that
|
||||
// of the parent. This avoid having to add an extra level of indentation for the children. e.g.:
|
||||
// return {
|
||||
// a:1
|
||||
// };
|
||||
// instead of:
|
||||
// return {
|
||||
// a:1
|
||||
// };
|
||||
// We also need to pass the delta (if it is nonzero) to the children, so that subsequent lines get indented. Essentially, if any node starting on the given line
|
||||
// has a nonzero delta , the resulting delta should be inherited from this node. This is to indent cases like the following:
|
||||
// return a
|
||||
// || b;
|
||||
// Lastly, it is possible the node indentation needs to be recomputed because the formatter inserted a newline before its first token.
|
||||
// If this is the case, we know the node no longer starts on the same line as its parent (or at least we shouldn't treat it as such).
|
||||
if (parentNode) {
|
||||
if (!newLineInsertedByFormatting /*This could be false or undefined here*/) {
|
||||
var parentStartLine = this._snapshot.getLineNumberFromPosition(parent.start());
|
||||
var currentNodeStartLine = this._snapshot.getLineNumberFromPosition(this._position + leadingTriviaWidth(node));
|
||||
if (parentStartLine === currentNodeStartLine || newLineInsertedByFormatting === false /*meaning a new line was removed and we are force recomputing*/) {
|
||||
indentationAmount = parentIndentationAmount;
|
||||
indentationAmountDelta = Math.min(this.options.indentSpaces, parentIndentationAmountDelta + indentationAmountDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
indentationAmount: indentationAmount,
|
||||
indentationAmountDelta: indentationAmountDelta
|
||||
};
|
||||
}
|
||||
|
||||
private shouldIndentBlockInParent(parent: IndentationNodeContext): boolean {
|
||||
switch (parent.kind()) {
|
||||
case SyntaxKind.SourceUnit:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
case SyntaxKind.Block:
|
||||
case SyntaxKind.CaseSwitchClause:
|
||||
case SyntaxKind.DefaultSwitchClause:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private forceRecomputeIndentationOfParent(tokenStart: number, newLineAdded: boolean /*as opposed to removed*/): void {
|
||||
var parent = this._parent;
|
||||
if (start(parent.node()) === tokenStart) {
|
||||
// Temporarily pop the parent before recomputing
|
||||
this._parent = parent.parent();
|
||||
var indentation = this.getNodeIndentation(parent.node(), /* newLineInsertedByFormatting */ newLineAdded);
|
||||
parent.update(parent.parent(), parent.node(), parent.fullStart(), indentation.indentationAmount, indentation.indentationAmountDelta);
|
||||
this._parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,221 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export class MultipleTokenIndenter extends IndentationTrackingWalker {
|
||||
private _edits: TextEditInfo[] = [];
|
||||
|
||||
constructor(textSpan: TextSpan, sourceUnit: SourceUnitSyntax, snapshot: ITextSnapshot, indentFirstToken: boolean, options: FormattingOptions) {
|
||||
super(textSpan, sourceUnit, snapshot, indentFirstToken, options);
|
||||
}
|
||||
|
||||
public indentToken(token: ISyntaxToken, indentationAmount: number, commentIndentationAmount: number): void {
|
||||
// Ignore generated tokens
|
||||
if (token.fullWidth() === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have any skipped tokens as children, do not process this node for indentation or formatting
|
||||
if (this.parent().hasSkippedOrMissingTokenChild()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Be strict, and only consider nodes that fall inside the span. This avoids indenting a multiline string
|
||||
// on enter at the end of, as the whole token was not included in the span
|
||||
var tokenSpan = new TextSpan(this.position() + token.leadingTriviaWidth(), width(token));
|
||||
if (!this.textSpan().containsTextSpan(tokenSpan)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute an indentation string for this token
|
||||
var indentationString = Indentation.indentationString(indentationAmount, this.options);
|
||||
|
||||
var commentIndentationString = Indentation.indentationString(commentIndentationAmount, this.options);
|
||||
|
||||
// Record any needed indentation edits
|
||||
this.recordIndentationEditsForToken(token, indentationString, commentIndentationString);
|
||||
}
|
||||
|
||||
public edits(): TextEditInfo[]{
|
||||
return this._edits;
|
||||
}
|
||||
|
||||
public recordEdit(position: number, length: number, replaceWith: string): void {
|
||||
this._edits.push(new TextEditInfo(position, length, replaceWith));
|
||||
}
|
||||
|
||||
private recordIndentationEditsForToken(token: ISyntaxToken, indentationString: string, commentIndentationString: string) {
|
||||
var position = this.position();
|
||||
var indentNextTokenOrTrivia = true;
|
||||
var leadingWhiteSpace = ""; // We need to track the whitespace before a multiline comment
|
||||
|
||||
// Process any leading trivia if any
|
||||
var triviaList = token.leadingTrivia();
|
||||
if (triviaList) {
|
||||
var seenNewLine = position === 0;
|
||||
|
||||
for (var i = 0, length = triviaList.count(); i < length; i++, position += trivia.fullWidth()) {
|
||||
var trivia = triviaList.syntaxTriviaAt(i);
|
||||
|
||||
// Skip all trivia up to the first newline we see. We consider this trivia to
|
||||
// 'belong' to the previous token.
|
||||
if (!seenNewLine) {
|
||||
if (trivia.kind !== SyntaxKind.NewLineTrivia) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
seenNewLine = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip this trivia if it is not in the span
|
||||
if (!this.textSpan().containsTextSpan(new TextSpan(position, trivia.fullWidth()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (trivia.kind) {
|
||||
case SyntaxKind.MultiLineCommentTrivia:
|
||||
// We will only indent the first line of the multiline comment if we were planning to indent the next trivia. However,
|
||||
// subsequent lines will always be indented
|
||||
this.recordIndentationEditsForMultiLineComment(trivia, position, commentIndentationString, leadingWhiteSpace, !indentNextTokenOrTrivia /* already indented first line */);
|
||||
indentNextTokenOrTrivia = false;
|
||||
leadingWhiteSpace = "";
|
||||
break;
|
||||
|
||||
case SyntaxKind.SingleLineCommentTrivia:
|
||||
case SyntaxKind.SkippedTokenTrivia:
|
||||
if (indentNextTokenOrTrivia) {
|
||||
this.recordIndentationEditsForSingleLineOrSkippedText(trivia, position, commentIndentationString);
|
||||
indentNextTokenOrTrivia = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.WhitespaceTrivia:
|
||||
// If the next trivia is a comment, use the comment indentation level instead of the regular indentation level
|
||||
// If the next trivia is a newline, this whole line is just whitespace, so don't do anything (trimming will take care of it)
|
||||
var nextTrivia = length > i + 1 && triviaList.syntaxTriviaAt(i + 1);
|
||||
var whiteSpaceIndentationString = nextTrivia && nextTrivia.isComment() ? commentIndentationString : indentationString;
|
||||
if (indentNextTokenOrTrivia) {
|
||||
if (!(nextTrivia && nextTrivia.isNewLine())) {
|
||||
this.recordIndentationEditsForWhitespace(trivia, position, whiteSpaceIndentationString);
|
||||
}
|
||||
indentNextTokenOrTrivia = false;
|
||||
}
|
||||
leadingWhiteSpace += trivia.fullText();
|
||||
break;
|
||||
|
||||
case SyntaxKind.NewLineTrivia:
|
||||
// We hit a newline processing the trivia. We need to add the indentation to the
|
||||
// next line as well. Note: don't bother indenting the newline itself. This will
|
||||
// just insert ugly whitespace that most users probably will not want.
|
||||
indentNextTokenOrTrivia = true;
|
||||
leadingWhiteSpace = "";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Errors.invalidOperation();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (token.kind !== SyntaxKind.EndOfFileToken && indentNextTokenOrTrivia) {
|
||||
// If the last trivia item was a new line, or no trivia items were encounterd record the
|
||||
// indentation edit at the token position
|
||||
if (indentationString.length > 0) {
|
||||
this.recordEdit(position, 0, indentationString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private recordIndentationEditsForSingleLineOrSkippedText(trivia: ISyntaxTrivia, fullStart: number, indentationString: string): void {
|
||||
// Record the edit
|
||||
if (indentationString.length > 0) {
|
||||
this.recordEdit(fullStart, 0, indentationString);
|
||||
}
|
||||
}
|
||||
|
||||
private recordIndentationEditsForWhitespace(trivia: ISyntaxTrivia, fullStart: number, indentationString: string): void {
|
||||
var text = trivia.fullText();
|
||||
|
||||
// Check if the current indentation matches the desired indentation or not
|
||||
if (indentationString === text) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Record the edit
|
||||
this.recordEdit(fullStart, text.length, indentationString);
|
||||
}
|
||||
|
||||
private recordIndentationEditsForMultiLineComment(trivia: ISyntaxTrivia, fullStart: number, indentationString: string, leadingWhiteSpace: string, firstLineAlreadyIndented: boolean): void {
|
||||
// If the multiline comment spans multiple lines, we need to add the right indent amount to
|
||||
// each successive line segment as well.
|
||||
var position = fullStart;
|
||||
var segments = Syntax.splitMultiLineCommentTriviaIntoMultipleLines(trivia);
|
||||
|
||||
if (segments.length <= 1) {
|
||||
if (!firstLineAlreadyIndented) {
|
||||
// Process the one-line multiline comment just like a single line comment
|
||||
this.recordIndentationEditsForSingleLineOrSkippedText(trivia, fullStart, indentationString);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Find number of columns in first segment
|
||||
var whiteSpaceColumnsInFirstSegment = Indentation.columnForPositionInString(leadingWhiteSpace, leadingWhiteSpace.length, this.options);
|
||||
|
||||
var indentationColumns = Indentation.columnForPositionInString(indentationString, indentationString.length, this.options);
|
||||
var startIndex = 0;
|
||||
if (firstLineAlreadyIndented) {
|
||||
startIndex = 1;
|
||||
position += segments[0].length;
|
||||
}
|
||||
for (var i = startIndex; i < segments.length; i++) {
|
||||
var segment = segments[i];
|
||||
this.recordIndentationEditsForSegment(segment, position, indentationColumns, whiteSpaceColumnsInFirstSegment);
|
||||
position += segment.length;
|
||||
}
|
||||
}
|
||||
|
||||
private recordIndentationEditsForSegment(segment: string, fullStart: number, indentationColumns: number, whiteSpaceColumnsInFirstSegment: number): void {
|
||||
// Indent subsequent lines using a column delta of the actual indentation relative to the first line
|
||||
var firstNonWhitespacePosition = Indentation.firstNonWhitespacePosition(segment);
|
||||
var leadingWhiteSpaceColumns = Indentation.columnForPositionInString(segment, firstNonWhitespacePosition, this.options);
|
||||
var deltaFromFirstSegment = leadingWhiteSpaceColumns - whiteSpaceColumnsInFirstSegment;
|
||||
var finalColumns = indentationColumns + deltaFromFirstSegment;
|
||||
if (finalColumns < 0) {
|
||||
finalColumns = 0;
|
||||
}
|
||||
var indentationString = Indentation.indentationString(finalColumns, this.options);
|
||||
|
||||
if (firstNonWhitespacePosition < segment.length &&
|
||||
CharacterInfo.isLineTerminator(segment.charCodeAt(firstNonWhitespacePosition))) {
|
||||
// If this segment was just a newline, then don't bother indenting it. That will just
|
||||
// leave the user with an ugly indent in their output that they probably do not want.
|
||||
return;
|
||||
}
|
||||
|
||||
if (indentationString === segment.substring(0, firstNonWhitespacePosition)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Record the edit
|
||||
this.recordEdit(fullStart, firstNonWhitespacePosition, indentationString);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,11 +14,7 @@
|
|||
//
|
||||
|
||||
///<reference path='..\services.ts' />
|
||||
///<reference path='textSnapshot.ts' />
|
||||
///<reference path='textSnapshotLine.ts' />
|
||||
///<reference path='snapshotPoint.ts' />
|
||||
///<reference path='formattingContext.ts' />
|
||||
///<reference path='formattingManager.ts' />
|
||||
///<reference path='formattingRequestKind.ts' />
|
||||
///<reference path='rule.ts' />
|
||||
///<reference path='ruleAction.ts' />
|
||||
|
@ -28,12 +24,5 @@
|
|||
///<reference path='ruleOperationContext.ts' />
|
||||
///<reference path='rules.ts' />
|
||||
///<reference path='rulesMap.ts' />
|
||||
///<reference path='rulesProvider.ts' />
|
||||
///<reference path='textEditInfo.ts' />
|
||||
///<reference path='tokenRange.ts' />
|
||||
///<reference path='tokenSpan.ts' />
|
||||
///<reference path='indentationNodeContext.ts' />
|
||||
///<reference path='indentationNodeContextPool.ts' />
|
||||
///<reference path='indentationTrackingWalker.ts' />
|
||||
///<reference path='multipleTokenIndenter.ts' />
|
||||
///<reference path='formatter.ts' />
|
||||
///<reference path='tokenSpan.ts' />
|
|
@ -13,9 +13,9 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
///<reference path='references.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
module ts.formatting {
|
||||
export class Rule {
|
||||
constructor(
|
||||
public Descriptor: RuleDescriptor,
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
///<reference path='references.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export enum RuleAction {
|
||||
Ignore,
|
||||
Space,
|
||||
NewLine,
|
||||
Delete
|
||||
module ts.formatting {
|
||||
export const enum RuleAction {
|
||||
Ignore = 0x00000001,
|
||||
Space = 0x00000002,
|
||||
NewLine = 0x00000004,
|
||||
Delete = 0x00000008
|
||||
}
|
||||
}
|
|
@ -13,9 +13,9 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
///<reference path='references.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
module ts.formatting {
|
||||
export class RuleDescriptor {
|
||||
constructor(public LeftTokenRange: Shared.TokenRange, public RightTokenRange: Shared.TokenRange) {
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ module TypeScript.Services.Formatting {
|
|||
}
|
||||
|
||||
static create3(left: SyntaxKind, right: Shared.TokenRange): RuleDescriptor
|
||||
//: this(TokenRange.FromToken(left), right)
|
||||
{
|
||||
return RuleDescriptor.create4(Shared.TokenRange.FromToken(left), right);
|
||||
}
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
///<reference path='references.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export enum RuleFlags {
|
||||
module ts.formatting {
|
||||
export const enum RuleFlags {
|
||||
None,
|
||||
CanDeleteNewLines
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
///<reference path='references.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
module ts.formatting {
|
||||
export class RuleOperation {
|
||||
public Context: RuleOperationContext;
|
||||
public Action: RuleAction;
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
///<reference path='references.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
module ts.formatting {
|
||||
|
||||
export class RuleOperationContext {
|
||||
private customContextChecks: { (context: FormattingContext): boolean; }[];
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
///<reference path='references.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
module ts.formatting {
|
||||
export class Rules {
|
||||
public getRuleName(rule: Rule) {
|
||||
var o: ts.Map<any> = <any>this;
|
||||
|
@ -241,7 +241,7 @@ module TypeScript.Services.Formatting {
|
|||
this.SpaceBeforeOpenBraceInFunction = new Rule(RuleDescriptor.create2(this.FunctionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext, Rules.IsNotFormatOnEnter, Rules.IsSameLineTokenOrBeforeMultilineBlockContext), RuleAction.Space), RuleFlags.CanDeleteNewLines);
|
||||
|
||||
// Place a space before open brace in a TypeScript declaration that has braces as children (class, module, enum, etc)
|
||||
this.TypeScriptOpenBraceLeftTokenRange = Shared.TokenRange.FromTokens([SyntaxKind.IdentifierName, SyntaxKind.MultiLineCommentTrivia]);
|
||||
this.TypeScriptOpenBraceLeftTokenRange = Shared.TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.MultiLineCommentTrivia]);
|
||||
this.SpaceBeforeOpenBraceInTypeScriptDeclWithBlock = new Rule(RuleDescriptor.create2(this.TypeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsTypeScriptDeclWithBlockContext, Rules.IsNotFormatOnEnter, Rules.IsSameLineTokenOrBeforeMultilineBlockContext), RuleAction.Space), RuleFlags.CanDeleteNewLines);
|
||||
|
||||
// Place a space before open brace in a control flow construct
|
||||
|
@ -299,7 +299,7 @@ module TypeScript.Services.Formatting {
|
|||
|
||||
// get x() {}
|
||||
// set x(val) {}
|
||||
this.SpaceAfterGetSetInMember = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.GetKeyword, SyntaxKind.SetKeyword]), SyntaxKind.IdentifierName), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
|
||||
this.SpaceAfterGetSetInMember = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.GetKeyword, SyntaxKind.SetKeyword]), SyntaxKind.Identifier), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
|
||||
|
||||
// Special case for binary operators (that are keywords). For these we have to add a space and shouldn't follow any user options.
|
||||
this.SpaceBeforeBinaryKeywordOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryKeywordOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
|
||||
|
@ -324,7 +324,7 @@ module TypeScript.Services.Formatting {
|
|||
this.SpaceAfterArrow = new Rule(RuleDescriptor.create3(SyntaxKind.EqualsGreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
|
||||
|
||||
// Optional parameters and var args
|
||||
this.NoSpaceAfterEllipsis = new Rule(RuleDescriptor.create1(SyntaxKind.DotDotDotToken, SyntaxKind.IdentifierName), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceAfterEllipsis = new Rule(RuleDescriptor.create1(SyntaxKind.DotDotDotToken, SyntaxKind.Identifier), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceAfterOptionalParameters = new Rule(RuleDescriptor.create3(SyntaxKind.QuestionToken, Shared.TokenRange.FromTokens([SyntaxKind.CloseParenToken, SyntaxKind.CommaToken])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotBinaryOpContext), RuleAction.Delete));
|
||||
|
||||
// generics
|
||||
|
@ -437,7 +437,7 @@ module TypeScript.Services.Formatting {
|
|||
///
|
||||
|
||||
static IsForContext(context: FormattingContext): boolean {
|
||||
return context.contextNode.kind() === SyntaxKind.ForStatement;
|
||||
return context.contextNode.kind === SyntaxKind.ForStatement;
|
||||
}
|
||||
|
||||
static IsNotForContext(context: FormattingContext): boolean {
|
||||
|
@ -446,8 +446,7 @@ module TypeScript.Services.Formatting {
|
|||
|
||||
static IsBinaryOpContext(context: FormattingContext): boolean {
|
||||
|
||||
switch (context.contextNode.kind()) {
|
||||
// binary expressions
|
||||
switch (context.contextNode.kind) {
|
||||
case SyntaxKind.BinaryExpression:
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
return true;
|
||||
|
@ -455,8 +454,11 @@ module TypeScript.Services.Formatting {
|
|||
// equal in import a = module('a');
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
// equal in var a = 0;
|
||||
case SyntaxKind.VariableDeclarator:
|
||||
case SyntaxKind.EqualsValueClause:
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
// equal in p = 0;
|
||||
case SyntaxKind.Parameter:
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.Property:
|
||||
return context.currentTokenSpan.kind === SyntaxKind.EqualsToken || context.nextTokenSpan.kind === SyntaxKind.EqualsToken;
|
||||
// "in" keyword in for (var x in []) { }
|
||||
case SyntaxKind.ForInStatement:
|
||||
|
@ -512,16 +514,21 @@ module TypeScript.Services.Formatting {
|
|||
}
|
||||
|
||||
// IMPORTANT!!! This method must return true ONLY for nodes with open and close braces as immediate children
|
||||
static NodeIsBlockContext(node: IndentationNodeContext): boolean {
|
||||
static NodeIsBlockContext(node: Node): boolean {
|
||||
if (Rules.NodeIsTypeScriptDeclWithBlockContext(node)) {
|
||||
// This means we are in a context that looks like a block to the user, but in the grammar is actually not a node (it's a class, module, enum, object type literal, etc).
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (node.kind()) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Block:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.ObjectLiteral:
|
||||
case SyntaxKind.TryBlock:
|
||||
case SyntaxKind.CatchBlock:
|
||||
case SyntaxKind.FinallyBlock:
|
||||
case SyntaxKind.FunctionBlock:
|
||||
case SyntaxKind.ModuleBlock:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -529,17 +536,20 @@ module TypeScript.Services.Formatting {
|
|||
}
|
||||
|
||||
static IsFunctionDeclContext(context: FormattingContext): boolean {
|
||||
switch (context.contextNode.kind()) {
|
||||
switch (context.contextNode.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.MemberFunctionDeclaration:
|
||||
case SyntaxKind.Method:
|
||||
//case SyntaxKind.MemberFunctionDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.MethodSignature:
|
||||
///case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ConstructorDeclaration:
|
||||
case SyntaxKind.SimpleArrowFunctionExpression:
|
||||
case SyntaxKind.ParenthesizedArrowFunctionExpression:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
//case SyntaxKind.ConstructorDeclaration:
|
||||
//case SyntaxKind.SimpleArrowFunctionExpression:
|
||||
//case SyntaxKind.ParenthesizedArrowFunctionExpression:
|
||||
case SyntaxKind.InterfaceDeclaration: // This one is not truly a function, but for formatting purposes, it acts just like one
|
||||
return true;
|
||||
}
|
||||
|
@ -551,11 +561,12 @@ module TypeScript.Services.Formatting {
|
|||
return Rules.NodeIsTypeScriptDeclWithBlockContext(context.contextNode);
|
||||
}
|
||||
|
||||
static NodeIsTypeScriptDeclWithBlockContext(node: IndentationNodeContext): boolean {
|
||||
switch (node.kind()) {
|
||||
static NodeIsTypeScriptDeclWithBlockContext(node: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.ObjectType:
|
||||
case SyntaxKind.TypeLiteral:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return true;
|
||||
}
|
||||
|
@ -564,11 +575,16 @@ module TypeScript.Services.Formatting {
|
|||
}
|
||||
|
||||
static IsAfterCodeBlockContext(context: FormattingContext): boolean {
|
||||
switch (context.currentTokenParent.kind()) {
|
||||
switch (context.currentTokenParent.kind) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.Block:
|
||||
case SyntaxKind.TryBlock:
|
||||
case SyntaxKind.CatchBlock:
|
||||
case SyntaxKind.FinallyBlock:
|
||||
case SyntaxKind.FunctionBlock:
|
||||
case SyntaxKind.ModuleBlock:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
return true;
|
||||
}
|
||||
|
@ -576,7 +592,7 @@ module TypeScript.Services.Formatting {
|
|||
}
|
||||
|
||||
static IsControlDeclContext(context: FormattingContext): boolean {
|
||||
switch (context.contextNode.kind()) {
|
||||
switch (context.contextNode.kind) {
|
||||
case SyntaxKind.IfStatement:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
case SyntaxKind.ForStatement:
|
||||
|
@ -585,9 +601,10 @@ module TypeScript.Services.Formatting {
|
|||
case SyntaxKind.TryStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WithStatement:
|
||||
case SyntaxKind.ElseClause:
|
||||
case SyntaxKind.CatchClause:
|
||||
case SyntaxKind.FinallyClause:
|
||||
// TODO
|
||||
// case SyntaxKind.ElseClause:
|
||||
case SyntaxKind.CatchBlock:
|
||||
case SyntaxKind.FinallyBlock:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -596,15 +613,15 @@ module TypeScript.Services.Formatting {
|
|||
}
|
||||
|
||||
static IsObjectContext(context: FormattingContext): boolean {
|
||||
return context.contextNode.kind() === SyntaxKind.ObjectLiteralExpression;
|
||||
return context.contextNode.kind === SyntaxKind.ObjectLiteral;
|
||||
}
|
||||
|
||||
static IsFunctionCallContext(context: FormattingContext): boolean {
|
||||
return context.contextNode.kind() === SyntaxKind.InvocationExpression;
|
||||
return context.contextNode.kind === SyntaxKind.CallExpression;
|
||||
}
|
||||
|
||||
static IsNewContext(context: FormattingContext): boolean {
|
||||
return context.contextNode.kind() === SyntaxKind.ObjectCreationExpression;
|
||||
return context.contextNode.kind === SyntaxKind.NewExpression;
|
||||
}
|
||||
|
||||
static IsFunctionCallOrNewContext(context: FormattingContext): boolean {
|
||||
|
@ -620,25 +637,43 @@ module TypeScript.Services.Formatting {
|
|||
}
|
||||
|
||||
static IsModuleDeclContext(context: FormattingContext): boolean {
|
||||
return context.contextNode.kind() === SyntaxKind.ModuleDeclaration;
|
||||
return context.contextNode.kind === SyntaxKind.ModuleDeclaration;
|
||||
}
|
||||
|
||||
static IsObjectTypeContext(context: FormattingContext): boolean {
|
||||
return context.contextNode.kind() === SyntaxKind.ObjectType && context.contextNode.parent().kind() !== SyntaxKind.InterfaceDeclaration;
|
||||
return context.contextNode.kind === SyntaxKind.TypeLiteral;// && context.contextNode.parent.kind !== SyntaxKind.InterfaceDeclaration;
|
||||
}
|
||||
|
||||
static IsTypeArgumentOrParameter(tokenKind: SyntaxKind, parentKind: SyntaxKind): boolean {
|
||||
return ((tokenKind === SyntaxKind.LessThanToken || tokenKind === SyntaxKind.GreaterThanToken) &&
|
||||
(parentKind === SyntaxKind.TypeParameterList || parentKind === SyntaxKind.TypeArgumentList));
|
||||
static IsTypeArgumentOrParameter(token: TextRangeWithKind, parent: Node): boolean {
|
||||
if (token.kind !== SyntaxKind.LessThanToken && token.kind !== SyntaxKind.GreaterThanToken) {
|
||||
return false;
|
||||
}
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.TypeReference:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.Method:
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.NewExpression:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static IsTypeArgumentOrParameterContext(context: FormattingContext): boolean {
|
||||
return Rules.IsTypeArgumentOrParameter(context.currentTokenSpan.kind, context.currentTokenParent.kind()) ||
|
||||
Rules.IsTypeArgumentOrParameter(context.nextTokenSpan.kind, context.nextTokenParent.kind());
|
||||
return Rules.IsTypeArgumentOrParameter(context.currentTokenSpan, context.currentTokenParent) ||
|
||||
Rules.IsTypeArgumentOrParameter(context.nextTokenSpan, context.nextTokenParent);
|
||||
}
|
||||
|
||||
static IsVoidOpContext(context: FormattingContext): boolean {
|
||||
return context.currentTokenSpan.kind === SyntaxKind.VoidKeyword && context.currentTokenParent.kind() === SyntaxKind.VoidExpression;
|
||||
return context.currentTokenSpan.kind === SyntaxKind.VoidKeyword && context.currentTokenParent.kind === SyntaxKind.PrefixOperator;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,9 +13,9 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
///<reference path='references.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
module ts.formatting {
|
||||
export class RulesMap {
|
||||
public map: RulesBucket[];
|
||||
public mapRowLength: number;
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
/// <reference path="formatting.ts"/>
|
||||
/// <reference path="references.ts"/>
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
module ts.formatting {
|
||||
export class RulesProvider {
|
||||
private globalRules: Rules;
|
||||
private options: ts.FormatCodeOptions;
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
|
||||
export class SnapshotPoint {
|
||||
constructor(public snapshot: ITextSnapshot, public position: number) {
|
||||
}
|
||||
public getContainingLine(): ITextSnapshotLine {
|
||||
return this.snapshot.getLineFromPosition(this.position);
|
||||
}
|
||||
public add(offset: number): SnapshotPoint {
|
||||
return new SnapshotPoint(this.snapshot, this.position + offset);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export class TextEditInfo {
|
||||
|
||||
constructor(public position: number, public length: number, public replaceWith: string) {
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return "[ position: " + this.position + ", length: " + this.length + ", replaceWith: '" + this.replaceWith + "' ]";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export interface ITextSnapshot {
|
||||
getLength(): number;
|
||||
getText(span: TextSpan): string;
|
||||
getLineNumberFromPosition(position: number): number;
|
||||
getLineFromPosition(position: number): ITextSnapshotLine;
|
||||
getLineFromLineNumber(lineNumber: number): ITextSnapshotLine;
|
||||
}
|
||||
|
||||
export class TextSnapshot implements ITextSnapshot {
|
||||
private lines: TextSnapshotLine[];
|
||||
|
||||
constructor(private snapshot: ISimpleText) {
|
||||
this.lines = [];
|
||||
}
|
||||
|
||||
public getLength(): number {
|
||||
return this.snapshot.length();
|
||||
}
|
||||
|
||||
public getText(span: TextSpan): string {
|
||||
return this.snapshot.substr(span.start(), span.length());
|
||||
}
|
||||
|
||||
public getLineNumberFromPosition(position: number): number {
|
||||
return this.snapshot.lineMap().getLineNumberFromPosition(position);
|
||||
}
|
||||
|
||||
public getLineFromPosition(position: number): ITextSnapshotLine {
|
||||
var lineNumber = this.getLineNumberFromPosition(position);
|
||||
return this.getLineFromLineNumber(lineNumber);
|
||||
}
|
||||
|
||||
public getLineFromLineNumber(lineNumber: number): ITextSnapshotLine {
|
||||
var line = this.lines[lineNumber];
|
||||
if (line === undefined) {
|
||||
line = <TextSnapshotLine>this.getLineFromLineNumberWorker(lineNumber);
|
||||
this.lines[lineNumber] = line;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
private getLineFromLineNumberWorker(lineNumber: number): ITextSnapshotLine {
|
||||
var lineMap = this.snapshot.lineMap().lineStarts();
|
||||
var lineMapIndex = lineNumber; //Note: lineMap is 0-based
|
||||
if (lineMapIndex < 0 || lineMapIndex >= lineMap.length)
|
||||
throw new Error(TypeScript.getDiagnosticMessage(TypeScript.DiagnosticCode.Invalid_line_number_0, [lineMapIndex]));
|
||||
var start = lineMap[lineMapIndex];
|
||||
|
||||
var end: number;
|
||||
var endIncludingLineBreak: number;
|
||||
var lineBreak = "";
|
||||
if (lineMapIndex == lineMap.length) {
|
||||
end = endIncludingLineBreak = this.snapshot.length();
|
||||
}
|
||||
else {
|
||||
endIncludingLineBreak = (lineMapIndex >= lineMap.length - 1 ? this.snapshot.length() : lineMap[lineMapIndex + 1]);
|
||||
for (var p = endIncludingLineBreak - 1; p >= start; p--) {
|
||||
var c = this.snapshot.substr(p, 1);
|
||||
//TODO: Other ones?
|
||||
if (c != "\r" && c != "\n") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
end = p + 1;
|
||||
lineBreak = this.snapshot.substr(end, endIncludingLineBreak - end);
|
||||
}
|
||||
var result = new TextSnapshotLine(this, lineNumber, start, end, lineBreak);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export interface ITextSnapshotLine {
|
||||
snapshot(): ITextSnapshot;
|
||||
|
||||
start(): SnapshotPoint;
|
||||
startPosition(): number;
|
||||
|
||||
end(): SnapshotPoint;
|
||||
endPosition(): number;
|
||||
|
||||
endIncludingLineBreak(): SnapshotPoint;
|
||||
endIncludingLineBreakPosition(): number;
|
||||
|
||||
length(): number;
|
||||
lineNumber(): number;
|
||||
getText(): string;
|
||||
}
|
||||
|
||||
export class TextSnapshotLine implements ITextSnapshotLine {
|
||||
constructor(private _snapshot: ITextSnapshot, private _lineNumber: number, private _start: number, private _end: number, private _lineBreak: string) {
|
||||
}
|
||||
|
||||
public snapshot() {
|
||||
return this._snapshot;
|
||||
}
|
||||
|
||||
public start() {
|
||||
return new SnapshotPoint(this._snapshot, this._start);
|
||||
}
|
||||
|
||||
public startPosition() {
|
||||
return this._start;
|
||||
}
|
||||
|
||||
public end() {
|
||||
return new SnapshotPoint(this._snapshot, this._end);
|
||||
}
|
||||
|
||||
public endPosition() {
|
||||
return this._end;
|
||||
}
|
||||
|
||||
public endIncludingLineBreak() {
|
||||
return new SnapshotPoint(this._snapshot, this._end + this._lineBreak.length);
|
||||
}
|
||||
|
||||
public endIncludingLineBreakPosition() {
|
||||
return this._end + this._lineBreak.length;
|
||||
}
|
||||
|
||||
public length() {
|
||||
return this._end - this._start;
|
||||
}
|
||||
|
||||
public lineNumber() {
|
||||
return this._lineNumber;
|
||||
}
|
||||
|
||||
public getText(): string {
|
||||
return this._snapshot.getText(TextSpan.fromBounds(this._start, this._end));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,9 +13,9 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
///<reference path='references.ts' />
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
module ts.formatting {
|
||||
export module Shared {
|
||||
export interface ITokenAccess {
|
||||
GetTokens(): SyntaxKind[];
|
||||
|
@ -41,12 +41,6 @@ module TypeScript.Services.Formatting {
|
|||
public Contains(token: SyntaxKind): boolean {
|
||||
return this.tokens.indexOf(token) >= 0;
|
||||
}
|
||||
|
||||
|
||||
public toString(): string {
|
||||
return "[tokenRangeStart=" + SyntaxKind[this.tokens[0]] + "," +
|
||||
"tokenRangeEnd=" + SyntaxKind[this.tokens[this.tokens.length - 1]] + "]";
|
||||
}
|
||||
}
|
||||
|
||||
export class TokenValuesAccess implements ITokenAccess {
|
||||
|
@ -76,10 +70,6 @@ module TypeScript.Services.Formatting {
|
|||
public Contains(tokenValue: SyntaxKind): boolean {
|
||||
return tokenValue == this.token;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return "[singleTokenKind=" + SyntaxKind[this.token] + "]";
|
||||
}
|
||||
}
|
||||
|
||||
export class TokenAllAccess implements ITokenAccess {
|
||||
|
@ -135,18 +125,18 @@ module TypeScript.Services.Formatting {
|
|||
static Any: TokenRange = TokenRange.AllTokens();
|
||||
static AnyIncludingMultilineComments = TokenRange.FromTokens(TokenRange.Any.GetTokens().concat([SyntaxKind.MultiLineCommentTrivia]));
|
||||
static Keywords = TokenRange.FromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword);
|
||||
static Operators = TokenRange.FromRange(SyntaxKind.SemicolonToken, SyntaxKind.SlashEqualsToken);
|
||||
static BinaryOperators = TokenRange.FromRange(SyntaxKind.LessThanToken, SyntaxKind.SlashEqualsToken);
|
||||
static Operators = TokenRange.FromRange(SyntaxKind.FirstOperator, SyntaxKind.LastOperator);
|
||||
static BinaryOperators = TokenRange.FromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
|
||||
static BinaryKeywordOperators = TokenRange.FromTokens([SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword]);
|
||||
static ReservedKeywords = TokenRange.FromRange(SyntaxKind.FirstFutureReservedStrictKeyword, SyntaxKind.LastFutureReservedStrictKeyword);
|
||||
static ReservedKeywords = TokenRange.FromRange(SyntaxKind.FirstFutureReservedWord, SyntaxKind.LastFutureReservedWord);
|
||||
static UnaryPrefixOperators = TokenRange.FromTokens([SyntaxKind.PlusPlusToken, SyntaxKind.MinusMinusToken, SyntaxKind.TildeToken, SyntaxKind.ExclamationToken]);
|
||||
static UnaryPrefixExpressions = TokenRange.FromTokens([SyntaxKind.NumericLiteral, SyntaxKind.IdentifierName, SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.OpenBraceToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
static UnaryPreincrementExpressions = TokenRange.FromTokens([SyntaxKind.IdentifierName, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
static UnaryPostincrementExpressions = TokenRange.FromTokens([SyntaxKind.IdentifierName, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
|
||||
static UnaryPredecrementExpressions = TokenRange.FromTokens([SyntaxKind.IdentifierName, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
static UnaryPostdecrementExpressions = TokenRange.FromTokens([SyntaxKind.IdentifierName, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
|
||||
static UnaryPrefixExpressions = TokenRange.FromTokens([SyntaxKind.NumericLiteral, SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.OpenBraceToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
static UnaryPreincrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
static UnaryPostincrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
|
||||
static UnaryPredecrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
static UnaryPostdecrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
|
||||
static Comments = TokenRange.FromTokens([SyntaxKind.SingleLineCommentTrivia, SyntaxKind.MultiLineCommentTrivia]);
|
||||
static TypeNames = TokenRange.FromTokens([SyntaxKind.IdentifierName, SyntaxKind.NumberKeyword, SyntaxKind.StringKeyword, SyntaxKind.BooleanKeyword, SyntaxKind.VoidKeyword, SyntaxKind.AnyKeyword]);
|
||||
static TypeNames = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.NumberKeyword, SyntaxKind.StringKeyword, SyntaxKind.BooleanKeyword, SyntaxKind.VoidKeyword, SyntaxKind.AnyKeyword]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,11 +13,10 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
///<reference path='formatting.ts' />
|
||||
///<reference path='references.ts' />
|
||||
|
||||
|
||||
module TypeScript.Services.Formatting {
|
||||
export class TokenSpan extends TextSpan {
|
||||
module ts.formatting {
|
||||
export class TokenSpan extends TypeScript.TextSpan {
|
||||
constructor(public kind: SyntaxKind, start: number, length: number) {
|
||||
super(start, length);
|
||||
}
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
|
||||
module TypeScript.Indentation {
|
||||
// Returns the column that this input string ends at (assuming it starts at column 0).
|
||||
export function columnForPositionInString(input: string, position: number, options: FormattingOptions): number {
|
||||
return columnForPositionInStringWorker(input, position, 0, options);
|
||||
}
|
||||
|
||||
function columnForPositionInStringWorker(input: string, position: number, startColumn: number, options: FormattingOptions): number {
|
||||
var column = startColumn;
|
||||
var spacesPerTab = options.spacesPerTab;
|
||||
|
||||
for (var j = 0; j < position; j++) {
|
||||
var ch = input.charCodeAt(j);
|
||||
|
||||
if (ch === CharacterCodes.tab) {
|
||||
column += spacesPerTab - column % spacesPerTab;
|
||||
}
|
||||
else {
|
||||
column++;
|
||||
}
|
||||
}
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
export function indentationString(column: number, options: FormattingOptions): string {
|
||||
var numberOfTabs = 0;
|
||||
var numberOfSpaces = Math.max(0, column);
|
||||
|
||||
if (options.useTabs) {
|
||||
numberOfTabs = Math.floor(column / options.spacesPerTab);
|
||||
numberOfSpaces -= numberOfTabs * options.spacesPerTab;
|
||||
}
|
||||
|
||||
return StringUtilities.repeat('\t', numberOfTabs) +
|
||||
StringUtilities.repeat(' ', numberOfSpaces);
|
||||
}
|
||||
|
||||
export function firstNonWhitespacePosition(value: string): number {
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
var ch = value.charCodeAt(i);
|
||||
if (!CharacterInfo.isWhitespace(ch)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return value.length;
|
||||
}
|
||||
}
|
|
@ -96,6 +96,7 @@ module TypeScript {
|
|||
Type_expected: "Type expected.",
|
||||
Template_literal_cannot_be_used_as_an_element_name: "Template literal cannot be used as an element name.",
|
||||
Computed_property_names_cannot_be_used_here: "Computed property names cannot be used here.",
|
||||
yield_expression_must_be_contained_within_a_generator_declaration: "'yield' expression must be contained within a generator declaration.",
|
||||
Duplicate_identifier_0: "Duplicate identifier '{0}'.",
|
||||
The_name_0_does_not_exist_in_the_current_scope: "The name '{0}' does not exist in the current scope.",
|
||||
The_name_0_does_not_refer_to_a_value: "The name '{0}' does not refer to a value.",
|
||||
|
|
|
@ -98,6 +98,7 @@ module TypeScript {
|
|||
"Type expected.": { "code": 1110, "category": DiagnosticCategory.Error },
|
||||
"Template literal cannot be used as an element name.": { "code": 1111, "category": DiagnosticCategory.Error },
|
||||
"Computed property names cannot be used here.": { "code": 1112, "category": DiagnosticCategory.Error },
|
||||
"'yield' expression must be contained within a generator declaration.": { "code": 1113, "category": DiagnosticCategory.Error },
|
||||
"Duplicate identifier '{0}'.": { "code": 2000, "category": DiagnosticCategory.Error },
|
||||
"The name '{0}' does not exist in the current scope.": { "code": 2001, "category": DiagnosticCategory.Error },
|
||||
"The name '{0}' does not refer to a value.": { "code": 2002, "category": DiagnosticCategory.Error },
|
||||
|
|
|
@ -379,6 +379,10 @@
|
|||
"category": "Error",
|
||||
"code": 1112
|
||||
},
|
||||
"'yield' expression must be contained within a generator declaration.": {
|
||||
"category": "Error",
|
||||
"code": 1113
|
||||
},
|
||||
"Duplicate identifier '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 2000
|
||||
|
|
|
@ -8,11 +8,10 @@
|
|||
/// <reference path='outliningElementsCollector.ts' />
|
||||
/// <reference path='navigationBar.ts' />
|
||||
/// <reference path='breakpoints.ts' />
|
||||
/// <reference path='indentation.ts' />
|
||||
/// <reference path='signatureHelp.ts' />
|
||||
/// <reference path='utilities.ts' />
|
||||
/// <reference path='formatting\formatting.ts' />
|
||||
/// <reference path='formatting\smartIndenter.ts' />
|
||||
/// <reference path='smartIndenter.ts' />
|
||||
/// <reference path='formatting.ts' />
|
||||
|
||||
/// <reference path='core\references.ts' />
|
||||
/// <reference path='resources\references.ts' />
|
||||
|
@ -667,6 +666,7 @@ module ts {
|
|||
public text: string;
|
||||
public getLineAndCharacterFromPosition(position: number): { line: number; character: number } { return null; }
|
||||
public getPositionFromLineAndCharacter(line: number, character: number): number { return -1; }
|
||||
public getLineStarts(): number[] { return undefined; }
|
||||
public amdDependencies: string[];
|
||||
public referencedFiles: FileReference[];
|
||||
public syntacticErrors: Diagnostic[];
|
||||
|
@ -2137,7 +2137,7 @@ module ts {
|
|||
|
||||
export function createLanguageService(host: LanguageServiceHost, documentRegistry: DocumentRegistry): LanguageService {
|
||||
var syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
|
||||
var formattingRulesProvider: TypeScript.Services.Formatting.RulesProvider;
|
||||
var ruleProvider: ts.formatting.RulesProvider;
|
||||
var hostCache: HostCache; // A cache of all the information about the files on the host side.
|
||||
var program: Program;
|
||||
|
||||
|
@ -2168,6 +2168,16 @@ module ts {
|
|||
return fullTypeCheckChecker_doNotAccessDirectly || (fullTypeCheckChecker_doNotAccessDirectly = program.getTypeChecker(/*fullTypeCheck*/ true));
|
||||
}
|
||||
|
||||
function getRuleProvider(options: FormatCodeOptions) {
|
||||
// Ensure rules are initialized and up to date wrt to formatting options
|
||||
if (!ruleProvider) {
|
||||
ruleProvider = new ts.formatting.RulesProvider(host);
|
||||
}
|
||||
|
||||
ruleProvider.ensureUpToDate(options);
|
||||
return ruleProvider;
|
||||
}
|
||||
|
||||
function createCompilerHost(): CompilerHost {
|
||||
return {
|
||||
getSourceFile: (filename, languageVersion) => {
|
||||
|
@ -5044,7 +5054,7 @@ module ts {
|
|||
}
|
||||
}
|
||||
|
||||
if (isPunctuation(token)) {
|
||||
if (isPunctuation(token.kind)) {
|
||||
// the '=' in a variable declaration is special cased here.
|
||||
if (token.parent.kind === SyntaxKind.BinaryExpression ||
|
||||
token.parent.kind === SyntaxKind.VariableDeclaration ||
|
||||
|
@ -5190,62 +5200,39 @@ module ts {
|
|||
host.log("getIndentationAtPosition: getCurrentSourceFile: " + (new Date().getTime() - start));
|
||||
|
||||
var start = new Date().getTime();
|
||||
var options = new TypeScript.FormattingOptions(!editorOptions.ConvertTabsToSpaces, editorOptions.TabSize, editorOptions.IndentSize, editorOptions.NewLineCharacter)
|
||||
|
||||
var result = formatting.SmartIndenter.getIndentation(position, sourceFile, options);
|
||||
var result = formatting.SmartIndenter.getIndentation(position, sourceFile, editorOptions);
|
||||
host.log("getIndentationAtPosition: computeIndentation : " + (new Date().getTime() - start));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getFormattingManager(filename: string, options: FormatCodeOptions) {
|
||||
// Ensure rules are initialized and up to date wrt to formatting options
|
||||
if (formattingRulesProvider == null) {
|
||||
formattingRulesProvider = new TypeScript.Services.Formatting.RulesProvider(host);
|
||||
}
|
||||
|
||||
formattingRulesProvider.ensureUpToDate(options);
|
||||
|
||||
// Get the Syntax Tree
|
||||
var syntaxTree = getSyntaxTree(filename);
|
||||
|
||||
// Convert IScriptSnapshot to ITextSnapshot
|
||||
var scriptSnapshot = syntaxTreeCache.getCurrentScriptSnapshot(filename);
|
||||
var scriptText = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot);
|
||||
var textSnapshot = new TypeScript.Services.Formatting.TextSnapshot(scriptText);
|
||||
|
||||
var manager = new TypeScript.Services.Formatting.FormattingManager(syntaxTree, textSnapshot, formattingRulesProvider, options);
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
function getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions): TextChange[] {
|
||||
fileName = normalizeSlashes(fileName);
|
||||
|
||||
var manager = getFormattingManager(fileName, options);
|
||||
return manager.formatSelection(start, end);
|
||||
var sourceFile = getCurrentSourceFile(fileName);
|
||||
return formatting.formatSelection(start, end, sourceFile, getRuleProvider(options), options);
|
||||
}
|
||||
|
||||
function getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions): TextChange[] {
|
||||
fileName = normalizeSlashes(fileName);
|
||||
|
||||
var manager = getFormattingManager(fileName, options);
|
||||
return manager.formatDocument();
|
||||
var sourceFile = getCurrentSourceFile(fileName);
|
||||
return formatting.formatDocument(sourceFile, getRuleProvider(options), options);
|
||||
}
|
||||
|
||||
function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): TextChange[] {
|
||||
fileName = normalizeSlashes(fileName);
|
||||
|
||||
var manager = getFormattingManager(fileName, options);
|
||||
var sourceFile = getCurrentSourceFile(fileName);
|
||||
|
||||
if (key === "}") {
|
||||
return manager.formatOnClosingCurlyBrace(position);
|
||||
return formatting.formatOnClosingCurly(position, sourceFile, getRuleProvider(options), options);
|
||||
}
|
||||
else if (key === ";") {
|
||||
return manager.formatOnSemicolon(position);
|
||||
return formatting.formatOnSemicolon(position, sourceFile, getRuleProvider(options), options);
|
||||
}
|
||||
else if (key === "\n") {
|
||||
return manager.formatOnEnter(position);
|
||||
return formatting.formatOnEnter(position, sourceFile, getRuleProvider(options), options);
|
||||
}
|
||||
|
||||
return [];
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
///<reference path='..\services.ts' />
|
||||
///<reference path='services.ts' />
|
||||
|
||||
module ts.formatting {
|
||||
export module SmartIndenter {
|
||||
export function getIndentation(position: number, sourceFile: SourceFile, options: TypeScript.FormattingOptions): number {
|
||||
export function getIndentation(position: number, sourceFile: SourceFile, options: EditorOptions): number {
|
||||
if (position > sourceFile.text.length) {
|
||||
return 0; // past EOF
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ module ts.formatting {
|
|||
}
|
||||
|
||||
// no indentation in string \regex literals
|
||||
if ((precedingToken.kind === SyntaxKind.StringLiteral || precedingToken.kind === SyntaxKind.RegularExpressionLiteral) &&
|
||||
precedingToken.getStart(sourceFile) <= position &&
|
||||
if ((precedingToken.kind === SyntaxKind.StringLiteral || precedingToken.kind === SyntaxKind.RegularExpressionLiteral) &&
|
||||
precedingToken.getStart(sourceFile) <= position &&
|
||||
precedingToken.end > position) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -37,14 +37,14 @@ module ts.formatting {
|
|||
var indentationDelta: number;
|
||||
|
||||
while (current) {
|
||||
if (positionBelongsToNode(current, position, sourceFile) && nodeContentIsIndented(current, previous)) {
|
||||
if (positionBelongsToNode(current, position, sourceFile) && shouldIndentChildNode(current.kind, previous ? previous.kind : SyntaxKind.Unknown)) {
|
||||
currentStart = getStartLineAndCharacterForNode(current, sourceFile);
|
||||
|
||||
if (nextTokenIsCurlyBraceOnSameLineAsCursor(precedingToken, current, lineAtPosition, sourceFile)) {
|
||||
indentationDelta = 0;
|
||||
}
|
||||
else {
|
||||
indentationDelta = lineAtPosition !== currentStart.line ? options.indentSpaces : 0;
|
||||
indentationDelta = lineAtPosition !== currentStart.line ? options.IndentSize : 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -59,12 +59,27 @@ module ts.formatting {
|
|||
previous = current;
|
||||
current = current.parent;
|
||||
}
|
||||
|
||||
|
||||
if (!current) {
|
||||
// no parent was found - return 0 to be indented on the level of SourceFile
|
||||
return 0;
|
||||
}
|
||||
|
||||
return getIndentationForNodeWorker(current, currentStart, /*ignoreActualIndentationRange*/ undefined, indentationDelta, sourceFile, options);
|
||||
}
|
||||
|
||||
export function getIndentationForNode(n: Node, ignoreActualIndentationRange: TextRange, sourceFile: SourceFile, options: FormatCodeOptions): number {
|
||||
var start = sourceFile.getLineAndCharacterFromPosition(n.getStart(sourceFile));
|
||||
return getIndentationForNodeWorker(n, start, ignoreActualIndentationRange, /*indentationDelta*/ 0, sourceFile, options);
|
||||
}
|
||||
|
||||
function getIndentationForNodeWorker(
|
||||
current: Node,
|
||||
currentStart: LineAndCharacter,
|
||||
ignoreActualIndentationRange: TextRange,
|
||||
indentationDelta: number,
|
||||
sourceFile: SourceFile,
|
||||
options: EditorOptions): number {
|
||||
|
||||
var parent: Node = current.parent;
|
||||
var parentStart: LineAndCharacter;
|
||||
|
@ -72,26 +87,35 @@ module ts.formatting {
|
|||
// walk upwards and collect indentations for pairs of parent-child nodes
|
||||
// indentation is not added if parent and child nodes start on the same line or if parent is IfStatement and child starts on the same line with 'else clause'
|
||||
while (parent) {
|
||||
// check if current node is a list item - if yes, take indentation from it
|
||||
var actualIndentation = getActualIndentationForListItem(current, sourceFile, options);
|
||||
if (actualIndentation !== -1) {
|
||||
return actualIndentation + indentationDelta;
|
||||
var useActualIndentation = true;
|
||||
if (ignoreActualIndentationRange) {
|
||||
var start = current.getStart(sourceFile);
|
||||
useActualIndentation = start < ignoreActualIndentationRange.pos || start > ignoreActualIndentationRange.end;
|
||||
}
|
||||
|
||||
parentStart = sourceFile.getLineAndCharacterFromPosition(parent.getStart(sourceFile));
|
||||
var parentAndChildShareLine =
|
||||
parentStart.line === currentStart.line ||
|
||||
if (useActualIndentation) {
|
||||
// check if current node is a list item - if yes, take indentation from it
|
||||
var actualIndentation = getActualIndentationForListItem(current, sourceFile, options);
|
||||
if (actualIndentation !== -1) {
|
||||
return actualIndentation + indentationDelta;
|
||||
}
|
||||
}
|
||||
parentStart = getParentStart(parent, current, sourceFile);
|
||||
var parentAndChildShareLine =
|
||||
parentStart.line === currentStart.line ||
|
||||
childStartsOnTheSameLineWithElseInIfStatement(parent, current, currentStart.line, sourceFile);
|
||||
|
||||
// try to fetch actual indentation for current node from source text
|
||||
var actualIndentation = getActualIndentationForNode(current, parent, currentStart, parentAndChildShareLine, sourceFile, options);
|
||||
if (actualIndentation !== -1) {
|
||||
return actualIndentation + indentationDelta;
|
||||
if (useActualIndentation) {
|
||||
// try to fetch actual indentation for current node from source text
|
||||
var actualIndentation = getActualIndentationForNode(current, parent, currentStart, parentAndChildShareLine, sourceFile, options);
|
||||
if (actualIndentation !== -1) {
|
||||
return actualIndentation + indentationDelta;
|
||||
}
|
||||
}
|
||||
|
||||
// increase indentation if parent node wants its content to be indented and parent and child nodes don't start on the same line
|
||||
if (nodeContentIsIndented(parent, current) && !parentAndChildShareLine) {
|
||||
indentationDelta += options.indentSpaces;
|
||||
if (shouldIndentChildNode(parent.kind, current.kind) && !parentAndChildShareLine) {
|
||||
indentationDelta += options.IndentSize;
|
||||
}
|
||||
|
||||
current = parent;
|
||||
|
@ -102,10 +126,20 @@ module ts.formatting {
|
|||
return indentationDelta;
|
||||
}
|
||||
|
||||
|
||||
function getParentStart(parent: Node, child: Node, sourceFile: SourceFile): LineAndCharacter {
|
||||
var containingList = getContainingList(child, sourceFile);
|
||||
if (containingList) {
|
||||
return sourceFile.getLineAndCharacterFromPosition(containingList.pos);
|
||||
}
|
||||
|
||||
return sourceFile.getLineAndCharacterFromPosition(parent.getStart(sourceFile));
|
||||
}
|
||||
|
||||
/*
|
||||
* Function returns -1 if indentation cannot be determined
|
||||
*/
|
||||
function getActualIndentationForListItemBeforeComma(commaToken: Node, sourceFile: SourceFile, options: TypeScript.FormattingOptions): number {
|
||||
*/
|
||||
function getActualIndentationForListItemBeforeComma(commaToken: Node, sourceFile: SourceFile, options: EditorOptions): number {
|
||||
// previous token is comma that separates items in list - find the previous item and try to derive indentation from it
|
||||
var commaItemInfo = findListItemInfo(commaToken);
|
||||
Debug.assert(commaItemInfo && commaItemInfo.listItemIndex > 0);
|
||||
|
@ -116,20 +150,20 @@ module ts.formatting {
|
|||
/*
|
||||
* Function returns -1 if actual indentation for node should not be used (i.e because node is nested expression)
|
||||
*/
|
||||
function getActualIndentationForNode(current: Node,
|
||||
parent: Node,
|
||||
currentLineAndChar: LineAndCharacter,
|
||||
parentAndChildShareLine: boolean,
|
||||
sourceFile: SourceFile,
|
||||
options: TypeScript.FormattingOptions): number {
|
||||
function getActualIndentationForNode(current: Node,
|
||||
parent: Node,
|
||||
currentLineAndChar: LineAndCharacter,
|
||||
parentAndChildShareLine: boolean,
|
||||
sourceFile: SourceFile,
|
||||
options: EditorOptions): number {
|
||||
|
||||
// actual indentation is used for statements\declarations if one of cases below is true:
|
||||
// - parent is SourceFile - by default immediate children of SourceFile are not indented except when user indents them manually
|
||||
// - parent and child are not on the same line
|
||||
var useActualIndentation =
|
||||
var useActualIndentation =
|
||||
(isDeclaration(current) || isStatement(current)) &&
|
||||
(parent.kind === SyntaxKind.SourceFile || !parentAndChildShareLine);
|
||||
|
||||
|
||||
if (!useActualIndentation) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -142,7 +176,7 @@ module ts.formatting {
|
|||
if (!nextToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (nextToken.kind === SyntaxKind.OpenBraceToken) {
|
||||
// open braces are always indented at the parent level
|
||||
return true;
|
||||
|
@ -172,30 +206,30 @@ module ts.formatting {
|
|||
return candidate.end > position || !isCompletedNode(candidate, sourceFile);
|
||||
}
|
||||
|
||||
function childStartsOnTheSameLineWithElseInIfStatement(parent: Node, child: Node, childStartLine: number, sourceFile: SourceFile): boolean {
|
||||
export function childStartsOnTheSameLineWithElseInIfStatement(parent: Node, child: TextRangeWithKind, childStartLine: number, sourceFile: SourceFile): boolean {
|
||||
if (parent.kind === SyntaxKind.IfStatement && (<IfStatement>parent).elseStatement === child) {
|
||||
var elseKeyword = findChildOfKind(parent, SyntaxKind.ElseKeyword, sourceFile);
|
||||
Debug.assert(elseKeyword !== undefined);
|
||||
|
||||
var elseKeywordStartLine = getStartLineAndCharacterForNode(elseKeyword, sourceFile).line;
|
||||
var elseKeywordStartLine = getStartLineAndCharacterForNode(elseKeyword, sourceFile).line;
|
||||
return elseKeywordStartLine === childStartLine;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getActualIndentationForListItem(node: Node, sourceFile: SourceFile, options: TypeScript.FormattingOptions): number {
|
||||
function getContainingList(node: Node, sourceFile: SourceFile): NodeArray<Node> {
|
||||
if (node.parent) {
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.TypeReference:
|
||||
if ((<TypeReferenceNode>node.parent).typeArguments) {
|
||||
return getActualIndentationFromList((<TypeReferenceNode>node.parent).typeArguments);
|
||||
return (<TypeReferenceNode>node.parent).typeArguments;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ObjectLiteral:
|
||||
return getActualIndentationFromList((<ObjectLiteral>node.parent).properties);
|
||||
case SyntaxKind.TypeLiteral:
|
||||
return getActualIndentationFromList((<TypeLiteralNode>node.parent).members);
|
||||
return (<ObjectLiteral>node.parent).properties;
|
||||
case SyntaxKind.ArrayLiteral:
|
||||
return getActualIndentationFromList((<ArrayLiteral>node.parent).elements);
|
||||
return (<ArrayLiteral>node.parent).elements;
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
|
@ -203,21 +237,26 @@ module ts.formatting {
|
|||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
if ((<SignatureDeclaration>node.parent).typeParameters && node.end < (<SignatureDeclaration>node.parent).typeParameters.end) {
|
||||
return getActualIndentationFromList((<SignatureDeclaration>node.parent).typeParameters);
|
||||
return (<SignatureDeclaration>node.parent).typeParameters;
|
||||
}
|
||||
|
||||
return getActualIndentationFromList((<SignatureDeclaration>node.parent).parameters);
|
||||
|
||||
return (<SignatureDeclaration>node.parent).parameters;
|
||||
case SyntaxKind.NewExpression:
|
||||
case SyntaxKind.CallExpression:
|
||||
if ((<CallExpression>node.parent).typeArguments && node.end < (<CallExpression>node.parent).typeArguments.end) {
|
||||
return getActualIndentationFromList((<CallExpression>node.parent).typeArguments);
|
||||
return (<CallExpression>node.parent).typeArguments;
|
||||
}
|
||||
|
||||
return getActualIndentationFromList((<CallExpression>node.parent).arguments);
|
||||
|
||||
return (<CallExpression>node.parent).arguments;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getActualIndentationForListItem(node: Node, sourceFile: SourceFile, options: EditorOptions): number {
|
||||
var containingList = getContainingList(node, sourceFile);
|
||||
return containingList ? getActualIndentationFromList(containingList) : -1;
|
||||
|
||||
function getActualIndentationFromList(list: Node[]): number {
|
||||
var index = indexOf(list, node);
|
||||
|
@ -226,13 +265,13 @@ module ts.formatting {
|
|||
}
|
||||
|
||||
|
||||
function deriveActualIndentationFromList(list: Node[], index: number, sourceFile: SourceFile, options: TypeScript.FormattingOptions): number {
|
||||
function deriveActualIndentationFromList(list: Node[], index: number, sourceFile: SourceFile, options: EditorOptions): number {
|
||||
Debug.assert(index >= 0 && index < list.length);
|
||||
var node = list[index];
|
||||
|
||||
// walk toward the start of the list starting from current node and check if the line is the same for all items.
|
||||
// if end line for item [i - 1] differs from the start line for item [i] - find column of the first non-whitespace character on the line of item [i]
|
||||
var lineAndCharacter = getStartLineAndCharacterForNode(node, sourceFile);
|
||||
var lineAndCharacter = getStartLineAndCharacterForNode(node, sourceFile);
|
||||
for (var i = index - 1; i >= 0; --i) {
|
||||
if (list[i].kind === SyntaxKind.CommaToken) {
|
||||
continue;
|
||||
|
@ -248,53 +287,34 @@ module ts.formatting {
|
|||
return -1;
|
||||
}
|
||||
|
||||
function findColumnForFirstNonWhitespaceCharacterInLine(lineAndCharacter: LineAndCharacter, sourceFile: SourceFile, options: TypeScript.FormattingOptions): number {
|
||||
function findColumnForFirstNonWhitespaceCharacterInLine(lineAndCharacter: LineAndCharacter, sourceFile: SourceFile, options: EditorOptions): number {
|
||||
var lineStart = sourceFile.getPositionFromLineAndCharacter(lineAndCharacter.line, 1);
|
||||
return findFirstNonWhitespaceColumn(lineStart, lineStart + lineAndCharacter.character, sourceFile, options);
|
||||
}
|
||||
|
||||
export function findFirstNonWhitespaceColumn(startPos: number, endPos: number, sourceFile: SourceFile, options: EditorOptions): number {
|
||||
var column = 0;
|
||||
for (var i = 0; i < lineAndCharacter.character; ++i) {
|
||||
var charCode = sourceFile.text.charCodeAt(lineStart + i);
|
||||
if (!isWhiteSpace(charCode)) {
|
||||
for (var pos = startPos; pos < endPos; ++pos) {
|
||||
var ch = sourceFile.text.charCodeAt(pos);
|
||||
if (!isWhiteSpace(ch)) {
|
||||
return column;
|
||||
}
|
||||
|
||||
if (charCode === CharacterCodes.tab) {
|
||||
column += options.spacesPerTab;
|
||||
if (ch === CharacterCodes.tab) {
|
||||
column += options.TabSize + (column % options.TabSize);
|
||||
}
|
||||
else {
|
||||
column++;
|
||||
}
|
||||
}
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
function nodeContentIsIndented(parent: Node, child: Node): boolean {
|
||||
switch (parent.kind) {
|
||||
function nodeContentIsAlwaysIndented(kind: SyntaxKind): boolean {
|
||||
switch (kind) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
return true;
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
// ModuleBlock should take care of indentation
|
||||
return false;
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.Method:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.Constructor:
|
||||
// FunctionBlock should take care of indentation
|
||||
return false;
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForStatement:
|
||||
return child && child.kind !== SyntaxKind.Block;
|
||||
case SyntaxKind.IfStatement:
|
||||
return child && child.kind !== SyntaxKind.Block;
|
||||
case SyntaxKind.TryStatement:
|
||||
// TryBlock\CatchBlock\FinallyBlock should take care of indentation
|
||||
return false;
|
||||
case SyntaxKind.ArrayLiteral:
|
||||
case SyntaxKind.Block:
|
||||
case SyntaxKind.FunctionBlock:
|
||||
|
@ -312,7 +332,32 @@ module ts.formatting {
|
|||
case SyntaxKind.NewExpression:
|
||||
case SyntaxKind.VariableStatement:
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.ExportAssignment:
|
||||
case SyntaxKind.ReturnStatement:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function shouldIndentChildNode(parent: SyntaxKind, child: SyntaxKind): boolean {
|
||||
if (nodeContentIsAlwaysIndented(parent)) {
|
||||
return true;
|
||||
}
|
||||
switch (parent) {
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.IfStatement:
|
||||
return child !== SyntaxKind.Block;
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.Method:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return child !== SyntaxKind.FunctionBlock;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -368,7 +413,7 @@ module ts.formatting {
|
|||
if ((<IfStatement>n).elseStatement) {
|
||||
return isCompletedNode((<IfStatement>n).elseStatement, sourceFile);
|
||||
}
|
||||
return isCompletedNode((<IfStatement>n).thenStatement, sourceFile);
|
||||
return isCompletedNode((<IfStatement>n).thenStatement, sourceFile);
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
return isCompletedNode((<ExpressionStatement>n).expression, sourceFile);
|
||||
case SyntaxKind.ArrayLiteral:
|
||||
|
@ -384,10 +429,10 @@ module ts.formatting {
|
|||
case SyntaxKind.DoStatement:
|
||||
// rough approximation: if DoStatement has While keyword - then if node is completed is checking the presence of ')';
|
||||
var hasWhileKeyword = findChildOfKind(n, SyntaxKind.WhileKeyword, sourceFile);
|
||||
if(hasWhileKeyword) {
|
||||
if (hasWhileKeyword) {
|
||||
return nodeEndsWith(n, SyntaxKind.CloseParenToken, sourceFile);
|
||||
}
|
||||
return isCompletedNode((<DoStatement>n).statement, sourceFile);
|
||||
return isCompletedNode((<DoStatement>n).statement, sourceFile);
|
||||
default:
|
||||
return true;
|
||||
}
|
|
@ -599,30 +599,31 @@ var TypeScript;
|
|||
SyntaxKind[SyntaxKind["OmittedExpression"] = 187] = "OmittedExpression";
|
||||
SyntaxKind[SyntaxKind["TemplateExpression"] = 188] = "TemplateExpression";
|
||||
SyntaxKind[SyntaxKind["TemplateAccessExpression"] = 189] = "TemplateAccessExpression";
|
||||
SyntaxKind[SyntaxKind["VariableDeclaration"] = 190] = "VariableDeclaration";
|
||||
SyntaxKind[SyntaxKind["VariableDeclarator"] = 191] = "VariableDeclarator";
|
||||
SyntaxKind[SyntaxKind["ArgumentList"] = 192] = "ArgumentList";
|
||||
SyntaxKind[SyntaxKind["ParameterList"] = 193] = "ParameterList";
|
||||
SyntaxKind[SyntaxKind["TypeArgumentList"] = 194] = "TypeArgumentList";
|
||||
SyntaxKind[SyntaxKind["TypeParameterList"] = 195] = "TypeParameterList";
|
||||
SyntaxKind[SyntaxKind["HeritageClause"] = 196] = "HeritageClause";
|
||||
SyntaxKind[SyntaxKind["EqualsValueClause"] = 197] = "EqualsValueClause";
|
||||
SyntaxKind[SyntaxKind["CaseSwitchClause"] = 198] = "CaseSwitchClause";
|
||||
SyntaxKind[SyntaxKind["DefaultSwitchClause"] = 199] = "DefaultSwitchClause";
|
||||
SyntaxKind[SyntaxKind["ElseClause"] = 200] = "ElseClause";
|
||||
SyntaxKind[SyntaxKind["CatchClause"] = 201] = "CatchClause";
|
||||
SyntaxKind[SyntaxKind["FinallyClause"] = 202] = "FinallyClause";
|
||||
SyntaxKind[SyntaxKind["TemplateClause"] = 203] = "TemplateClause";
|
||||
SyntaxKind[SyntaxKind["TypeParameter"] = 204] = "TypeParameter";
|
||||
SyntaxKind[SyntaxKind["Constraint"] = 205] = "Constraint";
|
||||
SyntaxKind[SyntaxKind["SimplePropertyAssignment"] = 206] = "SimplePropertyAssignment";
|
||||
SyntaxKind[SyntaxKind["FunctionPropertyAssignment"] = 207] = "FunctionPropertyAssignment";
|
||||
SyntaxKind[SyntaxKind["Parameter"] = 208] = "Parameter";
|
||||
SyntaxKind[SyntaxKind["EnumElement"] = 209] = "EnumElement";
|
||||
SyntaxKind[SyntaxKind["TypeAnnotation"] = 210] = "TypeAnnotation";
|
||||
SyntaxKind[SyntaxKind["ComputedPropertyName"] = 211] = "ComputedPropertyName";
|
||||
SyntaxKind[SyntaxKind["ExternalModuleReference"] = 212] = "ExternalModuleReference";
|
||||
SyntaxKind[SyntaxKind["ModuleNameModuleReference"] = 213] = "ModuleNameModuleReference";
|
||||
SyntaxKind[SyntaxKind["YieldExpression"] = 190] = "YieldExpression";
|
||||
SyntaxKind[SyntaxKind["VariableDeclaration"] = 191] = "VariableDeclaration";
|
||||
SyntaxKind[SyntaxKind["VariableDeclarator"] = 192] = "VariableDeclarator";
|
||||
SyntaxKind[SyntaxKind["ArgumentList"] = 193] = "ArgumentList";
|
||||
SyntaxKind[SyntaxKind["ParameterList"] = 194] = "ParameterList";
|
||||
SyntaxKind[SyntaxKind["TypeArgumentList"] = 195] = "TypeArgumentList";
|
||||
SyntaxKind[SyntaxKind["TypeParameterList"] = 196] = "TypeParameterList";
|
||||
SyntaxKind[SyntaxKind["HeritageClause"] = 197] = "HeritageClause";
|
||||
SyntaxKind[SyntaxKind["EqualsValueClause"] = 198] = "EqualsValueClause";
|
||||
SyntaxKind[SyntaxKind["CaseSwitchClause"] = 199] = "CaseSwitchClause";
|
||||
SyntaxKind[SyntaxKind["DefaultSwitchClause"] = 200] = "DefaultSwitchClause";
|
||||
SyntaxKind[SyntaxKind["ElseClause"] = 201] = "ElseClause";
|
||||
SyntaxKind[SyntaxKind["CatchClause"] = 202] = "CatchClause";
|
||||
SyntaxKind[SyntaxKind["FinallyClause"] = 203] = "FinallyClause";
|
||||
SyntaxKind[SyntaxKind["TemplateClause"] = 204] = "TemplateClause";
|
||||
SyntaxKind[SyntaxKind["TypeParameter"] = 205] = "TypeParameter";
|
||||
SyntaxKind[SyntaxKind["Constraint"] = 206] = "Constraint";
|
||||
SyntaxKind[SyntaxKind["SimplePropertyAssignment"] = 207] = "SimplePropertyAssignment";
|
||||
SyntaxKind[SyntaxKind["FunctionPropertyAssignment"] = 208] = "FunctionPropertyAssignment";
|
||||
SyntaxKind[SyntaxKind["Parameter"] = 209] = "Parameter";
|
||||
SyntaxKind[SyntaxKind["EnumElement"] = 210] = "EnumElement";
|
||||
SyntaxKind[SyntaxKind["TypeAnnotation"] = 211] = "TypeAnnotation";
|
||||
SyntaxKind[SyntaxKind["ComputedPropertyName"] = 212] = "ComputedPropertyName";
|
||||
SyntaxKind[SyntaxKind["ExternalModuleReference"] = 213] = "ExternalModuleReference";
|
||||
SyntaxKind[SyntaxKind["ModuleNameModuleReference"] = 214] = "ModuleNameModuleReference";
|
||||
SyntaxKind[SyntaxKind["FirstStandardKeyword"] = SyntaxKind.BreakKeyword] = "FirstStandardKeyword";
|
||||
SyntaxKind[SyntaxKind["LastStandardKeyword"] = SyntaxKind.WithKeyword] = "LastStandardKeyword";
|
||||
SyntaxKind[SyntaxKind["FirstFutureReservedKeyword"] = SyntaxKind.ClassKeyword] = "FirstFutureReservedKeyword";
|
||||
|
@ -910,9 +911,9 @@ var definitions = [
|
|||
baseType: 'ISyntaxNode',
|
||||
interfaces: ['IModuleReferenceSyntax'],
|
||||
children: [
|
||||
{ name: 'requireKeyword', isToken: true, tokenKinds: ['RequireKeyword'], excludeFromAST: true },
|
||||
{ name: 'requireKeyword', isToken: true, excludeFromAST: true },
|
||||
{ name: 'openParenToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'stringLiteral', isToken: true, tokenKinds: ['StringLiteral'] },
|
||||
{ name: 'stringLiteral', isToken: true },
|
||||
{ name: 'closeParenToken', isToken: true, excludeFromAST: true }
|
||||
],
|
||||
isTypeScriptSpecific: true
|
||||
|
@ -933,7 +934,7 @@ var definitions = [
|
|||
children: [
|
||||
{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken' },
|
||||
{ name: 'importKeyword', isToken: true, excludeFromAST: true },
|
||||
{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'identifier', isToken: true },
|
||||
{ name: 'equalsToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'moduleReference', type: 'IModuleReferenceSyntax' },
|
||||
{ name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true }
|
||||
|
@ -947,7 +948,7 @@ var definitions = [
|
|||
children: [
|
||||
{ name: 'exportKeyword', isToken: true, excludeFromAST: true },
|
||||
{ name: 'equalsToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'identifier', isToken: true },
|
||||
{ name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true }
|
||||
],
|
||||
isTypeScriptSpecific: true
|
||||
|
@ -959,7 +960,7 @@ var definitions = [
|
|||
children: [
|
||||
{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken' },
|
||||
{ name: 'classKeyword', isToken: true, excludeFromAST: true },
|
||||
{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'identifier', isToken: true },
|
||||
{ name: 'typeParameterList', type: 'TypeParameterListSyntax', isOptional: true },
|
||||
{ name: 'heritageClauses', isList: true, elementType: 'HeritageClauseSyntax' },
|
||||
{ name: 'openBraceToken', isToken: true, excludeFromAST: true },
|
||||
|
@ -975,7 +976,7 @@ var definitions = [
|
|||
children: [
|
||||
{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken' },
|
||||
{ name: 'interfaceKeyword', isToken: true, excludeFromAST: true },
|
||||
{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'identifier', isToken: true },
|
||||
{ name: 'typeParameterList', type: 'TypeParameterListSyntax', isOptional: true },
|
||||
{ name: 'heritageClauses', isList: true, elementType: 'HeritageClauseSyntax' },
|
||||
{ name: 'body', type: 'ObjectTypeSyntax' }
|
||||
|
@ -1012,7 +1013,8 @@ var definitions = [
|
|||
children: [
|
||||
{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken', isTypeScriptSpecific: true },
|
||||
{ name: 'functionKeyword', isToken: true, excludeFromAST: true },
|
||||
{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'asterixToken', isToken: true, isOptional: true },
|
||||
{ name: 'identifier', isToken: true },
|
||||
{ name: 'callSignature', type: 'CallSignatureSyntax' },
|
||||
{ name: 'body', type: 'BlockSyntax | ISyntaxToken', isOptional: true }
|
||||
]
|
||||
|
@ -1116,7 +1118,7 @@ var definitions = [
|
|||
children: [
|
||||
{ name: 'left', type: 'INameSyntax' },
|
||||
{ name: 'dotToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'right', isToken: true, tokenKinds: ['IdentifierName'] }
|
||||
{ name: 'right', isToken: true }
|
||||
],
|
||||
isTypeScriptSpecific: true
|
||||
},
|
||||
|
@ -1255,7 +1257,7 @@ var definitions = [
|
|||
children: [
|
||||
{ name: 'dotDotDotToken', isToken: true, isOptional: true, isTypeScriptSpecific: true },
|
||||
{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken' },
|
||||
{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'identifier', isToken: true },
|
||||
{ name: 'questionToken', isToken: true, isOptional: true, isTypeScriptSpecific: true },
|
||||
{ name: 'typeAnnotation', type: 'TypeAnnotationSyntax', isOptional: true, isTypeScriptSpecific: true },
|
||||
{ name: 'equalsValueClause', type: 'EqualsValueClauseSyntax', isOptional: true, isTypeScriptSpecific: true }
|
||||
|
@ -1268,7 +1270,7 @@ var definitions = [
|
|||
children: [
|
||||
{ name: 'expression', type: 'ILeftHandSideExpressionSyntax' },
|
||||
{ name: 'dotToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'name', isToken: true, tokenKinds: ['IdentifierName'] }
|
||||
{ name: 'name', isToken: true }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -1434,7 +1436,7 @@ var definitions = [
|
|||
name: 'TypeParameterSyntax',
|
||||
baseType: 'ISyntaxNode',
|
||||
children: [
|
||||
{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'identifier', isToken: true },
|
||||
{ name: 'constraint', type: 'ConstraintSyntax', isOptional: true }
|
||||
],
|
||||
isTypeScriptSpecific: true
|
||||
|
@ -1496,6 +1498,7 @@ var definitions = [
|
|||
interfaces: ['IMemberDeclarationSyntax'],
|
||||
children: [
|
||||
{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken' },
|
||||
{ name: 'asterixToken', isToken: true, isOptional: true },
|
||||
{ name: 'propertyName', type: 'IPropertyNameSyntax' },
|
||||
{ name: 'callSignature', type: 'CallSignatureSyntax' },
|
||||
{ name: 'body', type: 'BlockSyntax | ISyntaxToken', isOptional: true }
|
||||
|
@ -1620,7 +1623,7 @@ var definitions = [
|
|||
interfaces: ['IStatementSyntax'],
|
||||
children: [
|
||||
{ name: 'breakKeyword', isToken: true },
|
||||
{ name: 'identifier', isToken: true, isOptional: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'identifier', isToken: true, isOptional: true },
|
||||
{ name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true }
|
||||
]
|
||||
},
|
||||
|
@ -1630,7 +1633,7 @@ var definitions = [
|
|||
interfaces: ['IStatementSyntax'],
|
||||
children: [
|
||||
{ name: 'continueKeyword', isToken: true },
|
||||
{ name: 'identifier', isToken: true, isOptional: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'identifier', isToken: true, isOptional: true },
|
||||
{ name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true }
|
||||
]
|
||||
},
|
||||
|
@ -1642,9 +1645,9 @@ var definitions = [
|
|||
{ name: 'forKeyword', isToken: true, excludeFromAST: true },
|
||||
{ name: 'openParenToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'initializer', type: 'VariableDeclarationSyntax | IExpressionSyntax', isOptional: true },
|
||||
{ name: 'firstSemicolonToken', isToken: true, tokenKinds: ['SemicolonToken'], excludeFromAST: true },
|
||||
{ name: 'firstSemicolonToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'condition', type: 'IExpressionSyntax', isOptional: true },
|
||||
{ name: 'secondSemicolonToken', isToken: true, tokenKinds: ['SemicolonToken'], excludeFromAST: true },
|
||||
{ name: 'secondSemicolonToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'incrementor', type: 'IExpressionSyntax', isOptional: true },
|
||||
{ name: 'closeParenToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'statement', type: 'IStatementSyntax' }
|
||||
|
@ -1695,7 +1698,7 @@ var definitions = [
|
|||
children: [
|
||||
{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken' },
|
||||
{ name: 'enumKeyword', isToken: true, excludeFromAST: true },
|
||||
{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'identifier', isToken: true },
|
||||
{ name: 'openBraceToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'enumElements', isSeparatedList: true, elementType: 'EnumElementSyntax' },
|
||||
{ name: 'closeBraceToken', isToken: true, excludeFromAST: true }
|
||||
|
@ -1757,6 +1760,7 @@ var definitions = [
|
|||
baseType: 'ISyntaxNode',
|
||||
interfaces: ['IPropertyAssignmentSyntax'],
|
||||
children: [
|
||||
{ name: 'asterixToken', isToken: true, isOptional: true },
|
||||
{ name: 'propertyName', type: 'IPropertyNameSyntax' },
|
||||
{ name: 'callSignature', type: 'CallSignatureSyntax' },
|
||||
{ name: 'block', type: 'BlockSyntax' }
|
||||
|
@ -1768,7 +1772,8 @@ var definitions = [
|
|||
interfaces: ['IPrimaryExpressionSyntax'],
|
||||
children: [
|
||||
{ name: 'functionKeyword', isToken: true, excludeFromAST: true },
|
||||
{ name: 'identifier', isToken: true, isOptional: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'asterixToken', isToken: true, isOptional: true },
|
||||
{ name: 'identifier', isToken: true, isOptional: true },
|
||||
{ name: 'callSignature', type: 'CallSignatureSyntax' },
|
||||
{ name: 'block', type: 'BlockSyntax' }
|
||||
]
|
||||
|
@ -1798,7 +1803,7 @@ var definitions = [
|
|||
children: [
|
||||
{ name: 'catchKeyword', isToken: true, excludeFromAST: true },
|
||||
{ name: 'openParenToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'identifier', isToken: true },
|
||||
{ name: 'typeAnnotation', type: 'TypeAnnotationSyntax', isOptional: true, isTypeScriptSpecified: true },
|
||||
{ name: 'closeParenToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'block', type: 'BlockSyntax' }
|
||||
|
@ -1817,7 +1822,7 @@ var definitions = [
|
|||
baseType: 'ISyntaxNode',
|
||||
interfaces: ['IStatementSyntax'],
|
||||
children: [
|
||||
{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
{ name: 'identifier', isToken: true },
|
||||
{ name: 'colonToken', isToken: true, excludeFromAST: true },
|
||||
{ name: 'statement', type: 'IStatementSyntax' }
|
||||
]
|
||||
|
@ -1863,6 +1868,16 @@ var definitions = [
|
|||
{ name: 'expression', type: 'IUnaryExpressionSyntax' }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'YieldExpressionSyntax',
|
||||
baseType: 'ISyntaxNode',
|
||||
interfaces: ['IExpressionSyntax'],
|
||||
children: [
|
||||
{ name: 'yieldKeyword', isToken: true },
|
||||
{ name: 'asterixToken', isToken: true, isOptional: true },
|
||||
{ name: 'expression', type: 'IExpressionSyntax', isOptional: true }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'DebuggerStatementSyntax',
|
||||
baseType: 'ISyntaxNode',
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,7 @@
|
|||
///<reference path='references.ts' />
|
||||
|
||||
module TypeScript {
|
||||
export enum SyntaxConstants {
|
||||
export enum SyntaxNodeConstants {
|
||||
None = 0,
|
||||
|
||||
// Masks that we use to place information about a node into a single int. The first bit tells
|
||||
|
@ -15,13 +15,15 @@ module TypeScript {
|
|||
// only be used by the incremental parser if it is parsed in the same strict context as before.
|
||||
// last masks off the part of the int
|
||||
//
|
||||
// The width of the node is stored in the remainder of the int. This allows us up to 256MB
|
||||
// for a node by using all 28 bits. However, in the common case, we'll use less than 28 bits
|
||||
// The width of the node is stored in the remainder of the int. This allows us up to 128MB
|
||||
// for a node by using all 27 bits. However, in the common case, we'll use less than 27 bits
|
||||
// for the width. Thus, the info will be stored in a single int in chakra.
|
||||
NodeDataComputed = 0x00000001, // 0000 0000 0000 0000 0000 0000 0000 0001
|
||||
NodeIncrementallyUnusableMask = 0x00000002, // 0000 0000 0000 0000 0000 0000 0000 0010
|
||||
NodeParsedInStrictModeMask = 0x00000004, // 0000 0000 0000 0000 0000 0000 0000 0100
|
||||
NodeParsedInDisallowInMask = 0x00000008, // 0000 0000 0000 0000 0000 0000 0000 1000
|
||||
NodeFullWidthShift = 4, // 1111 1111 1111 1111 1111 1111 1111 0000
|
||||
DataComputed = 0x00000001, // 0000 0000 0000 0000 0000 0000 0000 0001
|
||||
IncrementallyUnusableMask = 0x00000002, // 0000 0000 0000 0000 0000 0000 0000 0010
|
||||
ParsedInStrictModeContext = 0x00000004, // 0000 0000 0000 0000 0000 0000 0000 0100
|
||||
ParsedInDisallowInContext = 0x00000008, // 0000 0000 0000 0000 0000 0000 0000 1000
|
||||
ParsedInYieldContext = 0x00000010, // 0000 0000 0000 0000 0000 0000 0001 0000
|
||||
ParsedInGeneratorParameterContext = 0x00000020, // 0000 0000 0000 0000 0000 0000 0010 0000
|
||||
FullWidthShift = 1 << 6, // 1111 1111 1111 1111 1111 1111 1100 0000
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1018,6 +1018,12 @@ module TypeScript.PrettyPrinter {
|
|||
visitNodeOrToken(this, node.expression);
|
||||
}
|
||||
|
||||
public visitYieldExpression(node: YieldExpressionSyntax): void {
|
||||
this.appendToken(node.yieldKeyword);
|
||||
this.ensureSpace();
|
||||
visitNodeOrToken(this, node.expression);
|
||||
}
|
||||
|
||||
public visitDebuggerStatement(node: DebuggerStatementSyntax): void {
|
||||
this.appendToken(node.debuggerKeyword);
|
||||
this.appendToken(node.semicolonToken);
|
||||
|
|
|
@ -17,22 +17,40 @@ module TypeScript {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
export function parsedInStrictMode(node: ISyntaxNode): boolean {
|
||||
export function parsedInStrictModeContext(node: ISyntaxNode): boolean {
|
||||
var info = node.__data;
|
||||
if (info === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (info & SyntaxConstants.NodeParsedInStrictModeMask) !== 0;
|
||||
return (info & SyntaxNodeConstants.ParsedInStrictModeContext) !== 0;
|
||||
}
|
||||
|
||||
export function parsedInDisallowInMode(node: ISyntaxNode): boolean {
|
||||
export function parsedInDisallowInContext(node: ISyntaxNode): boolean {
|
||||
var info = node.__data;
|
||||
if (info === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (info & SyntaxConstants.NodeParsedInDisallowInMask) !== 0;
|
||||
return (info & SyntaxNodeConstants.ParsedInDisallowInContext) !== 0;
|
||||
}
|
||||
|
||||
export function parsedInYieldContext(node: ISyntaxNode): boolean {
|
||||
var info = node.__data;
|
||||
if (info === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (info & SyntaxNodeConstants.ParsedInYieldContext) !== 0;
|
||||
}
|
||||
|
||||
export function parsedInGeneratorParameterContext(node: ISyntaxNode): boolean {
|
||||
var info = node.__data;
|
||||
if (info === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (info & SyntaxNodeConstants.ParsedInGeneratorParameterContext) !== 0;
|
||||
}
|
||||
|
||||
export function previousToken(token: ISyntaxToken): ISyntaxToken {
|
||||
|
@ -266,7 +284,7 @@ module TypeScript {
|
|||
}
|
||||
|
||||
var info = data(element);
|
||||
return info >>> SyntaxConstants.NodeFullWidthShift;
|
||||
return (info / SyntaxNodeConstants.FullWidthShift) | 0;
|
||||
}
|
||||
|
||||
export function isIncrementallyUnusable(element: ISyntaxElement): boolean {
|
||||
|
@ -274,7 +292,7 @@ module TypeScript {
|
|||
return (<ISyntaxToken>element).isIncrementallyUnusable();
|
||||
}
|
||||
|
||||
return (data(element) & SyntaxConstants.NodeIncrementallyUnusableMask) !== 0;
|
||||
return (data(element) & SyntaxNodeConstants.IncrementallyUnusableMask) !== 0;
|
||||
}
|
||||
|
||||
function data(element: ISyntaxElement): number {
|
||||
|
@ -288,7 +306,7 @@ module TypeScript {
|
|||
info = 0;
|
||||
}
|
||||
|
||||
if ((info & SyntaxConstants.NodeDataComputed) === 0) {
|
||||
if ((info & SyntaxNodeConstants.DataComputed) === 0) {
|
||||
info |= computeData(element);
|
||||
dataElement.__data = info;
|
||||
}
|
||||
|
@ -297,9 +315,9 @@ module TypeScript {
|
|||
}
|
||||
|
||||
function combineData(fullWidth: number, isIncrementallyUnusable: boolean) {
|
||||
return (fullWidth << SyntaxConstants.NodeFullWidthShift)
|
||||
| (isIncrementallyUnusable ? SyntaxConstants.NodeIncrementallyUnusableMask : 0)
|
||||
| SyntaxConstants.NodeDataComputed;
|
||||
return (fullWidth * SyntaxNodeConstants.FullWidthShift) +
|
||||
(isIncrementallyUnusable ? SyntaxNodeConstants.IncrementallyUnusableMask : 0) +
|
||||
SyntaxNodeConstants.DataComputed;
|
||||
}
|
||||
|
||||
function listComputeData(list: ISyntaxNodeOrToken[]): number {
|
||||
|
|
|
@ -23,7 +23,6 @@ interface IMemberDefinition {
|
|||
isSeparatedList?: boolean;
|
||||
requiresAtLeastOneItem?: boolean;
|
||||
isOptional?: boolean;
|
||||
tokenKinds?: string[];
|
||||
isTypeScriptSpecific: boolean;
|
||||
elementType?: string;
|
||||
}
|
||||
|
@ -56,9 +55,9 @@ var definitions:ITypeDefinition[] = [
|
|||
baseType: 'ISyntaxNode',
|
||||
interfaces: ['IModuleReferenceSyntax'],
|
||||
children: [
|
||||
<any>{ name: 'requireKeyword', isToken: true, tokenKinds: ['RequireKeyword'], excludeFromAST: true },
|
||||
<any>{ name: 'requireKeyword', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'openParenToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'stringLiteral', isToken: true, tokenKinds: ['StringLiteral'] },
|
||||
<any>{ name: 'stringLiteral', isToken: true },
|
||||
<any>{ name: 'closeParenToken', isToken: true, excludeFromAST: true }
|
||||
],
|
||||
isTypeScriptSpecific: true
|
||||
|
@ -79,7 +78,7 @@ var definitions:ITypeDefinition[] = [
|
|||
children: [
|
||||
<any>{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken' },
|
||||
<any>{ name: 'importKeyword', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'identifier', isToken: true },
|
||||
<any>{ name: 'equalsToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'moduleReference', type: 'IModuleReferenceSyntax' },
|
||||
<any>{ name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true }
|
||||
|
@ -93,7 +92,7 @@ var definitions:ITypeDefinition[] = [
|
|||
children: [
|
||||
<any>{ name: 'exportKeyword', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'equalsToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'identifier', isToken: true },
|
||||
<any>{ name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true }
|
||||
],
|
||||
isTypeScriptSpecific: true
|
||||
|
@ -105,7 +104,7 @@ var definitions:ITypeDefinition[] = [
|
|||
children: [
|
||||
<any>{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken' },
|
||||
<any>{ name: 'classKeyword', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'identifier', isToken: true },
|
||||
<any>{ name: 'typeParameterList', type: 'TypeParameterListSyntax', isOptional: true },
|
||||
<any>{ name: 'heritageClauses', isList: true, elementType: 'HeritageClauseSyntax' },
|
||||
<any>{ name: 'openBraceToken', isToken: true, excludeFromAST: true },
|
||||
|
@ -121,7 +120,7 @@ var definitions:ITypeDefinition[] = [
|
|||
children: [
|
||||
<any>{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken' },
|
||||
<any>{ name: 'interfaceKeyword', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'identifier', isToken: true },
|
||||
<any>{ name: 'typeParameterList', type: 'TypeParameterListSyntax', isOptional: true },
|
||||
<any>{ name: 'heritageClauses', isList: true, elementType: 'HeritageClauseSyntax' },
|
||||
<any>{ name: 'body', type: 'ObjectTypeSyntax' }
|
||||
|
@ -158,7 +157,8 @@ var definitions:ITypeDefinition[] = [
|
|||
children: [
|
||||
<any>{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken', isTypeScriptSpecific: true },
|
||||
<any>{ name: 'functionKeyword', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'asterixToken', isToken: true, isOptional: true },
|
||||
<any>{ name: 'identifier', isToken: true },
|
||||
<any>{ name: 'callSignature', type: 'CallSignatureSyntax' },
|
||||
<any>{ name: 'body', type: 'BlockSyntax | ISyntaxToken', isOptional: true }
|
||||
]
|
||||
|
@ -262,7 +262,7 @@ var definitions:ITypeDefinition[] = [
|
|||
children: [
|
||||
<any>{ name: 'left', type: 'INameSyntax' },
|
||||
<any>{ name: 'dotToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'right', isToken: true, tokenKinds:['IdentifierName'] }
|
||||
<any>{ name: 'right', isToken: true }
|
||||
],
|
||||
// Qualified names only show up in Types, which are TypeScript specific. Note that a dotted
|
||||
// expression (like A.B.Foo()) is a MemberAccessExpression, not a QualifiedName.
|
||||
|
@ -403,7 +403,7 @@ var definitions:ITypeDefinition[] = [
|
|||
children: [
|
||||
<any>{ name: 'dotDotDotToken', isToken: true, isOptional: true, isTypeScriptSpecific: true },
|
||||
<any>{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken' },
|
||||
<any>{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'identifier', isToken: true },
|
||||
<any>{ name: 'questionToken', isToken: true, isOptional: true, isTypeScriptSpecific: true },
|
||||
<any>{ name: 'typeAnnotation', type: 'TypeAnnotationSyntax', isOptional: true, isTypeScriptSpecific: true },
|
||||
<any>{ name: 'equalsValueClause', type: 'EqualsValueClauseSyntax', isOptional: true, isTypeScriptSpecific: true }
|
||||
|
@ -416,7 +416,7 @@ var definitions:ITypeDefinition[] = [
|
|||
children: [
|
||||
<any>{ name: 'expression', type: 'ILeftHandSideExpressionSyntax' },
|
||||
<any>{ name: 'dotToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'name', isToken: true, tokenKinds: ['IdentifierName'] }
|
||||
<any>{ name: 'name', isToken: true }
|
||||
]
|
||||
},
|
||||
<any>{
|
||||
|
@ -582,7 +582,7 @@ var definitions:ITypeDefinition[] = [
|
|||
name: 'TypeParameterSyntax',
|
||||
baseType: 'ISyntaxNode',
|
||||
children: [
|
||||
<any>{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'identifier', isToken: true },
|
||||
<any>{ name: 'constraint', type: 'ConstraintSyntax', isOptional: true }
|
||||
],
|
||||
isTypeScriptSpecific: true
|
||||
|
@ -645,6 +645,7 @@ var definitions:ITypeDefinition[] = [
|
|||
interfaces: ['IMemberDeclarationSyntax'],
|
||||
children: [
|
||||
<any>{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken' },
|
||||
<any>{ name: 'asterixToken', isToken: true, isOptional: true },
|
||||
<any>{ name: 'propertyName', type: 'IPropertyNameSyntax' },
|
||||
<any>{ name: 'callSignature', type: 'CallSignatureSyntax' },
|
||||
<any>{ name: 'body', type: 'BlockSyntax | ISyntaxToken', isOptional: true }
|
||||
|
@ -769,7 +770,7 @@ var definitions:ITypeDefinition[] = [
|
|||
interfaces: ['IStatementSyntax'],
|
||||
children: [
|
||||
<any>{ name: 'breakKeyword', isToken: true },
|
||||
<any>{ name: 'identifier', isToken: true, isOptional: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'identifier', isToken: true, isOptional: true },
|
||||
<any>{ name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true }
|
||||
]
|
||||
},
|
||||
|
@ -779,7 +780,7 @@ var definitions:ITypeDefinition[] = [
|
|||
interfaces: ['IStatementSyntax'],
|
||||
children: [
|
||||
<any>{ name: 'continueKeyword', isToken: true },
|
||||
<any>{ name: 'identifier', isToken: true, isOptional: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'identifier', isToken: true, isOptional: true },
|
||||
<any>{ name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true }
|
||||
]
|
||||
},
|
||||
|
@ -791,9 +792,9 @@ var definitions:ITypeDefinition[] = [
|
|||
<any>{ name: 'forKeyword', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'openParenToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'initializer', type: 'VariableDeclarationSyntax | IExpressionSyntax', isOptional: true },
|
||||
<any>{ name: 'firstSemicolonToken', isToken: true, tokenKinds: ['SemicolonToken'], excludeFromAST: true },
|
||||
<any>{ name: 'firstSemicolonToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'condition', type: 'IExpressionSyntax', isOptional: true },
|
||||
<any>{ name: 'secondSemicolonToken', isToken: true, tokenKinds: ['SemicolonToken'], excludeFromAST: true },
|
||||
<any>{ name: 'secondSemicolonToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'incrementor', type: 'IExpressionSyntax', isOptional: true },
|
||||
<any>{ name: 'closeParenToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'statement', type: 'IStatementSyntax' }
|
||||
|
@ -844,7 +845,7 @@ var definitions:ITypeDefinition[] = [
|
|||
children: [
|
||||
<any>{ name: 'modifiers', isList: true, elementType: 'ISyntaxToken' },
|
||||
<any>{ name: 'enumKeyword', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'identifier', isToken: true },
|
||||
<any>{ name: 'openBraceToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'enumElements', isSeparatedList: true, elementType: 'EnumElementSyntax' },
|
||||
<any>{ name: 'closeBraceToken', isToken: true, excludeFromAST: true }
|
||||
|
@ -906,6 +907,7 @@ var definitions:ITypeDefinition[] = [
|
|||
baseType: 'ISyntaxNode',
|
||||
interfaces: ['IPropertyAssignmentSyntax'],
|
||||
children: [
|
||||
<any>{ name: 'asterixToken', isToken: true, isOptional: true },
|
||||
<any>{ name: 'propertyName', type: 'IPropertyNameSyntax' },
|
||||
<any>{ name: 'callSignature', type: 'CallSignatureSyntax' },
|
||||
<any>{ name: 'block', type: 'BlockSyntax' }
|
||||
|
@ -917,7 +919,8 @@ var definitions:ITypeDefinition[] = [
|
|||
interfaces: ['IPrimaryExpressionSyntax'],
|
||||
children: [
|
||||
<any>{ name: 'functionKeyword', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'identifier', isToken: true, isOptional: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'asterixToken', isToken: true, isOptional: true },
|
||||
<any>{ name: 'identifier', isToken: true, isOptional: true },
|
||||
<any>{ name: 'callSignature', type: 'CallSignatureSyntax' },
|
||||
<any>{ name: 'block', type: 'BlockSyntax' }]
|
||||
},
|
||||
|
@ -944,7 +947,7 @@ var definitions:ITypeDefinition[] = [
|
|||
children: [
|
||||
<any>{ name: 'catchKeyword', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'openParenToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'identifier', isToken: true },
|
||||
<any>{ name: 'typeAnnotation', type: 'TypeAnnotationSyntax', isOptional: true, isTypeScriptSpecified: true },
|
||||
<any>{ name: 'closeParenToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'block', type: 'BlockSyntax' }]
|
||||
|
@ -961,7 +964,7 @@ var definitions:ITypeDefinition[] = [
|
|||
baseType: 'ISyntaxNode',
|
||||
interfaces: ['IStatementSyntax'],
|
||||
children: [
|
||||
<any>{ name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] },
|
||||
<any>{ name: 'identifier', isToken: true },
|
||||
<any>{ name: 'colonToken', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'statement', type: 'IStatementSyntax' }]
|
||||
},
|
||||
|
@ -1002,6 +1005,15 @@ var definitions:ITypeDefinition[] = [
|
|||
<any>{ name: 'voidKeyword', isToken: true, excludeFromAST: true },
|
||||
<any>{ name: 'expression', type: 'IUnaryExpressionSyntax' }]
|
||||
},
|
||||
<any>{
|
||||
name: 'YieldExpressionSyntax',
|
||||
baseType: 'ISyntaxNode',
|
||||
interfaces: ['IExpressionSyntax'],
|
||||
children: [
|
||||
<any>{ name: 'yieldKeyword', isToken: true },
|
||||
<any>{ name: 'asterixToken', isToken: true, isOptional: true },
|
||||
<any>{ name: 'expression', type: 'IExpressionSyntax', isOptional: true }]
|
||||
},
|
||||
<any>{
|
||||
name: 'DebuggerStatementSyntax',
|
||||
baseType: 'ISyntaxNode',
|
||||
|
|
|
@ -92,11 +92,12 @@ module TypeScript {
|
|||
export interface FunctionDeclarationSyntax extends ISyntaxNode, IStatementSyntax {
|
||||
modifiers: ISyntaxToken[];
|
||||
functionKeyword: ISyntaxToken;
|
||||
asterixToken: ISyntaxToken;
|
||||
identifier: ISyntaxToken;
|
||||
callSignature: CallSignatureSyntax;
|
||||
body: BlockSyntax | ISyntaxToken;
|
||||
}
|
||||
export interface FunctionDeclarationConstructor { new (data: number, modifiers: ISyntaxToken[], functionKeyword: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, body: BlockSyntax | ISyntaxToken): FunctionDeclarationSyntax }
|
||||
export interface FunctionDeclarationConstructor { new (data: number, modifiers: ISyntaxToken[], functionKeyword: ISyntaxToken, asterixToken: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, body: BlockSyntax | ISyntaxToken): FunctionDeclarationSyntax }
|
||||
|
||||
export interface ModuleDeclarationSyntax extends ISyntaxNode, IModuleElementSyntax {
|
||||
modifiers: ISyntaxToken[];
|
||||
|
@ -150,11 +151,12 @@ module TypeScript {
|
|||
|
||||
export interface MemberFunctionDeclarationSyntax extends ISyntaxNode, IMemberDeclarationSyntax {
|
||||
modifiers: ISyntaxToken[];
|
||||
asterixToken: ISyntaxToken;
|
||||
propertyName: IPropertyNameSyntax;
|
||||
callSignature: CallSignatureSyntax;
|
||||
body: BlockSyntax | ISyntaxToken;
|
||||
}
|
||||
export interface MemberFunctionDeclarationConstructor { new (data: number, modifiers: ISyntaxToken[], propertyName: IPropertyNameSyntax, callSignature: CallSignatureSyntax, body: BlockSyntax | ISyntaxToken): MemberFunctionDeclarationSyntax }
|
||||
export interface MemberFunctionDeclarationConstructor { new (data: number, modifiers: ISyntaxToken[], asterixToken: ISyntaxToken, propertyName: IPropertyNameSyntax, callSignature: CallSignatureSyntax, body: BlockSyntax | ISyntaxToken): MemberFunctionDeclarationSyntax }
|
||||
|
||||
export interface MemberVariableDeclarationSyntax extends ISyntaxNode, IMemberDeclarationSyntax {
|
||||
modifiers: ISyntaxToken[];
|
||||
|
@ -498,11 +500,12 @@ module TypeScript {
|
|||
|
||||
export interface FunctionExpressionSyntax extends ISyntaxNode, IPrimaryExpressionSyntax {
|
||||
functionKeyword: ISyntaxToken;
|
||||
asterixToken: ISyntaxToken;
|
||||
identifier: ISyntaxToken;
|
||||
callSignature: CallSignatureSyntax;
|
||||
block: BlockSyntax;
|
||||
}
|
||||
export interface FunctionExpressionConstructor { new (data: number, functionKeyword: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax): FunctionExpressionSyntax }
|
||||
export interface FunctionExpressionConstructor { new (data: number, functionKeyword: ISyntaxToken, asterixToken: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax): FunctionExpressionSyntax }
|
||||
|
||||
export interface OmittedExpressionSyntax extends ISyntaxNode, IExpressionSyntax {
|
||||
}
|
||||
|
@ -520,6 +523,13 @@ module TypeScript {
|
|||
}
|
||||
export interface TemplateAccessExpressionConstructor { new (data: number, expression: ILeftHandSideExpressionSyntax, templateExpression: IPrimaryExpressionSyntax): TemplateAccessExpressionSyntax }
|
||||
|
||||
export interface YieldExpressionSyntax extends ISyntaxNode, IExpressionSyntax {
|
||||
yieldKeyword: ISyntaxToken;
|
||||
asterixToken: ISyntaxToken;
|
||||
expression: IExpressionSyntax;
|
||||
}
|
||||
export interface YieldExpressionConstructor { new (data: number, yieldKeyword: ISyntaxToken, asterixToken: ISyntaxToken, expression: IExpressionSyntax): YieldExpressionSyntax }
|
||||
|
||||
export interface VariableDeclarationSyntax extends ISyntaxNode {
|
||||
varKeyword: ISyntaxToken;
|
||||
variableDeclarators: ISeparatedSyntaxList<VariableDeclaratorSyntax>;
|
||||
|
@ -637,11 +647,12 @@ module TypeScript {
|
|||
export interface SimplePropertyAssignmentConstructor { new (data: number, propertyName: IPropertyNameSyntax, colonToken: ISyntaxToken, expression: IExpressionSyntax): SimplePropertyAssignmentSyntax }
|
||||
|
||||
export interface FunctionPropertyAssignmentSyntax extends ISyntaxNode, IPropertyAssignmentSyntax {
|
||||
asterixToken: ISyntaxToken;
|
||||
propertyName: IPropertyNameSyntax;
|
||||
callSignature: CallSignatureSyntax;
|
||||
block: BlockSyntax;
|
||||
}
|
||||
export interface FunctionPropertyAssignmentConstructor { new (data: number, propertyName: IPropertyNameSyntax, callSignature: CallSignatureSyntax, block: BlockSyntax): FunctionPropertyAssignmentSyntax }
|
||||
export interface FunctionPropertyAssignmentConstructor { new (data: number, asterixToken: ISyntaxToken, propertyName: IPropertyNameSyntax, callSignature: CallSignatureSyntax, block: BlockSyntax): FunctionPropertyAssignmentSyntax }
|
||||
|
||||
export interface ParameterSyntax extends ISyntaxNode {
|
||||
dotDotDotToken: ISyntaxToken;
|
||||
|
|
|
@ -234,6 +234,7 @@ module TypeScript {
|
|||
OmittedExpression,
|
||||
TemplateExpression,
|
||||
TemplateAccessExpression,
|
||||
YieldExpression,
|
||||
|
||||
// Variable declarations
|
||||
VariableDeclaration,
|
||||
|
|
|
@ -238,28 +238,31 @@ module TypeScript {
|
|||
}
|
||||
}
|
||||
|
||||
export var FunctionDeclarationSyntax: FunctionDeclarationConstructor = <any>function(data: number, modifiers: ISyntaxToken[], functionKeyword: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, body: BlockSyntax | ISyntaxToken) {
|
||||
export var FunctionDeclarationSyntax: FunctionDeclarationConstructor = <any>function(data: number, modifiers: ISyntaxToken[], functionKeyword: ISyntaxToken, asterixToken: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, body: BlockSyntax | ISyntaxToken) {
|
||||
if (data) { this.__data = data; }
|
||||
this.modifiers = modifiers,
|
||||
this.functionKeyword = functionKeyword,
|
||||
this.asterixToken = asterixToken,
|
||||
this.identifier = identifier,
|
||||
this.callSignature = callSignature,
|
||||
this.body = body,
|
||||
modifiers.parent = this,
|
||||
functionKeyword.parent = this,
|
||||
asterixToken && (asterixToken.parent = this),
|
||||
identifier.parent = this,
|
||||
callSignature.parent = this,
|
||||
body && (body.parent = this);
|
||||
};
|
||||
FunctionDeclarationSyntax.prototype.kind = SyntaxKind.FunctionDeclaration;
|
||||
FunctionDeclarationSyntax.prototype.childCount = 5;
|
||||
FunctionDeclarationSyntax.prototype.childCount = 6;
|
||||
FunctionDeclarationSyntax.prototype.childAt = function(index: number): ISyntaxElement {
|
||||
switch (index) {
|
||||
case 0: return this.modifiers;
|
||||
case 1: return this.functionKeyword;
|
||||
case 2: return this.identifier;
|
||||
case 3: return this.callSignature;
|
||||
case 4: return this.body;
|
||||
case 2: return this.asterixToken;
|
||||
case 3: return this.identifier;
|
||||
case 4: return this.callSignature;
|
||||
case 5: return this.body;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -403,25 +406,28 @@ module TypeScript {
|
|||
}
|
||||
}
|
||||
|
||||
export var MemberFunctionDeclarationSyntax: MemberFunctionDeclarationConstructor = <any>function(data: number, modifiers: ISyntaxToken[], propertyName: IPropertyNameSyntax, callSignature: CallSignatureSyntax, body: BlockSyntax | ISyntaxToken) {
|
||||
export var MemberFunctionDeclarationSyntax: MemberFunctionDeclarationConstructor = <any>function(data: number, modifiers: ISyntaxToken[], asterixToken: ISyntaxToken, propertyName: IPropertyNameSyntax, callSignature: CallSignatureSyntax, body: BlockSyntax | ISyntaxToken) {
|
||||
if (data) { this.__data = data; }
|
||||
this.modifiers = modifiers,
|
||||
this.asterixToken = asterixToken,
|
||||
this.propertyName = propertyName,
|
||||
this.callSignature = callSignature,
|
||||
this.body = body,
|
||||
modifiers.parent = this,
|
||||
asterixToken && (asterixToken.parent = this),
|
||||
propertyName.parent = this,
|
||||
callSignature.parent = this,
|
||||
body && (body.parent = this);
|
||||
};
|
||||
MemberFunctionDeclarationSyntax.prototype.kind = SyntaxKind.MemberFunctionDeclaration;
|
||||
MemberFunctionDeclarationSyntax.prototype.childCount = 4;
|
||||
MemberFunctionDeclarationSyntax.prototype.childCount = 5;
|
||||
MemberFunctionDeclarationSyntax.prototype.childAt = function(index: number): ISyntaxElement {
|
||||
switch (index) {
|
||||
case 0: return this.modifiers;
|
||||
case 1: return this.propertyName;
|
||||
case 2: return this.callSignature;
|
||||
case 3: return this.body;
|
||||
case 1: return this.asterixToken;
|
||||
case 2: return this.propertyName;
|
||||
case 3: return this.callSignature;
|
||||
case 4: return this.body;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1355,25 +1361,28 @@ module TypeScript {
|
|||
}
|
||||
}
|
||||
|
||||
export var FunctionExpressionSyntax: FunctionExpressionConstructor = <any>function(data: number, functionKeyword: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax) {
|
||||
export var FunctionExpressionSyntax: FunctionExpressionConstructor = <any>function(data: number, functionKeyword: ISyntaxToken, asterixToken: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax) {
|
||||
if (data) { this.__data = data; }
|
||||
this.functionKeyword = functionKeyword,
|
||||
this.asterixToken = asterixToken,
|
||||
this.identifier = identifier,
|
||||
this.callSignature = callSignature,
|
||||
this.block = block,
|
||||
functionKeyword.parent = this,
|
||||
asterixToken && (asterixToken.parent = this),
|
||||
identifier && (identifier.parent = this),
|
||||
callSignature.parent = this,
|
||||
block.parent = this;
|
||||
};
|
||||
FunctionExpressionSyntax.prototype.kind = SyntaxKind.FunctionExpression;
|
||||
FunctionExpressionSyntax.prototype.childCount = 4;
|
||||
FunctionExpressionSyntax.prototype.childCount = 5;
|
||||
FunctionExpressionSyntax.prototype.childAt = function(index: number): ISyntaxElement {
|
||||
switch (index) {
|
||||
case 0: return this.functionKeyword;
|
||||
case 1: return this.identifier;
|
||||
case 2: return this.callSignature;
|
||||
case 3: return this.block;
|
||||
case 1: return this.asterixToken;
|
||||
case 2: return this.identifier;
|
||||
case 3: return this.callSignature;
|
||||
case 4: return this.block;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1418,6 +1427,25 @@ module TypeScript {
|
|||
}
|
||||
}
|
||||
|
||||
export var YieldExpressionSyntax: YieldExpressionConstructor = <any>function(data: number, yieldKeyword: ISyntaxToken, asterixToken: ISyntaxToken, expression: IExpressionSyntax) {
|
||||
if (data) { this.__data = data; }
|
||||
this.yieldKeyword = yieldKeyword,
|
||||
this.asterixToken = asterixToken,
|
||||
this.expression = expression,
|
||||
yieldKeyword.parent = this,
|
||||
asterixToken && (asterixToken.parent = this),
|
||||
expression && (expression.parent = this);
|
||||
};
|
||||
YieldExpressionSyntax.prototype.kind = SyntaxKind.YieldExpression;
|
||||
YieldExpressionSyntax.prototype.childCount = 3;
|
||||
YieldExpressionSyntax.prototype.childAt = function(index: number): ISyntaxElement {
|
||||
switch (index) {
|
||||
case 0: return this.yieldKeyword;
|
||||
case 1: return this.asterixToken;
|
||||
case 2: return this.expression;
|
||||
}
|
||||
}
|
||||
|
||||
export var VariableDeclarationSyntax: VariableDeclarationConstructor = <any>function(data: number, varKeyword: ISyntaxToken, variableDeclarators: ISeparatedSyntaxList<VariableDeclaratorSyntax>) {
|
||||
if (data) { this.__data = data; }
|
||||
this.varKeyword = varKeyword,
|
||||
|
@ -1732,22 +1760,25 @@ module TypeScript {
|
|||
}
|
||||
}
|
||||
|
||||
export var FunctionPropertyAssignmentSyntax: FunctionPropertyAssignmentConstructor = <any>function(data: number, propertyName: IPropertyNameSyntax, callSignature: CallSignatureSyntax, block: BlockSyntax) {
|
||||
export var FunctionPropertyAssignmentSyntax: FunctionPropertyAssignmentConstructor = <any>function(data: number, asterixToken: ISyntaxToken, propertyName: IPropertyNameSyntax, callSignature: CallSignatureSyntax, block: BlockSyntax) {
|
||||
if (data) { this.__data = data; }
|
||||
this.asterixToken = asterixToken,
|
||||
this.propertyName = propertyName,
|
||||
this.callSignature = callSignature,
|
||||
this.block = block,
|
||||
asterixToken && (asterixToken.parent = this),
|
||||
propertyName.parent = this,
|
||||
callSignature.parent = this,
|
||||
block.parent = this;
|
||||
};
|
||||
FunctionPropertyAssignmentSyntax.prototype.kind = SyntaxKind.FunctionPropertyAssignment;
|
||||
FunctionPropertyAssignmentSyntax.prototype.childCount = 3;
|
||||
FunctionPropertyAssignmentSyntax.prototype.childCount = 4;
|
||||
FunctionPropertyAssignmentSyntax.prototype.childAt = function(index: number): ISyntaxElement {
|
||||
switch (index) {
|
||||
case 0: return this.propertyName;
|
||||
case 1: return this.callSignature;
|
||||
case 2: return this.block;
|
||||
case 0: return this.asterixToken;
|
||||
case 1: return this.propertyName;
|
||||
case 2: return this.callSignature;
|
||||
case 3: return this.block;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1297,7 +1297,7 @@ module TypeScript {
|
|||
}
|
||||
|
||||
private checkForWithInStrictMode(node: WithStatementSyntax): boolean {
|
||||
if (parsedInStrictMode(node)) {
|
||||
if (parsedInStrictModeContext(node)) {
|
||||
this.pushDiagnostic(firstToken(node), DiagnosticCode.with_statements_are_not_allowed_in_strict_mode);
|
||||
return true;
|
||||
}
|
||||
|
@ -1509,7 +1509,7 @@ module TypeScript {
|
|||
}
|
||||
|
||||
public visitPrefixUnaryExpression(node: PrefixUnaryExpressionSyntax): void {
|
||||
if (parsedInStrictMode(node) && this.isPreIncrementOrDecrementExpression(node) && this.isEvalOrArguments(node.operand)) {
|
||||
if (parsedInStrictModeContext(node) && this.isPreIncrementOrDecrementExpression(node) && this.isEvalOrArguments(node.operand)) {
|
||||
this.pushDiagnostic(node.operatorToken, DiagnosticCode.Invalid_use_of_0_in_strict_mode, [this.getEvalOrArguments(node.operand)]);
|
||||
}
|
||||
|
||||
|
@ -1517,7 +1517,7 @@ module TypeScript {
|
|||
}
|
||||
|
||||
public visitPostfixUnaryExpression(node: PostfixUnaryExpressionSyntax): void {
|
||||
if (parsedInStrictMode(node) && this.isEvalOrArguments(node.operand)) {
|
||||
if (parsedInStrictModeContext(node) && this.isEvalOrArguments(node.operand)) {
|
||||
this.pushDiagnostic(node.operatorToken, DiagnosticCode.Invalid_use_of_0_in_strict_mode, [this.getEvalOrArguments(node.operand)]);
|
||||
}
|
||||
|
||||
|
@ -1534,7 +1534,7 @@ module TypeScript {
|
|||
|
||||
private checkForDisallowedEvalOrArguments(node: ISyntaxNode, token: ISyntaxToken): boolean {
|
||||
if (token) {
|
||||
if (parsedInStrictMode(node) && this.isEvalOrArguments(token)) {
|
||||
if (parsedInStrictModeContext(node) && this.isEvalOrArguments(token)) {
|
||||
this.pushDiagnostic(token, DiagnosticCode.Invalid_use_of_0_in_strict_mode, [this.getEvalOrArguments(token)]);
|
||||
return true;
|
||||
}
|
||||
|
@ -1554,16 +1554,25 @@ module TypeScript {
|
|||
}
|
||||
|
||||
public visitDeleteExpression(node: DeleteExpressionSyntax): void {
|
||||
if (parsedInStrictMode(node) && node.expression.kind === SyntaxKind.IdentifierName) {
|
||||
this.pushDiagnostic(firstToken(node), DiagnosticCode.delete_cannot_be_called_on_an_identifier_in_strict_mode);
|
||||
if (parsedInStrictModeContext(node) && node.expression.kind === SyntaxKind.IdentifierName) {
|
||||
this.pushDiagnostic(node.deleteKeyword, DiagnosticCode.delete_cannot_be_called_on_an_identifier_in_strict_mode);
|
||||
return;
|
||||
}
|
||||
|
||||
super.visitDeleteExpression(node);
|
||||
}
|
||||
|
||||
public visitYieldExpression(node: YieldExpressionSyntax): void {
|
||||
if (!parsedInYieldContext(node)) {
|
||||
this.pushDiagnostic(node.yieldKeyword, DiagnosticCode.yield_expression_must_be_contained_within_a_generator_declaration);
|
||||
return;
|
||||
}
|
||||
|
||||
super.visitYieldExpression(node);
|
||||
}
|
||||
|
||||
private checkIllegalAssignment(node: BinaryExpressionSyntax): boolean {
|
||||
if (parsedInStrictMode(node) && SyntaxFacts.isAssignmentOperatorToken(node.operatorToken.kind) && this.isEvalOrArguments(node.left)) {
|
||||
if (parsedInStrictModeContext(node) && SyntaxFacts.isAssignmentOperatorToken(node.operatorToken.kind) && this.isEvalOrArguments(node.left)) {
|
||||
this.pushDiagnostic(node.operatorToken, DiagnosticCode.Invalid_use_of_0_in_strict_mode, [this.getEvalOrArguments(node.left)]);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@ module TypeScript {
|
|||
case SyntaxKind.OmittedExpression: return visitor.visitOmittedExpression(<OmittedExpressionSyntax>element);
|
||||
case SyntaxKind.TemplateExpression: return visitor.visitTemplateExpression(<TemplateExpressionSyntax>element);
|
||||
case SyntaxKind.TemplateAccessExpression: return visitor.visitTemplateAccessExpression(<TemplateAccessExpressionSyntax>element);
|
||||
case SyntaxKind.YieldExpression: return visitor.visitYieldExpression(<YieldExpressionSyntax>element);
|
||||
case SyntaxKind.VariableDeclaration: return visitor.visitVariableDeclaration(<VariableDeclarationSyntax>element);
|
||||
case SyntaxKind.VariableDeclarator: return visitor.visitVariableDeclarator(<VariableDeclaratorSyntax>element);
|
||||
case SyntaxKind.ArgumentList: return visitor.visitArgumentList(<ArgumentListSyntax>element);
|
||||
|
@ -170,6 +171,7 @@ module TypeScript {
|
|||
visitOmittedExpression(node: OmittedExpressionSyntax): any;
|
||||
visitTemplateExpression(node: TemplateExpressionSyntax): any;
|
||||
visitTemplateAccessExpression(node: TemplateAccessExpressionSyntax): any;
|
||||
visitYieldExpression(node: YieldExpressionSyntax): any;
|
||||
visitVariableDeclaration(node: VariableDeclarationSyntax): any;
|
||||
visitVariableDeclarator(node: VariableDeclaratorSyntax): any;
|
||||
visitArgumentList(node: ArgumentListSyntax): any;
|
||||
|
|
|
@ -97,6 +97,7 @@ module TypeScript {
|
|||
public visitFunctionDeclaration(node: FunctionDeclarationSyntax): void {
|
||||
this.visitList(node.modifiers);
|
||||
this.visitToken(node.functionKeyword);
|
||||
this.visitOptionalToken(node.asterixToken);
|
||||
this.visitToken(node.identifier);
|
||||
visitNodeOrToken(this, node.callSignature);
|
||||
visitNodeOrToken(this, node.body);
|
||||
|
@ -149,6 +150,7 @@ module TypeScript {
|
|||
|
||||
public visitMemberFunctionDeclaration(node: MemberFunctionDeclarationSyntax): void {
|
||||
this.visitList(node.modifiers);
|
||||
this.visitOptionalToken(node.asterixToken);
|
||||
visitNodeOrToken(this, node.propertyName);
|
||||
visitNodeOrToken(this, node.callSignature);
|
||||
visitNodeOrToken(this, node.body);
|
||||
|
@ -451,6 +453,7 @@ module TypeScript {
|
|||
|
||||
public visitFunctionExpression(node: FunctionExpressionSyntax): void {
|
||||
this.visitToken(node.functionKeyword);
|
||||
this.visitOptionalToken(node.asterixToken);
|
||||
this.visitOptionalToken(node.identifier);
|
||||
visitNodeOrToken(this, node.callSignature);
|
||||
visitNodeOrToken(this, node.block);
|
||||
|
@ -469,6 +472,12 @@ module TypeScript {
|
|||
visitNodeOrToken(this, node.templateExpression);
|
||||
}
|
||||
|
||||
public visitYieldExpression(node: YieldExpressionSyntax): void {
|
||||
this.visitToken(node.yieldKeyword);
|
||||
this.visitOptionalToken(node.asterixToken);
|
||||
visitNodeOrToken(this, node.expression);
|
||||
}
|
||||
|
||||
public visitVariableDeclaration(node: VariableDeclarationSyntax): void {
|
||||
this.visitToken(node.varKeyword);
|
||||
this.visitList(node.variableDeclarators);
|
||||
|
@ -569,6 +578,7 @@ module TypeScript {
|
|||
}
|
||||
|
||||
public visitFunctionPropertyAssignment(node: FunctionPropertyAssignmentSyntax): void {
|
||||
this.visitOptionalToken(node.asterixToken);
|
||||
visitNodeOrToken(this, node.propertyName);
|
||||
visitNodeOrToken(this, node.callSignature);
|
||||
visitNodeOrToken(this, node.block);
|
||||
|
|
|
@ -5,6 +5,66 @@ module ts {
|
|||
list: Node;
|
||||
}
|
||||
|
||||
export function getEndLinePosition(line: number, sourceFile: SourceFile): number {
|
||||
Debug.assert(line >= 1);
|
||||
var lineStarts = sourceFile.getLineStarts();
|
||||
|
||||
// lines returned by SourceFile.getLineAndCharacterForPosition are 1-based
|
||||
var lineIndex = line - 1;
|
||||
if (lineIndex === lineStarts.length - 1) {
|
||||
// last line - return EOF
|
||||
return sourceFile.text.length - 1;
|
||||
}
|
||||
else {
|
||||
// current line start
|
||||
var start = lineStarts[lineIndex];
|
||||
// take the start position of the next line -1 = it should be some line break
|
||||
var pos = lineStarts[lineIndex + 1] - 1;
|
||||
Debug.assert(isLineBreak(sourceFile.text.charCodeAt(pos)));
|
||||
// walk backwards skipping line breaks, stop the the beginning of current line.
|
||||
// i.e:
|
||||
// <some text>
|
||||
// $ <- end of line for this position should match the start position
|
||||
while (start <= pos && isLineBreak(sourceFile.text.charCodeAt(pos))) {
|
||||
pos--;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
export function getStartPositionOfLine(line: number, sourceFile: SourceFile): number {
|
||||
Debug.assert(line >= 1);
|
||||
return sourceFile.getLineStarts()[line - 1];
|
||||
}
|
||||
|
||||
export function getStartLinePositionForPosition(position: number, sourceFile: SourceFile): number {
|
||||
var lineStarts = sourceFile.getLineStarts();
|
||||
var line = sourceFile.getLineAndCharacterFromPosition(position).line;
|
||||
return lineStarts[line - 1];
|
||||
}
|
||||
|
||||
export function rangeContainsRange(r1: TextRange, r2: TextRange): boolean {
|
||||
return startEndContainsRange(r1.pos, r1.end, r2);
|
||||
}
|
||||
|
||||
export function startEndContainsRange(start: number, end: number, range: TextRange): boolean {
|
||||
return start <= range.pos && end >= range.end;
|
||||
}
|
||||
|
||||
export function rangeContainsStartEnd(range: TextRange, start: number, end: number): boolean {
|
||||
return range.pos <= start && range.end >= end;
|
||||
}
|
||||
|
||||
export function rangeOverlapsWithStartEnd(r1: TextRange, start: number, end: number) {
|
||||
return startEndOverlapsWithStartEnd(r1.pos, r1.end, start, end);
|
||||
}
|
||||
|
||||
export function startEndOverlapsWithStartEnd(start1: number, end1: number, start2: number, end2: number) {
|
||||
var start = Math.max(start1, start2);
|
||||
var end = Math.min(end1, end2);
|
||||
return start < end;
|
||||
}
|
||||
|
||||
export function findListItemInfo(node: Node): ListItemInfo {
|
||||
var syntaxList = findContainingList(node);
|
||||
|
||||
|
@ -63,14 +123,14 @@ module ts {
|
|||
* position >= start and (position < end or (position === end && token is keyword or identifier))
|
||||
*/
|
||||
export function getTouchingWord(sourceFile: SourceFile, position: number): Node {
|
||||
return getTouchingToken(sourceFile, position, isWord);
|
||||
return getTouchingToken(sourceFile, position, n => isWord(n.kind));
|
||||
}
|
||||
|
||||
/* Gets the token whose text has range [start, end) and position >= start
|
||||
* and (position < end or (position === end && token is keyword or identifier or numeric\string litera))
|
||||
*/
|
||||
export function getTouchingPropertyName(sourceFile: SourceFile, position: number): Node {
|
||||
return getTouchingToken(sourceFile, position, isPropertyName);
|
||||
return getTouchingToken(sourceFile, position, n => isPropertyName(n.kind));
|
||||
}
|
||||
|
||||
/** Returns the token if position is in [start, end) or if position === end and includeItemAtEndPosition(token) === true */
|
||||
|
@ -229,7 +289,7 @@ module ts {
|
|||
return n.getWidth() !== 0;
|
||||
}
|
||||
|
||||
export function getTypeArgumentOrTypeParameterList(node: Node): NodeArray<Node> {
|
||||
export function getTypeArgumentOrTypeParameterList(node: Node): NodeArray<Node> {
|
||||
if (node.kind === SyntaxKind.TypeReference || node.kind === SyntaxKind.CallExpression) {
|
||||
return (<CallExpression>node).typeArguments;
|
||||
}
|
||||
|
@ -245,19 +305,19 @@ module ts {
|
|||
return n.kind >= SyntaxKind.FirstToken && n.kind <= SyntaxKind.LastToken;
|
||||
}
|
||||
|
||||
function isWord(n: Node): boolean {
|
||||
return n.kind === SyntaxKind.Identifier || isKeyword(n.kind);
|
||||
function isWord(kind: SyntaxKind): boolean {
|
||||
return kind === SyntaxKind.Identifier || isKeyword(kind);
|
||||
}
|
||||
|
||||
function isPropertyName(n: Node): boolean {
|
||||
return n.kind === SyntaxKind.StringLiteral || n.kind === SyntaxKind.NumericLiteral || isWord(n);
|
||||
function isPropertyName(kind: SyntaxKind): boolean {
|
||||
return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NumericLiteral || isWord(kind);
|
||||
}
|
||||
|
||||
export function isComment(n: Node): boolean {
|
||||
return n.kind === SyntaxKind.SingleLineCommentTrivia || n.kind === SyntaxKind.MultiLineCommentTrivia;
|
||||
export function isComment(kind: SyntaxKind): boolean {
|
||||
return kind === SyntaxKind.SingleLineCommentTrivia || kind === SyntaxKind.MultiLineCommentTrivia;
|
||||
}
|
||||
|
||||
export function isPunctuation(n: Node): boolean {
|
||||
return SyntaxKind.FirstPunctuation <= n.kind && n.kind <= SyntaxKind.LastPunctuation;
|
||||
export function isPunctuation(kind: SyntaxKind): boolean {
|
||||
return SyntaxKind.FirstPunctuation <= kind && kind <= SyntaxKind.LastPunctuation;
|
||||
}
|
||||
}
|
|
@ -1,12 +1,10 @@
|
|||
error TS2318: Cannot find global type 'Boolean'.
|
||||
error TS2318: Cannot find global type 'IArguments'.
|
||||
error TS2318: Cannot find global type 'TemplateStringsArray'.
|
||||
tests/cases/compiler/noDefaultLib.ts(4,11): error TS2317: Global type 'Array' must have 1 type parameter(s).
|
||||
|
||||
|
||||
!!! error TS2318: Cannot find global type 'Boolean'.
|
||||
!!! error TS2318: Cannot find global type 'IArguments'.
|
||||
!!! error TS2318: Cannot find global type 'TemplateStringsArray'.
|
||||
==== tests/cases/compiler/noDefaultLib.ts (1 errors) ====
|
||||
/// <reference no-default-lib="true"/>
|
||||
var x;
|
||||
|
|
|
@ -6,7 +6,6 @@ error TS2318: Cannot find global type 'Number'.
|
|||
error TS2318: Cannot find global type 'Object'.
|
||||
error TS2318: Cannot find global type 'RegExp'.
|
||||
error TS2318: Cannot find global type 'String'.
|
||||
error TS2318: Cannot find global type 'TemplateStringsArray'.
|
||||
|
||||
|
||||
!!! error TS2318: Cannot find global type 'Array'.
|
||||
|
@ -17,7 +16,6 @@ error TS2318: Cannot find global type 'TemplateStringsArray'.
|
|||
!!! error TS2318: Cannot find global type 'Object'.
|
||||
!!! error TS2318: Cannot find global type 'RegExp'.
|
||||
!!! error TS2318: Cannot find global type 'String'.
|
||||
!!! error TS2318: Cannot find global type 'TemplateStringsArray'.
|
||||
==== tests/cases/conformance/parser/ecmascript5/RegressionTests/parser509698.ts (0 errors) ====
|
||||
/// <style requireSemi="on" />
|
||||
/// <reference no-default-lib="true"/>
|
||||
|
|
|
@ -6,7 +6,6 @@ error TS2318: Cannot find global type 'Number'.
|
|||
error TS2318: Cannot find global type 'Object'.
|
||||
error TS2318: Cannot find global type 'RegExp'.
|
||||
error TS2318: Cannot find global type 'String'.
|
||||
error TS2318: Cannot find global type 'TemplateStringsArray'.
|
||||
test.ts(3,8): error TS2304: Cannot find name 'Array'.
|
||||
|
||||
|
||||
|
@ -18,7 +17,6 @@ test.ts(3,8): error TS2304: Cannot find name 'Array'.
|
|||
!!! error TS2318: Cannot find global type 'Object'.
|
||||
!!! error TS2318: Cannot find global type 'RegExp'.
|
||||
!!! error TS2318: Cannot find global type 'String'.
|
||||
!!! error TS2318: Cannot find global type 'TemplateStringsArray'.
|
||||
==== test.ts (1 errors) ====
|
||||
/// <reference no-default-lib="true"/>
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ error TS2318: Cannot find global type 'Number'.
|
|||
error TS2318: Cannot find global type 'Object'.
|
||||
error TS2318: Cannot find global type 'RegExp'.
|
||||
error TS2318: Cannot find global type 'String'.
|
||||
error TS2318: Cannot find global type 'TemplateStringsArray'.
|
||||
test.ts(3,8): error TS2304: Cannot find name 'Array'.
|
||||
|
||||
|
||||
|
@ -18,7 +17,6 @@ test.ts(3,8): error TS2304: Cannot find name 'Array'.
|
|||
!!! error TS2318: Cannot find global type 'Object'.
|
||||
!!! error TS2318: Cannot find global type 'RegExp'.
|
||||
!!! error TS2318: Cannot find global type 'String'.
|
||||
!!! error TS2318: Cannot find global type 'TemplateStringsArray'.
|
||||
==== test.ts (1 errors) ====
|
||||
/// <reference no-default-lib="true"/>
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
tests/cases/compiler/templateStringsArrayTypeDefinedInES5Mode.ts(10,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
|
||||
lib.d.ts(502,11): error TS2300: Duplicate identifier 'TemplateStringsArray'.
|
||||
tests/cases/compiler/templateStringsArrayTypeDefinedInES5Mode.ts(2,7): error TS2300: Duplicate identifier 'TemplateStringsArray'.
|
||||
tests/cases/compiler/templateStringsArrayTypeDefinedInES5Mode.ts(8,3): error TS2345: Argument of type '{ [x: number]: undefined; }' is not assignable to parameter of type 'TemplateStringsArray'.
|
||||
Property 'raw' is missing in type '{ [x: number]: undefined; }'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/templateStringsArrayTypeDefinedInES5Mode.ts (3 errors) ====
|
||||
|
||||
class TemplateStringsArray {
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2300: Duplicate identifier 'TemplateStringsArray'.
|
||||
}
|
||||
|
||||
function f(x: TemplateStringsArray, y: number, z: number) {
|
||||
}
|
||||
|
||||
f({}, 10, 10);
|
||||
~~
|
||||
!!! error TS2345: Argument of type '{ [x: number]: undefined; }' is not assignable to parameter of type 'TemplateStringsArray'.
|
||||
!!! error TS2345: Property 'raw' is missing in type '{ [x: number]: undefined; }'.
|
||||
|
||||
f `abcdef${ 1234 }${ 5678 }ghijkl`;
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
|
|
@ -0,0 +1,18 @@
|
|||
tests/cases/compiler/templateStringsArrayTypeNotDefinedES5Mode.ts(7,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
|
||||
tests/cases/compiler/templateStringsArrayTypeNotDefinedES5Mode.ts(5,3): error TS2345: Argument of type '{ [x: number]: undefined; }' is not assignable to parameter of type 'TemplateStringsArray'.
|
||||
Property 'raw' is missing in type '{ [x: number]: undefined; }'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/templateStringsArrayTypeNotDefinedES5Mode.ts (2 errors) ====
|
||||
|
||||
function f(x: TemplateStringsArray, y: number, z: number) {
|
||||
}
|
||||
|
||||
f({}, 10, 10);
|
||||
~~
|
||||
!!! error TS2345: Argument of type '{ [x: number]: undefined; }' is not assignable to parameter of type 'TemplateStringsArray'.
|
||||
!!! error TS2345: Property 'raw' is missing in type '{ [x: number]: undefined; }'.
|
||||
|
||||
f `abcdef${ 1234 }${ 5678 }ghijkl`;
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
|
|
@ -0,0 +1,22 @@
|
|||
lib.d.ts(502,11): error TS2300: Duplicate identifier 'TemplateStringsArray'.
|
||||
tests/cases/compiler/templateStringsArrayTypeRedefinedInES6Mode.ts(2,7): error TS2300: Duplicate identifier 'TemplateStringsArray'.
|
||||
tests/cases/compiler/templateStringsArrayTypeRedefinedInES6Mode.ts(8,3): error TS2345: Argument of type '{ [x: number]: undefined; }' is not assignable to parameter of type 'TemplateStringsArray'.
|
||||
Property 'raw' is missing in type '{ [x: number]: undefined; }'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/templateStringsArrayTypeRedefinedInES6Mode.ts (2 errors) ====
|
||||
|
||||
class TemplateStringsArray {
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2300: Duplicate identifier 'TemplateStringsArray'.
|
||||
}
|
||||
|
||||
function f(x: TemplateStringsArray, y: number, z: number) {
|
||||
}
|
||||
|
||||
f({}, 10, 10);
|
||||
~~
|
||||
!!! error TS2345: Argument of type '{ [x: number]: undefined; }' is not assignable to parameter of type 'TemplateStringsArray'.
|
||||
!!! error TS2345: Property 'raw' is missing in type '{ [x: number]: undefined; }'.
|
||||
|
||||
f `abcdef${ 1234 }${ 5678 }ghijkl`;
|
|
@ -0,0 +1,22 @@
|
|||
//// [templateStringsArrayTypeRedefinedInES6Mode.ts]
|
||||
|
||||
class TemplateStringsArray {
|
||||
}
|
||||
|
||||
function f(x: TemplateStringsArray, y: number, z: number) {
|
||||
}
|
||||
|
||||
f({}, 10, 10);
|
||||
|
||||
f `abcdef${ 1234 }${ 5678 }ghijkl`;
|
||||
|
||||
//// [templateStringsArrayTypeRedefinedInES6Mode.js]
|
||||
var TemplateStringsArray = (function () {
|
||||
function TemplateStringsArray() {
|
||||
}
|
||||
return TemplateStringsArray;
|
||||
})();
|
||||
function f(x, y, z) {
|
||||
}
|
||||
f({}, 10, 10);
|
||||
f `abcdef${1234}${5678}ghijkl`;
|
|
@ -6,7 +6,6 @@ error TS2318: Cannot find global type 'Number'.
|
|||
error TS2318: Cannot find global type 'Object'.
|
||||
error TS2318: Cannot find global type 'RegExp'.
|
||||
error TS2318: Cannot find global type 'String'.
|
||||
error TS2318: Cannot find global type 'TemplateStringsArray'.
|
||||
tests/cases/compiler/typeCheckTypeArgument.ts(3,19): error TS2304: Cannot find name 'UNKNOWN'.
|
||||
tests/cases/compiler/typeCheckTypeArgument.ts(5,26): error TS2304: Cannot find name 'UNKNOWN'.
|
||||
tests/cases/compiler/typeCheckTypeArgument.ts(7,21): error TS2304: Cannot find name 'UNKNOWN'.
|
||||
|
@ -23,7 +22,6 @@ tests/cases/compiler/typeCheckTypeArgument.ts(15,13): error TS2304: Cannot find
|
|||
!!! error TS2318: Cannot find global type 'Object'.
|
||||
!!! error TS2318: Cannot find global type 'RegExp'.
|
||||
!!! error TS2318: Cannot find global type 'String'.
|
||||
!!! error TS2318: Cannot find global type 'TemplateStringsArray'.
|
||||
==== tests/cases/compiler/typeCheckTypeArgument.ts (6 errors) ====
|
||||
/// <reference no-default-lib="true"/>
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// @target: es5
|
||||
|
||||
class TemplateStringsArray {
|
||||
}
|
||||
|
||||
function f(x: TemplateStringsArray, y: number, z: number) {
|
||||
}
|
||||
|
||||
f({}, 10, 10);
|
||||
|
||||
f `abcdef${ 1234 }${ 5678 }ghijkl`;
|
|
@ -0,0 +1,8 @@
|
|||
// @target: es5
|
||||
|
||||
function f(x: TemplateStringsArray, y: number, z: number) {
|
||||
}
|
||||
|
||||
f({}, 10, 10);
|
||||
|
||||
f `abcdef${ 1234 }${ 5678 }ghijkl`;
|
|
@ -0,0 +1,11 @@
|
|||
// @target: es6
|
||||
|
||||
class TemplateStringsArray {
|
||||
}
|
||||
|
||||
function f(x: TemplateStringsArray, y: number, z: number) {
|
||||
}
|
||||
|
||||
f({}, 10, 10);
|
||||
|
||||
f `abcdef${ 1234 }${ 5678 }ghijkl`;
|
13
tests/cases/fourslash/formatInTryCatchFinally.ts
Normal file
13
tests/cases/fourslash/formatInTryCatchFinally.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////try
|
||||
////{
|
||||
//// var x = 1/*1*/
|
||||
////}
|
||||
////catch (e)
|
||||
////{
|
||||
////}
|
||||
|
||||
goTo.marker("1");
|
||||
edit.insert(";")
|
||||
verify.currentLineContentIs(" var x = 1;");
|
|
@ -11,4 +11,4 @@ goTo.marker();
|
|||
edit.insert('}');
|
||||
goTo.marker('comment');
|
||||
// Comment below multi-line 'if' condition formatting
|
||||
verify.currentLineContentIs(' // This is a comment');
|
||||
verify.currentLineContentIs(' // This is a comment');
|
12
tests/cases/fourslash/formattingBlockInCaseClauses.ts
Normal file
12
tests/cases/fourslash/formattingBlockInCaseClauses.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////switch (1) {
|
||||
//// case 1:
|
||||
//// {
|
||||
//// /*1*/
|
||||
//// break;
|
||||
////}
|
||||
|
||||
goTo.marker("1");
|
||||
edit.insert("}");
|
||||
verify.currentLineContentIs(" }");
|
|
@ -120,13 +120,13 @@ verify.currentLineContentIs("(arg) => 2;");
|
|||
goTo.marker("3");
|
||||
verify.currentLineContentIs("arg => 2;");
|
||||
goTo.marker("4");
|
||||
verify.currentLineContentIs("(arg = 1) => 3;");
|
||||
verify.currentLineContentIs("(arg = 1) => 3;");
|
||||
goTo.marker("5");
|
||||
verify.currentLineContentIs("(arg?) => 4;");
|
||||
goTo.marker("6");
|
||||
verify.currentLineContentIs("(arg: number) => 5;");
|
||||
goTo.marker("7");
|
||||
verify.currentLineContentIs("(arg: number = 0) => 6;");
|
||||
verify.currentLineContentIs("(arg: number = 0) => 6;");
|
||||
goTo.marker("8");
|
||||
verify.currentLineContentIs("(arg?: number) => 7;");
|
||||
goTo.marker("9");
|
||||
|
@ -134,13 +134,13 @@ verify.currentLineContentIs("(...arg: number[]) => 8;");
|
|||
goTo.marker("10");
|
||||
verify.currentLineContentIs("(arg1, arg2) => 12;");
|
||||
goTo.marker("11");
|
||||
verify.currentLineContentIs("(arg1 = 1, arg2 = 3) => 13;");
|
||||
verify.currentLineContentIs("(arg1 = 1, arg2 = 3) => 13;");
|
||||
goTo.marker("12");
|
||||
verify.currentLineContentIs("(arg1?, arg2?) => 14;");
|
||||
goTo.marker("13");
|
||||
verify.currentLineContentIs("(arg1: number, arg2: number) => 15;");
|
||||
goTo.marker("14");
|
||||
verify.currentLineContentIs("(arg1: number = 0, arg2: number = 1) => 16;");
|
||||
verify.currentLineContentIs("(arg1: number = 0, arg2: number = 1) => 16;");
|
||||
goTo.marker("15");
|
||||
verify.currentLineContentIs("(arg1?: number, arg2?: number) => 17;");
|
||||
goTo.marker("16");
|
||||
|
@ -152,13 +152,13 @@ verify.currentLineContentIs("(() => 21);");
|
|||
goTo.marker("19");
|
||||
verify.currentLineContentIs("((arg) => 22);");
|
||||
goTo.marker("20");
|
||||
verify.currentLineContentIs("((arg = 1) => 23);");
|
||||
verify.currentLineContentIs("((arg = 1) => 23);");
|
||||
goTo.marker("21");
|
||||
verify.currentLineContentIs("((arg?) => 24);");
|
||||
goTo.marker("22");
|
||||
verify.currentLineContentIs("((arg: number) => 25);");
|
||||
goTo.marker("23");
|
||||
verify.currentLineContentIs("((arg: number = 0) => 26);");
|
||||
verify.currentLineContentIs("((arg: number = 0) => 26);");
|
||||
goTo.marker("24");
|
||||
verify.currentLineContentIs("((arg?: number) => 27);");
|
||||
goTo.marker("25");
|
||||
|
@ -170,7 +170,7 @@ verify.currentLineContentIs("false ? () => 41 : null;");
|
|||
goTo.marker("28");
|
||||
verify.currentLineContentIs("false ? (arg) => 42 : null;");
|
||||
goTo.marker("29");
|
||||
verify.currentLineContentIs("false ? (arg = 1) => 43 : null;");
|
||||
verify.currentLineContentIs("false ? (arg = 1) => 43 : null;");
|
||||
goTo.marker("30");
|
||||
verify.currentLineContentIs("false ? (arg?) => 44 : null;");
|
||||
goTo.marker("31");
|
||||
|
@ -178,7 +178,7 @@ verify.currentLineContentIs("false ? (arg: number) => 45 : null;");
|
|||
goTo.marker("32");
|
||||
verify.currentLineContentIs("false ? (arg?: number) => 46 : null;");
|
||||
goTo.marker("33");
|
||||
verify.currentLineContentIs("false ? (arg?: number = 0) => 47 : null;");
|
||||
verify.currentLineContentIs("false ? (arg?: number = 0) => 47 : null;");
|
||||
goTo.marker("34");
|
||||
verify.currentLineContentIs("false ? (...arg: number[]) => 48 : null;");
|
||||
goTo.marker("35");
|
||||
|
@ -186,7 +186,7 @@ verify.currentLineContentIs("false ? (() => 51) : null;");
|
|||
goTo.marker("36");
|
||||
verify.currentLineContentIs("false ? ((arg) => 52) : null;");
|
||||
goTo.marker("37");
|
||||
verify.currentLineContentIs("false ? ((arg = 1) => 53) : null;");
|
||||
verify.currentLineContentIs("false ? ((arg = 1) => 53) : null;");
|
||||
goTo.marker("38");
|
||||
verify.currentLineContentIs("false ? ((arg?) => 54) : null;");
|
||||
goTo.marker("39");
|
||||
|
@ -194,7 +194,7 @@ verify.currentLineContentIs("false ? ((arg: number) => 55) : null;");
|
|||
goTo.marker("40");
|
||||
verify.currentLineContentIs("false ? ((arg?: number) => 56) : null;");
|
||||
goTo.marker("41");
|
||||
verify.currentLineContentIs("false ? ((arg?: number = 0) => 57) : null;");
|
||||
verify.currentLineContentIs("false ? ((arg?: number = 0) => 57) : null;");
|
||||
goTo.marker("42");
|
||||
verify.currentLineContentIs("false ? ((...arg: number[]) => 58) : null;");
|
||||
goTo.marker("43");
|
||||
|
@ -202,7 +202,7 @@ verify.currentLineContentIs("false ? null : () => 61;");
|
|||
goTo.marker("44");
|
||||
verify.currentLineContentIs("false ? null : (arg) => 62;");
|
||||
goTo.marker("45");
|
||||
verify.currentLineContentIs("false ? null : (arg = 1) => 63;");
|
||||
verify.currentLineContentIs("false ? null : (arg = 1) => 63;");
|
||||
goTo.marker("46");
|
||||
verify.currentLineContentIs("false ? null : (arg?) => 64;");
|
||||
goTo.marker("47");
|
||||
|
@ -210,7 +210,7 @@ verify.currentLineContentIs("false ? null : (arg: number) => 65;");
|
|||
goTo.marker("48");
|
||||
verify.currentLineContentIs("false ? null : (arg?: number) => 66;");
|
||||
goTo.marker("49");
|
||||
verify.currentLineContentIs("false ? null : (arg?: number = 0) => 67;");
|
||||
verify.currentLineContentIs("false ? null : (arg?: number = 0) => 67;");
|
||||
goTo.marker("50");
|
||||
verify.currentLineContentIs("false ? null : (...arg: number[]) => 68;");
|
||||
goTo.marker("51");
|
||||
|
@ -220,13 +220,13 @@ verify.currentLineContentIs("((a?) => { return a; }) ? (b) => (c) => 81 : (c) =>
|
|||
goTo.marker("53");
|
||||
verify.currentLineContentIs("((arg) => 90) instanceof Function;");
|
||||
goTo.marker("54");
|
||||
verify.currentLineContentIs("((arg = 1) => 91) instanceof Function;");
|
||||
verify.currentLineContentIs("((arg = 1) => 91) instanceof Function;");
|
||||
goTo.marker("55");
|
||||
verify.currentLineContentIs("((arg?) => 92) instanceof Function;");
|
||||
goTo.marker("56");
|
||||
verify.currentLineContentIs("((arg: number) => 93) instanceof Function;");
|
||||
goTo.marker("57");
|
||||
verify.currentLineContentIs("((arg: number = 1) => 94) instanceof Function;");
|
||||
verify.currentLineContentIs("((arg: number = 1) => 94) instanceof Function;");
|
||||
goTo.marker("58");
|
||||
verify.currentLineContentIs("((arg?: number) => 95) instanceof Function;");
|
||||
goTo.marker("59");
|
||||
|
@ -237,13 +237,13 @@ verify.currentLineContentIs("'' + ((arg) => 100);");
|
|||
goTo.marker("61");
|
||||
verify.currentLineContentIs("((arg) => 0) + '' + ((arg) => 101);");
|
||||
goTo.marker("62");
|
||||
verify.currentLineContentIs("((arg = 1) => 0) + '' + ((arg = 2) => 102);");
|
||||
verify.currentLineContentIs("((arg = 1) => 0) + '' + ((arg = 2) => 102);");
|
||||
goTo.marker("63");
|
||||
verify.currentLineContentIs("((arg?) => 0) + '' + ((arg?) => 103);");
|
||||
goTo.marker("64");
|
||||
verify.currentLineContentIs("((arg: number) => 0) + '' + ((arg: number) => 104);");
|
||||
goTo.marker("65");
|
||||
verify.currentLineContentIs("((arg: number = 1) => 0) + '' + ((arg: number = 2) => 105);");
|
||||
verify.currentLineContentIs("((arg: number = 1) => 0) + '' + ((arg: number = 2) => 105);");
|
||||
goTo.marker("66");
|
||||
verify.currentLineContentIs("((arg?: number) => 0) + '' + ((arg?: number) => 106);");
|
||||
goTo.marker("67");
|
||||
|
@ -273,17 +273,17 @@ verify.currentLineContentIs(" (a, b?) => 114,");
|
|||
goTo.marker("79");
|
||||
verify.currentLineContentIs(" (a: number) => 115,");
|
||||
goTo.marker("80");
|
||||
verify.currentLineContentIs(" (a: number = 0) => 116,");
|
||||
verify.currentLineContentIs(" (a: number = 0) => 116,");
|
||||
goTo.marker("81");
|
||||
verify.currentLineContentIs(" (a = 0) => 117,");
|
||||
verify.currentLineContentIs(" (a = 0) => 117,");
|
||||
goTo.marker("82");
|
||||
verify.currentLineContentIs(" (a: number = 0) => 118,");
|
||||
verify.currentLineContentIs(" (a: number = 0) => 118,");
|
||||
goTo.marker("83");
|
||||
verify.currentLineContentIs(" (a?, b?: number) => 118,");
|
||||
goTo.marker("84");
|
||||
verify.currentLineContentIs(" (...a: number[]) => 119,");
|
||||
goTo.marker("85");
|
||||
verify.currentLineContentIs(" (a, b = 0, ...c: number[]) => 120,");
|
||||
verify.currentLineContentIs(" (a, b = 0, ...c: number[]) => 120,");
|
||||
goTo.marker("86");
|
||||
verify.currentLineContentIs(" (a) => (b) => (c) => 121,");
|
||||
goTo.marker("87");
|
||||
|
|
12
tests/cases/fourslash/formattingIfInElseBlock.ts
Normal file
12
tests/cases/fourslash/formattingIfInElseBlock.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////if (true) {
|
||||
////}
|
||||
////else {
|
||||
//// if (true) {
|
||||
//// /*1*/
|
||||
////}
|
||||
|
||||
goTo.marker("1");
|
||||
edit.insert("}")
|
||||
verify.currentLineContentIs(" }");
|
|
@ -135,9 +135,9 @@ verify.currentLineContentIs(" return 0");
|
|||
goTo.marker("51");
|
||||
verify.currentLineContentIs("}).then(function(doc) {");
|
||||
goTo.marker("52");
|
||||
verify.currentLineContentIs(" return 1");
|
||||
verify.currentLineContentIs(" return 1");
|
||||
goTo.marker("53");
|
||||
verify.currentLineContentIs(" });");
|
||||
verify.currentLineContentIs("});");
|
||||
goTo.marker("54");
|
||||
verify.currentLineContentIs("if (1)");
|
||||
goTo.marker("55");
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//// export enum NodeType {/*2*/
|
||||
//// Error,/*3*/
|
||||
//// Comment,/*4*/
|
||||
//// } /*5*/
|
||||
//// } /*5*/
|
||||
//// export enum foob/*6*/
|
||||
//// {
|
||||
//// Blah=1, Bleah=2/*7*/
|
||||
|
@ -25,7 +25,7 @@ verify.currentLineContentIs(" }");
|
|||
goTo.marker("6");
|
||||
verify.currentLineContentIs(" export enum foob {");
|
||||
goTo.marker("7");
|
||||
verify.currentLineContentIs(" Blah= 1, Bleah= 2");
|
||||
verify.currentLineContentIs(" Blah = 1, Bleah = 2");
|
||||
goTo.marker("8");
|
||||
verify.currentLineContentIs(" }");
|
||||
goTo.marker("9");
|
||||
|
|
|
@ -15,8 +15,8 @@ verify.currentLineContentIs('foo(): Bar { }');
|
|||
goTo.marker('2');
|
||||
verify.currentLineContentIs('function Foo() # { }');
|
||||
goTo.marker('3');
|
||||
verify.currentLineContentIs('4+:5');
|
||||
verify.currentLineContentIs('4 +:5');
|
||||
goTo.marker('4');
|
||||
verify.currentLineContentIs(' : T) { }');
|
||||
goTo.marker('5');
|
||||
verify.currentLineContentIs('var x =');
|
||||
verify.currentLineContentIs('var x =');
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
debugger;
|
||||
format.document();
|
||||
goTo.marker('1');
|
||||
verify.currentLineContentIs('function test() /* %^ */');
|
||||
verify.currentLineContentIs('function test() /* %^ */ {');
|
||||
goTo.marker('2');
|
||||
verify.currentLineContentIs(' if (true) /* %^ */');
|
||||
verify.currentLineContentIs(' if (true) /* %^ */ {');
|
||||
goTo.marker('3');
|
||||
verify.currentLineContentIs('}');
|
|
@ -4,4 +4,4 @@
|
|||
|
||||
goTo.eof();
|
||||
edit.insert(';');
|
||||
verify.currentLineContentIs('function of1 (b:{ r:{ c: number;');
|
||||
verify.currentLineContentIs('function of1(b: { r: { c: number;');
|
|
@ -12,11 +12,11 @@ goTo.marker("innermost");
|
|||
edit.insert(";");
|
||||
|
||||
// Adding smicolon should format the innermost statement
|
||||
verify.currentLineContentIs(' var x = 0;');
|
||||
verify.currentLineContentIs(' var x = 0;');
|
||||
|
||||
// Also should format any parent statement that is terminated by the semicolon
|
||||
goTo.marker("directParent");
|
||||
verify.currentLineContentIs(' if (true)');
|
||||
verify.currentLineContentIs(' if (true)');
|
||||
|
||||
// But not parents that are not terminated by it
|
||||
goTo.marker("parentOutsideBlock");
|
||||
|
|
8
tests/cases/fourslash/smartIndentStartLineInLists.ts
Normal file
8
tests/cases/fourslash/smartIndentStartLineInLists.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
/// <reference path='fourslash.ts'/>
|
||||
////foo(function () {
|
||||
////}).then(function () {/*1*/
|
||||
////})
|
||||
|
||||
goTo.marker("1");
|
||||
edit.insert("\r\n");
|
||||
verify.indentationIs(4);
|
Loading…
Reference in a new issue