mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-27 03:54:24 +01:00
Fixes #19624: Adopt "php.validate.executablePath" handling to new TS behavior
This commit is contained in:
@@ -79,6 +79,8 @@ namespace RunTrigger {
|
||||
};
|
||||
}
|
||||
|
||||
const CheckedExecutablePath = 'php.validate.checkedExecutablePath';
|
||||
|
||||
export default class PHPValidationProvider {
|
||||
|
||||
private static MatchExpression: RegExp = /(?:(?:Parse|Fatal) error): (.*)(?: in )(.*?)(?: on line )(\d+)/;
|
||||
@@ -86,19 +88,20 @@ export default class PHPValidationProvider {
|
||||
private static FileArgs: string[] = ['-l', '-n', '-d', 'display_errors=On', '-d', 'log_errors=Off', '-f'];
|
||||
|
||||
private validationEnabled: boolean;
|
||||
private executableIsUserDefined: boolean;
|
||||
private executable: string;
|
||||
private trigger: RunTrigger;
|
||||
private executableNotFound: boolean;
|
||||
private pauseValidation: boolean;
|
||||
|
||||
private documentListener: vscode.Disposable;
|
||||
private diagnosticCollection: vscode.DiagnosticCollection;
|
||||
private delayers: { [key: string]: ThrottledDelayer<void> };
|
||||
|
||||
constructor(private workspaceExecutablePath: string) {
|
||||
constructor(private workspaceStore: vscode.Memento) {
|
||||
this.executable = null;
|
||||
this.validationEnabled = true;
|
||||
this.trigger = RunTrigger.onSave;
|
||||
this.executableNotFound = false;
|
||||
this.pauseValidation = false;
|
||||
}
|
||||
|
||||
public activate(subscriptions: vscode.Disposable[]) {
|
||||
@@ -114,17 +117,6 @@ export default class PHPValidationProvider {
|
||||
}, null, subscriptions);
|
||||
}
|
||||
|
||||
public updateWorkspaceExecutablePath(workspaceExecutablePath: string, loadConfig: boolean = false): void {
|
||||
if (workspaceExecutablePath && workspaceExecutablePath.length === 0) {
|
||||
this.workspaceExecutablePath = undefined;
|
||||
} else {
|
||||
this.workspaceExecutablePath = workspaceExecutablePath;
|
||||
}
|
||||
if (loadConfig) {
|
||||
this.loadConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.diagnosticCollection.clear();
|
||||
this.diagnosticCollection.dispose();
|
||||
@@ -135,12 +127,22 @@ export default class PHPValidationProvider {
|
||||
let oldExecutable = this.executable;
|
||||
if (section) {
|
||||
this.validationEnabled = section.get<boolean>('validate.enable', true);
|
||||
this.executable = this.workspaceExecutablePath || section.get<string>('validate.executablePath', null);
|
||||
let inspect = section.inspect<string>('validate.executablePath');
|
||||
if (inspect.workspaceValue) {
|
||||
this.executable = inspect.workspaceValue;
|
||||
this.executableIsUserDefined = false;
|
||||
} else if (inspect.globalValue) {
|
||||
this.executable = inspect.globalValue;
|
||||
this.executableIsUserDefined = true;
|
||||
} else {
|
||||
this.executable = undefined;
|
||||
this.executableIsUserDefined = undefined;
|
||||
}
|
||||
this.trigger = RunTrigger.from(section.get<string>('validate.run', RunTrigger.strings.onSave));
|
||||
}
|
||||
this.delayers = Object.create(null);
|
||||
if (this.executableNotFound) {
|
||||
this.executableNotFound = oldExecutable === this.executable;
|
||||
if (this.pauseValidation) {
|
||||
this.pauseValidation = oldExecutable === this.executable;
|
||||
}
|
||||
if (this.documentListener) {
|
||||
this.documentListener.dispose();
|
||||
@@ -160,16 +162,56 @@ export default class PHPValidationProvider {
|
||||
}
|
||||
|
||||
private triggerValidate(textDocument: vscode.TextDocument): void {
|
||||
if (textDocument.languageId !== 'php' || this.executableNotFound || !this.validationEnabled) {
|
||||
if (textDocument.languageId !== 'php' || this.pauseValidation || !this.validationEnabled) {
|
||||
return;
|
||||
}
|
||||
let key = textDocument.uri.toString();
|
||||
let delayer = this.delayers[key];
|
||||
if (!delayer) {
|
||||
delayer = new ThrottledDelayer<void>(this.trigger === RunTrigger.onType ? 250 : 0);
|
||||
this.delayers[key] = delayer;
|
||||
|
||||
interface MessageItem extends vscode.MessageItem {
|
||||
id: string;
|
||||
}
|
||||
delayer.trigger(() => this.doValidate(textDocument));
|
||||
|
||||
let trigger = () => {
|
||||
let key = textDocument.uri.toString();
|
||||
let delayer = this.delayers[key];
|
||||
if (!delayer) {
|
||||
delayer = new ThrottledDelayer<void>(this.trigger === RunTrigger.onType ? 250 : 0);
|
||||
this.delayers[key] = delayer;
|
||||
}
|
||||
delayer.trigger(() => this.doValidate(textDocument));
|
||||
};
|
||||
|
||||
if (this.executableIsUserDefined !== void 0 && !this.executableIsUserDefined) {
|
||||
let checkedExecutablePath = this.workspaceStore.get<string>(CheckedExecutablePath, undefined);
|
||||
if (!checkedExecutablePath || checkedExecutablePath !== this.executable) {
|
||||
vscode.window.showInformationMessage<MessageItem>(
|
||||
localize('php.useExecutablePath', 'Do you allow {0} to be executed to lint this file?', this.executable),
|
||||
{
|
||||
title: localize('php.yes', 'Yes'),
|
||||
id: 'yes'
|
||||
},
|
||||
{
|
||||
title: localize('php.no', 'No'),
|
||||
isCloseAffordance: true,
|
||||
id: 'no'
|
||||
},
|
||||
{
|
||||
title: localize('php.more', 'Learn More'),
|
||||
id: 'more'
|
||||
}
|
||||
).then(selected => {
|
||||
if (!selected || selected.id === 'no') {
|
||||
this.pauseValidation = true;
|
||||
} else if (selected.id === 'yes') {
|
||||
this.workspaceStore.update(CheckedExecutablePath, this.executable);
|
||||
trigger();
|
||||
} else if (selected.id === 'more') {
|
||||
vscode.commands.executeCommand('vscode.open', vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=839878'));
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
trigger();
|
||||
}
|
||||
|
||||
private doValidate(textDocument: vscode.TextDocument): Promise<void> {
|
||||
@@ -201,12 +243,12 @@ export default class PHPValidationProvider {
|
||||
try {
|
||||
let childProcess = cp.spawn(executable, args, options);
|
||||
childProcess.on('error', (error: Error) => {
|
||||
if (this.executableNotFound) {
|
||||
if (this.pauseValidation) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
this.showError(error, executable);
|
||||
this.executableNotFound = true;
|
||||
this.pauseValidation = true;
|
||||
resolve();
|
||||
});
|
||||
if (childProcess.pid) {
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import PHPCompletionItemProvider from './features/completionItemProvider';
|
||||
import PHPHoverProvider from './features/hoverProvider';
|
||||
import PHPSignatureHelpProvider from './features/signatureHelpProvider';
|
||||
@@ -15,63 +12,11 @@ import * as vscode from 'vscode';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
nls.config({ locale: vscode.env.language });
|
||||
let localize = nls.loadMessageBundle();
|
||||
|
||||
const MigratedKey = 'php.validate.executablePaht.migrated';
|
||||
const PathKey = 'php.validate.executablePath';
|
||||
|
||||
namespace is {
|
||||
const toString = Object.prototype.toString;
|
||||
|
||||
export function string(value: any): value is string {
|
||||
return toString.call(value) === '[object String]';
|
||||
}
|
||||
}
|
||||
|
||||
let statusBarItem: vscode.StatusBarItem;
|
||||
|
||||
export function activate(context: vscode.ExtensionContext): any {
|
||||
|
||||
let workspaceExecutablePath = context.workspaceState.get<string>(PathKey, undefined);
|
||||
let migrated = context.workspaceState.get<boolean>(MigratedKey, false);
|
||||
let validator = new PHPValidationProvider(workspaceExecutablePath);
|
||||
context.subscriptions.push(vscode.commands.registerCommand('_php.onPathClicked', () => {
|
||||
onPathClicked(context, validator);
|
||||
}));
|
||||
|
||||
statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, Number.MIN_VALUE);
|
||||
statusBarItem.text = localize('php.path', 'Path');
|
||||
statusBarItem.color = 'white';
|
||||
statusBarItem.command = '_php.onPathClicked';
|
||||
vscode.workspace.onDidChangeConfiguration(() => updateStatusBarItem(context));
|
||||
vscode.window.onDidChangeActiveTextEditor((editor) => {
|
||||
updateStatusBarItem(context, editor);
|
||||
});
|
||||
updateStatusBarItem(context, vscode.window.activeTextEditor);
|
||||
|
||||
if (workspaceExecutablePath === void 0 && !migrated) {
|
||||
let settingsExecutablePath = readLocalExecutableSetting();
|
||||
if (settingsExecutablePath) {
|
||||
migrateExecutablePath(settingsExecutablePath).then((value) => {
|
||||
context.workspaceState.update(MigratedKey, true);
|
||||
// User has pressed escape;
|
||||
if (!value) {
|
||||
// activate the validator with the current settings.
|
||||
validator.activate(context.subscriptions);
|
||||
return;
|
||||
}
|
||||
context.workspaceState.update(PathKey, value);
|
||||
validator.updateWorkspaceExecutablePath(value, false);
|
||||
validator.activate(context.subscriptions);
|
||||
updateStatusBarItem(context);
|
||||
});
|
||||
} else {
|
||||
context.workspaceState.update(MigratedKey, true);
|
||||
validator.activate(context.subscriptions);
|
||||
}
|
||||
} else {
|
||||
validator.activate(context.subscriptions);
|
||||
}
|
||||
let validator = new PHPValidationProvider(context.workspaceState);
|
||||
validator.activate(context.subscriptions);
|
||||
|
||||
// add providers
|
||||
context.subscriptions.push(vscode.languages.registerCompletionItemProvider('php', new PHPCompletionItemProvider(), '.', '$'));
|
||||
@@ -83,126 +28,4 @@ export function activate(context: vscode.ExtensionContext): any {
|
||||
vscode.languages.setLanguageConfiguration('php', {
|
||||
wordPattern: /(-?\d*\.\d\w*)|([^\-\`\~\!\@\#\%\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g
|
||||
});
|
||||
}
|
||||
|
||||
function updateStatusBarItem(context: vscode.ExtensionContext, editor: vscode.TextEditor = vscode.window.activeTextEditor): void {
|
||||
statusBarItem.tooltip = getExecutablePath(context);
|
||||
if (editor && editor.document && editor.document.languageId === 'php') {
|
||||
statusBarItem.show();
|
||||
} else {
|
||||
statusBarItem.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function onPathClicked(context: vscode.ExtensionContext, validator: PHPValidationProvider) {
|
||||
let value = getExecutablePath(context);
|
||||
vscode.window.showInputBox({ prompt: localize('php.enterPath', 'The path to the PHP executable'), value: value || '' }).then(value => {
|
||||
if (!value) {
|
||||
// User pressed Escape
|
||||
return;
|
||||
}
|
||||
context.workspaceState.update(PathKey, value);
|
||||
validator.updateWorkspaceExecutablePath(value, true);
|
||||
updateStatusBarItem(context);
|
||||
}, (error) => {
|
||||
});
|
||||
}
|
||||
|
||||
function getExecutablePath(context: vscode.ExtensionContext): string {
|
||||
let result = context.workspaceState.get<string>(PathKey, undefined);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
let section = vscode.workspace.getConfiguration('php.validate');
|
||||
if (section) {
|
||||
return section.get('executablePath', undefined);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function migrateExecutablePath(settingsExecutablePath: string): Thenable<string> {
|
||||
return vscode.window.showInformationMessage(
|
||||
localize('php.migrateWorkspaceSetting', 'Do you want to use {0} as your future PHP executable path?', settingsExecutablePath),
|
||||
{
|
||||
title: localize('php.yes', 'Yes'),
|
||||
id: 'yes'
|
||||
},
|
||||
{
|
||||
title: localize('php.edit', 'Edit'),
|
||||
id: 'edit'
|
||||
},
|
||||
{
|
||||
title: localize('php.more', 'Learn More'),
|
||||
id: 'more'
|
||||
}
|
||||
).then((selected) => {
|
||||
if (!selected) {
|
||||
return undefined;
|
||||
}
|
||||
if (selected.id === 'yes') {
|
||||
return settingsExecutablePath;
|
||||
} else if (selected.id === 'edit') {
|
||||
return vscode.window.showInputBox(
|
||||
{
|
||||
prompt: localize('php.migrateExecutablePath', 'Use the above path as the PHP executable path?'),
|
||||
value: settingsExecutablePath
|
||||
}
|
||||
);
|
||||
} else if (selected.id === 'more') {
|
||||
vscode.commands.executeCommand('vscode.open', vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919'));
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function readLocalExecutableSetting(): string {
|
||||
function stripComments(content: string): string {
|
||||
/**
|
||||
* First capturing group matches double quoted string
|
||||
* Second matches single quotes string
|
||||
* Third matches block comments
|
||||
* Fourth matches line comments
|
||||
*/
|
||||
var regexp: RegExp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
|
||||
let result = content.replace(regexp, (match, m1, m2, m3, m4) => {
|
||||
// Only one of m1, m2, m3, m4 matches
|
||||
if (m3) {
|
||||
// A block comment. Replace with nothing
|
||||
return '';
|
||||
} else if (m4) {
|
||||
// A line comment. If it ends in \r?\n then keep it.
|
||||
let length = m4.length;
|
||||
if (length > 2 && m4[length - 1] === '\n') {
|
||||
return m4[length - 2] === '\r' ? '\r\n' : '\n';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
} else {
|
||||
// We match a string
|
||||
return match;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
try {
|
||||
let rootPath = vscode.workspace.rootPath;
|
||||
if (!rootPath) {
|
||||
return undefined;
|
||||
}
|
||||
let settingsFile = path.join(rootPath, '.vscode', 'settings.json');
|
||||
if (!fs.existsSync(settingsFile)) {
|
||||
return undefined;
|
||||
}
|
||||
let content = fs.readFileSync(settingsFile, 'utf8');
|
||||
if (!content || content.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
content = stripComments(content);
|
||||
let json = JSON.parse(content);
|
||||
let value = json['php.validate.executablePath'];
|
||||
return is.string(value) ? value : undefined;
|
||||
} catch (error) {
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user