Skip to content

Commit 27e2f89

Browse files
author
Andy Hanson
committed
Merge branch 'master' into sortAndDeduplicate
2 parents abcd48e + 1c45903 commit 27e2f89

23 files changed

+264
-71
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4608,7 +4608,7 @@ namespace ts {
46084608
if (!names.has(prop.escapedName)
46094609
&& !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected))
46104610
&& isSpreadableProperty(prop)) {
4611-
members.set(prop.escapedName, getNonReadonlySymbol(prop));
4611+
members.set(prop.escapedName, getSpreadSymbol(prop));
46124612
}
46134613
}
46144614
const stringIndexInfo = getIndexInfoOfType(source, IndexKind.String);
@@ -9887,7 +9887,7 @@ namespace ts {
98879887
skippedPrivateMembers.set(rightProp.escapedName, true);
98889888
}
98899889
else if (isSpreadableProperty(rightProp)) {
9890-
members.set(rightProp.escapedName, getNonReadonlySymbol(rightProp));
9890+
members.set(rightProp.escapedName, getSpreadSymbol(rightProp));
98919891
}
98929892
}
98939893

@@ -9911,7 +9911,7 @@ namespace ts {
99119911
}
99129912
}
99139913
else {
9914-
members.set(leftProp.escapedName, getNonReadonlySymbol(leftProp));
9914+
members.set(leftProp.escapedName, getSpreadSymbol(leftProp));
99159915
}
99169916
}
99179917

@@ -9929,18 +9929,19 @@ namespace ts {
99299929

99309930
/** We approximate own properties as non-methods plus methods that are inside the object literal */
99319931
function isSpreadableProperty(prop: Symbol): boolean {
9932-
return prop.flags & (SymbolFlags.Method | SymbolFlags.GetAccessor)
9933-
? !prop.declarations.some(decl => isClassLike(decl.parent))
9934-
: !(prop.flags & SymbolFlags.SetAccessor); // Setter without getter is not spreadable
9932+
return !(prop.flags & (SymbolFlags.Method | SymbolFlags.GetAccessor | SymbolFlags.SetAccessor)) ||
9933+
!prop.declarations.some(decl => isClassLike(decl.parent));
99359934
}
99369935

9937-
function getNonReadonlySymbol(prop: Symbol) {
9938-
if (!isReadonlySymbol(prop)) {
9936+
function getSpreadSymbol(prop: Symbol) {
9937+
const isReadonly = isReadonlySymbol(prop);
9938+
const isSetonlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor);
9939+
if (!isReadonly && !isSetonlyAccessor) {
99399940
return prop;
99409941
}
99419942
const flags = SymbolFlags.Property | (prop.flags & SymbolFlags.Optional);
99429943
const result = createSymbol(flags, prop.escapedName);
9943-
result.type = getTypeOfSymbol(prop);
9944+
result.type = isSetonlyAccessor ? undefinedType : getTypeOfSymbol(prop);
99449945
result.declarations = prop.declarations;
99459946
result.nameType = prop.nameType;
99469947
result.syntheticOrigin = prop;

src/harness/client.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,10 @@ namespace ts.server {
694694
return response.body!.map(entry => this.decodeSpan(entry, fileName)); // TODO: GH#18217
695695
}
696696

697+
configurePlugin(pluginName: string, configuration: any): void {
698+
this.processRequest<protocol.ConfigurePluginRequest>("configurePlugin", { pluginName, configuration });
699+
}
700+
697701
getIndentationAtPosition(_fileName: string, _position: number, _options: EditorOptions): number {
698702
return notImplemented();
699703
}

src/harness/fourslash.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3076,6 +3076,10 @@ Actual: ${stringify(fullActual)}`);
30763076
}
30773077
}
30783078
}
3079+
3080+
public configurePlugin(pluginName: string, configuration: any): void {
3081+
(<ts.server.SessionClient>this.languageService).configurePlugin(pluginName, configuration);
3082+
}
30793083
}
30803084

30813085
function updateTextRangeForTextChanges({ pos, end }: ts.TextRange, textChanges: ReadonlyArray<ts.TextChange>): ts.TextRange {
@@ -3139,19 +3143,20 @@ Actual: ${stringify(fullActual)}`);
31393143
function runCode(code: string, state: TestState): void {
31403144
// Compile and execute the test
31413145
const wrappedCode =
3142-
`(function(test, goTo, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) {
3146+
`(function(test, goTo, plugins, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) {
31433147
${code}
31443148
})`;
31453149
try {
31463150
const test = new FourSlashInterface.Test(state);
31473151
const goTo = new FourSlashInterface.GoTo(state);
3152+
const plugins = new FourSlashInterface.Plugins(state);
31483153
const verify = new FourSlashInterface.Verify(state);
31493154
const edit = new FourSlashInterface.Edit(state);
31503155
const debug = new FourSlashInterface.Debug(state);
31513156
const format = new FourSlashInterface.Format(state);
31523157
const cancellation = new FourSlashInterface.Cancellation(state);
31533158
const f = eval(wrappedCode);
3154-
f(test, goTo, verify, edit, debug, format, cancellation, FourSlashInterface.Classification, FourSlashInterface.Completion, verifyOperationIsCancelled);
3159+
f(test, goTo, plugins, verify, edit, debug, format, cancellation, FourSlashInterface.Classification, FourSlashInterface.Completion, verifyOperationIsCancelled);
31553160
}
31563161
catch (err) {
31573162
throw err;
@@ -3651,6 +3656,15 @@ namespace FourSlashInterface {
36513656
}
36523657
}
36533658

3659+
export class Plugins {
3660+
constructor (private state: FourSlash.TestState) {
3661+
}
3662+
3663+
public configurePlugin(pluginName: string, configuration: any): void {
3664+
this.state.configurePlugin(pluginName, configuration);
3665+
}
3666+
}
3667+
36543668
export class GoTo {
36553669
constructor(private state: FourSlash.TestState) {
36563670
}

src/harness/harnessLanguageService.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,36 @@ namespace Harness.LanguageService {
833833
error: undefined
834834
};
835835

836+
// Accepts configurations
837+
case "configurable-diagnostic-adder":
838+
let customMessage = "default message";
839+
return {
840+
module: () => ({
841+
create(info: ts.server.PluginCreateInfo) {
842+
customMessage = info.config.message;
843+
const proxy = makeDefaultProxy(info);
844+
proxy.getSemanticDiagnostics = filename => {
845+
const prev = info.languageService.getSemanticDiagnostics(filename);
846+
const sourceFile: ts.SourceFile = info.project.getSourceFile(ts.toPath(filename, /*basePath*/ undefined, ts.createGetCanonicalFileName(info.serverHost.useCaseSensitiveFileNames)))!;
847+
prev.push({
848+
category: ts.DiagnosticCategory.Error,
849+
file: sourceFile,
850+
code: 9999,
851+
length: 3,
852+
messageText: customMessage,
853+
start: 0
854+
});
855+
return prev;
856+
};
857+
return proxy;
858+
},
859+
onConfigurationChanged(config: any) {
860+
customMessage = config.message;
861+
}
862+
}),
863+
error: undefined
864+
};
865+
836866
default:
837867
return {
838868
module: undefined,

src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3766,7 +3766,7 @@
37663766
<Str Cat="Text">
37673767
<Val><![CDATA[Extract constant]]></Val>
37683768
<Tgt Cat="Text" Stat="Loc" Orig="New">
3769-
<Val><![CDATA[解壓縮常數]]></Val>
3769+
<Val><![CDATA[擷取常數]]></Val>
37703770
</Tgt>
37713771
</Str>
37723772
<Disp Icon="Str" />
@@ -3775,7 +3775,7 @@
37753775
<Str Cat="Text">
37763776
<Val><![CDATA[Extract function]]></Val>
37773777
<Tgt Cat="Text" Stat="Loc" Orig="New">
3778-
<Val><![CDATA[解壓縮函式]]></Val>
3778+
<Val><![CDATA[擷取函式]]></Val>
37793779
</Tgt>
37803780
</Str>
37813781
<Disp Icon="Str" />

src/server/editorServices.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,8 @@ namespace ts.server {
509509
public readonly globalPlugins: ReadonlyArray<string>;
510510
public readonly pluginProbeLocations: ReadonlyArray<string>;
511511
public readonly allowLocalPluginLoads: boolean;
512+
private currentPluginConfigOverrides: Map<any> | undefined;
513+
512514
public readonly typesMapLocation: string | undefined;
513515

514516
public readonly syntaxOnly?: boolean;
@@ -1742,7 +1744,7 @@ namespace ts.server {
17421744
project.enableLanguageService();
17431745
project.watchWildcards(createMapFromTemplate(parsedCommandLine.wildcardDirectories!)); // TODO: GH#18217
17441746
}
1745-
project.enablePluginsWithOptions(compilerOptions);
1747+
project.enablePluginsWithOptions(compilerOptions, this.currentPluginConfigOverrides);
17461748
const filesToAdd = parsedCommandLine.fileNames.concat(project.getExternalFiles());
17471749
this.updateRootAndOptionsOfNonInferredProject(project, filesToAdd, fileNamePropertyReader, compilerOptions, parsedCommandLine.typeAcquisition!, parsedCommandLine.compileOnSave!); // TODO: GH#18217
17481750
}
@@ -1932,7 +1934,7 @@ namespace ts.server {
19321934

19331935
private createInferredProject(currentDirectory: string | undefined, isSingleInferredProject?: boolean, projectRootPath?: NormalizedPath): InferredProject {
19341936
const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects;
1935-
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath, currentDirectory);
1937+
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath, currentDirectory, this.currentPluginConfigOverrides);
19361938
if (isSingleInferredProject) {
19371939
this.inferredProjects.unshift(project);
19381940
}
@@ -2880,6 +2882,16 @@ namespace ts.server {
28802882

28812883
return false;
28822884
}
2885+
2886+
configurePlugin(args: protocol.ConfigurePluginRequestArguments) {
2887+
// For any projects that already have the plugin loaded, configure the plugin
2888+
this.forEachEnabledProject(project => project.onPluginConfigurationChanged(args.pluginName, args.configuration));
2889+
2890+
// Also save the current configuration to pass on to any projects that are yet to be loaded.
2891+
// If a plugin is configured twice, only the latest configuration will be remembered.
2892+
this.currentPluginConfigOverrides = this.currentPluginConfigOverrides || createMap();
2893+
this.currentPluginConfigOverrides.set(args.pluginName, args.configuration);
2894+
}
28832895
}
28842896

28852897
/* @internal */

src/server/project.ts

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ namespace ts.server {
7272
export interface PluginModule {
7373
create(createInfo: PluginCreateInfo): LanguageService;
7474
getExternalFiles?(proj: Project): string[];
75+
onConfigurationChanged?(config: any): void;
76+
}
77+
78+
export interface PluginModuleWithName {
79+
name: string;
80+
module: PluginModule;
7581
}
7682

7783
export type PluginModuleFactory = (mod: { typescript: typeof ts }) => PluginModule;
@@ -92,7 +98,7 @@ namespace ts.server {
9298
private program: Program;
9399
private externalFiles: SortedReadonlyArray<string>;
94100
private missingFilesMap: Map<FileWatcher>;
95-
private plugins: PluginModule[] = [];
101+
private plugins: PluginModuleWithName[] = [];
96102

97103
/*@internal*/
98104
/**
@@ -549,9 +555,9 @@ namespace ts.server {
549555

550556
getExternalFiles(): SortedReadonlyArray<string> {
551557
return sort(flatMap(this.plugins, plugin => {
552-
if (typeof plugin.getExternalFiles !== "function") return;
558+
if (typeof plugin.module.getExternalFiles !== "function") return;
553559
try {
554-
return plugin.getExternalFiles(this);
560+
return plugin.module.getExternalFiles(this);
555561
}
556562
catch (e) {
557563
this.projectService.logger.info(`A plugin threw an exception in getExternalFiles: ${e}`);
@@ -1111,7 +1117,7 @@ namespace ts.server {
11111117
this.rootFilesMap.delete(info.path);
11121118
}
11131119

1114-
protected enableGlobalPlugins(options: CompilerOptions) {
1120+
protected enableGlobalPlugins(options: CompilerOptions, pluginConfigOverrides: Map<any> | undefined) {
11151121
const host = this.projectService.host;
11161122

11171123
if (!host.require) {
@@ -1134,12 +1140,13 @@ namespace ts.server {
11341140

11351141
// Provide global: true so plugins can detect why they can't find their config
11361142
this.projectService.logger.info(`Loading global plugin ${globalPluginName}`);
1137-
this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths);
1143+
1144+
this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths, pluginConfigOverrides);
11381145
}
11391146
}
11401147
}
11411148

1142-
protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]) {
1149+
protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[], pluginConfigOverrides: Map<any> | undefined) {
11431150
this.projectService.logger.info(`Enabling plugin ${pluginConfigEntry.name} from candidate paths: ${searchPaths.join(",")}`);
11441151

11451152
const log = (message: string) => {
@@ -1149,18 +1156,21 @@ namespace ts.server {
11491156
const resolvedModule = firstDefined(searchPaths, searchPath =>
11501157
<PluginModuleFactory | undefined>Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log));
11511158
if (resolvedModule) {
1159+
const configurationOverride = pluginConfigOverrides && pluginConfigOverrides.get(pluginConfigEntry.name);
1160+
if (configurationOverride) {
1161+
// Preserve the name property since it's immutable
1162+
const pluginName = pluginConfigEntry.name;
1163+
pluginConfigEntry = configurationOverride;
1164+
pluginConfigEntry.name = pluginName;
1165+
}
1166+
11521167
this.enableProxy(resolvedModule, pluginConfigEntry);
11531168
}
11541169
else {
11551170
this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name}`);
11561171
}
11571172
}
11581173

1159-
/** Starts a new check for diagnostics. Call this if some file has updated that would cause diagnostics to be changed. */
1160-
refreshDiagnostics() {
1161-
this.projectService.sendProjectsUpdatedInBackgroundEvent();
1162-
}
1163-
11641174
private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) {
11651175
try {
11661176
if (typeof pluginModuleFactory !== "function") {
@@ -1186,12 +1196,26 @@ namespace ts.server {
11861196
}
11871197
this.projectService.logger.info(`Plugin validation succeded`);
11881198
this.languageService = newLS;
1189-
this.plugins.push(pluginModule);
1199+
this.plugins.push({ name: configEntry.name, module: pluginModule });
11901200
}
11911201
catch (e) {
11921202
this.projectService.logger.info(`Plugin activation failed: ${e}`);
11931203
}
11941204
}
1205+
1206+
/*@internal*/
1207+
onPluginConfigurationChanged(pluginName: string, configuration: any) {
1208+
this.plugins.filter(plugin => plugin.name === pluginName).forEach(plugin => {
1209+
if (plugin.module.onConfigurationChanged) {
1210+
plugin.module.onConfigurationChanged(configuration);
1211+
}
1212+
});
1213+
}
1214+
1215+
/** Starts a new check for diagnostics. Call this if some file has updated that would cause diagnostics to be changed. */
1216+
refreshDiagnostics() {
1217+
this.projectService.sendProjectsUpdatedInBackgroundEvent();
1218+
}
11951219
}
11961220

11971221
/**
@@ -1247,7 +1271,8 @@ namespace ts.server {
12471271
documentRegistry: DocumentRegistry,
12481272
compilerOptions: CompilerOptions,
12491273
projectRootPath: NormalizedPath | undefined,
1250-
currentDirectory: string | undefined) {
1274+
currentDirectory: string | undefined,
1275+
pluginConfigOverrides: Map<any> | undefined) {
12511276
super(InferredProject.newName(),
12521277
ProjectKind.Inferred,
12531278
projectService,
@@ -1263,7 +1288,7 @@ namespace ts.server {
12631288
if (!projectRootPath && !projectService.useSingleInferredProject) {
12641289
this.canonicalCurrentDirectory = projectService.toCanonicalFileName(this.currentDirectory);
12651290
}
1266-
this.enableGlobalPlugins(this.getCompilerOptions());
1291+
this.enableGlobalPlugins(this.getCompilerOptions(), pluginConfigOverrides);
12671292
}
12681293

12691294
addRoot(info: ScriptInfo) {
@@ -1419,12 +1444,8 @@ namespace ts.server {
14191444
return program && program.forEachResolvedProjectReference(cb);
14201445
}
14211446

1422-
enablePlugins() {
1423-
this.enablePluginsWithOptions(this.getCompilerOptions());
1424-
}
1425-
14261447
/*@internal*/
1427-
enablePluginsWithOptions(options: CompilerOptions) {
1448+
enablePluginsWithOptions(options: CompilerOptions, pluginConfigOverrides: Map<any> | undefined) {
14281449
const host = this.projectService.host;
14291450

14301451
if (!host.require) {
@@ -1445,11 +1466,11 @@ namespace ts.server {
14451466
// Enable tsconfig-specified plugins
14461467
if (options.plugins) {
14471468
for (const pluginConfigEntry of options.plugins) {
1448-
this.enablePlugin(pluginConfigEntry, searchPaths);
1469+
this.enablePlugin(pluginConfigEntry, searchPaths, pluginConfigOverrides);
14491470
}
14501471
}
14511472

1452-
this.enableGlobalPlugins(options);
1473+
this.enableGlobalPlugins(options, pluginConfigOverrides);
14531474
}
14541475

14551476
/**

src/server/protocol.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ namespace ts.server.protocol {
129129
GetEditsForFileRename = "getEditsForFileRename",
130130
/* @internal */
131131
GetEditsForFileRenameFull = "getEditsForFileRename-full",
132+
ConfigurePlugin = "configurePlugin"
132133

133134
// NOTE: If updating this, be sure to also update `allCommandNames` in `harness/unittests/session.ts`.
134135
}
@@ -1375,6 +1376,16 @@ namespace ts.server.protocol {
13751376
export interface ConfigureResponse extends Response {
13761377
}
13771378

1379+
export interface ConfigurePluginRequestArguments {
1380+
pluginName: string;
1381+
configuration: any;
1382+
}
1383+
1384+
export interface ConfigurePluginRequest extends Request {
1385+
command: CommandTypes.ConfigurePlugin;
1386+
arguments: ConfigurePluginRequestArguments;
1387+
}
1388+
13781389
/**
13791390
* Information found in an "open" request.
13801391
*/

0 commit comments

Comments
 (0)