mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge remote-tracking branch 'origin/master' into go_to_implementation_pr
Refactored goToImplementation out of services
This commit is contained in:
@@ -54,3 +54,4 @@ internal/
|
||||
!tests/cases/projects/projectOption/**/node_modules
|
||||
!tests/cases/projects/NodeModulesSearch/**/*
|
||||
!tests/baselines/reference/project/nodeModules*/**/*
|
||||
.idea
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ matrix:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- transforms
|
||||
- release-2.0
|
||||
|
||||
install:
|
||||
- npm uninstall typescript
|
||||
|
||||
@@ -40,10 +40,6 @@ In general, things we find useful when reviewing suggestions are:
|
||||
|
||||
# Instructions for Contributing Code
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Contributing bug fixes
|
||||
|
||||
TypeScript is currently accepting contributions in the form of bug fixes. A bug must have an issue tracking it in the issue tracker that has been approved ("Milestone == Community") by the TypeScript team. Your pull request should include a link to the bug that you are fixing. If you've submitted a PR for a bug, please post a comment in the bug to avoid duplication of effort.
|
||||
|
||||
+11
-7
@@ -39,9 +39,11 @@ const gulp = helpMaker(originalGulp);
|
||||
const mochaParallel = require("./scripts/mocha-parallel.js");
|
||||
const {runTestsInParallel} = mochaParallel;
|
||||
|
||||
Error.stackTraceLimit = 1000;
|
||||
|
||||
const cmdLineOptions = minimist(process.argv.slice(2), {
|
||||
boolean: ["debug", "light", "colors", "lint", "soft"],
|
||||
string: ["browser", "tests", "host", "reporter"],
|
||||
string: ["browser", "tests", "host", "reporter", "stackTraceLimit"],
|
||||
alias: {
|
||||
d: "debug",
|
||||
t: "tests",
|
||||
@@ -559,6 +561,7 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
|
||||
const debug = cmdLineOptions["debug"];
|
||||
const tests = cmdLineOptions["tests"];
|
||||
const light = cmdLineOptions["light"];
|
||||
const stackTraceLimit = cmdLineOptions["stackTraceLimit"];
|
||||
const testConfigFile = "test.config";
|
||||
if (fs.existsSync(testConfigFile)) {
|
||||
fs.unlinkSync(testConfigFile);
|
||||
@@ -578,7 +581,7 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
|
||||
}
|
||||
|
||||
if (tests || light || taskConfigsFolder) {
|
||||
writeTestConfigFile(tests, light, taskConfigsFolder, workerCount);
|
||||
writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit);
|
||||
}
|
||||
|
||||
if (tests && tests.toLocaleLowerCase() === "rwc") {
|
||||
@@ -727,6 +730,7 @@ gulp.task("browserify", "Runs browserify on run.js to produce a file suitable fo
|
||||
sourcemaps: {
|
||||
"built/local/_stream_0.js": originalMap,
|
||||
"built/local/bundle.js": maps,
|
||||
"node_modules/source-map-support/source-map-support.js": undefined,
|
||||
}
|
||||
});
|
||||
const finalMap = chain.apply();
|
||||
@@ -756,8 +760,8 @@ function cleanTestDirs(done: (e?: any) => void) {
|
||||
}
|
||||
|
||||
// used to pass data from jake command line directly to run.js
|
||||
function writeTestConfigFile(tests: string, light: boolean, taskConfigsFolder?: string, workerCount?: number) {
|
||||
const testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light: light, workerCount: workerCount, taskConfigsFolder: taskConfigsFolder });
|
||||
function writeTestConfigFile(tests: string, light: boolean, taskConfigsFolder?: string, workerCount?: number, stackTraceLimit?: string) {
|
||||
const testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light, workerCount, stackTraceLimit, taskConfigsFolder });
|
||||
console.log("Running tests with config: " + testConfigContents);
|
||||
fs.writeFileSync("test.config", testConfigContents);
|
||||
}
|
||||
@@ -887,7 +891,7 @@ gulp.task(loggedIOJsPath, false, [], (done) => {
|
||||
const temp = path.join(builtLocalDirectory, "temp");
|
||||
mkdirP(temp, (err) => {
|
||||
if (err) { console.error(err); done(err); process.exit(1); };
|
||||
exec(host, [LKGCompiler, "--outdir", temp, loggedIOpath], () => {
|
||||
exec(host, [LKGCompiler, "--types --outdir", temp, loggedIOpath], () => {
|
||||
fs.renameSync(path.join(temp, "/harness/loggedIO.js"), loggedIOJsPath);
|
||||
del(temp).then(() => done(), done);
|
||||
}, done);
|
||||
@@ -908,8 +912,8 @@ gulp.task(instrumenterJsPath, false, [servicesFile], () => {
|
||||
.pipe(gulp.dest("."));
|
||||
});
|
||||
|
||||
gulp.task("tsc-instrumented", "Builds an instrumented tsc.js", [loggedIOJsPath, instrumenterJsPath, servicesFile], (done) => {
|
||||
exec(host, [instrumenterJsPath, "record", "iocapture", builtLocalDirectory, compilerFilename], done, done);
|
||||
gulp.task("tsc-instrumented", "Builds an instrumented tsc.js", ["local", loggedIOJsPath, instrumenterJsPath, servicesFile], (done) => {
|
||||
exec(host, [instrumenterJsPath, "record", "iocapture", builtLocalCompiler], done, done);
|
||||
});
|
||||
|
||||
gulp.task("update-sublime", "Updates the sublime plugin's tsserver", ["local", serverFile], () => {
|
||||
|
||||
+29
-12
@@ -120,16 +120,29 @@ var servicesSources = [
|
||||
].map(function (f) {
|
||||
return path.join(compilerDirectory, f);
|
||||
}).concat([
|
||||
"types.ts",
|
||||
"utilities.ts",
|
||||
"breakpoints.ts",
|
||||
"classifier.ts",
|
||||
"completions.ts",
|
||||
"documentHighlights.ts",
|
||||
"documentRegistry.ts",
|
||||
"findAllReferences.ts",
|
||||
"goToDefinition.ts",
|
||||
"goToImplementation.ts",
|
||||
"jsDoc.ts",
|
||||
"jsTyping.ts",
|
||||
"navigateTo.ts",
|
||||
"navigationBar.ts",
|
||||
"outliningElementsCollector.ts",
|
||||
"patternMatcher.ts",
|
||||
"preProcess.ts",
|
||||
"rename.ts",
|
||||
"services.ts",
|
||||
"shims.ts",
|
||||
"signatureHelp.ts",
|
||||
"types.ts",
|
||||
"utilities.ts",
|
||||
"symbolDisplay.ts",
|
||||
"transpile.ts",
|
||||
"formatting/formatting.ts",
|
||||
"formatting/formattingContext.ts",
|
||||
"formatting/formattingRequestKind.ts",
|
||||
@@ -204,6 +217,7 @@ var harnessSources = harnessCoreSources.concat([
|
||||
"moduleResolution.ts",
|
||||
"tsconfigParsing.ts",
|
||||
"commandLineParsing.ts",
|
||||
"configurationExtension.ts",
|
||||
"convertCompilerOptionsFromJson.ts",
|
||||
"convertTypingOptionsFromJson.ts",
|
||||
"tsserverProjectSystem.ts",
|
||||
@@ -776,7 +790,7 @@ function cleanTestDirs() {
|
||||
|
||||
// used to pass data from jake command line directly to run.js
|
||||
function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit) {
|
||||
var testConfigContents = JSON.stringify({
|
||||
var testConfigContents = JSON.stringify({
|
||||
test: tests ? [tests] : undefined,
|
||||
light: light,
|
||||
workerCount: workerCount,
|
||||
@@ -992,15 +1006,18 @@ function acceptBaseline(containerFolder) {
|
||||
var deleteEnding = '.delete';
|
||||
for (var i in files) {
|
||||
var filename = files[i];
|
||||
if (filename.substr(filename.length - deleteEnding.length) === deleteEnding) {
|
||||
filename = filename.substr(0, filename.length - deleteEnding.length);
|
||||
fs.unlinkSync(path.join(targetFolder, filename));
|
||||
} else {
|
||||
var target = path.join(targetFolder, filename);
|
||||
if (fs.existsSync(target)) {
|
||||
fs.unlinkSync(target);
|
||||
var fullLocalPath = path.join(sourceFolder, filename);
|
||||
if (fs.statSync(fullLocalPath).isFile()) {
|
||||
if (filename.substr(filename.length - deleteEnding.length) === deleteEnding) {
|
||||
filename = filename.substr(0, filename.length - deleteEnding.length);
|
||||
fs.unlinkSync(path.join(targetFolder, filename));
|
||||
} else {
|
||||
var target = path.join(targetFolder, filename);
|
||||
if (fs.existsSync(target)) {
|
||||
fs.unlinkSync(target);
|
||||
}
|
||||
fs.renameSync(path.join(sourceFolder, filename), target);
|
||||
}
|
||||
fs.renameSync(path.join(sourceFolder, filename), target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1039,7 +1056,7 @@ var loggedIOJsPath = builtLocalDirectory + 'loggedIO.js';
|
||||
file(loggedIOJsPath, [builtLocalDirectory, loggedIOpath], function () {
|
||||
var temp = builtLocalDirectory + 'temp';
|
||||
jake.mkdirP(temp);
|
||||
var options = "--outdir " + temp + ' ' + loggedIOpath;
|
||||
var options = "--types --outdir " + temp + ' ' + loggedIOpath;
|
||||
var cmd = host + " " + LKGDirectory + compilerFilename + " " + options + " ";
|
||||
console.log(cmd + "\n");
|
||||
var ex = jake.createExec([cmd]);
|
||||
|
||||
@@ -30,8 +30,12 @@ There are many ways to [contribute](https://github.com/Microsoft/TypeScript/blob
|
||||
* Engage with other TypeScript users and developers on [StackOverflow](http://stackoverflow.com/questions/tagged/typescript).
|
||||
* Join the [#typescript](http://twitter.com/#!/search/realtime/%23typescript) discussion on Twitter.
|
||||
* [Contribute bug fixes](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md).
|
||||
* Read the language specification ([docx](https://github.com/Microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.docx?raw=true), [pdf](https://github.com/Microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.pdf?raw=true), [md](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md)).
|
||||
* Read the language specification ([docx](https://github.com/Microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.docx?raw=true),
|
||||
[pdf](https://github.com/Microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.pdf?raw=true), [md](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md)).
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see
|
||||
the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com)
|
||||
with any additional questions or comments.
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -91,4 +95,4 @@ node built/local/tsc.js hello.ts
|
||||
|
||||
## Roadmap
|
||||
|
||||
For details on our planned features and future direction please refer to our [roadmap](https://github.com/Microsoft/TypeScript/wiki/Roadmap).
|
||||
For details on our planned features and future direction please refer to our [roadmap](https://github.com/Microsoft/TypeScript/wiki/Roadmap).
|
||||
Vendored
+2
-2
@@ -20,7 +20,7 @@ interface Map<K, V> {
|
||||
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void;
|
||||
get(key: K): V | undefined;
|
||||
has(key: K): boolean;
|
||||
set(key: K, value?: V): this;
|
||||
set(key: K, value: V): this;
|
||||
readonly size: number;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ interface WeakMap<K, V> {
|
||||
delete(key: K): boolean;
|
||||
get(key: K): V | undefined;
|
||||
has(key: K): boolean;
|
||||
set(key: K, value?: V): this;
|
||||
set(key: K, value: V): this;
|
||||
}
|
||||
|
||||
interface WeakMapConstructor {
|
||||
|
||||
Vendored
+2
-2
@@ -19,7 +19,7 @@ interface ProxyHandler<T> {
|
||||
setPrototypeOf? (target: T, v: any): boolean;
|
||||
isExtensible? (target: T): boolean;
|
||||
preventExtensions? (target: T): boolean;
|
||||
getOwnPropertyDescriptor? (target: T, p: PropertyKey): PropertyDescriptor;
|
||||
getOwnPropertyDescriptor? (target: T, p: PropertyKey): PropertyDescriptor | undefined;
|
||||
has? (target: T, p: PropertyKey): boolean;
|
||||
get? (target: T, p: PropertyKey, receiver: any): any;
|
||||
set? (target: T, p: PropertyKey, value: any, receiver: any): boolean;
|
||||
@@ -35,4 +35,4 @@ interface ProxyConstructor {
|
||||
revocable<T>(target: T, handler: ProxyHandler<T>): { proxy: T; revoke: () => void; };
|
||||
new <T>(target: T, handler: ProxyHandler<T>): T
|
||||
}
|
||||
declare var Proxy: ProxyConstructor;
|
||||
declare var Proxy: ProxyConstructor;
|
||||
|
||||
@@ -9,44 +9,12 @@ export class Rule extends Lint.Rules.AbstractRule {
|
||||
}
|
||||
}
|
||||
|
||||
function isBindingPattern(node: ts.Node): node is ts.BindingPattern {
|
||||
return !!node && (node.kind === ts.SyntaxKind.ArrayBindingPattern || node.kind === ts.SyntaxKind.ObjectBindingPattern);
|
||||
}
|
||||
|
||||
function walkUpBindingElementsAndPatterns(node: ts.Node): ts.Node {
|
||||
while (node && (node.kind === ts.SyntaxKind.BindingElement || isBindingPattern(node))) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
function getCombinedNodeFlags(node: ts.Node): ts.NodeFlags {
|
||||
node = walkUpBindingElementsAndPatterns(node);
|
||||
|
||||
let flags = node.flags;
|
||||
if (node.kind === ts.SyntaxKind.VariableDeclaration) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (node && node.kind === ts.SyntaxKind.VariableDeclarationList) {
|
||||
flags |= node.flags;
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (node && node.kind === ts.SyntaxKind.VariableStatement) {
|
||||
flags |= node.flags;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
function isLet(node: ts.Node) {
|
||||
return !!(getCombinedNodeFlags(node) & ts.NodeFlags.Let);
|
||||
return !!(ts.getCombinedNodeFlags(node) & ts.NodeFlags.Let);
|
||||
}
|
||||
|
||||
function isExported(node: ts.Node) {
|
||||
return !!(getCombinedNodeFlags(node) & ts.NodeFlags.Export);
|
||||
return !!(ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export);
|
||||
}
|
||||
|
||||
function isAssignmentOperator(token: ts.SyntaxKind): boolean {
|
||||
@@ -125,11 +93,16 @@ class PreferConstWalker extends Lint.RuleWalker {
|
||||
|
||||
private visitBindingPatternIdentifiers(pattern: ts.BindingPattern) {
|
||||
for (const element of pattern.elements) {
|
||||
if (element.name.kind === ts.SyntaxKind.Identifier) {
|
||||
this.markAssignment(element.name as ts.Identifier);
|
||||
if (element.kind !== ts.SyntaxKind.BindingElement) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const name = (<ts.BindingElement>element).name;
|
||||
if (name.kind === ts.SyntaxKind.Identifier) {
|
||||
this.markAssignment(name as ts.Identifier);
|
||||
}
|
||||
else {
|
||||
this.visitBindingPatternIdentifiers(element.name as ts.BindingPattern);
|
||||
this.visitBindingPatternIdentifiers(name as ts.BindingPattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,7 +196,9 @@ class PreferConstWalker extends Lint.RuleWalker {
|
||||
|
||||
private collectBindingPatternIdentifiers(value: ts.VariableDeclaration, pattern: ts.BindingPattern, table: ts.MapLike<DeclarationUsages>) {
|
||||
for (const element of pattern.elements) {
|
||||
this.collectNameIdentifiers(value, element.name, table);
|
||||
if (element.kind === ts.SyntaxKind.BindingElement) {
|
||||
this.collectNameIdentifiers(value, (<ts.BindingElement>element).name, table);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-2
@@ -591,6 +591,9 @@ namespace ts {
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
bindPrefixUnaryExpressionFlow(<PrefixUnaryExpression>node);
|
||||
break;
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
bindPostfixUnaryExpressionFlow(<PostfixUnaryExpression>node);
|
||||
break;
|
||||
case SyntaxKind.BinaryExpression:
|
||||
bindBinaryExpressionFlow(<BinaryExpression>node);
|
||||
break;
|
||||
@@ -1106,6 +1109,16 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
forEachChild(node, bind);
|
||||
if (node.operator === SyntaxKind.PlusEqualsToken || node.operator === SyntaxKind.MinusMinusToken) {
|
||||
bindAssignmentTargetFlow(node.operand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bindPostfixUnaryExpressionFlow(node: PostfixUnaryExpression) {
|
||||
forEachChild(node, bind);
|
||||
if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) {
|
||||
bindAssignmentTargetFlow(node.operand);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1150,8 +1163,8 @@ namespace ts {
|
||||
currentFlow = finishFlowLabel(postExpressionLabel);
|
||||
}
|
||||
|
||||
function bindInitializedVariableFlow(node: VariableDeclaration | BindingElement) {
|
||||
const name = node.name;
|
||||
function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) {
|
||||
const name = !isOmittedExpression(node) ? node.name : undefined;
|
||||
if (isBindingPattern(name)) {
|
||||
for (const child of name.elements) {
|
||||
bindInitializedVariableFlow(child);
|
||||
|
||||
+323
-201
File diff suppressed because it is too large
Load Diff
@@ -294,6 +294,7 @@ namespace ts {
|
||||
"classic": ModuleResolutionKind.Classic,
|
||||
}),
|
||||
description: Diagnostics.Specify_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6,
|
||||
paramType: Diagnostics.STRATEGY,
|
||||
},
|
||||
{
|
||||
name: "allowUnusedLabels",
|
||||
@@ -805,12 +806,45 @@ namespace ts {
|
||||
* @param basePath A root directory to resolve relative path entries in the config
|
||||
* file to. e.g. outDir
|
||||
*/
|
||||
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string): ParsedCommandLine {
|
||||
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, configFileName?: string, resolutionStack: Path[] = []): ParsedCommandLine {
|
||||
const errors: Diagnostic[] = [];
|
||||
const compilerOptions: CompilerOptions = convertCompilerOptionsFromJsonWorker(json["compilerOptions"], basePath, errors, configFileName);
|
||||
const options = extend(existingOptions, compilerOptions);
|
||||
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
|
||||
const resolvedPath = toPath(configFileName || "", basePath, getCanonicalFileName);
|
||||
if (resolutionStack.indexOf(resolvedPath) >= 0) {
|
||||
return {
|
||||
options: {},
|
||||
fileNames: [],
|
||||
typingOptions: {},
|
||||
raw: json,
|
||||
errors: [createCompilerDiagnostic(Diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, [...resolutionStack, resolvedPath].join(" -> "))],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
}
|
||||
|
||||
let options: CompilerOptions = convertCompilerOptionsFromJsonWorker(json["compilerOptions"], basePath, errors, configFileName);
|
||||
const typingOptions: TypingOptions = convertTypingOptionsFromJsonWorker(json["typingOptions"], basePath, errors, configFileName);
|
||||
|
||||
if (json["extends"]) {
|
||||
let [include, exclude, files, baseOptions]: [string[], string[], string[], CompilerOptions] = [undefined, undefined, undefined, {}];
|
||||
if (typeof json["extends"] === "string") {
|
||||
[include, exclude, files, baseOptions] = (tryExtendsName(json["extends"]) || [include, exclude, files, baseOptions]);
|
||||
}
|
||||
else {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", "string"));
|
||||
}
|
||||
if (include && !json["include"]) {
|
||||
json["include"] = include;
|
||||
}
|
||||
if (exclude && !json["exclude"]) {
|
||||
json["exclude"] = exclude;
|
||||
}
|
||||
if (files && !json["files"]) {
|
||||
json["files"] = files;
|
||||
}
|
||||
options = assign({}, baseOptions, options);
|
||||
}
|
||||
|
||||
options = extend(existingOptions, options);
|
||||
options.configFilePath = configFileName;
|
||||
|
||||
const { fileNames, wildcardDirectories } = getFileNames(errors);
|
||||
@@ -824,6 +858,39 @@ namespace ts {
|
||||
wildcardDirectories
|
||||
};
|
||||
|
||||
function tryExtendsName(extendedConfig: string): [string[], string[], string[], CompilerOptions] {
|
||||
// If the path isn't a rooted or relative path, don't try to resolve it (we reserve the right to special case module-id like paths in the future)
|
||||
if (!(isRootedDiskPath(extendedConfig) || startsWith(normalizeSlashes(extendedConfig), "./") || startsWith(normalizeSlashes(extendedConfig), "../"))) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.The_path_in_an_extends_options_must_be_relative_or_rooted));
|
||||
return;
|
||||
}
|
||||
let extendedConfigPath = toPath(extendedConfig, basePath, getCanonicalFileName);
|
||||
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, ".json")) {
|
||||
extendedConfigPath = `${extendedConfigPath}.json` as Path;
|
||||
if (!host.fileExists(extendedConfigPath)) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
|
||||
return;
|
||||
}
|
||||
}
|
||||
const extendedResult = readConfigFile(extendedConfigPath, path => host.readFile(path));
|
||||
if (extendedResult.error) {
|
||||
errors.push(extendedResult.error);
|
||||
return;
|
||||
}
|
||||
const extendedDirname = getDirectoryPath(extendedConfigPath);
|
||||
const relativeDifference = convertToRelativePath(extendedDirname, basePath, getCanonicalFileName);
|
||||
const updatePath: (path: string) => string = path => isRootedDiskPath(path) ? path : combinePaths(relativeDifference, path);
|
||||
// Merge configs (copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios)
|
||||
const result = parseJsonConfigFileContent(extendedResult.config, host, extendedDirname, /*existingOptions*/undefined, getBaseFileName(extendedConfigPath), resolutionStack.concat([resolvedPath]));
|
||||
errors.push(...result.errors);
|
||||
const [include, exclude, files] = map(["include", "exclude", "files"], key => {
|
||||
if (!json[key] && extendedResult.config[key]) {
|
||||
return map(extendedResult.config[key], updatePath);
|
||||
}
|
||||
});
|
||||
return [include, exclude, files, result.options];
|
||||
}
|
||||
|
||||
function getFileNames(errors: Diagnostic[]): ExpandResult {
|
||||
let fileNames: string[];
|
||||
if (hasProperty(json, "files")) {
|
||||
@@ -858,14 +925,13 @@ namespace ts {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
|
||||
}
|
||||
else {
|
||||
// By default, exclude common package folders
|
||||
// By default, exclude common package folders and the outDir
|
||||
excludeSpecs = ["node_modules", "bower_components", "jspm_packages"];
|
||||
}
|
||||
|
||||
// Always exclude the output directory unless explicitly included
|
||||
const outDir = json["compilerOptions"] && json["compilerOptions"]["outDir"];
|
||||
if (outDir) {
|
||||
excludeSpecs.push(outDir);
|
||||
const outDir = json["compilerOptions"] && json["compilerOptions"]["outDir"];
|
||||
if (outDir) {
|
||||
excludeSpecs.push(outDir);
|
||||
}
|
||||
}
|
||||
|
||||
if (fileNames === undefined && includeSpecs === undefined) {
|
||||
|
||||
+41
-2
@@ -377,6 +377,20 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function mapObject<T, U>(object: MapLike<T>, f: (key: string, x: T) => [string, U]): MapLike<U> {
|
||||
let result: MapLike<U>;
|
||||
if (object) {
|
||||
result = {};
|
||||
for (const v of getOwnKeys(object)) {
|
||||
const [key, value]: [string, U] = f(v, object[v]) || [undefined, undefined];
|
||||
if (key !== undefined) {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function concatenate<T>(array1: T[], array2: T[]): T[] {
|
||||
if (!array2 || !array2.length) return array1;
|
||||
if (!array1 || !array1.length) return array2;
|
||||
@@ -639,6 +653,18 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function assign<T1 extends MapLike<{}>, T2, T3>(t: T1, arg1: T2, arg2: T3): T1 & T2 & T3;
|
||||
export function assign<T1 extends MapLike<{}>, T2>(t: T1, arg1: T2): T1 & T2;
|
||||
export function assign<T1 extends MapLike<{}>>(t: T1, ...args: any[]): any;
|
||||
export function assign<T1 extends MapLike<{}>>(t: T1, ...args: any[]) {
|
||||
for (const arg of args) {
|
||||
for (const p of getOwnKeys(arg)) {
|
||||
t[p] = arg[p];
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the properties of a map.
|
||||
*
|
||||
@@ -1018,7 +1044,8 @@ namespace ts {
|
||||
return 0;
|
||||
}
|
||||
|
||||
export let directorySeparator = "/";
|
||||
export const directorySeparator = "/";
|
||||
const directorySeparatorCharCode = CharacterCodes.slash;
|
||||
function getNormalizedParts(normalizedSlashedPath: string, rootLength: number) {
|
||||
const parts = normalizedSlashedPath.substr(rootLength).split(directorySeparator);
|
||||
const normalized: string[] = [];
|
||||
@@ -1043,8 +1070,20 @@ namespace ts {
|
||||
export function normalizePath(path: string): string {
|
||||
path = normalizeSlashes(path);
|
||||
const rootLength = getRootLength(path);
|
||||
const root = path.substr(0, rootLength);
|
||||
const normalized = getNormalizedParts(path, rootLength);
|
||||
return path.substr(0, rootLength) + normalized.join(directorySeparator);
|
||||
if (normalized.length) {
|
||||
const joinedParts = root + normalized.join(directorySeparator);
|
||||
return pathEndsWithDirectorySeparator(path) ? joinedParts + directorySeparator : joinedParts;
|
||||
}
|
||||
else {
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
/** A path ending with '/' refers to a directory only, never a file. */
|
||||
export function pathEndsWithDirectorySeparator(path: string): boolean {
|
||||
return path.charCodeAt(path.length - 1) === directorySeparatorCharCode;
|
||||
}
|
||||
|
||||
export function getDirectoryPath(path: Path): Path;
|
||||
|
||||
@@ -1043,7 +1043,7 @@
|
||||
"category": "Error",
|
||||
"code": 2348
|
||||
},
|
||||
"Cannot invoke an expression whose type lacks a call signature.": {
|
||||
"Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.": {
|
||||
"category": "Error",
|
||||
"code": 2349
|
||||
},
|
||||
@@ -1063,10 +1063,6 @@
|
||||
"category": "Error",
|
||||
"code": 2353
|
||||
},
|
||||
"No best common type exists among return expressions.": {
|
||||
"category": "Error",
|
||||
"code": 2354
|
||||
},
|
||||
"A function whose declared type is neither 'void' nor 'any' must return a value.": {
|
||||
"category": "Error",
|
||||
"code": 2355
|
||||
@@ -1631,10 +1627,6 @@
|
||||
"category": "Error",
|
||||
"code": 2503
|
||||
},
|
||||
"No best common type exists among yield expressions.": {
|
||||
"category": "Error",
|
||||
"code": 2504
|
||||
},
|
||||
"A generator cannot have a 'void' type annotation.": {
|
||||
"category": "Error",
|
||||
"code": 2505
|
||||
@@ -1955,6 +1947,19 @@
|
||||
"category": "Error",
|
||||
"code": 2692
|
||||
},
|
||||
"'{0}' only refers to a type, but is being used as a value here.": {
|
||||
"category": "Error",
|
||||
"code": 2693
|
||||
},
|
||||
"Namespace '{0}' has no exported member '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2694
|
||||
},
|
||||
"Left side of comma operator is unused and has no side effects.": {
|
||||
"category": "Error",
|
||||
"code": 2695
|
||||
},
|
||||
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 4000
|
||||
@@ -2472,6 +2477,10 @@
|
||||
"category": "Message",
|
||||
"code": 6038
|
||||
},
|
||||
"STRATEGY": {
|
||||
"category": "Message",
|
||||
"code": 6039
|
||||
},
|
||||
"Compilation complete. Watching for file changes.": {
|
||||
"category": "Message",
|
||||
"code": 6042
|
||||
@@ -2871,7 +2880,7 @@
|
||||
"Element implicitly has an 'any' type because index expression is not of type 'number'.": {
|
||||
"category": "Error",
|
||||
"code": 7015
|
||||
},
|
||||
},
|
||||
"Index signature of object type implicitly has an 'any' type.": {
|
||||
"category": "Error",
|
||||
"code": 7017
|
||||
@@ -3047,5 +3056,14 @@
|
||||
"Unknown typing option '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 17010
|
||||
},
|
||||
|
||||
"Circularity detected while resolving configuration: {0}": {
|
||||
"category": "Error",
|
||||
"code": 18000
|
||||
},
|
||||
"The path in an 'extends' options must be relative or rooted.": {
|
||||
"category": "Error",
|
||||
"code": 18001
|
||||
}
|
||||
}
|
||||
|
||||
+86
-35
@@ -71,43 +71,92 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
});
|
||||
};`;
|
||||
|
||||
// The __generator helper is used by down-level transformations to emulate the runtime
|
||||
// semantics of an ES2015 generator function. When called, this helper returns an
|
||||
// object that implements the Iterator protocol, in that it has `next`, `return`, and
|
||||
// `throw` methods that step through the generator when invoked.
|
||||
//
|
||||
// parameters:
|
||||
// thisArg The value to use as the `this` binding for the transformed generator body.
|
||||
// body A function that acts as the transformed generator body.
|
||||
//
|
||||
// variables:
|
||||
// _ Persistent state for the generator that is shared between the helper and the
|
||||
// generator body. The state object has the following members:
|
||||
// sent() - A method that returns or throws the current completion value.
|
||||
// label - The next point at which to resume evaluation of the generator body.
|
||||
// trys - A stack of protected regions (try/catch/finally blocks).
|
||||
// ops - A stack of pending instructions when inside of a finally block.
|
||||
// f A value indicating whether the generator is executing.
|
||||
// y An iterator to delegate for a yield*.
|
||||
// t A temporary variable that holds one of the following values (note that these
|
||||
// cases do not overlap):
|
||||
// - The completion value when resuming from a `yield` or `yield*`.
|
||||
// - The error value for a catch block.
|
||||
// - The current protected region (array of try/catch/finally/end labels).
|
||||
// - The verb (`next`, `throw`, or `return` method) to delegate to the expression
|
||||
// of a `yield*`.
|
||||
// - The result of evaluating the verb delegated to the expression of a `yield*`.
|
||||
//
|
||||
// functions:
|
||||
// verb(n) Creates a bound callback to the `step` function for opcode `n`.
|
||||
// step(op) Evaluates opcodes in a generator body until execution is suspended or
|
||||
// completed.
|
||||
//
|
||||
// The __generator helper understands a limited set of instructions:
|
||||
// 0: next(value?) - Start or resume the generator with the specified value.
|
||||
// 1: throw(error) - Resume the generator with an exception. If the generator is
|
||||
// suspended inside of one or more protected regions, evaluates
|
||||
// any intervening finally blocks between the current label and
|
||||
// the nearest catch block or function boundary. If uncaught, the
|
||||
// exception is thrown to the caller.
|
||||
// 2: return(value?) - Resume the generator as if with a return. If the generator is
|
||||
// suspended inside of one or more protected regions, evaluates any
|
||||
// intervening finally blocks.
|
||||
// 3: break(label) - Jump to the specified label. If the label is outside of the
|
||||
// current protected region, evaluates any intervening finally
|
||||
// blocks.
|
||||
// 4: yield(value?) - Yield execution to the caller with an optional value. When
|
||||
// resumed, the generator will continue at the next label.
|
||||
// 5: yield*(value) - Delegates evaluation to the supplied iterator. When
|
||||
// delegation completes, the generator will continue at the next
|
||||
// label.
|
||||
// 6: catch(error) - Handles an exception thrown from within the generator body. If
|
||||
// the current label is inside of one or more protected regions,
|
||||
// evaluates any intervening finally blocks between the current
|
||||
// label and the nearest catch block or function boundary. If
|
||||
// uncaught, the exception is thrown to the caller.
|
||||
// 7: endfinally - Ends a finally block, resuming the last instruction prior to
|
||||
// entering a finally block.
|
||||
//
|
||||
// For examples of how these are used, see the comments in ./transformers/generators.ts
|
||||
const generatorHelper = `
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (sent[0] === 1) throw sent[1]; return sent[1]; }, trys: [], stack: [] }, sent, f;
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t;
|
||||
return { next: verb(0), "throw": verb(1), "return": verb(2) };
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (1) {
|
||||
if (_.done) switch (op[0]) {
|
||||
case 0: return { value: void 0, done: true };
|
||||
case 1: case 6: throw op[1];
|
||||
case 2: return { value: op[1], done: true };
|
||||
while (_) try {
|
||||
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [0, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
try {
|
||||
switch (f = 1, op[0]) {
|
||||
case 0: case 1: sent = op; break;
|
||||
case 4: return _.label++, { value: op[1], done: false };
|
||||
case 7: op = _.stack.pop(), _.trys.pop(); continue;
|
||||
default:
|
||||
var r = _.trys.length > 0 && _.trys[_.trys.length - 1];
|
||||
if (!r && (op[0] === 6 || op[0] === 2)) { _.done = 1; continue; }
|
||||
if (op[0] === 3 && (!r || (op[1] > r[0] && op[1] < r[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < r[1]) { _.label = r[1], sent = op; break; }
|
||||
if (r && _.label < r[2]) { _.label = r[2], _.stack.push(op); break; }
|
||||
if (r[2]) { _.stack.pop(); }
|
||||
_.trys.pop();
|
||||
continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
}
|
||||
catch (e) { op = [6, e]; }
|
||||
finally { f = 0, sent = void 0; }
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
return {
|
||||
next: function (v) { return step([0, v]); },
|
||||
"throw": function (v) { return step([1, v]); },
|
||||
"return": function (v) { return step([2, v]); }
|
||||
};
|
||||
};`;
|
||||
|
||||
// emit output for the __export helper function
|
||||
@@ -252,7 +301,9 @@ const _super = (function (geti, seti) {
|
||||
|
||||
// Emit helpers from all the files
|
||||
if (isBundledEmit && moduleKind) {
|
||||
forEach(sourceFiles, emitEmitHelpers);
|
||||
for (const sourceFile of sourceFiles) {
|
||||
emitEmitHelpers(sourceFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Print each transformed source file.
|
||||
@@ -554,9 +605,9 @@ const _super = (function (geti, seti) {
|
||||
|
||||
// Binding patterns
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
return emitObjectBindingPattern(<BindingPattern>node);
|
||||
return emitObjectBindingPattern(<ObjectBindingPattern>node);
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
return emitArrayBindingPattern(<BindingPattern>node);
|
||||
return emitArrayBindingPattern(<ArrayBindingPattern>node);
|
||||
case SyntaxKind.BindingElement:
|
||||
return emitBindingElement(<BindingElement>node);
|
||||
|
||||
@@ -2030,7 +2081,7 @@ const _super = (function (geti, seti) {
|
||||
emitTrailingCommentsOfPosition(commentRange.pos);
|
||||
}
|
||||
|
||||
emitExpression(node.initializer);
|
||||
emitExpression(initializer);
|
||||
}
|
||||
|
||||
function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) {
|
||||
|
||||
@@ -368,13 +368,13 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createArrayBindingPattern(elements: BindingElement[], location?: TextRange) {
|
||||
export function createArrayBindingPattern(elements: ArrayBindingElement[], location?: TextRange) {
|
||||
const node = <ArrayBindingPattern>createNode(SyntaxKind.ArrayBindingPattern, location);
|
||||
node.elements = createNodeArray(elements);
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateArrayBindingPattern(node: ArrayBindingPattern, elements: BindingElement[]) {
|
||||
export function updateArrayBindingPattern(node: ArrayBindingPattern, elements: ArrayBindingElement[]) {
|
||||
if (node.elements !== elements) {
|
||||
return updateNode(createArrayBindingPattern(elements, node), node);
|
||||
}
|
||||
|
||||
+12
-8
@@ -905,6 +905,10 @@ namespace ts {
|
||||
return currentToken = scanner.scanJsxToken();
|
||||
}
|
||||
|
||||
function scanJsxAttributeValue(): SyntaxKind {
|
||||
return currentToken = scanner.scanJsxAttributeValue();
|
||||
}
|
||||
|
||||
function speculationHelper<T>(callback: () => T, isLookAhead: boolean): T {
|
||||
// Keep track of the state we'll need to rollback to if lookahead fails (or if the
|
||||
// caller asked us to always reset our state).
|
||||
@@ -3831,8 +3835,8 @@ namespace ts {
|
||||
scanJsxIdentifier();
|
||||
const node = <JsxAttribute>createNode(SyntaxKind.JsxAttribute);
|
||||
node.name = parseIdentifierName();
|
||||
if (parseOptional(SyntaxKind.EqualsToken)) {
|
||||
switch (token()) {
|
||||
if (token() === SyntaxKind.EqualsToken) {
|
||||
switch (scanJsxAttributeValue()) {
|
||||
case SyntaxKind.StringLiteral:
|
||||
node.initializer = parseLiteralNode();
|
||||
break;
|
||||
@@ -4811,9 +4815,9 @@ namespace ts {
|
||||
|
||||
// DECLARATIONS
|
||||
|
||||
function parseArrayBindingElement(): BindingElement {
|
||||
function parseArrayBindingElement(): ArrayBindingElement {
|
||||
if (token() === SyntaxKind.CommaToken) {
|
||||
return <BindingElement>createNode(SyntaxKind.OmittedExpression);
|
||||
return <OmittedExpression>createNode(SyntaxKind.OmittedExpression);
|
||||
}
|
||||
const node = <BindingElement>createNode(SyntaxKind.BindingElement);
|
||||
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
|
||||
@@ -4838,16 +4842,16 @@ namespace ts {
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseObjectBindingPattern(): BindingPattern {
|
||||
const node = <BindingPattern>createNode(SyntaxKind.ObjectBindingPattern);
|
||||
function parseObjectBindingPattern(): ObjectBindingPattern {
|
||||
const node = <ObjectBindingPattern>createNode(SyntaxKind.ObjectBindingPattern);
|
||||
parseExpected(SyntaxKind.OpenBraceToken);
|
||||
node.elements = parseDelimitedList(ParsingContext.ObjectBindingElements, parseObjectBindingElement);
|
||||
parseExpected(SyntaxKind.CloseBraceToken);
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseArrayBindingPattern(): BindingPattern {
|
||||
const node = <BindingPattern>createNode(SyntaxKind.ArrayBindingPattern);
|
||||
function parseArrayBindingPattern(): ArrayBindingPattern {
|
||||
const node = <ArrayBindingPattern>createNode(SyntaxKind.ArrayBindingPattern);
|
||||
parseExpected(SyntaxKind.OpenBracketToken);
|
||||
node.elements = parseDelimitedList(ParsingContext.ArrayBindingElements, parseArrayBindingElement);
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
|
||||
@@ -670,7 +670,7 @@ namespace ts {
|
||||
trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0, candidate);
|
||||
}
|
||||
|
||||
const resolvedFileName = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, onlyRecordFailures, state);
|
||||
const resolvedFileName = !pathEndsWithDirectorySeparator(candidate) && loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, onlyRecordFailures, state);
|
||||
|
||||
return resolvedFileName || loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, onlyRecordFailures, state);
|
||||
}
|
||||
|
||||
+18
-2
@@ -27,6 +27,7 @@ namespace ts {
|
||||
reScanSlashToken(): SyntaxKind;
|
||||
reScanTemplateToken(): SyntaxKind;
|
||||
scanJsxIdentifier(): SyntaxKind;
|
||||
scanJsxAttributeValue(): SyntaxKind;
|
||||
reScanJsxToken(): SyntaxKind;
|
||||
scanJsxToken(): SyntaxKind;
|
||||
scanJSDocToken(): SyntaxKind;
|
||||
@@ -817,6 +818,7 @@ namespace ts {
|
||||
reScanSlashToken,
|
||||
reScanTemplateToken,
|
||||
scanJsxIdentifier,
|
||||
scanJsxAttributeValue,
|
||||
reScanJsxToken,
|
||||
scanJsxToken,
|
||||
scanJSDocToken,
|
||||
@@ -911,7 +913,7 @@ namespace ts {
|
||||
return value;
|
||||
}
|
||||
|
||||
function scanString(): string {
|
||||
function scanString(allowEscapes = true): string {
|
||||
const quote = text.charCodeAt(pos);
|
||||
pos++;
|
||||
let result = "";
|
||||
@@ -929,7 +931,7 @@ namespace ts {
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
if (ch === CharacterCodes.backslash) {
|
||||
if (ch === CharacterCodes.backslash && allowEscapes) {
|
||||
result += text.substring(start, pos);
|
||||
result += scanEscapeSequence();
|
||||
start = pos;
|
||||
@@ -1737,6 +1739,20 @@ namespace ts {
|
||||
return token;
|
||||
}
|
||||
|
||||
function scanJsxAttributeValue(): SyntaxKind {
|
||||
startPos = pos;
|
||||
|
||||
switch (text.charCodeAt(pos)) {
|
||||
case CharacterCodes.doubleQuote:
|
||||
case CharacterCodes.singleQuote:
|
||||
tokenValue = scanString(/*allowEscapes*/ false);
|
||||
return token = SyntaxKind.StringLiteral;
|
||||
default:
|
||||
// If this scans anything other than `{`, it's a parse error.
|
||||
return scan();
|
||||
}
|
||||
}
|
||||
|
||||
function scanJSDocToken(): SyntaxKind {
|
||||
if (pos >= end) {
|
||||
return token = SyntaxKind.EndOfFileToken;
|
||||
|
||||
@@ -326,12 +326,15 @@ namespace ts {
|
||||
}
|
||||
for (let i = 0; i < numElements; i++) {
|
||||
const element = elements[i];
|
||||
if (name.kind === SyntaxKind.ObjectBindingPattern) {
|
||||
if (isOmittedExpression(element)) {
|
||||
continue;
|
||||
}
|
||||
else if (name.kind === SyntaxKind.ObjectBindingPattern) {
|
||||
// Rewrite element to a declaration with an initializer that fetches property
|
||||
const propName = element.propertyName || <Identifier>element.name;
|
||||
emitBindingElement(element, createDestructuringPropertyAccess(value, propName));
|
||||
}
|
||||
else if (element.kind !== SyntaxKind.OmittedExpression) {
|
||||
else {
|
||||
if (!element.dotDotDotToken) {
|
||||
// Rewrite element to a declaration that accesses array element at index i
|
||||
emitBindingElement(element, createElementAccess(value, i));
|
||||
|
||||
@@ -541,7 +541,7 @@ namespace ts {
|
||||
*
|
||||
* @param node A ClassDeclaration node.
|
||||
*/
|
||||
function visitClassDeclaration(node: ClassDeclaration): Statement {
|
||||
function visitClassDeclaration(node: ClassDeclaration): VisitResult<Statement> {
|
||||
// [source]
|
||||
// class C { }
|
||||
//
|
||||
@@ -552,8 +552,17 @@ namespace ts {
|
||||
// return C;
|
||||
// }());
|
||||
|
||||
const modifierFlags = getModifierFlags(node);
|
||||
const isExported = modifierFlags & ModifierFlags.Export;
|
||||
const isDefault = modifierFlags & ModifierFlags.Default;
|
||||
|
||||
// Add an `export` modifier to the statement if needed (for `--target es5 --module es6`)
|
||||
const modifiers = isExported && !isDefault
|
||||
? filter(node.modifiers, isExportModifier)
|
||||
: undefined;
|
||||
|
||||
const statement = createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
modifiers,
|
||||
createVariableDeclarationList([
|
||||
createVariableDeclaration(
|
||||
getDeclarationName(node, /*allowComments*/ true),
|
||||
@@ -566,9 +575,26 @@ namespace ts {
|
||||
|
||||
setOriginalNode(statement, node);
|
||||
startOnNewLine(statement);
|
||||
|
||||
// Add an `export default` statement for default exports (for `--target es5 --module es6`)
|
||||
if (isExported && isDefault) {
|
||||
const statements: Statement[] = [statement];
|
||||
statements.push(createExportAssignment(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*isExportEquals*/ false,
|
||||
getDeclarationName(node, /*allowComments*/ false)
|
||||
));
|
||||
return statements;
|
||||
}
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
function isExportModifier(node: Modifier) {
|
||||
return node.kind === SyntaxKind.ExportKeyword;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a ClassExpression and transforms it into an expression.
|
||||
*
|
||||
@@ -1945,7 +1971,9 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
for (const element of (<BindingPattern>node).elements) {
|
||||
visit(element.name);
|
||||
if (!isOmittedExpression(element)) {
|
||||
visit(element.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2027,9 +2055,24 @@ namespace ts {
|
||||
if (!isBlock(loopBody)) {
|
||||
loopBody = createBlock([loopBody], /*location*/ undefined, /*multiline*/ true);
|
||||
}
|
||||
|
||||
const isAsyncBlockContainingAwait =
|
||||
containingNonArrowFunction
|
||||
&& (containingNonArrowFunction.emitFlags & NodeEmitFlags.AsyncFunctionBody) !== 0
|
||||
&& (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0;
|
||||
|
||||
let loopBodyFlags: NodeEmitFlags = 0;
|
||||
if (currentState.containsLexicalThis) {
|
||||
loopBodyFlags |= NodeEmitFlags.CapturesThis;
|
||||
}
|
||||
|
||||
if (isAsyncBlockContainingAwait) {
|
||||
loopBodyFlags |= NodeEmitFlags.AsyncFunctionBody;
|
||||
}
|
||||
|
||||
const convertedLoopVariable =
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList(
|
||||
[
|
||||
createVariableDeclaration(
|
||||
@@ -2037,16 +2080,14 @@ namespace ts {
|
||||
/*type*/ undefined,
|
||||
setNodeEmitFlags(
|
||||
createFunctionExpression(
|
||||
/*asteriskToken*/ undefined,
|
||||
isAsyncBlockContainingAwait ? createToken(SyntaxKind.AsteriskToken) : undefined,
|
||||
/*name*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
loopParameters,
|
||||
/*type*/ undefined,
|
||||
<Block>loopBody
|
||||
),
|
||||
currentState.containsLexicalThis
|
||||
? NodeEmitFlags.CapturesThis
|
||||
: 0
|
||||
loopBodyFlags
|
||||
)
|
||||
)
|
||||
]
|
||||
@@ -2132,7 +2173,7 @@ namespace ts {
|
||||
));
|
||||
}
|
||||
|
||||
const convertedLoopBodyStatements = generateCallToConvertedLoop(functionName, loopParameters, currentState);
|
||||
const convertedLoopBodyStatements = generateCallToConvertedLoop(functionName, loopParameters, currentState, isAsyncBlockContainingAwait);
|
||||
let loop: IterationStatement;
|
||||
if (convert) {
|
||||
loop = convert(node, convertedLoopBodyStatements);
|
||||
@@ -2145,12 +2186,17 @@ namespace ts {
|
||||
loop = visitEachChild(loop, visitor, context);
|
||||
// set loop statement
|
||||
loop.statement = createBlock(
|
||||
generateCallToConvertedLoop(functionName, loopParameters, currentState),
|
||||
convertedLoopBodyStatements,
|
||||
/*location*/ undefined,
|
||||
/*multiline*/ true
|
||||
);
|
||||
|
||||
// reset and re-aggregate the transform flags
|
||||
loop.transformFlags = 0;
|
||||
aggregateTransformFlags(loop);
|
||||
}
|
||||
|
||||
|
||||
statements.push(
|
||||
currentParent.kind === SyntaxKind.LabeledStatement
|
||||
? createLabel((<LabeledStatement>currentParent).label, loop)
|
||||
@@ -2171,7 +2217,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function generateCallToConvertedLoop(loopFunctionExpressionName: Identifier, parameters: ParameterDeclaration[], state: ConvertedLoopState): Statement[] {
|
||||
function generateCallToConvertedLoop(loopFunctionExpressionName: Identifier, parameters: ParameterDeclaration[], state: ConvertedLoopState, isAsyncBlockContainingAwait: boolean): Statement[] {
|
||||
const outerConvertedLoopState = convertedLoopState;
|
||||
|
||||
const statements: Statement[] = [];
|
||||
@@ -2184,8 +2230,9 @@ namespace ts {
|
||||
!state.labeledNonLocalContinues;
|
||||
|
||||
const call = createCall(loopFunctionExpressionName, /*typeArguments*/ undefined, map(parameters, p => <Identifier>p.name));
|
||||
const callResult = isAsyncBlockContainingAwait ? createYield(createToken(SyntaxKind.AsteriskToken), call) : call;
|
||||
if (isSimpleLoop) {
|
||||
statements.push(createStatement(call));
|
||||
statements.push(createStatement(callResult));
|
||||
copyOutParameters(state.loopOutParameters, CopyDirection.ToOriginal, statements);
|
||||
}
|
||||
else {
|
||||
@@ -2193,7 +2240,7 @@ namespace ts {
|
||||
const stateVariable = createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
createVariableDeclarationList(
|
||||
[createVariableDeclaration(loopResultName, /*type*/ undefined, call)]
|
||||
[createVariableDeclaration(loopResultName, /*type*/ undefined, callResult)]
|
||||
)
|
||||
);
|
||||
statements.push(stateVariable);
|
||||
@@ -2289,7 +2336,9 @@ namespace ts {
|
||||
const name = decl.name;
|
||||
if (isBindingPattern(name)) {
|
||||
for (const element of name.elements) {
|
||||
processLoopVariableDeclaration(element, loopParameters, loopOutParameters);
|
||||
if (!isOmittedExpression(element)) {
|
||||
processLoopVariableDeclaration(element, loopParameters, loopOutParameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -21,10 +21,11 @@
|
||||
// .brfalse LABEL, (x) - Jump to a label IIF the expression `x` is falsey.
|
||||
// If jumping out of a protected region, all .finally
|
||||
// blocks are executed.
|
||||
// .yield RESUME, (x) - Yield the value of the optional expression `x`.
|
||||
// Resume at the label RESUME.
|
||||
// .yieldstar RESUME, (x) - Delegate yield to the value of the optional
|
||||
// expression `x`. Resume at the label RESUME.
|
||||
// .yield (x) - Yield the value of the optional expression `x`.
|
||||
// Resume at the next label.
|
||||
// .yieldstar (x) - Delegate yield to the value of the optional
|
||||
// expression `x`. Resume at the next label.
|
||||
// NOTE: `x` must be an Iterator, not an Iterable.
|
||||
// .loop CONTINUE, BREAK - Marks the beginning of a loop. Any "continue" or
|
||||
// "break" abrupt completions jump to the CONTINUE or
|
||||
// BREAK labels, respectively.
|
||||
@@ -80,13 +81,13 @@
|
||||
// -------------------------------|----------------------------------------------
|
||||
// .brfalse LABEL, (x) | if (!(x)) return [3, /*break*/, LABEL];
|
||||
// -------------------------------|----------------------------------------------
|
||||
// .yield RESUME, (x) | return [4 /*yield*/, x];
|
||||
// .yield (x) | return [4 /*yield*/, x];
|
||||
// .mark RESUME | case RESUME:
|
||||
// a = %sent%; | a = state.sent();
|
||||
// a = %sent%; | a = state.sent();
|
||||
// -------------------------------|----------------------------------------------
|
||||
// .yieldstar RESUME, (X) | return [5 /*yield**/, x];
|
||||
// .yieldstar (x) | return [5 /*yield**/, x];
|
||||
// .mark RESUME | case RESUME:
|
||||
// a = %sent%; | a = state.sent();
|
||||
// a = %sent%; | a = state.sent();
|
||||
// -------------------------------|----------------------------------------------
|
||||
// .with (_a) | with (_a) {
|
||||
// a(); | a();
|
||||
@@ -109,7 +110,7 @@
|
||||
// .br END | return [3 /*break*/, END];
|
||||
// .catch (e) |
|
||||
// .mark CATCH | case CATCH:
|
||||
// | e = state.error;
|
||||
// | e = state.sent();
|
||||
// b(); | b();
|
||||
// .br END | return [3 /*break*/, END];
|
||||
// .finally |
|
||||
@@ -906,7 +907,7 @@ namespace ts {
|
||||
*
|
||||
* @param node The node to visit.
|
||||
*/
|
||||
function visitYieldExpression(node: YieldExpression) {
|
||||
function visitYieldExpression(node: YieldExpression): LeftHandSideExpression {
|
||||
// [source]
|
||||
// x = yield a();
|
||||
//
|
||||
@@ -917,7 +918,14 @@ namespace ts {
|
||||
|
||||
// NOTE: we are explicitly not handling YieldStar at this time.
|
||||
const resumeLabel = defineLabel();
|
||||
emitYield(visitNode(node.expression, visitor, isExpression), /*location*/ node);
|
||||
const expression = visitNode(node.expression, visitor, isExpression);
|
||||
if (node.asteriskToken) {
|
||||
emitYieldStar(expression, /*location*/ node);
|
||||
}
|
||||
else {
|
||||
emitYield(expression, /*location*/ node);
|
||||
}
|
||||
|
||||
markLabel(resumeLabel);
|
||||
return createGeneratorResume();
|
||||
}
|
||||
@@ -2480,6 +2488,16 @@ namespace ts {
|
||||
emitWorker(OpCode.BreakWhenFalse, [label, condition], location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a YieldStar operation for the provided expression.
|
||||
*
|
||||
* @param expression An optional value for the yield operation.
|
||||
* @param location An optional source map location for the assignment.
|
||||
*/
|
||||
function emitYieldStar(expression?: Expression, location?: TextRange): void {
|
||||
emitWorker(OpCode.YieldStar, [expression], location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a Yield operation for the provided expression.
|
||||
*
|
||||
@@ -2629,7 +2647,7 @@ namespace ts {
|
||||
* Flush the final label of the generator function body.
|
||||
*/
|
||||
function flushFinalLabel(operationIndex: number): void {
|
||||
if (!lastOperationWasCompletion) {
|
||||
if (isFinalLabelReachable(operationIndex)) {
|
||||
tryEnterLabel(operationIndex);
|
||||
withBlockStack = undefined;
|
||||
writeReturn(/*expression*/ undefined, /*operationLocation*/ undefined);
|
||||
@@ -2642,6 +2660,34 @@ namespace ts {
|
||||
updateLabelExpressions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the final label of the generator function body
|
||||
* is reachable by user code.
|
||||
*/
|
||||
function isFinalLabelReachable(operationIndex: number) {
|
||||
// if the last operation was *not* a completion (return/throw) then
|
||||
// the final label is reachable.
|
||||
if (!lastOperationWasCompletion) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if there are no labels defined or referenced, then the final label is
|
||||
// not reachable.
|
||||
if (!labelOffsets || !labelExpressions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the label for this offset is referenced, then the final label
|
||||
// is reachable.
|
||||
for (let label = 0; label < labelOffsets.length; label++) {
|
||||
if (labelOffsets[label] === operationIndex && labelExpressions[label]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a case clause for the last label and sets the new label.
|
||||
*
|
||||
|
||||
@@ -140,7 +140,8 @@ namespace ts {
|
||||
return createLiteral(true);
|
||||
}
|
||||
else if (node.kind === SyntaxKind.StringLiteral) {
|
||||
return node;
|
||||
const decoded = tryDecodeEntities((<StringLiteral>node).text);
|
||||
return decoded ? createLiteral(decoded, /*location*/ node) : node;
|
||||
}
|
||||
else if (node.kind === SyntaxKind.JsxExpression) {
|
||||
return visitJsxExpression(<JsxExpression>node);
|
||||
@@ -210,19 +211,31 @@ namespace ts {
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes JSX entities.
|
||||
* Replace entities like " ", "{", and "�" with the characters they encode.
|
||||
* See https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
|
||||
*/
|
||||
function decodeEntities(text: string) {
|
||||
return text.replace(/&(\w+);/g, function(s: any, m: string) {
|
||||
if (entities[m] !== undefined) {
|
||||
return String.fromCharCode(entities[m]);
|
||||
function decodeEntities(text: string): string {
|
||||
return text.replace(/&((#((\d+)|x([\da-fA-F]+)))|(\w+));/g, (match, _all, _number, _digits, decimal, hex, word) => {
|
||||
if (decimal) {
|
||||
return String.fromCharCode(parseInt(decimal, 10));
|
||||
}
|
||||
else if (hex) {
|
||||
return String.fromCharCode(parseInt(hex, 16));
|
||||
}
|
||||
else {
|
||||
return s;
|
||||
const ch = entities[word];
|
||||
// If this is not a valid entity, then just use `match` (replace it with itself, i.e. don't replace)
|
||||
return ch ? String.fromCharCode(ch) : match;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Like `decodeEntities` but returns `undefined` if there were no entities to decode. */
|
||||
function tryDecodeEntities(text: string): string | undefined {
|
||||
const decoded = decodeEntities(text);
|
||||
return decoded === text ? undefined : decoded;
|
||||
}
|
||||
|
||||
function getTagName(node: JsxElement | JsxOpeningLikeElement): Expression {
|
||||
if (node.kind === SyntaxKind.JsxElement) {
|
||||
return getTagName((<JsxElement>node).openingElement);
|
||||
|
||||
@@ -660,7 +660,9 @@ namespace ts {
|
||||
function addExportMemberAssignmentsForBindingName(resultStatements: Statement[], name: BindingName): void {
|
||||
if (isBindingPattern(name)) {
|
||||
for (const element of name.elements) {
|
||||
addExportMemberAssignmentsForBindingName(resultStatements, element.name);
|
||||
if (!isOmittedExpression(element)) {
|
||||
addExportMemberAssignmentsForBindingName(resultStatements, element.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -703,7 +705,7 @@ namespace ts {
|
||||
createFunctionDeclaration(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*asteriskToken*/ undefined,
|
||||
node.asteriskToken,
|
||||
name,
|
||||
/*typeParameters*/ undefined,
|
||||
node.parameters,
|
||||
|
||||
@@ -1368,7 +1368,11 @@ namespace ts {
|
||||
exportedFunctionDeclarations.push(createDeclarationExport(node));
|
||||
}
|
||||
|
||||
function hoistBindingElement(node: VariableDeclaration | BindingElement, isExported: boolean): void {
|
||||
function hoistBindingElement(node: VariableDeclaration | ArrayBindingElement, isExported: boolean): void {
|
||||
if (isOmittedExpression(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const name = node.name;
|
||||
if (isIdentifier(name)) {
|
||||
hoistVariableDeclaration(getSynthesizedClone(name));
|
||||
@@ -1381,11 +1385,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function hoistExportedBindingElement(node: VariableDeclaration | BindingElement) {
|
||||
function hoistExportedBindingElement(node: VariableDeclaration | ArrayBindingElement) {
|
||||
hoistBindingElement(node, /*isExported*/ true);
|
||||
}
|
||||
|
||||
function hoistNonExportedBindingElement(node: VariableDeclaration | BindingElement) {
|
||||
function hoistNonExportedBindingElement(node: VariableDeclaration | ArrayBindingElement) {
|
||||
hoistBindingElement(node, /*isExported*/ false);
|
||||
}
|
||||
|
||||
|
||||
@@ -2187,7 +2187,7 @@ namespace ts {
|
||||
*
|
||||
* @param node The function expression node.
|
||||
*/
|
||||
function visitFunctionExpression(node: FunctionExpression) {
|
||||
function visitFunctionExpression(node: FunctionExpression): Expression {
|
||||
if (nodeIsMissing(node.body)) {
|
||||
return createOmittedExpression();
|
||||
}
|
||||
|
||||
+19
-6
@@ -435,6 +435,8 @@ namespace ts {
|
||||
TypeExcludesFlags = YieldContext | AwaitContext,
|
||||
}
|
||||
|
||||
export type ModifiersArray = NodeArray<Modifier>;
|
||||
|
||||
export const enum ModifierFlags {
|
||||
None = 0,
|
||||
Export = 1 << 0, // Declarations
|
||||
@@ -480,7 +482,7 @@ namespace ts {
|
||||
/* @internal */ modifierFlagsCache?: ModifierFlags;
|
||||
/* @internal */ transformFlags?: TransformFlags;
|
||||
decorators?: NodeArray<Decorator>; // Array of decorators (in document order)
|
||||
modifiers?: NodeArray<Modifier>; // Array of modifiers
|
||||
modifiers?: ModifiersArray; // Array of modifiers
|
||||
/* @internal */ id?: number; // Unique id (used to look up NodeLinks)
|
||||
parent?: Node; // Parent node (initialized by binding)
|
||||
/* @internal */ original?: Node; // The original node if this is an updated node.
|
||||
@@ -686,14 +688,20 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface BindingPattern extends Node {
|
||||
elements: NodeArray<BindingElement>;
|
||||
elements: NodeArray<BindingElement | ArrayBindingElement>;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.ObjectBindingPattern)
|
||||
export interface ObjectBindingPattern extends BindingPattern { }
|
||||
export interface ObjectBindingPattern extends BindingPattern {
|
||||
elements: NodeArray<BindingElement>;
|
||||
}
|
||||
|
||||
export type ArrayBindingElement = BindingElement | OmittedExpression;
|
||||
|
||||
// @kind(SyntaxKind.ArrayBindingPattern)
|
||||
export interface ArrayBindingPattern extends BindingPattern { }
|
||||
export interface ArrayBindingPattern extends BindingPattern {
|
||||
elements: NodeArray<ArrayBindingElement>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Several node kinds share function-like features such as a signature,
|
||||
@@ -866,7 +874,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.OmittedExpression)
|
||||
export interface OmittedExpression extends Expression { }
|
||||
export interface OmittedExpression extends Expression {
|
||||
_omittedExpressionBrand: any;
|
||||
}
|
||||
|
||||
// Represents an expression that is elided as part of a transformation to emit comments on a
|
||||
// not-emitted node. The 'expression' property of a NotEmittedExpression should be emitted.
|
||||
@@ -1797,6 +1807,8 @@ namespace ts {
|
||||
* @param path The path to test.
|
||||
*/
|
||||
fileExists(path: string): boolean;
|
||||
|
||||
readFile(path: string): string;
|
||||
}
|
||||
|
||||
export interface WriteFileCallback {
|
||||
@@ -2583,13 +2595,14 @@ namespace ts {
|
||||
export interface TypeInferences {
|
||||
primary: Type[]; // Inferences made directly to a type parameter
|
||||
secondary: Type[]; // Inferences made to a type parameter in a union type
|
||||
topLevel: boolean; // True if all inferences were made from top-level (not nested in object type) locations
|
||||
isFixed: boolean; // Whether the type parameter is fixed, as defined in section 4.12.2 of the TypeScript spec
|
||||
// If a type parameter is fixed, no more inferences can be made for the type parameter
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface InferenceContext {
|
||||
typeParameters: TypeParameter[]; // Type parameters for which inferences are made
|
||||
signature: Signature; // Generic signature for which inferences are made
|
||||
inferUnionTypes: boolean; // Infer union types for disjoint candidates (otherwise undefinedType)
|
||||
inferences: TypeInferences[]; // Inferences made for each type parameter
|
||||
inferredTypes: Type[]; // Inferred type for each type parameter
|
||||
|
||||
+69
-55
@@ -414,7 +414,11 @@ namespace ts {
|
||||
((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(<ModuleDeclaration>node));
|
||||
}
|
||||
|
||||
export function isShorthandAmbientModule(node: Node): boolean {
|
||||
export function isShorthandAmbientModuleSymbol(moduleSymbol: Symbol): boolean {
|
||||
return isShorthandAmbientModule(moduleSymbol.valueDeclaration);
|
||||
}
|
||||
|
||||
function isShorthandAmbientModule(node: Node): boolean {
|
||||
// The only kind of module that can be missing a body is a shorthand ambient module.
|
||||
return node.kind === SyntaxKind.ModuleDeclaration && (!(<ModuleDeclaration>node).body);
|
||||
}
|
||||
@@ -596,60 +600,6 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.EnumDeclaration && isConst(node);
|
||||
}
|
||||
|
||||
function walkUpBindingElementsAndPatterns(node: Node): Node {
|
||||
while (node && (node.kind === SyntaxKind.BindingElement || isBindingPattern(node))) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
export function getCombinedModifierFlags(node: Node): ModifierFlags {
|
||||
node = walkUpBindingElementsAndPatterns(node);
|
||||
let flags = getModifierFlags(node);
|
||||
if (node.kind === SyntaxKind.VariableDeclaration) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (node && node.kind === SyntaxKind.VariableDeclarationList) {
|
||||
flags |= getModifierFlags(node);
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (node && node.kind === SyntaxKind.VariableStatement) {
|
||||
flags |= getModifierFlags(node);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Returns the node flags for this node and all relevant parent nodes. This is done so that
|
||||
// nodes like variable declarations and binding elements can returned a view of their flags
|
||||
// that includes the modifiers from their container. i.e. flags like export/declare aren't
|
||||
// stored on the variable declaration directly, but on the containing variable statement
|
||||
// (if it has one). Similarly, flags for let/const are store on the variable declaration
|
||||
// list. By calling this function, all those flags are combined so that the client can treat
|
||||
// the node as if it actually had those flags.
|
||||
export function getCombinedNodeFlags(node: Node): NodeFlags {
|
||||
node = walkUpBindingElementsAndPatterns(node);
|
||||
|
||||
let flags = node.flags;
|
||||
if (node.kind === SyntaxKind.VariableDeclaration) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (node && node.kind === SyntaxKind.VariableDeclarationList) {
|
||||
flags |= node.flags;
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (node && node.kind === SyntaxKind.VariableStatement) {
|
||||
flags |= node.flags;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
export function isConst(node: Node): boolean {
|
||||
return !!(getCombinedNodeFlags(node) & NodeFlags.Const)
|
||||
|| !!(getCombinedModifierFlags(node) & ModifierFlags.Const);
|
||||
@@ -3755,6 +3705,12 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.BindingElement;
|
||||
}
|
||||
|
||||
export function isArrayBindingElement(node: Node): node is ArrayBindingElement {
|
||||
const kind = node.kind;
|
||||
return kind === SyntaxKind.BindingElement
|
||||
|| kind === SyntaxKind.OmittedExpression;
|
||||
}
|
||||
|
||||
// Expression
|
||||
|
||||
export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression {
|
||||
@@ -3871,6 +3827,10 @@ namespace ts {
|
||||
|| isPartiallyEmittedExpression(node);
|
||||
}
|
||||
|
||||
export function isOmittedExpression(node: Node): node is OmittedExpression {
|
||||
return node.kind === SyntaxKind.OmittedExpression;
|
||||
}
|
||||
|
||||
// Misc
|
||||
|
||||
export function isTemplateSpan(node: Node): node is TemplateSpan {
|
||||
@@ -4370,4 +4330,58 @@ namespace ts {
|
||||
export function isParameterPropertyDeclaration(node: ParameterDeclaration): boolean {
|
||||
return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent);
|
||||
}
|
||||
|
||||
function walkUpBindingElementsAndPatterns(node: Node): Node {
|
||||
while (node && (node.kind === SyntaxKind.BindingElement || isBindingPattern(node))) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
export function getCombinedModifierFlags(node: Node): ModifierFlags {
|
||||
node = walkUpBindingElementsAndPatterns(node);
|
||||
let flags = getModifierFlags(node);
|
||||
if (node.kind === SyntaxKind.VariableDeclaration) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (node && node.kind === SyntaxKind.VariableDeclarationList) {
|
||||
flags |= getModifierFlags(node);
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (node && node.kind === SyntaxKind.VariableStatement) {
|
||||
flags |= getModifierFlags(node);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Returns the node flags for this node and all relevant parent nodes. This is done so that
|
||||
// nodes like variable declarations and binding elements can returned a view of their flags
|
||||
// that includes the modifiers from their container. i.e. flags like export/declare aren't
|
||||
// stored on the variable declaration directly, but on the containing variable statement
|
||||
// (if it has one). Similarly, flags for let/const are store on the variable declaration
|
||||
// list. By calling this function, all those flags are combined so that the client can treat
|
||||
// the node as if it actually had those flags.
|
||||
export function getCombinedNodeFlags(node: Node): NodeFlags {
|
||||
node = walkUpBindingElementsAndPatterns(node);
|
||||
|
||||
let flags = node.flags;
|
||||
if (node.kind === SyntaxKind.VariableDeclaration) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (node && node.kind === SyntaxKind.VariableDeclarationList) {
|
||||
flags |= node.flags;
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (node && node.kind === SyntaxKind.VariableStatement) {
|
||||
flags |= node.flags;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,7 +757,7 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
return updateArrayBindingPattern(<ArrayBindingPattern>node,
|
||||
visitNodes((<ArrayBindingPattern>node).elements, visitor, isBindingElement));
|
||||
visitNodes((<ArrayBindingPattern>node).elements, visitor, isArrayBindingElement));
|
||||
|
||||
case SyntaxKind.BindingElement:
|
||||
return updateBindingElement(<BindingElement>node,
|
||||
|
||||
+64
-28
@@ -367,6 +367,7 @@ namespace FourSlash {
|
||||
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
|
||||
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
|
||||
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
|
||||
InsertSpaceAfterTypeAssertion: false,
|
||||
PlaceOpenBraceOnNewLineForFunctions: false,
|
||||
PlaceOpenBraceOnNewLineForControlBlocks: false,
|
||||
};
|
||||
@@ -958,29 +959,35 @@ namespace FourSlash {
|
||||
assert.equal(actual, expected);
|
||||
}
|
||||
|
||||
public verifyQuickInfoString(negative: boolean, expectedText?: string, expectedDocumentation?: string) {
|
||||
public verifyQuickInfoAt(markerName: string, expectedText: string, expectedDocumentation?: string) {
|
||||
this.goToMarker(markerName);
|
||||
this.verifyQuickInfoString(expectedText, expectedDocumentation);
|
||||
}
|
||||
|
||||
public verifyQuickInfos(namesAndTexts: { [name: string]: string | [string, string] }) {
|
||||
ts.forEachProperty(ts.createMap(namesAndTexts), (text, name) => {
|
||||
if (text instanceof Array) {
|
||||
assert(text.length === 2);
|
||||
const [expectedText, expectedDocumentation] = text;
|
||||
this.verifyQuickInfoAt(name, expectedText, expectedDocumentation);
|
||||
}
|
||||
else {
|
||||
this.verifyQuickInfoAt(name, text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public verifyQuickInfoString(expectedText: string, expectedDocumentation?: string) {
|
||||
if (expectedDocumentation === "") {
|
||||
throw new Error("Use 'undefined' instead");
|
||||
}
|
||||
|
||||
const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
const actualQuickInfoText = actualQuickInfo ? ts.displayPartsToString(actualQuickInfo.displayParts) : "";
|
||||
const actualQuickInfoDocumentation = actualQuickInfo ? ts.displayPartsToString(actualQuickInfo.documentation) : "";
|
||||
|
||||
if (negative) {
|
||||
if (expectedText !== undefined) {
|
||||
assert.notEqual(actualQuickInfoText, expectedText, this.messageAtLastKnownMarker("quick info text"));
|
||||
}
|
||||
// TODO: should be '==='?
|
||||
if (expectedDocumentation != undefined) {
|
||||
assert.notEqual(actualQuickInfoDocumentation, expectedDocumentation, this.messageAtLastKnownMarker("quick info doc comment"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (expectedText !== undefined) {
|
||||
assert.equal(actualQuickInfoText, expectedText, this.messageAtLastKnownMarker("quick info text"));
|
||||
}
|
||||
// TODO: should be '==='?
|
||||
if (expectedDocumentation != undefined) {
|
||||
assert.equal(actualQuickInfoDocumentation, expectedDocumentation, this.assertionMessageAtLastKnownMarker("quick info doc"));
|
||||
}
|
||||
}
|
||||
assert.equal(actualQuickInfoText, expectedText, this.messageAtLastKnownMarker("quick info text"));
|
||||
assert.equal(actualQuickInfoDocumentation, expectedDocumentation || "", this.assertionMessageAtLastKnownMarker("quick info doc"));
|
||||
}
|
||||
|
||||
public verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: { start: number; length: number; },
|
||||
@@ -1298,6 +1305,22 @@ namespace FourSlash {
|
||||
});
|
||||
}
|
||||
|
||||
public baselineQuickInfo() {
|
||||
let baselineFile = this.testData.globalOptions[metadataOptionNames.baselineFile];
|
||||
if (!baselineFile) {
|
||||
baselineFile = ts.getBaseFileName(this.activeFile.fileName).replace(".ts", ".baseline");
|
||||
}
|
||||
|
||||
Harness.Baseline.runBaseline(
|
||||
baselineFile,
|
||||
() => stringify(
|
||||
this.testData.markers.map(marker => ({
|
||||
marker,
|
||||
quickInfo: this.languageService.getQuickInfoAtPosition(marker.fileName, marker.position)
|
||||
}))
|
||||
));
|
||||
}
|
||||
|
||||
public printBreakpointLocation(pos: number) {
|
||||
Harness.IO.log("\n**Pos: " + pos + " " + this.spanInfoToString(pos, this.getBreakpointStatementLocation(pos), " "));
|
||||
}
|
||||
@@ -2106,11 +2129,12 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
/*
|
||||
Check number of navigationItems which match both searchValue and matchKind.
|
||||
Check number of navigationItems which match both searchValue and matchKind,
|
||||
if a filename is passed in, limit the results to that file.
|
||||
Report an error if expected value and actual value do not match.
|
||||
*/
|
||||
public verifyNavigationItemsCount(expected: number, searchValue: string, matchKind?: string) {
|
||||
const items = this.languageService.getNavigateToItems(searchValue);
|
||||
public verifyNavigationItemsCount(expected: number, searchValue: string, matchKind?: string, fileName?: string) {
|
||||
const items = this.languageService.getNavigateToItems(searchValue, /*maxResultCount*/ undefined, fileName);
|
||||
let actual = 0;
|
||||
let item: ts.NavigateToItem;
|
||||
|
||||
@@ -3048,10 +3072,6 @@ namespace FourSlashInterface {
|
||||
this.state.verifyErrorExistsAfterMarker(markerName, !this.negative, /*after*/ false);
|
||||
}
|
||||
|
||||
public quickInfoIs(expectedText?: string, expectedDocumentation?: string) {
|
||||
this.state.verifyQuickInfoString(this.negative, expectedText, expectedDocumentation);
|
||||
}
|
||||
|
||||
public quickInfoExists() {
|
||||
this.state.verifyQuickInfoExists(this.negative);
|
||||
}
|
||||
@@ -3074,6 +3094,18 @@ namespace FourSlashInterface {
|
||||
super(state);
|
||||
}
|
||||
|
||||
public quickInfoIs(expectedText: string, expectedDocumentation?: string) {
|
||||
this.state.verifyQuickInfoString(expectedText, expectedDocumentation);
|
||||
}
|
||||
|
||||
public quickInfoAt(markerName: string, expectedText?: string, expectedDocumentation?: string) {
|
||||
this.state.verifyQuickInfoAt(markerName, expectedText, expectedDocumentation);
|
||||
}
|
||||
|
||||
public quickInfos(namesAndTexts: { [name: string]: string }) {
|
||||
this.state.verifyQuickInfos(namesAndTexts);
|
||||
}
|
||||
|
||||
public caretAtMarker(markerName?: string) {
|
||||
this.state.verifyCaretAtMarker(markerName);
|
||||
}
|
||||
@@ -3202,6 +3234,10 @@ namespace FourSlashInterface {
|
||||
this.state.baselineGetEmitOutput();
|
||||
}
|
||||
|
||||
public baselineQuickInfo() {
|
||||
this.state.baselineQuickInfo();
|
||||
}
|
||||
|
||||
public nameOrDottedNameSpanTextIs(text: string) {
|
||||
this.state.verifyCurrentNameOrDottedNameSpanText(text);
|
||||
}
|
||||
@@ -3234,8 +3270,8 @@ namespace FourSlashInterface {
|
||||
this.state.verifyNavigationBar(json);
|
||||
}
|
||||
|
||||
public navigationItemsListCount(count: number, searchValue: string, matchKind?: string) {
|
||||
this.state.verifyNavigationItemsCount(count, searchValue, matchKind);
|
||||
public navigationItemsListCount(count: number, searchValue: string, matchKind?: string, fileName?: string) {
|
||||
this.state.verifyNavigationItemsCount(count, searchValue, matchKind, fileName);
|
||||
}
|
||||
|
||||
public navigationItemsListContains(
|
||||
|
||||
@@ -1844,7 +1844,8 @@ namespace Harness {
|
||||
const parseConfigHost: ts.ParseConfigHost = {
|
||||
useCaseSensitiveFileNames: false,
|
||||
readDirectory: (name) => [],
|
||||
fileExists: (name) => true
|
||||
fileExists: (name) => true,
|
||||
readFile: (name) => ts.forEach(testUnitData, data => data.name.toLowerCase() === name.toLowerCase() ? data.content : undefined)
|
||||
};
|
||||
|
||||
// check if project has tsconfig.json in the list of files
|
||||
|
||||
@@ -408,6 +408,9 @@ namespace Harness.LanguageService {
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string): ts.CompletionEntryDetails {
|
||||
return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName));
|
||||
}
|
||||
getCompletionEntrySymbol(fileName: string, position: number, entryName: string): ts.Symbol {
|
||||
throw new Error("getCompletionEntrySymbol not implemented across the shim layer.");
|
||||
}
|
||||
getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo {
|
||||
return unwrapJSONCallResult(this.shim.getQuickInfoAtPosition(fileName, position));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/// <reference path="..\..\src\compiler\sys.ts" />
|
||||
/// <reference path="..\..\src\harness\harness.ts" />
|
||||
/// <reference path="..\..\src\harness\harnessLanguageService.ts" />
|
||||
/// <reference path="..\..\src\harness\runnerbase.ts" />
|
||||
/// <reference path="..\..\src\harness\typeWriter.ts" />
|
||||
|
||||
interface FileInformation {
|
||||
contents: string;
|
||||
|
||||
@@ -222,6 +222,7 @@ class ProjectRunner extends RunnerBase {
|
||||
useCaseSensitiveFileNames: Harness.IO.useCaseSensitiveFileNames(),
|
||||
fileExists,
|
||||
readDirectory,
|
||||
readFile
|
||||
};
|
||||
const configParseResult = ts.parseJsonConfigFileContent(configObject, configParseHost, ts.getDirectoryPath(configFileName), compilerOptions);
|
||||
if (configParseResult.errors.length > 0) {
|
||||
@@ -292,6 +293,10 @@ class ProjectRunner extends RunnerBase {
|
||||
return Harness.IO.fileExists(getFileNameInTheProjectTest(fileName));
|
||||
}
|
||||
|
||||
function readFile(fileName: string): string {
|
||||
return Harness.IO.readFile(getFileNameInTheProjectTest(fileName));
|
||||
}
|
||||
|
||||
function getSourceFileText(fileName: string): string {
|
||||
let text: string = undefined;
|
||||
try {
|
||||
|
||||
@@ -79,6 +79,7 @@ namespace RWC {
|
||||
useCaseSensitiveFileNames: Harness.IO.useCaseSensitiveFileNames(),
|
||||
fileExists: Harness.IO.fileExists,
|
||||
readDirectory: Harness.IO.readDirectory,
|
||||
readFile: Harness.IO.readFile
|
||||
};
|
||||
const configParseResult = ts.parseJsonConfigFileContent(parsedTsconfigFileContents.config, configParseHost, ts.getDirectoryPath(tsconfigFile.path));
|
||||
fileNames = configParseResult.fileNames;
|
||||
|
||||
@@ -22,6 +22,19 @@
|
||||
"../compiler/utilities.ts",
|
||||
"../compiler/binder.ts",
|
||||
"../compiler/checker.ts",
|
||||
"../compiler/factory.ts",
|
||||
"../compiler/visitor.ts",
|
||||
"../compiler/transformers/ts.ts",
|
||||
"../compiler/transformers/jsx.ts",
|
||||
"../compiler/transformers/es7.ts",
|
||||
"../compiler/transformers/es6.ts",
|
||||
"../compiler/transformers/generators.ts",
|
||||
"../compiler/transformers/destructuring.ts",
|
||||
"../compiler/transformers/module/module.ts",
|
||||
"../compiler/transformers/module/system.ts",
|
||||
"../compiler/transformers/module/es6.ts",
|
||||
"../compiler/transformer.ts",
|
||||
"../compiler/comments.ts",
|
||||
"../compiler/sourcemap.ts",
|
||||
"../compiler/declarationEmitter.ts",
|
||||
"../compiler/emitter.ts",
|
||||
@@ -86,6 +99,7 @@
|
||||
"./unittests/moduleResolution.ts",
|
||||
"./unittests/tsconfigParsing.ts",
|
||||
"./unittests/commandLineParsing.ts",
|
||||
"./unittests/configurationExtension.ts",
|
||||
"./unittests/convertCompilerOptionsFromJson.ts",
|
||||
"./unittests/convertTypingOptionsFromJson.ts",
|
||||
"./unittests/tsserverProjectSystem.ts",
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="..\virtualFileSystem.ts" />
|
||||
|
||||
namespace ts {
|
||||
const testContents = {
|
||||
"/dev/tsconfig.json": `{
|
||||
"extends": "./configs/base",
|
||||
"files": [
|
||||
"main.ts",
|
||||
"supplemental.ts"
|
||||
]
|
||||
}`,
|
||||
"/dev/tsconfig.nostrictnull.json": `{
|
||||
"extends": "./tsconfig",
|
||||
"compilerOptions": {
|
||||
"strictNullChecks": false
|
||||
}
|
||||
}`,
|
||||
"/dev/configs/base.json": `{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true
|
||||
}
|
||||
}`,
|
||||
"/dev/configs/tests.json": `{
|
||||
"compilerOptions": {
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": false,
|
||||
"sourceMap": true
|
||||
},
|
||||
"exclude": [
|
||||
"../tests/baselines",
|
||||
"../tests/scenarios"
|
||||
],
|
||||
"include": [
|
||||
"../tests/**/*.ts"
|
||||
]
|
||||
}`,
|
||||
"/dev/circular.json": `{
|
||||
"extends": "./circular2",
|
||||
"compilerOptions": {
|
||||
"module": "amd"
|
||||
}
|
||||
}`,
|
||||
"/dev/circular2.json": `{
|
||||
"extends": "./circular",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
}
|
||||
}`,
|
||||
"/dev/missing.json": `{
|
||||
"extends": "./missing2",
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
}
|
||||
}`,
|
||||
"/dev/failure.json": `{
|
||||
"extends": "./failure2.json",
|
||||
"compilerOptions": {
|
||||
"typeRoots": []
|
||||
}
|
||||
}`,
|
||||
"/dev/failure2.json": `{
|
||||
"excludes": ["*.js"]
|
||||
}`,
|
||||
"/dev/configs/first.json": `{
|
||||
"extends": "./base",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
},
|
||||
"files": ["../main.ts"]
|
||||
}`,
|
||||
"/dev/configs/second.json": `{
|
||||
"extends": "./base",
|
||||
"compilerOptions": {
|
||||
"module": "amd"
|
||||
},
|
||||
"include": ["../supplemental.*"]
|
||||
}`,
|
||||
"/dev/extends.json": `{ "extends": 42 }`,
|
||||
"/dev/extends2.json": `{ "extends": "configs/base" }`,
|
||||
"/dev/main.ts": "",
|
||||
"/dev/supplemental.ts": "",
|
||||
"/dev/tests/unit/spec.ts": "",
|
||||
"/dev/tests/utils.ts": "",
|
||||
"/dev/tests/scenarios/first.json": "",
|
||||
"/dev/tests/baselines/first/output.ts": ""
|
||||
};
|
||||
|
||||
const caseInsensitiveBasePath = "c:/dev/";
|
||||
const caseInsensitiveHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, mapObject(testContents, (key, content) => [`c:${key}`, content]));
|
||||
|
||||
const caseSensitiveBasePath = "/dev/";
|
||||
const caseSensitiveHost = new Utils.MockParseConfigHost(caseSensitiveBasePath, /*useCaseSensitiveFileNames*/ true, testContents);
|
||||
|
||||
function verifyDiagnostics(actual: Diagnostic[], expected: {code: number, category: DiagnosticCategory, messageText: string}[]) {
|
||||
assert.isTrue(expected.length === actual.length, `Expected error: ${JSON.stringify(expected)}. Actual error: ${JSON.stringify(actual)}.`);
|
||||
for (let i = 0; i < actual.length; i++) {
|
||||
const actualError = actual[i];
|
||||
const expectedError = expected[i];
|
||||
assert.equal(actualError.code, expectedError.code, "Error code mismatch");
|
||||
assert.equal(actualError.category, expectedError.category, "Category mismatch");
|
||||
assert.equal(flattenDiagnosticMessageText(actualError.messageText, "\n"), expectedError.messageText);
|
||||
}
|
||||
}
|
||||
|
||||
describe("Configuration Extension", () => {
|
||||
forEach<[string, string, Utils.MockParseConfigHost], void>([
|
||||
["under a case insensitive host", caseInsensitiveBasePath, caseInsensitiveHost],
|
||||
["under a case sensitive host", caseSensitiveBasePath, caseSensitiveHost]
|
||||
], ([testName, basePath, host]) => {
|
||||
function testSuccess(name: string, entry: string, expected: CompilerOptions, expectedFiles: string[]) {
|
||||
it(name, () => {
|
||||
const {config, error} = ts.readConfigFile(entry, name => host.readFile(name));
|
||||
assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n"));
|
||||
const parsed = ts.parseJsonConfigFileContent(config, host, basePath, {}, entry);
|
||||
assert(!parsed.errors.length, flattenDiagnosticMessageText(parsed.errors[0] && parsed.errors[0].messageText, "\n"));
|
||||
expected.configFilePath = entry;
|
||||
assert.deepEqual(parsed.options, expected);
|
||||
assert.deepEqual(parsed.fileNames, expectedFiles);
|
||||
});
|
||||
}
|
||||
|
||||
function testFailure(name: string, entry: string, expectedDiagnostics: {code: number, category: DiagnosticCategory, messageText: string}[]) {
|
||||
it(name, () => {
|
||||
const {config, error} = ts.readConfigFile(entry, name => host.readFile(name));
|
||||
assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n"));
|
||||
const parsed = ts.parseJsonConfigFileContent(config, host, basePath, {}, entry);
|
||||
verifyDiagnostics(parsed.errors, expectedDiagnostics);
|
||||
});
|
||||
}
|
||||
|
||||
describe(testName, () => {
|
||||
testSuccess("can resolve an extension with a base extension", "tsconfig.json", {
|
||||
allowJs: true,
|
||||
noImplicitAny: true,
|
||||
strictNullChecks: true,
|
||||
}, [
|
||||
combinePaths(basePath, "main.ts"),
|
||||
combinePaths(basePath, "supplemental.ts"),
|
||||
]);
|
||||
|
||||
testSuccess("can resolve an extension with a base extension that overrides options", "tsconfig.nostrictnull.json", {
|
||||
allowJs: true,
|
||||
noImplicitAny: true,
|
||||
strictNullChecks: false,
|
||||
}, [
|
||||
combinePaths(basePath, "main.ts"),
|
||||
combinePaths(basePath, "supplemental.ts"),
|
||||
]);
|
||||
|
||||
testFailure("can report errors on circular imports", "circular.json", [
|
||||
{
|
||||
code: 18000,
|
||||
category: DiagnosticCategory.Error,
|
||||
messageText: `Circularity detected while resolving configuration: ${[combinePaths(basePath, "circular.json"), combinePaths(basePath, "circular2.json"), combinePaths(basePath, "circular.json")].join(" -> ")}`
|
||||
}
|
||||
]);
|
||||
|
||||
testFailure("can report missing configurations", "missing.json", [{
|
||||
code: 6096,
|
||||
category: DiagnosticCategory.Message,
|
||||
messageText: `File './missing2' does not exist.`
|
||||
}]);
|
||||
|
||||
testFailure("can report errors in extended configs", "failure.json", [{
|
||||
code: 6114,
|
||||
category: DiagnosticCategory.Error,
|
||||
messageText: `Unknown option 'excludes'. Did you mean 'exclude'?`
|
||||
}]);
|
||||
|
||||
testFailure("can error when 'extends' is not a string", "extends.json", [{
|
||||
code: 5024,
|
||||
category: DiagnosticCategory.Error,
|
||||
messageText: `Compiler option 'extends' requires a value of type string.`
|
||||
}]);
|
||||
|
||||
testFailure("can error when 'extends' is neither relative nor rooted.", "extends2.json", [{
|
||||
code: 18001,
|
||||
category: DiagnosticCategory.Error,
|
||||
messageText: `The path in an 'extends' options must be relative or rooted.`
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -151,7 +151,7 @@ namespace ts {
|
||||
);
|
||||
});
|
||||
|
||||
it("always exclude outDir", () => {
|
||||
it("exclude outDir unless overridden", () => {
|
||||
const tsconfigWithoutExclude =
|
||||
`{
|
||||
"compilerOptions": {
|
||||
@@ -169,7 +169,7 @@ namespace ts {
|
||||
const allFiles = ["/bin/a.ts", "/b.ts"];
|
||||
const expectedFiles = ["/b.ts"];
|
||||
assertParseFileList(tsconfigWithoutExclude, "tsconfig.json", rootDir, allFiles, expectedFiles);
|
||||
assertParseFileList(tsconfigWithExclude, "tsconfig.json", rootDir, allFiles, expectedFiles);
|
||||
assertParseFileList(tsconfigWithExclude, "tsconfig.json", rootDir, allFiles, allFiles);
|
||||
});
|
||||
|
||||
it("implicitly exclude common package folders", () => {
|
||||
|
||||
@@ -10,9 +10,9 @@ namespace Utils {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
isDirectory() { return false; }
|
||||
isFile() { return false; }
|
||||
isFileSystem() { return false; }
|
||||
isDirectory(): this is VirtualDirectory { return false; }
|
||||
isFile(): this is VirtualFile { return false; }
|
||||
isFileSystem(): this is VirtualFileSystem { return false; }
|
||||
}
|
||||
|
||||
export class VirtualFile extends VirtualFileSystemEntry {
|
||||
@@ -82,9 +82,8 @@ namespace Utils {
|
||||
return file;
|
||||
}
|
||||
else if (entry.isFile()) {
|
||||
const file = <VirtualFile>entry;
|
||||
file.content = content;
|
||||
return file;
|
||||
entry.content = content;
|
||||
return entry;
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
@@ -196,10 +195,18 @@ namespace Utils {
|
||||
}
|
||||
|
||||
export class MockParseConfigHost extends VirtualFileSystem implements ts.ParseConfigHost {
|
||||
constructor(currentDirectory: string, ignoreCase: boolean, files: string[]) {
|
||||
constructor(currentDirectory: string, ignoreCase: boolean, files: ts.MapLike<string> | string[]) {
|
||||
super(currentDirectory, ignoreCase);
|
||||
for (const file of files) {
|
||||
this.addFile(file);
|
||||
const fileNames = (files instanceof Array) ? files : ts.getOwnKeys(files);
|
||||
for (const file of fileNames) {
|
||||
this.addFile(file, new Harness.LanguageService.ScriptInfo(file, (files as ts.MapLike<string>)[file], /*isRootFile*/false));
|
||||
}
|
||||
}
|
||||
|
||||
readFile(path: string): string {
|
||||
const value = this.traversePath(path);
|
||||
if (value && value.isFile()) {
|
||||
return value.content.content;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+4
-4
@@ -13,10 +13,10 @@ interface Array<T> {
|
||||
find(predicate: (value: T, index: number, obj: Array<T>) => boolean, thisArg?: any): T | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
@@ -360,10 +360,10 @@ interface ReadonlyArray<T> {
|
||||
find(predicate: (value: T, index: number, obj: ReadonlyArray<T>) => boolean, thisArg?: any): T | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
|
||||
Vendored
+96
-13
@@ -8,7 +8,7 @@ interface Promise<T> {
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then<TResult1, TResult2>(onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>, onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>;
|
||||
then(onfulfilled?: ((value: T) => T | PromiseLike<T>) | undefined | null, onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): Promise<T>;
|
||||
|
||||
/**
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
@@ -16,20 +16,30 @@ interface Promise<T> {
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>, onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<TResult>;
|
||||
then<TResult>(onfulfilled: ((value: T) => T | PromiseLike<T>) | undefined | null, onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<T | TResult>;
|
||||
|
||||
/**
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>): Promise<TResult>;
|
||||
then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>, onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<TResult>;
|
||||
|
||||
/**
|
||||
* Creates a new Promise with the same internal state of this Promise.
|
||||
* @returns A Promise.
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then(): Promise<T>;
|
||||
then<TResult1, TResult2>(onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>, onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>;
|
||||
|
||||
/**
|
||||
* Attaches a callback for only the rejection of the Promise.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of the callback.
|
||||
*/
|
||||
catch(onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): Promise<T>;
|
||||
|
||||
/**
|
||||
* Attaches a callback for only the rejection of the Promise.
|
||||
@@ -37,13 +47,6 @@ interface Promise<T> {
|
||||
* @returns A Promise for the completion of the callback.
|
||||
*/
|
||||
catch<TResult>(onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<T | TResult>;
|
||||
|
||||
/**
|
||||
* Attaches a callback for only the rejection of the Promise.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of the callback.
|
||||
*/
|
||||
catch(onrejected: (reason: any) => T | PromiseLike<T>): Promise<T>;
|
||||
}
|
||||
|
||||
interface PromiseConstructor {
|
||||
@@ -140,6 +143,86 @@ interface PromiseConstructor {
|
||||
*/
|
||||
all<T>(values: (T | PromiseLike<T>)[]): Promise<T[]>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
|
||||
* or rejected.
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
race<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9 | T10>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
|
||||
* or rejected.
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
race<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8 | T9>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
|
||||
* or rejected.
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
race<T1, T2, T3, T4, T5, T6, T7, T8>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7 | T8>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
|
||||
* or rejected.
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
race<T1, T2, T3, T4, T5, T6, T7>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>]): Promise<T1 | T2 | T3 | T4 | T5 | T6 | T7>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
|
||||
* or rejected.
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
race<T1, T2, T3, T4, T5, T6>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>]): Promise<T1 | T2 | T3 | T4 | T5 | T6>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
|
||||
* or rejected.
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
race<T1, T2, T3, T4, T5>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>]): Promise<T1 | T2 | T3 | T4 | T5>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
|
||||
* or rejected.
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
race<T1, T2, T3, T4>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>]): Promise<T1 | T2 | T3 | T4>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
|
||||
* or rejected.
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
race<T1, T2, T3>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<T1 | T2 | T3>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
|
||||
* or rejected.
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
race<T1, T2>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<T1 | T2>;
|
||||
|
||||
/**
|
||||
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
|
||||
* or rejected.
|
||||
* @param values An array of Promises.
|
||||
* @returns A new Promise.
|
||||
*/
|
||||
race<T>(values: (T | PromiseLike<T>)[]): Promise<T>;
|
||||
|
||||
/**
|
||||
* Creates a new rejected promise for the provided reason.
|
||||
* @param reason The reason the promise was rejected.
|
||||
|
||||
Vendored
+67
-81
@@ -1271,13 +1271,44 @@ declare type PromiseConstructorLike = new <T>(executor: (resolve: (value?: T | P
|
||||
|
||||
interface PromiseLike<T> {
|
||||
/**
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => TResult | PromiseLike<TResult>): PromiseLike<TResult>;
|
||||
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => void): PromiseLike<TResult>;
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then(
|
||||
onfulfilled?: ((value: T) => T | PromiseLike<T>) | undefined | null,
|
||||
onrejected?: ((reason: any) => T | PromiseLike<T>) | undefined | null): PromiseLike<T>;
|
||||
|
||||
/**
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then<TResult>(
|
||||
onfulfilled: ((value: T) => T | PromiseLike<T>) | undefined | null,
|
||||
onrejected: (reason: any) => TResult | PromiseLike<TResult>): PromiseLike<T | TResult>;
|
||||
|
||||
/**
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then<TResult>(
|
||||
onfulfilled: (value: T) => TResult | PromiseLike<TResult>,
|
||||
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): PromiseLike<TResult>;
|
||||
|
||||
/**
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then<TResult1, TResult2>(
|
||||
onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>,
|
||||
onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): PromiseLike<TResult1 | TResult2>;
|
||||
}
|
||||
|
||||
interface ArrayLike<T> {
|
||||
@@ -1537,7 +1568,7 @@ interface Int8Array {
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
@@ -1810,7 +1841,7 @@ interface Uint8Array {
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
@@ -2084,7 +2115,7 @@ interface Uint8ClampedArray {
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
@@ -2357,7 +2388,7 @@ interface Int16Array {
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
@@ -2631,7 +2662,7 @@ interface Uint16Array {
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
@@ -2904,7 +2935,7 @@ interface Int32Array {
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
@@ -3177,7 +3208,7 @@ interface Uint32Array {
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
@@ -3450,7 +3481,7 @@ interface Float32Array {
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
@@ -3724,7 +3755,7 @@ interface Float64Array {
|
||||
find(predicate: (value: number, index: number, obj: Array<number>) => boolean, thisArg?: any): number | undefined;
|
||||
|
||||
/**
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* Returns the index of the first element in the array where predicate is true, and -1
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
@@ -3949,12 +3980,9 @@ declare module Intl {
|
||||
resolvedOptions(): ResolvedCollatorOptions;
|
||||
}
|
||||
var Collator: {
|
||||
new (locales?: string[], options?: CollatorOptions): Collator;
|
||||
new (locale?: string, options?: CollatorOptions): Collator;
|
||||
(locales?: string[], options?: CollatorOptions): Collator;
|
||||
(locale?: string, options?: CollatorOptions): Collator;
|
||||
supportedLocalesOf(locales: string[], options?: CollatorOptions): string[];
|
||||
supportedLocalesOf(locale: string, options?: CollatorOptions): string[];
|
||||
new (locales?: string | string[], options?: CollatorOptions): Collator;
|
||||
(locales?: string | string[], options?: CollatorOptions): Collator;
|
||||
supportedLocalesOf(locales: string | string[], options?: CollatorOptions): string[];
|
||||
}
|
||||
|
||||
interface NumberFormatOptions {
|
||||
@@ -3989,12 +4017,9 @@ declare module Intl {
|
||||
resolvedOptions(): ResolvedNumberFormatOptions;
|
||||
}
|
||||
var NumberFormat: {
|
||||
new (locales?: string[], options?: NumberFormatOptions): NumberFormat;
|
||||
new (locale?: string, options?: NumberFormatOptions): NumberFormat;
|
||||
(locales?: string[], options?: NumberFormatOptions): NumberFormat;
|
||||
(locale?: string, options?: NumberFormatOptions): NumberFormat;
|
||||
supportedLocalesOf(locales: string[], options?: NumberFormatOptions): string[];
|
||||
supportedLocalesOf(locale: string, options?: NumberFormatOptions): string[];
|
||||
new (locales?: string | string[], options?: NumberFormatOptions): NumberFormat;
|
||||
(locales?: string | string[], options?: NumberFormatOptions): NumberFormat;
|
||||
supportedLocalesOf(locales: string | string[], options?: NumberFormatOptions): string[];
|
||||
}
|
||||
|
||||
interface DateTimeFormatOptions {
|
||||
@@ -4035,88 +4060,49 @@ declare module Intl {
|
||||
resolvedOptions(): ResolvedDateTimeFormatOptions;
|
||||
}
|
||||
var DateTimeFormat: {
|
||||
new (locales?: string[], options?: DateTimeFormatOptions): DateTimeFormat;
|
||||
new (locale?: string, options?: DateTimeFormatOptions): DateTimeFormat;
|
||||
(locales?: string[], options?: DateTimeFormatOptions): DateTimeFormat;
|
||||
(locale?: string, options?: DateTimeFormatOptions): DateTimeFormat;
|
||||
supportedLocalesOf(locales: string[], options?: DateTimeFormatOptions): string[];
|
||||
supportedLocalesOf(locale: string, options?: DateTimeFormatOptions): string[];
|
||||
new (locales?: string | string[], options?: DateTimeFormatOptions): DateTimeFormat;
|
||||
(locales?: string | string[], options?: DateTimeFormatOptions): DateTimeFormat;
|
||||
supportedLocalesOf(locales: string | string[], options?: DateTimeFormatOptions): string[];
|
||||
}
|
||||
}
|
||||
|
||||
interface String {
|
||||
/**
|
||||
* Determines whether two strings are equivalent in the current locale.
|
||||
* Determines whether two strings are equivalent in the current or specified locale.
|
||||
* @param that String to compare to target string
|
||||
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.
|
||||
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.
|
||||
* @param options An object that contains one or more properties that specify comparison options. see the Intl.Collator object for details.
|
||||
*/
|
||||
localeCompare(that: string, locales: string[], options?: Intl.CollatorOptions): number;
|
||||
|
||||
/**
|
||||
* Determines whether two strings are equivalent in the current locale.
|
||||
* @param that String to compare to target string
|
||||
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used. This parameter must conform to BCP 47 standards; see the Intl.Collator object for details.
|
||||
* @param options An object that contains one or more properties that specify comparison options. see the Intl.Collator object for details.
|
||||
*/
|
||||
localeCompare(that: string, locale: string, options?: Intl.CollatorOptions): number;
|
||||
localeCompare(that: string, locales?: string | string[], options?: Intl.CollatorOptions): number;
|
||||
}
|
||||
|
||||
interface Number {
|
||||
/**
|
||||
* Converts a number to a string by using the current or specified locale.
|
||||
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
|
||||
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
|
||||
* @param options An object that contains one or more properties that specify comparison options.
|
||||
*/
|
||||
toLocaleString(locales?: string[], options?: Intl.NumberFormatOptions): string;
|
||||
|
||||
/**
|
||||
* Converts a number to a string by using the current or specified locale.
|
||||
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
|
||||
* @param options An object that contains one or more properties that specify comparison options.
|
||||
*/
|
||||
toLocaleString(locale?: string, options?: Intl.NumberFormatOptions): string;
|
||||
toLocaleString(locales?: string | string[], options?: Intl.NumberFormatOptions): string;
|
||||
}
|
||||
|
||||
interface Date {
|
||||
/**
|
||||
* Converts a date and time to a string by using the current or specified locale.
|
||||
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
|
||||
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
|
||||
* @param options An object that contains one or more properties that specify comparison options.
|
||||
*/
|
||||
toLocaleString(locales?: string[], options?: Intl.DateTimeFormatOptions): string;
|
||||
toLocaleString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
|
||||
/**
|
||||
* Converts a date to a string by using the current or specified locale.
|
||||
* @param locales An array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
|
||||
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
|
||||
* @param options An object that contains one or more properties that specify comparison options.
|
||||
*/
|
||||
toLocaleDateString(locales?: string[], options?: Intl.DateTimeFormatOptions): string;
|
||||
toLocaleDateString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
|
||||
|
||||
/**
|
||||
* Converts a time to a string by using the current or specified locale.
|
||||
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
|
||||
* @param locales A locale string or array of locale strings that contain one or more language or locale tags. If you include more than one locale string, list them in descending order of priority so that the first entry is the preferred locale. If you omit this parameter, the default locale of the JavaScript runtime is used.
|
||||
* @param options An object that contains one or more properties that specify comparison options.
|
||||
*/
|
||||
toLocaleTimeString(locale?: string[], options?: Intl.DateTimeFormatOptions): string;
|
||||
|
||||
/**
|
||||
* Converts a date and time to a string by using the current or specified locale.
|
||||
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
|
||||
* @param options An object that contains one or more properties that specify comparison options.
|
||||
*/
|
||||
toLocaleString(locale?: string, options?: Intl.DateTimeFormatOptions): string;
|
||||
|
||||
/**
|
||||
* Converts a date to a string by using the current or specified locale.
|
||||
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
|
||||
* @param options An object that contains one or more properties that specify comparison options.
|
||||
*/
|
||||
toLocaleDateString(locale?: string, options?: Intl.DateTimeFormatOptions): string;
|
||||
|
||||
/**
|
||||
* Converts a time to a string by using the current or specified locale.
|
||||
* @param locale Locale tag. If you omit this parameter, the default locale of the JavaScript runtime is used.
|
||||
* @param options An object that contains one or more properties that specify comparison options.
|
||||
*/
|
||||
toLocaleTimeString(locale?: string, options?: Intl.DateTimeFormatOptions): string;
|
||||
toLocaleTimeString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
|
||||
}
|
||||
|
||||
@@ -246,6 +246,10 @@ namespace ts.server {
|
||||
return response.body[0];
|
||||
}
|
||||
|
||||
getCompletionEntrySymbol(fileName: string, position: number, entryName: string): Symbol {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getNavigateToItems(searchValue: string): NavigateToItem[] {
|
||||
const args: protocol.NavtoRequestArgs = {
|
||||
searchValue,
|
||||
|
||||
@@ -1597,6 +1597,7 @@ namespace ts.server {
|
||||
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
|
||||
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
|
||||
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
|
||||
InsertSpaceAfterTypeAssertion: false,
|
||||
PlaceOpenBraceOnNewLineForFunctions: false,
|
||||
PlaceOpenBraceOnNewLineForControlBlocks: false,
|
||||
});
|
||||
|
||||
Vendored
+5
@@ -1156,6 +1156,11 @@ declare namespace ts.server.protocol {
|
||||
* Optional limit on the number of items to return.
|
||||
*/
|
||||
maxResultCount?: number;
|
||||
/**
|
||||
* Optional flag to indicate we want results for just the current file
|
||||
* or the entire project.
|
||||
*/
|
||||
currentFileOnly?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -963,7 +963,7 @@ namespace ts.server {
|
||||
return this.decorateNavigationBarItem(project, fileName, items, compilerService.host.getLineIndex(fileName));
|
||||
}
|
||||
|
||||
private getNavigateToItems(searchValue: string, fileName: string, maxResultCount?: number): protocol.NavtoItem[] {
|
||||
private getNavigateToItems(searchValue: string, fileName: string, maxResultCount?: number, currentFileOnly?: boolean): protocol.NavtoItem[] {
|
||||
const file = ts.normalizePath(fileName);
|
||||
const info = this.projectService.getScriptInfo(file);
|
||||
const projects = this.projectService.findReferencingProjects(info);
|
||||
@@ -976,7 +976,7 @@ namespace ts.server {
|
||||
projectsWithLanguageServiceEnabeld,
|
||||
(project: Project) => {
|
||||
const compilerService = project.compilerService;
|
||||
const navItems = compilerService.languageService.getNavigateToItems(searchValue, maxResultCount);
|
||||
const navItems = compilerService.languageService.getNavigateToItems(searchValue, maxResultCount, currentFileOnly ? fileName : undefined);
|
||||
if (!navItems) {
|
||||
return [];
|
||||
}
|
||||
@@ -1215,7 +1215,7 @@ namespace ts.server {
|
||||
},
|
||||
[CommandNames.Navto]: (request: protocol.Request) => {
|
||||
const navtoArgs = <protocol.NavtoRequestArgs>request.arguments;
|
||||
return { response: this.getNavigateToItems(navtoArgs.searchValue, navtoArgs.file, navtoArgs.maxResultCount), responseRequired: true };
|
||||
return { response: this.getNavigateToItems(navtoArgs.searchValue, navtoArgs.file, navtoArgs.maxResultCount, navtoArgs.currentFileOnly), responseRequired: true };
|
||||
},
|
||||
[CommandNames.Brace]: (request: protocol.Request) => {
|
||||
const braceArguments = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
|
||||
@@ -0,0 +1,985 @@
|
||||
namespace ts {
|
||||
/// Classifier
|
||||
export function createClassifier(): Classifier {
|
||||
const scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false);
|
||||
|
||||
/// We do not have a full parser support to know when we should parse a regex or not
|
||||
/// If we consider every slash token to be a regex, we could be missing cases like "1/2/3", where
|
||||
/// we have a series of divide operator. this list allows us to be more accurate by ruling out
|
||||
/// locations where a regexp cannot exist.
|
||||
const noRegexTable: boolean[] = [];
|
||||
noRegexTable[SyntaxKind.Identifier] = true;
|
||||
noRegexTable[SyntaxKind.StringLiteral] = true;
|
||||
noRegexTable[SyntaxKind.NumericLiteral] = true;
|
||||
noRegexTable[SyntaxKind.RegularExpressionLiteral] = true;
|
||||
noRegexTable[SyntaxKind.ThisKeyword] = true;
|
||||
noRegexTable[SyntaxKind.PlusPlusToken] = true;
|
||||
noRegexTable[SyntaxKind.MinusMinusToken] = true;
|
||||
noRegexTable[SyntaxKind.CloseParenToken] = true;
|
||||
noRegexTable[SyntaxKind.CloseBracketToken] = true;
|
||||
noRegexTable[SyntaxKind.CloseBraceToken] = true;
|
||||
noRegexTable[SyntaxKind.TrueKeyword] = true;
|
||||
noRegexTable[SyntaxKind.FalseKeyword] = true;
|
||||
|
||||
// Just a stack of TemplateHeads and OpenCurlyBraces, used to perform rudimentary (inexact)
|
||||
// classification on template strings. Because of the context free nature of templates,
|
||||
// the only precise way to classify a template portion would be by propagating the stack across
|
||||
// lines, just as we do with the end-of-line state. However, this is a burden for implementers,
|
||||
// and the behavior is entirely subsumed by the syntactic classifier anyway, so we instead
|
||||
// flatten any nesting when the template stack is non-empty and encode it in the end-of-line state.
|
||||
// Situations in which this fails are
|
||||
// 1) When template strings are nested across different lines:
|
||||
// `hello ${ `world
|
||||
// ` }`
|
||||
//
|
||||
// Where on the second line, you will get the closing of a template,
|
||||
// a closing curly, and a new template.
|
||||
//
|
||||
// 2) When substitution expressions have curly braces and the curly brace falls on the next line:
|
||||
// `hello ${ () => {
|
||||
// return "world" } } `
|
||||
//
|
||||
// Where on the second line, you will get the 'return' keyword,
|
||||
// a string literal, and a template end consisting of '} } `'.
|
||||
const templateStack: SyntaxKind[] = [];
|
||||
|
||||
/** Returns true if 'keyword2' can legally follow 'keyword1' in any language construct. */
|
||||
function canFollow(keyword1: SyntaxKind, keyword2: SyntaxKind) {
|
||||
if (isAccessibilityModifier(keyword1)) {
|
||||
if (keyword2 === SyntaxKind.GetKeyword ||
|
||||
keyword2 === SyntaxKind.SetKeyword ||
|
||||
keyword2 === SyntaxKind.ConstructorKeyword ||
|
||||
keyword2 === SyntaxKind.StaticKeyword) {
|
||||
|
||||
// Allow things like "public get", "public constructor" and "public static".
|
||||
// These are all legal.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Any other keyword following "public" is actually an identifier an not a real
|
||||
// keyword.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assume any other keyword combination is legal. This can be refined in the future
|
||||
// if there are more cases we want the classifier to be better at.
|
||||
return true;
|
||||
}
|
||||
|
||||
function convertClassifications(classifications: Classifications, text: string): ClassificationResult {
|
||||
const entries: ClassificationInfo[] = [];
|
||||
const dense = classifications.spans;
|
||||
let lastEnd = 0;
|
||||
|
||||
for (let i = 0, n = dense.length; i < n; i += 3) {
|
||||
const start = dense[i];
|
||||
const length = dense[i + 1];
|
||||
const type = <ClassificationType>dense[i + 2];
|
||||
|
||||
// Make a whitespace entry between the last item and this one.
|
||||
if (lastEnd >= 0) {
|
||||
const whitespaceLength = start - lastEnd;
|
||||
if (whitespaceLength > 0) {
|
||||
entries.push({ length: whitespaceLength, classification: TokenClass.Whitespace });
|
||||
}
|
||||
}
|
||||
|
||||
entries.push({ length, classification: convertClassification(type) });
|
||||
lastEnd = start + length;
|
||||
}
|
||||
|
||||
const whitespaceLength = text.length - lastEnd;
|
||||
if (whitespaceLength > 0) {
|
||||
entries.push({ length: whitespaceLength, classification: TokenClass.Whitespace });
|
||||
}
|
||||
|
||||
return { entries, finalLexState: classifications.endOfLineState };
|
||||
}
|
||||
|
||||
function convertClassification(type: ClassificationType): TokenClass {
|
||||
switch (type) {
|
||||
case ClassificationType.comment: return TokenClass.Comment;
|
||||
case ClassificationType.keyword: return TokenClass.Keyword;
|
||||
case ClassificationType.numericLiteral: return TokenClass.NumberLiteral;
|
||||
case ClassificationType.operator: return TokenClass.Operator;
|
||||
case ClassificationType.stringLiteral: return TokenClass.StringLiteral;
|
||||
case ClassificationType.whiteSpace: return TokenClass.Whitespace;
|
||||
case ClassificationType.punctuation: return TokenClass.Punctuation;
|
||||
case ClassificationType.identifier:
|
||||
case ClassificationType.className:
|
||||
case ClassificationType.enumName:
|
||||
case ClassificationType.interfaceName:
|
||||
case ClassificationType.moduleName:
|
||||
case ClassificationType.typeParameterName:
|
||||
case ClassificationType.typeAliasName:
|
||||
case ClassificationType.text:
|
||||
case ClassificationType.parameterName:
|
||||
default:
|
||||
return TokenClass.Identifier;
|
||||
}
|
||||
}
|
||||
|
||||
function getClassificationsForLine(text: string, lexState: EndOfLineState, syntacticClassifierAbsent: boolean): ClassificationResult {
|
||||
return convertClassifications(getEncodedLexicalClassifications(text, lexState, syntacticClassifierAbsent), text);
|
||||
}
|
||||
|
||||
// If there is a syntactic classifier ('syntacticClassifierAbsent' is false),
|
||||
// we will be more conservative in order to avoid conflicting with the syntactic classifier.
|
||||
function getEncodedLexicalClassifications(text: string, lexState: EndOfLineState, syntacticClassifierAbsent: boolean): Classifications {
|
||||
let offset = 0;
|
||||
let token = SyntaxKind.Unknown;
|
||||
let lastNonTriviaToken = SyntaxKind.Unknown;
|
||||
|
||||
// Empty out the template stack for reuse.
|
||||
while (templateStack.length > 0) {
|
||||
templateStack.pop();
|
||||
}
|
||||
|
||||
// If we're in a string literal, then prepend: "\
|
||||
// (and a newline). That way when we lex we'll think we're still in a string literal.
|
||||
//
|
||||
// If we're in a multiline comment, then prepend: /*
|
||||
// (and a newline). That way when we lex we'll think we're still in a multiline comment.
|
||||
switch (lexState) {
|
||||
case EndOfLineState.InDoubleQuoteStringLiteral:
|
||||
text = "\"\\\n" + text;
|
||||
offset = 3;
|
||||
break;
|
||||
case EndOfLineState.InSingleQuoteStringLiteral:
|
||||
text = "'\\\n" + text;
|
||||
offset = 3;
|
||||
break;
|
||||
case EndOfLineState.InMultiLineCommentTrivia:
|
||||
text = "/*\n" + text;
|
||||
offset = 3;
|
||||
break;
|
||||
case EndOfLineState.InTemplateHeadOrNoSubstitutionTemplate:
|
||||
text = "`\n" + text;
|
||||
offset = 2;
|
||||
break;
|
||||
case EndOfLineState.InTemplateMiddleOrTail:
|
||||
text = "}\n" + text;
|
||||
offset = 2;
|
||||
// fallthrough
|
||||
case EndOfLineState.InTemplateSubstitutionPosition:
|
||||
templateStack.push(SyntaxKind.TemplateHead);
|
||||
break;
|
||||
}
|
||||
|
||||
scanner.setText(text);
|
||||
|
||||
const result: Classifications = {
|
||||
endOfLineState: EndOfLineState.None,
|
||||
spans: []
|
||||
};
|
||||
|
||||
// We can run into an unfortunate interaction between the lexical and syntactic classifier
|
||||
// when the user is typing something generic. Consider the case where the user types:
|
||||
//
|
||||
// Foo<number
|
||||
//
|
||||
// From the lexical classifier's perspective, 'number' is a keyword, and so the word will
|
||||
// be classified as such. However, from the syntactic classifier's tree-based perspective
|
||||
// this is simply an expression with the identifier 'number' on the RHS of the less than
|
||||
// token. So the classification will go back to being an identifier. The moment the user
|
||||
// types again, number will become a keyword, then an identifier, etc. etc.
|
||||
//
|
||||
// To try to avoid this problem, we avoid classifying contextual keywords as keywords
|
||||
// when the user is potentially typing something generic. We just can't do a good enough
|
||||
// job at the lexical level, and so well leave it up to the syntactic classifier to make
|
||||
// the determination.
|
||||
//
|
||||
// In order to determine if the user is potentially typing something generic, we use a
|
||||
// weak heuristic where we track < and > tokens. It's a weak heuristic, but should
|
||||
// work well enough in practice.
|
||||
let angleBracketStack = 0;
|
||||
|
||||
do {
|
||||
token = scanner.scan();
|
||||
|
||||
if (!isTrivia(token)) {
|
||||
if ((token === SyntaxKind.SlashToken || token === SyntaxKind.SlashEqualsToken) && !noRegexTable[lastNonTriviaToken]) {
|
||||
if (scanner.reScanSlashToken() === SyntaxKind.RegularExpressionLiteral) {
|
||||
token = SyntaxKind.RegularExpressionLiteral;
|
||||
}
|
||||
}
|
||||
else if (lastNonTriviaToken === SyntaxKind.DotToken && isKeyword(token)) {
|
||||
token = SyntaxKind.Identifier;
|
||||
}
|
||||
else if (isKeyword(lastNonTriviaToken) && isKeyword(token) && !canFollow(lastNonTriviaToken, token)) {
|
||||
// We have two keywords in a row. Only treat the second as a keyword if
|
||||
// it's a sequence that could legally occur in the language. Otherwise
|
||||
// treat it as an identifier. This way, if someone writes "private var"
|
||||
// we recognize that 'var' is actually an identifier here.
|
||||
token = SyntaxKind.Identifier;
|
||||
}
|
||||
else if (lastNonTriviaToken === SyntaxKind.Identifier &&
|
||||
token === SyntaxKind.LessThanToken) {
|
||||
// Could be the start of something generic. Keep track of that by bumping
|
||||
// up the current count of generic contexts we may be in.
|
||||
angleBracketStack++;
|
||||
}
|
||||
else if (token === SyntaxKind.GreaterThanToken && angleBracketStack > 0) {
|
||||
// If we think we're currently in something generic, then mark that that
|
||||
// generic entity is complete.
|
||||
angleBracketStack--;
|
||||
}
|
||||
else if (token === SyntaxKind.AnyKeyword ||
|
||||
token === SyntaxKind.StringKeyword ||
|
||||
token === SyntaxKind.NumberKeyword ||
|
||||
token === SyntaxKind.BooleanKeyword ||
|
||||
token === SyntaxKind.SymbolKeyword) {
|
||||
if (angleBracketStack > 0 && !syntacticClassifierAbsent) {
|
||||
// If it looks like we're could be in something generic, don't classify this
|
||||
// as a keyword. We may just get overwritten by the syntactic classifier,
|
||||
// causing a noisy experience for the user.
|
||||
token = SyntaxKind.Identifier;
|
||||
}
|
||||
}
|
||||
else if (token === SyntaxKind.TemplateHead) {
|
||||
templateStack.push(token);
|
||||
}
|
||||
else if (token === SyntaxKind.OpenBraceToken) {
|
||||
// If we don't have anything on the template stack,
|
||||
// then we aren't trying to keep track of a previously scanned template head.
|
||||
if (templateStack.length > 0) {
|
||||
templateStack.push(token);
|
||||
}
|
||||
}
|
||||
else if (token === SyntaxKind.CloseBraceToken) {
|
||||
// If we don't have anything on the template stack,
|
||||
// then we aren't trying to keep track of a previously scanned template head.
|
||||
if (templateStack.length > 0) {
|
||||
const lastTemplateStackToken = lastOrUndefined(templateStack);
|
||||
|
||||
if (lastTemplateStackToken === SyntaxKind.TemplateHead) {
|
||||
token = scanner.reScanTemplateToken();
|
||||
|
||||
// Only pop on a TemplateTail; a TemplateMiddle indicates there is more for us.
|
||||
if (token === SyntaxKind.TemplateTail) {
|
||||
templateStack.pop();
|
||||
}
|
||||
else {
|
||||
Debug.assert(token === SyntaxKind.TemplateMiddle, "Should have been a template middle. Was " + token);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.assert(lastTemplateStackToken === SyntaxKind.OpenBraceToken, "Should have been an open brace. Was: " + token);
|
||||
templateStack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastNonTriviaToken = token;
|
||||
}
|
||||
|
||||
processToken();
|
||||
}
|
||||
while (token !== SyntaxKind.EndOfFileToken);
|
||||
|
||||
return result;
|
||||
|
||||
function processToken(): void {
|
||||
const start = scanner.getTokenPos();
|
||||
const end = scanner.getTextPos();
|
||||
|
||||
addResult(start, end, classFromKind(token));
|
||||
|
||||
if (end >= text.length) {
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
// Check to see if we finished up on a multiline string literal.
|
||||
const tokenText = scanner.getTokenText();
|
||||
if (scanner.isUnterminated()) {
|
||||
const lastCharIndex = tokenText.length - 1;
|
||||
|
||||
let numBackslashes = 0;
|
||||
while (tokenText.charCodeAt(lastCharIndex - numBackslashes) === CharacterCodes.backslash) {
|
||||
numBackslashes++;
|
||||
}
|
||||
|
||||
// If we have an odd number of backslashes, then the multiline string is unclosed
|
||||
if (numBackslashes & 1) {
|
||||
const quoteChar = tokenText.charCodeAt(0);
|
||||
result.endOfLineState = quoteChar === CharacterCodes.doubleQuote
|
||||
? EndOfLineState.InDoubleQuoteStringLiteral
|
||||
: EndOfLineState.InSingleQuoteStringLiteral;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (token === SyntaxKind.MultiLineCommentTrivia) {
|
||||
// Check to see if the multiline comment was unclosed.
|
||||
if (scanner.isUnterminated()) {
|
||||
result.endOfLineState = EndOfLineState.InMultiLineCommentTrivia;
|
||||
}
|
||||
}
|
||||
else if (isTemplateLiteralKind(token)) {
|
||||
if (scanner.isUnterminated()) {
|
||||
if (token === SyntaxKind.TemplateTail) {
|
||||
result.endOfLineState = EndOfLineState.InTemplateMiddleOrTail;
|
||||
}
|
||||
else if (token === SyntaxKind.NoSubstitutionTemplateLiteral) {
|
||||
result.endOfLineState = EndOfLineState.InTemplateHeadOrNoSubstitutionTemplate;
|
||||
}
|
||||
else {
|
||||
Debug.fail("Only 'NoSubstitutionTemplateLiteral's and 'TemplateTail's can be unterminated; got SyntaxKind #" + token);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (templateStack.length > 0 && lastOrUndefined(templateStack) === SyntaxKind.TemplateHead) {
|
||||
result.endOfLineState = EndOfLineState.InTemplateSubstitutionPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addResult(start: number, end: number, classification: ClassificationType): void {
|
||||
if (classification === ClassificationType.whiteSpace) {
|
||||
// Don't bother with whitespace classifications. They're not needed.
|
||||
return;
|
||||
}
|
||||
|
||||
if (start === 0 && offset > 0) {
|
||||
// We're classifying the first token, and this was a case where we prepended
|
||||
// text. We should consider the start of this token to be at the start of
|
||||
// the original text.
|
||||
start += offset;
|
||||
}
|
||||
|
||||
// All our tokens are in relation to the augmented text. Move them back to be
|
||||
// relative to the original text.
|
||||
start -= offset;
|
||||
end -= offset;
|
||||
const length = end - start;
|
||||
|
||||
if (length > 0) {
|
||||
result.spans.push(start);
|
||||
result.spans.push(length);
|
||||
result.spans.push(classification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isBinaryExpressionOperatorToken(token: SyntaxKind): boolean {
|
||||
switch (token) {
|
||||
case SyntaxKind.AsteriskToken:
|
||||
case SyntaxKind.SlashToken:
|
||||
case SyntaxKind.PercentToken:
|
||||
case SyntaxKind.PlusToken:
|
||||
case SyntaxKind.MinusToken:
|
||||
case SyntaxKind.LessThanLessThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
case SyntaxKind.LessThanToken:
|
||||
case SyntaxKind.GreaterThanToken:
|
||||
case SyntaxKind.LessThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanEqualsToken:
|
||||
case SyntaxKind.InstanceOfKeyword:
|
||||
case SyntaxKind.InKeyword:
|
||||
case SyntaxKind.AsKeyword:
|
||||
case SyntaxKind.EqualsEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsToken:
|
||||
case SyntaxKind.EqualsEqualsEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsEqualsToken:
|
||||
case SyntaxKind.AmpersandToken:
|
||||
case SyntaxKind.CaretToken:
|
||||
case SyntaxKind.BarToken:
|
||||
case SyntaxKind.AmpersandAmpersandToken:
|
||||
case SyntaxKind.BarBarToken:
|
||||
case SyntaxKind.BarEqualsToken:
|
||||
case SyntaxKind.AmpersandEqualsToken:
|
||||
case SyntaxKind.CaretEqualsToken:
|
||||
case SyntaxKind.LessThanLessThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.PlusEqualsToken:
|
||||
case SyntaxKind.MinusEqualsToken:
|
||||
case SyntaxKind.AsteriskEqualsToken:
|
||||
case SyntaxKind.SlashEqualsToken:
|
||||
case SyntaxKind.PercentEqualsToken:
|
||||
case SyntaxKind.EqualsToken:
|
||||
case SyntaxKind.CommaToken:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isPrefixUnaryExpressionOperatorToken(token: SyntaxKind): boolean {
|
||||
switch (token) {
|
||||
case SyntaxKind.PlusToken:
|
||||
case SyntaxKind.MinusToken:
|
||||
case SyntaxKind.TildeToken:
|
||||
case SyntaxKind.ExclamationToken:
|
||||
case SyntaxKind.PlusPlusToken:
|
||||
case SyntaxKind.MinusMinusToken:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isKeyword(token: SyntaxKind): boolean {
|
||||
return token >= SyntaxKind.FirstKeyword && token <= SyntaxKind.LastKeyword;
|
||||
}
|
||||
|
||||
function classFromKind(token: SyntaxKind): ClassificationType {
|
||||
if (isKeyword(token)) {
|
||||
return ClassificationType.keyword;
|
||||
}
|
||||
else if (isBinaryExpressionOperatorToken(token) || isPrefixUnaryExpressionOperatorToken(token)) {
|
||||
return ClassificationType.operator;
|
||||
}
|
||||
else if (token >= SyntaxKind.FirstPunctuation && token <= SyntaxKind.LastPunctuation) {
|
||||
return ClassificationType.punctuation;
|
||||
}
|
||||
|
||||
switch (token) {
|
||||
case SyntaxKind.NumericLiteral:
|
||||
return ClassificationType.numericLiteral;
|
||||
case SyntaxKind.StringLiteral:
|
||||
return ClassificationType.stringLiteral;
|
||||
case SyntaxKind.RegularExpressionLiteral:
|
||||
return ClassificationType.regularExpressionLiteral;
|
||||
case SyntaxKind.ConflictMarkerTrivia:
|
||||
case SyntaxKind.MultiLineCommentTrivia:
|
||||
case SyntaxKind.SingleLineCommentTrivia:
|
||||
return ClassificationType.comment;
|
||||
case SyntaxKind.WhitespaceTrivia:
|
||||
case SyntaxKind.NewLineTrivia:
|
||||
return ClassificationType.whiteSpace;
|
||||
case SyntaxKind.Identifier:
|
||||
default:
|
||||
if (isTemplateLiteralKind(token)) {
|
||||
return ClassificationType.stringLiteral;
|
||||
}
|
||||
return ClassificationType.identifier;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getClassificationsForLine,
|
||||
getEncodedLexicalClassifications
|
||||
};
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getSemanticClassifications(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, classifiableNames: Map<string>, span: TextSpan): ClassifiedSpan[] {
|
||||
return convertClassifications(getEncodedSemanticClassifications(typeChecker, cancellationToken, sourceFile, classifiableNames, span));
|
||||
}
|
||||
|
||||
function checkForClassificationCancellation(cancellationToken: CancellationToken, kind: SyntaxKind) {
|
||||
// We don't want to actually call back into our host on every node to find out if we've
|
||||
// been canceled. That would be an enormous amount of chattyness, along with the all
|
||||
// the overhead of marshalling the data to/from the host. So instead we pick a few
|
||||
// reasonable node kinds to bother checking on. These node kinds represent high level
|
||||
// constructs that we would expect to see commonly, but just at a far less frequent
|
||||
// interval.
|
||||
//
|
||||
// For example, in checker.ts (around 750k) we only have around 600 of these constructs.
|
||||
// That means we're calling back into the host around every 1.2k of the file we process.
|
||||
// Lib.d.ts has similar numbers.
|
||||
switch (kind) {
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getEncodedSemanticClassifications(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, classifiableNames: Map<string>, span: TextSpan): Classifications {
|
||||
const result: number[] = [];
|
||||
processNode(sourceFile);
|
||||
|
||||
return { spans: result, endOfLineState: EndOfLineState.None };
|
||||
|
||||
function pushClassification(start: number, length: number, type: ClassificationType) {
|
||||
result.push(start);
|
||||
result.push(length);
|
||||
result.push(type);
|
||||
}
|
||||
|
||||
function classifySymbol(symbol: Symbol, meaningAtPosition: SemanticMeaning): ClassificationType {
|
||||
const flags = symbol.getFlags();
|
||||
if ((flags & SymbolFlags.Classifiable) === SymbolFlags.None) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & SymbolFlags.Class) {
|
||||
return ClassificationType.className;
|
||||
}
|
||||
else if (flags & SymbolFlags.Enum) {
|
||||
return ClassificationType.enumName;
|
||||
}
|
||||
else if (flags & SymbolFlags.TypeAlias) {
|
||||
return ClassificationType.typeAliasName;
|
||||
}
|
||||
else if (meaningAtPosition & SemanticMeaning.Type) {
|
||||
if (flags & SymbolFlags.Interface) {
|
||||
return ClassificationType.interfaceName;
|
||||
}
|
||||
else if (flags & SymbolFlags.TypeParameter) {
|
||||
return ClassificationType.typeParameterName;
|
||||
}
|
||||
}
|
||||
else if (flags & SymbolFlags.Module) {
|
||||
// Only classify a module as such if
|
||||
// - It appears in a namespace context.
|
||||
// - There exists a module declaration which actually impacts the value side.
|
||||
if (meaningAtPosition & SemanticMeaning.Namespace ||
|
||||
(meaningAtPosition & SemanticMeaning.Value && hasValueSideModule(symbol))) {
|
||||
return ClassificationType.moduleName;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
/**
|
||||
* Returns true if there exists a module that introduces entities on the value side.
|
||||
*/
|
||||
function hasValueSideModule(symbol: Symbol): boolean {
|
||||
return forEach(symbol.declarations, declaration => {
|
||||
return declaration.kind === SyntaxKind.ModuleDeclaration &&
|
||||
getModuleInstanceState(declaration) === ModuleInstanceState.Instantiated;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function processNode(node: Node) {
|
||||
// Only walk into nodes that intersect the requested span.
|
||||
if (node && textSpanIntersectsWith(span, node.getFullStart(), node.getFullWidth())) {
|
||||
const kind = node.kind;
|
||||
checkForClassificationCancellation(cancellationToken, kind);
|
||||
|
||||
if (kind === SyntaxKind.Identifier && !nodeIsMissing(node)) {
|
||||
const identifier = <Identifier>node;
|
||||
|
||||
// Only bother calling into the typechecker if this is an identifier that
|
||||
// could possibly resolve to a type name. This makes classification run
|
||||
// in a third of the time it would normally take.
|
||||
if (classifiableNames[identifier.text]) {
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
if (symbol) {
|
||||
const type = classifySymbol(symbol, getMeaningFromLocation(node));
|
||||
if (type) {
|
||||
pushClassification(node.getStart(), node.getWidth(), type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
forEachChild(node, processNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getClassificationTypeName(type: ClassificationType) {
|
||||
switch (type) {
|
||||
case ClassificationType.comment: return ClassificationTypeNames.comment;
|
||||
case ClassificationType.identifier: return ClassificationTypeNames.identifier;
|
||||
case ClassificationType.keyword: return ClassificationTypeNames.keyword;
|
||||
case ClassificationType.numericLiteral: return ClassificationTypeNames.numericLiteral;
|
||||
case ClassificationType.operator: return ClassificationTypeNames.operator;
|
||||
case ClassificationType.stringLiteral: return ClassificationTypeNames.stringLiteral;
|
||||
case ClassificationType.whiteSpace: return ClassificationTypeNames.whiteSpace;
|
||||
case ClassificationType.text: return ClassificationTypeNames.text;
|
||||
case ClassificationType.punctuation: return ClassificationTypeNames.punctuation;
|
||||
case ClassificationType.className: return ClassificationTypeNames.className;
|
||||
case ClassificationType.enumName: return ClassificationTypeNames.enumName;
|
||||
case ClassificationType.interfaceName: return ClassificationTypeNames.interfaceName;
|
||||
case ClassificationType.moduleName: return ClassificationTypeNames.moduleName;
|
||||
case ClassificationType.typeParameterName: return ClassificationTypeNames.typeParameterName;
|
||||
case ClassificationType.typeAliasName: return ClassificationTypeNames.typeAliasName;
|
||||
case ClassificationType.parameterName: return ClassificationTypeNames.parameterName;
|
||||
case ClassificationType.docCommentTagName: return ClassificationTypeNames.docCommentTagName;
|
||||
case ClassificationType.jsxOpenTagName: return ClassificationTypeNames.jsxOpenTagName;
|
||||
case ClassificationType.jsxCloseTagName: return ClassificationTypeNames.jsxCloseTagName;
|
||||
case ClassificationType.jsxSelfClosingTagName: return ClassificationTypeNames.jsxSelfClosingTagName;
|
||||
case ClassificationType.jsxAttribute: return ClassificationTypeNames.jsxAttribute;
|
||||
case ClassificationType.jsxText: return ClassificationTypeNames.jsxText;
|
||||
case ClassificationType.jsxAttributeStringLiteralValue: return ClassificationTypeNames.jsxAttributeStringLiteralValue;
|
||||
}
|
||||
}
|
||||
|
||||
function convertClassifications(classifications: Classifications): ClassifiedSpan[] {
|
||||
Debug.assert(classifications.spans.length % 3 === 0);
|
||||
const dense = classifications.spans;
|
||||
const result: ClassifiedSpan[] = [];
|
||||
for (let i = 0, n = dense.length; i < n; i += 3) {
|
||||
result.push({
|
||||
textSpan: createTextSpan(dense[i], dense[i + 1]),
|
||||
classificationType: getClassificationTypeName(dense[i + 2])
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getSyntacticClassifications(cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): ClassifiedSpan[] {
|
||||
return convertClassifications(getEncodedSyntacticClassifications(cancellationToken, sourceFile, span));
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function getEncodedSyntacticClassifications(cancellationToken: CancellationToken, sourceFile: SourceFile, span: TextSpan): Classifications {
|
||||
const spanStart = span.start;
|
||||
const spanLength = span.length;
|
||||
|
||||
// Make a scanner we can get trivia from.
|
||||
const triviaScanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false, sourceFile.languageVariant, sourceFile.text);
|
||||
const mergeConflictScanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false, sourceFile.languageVariant, sourceFile.text);
|
||||
|
||||
const result: number[] = [];
|
||||
processElement(sourceFile);
|
||||
|
||||
return { spans: result, endOfLineState: EndOfLineState.None };
|
||||
|
||||
function pushClassification(start: number, length: number, type: ClassificationType) {
|
||||
result.push(start);
|
||||
result.push(length);
|
||||
result.push(type);
|
||||
}
|
||||
|
||||
function classifyLeadingTriviaAndGetTokenStart(token: Node): number {
|
||||
triviaScanner.setTextPos(token.pos);
|
||||
while (true) {
|
||||
const start = triviaScanner.getTextPos();
|
||||
// only bother scanning if we have something that could be trivia.
|
||||
if (!couldStartTrivia(sourceFile.text, start)) {
|
||||
return start;
|
||||
}
|
||||
|
||||
const kind = triviaScanner.scan();
|
||||
const end = triviaScanner.getTextPos();
|
||||
const width = end - start;
|
||||
|
||||
// The moment we get something that isn't trivia, then stop processing.
|
||||
if (!isTrivia(kind)) {
|
||||
return start;
|
||||
}
|
||||
|
||||
// Don't bother with newlines/whitespace.
|
||||
if (kind === SyntaxKind.NewLineTrivia || kind === SyntaxKind.WhitespaceTrivia) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only bother with the trivia if it at least intersects the span of interest.
|
||||
if (isComment(kind)) {
|
||||
classifyComment(token, kind, start, width);
|
||||
|
||||
// Classifying a comment might cause us to reuse the trivia scanner
|
||||
// (because of jsdoc comments). So after we classify the comment make
|
||||
// sure we set the scanner position back to where it needs to be.
|
||||
triviaScanner.setTextPos(end);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (kind === SyntaxKind.ConflictMarkerTrivia) {
|
||||
const text = sourceFile.text;
|
||||
const ch = text.charCodeAt(start);
|
||||
|
||||
// for the <<<<<<< and >>>>>>> markers, we just add them in as comments
|
||||
// in the classification stream.
|
||||
if (ch === CharacterCodes.lessThan || ch === CharacterCodes.greaterThan) {
|
||||
pushClassification(start, width, ClassificationType.comment);
|
||||
continue;
|
||||
}
|
||||
|
||||
// for the ======== add a comment for the first line, and then lex all
|
||||
// subsequent lines up until the end of the conflict marker.
|
||||
Debug.assert(ch === CharacterCodes.equals);
|
||||
classifyDisabledMergeCode(text, start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function classifyComment(token: Node, kind: SyntaxKind, start: number, width: number) {
|
||||
if (kind === SyntaxKind.MultiLineCommentTrivia) {
|
||||
// See if this is a doc comment. If so, we'll classify certain portions of it
|
||||
// specially.
|
||||
const docCommentAndDiagnostics = parseIsolatedJSDocComment(sourceFile.text, start, width);
|
||||
if (docCommentAndDiagnostics && docCommentAndDiagnostics.jsDocComment) {
|
||||
docCommentAndDiagnostics.jsDocComment.parent = token;
|
||||
classifyJSDocComment(docCommentAndDiagnostics.jsDocComment);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Simple comment. Just add as is.
|
||||
pushCommentRange(start, width);
|
||||
}
|
||||
|
||||
function pushCommentRange(start: number, width: number) {
|
||||
pushClassification(start, width, ClassificationType.comment);
|
||||
}
|
||||
|
||||
function classifyJSDocComment(docComment: JSDocComment) {
|
||||
let pos = docComment.pos;
|
||||
|
||||
for (const tag of docComment.tags) {
|
||||
// As we walk through each tag, classify the portion of text from the end of
|
||||
// the last tag (or the start of the entire doc comment) as 'comment'.
|
||||
if (tag.pos !== pos) {
|
||||
pushCommentRange(pos, tag.pos - pos);
|
||||
}
|
||||
|
||||
pushClassification(tag.atToken.pos, tag.atToken.end - tag.atToken.pos, ClassificationType.punctuation);
|
||||
pushClassification(tag.tagName.pos, tag.tagName.end - tag.tagName.pos, ClassificationType.docCommentTagName);
|
||||
|
||||
pos = tag.tagName.end;
|
||||
|
||||
switch (tag.kind) {
|
||||
case SyntaxKind.JSDocParameterTag:
|
||||
processJSDocParameterTag(<JSDocParameterTag>tag);
|
||||
break;
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
processJSDocTemplateTag(<JSDocTemplateTag>tag);
|
||||
break;
|
||||
case SyntaxKind.JSDocTypeTag:
|
||||
processElement((<JSDocTypeTag>tag).typeExpression);
|
||||
break;
|
||||
case SyntaxKind.JSDocReturnTag:
|
||||
processElement((<JSDocReturnTag>tag).typeExpression);
|
||||
break;
|
||||
}
|
||||
|
||||
pos = tag.end;
|
||||
}
|
||||
|
||||
if (pos !== docComment.end) {
|
||||
pushCommentRange(pos, docComment.end - pos);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
function processJSDocParameterTag(tag: JSDocParameterTag) {
|
||||
if (tag.preParameterName) {
|
||||
pushCommentRange(pos, tag.preParameterName.pos - pos);
|
||||
pushClassification(tag.preParameterName.pos, tag.preParameterName.end - tag.preParameterName.pos, ClassificationType.parameterName);
|
||||
pos = tag.preParameterName.end;
|
||||
}
|
||||
|
||||
if (tag.typeExpression) {
|
||||
pushCommentRange(pos, tag.typeExpression.pos - pos);
|
||||
processElement(tag.typeExpression);
|
||||
pos = tag.typeExpression.end;
|
||||
}
|
||||
|
||||
if (tag.postParameterName) {
|
||||
pushCommentRange(pos, tag.postParameterName.pos - pos);
|
||||
pushClassification(tag.postParameterName.pos, tag.postParameterName.end - tag.postParameterName.pos, ClassificationType.parameterName);
|
||||
pos = tag.postParameterName.end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processJSDocTemplateTag(tag: JSDocTemplateTag) {
|
||||
for (const child of tag.getChildren()) {
|
||||
processElement(child);
|
||||
}
|
||||
}
|
||||
|
||||
function classifyDisabledMergeCode(text: string, start: number, end: number) {
|
||||
// Classify the line that the ======= marker is on as a comment. Then just lex
|
||||
// all further tokens and add them to the result.
|
||||
let i: number;
|
||||
for (i = start; i < end; i++) {
|
||||
if (isLineBreak(text.charCodeAt(i))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
pushClassification(start, i - start, ClassificationType.comment);
|
||||
mergeConflictScanner.setTextPos(i);
|
||||
|
||||
while (mergeConflictScanner.getTextPos() < end) {
|
||||
classifyDisabledCodeToken();
|
||||
}
|
||||
}
|
||||
|
||||
function classifyDisabledCodeToken() {
|
||||
const start = mergeConflictScanner.getTextPos();
|
||||
const tokenKind = mergeConflictScanner.scan();
|
||||
const end = mergeConflictScanner.getTextPos();
|
||||
|
||||
const type = classifyTokenType(tokenKind);
|
||||
if (type) {
|
||||
pushClassification(start, end - start, type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if node should be treated as classified and no further processing is required.
|
||||
* False will mean that node is not classified and traverse routine should recurse into node contents.
|
||||
*/
|
||||
function tryClassifyNode(node: Node): boolean {
|
||||
if (isJSDocTag(node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nodeIsMissing(node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const classifiedElementName = tryClassifyJsxElementName(node);
|
||||
if (!isToken(node) && node.kind !== SyntaxKind.JsxText && classifiedElementName === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const tokenStart = node.kind === SyntaxKind.JsxText ? node.pos : classifyLeadingTriviaAndGetTokenStart(node);
|
||||
|
||||
const tokenWidth = node.end - tokenStart;
|
||||
Debug.assert(tokenWidth >= 0);
|
||||
if (tokenWidth > 0) {
|
||||
const type = classifiedElementName || classifyTokenType(node.kind, node);
|
||||
if (type) {
|
||||
pushClassification(tokenStart, tokenWidth, type);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function tryClassifyJsxElementName(token: Node): ClassificationType {
|
||||
switch (token.parent && token.parent.kind) {
|
||||
case SyntaxKind.JsxOpeningElement:
|
||||
if ((<JsxOpeningElement>token.parent).tagName === token) {
|
||||
return ClassificationType.jsxOpenTagName;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.JsxClosingElement:
|
||||
if ((<JsxClosingElement>token.parent).tagName === token) {
|
||||
return ClassificationType.jsxCloseTagName;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
if ((<JsxSelfClosingElement>token.parent).tagName === token) {
|
||||
return ClassificationType.jsxSelfClosingTagName;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.JsxAttribute:
|
||||
if ((<JsxAttribute>token.parent).name === token) {
|
||||
return ClassificationType.jsxAttribute;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// for accurate classification, the actual token should be passed in. however, for
|
||||
// cases like 'disabled merge code' classification, we just get the token kind and
|
||||
// classify based on that instead.
|
||||
function classifyTokenType(tokenKind: SyntaxKind, token?: Node): ClassificationType {
|
||||
if (isKeyword(tokenKind)) {
|
||||
return ClassificationType.keyword;
|
||||
}
|
||||
|
||||
// Special case < and > If they appear in a generic context they are punctuation,
|
||||
// not operators.
|
||||
if (tokenKind === SyntaxKind.LessThanToken || tokenKind === SyntaxKind.GreaterThanToken) {
|
||||
// If the node owning the token has a type argument list or type parameter list, then
|
||||
// we can effectively assume that a '<' and '>' belong to those lists.
|
||||
if (token && getTypeArgumentOrTypeParameterList(token.parent)) {
|
||||
return ClassificationType.punctuation;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPunctuation(tokenKind)) {
|
||||
if (token) {
|
||||
if (tokenKind === SyntaxKind.EqualsToken) {
|
||||
// the '=' in a variable declaration is special cased here.
|
||||
if (token.parent.kind === SyntaxKind.VariableDeclaration ||
|
||||
token.parent.kind === SyntaxKind.PropertyDeclaration ||
|
||||
token.parent.kind === SyntaxKind.Parameter ||
|
||||
token.parent.kind === SyntaxKind.JsxAttribute) {
|
||||
return ClassificationType.operator;
|
||||
}
|
||||
}
|
||||
|
||||
if (token.parent.kind === SyntaxKind.BinaryExpression ||
|
||||
token.parent.kind === SyntaxKind.PrefixUnaryExpression ||
|
||||
token.parent.kind === SyntaxKind.PostfixUnaryExpression ||
|
||||
token.parent.kind === SyntaxKind.ConditionalExpression) {
|
||||
return ClassificationType.operator;
|
||||
}
|
||||
}
|
||||
|
||||
return ClassificationType.punctuation;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.NumericLiteral) {
|
||||
return ClassificationType.numericLiteral;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.StringLiteral) {
|
||||
return token.parent.kind === SyntaxKind.JsxAttribute ? ClassificationType.jsxAttributeStringLiteralValue : ClassificationType.stringLiteral;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.RegularExpressionLiteral) {
|
||||
// TODO: we should get another classification type for these literals.
|
||||
return ClassificationType.stringLiteral;
|
||||
}
|
||||
else if (isTemplateLiteralKind(tokenKind)) {
|
||||
// TODO (drosen): we should *also* get another classification type for these literals.
|
||||
return ClassificationType.stringLiteral;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.JsxText) {
|
||||
return ClassificationType.jsxText;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.Identifier) {
|
||||
if (token) {
|
||||
switch (token.parent.kind) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
if ((<ClassDeclaration>token.parent).name === token) {
|
||||
return ClassificationType.className;
|
||||
}
|
||||
return;
|
||||
case SyntaxKind.TypeParameter:
|
||||
if ((<TypeParameterDeclaration>token.parent).name === token) {
|
||||
return ClassificationType.typeParameterName;
|
||||
}
|
||||
return;
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
if ((<InterfaceDeclaration>token.parent).name === token) {
|
||||
return ClassificationType.interfaceName;
|
||||
}
|
||||
return;
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
if ((<EnumDeclaration>token.parent).name === token) {
|
||||
return ClassificationType.enumName;
|
||||
}
|
||||
return;
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
if ((<ModuleDeclaration>token.parent).name === token) {
|
||||
return ClassificationType.moduleName;
|
||||
}
|
||||
return;
|
||||
case SyntaxKind.Parameter:
|
||||
if ((<ParameterDeclaration>token.parent).name === token) {
|
||||
const isThis = token.kind === SyntaxKind.Identifier && (<Identifier>token).originalKeywordKind === SyntaxKind.ThisKeyword;
|
||||
return isThis ? ClassificationType.keyword : ClassificationType.parameterName;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
return ClassificationType.identifier;
|
||||
}
|
||||
}
|
||||
|
||||
function processElement(element: Node) {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore nodes that don't intersect the original span to classify.
|
||||
if (decodedTextSpanIntersectsWith(spanStart, spanLength, element.pos, element.getFullWidth())) {
|
||||
checkForClassificationCancellation(cancellationToken, element.kind);
|
||||
|
||||
const children = element.getChildren(sourceFile);
|
||||
for (let i = 0, n = children.length; i < n; i++) {
|
||||
const child = children[i];
|
||||
if (!tryClassifyNode(child)) {
|
||||
// Recurse into our child nodes.
|
||||
processElement(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,638 @@
|
||||
/* @internal */
|
||||
namespace ts.DocumentHighlights {
|
||||
export function getDocumentHighlights(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, position: number, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] {
|
||||
const node = getTouchingWord(sourceFile, position);
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getSemanticDocumentHighlights(node) || getSyntacticDocumentHighlights(node);
|
||||
|
||||
function getHighlightSpanForNode(node: Node): HighlightSpan {
|
||||
const start = node.getStart();
|
||||
const end = node.getEnd();
|
||||
|
||||
return {
|
||||
fileName: sourceFile.fileName,
|
||||
textSpan: createTextSpanFromBounds(start, end),
|
||||
kind: HighlightSpanKind.none
|
||||
};
|
||||
}
|
||||
|
||||
function getSemanticDocumentHighlights(node: Node): DocumentHighlights[] {
|
||||
if (node.kind === SyntaxKind.Identifier ||
|
||||
node.kind === SyntaxKind.ThisKeyword ||
|
||||
node.kind === SyntaxKind.ThisType ||
|
||||
node.kind === SyntaxKind.SuperKeyword ||
|
||||
node.kind === SyntaxKind.StringLiteral ||
|
||||
isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) {
|
||||
|
||||
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFilesToSearch, /*findInStrings*/ false, /*findInComments*/ false, /*implementations*/false);
|
||||
return convertReferencedSymbols(referencedSymbols);
|
||||
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
function convertReferencedSymbols(referencedSymbols: ReferencedSymbol[]): DocumentHighlights[] {
|
||||
if (!referencedSymbols) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const fileNameToDocumentHighlights = createMap<DocumentHighlights>();
|
||||
const result: DocumentHighlights[] = [];
|
||||
for (const referencedSymbol of referencedSymbols) {
|
||||
for (const referenceEntry of referencedSymbol.references) {
|
||||
const fileName = referenceEntry.fileName;
|
||||
let documentHighlights = fileNameToDocumentHighlights[fileName];
|
||||
if (!documentHighlights) {
|
||||
documentHighlights = { fileName, highlightSpans: [] };
|
||||
|
||||
fileNameToDocumentHighlights[fileName] = documentHighlights;
|
||||
result.push(documentHighlights);
|
||||
}
|
||||
|
||||
documentHighlights.highlightSpans.push({
|
||||
textSpan: referenceEntry.textSpan,
|
||||
kind: referenceEntry.isWriteAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function getSyntacticDocumentHighlights(node: Node): DocumentHighlights[] {
|
||||
const fileName = sourceFile.fileName;
|
||||
|
||||
const highlightSpans = getHighlightSpans(node);
|
||||
if (!highlightSpans || highlightSpans.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return [{ fileName, highlightSpans }];
|
||||
|
||||
// returns true if 'node' is defined and has a matching 'kind'.
|
||||
function hasKind(node: Node, kind: SyntaxKind) {
|
||||
return node !== undefined && node.kind === kind;
|
||||
}
|
||||
|
||||
// Null-propagating 'parent' function.
|
||||
function parent(node: Node): Node {
|
||||
return node && node.parent;
|
||||
}
|
||||
|
||||
function getHighlightSpans(node: Node): HighlightSpan[] {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.IfKeyword:
|
||||
case SyntaxKind.ElseKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.IfStatement)) {
|
||||
return getIfElseOccurrences(<IfStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ReturnKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.ReturnStatement)) {
|
||||
return getReturnOccurrences(<ReturnStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ThrowKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.ThrowStatement)) {
|
||||
return getThrowOccurrences(<ThrowStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.CatchKeyword:
|
||||
if (hasKind(parent(parent(node)), SyntaxKind.TryStatement)) {
|
||||
return getTryCatchFinallyOccurrences(<TryStatement>node.parent.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.TryKeyword:
|
||||
case SyntaxKind.FinallyKeyword:
|
||||
if (hasKind(parent(node), SyntaxKind.TryStatement)) {
|
||||
return getTryCatchFinallyOccurrences(<TryStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.SwitchKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.SwitchStatement)) {
|
||||
return getSwitchCaseDefaultOccurrences(<SwitchStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.CaseKeyword:
|
||||
case SyntaxKind.DefaultKeyword:
|
||||
if (hasKind(parent(parent(parent(node))), SyntaxKind.SwitchStatement)) {
|
||||
return getSwitchCaseDefaultOccurrences(<SwitchStatement>node.parent.parent.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.BreakKeyword:
|
||||
case SyntaxKind.ContinueKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.BreakStatement) || hasKind(node.parent, SyntaxKind.ContinueStatement)) {
|
||||
return getBreakOrContinueStatementOccurrences(<BreakOrContinueStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ForKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.ForStatement) ||
|
||||
hasKind(node.parent, SyntaxKind.ForInStatement) ||
|
||||
hasKind(node.parent, SyntaxKind.ForOfStatement)) {
|
||||
return getLoopBreakContinueOccurrences(<IterationStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.WhileKeyword:
|
||||
case SyntaxKind.DoKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.WhileStatement) || hasKind(node.parent, SyntaxKind.DoStatement)) {
|
||||
return getLoopBreakContinueOccurrences(<IterationStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ConstructorKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.Constructor)) {
|
||||
return getConstructorOccurrences(<ConstructorDeclaration>node.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.GetKeyword:
|
||||
case SyntaxKind.SetKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.GetAccessor) || hasKind(node.parent, SyntaxKind.SetAccessor)) {
|
||||
return getGetAndSetOccurrences(<AccessorDeclaration>node.parent);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (isModifierKind(node.kind) && node.parent &&
|
||||
(isDeclaration(node.parent) || node.parent.kind === SyntaxKind.VariableStatement)) {
|
||||
return getModifierOccurrences(node.kind, node.parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregates all throw-statements within this node *without* crossing
|
||||
* into function boundaries and try-blocks with catch-clauses.
|
||||
*/
|
||||
function aggregateOwnedThrowStatements(node: Node): ThrowStatement[] {
|
||||
const statementAccumulator: ThrowStatement[] = [];
|
||||
aggregate(node);
|
||||
return statementAccumulator;
|
||||
|
||||
function aggregate(node: Node): void {
|
||||
if (node.kind === SyntaxKind.ThrowStatement) {
|
||||
statementAccumulator.push(<ThrowStatement>node);
|
||||
}
|
||||
else if (node.kind === SyntaxKind.TryStatement) {
|
||||
const tryStatement = <TryStatement>node;
|
||||
|
||||
if (tryStatement.catchClause) {
|
||||
aggregate(tryStatement.catchClause);
|
||||
}
|
||||
else {
|
||||
// Exceptions thrown within a try block lacking a catch clause
|
||||
// are "owned" in the current context.
|
||||
aggregate(tryStatement.tryBlock);
|
||||
}
|
||||
|
||||
if (tryStatement.finallyBlock) {
|
||||
aggregate(tryStatement.finallyBlock);
|
||||
}
|
||||
}
|
||||
// Do not cross function boundaries.
|
||||
else if (!isFunctionLike(node)) {
|
||||
forEachChild(node, aggregate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For lack of a better name, this function takes a throw statement and returns the
|
||||
* nearest ancestor that is a try-block (whose try statement has a catch clause),
|
||||
* function-block, or source file.
|
||||
*/
|
||||
function getThrowStatementOwner(throwStatement: ThrowStatement): Node {
|
||||
let child: Node = throwStatement;
|
||||
|
||||
while (child.parent) {
|
||||
const parent = child.parent;
|
||||
|
||||
if (isFunctionBlock(parent) || parent.kind === SyntaxKind.SourceFile) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
// A throw-statement is only owned by a try-statement if the try-statement has
|
||||
// a catch clause, and if the throw-statement occurs within the try block.
|
||||
if (parent.kind === SyntaxKind.TryStatement) {
|
||||
const tryStatement = <TryStatement>parent;
|
||||
|
||||
if (tryStatement.tryBlock === child && tryStatement.catchClause) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
child = parent;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function aggregateAllBreakAndContinueStatements(node: Node): BreakOrContinueStatement[] {
|
||||
const statementAccumulator: BreakOrContinueStatement[] = [];
|
||||
aggregate(node);
|
||||
return statementAccumulator;
|
||||
|
||||
function aggregate(node: Node): void {
|
||||
if (node.kind === SyntaxKind.BreakStatement || node.kind === SyntaxKind.ContinueStatement) {
|
||||
statementAccumulator.push(<BreakOrContinueStatement>node);
|
||||
}
|
||||
// Do not cross function boundaries.
|
||||
else if (!isFunctionLike(node)) {
|
||||
forEachChild(node, aggregate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ownsBreakOrContinueStatement(owner: Node, statement: BreakOrContinueStatement): boolean {
|
||||
const actualOwner = getBreakOrContinueOwner(statement);
|
||||
|
||||
return actualOwner && actualOwner === owner;
|
||||
}
|
||||
|
||||
function getBreakOrContinueOwner(statement: BreakOrContinueStatement): Node {
|
||||
for (let node = statement.parent; node; node = node.parent) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.SwitchStatement:
|
||||
if (statement.kind === SyntaxKind.ContinueStatement) {
|
||||
continue;
|
||||
}
|
||||
// Fall through.
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
if (!statement.label || isLabeledBy(node, statement.label.text)) {
|
||||
return node;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Don't cross function boundaries.
|
||||
if (isFunctionLike(node)) {
|
||||
return undefined;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getModifierOccurrences(modifier: SyntaxKind, declaration: Node): HighlightSpan[] {
|
||||
const container = declaration.parent;
|
||||
|
||||
// Make sure we only highlight the keyword when it makes sense to do so.
|
||||
if (isAccessibilityModifier(modifier)) {
|
||||
if (!(container.kind === SyntaxKind.ClassDeclaration ||
|
||||
container.kind === SyntaxKind.ClassExpression ||
|
||||
(declaration.kind === SyntaxKind.Parameter && hasKind(container, SyntaxKind.Constructor)))) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
else if (modifier === SyntaxKind.StaticKeyword) {
|
||||
if (!(container.kind === SyntaxKind.ClassDeclaration || container.kind === SyntaxKind.ClassExpression)) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
else if (modifier === SyntaxKind.ExportKeyword || modifier === SyntaxKind.DeclareKeyword) {
|
||||
if (!(container.kind === SyntaxKind.ModuleBlock || container.kind === SyntaxKind.SourceFile)) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
else if (modifier === SyntaxKind.AbstractKeyword) {
|
||||
if (!(container.kind === SyntaxKind.ClassDeclaration || declaration.kind === SyntaxKind.ClassDeclaration)) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// unsupported modifier
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const keywords: Node[] = [];
|
||||
const modifierFlag: ModifierFlags = getFlagFromModifier(modifier);
|
||||
|
||||
let nodes: Node[];
|
||||
switch (container.kind) {
|
||||
case SyntaxKind.ModuleBlock:
|
||||
case SyntaxKind.SourceFile:
|
||||
// Container is either a class declaration or the declaration is a classDeclaration
|
||||
if (modifierFlag & ModifierFlags.Abstract) {
|
||||
nodes = (<Node[]>(<ClassDeclaration>declaration).members).concat(declaration);
|
||||
}
|
||||
else {
|
||||
nodes = (<Block>container).statements;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.Constructor:
|
||||
nodes = (<Node[]>(<ConstructorDeclaration>container).parameters).concat(
|
||||
(<ClassDeclaration>container.parent).members);
|
||||
break;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
nodes = (<ClassLikeDeclaration>container).members;
|
||||
|
||||
// If we're an accessibility modifier, we're in an instance member and should search
|
||||
// the constructor's parameter list for instance members as well.
|
||||
if (modifierFlag & ModifierFlags.AccessibilityModifier) {
|
||||
const constructor = forEach((<ClassLikeDeclaration>container).members, member => {
|
||||
return member.kind === SyntaxKind.Constructor && <ConstructorDeclaration>member;
|
||||
});
|
||||
|
||||
if (constructor) {
|
||||
nodes = nodes.concat(constructor.parameters);
|
||||
}
|
||||
}
|
||||
else if (modifierFlag & ModifierFlags.Abstract) {
|
||||
nodes = nodes.concat(container);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Debug.fail("Invalid container kind.");
|
||||
}
|
||||
|
||||
forEach(nodes, node => {
|
||||
if (getModifierFlags(node) & modifierFlag) {
|
||||
forEach(node.modifiers, child => pushKeywordIf(keywords, child, modifier));
|
||||
}
|
||||
});
|
||||
|
||||
return map(keywords, getHighlightSpanForNode);
|
||||
|
||||
function getFlagFromModifier(modifier: SyntaxKind) {
|
||||
switch (modifier) {
|
||||
case SyntaxKind.PublicKeyword:
|
||||
return ModifierFlags.Public;
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
return ModifierFlags.Private;
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
return ModifierFlags.Protected;
|
||||
case SyntaxKind.StaticKeyword:
|
||||
return ModifierFlags.Static;
|
||||
case SyntaxKind.ExportKeyword:
|
||||
return ModifierFlags.Export;
|
||||
case SyntaxKind.DeclareKeyword:
|
||||
return ModifierFlags.Ambient;
|
||||
case SyntaxKind.AbstractKeyword:
|
||||
return ModifierFlags.Abstract;
|
||||
default:
|
||||
Debug.fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pushKeywordIf(keywordList: Node[], token: Node, ...expected: SyntaxKind[]): boolean {
|
||||
if (token && contains(expected, token.kind)) {
|
||||
keywordList.push(token);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getGetAndSetOccurrences(accessorDeclaration: AccessorDeclaration): HighlightSpan[] {
|
||||
const keywords: Node[] = [];
|
||||
|
||||
tryPushAccessorKeyword(accessorDeclaration.symbol, SyntaxKind.GetAccessor);
|
||||
tryPushAccessorKeyword(accessorDeclaration.symbol, SyntaxKind.SetAccessor);
|
||||
|
||||
return map(keywords, getHighlightSpanForNode);
|
||||
|
||||
function tryPushAccessorKeyword(accessorSymbol: Symbol, accessorKind: SyntaxKind): void {
|
||||
const accessor = getDeclarationOfKind(accessorSymbol, accessorKind);
|
||||
|
||||
if (accessor) {
|
||||
forEach(accessor.getChildren(), child => pushKeywordIf(keywords, child, SyntaxKind.GetKeyword, SyntaxKind.SetKeyword));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getConstructorOccurrences(constructorDeclaration: ConstructorDeclaration): HighlightSpan[] {
|
||||
const declarations = constructorDeclaration.symbol.getDeclarations();
|
||||
|
||||
const keywords: Node[] = [];
|
||||
|
||||
forEach(declarations, declaration => {
|
||||
forEach(declaration.getChildren(), token => {
|
||||
return pushKeywordIf(keywords, token, SyntaxKind.ConstructorKeyword);
|
||||
});
|
||||
});
|
||||
|
||||
return map(keywords, getHighlightSpanForNode);
|
||||
}
|
||||
|
||||
function getLoopBreakContinueOccurrences(loopNode: IterationStatement): HighlightSpan[] {
|
||||
const keywords: Node[] = [];
|
||||
|
||||
if (pushKeywordIf(keywords, loopNode.getFirstToken(), SyntaxKind.ForKeyword, SyntaxKind.WhileKeyword, SyntaxKind.DoKeyword)) {
|
||||
// If we succeeded and got a do-while loop, then start looking for a 'while' keyword.
|
||||
if (loopNode.kind === SyntaxKind.DoStatement) {
|
||||
const loopTokens = loopNode.getChildren();
|
||||
|
||||
for (let i = loopTokens.length - 1; i >= 0; i--) {
|
||||
if (pushKeywordIf(keywords, loopTokens[i], SyntaxKind.WhileKeyword)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const breaksAndContinues = aggregateAllBreakAndContinueStatements(loopNode.statement);
|
||||
|
||||
forEach(breaksAndContinues, statement => {
|
||||
if (ownsBreakOrContinueStatement(loopNode, statement)) {
|
||||
pushKeywordIf(keywords, statement.getFirstToken(), SyntaxKind.BreakKeyword, SyntaxKind.ContinueKeyword);
|
||||
}
|
||||
});
|
||||
|
||||
return map(keywords, getHighlightSpanForNode);
|
||||
}
|
||||
|
||||
function getBreakOrContinueStatementOccurrences(breakOrContinueStatement: BreakOrContinueStatement): HighlightSpan[] {
|
||||
const owner = getBreakOrContinueOwner(breakOrContinueStatement);
|
||||
|
||||
if (owner) {
|
||||
switch (owner.kind) {
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
return getLoopBreakContinueOccurrences(<IterationStatement>owner);
|
||||
case SyntaxKind.SwitchStatement:
|
||||
return getSwitchCaseDefaultOccurrences(<SwitchStatement>owner);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement): HighlightSpan[] {
|
||||
const keywords: Node[] = [];
|
||||
|
||||
pushKeywordIf(keywords, switchStatement.getFirstToken(), SyntaxKind.SwitchKeyword);
|
||||
|
||||
// Go through each clause in the switch statement, collecting the 'case'/'default' keywords.
|
||||
forEach(switchStatement.caseBlock.clauses, clause => {
|
||||
pushKeywordIf(keywords, clause.getFirstToken(), SyntaxKind.CaseKeyword, SyntaxKind.DefaultKeyword);
|
||||
|
||||
const breaksAndContinues = aggregateAllBreakAndContinueStatements(clause);
|
||||
|
||||
forEach(breaksAndContinues, statement => {
|
||||
if (ownsBreakOrContinueStatement(switchStatement, statement)) {
|
||||
pushKeywordIf(keywords, statement.getFirstToken(), SyntaxKind.BreakKeyword);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return map(keywords, getHighlightSpanForNode);
|
||||
}
|
||||
|
||||
function getTryCatchFinallyOccurrences(tryStatement: TryStatement): HighlightSpan[] {
|
||||
const keywords: Node[] = [];
|
||||
|
||||
pushKeywordIf(keywords, tryStatement.getFirstToken(), SyntaxKind.TryKeyword);
|
||||
|
||||
if (tryStatement.catchClause) {
|
||||
pushKeywordIf(keywords, tryStatement.catchClause.getFirstToken(), SyntaxKind.CatchKeyword);
|
||||
}
|
||||
|
||||
if (tryStatement.finallyBlock) {
|
||||
const finallyKeyword = findChildOfKind(tryStatement, SyntaxKind.FinallyKeyword, sourceFile);
|
||||
pushKeywordIf(keywords, finallyKeyword, SyntaxKind.FinallyKeyword);
|
||||
}
|
||||
|
||||
return map(keywords, getHighlightSpanForNode);
|
||||
}
|
||||
|
||||
function getThrowOccurrences(throwStatement: ThrowStatement): HighlightSpan[] {
|
||||
const owner = getThrowStatementOwner(throwStatement);
|
||||
|
||||
if (!owner) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const keywords: Node[] = [];
|
||||
|
||||
forEach(aggregateOwnedThrowStatements(owner), throwStatement => {
|
||||
pushKeywordIf(keywords, throwStatement.getFirstToken(), SyntaxKind.ThrowKeyword);
|
||||
});
|
||||
|
||||
// If the "owner" is a function, then we equate 'return' and 'throw' statements in their
|
||||
// ability to "jump out" of the function, and include occurrences for both.
|
||||
if (isFunctionBlock(owner)) {
|
||||
forEachReturnStatement(<Block>owner, returnStatement => {
|
||||
pushKeywordIf(keywords, returnStatement.getFirstToken(), SyntaxKind.ReturnKeyword);
|
||||
});
|
||||
}
|
||||
|
||||
return map(keywords, getHighlightSpanForNode);
|
||||
}
|
||||
|
||||
function getReturnOccurrences(returnStatement: ReturnStatement): HighlightSpan[] {
|
||||
const func = <FunctionLikeDeclaration>getContainingFunction(returnStatement);
|
||||
|
||||
// If we didn't find a containing function with a block body, bail out.
|
||||
if (!(func && hasKind(func.body, SyntaxKind.Block))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const keywords: Node[] = [];
|
||||
forEachReturnStatement(<Block>func.body, returnStatement => {
|
||||
pushKeywordIf(keywords, returnStatement.getFirstToken(), SyntaxKind.ReturnKeyword);
|
||||
});
|
||||
|
||||
// Include 'throw' statements that do not occur within a try block.
|
||||
forEach(aggregateOwnedThrowStatements(func.body), throwStatement => {
|
||||
pushKeywordIf(keywords, throwStatement.getFirstToken(), SyntaxKind.ThrowKeyword);
|
||||
});
|
||||
|
||||
return map(keywords, getHighlightSpanForNode);
|
||||
}
|
||||
|
||||
function getIfElseOccurrences(ifStatement: IfStatement): HighlightSpan[] {
|
||||
const keywords: Node[] = [];
|
||||
|
||||
// Traverse upwards through all parent if-statements linked by their else-branches.
|
||||
while (hasKind(ifStatement.parent, SyntaxKind.IfStatement) && (<IfStatement>ifStatement.parent).elseStatement === ifStatement) {
|
||||
ifStatement = <IfStatement>ifStatement.parent;
|
||||
}
|
||||
|
||||
// Now traverse back down through the else branches, aggregating if/else keywords of if-statements.
|
||||
while (ifStatement) {
|
||||
const children = ifStatement.getChildren();
|
||||
pushKeywordIf(keywords, children[0], SyntaxKind.IfKeyword);
|
||||
|
||||
// Generally the 'else' keyword is second-to-last, so we traverse backwards.
|
||||
for (let i = children.length - 1; i >= 0; i--) {
|
||||
if (pushKeywordIf(keywords, children[i], SyntaxKind.ElseKeyword)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasKind(ifStatement.elseStatement, SyntaxKind.IfStatement)) {
|
||||
break;
|
||||
}
|
||||
|
||||
ifStatement = <IfStatement>ifStatement.elseStatement;
|
||||
}
|
||||
|
||||
const result: HighlightSpan[] = [];
|
||||
|
||||
// We'd like to highlight else/ifs together if they are only separated by whitespace
|
||||
// (i.e. the keywords are separated by no comments, no newlines).
|
||||
for (let i = 0; i < keywords.length; i++) {
|
||||
if (keywords[i].kind === SyntaxKind.ElseKeyword && i < keywords.length - 1) {
|
||||
const elseKeyword = keywords[i];
|
||||
const ifKeyword = keywords[i + 1]; // this *should* always be an 'if' keyword.
|
||||
|
||||
let shouldCombindElseAndIf = true;
|
||||
|
||||
// Avoid recalculating getStart() by iterating backwards.
|
||||
for (let j = ifKeyword.getStart() - 1; j >= elseKeyword.end; j--) {
|
||||
if (!isWhiteSpaceSingleLine(sourceFile.text.charCodeAt(j))) {
|
||||
shouldCombindElseAndIf = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldCombindElseAndIf) {
|
||||
result.push({
|
||||
fileName: fileName,
|
||||
textSpan: createTextSpanFromBounds(elseKeyword.getStart(), ifKeyword.end),
|
||||
kind: HighlightSpanKind.reference
|
||||
});
|
||||
i++; // skip the next keyword
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Ordinary case: just highlight the keyword.
|
||||
result.push(getHighlightSpanForNode(keywords[i]));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not a 'node' is preceded by a label of the given string.
|
||||
* Note: 'node' cannot be a SourceFile.
|
||||
*/
|
||||
function isLabeledBy(node: Node, labelName: string) {
|
||||
for (let owner = node.parent; owner.kind === SyntaxKind.LabeledStatement; owner = owner.parent) {
|
||||
if ((<LabeledStatement>owner).label.text === labelName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
namespace ts {
|
||||
/**
|
||||
* The document registry represents a store of SourceFile objects that can be shared between
|
||||
* multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST)
|
||||
* of files in the context.
|
||||
* SourceFile objects account for most of the memory usage by the language service. Sharing
|
||||
* the same DocumentRegistry instance between different instances of LanguageService allow
|
||||
* for more efficient memory utilization since all projects will share at least the library
|
||||
* file (lib.d.ts).
|
||||
*
|
||||
* A more advanced use of the document registry is to serialize sourceFile objects to disk
|
||||
* and re-hydrate them when needed.
|
||||
*
|
||||
* To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it
|
||||
* to all subsequent createLanguageService calls.
|
||||
*/
|
||||
export interface DocumentRegistry {
|
||||
/**
|
||||
* Request a stored SourceFile with a given fileName and compilationSettings.
|
||||
* The first call to acquire will call createLanguageServiceSourceFile to generate
|
||||
* the SourceFile if was not found in the registry.
|
||||
*
|
||||
* @param fileName The name of the file requested
|
||||
* @param compilationSettings Some compilation settings like target affects the
|
||||
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
|
||||
* multiple copies of the same file for different compilation settings.
|
||||
* @parm scriptSnapshot Text of the file. Only used if the file was not found
|
||||
* in the registry and a new one was created.
|
||||
* @parm version Current version of the file. Only used if the file was not found
|
||||
* in the registry and a new one was created.
|
||||
*/
|
||||
acquireDocument(
|
||||
fileName: string,
|
||||
compilationSettings: CompilerOptions,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
acquireDocumentWithKey(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
/**
|
||||
* Request an updated version of an already existing SourceFile with a given fileName
|
||||
* and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile
|
||||
* to get an updated SourceFile.
|
||||
*
|
||||
* @param fileName The name of the file requested
|
||||
* @param compilationSettings Some compilation settings like target affects the
|
||||
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
|
||||
* multiple copies of the same file for different compilation settings.
|
||||
* @param scriptSnapshot Text of the file.
|
||||
* @param version Current version of the file.
|
||||
*/
|
||||
updateDocument(
|
||||
fileName: string,
|
||||
compilationSettings: CompilerOptions,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
updateDocumentWithKey(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey;
|
||||
/**
|
||||
* Informs the DocumentRegistry that a file is not needed any longer.
|
||||
*
|
||||
* Note: It is not allowed to call release on a SourceFile that was not acquired from
|
||||
* this registry originally.
|
||||
*
|
||||
* @param fileName The name of the file to be released
|
||||
* @param compilationSettings The compilation settings used to acquire the file
|
||||
*/
|
||||
releaseDocument(fileName: string, compilationSettings: CompilerOptions): void;
|
||||
|
||||
releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void;
|
||||
|
||||
reportStats(): string;
|
||||
}
|
||||
|
||||
export type DocumentRegistryBucketKey = string & { __bucketKey: any };
|
||||
|
||||
interface DocumentRegistryEntry {
|
||||
sourceFile: SourceFile;
|
||||
|
||||
// The number of language services that this source file is referenced in. When no more
|
||||
// language services are referencing the file, then the file can be removed from the
|
||||
// registry.
|
||||
languageServiceRefCount: number;
|
||||
owners: string[];
|
||||
}
|
||||
|
||||
export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory = ""): DocumentRegistry {
|
||||
// Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have
|
||||
// for those settings.
|
||||
const buckets = createMap<FileMap<DocumentRegistryEntry>>();
|
||||
const getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
|
||||
|
||||
function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey {
|
||||
return <DocumentRegistryBucketKey>`_${settings.target}|${settings.module}|${settings.noResolve}|${settings.jsx}|${settings.allowJs}|${settings.baseUrl}|${JSON.stringify(settings.typeRoots)}|${JSON.stringify(settings.rootDirs)}|${JSON.stringify(settings.paths)}`;
|
||||
}
|
||||
|
||||
function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
|
||||
let bucket = buckets[key];
|
||||
if (!bucket && createIfMissing) {
|
||||
buckets[key] = bucket = createFileMap<DocumentRegistryEntry>();
|
||||
}
|
||||
return bucket;
|
||||
}
|
||||
|
||||
function reportStats() {
|
||||
const bucketInfoArray = Object.keys(buckets).filter(name => name && name.charAt(0) === "_").map(name => {
|
||||
const entries = buckets[name];
|
||||
const sourceFiles: { name: string; refCount: number; references: string[]; }[] = [];
|
||||
entries.forEachValue((key, entry) => {
|
||||
sourceFiles.push({
|
||||
name: key,
|
||||
refCount: entry.languageServiceRefCount,
|
||||
references: entry.owners.slice(0)
|
||||
});
|
||||
});
|
||||
sourceFiles.sort((x, y) => y.refCount - x.refCount);
|
||||
return {
|
||||
bucket: name,
|
||||
sourceFiles
|
||||
};
|
||||
});
|
||||
return JSON.stringify(bucketInfoArray, undefined, 2);
|
||||
}
|
||||
|
||||
function acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return acquireDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind);
|
||||
}
|
||||
|
||||
function acquireDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ true, scriptKind);
|
||||
}
|
||||
|
||||
function updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return updateDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind);
|
||||
}
|
||||
|
||||
function updateDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ false, scriptKind);
|
||||
}
|
||||
|
||||
function acquireOrUpdateDocument(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
acquiring: boolean,
|
||||
scriptKind?: ScriptKind): SourceFile {
|
||||
|
||||
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/ true);
|
||||
let entry = bucket.get(path);
|
||||
if (!entry) {
|
||||
Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?");
|
||||
|
||||
// Have never seen this file with these settings. Create a new source file for it.
|
||||
const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false, scriptKind);
|
||||
|
||||
entry = {
|
||||
sourceFile: sourceFile,
|
||||
languageServiceRefCount: 0,
|
||||
owners: []
|
||||
};
|
||||
bucket.set(path, entry);
|
||||
}
|
||||
else {
|
||||
// We have an entry for this file. However, it may be for a different version of
|
||||
// the script snapshot. If so, update it appropriately. Otherwise, we can just
|
||||
// return it as is.
|
||||
if (entry.sourceFile.version !== version) {
|
||||
entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version,
|
||||
scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot));
|
||||
}
|
||||
}
|
||||
|
||||
// If we're acquiring, then this is the first time this LS is asking for this document.
|
||||
// Increase our ref count so we know there's another LS using the document. If we're
|
||||
// not acquiring, then that means the LS is 'updating' the file instead, and that means
|
||||
// it has already acquired the document previously. As such, we do not need to increase
|
||||
// the ref count.
|
||||
if (acquiring) {
|
||||
entry.languageServiceRefCount++;
|
||||
}
|
||||
|
||||
return entry.sourceFile;
|
||||
}
|
||||
|
||||
function releaseDocument(fileName: string, compilationSettings: CompilerOptions): void {
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return releaseDocumentWithKey(path, key);
|
||||
}
|
||||
|
||||
function releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void {
|
||||
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/false);
|
||||
Debug.assert(bucket !== undefined);
|
||||
|
||||
const entry = bucket.get(path);
|
||||
entry.languageServiceRefCount--;
|
||||
|
||||
Debug.assert(entry.languageServiceRefCount >= 0);
|
||||
if (entry.languageServiceRefCount === 0) {
|
||||
bucket.remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
acquireDocument,
|
||||
acquireDocumentWithKey,
|
||||
updateDocument,
|
||||
updateDocumentWithKey,
|
||||
releaseDocument,
|
||||
releaseDocumentWithKey,
|
||||
reportStats,
|
||||
getKeyForCompilationSettings
|
||||
};
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -138,7 +138,6 @@ namespace ts.formatting {
|
||||
public NoSpaceAfterOpenAngularBracket: Rule;
|
||||
public NoSpaceBeforeCloseAngularBracket: Rule;
|
||||
public NoSpaceAfterCloseAngularBracket: Rule;
|
||||
public NoSpaceAfterTypeAssertion: Rule;
|
||||
|
||||
// Remove spaces in empty interface literals. e.g.: x: {}
|
||||
public NoSpaceBetweenEmptyInterfaceBraceBrackets: Rule;
|
||||
@@ -240,6 +239,10 @@ namespace ts.formatting {
|
||||
public NoSpaceBeforeEqualInJsxAttribute: Rule;
|
||||
public NoSpaceAfterEqualInJsxAttribute: Rule;
|
||||
|
||||
// No space after type assertions
|
||||
public NoSpaceAfterTypeAssertion: Rule;
|
||||
public SpaceAfterTypeAssertion: Rule;
|
||||
|
||||
constructor() {
|
||||
///
|
||||
/// Common Rules
|
||||
@@ -375,7 +378,6 @@ namespace ts.formatting {
|
||||
this.NoSpaceAfterOpenAngularBracket = new Rule(RuleDescriptor.create3(SyntaxKind.LessThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeArgumentOrParameterOrAssertionContext), RuleAction.Delete));
|
||||
this.NoSpaceBeforeCloseAngularBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.GreaterThanToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeArgumentOrParameterOrAssertionContext), RuleAction.Delete));
|
||||
this.NoSpaceAfterCloseAngularBracket = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.FromTokens([SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.GreaterThanToken, SyntaxKind.CommaToken])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeArgumentOrParameterOrAssertionContext), RuleAction.Delete));
|
||||
this.NoSpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Delete));
|
||||
|
||||
// Remove spaces in empty interface literals. e.g.: x: {}
|
||||
this.NoSpaceBetweenEmptyInterfaceBraceBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsObjectTypeContext), RuleAction.Delete));
|
||||
@@ -447,7 +449,6 @@ namespace ts.formatting {
|
||||
this.NoSpaceAfterOpenAngularBracket,
|
||||
this.NoSpaceBeforeCloseAngularBracket,
|
||||
this.NoSpaceAfterCloseAngularBracket,
|
||||
this.NoSpaceAfterTypeAssertion,
|
||||
this.SpaceBeforeAt,
|
||||
this.NoSpaceAfterAt,
|
||||
this.SpaceAfterDecorator,
|
||||
@@ -526,6 +527,11 @@ namespace ts.formatting {
|
||||
// Insert space after function keyword for anonymous functions
|
||||
this.SpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
|
||||
this.NoSpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Delete));
|
||||
|
||||
// No space after type assertion
|
||||
this.NoSpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Delete));
|
||||
this.SpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Space));
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
@@ -137,9 +137,16 @@ namespace ts.formatting {
|
||||
rules.push(this.globalRules.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock);
|
||||
}
|
||||
|
||||
if (options.InsertSpaceAfterTypeAssertion) {
|
||||
rules.push(this.globalRules.SpaceAfterTypeAssertion);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterTypeAssertion);
|
||||
}
|
||||
|
||||
rules = rules.concat(this.globalRules.LowPriorityCommonRules);
|
||||
|
||||
return rules;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
/* @internal */
|
||||
namespace ts.GoToDefinition {
|
||||
export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number): DefinitionInfo[] {
|
||||
/// Triple slash reference comments
|
||||
const comment = findReferenceInPosition(sourceFile.referencedFiles, position);
|
||||
if (comment) {
|
||||
const referenceFile = tryResolveScriptReference(program, sourceFile, comment);
|
||||
if (referenceFile) {
|
||||
return [getDefinitionInfoForFileReference(comment.fileName, referenceFile.fileName)];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Type reference directives
|
||||
const typeReferenceDirective = findReferenceInPosition(sourceFile.typeReferenceDirectives, position);
|
||||
if (typeReferenceDirective) {
|
||||
const referenceFile = program.getResolvedTypeReferenceDirectives()[typeReferenceDirective.fileName];
|
||||
if (referenceFile && referenceFile.resolvedFileName) {
|
||||
return [getDefinitionInfoForFileReference(typeReferenceDirective.fileName, referenceFile.resolvedFileName)];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
if (node === sourceFile) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Labels
|
||||
if (isJumpStatementTarget(node)) {
|
||||
const labelName = (<Identifier>node).text;
|
||||
const label = getTargetLabel((<BreakOrContinueStatement>node.parent), (<Identifier>node).text);
|
||||
return label ? [createDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined;
|
||||
}
|
||||
|
||||
const typeChecker = program.getTypeChecker();
|
||||
|
||||
const calledDeclaration = tryGetSignatureDeclaration(typeChecker, node);
|
||||
if (calledDeclaration) {
|
||||
return [createDefinitionFromSignatureDeclaration(typeChecker, calledDeclaration)];
|
||||
}
|
||||
|
||||
let symbol = typeChecker.getSymbolAtLocation(node);
|
||||
|
||||
// Could not find a symbol e.g. node is string or number keyword,
|
||||
// or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol
|
||||
if (!symbol) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If this is an alias, and the request came at the declaration location
|
||||
// get the aliased symbol instead. This allows for goto def on an import e.g.
|
||||
// import {A, B} from "mod";
|
||||
// to jump to the implementation directly.
|
||||
if (symbol.flags & SymbolFlags.Alias) {
|
||||
const declaration = symbol.declarations[0];
|
||||
|
||||
// Go to the original declaration for cases:
|
||||
//
|
||||
// (1) when the aliased symbol was declared in the location(parent).
|
||||
// (2) when the aliased symbol is originating from a named import.
|
||||
//
|
||||
if (node.kind === SyntaxKind.Identifier &&
|
||||
(node.parent === declaration ||
|
||||
(declaration.kind === SyntaxKind.ImportSpecifier && declaration.parent && declaration.parent.kind === SyntaxKind.NamedImports))) {
|
||||
|
||||
symbol = typeChecker.getAliasedSymbol(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
// Because name in short-hand property assignment has two different meanings: property name and property value,
|
||||
// using go-to-definition at such position should go to the variable declaration of the property value rather than
|
||||
// go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition
|
||||
// is performed at the location of property access, we would like to go to definition of the property in the short-hand
|
||||
// assignment. This case and others are handled by the following code.
|
||||
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration);
|
||||
if (!shorthandSymbol) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const shorthandDeclarations = shorthandSymbol.getDeclarations();
|
||||
const shorthandSymbolKind = SymbolDisplay.getSymbolKind(typeChecker, shorthandSymbol, node);
|
||||
const shorthandSymbolName = typeChecker.symbolToString(shorthandSymbol);
|
||||
const shorthandContainerName = typeChecker.symbolToString(symbol.parent, node);
|
||||
return map(shorthandDeclarations,
|
||||
declaration => createDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName));
|
||||
}
|
||||
|
||||
return getDefinitionFromSymbol(typeChecker, symbol, node);
|
||||
}
|
||||
|
||||
/// Goto type
|
||||
export function getTypeDefinitionAtPosition(typeChecker: TypeChecker, sourceFile: SourceFile, position: number): DefinitionInfo[] {
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
if (node === sourceFile) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
if (!symbol) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const type = typeChecker.getTypeOfSymbolAtLocation(symbol, node);
|
||||
if (!type) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum)) {
|
||||
const result: DefinitionInfo[] = [];
|
||||
forEach((<UnionType>type).types, t => {
|
||||
if (t.symbol) {
|
||||
addRange(/*to*/ result, /*from*/ getDefinitionFromSymbol(typeChecker, t.symbol, node));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!type.symbol) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getDefinitionFromSymbol(typeChecker, type.symbol, node);
|
||||
}
|
||||
|
||||
function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node): DefinitionInfo[] {
|
||||
const result: DefinitionInfo[] = [];
|
||||
const declarations = symbol.getDeclarations();
|
||||
const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, symbol, node);
|
||||
|
||||
if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) &&
|
||||
!tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) {
|
||||
// Just add all the declarations.
|
||||
forEach(declarations, declaration => {
|
||||
result.push(createDefinitionInfo(declaration, symbolKind, symbolName, containerName));
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
function tryAddConstructSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
|
||||
// Applicable only if we are in a new expression, or we are on a constructor declaration
|
||||
// and in either case the symbol has a construct signature definition, i.e. class
|
||||
if (isNewExpressionTarget(location) || location.kind === SyntaxKind.ConstructorKeyword) {
|
||||
if (symbol.flags & SymbolFlags.Class) {
|
||||
// Find the first class-like declaration and try to get the construct signature.
|
||||
for (const declaration of symbol.getDeclarations()) {
|
||||
if (isClassLike(declaration)) {
|
||||
return tryAddSignature(declaration.members,
|
||||
/*selectConstructors*/ true,
|
||||
symbolKind,
|
||||
symbolName,
|
||||
containerName,
|
||||
result);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.fail("Expected declaration to have at least one class-like declaration");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function tryAddCallSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
|
||||
if (isCallExpressionTarget(location) || isNewExpressionTarget(location) || isNameOfFunctionDeclaration(location)) {
|
||||
return tryAddSignature(symbol.declarations, /*selectConstructors*/ false, symbolKind, symbolName, containerName, result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function tryAddSignature(signatureDeclarations: Declaration[], selectConstructors: boolean, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
|
||||
const declarations: Declaration[] = [];
|
||||
let definition: Declaration;
|
||||
|
||||
forEach(signatureDeclarations, d => {
|
||||
if ((selectConstructors && d.kind === SyntaxKind.Constructor) ||
|
||||
(!selectConstructors && (d.kind === SyntaxKind.FunctionDeclaration || d.kind === SyntaxKind.MethodDeclaration || d.kind === SyntaxKind.MethodSignature))) {
|
||||
declarations.push(d);
|
||||
if ((<FunctionLikeDeclaration>d).body) definition = d;
|
||||
}
|
||||
});
|
||||
|
||||
if (definition) {
|
||||
result.push(createDefinitionInfo(definition, symbolKind, symbolName, containerName));
|
||||
return true;
|
||||
}
|
||||
else if (declarations.length) {
|
||||
result.push(createDefinitionInfo(lastOrUndefined(declarations), symbolKind, symbolName, containerName));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function createDefinitionInfo(node: Node, symbolKind: string, symbolName: string, containerName: string): DefinitionInfo {
|
||||
return {
|
||||
fileName: node.getSourceFile().fileName,
|
||||
textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()),
|
||||
kind: symbolKind,
|
||||
name: symbolName,
|
||||
containerKind: undefined,
|
||||
containerName
|
||||
};
|
||||
}
|
||||
|
||||
function getSymbolInfo(typeChecker: TypeChecker, symbol: Symbol, node: Node) {
|
||||
return {
|
||||
symbolName: typeChecker.symbolToString(symbol), // Do not get scoped name, just the name of the symbol
|
||||
symbolKind: SymbolDisplay.getSymbolKind(typeChecker, symbol, node),
|
||||
containerName: symbol.parent ? typeChecker.symbolToString(symbol.parent, node) : ""
|
||||
};
|
||||
}
|
||||
|
||||
function createDefinitionFromSignatureDeclaration(typeChecker: TypeChecker, decl: SignatureDeclaration): DefinitionInfo {
|
||||
const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, decl.symbol, decl);
|
||||
return createDefinitionInfo(decl, symbolKind, symbolName, containerName);
|
||||
}
|
||||
|
||||
function findReferenceInPosition(refs: FileReference[], pos: number): FileReference {
|
||||
for (const ref of refs) {
|
||||
if (ref.pos <= pos && pos < ref.end) {
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getDefinitionInfoForFileReference(name: string, targetFileName: string): DefinitionInfo {
|
||||
return {
|
||||
fileName: targetFileName,
|
||||
textSpan: createTextSpanFromBounds(0, 0),
|
||||
kind: ScriptElementKind.scriptElement,
|
||||
name: name,
|
||||
containerName: undefined,
|
||||
containerKind: undefined
|
||||
};
|
||||
}
|
||||
|
||||
/** Returns a CallLikeExpression where `node` is the target being invoked. */
|
||||
function getAncestorCallLikeExpression(node: Node): CallLikeExpression | undefined {
|
||||
const target = climbPastManyPropertyAccesses(node);
|
||||
const callLike = target.parent;
|
||||
return callLike && isCallLikeExpression(callLike) && getInvokedExpression(callLike) === target && callLike;
|
||||
}
|
||||
|
||||
function climbPastManyPropertyAccesses(node: Node): Node {
|
||||
return isRightSideOfPropertyAccess(node) ? climbPastManyPropertyAccesses(node.parent) : node;
|
||||
}
|
||||
|
||||
function tryGetSignatureDeclaration(typeChecker: TypeChecker, node: Node): SignatureDeclaration | undefined {
|
||||
const callLike = getAncestorCallLikeExpression(node);
|
||||
return callLike && typeChecker.getResolvedSignature(callLike).declaration;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/* @internal */
|
||||
namespace ts.GoToImplementation {
|
||||
export function getImplementationAtPosition(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], node: Node): ImplementationLocation[] {
|
||||
// If invoked directly on a shorthand property assignment, then return
|
||||
// the declaration of the symbol being assigned (not the symbol being assigned to).
|
||||
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const result: ReferenceEntry[] = [];
|
||||
FindAllReferences.getReferenceEntriesForShorthandPropertyAssignment(node, typeChecker, result);
|
||||
return result.length > 0 ? result : undefined;
|
||||
}
|
||||
else if (node.kind === SyntaxKind.SuperKeyword || isSuperProperty(node.parent)) {
|
||||
// References to and accesses on the super keyword only have one possible implementation, so no
|
||||
// need to "Find all References"
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
return symbol.valueDeclaration && [FindAllReferences.getReferenceEntryFromNode(symbol.valueDeclaration)];
|
||||
}
|
||||
else {
|
||||
// Perform "Find all References" and retrieve only those that are implementations
|
||||
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(typeChecker, cancellationToken,
|
||||
node, sourceFiles, /*findInStrings*/false, /*findInComments*/false, /*implementations*/true);
|
||||
const result = flatMap(referencedSymbols, symbol =>
|
||||
map(symbol.references, ({ textSpan, fileName }) => ({ textSpan, fileName })));
|
||||
|
||||
return result && result.length > 0 ? result : undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,545 @@
|
||||
/* @internal */
|
||||
namespace ts.JsDoc {
|
||||
const jsDocTagNames = [
|
||||
"augments",
|
||||
"author",
|
||||
"argument",
|
||||
"borrows",
|
||||
"class",
|
||||
"constant",
|
||||
"constructor",
|
||||
"constructs",
|
||||
"default",
|
||||
"deprecated",
|
||||
"description",
|
||||
"event",
|
||||
"example",
|
||||
"extends",
|
||||
"field",
|
||||
"fileOverview",
|
||||
"function",
|
||||
"ignore",
|
||||
"inner",
|
||||
"lends",
|
||||
"link",
|
||||
"memberOf",
|
||||
"name",
|
||||
"namespace",
|
||||
"param",
|
||||
"private",
|
||||
"property",
|
||||
"public",
|
||||
"requires",
|
||||
"returns",
|
||||
"see",
|
||||
"since",
|
||||
"static",
|
||||
"throws",
|
||||
"type",
|
||||
"typedef",
|
||||
"property",
|
||||
"prop",
|
||||
"version"
|
||||
];
|
||||
let jsDocCompletionEntries: CompletionEntry[];
|
||||
|
||||
export function getJsDocCommentsFromDeclarations(declarations: Declaration[], name: string, canUseParsedParamTagComments: boolean) {
|
||||
const documentationComment = <SymbolDisplayPart[]>[];
|
||||
const docComments = getJsDocCommentsSeparatedByNewLines();
|
||||
ts.forEach(docComments, docComment => {
|
||||
if (documentationComment.length) {
|
||||
documentationComment.push(lineBreakPart());
|
||||
}
|
||||
documentationComment.push(docComment);
|
||||
});
|
||||
|
||||
return documentationComment;
|
||||
|
||||
function getJsDocCommentsSeparatedByNewLines() {
|
||||
const paramTag = "@param";
|
||||
const jsDocCommentParts: SymbolDisplayPart[] = [];
|
||||
|
||||
ts.forEach(declarations, (declaration, indexOfDeclaration) => {
|
||||
// Make sure we are collecting doc comment from declaration once,
|
||||
// In case of union property there might be same declaration multiple times
|
||||
// which only varies in type parameter
|
||||
// Eg. const a: Array<string> | Array<number>; a.length
|
||||
// The property length will have two declarations of property length coming
|
||||
// from Array<T> - Array<string> and Array<number>
|
||||
if (indexOf(declarations, declaration) === indexOfDeclaration) {
|
||||
const sourceFileOfDeclaration = getSourceFileOfNode(declaration);
|
||||
// If it is parameter - try and get the jsDoc comment with @param tag from function declaration's jsDoc comments
|
||||
if (canUseParsedParamTagComments && declaration.kind === SyntaxKind.Parameter) {
|
||||
if ((declaration.parent.kind === SyntaxKind.FunctionExpression || declaration.parent.kind === SyntaxKind.ArrowFunction) &&
|
||||
declaration.parent.parent.kind === SyntaxKind.VariableDeclaration) {
|
||||
addCommentParts(declaration.parent.parent.parent, sourceFileOfDeclaration, getCleanedParamJsDocComment);
|
||||
}
|
||||
addCommentParts(declaration.parent, sourceFileOfDeclaration, getCleanedParamJsDocComment);
|
||||
}
|
||||
|
||||
// If this is left side of dotted module declaration, there is no doc comments associated with this node
|
||||
if (declaration.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>declaration).body && (<ModuleDeclaration>declaration).body.kind === SyntaxKind.ModuleDeclaration) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((declaration.kind === SyntaxKind.FunctionExpression || declaration.kind === SyntaxKind.ArrowFunction) &&
|
||||
declaration.parent.kind === SyntaxKind.VariableDeclaration) {
|
||||
addCommentParts(declaration.parent.parent, sourceFileOfDeclaration, getCleanedJsDocComment);
|
||||
}
|
||||
|
||||
// If this is dotted module name, get the doc comments from the parent
|
||||
while (declaration.kind === SyntaxKind.ModuleDeclaration && declaration.parent.kind === SyntaxKind.ModuleDeclaration) {
|
||||
declaration = <ModuleDeclaration>declaration.parent;
|
||||
}
|
||||
addCommentParts(declaration.kind === SyntaxKind.VariableDeclaration ? declaration.parent.parent : declaration,
|
||||
sourceFileOfDeclaration,
|
||||
getCleanedJsDocComment);
|
||||
|
||||
if (declaration.kind === SyntaxKind.VariableDeclaration) {
|
||||
const init = (declaration as VariableDeclaration).initializer;
|
||||
if (init && (init.kind === SyntaxKind.FunctionExpression || init.kind === SyntaxKind.ArrowFunction)) {
|
||||
// Get the cleaned js doc comment text from the initializer
|
||||
addCommentParts(init, sourceFileOfDeclaration, getCleanedJsDocComment);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return jsDocCommentParts;
|
||||
|
||||
function addCommentParts(commented: Node,
|
||||
sourceFileOfDeclaration: SourceFile,
|
||||
getCommentPart: (pos: number, end: number, file: SourceFile) => SymbolDisplayPart[]): void {
|
||||
const ranges = getJsDocCommentTextRange(commented, sourceFileOfDeclaration);
|
||||
// Get the cleaned js doc comment text from the declaration
|
||||
ts.forEach(ranges, jsDocCommentTextRange => {
|
||||
const cleanedComment = getCommentPart(jsDocCommentTextRange.pos, jsDocCommentTextRange.end, sourceFileOfDeclaration);
|
||||
if (cleanedComment) {
|
||||
addRange(jsDocCommentParts, cleanedComment);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getJsDocCommentTextRange(node: Node, sourceFile: SourceFile): TextRange[] {
|
||||
return ts.map(getJsDocComments(node, sourceFile),
|
||||
jsDocComment => {
|
||||
return {
|
||||
pos: jsDocComment.pos + "/*".length, // Consume /* from the comment
|
||||
end: jsDocComment.end - "*/".length // Trim off comment end indicator
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function consumeWhiteSpacesOnTheLine(pos: number, end: number, sourceFile: SourceFile, maxSpacesToRemove?: number) {
|
||||
if (maxSpacesToRemove !== undefined) {
|
||||
end = Math.min(end, pos + maxSpacesToRemove);
|
||||
}
|
||||
|
||||
for (; pos < end; pos++) {
|
||||
const ch = sourceFile.text.charCodeAt(pos);
|
||||
if (!isWhiteSpaceSingleLine(ch)) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
function consumeLineBreaks(pos: number, end: number, sourceFile: SourceFile) {
|
||||
while (pos < end && isLineBreak(sourceFile.text.charCodeAt(pos))) {
|
||||
pos++;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
function isName(pos: number, end: number, sourceFile: SourceFile, name: string) {
|
||||
return pos + name.length < end &&
|
||||
sourceFile.text.substr(pos, name.length) === name &&
|
||||
isWhiteSpace(sourceFile.text.charCodeAt(pos + name.length));
|
||||
}
|
||||
|
||||
function isParamTag(pos: number, end: number, sourceFile: SourceFile) {
|
||||
// If it is @param tag
|
||||
return isName(pos, end, sourceFile, paramTag);
|
||||
}
|
||||
|
||||
function pushDocCommentLineText(docComments: SymbolDisplayPart[], text: string, blankLineCount: number) {
|
||||
// Add the empty lines in between texts
|
||||
while (blankLineCount) {
|
||||
blankLineCount--;
|
||||
docComments.push(textPart(""));
|
||||
}
|
||||
|
||||
docComments.push(textPart(text));
|
||||
}
|
||||
|
||||
function getCleanedJsDocComment(pos: number, end: number, sourceFile: SourceFile) {
|
||||
let spacesToRemoveAfterAsterisk: number;
|
||||
const docComments: SymbolDisplayPart[] = [];
|
||||
let blankLineCount = 0;
|
||||
let isInParamTag = false;
|
||||
|
||||
while (pos < end) {
|
||||
let docCommentTextOfLine = "";
|
||||
// First consume leading white space
|
||||
pos = consumeWhiteSpacesOnTheLine(pos, end, sourceFile);
|
||||
|
||||
// If the comment starts with '*' consume the spaces on this line
|
||||
if (pos < end && sourceFile.text.charCodeAt(pos) === CharacterCodes.asterisk) {
|
||||
const lineStartPos = pos + 1;
|
||||
pos = consumeWhiteSpacesOnTheLine(pos + 1, end, sourceFile, spacesToRemoveAfterAsterisk);
|
||||
|
||||
// Set the spaces to remove after asterisk as margin if not already set
|
||||
if (spacesToRemoveAfterAsterisk === undefined && pos < end && !isLineBreak(sourceFile.text.charCodeAt(pos))) {
|
||||
spacesToRemoveAfterAsterisk = pos - lineStartPos;
|
||||
}
|
||||
}
|
||||
else if (spacesToRemoveAfterAsterisk === undefined) {
|
||||
spacesToRemoveAfterAsterisk = 0;
|
||||
}
|
||||
|
||||
// Analyze text on this line
|
||||
while (pos < end && !isLineBreak(sourceFile.text.charCodeAt(pos))) {
|
||||
const ch = sourceFile.text.charAt(pos);
|
||||
if (ch === "@") {
|
||||
// If it is @param tag
|
||||
if (isParamTag(pos, end, sourceFile)) {
|
||||
isInParamTag = true;
|
||||
pos += paramTag.length;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
isInParamTag = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the ch to doc text if we arent in param tag
|
||||
if (!isInParamTag) {
|
||||
docCommentTextOfLine += ch;
|
||||
}
|
||||
|
||||
// Scan next character
|
||||
pos++;
|
||||
}
|
||||
|
||||
// Continue with next line
|
||||
pos = consumeLineBreaks(pos, end, sourceFile);
|
||||
if (docCommentTextOfLine) {
|
||||
pushDocCommentLineText(docComments, docCommentTextOfLine, blankLineCount);
|
||||
blankLineCount = 0;
|
||||
}
|
||||
else if (!isInParamTag && docComments.length) {
|
||||
// This is blank line when there is text already parsed
|
||||
blankLineCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return docComments;
|
||||
}
|
||||
|
||||
function getCleanedParamJsDocComment(pos: number, end: number, sourceFile: SourceFile) {
|
||||
let paramHelpStringMargin: number;
|
||||
const paramDocComments: SymbolDisplayPart[] = [];
|
||||
while (pos < end) {
|
||||
if (isParamTag(pos, end, sourceFile)) {
|
||||
let blankLineCount = 0;
|
||||
let recordedParamTag = false;
|
||||
// Consume leading spaces
|
||||
pos = consumeWhiteSpaces(pos + paramTag.length);
|
||||
if (pos >= end) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Ignore type expression
|
||||
if (sourceFile.text.charCodeAt(pos) === CharacterCodes.openBrace) {
|
||||
pos++;
|
||||
for (let curlies = 1; pos < end; pos++) {
|
||||
const charCode = sourceFile.text.charCodeAt(pos);
|
||||
|
||||
// { character means we need to find another } to match the found one
|
||||
if (charCode === CharacterCodes.openBrace) {
|
||||
curlies++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// } char
|
||||
if (charCode === CharacterCodes.closeBrace) {
|
||||
curlies--;
|
||||
if (curlies === 0) {
|
||||
// We do not have any more } to match the type expression is ignored completely
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// there are more { to be matched with }
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Found start of another tag
|
||||
if (charCode === CharacterCodes.at) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Consume white spaces
|
||||
pos = consumeWhiteSpaces(pos);
|
||||
if (pos >= end) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Parameter name
|
||||
if (isName(pos, end, sourceFile, name)) {
|
||||
// Found the parameter we are looking for consume white spaces
|
||||
pos = consumeWhiteSpaces(pos + name.length);
|
||||
if (pos >= end) {
|
||||
break;
|
||||
}
|
||||
|
||||
let paramHelpString = "";
|
||||
const firstLineParamHelpStringPos = pos;
|
||||
while (pos < end) {
|
||||
const ch = sourceFile.text.charCodeAt(pos);
|
||||
|
||||
// at line break, set this comment line text and go to next line
|
||||
if (isLineBreak(ch)) {
|
||||
if (paramHelpString) {
|
||||
pushDocCommentLineText(paramDocComments, paramHelpString, blankLineCount);
|
||||
paramHelpString = "";
|
||||
blankLineCount = 0;
|
||||
recordedParamTag = true;
|
||||
}
|
||||
else if (recordedParamTag) {
|
||||
blankLineCount++;
|
||||
}
|
||||
|
||||
// Get the pos after cleaning start of the line
|
||||
setPosForParamHelpStringOnNextLine(firstLineParamHelpStringPos);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Done scanning param help string - next tag found
|
||||
if (ch === CharacterCodes.at) {
|
||||
break;
|
||||
}
|
||||
|
||||
paramHelpString += sourceFile.text.charAt(pos);
|
||||
|
||||
// Go to next character
|
||||
pos++;
|
||||
}
|
||||
|
||||
// If there is param help text, add it top the doc comments
|
||||
if (paramHelpString) {
|
||||
pushDocCommentLineText(paramDocComments, paramHelpString, blankLineCount);
|
||||
}
|
||||
paramHelpStringMargin = undefined;
|
||||
}
|
||||
|
||||
// If this is the start of another tag, continue with the loop in search of param tag with symbol name
|
||||
if (sourceFile.text.charCodeAt(pos) === CharacterCodes.at) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Next character
|
||||
pos++;
|
||||
}
|
||||
|
||||
return paramDocComments;
|
||||
|
||||
function consumeWhiteSpaces(pos: number) {
|
||||
while (pos < end && isWhiteSpaceSingleLine(sourceFile.text.charCodeAt(pos))) {
|
||||
pos++;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
function setPosForParamHelpStringOnNextLine(firstLineParamHelpStringPos: number) {
|
||||
// Get the pos after consuming line breaks
|
||||
pos = consumeLineBreaks(pos, end, sourceFile);
|
||||
if (pos >= end) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (paramHelpStringMargin === undefined) {
|
||||
paramHelpStringMargin = sourceFile.getLineAndCharacterOfPosition(firstLineParamHelpStringPos).character;
|
||||
}
|
||||
|
||||
// Now consume white spaces max
|
||||
const startOfLinePos = pos;
|
||||
pos = consumeWhiteSpacesOnTheLine(pos, end, sourceFile, paramHelpStringMargin);
|
||||
if (pos >= end) {
|
||||
return;
|
||||
}
|
||||
|
||||
const consumedSpaces = pos - startOfLinePos;
|
||||
if (consumedSpaces < paramHelpStringMargin) {
|
||||
const ch = sourceFile.text.charCodeAt(pos);
|
||||
if (ch === CharacterCodes.asterisk) {
|
||||
// Consume more spaces after asterisk
|
||||
pos = consumeWhiteSpacesOnTheLine(pos + 1, end, sourceFile, paramHelpStringMargin - consumedSpaces - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getAllJsDocCompletionEntries(): CompletionEntry[] {
|
||||
return jsDocCompletionEntries || (jsDocCompletionEntries = ts.map(jsDocTagNames, tagName => {
|
||||
return {
|
||||
name: tagName,
|
||||
kind: ScriptElementKind.keyword,
|
||||
kindModifiers: "",
|
||||
sortText: "0",
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if position points to a valid position to add JSDoc comments, and if so,
|
||||
* returns the appropriate template. Otherwise returns an empty string.
|
||||
* Valid positions are
|
||||
* - outside of comments, statements, and expressions, and
|
||||
* - preceding a:
|
||||
* - function/constructor/method declaration
|
||||
* - class declarations
|
||||
* - variable statements
|
||||
* - namespace declarations
|
||||
*
|
||||
* Hosts should ideally check that:
|
||||
* - The line is all whitespace up to 'position' before performing the insertion.
|
||||
* - If the keystroke sequence "/\*\*" induced the call, we also check that the next
|
||||
* non-whitespace character is '*', which (approximately) indicates whether we added
|
||||
* the second '*' to complete an existing (JSDoc) comment.
|
||||
* @param fileName The file in which to perform the check.
|
||||
* @param position The (character-indexed) position in the file where the check should
|
||||
* be performed.
|
||||
*/
|
||||
export function getDocCommentTemplateAtPosition(newLine: string, sourceFile: SourceFile, position: number): TextInsertion {
|
||||
// Check if in a context where we don't want to perform any insertion
|
||||
if (isInString(sourceFile, position) || isInComment(sourceFile, position) || hasDocComment(sourceFile, position)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const tokenAtPos = getTokenAtPosition(sourceFile, position);
|
||||
const tokenStart = tokenAtPos.getStart();
|
||||
if (!tokenAtPos || tokenStart < position) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// TODO: add support for:
|
||||
// - enums/enum members
|
||||
// - interfaces
|
||||
// - property declarations
|
||||
// - potentially property assignments
|
||||
let commentOwner: Node;
|
||||
findOwner: for (commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) {
|
||||
switch (commentOwner.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.VariableStatement:
|
||||
break findOwner;
|
||||
case SyntaxKind.SourceFile:
|
||||
return undefined;
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
// If in walking up the tree, we hit a a nested namespace declaration,
|
||||
// then we must be somewhere within a dotted namespace name; however we don't
|
||||
// want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'.
|
||||
if (commentOwner.parent.kind === SyntaxKind.ModuleDeclaration) {
|
||||
return undefined;
|
||||
}
|
||||
break findOwner;
|
||||
}
|
||||
}
|
||||
|
||||
if (!commentOwner || commentOwner.getStart() < position) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parameters = getParametersForJsDocOwningNode(commentOwner);
|
||||
const posLineAndChar = sourceFile.getLineAndCharacterOfPosition(position);
|
||||
const lineStart = sourceFile.getLineStarts()[posLineAndChar.line];
|
||||
|
||||
const indentationStr = sourceFile.text.substr(lineStart, posLineAndChar.character);
|
||||
|
||||
let docParams = "";
|
||||
for (let i = 0, numParams = parameters.length; i < numParams; i++) {
|
||||
const currentName = parameters[i].name;
|
||||
const paramName = currentName.kind === SyntaxKind.Identifier ?
|
||||
(<Identifier>currentName).text :
|
||||
"param" + i;
|
||||
|
||||
docParams += `${indentationStr} * @param ${paramName}${newLine}`;
|
||||
}
|
||||
|
||||
// A doc comment consists of the following
|
||||
// * The opening comment line
|
||||
// * the first line (without a param) for the object's untagged info (this is also where the caret ends up)
|
||||
// * the '@param'-tagged lines
|
||||
// * TODO: other tags.
|
||||
// * the closing comment line
|
||||
// * if the caret was directly in front of the object, then we add an extra line and indentation.
|
||||
const preamble = "/**" + newLine +
|
||||
indentationStr + " * ";
|
||||
const result =
|
||||
preamble + newLine +
|
||||
docParams +
|
||||
indentationStr + " */" +
|
||||
(tokenStart === position ? newLine + indentationStr : "");
|
||||
|
||||
return { newText: result, caretOffset: preamble.length };
|
||||
}
|
||||
|
||||
function getParametersForJsDocOwningNode(commentOwner: Node): ParameterDeclaration[] {
|
||||
if (isFunctionLike(commentOwner)) {
|
||||
return commentOwner.parameters;
|
||||
}
|
||||
|
||||
if (commentOwner.kind === SyntaxKind.VariableStatement) {
|
||||
const varStatement = <VariableStatement>commentOwner;
|
||||
const varDeclarations = varStatement.declarationList.declarations;
|
||||
|
||||
if (varDeclarations.length === 1 && varDeclarations[0].initializer) {
|
||||
return getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer);
|
||||
}
|
||||
}
|
||||
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Digs into an an initializer or RHS operand of an assignment operation
|
||||
* to get the parameters of an apt signature corresponding to a
|
||||
* function expression or a class expression.
|
||||
*
|
||||
* @param rightHandSide the expression which may contain an appropriate set of parameters
|
||||
* @returns the parameters of a signature found on the RHS if one exists; otherwise 'emptyArray'.
|
||||
*/
|
||||
function getParametersFromRightHandSideOfAssignment(rightHandSide: Expression): ParameterDeclaration[] {
|
||||
while (rightHandSide.kind === SyntaxKind.ParenthesizedExpression) {
|
||||
rightHandSide = (<ParenthesizedExpression>rightHandSide).expression;
|
||||
}
|
||||
|
||||
switch (rightHandSide.kind) {
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
return (<FunctionExpression>rightHandSide).parameters;
|
||||
case SyntaxKind.ClassExpression:
|
||||
for (const member of (<ClassExpression>rightHandSide).members) {
|
||||
if (member.kind === SyntaxKind.Constructor) {
|
||||
return (<ConstructorDeclaration>member).parameters;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return emptyArray;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace ts.NavigateTo {
|
||||
type RawNavigateToItem = { name: string; fileName: string; matchKind: PatternMatchKind; isCaseSensitive: boolean; declaration: Declaration };
|
||||
|
||||
export function getNavigateToItems(program: Program, checker: TypeChecker, cancellationToken: CancellationToken, searchValue: string, maxResultCount: number): NavigateToItem[] {
|
||||
export function getNavigateToItems(sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken, searchValue: string, maxResultCount: number): NavigateToItem[] {
|
||||
const patternMatcher = createPatternMatcher(searchValue);
|
||||
let rawItems: RawNavigateToItem[] = [];
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace ts.NavigateTo {
|
||||
const baseSensitivity: Intl.CollatorOptions = { sensitivity: "base" };
|
||||
|
||||
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
|
||||
forEach(program.getSourceFiles(), sourceFile => {
|
||||
forEach(sourceFiles, sourceFile => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
const nameToDeclarations = sourceFile.getNamedDeclarations();
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
namespace ts {
|
||||
export function preProcessFile(sourceText: string, readImportFiles = true, detectJavaScriptImports = false): PreProcessedFileInfo {
|
||||
const referencedFiles: FileReference[] = [];
|
||||
const typeReferenceDirectives: FileReference[] = [];
|
||||
const importedFiles: FileReference[] = [];
|
||||
let ambientExternalModules: { ref: FileReference, depth: number }[];
|
||||
let isNoDefaultLib = false;
|
||||
let braceNesting = 0;
|
||||
// assume that text represent an external module if it contains at least one top level import/export
|
||||
// ambient modules that are found inside external modules are interpreted as module augmentations
|
||||
let externalModule = false;
|
||||
|
||||
function nextToken() {
|
||||
const token = scanner.scan();
|
||||
if (token === SyntaxKind.OpenBraceToken) {
|
||||
braceNesting++;
|
||||
}
|
||||
else if (token === SyntaxKind.CloseBraceToken) {
|
||||
braceNesting--;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
function processTripleSlashDirectives(): void {
|
||||
const commentRanges = getLeadingCommentRanges(sourceText, 0);
|
||||
forEach(commentRanges, commentRange => {
|
||||
const comment = sourceText.substring(commentRange.pos, commentRange.end);
|
||||
const referencePathMatchResult = getFileReferenceFromReferencePath(comment, commentRange);
|
||||
if (referencePathMatchResult) {
|
||||
isNoDefaultLib = referencePathMatchResult.isNoDefaultLib;
|
||||
const fileReference = referencePathMatchResult.fileReference;
|
||||
if (fileReference) {
|
||||
const collection = referencePathMatchResult.isTypeReferenceDirective
|
||||
? typeReferenceDirectives
|
||||
: referencedFiles;
|
||||
|
||||
collection.push(fileReference);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getFileReference() {
|
||||
const file = scanner.getTokenValue();
|
||||
const pos = scanner.getTokenPos();
|
||||
return {
|
||||
fileName: file,
|
||||
pos: pos,
|
||||
end: pos + file.length
|
||||
};
|
||||
}
|
||||
|
||||
function recordAmbientExternalModule(): void {
|
||||
if (!ambientExternalModules) {
|
||||
ambientExternalModules = [];
|
||||
}
|
||||
ambientExternalModules.push({ ref: getFileReference(), depth: braceNesting });
|
||||
}
|
||||
|
||||
function recordModuleName() {
|
||||
importedFiles.push(getFileReference());
|
||||
|
||||
markAsExternalModuleIfTopLevel();
|
||||
}
|
||||
|
||||
function markAsExternalModuleIfTopLevel() {
|
||||
if (braceNesting === 0) {
|
||||
externalModule = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if at least one token was consumed from the stream
|
||||
*/
|
||||
function tryConsumeDeclare(): boolean {
|
||||
let token = scanner.getToken();
|
||||
if (token === SyntaxKind.DeclareKeyword) {
|
||||
// declare module "mod"
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.ModuleKeyword) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
recordAmbientExternalModule();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if at least one token was consumed from the stream
|
||||
*/
|
||||
function tryConsumeImport(): boolean {
|
||||
let token = scanner.getToken();
|
||||
if (token === SyntaxKind.ImportKeyword) {
|
||||
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
// import "mod";
|
||||
recordModuleName();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (token === SyntaxKind.Identifier || isKeyword(token)) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.FromKeyword) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
// import d from "mod";
|
||||
recordModuleName();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (token === SyntaxKind.EqualsToken) {
|
||||
if (tryConsumeRequireCall(/*skipCurrentToken*/ true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (token === SyntaxKind.CommaToken) {
|
||||
// consume comma and keep going
|
||||
token = nextToken();
|
||||
}
|
||||
else {
|
||||
// unknown syntax
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (token === SyntaxKind.OpenBraceToken) {
|
||||
token = nextToken();
|
||||
// consume "{ a as B, c, d as D}" clauses
|
||||
// make sure that it stops on EOF
|
||||
while (token !== SyntaxKind.CloseBraceToken && token !== SyntaxKind.EndOfFileToken) {
|
||||
token = nextToken();
|
||||
}
|
||||
|
||||
if (token === SyntaxKind.CloseBraceToken) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.FromKeyword) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
// import {a as A} from "mod";
|
||||
// import d, {a, b as B} from "mod"
|
||||
recordModuleName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (token === SyntaxKind.AsteriskToken) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.AsKeyword) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.Identifier || isKeyword(token)) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.FromKeyword) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
// import * as NS from "mod"
|
||||
// import d, * as NS from "mod"
|
||||
recordModuleName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function tryConsumeExport(): boolean {
|
||||
let token = scanner.getToken();
|
||||
if (token === SyntaxKind.ExportKeyword) {
|
||||
markAsExternalModuleIfTopLevel();
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.OpenBraceToken) {
|
||||
token = nextToken();
|
||||
// consume "{ a as B, c, d as D}" clauses
|
||||
// make sure it stops on EOF
|
||||
while (token !== SyntaxKind.CloseBraceToken && token !== SyntaxKind.EndOfFileToken) {
|
||||
token = nextToken();
|
||||
}
|
||||
|
||||
if (token === SyntaxKind.CloseBraceToken) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.FromKeyword) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
// export {a as A} from "mod";
|
||||
// export {a, b as B} from "mod"
|
||||
recordModuleName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (token === SyntaxKind.AsteriskToken) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.FromKeyword) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
// export * from "mod"
|
||||
recordModuleName();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (token === SyntaxKind.ImportKeyword) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.Identifier || isKeyword(token)) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.EqualsToken) {
|
||||
if (tryConsumeRequireCall(/*skipCurrentToken*/ true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function tryConsumeRequireCall(skipCurrentToken: boolean): boolean {
|
||||
let token = skipCurrentToken ? nextToken() : scanner.getToken();
|
||||
if (token === SyntaxKind.RequireKeyword) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.OpenParenToken) {
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
// require("mod");
|
||||
recordModuleName();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function tryConsumeDefine(): boolean {
|
||||
let token = scanner.getToken();
|
||||
if (token === SyntaxKind.Identifier && scanner.getTokenValue() === "define") {
|
||||
token = nextToken();
|
||||
if (token !== SyntaxKind.OpenParenToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
// looks like define ("modname", ... - skip string literal and comma
|
||||
token = nextToken();
|
||||
if (token === SyntaxKind.CommaToken) {
|
||||
token = nextToken();
|
||||
}
|
||||
else {
|
||||
// unexpected token
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// should be start of dependency list
|
||||
if (token !== SyntaxKind.OpenBracketToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// skip open bracket
|
||||
token = nextToken();
|
||||
let i = 0;
|
||||
// scan until ']' or EOF
|
||||
while (token !== SyntaxKind.CloseBracketToken && token !== SyntaxKind.EndOfFileToken) {
|
||||
// record string literals as module names
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
recordModuleName();
|
||||
i++;
|
||||
}
|
||||
|
||||
token = nextToken();
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function processImports(): void {
|
||||
scanner.setText(sourceText);
|
||||
nextToken();
|
||||
// Look for:
|
||||
// import "mod";
|
||||
// import d from "mod"
|
||||
// import {a as A } from "mod";
|
||||
// import * as NS from "mod"
|
||||
// import d, {a, b as B} from "mod"
|
||||
// import i = require("mod");
|
||||
//
|
||||
// export * from "mod"
|
||||
// export {a as b} from "mod"
|
||||
// export import i = require("mod")
|
||||
// (for JavaScript files) require("mod")
|
||||
|
||||
while (true) {
|
||||
if (scanner.getToken() === SyntaxKind.EndOfFileToken) {
|
||||
break;
|
||||
}
|
||||
|
||||
// check if at least one of alternative have moved scanner forward
|
||||
if (tryConsumeDeclare() ||
|
||||
tryConsumeImport() ||
|
||||
tryConsumeExport() ||
|
||||
(detectJavaScriptImports && (tryConsumeRequireCall(/*skipCurrentToken*/ false) || tryConsumeDefine()))) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
nextToken();
|
||||
}
|
||||
}
|
||||
|
||||
scanner.setText(undefined);
|
||||
}
|
||||
|
||||
if (readImportFiles) {
|
||||
processImports();
|
||||
}
|
||||
processTripleSlashDirectives();
|
||||
if (externalModule) {
|
||||
// for external modules module all nested ambient modules are augmentations
|
||||
if (ambientExternalModules) {
|
||||
// move all detected ambient modules to imported files since they need to be resolved
|
||||
for (const decl of ambientExternalModules) {
|
||||
importedFiles.push(decl.ref);
|
||||
}
|
||||
}
|
||||
return { referencedFiles, typeReferenceDirectives, importedFiles, isLibFile: isNoDefaultLib, ambientExternalModules: undefined };
|
||||
}
|
||||
else {
|
||||
// for global scripts ambient modules still can have augmentations - look for ambient modules with depth > 0
|
||||
let ambientModuleNames: string[];
|
||||
if (ambientExternalModules) {
|
||||
for (const decl of ambientExternalModules) {
|
||||
if (decl.depth === 0) {
|
||||
if (!ambientModuleNames) {
|
||||
ambientModuleNames = [];
|
||||
}
|
||||
ambientModuleNames.push(decl.ref.fileName);
|
||||
}
|
||||
else {
|
||||
importedFiles.push(decl.ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
return { referencedFiles, typeReferenceDirectives, importedFiles, isLibFile: isNoDefaultLib, ambientExternalModules: ambientModuleNames };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/* @internal */
|
||||
namespace ts.Rename {
|
||||
export function getRenameInfo(typeChecker: TypeChecker, defaultLibFileName: string, getCanonicalFileName: (fileName: string) => string, sourceFile: SourceFile, position: number): RenameInfo {
|
||||
const canonicalDefaultLibName = getCanonicalFileName(ts.normalizePath(defaultLibFileName));
|
||||
|
||||
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
|
||||
if (node) {
|
||||
if (node.kind === SyntaxKind.Identifier ||
|
||||
node.kind === SyntaxKind.StringLiteral ||
|
||||
isLiteralNameOfPropertyDeclarationOrIndexAccess(node) ||
|
||||
isThis(node)) {
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
|
||||
// Only allow a symbol to be renamed if it actually has at least one declaration.
|
||||
if (symbol) {
|
||||
const declarations = symbol.getDeclarations();
|
||||
if (declarations && declarations.length > 0) {
|
||||
// Disallow rename for elements that are defined in the standard TypeScript library.
|
||||
if (forEach(declarations, isDefinedInLibraryFile)) {
|
||||
return getRenameInfoError(getLocaleSpecificMessage(Diagnostics.You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library));
|
||||
}
|
||||
|
||||
const displayName = stripQuotes(getDeclaredName(typeChecker, symbol, node));
|
||||
const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, node);
|
||||
if (kind) {
|
||||
return {
|
||||
canRename: true,
|
||||
kind,
|
||||
displayName,
|
||||
localizedErrorMessage: undefined,
|
||||
fullDisplayName: typeChecker.getFullyQualifiedName(symbol),
|
||||
kindModifiers: SymbolDisplay.getSymbolModifiers(symbol),
|
||||
triggerSpan: createTriggerSpanForNode(node, sourceFile)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (node.kind === SyntaxKind.StringLiteral) {
|
||||
const type = getStringLiteralTypeForNode(<StringLiteral>node, typeChecker);
|
||||
if (type) {
|
||||
if (isDefinedInLibraryFile(node)) {
|
||||
return getRenameInfoError(getLocaleSpecificMessage(Diagnostics.You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library));
|
||||
}
|
||||
else {
|
||||
const displayName = stripQuotes(type.text);
|
||||
return {
|
||||
canRename: true,
|
||||
kind: ScriptElementKind.variableElement,
|
||||
displayName,
|
||||
localizedErrorMessage: undefined,
|
||||
fullDisplayName: displayName,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
triggerSpan: createTriggerSpanForNode(node, sourceFile)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getRenameInfoError(getLocaleSpecificMessage(Diagnostics.You_cannot_rename_this_element));
|
||||
|
||||
function getRenameInfoError(localizedErrorMessage: string): RenameInfo {
|
||||
return {
|
||||
canRename: false,
|
||||
localizedErrorMessage: localizedErrorMessage,
|
||||
displayName: undefined,
|
||||
fullDisplayName: undefined,
|
||||
kind: undefined,
|
||||
kindModifiers: undefined,
|
||||
triggerSpan: undefined
|
||||
};
|
||||
}
|
||||
|
||||
function isDefinedInLibraryFile(declaration: Node) {
|
||||
if (defaultLibFileName) {
|
||||
const sourceFile = declaration.getSourceFile();
|
||||
const canonicalName = getCanonicalFileName(ts.normalizePath(sourceFile.fileName));
|
||||
if (canonicalName === canonicalDefaultLibName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function createTriggerSpanForNode(node: Node, sourceFile: SourceFile) {
|
||||
let start = node.getStart(sourceFile);
|
||||
let width = node.getWidth(sourceFile);
|
||||
if (node.kind === SyntaxKind.StringLiteral) {
|
||||
// Exclude the quotes
|
||||
start += 1;
|
||||
width -= 2;
|
||||
}
|
||||
return createTextSpan(start, width);
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
-7203
File diff suppressed because it is too large
Load Diff
@@ -215,7 +215,7 @@ namespace ts {
|
||||
* Returns a JSON-encoded value of the type:
|
||||
* { name: string; kind: string; kindModifiers: string; containerName: string; containerKind: string; matchKind: string; fileName: string; textSpan: { start: number; length: number}; } [] = [];
|
||||
*/
|
||||
getNavigateToItems(searchValue: string, maxResultCount?: number): string;
|
||||
getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string): string;
|
||||
|
||||
/**
|
||||
* Returns a JSON-encoded value of the type:
|
||||
@@ -949,10 +949,10 @@ namespace ts {
|
||||
/// NAVIGATE TO
|
||||
|
||||
/** Return a list of symbols that are interesting to navigate to */
|
||||
public getNavigateToItems(searchValue: string, maxResultCount?: number): string {
|
||||
public getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string): string {
|
||||
return this.forwardJSONCall(
|
||||
`getNavigateToItems('${searchValue}', ${maxResultCount})`,
|
||||
() => this.languageService.getNavigateToItems(searchValue, maxResultCount)
|
||||
`getNavigateToItems('${searchValue}', ${maxResultCount}, ${fileName})`,
|
||||
() => this.languageService.getNavigateToItems(searchValue, maxResultCount, fileName)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,523 @@
|
||||
/* @internal */
|
||||
namespace ts.SymbolDisplay {
|
||||
// TODO(drosen): use contextual SemanticMeaning.
|
||||
export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node): string {
|
||||
const flags = symbol.getFlags();
|
||||
|
||||
if (flags & SymbolFlags.Class) return getDeclarationOfKind(symbol, SyntaxKind.ClassExpression) ?
|
||||
ScriptElementKind.localClassElement : ScriptElementKind.classElement;
|
||||
if (flags & SymbolFlags.Enum) return ScriptElementKind.enumElement;
|
||||
if (flags & SymbolFlags.TypeAlias) return ScriptElementKind.typeElement;
|
||||
if (flags & SymbolFlags.Interface) return ScriptElementKind.interfaceElement;
|
||||
if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement;
|
||||
|
||||
const result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, flags, location);
|
||||
if (result === ScriptElementKind.unknown) {
|
||||
if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement;
|
||||
if (flags & SymbolFlags.EnumMember) return ScriptElementKind.variableElement;
|
||||
if (flags & SymbolFlags.Alias) return ScriptElementKind.alias;
|
||||
if (flags & SymbolFlags.Module) return ScriptElementKind.moduleElement;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker: TypeChecker, symbol: Symbol, flags: SymbolFlags, location: Node) {
|
||||
if (typeChecker.isUndefinedSymbol(symbol)) {
|
||||
return ScriptElementKind.variableElement;
|
||||
}
|
||||
if (typeChecker.isArgumentsSymbol(symbol)) {
|
||||
return ScriptElementKind.localVariableElement;
|
||||
}
|
||||
if (location.kind === SyntaxKind.ThisKeyword && isExpression(location)) {
|
||||
return ScriptElementKind.parameterElement;
|
||||
}
|
||||
if (flags & SymbolFlags.Variable) {
|
||||
if (isFirstDeclarationOfSymbolParameter(symbol)) {
|
||||
return ScriptElementKind.parameterElement;
|
||||
}
|
||||
else if (symbol.valueDeclaration && isConst(symbol.valueDeclaration)) {
|
||||
return ScriptElementKind.constElement;
|
||||
}
|
||||
else if (forEach(symbol.declarations, isLet)) {
|
||||
return ScriptElementKind.letElement;
|
||||
}
|
||||
return isLocalVariableOrFunction(symbol) ? ScriptElementKind.localVariableElement : ScriptElementKind.variableElement;
|
||||
}
|
||||
if (flags & SymbolFlags.Function) return isLocalVariableOrFunction(symbol) ? ScriptElementKind.localFunctionElement : ScriptElementKind.functionElement;
|
||||
if (flags & SymbolFlags.GetAccessor) return ScriptElementKind.memberGetAccessorElement;
|
||||
if (flags & SymbolFlags.SetAccessor) return ScriptElementKind.memberSetAccessorElement;
|
||||
if (flags & SymbolFlags.Method) return ScriptElementKind.memberFunctionElement;
|
||||
if (flags & SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement;
|
||||
|
||||
if (flags & SymbolFlags.Property) {
|
||||
if (flags & SymbolFlags.SyntheticProperty) {
|
||||
// If union property is result of union of non method (property/accessors/variables), it is labeled as property
|
||||
const unionPropertyKind = forEach(typeChecker.getRootSymbols(symbol), rootSymbol => {
|
||||
const rootSymbolFlags = rootSymbol.getFlags();
|
||||
if (rootSymbolFlags & (SymbolFlags.PropertyOrAccessor | SymbolFlags.Variable)) {
|
||||
return ScriptElementKind.memberVariableElement;
|
||||
}
|
||||
Debug.assert(!!(rootSymbolFlags & SymbolFlags.Method));
|
||||
});
|
||||
if (!unionPropertyKind) {
|
||||
// If this was union of all methods,
|
||||
// make sure it has call signatures before we can label it as method
|
||||
const typeOfUnionProperty = typeChecker.getTypeOfSymbolAtLocation(symbol, location);
|
||||
if (typeOfUnionProperty.getCallSignatures().length) {
|
||||
return ScriptElementKind.memberFunctionElement;
|
||||
}
|
||||
return ScriptElementKind.memberVariableElement;
|
||||
}
|
||||
return unionPropertyKind;
|
||||
}
|
||||
return ScriptElementKind.memberVariableElement;
|
||||
}
|
||||
|
||||
return ScriptElementKind.unknown;
|
||||
}
|
||||
|
||||
export function getSymbolModifiers(symbol: Symbol): string {
|
||||
return symbol && symbol.declarations && symbol.declarations.length > 0
|
||||
? getNodeModifiers(symbol.declarations[0])
|
||||
: ScriptElementKindModifier.none;
|
||||
}
|
||||
|
||||
// TODO(drosen): Currently completion entry details passes the SemanticMeaning.All instead of using semanticMeaning of location
|
||||
export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: TypeChecker, symbol: Symbol, sourceFile: SourceFile, enclosingDeclaration: Node,
|
||||
location: Node, semanticMeaning = getMeaningFromLocation(location)) {
|
||||
|
||||
const displayParts: SymbolDisplayPart[] = [];
|
||||
let documentation: SymbolDisplayPart[];
|
||||
const symbolFlags = symbol.flags;
|
||||
let symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, symbolFlags, location);
|
||||
let hasAddedSymbolInfo: boolean;
|
||||
const isThisExpression = location.kind === SyntaxKind.ThisKeyword && isExpression(location);
|
||||
let type: Type;
|
||||
|
||||
// Class at constructor site need to be shown as constructor apart from property,method, vars
|
||||
if (symbolKind !== ScriptElementKind.unknown || symbolFlags & SymbolFlags.Class || symbolFlags & SymbolFlags.Alias) {
|
||||
// If it is accessor they are allowed only if location is at name of the accessor
|
||||
if (symbolKind === ScriptElementKind.memberGetAccessorElement || symbolKind === ScriptElementKind.memberSetAccessorElement) {
|
||||
symbolKind = ScriptElementKind.memberVariableElement;
|
||||
}
|
||||
|
||||
let signature: Signature;
|
||||
type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol, location);
|
||||
if (type) {
|
||||
if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
const right = (<PropertyAccessExpression>location.parent).name;
|
||||
// Either the location is on the right of a property access, or on the left and the right is missing
|
||||
if (right === location || (right && right.getFullWidth() === 0)) {
|
||||
location = location.parent;
|
||||
}
|
||||
}
|
||||
|
||||
// try get the call/construct signature from the type if it matches
|
||||
let callExpression: CallExpression;
|
||||
if (location.kind === SyntaxKind.CallExpression || location.kind === SyntaxKind.NewExpression) {
|
||||
callExpression = <CallExpression>location;
|
||||
}
|
||||
else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) {
|
||||
callExpression = <CallExpression>location.parent;
|
||||
}
|
||||
|
||||
if (callExpression) {
|
||||
const candidateSignatures: Signature[] = [];
|
||||
signature = typeChecker.getResolvedSignature(callExpression, candidateSignatures);
|
||||
if (!signature && candidateSignatures.length) {
|
||||
// Use the first candidate:
|
||||
signature = candidateSignatures[0];
|
||||
}
|
||||
|
||||
const useConstructSignatures = callExpression.kind === SyntaxKind.NewExpression || callExpression.expression.kind === SyntaxKind.SuperKeyword;
|
||||
const allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures();
|
||||
|
||||
if (!contains(allSignatures, signature.target) && !contains(allSignatures, signature)) {
|
||||
// Get the first signature if there is one -- allSignatures may contain
|
||||
// either the original signature or its target, so check for either
|
||||
signature = allSignatures.length ? allSignatures[0] : undefined;
|
||||
}
|
||||
|
||||
if (signature) {
|
||||
if (useConstructSignatures && (symbolFlags & SymbolFlags.Class)) {
|
||||
// Constructor
|
||||
symbolKind = ScriptElementKind.constructorImplementationElement;
|
||||
addPrefixForAnyFunctionOrVar(type.symbol, symbolKind);
|
||||
}
|
||||
else if (symbolFlags & SymbolFlags.Alias) {
|
||||
symbolKind = ScriptElementKind.alias;
|
||||
pushTypePart(symbolKind);
|
||||
displayParts.push(spacePart());
|
||||
if (useConstructSignatures) {
|
||||
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
|
||||
displayParts.push(spacePart());
|
||||
}
|
||||
addFullSymbolName(symbol);
|
||||
}
|
||||
else {
|
||||
addPrefixForAnyFunctionOrVar(symbol, symbolKind);
|
||||
}
|
||||
|
||||
switch (symbolKind) {
|
||||
case ScriptElementKind.memberVariableElement:
|
||||
case ScriptElementKind.variableElement:
|
||||
case ScriptElementKind.constElement:
|
||||
case ScriptElementKind.letElement:
|
||||
case ScriptElementKind.parameterElement:
|
||||
case ScriptElementKind.localVariableElement:
|
||||
// If it is call or construct signature of lambda's write type name
|
||||
displayParts.push(punctuationPart(SyntaxKind.ColonToken));
|
||||
displayParts.push(spacePart());
|
||||
if (useConstructSignatures) {
|
||||
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
|
||||
displayParts.push(spacePart());
|
||||
}
|
||||
if (!(type.flags & TypeFlags.Anonymous) && type.symbol) {
|
||||
addRange(displayParts, symbolToDisplayParts(typeChecker, type.symbol, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments));
|
||||
}
|
||||
addSignatureDisplayParts(signature, allSignatures, TypeFormatFlags.WriteArrowStyleSignature);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Just signature
|
||||
addSignatureDisplayParts(signature, allSignatures);
|
||||
}
|
||||
hasAddedSymbolInfo = true;
|
||||
}
|
||||
}
|
||||
else if ((isNameOfFunctionDeclaration(location) && !(symbol.flags & SymbolFlags.Accessor)) || // name of function declaration
|
||||
(location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration
|
||||
// get the signature from the declaration and write it
|
||||
const functionDeclaration = <FunctionLikeDeclaration>location.parent;
|
||||
const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures();
|
||||
if (!typeChecker.isImplementationOfOverload(functionDeclaration)) {
|
||||
signature = typeChecker.getSignatureFromDeclaration(functionDeclaration);
|
||||
}
|
||||
else {
|
||||
signature = allSignatures[0];
|
||||
}
|
||||
|
||||
if (functionDeclaration.kind === SyntaxKind.Constructor) {
|
||||
// show (constructor) Type(...) signature
|
||||
symbolKind = ScriptElementKind.constructorImplementationElement;
|
||||
addPrefixForAnyFunctionOrVar(type.symbol, symbolKind);
|
||||
}
|
||||
else {
|
||||
// (function/method) symbol(..signature)
|
||||
addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature &&
|
||||
!(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind);
|
||||
}
|
||||
|
||||
addSignatureDisplayParts(signature, allSignatures);
|
||||
hasAddedSymbolInfo = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo && !isThisExpression) {
|
||||
if (getDeclarationOfKind(symbol, SyntaxKind.ClassExpression)) {
|
||||
// Special case for class expressions because we would like to indicate that
|
||||
// the class name is local to the class body (similar to function expression)
|
||||
// (local class) class <className>
|
||||
pushTypePart(ScriptElementKind.localClassElement);
|
||||
}
|
||||
else {
|
||||
// Class declaration has name which is not local.
|
||||
displayParts.push(keywordPart(SyntaxKind.ClassKeyword));
|
||||
}
|
||||
displayParts.push(spacePart());
|
||||
addFullSymbolName(symbol);
|
||||
writeTypeParametersOfSymbol(symbol, sourceFile);
|
||||
}
|
||||
if ((symbolFlags & SymbolFlags.Interface) && (semanticMeaning & SemanticMeaning.Type)) {
|
||||
addNewLineIfDisplayPartsExist();
|
||||
displayParts.push(keywordPart(SyntaxKind.InterfaceKeyword));
|
||||
displayParts.push(spacePart());
|
||||
addFullSymbolName(symbol);
|
||||
writeTypeParametersOfSymbol(symbol, sourceFile);
|
||||
}
|
||||
if (symbolFlags & SymbolFlags.TypeAlias) {
|
||||
addNewLineIfDisplayPartsExist();
|
||||
displayParts.push(keywordPart(SyntaxKind.TypeKeyword));
|
||||
displayParts.push(spacePart());
|
||||
addFullSymbolName(symbol);
|
||||
writeTypeParametersOfSymbol(symbol, sourceFile);
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(operatorPart(SyntaxKind.EqualsToken));
|
||||
displayParts.push(spacePart());
|
||||
addRange(displayParts, typeToDisplayParts(typeChecker, typeChecker.getDeclaredTypeOfSymbol(symbol), enclosingDeclaration, TypeFormatFlags.InTypeAlias));
|
||||
}
|
||||
if (symbolFlags & SymbolFlags.Enum) {
|
||||
addNewLineIfDisplayPartsExist();
|
||||
if (forEach(symbol.declarations, isConstEnumDeclaration)) {
|
||||
displayParts.push(keywordPart(SyntaxKind.ConstKeyword));
|
||||
displayParts.push(spacePart());
|
||||
}
|
||||
displayParts.push(keywordPart(SyntaxKind.EnumKeyword));
|
||||
displayParts.push(spacePart());
|
||||
addFullSymbolName(symbol);
|
||||
}
|
||||
if (symbolFlags & SymbolFlags.Module) {
|
||||
addNewLineIfDisplayPartsExist();
|
||||
const declaration = <ModuleDeclaration>getDeclarationOfKind(symbol, SyntaxKind.ModuleDeclaration);
|
||||
const isNamespace = declaration && declaration.name && declaration.name.kind === SyntaxKind.Identifier;
|
||||
displayParts.push(keywordPart(isNamespace ? SyntaxKind.NamespaceKeyword : SyntaxKind.ModuleKeyword));
|
||||
displayParts.push(spacePart());
|
||||
addFullSymbolName(symbol);
|
||||
}
|
||||
if ((symbolFlags & SymbolFlags.TypeParameter) && (semanticMeaning & SemanticMeaning.Type)) {
|
||||
addNewLineIfDisplayPartsExist();
|
||||
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
|
||||
displayParts.push(textPart("type parameter"));
|
||||
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
|
||||
displayParts.push(spacePart());
|
||||
addFullSymbolName(symbol);
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(keywordPart(SyntaxKind.InKeyword));
|
||||
displayParts.push(spacePart());
|
||||
if (symbol.parent) {
|
||||
// Class/Interface type parameter
|
||||
addFullSymbolName(symbol.parent, enclosingDeclaration);
|
||||
writeTypeParametersOfSymbol(symbol.parent, enclosingDeclaration);
|
||||
}
|
||||
else {
|
||||
// Method/function type parameter
|
||||
let declaration = <Node>getDeclarationOfKind(symbol, SyntaxKind.TypeParameter);
|
||||
Debug.assert(declaration !== undefined);
|
||||
declaration = declaration.parent;
|
||||
|
||||
if (declaration) {
|
||||
if (isFunctionLikeKind(declaration.kind)) {
|
||||
const signature = typeChecker.getSignatureFromDeclaration(<SignatureDeclaration>declaration);
|
||||
if (declaration.kind === SyntaxKind.ConstructSignature) {
|
||||
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
|
||||
displayParts.push(spacePart());
|
||||
}
|
||||
else if (declaration.kind !== SyntaxKind.CallSignature && (<SignatureDeclaration>declaration).name) {
|
||||
addFullSymbolName(declaration.symbol);
|
||||
}
|
||||
addRange(displayParts, signatureToDisplayParts(typeChecker, signature, sourceFile, TypeFormatFlags.WriteTypeArgumentsOfSignature));
|
||||
}
|
||||
else {
|
||||
// Type alias type parameter
|
||||
// For example
|
||||
// type list<T> = T[]; // Both T will go through same code path
|
||||
displayParts.push(keywordPart(SyntaxKind.TypeKeyword));
|
||||
displayParts.push(spacePart());
|
||||
addFullSymbolName(declaration.symbol);
|
||||
writeTypeParametersOfSymbol(declaration.symbol, sourceFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (symbolFlags & SymbolFlags.EnumMember) {
|
||||
addPrefixForAnyFunctionOrVar(symbol, "enum member");
|
||||
const declaration = symbol.declarations[0];
|
||||
if (declaration.kind === SyntaxKind.EnumMember) {
|
||||
const constantValue = typeChecker.getConstantValue(<EnumMember>declaration);
|
||||
if (constantValue !== undefined) {
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(operatorPart(SyntaxKind.EqualsToken));
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(displayPart(constantValue.toString(), SymbolDisplayPartKind.numericLiteral));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (symbolFlags & SymbolFlags.Alias) {
|
||||
addNewLineIfDisplayPartsExist();
|
||||
if (symbol.declarations[0].kind === SyntaxKind.NamespaceExportDeclaration) {
|
||||
displayParts.push(keywordPart(SyntaxKind.ExportKeyword));
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(keywordPart(SyntaxKind.NamespaceKeyword));
|
||||
}
|
||||
else {
|
||||
displayParts.push(keywordPart(SyntaxKind.ImportKeyword));
|
||||
}
|
||||
displayParts.push(spacePart());
|
||||
addFullSymbolName(symbol);
|
||||
ts.forEach(symbol.declarations, declaration => {
|
||||
if (declaration.kind === SyntaxKind.ImportEqualsDeclaration) {
|
||||
const importEqualsDeclaration = <ImportEqualsDeclaration>declaration;
|
||||
if (isExternalModuleImportEqualsDeclaration(importEqualsDeclaration)) {
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(operatorPart(SyntaxKind.EqualsToken));
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(keywordPart(SyntaxKind.RequireKeyword));
|
||||
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
|
||||
displayParts.push(displayPart(getTextOfNode(getExternalModuleImportEqualsDeclarationExpression(importEqualsDeclaration)), SymbolDisplayPartKind.stringLiteral));
|
||||
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
|
||||
}
|
||||
else {
|
||||
const internalAliasSymbol = typeChecker.getSymbolAtLocation(importEqualsDeclaration.moduleReference);
|
||||
if (internalAliasSymbol) {
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(operatorPart(SyntaxKind.EqualsToken));
|
||||
displayParts.push(spacePart());
|
||||
addFullSymbolName(internalAliasSymbol, enclosingDeclaration);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!hasAddedSymbolInfo) {
|
||||
if (symbolKind !== ScriptElementKind.unknown) {
|
||||
if (type) {
|
||||
if (isThisExpression) {
|
||||
addNewLineIfDisplayPartsExist();
|
||||
displayParts.push(keywordPart(SyntaxKind.ThisKeyword));
|
||||
}
|
||||
else {
|
||||
addPrefixForAnyFunctionOrVar(symbol, symbolKind);
|
||||
}
|
||||
|
||||
// For properties, variables and local vars: show the type
|
||||
if (symbolKind === ScriptElementKind.memberVariableElement ||
|
||||
symbolFlags & SymbolFlags.Variable ||
|
||||
symbolKind === ScriptElementKind.localVariableElement ||
|
||||
isThisExpression) {
|
||||
displayParts.push(punctuationPart(SyntaxKind.ColonToken));
|
||||
displayParts.push(spacePart());
|
||||
// If the type is type parameter, format it specially
|
||||
if (type.symbol && type.symbol.flags & SymbolFlags.TypeParameter) {
|
||||
const typeParameterParts = mapToDisplayParts(writer => {
|
||||
typeChecker.getSymbolDisplayBuilder().buildTypeParameterDisplay(<TypeParameter>type, writer, enclosingDeclaration);
|
||||
});
|
||||
addRange(displayParts, typeParameterParts);
|
||||
}
|
||||
else {
|
||||
addRange(displayParts, typeToDisplayParts(typeChecker, type, enclosingDeclaration));
|
||||
}
|
||||
}
|
||||
else if (symbolFlags & SymbolFlags.Function ||
|
||||
symbolFlags & SymbolFlags.Method ||
|
||||
symbolFlags & SymbolFlags.Constructor ||
|
||||
symbolFlags & SymbolFlags.Signature ||
|
||||
symbolFlags & SymbolFlags.Accessor ||
|
||||
symbolKind === ScriptElementKind.memberFunctionElement) {
|
||||
const allSignatures = type.getNonNullableType().getCallSignatures();
|
||||
addSignatureDisplayParts(allSignatures[0], allSignatures);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
symbolKind = getSymbolKind(typeChecker, symbol, location);
|
||||
}
|
||||
}
|
||||
|
||||
if (!documentation) {
|
||||
documentation = symbol.getDocumentationComment();
|
||||
if (documentation.length === 0 && symbol.flags & SymbolFlags.Property) {
|
||||
// For some special property access expressions like `experts.foo = foo` or `module.exports.foo = foo`
|
||||
// there documentation comments might be attached to the right hand side symbol of their declarations.
|
||||
// The pattern of such special property access is that the parent symbol is the symbol of the file.
|
||||
if (symbol.parent && forEach(symbol.parent.declarations, declaration => declaration.kind === SyntaxKind.SourceFile)) {
|
||||
for (const declaration of symbol.declarations) {
|
||||
if (!declaration.parent || declaration.parent.kind !== SyntaxKind.BinaryExpression) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const rhsSymbol = typeChecker.getSymbolAtLocation((<BinaryExpression>declaration.parent).right);
|
||||
if (!rhsSymbol) {
|
||||
continue;
|
||||
}
|
||||
|
||||
documentation = rhsSymbol.getDocumentationComment();
|
||||
if (documentation.length > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { displayParts, documentation, symbolKind };
|
||||
|
||||
function addNewLineIfDisplayPartsExist() {
|
||||
if (displayParts.length) {
|
||||
displayParts.push(lineBreakPart());
|
||||
}
|
||||
}
|
||||
|
||||
function addFullSymbolName(symbol: Symbol, enclosingDeclaration?: Node) {
|
||||
const fullSymbolDisplayParts = symbolToDisplayParts(typeChecker, symbol, enclosingDeclaration || sourceFile, /*meaning*/ undefined,
|
||||
SymbolFormatFlags.WriteTypeParametersOrArguments | SymbolFormatFlags.UseOnlyExternalAliasing);
|
||||
addRange(displayParts, fullSymbolDisplayParts);
|
||||
}
|
||||
|
||||
function addPrefixForAnyFunctionOrVar(symbol: Symbol, symbolKind: string) {
|
||||
addNewLineIfDisplayPartsExist();
|
||||
if (symbolKind) {
|
||||
pushTypePart(symbolKind);
|
||||
displayParts.push(spacePart());
|
||||
addFullSymbolName(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
function pushTypePart(symbolKind: string) {
|
||||
switch (symbolKind) {
|
||||
case ScriptElementKind.variableElement:
|
||||
case ScriptElementKind.functionElement:
|
||||
case ScriptElementKind.letElement:
|
||||
case ScriptElementKind.constElement:
|
||||
case ScriptElementKind.constructorImplementationElement:
|
||||
displayParts.push(textOrKeywordPart(symbolKind));
|
||||
return;
|
||||
default:
|
||||
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
|
||||
displayParts.push(textOrKeywordPart(symbolKind));
|
||||
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function addSignatureDisplayParts(signature: Signature, allSignatures: Signature[], flags?: TypeFormatFlags) {
|
||||
addRange(displayParts, signatureToDisplayParts(typeChecker, signature, enclosingDeclaration, flags | TypeFormatFlags.WriteTypeArgumentsOfSignature));
|
||||
if (allSignatures.length > 1) {
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
|
||||
displayParts.push(operatorPart(SyntaxKind.PlusToken));
|
||||
displayParts.push(displayPart((allSignatures.length - 1).toString(), SymbolDisplayPartKind.numericLiteral));
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(textPart(allSignatures.length === 2 ? "overload" : "overloads"));
|
||||
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
|
||||
}
|
||||
documentation = signature.getDocumentationComment();
|
||||
}
|
||||
|
||||
function writeTypeParametersOfSymbol(symbol: Symbol, enclosingDeclaration: Node) {
|
||||
const typeParameterParts = mapToDisplayParts(writer => {
|
||||
typeChecker.getSymbolDisplayBuilder().buildTypeParameterDisplayFromSymbol(symbol, writer, enclosingDeclaration);
|
||||
});
|
||||
addRange(displayParts, typeParameterParts);
|
||||
}
|
||||
}
|
||||
|
||||
function isLocalVariableOrFunction(symbol: Symbol) {
|
||||
if (symbol.parent) {
|
||||
return false; // This is exported symbol
|
||||
}
|
||||
|
||||
return ts.forEach(symbol.declarations, declaration => {
|
||||
// Function expressions are local
|
||||
if (declaration.kind === SyntaxKind.FunctionExpression) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (declaration.kind !== SyntaxKind.VariableDeclaration && declaration.kind !== SyntaxKind.FunctionDeclaration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the parent is not sourceFile or module block it is local variable
|
||||
for (let parent = declaration.parent; !isFunctionBlock(parent); parent = parent.parent) {
|
||||
// Reached source file or module block
|
||||
if (parent.kind === SyntaxKind.SourceFile || parent.kind === SyntaxKind.ModuleBlock) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// parent is in function block
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
namespace ts {
|
||||
export interface TranspileOptions {
|
||||
compilerOptions?: CompilerOptions;
|
||||
fileName?: string;
|
||||
reportDiagnostics?: boolean;
|
||||
moduleName?: string;
|
||||
renamedDependencies?: MapLike<string>;
|
||||
}
|
||||
|
||||
export interface TranspileOutput {
|
||||
outputText: string;
|
||||
diagnostics?: Diagnostic[];
|
||||
sourceMapText?: string;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function will compile source text from 'input' argument using specified compiler options.
|
||||
* If not options are provided - it will use a set of default compiler options.
|
||||
* Extra compiler options that will unconditionally be used by this function are:
|
||||
* - isolatedModules = true
|
||||
* - allowNonTsExtensions = true
|
||||
* - noLib = true
|
||||
* - noResolve = true
|
||||
*/
|
||||
export function transpileModule(input: string, transpileOptions: TranspileOptions): TranspileOutput {
|
||||
const diagnostics: Diagnostic[] = [];
|
||||
|
||||
const options: CompilerOptions = transpileOptions.compilerOptions ? fixupCompilerOptions(transpileOptions.compilerOptions, diagnostics) : getDefaultCompilerOptions();
|
||||
|
||||
options.isolatedModules = true;
|
||||
|
||||
// transpileModule does not write anything to disk so there is no need to verify that there are no conflicts between input and output paths.
|
||||
options.suppressOutputPathCheck = true;
|
||||
|
||||
// Filename can be non-ts file.
|
||||
options.allowNonTsExtensions = true;
|
||||
|
||||
// We are not returning a sourceFile for lib file when asked by the program,
|
||||
// so pass --noLib to avoid reporting a file not found error.
|
||||
options.noLib = true;
|
||||
|
||||
// Clear out other settings that would not be used in transpiling this module
|
||||
options.lib = undefined;
|
||||
options.types = undefined;
|
||||
options.noEmit = undefined;
|
||||
options.noEmitOnError = undefined;
|
||||
options.paths = undefined;
|
||||
options.rootDirs = undefined;
|
||||
options.declaration = undefined;
|
||||
options.declarationDir = undefined;
|
||||
options.out = undefined;
|
||||
options.outFile = undefined;
|
||||
|
||||
// We are not doing a full typecheck, we are not resolving the whole context,
|
||||
// so pass --noResolve to avoid reporting missing file errors.
|
||||
options.noResolve = true;
|
||||
|
||||
// if jsx is specified then treat file as .tsx
|
||||
const inputFileName = transpileOptions.fileName || (options.jsx ? "module.tsx" : "module.ts");
|
||||
const sourceFile = createSourceFile(inputFileName, input, options.target);
|
||||
if (transpileOptions.moduleName) {
|
||||
sourceFile.moduleName = transpileOptions.moduleName;
|
||||
}
|
||||
|
||||
if (transpileOptions.renamedDependencies) {
|
||||
sourceFile.renamedDependencies = createMap(transpileOptions.renamedDependencies);
|
||||
}
|
||||
|
||||
const newLine = getNewLineCharacter(options);
|
||||
|
||||
// Output
|
||||
let outputText: string;
|
||||
let sourceMapText: string;
|
||||
|
||||
// Create a compilerHost object to allow the compiler to read and write files
|
||||
const compilerHost: CompilerHost = {
|
||||
getSourceFile: (fileName, target) => fileName === normalizePath(inputFileName) ? sourceFile : undefined,
|
||||
writeFile: (name, text, writeByteOrderMark) => {
|
||||
if (fileExtensionIs(name, ".map")) {
|
||||
Debug.assert(sourceMapText === undefined, `Unexpected multiple source map outputs for the file '${name}'`);
|
||||
sourceMapText = text;
|
||||
}
|
||||
else {
|
||||
Debug.assert(outputText === undefined, `Unexpected multiple outputs for the file: '${name}'`);
|
||||
outputText = text;
|
||||
}
|
||||
},
|
||||
getDefaultLibFileName: () => "lib.d.ts",
|
||||
useCaseSensitiveFileNames: () => false,
|
||||
getCanonicalFileName: fileName => fileName,
|
||||
getCurrentDirectory: () => "",
|
||||
getNewLine: () => newLine,
|
||||
fileExists: (fileName): boolean => fileName === inputFileName,
|
||||
readFile: (fileName): string => "",
|
||||
directoryExists: directoryExists => true,
|
||||
getDirectories: (path: string) => []
|
||||
};
|
||||
|
||||
const program = createProgram([inputFileName], options, compilerHost);
|
||||
|
||||
if (transpileOptions.reportDiagnostics) {
|
||||
addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile));
|
||||
addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics());
|
||||
}
|
||||
// Emit
|
||||
program.emit();
|
||||
|
||||
Debug.assert(outputText !== undefined, "Output generation failed");
|
||||
|
||||
return { outputText, diagnostics, sourceMapText };
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a shortcut function for transpileModule - it accepts transpileOptions as parameters and returns only outputText part of the result.
|
||||
*/
|
||||
export function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, diagnostics?: Diagnostic[], moduleName?: string): string {
|
||||
const output = transpileModule(input, { compilerOptions, fileName, reportDiagnostics: !!diagnostics, moduleName });
|
||||
// addRange correctly handles cases when wither 'from' or 'to' argument is missing
|
||||
addRange(diagnostics, output.diagnostics);
|
||||
return output.outputText;
|
||||
}
|
||||
|
||||
let commandLineOptionsStringToEnum: CommandLineOptionOfCustomType[];
|
||||
|
||||
/** JS users may pass in string values for enum compiler options (such as ModuleKind), so convert. */
|
||||
function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions {
|
||||
// Lazily create this value to fix module loading errors.
|
||||
commandLineOptionsStringToEnum = commandLineOptionsStringToEnum || <CommandLineOptionOfCustomType[]>filter(optionDeclarations, o =>
|
||||
typeof o.type === "object" && !forEachProperty(o.type, v => typeof v !== "number"));
|
||||
|
||||
options = clone(options);
|
||||
|
||||
for (const opt of commandLineOptionsStringToEnum) {
|
||||
if (!hasProperty(options, opt.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = options[opt.name];
|
||||
// Value should be a key of opt.type
|
||||
if (typeof value === "string") {
|
||||
// If value is not a string, this will fail
|
||||
options[opt.name] = parseCustomTypeOption(opt, value, diagnostics);
|
||||
}
|
||||
else {
|
||||
if (!forEachProperty(opt.type, v => v === value)) {
|
||||
// Supplied value isn't a valid enum value.
|
||||
diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
@@ -40,17 +40,29 @@
|
||||
"../compiler/program.ts",
|
||||
"../compiler/commandLineParser.ts",
|
||||
"../compiler/diagnosticInformationMap.generated.ts",
|
||||
"types.ts",
|
||||
"utilities.ts",
|
||||
"breakpoints.ts",
|
||||
"classifier.ts",
|
||||
"completions.ts",
|
||||
"documentHighlights.ts",
|
||||
"documentRegistry.ts",
|
||||
"findAllReferences.ts",
|
||||
"goToDefinition.ts",
|
||||
"goToImplementation.ts",
|
||||
"jsDoc.ts",
|
||||
"jsTyping.ts",
|
||||
"navigateTo.ts",
|
||||
"navigationBar.ts",
|
||||
"outliningElementsCollector.ts",
|
||||
"patternMatcher.ts",
|
||||
"preProcess.ts",
|
||||
"rename.ts",
|
||||
"services.ts",
|
||||
"transpile.ts",
|
||||
"shims.ts",
|
||||
"signatureHelp.ts",
|
||||
"types.ts",
|
||||
"utilities.ts",
|
||||
"jsTyping.ts",
|
||||
"symbolDisplay.ts",
|
||||
"formatting/formatting.ts",
|
||||
"formatting/formattingContext.ts",
|
||||
"formatting/formattingRequestKind.ts",
|
||||
|
||||
+3
-94
@@ -194,6 +194,7 @@ namespace ts {
|
||||
|
||||
getCompletionsAtPosition(fileName: string, position: number): CompletionInfo;
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails;
|
||||
getCompletionEntrySymbol(fileName: string, position: number, entryName: string): Symbol;
|
||||
|
||||
getQuickInfoAtPosition(fileName: string, position: number): QuickInfo;
|
||||
|
||||
@@ -217,7 +218,7 @@ namespace ts {
|
||||
/** @deprecated */
|
||||
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
|
||||
|
||||
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
|
||||
getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string): NavigateToItem[];
|
||||
getNavigationBarItems(fileName: string): NavigationBarItem[];
|
||||
|
||||
getOutliningSpans(fileName: string): OutliningSpan[];
|
||||
@@ -358,6 +359,7 @@ namespace ts {
|
||||
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces?: boolean;
|
||||
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: boolean;
|
||||
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean;
|
||||
InsertSpaceAfterTypeAssertion?: boolean;
|
||||
PlaceOpenBraceOnNewLineForFunctions: boolean;
|
||||
PlaceOpenBraceOnNewLineForControlBlocks: boolean;
|
||||
[s: string]: boolean | number | string | undefined;
|
||||
@@ -581,99 +583,6 @@ namespace ts {
|
||||
getEncodedLexicalClassifications(text: string, endOfLineState: EndOfLineState, syntacticClassifierAbsent: boolean): Classifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* The document registry represents a store of SourceFile objects that can be shared between
|
||||
* multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST)
|
||||
* of files in the context.
|
||||
* SourceFile objects account for most of the memory usage by the language service. Sharing
|
||||
* the same DocumentRegistry instance between different instances of LanguageService allow
|
||||
* for more efficient memory utilization since all projects will share at least the library
|
||||
* file (lib.d.ts).
|
||||
*
|
||||
* A more advanced use of the document registry is to serialize sourceFile objects to disk
|
||||
* and re-hydrate them when needed.
|
||||
*
|
||||
* To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it
|
||||
* to all subsequent createLanguageService calls.
|
||||
*/
|
||||
export interface DocumentRegistry {
|
||||
/**
|
||||
* Request a stored SourceFile with a given fileName and compilationSettings.
|
||||
* The first call to acquire will call createLanguageServiceSourceFile to generate
|
||||
* the SourceFile if was not found in the registry.
|
||||
*
|
||||
* @param fileName The name of the file requested
|
||||
* @param compilationSettings Some compilation settings like target affects the
|
||||
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
|
||||
* multiple copies of the same file for different compilation settings.
|
||||
* @parm scriptSnapshot Text of the file. Only used if the file was not found
|
||||
* in the registry and a new one was created.
|
||||
* @parm version Current version of the file. Only used if the file was not found
|
||||
* in the registry and a new one was created.
|
||||
*/
|
||||
acquireDocument(
|
||||
fileName: string,
|
||||
compilationSettings: CompilerOptions,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
acquireDocumentWithKey(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
/**
|
||||
* Request an updated version of an already existing SourceFile with a given fileName
|
||||
* and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile
|
||||
* to get an updated SourceFile.
|
||||
*
|
||||
* @param fileName The name of the file requested
|
||||
* @param compilationSettings Some compilation settings like target affects the
|
||||
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
|
||||
* multiple copies of the same file for different compilation settings.
|
||||
* @param scriptSnapshot Text of the file.
|
||||
* @param version Current version of the file.
|
||||
*/
|
||||
updateDocument(
|
||||
fileName: string,
|
||||
compilationSettings: CompilerOptions,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
updateDocumentWithKey(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey;
|
||||
/**
|
||||
* Informs the DocumentRegistry that a file is not needed any longer.
|
||||
*
|
||||
* Note: It is not allowed to call release on a SourceFile that was not acquired from
|
||||
* this registry originally.
|
||||
*
|
||||
* @param fileName The name of the file to be released
|
||||
* @param compilationSettings The compilation settings used to acquire the file
|
||||
*/
|
||||
releaseDocument(fileName: string, compilationSettings: CompilerOptions): void;
|
||||
|
||||
releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void;
|
||||
|
||||
reportStats(): string;
|
||||
}
|
||||
|
||||
export type DocumentRegistryBucketKey = string & { __bucketKey: any };
|
||||
|
||||
// TODO: move these to enums
|
||||
export namespace ScriptElementKind {
|
||||
export const unknown = "";
|
||||
|
||||
@@ -1,6 +1,387 @@
|
||||
// These utilities are common to multiple language service features.
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export const scanner: Scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ true);
|
||||
export const emptyArray: any[] = [];
|
||||
|
||||
export const enum SemanticMeaning {
|
||||
None = 0x0,
|
||||
Value = 0x1,
|
||||
Type = 0x2,
|
||||
Namespace = 0x4,
|
||||
All = Value | Type | Namespace
|
||||
}
|
||||
|
||||
export function getMeaningFromDeclaration(node: Node): SemanticMeaning {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Parameter:
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.BindingElement:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.ShorthandPropertyAssignment:
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.CatchClause:
|
||||
return SemanticMeaning.Value;
|
||||
|
||||
case SyntaxKind.TypeParameter:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.TypeLiteral:
|
||||
return SemanticMeaning.Type;
|
||||
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type;
|
||||
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
if (isAmbientModule(<ModuleDeclaration>node)) {
|
||||
return SemanticMeaning.Namespace | SemanticMeaning.Value;
|
||||
}
|
||||
else if (getModuleInstanceState(node) === ModuleInstanceState.Instantiated) {
|
||||
return SemanticMeaning.Namespace | SemanticMeaning.Value;
|
||||
}
|
||||
else {
|
||||
return SemanticMeaning.Namespace;
|
||||
}
|
||||
|
||||
case SyntaxKind.NamedImports:
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
case SyntaxKind.ExportAssignment:
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
|
||||
|
||||
// An external module can be a Value
|
||||
case SyntaxKind.SourceFile:
|
||||
return SemanticMeaning.Namespace | SemanticMeaning.Value;
|
||||
}
|
||||
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
|
||||
}
|
||||
|
||||
export function getMeaningFromLocation(node: Node): SemanticMeaning {
|
||||
if (node.parent.kind === SyntaxKind.ExportAssignment) {
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
|
||||
}
|
||||
else if (isInRightSideOfImport(node)) {
|
||||
return getMeaningFromRightHandSideOfImportEquals(node);
|
||||
}
|
||||
else if (isDeclarationName(node)) {
|
||||
return getMeaningFromDeclaration(node.parent);
|
||||
}
|
||||
else if (isTypeReference(node)) {
|
||||
return SemanticMeaning.Type;
|
||||
}
|
||||
else if (isNamespaceReference(node)) {
|
||||
return SemanticMeaning.Namespace;
|
||||
}
|
||||
else {
|
||||
return SemanticMeaning.Value;
|
||||
}
|
||||
}
|
||||
|
||||
function getMeaningFromRightHandSideOfImportEquals(node: Node) {
|
||||
Debug.assert(node.kind === SyntaxKind.Identifier);
|
||||
|
||||
// import a = |b|; // Namespace
|
||||
// import a = |b.c|; // Value, type, namespace
|
||||
// import a = |b.c|.d; // Namespace
|
||||
|
||||
if (node.parent.kind === SyntaxKind.QualifiedName &&
|
||||
(<QualifiedName>node.parent).right === node &&
|
||||
node.parent.parent.kind === SyntaxKind.ImportEqualsDeclaration) {
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
|
||||
}
|
||||
return SemanticMeaning.Namespace;
|
||||
}
|
||||
|
||||
function isInRightSideOfImport(node: Node) {
|
||||
while (node.parent.kind === SyntaxKind.QualifiedName) {
|
||||
node = node.parent;
|
||||
}
|
||||
return isInternalModuleImportEqualsDeclaration(node.parent) && (<ImportEqualsDeclaration>node.parent).moduleReference === node;
|
||||
}
|
||||
|
||||
function isNamespaceReference(node: Node): boolean {
|
||||
return isQualifiedNameNamespaceReference(node) || isPropertyAccessNamespaceReference(node);
|
||||
}
|
||||
|
||||
function isQualifiedNameNamespaceReference(node: Node): boolean {
|
||||
let root = node;
|
||||
let isLastClause = true;
|
||||
if (root.parent.kind === SyntaxKind.QualifiedName) {
|
||||
while (root.parent && root.parent.kind === SyntaxKind.QualifiedName) {
|
||||
root = root.parent;
|
||||
}
|
||||
|
||||
isLastClause = (<QualifiedName>root).right === node;
|
||||
}
|
||||
|
||||
return root.parent.kind === SyntaxKind.TypeReference && !isLastClause;
|
||||
}
|
||||
|
||||
function isPropertyAccessNamespaceReference(node: Node): boolean {
|
||||
let root = node;
|
||||
let isLastClause = true;
|
||||
if (root.parent.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
while (root.parent && root.parent.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
root = root.parent;
|
||||
}
|
||||
|
||||
isLastClause = (<PropertyAccessExpression>root).name === node;
|
||||
}
|
||||
|
||||
if (!isLastClause && root.parent.kind === SyntaxKind.ExpressionWithTypeArguments && root.parent.parent.kind === SyntaxKind.HeritageClause) {
|
||||
const decl = root.parent.parent.parent;
|
||||
return (decl.kind === SyntaxKind.ClassDeclaration && (<HeritageClause>root.parent.parent).token === SyntaxKind.ImplementsKeyword) ||
|
||||
(decl.kind === SyntaxKind.InterfaceDeclaration && (<HeritageClause>root.parent.parent).token === SyntaxKind.ExtendsKeyword);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isTypeReference(node: Node): boolean {
|
||||
if (isRightSideOfQualifiedNameOrPropertyAccess(node)) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return node.parent.kind === SyntaxKind.TypeReference ||
|
||||
(node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isExpressionWithTypeArgumentsInClassExtendsClause(<ExpressionWithTypeArguments>node.parent)) ||
|
||||
(node.kind === SyntaxKind.ThisKeyword && !isPartOfExpression(node)) ||
|
||||
node.kind === SyntaxKind.ThisType;
|
||||
}
|
||||
|
||||
export function isCallExpressionTarget(node: Node): boolean {
|
||||
return isCallOrNewExpressionTarget(node, SyntaxKind.CallExpression);
|
||||
}
|
||||
|
||||
export function isNewExpressionTarget(node: Node): boolean {
|
||||
return isCallOrNewExpressionTarget(node, SyntaxKind.NewExpression);
|
||||
}
|
||||
|
||||
function isCallOrNewExpressionTarget(node: Node, kind: SyntaxKind) {
|
||||
const target = climbPastPropertyAccess(node);
|
||||
return target && target.parent && target.parent.kind === kind && (<CallExpression>target.parent).expression === target;
|
||||
}
|
||||
|
||||
export function climbPastPropertyAccess(node: Node) {
|
||||
return isRightSideOfPropertyAccess(node) ? node.parent : node;
|
||||
}
|
||||
|
||||
export function getTargetLabel(referenceNode: Node, labelName: string): Identifier {
|
||||
while (referenceNode) {
|
||||
if (referenceNode.kind === SyntaxKind.LabeledStatement && (<LabeledStatement>referenceNode).label.text === labelName) {
|
||||
return (<LabeledStatement>referenceNode).label;
|
||||
}
|
||||
referenceNode = referenceNode.parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function isJumpStatementTarget(node: Node): boolean {
|
||||
return node.kind === SyntaxKind.Identifier &&
|
||||
(node.parent.kind === SyntaxKind.BreakStatement || node.parent.kind === SyntaxKind.ContinueStatement) &&
|
||||
(<BreakOrContinueStatement>node.parent).label === node;
|
||||
}
|
||||
|
||||
function isLabelOfLabeledStatement(node: Node): boolean {
|
||||
return node.kind === SyntaxKind.Identifier &&
|
||||
node.parent.kind === SyntaxKind.LabeledStatement &&
|
||||
(<LabeledStatement>node.parent).label === node;
|
||||
}
|
||||
|
||||
export function isLabelName(node: Node): boolean {
|
||||
return isLabelOfLabeledStatement(node) || isJumpStatementTarget(node);
|
||||
}
|
||||
|
||||
export function isRightSideOfQualifiedName(node: Node) {
|
||||
return node.parent.kind === SyntaxKind.QualifiedName && (<QualifiedName>node.parent).right === node;
|
||||
}
|
||||
|
||||
export function isRightSideOfPropertyAccess(node: Node) {
|
||||
return node && node.parent && node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).name === node;
|
||||
}
|
||||
|
||||
export function isNameOfModuleDeclaration(node: Node) {
|
||||
return node.parent.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>node.parent).name === node;
|
||||
}
|
||||
|
||||
export function isNameOfFunctionDeclaration(node: Node): boolean {
|
||||
return node.kind === SyntaxKind.Identifier &&
|
||||
isFunctionLike(node.parent) && (<FunctionLikeDeclaration>node.parent).name === node;
|
||||
}
|
||||
|
||||
export function isLiteralNameOfPropertyDeclarationOrIndexAccess(node: Node): boolean {
|
||||
if (node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NumericLiteral) {
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return (<Declaration>node.parent).name === node;
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
return (<ElementAccessExpression>node.parent).argumentExpression === node;
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isExpressionOfExternalModuleImportEqualsDeclaration(node: Node) {
|
||||
return isExternalModuleImportEqualsDeclaration(node.parent.parent) &&
|
||||
getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node;
|
||||
}
|
||||
|
||||
/** Returns true if the position is within a comment */
|
||||
export function isInsideComment(sourceFile: SourceFile, token: Node, position: number): boolean {
|
||||
// The position has to be: 1. in the leading trivia (before token.getStart()), and 2. within a comment
|
||||
return position <= token.getStart(sourceFile) &&
|
||||
(isInsideCommentRange(getTrailingCommentRanges(sourceFile.text, token.getFullStart())) ||
|
||||
isInsideCommentRange(getLeadingCommentRanges(sourceFile.text, token.getFullStart())));
|
||||
|
||||
function isInsideCommentRange(comments: CommentRange[]): boolean {
|
||||
return forEach(comments, comment => {
|
||||
// either we are 1. completely inside the comment, or 2. at the end of the comment
|
||||
if (comment.pos < position && position < comment.end) {
|
||||
return true;
|
||||
}
|
||||
else if (position === comment.end) {
|
||||
const text = sourceFile.text;
|
||||
const width = comment.end - comment.pos;
|
||||
// is single line comment or just /*
|
||||
if (width <= 2 || text.charCodeAt(comment.pos + 1) === CharacterCodes.slash) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// is unterminated multi-line comment
|
||||
return !(text.charCodeAt(comment.end - 1) === CharacterCodes.slash &&
|
||||
text.charCodeAt(comment.end - 2) === CharacterCodes.asterisk);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function getContainerNode(node: Node): Declaration {
|
||||
while (true) {
|
||||
node = node.parent;
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.SourceFile:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return <Declaration>node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getNodeKind(node: Node): string {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.SourceFile:
|
||||
return isExternalModule(<SourceFile>node) ? ScriptElementKind.moduleElement : ScriptElementKind.scriptElement;
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return ScriptElementKind.moduleElement;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
return ScriptElementKind.classElement;
|
||||
case SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement;
|
||||
case SyntaxKind.TypeAliasDeclaration: return ScriptElementKind.typeElement;
|
||||
case SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement;
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
return getKindOfVariableDeclaration(<VariableDeclaration>node);
|
||||
case SyntaxKind.BindingElement:
|
||||
return getKindOfVariableDeclaration(<VariableDeclaration>getRootDeclaration(node));
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
return ScriptElementKind.functionElement;
|
||||
case SyntaxKind.GetAccessor: return ScriptElementKind.memberGetAccessorElement;
|
||||
case SyntaxKind.SetAccessor: return ScriptElementKind.memberSetAccessorElement;
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
return ScriptElementKind.memberFunctionElement;
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
return ScriptElementKind.memberVariableElement;
|
||||
case SyntaxKind.IndexSignature: return ScriptElementKind.indexSignatureElement;
|
||||
case SyntaxKind.ConstructSignature: return ScriptElementKind.constructSignatureElement;
|
||||
case SyntaxKind.CallSignature: return ScriptElementKind.callSignatureElement;
|
||||
case SyntaxKind.Constructor: return ScriptElementKind.constructorImplementationElement;
|
||||
case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
|
||||
case SyntaxKind.EnumMember: return ScriptElementKind.enumMemberElement;
|
||||
case SyntaxKind.Parameter: return hasModifier(node, ModifierFlags.ParameterPropertyModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
case SyntaxKind.ImportClause:
|
||||
case SyntaxKind.ExportSpecifier:
|
||||
case SyntaxKind.NamespaceImport:
|
||||
return ScriptElementKind.alias;
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
return ScriptElementKind.typeElement;
|
||||
default:
|
||||
return ScriptElementKind.unknown;
|
||||
}
|
||||
|
||||
function getKindOfVariableDeclaration(v: VariableDeclaration): string {
|
||||
return isConst(v)
|
||||
? ScriptElementKind.constElement
|
||||
: isLet(v)
|
||||
? ScriptElementKind.letElement
|
||||
: ScriptElementKind.variableElement;
|
||||
}
|
||||
}
|
||||
|
||||
export function getStringLiteralTypeForNode(node: StringLiteral | LiteralTypeNode, typeChecker: TypeChecker): LiteralType {
|
||||
const searchNode = node.parent.kind === SyntaxKind.LiteralType ? <LiteralTypeNode>node.parent : node;
|
||||
const type = typeChecker.getTypeAtLocation(searchNode);
|
||||
if (type && type.flags & TypeFlags.StringLiteral) {
|
||||
return <LiteralType>type;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function isThis(node: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ThisKeyword:
|
||||
// case SyntaxKind.ThisType: TODO: GH#9267
|
||||
return true;
|
||||
case SyntaxKind.Identifier:
|
||||
// 'this' as a parameter
|
||||
return (node as Identifier).originalKeywordKind === SyntaxKind.ThisKeyword && node.parent.kind === SyntaxKind.Parameter;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Matches the beginning of a triple slash directive
|
||||
const tripleSlashDirectivePrefixRegex = /^\/\/\/\s*</;
|
||||
|
||||
|
||||
@@ -56,6 +56,6 @@ var p = new A.Point(0, 0); // unexpected error here, bug 840000
|
||||
>A.Point : typeof A.Point
|
||||
>A : typeof A
|
||||
>Point : typeof A.Point
|
||||
>0 : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>0 : 0
|
||||
|
||||
|
||||
+2
-2
@@ -50,6 +50,6 @@ var p = new A.Point(0, 0); // unexpected error here, bug 840000
|
||||
>A.Point : typeof A.Point
|
||||
>A : typeof A
|
||||
>Point : typeof A.Point
|
||||
>0 : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>0 : 0
|
||||
|
||||
|
||||
+2
-2
@@ -15,9 +15,9 @@ function Point() {
|
||||
return { x: 0, y: 0 };
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
=== tests/cases/conformance/internalModules/DeclarationMerging/test.ts ===
|
||||
|
||||
+6
-6
@@ -11,9 +11,9 @@ class Point {
|
||||
>Point : Point
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
module Point {
|
||||
@@ -21,7 +21,7 @@ module Point {
|
||||
|
||||
function Origin() { return ""; }// not an error, since not exported
|
||||
>Origin : () => string
|
||||
>"" : string
|
||||
>"" : ""
|
||||
}
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ module A {
|
||||
>Point : Point
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
export module Point {
|
||||
@@ -50,6 +50,6 @@ module A {
|
||||
|
||||
function Origin() { return ""; }// not an error since not exported
|
||||
>Origin : () => string
|
||||
>"" : string
|
||||
>"" : ""
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -11,9 +11,9 @@ class Point {
|
||||
>Point : Point
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
module Point {
|
||||
@@ -21,7 +21,7 @@ module Point {
|
||||
|
||||
var Origin = ""; // not an error, since not exported
|
||||
>Origin : string
|
||||
>"" : string
|
||||
>"" : ""
|
||||
}
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ module A {
|
||||
>Point : Point
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
export module Point {
|
||||
@@ -50,6 +50,6 @@ module A {
|
||||
|
||||
var Origin = ""; // not an error since not exported
|
||||
>Origin : string
|
||||
>"" : string
|
||||
>"" : ""
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,5 +7,5 @@ class C {
|
||||
|
||||
x = 10;
|
||||
>x : number
|
||||
>10 : number
|
||||
>10 : 10
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
for (var v of [true]) { }
|
||||
>v : boolean
|
||||
>[true] : boolean[]
|
||||
>true : boolean
|
||||
>true : true
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ function foo() {
|
||||
return { x: 0 };
|
||||
>{ x: 0 } : { x: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
}
|
||||
for (foo().x of []) {
|
||||
>foo().x : number
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
for (let v of ['a', 'b', 'c']) {
|
||||
>v : string
|
||||
>['a', 'b', 'c'] : string[]
|
||||
>'a' : string
|
||||
>'b' : string
|
||||
>'c' : string
|
||||
>'a' : "a"
|
||||
>'b' : "b"
|
||||
>'c' : "c"
|
||||
|
||||
var x = v;
|
||||
>x : string
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
var a = [1, 2, 3];
|
||||
>a : number[]
|
||||
>[1, 2, 3] : number[]
|
||||
>1 : number
|
||||
>2 : number
|
||||
>3 : number
|
||||
>1 : 1
|
||||
>2 : 2
|
||||
>3 : 3
|
||||
|
||||
for (var v of a) {
|
||||
>v : number
|
||||
@@ -12,5 +12,5 @@ for (var v of a) {
|
||||
|
||||
let a = 0;
|
||||
>a : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
var a = [1, 2, 3];
|
||||
>a : number[]
|
||||
>[1, 2, 3] : number[]
|
||||
>1 : number
|
||||
>2 : number
|
||||
>3 : number
|
||||
>1 : 1
|
||||
>2 : 2
|
||||
>3 : 3
|
||||
|
||||
for (var v of a) {
|
||||
>v : number
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
for (var v of ['a', 'b', 'c'])
|
||||
>v : string
|
||||
>['a', 'b', 'c'] : string[]
|
||||
>'a' : string
|
||||
>'b' : string
|
||||
>'c' : string
|
||||
>'a' : "a"
|
||||
>'b' : "b"
|
||||
>'c' : "c"
|
||||
|
||||
var x = v;
|
||||
>x : string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
tests/cases/conformance/statements/for-ofStatements/ES5For-of30.ts(3,6): error TS2461: Type 'string | number' is not an array type.
|
||||
tests/cases/conformance/statements/for-ofStatements/ES5For-of30.ts(3,7): error TS2322: Type 'number' is not assignable to type 'string'.
|
||||
tests/cases/conformance/statements/for-ofStatements/ES5For-of30.ts(3,14): error TS2322: Type 'string' is not assignable to type 'number'.
|
||||
tests/cases/conformance/statements/for-ofStatements/ES5For-of30.ts(3,7): error TS2322: Type '1' is not assignable to type 'string'.
|
||||
tests/cases/conformance/statements/for-ofStatements/ES5For-of30.ts(3,14): error TS2322: Type '""' is not assignable to type 'number'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/statements/for-ofStatements/ES5For-of30.ts (3 errors) ====
|
||||
@@ -10,9 +10,9 @@ tests/cases/conformance/statements/for-ofStatements/ES5For-of30.ts(3,14): error
|
||||
~~~~~~~~~~~~~~~
|
||||
!!! error TS2461: Type 'string | number' is not an array type.
|
||||
~
|
||||
!!! error TS2322: Type 'number' is not assignable to type 'string'.
|
||||
!!! error TS2322: Type '1' is not assignable to type 'string'.
|
||||
~
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'number'.
|
||||
!!! error TS2322: Type '""' is not assignable to type 'number'.
|
||||
a;
|
||||
b;
|
||||
}
|
||||
@@ -5,7 +5,7 @@ function foo() {
|
||||
return { x: 0 };
|
||||
>{ x: 0 } : { x: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
}
|
||||
for (foo().x of []) {
|
||||
>foo().x : number
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
=== tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck1.ts ===
|
||||
for (var v of "") { }
|
||||
>v : string
|
||||
>"" : string
|
||||
>"" : ""
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck12.ts(1,17): error TS2495: Type 'number' is not an array type or a string type.
|
||||
tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck12.ts(1,17): error TS2495: Type '0' is not an array type or a string type.
|
||||
|
||||
|
||||
==== tests/cases/conformance/statements/for-ofStatements/ES5For-ofTypeCheck12.ts (1 errors) ====
|
||||
for (const v of 0) { }
|
||||
~
|
||||
!!! error TS2495: Type 'number' is not an array type or a string type.
|
||||
!!! error TS2495: Type '0' is not an array type or a string type.
|
||||
@@ -2,5 +2,5 @@
|
||||
for (var v of [true]) { }
|
||||
>v : boolean
|
||||
>[true] : boolean[]
|
||||
>true : boolean
|
||||
>true : true
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
var tuple: [string, number] = ["", 0];
|
||||
>tuple : [string, number]
|
||||
>["", 0] : [string, number]
|
||||
>"" : string
|
||||
>0 : number
|
||||
>"" : ""
|
||||
>0 : 0
|
||||
|
||||
for (var v of tuple) { }
|
||||
>v : string | number
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
var array = [1,2,3];
|
||||
>array : number[]
|
||||
>[1,2,3] : number[]
|
||||
>1 : number
|
||||
>2 : number
|
||||
>3 : number
|
||||
>1 : 1
|
||||
>2 : 2
|
||||
>3 : 3
|
||||
|
||||
var sum = 0;
|
||||
>sum : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
|
||||
for (let num of array) {
|
||||
>num : number
|
||||
@@ -24,9 +24,9 @@ for (let num of array) {
|
||||
>array = [4,5,6] : number[]
|
||||
>array : number[]
|
||||
>[4,5,6] : number[]
|
||||
>4 : number
|
||||
>5 : number
|
||||
>6 : number
|
||||
>4 : 4
|
||||
>5 : 5
|
||||
>6 : 6
|
||||
}
|
||||
|
||||
sum += num;
|
||||
|
||||
@@ -3,8 +3,8 @@ enum enumdule {
|
||||
>enumdule : enumdule
|
||||
|
||||
Red, Blue
|
||||
>Red : enumdule
|
||||
>Blue : enumdule
|
||||
>Red : enumdule.Red
|
||||
>Blue : enumdule.Blue
|
||||
}
|
||||
|
||||
module enumdule {
|
||||
@@ -25,9 +25,9 @@ var x: enumdule;
|
||||
|
||||
var x = enumdule.Red;
|
||||
>x : enumdule
|
||||
>enumdule.Red : enumdule
|
||||
>enumdule.Red : enumdule.Red
|
||||
>enumdule : typeof enumdule
|
||||
>Red : enumdule
|
||||
>Red : enumdule.Red
|
||||
|
||||
var y: { x: number; y: number };
|
||||
>y : { x: number; y: number; }
|
||||
@@ -40,6 +40,6 @@ var y = new enumdule.Point(0, 0);
|
||||
>enumdule.Point : typeof enumdule.Point
|
||||
>enumdule : typeof enumdule
|
||||
>Point : typeof enumdule.Point
|
||||
>0 : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>0 : 0
|
||||
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ module A {
|
||||
>Point : Point
|
||||
|
||||
return 1;
|
||||
>1 : number
|
||||
>1 : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -17,9 +17,9 @@ module A {
|
||||
>Point : Point
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
|
||||
export class Point3d extends Point {
|
||||
>Point3d : Point3d
|
||||
@@ -34,11 +34,11 @@ module A {
|
||||
>Point3d : Point3d
|
||||
>{ x: 0, y: 0, z: 0 } : { x: number; y: number; z: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>z : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
|
||||
export class Line<TPoint extends Point>{
|
||||
>Line : Line<TPoint>
|
||||
|
||||
+5
-5
@@ -17,9 +17,9 @@ module A {
|
||||
>Point : Point
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
|
||||
export class Point3d extends Point {
|
||||
>Point3d : Point3d
|
||||
@@ -34,11 +34,11 @@ module A {
|
||||
>Point3d : Point3d
|
||||
>{ x: 0, y: 0, z: 0 } : { x: number; y: number; z: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>z : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
|
||||
export class Line<TPoint extends Point>{
|
||||
>Line : Line<TPoint>
|
||||
|
||||
+2
-2
@@ -33,9 +33,9 @@ module A {
|
||||
>Line : typeof Line
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>p : Point
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -33,9 +33,9 @@ module A {
|
||||
>Line : typeof Line
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>p : Point
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -33,9 +33,9 @@ module A {
|
||||
>Line : typeof Line
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>p : Point
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -17,9 +17,9 @@ module A {
|
||||
>Point : Point
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
|
||||
export interface Point3d extends Point {
|
||||
>Point3d : Point3d
|
||||
@@ -34,11 +34,11 @@ module A {
|
||||
>Point3d : Point3d
|
||||
>{ x: 0, y: 0, z: 0 } : { x: number; y: number; z: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>z : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
|
||||
export interface Line<TPoint extends Point>{
|
||||
>Line : Line<TPoint>
|
||||
|
||||
+5
-5
@@ -17,9 +17,9 @@ module A {
|
||||
>Point : Point
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
|
||||
export interface Point3d extends Point {
|
||||
>Point3d : Point3d
|
||||
@@ -34,11 +34,11 @@ module A {
|
||||
>Point3d : Point3d
|
||||
>{ x: 0, y: 0, z: 0 } : { x: number; y: number; z: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>z : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
|
||||
export interface Line<TPoint extends Point>{
|
||||
>Line : Line<TPoint>
|
||||
|
||||
@@ -18,8 +18,8 @@ module A {
|
||||
>Point : Point
|
||||
>new Point(0, 0) : Point
|
||||
>Point : typeof Point
|
||||
>0 : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>0 : 0
|
||||
|
||||
export class Line {
|
||||
>Line : Line
|
||||
@@ -42,9 +42,9 @@ module A {
|
||||
>Line : typeof Line
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>p : Point
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -15,9 +15,9 @@ module A {
|
||||
>Point : Point
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
|
||||
export var Unity = { start: new Point(0, 0), end: new Point(1, 0) };
|
||||
>Unity : { start: Point; end: Point; }
|
||||
@@ -25,12 +25,12 @@ module A {
|
||||
>start : Point
|
||||
>new Point(0, 0) : Point
|
||||
>Point : typeof Point
|
||||
>0 : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>0 : 0
|
||||
>end : Point
|
||||
>new Point(1, 0) : Point
|
||||
>Point : typeof Point
|
||||
>1 : number
|
||||
>0 : number
|
||||
>1 : 1
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ module A {
|
||||
>Point : Point
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ module A {
|
||||
>Point : Point
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
|
||||
interface Point3d extends Point {
|
||||
>Point3d : Point3d
|
||||
@@ -36,10 +36,10 @@ module A {
|
||||
>Point3d : Point3d
|
||||
>{ x: 0, y: 0, z: 0 } : { x: number; y: number; z: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>z : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ module A {
|
||||
return { x: 0, y: 0 };
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ module B {
|
||||
>Origin : { x: number; y: number; }
|
||||
>{ x: 0, y: 0 } : { x: number; y: number; }
|
||||
>x : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>y : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ enum enumdule {
|
||||
>enumdule : enumdule
|
||||
|
||||
Red, Blue
|
||||
>Red : enumdule
|
||||
>Blue : enumdule
|
||||
>Red : enumdule.Red
|
||||
>Blue : enumdule.Blue
|
||||
}
|
||||
|
||||
var x: enumdule;
|
||||
@@ -25,9 +25,9 @@ var x: enumdule;
|
||||
|
||||
var x = enumdule.Red;
|
||||
>x : enumdule
|
||||
>enumdule.Red : enumdule
|
||||
>enumdule.Red : enumdule.Red
|
||||
>enumdule : typeof enumdule
|
||||
>Red : enumdule
|
||||
>Red : enumdule.Red
|
||||
|
||||
var y: { x: number; y: number };
|
||||
>y : { x: number; y: number; }
|
||||
@@ -40,6 +40,6 @@ var y = new enumdule.Point(0, 0);
|
||||
>enumdule.Point : typeof enumdule.Point
|
||||
>enumdule : typeof enumdule
|
||||
>Point : typeof enumdule.Point
|
||||
>0 : number
|
||||
>0 : number
|
||||
>0 : 0
|
||||
>0 : 0
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user