Skip to content

Commit

Permalink
- Added support for completion and file reference (Ctrl+Click, Rename…
Browse files Browse the repository at this point in the history
…..) for various file and folder related functions and methods

- Added file mode completion support for SplFileInfo::openFile
- Added basic file url completion support for header('Location: ... and header('Content-Location: ...
  • Loading branch information
King2500 committed Aug 17, 2013
1 parent 5642399 commit 5f8cccd
Show file tree
Hide file tree
Showing 11 changed files with 390 additions and 28 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

### 1.0.2
* Added support for completion and file reference (Ctrl+Click, Rename..) for various file and folder related functions and methods
* Added file mode completion support for SplFileInfo::openFile
* Added basic file url completion support for header('Location: ... and header('Content-Location: ...

### 1.0.1
* Added infos for date format characters
* Added infos for fopen/popen file modes
Expand Down
16 changes: 13 additions & 3 deletions META-INF/plugin.xml
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<idea-plugin version="2">
<id>net.king2500.plugins.PhpAdvancedAutoComplete</id>
<name>PHP Advanced AutoComplete</name>
<version>1.0.1</version>
<version>1.0.2</version>
<vendor email="phpstorm@king2500.net" url="https://github.com/King2500/PhpAdvancedAutoComplete">Thomas Schulz</vendor>

<description><![CDATA[
<p>Adds auto-completion support for various built-in PHP functions and methods, where parameter is a string literal.</p>
<p>The following functions are currently supported:</p>
<ul>
<li><b>header/header_remove</b><br>
HTTP response headers, status codes, charsets, mime-types, and much more<br><br></li>
HTTP response headers, status codes, charsets, mime-types, locations, and much more<br><br></li>
<li><b>File and folder related functions and methods (fopen, file_get_contents, dir...)</b><br>
Files and/or folders paths relative to the current file (completion and reference)<br><br></li>
<li><b>date</b><br>
Format characters and common format strings<br><br></li>
<li><b>htmlentities/htmlspecialchars</b><br>
Expand All @@ -20,7 +22,7 @@
Known INI variable names<br><br></li>
<li><b>extension_loaded</b><br>
Known PHP extensions<br><br></li>
<li><b>fopen/popen</b><br>
<li><b>fopen/popen/SplFileInfo::openFile</b><br>
File modes<br><br></li>
<li><b>mysql_connect/mysqli_connect/mysqli/PDO</b><br>
Hostnames, database names and usernames from data sources defined in project<br><br></li>
Expand All @@ -39,6 +41,13 @@

<change-notes><![CDATA[
<h2>1.0.2</h2>
<ul>
<li>Added support for completion and file reference (Ctrl+Click, Rename..) for various file and folder related functions and methods</li>
<li>Added file mode completion support for SplFileInfo::openFile</li>
<li>Added basic file url completion support for header('Location: ... and header('Content-Location: ...</li>
</ul>
<h2>1.0.1</h2>
<ul>
<li>Added infos for date format characters</li>
Expand Down Expand Up @@ -78,6 +87,7 @@
</actions>

<extensions defaultExtensionNs="com.intellij">
<psi.referenceContributor implementation="net.king2500.plugins.PhpAdvancedAutoComplete.PhpFileReferenceContributor"/>
<completion.contributor language="PHP" implementationClass="net.king2500.plugins.PhpAdvancedAutoComplete.PhpFunctionCompletionContributor"/>
</extensions>
</idea-plugin>
Binary file modified PhpAdvancedAutoComplete.jar
Binary file not shown.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ Adds auto-completion support for various built-in PHP functions and methods, whe
The following functions are currently supported:

* <b>header / header_remove</b><br>
HTTP response headers, status codes, charsets, mime-types, and much more
HTTP response headers, status codes, charsets, mime-types, locations, and much more

* <b>File and folder related functions and methods (fopen, file_get_contents, dir...)</b><br>
Files and/or folders paths relative to the current file (completion and reference)

* <b>date</b><br>
Format characters and common format strings
Expand All @@ -25,7 +28,7 @@ The following functions are currently supported:
* <b>extension_loaded</b><br>
Known PHP extensions

* <b>fopen / popen</b><br>
* <b>fopen / popen / SplFileInfo::openFile</b><br>
File modes

* <b>mysql_connect/mysqli_connect/mysqli/PDO</b><br>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package net.king2500.plugins.PhpAdvancedAutoComplete;

import com.intellij.codeInsight.completion.InsertHandler;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFileSystemItem;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Created with IntelliJ IDEA.
* User: Thomas
* Date: 16.08.13
* Time: 21:59
*/
public class GenericFileLookupElement extends LookupElement {
private String fileName;
private PsiFileSystemItem psiFile;
private PsiElement psiElement = null;

@Nullable
private InsertHandler<LookupElement> insertHandler = null;

public GenericFileLookupElement(String fileName, PsiFileSystemItem psiFile) {
this.fileName = fileName;
this.psiFile = psiFile;
}

public GenericFileLookupElement(String fileName, PsiFileSystemItem psiFile, PsiElement psiElement, InsertHandler<LookupElement> insertHandler) {
this.fileName = fileName;
this.psiFile = psiFile;
this.insertHandler = insertHandler;
this.psiElement = psiElement;
}

@NotNull
@Override
public String getLookupString() {
return fileName;
}

@NotNull
public Object getObject() {
return this.psiElement != null ? this.psiElement : super.getObject();
}

public void handleInsert(InsertionContext context) {
if (this.insertHandler != null) {
this.insertHandler.handleInsert(context, this);
}
}

public void renderElement(LookupElementPresentation presentation) {
presentation.setItemText(getLookupString());
presentation.setIcon(psiFile.getIcon(0));
//presentation.setTypeText(VfsUtil.getRelativePath(psiFile.getContainingDirectory().getVirtualFile(), psiFile.getProject().getBaseDir(), '/'));
//presentation.setTypeGrayed(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package net.king2500.plugins.PhpAdvancedAutoComplete;

import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.psi.*;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import net.king2500.plugins.PhpAdvancedAutoComplete.utils.FileHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* Created with IntelliJ IDEA.
* User: Thomas
* Date: 16.08.13
* Time: 21:29
*/
public class GenericFileReference extends PsiReferenceBase<PsiElement> implements PsiReference {

private String fileName;
private int fileType;

public GenericFileReference(@NotNull StringLiteralExpression element, int type) {
super(element);

fileName = element.getText().substring(
element.getValueRange().getStartOffset(),
element.getValueRange().getEndOffset()
);

fileType = type;
}

@Nullable
@Override
public PsiElement resolve() {
Map<String, PsiFileSystemItem> filesByName = FileHelper.getRelativeFilesByName(getElement().getContainingFile(), fileType);

return filesByName.get(fileName);
}

@NotNull
@Override
public Object[] getVariants() {
List<LookupElement> results = new ArrayList<LookupElement>();

Map<String, PsiFileSystemItem> filesByName = FileHelper.getRelativeFilesByName(getElement().getContainingFile(), fileType);
for (Map.Entry<String, PsiFileSystemItem> entry : filesByName.entrySet()) {
results.add(
new GenericFileLookupElement(entry.getKey(), entry.getValue())
);
}

return results.toArray();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,14 @@ public class PhpCompletionTokens {
public static String[] mbStringLanguageFuncs = { "mb_language" };
public static String[] mbStringLanguageElements = { "uni", "en", "English", "ja", "Japanese" };

public static String[] obHandlerFuncs = { "ob_start" };
public static String[] obHandlerElements = { "ob_gzhandler" };

public static String[] dateFormatFuncs = { "date" };
public static String[] dateFormatTokens = { "d", "D", "j", "jS", "l", "N", "S", "w", "z", "W", "F", "m", "M", "n", "t", "L", "o", "Y", "y", "a", "A", "B", "g", "G", "h", "H", "i", "s", "u", "e", "I", "O", "P", "T", "Z", "c", "r", "U", "H:i", "H:i:s", "m.d.y", "m.d.Y", "m/d/Y", "d.m.y", "d.m.Y", "d.m.Y H:i:s", "Ymd", "Y-m-d", "Y-m-d H:i:s", "Y/m/d H:i:s", "F j, Y, g:i a" };
public static String[] dateFormatInfos = { "Day of month (01..31)", "Weekday (Mon..Sun)", "Day of month (1..31)", "Day of month (1st..31th)", "Weekday (Monday..Sunday)", "Weekday (1..7)", "Day of month suffix (st, nd, rd, th)", "Weekday (0..6)", "Day of year (0..365)", "Week of year (1..52)", "Month (January..December)", "Month (01..12)", "Month (Jan..Dec)", "Month (1..12)", "Number of days in month (28..31)", "Leap year (1=yes, 0=no)", "Year in ISO-8601 (ex. 2013)", "Year (ex. 2013)", "Year (ex. 13)", "am or pm", "AM or PM", "Swatch Internet time (000..999)", "Hour (1..12)", "Hour (0..23)", "Hour (01..12)", "Hour (00..23)", "Minutes (00..59)", "Seconds (00..59)", "Microseconds (000000..999999)", "Timezone identifier (UTC, GMT, ...)", "Daylight Saving Time (1=yes, 0=no)", "GMT in hours (ex. +0200)", "GMT with colon (ex. +02:00)", "Timezone abbreviation (EST, MDT, ...)", "Timezone offset in seconds (-43200..50400)", "ISO 8601 formatted date", "RFC 2822 formatted date", "Seconds since Jan 1st, 1970", "Hour and minutes", "Hour, minutes and seconds", "Month, day, year", "Month, day, year", "Month, day, year", "Day, month, year", "Day, month, year", "Day, month, year, hour, minutes, seconds", "Year, month, day", "Year, month, day", "Year, month, day, hour, minutes, seconds", "Year, month, day, hour, minutes, seconds", "Month, day, year, hour, minutes, am/pm" };

public static String[] fileModeFuncs = { "fopen", "popen" };
public static String[] fileModeFuncs = { "fopen:1", "popen:1", "SplFileInfo::openFile:0" };
public static String[] fileModeElements = { "r", "r+", "w", "w+", "a", "a+", "x", "x+", "c", "c+" };
public static String[] fileModeInfos = {
"Read from beginning", "Read/write from beginning",
Expand All @@ -262,4 +265,6 @@ public class PhpCompletionTokens {
"Create/write from beginning (fails if file exists)", "Create/write/read from beginning (fails if file exists)",
"Create/write from beginning", "Create/write/read from beginning"
};

public static String[] fileFuncs = { "basename:0", "chgrp:0", "chmod:0", "chown:0", "clearstatcache:1", "copy:0", "copy:1", "dirname:0", "file_exists:0", "file_get_contents:0:f", "file_put_contents:0:f", "file:0:f", "fileatime:0", "filectime:0", "filegroup:0", "fileinode:0", "filemtime:0", "fileowner:0", "fileperms:0", "filesize:0:f", "filetype:0:f", "fopen:0:f", "is_dir:0:d", "is_executable:0:f", "is_file:0:f", "is_link:0", "is_readable:0:f", "is_writable:0:f", "is_writeable:0:f", "lchgrp:0", "lchown:0", "link:0", "link:1", "linkinfo:0", "lstat:0", "move_uploaded_file:1:f", "parse_ini_file:0:f", "pathinfo:0", "readfile:0:f", "readlink:0", "realpath:0", "rename:0", "rename:1", "stat:0", "symlink:0", "symlink:1", "touch:0", "unlink:0:f", "dir:0:d", "chdir:0:d", "chroot:0:d", "mkdir:0:d", "rmdir:0:d", "opendir:0:d", "scandir:0:d", "stream_resolve_include_path:0", "SplFileInfo::__construct:0", "SplFileObject::__construct:0", "DirectoryIterator::__construct:0:d", "FilesystemIterator::__construct:0:d", "RecursiveDirectoryIterator::__construct:0:d", "ZipArchive::open:0:f", "ZipArchive::addFile:0:f", "DOMDocument::load:0:f", "simplexml_load_file:0:f", "SimpleXMLElement::asXML:0:f", "SimpleXMLElement::saveXML:0:f", "XMLWriter::openURI:0:f", "xmlwriter_open_uri:0:f", "XMLReader::open:0:f", "imagecreatefromjpeg:0:f", "ImageCreateFromJpeg:0:f", "imagecreatefrompng:0:f", "ImageCreateFromPng:0:f", "imagecreatefromgif:0:f", "ImageCreateFromGif:0:f" };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package net.king2500.plugins.PhpAdvancedAutoComplete;

import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.*;
import com.intellij.util.ProcessingContext;
import com.jetbrains.php.lang.PhpLanguage;
import com.jetbrains.php.lang.psi.elements.FunctionReference;
import com.jetbrains.php.lang.psi.elements.MethodReference;
import com.jetbrains.php.lang.psi.elements.ParameterList;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import com.jetbrains.php.lang.psi.elements.impl.NewExpressionImpl;
import net.king2500.plugins.PhpAdvancedAutoComplete.utils.FileHelper;
import net.king2500.plugins.PhpAdvancedAutoComplete.utils.PhpHelper;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;

/**
* Created with IntelliJ IDEA.
* User: Thomas
* Date: 16.08.13
* Time: 21:11
*/
public class PhpFileReferenceContributor extends PsiReferenceContributor {
@Override
public void registerReferenceProviders(PsiReferenceRegistrar psiReferenceRegistrar) {
psiReferenceRegistrar.registerReferenceProvider(
PlatformPatterns.psiElement(StringLiteralExpression.class).withLanguage(PhpLanguage.INSTANCE),
new PsiReferenceProvider() {
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @NotNull ProcessingContext processingContext) {
if(!(psiElement.getContext() instanceof ParameterList)) {
return new PsiReference[0];
}

ParameterList parameterList = (ParameterList)psiElement.getContext();

if (parameterList == null || !(parameterList.getContext() instanceof FunctionReference || parameterList.getContext() instanceof MethodReference || parameterList.getContext() instanceof NewExpressionImpl)) {
return new PsiReference[0];
}

String funcName = PhpHelper.getCanonicalFuncName(psiElement.getParent().getParent());
int paramIndex = PhpHelper.getParameterIndex(psiElement);

if(Arrays.asList(PhpCompletionTokens.fileFuncs).contains(funcName + ":" + paramIndex + ":f")) {
return new PsiReference[]{ new GenericFileReference((StringLiteralExpression)psiElement, FileHelper.TYPE_FILE ) };
}
else if(Arrays.asList(PhpCompletionTokens.fileFuncs).contains(funcName + ":" + paramIndex + ":d")) {
return new PsiReference[]{ new GenericFileReference((StringLiteralExpression)psiElement, FileHelper.TYPE_DIR ) };
}
else if(Arrays.asList(PhpCompletionTokens.fileFuncs).contains(funcName + ":" + paramIndex)) {
return new PsiReference[]{ new GenericFileReference((StringLiteralExpression)psiElement, FileHelper.TYPE_ALL ) };
}

return new PsiReference[0];
}
}
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.jetbrains.php.lang.lexer.PhpTokenTypes;
import com.jetbrains.php.lang.parser.PhpElementTypes;
import net.king2500.plugins.PhpAdvancedAutoComplete.utils.DbHelper;
import net.king2500.plugins.PhpAdvancedAutoComplete.utils.FileHelper;
import net.king2500.plugins.PhpAdvancedAutoComplete.utils.PhpHelper;
import org.jetbrains.annotations.NotNull;

Expand Down Expand Up @@ -54,7 +55,7 @@ public void addCompletions(@NotNull CompletionParameters parameters,
boolean resultBold = false;
boolean resultCaseSensitivity = true;

int paramIndex = getParameterIndex(parameters.getPosition().getParent());
int paramIndex = PhpHelper.getParameterIndex(parameters.getPosition().getParent());

if(Arrays.asList(PhpCompletionTokens.iniFuncs).contains(funcName) && paramIndex == 0) {
resultElements = PhpCompletionTokens.iniElements;
Expand Down Expand Up @@ -97,7 +98,13 @@ public void addCompletions(@NotNull CompletionParameters parameters,
resultElements = PhpCompletionTokens.phpExtensionElements;
}

if(Arrays.asList(PhpCompletionTokens.fileModeFuncs).contains(funcName) && paramIndex == 1) {
/*
if(Arrays.asList(PhpCompletionTokens.fileFuncs).contains(funcName + ":" + paramIndex)) {
resultElements = FileHelper.getRelativeFiles(parameters.getPosition().getContainingFile().getOriginalFile());
}
*/

if(Arrays.asList(PhpCompletionTokens.fileModeFuncs).contains(funcName + ":" + paramIndex)) {
resultElements = PhpCompletionTokens.fileModeElements;
resultInfos = PhpCompletionTokens.fileModeInfos;
resultBold = true;
Expand Down Expand Up @@ -127,6 +134,10 @@ public void addCompletions(@NotNull CompletionParameters parameters,
resultElements = PhpCompletionTokens.mbStringLanguageElements;
}

if(Arrays.asList(PhpCompletionTokens.obHandlerFuncs).contains(funcName) && paramIndex == 0) {
resultElements = PhpCompletionTokens.obHandlerElements;
}

if(Arrays.asList(PhpCompletionTokens.httpHeaderResponseFuncs).contains(funcName) && paramIndex == 0) {
String stringLiteral = parameters.getPosition().getText();
String stringPrefix = stringLiteral.substring(1, stringLiteral.indexOf("IntellijIdeaRulezzz"));
Expand All @@ -151,7 +162,8 @@ else if(stringPrefix.startsWith("Content-Language:")) {
resultElements = PhpCompletionTokens.isoLanguageCodes;
}
else if(stringPrefix.startsWith("Content-Location:") || stringPrefix.startsWith("Location:")) {
// TODO: possible locations (.php or .html)
resultElements = prefixArray("/", FileHelper.getProjectFiles(project));
resultElements = concatArrays(new String[] { "/" }, resultElements);
}
else if(stringPrefix.startsWith("Content-Disposition:")) {
resultElements = PhpCompletionTokens.httpContentDispositionTokens;
Expand Down Expand Up @@ -226,27 +238,12 @@ else if(!stringPrefix.contains(":")) {
if(resultInfos.length > 0)
builder = builder.withTypeText(resultInfos[i]);

//LookupElement element = builder.withAutoCompletionPolicy(AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE);
//resultSet.addElement(element);
resultSet.addElement(builder);
}
}

private int getParameterIndex(PsiElement paramElement) {
int index = 0;
PsiElement element = paramElement;

while(element != null && element.getPrevSibling() != null) {
String elementClass = element.getPrevSibling().getClass().getSimpleName();

if(elementClass.equals("LeafPsiElement")) {
index++;
}

element = element.getPrevSibling();
}

return index;
}

private String[] concatArrays(String[] A, String[] B) {
int aLen = A.length;
int bLen = B.length;
Expand All @@ -263,8 +260,6 @@ private String[] prefixArray(String prefix, String[] array) {
}
return B;
}


}
);
}
Expand Down
Loading

0 comments on commit 5f8cccd

Please sign in to comment.