mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-22 16:52:14 -06:00
Merge branch 'master' into lint-nested-++
This commit is contained in:
commit
ec4c6fea2d
10
Jakefile.js
10
Jakefile.js
@ -911,11 +911,19 @@ function lintFileAsync(options, path, cb) {
|
||||
});
|
||||
}
|
||||
|
||||
var servicesLintTargets = [
|
||||
"services.ts",
|
||||
"outliningElementsCollector.ts",
|
||||
"navigateTo.ts",
|
||||
"patternMatcher.ts",
|
||||
].map(function (s) {
|
||||
return path.join(servicesDirectory, s);
|
||||
});
|
||||
var lintTargets = compilerSources
|
||||
.concat(harnessCoreSources)
|
||||
.concat(serverCoreSources)
|
||||
.concat(tslintRulesFiles)
|
||||
.concat([path.join(servicesDirectory, "services.ts")]);
|
||||
.concat(servicesLintTargets);
|
||||
|
||||
desc("Runs tslint on the compiler sources");
|
||||
task("lint", ["build-rules"], function() {
|
||||
|
||||
Binary file not shown.
BIN
doc/images/image1.png
Normal file
BIN
doc/images/image1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
doc/images/image2.png
Normal file
BIN
doc/images/image2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
doc/images/image3.png
Normal file
BIN
doc/images/image3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
BIN
doc/images/image4.png
Normal file
BIN
doc/images/image4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@ -262,7 +262,7 @@ function f() {
|
||||
|
||||
To benefit from this inference, a programmer can use the TypeScript language service. For example, a code editor can incorporate the TypeScript language service and use the service to find the members of a string object as in the following screen shot.
|
||||
|
||||
/
|
||||
  
|
||||
|
||||
In this example, the programmer benefits from type inference without providing type annotations. Some beneficial tools, however, do require the programmer to provide type annotations. In TypeScript, we can express a parameter requirement as in the following code fragment.
|
||||
|
||||
@ -410,7 +410,7 @@ This signature denotes that a function may be passed as the parameter of the '$'
|
||||
|
||||
A typical client would not need to add any additional typing but could just use a community-supplied typing to discover (through statement completion with documentation tips) and verify (through static checking) correct use of the library, as in the following screen shot.
|
||||
|
||||
/
|
||||
  
|
||||
|
||||
Section [3.3](#3.3) provides additional information about object types.
|
||||
|
||||
@ -627,7 +627,7 @@ An important goal of TypeScript is to provide accurate and straightforward types
|
||||
|
||||
JavaScript programming interfaces often include functions whose behavior is discriminated by a string constant passed to the function. The Document Object Model makes heavy use of this pattern. For example, the following screen shot shows that the 'createElement' method of the 'document' object has multiple signatures, some of which identify the types returned when specific strings are passed into the method.
|
||||
|
||||
/
|
||||
  
|
||||
|
||||
The following code fragment uses this feature. Because the 'span' variable is inferred to have the type 'HTMLSpanElement', the code can reference without static error the 'isMultiline' property of 'span'.
|
||||
|
||||
@ -638,7 +638,7 @@ span.isMultiLine = false; // OK: HTMLSpanElement has isMultiline property
|
||||
|
||||
In the following screen shot, a programming tool combines information from overloading on string parameters with contextual typing to infer that the type of the variable 'e' is 'MouseEvent' and that therefore 'e' has a 'clientX' property.
|
||||
|
||||
/
|
||||
  
|
||||
|
||||
Section [3.9.2.4](#3.9.2.4) provides details on how to use string literals in function signatures.
|
||||
|
||||
|
||||
@ -26,7 +26,9 @@ class BooleanTriviaWalker extends Lint.RuleWalker {
|
||||
for (let index = 0; index < targetParameters.length; index++) {
|
||||
const param = targetParameters[index];
|
||||
const arg = node.arguments[index];
|
||||
if (!(arg && param)) continue;
|
||||
if (!(arg && param)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const argType = this.checker.getContextualType(arg);
|
||||
if (argType && (argType.getFlags() & ts.TypeFlags.Boolean)) {
|
||||
@ -38,7 +40,9 @@ class BooleanTriviaWalker extends Lint.RuleWalker {
|
||||
if (ranges && ranges.length === 1 && ranges[0].kind === ts.SyntaxKind.MultiLineCommentTrivia) {
|
||||
triviaContent = arg.getFullText().slice(ranges[0].pos + 2, ranges[0].end - 2); // +/-2 to remove /**/
|
||||
}
|
||||
if (triviaContent !== param.getName()) {
|
||||
|
||||
const paramName = param.getName();
|
||||
if (triviaContent !== paramName && triviaContent !== paramName + ":") {
|
||||
this.addFailure(this.createFailure(arg.getStart(source), arg.getWidth(source), Rule.FAILURE_STRING_FACTORY(param.getName(), triviaContent)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,235 +0,0 @@
|
||||
// word2md - Word to Markdown conversion tool
|
||||
//
|
||||
// word2md converts a Microsoft Word document to Markdown formatted text. The tool uses the
|
||||
// Word Automation APIs to start an instance of Word and access the contents of the document
|
||||
// being converted. The tool must be run using the cscript.exe script host and requires Word
|
||||
// to be installed on the target machine. The name of the document to convert must be specified
|
||||
// as a command line argument and the resulting Markdown is written to standard output. The
|
||||
// tool recognizes the specific Word styles used in the TypeScript Language Specification.
|
||||
var sys = (function () {
|
||||
var fileStream = new ActiveXObject("ADODB.Stream");
|
||||
fileStream.Type = 2 /*text*/;
|
||||
var binaryStream = new ActiveXObject("ADODB.Stream");
|
||||
binaryStream.Type = 1 /*binary*/;
|
||||
var args = [];
|
||||
for (var i = 0; i < WScript.Arguments.length; i++) {
|
||||
args[i] = WScript.Arguments.Item(i);
|
||||
}
|
||||
return {
|
||||
args: args,
|
||||
createObject: function (typeName) { return new ActiveXObject(typeName); },
|
||||
write: function (s) {
|
||||
WScript.StdOut.Write(s);
|
||||
},
|
||||
writeFile: function (fileName, data) {
|
||||
fileStream.Open();
|
||||
binaryStream.Open();
|
||||
try {
|
||||
// Write characters in UTF-8 encoding
|
||||
fileStream.Charset = "utf-8";
|
||||
fileStream.WriteText(data);
|
||||
// We don't want the BOM, skip it by setting the starting location to 3 (size of BOM).
|
||||
fileStream.Position = 3;
|
||||
fileStream.CopyTo(binaryStream);
|
||||
binaryStream.SaveToFile(fileName, 2 /*overwrite*/);
|
||||
}
|
||||
finally {
|
||||
binaryStream.Close();
|
||||
fileStream.Close();
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
function convertDocumentToMarkdown(doc) {
|
||||
var result = "";
|
||||
var lastStyle;
|
||||
var lastInTable;
|
||||
var tableColumnCount;
|
||||
var tableCellIndex;
|
||||
var columnAlignment = [];
|
||||
function setProperties(target, properties) {
|
||||
for (var name in properties) {
|
||||
if (properties.hasOwnProperty(name)) {
|
||||
var value = properties[name];
|
||||
if (typeof value === "object") {
|
||||
setProperties(target[name], value);
|
||||
}
|
||||
else {
|
||||
target[name] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function findReplace(findText, findOptions, replaceText, replaceOptions) {
|
||||
var find = doc.range().find;
|
||||
find.clearFormatting();
|
||||
setProperties(find, findOptions);
|
||||
var replace = find.replacement;
|
||||
replace.clearFormatting();
|
||||
setProperties(replace, replaceOptions);
|
||||
find.execute(findText, false, false, false, false, false, true, 0, true, replaceText, 2);
|
||||
}
|
||||
function fixHyperlinks() {
|
||||
var count = doc.hyperlinks.count;
|
||||
for (var i = 0; i < count; i++) {
|
||||
var hyperlink = doc.hyperlinks.item(i + 1);
|
||||
var address = hyperlink.address;
|
||||
if (address && address.length > 0) {
|
||||
var textToDisplay = hyperlink.textToDisplay;
|
||||
hyperlink.textToDisplay = "[" + textToDisplay + "](" + address + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
function write(s) {
|
||||
result += s;
|
||||
}
|
||||
function writeTableHeader() {
|
||||
for (var i = 0; i < tableColumnCount - 1; i++) {
|
||||
switch (columnAlignment[i]) {
|
||||
case 1:
|
||||
write("|:---:");
|
||||
break;
|
||||
case 2:
|
||||
write("|---:");
|
||||
break;
|
||||
default:
|
||||
write("|---");
|
||||
}
|
||||
}
|
||||
write("|\n");
|
||||
}
|
||||
function trimEndFormattingMarks(text) {
|
||||
var i = text.length;
|
||||
while (i > 0 && text.charCodeAt(i - 1) < 0x20)
|
||||
i--;
|
||||
return text.substr(0, i);
|
||||
}
|
||||
function writeBlockEnd() {
|
||||
switch (lastStyle) {
|
||||
case "Code":
|
||||
write("```\n\n");
|
||||
break;
|
||||
case "List Paragraph":
|
||||
case "Table":
|
||||
case "TOC":
|
||||
write("\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
function writeParagraph(p) {
|
||||
var text = p.range.text;
|
||||
var style = p.style.nameLocal;
|
||||
var inTable = p.range.tables.count > 0;
|
||||
var level = 1;
|
||||
var sectionBreak = text.indexOf("\x0C") >= 0;
|
||||
text = trimEndFormattingMarks(text);
|
||||
if (inTable) {
|
||||
style = "Table";
|
||||
}
|
||||
else if (style.match(/\s\d$/)) {
|
||||
level = +style.substr(style.length - 1);
|
||||
style = style.substr(0, style.length - 2);
|
||||
}
|
||||
if (lastStyle && style !== lastStyle) {
|
||||
writeBlockEnd();
|
||||
}
|
||||
switch (style) {
|
||||
case "Heading":
|
||||
case "Appendix":
|
||||
var section = p.range.listFormat.listString;
|
||||
write("####".substr(0, level) + ' <a name="' + section + '"/>' + section + " " + text + "\n\n");
|
||||
break;
|
||||
case "Normal":
|
||||
if (text.length) {
|
||||
write(text + "\n\n");
|
||||
}
|
||||
break;
|
||||
case "List Paragraph":
|
||||
write(" ".substr(0, p.range.listFormat.listLevelNumber * 2 - 2) + "* " + text + "\n");
|
||||
break;
|
||||
case "Grammar":
|
||||
write("  " + text.replace(/\s\s\s/g, " ").replace(/\x0B/g, " \n   ") + "\n\n");
|
||||
break;
|
||||
case "Code":
|
||||
if (lastStyle !== "Code") {
|
||||
write("```TypeScript\n");
|
||||
}
|
||||
else {
|
||||
write("\n");
|
||||
}
|
||||
write(text.replace(/\x0B/g, " \n") + "\n");
|
||||
break;
|
||||
case "Table":
|
||||
if (!lastInTable) {
|
||||
tableColumnCount = p.range.tables.item(1).columns.count + 1;
|
||||
tableCellIndex = 0;
|
||||
}
|
||||
if (tableCellIndex < tableColumnCount) {
|
||||
columnAlignment[tableCellIndex] = p.alignment;
|
||||
}
|
||||
write("|" + text);
|
||||
tableCellIndex++;
|
||||
if (tableCellIndex % tableColumnCount === 0) {
|
||||
write("\n");
|
||||
if (tableCellIndex === tableColumnCount) {
|
||||
writeTableHeader();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "TOC Heading":
|
||||
write("## " + text + "\n\n");
|
||||
break;
|
||||
case "TOC":
|
||||
var strings = text.split("\t");
|
||||
write(" ".substr(0, level * 2 - 2) + "* [" + strings[0] + " " + strings[1] + "](#" + strings[0] + ")\n");
|
||||
break;
|
||||
}
|
||||
if (sectionBreak) {
|
||||
write("<br/>\n\n");
|
||||
}
|
||||
lastStyle = style;
|
||||
lastInTable = inTable;
|
||||
}
|
||||
function writeDocument() {
|
||||
var title = doc.builtInDocumentProperties.item(1) + "";
|
||||
if (title.length) {
|
||||
write("# " + title + "\n\n");
|
||||
}
|
||||
for (var p = doc.paragraphs.first; p; p = p.next()) {
|
||||
writeParagraph(p);
|
||||
}
|
||||
writeBlockEnd();
|
||||
}
|
||||
findReplace("<", {}, "<", {});
|
||||
findReplace("<", { style: "Code" }, "<", {});
|
||||
findReplace("<", { style: "Code Fragment" }, "<", {});
|
||||
findReplace("<", { style: "Terminal" }, "<", {});
|
||||
findReplace("", { font: { subscript: true } }, "<sub>^&</sub>", { font: { subscript: false } });
|
||||
findReplace("", { style: "Code Fragment" }, "`^&`", { style: -66 /* default font */ });
|
||||
findReplace("", { style: "Production" }, "*^&*", { style: -66 /* default font */ });
|
||||
findReplace("", { style: "Terminal" }, "`^&`", { style: -66 /* default font */ });
|
||||
findReplace("", { font: { bold: true, italic: true } }, "***^&***", { font: { bold: false, italic: false } });
|
||||
findReplace("", { font: { italic: true } }, "*^&*", { font: { italic: false } });
|
||||
doc.fields.toggleShowCodes();
|
||||
findReplace("^19 REF", {}, "[^&](#^&)", {});
|
||||
doc.fields.toggleShowCodes();
|
||||
fixHyperlinks();
|
||||
writeDocument();
|
||||
result = result.replace(/\x85/g, "\u2026");
|
||||
result = result.replace(/\x96/g, "\u2013");
|
||||
result = result.replace(/\x97/g, "\u2014");
|
||||
return result;
|
||||
}
|
||||
function main(args) {
|
||||
if (args.length !== 2) {
|
||||
sys.write("Syntax: word2md <inputfile> <outputfile>\n");
|
||||
return;
|
||||
}
|
||||
var app = sys.createObject("Word.Application");
|
||||
var doc = app.documents.open(args[0]);
|
||||
sys.writeFile(args[1], convertDocumentToMarkdown(doc));
|
||||
doc.close(false);
|
||||
app.quit();
|
||||
}
|
||||
main(sys.args);
|
||||
//# sourceMappingURL=file:///c:/ts/scripts/word2md.js.map
|
||||
@ -72,6 +72,9 @@ module Word {
|
||||
listFormat: ListFormat;
|
||||
tables: Tables;
|
||||
text: string;
|
||||
textRetrievalMode: {
|
||||
includeHiddenText: boolean;
|
||||
}
|
||||
words: Ranges;
|
||||
}
|
||||
|
||||
@ -258,13 +261,27 @@ function convertDocumentToMarkdown(doc: Word.Document): string {
|
||||
|
||||
function writeParagraph(p: Word.Paragraph) {
|
||||
|
||||
var text = p.range.text;
|
||||
var range = p.range;
|
||||
var text = range.text;
|
||||
var style = p.style.nameLocal;
|
||||
var inTable = p.range.tables.count > 0;
|
||||
var inTable = range.tables.count > 0;
|
||||
var level = 1;
|
||||
var sectionBreak = text.indexOf("\x0C") >= 0;
|
||||
|
||||
text = trimEndFormattingMarks(text);
|
||||
if (text === "/") {
|
||||
// An inline image shows up in the text as a "/". When we see a paragraph
|
||||
// consisting of nothing but "/", we check to see if the paragraph contains
|
||||
// hidden text and, if so, emit that instead. The hidden text is assumed to
|
||||
// contain an appropriate markdown image link.
|
||||
range.textRetrievalMode.includeHiddenText = true;
|
||||
var fullText = range.text;
|
||||
range.textRetrievalMode.includeHiddenText = false;
|
||||
if (text !== fullText) {
|
||||
text = "  " + fullText.substr(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (inTable) {
|
||||
style = "Table";
|
||||
}
|
||||
@ -280,7 +297,7 @@ function convertDocumentToMarkdown(doc: Word.Document): string {
|
||||
|
||||
case "Heading":
|
||||
case "Appendix":
|
||||
var section = p.range.listFormat.listString;
|
||||
var section = range.listFormat.listString;
|
||||
write("####".substr(0, level) + ' <a name="' + section + '"/>' + section + " " + text + "\n\n");
|
||||
break;
|
||||
|
||||
@ -291,7 +308,7 @@ function convertDocumentToMarkdown(doc: Word.Document): string {
|
||||
break;
|
||||
|
||||
case "List Paragraph":
|
||||
write(" ".substr(0, p.range.listFormat.listLevelNumber * 2 - 2) + "* " + text + "\n");
|
||||
write(" ".substr(0, range.listFormat.listLevelNumber * 2 - 2) + "* " + text + "\n");
|
||||
break;
|
||||
|
||||
case "Grammar":
|
||||
@ -310,7 +327,7 @@ function convertDocumentToMarkdown(doc: Word.Document): string {
|
||||
|
||||
case "Table":
|
||||
if (!lastInTable) {
|
||||
tableColumnCount = p.range.tables.item(1).columns.count + 1;
|
||||
tableColumnCount = range.tables.item(1).columns.count + 1;
|
||||
tableCellIndex = 0;
|
||||
}
|
||||
if (tableCellIndex < tableColumnCount) {
|
||||
|
||||
@ -8373,14 +8373,12 @@ namespace ts {
|
||||
checkGrammarJsxElement(node);
|
||||
checkJsxPreconditions(node);
|
||||
|
||||
// If we're compiling under --jsx react, the symbol 'React' should
|
||||
// be marked as 'used' so we don't incorrectly elide its import. And if there
|
||||
// is no 'React' symbol in scope, we should issue an error.
|
||||
if (compilerOptions.jsx === JsxEmit.React) {
|
||||
const reactSym = resolveName(node.tagName, "React", SymbolFlags.Value, Diagnostics.Cannot_find_name_0, "React");
|
||||
if (reactSym) {
|
||||
getSymbolLinks(reactSym).referenced = true;
|
||||
}
|
||||
// The symbol 'React' should be marked as 'used' so we don't incorrectly elide its import. And if there
|
||||
// is no 'React' symbol in scope when targeting React emit, we should issue an error.
|
||||
const reactRefErr = compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
|
||||
const reactSym = resolveName(node.tagName, "React", SymbolFlags.Value, reactRefErr, "React");
|
||||
if (reactSym) {
|
||||
getSymbolLinks(reactSym).referenced = true;
|
||||
}
|
||||
|
||||
const targetAttributesType = getJsxElementAttributesType(node);
|
||||
|
||||
@ -7221,7 +7221,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
||||
// Replace entities like
|
||||
result = result.replace(/&(\w+);/g, function(s: any, m: string) {
|
||||
if (entities[m] !== undefined) {
|
||||
return String.fromCharCode(entities[m]);
|
||||
const ch = String.fromCharCode(entities[m]);
|
||||
// " needs to be escaped
|
||||
return ch === "\"" ? "\\\"" : ch;
|
||||
}
|
||||
else {
|
||||
return s;
|
||||
|
||||
@ -360,7 +360,9 @@ namespace ts.formatting {
|
||||
range: TextRange,
|
||||
inheritedIndentation: number): number {
|
||||
|
||||
if (rangeOverlapsWithStartEnd(range, startPos, endPos)) {
|
||||
if (rangeOverlapsWithStartEnd(range, startPos, endPos) ||
|
||||
rangeContainsStartEnd(range, startPos, endPos) /* Not to miss zero-range nodes e.g. JsxText */) {
|
||||
|
||||
if (inheritedIndentation !== Constants.Unknown) {
|
||||
return inheritedIndentation;
|
||||
}
|
||||
|
||||
@ -450,8 +450,9 @@ namespace ts.formatting {
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
case SyntaxKind.JsxElement:
|
||||
case SyntaxKind.JsxOpeningElement:
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
case SyntaxKind.JsxExpression:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
@ -467,6 +468,7 @@ namespace ts.formatting {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function nodeWillIndentChild(parent: TextRangeWithKind, child: TextRangeWithKind, indentByDefault: boolean) {
|
||||
let childKind = child ? child.kind : SyntaxKind.Unknown;
|
||||
switch (parent.kind) {
|
||||
@ -484,6 +486,8 @@ namespace ts.formatting {
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return childKind !== SyntaxKind.Block;
|
||||
case SyntaxKind.JsxElement:
|
||||
return childKind !== SyntaxKind.JsxClosingElement;
|
||||
}
|
||||
// No explicit rule for given nodes so the result will follow the default value argument
|
||||
return indentByDefault;
|
||||
|
||||
@ -3,19 +3,19 @@ namespace ts.NavigateTo {
|
||||
type RawNavigateToItem = { name: string; fileName: string; matchKind: PatternMatchKind; isCaseSensitive: boolean; declaration: Declaration };
|
||||
|
||||
export function getNavigateToItems(program: Program, cancellationToken: CancellationToken, searchValue: string, maxResultCount: number): NavigateToItem[] {
|
||||
let patternMatcher = createPatternMatcher(searchValue);
|
||||
const patternMatcher = createPatternMatcher(searchValue);
|
||||
let rawItems: RawNavigateToItem[] = [];
|
||||
|
||||
// This means "compare in a case insensitive manner."
|
||||
let baseSensitivity: Intl.CollatorOptions = { sensitivity: "base" };
|
||||
const baseSensitivity: Intl.CollatorOptions = { sensitivity: "base" };
|
||||
|
||||
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
|
||||
forEach(program.getSourceFiles(), sourceFile => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
let nameToDeclarations = sourceFile.getNamedDeclarations();
|
||||
for (let name in nameToDeclarations) {
|
||||
let declarations = getProperty(nameToDeclarations, name);
|
||||
const nameToDeclarations = sourceFile.getNamedDeclarations();
|
||||
for (const name in nameToDeclarations) {
|
||||
const declarations = getProperty(nameToDeclarations, name);
|
||||
if (declarations) {
|
||||
// First do a quick check to see if the name of the declaration matches the
|
||||
// last portion of the (possibly) dotted name they're searching for.
|
||||
@ -25,11 +25,11 @@ namespace ts.NavigateTo {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let declaration of declarations) {
|
||||
for (const declaration of declarations) {
|
||||
// It was a match! If the pattern has dots in it, then also see if the
|
||||
// declaration container matches as well.
|
||||
if (patternMatcher.patternContainsDots) {
|
||||
let containers = getContainers(declaration);
|
||||
const containers = getContainers(declaration);
|
||||
if (!containers) {
|
||||
return undefined;
|
||||
}
|
||||
@ -41,8 +41,8 @@ namespace ts.NavigateTo {
|
||||
}
|
||||
}
|
||||
|
||||
let fileName = sourceFile.fileName;
|
||||
let matchKind = bestMatchKind(matches);
|
||||
const fileName = sourceFile.fileName;
|
||||
const matchKind = bestMatchKind(matches);
|
||||
rawItems.push({ name, fileName, matchKind, isCaseSensitive: allMatchesAreCaseSensitive(matches), declaration });
|
||||
}
|
||||
}
|
||||
@ -54,7 +54,7 @@ namespace ts.NavigateTo {
|
||||
rawItems = rawItems.slice(0, maxResultCount);
|
||||
}
|
||||
|
||||
let items = map(rawItems, createNavigateToItem);
|
||||
const items = map(rawItems, createNavigateToItem);
|
||||
|
||||
return items;
|
||||
|
||||
@ -62,7 +62,7 @@ namespace ts.NavigateTo {
|
||||
Debug.assert(matches.length > 0);
|
||||
|
||||
// This is a case sensitive match, only if all the submatches were case sensitive.
|
||||
for (let match of matches) {
|
||||
for (const match of matches) {
|
||||
if (!match.isCaseSensitive) {
|
||||
return false;
|
||||
}
|
||||
@ -86,16 +86,16 @@ namespace ts.NavigateTo {
|
||||
|
||||
function tryAddSingleDeclarationName(declaration: Declaration, containers: string[]) {
|
||||
if (declaration && declaration.name) {
|
||||
let text = getTextOfIdentifierOrLiteral(declaration.name);
|
||||
const text = getTextOfIdentifierOrLiteral(declaration.name);
|
||||
if (text !== undefined) {
|
||||
containers.unshift(text);
|
||||
}
|
||||
else if (declaration.name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
return tryAddComputedPropertyName((<ComputedPropertyName>declaration.name).expression, containers, /*includeLastPortion:*/ true);
|
||||
return tryAddComputedPropertyName((<ComputedPropertyName>declaration.name).expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
else {
|
||||
// Don't know how to add this.
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ namespace ts.NavigateTo {
|
||||
//
|
||||
// [X.Y.Z]() { }
|
||||
function tryAddComputedPropertyName(expression: Expression, containers: string[], includeLastPortion: boolean): boolean {
|
||||
let text = getTextOfIdentifierOrLiteral(expression);
|
||||
const text = getTextOfIdentifierOrLiteral(expression);
|
||||
if (text !== undefined) {
|
||||
if (includeLastPortion) {
|
||||
containers.unshift(text);
|
||||
@ -115,24 +115,24 @@ namespace ts.NavigateTo {
|
||||
}
|
||||
|
||||
if (expression.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
let propertyAccess = <PropertyAccessExpression>expression;
|
||||
const propertyAccess = <PropertyAccessExpression>expression;
|
||||
if (includeLastPortion) {
|
||||
containers.unshift(propertyAccess.name.text);
|
||||
}
|
||||
|
||||
return tryAddComputedPropertyName(propertyAccess.expression, containers, /*includeLastPortion:*/ true);
|
||||
return tryAddComputedPropertyName(propertyAccess.expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getContainers(declaration: Declaration) {
|
||||
let containers: string[] = [];
|
||||
const containers: string[] = [];
|
||||
|
||||
// First, if we started with a computed property name, then add all but the last
|
||||
// portion into the container array.
|
||||
if (declaration.name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
if (!tryAddComputedPropertyName((<ComputedPropertyName>declaration.name).expression, containers, /*includeLastPortion:*/ false)) {
|
||||
if (!tryAddComputedPropertyName((<ComputedPropertyName>declaration.name).expression, containers, /*includeLastPortion*/ false)) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@ -155,8 +155,8 @@ namespace ts.NavigateTo {
|
||||
Debug.assert(matches.length > 0);
|
||||
let bestMatchKind = PatternMatchKind.camelCase;
|
||||
|
||||
for (let match of matches) {
|
||||
let kind = match.kind;
|
||||
for (const match of matches) {
|
||||
const kind = match.kind;
|
||||
if (kind < bestMatchKind) {
|
||||
bestMatchKind = kind;
|
||||
}
|
||||
@ -171,13 +171,13 @@ namespace ts.NavigateTo {
|
||||
// We first sort case insensitively. So "Aaa" will come before "bar".
|
||||
// Then we sort case sensitively, so "aaa" will come before "Aaa".
|
||||
return i1.matchKind - i2.matchKind ||
|
||||
i1.name.localeCompare(i2.name, undefined, baseSensitivity) ||
|
||||
i1.name.localeCompare(i2.name, undefined, baseSensitivity) ||
|
||||
i1.name.localeCompare(i2.name);
|
||||
}
|
||||
|
||||
function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
|
||||
let declaration = rawItem.declaration;
|
||||
let container = <Declaration>getContainerNode(declaration);
|
||||
const declaration = rawItem.declaration;
|
||||
const container = <Declaration>getContainerNode(declaration);
|
||||
return {
|
||||
name: rawItem.name,
|
||||
kind: getNodeKind(declaration),
|
||||
|
||||
@ -1,180 +1,179 @@
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export module OutliningElementsCollector {
|
||||
export function collectElements(sourceFile: SourceFile): OutliningSpan[] {
|
||||
let elements: OutliningSpan[] = [];
|
||||
let collapseText = "...";
|
||||
namespace ts.OutliningElementsCollector {
|
||||
export function collectElements(sourceFile: SourceFile): OutliningSpan[] {
|
||||
const elements: OutliningSpan[] = [];
|
||||
const collapseText = "...";
|
||||
|
||||
function addOutliningSpan(hintSpanNode: Node, startElement: Node, endElement: Node, autoCollapse: boolean) {
|
||||
if (hintSpanNode && startElement && endElement) {
|
||||
let span: OutliningSpan = {
|
||||
textSpan: createTextSpanFromBounds(startElement.pos, endElement.end),
|
||||
hintSpan: createTextSpanFromBounds(hintSpanNode.getStart(), hintSpanNode.end),
|
||||
bannerText: collapseText,
|
||||
autoCollapse: autoCollapse
|
||||
};
|
||||
elements.push(span);
|
||||
}
|
||||
function addOutliningSpan(hintSpanNode: Node, startElement: Node, endElement: Node, autoCollapse: boolean) {
|
||||
if (hintSpanNode && startElement && endElement) {
|
||||
const span: OutliningSpan = {
|
||||
textSpan: createTextSpanFromBounds(startElement.pos, endElement.end),
|
||||
hintSpan: createTextSpanFromBounds(hintSpanNode.getStart(), hintSpanNode.end),
|
||||
bannerText: collapseText,
|
||||
autoCollapse: autoCollapse
|
||||
};
|
||||
elements.push(span);
|
||||
}
|
||||
}
|
||||
|
||||
function addOutliningSpanComments(commentSpan: CommentRange, autoCollapse: boolean) {
|
||||
if (commentSpan) {
|
||||
let span: OutliningSpan = {
|
||||
textSpan: createTextSpanFromBounds(commentSpan.pos, commentSpan.end),
|
||||
hintSpan: createTextSpanFromBounds(commentSpan.pos, commentSpan.end),
|
||||
bannerText: collapseText,
|
||||
autoCollapse: autoCollapse
|
||||
};
|
||||
elements.push(span);
|
||||
}
|
||||
function addOutliningSpanComments(commentSpan: CommentRange, autoCollapse: boolean) {
|
||||
if (commentSpan) {
|
||||
const span: OutliningSpan = {
|
||||
textSpan: createTextSpanFromBounds(commentSpan.pos, commentSpan.end),
|
||||
hintSpan: createTextSpanFromBounds(commentSpan.pos, commentSpan.end),
|
||||
bannerText: collapseText,
|
||||
autoCollapse: autoCollapse
|
||||
};
|
||||
elements.push(span);
|
||||
}
|
||||
}
|
||||
|
||||
function addOutliningForLeadingCommentsForNode(n: Node) {
|
||||
let comments = ts.getLeadingCommentRangesOfNode(n, sourceFile);
|
||||
function addOutliningForLeadingCommentsForNode(n: Node) {
|
||||
const comments = ts.getLeadingCommentRangesOfNode(n, sourceFile);
|
||||
|
||||
if (comments) {
|
||||
let firstSingleLineCommentStart = -1;
|
||||
let lastSingleLineCommentEnd = -1;
|
||||
let isFirstSingleLineComment = true;
|
||||
let singleLineCommentCount = 0;
|
||||
if (comments) {
|
||||
let firstSingleLineCommentStart = -1;
|
||||
let lastSingleLineCommentEnd = -1;
|
||||
let isFirstSingleLineComment = true;
|
||||
let singleLineCommentCount = 0;
|
||||
|
||||
for (let currentComment of comments) {
|
||||
for (const currentComment of comments) {
|
||||
|
||||
// For single line comments, combine consecutive ones (2 or more) into
|
||||
// a single span from the start of the first till the end of the last
|
||||
if (currentComment.kind === SyntaxKind.SingleLineCommentTrivia) {
|
||||
if (isFirstSingleLineComment) {
|
||||
firstSingleLineCommentStart = currentComment.pos;
|
||||
}
|
||||
isFirstSingleLineComment = false;
|
||||
lastSingleLineCommentEnd = currentComment.end;
|
||||
singleLineCommentCount++;
|
||||
// For single line comments, combine consecutive ones (2 or more) into
|
||||
// a single span from the start of the first till the end of the last
|
||||
if (currentComment.kind === SyntaxKind.SingleLineCommentTrivia) {
|
||||
if (isFirstSingleLineComment) {
|
||||
firstSingleLineCommentStart = currentComment.pos;
|
||||
}
|
||||
else if (currentComment.kind === SyntaxKind.MultiLineCommentTrivia) {
|
||||
combineAndAddMultipleSingleLineComments(singleLineCommentCount, firstSingleLineCommentStart, lastSingleLineCommentEnd);
|
||||
addOutliningSpanComments(currentComment, /*autoCollapse*/ false);
|
||||
isFirstSingleLineComment = false;
|
||||
lastSingleLineCommentEnd = currentComment.end;
|
||||
singleLineCommentCount++;
|
||||
}
|
||||
else if (currentComment.kind === SyntaxKind.MultiLineCommentTrivia) {
|
||||
combineAndAddMultipleSingleLineComments(singleLineCommentCount, firstSingleLineCommentStart, lastSingleLineCommentEnd);
|
||||
addOutliningSpanComments(currentComment, /*autoCollapse*/ false);
|
||||
|
||||
singleLineCommentCount = 0;
|
||||
lastSingleLineCommentEnd = -1;
|
||||
isFirstSingleLineComment = true;
|
||||
singleLineCommentCount = 0;
|
||||
lastSingleLineCommentEnd = -1;
|
||||
isFirstSingleLineComment = true;
|
||||
}
|
||||
}
|
||||
|
||||
combineAndAddMultipleSingleLineComments(singleLineCommentCount, firstSingleLineCommentStart, lastSingleLineCommentEnd);
|
||||
}
|
||||
}
|
||||
|
||||
function combineAndAddMultipleSingleLineComments(count: number, start: number, end: number) {
|
||||
|
||||
// Only outline spans of two or more consecutive single line comments
|
||||
if (count > 1) {
|
||||
const multipleSingleLineComments = {
|
||||
pos: start,
|
||||
end: end,
|
||||
kind: SyntaxKind.SingleLineCommentTrivia
|
||||
};
|
||||
|
||||
addOutliningSpanComments(multipleSingleLineComments, /*autoCollapse*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
function autoCollapse(node: Node) {
|
||||
return isFunctionBlock(node) && node.parent.kind !== SyntaxKind.ArrowFunction;
|
||||
}
|
||||
|
||||
let depth = 0;
|
||||
const maxDepth = 20;
|
||||
function walk(n: Node): void {
|
||||
if (depth > maxDepth) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDeclaration(n)) {
|
||||
addOutliningForLeadingCommentsForNode(n);
|
||||
}
|
||||
|
||||
switch (n.kind) {
|
||||
case SyntaxKind.Block:
|
||||
if (!isFunctionBlock(n)) {
|
||||
const parent = n.parent;
|
||||
const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
|
||||
// Check if the block is standalone, or 'attached' to some parent statement.
|
||||
// If the latter, we want to collaps the block, but consider its hint span
|
||||
// to be the entire span of the parent.
|
||||
if (parent.kind === SyntaxKind.DoStatement ||
|
||||
parent.kind === SyntaxKind.ForInStatement ||
|
||||
parent.kind === SyntaxKind.ForOfStatement ||
|
||||
parent.kind === SyntaxKind.ForStatement ||
|
||||
parent.kind === SyntaxKind.IfStatement ||
|
||||
parent.kind === SyntaxKind.WhileStatement ||
|
||||
parent.kind === SyntaxKind.WithStatement ||
|
||||
parent.kind === SyntaxKind.CatchClause) {
|
||||
|
||||
addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
combineAndAddMultipleSingleLineComments(singleLineCommentCount, firstSingleLineCommentStart, lastSingleLineCommentEnd);
|
||||
}
|
||||
}
|
||||
|
||||
function combineAndAddMultipleSingleLineComments(count: number, start: number, end: number) {
|
||||
// Only outline spans of two or more consecutive single line comments
|
||||
if (count > 1) {
|
||||
let multipleSingleLineComments = {
|
||||
pos: start,
|
||||
end: end,
|
||||
kind: SyntaxKind.SingleLineCommentTrivia
|
||||
}
|
||||
|
||||
addOutliningSpanComments(multipleSingleLineComments, /*autoCollapse*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
function autoCollapse(node: Node) {
|
||||
return isFunctionBlock(node) && node.parent.kind !== SyntaxKind.ArrowFunction;
|
||||
}
|
||||
|
||||
let depth = 0;
|
||||
let maxDepth = 20;
|
||||
function walk(n: Node): void {
|
||||
if (depth > maxDepth) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDeclaration(n)) {
|
||||
addOutliningForLeadingCommentsForNode(n);
|
||||
}
|
||||
|
||||
switch (n.kind) {
|
||||
case SyntaxKind.Block:
|
||||
if (!isFunctionBlock(n)) {
|
||||
let parent = n.parent;
|
||||
let openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
let closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
|
||||
// Check if the block is standalone, or 'attached' to some parent statement.
|
||||
// If the latter, we want to collaps the block, but consider its hint span
|
||||
// to be the entire span of the parent.
|
||||
if (parent.kind === SyntaxKind.DoStatement ||
|
||||
parent.kind === SyntaxKind.ForInStatement ||
|
||||
parent.kind === SyntaxKind.ForOfStatement ||
|
||||
parent.kind === SyntaxKind.ForStatement ||
|
||||
parent.kind === SyntaxKind.IfStatement ||
|
||||
parent.kind === SyntaxKind.WhileStatement ||
|
||||
parent.kind === SyntaxKind.WithStatement ||
|
||||
parent.kind === SyntaxKind.CatchClause) {
|
||||
|
||||
if (parent.kind === SyntaxKind.TryStatement) {
|
||||
// Could be the try-block, or the finally-block.
|
||||
const tryStatement = <TryStatement>parent;
|
||||
if (tryStatement.tryBlock === n) {
|
||||
addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
|
||||
if (parent.kind === SyntaxKind.TryStatement) {
|
||||
// Could be the try-block, or the finally-block.
|
||||
let tryStatement = <TryStatement>parent;
|
||||
if (tryStatement.tryBlock === n) {
|
||||
addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n));
|
||||
else if (tryStatement.finallyBlock === n) {
|
||||
const finallyKeyword = findChildOfKind(tryStatement, SyntaxKind.FinallyKeyword, sourceFile);
|
||||
if (finallyKeyword) {
|
||||
addOutliningSpan(finallyKeyword, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
else if (tryStatement.finallyBlock === n) {
|
||||
let finallyKeyword = findChildOfKind(tryStatement, SyntaxKind.FinallyKeyword, sourceFile);
|
||||
if (finallyKeyword) {
|
||||
addOutliningSpan(finallyKeyword, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// fall through.
|
||||
}
|
||||
|
||||
// Block was a standalone block. In this case we want to only collapse
|
||||
// the span of the block, independent of any parent span.
|
||||
let span = createTextSpanFromBounds(n.getStart(), n.end);
|
||||
elements.push({
|
||||
textSpan: span,
|
||||
hintSpan: span,
|
||||
bannerText: collapseText,
|
||||
autoCollapse: autoCollapse(n)
|
||||
});
|
||||
break;
|
||||
// fall through.
|
||||
}
|
||||
// Fallthrough.
|
||||
|
||||
case SyntaxKind.ModuleBlock: {
|
||||
let openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
let closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
addOutliningSpan(n.parent, openBrace, closeBrace, autoCollapse(n));
|
||||
// Block was a standalone block. In this case we want to only collapse
|
||||
// the span of the block, independent of any parent span.
|
||||
const span = createTextSpanFromBounds(n.getStart(), n.end);
|
||||
elements.push({
|
||||
textSpan: span,
|
||||
hintSpan: span,
|
||||
bannerText: collapseText,
|
||||
autoCollapse: autoCollapse(n)
|
||||
});
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.CaseBlock: {
|
||||
let openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
let closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
let openBracket = findChildOfKind(n, SyntaxKind.OpenBracketToken, sourceFile);
|
||||
let closeBracket = findChildOfKind(n, SyntaxKind.CloseBracketToken, sourceFile);
|
||||
addOutliningSpan(n, openBracket, closeBracket, autoCollapse(n));
|
||||
break;
|
||||
// Fallthrough.
|
||||
|
||||
case SyntaxKind.ModuleBlock: {
|
||||
const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
addOutliningSpan(n.parent, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
depth++;
|
||||
forEachChild(n, walk);
|
||||
depth--;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.CaseBlock: {
|
||||
const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
const openBracket = findChildOfKind(n, SyntaxKind.OpenBracketToken, sourceFile);
|
||||
const closeBracket = findChildOfKind(n, SyntaxKind.CloseBracketToken, sourceFile);
|
||||
addOutliningSpan(n, openBracket, closeBracket, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
|
||||
walk(sourceFile);
|
||||
return elements;
|
||||
depth++;
|
||||
forEachChild(n, walk);
|
||||
depth--;
|
||||
}
|
||||
|
||||
walk(sourceFile);
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
@ -8,10 +8,10 @@ namespace ts {
|
||||
camelCase
|
||||
}
|
||||
|
||||
// Information about a match made by the pattern matcher between a candidate and the
|
||||
// Information about a match made by the pattern matcher between a candidate and the
|
||||
// search pattern.
|
||||
export interface PatternMatch {
|
||||
// What kind of match this was. Exact matches are better than prefix matches which are
|
||||
// What kind of match this was. Exact matches are better than prefix matches which are
|
||||
// better than substring matches which are better than CamelCase matches.
|
||||
kind: PatternMatchKind;
|
||||
|
||||
@ -19,7 +19,7 @@ namespace ts {
|
||||
// it was a better match.
|
||||
camelCaseWeight?: number;
|
||||
|
||||
// If this was a match where all constituent parts of the candidate and search pattern
|
||||
// If this was a match where all constituent parts of the candidate and search pattern
|
||||
// matched case sensitively or case insensitively. Case sensitive matches of the kind
|
||||
// are better matches than insensitive matches.
|
||||
isCaseSensitive: boolean;
|
||||
@ -35,7 +35,7 @@ namespace ts {
|
||||
// once you no longer need it.
|
||||
export interface PatternMatcher {
|
||||
// Used to match a candidate against the last segment of a possibly dotted pattern. This
|
||||
// is useful as a quick check to prevent having to compute a container before calling
|
||||
// is useful as a quick check to prevent having to compute a container before calling
|
||||
// "getMatches".
|
||||
//
|
||||
// For example, if the search pattern is "ts.c.SK" and the candidate is "SyntaxKind", then
|
||||
@ -55,8 +55,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
// First we break up the pattern given by dots. Each portion of the pattern between the
|
||||
// dots is a 'Segment'. The 'Segment' contains information about the entire section of
|
||||
// text between the dots, as well as information about any individual 'Words' that we
|
||||
// dots is a 'Segment'. The 'Segment' contains information about the entire section of
|
||||
// text between the dots, as well as information about any individual 'Words' that we
|
||||
// can break the segment into. A 'Word' is simply a contiguous sequence of characters
|
||||
// that can appear in a typescript identifier. So "GetKeyword" would be one word, while
|
||||
// "Get Keyword" would be two words. Once we have the individual 'words', we break those
|
||||
@ -64,20 +64,20 @@ namespace ts {
|
||||
// word, it make character spans corresponding to "U", "I" and "Element". These spans
|
||||
// are then used when doing camel cased matches against candidate patterns.
|
||||
interface Segment {
|
||||
// Information about the entire piece of text between the dots. For example, if the
|
||||
// text between the dots is 'GetKeyword', then TotalTextChunk.Text will be 'GetKeyword' and
|
||||
// Information about the entire piece of text between the dots. For example, if the
|
||||
// text between the dots is 'GetKeyword', then TotalTextChunk.Text will be 'GetKeyword' and
|
||||
// TotalTextChunk.CharacterSpans will correspond to 'Get', 'Keyword'.
|
||||
totalTextChunk: TextChunk;
|
||||
|
||||
// Information about the subwords compromising the total word. For example, if the
|
||||
// text between the dots is 'GetFoo KeywordBar', then the subwords will be 'GetFoo'
|
||||
// and 'KeywordBar'. Those individual words will have CharacterSpans of ('Get' and
|
||||
// 'Foo') and('Keyword' and 'Bar') respectively.
|
||||
// Information about the subwords compromising the total word. For example, if the
|
||||
// text between the dots is 'GetFoo KeywordBar', then the subwords will be 'GetFoo'
|
||||
// and 'KeywordBar'. Those individual words will have CharacterSpans of ('Get' and
|
||||
// 'Foo') and('Keyword' and 'Bar') respectively.
|
||||
subWordTextChunks: TextChunk[];
|
||||
}
|
||||
|
||||
// Information about a chunk of text from the pattern. The chunk is a piece of text, with
|
||||
// cached information about the character spans within in. Character spans are used for
|
||||
// Information about a chunk of text from the pattern. The chunk is a piece of text, with
|
||||
// cached information about the character spans within in. Character spans are used for
|
||||
// camel case matching.
|
||||
interface TextChunk {
|
||||
// The text of the chunk. This should be a contiguous sequence of character that could
|
||||
@ -92,7 +92,7 @@ namespace ts {
|
||||
// for something entirely lowercase or not.
|
||||
isLowerCase: boolean;
|
||||
|
||||
// The spans in this text chunk that we think are of interest and should be matched
|
||||
// The spans in this text chunk that we think are of interest and should be matched
|
||||
// independently. For example, if the chunk is for "UIElement" the the spans of interest
|
||||
// correspond to "U", "I" and "Element". If "UIElement" isn't found as an exaxt, prefix.
|
||||
// or substring match, then the character spans will be used to attempt a camel case match.
|
||||
@ -110,20 +110,19 @@ namespace ts {
|
||||
|
||||
export function createPatternMatcher(pattern: string): PatternMatcher {
|
||||
// We'll often see the same candidate string many times when searching (For example, when
|
||||
// we see the name of a module that is used everywhere, or the name of an overload). As
|
||||
// such, we cache the information we compute about the candidate for the life of this
|
||||
// we see the name of a module that is used everywhere, or the name of an overload). As
|
||||
// such, we cache the information we compute about the candidate for the life of this
|
||||
// pattern matcher so we don't have to compute it multiple times.
|
||||
let stringToWordSpans: Map<TextSpan[]> = {};
|
||||
const stringToWordSpans: Map<TextSpan[]> = {};
|
||||
|
||||
pattern = pattern.trim();
|
||||
|
||||
let fullPatternSegment = createSegment(pattern);
|
||||
let dotSeparatedSegments = pattern.split(".").map(p => createSegment(p.trim()));
|
||||
let invalidPattern = dotSeparatedSegments.length === 0 || forEach(dotSeparatedSegments, segmentIsInvalid);
|
||||
const dotSeparatedSegments = pattern.split(".").map(p => createSegment(p.trim()));
|
||||
const invalidPattern = dotSeparatedSegments.length === 0 || forEach(dotSeparatedSegments, segmentIsInvalid);
|
||||
|
||||
return {
|
||||
getMatches,
|
||||
getMatchesForLastSegmentOfPattern,
|
||||
getMatchesForLastSegmentOfPattern,
|
||||
patternContainsDots: dotSeparatedSegments.length > 1
|
||||
};
|
||||
|
||||
@ -131,7 +130,7 @@ namespace ts {
|
||||
function skipMatch(candidate: string) {
|
||||
return invalidPattern || !candidate;
|
||||
}
|
||||
|
||||
|
||||
function getMatchesForLastSegmentOfPattern(candidate: string): PatternMatch[] {
|
||||
if (skipMatch(candidate)) {
|
||||
return undefined;
|
||||
@ -148,7 +147,7 @@ namespace ts {
|
||||
// First, check that the last part of the dot separated pattern matches the name of the
|
||||
// candidate. If not, then there's no point in proceeding and doing the more
|
||||
// expensive work.
|
||||
let candidateMatch = matchSegment(candidate, lastOrUndefined(dotSeparatedSegments));
|
||||
const candidateMatch = matchSegment(candidate, lastOrUndefined(dotSeparatedSegments));
|
||||
if (!candidateMatch) {
|
||||
return undefined;
|
||||
}
|
||||
@ -165,16 +164,16 @@ namespace ts {
|
||||
|
||||
// So far so good. Now break up the container for the candidate and check if all
|
||||
// the dotted parts match up correctly.
|
||||
let totalMatch = candidateMatch;
|
||||
const totalMatch = candidateMatch;
|
||||
|
||||
for (let i = dotSeparatedSegments.length - 2, j = candidateContainers.length - 1;
|
||||
i >= 0;
|
||||
i--, j--) {
|
||||
|
||||
let segment = dotSeparatedSegments[i];
|
||||
let containerName = candidateContainers[j];
|
||||
const segment = dotSeparatedSegments[i];
|
||||
const containerName = candidateContainers[j];
|
||||
|
||||
let containerMatch = matchSegment(containerName, segment);
|
||||
const containerMatch = matchSegment(containerName, segment);
|
||||
if (!containerMatch) {
|
||||
// This container didn't match the pattern piece. So there's no match at all.
|
||||
return undefined;
|
||||
@ -197,7 +196,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function matchTextChunk(candidate: string, chunk: TextChunk, punctuationStripped: boolean): PatternMatch {
|
||||
let index = indexOfIgnoringCase(candidate, chunk.textLowerCase);
|
||||
const index = indexOfIgnoringCase(candidate, chunk.textLowerCase);
|
||||
if (index === 0) {
|
||||
if (chunk.text.length === candidate.length) {
|
||||
// a) Check if the part matches the candidate entirely, in an case insensitive or
|
||||
@ -211,18 +210,18 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
let isLowercase = chunk.isLowerCase;
|
||||
const isLowercase = chunk.isLowerCase;
|
||||
if (isLowercase) {
|
||||
if (index > 0) {
|
||||
// c) If the part is entirely lowercase, then check if it is contained anywhere in the
|
||||
// candidate in a case insensitive manner. If so, return that there was a substring
|
||||
// match.
|
||||
// match.
|
||||
//
|
||||
// Note: We only have a substring match if the lowercase part is prefix match of some
|
||||
// word part. That way we don't match something like 'Class' when the user types 'a'.
|
||||
// But we would match 'FooAttribute' (since 'Attribute' starts with 'a').
|
||||
let wordSpans = getWordSpans(candidate);
|
||||
for (let span of wordSpans) {
|
||||
const wordSpans = getWordSpans(candidate);
|
||||
for (const span of wordSpans) {
|
||||
if (partStartsWith(candidate, span, chunk.text, /*ignoreCase:*/ true)) {
|
||||
return createPatternMatch(PatternMatchKind.substring, punctuationStripped,
|
||||
/*isCaseSensitive:*/ partStartsWith(candidate, span, chunk.text, /*ignoreCase:*/ false));
|
||||
@ -242,7 +241,7 @@ namespace ts {
|
||||
if (!isLowercase) {
|
||||
// e) If the part was not entirely lowercase, then attempt a camel cased match as well.
|
||||
if (chunk.characterSpans.length > 0) {
|
||||
let candidateParts = getWordSpans(candidate);
|
||||
const candidateParts = getWordSpans(candidate);
|
||||
let camelCaseWeight = tryCamelCaseMatch(candidate, candidateParts, chunk, /*ignoreCase:*/ false);
|
||||
if (camelCaseWeight !== undefined) {
|
||||
return createPatternMatch(PatternMatchKind.camelCase, punctuationStripped, /*isCaseSensitive:*/ true, /*camelCaseWeight:*/ camelCaseWeight);
|
||||
@ -259,8 +258,8 @@ namespace ts {
|
||||
// f) Is the pattern a substring of the candidate starting on one of the candidate's word boundaries?
|
||||
|
||||
// We could check every character boundary start of the candidate for the pattern. However, that's
|
||||
// an m * n operation in the wost case. Instead, find the first instance of the pattern
|
||||
// substring, and see if it starts on a capital letter. It seems unlikely that the user will try to
|
||||
// an m * n operation in the wost case. Instead, find the first instance of the pattern
|
||||
// substring, and see if it starts on a capital letter. It seems unlikely that the user will try to
|
||||
// filter the list based on a substring that starts on a capital letter and also with a lowercase one.
|
||||
// (Pattern: fogbar, Candidate: quuxfogbarFogBar).
|
||||
if (chunk.text.length < candidate.length) {
|
||||
@ -275,7 +274,7 @@ namespace ts {
|
||||
|
||||
function containsSpaceOrAsterisk(text: string): boolean {
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
let ch = text.charCodeAt(i);
|
||||
const ch = text.charCodeAt(i);
|
||||
if (ch === CharacterCodes.space || ch === CharacterCodes.asterisk) {
|
||||
return true;
|
||||
}
|
||||
@ -293,7 +292,7 @@ namespace ts {
|
||||
// Note: if the segment contains a space or an asterisk then we must assume that it's a
|
||||
// multi-word segment.
|
||||
if (!containsSpaceOrAsterisk(segment.totalTextChunk.text)) {
|
||||
let match = matchTextChunk(candidate, segment.totalTextChunk, /*punctuationStripped:*/ false);
|
||||
const match = matchTextChunk(candidate, segment.totalTextChunk, /*punctuationStripped:*/ false);
|
||||
if (match) {
|
||||
return [match];
|
||||
}
|
||||
@ -317,7 +316,7 @@ namespace ts {
|
||||
//
|
||||
// c) If the word is entirely lowercase, then check if it is contained anywhere in the
|
||||
// candidate in a case insensitive manner. If so, return that there was a substring
|
||||
// match.
|
||||
// match.
|
||||
//
|
||||
// Note: We only have a substring match if the lowercase part is prefix match of
|
||||
// some word part. That way we don't match something like 'Class' when the user
|
||||
@ -331,17 +330,17 @@ namespace ts {
|
||||
// e) If the word was not entirely lowercase, then attempt a camel cased match as
|
||||
// well.
|
||||
//
|
||||
// f) The word is all lower case. Is it a case insensitive substring of the candidate starting
|
||||
// f) The word is all lower case. Is it a case insensitive substring of the candidate starting
|
||||
// on a part boundary of the candidate?
|
||||
//
|
||||
// Only if all words have some sort of match is the pattern considered matched.
|
||||
|
||||
let subWordTextChunks = segment.subWordTextChunks;
|
||||
const subWordTextChunks = segment.subWordTextChunks;
|
||||
let matches: PatternMatch[] = undefined;
|
||||
|
||||
for (let subWordTextChunk of subWordTextChunks) {
|
||||
for (const subWordTextChunk of subWordTextChunks) {
|
||||
// Try to match the candidate with this word
|
||||
let result = matchTextChunk(candidate, subWordTextChunk, /*punctuationStripped:*/ true);
|
||||
const result = matchTextChunk(candidate, subWordTextChunk, /*punctuationStripped:*/ true);
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
@ -354,18 +353,18 @@ namespace ts {
|
||||
}
|
||||
|
||||
function partStartsWith(candidate: string, candidateSpan: TextSpan, pattern: string, ignoreCase: boolean, patternSpan?: TextSpan): boolean {
|
||||
let patternPartStart = patternSpan ? patternSpan.start : 0;
|
||||
let patternPartLength = patternSpan ? patternSpan.length : pattern.length;
|
||||
const patternPartStart = patternSpan ? patternSpan.start : 0;
|
||||
const patternPartLength = patternSpan ? patternSpan.length : pattern.length;
|
||||
|
||||
if (patternPartLength > candidateSpan.length) {
|
||||
// Pattern part is longer than the candidate part. There can never be a match.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (ignoreCase) {
|
||||
for (let i = 0; i < patternPartLength; i++) {
|
||||
let ch1 = pattern.charCodeAt(patternPartStart + i);
|
||||
let ch2 = candidate.charCodeAt(candidateSpan.start + i);
|
||||
const ch1 = pattern.charCodeAt(patternPartStart + i);
|
||||
const ch2 = candidate.charCodeAt(candidateSpan.start + i);
|
||||
if (toLowerCase(ch1) !== toLowerCase(ch2)) {
|
||||
return false;
|
||||
}
|
||||
@ -373,8 +372,8 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < patternPartLength; i++) {
|
||||
let ch1 = pattern.charCodeAt(patternPartStart + i);
|
||||
let ch2 = candidate.charCodeAt(candidateSpan.start + i);
|
||||
const ch1 = pattern.charCodeAt(patternPartStart + i);
|
||||
const ch2 = candidate.charCodeAt(candidateSpan.start + i);
|
||||
if (ch1 !== ch2) {
|
||||
return false;
|
||||
}
|
||||
@ -385,12 +384,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function tryCamelCaseMatch(candidate: string, candidateParts: TextSpan[], chunk: TextChunk, ignoreCase: boolean): number {
|
||||
let chunkCharacterSpans = chunk.characterSpans;
|
||||
const chunkCharacterSpans = chunk.characterSpans;
|
||||
|
||||
// Note: we may have more pattern parts than candidate parts. This is because multiple
|
||||
// pattern parts may match a candidate part. For example "SiUI" against "SimpleUI".
|
||||
// We'll have 3 pattern parts Si/U/I against two candidate parts Simple/UI. However, U
|
||||
// and I will both match in UI.
|
||||
// and I will both match in UI.
|
||||
|
||||
let currentCandidate = 0;
|
||||
let currentChunkSpan = 0;
|
||||
@ -426,14 +425,14 @@ namespace ts {
|
||||
// Consider the case of matching SiUI against SimpleUIElement. The candidate parts
|
||||
// will be Simple/UI/Element, and the pattern parts will be Si/U/I. We'll match 'Si'
|
||||
// against 'Simple' first. Then we'll match 'U' against 'UI'. However, we want to
|
||||
// still keep matching pattern parts against that candidate part.
|
||||
// still keep matching pattern parts against that candidate part.
|
||||
for (; currentChunkSpan < chunkCharacterSpans.length; currentChunkSpan++) {
|
||||
let chunkCharacterSpan = chunkCharacterSpans[currentChunkSpan];
|
||||
const chunkCharacterSpan = chunkCharacterSpans[currentChunkSpan];
|
||||
|
||||
if (gotOneMatchThisCandidate) {
|
||||
// We've already gotten one pattern part match in this candidate. We will
|
||||
// only continue trying to consumer pattern parts if the last part and this
|
||||
// part are both upper case.
|
||||
// part are both upper case.
|
||||
if (!isUpperCaseLetter(chunk.text.charCodeAt(chunkCharacterSpans[currentChunkSpan - 1].start)) ||
|
||||
!isUpperCaseLetter(chunk.text.charCodeAt(chunkCharacterSpans[currentChunkSpan].start))) {
|
||||
break;
|
||||
@ -470,55 +469,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to compare two matches to determine which is better. Matches are first
|
||||
// ordered by kind (so all prefix matches always beat all substring matches). Then, if the
|
||||
// match is a camel case match, the relative weights of the match are used to determine
|
||||
// which is better (with a greater weight being better). Then if the match is of the same
|
||||
// type, then a case sensitive match is considered better than an insensitive one.
|
||||
function patternMatchCompareTo(match1: PatternMatch, match2: PatternMatch): number {
|
||||
return compareType(match1, match2) ||
|
||||
compareCamelCase(match1, match2) ||
|
||||
compareCase(match1, match2) ||
|
||||
comparePunctuation(match1, match2);
|
||||
}
|
||||
|
||||
function comparePunctuation(result1: PatternMatch, result2: PatternMatch) {
|
||||
// Consider a match to be better if it was successful without stripping punctuation
|
||||
// versus a match that had to strip punctuation to succeed.
|
||||
if (result1.punctuationStripped !== result2.punctuationStripped) {
|
||||
return result1.punctuationStripped ? 1 : -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function compareCase(result1: PatternMatch, result2: PatternMatch) {
|
||||
if (result1.isCaseSensitive !== result2.isCaseSensitive) {
|
||||
return result1.isCaseSensitive ? -1 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function compareType(result1: PatternMatch, result2: PatternMatch) {
|
||||
return result1.kind - result2.kind;
|
||||
}
|
||||
|
||||
function compareCamelCase(result1: PatternMatch, result2: PatternMatch) {
|
||||
if (result1.kind === PatternMatchKind.camelCase && result2.kind === PatternMatchKind.camelCase) {
|
||||
// Swap the values here. If result1 has a higher weight, then we want it to come
|
||||
// first.
|
||||
return result2.camelCaseWeight - result1.camelCaseWeight;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function createSegment(text: string): Segment {
|
||||
return {
|
||||
totalTextChunk: createTextChunk(text),
|
||||
subWordTextChunks: breakPatternIntoTextChunks(text)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// A segment is considered invalid if we couldn't find any words in it.
|
||||
@ -536,9 +491,9 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: find a way to determine this for any unicode characters in a
|
||||
// TODO: find a way to determine this for any unicode characters in a
|
||||
// non-allocating manner.
|
||||
let str = String.fromCharCode(ch);
|
||||
const str = String.fromCharCode(ch);
|
||||
return str === str.toUpperCase();
|
||||
}
|
||||
|
||||
@ -553,22 +508,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
|
||||
// TODO: find a way to determine this for any unicode characters in a
|
||||
// TODO: find a way to determine this for any unicode characters in a
|
||||
// non-allocating manner.
|
||||
let str = String.fromCharCode(ch);
|
||||
const str = String.fromCharCode(ch);
|
||||
return str === str.toLowerCase();
|
||||
}
|
||||
|
||||
function containsUpperCaseLetter(string: string): boolean {
|
||||
for (let i = 0, n = string.length; i < n; i++) {
|
||||
if (isUpperCaseLetter(string.charCodeAt(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function startsWith(string: string, search: string) {
|
||||
for (let i = 0, n = search.length; i < n; i++) {
|
||||
if (string.charCodeAt(i) !== search.charCodeAt(i)) {
|
||||
@ -593,8 +538,8 @@ namespace ts {
|
||||
// Assumes 'value' is already lowercase.
|
||||
function startsWithIgnoringCase(string: string, value: string, start: number): boolean {
|
||||
for (let i = 0, n = value.length; i < n; i++) {
|
||||
let ch1 = toLowerCase(string.charCodeAt(i + start));
|
||||
let ch2 = value.charCodeAt(i);
|
||||
const ch1 = toLowerCase(string.charCodeAt(i + start));
|
||||
const ch2 = value.charCodeAt(i);
|
||||
|
||||
if (ch1 !== ch2) {
|
||||
return false;
|
||||
@ -614,7 +559,7 @@ namespace ts {
|
||||
return ch;
|
||||
}
|
||||
|
||||
// TODO: find a way to compute this for any unicode characters in a
|
||||
// TODO: find a way to compute this for any unicode characters in a
|
||||
// non-allocating manner.
|
||||
return String.fromCharCode(ch).toLowerCase().charCodeAt(0);
|
||||
}
|
||||
@ -629,12 +574,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function breakPatternIntoTextChunks(pattern: string): TextChunk[] {
|
||||
let result: TextChunk[] = [];
|
||||
const result: TextChunk[] = [];
|
||||
let wordStart = 0;
|
||||
let wordLength = 0;
|
||||
|
||||
for (let i = 0; i < pattern.length; i++) {
|
||||
let ch = pattern.charCodeAt(i);
|
||||
const ch = pattern.charCodeAt(i);
|
||||
if (isWordChar(ch)) {
|
||||
if (wordLength++ === 0) {
|
||||
wordStart = i;
|
||||
@ -656,13 +601,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
function createTextChunk(text: string): TextChunk {
|
||||
let textLowerCase = text.toLowerCase();
|
||||
const textLowerCase = text.toLowerCase();
|
||||
return {
|
||||
text,
|
||||
textLowerCase,
|
||||
isLowerCase: text === textLowerCase,
|
||||
characterSpans: breakIntoCharacterSpans(text)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* @internal */ export function breakIntoCharacterSpans(identifier: string): TextSpan[] {
|
||||
@ -674,15 +619,15 @@ namespace ts {
|
||||
}
|
||||
|
||||
function breakIntoSpans(identifier: string, word: boolean): TextSpan[] {
|
||||
let result: TextSpan[] = [];
|
||||
const result: TextSpan[] = [];
|
||||
|
||||
let wordStart = 0;
|
||||
for (let i = 1, n = identifier.length; i < n; i++) {
|
||||
let lastIsDigit = isDigit(identifier.charCodeAt(i - 1));
|
||||
let currentIsDigit = isDigit(identifier.charCodeAt(i));
|
||||
const lastIsDigit = isDigit(identifier.charCodeAt(i - 1));
|
||||
const currentIsDigit = isDigit(identifier.charCodeAt(i));
|
||||
|
||||
let hasTransitionFromLowerToUpper = transitionFromLowerToUpper(identifier, word, i);
|
||||
let hasTransitionFromUpperToLower = transitionFromUpperToLower(identifier, word, i, wordStart);
|
||||
const hasTransitionFromLowerToUpper = transitionFromLowerToUpper(identifier, word, i);
|
||||
const hasTransitionFromUpperToLower = transitionFromUpperToLower(identifier, word, i, wordStart);
|
||||
|
||||
if (charIsPunctuation(identifier.charCodeAt(i - 1)) ||
|
||||
charIsPunctuation(identifier.charCodeAt(i)) ||
|
||||
@ -738,7 +683,7 @@ namespace ts {
|
||||
|
||||
function isAllPunctuation(identifier: string, start: number, end: number): boolean {
|
||||
for (let i = start; i < end; i++) {
|
||||
let ch = identifier.charCodeAt(i);
|
||||
const ch = identifier.charCodeAt(i);
|
||||
|
||||
// We don't consider _ or $ as punctuation as there may be things with that name.
|
||||
if (!charIsPunctuation(ch) || ch === CharacterCodes._ || ch === CharacterCodes.$) {
|
||||
@ -759,8 +704,8 @@ namespace ts {
|
||||
// etc.
|
||||
if (index !== wordStart &&
|
||||
index + 1 < identifier.length) {
|
||||
let currentIsUpper = isUpperCaseLetter(identifier.charCodeAt(index));
|
||||
let nextIsLower = isLowerCaseLetter(identifier.charCodeAt(index + 1));
|
||||
const currentIsUpper = isUpperCaseLetter(identifier.charCodeAt(index));
|
||||
const nextIsLower = isLowerCaseLetter(identifier.charCodeAt(index + 1));
|
||||
|
||||
if (currentIsUpper && nextIsLower) {
|
||||
// We have a transition from an upper to a lower letter here. But we only
|
||||
@ -786,12 +731,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function transitionFromLowerToUpper(identifier: string, word: boolean, index: number): boolean {
|
||||
let lastIsUpper = isUpperCaseLetter(identifier.charCodeAt(index - 1));
|
||||
let currentIsUpper = isUpperCaseLetter(identifier.charCodeAt(index));
|
||||
const lastIsUpper = isUpperCaseLetter(identifier.charCodeAt(index - 1));
|
||||
const currentIsUpper = isUpperCaseLetter(identifier.charCodeAt(index));
|
||||
|
||||
// See if the casing indicates we're starting a new word. Note: if we're breaking on
|
||||
// words, then just seeing an upper case character isn't enough. Instead, it has to
|
||||
// be uppercase and the previous character can't be uppercase.
|
||||
// be uppercase and the previous character can't be uppercase.
|
||||
//
|
||||
// For example, breaking "AddMetadata" on words would make: Add Metadata
|
||||
//
|
||||
@ -802,7 +747,7 @@ namespace ts {
|
||||
// on characters would be: A M
|
||||
//
|
||||
// We break the search string on characters. But we break the symbol name on words.
|
||||
let transition = word
|
||||
const transition = word
|
||||
? (currentIsUpper && !lastIsUpper)
|
||||
: currentIsUpper;
|
||||
return transition;
|
||||
|
||||
@ -34,7 +34,7 @@ module M {
|
||||
|
||||
|
||||
//// [test.jsx]
|
||||
define(["require", "exports", 'react-router'], function (require, exports, ReactRouter) {
|
||||
define(["require", "exports", 'react', 'react-router'], function (require, exports, React, ReactRouter) {
|
||||
"use strict";
|
||||
var Route = ReactRouter.Route;
|
||||
var routes1 = <Route />;
|
||||
|
||||
10
tests/baselines/reference/tsxPreserveEmit2.js
Normal file
10
tests/baselines/reference/tsxPreserveEmit2.js
Normal file
@ -0,0 +1,10 @@
|
||||
//// [test.tsx]
|
||||
|
||||
|
||||
var Route: any;
|
||||
var routes1 = <Route />;
|
||||
|
||||
|
||||
//// [test.jsx]
|
||||
var Route;
|
||||
var routes1 = <Route />;
|
||||
10
tests/baselines/reference/tsxPreserveEmit2.symbols
Normal file
10
tests/baselines/reference/tsxPreserveEmit2.symbols
Normal file
@ -0,0 +1,10 @@
|
||||
=== tests/cases/conformance/jsx/test.tsx ===
|
||||
|
||||
|
||||
var Route: any;
|
||||
>Route : Symbol(Route, Decl(test.tsx, 2, 3))
|
||||
|
||||
var routes1 = <Route />;
|
||||
>routes1 : Symbol(routes1, Decl(test.tsx, 3, 3))
|
||||
>Route : Symbol(Route, Decl(test.tsx, 2, 3))
|
||||
|
||||
11
tests/baselines/reference/tsxPreserveEmit2.types
Normal file
11
tests/baselines/reference/tsxPreserveEmit2.types
Normal file
@ -0,0 +1,11 @@
|
||||
=== tests/cases/conformance/jsx/test.tsx ===
|
||||
|
||||
|
||||
var Route: any;
|
||||
>Route : any
|
||||
|
||||
var routes1 = <Route />;
|
||||
>routes1 : any
|
||||
><Route /> : any
|
||||
>Route : any
|
||||
|
||||
24
tests/baselines/reference/tsxPreserveEmit3.js
Normal file
24
tests/baselines/reference/tsxPreserveEmit3.js
Normal file
@ -0,0 +1,24 @@
|
||||
//// [tests/cases/conformance/jsx/tsxPreserveEmit3.tsx] ////
|
||||
|
||||
//// [file.tsx]
|
||||
|
||||
declare module JSX {
|
||||
interface Element { }
|
||||
interface IntrinsicElements {
|
||||
[s: string]: any;
|
||||
}
|
||||
}
|
||||
|
||||
//// [test.d.ts]
|
||||
export var React;
|
||||
|
||||
//// [react-consumer.tsx]
|
||||
// This import should be elided
|
||||
import {React} from "./test";
|
||||
|
||||
|
||||
//// [file.jsx]
|
||||
//// [react-consumer.jsx]
|
||||
define(["require", "exports"], function (require, exports) {
|
||||
"use strict";
|
||||
});
|
||||
25
tests/baselines/reference/tsxPreserveEmit3.symbols
Normal file
25
tests/baselines/reference/tsxPreserveEmit3.symbols
Normal file
@ -0,0 +1,25 @@
|
||||
=== tests/cases/conformance/jsx/file.tsx ===
|
||||
|
||||
declare module JSX {
|
||||
>JSX : Symbol(JSX, Decl(file.tsx, 0, 0))
|
||||
|
||||
interface Element { }
|
||||
>Element : Symbol(Element, Decl(file.tsx, 1, 20))
|
||||
|
||||
interface IntrinsicElements {
|
||||
>IntrinsicElements : Symbol(IntrinsicElements, Decl(file.tsx, 2, 22))
|
||||
|
||||
[s: string]: any;
|
||||
>s : Symbol(s, Decl(file.tsx, 4, 3))
|
||||
}
|
||||
}
|
||||
|
||||
=== tests/cases/conformance/jsx/test.d.ts ===
|
||||
export var React;
|
||||
>React : Symbol(React, Decl(test.d.ts, 0, 10))
|
||||
|
||||
=== tests/cases/conformance/jsx/react-consumer.tsx ===
|
||||
// This import should be elided
|
||||
import {React} from "./test";
|
||||
>React : Symbol(React, Decl(react-consumer.tsx, 1, 8))
|
||||
|
||||
25
tests/baselines/reference/tsxPreserveEmit3.types
Normal file
25
tests/baselines/reference/tsxPreserveEmit3.types
Normal file
@ -0,0 +1,25 @@
|
||||
=== tests/cases/conformance/jsx/file.tsx ===
|
||||
|
||||
declare module JSX {
|
||||
>JSX : any
|
||||
|
||||
interface Element { }
|
||||
>Element : Element
|
||||
|
||||
interface IntrinsicElements {
|
||||
>IntrinsicElements : IntrinsicElements
|
||||
|
||||
[s: string]: any;
|
||||
>s : string
|
||||
}
|
||||
}
|
||||
|
||||
=== tests/cases/conformance/jsx/test.d.ts ===
|
||||
export var React;
|
||||
>React : any
|
||||
|
||||
=== tests/cases/conformance/jsx/react-consumer.tsx ===
|
||||
// This import should be elided
|
||||
import {React} from "./test";
|
||||
>React : any
|
||||
|
||||
@ -8,7 +8,9 @@ declare module JSX {
|
||||
declare var React: any;
|
||||
|
||||
<div>Dot goes here: · ¬AnEntity; </div>;
|
||||
<div>Be careful of "-ed strings!</div>;
|
||||
|
||||
|
||||
//// [file.js]
|
||||
React.createElement("div", null, "Dot goes here: · ¬AnEntity; ");
|
||||
React.createElement("div", null, "Be careful of \"-ed strings!");
|
||||
|
||||
@ -19,3 +19,7 @@ declare var React: any;
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
|
||||
<div>Be careful of "-ed strings!</div>;
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
|
||||
|
||||
|
||||
@ -20,3 +20,8 @@ declare var React: any;
|
||||
>div : any
|
||||
>div : any
|
||||
|
||||
<div>Be careful of "-ed strings!</div>;
|
||||
><div>Be careful of "-ed strings!</div> : JSX.Element
|
||||
>div : any
|
||||
>div : any
|
||||
|
||||
|
||||
8
tests/cases/conformance/jsx/tsxPreserveEmit2.tsx
Normal file
8
tests/cases/conformance/jsx/tsxPreserveEmit2.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
//@module: amd
|
||||
//@jsx: preserve
|
||||
//@target: ES5
|
||||
|
||||
//@Filename: test.tsx
|
||||
|
||||
var Route: any;
|
||||
var routes1 = <Route />;
|
||||
17
tests/cases/conformance/jsx/tsxPreserveEmit3.tsx
Normal file
17
tests/cases/conformance/jsx/tsxPreserveEmit3.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
//@jsx: preserve
|
||||
//@module: amd
|
||||
|
||||
//@filename: file.tsx
|
||||
declare module JSX {
|
||||
interface Element { }
|
||||
interface IntrinsicElements {
|
||||
[s: string]: any;
|
||||
}
|
||||
}
|
||||
|
||||
//@filename: test.d.ts
|
||||
export var React;
|
||||
|
||||
//@filename: react-consumer.tsx
|
||||
// This import should be elided
|
||||
import {React} from "./test";
|
||||
@ -9,3 +9,4 @@ declare module JSX {
|
||||
declare var React: any;
|
||||
|
||||
<div>Dot goes here: · ¬AnEntity; </div>;
|
||||
<div>Be careful of "-ed strings!</div>;
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
//// </div>
|
||||
//// )
|
||||
////}
|
||||
////
|
||||
////
|
||||
////function foo1() {
|
||||
//// return (
|
||||
//// <div className="commentBox" data-id="test">
|
||||
@ -45,8 +45,26 @@
|
||||
//// class3= {/*5*/
|
||||
//// }/>/*6*/
|
||||
//// )
|
||||
////}
|
||||
////
|
||||
////(function () {
|
||||
//// return <div
|
||||
////className=""/*attrAutoformat*/
|
||||
/////*attrIndent*/
|
||||
////id={
|
||||
////"abc" + "cde"/*expressionAutoformat*/
|
||||
/////*expressionIndent*/
|
||||
////}
|
||||
|
||||
//// >/*danglingBracketAutoformat*/
|
||||
//// </div>/*closingTagAutoformat*/
|
||||
////})
|
||||
////
|
||||
////let h5 = <h5>
|
||||
////<span>/*childJsxElementAutoformat*/
|
||||
/////*childJsxElementIndent*/
|
||||
////<span></span>/*grandchildJsxElementAutoformat*/
|
||||
////</span>/*containedClosingTagAutoformat*/
|
||||
////</h5>
|
||||
|
||||
format.document();
|
||||
goTo.marker("autoformat");
|
||||
@ -83,3 +101,28 @@ goTo.marker("5");
|
||||
verify.currentLineContentIs(' class3= {');
|
||||
goTo.marker("6");
|
||||
verify.currentLineContentIs(' }/>');
|
||||
|
||||
|
||||
goTo.marker("attrAutoformat");
|
||||
verify.currentLineContentIs(' className=""');
|
||||
goTo.marker("attrIndent");
|
||||
verify.indentationIs(8);
|
||||
goTo.marker("expressionAutoformat");
|
||||
verify.currentLineContentIs(' "abc" + "cde"');
|
||||
goTo.marker("expressionIndent");
|
||||
verify.indentationIs(12);
|
||||
|
||||
goTo.marker("danglingBracketAutoformat")
|
||||
// TODO: verify.currentLineContentIs(" >");
|
||||
verify.currentLineContentIs(" >");
|
||||
goTo.marker("closingTagAutoformat");
|
||||
verify.currentLineContentIs(" </div>");
|
||||
|
||||
goTo.marker("childJsxElementAutoformat");
|
||||
verify.currentLineContentIs(" <span>");
|
||||
goTo.marker("childJsxElementIndent");
|
||||
verify.indentationIs(8);
|
||||
goTo.marker("grandchildJsxElementAutoformat");
|
||||
verify.currentLineContentIs(" <span></span>");
|
||||
goTo.marker("containedClosingTagAutoformat");
|
||||
verify.currentLineContentIs(" </span>");
|
||||
Loading…
x
Reference in New Issue
Block a user