Skip to content

Commit

Permalink
Implement multiple configs
Browse files Browse the repository at this point in the history
  • Loading branch information
Sander Ronde committed Sep 15, 2024
1 parent 228d517 commit 2d1c174
Show file tree
Hide file tree
Showing 38 changed files with 14,609 additions and 126 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ php/vendor
test/demo/vendor
test/demo/cache
test/demo/reported.json
test/multi-config-demo/vendor
test/multi-config-demo/cache
test/multi-config-demo/reported.json
test/scratchpad
**/reported.json
user_config.json
Expand Down
21 changes: 21 additions & 0 deletions client/src/lib/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,30 @@ export function registerListeners(
context.subscriptions.push(
autoRegisterCommand(
Commands.SCAN_PROJECT,
async () => {
await client.sendNotification(watcherNotification, {
operation: 'checkAllProjects',
});
},
commands
)
);
context.subscriptions.push(
autoRegisterCommand(
Commands.SCAN_CURRENT_PROJECT,
async () => {
await client.sendNotification(watcherNotification, {
operation: 'checkProject',
file: vscode.window.activeTextEditor
? {
content:
vscode.window.activeTextEditor.document.getText(),
uri: vscode.window.activeTextEditor.document.uri.toString(),
languageId:
vscode.window.activeTextEditor.document
.languageId,
}
: null,
});
},
commands
Expand Down
4 changes: 4 additions & 0 deletions client/src/lib/editorConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export function getReadonlyEditorConfiguration(): ConfigWithoutPrefix<ConfigSett
ConfigWithoutPrefix<DeprecatedConfigSettings>;
return {
...configuration,
configFiles: configuration.configFile
? [configuration.configFile]
: configuration.configFiles,
};
}

Expand Down Expand Up @@ -52,6 +55,7 @@ export function registerEditorConfigurationListener(

await client.sendNotification(watcherNotification, {
operation: 'onConfigChange',
file: null,
});

if (e.affectsConfiguration('phpstan.paths')) {
Expand Down
27 changes: 18 additions & 9 deletions client/src/lib/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ export async function launchSetup(client: LanguageClient): Promise<void> {
state[key],
ConfigurationTarget.Workspace
);
if (key === 'configFiles') {
await writableConfig.update(
'phpstan.configFile',
undefined,
ConfigurationTarget.Workspace
);
}
}
};

Expand Down Expand Up @@ -274,14 +281,14 @@ abstract class SetupSteps {
}
return `File does not exist container at \`${filePath}\``;
},
value: this._state.configFile,
value: this._state.configFiles[0],
ignoreFocusOut: true,
buttons: [SHOW_FILE_PICKER_BUTTON],
shouldValidateInitially,
});

if (typeof choice === 'string') {
this._state.configFile = choice;
this._state.configFiles = [choice];
return next;
}

Expand All @@ -292,12 +299,14 @@ abstract class SetupSteps {
title: 'Select config file',
});
if (file) {
this._state.configFile = this._workspaceFolders
? path.relative(
this._workspaceFolders.default.fsPath,
file[0].fsPath
)
: file[0].fsPath;
this._state.configFiles = [
this._workspaceFolders
? path.relative(
this._workspaceFolders.default.fsPath,
file[0].fsPath
)
: file[0].fsPath,
];
}
return (input) => this._configFileStep(input, next, true);
}
Expand Down Expand Up @@ -706,7 +715,7 @@ class DockerSetupSteps extends SetupSteps {
this._localState.lastFoundDockerConfigFile = configFile;
return undefined;
},
value: this._state.configFile,
value: this._state.configFiles[0],
ignoreFocusOut: true,
});

Expand Down
16 changes: 10 additions & 6 deletions client/src/notificationSenders/documentManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ export class DocumentManager implements Disposable {
if (e.isDirty) {
return false;
}
const configFiles = getReadonlyEditorConfiguration()
.configFile.split(',')
.map((e) => e.trim());
for (const configFile of configFiles) {
if (e.uri.fsPath.includes(configFile)) {
return true;
for (const configFileString of getReadonlyEditorConfiguration()
.configFiles) {
const configFiles = configFileString
.split(',')
.map((e) => e.trim());
for (const configFile of configFiles) {
if (e.uri.fsPath.includes(configFile)) {
return true;
}
}
}
return false;
Expand All @@ -49,6 +52,7 @@ export class DocumentManager implements Disposable {
if (this._isConfigFile(e)) {
await this._client.sendNotification(watcherNotification, {
operation: 'onConfigChange',
file: this._toSendData(e),
});
}
if (this._shouldSyncDocument(e)) {
Expand Down
52 changes: 44 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,35 @@
},
"description": "PHPStan command. Use this instead of \"binPath\" if, for example, the phpstan binary is in your path"
},
"phpstan.configFile": {
"type": "string",
"default": "phpstan.neon,phpstan.neon.dist,phpstan.dist.neon",
"examples": [
"phpstan.neon",
"backend/phpstan.neon",
"phpstan.neon,phpstan.neon.dist"
"phpstan.configFiles": {
"type": "array",
"items": {
"type": "string",
"examples": [
"phpstan.neon",
"backend/phpstan.neon",
"phpstan.neon,phpstan.neon.dist"
]
},
"default": [
"phpstan.neon,phpstan.neon.dist,phpstan.dist.neon"
],
"description": "Path to the config file (use a comma-separated list to resolve in order)"
"description": "Paths to all projects' config files (use a comma-separated list to resolve in order)",
"examples": [
[
"phpstan.neon"
],
[
"src/phpstan.neon",
"test/phpstan.neon"
],
[
"backend/phpstan.neon"
],
[
"phpstan.neon,phpstan.neon.dist"
]
]
},
"phpstan.paths": {
"type": "object",
Expand Down Expand Up @@ -185,6 +205,10 @@
"command": "phpstan.scanProjectForErrors",
"title": "Scan project for errors"
},
{
"command": "phpstan.scanCurrentProjectForErrors",
"title": "Scan current project for errors"
},
{
"command": "phpstan.reload",
"title": "Reload language server"
Expand Down Expand Up @@ -213,6 +237,10 @@
"command": "cmd.phpstan.scanProjectForErrors",
"title": "PHPStan: Scan project for errors"
},
{
"command": "cmd.phpstan.scanCurrentProjectForErrors",
"title": "PHPStan: Scan current project for errors"
},
{
"command": "cmd.phpstan.reload",
"title": "PHPStan: Reload language server"
Expand Down Expand Up @@ -244,6 +272,10 @@
"command": "phpstan.scanProjectForErrors",
"when": "false"
},
{
"command": "phpstan.scanCurrentProjectForErrors",
"when": "false"
},
{
"command": "phpstan.reload",
"when": "false"
Expand Down Expand Up @@ -272,6 +304,10 @@
"command": "cmd.phpstan.scanProjectForErrors",
"when": "true"
},
{
"command": "cmd.phpstan.scanCurrentProjectForErrors",
"when": "true"
},
{
"command": "cmd.phpstan.reload",
"when": "true"
Expand Down
54 changes: 40 additions & 14 deletions server/src/lib/checkConfigManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { execute, getConfigFile, getPathMapper } from '../../../shared/util';
import { getEditorConfiguration } from './editorConfig';
import { showErrorOnce } from './errorUtil';
import { showError, showErrorOnce } from './errorUtil';
import { isInPaths } from '../../../shared/neon';
import type { ClassConfig } from './types';
import type { URI } from 'vscode-uri';
import * as fs from 'fs/promises';
import { constants } from 'fs';
import * as path from 'path';
Expand All @@ -15,7 +17,7 @@ export interface CheckConfig {
args: string[];
memoryLimit: string;
tmpDir: string | undefined;
operation: 'analyse' | 'diagnose';
operation: 'analyse';
}

export class ConfigurationManager {
Expand Down Expand Up @@ -91,24 +93,43 @@ export class ConfigurationManager {

private static async _getConfigFile(
classConfig: ClassConfig,
cwd: string
cwd: string,
currentFile: URI | null
): Promise<string | null> {
const extensionConfig = await getEditorConfiguration(classConfig);
const absoluteConfigPath = await getConfigFile(
extensionConfig.configFile,
cwd
);
if (!absoluteConfigPath) {
// Config file was set but not found
if (extensionConfig.configFile) {

for (const configFile of extensionConfig.configFiles) {
const absoluteConfigPath = await getConfigFile(configFile, cwd);
if (!absoluteConfigPath) {
// Config file was set but not found
await showErrorOnce(
classConfig.connection,
`PHPStan: failed to find config file in "${extensionConfig.configFile}"`
`PHPStan: failed to find config file in "${configFile}"`
);
return null;
}

if (extensionConfig.configFiles.length > 1) {
if (!currentFile) {
showError(
classConfig.connection,
'PHPStan: multiple config files provided but no file is open'
);
return null;
}

if (
currentFile.fsPath === absoluteConfigPath ||
(await isInPaths(currentFile.fsPath, absoluteConfigPath))
) {
return absoluteConfigPath;
}
} else {
return absoluteConfigPath;
}
}

return absoluteConfigPath;
return null;
}

public static async getCwd(
Expand Down Expand Up @@ -211,7 +232,8 @@ export class ConfigurationManager {

public static async collectConfiguration(
classConfig: ClassConfig,
operation: 'analyse' | 'diagnose',
operation: 'analyse',
currentFile: URI | null,
onError: null | ((error: string) => void)
): Promise<CheckConfig | null> {
// Settings
Expand All @@ -233,7 +255,11 @@ export class ConfigurationManager {
}
return null;
}
const configFile = await this._getConfigFile(classConfig, cwd);
const configFile = await this._getConfigFile(
classConfig,
cwd,
currentFile
);
if (!configFile) {
if (onError) {
onError('Failed to find config file');
Expand Down
Loading

0 comments on commit 2d1c174

Please sign in to comment.