mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into refactor-jsdoc-types-to-typescript
This commit is contained in:
+28
-28
@@ -15675,34 +15675,32 @@ namespace ts {
|
||||
return getInferredTypes(context);
|
||||
}
|
||||
|
||||
function checkTypeArguments(signature: Signature, typeArgumentNodes: ReadonlyArray<TypeNode>, typeArgumentTypes: Type[], reportErrors: boolean, headMessage?: DiagnosticMessage): boolean {
|
||||
function checkTypeArguments(signature: Signature, typeArgumentNodes: ReadonlyArray<TypeNode>, reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | false {
|
||||
const isJavascript = isInJavaScriptFile(signature.declaration);
|
||||
const typeParameters = signature.typeParameters;
|
||||
let typeArgumentsAreAssignable = true;
|
||||
const typeArgumentTypes = fillMissingTypeArguments(map(typeArgumentNodes, getTypeFromTypeNode), typeParameters, getMinTypeArgumentCount(typeParameters), isJavascript);
|
||||
let mapper: TypeMapper;
|
||||
for (let i = 0; i < typeArgumentNodes.length; i++) {
|
||||
if (typeArgumentsAreAssignable /* so far */) {
|
||||
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
|
||||
if (constraint) {
|
||||
let errorInfo: DiagnosticMessageChain;
|
||||
let typeArgumentHeadMessage = Diagnostics.Type_0_does_not_satisfy_the_constraint_1;
|
||||
if (reportErrors && headMessage) {
|
||||
errorInfo = chainDiagnosticMessages(errorInfo, typeArgumentHeadMessage);
|
||||
typeArgumentHeadMessage = headMessage;
|
||||
}
|
||||
if (!mapper) {
|
||||
mapper = createTypeMapper(typeParameters, typeArgumentTypes);
|
||||
}
|
||||
const typeArgument = typeArgumentTypes[i];
|
||||
typeArgumentsAreAssignable = checkTypeAssignableTo(
|
||||
typeArgument,
|
||||
getTypeWithThisArgument(instantiateType(constraint, mapper), typeArgument),
|
||||
reportErrors ? typeArgumentNodes[i] : undefined,
|
||||
typeArgumentHeadMessage,
|
||||
errorInfo);
|
||||
}
|
||||
Debug.assert(typeParameters[i] !== undefined, "Should not call checkTypeArguments with too many type arguments");
|
||||
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
|
||||
if (!constraint) continue;
|
||||
|
||||
const errorInfo = reportErrors && headMessage && chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
|
||||
const typeArgumentHeadMessage = headMessage || Diagnostics.Type_0_does_not_satisfy_the_constraint_1;
|
||||
if (!mapper) {
|
||||
mapper = createTypeMapper(typeParameters, typeArgumentTypes);
|
||||
}
|
||||
const typeArgument = typeArgumentTypes[i];
|
||||
if (!checkTypeAssignableTo(
|
||||
typeArgument,
|
||||
getTypeWithThisArgument(instantiateType(constraint, mapper), typeArgument),
|
||||
reportErrors ? typeArgumentNodes[i] : undefined,
|
||||
typeArgumentHeadMessage,
|
||||
errorInfo)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return typeArgumentsAreAssignable;
|
||||
return typeArgumentTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -16235,8 +16233,7 @@ namespace ts {
|
||||
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
|
||||
}
|
||||
else if (candidateForTypeArgumentError) {
|
||||
const typeArguments = (<CallExpression>node).typeArguments;
|
||||
checkTypeArguments(candidateForTypeArgumentError, typeArguments, map(typeArguments, getTypeFromTypeNode), /*reportErrors*/ true, fallbackError);
|
||||
checkTypeArguments(candidateForTypeArgumentError, (node as CallExpression).typeArguments, /*reportErrors*/ true, fallbackError);
|
||||
}
|
||||
else if (typeArguments && every(signatures, sig => length(sig.typeParameters) !== typeArguments.length)) {
|
||||
let min = Number.POSITIVE_INFINITY;
|
||||
@@ -16335,10 +16332,12 @@ namespace ts {
|
||||
candidate = originalCandidate;
|
||||
if (candidate.typeParameters) {
|
||||
let typeArgumentTypes: Type[];
|
||||
const isJavascript = isInJavaScriptFile(candidate.declaration);
|
||||
if (typeArguments) {
|
||||
typeArgumentTypes = fillMissingTypeArguments(map(typeArguments, getTypeFromTypeNode), candidate.typeParameters, getMinTypeArgumentCount(candidate.typeParameters), isJavascript);
|
||||
if (!checkTypeArguments(candidate, typeArguments, typeArgumentTypes, /*reportErrors*/ false)) {
|
||||
const typeArgumentResult = checkTypeArguments(candidate, typeArguments, /*reportErrors*/ false);
|
||||
if (typeArgumentResult) {
|
||||
typeArgumentTypes = typeArgumentResult;
|
||||
}
|
||||
else {
|
||||
candidateForTypeArgumentError = originalCandidate;
|
||||
break;
|
||||
}
|
||||
@@ -16346,6 +16345,7 @@ namespace ts {
|
||||
else {
|
||||
typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext);
|
||||
}
|
||||
const isJavascript = isInJavaScriptFile(candidate.declaration);
|
||||
candidate = getSignatureInstantiation(candidate, typeArgumentTypes, isJavascript);
|
||||
}
|
||||
if (!checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
|
||||
|
||||
@@ -2612,6 +2612,16 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flags that control emit behavior of a node.
|
||||
*/
|
||||
/* @internal */
|
||||
export function addEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
|
||||
const emitNode = getOrCreateEmitNode(node);
|
||||
emitNode.flags = emitNode.flags | emitFlags;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a custom text range to use when emitting source maps.
|
||||
*/
|
||||
|
||||
@@ -1112,7 +1112,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function visitCallExpression(node: CallExpression) {
|
||||
if (forEach(node.arguments, containsYield)) {
|
||||
if (!isImportCall(node) && forEach(node.arguments, containsYield)) {
|
||||
// [source]
|
||||
// a.b(1, yield, 2);
|
||||
//
|
||||
@@ -1123,7 +1123,6 @@ namespace ts {
|
||||
// .yield resumeLabel
|
||||
// .mark resumeLabel
|
||||
// _b.apply(_a, _c.concat([%sent%, 2]));
|
||||
|
||||
const { target, thisArg } = createCallBinding(node.expression, hoistVariableDeclaration, languageVersion, /*cacheIdentifiers*/ true);
|
||||
return setOriginalNode(
|
||||
createFunctionApply(
|
||||
|
||||
@@ -21,7 +21,8 @@ namespace ts {
|
||||
|
||||
const {
|
||||
startLexicalEnvironment,
|
||||
endLexicalEnvironment
|
||||
endLexicalEnvironment,
|
||||
hoistVariableDeclaration
|
||||
} = context;
|
||||
|
||||
const compilerOptions = context.getCompilerOptions();
|
||||
@@ -519,18 +520,20 @@ namespace ts {
|
||||
}
|
||||
|
||||
function visitImportCallExpression(node: ImportCall): Expression {
|
||||
const argument = visitNode(firstOrUndefined(node.arguments), importCallExpressionVisitor);
|
||||
const containsLexicalThis = !!(node.transformFlags & TransformFlags.ContainsLexicalThis);
|
||||
switch (compilerOptions.module) {
|
||||
case ModuleKind.AMD:
|
||||
return transformImportCallExpressionAMD(node);
|
||||
return createImportCallExpressionAMD(argument, containsLexicalThis);
|
||||
case ModuleKind.UMD:
|
||||
return transformImportCallExpressionUMD(node);
|
||||
return createImportCallExpressionUMD(argument, containsLexicalThis);
|
||||
case ModuleKind.CommonJS:
|
||||
default:
|
||||
return transformImportCallExpressionCommonJS(node);
|
||||
return createImportCallExpressionCommonJS(argument, containsLexicalThis);
|
||||
}
|
||||
}
|
||||
|
||||
function transformImportCallExpressionUMD(node: ImportCall): Expression {
|
||||
function createImportCallExpressionUMD(arg: Expression | undefined, containsLexicalThis: boolean): Expression {
|
||||
// (function (factory) {
|
||||
// ... (regular UMD)
|
||||
// }
|
||||
@@ -545,14 +548,25 @@ namespace ts {
|
||||
// : new Promise(function (_a, _b) { require([x], _a, _b); }); /*Amd Require*/
|
||||
// });
|
||||
needUMDDynamicImportHelper = true;
|
||||
return createConditional(
|
||||
/*condition*/ createIdentifier("__syncRequire"),
|
||||
/*whenTrue*/ transformImportCallExpressionCommonJS(node),
|
||||
/*whenFalse*/ transformImportCallExpressionAMD(node)
|
||||
);
|
||||
if (isSimpleCopiableExpression(arg)) {
|
||||
const argClone = isGeneratedIdentifier(arg) ? arg : isStringLiteral(arg) ? createLiteral(arg) : setEmitFlags(setTextRange(getSynthesizedClone(arg), arg), EmitFlags.NoComments);
|
||||
return createConditional(
|
||||
/*condition*/ createIdentifier("__syncRequire"),
|
||||
/*whenTrue*/ createImportCallExpressionCommonJS(arg, containsLexicalThis),
|
||||
/*whenFalse*/ createImportCallExpressionAMD(argClone, containsLexicalThis)
|
||||
);
|
||||
}
|
||||
else {
|
||||
const temp = createTempVariable(hoistVariableDeclaration);
|
||||
return createComma(createAssignment(temp, arg), createConditional(
|
||||
/*condition*/ createIdentifier("__syncRequire"),
|
||||
/*whenTrue*/ createImportCallExpressionCommonJS(temp, containsLexicalThis),
|
||||
/*whenFalse*/ createImportCallExpressionAMD(temp, containsLexicalThis)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function transformImportCallExpressionAMD(node: ImportCall): Expression {
|
||||
function createImportCallExpressionAMD(arg: Expression | undefined, containsLexicalThis: boolean): Expression {
|
||||
// improt("./blah")
|
||||
// emit as
|
||||
// define(["require", "exports", "blah"], function (require, exports) {
|
||||
@@ -570,7 +584,7 @@ namespace ts {
|
||||
createCall(
|
||||
createIdentifier("require"),
|
||||
/*typeArguments*/ undefined,
|
||||
[createArrayLiteral([firstOrUndefined(node.arguments) || createOmittedExpression()]), resolve, reject]
|
||||
[createArrayLiteral([arg || createOmittedExpression()]), resolve, reject]
|
||||
)
|
||||
)
|
||||
]);
|
||||
@@ -598,7 +612,7 @@ namespace ts {
|
||||
// if there is a lexical 'this' in the import call arguments, ensure we indicate
|
||||
// that this new function expression indicates it captures 'this' so that the
|
||||
// es2015 transformer will properly substitute 'this' with '_this'.
|
||||
if (node.transformFlags & TransformFlags.ContainsLexicalThis) {
|
||||
if (containsLexicalThis) {
|
||||
setEmitFlags(func, EmitFlags.CapturesThis);
|
||||
}
|
||||
}
|
||||
@@ -606,14 +620,14 @@ namespace ts {
|
||||
return createNew(createIdentifier("Promise"), /*typeArguments*/ undefined, [func]);
|
||||
}
|
||||
|
||||
function transformImportCallExpressionCommonJS(node: ImportCall): Expression {
|
||||
function createImportCallExpressionCommonJS(arg: Expression | undefined, containsLexicalThis: boolean): Expression {
|
||||
// import("./blah")
|
||||
// emit as
|
||||
// Promise.resolve().then(function () { return require(x); }) /*CommonJs Require*/
|
||||
// We have to wrap require in then callback so that require is done in asynchronously
|
||||
// if we simply do require in resolve callback in Promise constructor. We will execute the loading immediately
|
||||
const promiseResolveCall = createCall(createPropertyAccess(createIdentifier("Promise"), "resolve"), /*typeArguments*/ undefined, /*argumentsArray*/ []);
|
||||
const requireCall = createCall(createIdentifier("require"), /*typeArguments*/ undefined, node.arguments);
|
||||
const requireCall = createCall(createIdentifier("require"), /*typeArguments*/ undefined, arg ? [arg] : []);
|
||||
|
||||
let func: FunctionExpression | ArrowFunction;
|
||||
if (languageVersion >= ScriptTarget.ES2015) {
|
||||
@@ -638,7 +652,7 @@ namespace ts {
|
||||
// if there is a lexical 'this' in the import call arguments, ensure we indicate
|
||||
// that this new function expression indicates it captures 'this' so that the
|
||||
// es2015 transformer will properly substitute 'this' with '_this'.
|
||||
if (node.transformFlags & TransformFlags.ContainsLexicalThis) {
|
||||
if (containsLexicalThis) {
|
||||
setEmitFlags(func, EmitFlags.CapturesThis);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1495,7 +1495,7 @@ namespace ts {
|
||||
createIdentifier("import")
|
||||
),
|
||||
/*typeArguments*/ undefined,
|
||||
node.arguments
|
||||
some(node.arguments) ? [visitNode(node.arguments[0], destructuringAndImportCallVisitor)] : []
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -178,4 +178,17 @@ namespace ts {
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in the module transformer to check if an expression is reasonably without sideeffect,
|
||||
* and thus better to copy into multiple places rather than to cache in a temporary variable
|
||||
* - this is mostly subjective beyond the requirement that the expression not be sideeffecting
|
||||
*/
|
||||
export function isSimpleCopiableExpression(expression: Expression) {
|
||||
return expression.kind === SyntaxKind.StringLiteral ||
|
||||
expression.kind === SyntaxKind.NumericLiteral ||
|
||||
expression.kind === SyntaxKind.NoSubstitutionTemplateLiteral ||
|
||||
isKeyword(expression.kind) ||
|
||||
isIdentifier(expression);
|
||||
}
|
||||
}
|
||||
@@ -223,6 +223,14 @@ const f = () => {
|
||||
testExtractConstant("extractConstant_ArrowFunction_Expression",
|
||||
`const f = () => [#|2 + 1|];`);
|
||||
|
||||
testExtractConstant("extractConstant_PreserveTrivia", `
|
||||
// a
|
||||
var q = /*b*/ //c
|
||||
/*d*/ [#|1 /*e*/ //f
|
||||
/*g*/ + /*h*/ //i
|
||||
/*j*/ 2|] /*k*/ //l
|
||||
/*m*/; /*n*/ //o`);
|
||||
|
||||
testExtractConstantFailed("extractConstant_Void", `
|
||||
function f(): void { }
|
||||
[#|f();|]`);
|
||||
@@ -230,6 +238,30 @@ function f(): void { }
|
||||
testExtractConstantFailed("extractConstant_Never", `
|
||||
function f(): never { }
|
||||
[#|f();|]`);
|
||||
|
||||
testExtractConstant("extractConstant_This_Constructor", `
|
||||
class C {
|
||||
constructor() {
|
||||
[#|this.m2()|];
|
||||
}
|
||||
m2() { return 1; }
|
||||
}`);
|
||||
|
||||
testExtractConstant("extractConstant_This_Method", `
|
||||
class C {
|
||||
m1() {
|
||||
[#|this.m2()|];
|
||||
}
|
||||
m2() { return 1; }
|
||||
}`);
|
||||
|
||||
testExtractConstant("extractConstant_This_Property", `
|
||||
namespace N { // Force this test to be TS-only
|
||||
class C {
|
||||
x = 1;
|
||||
y = [#|this.x|];
|
||||
}
|
||||
}`);
|
||||
});
|
||||
|
||||
function testExtractConstant(caption: string, text: string) {
|
||||
|
||||
@@ -532,6 +532,14 @@ function f() {
|
||||
[#|let x;|]
|
||||
return { x };
|
||||
}`);
|
||||
|
||||
testExtractFunction("extractFunction_PreserveTrivia", `
|
||||
// a
|
||||
var q = /*b*/ //c
|
||||
/*d*/ [#|1 /*e*/ //f
|
||||
/*g*/ + /*h*/ //i
|
||||
/*j*/ 2|] /*k*/ //l
|
||||
/*m*/; /*n*/ //o`);
|
||||
});
|
||||
|
||||
function testExtractFunction(caption: string, text: string) {
|
||||
|
||||
@@ -152,6 +152,16 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
`);
|
||||
testExtractRange(`
|
||||
function f(x: number) {
|
||||
[#|[$|try {
|
||||
x++;
|
||||
}
|
||||
finally {
|
||||
return 1;
|
||||
}|]|]
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
testExtractRangeFailed("extractRangeFailed1",
|
||||
@@ -313,6 +323,23 @@ switch (x) {
|
||||
refactor.extractSymbol.Messages.CannotExtractRange.message
|
||||
]);
|
||||
|
||||
testExtractRangeFailed("extractRangeFailed11",
|
||||
`
|
||||
function f(x: number) {
|
||||
while (true) {
|
||||
[#|try {
|
||||
x++;
|
||||
}
|
||||
finally {
|
||||
break;
|
||||
}|]
|
||||
}
|
||||
}
|
||||
`,
|
||||
[
|
||||
refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message
|
||||
]);
|
||||
|
||||
testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, [refactor.extractSymbol.Messages.CannotExtractIdentifier.message]);
|
||||
});
|
||||
}
|
||||
@@ -281,11 +281,11 @@ namespace ts.projectSystem {
|
||||
checkNumberOfProjects(this, count);
|
||||
}
|
||||
}
|
||||
export function createProjectService(host: server.ServerHost, parameters: CreateProjectServiceParameters = {}) {
|
||||
export function createProjectService(host: server.ServerHost, parameters: CreateProjectServiceParameters = {}, options?: Partial<server.ProjectServiceOptions>) {
|
||||
const cancellationToken = parameters.cancellationToken || server.nullCancellationToken;
|
||||
const logger = parameters.logger || nullLogger;
|
||||
const useSingleInferredProject = parameters.useSingleInferredProject !== undefined ? parameters.useSingleInferredProject : false;
|
||||
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller, parameters.eventHandler);
|
||||
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller, parameters.eventHandler, options);
|
||||
}
|
||||
|
||||
export function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) {
|
||||
@@ -3784,6 +3784,113 @@ namespace ts.projectSystem {
|
||||
assert.equal(projectService.inferredProjects[1].getCompilationSettings().target, ScriptTarget.ESNext);
|
||||
assert.equal(projectService.inferredProjects[2].getCompilationSettings().target, ScriptTarget.ES2015);
|
||||
});
|
||||
|
||||
function checkInferredProject(inferredProject: server.InferredProject, actualFiles: FileOrFolder[], target: ScriptTarget) {
|
||||
checkProjectActualFiles(inferredProject, actualFiles.map(f => f.path));
|
||||
assert.equal(inferredProject.getCompilationSettings().target, target);
|
||||
}
|
||||
|
||||
function verifyProjectRootWithCaseSensitivity(useCaseSensitiveFileNames: boolean) {
|
||||
const files: [FileOrFolder, FileOrFolder, FileOrFolder, FileOrFolder] = [
|
||||
{ path: "/a/file1.ts", content: "let x = 1;" },
|
||||
{ path: "/A/file2.ts", content: "let y = 2;" },
|
||||
{ path: "/b/file2.ts", content: "let x = 3;" },
|
||||
{ path: "/c/file3.ts", content: "let z = 4;" }
|
||||
];
|
||||
const host = createServerHost(files, { useCaseSensitiveFileNames });
|
||||
const projectService = createProjectService(host, { useSingleInferredProject: true, }, { useInferredProjectPerProjectRoot: true });
|
||||
projectService.setCompilerOptionsForInferredProjects({
|
||||
allowJs: true,
|
||||
target: ScriptTarget.ESNext
|
||||
});
|
||||
projectService.setCompilerOptionsForInferredProjects({
|
||||
allowJs: true,
|
||||
target: ScriptTarget.ES2015
|
||||
}, "/a");
|
||||
|
||||
openClientFiles(["/a", "/a", "/b", undefined]);
|
||||
verifyInferredProjectsState([
|
||||
[[files[3]], ScriptTarget.ESNext],
|
||||
[[files[0], files[1]], ScriptTarget.ES2015],
|
||||
[[files[2]], ScriptTarget.ESNext]
|
||||
]);
|
||||
closeClientFiles();
|
||||
|
||||
openClientFiles(["/a", "/A", "/b", undefined]);
|
||||
if (useCaseSensitiveFileNames) {
|
||||
verifyInferredProjectsState([
|
||||
[[files[3]], ScriptTarget.ESNext],
|
||||
[[files[0]], ScriptTarget.ES2015],
|
||||
[[files[1]], ScriptTarget.ESNext],
|
||||
[[files[2]], ScriptTarget.ESNext]
|
||||
]);
|
||||
}
|
||||
else {
|
||||
verifyInferredProjectsState([
|
||||
[[files[3]], ScriptTarget.ESNext],
|
||||
[[files[0], files[1]], ScriptTarget.ES2015],
|
||||
[[files[2]], ScriptTarget.ESNext]
|
||||
]);
|
||||
}
|
||||
closeClientFiles();
|
||||
|
||||
projectService.setCompilerOptionsForInferredProjects({
|
||||
allowJs: true,
|
||||
target: ScriptTarget.ES2017
|
||||
}, "/A");
|
||||
|
||||
openClientFiles(["/a", "/a", "/b", undefined]);
|
||||
verifyInferredProjectsState([
|
||||
[[files[3]], ScriptTarget.ESNext],
|
||||
[[files[0], files[1]], useCaseSensitiveFileNames ? ScriptTarget.ES2015 : ScriptTarget.ES2017],
|
||||
[[files[2]], ScriptTarget.ESNext]
|
||||
]);
|
||||
closeClientFiles();
|
||||
|
||||
openClientFiles(["/a", "/A", "/b", undefined]);
|
||||
if (useCaseSensitiveFileNames) {
|
||||
verifyInferredProjectsState([
|
||||
[[files[3]], ScriptTarget.ESNext],
|
||||
[[files[0]], ScriptTarget.ES2015],
|
||||
[[files[1]], ScriptTarget.ES2017],
|
||||
[[files[2]], ScriptTarget.ESNext]
|
||||
]);
|
||||
}
|
||||
else {
|
||||
verifyInferredProjectsState([
|
||||
[[files[3]], ScriptTarget.ESNext],
|
||||
[[files[0], files[1]], ScriptTarget.ES2017],
|
||||
[[files[2]], ScriptTarget.ESNext]
|
||||
]);
|
||||
}
|
||||
closeClientFiles();
|
||||
|
||||
function openClientFiles(projectRoots: [string | undefined, string | undefined, string | undefined, string | undefined]) {
|
||||
files.forEach((file, index) => {
|
||||
projectService.openClientFile(file.path, file.content, ScriptKind.JS, projectRoots[index]);
|
||||
});
|
||||
}
|
||||
|
||||
function closeClientFiles() {
|
||||
files.forEach(file => projectService.closeClientFile(file.path));
|
||||
}
|
||||
|
||||
function verifyInferredProjectsState(expected: [FileOrFolder[], ScriptTarget][]) {
|
||||
checkNumberOfProjects(projectService, { inferredProjects: expected.length });
|
||||
projectService.inferredProjects.forEach((p, index) => {
|
||||
const [actualFiles, target] = expected[index];
|
||||
checkInferredProject(p, actualFiles, target);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
it("inferred projects per project root with case sensitive system", () => {
|
||||
verifyProjectRootWithCaseSensitivity(/*useCaseSensitiveFileNames*/ true);
|
||||
});
|
||||
|
||||
it("inferred projects per project root with case insensitive system", () => {
|
||||
verifyProjectRootWithCaseSensitivity(/*useCaseSensitiveFileNames*/ false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("No overwrite emit error", () => {
|
||||
@@ -4412,9 +4519,9 @@ namespace ts.projectSystem {
|
||||
fileName: "/a.ts",
|
||||
textChanges: [
|
||||
{
|
||||
start: { line: 2, offset: 1 },
|
||||
end: { line: 3, offset: 1 },
|
||||
newText: " newFunction();\n",
|
||||
start: { line: 2, offset: 3 },
|
||||
end: { line: 2, offset: 5 },
|
||||
newText: "newFunction();",
|
||||
},
|
||||
{
|
||||
start: { line: 3, offset: 2 },
|
||||
|
||||
@@ -590,9 +590,9 @@ namespace ts.server {
|
||||
// always set 'allowNonTsExtensions' for inferred projects since user cannot configure it from the outside
|
||||
// previously we did not expose a way for user to change these settings and this option was enabled by default
|
||||
compilerOptions.allowNonTsExtensions = true;
|
||||
|
||||
if (projectRootPath) {
|
||||
this.compilerOptionsForInferredProjectsPerProjectRoot.set(projectRootPath, compilerOptions);
|
||||
const canonicalProjectRootPath = projectRootPath && this.toCanonicalFileName(projectRootPath);
|
||||
if (canonicalProjectRootPath) {
|
||||
this.compilerOptionsForInferredProjectsPerProjectRoot.set(canonicalProjectRootPath, compilerOptions);
|
||||
}
|
||||
else {
|
||||
this.compilerOptionsForInferredProjects = compilerOptions;
|
||||
@@ -608,9 +608,9 @@ namespace ts.server {
|
||||
// root path
|
||||
// - Inferred projects with a projectRootPath, if the new options apply to that
|
||||
// project root path.
|
||||
if (projectRootPath ?
|
||||
project.projectRootPath === projectRootPath :
|
||||
!project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) {
|
||||
if (canonicalProjectRootPath ?
|
||||
project.projectRootPath === canonicalProjectRootPath :
|
||||
!project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) {
|
||||
project.setCompilerOptions(compilerOptions);
|
||||
project.compileOnSaveEnabled = compilerOptions.compileOnSave;
|
||||
project.markAsDirty();
|
||||
@@ -1599,9 +1599,10 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
if (projectRootPath) {
|
||||
const canonicalProjectRootPath = this.toCanonicalFileName(projectRootPath);
|
||||
// if we have an explicit project root path, find (or create) the matching inferred project.
|
||||
for (const project of this.inferredProjects) {
|
||||
if (project.projectRootPath === projectRootPath) {
|
||||
if (project.projectRootPath === canonicalProjectRootPath) {
|
||||
return project;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1047,12 +1047,15 @@ namespace ts.server {
|
||||
super.setCompilerOptions(newOptions);
|
||||
}
|
||||
|
||||
/** this is canonical project root path */
|
||||
readonly projectRootPath: string | undefined;
|
||||
|
||||
/*@internal*/
|
||||
constructor(
|
||||
projectService: ProjectService,
|
||||
documentRegistry: DocumentRegistry,
|
||||
compilerOptions: CompilerOptions,
|
||||
readonly projectRootPath: string | undefined,
|
||||
projectRootPath: string | undefined,
|
||||
currentDirectory: string | undefined) {
|
||||
super(InferredProject.newName(),
|
||||
ProjectKind.Inferred,
|
||||
@@ -1064,6 +1067,7 @@ namespace ts.server {
|
||||
/*compileOnSaveEnabled*/ false,
|
||||
projectService.host,
|
||||
currentDirectory);
|
||||
this.projectRootPath = projectRootPath && projectService.toCanonicalFileName(projectRootPath);
|
||||
}
|
||||
|
||||
addRoot(info: ScriptInfo) {
|
||||
|
||||
@@ -476,7 +476,10 @@ namespace ts.refactor.extractSymbol {
|
||||
// if range uses this as keyword or as type inside the class then it can only be extracted to a method of the containing class
|
||||
const containingClass = getContainingClass(current);
|
||||
if (containingClass) {
|
||||
return [containingClass];
|
||||
const containingFunction = findAncestor(current, isFunctionLikeDeclaration);
|
||||
return containingFunction
|
||||
? [containingFunction, containingClass]
|
||||
: [containingClass];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,6 +740,8 @@ namespace ts.refactor.extractSymbol {
|
||||
}
|
||||
|
||||
const { body, returnValueProperty } = transformFunctionBody(node, exposedVariableDeclarations, writes, substitutions, !!(range.facts & RangeFacts.HasReturn));
|
||||
suppressLeadingAndTrailingTrivia(body);
|
||||
|
||||
let newFunction: MethodDeclaration | FunctionDeclaration;
|
||||
|
||||
if (isClassLike(scope)) {
|
||||
@@ -923,15 +928,10 @@ namespace ts.refactor.extractSymbol {
|
||||
}
|
||||
}
|
||||
|
||||
if (isReadonlyArray(range.range)) {
|
||||
changeTracker.replaceNodesWithNodes(context.file, range.range, newNodes, {
|
||||
nodeSeparator: context.newLineCharacter,
|
||||
suffix: context.newLineCharacter // insert newline only when replacing statements
|
||||
});
|
||||
}
|
||||
else {
|
||||
changeTracker.replaceNodeWithNodes(context.file, range.range, newNodes, { nodeSeparator: context.newLineCharacter });
|
||||
}
|
||||
const replacementRange = isReadonlyArray(range.range)
|
||||
? { pos: first(range.range).getStart(), end: last(range.range).end }
|
||||
: { pos: range.range.getStart(), end: range.range.end };
|
||||
changeTracker.replaceRangeWithNodes(context.file, replacementRange, newNodes, { nodeSeparator: context.newLineCharacter });
|
||||
|
||||
const edits = changeTracker.getChanges();
|
||||
const renameRange = isReadonlyArray(range.range) ? first(range.range) : range.range;
|
||||
@@ -979,6 +979,7 @@ namespace ts.refactor.extractSymbol {
|
||||
: checker.typeToTypeNode(checker.getContextualType(node), scope, NodeBuilderFlags.NoTruncation);
|
||||
|
||||
const initializer = transformConstantInitializer(node, substitutions);
|
||||
suppressLeadingAndTrailingTrivia(initializer);
|
||||
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
|
||||
@@ -1011,7 +1012,7 @@ namespace ts.refactor.extractSymbol {
|
||||
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariable, { suffix: context.newLineCharacter + context.newLineCharacter });
|
||||
|
||||
// Consume
|
||||
changeTracker.replaceNodeWithNodes(context.file, node, [localReference], { nodeSeparator: context.newLineCharacter });
|
||||
changeTracker.replaceRange(context.file, { pos: node.getStart(), end: node.end }, localReference);
|
||||
}
|
||||
else {
|
||||
const newVariableDeclaration = createVariableDeclaration(localNameText, variableType, initializer);
|
||||
|
||||
@@ -1369,4 +1369,39 @@ namespace ts {
|
||||
|
||||
return visited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets EmitFlags to suppress leading and trailing trivia on the node.
|
||||
*/
|
||||
/* @internal */
|
||||
export function suppressLeadingAndTrailingTrivia(node: Node) {
|
||||
Debug.assert(node !== undefined);
|
||||
|
||||
suppressLeading(node);
|
||||
suppressTrailing(node);
|
||||
|
||||
function suppressLeading(node: Node) {
|
||||
addEmitFlags(node, EmitFlags.NoLeadingComments);
|
||||
|
||||
const firstChild = forEachChild(node, child => child);
|
||||
firstChild && suppressLeading(firstChild);
|
||||
}
|
||||
|
||||
function suppressTrailing(node: Node) {
|
||||
addEmitFlags(node, EmitFlags.NoTrailingComments);
|
||||
|
||||
let lastChild: Node = undefined;
|
||||
forEachChild(
|
||||
node,
|
||||
child => (lastChild = child, undefined),
|
||||
children => {
|
||||
// As an optimization, jump straight to the end of the list.
|
||||
if (children.length) {
|
||||
lastChild = last(children);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
lastChild && suppressTrailing(lastChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user