mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-24 10:38:59 +01:00
Fix #46148 - split PHP extension
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { CompletionItemProvider, CompletionItem, CompletionItemKind, CancellationToken, TextDocument, Position, Range, TextEdit, workspace, CompletionContext } from 'vscode';
|
||||
import phpGlobals = require('./phpGlobals');
|
||||
|
||||
export default class PHPCompletionItemProvider implements CompletionItemProvider {
|
||||
|
||||
public provideCompletionItems(document: TextDocument, position: Position, _token: CancellationToken, context: CompletionContext): Promise<CompletionItem[]> {
|
||||
let result: CompletionItem[] = [];
|
||||
|
||||
let shouldProvideCompletionItems = workspace.getConfiguration('php').get<boolean>('suggest.basic', true);
|
||||
if (!shouldProvideCompletionItems) {
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
var range = document.getWordRangeAtPosition(position);
|
||||
var prefix = range ? document.getText(range) : '';
|
||||
if (!range) {
|
||||
range = new Range(position, position);
|
||||
}
|
||||
|
||||
if (context.triggerCharacter === '>') {
|
||||
const twoBeforeCursor = new Position(position.line, Math.max(0, position.character - 2));
|
||||
const previousTwoChars = document.getText(new Range(twoBeforeCursor, position));
|
||||
if (previousTwoChars !== '->') {
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
|
||||
var added: any = {};
|
||||
var createNewProposal = function (kind: CompletionItemKind, name: string, entry: phpGlobals.IEntry | null): CompletionItem {
|
||||
var proposal: CompletionItem = new CompletionItem(name);
|
||||
proposal.kind = kind;
|
||||
if (entry) {
|
||||
if (entry.description) {
|
||||
proposal.documentation = entry.description;
|
||||
}
|
||||
if (entry.signature) {
|
||||
proposal.detail = entry.signature;
|
||||
}
|
||||
}
|
||||
return proposal;
|
||||
};
|
||||
|
||||
var matches = (name: string) => {
|
||||
return prefix.length === 0 || name.length >= prefix.length && name.substr(0, prefix.length) === prefix;
|
||||
};
|
||||
|
||||
if (matches('php') && range.start.character >= 2) {
|
||||
let twoBeforePosition = new Position(range.start.line, range.start.character - 2);
|
||||
let beforeWord = document.getText(new Range(twoBeforePosition, range.start));
|
||||
|
||||
if (beforeWord === '<?') {
|
||||
let proposal = createNewProposal(CompletionItemKind.Class, '<?php', null);
|
||||
proposal.textEdit = new TextEdit(new Range(twoBeforePosition, position), '<?php');
|
||||
result.push(proposal);
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
|
||||
for (var globalvariables in phpGlobals.globalvariables) {
|
||||
if (phpGlobals.globalvariables.hasOwnProperty(globalvariables) && matches(globalvariables)) {
|
||||
added[globalvariables] = true;
|
||||
result.push(createNewProposal(CompletionItemKind.Variable, globalvariables, phpGlobals.globalvariables[globalvariables]));
|
||||
}
|
||||
}
|
||||
for (var globalfunctions in phpGlobals.globalfunctions) {
|
||||
if (phpGlobals.globalfunctions.hasOwnProperty(globalfunctions) && matches(globalfunctions)) {
|
||||
added[globalfunctions] = true;
|
||||
result.push(createNewProposal(CompletionItemKind.Function, globalfunctions, phpGlobals.globalfunctions[globalfunctions]));
|
||||
}
|
||||
}
|
||||
for (var compiletimeconstants in phpGlobals.compiletimeconstants) {
|
||||
if (phpGlobals.compiletimeconstants.hasOwnProperty(compiletimeconstants) && matches(compiletimeconstants)) {
|
||||
added[compiletimeconstants] = true;
|
||||
result.push(createNewProposal(CompletionItemKind.Field, compiletimeconstants, phpGlobals.compiletimeconstants[compiletimeconstants]));
|
||||
}
|
||||
}
|
||||
for (var keywords in phpGlobals.keywords) {
|
||||
if (phpGlobals.keywords.hasOwnProperty(keywords) && matches(keywords)) {
|
||||
added[keywords] = true;
|
||||
result.push(createNewProposal(CompletionItemKind.Keyword, keywords, phpGlobals.keywords[keywords]));
|
||||
}
|
||||
}
|
||||
|
||||
var text = document.getText();
|
||||
if (prefix[0] === '$') {
|
||||
var variableMatch = /\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/g;
|
||||
var match: RegExpExecArray | null = null;
|
||||
while (match = variableMatch.exec(text)) {
|
||||
var word = match[0];
|
||||
if (!added[word]) {
|
||||
added[word] = true;
|
||||
result.push(createNewProposal(CompletionItemKind.Variable, word, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
var functionMatch = /function\s+([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*\(/g;
|
||||
var match2: RegExpExecArray | null = null;
|
||||
while (match2 = functionMatch.exec(text)) {
|
||||
var word2 = match2[1];
|
||||
if (!added[word2]) {
|
||||
added[word2] = true;
|
||||
result.push(createNewProposal(CompletionItemKind.Function, word2, null));
|
||||
}
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { HoverProvider, Hover, MarkedString, TextDocument, CancellationToken, Position, workspace } from 'vscode';
|
||||
import phpGlobals = require('./phpGlobals');
|
||||
import { textToMarkedString } from './utils/markedTextUtil';
|
||||
|
||||
export default class PHPHoverProvider implements HoverProvider {
|
||||
|
||||
public provideHover(document: TextDocument, position: Position, _token: CancellationToken): Hover | undefined {
|
||||
let enable = workspace.getConfiguration('php').get<boolean>('suggest.basic', true);
|
||||
if (!enable) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let wordRange = document.getWordRangeAtPosition(position);
|
||||
if (!wordRange) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let name = document.getText(wordRange);
|
||||
|
||||
var entry = phpGlobals.globalfunctions[name] || phpGlobals.compiletimeconstants[name] || phpGlobals.globalvariables[name] || phpGlobals.keywords[name];
|
||||
if (entry && entry.description) {
|
||||
let signature = name + (entry.signature || '');
|
||||
let contents: MarkedString[] = [textToMarkedString(entry.description), { language: 'php', value: signature }];
|
||||
return new Hover(contents, wordRange);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
5759
extensions/php-language-features/src/features/phpGlobals.ts
Normal file
5759
extensions/php-language-features/src/features/phpGlobals.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,174 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { SignatureHelpProvider, SignatureHelp, SignatureInformation, CancellationToken, TextDocument, Position, workspace } from 'vscode';
|
||||
import phpGlobals = require('./phpGlobals');
|
||||
|
||||
var _NL = '\n'.charCodeAt(0);
|
||||
var _TAB = '\t'.charCodeAt(0);
|
||||
var _WSB = ' '.charCodeAt(0);
|
||||
var _LBracket = '['.charCodeAt(0);
|
||||
var _RBracket = ']'.charCodeAt(0);
|
||||
var _LCurly = '{'.charCodeAt(0);
|
||||
var _RCurly = '}'.charCodeAt(0);
|
||||
var _LParent = '('.charCodeAt(0);
|
||||
var _RParent = ')'.charCodeAt(0);
|
||||
var _Comma = ','.charCodeAt(0);
|
||||
var _Quote = '\''.charCodeAt(0);
|
||||
var _DQuote = '"'.charCodeAt(0);
|
||||
var _USC = '_'.charCodeAt(0);
|
||||
var _a = 'a'.charCodeAt(0);
|
||||
var _z = 'z'.charCodeAt(0);
|
||||
var _A = 'A'.charCodeAt(0);
|
||||
var _Z = 'Z'.charCodeAt(0);
|
||||
var _0 = '0'.charCodeAt(0);
|
||||
var _9 = '9'.charCodeAt(0);
|
||||
|
||||
var BOF = 0;
|
||||
|
||||
|
||||
class BackwardIterator {
|
||||
private lineNumber: number;
|
||||
private offset: number;
|
||||
private line: string;
|
||||
private model: TextDocument;
|
||||
|
||||
constructor(model: TextDocument, offset: number, lineNumber: number) {
|
||||
this.lineNumber = lineNumber;
|
||||
this.offset = offset;
|
||||
this.line = model.lineAt(this.lineNumber).text;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public hasNext(): boolean {
|
||||
return this.lineNumber >= 0;
|
||||
}
|
||||
|
||||
public next(): number {
|
||||
if (this.offset < 0) {
|
||||
if (this.lineNumber > 0) {
|
||||
this.lineNumber--;
|
||||
this.line = this.model.lineAt(this.lineNumber).text;
|
||||
this.offset = this.line.length - 1;
|
||||
return _NL;
|
||||
}
|
||||
this.lineNumber = -1;
|
||||
return BOF;
|
||||
}
|
||||
var ch = this.line.charCodeAt(this.offset);
|
||||
this.offset--;
|
||||
return ch;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default class PHPSignatureHelpProvider implements SignatureHelpProvider {
|
||||
|
||||
public provideSignatureHelp(document: TextDocument, position: Position, _token: CancellationToken): Promise<SignatureHelp> | null {
|
||||
let enable = workspace.getConfiguration('php').get<boolean>('suggest.basic', true);
|
||||
if (!enable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var iterator = new BackwardIterator(document, position.character - 1, position.line);
|
||||
|
||||
var paramCount = this.readArguments(iterator);
|
||||
if (paramCount < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var ident = this.readIdent(iterator);
|
||||
if (!ident) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var entry = phpGlobals.globalfunctions[ident] || phpGlobals.keywords[ident];
|
||||
if (!entry || !entry.signature) {
|
||||
return null;
|
||||
}
|
||||
var paramsString = entry.signature.substring(0, entry.signature.lastIndexOf(')') + 1);
|
||||
let signatureInfo = new SignatureInformation(ident + paramsString, entry.description);
|
||||
|
||||
var re = /\w*\s+\&?\$[\w_\.]+|void/g;
|
||||
var match: RegExpExecArray | null = null;
|
||||
while ((match = re.exec(paramsString)) !== null) {
|
||||
signatureInfo.parameters.push({ label: match[0], documentation: '' });
|
||||
}
|
||||
let ret = new SignatureHelp();
|
||||
ret.signatures.push(signatureInfo);
|
||||
ret.activeSignature = 0;
|
||||
ret.activeParameter = Math.min(paramCount, signatureInfo.parameters.length - 1);
|
||||
return Promise.resolve(ret);
|
||||
}
|
||||
|
||||
private readArguments(iterator: BackwardIterator): number {
|
||||
var parentNesting = 0;
|
||||
var bracketNesting = 0;
|
||||
var curlyNesting = 0;
|
||||
var paramCount = 0;
|
||||
while (iterator.hasNext()) {
|
||||
var ch = iterator.next();
|
||||
switch (ch) {
|
||||
case _LParent:
|
||||
parentNesting--;
|
||||
if (parentNesting < 0) {
|
||||
return paramCount;
|
||||
}
|
||||
break;
|
||||
case _RParent: parentNesting++; break;
|
||||
case _LCurly: curlyNesting--; break;
|
||||
case _RCurly: curlyNesting++; break;
|
||||
case _LBracket: bracketNesting--; break;
|
||||
case _RBracket: bracketNesting++; break;
|
||||
case _DQuote:
|
||||
case _Quote:
|
||||
while (iterator.hasNext() && ch !== iterator.next()) {
|
||||
// find the closing quote or double quote
|
||||
}
|
||||
break;
|
||||
case _Comma:
|
||||
if (!parentNesting && !bracketNesting && !curlyNesting) {
|
||||
paramCount++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private isIdentPart(ch: number): boolean {
|
||||
if (ch === _USC || // _
|
||||
ch >= _a && ch <= _z || // a-z
|
||||
ch >= _A && ch <= _Z || // A-Z
|
||||
ch >= _0 && ch <= _9 || // 0/9
|
||||
ch >= 0x80 && ch <= 0xFFFF) { // nonascii
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private readIdent(iterator: BackwardIterator): string {
|
||||
var identStarted = false;
|
||||
var ident = '';
|
||||
while (iterator.hasNext()) {
|
||||
var ch = iterator.next();
|
||||
if (!identStarted && (ch === _WSB || ch === _TAB || ch === _NL)) {
|
||||
continue;
|
||||
}
|
||||
if (this.isIdentPart(ch)) {
|
||||
identStarted = true;
|
||||
ident = String.fromCharCode(ch) + ident;
|
||||
} else if (identStarted) {
|
||||
return ident;
|
||||
}
|
||||
}
|
||||
return ident;
|
||||
}
|
||||
|
||||
}
|
||||
185
extensions/php-language-features/src/features/utils/async.ts
Normal file
185
extensions/php-language-features/src/features/utils/async.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export interface ITask<T> {
|
||||
(): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper to prevent accumulation of sequential async tasks.
|
||||
*
|
||||
* Imagine a mail man with the sole task of delivering letters. As soon as
|
||||
* a letter submitted for delivery, he drives to the destination, delivers it
|
||||
* and returns to his base. Imagine that during the trip, N more letters were submitted.
|
||||
* When the mail man returns, he picks those N letters and delivers them all in a
|
||||
* single trip. Even though N+1 submissions occurred, only 2 deliveries were made.
|
||||
*
|
||||
* The throttler implements this via the queue() method, by providing it a task
|
||||
* factory. Following the example:
|
||||
*
|
||||
* var throttler = new Throttler();
|
||||
* var letters = [];
|
||||
*
|
||||
* function letterReceived(l) {
|
||||
* letters.push(l);
|
||||
* throttler.queue(() => { return makeTheTrip(); });
|
||||
* }
|
||||
*/
|
||||
export class Throttler<T> {
|
||||
|
||||
private activePromise: Promise<T> | null;
|
||||
private queuedPromise: Promise<T> | null;
|
||||
private queuedPromiseFactory: ITask<Promise<T>> | null;
|
||||
|
||||
constructor() {
|
||||
this.activePromise = null;
|
||||
this.queuedPromise = null;
|
||||
this.queuedPromiseFactory = null;
|
||||
}
|
||||
|
||||
public queue(promiseFactory: ITask<Promise<T>>): Promise<T> {
|
||||
if (this.activePromise) {
|
||||
this.queuedPromiseFactory = promiseFactory;
|
||||
|
||||
if (!this.queuedPromise) {
|
||||
var onComplete = () => {
|
||||
this.queuedPromise = null;
|
||||
|
||||
var result = this.queue(this.queuedPromiseFactory!);
|
||||
this.queuedPromiseFactory = null;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
this.queuedPromise = new Promise<T>((resolve) => {
|
||||
this.activePromise!.then(onComplete, onComplete).then(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
this.queuedPromise!.then(resolve, reject);
|
||||
});
|
||||
}
|
||||
|
||||
this.activePromise = promiseFactory();
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
this.activePromise!.then((result: T) => {
|
||||
this.activePromise = null;
|
||||
resolve(result);
|
||||
}, (err: any) => {
|
||||
this.activePromise = null;
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper to delay execution of a task that is being requested often.
|
||||
*
|
||||
* Following the throttler, now imagine the mail man wants to optimize the number of
|
||||
* trips proactively. The trip itself can be long, so the he decides not to make the trip
|
||||
* as soon as a letter is submitted. Instead he waits a while, in case more
|
||||
* letters are submitted. After said waiting period, if no letters were submitted, he
|
||||
* decides to make the trip. Imagine that N more letters were submitted after the first
|
||||
* one, all within a short period of time between each other. Even though N+1
|
||||
* submissions occurred, only 1 delivery was made.
|
||||
*
|
||||
* The delayer offers this behavior via the trigger() method, into which both the task
|
||||
* to be executed and the waiting period (delay) must be passed in as arguments. Following
|
||||
* the example:
|
||||
*
|
||||
* var delayer = new Delayer(WAITING_PERIOD);
|
||||
* var letters = [];
|
||||
*
|
||||
* function letterReceived(l) {
|
||||
* letters.push(l);
|
||||
* delayer.trigger(() => { return makeTheTrip(); });
|
||||
* }
|
||||
*/
|
||||
export class Delayer<T> {
|
||||
|
||||
public defaultDelay: number;
|
||||
private timeout: NodeJS.Timer | null;
|
||||
private completionPromise: Promise<T> | null;
|
||||
private onResolve: ((value: T | Thenable<T> | undefined) => void) | null;
|
||||
private task: ITask<T> | null;
|
||||
|
||||
constructor(defaultDelay: number) {
|
||||
this.defaultDelay = defaultDelay;
|
||||
this.timeout = null;
|
||||
this.completionPromise = null;
|
||||
this.onResolve = null;
|
||||
this.task = null;
|
||||
}
|
||||
|
||||
public trigger(task: ITask<T>, delay: number = this.defaultDelay): Promise<T> {
|
||||
this.task = task;
|
||||
this.cancelTimeout();
|
||||
|
||||
if (!this.completionPromise) {
|
||||
this.completionPromise = new Promise<T>((resolve) => {
|
||||
this.onResolve = resolve;
|
||||
}).then(() => {
|
||||
this.completionPromise = null;
|
||||
this.onResolve = null;
|
||||
|
||||
var result = this.task!();
|
||||
this.task = null;
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
this.timeout = setTimeout(() => {
|
||||
this.timeout = null;
|
||||
this.onResolve!(undefined);
|
||||
}, delay);
|
||||
|
||||
return this.completionPromise;
|
||||
}
|
||||
|
||||
public isTriggered(): boolean {
|
||||
return this.timeout !== null;
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
this.cancelTimeout();
|
||||
|
||||
if (this.completionPromise) {
|
||||
this.completionPromise = null;
|
||||
}
|
||||
}
|
||||
|
||||
private cancelTimeout(): void {
|
||||
if (this.timeout !== null) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper to delay execution of a task that is being requested often, while
|
||||
* preventing accumulation of consecutive executions, while the task runs.
|
||||
*
|
||||
* Simply combine the two mail man strategies from the Throttler and Delayer
|
||||
* helpers, for an analogy.
|
||||
*/
|
||||
export class ThrottledDelayer<T> extends Delayer<Promise<T>> {
|
||||
|
||||
private throttler: Throttler<T>;
|
||||
|
||||
constructor(defaultDelay: number) {
|
||||
super(defaultDelay);
|
||||
|
||||
this.throttler = new Throttler<T>();
|
||||
}
|
||||
|
||||
public trigger(promiseFactory: ITask<Promise<T>>, delay?: number): Promise<Promise<T>> {
|
||||
return super.trigger(() => this.throttler.queue(promiseFactory), delay);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { MarkedString } from 'vscode';
|
||||
|
||||
export function textToMarkedString(text: string): MarkedString {
|
||||
return text.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&'); // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import { NodeStringDecoder, StringDecoder } from 'string_decoder';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { ThrottledDelayer } from './utils/async';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
let localize = nls.loadMessageBundle();
|
||||
|
||||
export class LineDecoder {
|
||||
private stringDecoder: NodeStringDecoder;
|
||||
private remaining: string | null;
|
||||
|
||||
constructor(encoding: string = 'utf8') {
|
||||
this.stringDecoder = new StringDecoder(encoding);
|
||||
this.remaining = null;
|
||||
}
|
||||
|
||||
public write(buffer: NodeBuffer): string[] {
|
||||
var result: string[] = [];
|
||||
var value = this.remaining
|
||||
? this.remaining + this.stringDecoder.write(buffer)
|
||||
: this.stringDecoder.write(buffer);
|
||||
|
||||
if (value.length < 1) {
|
||||
return result;
|
||||
}
|
||||
var start = 0;
|
||||
var ch: number;
|
||||
while (start < value.length && ((ch = value.charCodeAt(start)) === 13 || ch === 10)) {
|
||||
start++;
|
||||
}
|
||||
var idx = start;
|
||||
while (idx < value.length) {
|
||||
ch = value.charCodeAt(idx);
|
||||
if (ch === 13 || ch === 10) {
|
||||
result.push(value.substring(start, idx));
|
||||
idx++;
|
||||
while (idx < value.length && ((ch = value.charCodeAt(idx)) === 13 || ch === 10)) {
|
||||
idx++;
|
||||
}
|
||||
start = idx;
|
||||
} else {
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
this.remaining = start < value.length ? value.substr(start) : null;
|
||||
return result;
|
||||
}
|
||||
|
||||
public end(): string | null {
|
||||
return this.remaining;
|
||||
}
|
||||
}
|
||||
|
||||
enum RunTrigger {
|
||||
onSave,
|
||||
onType
|
||||
}
|
||||
|
||||
namespace RunTrigger {
|
||||
export let strings = {
|
||||
onSave: 'onSave',
|
||||
onType: 'onType'
|
||||
};
|
||||
export let from = function (value: string): RunTrigger {
|
||||
if (value === 'onType') {
|
||||
return RunTrigger.onType;
|
||||
} else {
|
||||
return RunTrigger.onSave;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const CheckedExecutablePath = 'php.validate.checkedExecutablePath';
|
||||
|
||||
export default class PHPValidationProvider {
|
||||
|
||||
private static MatchExpression: RegExp = /(?:(?:Parse|Fatal) error): (.*)(?: in )(.*?)(?: on line )(\d+)/;
|
||||
private static BufferArgs: string[] = ['-l', '-n', '-d', 'display_errors=On', '-d', 'log_errors=Off'];
|
||||
private static FileArgs: string[] = ['-l', '-n', '-d', 'display_errors=On', '-d', 'log_errors=Off', '-f'];
|
||||
|
||||
private validationEnabled: boolean;
|
||||
private executableIsUserDefined: boolean | undefined;
|
||||
private executable: string | undefined;
|
||||
private trigger: RunTrigger;
|
||||
private pauseValidation: boolean;
|
||||
|
||||
private documentListener: vscode.Disposable | null = null;
|
||||
private diagnosticCollection?: vscode.DiagnosticCollection;
|
||||
private delayers?: { [key: string]: ThrottledDelayer<void> };
|
||||
|
||||
constructor(private workspaceStore: vscode.Memento) {
|
||||
this.executable = undefined;
|
||||
this.validationEnabled = true;
|
||||
this.trigger = RunTrigger.onSave;
|
||||
this.pauseValidation = false;
|
||||
}
|
||||
|
||||
public activate(subscriptions: vscode.Disposable[]) {
|
||||
this.diagnosticCollection = vscode.languages.createDiagnosticCollection();
|
||||
subscriptions.push(this);
|
||||
vscode.workspace.onDidChangeConfiguration(this.loadConfiguration, this, subscriptions);
|
||||
this.loadConfiguration();
|
||||
|
||||
vscode.workspace.onDidOpenTextDocument(this.triggerValidate, this, subscriptions);
|
||||
vscode.workspace.onDidCloseTextDocument((textDocument) => {
|
||||
this.diagnosticCollection!.delete(textDocument.uri);
|
||||
delete this.delayers![textDocument.uri.toString()];
|
||||
}, null, subscriptions);
|
||||
subscriptions.push(vscode.commands.registerCommand('php.untrustValidationExecutable', this.untrustValidationExecutable, this));
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.diagnosticCollection) {
|
||||
this.diagnosticCollection.clear();
|
||||
this.diagnosticCollection.dispose();
|
||||
}
|
||||
if (this.documentListener) {
|
||||
this.documentListener.dispose();
|
||||
this.documentListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
private loadConfiguration(): void {
|
||||
let section = vscode.workspace.getConfiguration('php');
|
||||
let oldExecutable = this.executable;
|
||||
if (section) {
|
||||
this.validationEnabled = section.get<boolean>('validate.enable', true);
|
||||
let inspect = section.inspect<string>('validate.executablePath');
|
||||
if (inspect && inspect.workspaceValue) {
|
||||
this.executable = inspect.workspaceValue;
|
||||
this.executableIsUserDefined = false;
|
||||
} else if (inspect && 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));
|
||||
}
|
||||
if (this.executableIsUserDefined !== true && this.workspaceStore.get<string | undefined>(CheckedExecutablePath, undefined) !== void 0) {
|
||||
vscode.commands.executeCommand('setContext', 'php.untrustValidationExecutableContext', true);
|
||||
}
|
||||
this.delayers = Object.create(null);
|
||||
if (this.pauseValidation) {
|
||||
this.pauseValidation = oldExecutable === this.executable;
|
||||
}
|
||||
if (this.documentListener) {
|
||||
this.documentListener.dispose();
|
||||
this.documentListener = null;
|
||||
}
|
||||
this.diagnosticCollection!.clear();
|
||||
if (this.validationEnabled) {
|
||||
if (this.trigger === RunTrigger.onType) {
|
||||
this.documentListener = vscode.workspace.onDidChangeTextDocument((e) => {
|
||||
this.triggerValidate(e.document);
|
||||
});
|
||||
} else {
|
||||
this.documentListener = vscode.workspace.onDidSaveTextDocument(this.triggerValidate, this);
|
||||
}
|
||||
// Configuration has changed. Reevaluate all documents.
|
||||
vscode.workspace.textDocuments.forEach(this.triggerValidate, this);
|
||||
}
|
||||
}
|
||||
|
||||
private untrustValidationExecutable() {
|
||||
this.workspaceStore.update(CheckedExecutablePath, undefined);
|
||||
vscode.commands.executeCommand('setContext', 'php.untrustValidationExecutableContext', false);
|
||||
}
|
||||
|
||||
private triggerValidate(textDocument: vscode.TextDocument): void {
|
||||
if (textDocument.languageId !== 'php' || this.pauseValidation || !this.validationEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
interface MessageItem extends vscode.MessageItem {
|
||||
id: string;
|
||||
}
|
||||
|
||||
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 | undefined>(CheckedExecutablePath, undefined);
|
||||
if (!checkedExecutablePath || checkedExecutablePath !== this.executable) {
|
||||
vscode.window.showInformationMessage<MessageItem>(
|
||||
localize('php.useExecutablePath', 'Do you allow {0} (defined as a workspace setting) to be executed to lint PHP files?', this.executable),
|
||||
{
|
||||
title: localize('php.yes', 'Allow'),
|
||||
id: 'yes'
|
||||
},
|
||||
{
|
||||
title: localize('php.no', 'Disallow'),
|
||||
isCloseAffordance: true,
|
||||
id: 'no'
|
||||
}
|
||||
).then(selected => {
|
||||
if (!selected || selected.id === 'no') {
|
||||
this.pauseValidation = true;
|
||||
} else if (selected.id === 'yes') {
|
||||
this.workspaceStore.update(CheckedExecutablePath, this.executable);
|
||||
vscode.commands.executeCommand('setContext', 'php.untrustValidationExecutableContext', true);
|
||||
trigger();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
trigger();
|
||||
}
|
||||
|
||||
private doValidate(textDocument: vscode.TextDocument): Promise<void> {
|
||||
return new Promise<void>((resolve) => {
|
||||
let executable = this.executable || 'php';
|
||||
let decoder = new LineDecoder();
|
||||
let diagnostics: vscode.Diagnostic[] = [];
|
||||
let processLine = (line: string) => {
|
||||
let matches = line.match(PHPValidationProvider.MatchExpression);
|
||||
if (matches) {
|
||||
let message = matches[1];
|
||||
let line = parseInt(matches[3]) - 1;
|
||||
let diagnostic: vscode.Diagnostic = new vscode.Diagnostic(
|
||||
new vscode.Range(line, 0, line, Number.MAX_VALUE),
|
||||
message
|
||||
);
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
};
|
||||
|
||||
let options = vscode.workspace.rootPath ? { cwd: vscode.workspace.rootPath } : undefined;
|
||||
let args: string[];
|
||||
if (this.trigger === RunTrigger.onSave) {
|
||||
args = PHPValidationProvider.FileArgs.slice(0);
|
||||
args.push(textDocument.fileName);
|
||||
} else {
|
||||
args = PHPValidationProvider.BufferArgs;
|
||||
}
|
||||
try {
|
||||
let childProcess = cp.spawn(executable, args, options);
|
||||
childProcess.on('error', (error: Error) => {
|
||||
if (this.pauseValidation) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
this.showError(error, executable);
|
||||
this.pauseValidation = true;
|
||||
resolve();
|
||||
});
|
||||
if (childProcess.pid) {
|
||||
if (this.trigger === RunTrigger.onType) {
|
||||
childProcess.stdin.write(textDocument.getText());
|
||||
childProcess.stdin.end();
|
||||
}
|
||||
childProcess.stdout.on('data', (data: Buffer) => {
|
||||
decoder.write(data).forEach(processLine);
|
||||
});
|
||||
childProcess.stdout.on('end', () => {
|
||||
let line = decoder.end();
|
||||
if (line) {
|
||||
processLine(line);
|
||||
}
|
||||
this.diagnosticCollection!.set(textDocument.uri, diagnostics);
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError(error, executable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private showError(error: any, executable: string): void {
|
||||
let message: string | null = null;
|
||||
if (error.code === 'ENOENT') {
|
||||
if (this.executable) {
|
||||
message = localize('wrongExecutable', 'Cannot validate since {0} is not a valid php executable. Use the setting \'php.validate.executablePath\' to configure the PHP executable.', executable);
|
||||
} else {
|
||||
message = localize('noExecutable', 'Cannot validate since no PHP executable is set. Use the setting \'php.validate.executablePath\' to configure the PHP executable.');
|
||||
}
|
||||
} else {
|
||||
message = error.message ? error.message : localize('unknownReason', 'Failed to run php using path: {0}. Reason is unknown.', executable);
|
||||
}
|
||||
if (message) {
|
||||
vscode.window.showInformationMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
extensions/php-language-features/src/phpMain.ts
Normal file
56
extensions/php-language-features/src/phpMain.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import PHPCompletionItemProvider from './features/completionItemProvider';
|
||||
import PHPHoverProvider from './features/hoverProvider';
|
||||
import PHPSignatureHelpProvider from './features/signatureHelpProvider';
|
||||
import PHPValidationProvider from './features/validationProvider';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext): any {
|
||||
|
||||
let validator = new PHPValidationProvider(context.workspaceState);
|
||||
validator.activate(context.subscriptions);
|
||||
|
||||
// add providers
|
||||
context.subscriptions.push(vscode.languages.registerCompletionItemProvider('php', new PHPCompletionItemProvider(), '>', '$'));
|
||||
context.subscriptions.push(vscode.languages.registerHoverProvider('php', new PHPHoverProvider()));
|
||||
context.subscriptions.push(vscode.languages.registerSignatureHelpProvider('php', new PHPSignatureHelpProvider(), '(', ','));
|
||||
|
||||
// need to set in the extension host as well as the completion provider uses it.
|
||||
vscode.languages.setLanguageConfiguration('php', {
|
||||
wordPattern: /(-?\d*\.\d\w*)|([^\-\`\~\!\@\#\%\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
|
||||
onEnterRules: [
|
||||
{
|
||||
// e.g. /** | */
|
||||
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
|
||||
afterText: /^\s*\*\/$/,
|
||||
action: { indentAction: vscode.IndentAction.IndentOutdent, appendText: ' * ' }
|
||||
},
|
||||
{
|
||||
// e.g. /** ...|
|
||||
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
|
||||
action: { indentAction: vscode.IndentAction.None, appendText: ' * ' }
|
||||
},
|
||||
{
|
||||
// e.g. * ...|
|
||||
beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
|
||||
action: { indentAction: vscode.IndentAction.None, appendText: '* ' }
|
||||
},
|
||||
{
|
||||
// e.g. */|
|
||||
beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
|
||||
action: { indentAction: vscode.IndentAction.None, removeText: 1 }
|
||||
},
|
||||
{
|
||||
// e.g. *-----*/|
|
||||
beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/,
|
||||
action: { indentAction: vscode.IndentAction.None, removeText: 1 }
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
17
extensions/php-language-features/src/test/index.ts
Normal file
17
extensions/php-language-features/src/test/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
var testRunner = require('vscode/lib/testrunner');
|
||||
|
||||
// You can directly control Mocha options by uncommenting the following lines
|
||||
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
|
||||
testRunner.configure({
|
||||
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
|
||||
useColors: true // colored output from test results
|
||||
});
|
||||
|
||||
module.exports = testRunner;
|
||||
*/
|
||||
@@ -0,0 +1,251 @@
|
||||
import assert = require('assert');
|
||||
import {CompletionItemProvider, CompletionItem, CompletionItemKind, CancellationToken, TextDocument, Range, Position, Uri, workspace, window} from 'vscode';
|
||||
import PHPCompletionItemProvider from '../features/completionItemProvider';
|
||||
import HoverProvider from '../features/hoverProvider';
|
||||
import SignatureHelpProvider from '../features/signatureHelpProvider';
|
||||
|
||||
|
||||
var phpCompletionProvider = new PHPCompletionItemProvider();
|
||||
|
||||
var testSuggestionsFor = function(value:string, stringBefore:string):Thenable<CompletionItem[]> {
|
||||
return workspace.openTextDocument(Uri.parse("untitled:/foo/new.js")).then(document => {
|
||||
return window.showTextDocument(document).then(textEditor => {
|
||||
return textEditor.edit(editBuilder => {
|
||||
var lastLineLength = document.lineAt(document.lineCount - 1).text.length;
|
||||
editBuilder.replace(new vscode.Range(new vscode.Position(0, 0), new vscode.Position(textEditor.document.lineCount - 1, lastLineLength)), value);
|
||||
}).then(() => {
|
||||
var idx = stringBefore ? value.indexOf(stringBefore) + stringBefore.length : 0;
|
||||
var position = new Position(0, idx);
|
||||
return phpCompletionProvider.provideCompletionItems(document, position, null);
|
||||
})
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
var assertSuggestion= function(completions:CompletionItem[], label:string, kind: CompletionItemKind) {
|
||||
var entries = completions.filter(suggestion => {
|
||||
return suggestion.label === label;
|
||||
});
|
||||
assert.equal(entries.length, 1);
|
||||
assert.equal(entries[0].kind, kind);
|
||||
};
|
||||
|
||||
suite("PHP", () => {
|
||||
|
||||
test("Intellisense", (testDone:(err?:any) => void) => {
|
||||
Promise.all([
|
||||
testSuggestionsFor('<?php ', 'php ').then(completions => {
|
||||
assertSuggestion(completions, '__CLASS__', CompletionItemKind.Field);
|
||||
assertSuggestion(completions, 'mysql_thread_id', CompletionItemKind.Function);
|
||||
assertSuggestion(completions, '$argc', CompletionItemKind.Variable);
|
||||
}),
|
||||
testSuggestionsFor('<?php mysql_', 'mysql_').then(completions => {
|
||||
assertSuggestion(completions, 'mysql_db_name', CompletionItemKind.Function);
|
||||
assertSuggestion(completions, 'mysql_list_tables', CompletionItemKind.Function);
|
||||
})
|
||||
]).then(() => testDone(), (errors:any[]) => {
|
||||
testDone(errors.reduce((e1, e2) => e1 || e2));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
// /*---------------------------------------------------------
|
||||
// * Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
// *--------------------------------------------------------*/
|
||||
// 'use strict';
|
||||
|
||||
// import assert = require('assert');
|
||||
// import {CompletionItemProvider, CompletionItem, CompletionItemKind, CancellationToken, TextDocument, Range, Position} from 'vscode';
|
||||
|
||||
|
||||
|
||||
// var mockPHPWorkerEnv = function (url:Network.URL, content: string) : { worker: phpWorker.PHPWorker; model: mm.MirrorModel } {
|
||||
// var resourceService = new ResourceService.ResourceService();
|
||||
// var model = mm.createMirrorModelFromString(null, 0, content, modesUtil.createMockMode('mock.mode.id', /(-?\d*\.\d\w*)|(\$[\w-]*)|([\w-]+)/g), url);
|
||||
// resourceService.insert(url, model);
|
||||
|
||||
// let services = servicesUtil2.createMockEditorWorkerServices({
|
||||
// resourceService: resourceService,
|
||||
// });
|
||||
|
||||
// var worker = new phpWorker.PHPWorker(modesUtil.createMockMode('mock.mode.id'), [],
|
||||
// services.resourceService, services.markerService);
|
||||
|
||||
// return { worker: worker, model: model };
|
||||
// };
|
||||
|
||||
// var testComputeInfo = function(value:string, stringBefore:string):WinJS.TPromise<Modes.IComputeExtraInfoResult> {
|
||||
// var url = new Network.URL('test://1');
|
||||
// var env = mockPHPWorkerEnv(url, value);
|
||||
// var idx = stringBefore ? value.indexOf(stringBefore) : value.length;
|
||||
// var position = env.model.getPositionFromOffset(idx);
|
||||
// return env.worker.computeInfo(url, position);
|
||||
// };
|
||||
|
||||
// var testSuggestionsFor = function(value:string, stringBefore:string):WinJS.TPromise<Modes.ISuggestions> {
|
||||
// var url = new Network.URL('test://1');
|
||||
// var env = mockPHPWorkerEnv(url, value);
|
||||
// var idx = stringBefore ? value.indexOf(stringBefore) + stringBefore.length : 0;
|
||||
// var position = env.model.getPositionFromOffset(idx);
|
||||
// return env.worker.suggest(url, position).then(result => result[0]);
|
||||
// };
|
||||
|
||||
// var testParameterHintsFor = function(value:string, stringBefore:string):WinJS.TPromise<Modes.IParameterHints> {
|
||||
// var url = new Network.URL('test://1');
|
||||
// var env = mockPHPWorkerEnv(url, value);
|
||||
// var idx = stringBefore ? value.indexOf(stringBefore) + stringBefore.length : value.length;
|
||||
// var position = env.model.getPositionFromOffset(idx);
|
||||
// return env.worker.getParameterHints(url, position);
|
||||
// };
|
||||
|
||||
// var assertSuggestion= function(completion:Modes.ISuggestions, label:string, type: string) {
|
||||
// var entries = completion.suggestions.filter(function(suggestion: Modes.ISuggestion) {
|
||||
// return suggestion.label === label;
|
||||
// });
|
||||
// assert.equal(entries.length, 1);
|
||||
// assert.equal(entries[0].type, type);
|
||||
// };
|
||||
|
||||
// var assertParameterHints= function(hints:Modes.IParameterHints, paramNames: string[], currentParameter: number) {
|
||||
// assert.equal(hints.signatures.length, 1);
|
||||
// assert.equal(hints.signatures[0].parameters.length, paramNames.length);
|
||||
// var i= 0;
|
||||
// var label = hints.signatures[0].label;
|
||||
// hints.signatures[0].parameters.forEach(function(param) {
|
||||
// assert.equal(param.label, paramNames[i++]);
|
||||
// assert.equal(param.label, label.substring(param.signatureLabelOffset, param.signatureLabelEnd));
|
||||
// });
|
||||
// assert.equal(hints.currentParameter, currentParameter);
|
||||
// };
|
||||
|
||||
|
||||
// suite('PHP - Intellisense', () => {
|
||||
|
||||
// test('Globals', function(testDone):any {
|
||||
// Promise.join([
|
||||
// testSuggestionsFor('<?php ', 'php ').then(function(completion:Modes.ISuggestions):void {
|
||||
// assert.equal(completion.currentWord, '');
|
||||
// assertSuggestion(completion, '__CLASS__', 'field');
|
||||
// assertSuggestion(completion, 'mysql_thread_id', 'function');
|
||||
// assertSuggestion(completion, '$argc', 'variable');
|
||||
// }),
|
||||
// testSuggestionsFor('<?php mysql_', 'mysql_').then(function(completion:Modes.ISuggestions):void {
|
||||
// assert.equal(completion.currentWord, 'mysql_');
|
||||
// assertSuggestion(completion, 'mysql_db_name', 'function');
|
||||
// assertSuggestion(completion, 'mysql_list_tables', 'function');
|
||||
// })
|
||||
// ]).done(() => testDone(), (errors:any[]) => {
|
||||
// testDone(errors.reduce((e1, e2) => e1 || e2));
|
||||
// });
|
||||
// });
|
||||
|
||||
// test('Variables', function(testDone):any {
|
||||
// WinJS.Promise.join([
|
||||
// testSuggestionsFor('<?php $a = 1; $', '$a = 1; $').then(function(completion:Modes.ISuggestions):void {
|
||||
// assert.equal(completion.currentWord, '$');
|
||||
// assertSuggestion(completion, '$a', 'variable');
|
||||
// })
|
||||
// ]).done(() => testDone(), (errors:any[]) => {
|
||||
// testDone(errors.reduce((e1, e2) => e1 || e2));
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
// suite('PHP - Parameter hints', () => {
|
||||
|
||||
// test('Globals', function(testDone): any {
|
||||
// WinJS.Promise.join([
|
||||
// testParameterHintsFor('<?php mysql_data_seek(', null).then(function(hints: Modes.IParameterHints): void {
|
||||
// assertParameterHints(hints, ['$result', '$row_number'], 0);
|
||||
// }),
|
||||
|
||||
// testParameterHintsFor('<?php password_hash(', null).then(function(hints: Modes.IParameterHints): void {
|
||||
// assertParameterHints(hints, ['$password', '$algo', '$options'], 0);
|
||||
// }),
|
||||
// testParameterHintsFor('<?php localtime(', null).then(function(hints: Modes.IParameterHints): void {
|
||||
// assertParameterHints(hints, ['$timestamp', '$is_associative'], 0);
|
||||
// }),
|
||||
// testParameterHintsFor('<?php is_callable(', null).then(function(hints: Modes.IParameterHints): void {
|
||||
// assertParameterHints(hints, ['$name', '$syntax_only', '&$callable_name'], 0);
|
||||
// }),
|
||||
// testParameterHintsFor('<?php array_unshift(', null).then(function(hints: Modes.IParameterHints): void {
|
||||
// assertParameterHints(hints, ['&$array', '$var', '$...'], 0);
|
||||
// })
|
||||
// ]).done(() => testDone(), (errors:any[]) => {
|
||||
// testDone(errors.reduce((e1, e2) => e1 || e2));
|
||||
// });
|
||||
// });
|
||||
|
||||
// test('With arguments', function(testDone): any {
|
||||
// WinJS.Promise.join([
|
||||
// testParameterHintsFor('<?php array_unshift(foo', null).then(function(hints:Modes.IParameterHints):void {
|
||||
// assertParameterHints(hints, ['&$array', '$var', '$...'], 0);
|
||||
// }),
|
||||
// testParameterHintsFor('<?php array_unshift(foo, ', null).then(function(hints:Modes.IParameterHints):void {
|
||||
// assertParameterHints(hints, ['&$array', '$var', '$...'], 1);
|
||||
// }),
|
||||
// testParameterHintsFor('<?php array_unshift(foo, f[]', null).then(function(hints:Modes.IParameterHints):void {
|
||||
// assertParameterHints(hints, ['&$array', '$var', '$...'], 1);
|
||||
// }),
|
||||
// testParameterHintsFor('<?php array_unshift(foo, [a, 2]', null).then(function(hints:Modes.IParameterHints):void {
|
||||
// assertParameterHints(hints, ['&$array', '$var', '$...'], 1);
|
||||
// }),
|
||||
// testParameterHintsFor('<?php array_unshift(foo, [a, 2], x', null).then(function(hints:Modes.IParameterHints):void {
|
||||
// assertParameterHints(hints, ['&$array', '$var', '$...'], 2);
|
||||
// }),
|
||||
// testParameterHintsFor('<?php array_unshift(foo, [a, 2], x, y', null).then(function(hints:Modes.IParameterHints):void {
|
||||
// assertParameterHints(hints, ['&$array', '$var', '$...'], 2);
|
||||
// }),
|
||||
// testParameterHintsFor('<?php array_unshift(foo, f(a, 2), x, y', null).then(function(hints:Modes.IParameterHints):void {
|
||||
// assertParameterHints(hints, ['&$array', '$var', '$...'], 2);
|
||||
// }),
|
||||
// testParameterHintsFor('<?php array_unshift(foo, "f(a, 2"', null).then(function(hints:Modes.IParameterHints):void {
|
||||
// assertParameterHints(hints, ['&$array', '$var', '$...'], 1);
|
||||
// }),
|
||||
// testParameterHintsFor('<?php array_unshift(foo, \'f(a, 2\'', null).then(function(hints:Modes.IParameterHints):void {
|
||||
// assertParameterHints(hints, ['&$array', '$var', '$...'], 1);
|
||||
// })
|
||||
// ]).done(() => testDone(), (errors:any[]) => {
|
||||
// testDone(errors.reduce((e1, e2) => e1 || e2));
|
||||
// });
|
||||
// });
|
||||
|
||||
// test('all function', function(testDone):any {
|
||||
|
||||
// var testFunc = function(hints:Modes.IParameterHints):void {
|
||||
// assert.ok(hints !== null);
|
||||
// assert.equal(1, hints.signatures.length, name);
|
||||
|
||||
// var res= hints.signatures[0].parameters.map(function(param) { return param.label; } ).join(',');
|
||||
// assert.ok(hints.signatures[0].parameters.length > 0, 'parameters for ' + hints.signatures[0].label + ': ' + res);
|
||||
// };
|
||||
// var promises : WinJS.Promise[] = []
|
||||
// for (var name in phpGlobals.globalfunctions) {
|
||||
// if (phpGlobals.globalfunctions.hasOwnProperty(name)) {
|
||||
// var entry = phpGlobals.globalfunctions[name];
|
||||
// if (entry.signature) {
|
||||
// promises.push(testParameterHintsFor('<?php ' + name + '(', null).then(testFunc));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// WinJS.Promise.join(promises).done(() => testDone(), (errors) => {
|
||||
// testDone(errors[0]);
|
||||
// });
|
||||
|
||||
// });
|
||||
// });
|
||||
|
||||
// suite('PHP - compute info', () => {
|
||||
// test('Globals', function(testDone):any {
|
||||
// WinJS.Promise.join([
|
||||
// testComputeInfo('<?php $file=fopen("welcome.txt","r"); ?>', 'fopen').then((hints:Modes.IComputeExtraInfoResult) => {
|
||||
// assert.ok(!!(hints.value || hints.htmlContent));
|
||||
// }),
|
||||
// testComputeInfo('<?php $file=fopen("welcome.txt","r"); ?>', 'welcome').then((hints:Modes.IComputeExtraInfoResult) => {
|
||||
// assert.ok(hints === null);
|
||||
// })
|
||||
// ]).done(() => testDone(), (errors:any[]) => {
|
||||
// testDone(errors.reduce((e1, e2) => e1 || e2));
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
11
extensions/php-language-features/src/typings/node.additions.d.ts
vendored
Normal file
11
extensions/php-language-features/src/typings/node.additions.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer;
|
||||
declare function clearTimeout(timeoutId: NodeJS.Timer): void;
|
||||
declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer;
|
||||
declare function clearInterval(intervalId: NodeJS.Timer): void;
|
||||
declare function setImmediate(callback: (...args: any[]) => void, ...args: any[]): any;
|
||||
declare function clearImmediate(immediateId: any): void;
|
||||
7
extensions/php-language-features/src/typings/refs.d.ts
vendored
Normal file
7
extensions/php-language-features/src/typings/refs.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||
/// <reference types='@types/node'/>
|
||||
Reference in New Issue
Block a user