Merge IJI-2443

GitOrigin-RevId: a68106cf773ceab89cc73135f697e172f65e6c45
This commit is contained in:
Dmitriy.Panov
2024-12-20 15:25:01 +01:00
committed by intellij-monorepo-bot
155 changed files with 7658 additions and 0 deletions

11
plugins/env-files-support/.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
/.idea/
/out/
/dotenv.jar
/idea-flex.skeleton
/jflex-1.7.0.jar
/src/main/gen/ru/adelf/idea/dotenv/grammars/_DotEnvLexer.java~
/build/
/.gradle/
/*.iml
/*.ipr
/*.iws

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Adel Fayzrakhmanov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,70 @@
plugins {
id("org.jetbrains.intellij") version "1.14.0"
}
group = "ru.adelf"
version = "2023.2"
repositories {
mavenCentral()
}
apply {
plugin("idea")
plugin("org.jetbrains.intellij")
plugin("java")
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
sourceSets {
main {
java.srcDirs("src/main/java", "src/main/gen")
resources.srcDirs("src/main/resources")
}
test {
java.srcDirs("src/test/java")
resources.srcDirs("src/test/resources")
}
}
intellij {
version.set("IU-223.8617.56")
plugins.set(
listOf(
"com.jetbrains.php:223.8617.59",
"yaml",
"org.jetbrains.plugins.go:223.8617.56",
"Docker",
"pythonid:223.8617.56",
"org.jetbrains.plugins.ruby:223.8617.56",
"Kotlin",
"java-i18n",
"properties",
"java"
)
)
pluginName.set(".env files support")
}
tasks {
patchPluginXml {
sinceBuild.set("223")
untilBuild.set("232.*")
}
buildSearchableOptions {
enabled = false
}
runPluginVerifier {
ideVersions.set(listOf("IU-223.4884.69"))
}
}
tasks.withType<ProcessResources> {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PLUGIN_MODULE" version="4">
<component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/resources/META-INF/plugin.xml" />
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<content url="file://$MODULE_DIR$/build" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
plugins/env-files-support/gradlew vendored Normal file
View File

@@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
plugins/env-files-support/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,43 @@
IntelliJ IDEA .env files support plugin
-------------
[![Version](http://phpstorm.espend.de/badge/9525/version)](https://plugins.jetbrains.com/plugin/9525)
[![Downloads](http://phpstorm.espend.de/badge/9525/downloads)](https://plugins.jetbrains.com/plugin/9525)
[![Downloads last month](http://phpstorm.espend.de/badge/9525/last-month)](https://plugins.jetbrains.com/plugin/9525)
Plugin url: https://plugins.jetbrains.com/plugin/9525
## Main features
+ .env files support for PhpStorm, RubyMine and PyCharm.
+ docker-compose.yml and Dockerfile environment variables fetching
+ Go to declaration and Find usages of environment variables
## Sponsors
<br>
<a href="https://laravel-idea.com/?utm_source=idea&utm_campaign=env&utm_medium=banner" title="Laravel Idea" target="_blank">
<img src="https://laravel-idea.com/img/big_logo.png" alt="Laravel Idea"></a>
The most productive Laravel development environment.
<a href="https://laravel-idea.com/?utm_source=idea&utm_campaign=env&utm_medium=banner" target="_blank">Learn more</a>.
## Language packages
PHP dotenv package: https://github.com/vlucas/phpdotenv
JavaScript dotenv package: https://github.com/motdotla/dotenv
Python dotenv package: https://github.com/theskumar/python-dotenv
Ruby dotenv gem: https://github.com/bkeepers/dotenv
Go dotenv library: https://github.com/joho/godotenv
## Screenshots
![phpstorm dotenv support](https://plugins.jetbrains.com/files/9525/screenshot_16858.png)
![](src/main/resources/META-INF/pluginIcon.svg)
<div>Plugin icon made by <a href="https://www.flaticon.com/authors/freepik" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a></div>

View File

@@ -0,0 +1,585 @@
/* The following code was generated by JFlex 1.7.0 tweaked for IntelliJ platform */
package ru.adelf.idea.dotenv.grammars;
import com.intellij.lexer.FlexLexer;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
import ru.adelf.idea.dotenv.psi.DotEnvTypes;
/**
* This class is a scanner generated by
* <a href="http://www.jflex.de/">JFlex</a> 1.7.0
* from the specification file <tt>DotEnvLexer.flex</tt>
*/
class _DotEnvLexer implements FlexLexer {
/** This character denotes the end of file */
public static final int YYEOF = -1;
/** initial size of the lookahead buffer */
private static final int ZZ_BUFFERSIZE = 16384;
/** lexical states */
public static final int YYINITIAL = 0;
public static final int WAITING_KEY = 2;
public static final int WAITING_VALUE = 4;
public static final int WAITING_QUOTED_VALUE = 6;
public static final int WAITING_SINGLE_QUOTED_VALUE = 8;
public static final int WAITING_COMMENT = 10;
/**
* ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
* ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l
* at the beginning of a line
* l is of the form l = 2*k, k a non negative integer
*/
private static final int ZZ_LEXSTATE[] = {
0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5
};
/**
* Translates characters to character classes
* Chosen bits are [9, 6, 6]
* Total runtime size is 1568 bytes
*/
public static int ZZ_CMAP(int ch) {
return ZZ_CMAP_A[(ZZ_CMAP_Y[ZZ_CMAP_Z[ch>>12]|((ch>>6)&0x3f)]<<6)|(ch&0x3f)];
}
/* The ZZ_CMAP_Z table has 272 entries */
static final char ZZ_CMAP_Z[] = zzUnpackCMap(
"\1\0\1\100\1\200\u010d\100");
/* The ZZ_CMAP_Y table has 192 entries */
static final char ZZ_CMAP_Y[] = zzUnpackCMap(
"\1\0\1\1\1\2\175\3\1\4\77\3");
/* The ZZ_CMAP_A table has 320 entries */
static final char ZZ_CMAP_A[] = zzUnpackCMap(
"\11\0\1\4\1\2\1\1\1\5\1\3\22\0\1\7\1\0\1\12\1\11\3\0\1\6\22\0\1\13\2\0\1\13"+
"\7\0\1\14\11\0\1\17\1\16\1\0\1\20\1\0\1\21\3\0\1\15\3\0\1\10\10\0\1\14\11"+
"\0\1\17\1\16\1\0\1\20\1\0\1\21\3\0\1\15\14\0\1\1\242\0\2\1\26\0");
/**
* Translates DFA states to action switch labels.
*/
private static final int [] ZZ_ACTION = zzUnpackAction();
private static final String ZZ_ACTION_PACKED_0 =
"\6\0\2\1\1\2\1\3\1\4\1\5\1\1\1\3"+
"\2\6\2\7\1\10\1\7\1\3\1\11\1\12\1\13"+
"\1\3\1\14\1\15\1\3\3\11\1\0\2\4\1\1"+
"\1\2\1\6\2\0\1\2\4\1\1\16";
private static int [] zzUnpackAction() {
int [] result = new int[45];
int offset = 0;
offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
return result;
}
private static int zzUnpackAction(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int count = packed.charAt(i++);
int value = packed.charAt(i++);
do result[j++] = value; while (--count > 0);
}
return j;
}
/**
* Translates a state to a row index in the transition table
*/
private static final int [] ZZ_ROWMAP = zzUnpackRowMap();
private static final String ZZ_ROWMAP_PACKED_0 =
"\0\0\0\22\0\44\0\66\0\110\0\132\0\154\0\176"+
"\0\220\0\242\0\264\0\306\0\330\0\306\0\352\0\374"+
"\0\u010e\0\220\0\306\0\u0120\0\u0132\0\306\0\306\0\u0144"+
"\0\u0156\0\306\0\u0168\0\u017a\0\u018c\0\u019e\0\u01b0\0\242"+
"\0\u01c2\0\u01d4\0\u01e6\0\374\0\u01f8\0\u0156\0\u017a\0\u019e"+
"\0\u020a\0\u021c\0\u022e\0\u0240\0\u0252";
private static int [] zzUnpackRowMap() {
int [] result = new int[45];
int offset = 0;
offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
return result;
}
private static int zzUnpackRowMap(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int high = packed.charAt(i++) << 16;
result[j++] = high | packed.charAt(i++);
}
return j;
}
/**
* The transition table of the DFA
*/
private static final int [] ZZ_TRANS = zzUnpackTrans();
private static final String ZZ_TRANS_PACKED_0 =
"\1\7\1\10\1\11\1\10\2\11\1\7\1\11\1\12"+
"\1\13\1\7\1\14\1\15\6\7\1\10\1\11\1\10"+
"\2\11\1\7\1\11\1\12\2\7\1\16\6\7\1\17"+
"\1\20\2\11\1\21\1\22\1\23\1\24\1\25\1\26"+
"\1\27\7\17\10\30\1\31\1\30\1\32\7\30\6\33"+
"\1\32\1\33\1\34\11\33\1\35\1\36\2\11\1\37"+
"\1\36\1\35\1\37\12\35\2\7\1\0\1\7\2\0"+
"\1\7\1\0\1\40\2\7\1\0\7\7\1\10\1\11"+
"\1\10\2\11\1\7\1\11\1\40\2\7\1\0\6\7"+
"\1\0\5\11\1\0\1\11\21\0\1\7\12\0\2\13"+
"\1\0\1\7\2\41\1\13\1\41\1\42\2\13\1\41"+
"\6\13\22\0\2\7\1\0\1\7\2\0\1\7\1\0"+
"\1\40\2\7\1\0\1\7\1\43\4\7\2\17\2\0"+
"\5\17\1\0\11\17\1\44\2\11\2\44\1\17\1\44"+
"\1\17\1\0\11\17\1\45\2\11\2\21\1\17\1\21"+
"\1\17\1\0\10\17\1\0\3\11\2\24\1\0\1\24"+
"\12\0\1\17\3\0\1\17\1\0\14\17\10\30\1\46"+
"\1\30\1\0\31\30\6\33\1\0\1\33\1\47\33\33"+
"\2\35\2\0\17\35\1\50\2\11\2\50\1\35\1\50"+
"\13\35\1\37\2\11\2\37\1\35\1\37\12\35\2\41"+
"\2\0\20\41\2\0\3\41\1\13\12\41\2\7\1\0"+
"\1\7\2\0\1\7\1\0\1\40\2\7\1\0\2\7"+
"\1\51\3\7\1\17\1\45\2\11\2\45\1\17\1\45"+
"\1\17\1\0\10\17\2\7\1\0\1\7\2\0\1\7"+
"\1\0\1\40\2\7\1\0\3\7\1\52\4\7\1\0"+
"\1\7\2\0\1\7\1\0\1\40\2\7\1\0\4\7"+
"\1\53\3\7\1\0\1\7\2\0\1\7\1\0\1\40"+
"\2\7\1\0\5\7\1\54\2\7\1\0\1\7\2\0"+
"\1\7\1\55\1\40\2\7\1\0\6\7\7\0\1\55"+
"\12\0";
private static int [] zzUnpackTrans() {
int [] result = new int[612];
int offset = 0;
offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
return result;
}
private static int zzUnpackTrans(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int count = packed.charAt(i++);
int value = packed.charAt(i++);
value--;
do result[j++] = value; while (--count > 0);
}
return j;
}
/* error codes */
private static final int ZZ_UNKNOWN_ERROR = 0;
private static final int ZZ_NO_MATCH = 1;
private static final int ZZ_PUSHBACK_2BIG = 2;
/* error messages for the codes above */
private static final String[] ZZ_ERROR_MSG = {
"Unknown internal scanner error",
"Error: could not match input",
"Error: pushback value was too large"
};
/**
* ZZ_ATTRIBUTE[aState] contains the attributes of state <code>aState</code>
*/
private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
private static final String ZZ_ATTRIBUTE_PACKED_0 =
"\6\0\5\1\1\11\1\1\1\11\4\1\1\11\2\1"+
"\2\11\2\1\1\11\5\1\1\0\5\1\2\0\6\1";
private static int [] zzUnpackAttribute() {
int [] result = new int[45];
int offset = 0;
offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
return result;
}
private static int zzUnpackAttribute(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int count = packed.charAt(i++);
int value = packed.charAt(i++);
do result[j++] = value; while (--count > 0);
}
return j;
}
/** the input device */
private java.io.Reader zzReader;
/** the current state of the DFA */
private int zzState;
/** the current lexical state */
private int zzLexicalState = YYINITIAL;
/** this buffer contains the current text to be matched and is
the source of the yytext() string */
private CharSequence zzBuffer = "";
/** the textposition at the last accepting state */
private int zzMarkedPos;
/** the current text position in the buffer */
private int zzCurrentPos;
/** startRead marks the beginning of the yytext() string in the buffer */
private int zzStartRead;
/** endRead marks the last character in the buffer, that has been read
from input */
private int zzEndRead;
/**
* zzAtBOL == true <=> the scanner is currently at the beginning of a line
*/
private boolean zzAtBOL = true;
/** zzAtEOF == true <=> the scanner is at the EOF */
private boolean zzAtEOF;
/** denotes if the user-EOF-code has already been executed */
private boolean zzEOFDone;
/**
* Creates a new scanner
*
* @param in the java.io.Reader to read input from.
*/
_DotEnvLexer(java.io.Reader in) {
this.zzReader = in;
}
/**
* Unpacks the compressed character translation table.
*
* @param packed the packed character translation table
* @return the unpacked character translation table
*/
private static char [] zzUnpackCMap(String packed) {
int size = 0;
for (int i = 0, length = packed.length(); i < length; i += 2) {
size += packed.charAt(i);
}
char[] map = new char[size];
int i = 0; /* index in packed string */
int j = 0; /* index in unpacked array */
while (i < packed.length()) {
int count = packed.charAt(i++);
char value = packed.charAt(i++);
do map[j++] = value; while (--count > 0);
}
return map;
}
public final int getTokenStart() {
return zzStartRead;
}
public final int getTokenEnd() {
return getTokenStart() + yylength();
}
public void reset(CharSequence buffer, int start, int end, int initialState) {
zzBuffer = buffer;
zzCurrentPos = zzMarkedPos = zzStartRead = start;
zzAtEOF = false;
zzAtBOL = true;
zzEndRead = end;
yybegin(initialState);
}
/**
* Refills the input buffer.
*
* @return <code>false</code>, iff there was new input.
*
* @exception java.io.IOException if any I/O-Error occurs
*/
private boolean zzRefill() throws java.io.IOException {
return true;
}
/**
* Returns the current lexical state.
*/
public final int yystate() {
return zzLexicalState;
}
/**
* Enters a new lexical state
*
* @param newState the new lexical state
*/
public final void yybegin(int newState) {
zzLexicalState = newState;
}
/**
* Returns the text matched by the current regular expression.
*/
public final CharSequence yytext() {
return zzBuffer.subSequence(zzStartRead, zzMarkedPos);
}
/**
* Returns the character at position <tt>pos</tt> from the
* matched text.
*
* It is equivalent to yytext().charAt(pos), but faster
*
* @param pos the position of the character to fetch.
* A value from 0 to yylength()-1.
*
* @return the character at position pos
*/
public final char yycharat(int pos) {
return zzBuffer.charAt(zzStartRead+pos);
}
/**
* Returns the length of the matched text region.
*/
public final int yylength() {
return zzMarkedPos-zzStartRead;
}
/**
* Reports an error that occured while scanning.
*
* In a wellformed scanner (no or only correct usage of
* yypushback(int) and a match-all fallback rule) this method
* will only be called with things that "Can't Possibly Happen".
* If this method is called, something is seriously wrong
* (e.g. a JFlex bug producing a faulty scanner etc.).
*
* Usual syntax/scanner level error handling should be done
* in error fallback rules.
*
* @param errorCode the code of the errormessage to display
*/
private void zzScanError(int errorCode) {
String message;
try {
message = ZZ_ERROR_MSG[errorCode];
}
catch (ArrayIndexOutOfBoundsException e) {
message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
}
throw new Error(message);
}
/**
* Pushes the specified amount of characters back into the input stream.
*
* They will be read again by then next call of the scanning method
*
* @param number the number of characters to be read again.
* This number must not be greater than yylength()!
*/
public void yypushback(int number) {
if ( number > yylength() )
zzScanError(ZZ_PUSHBACK_2BIG);
zzMarkedPos -= number;
}
/**
* Resumes scanning until the next regular expression is matched,
* the end of input is encountered or an I/O-Error occurs.
*
* @return the next token
* @exception java.io.IOException if any I/O-Error occurs
*/
public IElementType advance() throws java.io.IOException {
int zzInput;
int zzAction;
// cached fields:
int zzCurrentPosL;
int zzMarkedPosL;
int zzEndReadL = zzEndRead;
CharSequence zzBufferL = zzBuffer;
int [] zzTransL = ZZ_TRANS;
int [] zzRowMapL = ZZ_ROWMAP;
int [] zzAttrL = ZZ_ATTRIBUTE;
while (true) {
zzMarkedPosL = zzMarkedPos;
zzAction = -1;
zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
zzState = ZZ_LEXSTATE[zzLexicalState];
// set up zzAction for empty match case:
int zzAttributes = zzAttrL[zzState];
if ( (zzAttributes & 1) == 1 ) {
zzAction = zzState;
}
zzForAction: {
while (true) {
if (zzCurrentPosL < zzEndReadL) {
zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL/*, zzEndReadL*/);
zzCurrentPosL += Character.charCount(zzInput);
}
else if (zzAtEOF) {
zzInput = YYEOF;
break zzForAction;
}
else {
// store back cached positions
zzCurrentPos = zzCurrentPosL;
zzMarkedPos = zzMarkedPosL;
boolean eof = zzRefill();
// get translated positions and possibly new buffer
zzCurrentPosL = zzCurrentPos;
zzMarkedPosL = zzMarkedPos;
zzBufferL = zzBuffer;
zzEndReadL = zzEndRead;
if (eof) {
zzInput = YYEOF;
break zzForAction;
}
else {
zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL/*, zzEndReadL*/);
zzCurrentPosL += Character.charCount(zzInput);
}
}
int zzNext = zzTransL[ zzRowMapL[zzState] + ZZ_CMAP(zzInput) ];
if (zzNext == -1) break zzForAction;
zzState = zzNext;
zzAttributes = zzAttrL[zzState];
if ( (zzAttributes & 1) == 1 ) {
zzAction = zzState;
zzMarkedPosL = zzCurrentPosL;
if ( (zzAttributes & 8) == 8 ) break zzForAction;
}
}
}
// store back cached position
zzMarkedPos = zzMarkedPosL;
if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
zzAtEOF = true;
return null;
}
else {
switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
case 1:
{ yybegin(YYINITIAL); return DotEnvTypes.KEY_CHARS;
}
// fall through
case 15: break;
case 2:
{ yybegin(YYINITIAL); return TokenType.WHITE_SPACE;
}
// fall through
case 16: break;
case 3:
{ return TokenType.BAD_CHARACTER;
}
// fall through
case 17: break;
case 4:
{ yybegin(YYINITIAL); return DotEnvTypes.COMMENT;
}
// fall through
case 18: break;
case 5:
{ yybegin(WAITING_VALUE); return DotEnvTypes.SEPARATOR;
}
// fall through
case 19: break;
case 6:
{ yybegin(YYINITIAL); return DotEnvTypes.VALUE_CHARS;
}
// fall through
case 20: break;
case 7:
{ yybegin(WAITING_VALUE); return TokenType.WHITE_SPACE;
}
// fall through
case 21: break;
case 8:
{ yybegin(WAITING_SINGLE_QUOTED_VALUE); return DotEnvTypes.QUOTE;
}
// fall through
case 22: break;
case 9:
{ yybegin(WAITING_COMMENT); return DotEnvTypes.COMMENT;
}
// fall through
case 23: break;
case 10:
{ yybegin(WAITING_QUOTED_VALUE); return DotEnvTypes.QUOTE;
}
// fall through
case 24: break;
case 11:
{ yybegin(WAITING_QUOTED_VALUE); return DotEnvTypes.VALUE_CHARS;
}
// fall through
case 25: break;
case 12:
{ yybegin(WAITING_COMMENT); return DotEnvTypes.QUOTE;
}
// fall through
case 26: break;
case 13:
{ yybegin(WAITING_SINGLE_QUOTED_VALUE); return DotEnvTypes.VALUE_CHARS;
}
// fall through
case 27: break;
case 14:
{ yybegin(WAITING_KEY); return DotEnvTypes.EXPORT;
}
// fall through
case 28: break;
default:
zzScanError(ZZ_NO_MATCH);
}
}
}
}
}

View File

@@ -0,0 +1,555 @@
/* The following code was generated by JFlex 1.7.0 tweaked for IntelliJ platform */
package ru.adelf.idea.dotenv.grammars;
import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
import ru.adelf.idea.dotenv.psi.DotEnvTypes;
import com.intellij.psi.TokenType;
import static ru.adelf.idea.dotenv.psi.DotEnvTypes.*;
/**
* This class is a scanner generated by
* <a href="http://www.jflex.de/">JFlex</a> 1.7.0
* from the specification file <tt>DotEnvLexer.flex</tt>
*/
class _DotEnvLexer implements FlexLexer {
/** This character denotes the end of file */
public static final int YYEOF = -1;
/** initial size of the lookahead buffer */
private static final int ZZ_BUFFERSIZE = 16384;
/** lexical states */
public static final int YYINITIAL = 0;
public static final int WAITING_VALUE = 2;
public static final int WAITING_QUOTED_VALUE = 4;
public static final int WAITING_COMMENT = 6;
/**
* ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
* ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l
* at the beginning of a line
* l is of the form l = 2*k, k a non negative integer
*/
private static final int ZZ_LEXSTATE[] = {
0, 0, 1, 1, 2, 2, 3, 3
};
/**
* Translates characters to character classes
* Chosen bits are [9, 6, 6]
* Total runtime size is 1568 bytes
*/
public static int ZZ_CMAP(int ch) {
return ZZ_CMAP_A[(ZZ_CMAP_Y[ZZ_CMAP_Z[ch>>12]|((ch>>6)&0x3f)]<<6)|(ch&0x3f)];
}
/* The ZZ_CMAP_Z table has 272 entries */
static final char ZZ_CMAP_Z[] = zzUnpackCMap(
"\1\0\1\100\1\200\u010d\100");
/* The ZZ_CMAP_Y table has 192 entries */
static final char ZZ_CMAP_Y[] = zzUnpackCMap(
"\1\0\1\1\1\2\175\3\1\4\77\3");
/* The ZZ_CMAP_A table has 320 entries */
static final char ZZ_CMAP_A[] = zzUnpackCMap(
"\11\0\1\4\1\2\1\1\1\5\1\3\22\0\1\7\1\0\1\6\1\11\26\0\1\12\2\0\1\12\36\0\1"+
"\10\50\0\1\1\242\0\2\1\26\0");
/**
* Translates DFA states to action switch labels.
*/
private static final int [] ZZ_ACTION = zzUnpackAction();
private static final String ZZ_ACTION_PACKED_0 =
"\4\0\2\1\1\2\1\3\1\4\1\5\2\6\2\7"+
"\1\10\1\7\1\3\1\11\2\12\1\13\1\12\3\11"+
"\1\0\2\4\1\2\1\6\1\2";
private static int [] zzUnpackAction() {
int [] result = new int[31];
int offset = 0;
offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
return result;
}
private static int zzUnpackAction(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int count = packed.charAt(i++);
int value = packed.charAt(i++);
do result[j++] = value; while (--count > 0);
}
return j;
}
/**
* Translates a state to a row index in the transition table
*/
private static final int [] ZZ_ROWMAP = zzUnpackRowMap();
private static final String ZZ_ROWMAP_PACKED_0 =
"\0\0\0\13\0\26\0\41\0\54\0\67\0\102\0\115"+
"\0\130\0\143\0\156\0\171\0\204\0\102\0\143\0\217"+
"\0\232\0\156\0\245\0\260\0\143\0\273\0\306\0\321"+
"\0\334\0\115\0\347\0\362\0\171\0\375\0\321";
private static int [] zzUnpackRowMap() {
int [] result = new int[31];
int offset = 0;
offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
return result;
}
private static int zzUnpackRowMap(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int high = packed.charAt(i++) << 16;
result[j++] = high | packed.charAt(i++);
}
return j;
}
/**
* The transition table of the DFA
*/
private static final int [] ZZ_TRANS = zzUnpackTrans();
private static final String ZZ_TRANS_PACKED_0 =
"\1\5\1\6\1\7\1\6\2\7\1\5\1\7\1\10"+
"\1\11\1\12\1\13\1\14\2\7\1\15\1\16\1\17"+
"\1\20\1\21\1\22\1\13\1\23\1\24\2\7\2\24"+
"\1\25\1\24\1\26\2\23\1\27\1\30\2\7\1\31"+
"\1\30\1\27\1\31\3\27\2\5\1\0\1\5\2\0"+
"\1\5\1\0\1\32\1\5\1\0\1\5\1\6\1\7"+
"\1\6\2\7\1\5\1\7\1\32\1\5\2\0\5\7"+
"\1\0\1\7\12\0\1\5\3\0\2\11\1\0\1\5"+
"\2\33\1\11\1\33\1\34\1\11\1\33\13\0\2\13"+
"\2\0\5\13\1\0\2\13\1\35\2\7\2\35\1\13"+
"\1\35\1\13\1\0\2\13\1\36\2\7\2\15\1\13"+
"\1\15\1\13\1\0\1\13\1\0\3\7\2\20\1\0"+
"\1\20\3\0\1\13\3\0\1\13\1\0\5\13\2\23"+
"\2\0\2\23\1\0\1\23\1\26\3\23\1\24\2\7"+
"\2\24\1\0\1\24\1\26\4\23\2\0\4\23\1\26"+
"\2\23\2\27\2\0\10\27\1\37\2\7\2\37\1\27"+
"\1\37\4\27\1\31\2\7\2\31\1\27\1\31\3\27"+
"\2\33\2\0\11\33\2\0\3\33\1\11\3\33\1\13"+
"\1\36\2\7\2\36\1\13\1\36\1\13\1\0\1\13";
private static int [] zzUnpackTrans() {
int [] result = new int[264];
int offset = 0;
offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
return result;
}
private static int zzUnpackTrans(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int count = packed.charAt(i++);
int value = packed.charAt(i++);
value--;
do result[j++] = value; while (--count > 0);
}
return j;
}
/* error codes */
private static final int ZZ_UNKNOWN_ERROR = 0;
private static final int ZZ_NO_MATCH = 1;
private static final int ZZ_PUSHBACK_2BIG = 2;
/* error messages for the codes above */
private static final String[] ZZ_ERROR_MSG = {
"Unknown internal scanner error",
"Error: could not match input",
"Error: pushback value was too large"
};
/**
* ZZ_ATTRIBUTE[aState] contains the attributes of state <code>aState</code>
*/
private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
private static final String ZZ_ATTRIBUTE_PACKED_0 =
"\4\0\5\1\1\11\4\1\1\11\5\1\1\11\4\1"+
"\1\0\5\1";
private static int [] zzUnpackAttribute() {
int [] result = new int[31];
int offset = 0;
offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
return result;
}
private static int zzUnpackAttribute(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int count = packed.charAt(i++);
int value = packed.charAt(i++);
do result[j++] = value; while (--count > 0);
}
return j;
}
/** the input device */
private java.io.Reader zzReader;
/** the current state of the DFA */
private int zzState;
/** the current lexical state */
private int zzLexicalState = YYINITIAL;
/** this buffer contains the current text to be matched and is
the source of the yytext() string */
private CharSequence zzBuffer = "";
/** the textposition at the last accepting state */
private int zzMarkedPos;
/** the current text position in the buffer */
private int zzCurrentPos;
/** startRead marks the beginning of the yytext() string in the buffer */
private int zzStartRead;
/** endRead marks the last character in the buffer, that has been read
from input */
private int zzEndRead;
/**
* zzAtBOL == true <=> the scanner is currently at the beginning of a line
*/
private boolean zzAtBOL = true;
/** zzAtEOF == true <=> the scanner is at the EOF */
private boolean zzAtEOF;
/** denotes if the user-EOF-code has already been executed */
private boolean zzEOFDone;
/**
* Creates a new scanner
*
* @param in the java.io.Reader to read input from.
*/
_DotEnvLexer(java.io.Reader in) {
this.zzReader = in;
}
/**
* Unpacks the compressed character translation table.
*
* @param packed the packed character translation table
* @return the unpacked character translation table
*/
private static char [] zzUnpackCMap(String packed) {
int size = 0;
for (int i = 0, length = packed.length(); i < length; i += 2) {
size += packed.charAt(i);
}
char[] map = new char[size];
int i = 0; /* index in packed string */
int j = 0; /* index in unpacked array */
while (i < packed.length()) {
int count = packed.charAt(i++);
char value = packed.charAt(i++);
do map[j++] = value; while (--count > 0);
}
return map;
}
public final int getTokenStart() {
return zzStartRead;
}
public final int getTokenEnd() {
return getTokenStart() + yylength();
}
public void reset(CharSequence buffer, int start, int end, int initialState) {
zzBuffer = buffer;
zzCurrentPos = zzMarkedPos = zzStartRead = start;
zzAtEOF = false;
zzAtBOL = true;
zzEndRead = end;
yybegin(initialState);
}
/**
* Refills the input buffer.
*
* @return <code>false</code>, iff there was new input.
*
* @exception java.io.IOException if any I/O-Error occurs
*/
private boolean zzRefill() throws java.io.IOException {
return true;
}
/**
* Returns the current lexical state.
*/
public final int yystate() {
return zzLexicalState;
}
/**
* Enters a new lexical state
*
* @param newState the new lexical state
*/
public final void yybegin(int newState) {
zzLexicalState = newState;
}
/**
* Returns the text matched by the current regular expression.
*/
public final CharSequence yytext() {
return zzBuffer.subSequence(zzStartRead, zzMarkedPos);
}
/**
* Returns the character at position <tt>pos</tt> from the
* matched text.
*
* It is equivalent to yytext().charAt(pos), but faster
*
* @param pos the position of the character to fetch.
* A value from 0 to yylength()-1.
*
* @return the character at position pos
*/
public final char yycharat(int pos) {
return zzBuffer.charAt(zzStartRead+pos);
}
/**
* Returns the length of the matched text region.
*/
public final int yylength() {
return zzMarkedPos-zzStartRead;
}
/**
* Reports an error that occured while scanning.
*
* In a wellformed scanner (no or only correct usage of
* yypushback(int) and a match-all fallback rule) this method
* will only be called with things that "Can't Possibly Happen".
* If this method is called, something is seriously wrong
* (e.g. a JFlex bug producing a faulty scanner etc.).
*
* Usual syntax/scanner level error handling should be done
* in error fallback rules.
*
* @param errorCode the code of the errormessage to display
*/
private void zzScanError(int errorCode) {
String message;
try {
message = ZZ_ERROR_MSG[errorCode];
}
catch (ArrayIndexOutOfBoundsException e) {
message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
}
throw new Error(message);
}
/**
* Pushes the specified amount of characters back into the input stream.
*
* They will be read again by then next call of the scanning method
*
* @param number the number of characters to be read again.
* This number must not be greater than yylength()!
*/
public void yypushback(int number) {
if ( number > yylength() )
zzScanError(ZZ_PUSHBACK_2BIG);
zzMarkedPos -= number;
}
/**
* Resumes scanning until the next regular expression is matched,
* the end of input is encountered or an I/O-Error occurs.
*
* @return the next token
* @exception java.io.IOException if any I/O-Error occurs
*/
public IElementType advance() throws java.io.IOException {
int zzInput;
int zzAction;
// cached fields:
int zzCurrentPosL;
int zzMarkedPosL;
int zzEndReadL = zzEndRead;
CharSequence zzBufferL = zzBuffer;
int [] zzTransL = ZZ_TRANS;
int [] zzRowMapL = ZZ_ROWMAP;
int [] zzAttrL = ZZ_ATTRIBUTE;
while (true) {
zzMarkedPosL = zzMarkedPos;
zzAction = -1;
zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
zzState = ZZ_LEXSTATE[zzLexicalState];
// set up zzAction for empty match case:
int zzAttributes = zzAttrL[zzState];
if ( (zzAttributes & 1) == 1 ) {
zzAction = zzState;
}
zzForAction: {
while (true) {
if (zzCurrentPosL < zzEndReadL) {
zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL/*, zzEndReadL*/);
zzCurrentPosL += Character.charCount(zzInput);
}
else if (zzAtEOF) {
zzInput = YYEOF;
break zzForAction;
}
else {
// store back cached positions
zzCurrentPos = zzCurrentPosL;
zzMarkedPos = zzMarkedPosL;
boolean eof = zzRefill();
// get translated positions and possibly new buffer
zzCurrentPosL = zzCurrentPos;
zzMarkedPosL = zzMarkedPos;
zzBufferL = zzBuffer;
zzEndReadL = zzEndRead;
if (eof) {
zzInput = YYEOF;
break zzForAction;
}
else {
zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL/*, zzEndReadL*/);
zzCurrentPosL += Character.charCount(zzInput);
}
}
int zzNext = zzTransL[ zzRowMapL[zzState] + ZZ_CMAP(zzInput) ];
if (zzNext == -1) break zzForAction;
zzState = zzNext;
zzAttributes = zzAttrL[zzState];
if ( (zzAttributes & 1) == 1 ) {
zzAction = zzState;
zzMarkedPosL = zzCurrentPosL;
if ( (zzAttributes & 8) == 8 ) break zzForAction;
}
}
}
// store back cached position
zzMarkedPos = zzMarkedPosL;
if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
zzAtEOF = true;
return null;
}
else {
switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
case 1:
{ yybegin(YYINITIAL); return DotEnvTypes.KEY_CHARS;
}
// fall through
case 12: break;
case 2:
{ yybegin(YYINITIAL); return TokenType.WHITE_SPACE;
}
// fall through
case 13: break;
case 3:
{ return TokenType.BAD_CHARACTER;
}
// fall through
case 14: break;
case 4:
{ yybegin(YYINITIAL); return DotEnvTypes.COMMENT;
}
// fall through
case 15: break;
case 5:
{ yybegin(WAITING_VALUE); return DotEnvTypes.SEPARATOR;
}
// fall through
case 16: break;
case 6:
{ yybegin(YYINITIAL); return DotEnvTypes.VALUE_CHARS;
}
// fall through
case 17: break;
case 7:
{ yybegin(WAITING_VALUE); return TokenType.WHITE_SPACE;
}
// fall through
case 18: break;
case 8:
{ yybegin(WAITING_QUOTED_VALUE); return DotEnvTypes.QUOTE;
}
// fall through
case 19: break;
case 9:
{ yybegin(WAITING_COMMENT); return DotEnvTypes.COMMENT;
}
// fall through
case 20: break;
case 10:
{ yybegin(WAITING_QUOTED_VALUE); return DotEnvTypes.VALUE_CHARS;
}
// fall through
case 21: break;
case 11:
{ yybegin(YYINITIAL); return DotEnvTypes.QUOTE;
}
// fall through
case 22: break;
default:
zzScanError(ZZ_NO_MATCH);
}
}
}
}
}

View File

@@ -0,0 +1,209 @@
// This is a generated file. Not intended for manual editing.
package ru.adelf.idea.dotenv.parser;
import com.intellij.lang.ASTNode;
import com.intellij.lang.LightPsiParser;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.PsiBuilder.Marker;
import com.intellij.lang.PsiParser;
import com.intellij.psi.tree.IElementType;
import static com.intellij.lang.parser.GeneratedParserUtilBase.*;
import static ru.adelf.idea.dotenv.psi.DotEnvTypes.*;
@SuppressWarnings({"SimplifiableIfStatement", "UnusedAssignment"})
public class DotEnvParser implements PsiParser, LightPsiParser {
public ASTNode parse(IElementType t, PsiBuilder b) {
parseLight(t, b);
return b.getTreeBuilt();
}
public void parseLight(IElementType t, PsiBuilder b) {
boolean r;
b = adapt_builder_(t, b, this, null);
Marker m = enter_section_(b, 0, _COLLAPSE_, null);
r = parse_root_(t, b);
exit_section_(b, 0, m, t, r, true, TRUE_CONDITION);
}
protected boolean parse_root_(IElementType t, PsiBuilder b) {
return parse_root_(t, b, 0);
}
static boolean parse_root_(IElementType t, PsiBuilder b, int l) {
return dotEnvFile(b, l + 1);
}
/* ********************************************************** */
// item_*
static boolean dotEnvFile(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "dotEnvFile")) return false;
while (true) {
int c = current_position_(b);
if (!item_(b, l + 1)) break;
if (!empty_element_parsed_guard_(b, "dotEnvFile", c)) break;
}
return true;
}
/* ********************************************************** */
// EXPORT? property|COMMENT|CRLF
static boolean item_(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "item_")) return false;
boolean r;
Marker m = enter_section_(b);
r = item__0(b, l + 1);
if (!r) r = consumeToken(b, COMMENT);
if (!r) r = consumeToken(b, CRLF);
exit_section_(b, m, null, r);
return r;
}
// EXPORT? property
private static boolean item__0(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "item__0")) return false;
boolean r;
Marker m = enter_section_(b);
r = item__0_0(b, l + 1);
r = r && property(b, l + 1);
exit_section_(b, m, null, r);
return r;
}
// EXPORT?
private static boolean item__0_0(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "item__0_0")) return false;
consumeToken(b, EXPORT);
return true;
}
/* ********************************************************** */
// KEY_CHARS
public static boolean key(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "key")) return false;
if (!nextTokenIs(b, KEY_CHARS)) return false;
boolean r;
Marker m = enter_section_(b);
r = consumeToken(b, KEY_CHARS);
exit_section_(b, m, KEY, r);
return r;
}
/* ********************************************************** */
// (key SEPARATOR value? COMMENT?) | key COMMENT?
public static boolean property(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "property")) return false;
if (!nextTokenIs(b, KEY_CHARS)) return false;
boolean r;
Marker m = enter_section_(b);
r = property_0(b, l + 1);
if (!r) r = property_1(b, l + 1);
exit_section_(b, m, PROPERTY, r);
return r;
}
// key SEPARATOR value? COMMENT?
private static boolean property_0(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "property_0")) return false;
boolean r;
Marker m = enter_section_(b);
r = key(b, l + 1);
r = r && consumeToken(b, SEPARATOR);
r = r && property_0_2(b, l + 1);
r = r && property_0_3(b, l + 1);
exit_section_(b, m, null, r);
return r;
}
// value?
private static boolean property_0_2(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "property_0_2")) return false;
value(b, l + 1);
return true;
}
// COMMENT?
private static boolean property_0_3(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "property_0_3")) return false;
consumeToken(b, COMMENT);
return true;
}
// key COMMENT?
private static boolean property_1(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "property_1")) return false;
boolean r;
Marker m = enter_section_(b);
r = key(b, l + 1);
r = r && property_1_1(b, l + 1);
exit_section_(b, m, null, r);
return r;
}
// COMMENT?
private static boolean property_1_1(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "property_1_1")) return false;
consumeToken(b, COMMENT);
return true;
}
/* ********************************************************** */
// VALUE_CHARS+ | QUOTE VALUE_CHARS* QUOTE?
public static boolean value(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "value")) return false;
if (!nextTokenIs(b, "<value>", QUOTE, VALUE_CHARS)) return false;
boolean r;
Marker m = enter_section_(b, l, _NONE_, VALUE, "<value>");
r = value_0(b, l + 1);
if (!r) r = value_1(b, l + 1);
exit_section_(b, l, m, r, false, null);
return r;
}
// VALUE_CHARS+
private static boolean value_0(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "value_0")) return false;
boolean r;
Marker m = enter_section_(b);
r = consumeToken(b, VALUE_CHARS);
while (r) {
int c = current_position_(b);
if (!consumeToken(b, VALUE_CHARS)) break;
if (!empty_element_parsed_guard_(b, "value_0", c)) break;
}
exit_section_(b, m, null, r);
return r;
}
// QUOTE VALUE_CHARS* QUOTE?
private static boolean value_1(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "value_1")) return false;
boolean r;
Marker m = enter_section_(b);
r = consumeToken(b, QUOTE);
r = r && value_1_1(b, l + 1);
r = r && value_1_2(b, l + 1);
exit_section_(b, m, null, r);
return r;
}
// VALUE_CHARS*
private static boolean value_1_1(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "value_1_1")) return false;
while (true) {
int c = current_position_(b);
if (!consumeToken(b, VALUE_CHARS)) break;
if (!empty_element_parsed_guard_(b, "value_1_1", c)) break;
}
return true;
}
// QUOTE?
private static boolean value_1_2(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "value_1_2")) return false;
consumeToken(b, QUOTE);
return true;
}
}

View File

@@ -0,0 +1,10 @@
// This is a generated file. Not intended for manual editing.
package ru.adelf.idea.dotenv.psi;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;
public interface DotEnvKey extends PsiElement {
}

View File

@@ -0,0 +1,26 @@
// This is a generated file. Not intended for manual editing.
package ru.adelf.idea.dotenv.psi;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;
public interface DotEnvProperty extends DotEnvNamedElement {
@NotNull
DotEnvKey getKey();
@Nullable
DotEnvValue getValue();
String getKeyText();
String getValueText();
String getName();
PsiElement setName(@NotNull String newName);
PsiElement getNameIdentifier();
}

View File

@@ -0,0 +1,38 @@
// This is a generated file. Not intended for manual editing.
package ru.adelf.idea.dotenv.psi;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.PsiElement;
import com.intellij.lang.ASTNode;
import ru.adelf.idea.dotenv.psi.impl.*;
public interface DotEnvTypes {
IElementType KEY = new DotEnvElementType("KEY");
IElementType PROPERTY = new DotEnvElementType("PROPERTY");
IElementType VALUE = new DotEnvElementType("VALUE");
IElementType COMMENT = new DotEnvTokenType("COMMENT");
IElementType CRLF = new DotEnvTokenType("CRLF");
IElementType EXPORT = new DotEnvTokenType("EXPORT");
IElementType KEY_CHARS = new DotEnvTokenType("KEY_CHARS");
IElementType QUOTE = new DotEnvTokenType("QUOTE");
IElementType SEPARATOR = new DotEnvTokenType("SEPARATOR");
IElementType VALUE_CHARS = new DotEnvTokenType("VALUE_CHARS");
class Factory {
public static PsiElement createElement(ASTNode node) {
IElementType type = node.getElementType();
if (type == KEY) {
return new DotEnvKeyImpl(node);
}
else if (type == PROPERTY) {
return new DotEnvPropertyImpl(node);
}
else if (type == VALUE) {
return new DotEnvValueImpl(node);
}
throw new AssertionError("Unknown element type: " + type);
}
}
}

View File

@@ -0,0 +1,10 @@
// This is a generated file. Not intended for manual editing.
package ru.adelf.idea.dotenv.psi;
import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;
public interface DotEnvValue extends PsiElement {
}

View File

@@ -0,0 +1,30 @@
// This is a generated file. Not intended for manual editing.
package ru.adelf.idea.dotenv.psi;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiElement;
public class DotEnvVisitor extends PsiElementVisitor {
public void visitKey(@NotNull DotEnvKey o) {
visitPsiElement(o);
}
public void visitProperty(@NotNull DotEnvProperty o) {
visitNamedElement(o);
}
public void visitValue(@NotNull DotEnvValue o) {
visitPsiElement(o);
}
public void visitNamedElement(@NotNull DotEnvNamedElement o) {
visitPsiElement(o);
}
public void visitPsiElement(@NotNull PsiElement o) {
visitElement(o);
}
}

View File

@@ -0,0 +1,27 @@
// This is a generated file. Not intended for manual editing.
package ru.adelf.idea.dotenv.psi.impl;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.psi.DotEnvKey;
import ru.adelf.idea.dotenv.psi.DotEnvVisitor;
public class DotEnvKeyImpl extends ASTWrapperPsiElement implements DotEnvKey {
public DotEnvKeyImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull DotEnvVisitor visitor) {
visitor.visitKey(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof DotEnvVisitor) accept((DotEnvVisitor)visitor);
else super.accept(visitor);
}
}

View File

@@ -0,0 +1,64 @@
// This is a generated file. Not intended for manual editing.
package ru.adelf.idea.dotenv.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.psi.*;
public class DotEnvPropertyImpl extends DotEnvNamedElementImpl implements DotEnvProperty {
public DotEnvPropertyImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull DotEnvVisitor visitor) {
visitor.visitProperty(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof DotEnvVisitor) accept((DotEnvVisitor)visitor);
else super.accept(visitor);
}
@Override
@NotNull
public DotEnvKey getKey() {
return findNotNullChildByClass(DotEnvKey.class);
}
@Override
@Nullable
public DotEnvValue getValue() {
return findChildByClass(DotEnvValue.class);
}
@Override
public String getKeyText() {
return DotEnvPsiUtil.getKeyText(this);
}
@Override
public String getValueText() {
return DotEnvPsiUtil.getValueText(this);
}
@Override
public String getName() {
return DotEnvPsiUtil.getName(this);
}
@Override
public PsiElement setName(@NotNull String newName) {
return DotEnvPsiUtil.setName(this, newName);
}
@Override
public PsiElement getNameIdentifier() {
return DotEnvPsiUtil.getNameIdentifier(this);
}
}

View File

@@ -0,0 +1,27 @@
// This is a generated file. Not intended for manual editing.
package ru.adelf.idea.dotenv.psi.impl;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.psi.DotEnvValue;
import ru.adelf.idea.dotenv.psi.DotEnvVisitor;
public class DotEnvValueImpl extends ASTWrapperPsiElement implements DotEnvValue {
public DotEnvValueImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull DotEnvVisitor visitor) {
visitor.visitValue(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof DotEnvVisitor) accept((DotEnvVisitor)visitor);
else super.accept(visitor);
}
}

View File

@@ -0,0 +1,38 @@
package ru.adelf.idea.dotenv;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
public class DotEnvFactory {
public static PsiElement createFromText(@NotNull Project project, @NotNull IElementType type, @NotNull String text) {
final Ref<PsiElement> ret = new Ref<>();
PsiFile dummyFile = createDummyFile(project, text);
dummyFile.accept(new PsiRecursiveElementWalkingVisitor() {
public void visitElement(@NotNull PsiElement element) {
ASTNode node = element.getNode();
if (node != null && node.getElementType() == type) {
ret.set(element);
stopWalking();
} else {
super.visitElement(element);
}
}
});
assert !ret.isNull() : "cannot create element from text:\n" + dummyFile.getText();
return ret.get();
}
@NotNull
private static PsiFile createDummyFile(Project project, String fileText) {
return PsiFileFactory.getInstance(project).createFileFromText("DUMMY__.env", DotEnvFileType.INSTANCE, fileText, System.currentTimeMillis(), false);
}
}

View File

@@ -0,0 +1,40 @@
package ru.adelf.idea.dotenv;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.fileTypes.LanguageFileType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class DotEnvFileType extends LanguageFileType {
public static final DotEnvFileType INSTANCE = new DotEnvFileType();
private DotEnvFileType() {
super(DotEnvLanguage.INSTANCE);
}
@NotNull
@Override
public String getName() {
return ".env file";
}
@NotNull
@Override
public String getDescription() {
return ".env file";
}
@NotNull
@Override
public String getDefaultExtension() {
return "env";
}
@Nullable
@Override
public Icon getIcon() {
return AllIcons.FileTypes.Text;
}
}

View File

@@ -0,0 +1,11 @@
package ru.adelf.idea.dotenv;
import com.intellij.lang.Language;
public class DotEnvLanguage extends Language {
public static final DotEnvLanguage INSTANCE = new DotEnvLanguage();
private DotEnvLanguage() {
super("DotEnv");
}
}

View File

@@ -0,0 +1,61 @@
package ru.adelf.idea.dotenv;
import com.intellij.lang.*;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.tree.*;
import ru.adelf.idea.dotenv.grammars.DotEnvLexerAdapter;
import ru.adelf.idea.dotenv.parser.DotEnvParser;
import ru.adelf.idea.dotenv.psi.*;
import org.jetbrains.annotations.NotNull;
public class DotEnvParserDefinition implements ParserDefinition {
private static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE);
private static final TokenSet COMMENTS = TokenSet.create(DotEnvTypes.COMMENT);
private static final IFileElementType FILE = new IFileElementType(DotEnvLanguage.INSTANCE);
@NotNull
@Override
public Lexer createLexer(Project project) {
return new DotEnvLexerAdapter();
}
@NotNull
public TokenSet getWhitespaceTokens() {
return WHITE_SPACES;
}
@NotNull
public TokenSet getCommentTokens() {
return COMMENTS;
}
@NotNull
public TokenSet getStringLiteralElements() {
return TokenSet.EMPTY;
}
@NotNull
public PsiParser createParser(final Project project) {
return new DotEnvParser();
}
@Override
public IFileElementType getFileNodeType() {
return FILE;
}
public PsiFile createFile(FileViewProvider viewProvider) {
return new DotEnvFile(viewProvider);
}
public SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) {
return SpaceRequirements.MAY;
}
@NotNull
public PsiElement createElement(ASTNode node) {
return DotEnvTypes.Factory.createElement(node);
}
}

View File

@@ -0,0 +1,26 @@
package ru.adelf.idea.dotenv;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.models.KeyValuePsiElement;
import ru.adelf.idea.dotenv.psi.DotEnvProperty;
import ru.adelf.idea.dotenv.psi.DotEnvVisitor;
import java.util.Collection;
import java.util.HashSet;
public class DotEnvPsiElementsVisitor extends DotEnvVisitor {
private final Collection<KeyValuePsiElement> collectedItems = new HashSet<>();
@Override
public void visitProperty(@NotNull DotEnvProperty property) {
collectedItems.add(new KeyValuePsiElement(
property.getKeyText(),
property.getValueText(),
property)
);
}
public Collection<KeyValuePsiElement> getCollectedItems() {
return collectedItems;
}
}

View File

@@ -0,0 +1,32 @@
package ru.adelf.idea.dotenv;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.util.xmlb.XmlSerializerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@State(name = "DotEnvSettings", storages = {@Storage("dot-env.xml")})
public class DotEnvSettings implements PersistentStateComponent<DotEnvSettings> {
public boolean completionEnabled = true;
public boolean storeValues = true;
public boolean hideValuesInTheFile = false;
@Nullable
@Override
public DotEnvSettings getState() {
return this;
}
@Override
public void loadState(@NotNull DotEnvSettings state) {
XmlSerializerUtil.copyBean(state, this);
}
public static DotEnvSettings getInstance() {
return ApplicationManager.getApplication().getService(DotEnvSettings.class);
}
}

View File

@@ -0,0 +1,57 @@
package ru.adelf.idea.dotenv;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.grammars.DotEnvLexerAdapter;
import ru.adelf.idea.dotenv.psi.DotEnvTypes;
import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey;
class DotEnvSyntaxHighlighter extends SyntaxHighlighterBase {
private static final TextAttributesKey SEPARATOR =
createTextAttributesKey("DOTENV_SEPARATOR", DefaultLanguageHighlighterColors.OPERATION_SIGN);
private static final TextAttributesKey KEY =
createTextAttributesKey("DOTENV_KEY", DefaultLanguageHighlighterColors.KEYWORD);
private static final TextAttributesKey VALUE =
createTextAttributesKey("DOTENV_VALUE", DefaultLanguageHighlighterColors.STRING);
private static final TextAttributesKey COMMENT =
createTextAttributesKey("DOTENV_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT);
private static final TextAttributesKey BAD_CHARACTER =
createTextAttributesKey("DOTENV_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER);
private static final TextAttributesKey[] BAD_CHAR_KEYS = new TextAttributesKey[]{BAD_CHARACTER};
private static final TextAttributesKey[] SEPARATOR_KEYS = new TextAttributesKey[]{SEPARATOR};
private static final TextAttributesKey[] KEY_KEYS = new TextAttributesKey[]{KEY};
private static final TextAttributesKey[] VALUE_KEYS = new TextAttributesKey[]{VALUE};
private static final TextAttributesKey[] COMMENT_KEYS = new TextAttributesKey[]{COMMENT};
private static final TextAttributesKey[] EMPTY_KEYS = new TextAttributesKey[0];
@NotNull
@Override
public Lexer getHighlightingLexer() {
return new DotEnvLexerAdapter();
}
@NotNull
@Override
public TextAttributesKey[] getTokenHighlights(IElementType tokenType) {
if (tokenType.equals(DotEnvTypes.SEPARATOR)) {
return SEPARATOR_KEYS;
} else if (tokenType.equals(DotEnvTypes.KEY_CHARS)) {
return KEY_KEYS;
} else if (tokenType.equals(DotEnvTypes.VALUE_CHARS)) {
return VALUE_KEYS;
} else if (tokenType.equals(DotEnvTypes.COMMENT) || tokenType.equals(DotEnvTypes.EXPORT)) {
return COMMENT_KEYS;
} else if (tokenType.equals(TokenType.BAD_CHARACTER)) {
return BAD_CHAR_KEYS;
} else {
return EMPTY_KEYS;
}
}
}

View File

@@ -0,0 +1,14 @@
package ru.adelf.idea.dotenv;
import com.intellij.openapi.fileTypes.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
public class DotEnvSyntaxHighlighterFactory extends SyntaxHighlighterFactory {
@NotNull
@Override
public SyntaxHighlighter getSyntaxHighlighter(Project project, VirtualFile virtualFile) {
return new DotEnvSyntaxHighlighter();
}
}

View File

@@ -0,0 +1,38 @@
package ru.adelf.idea.dotenv;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesProvider;
import ru.adelf.idea.dotenv.api.FileAcceptResult;
import ru.adelf.idea.dotenv.models.KeyValuePsiElement;
import ru.adelf.idea.dotenv.psi.DotEnvFile;
import java.util.Collection;
import java.util.Collections;
public class DotEnvVariablesProvider implements EnvironmentVariablesProvider {
@NotNull
@Override
public FileAcceptResult acceptFile(VirtualFile file) {
if(!file.getFileType().equals(DotEnvFileType.INSTANCE)) {
return FileAcceptResult.NOT_ACCEPTED;
}
// .env.dist , .env.example files are secondary
return file.getName().equals(".env") ? FileAcceptResult.ACCEPTED : FileAcceptResult.ACCEPTED_SECONDARY;
}
@NotNull
@Override
public Collection<KeyValuePsiElement> getElements(PsiFile psiFile) {
if(psiFile instanceof DotEnvFile) {
DotEnvPsiElementsVisitor visitor = new DotEnvPsiElementsVisitor();
psiFile.acceptChildren(visitor);
return visitor.getCollectedItems();
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,143 @@
package ru.adelf.idea.dotenv.api;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.util.Processor;
import com.intellij.util.indexing.FileBasedIndex;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.DotEnvSettings;
import ru.adelf.idea.dotenv.indexing.DotEnvKeyValuesIndex;
import ru.adelf.idea.dotenv.util.EnvironmentVariablesProviderUtil;
import ru.adelf.idea.dotenv.util.EnvironmentVariablesUtil;
import java.util.*;
public class EnvironmentVariablesApi {
@NotNull
public static Map<String, String> getAllKeyValues(Project project) {
FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
Map<String, String> keyValues = new HashMap<>();
Map<String, String> secondaryKeyValues = new HashMap<>();
Map<VirtualFile, FileAcceptResult> resultsCache = new HashMap<>();
GlobalSearchScope scope = GlobalSearchScope.allScope(project);
boolean showValues = DotEnvSettings.getInstance().storeValues;
fileBasedIndex.processAllKeys(DotEnvKeyValuesIndex.KEY, key -> {
for (VirtualFile virtualFile : fileBasedIndex.getContainingFiles(DotEnvKeyValuesIndex.KEY, key, scope)) {
FileAcceptResult fileAcceptResult;
if (resultsCache.containsKey(virtualFile)) {
fileAcceptResult = resultsCache.get(virtualFile);
} else {
fileAcceptResult = getFileAcceptResult(virtualFile);
resultsCache.put(virtualFile, fileAcceptResult);
}
if (!fileAcceptResult.isAccepted()) {
continue;
}
fileBasedIndex.processValues(DotEnvKeyValuesIndex.KEY, key, virtualFile, ((file, val) -> {
String keyValue = val;
if (!showValues) {
keyValue = "";
}
if (fileAcceptResult.isPrimary()) {
keyValues.putIfAbsent(key, keyValue);
} else {
secondaryKeyValues.putIfAbsent(key, keyValue);
}
return true;
}), scope);
}
return true;
}, project);
secondaryKeyValues.putAll(keyValues);
return secondaryKeyValues;
}
/**
* @param project project
* @param key environment variable key
* @return All key declarations, in .env files, Dockerfile, docker-compose.yml, etc
*/
@NotNull
public static PsiElement[] getKeyDeclarations(Project project, String key) {
List<PsiElement> targets = new ArrayList<>();
List<PsiElement> secondaryTargets = new ArrayList<>();
FileBasedIndex.getInstance().getFilesWithKey(DotEnvKeyValuesIndex.KEY, new HashSet<>(Collections.singletonList(key)), virtualFile -> {
PsiFile psiFileTarget = PsiManager.getInstance(project).findFile(virtualFile);
if (psiFileTarget == null) {
return true;
}
for (EnvironmentVariablesProvider provider : EnvironmentVariablesProviderUtil.PROVIDERS) {
FileAcceptResult fileAcceptResult = provider.acceptFile(virtualFile);
if (!fileAcceptResult.isAccepted()) {
continue;
}
(fileAcceptResult.isPrimary() ? targets : secondaryTargets).addAll(EnvironmentVariablesUtil.getElementsByKey(key, provider.getElements(psiFileTarget)));
}
return true;
}, GlobalSearchScope.allScope(project));
return (targets.size() > 0 ? targets : secondaryTargets).toArray(PsiElement.EMPTY_ARRAY);
}
/**
* @param project project
* @param key environment variable key
* @return All key usages, like getenv('KEY')
*/
@NotNull
public static PsiElement[] getKeyUsages(Project project, String key) {
List<PsiElement> targets = new ArrayList<>();
PsiSearchHelper searchHelper = PsiSearchHelper.getInstance(project);
Processor<PsiFile> psiFileProcessor = psiFile -> {
for (EnvironmentVariablesUsagesProvider provider : EnvironmentVariablesProviderUtil.USAGES_PROVIDERS) {
targets.addAll(EnvironmentVariablesUtil.getUsagesElementsByKey(key, provider.getUsages(psiFile)));
}
return true;
};
searchHelper.processAllFilesWithWord(key, GlobalSearchScope.allScope(project), psiFileProcessor, true);
searchHelper.processAllFilesWithWordInLiterals(key, GlobalSearchScope.allScope(project), psiFileProcessor);
searchHelper.processAllFilesWithWordInText(key, GlobalSearchScope.allScope(project), psiFileProcessor, true);
return targets.toArray(PsiElement.EMPTY_ARRAY);
}
private static FileAcceptResult getFileAcceptResult(VirtualFile virtualFile) {
for (EnvironmentVariablesProvider provider : EnvironmentVariablesProviderUtil.PROVIDERS) {
FileAcceptResult fileAcceptResult = provider.acceptFile(virtualFile);
if (!fileAcceptResult.isAccepted()) {
continue;
}
return fileAcceptResult;
}
return FileAcceptResult.NOT_ACCEPTED;
}
}

View File

@@ -0,0 +1,16 @@
package ru.adelf.idea.dotenv.api;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.models.KeyValuePsiElement;
import java.util.Collection;
public interface EnvironmentVariablesProvider {
@NotNull
FileAcceptResult acceptFile(VirtualFile file);
@NotNull
Collection<KeyValuePsiElement> getElements(PsiFile psiFile);
}

View File

@@ -0,0 +1,15 @@
package ru.adelf.idea.dotenv.api;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
public interface EnvironmentVariablesUsagesProvider {
boolean acceptFile(VirtualFile file);
@NotNull
Collection<KeyUsagePsiElement> getUsages(PsiFile psiFile);
}

View File

@@ -0,0 +1,23 @@
package ru.adelf.idea.dotenv.api;
public class FileAcceptResult {
public final static FileAcceptResult ACCEPTED = new FileAcceptResult(true, true);
public final static FileAcceptResult NOT_ACCEPTED = new FileAcceptResult(false, true);
public final static FileAcceptResult ACCEPTED_SECONDARY = new FileAcceptResult(true, false);
private final boolean accepted;
private final boolean primary;
private FileAcceptResult(boolean accepted, boolean primary) {
this.accepted = accepted;
this.primary = primary;
}
public boolean isAccepted() {
return accepted;
}
public boolean isPrimary() {
return primary;
}
}

View File

@@ -0,0 +1,29 @@
package ru.adelf.idea.dotenv.common;
import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.PrioritizedLookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.project.Project;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import java.util.Map;
abstract public class BaseEnvCompletionProvider extends CompletionContributor implements GotoDeclarationHandler {
protected void fillCompletionResultSet(@NotNull CompletionResultSet completionResultSet, @NotNull Project project) {
for (Map.Entry<String, String> entry : EnvironmentVariablesApi.getAllKeyValues(project).entrySet()) {
LookupElementBuilder lockup = LookupElementBuilder.create(entry.getKey())
.withCaseSensitivity(false);
if (StringUtils.isNotEmpty(entry.getValue())) {
lockup = lockup.withTailText(" = " + entry.getValue(), true);
}
completionResultSet.addElement(PrioritizedLookupElement.withPriority(lockup, 100));
}
}
}

View File

@@ -0,0 +1,78 @@
package ru.adelf.idea.dotenv.docker;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.yaml.psi.YAMLKeyValue;
import org.jetbrains.yaml.psi.YAMLMapping;
import org.jetbrains.yaml.psi.YAMLScalar;
import org.jetbrains.yaml.psi.YAMLSequenceItem;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import ru.adelf.idea.dotenv.util.EnvironmentVariablesUtil;
public class DockerComposeKeyGotoHandler implements GotoDeclarationHandler {
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) {
if (psiElement == null) {
return PsiElement.EMPTY_ARRAY;
}
if (!psiElement.getContainingFile().getName().equals("docker-compose.yml") && !psiElement.getContainingFile().getName().equals("docker-compose.yaml")) {
return PsiElement.EMPTY_ARRAY;
}
if (psiElement.getParent() == null || psiElement.getParent().getParent() == null || psiElement.getParent().getParent().getParent() == null) {
return PsiElement.EMPTY_ARRAY;
}
psiElement = psiElement.getParent();
if (psiElement instanceof YAMLScalar) {
if (!(psiElement.getParent() instanceof YAMLSequenceItem)) {
return PsiElement.EMPTY_ARRAY;
}
PsiElement yamlKeyValue = psiElement.getParent().getParent().getParent();
if (!(yamlKeyValue instanceof YAMLKeyValue)) {
return PsiElement.EMPTY_ARRAY;
}
if (!"environment".equals(((YAMLKeyValue) yamlKeyValue).getKeyText())) {
return PsiElement.EMPTY_ARRAY;
}
return EnvironmentVariablesApi.getKeyUsages(psiElement.getProject(), EnvironmentVariablesUtil.getKeyFromString(((YAMLScalar) psiElement).getTextValue()));
}
if (psiElement instanceof YAMLKeyValue) {
if (!(psiElement.getParent() instanceof YAMLMapping)) {
return PsiElement.EMPTY_ARRAY;
}
PsiElement yamlKeyValue = psiElement.getParent().getParent();
if (!(yamlKeyValue instanceof YAMLKeyValue)) {
return PsiElement.EMPTY_ARRAY;
}
if (!"environment".equals(((YAMLKeyValue) yamlKeyValue).getKeyText())) {
return PsiElement.EMPTY_ARRAY;
}
return EnvironmentVariablesApi.getKeyUsages(psiElement.getProject(), ((YAMLKeyValue) psiElement).getKeyText());
}
return PsiElement.EMPTY_ARRAY;
}
@Nullable
@Override
public String getActionText(@NotNull DataContext dataContext) {
return null;
}
}

View File

@@ -0,0 +1,84 @@
package ru.adelf.idea.dotenv.docker;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiRecursiveElementVisitor;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.yaml.psi.*;
import ru.adelf.idea.dotenv.models.EnvironmentKeyValue;
import ru.adelf.idea.dotenv.models.KeyValuePsiElement;
import ru.adelf.idea.dotenv.util.EnvironmentVariablesUtil;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
class DockerComposeYamlPsiElementsVisitor extends PsiRecursiveElementVisitor {
private final Collection<KeyValuePsiElement> collectedItems = new HashSet<>();
@Override
public void visitElement(@NotNull PsiElement element) {
if (element instanceof YAMLKeyValue) {
this.visitKeyValue((YAMLKeyValue) element);
}
super.visitElement(element);
}
Collection<KeyValuePsiElement> getCollectedItems() {
return collectedItems;
}
private void visitKeyValue(YAMLKeyValue yamlKeyValue) {
if ("environment".equals(yamlKeyValue.getKeyText())) {
for (YAMLSequenceItem yamlSequenceItem : getSequenceItems(yamlKeyValue)) {
YAMLValue el = yamlSequenceItem.getValue();
if (el instanceof YAMLScalar) {
EnvironmentKeyValue keyValue = EnvironmentVariablesUtil.getKeyValueFromString(((YAMLScalar) el).getTextValue());
if (StringUtils.isNotBlank(keyValue.getKey())) {
collectedItems.add(new KeyValuePsiElement(keyValue.getKey(), keyValue.getValue(), el));
}
}
}
for (YAMLKeyValue keyValue : getMappingItems(yamlKeyValue)) {
collectedItems.add(new KeyValuePsiElement(keyValue.getKeyText(), keyValue.getValueText(), keyValue));
}
}
}
/**
* FOO:
* - foobar
* <p>
* FOO: [foobar]
*/
@NotNull
private Collection<YAMLSequenceItem> getSequenceItems(@NotNull YAMLKeyValue yamlKeyValue) {
PsiElement yamlSequence = yamlKeyValue.getLastChild();
if (yamlSequence instanceof YAMLSequence) {
return ((YAMLSequence) yamlSequence).getItems();
}
return Collections.emptyList();
}
/**
* FOO:
* bar: true
*/
@NotNull
private Collection<YAMLKeyValue> getMappingItems(@NotNull YAMLKeyValue yamlKeyValue) {
PsiElement yamlMapping = yamlKeyValue.getLastChild();
if (yamlMapping instanceof YAMLMapping) {
return ((YAMLMapping) yamlMapping).getKeyValues();
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,38 @@
package ru.adelf.idea.dotenv.docker;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.yaml.YAMLFileType;
import org.jetbrains.yaml.psi.YAMLFile;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesProvider;
import ru.adelf.idea.dotenv.api.FileAcceptResult;
import ru.adelf.idea.dotenv.models.KeyValuePsiElement;
import java.util.Collection;
import java.util.Collections;
public class DockerComposeYamlVariablesProvider implements EnvironmentVariablesProvider {
@NotNull
@Override
public FileAcceptResult acceptFile(VirtualFile file) {
if (file.getName().equals("docker-compose.yml") || file.getName().equals("docker-compose.yaml")) {
return file.getFileType().equals(YAMLFileType.YML) ? FileAcceptResult.ACCEPTED : FileAcceptResult.NOT_ACCEPTED;
}
return FileAcceptResult.NOT_ACCEPTED;
}
@NotNull
@Override
public Collection<KeyValuePsiElement> getElements(PsiFile psiFile) {
if (psiFile instanceof YAMLFile) {
DockerComposeYamlPsiElementsVisitor visitor = new DockerComposeYamlPsiElementsVisitor();
psiFile.acceptChildren(visitor);
return visitor.getCollectedItems();
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,40 @@
package ru.adelf.idea.dotenv.docker;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.docker.dockerFile.parser.psi.DockerFileEnvRegularDeclaration;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import ru.adelf.idea.dotenv.util.EnvironmentVariablesUtil;
public class DockerfileKeyGotoHandler implements GotoDeclarationHandler {
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) {
if (psiElement == null || psiElement.getParent() == null) {
return PsiElement.EMPTY_ARRAY;
}
if (!psiElement.getContainingFile().getName().equals("Dockerfile")) {
return PsiElement.EMPTY_ARRAY;
}
psiElement = psiElement.getParent();
if (!(psiElement instanceof DockerFileEnvRegularDeclaration)) {
return PsiElement.EMPTY_ARRAY;
}
return EnvironmentVariablesApi.getKeyUsages(psiElement.getProject(), EnvironmentVariablesUtil.getKeyFromString((((DockerFileEnvRegularDeclaration) psiElement).getDeclaredName().getText())));
}
@Nullable
@Override
public String getActionText(@NotNull DataContext dataContext) {
return null;
}
}

View File

@@ -0,0 +1,37 @@
package ru.adelf.idea.dotenv.docker;
import com.intellij.docker.dockerFile.parser.psi.DockerFileEnvRegularDeclaration;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiRecursiveElementVisitor;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.models.KeyValuePsiElement;
import java.util.Collection;
import java.util.HashSet;
class DockerfilePsiElementsVisitor extends PsiRecursiveElementVisitor {
private final Collection<KeyValuePsiElement> collectedItems = new HashSet<>();
@Override
public void visitElement(@NotNull PsiElement element) {
if (element instanceof DockerFileEnvRegularDeclaration) {
this.visitProperty((DockerFileEnvRegularDeclaration) element);
}
super.visitElement(element);
}
private void visitProperty(DockerFileEnvRegularDeclaration envRegularDeclaration) {
if (StringUtils.isNotBlank(envRegularDeclaration.getDeclaredName().getText()) && envRegularDeclaration.getRegularValue() != null) {
collectedItems.add(new KeyValuePsiElement(
envRegularDeclaration.getDeclaredName().getText(),
envRegularDeclaration.getRegularValue().getText(),
envRegularDeclaration));
}
}
Collection<KeyValuePsiElement> getCollectedItems() {
return collectedItems;
}
}

View File

@@ -0,0 +1,34 @@
package ru.adelf.idea.dotenv.docker;
import com.intellij.docker.dockerFile.DockerFileType;
import com.intellij.docker.dockerFile.DockerPsiFile;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesProvider;
import ru.adelf.idea.dotenv.api.FileAcceptResult;
import ru.adelf.idea.dotenv.models.KeyValuePsiElement;
import java.util.Collection;
import java.util.Collections;
public class DockerfileVariablesProvider implements EnvironmentVariablesProvider {
@NotNull
@Override
public FileAcceptResult acceptFile(VirtualFile file) {
return file.getFileType().equals(DockerFileType.DOCKER_FILE_TYPE) ? FileAcceptResult.ACCEPTED : FileAcceptResult.NOT_ACCEPTED;
}
@NotNull
@Override
public Collection<KeyValuePsiElement> getElements(PsiFile psiFile) {
if(psiFile instanceof DockerPsiFile) {
DockerfilePsiElementsVisitor visitor = new DockerfilePsiElementsVisitor();
psiFile.acceptChildren(visitor);
return visitor.getCollectedItems();
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,103 @@
package ru.adelf.idea.dotenv.extension;
import com.intellij.codeInsight.generation.CommenterDataHolder;
import com.intellij.codeInsight.generation.SelfManagingCommenter;
import com.intellij.lang.Commenter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiFile;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class DotEnvCommenter implements Commenter, SelfManagingCommenter<CommenterDataHolder> {
private static final String HASH_COMMENT_PREFIX = "#";
public String getLineCommentPrefix() {
return HASH_COMMENT_PREFIX;
}
public String getBlockCommentPrefix() {
return null;
}
public String getBlockCommentSuffix() {
return null;
}
public String getCommentedBlockCommentPrefix() {
return null;
}
public String getCommentedBlockCommentSuffix() {
return null;
}
@Nullable
@Override
public CommenterDataHolder createLineCommentingState(int startLine, int endLine, @NotNull Document document, @NotNull PsiFile file) {
return null;
}
@Nullable
@Override
public CommenterDataHolder createBlockCommentingState(int selectionStart,
int selectionEnd,
@NotNull Document document,
@NotNull PsiFile file) {
return null;
}
@Override
public void commentLine(int line, int offset, @NotNull Document document, @NotNull CommenterDataHolder data) {
document.insertString(offset, HASH_COMMENT_PREFIX);
}
@Override
public void uncommentLine(int line, int offset, @NotNull Document document, @NotNull CommenterDataHolder data) {
document.deleteString(offset, offset + 1);
}
@Override
public boolean isLineCommented(int line, int offset, @NotNull Document document, @NotNull CommenterDataHolder data) {
return CharArrayUtil.regionMatches(document.getCharsSequence(), offset, HASH_COMMENT_PREFIX);
}
@Nullable
@Override
public String getCommentPrefix(int line, @NotNull Document document, @NotNull CommenterDataHolder data) {
return HASH_COMMENT_PREFIX;
}
@Nullable
@Override
public TextRange getBlockCommentRange(int selectionStart,
int selectionEnd,
@NotNull Document document,
@NotNull CommenterDataHolder data) {
throw new UnsupportedOperationException();
}
@Nullable
@Override
public String getBlockCommentPrefix(int selectionStart, @NotNull Document document, @NotNull CommenterDataHolder data) {
return getBlockCommentPrefix();
}
@Nullable
@Override
public String getBlockCommentSuffix(int selectionEnd, @NotNull Document document, @NotNull CommenterDataHolder data) {
return getBlockCommentSuffix();
}
@Override
public void uncommentBlockComment(int startOffset, int endOffset, Document document, CommenterDataHolder data) {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public TextRange insertBlockComment(int startOffset, int endOffset, Document document, CommenterDataHolder data) {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,65 @@
package ru.adelf.idea.dotenv.extension;
import com.intellij.lang.cacheBuilder.DefaultWordsScanner;
import com.intellij.lang.cacheBuilder.WordsScanner;
import com.intellij.lang.findUsages.FindUsagesProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.grammars.DotEnvLexerAdapter;
import ru.adelf.idea.dotenv.psi.DotEnvProperty;
import ru.adelf.idea.dotenv.psi.DotEnvTypes;
public class DotEnvFindUsagesProvider implements FindUsagesProvider {
@Nullable
@Override
public WordsScanner getWordsScanner() {
return new DefaultWordsScanner(new DotEnvLexerAdapter(),
TokenSet.create(DotEnvTypes.PROPERTY),
TokenSet.create(DotEnvTypes.COMMENT),
TokenSet.EMPTY);
}
@Override
public boolean canFindUsagesFor(@NotNull PsiElement psiElement) {
return psiElement instanceof PsiNamedElement;
}
@Nullable
@Override
public String getHelpId(@NotNull PsiElement psiElement) {
return null;
}
@NotNull
@Override
public String getType(@NotNull PsiElement element) {
if (element instanceof DotEnvProperty) {
return "Environment variable";
} else {
return "";
}
}
@NotNull
@Override
public String getDescriptiveName(@NotNull PsiElement element) {
if (element instanceof DotEnvProperty) {
return ((DotEnvProperty) element).getKeyText();
} else {
return "";
}
}
@NotNull
@Override
public String getNodeText(@NotNull PsiElement element, boolean useFullName) {
if (element instanceof DotEnvProperty) {
return ((DotEnvProperty) element).getKeyText() + ":" + ((DotEnvProperty) element).getValueText();
} else {
return "";
}
}
}

View File

@@ -0,0 +1,35 @@
package ru.adelf.idea.dotenv.extension;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import ru.adelf.idea.dotenv.psi.DotEnvKey;
public class DotEnvKeyGotoHandler implements GotoDeclarationHandler {
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) {
if(psiElement == null || psiElement.getParent() == null) {
return PsiElement.EMPTY_ARRAY;
}
psiElement = psiElement.getParent();
if(!(psiElement instanceof DotEnvKey)) {
return PsiElement.EMPTY_ARRAY;
}
return EnvironmentVariablesApi.getKeyUsages(psiElement.getProject(), psiElement.getText());
}
@Nullable
@Override
public String getActionText(@NotNull DataContext dataContext) {
return null;
}
}

View File

@@ -0,0 +1,13 @@
package ru.adelf.idea.dotenv.extension;
import com.intellij.lang.refactoring.RefactoringSupportProvider;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.psi.DotEnvProperty;
public class DotEnvRefactoringSupportProvider extends RefactoringSupportProvider {
@Override
public boolean isMemberInplaceRenameAvailable(@NotNull PsiElement element, PsiElement context) {
return element instanceof DotEnvProperty;
}
}

View File

@@ -0,0 +1,57 @@
package ru.adelf.idea.dotenv.extension;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import java.util.Arrays;
public class DotEnvReference extends PsiReferenceBase<PsiElement> implements PsiPolyVariantReference {
private final String key;
public DotEnvReference(@NotNull PsiElement element, TextRange textRange) {
super(element, textRange);
key = element.getText().substring(textRange.getStartOffset(), textRange.getEndOffset());
}
public DotEnvReference(@NotNull PsiElement element, TextRange textRange, String key) {
super(element, textRange);
this.key = key;
}
@NotNull
@Override
public ResolveResult[] multiResolve(boolean incompleteCode) {
final PsiElement[] elements = EnvironmentVariablesApi.getKeyDeclarations(myElement.getProject(), key);
return Arrays.stream(elements)
.filter(psiElement -> psiElement instanceof PsiNamedElement)
.map(PsiElementResolveResult::new)
.toArray(ResolveResult[]::new);
}
@Nullable
@Override
public PsiElement resolve() {
ResolveResult[] resolveResults = multiResolve(false);
return resolveResults.length == 1 ? resolveResults[0].getElement() : null;
}
@NotNull
@Override
public Object[] getVariants() {
Project project = myElement.getProject();
final PsiElement[] elements = EnvironmentVariablesApi.getKeyDeclarations(project, key);
return Arrays.stream(elements)
.filter(psiElement -> psiElement instanceof PsiNamedElement)
.map(psiElement -> LookupElementBuilder.create(psiElement).
withTypeText(psiElement.getContainingFile().getName()))
.toArray(LookupElement[]::new);
}
}

View File

@@ -0,0 +1,41 @@
package ru.adelf.idea.dotenv.extension;
import com.intellij.openapi.application.QueryExecutorBase;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.SearchRequestCollector;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.UsageSearchContext;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.psi.DotEnvProperty;
public class DotEnvReferencesSearcher extends QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters> {
public DotEnvReferencesSearcher() {
super(true);
}
@Override
public void processQuery(@NotNull ReferencesSearch.SearchParameters queryParameters, @NotNull Processor<? super PsiReference> consumer) {
PsiElement refElement = queryParameters.getElementToSearch();
if (!(refElement instanceof DotEnvProperty)) return;
addPropertyUsages((DotEnvProperty)refElement, queryParameters.getEffectiveSearchScope(), queryParameters.getOptimizer());
}
private static void addPropertyUsages(@NotNull DotEnvProperty property, @NotNull SearchScope scope, @NotNull SearchRequestCollector collector) {
final String propertyName = property.getName();
if (StringUtil.isNotEmpty(propertyName)) {
/*SearchScope additional = GlobalSearchScope.EMPTY_SCOPE;
for (CustomPropertyScopeProvider provider : CustomPropertyScopeProvider.EP_NAME.getExtensionList()) {
additional = additional.union(provider.getScope(property.getProject()));
}
SearchScope propScope = scope.intersectWith(property.getUseScope()).intersectWith(additional);*/
collector.searchWord(propertyName, scope, UsageSearchContext.ANY, true, property);
collector.searchWord("process.env." + propertyName, scope, UsageSearchContext.ANY, true, property);
}
}
}

View File

@@ -0,0 +1,43 @@
package ru.adelf.idea.dotenv.extension;
import com.intellij.lang.ASTNode;
import com.intellij.lang.folding.FoldingBuilderEx;
import com.intellij.lang.folding.FoldingDescriptor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.DumbAware;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvSettings;
import ru.adelf.idea.dotenv.psi.DotEnvValue;
public class DotEnvValuesHiding extends FoldingBuilderEx implements DumbAware {
@Override
public FoldingDescriptor @NotNull [] buildFoldRegions(@NotNull PsiElement root,
@NotNull Document document,
boolean quick) {
if (!DotEnvSettings.getInstance().hideValuesInTheFile) return emptyResult;
return PsiTreeUtil.collectElementsOfType(root, DotEnvValue.class).stream().map(
dotEnvValue -> new FoldingDescriptor(
dotEnvValue.getNode(),
dotEnvValue.getTextRange(),
null,
"Show value"
)
).toArray(FoldingDescriptor[]::new);
}
@Override
public @Nullable String getPlaceholderText(@NotNull ASTNode node) {
return null;
}
@Override
public boolean isCollapsedByDefault(@NotNull ASTNode node) {
return true;
}
private static final FoldingDescriptor[] emptyResult = new FoldingDescriptor[0];
}

View File

@@ -0,0 +1,84 @@
package ru.adelf.idea.dotenv.go;
import com.goide.psi.GoCallExpr;
import com.goide.psi.GoStringLiteral;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionProvider;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvSettings;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import ru.adelf.idea.dotenv.common.BaseEnvCompletionProvider;
public class GoEnvCompletionProvider extends BaseEnvCompletionProvider implements GotoDeclarationHandler {
public GoEnvCompletionProvider() {
extend(CompletionType.BASIC, PlatformPatterns.psiElement(), new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) {
PsiElement psiElement = completionParameters.getOriginalPosition();
if (psiElement == null || !DotEnvSettings.getInstance().completionEnabled) {
return;
}
if (getStringLiteral(psiElement) == null) {
return;
}
fillCompletionResultSet(completionResultSet, psiElement.getProject());
}
});
}
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) {
if (psiElement == null) {
return PsiElement.EMPTY_ARRAY;
}
GoStringLiteral stringLiteral = getStringLiteral(psiElement);
if (stringLiteral == null) {
return PsiElement.EMPTY_ARRAY;
}
return EnvironmentVariablesApi.getKeyDeclarations(psiElement.getProject(), stringLiteral.getDecodedText());
}
@Nullable
private GoStringLiteral getStringLiteral(@NotNull PsiElement psiElement) {
PsiElement parent = psiElement.getParent();
if (!(parent instanceof GoStringLiteral)) {
return null;
}
if (parent.getParent() == null) {
return null;
}
PsiElement candidate = parent.getParent().getParent();
if (candidate instanceof GoCallExpr) {
return GoPsiHelper.getEnvironmentGoLiteral((GoCallExpr) candidate);
}
return null;
}
@Nullable
@Override
public String getActionText(@NotNull DataContext dataContext) {
return null;
}
}

View File

@@ -0,0 +1,37 @@
package ru.adelf.idea.dotenv.go;
import com.goide.psi.GoCallExpr;
import com.goide.psi.GoStringLiteral;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiRecursiveElementVisitor;
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
import java.util.HashSet;
class GoEnvironmentCallsVisitor extends PsiRecursiveElementWalkingVisitor {
final private Collection<KeyUsagePsiElement> collectedItems = new HashSet<>();
@Override
public void visitElement(PsiElement element) {
if(element instanceof GoCallExpr) {
this.visitCall((GoCallExpr) element);
}
super.visitElement(element);
}
private void visitCall(GoCallExpr expression) {
GoStringLiteral stringLiteral = GoPsiHelper.getEnvironmentGoLiteral(expression);
if(stringLiteral != null) {
collectedItems.add(new KeyUsagePsiElement(stringLiteral.getDecodedText(), stringLiteral));
}
}
@NotNull
Collection<KeyUsagePsiElement> getCollectedItems() {
return collectedItems;
}
}

View File

@@ -0,0 +1,32 @@
package ru.adelf.idea.dotenv.go;
import com.goide.GoFileType;
import com.goide.psi.GoFile;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesUsagesProvider;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
import java.util.Collections;
public class GoEnvironmentVariablesUsagesProvider implements EnvironmentVariablesUsagesProvider {
@Override
public boolean acceptFile(VirtualFile file) {
return file.getFileType().equals(GoFileType.INSTANCE);
}
@NotNull
@Override
public Collection<KeyUsagePsiElement> getUsages(PsiFile psiFile) {
if(psiFile instanceof GoFile) {
GoEnvironmentCallsVisitor visitor = new GoEnvironmentCallsVisitor();
psiFile.acceptChildren(visitor);
return visitor.getCollectedItems();
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,50 @@
package ru.adelf.idea.dotenv.go;
import com.goide.psi.GoCallExpr;
import com.goide.psi.GoExpression;
import com.goide.psi.GoReferenceExpression;
import com.goide.psi.GoStringLiteral;
import com.goide.psi.impl.GoPsiUtil;
import java.util.Map;
class GoPsiHelper {
private static final Map<String, Integer> ENV_FUNCTIONS = Map.of(
"os.getenv", 0,
"os.setenv", 0,
"os.lookupenv", 0,
"os.unsetenv", 0
);
/**
* Checks if the call expression belongs to the stdlib os package and
* it's a function which can work with environment variables, such as Getenv or Setenv.
*
* @param callExpression checking element
* @return GoStringLiteral
*/
static GoStringLiteral getEnvironmentGoLiteral(GoCallExpr callExpression) {
GoReferenceExpression ref = GoPsiUtil.getCallReference(callExpression);
if (ref == null) return null;
/*String functionName = StringUtil.toLowerCase(ref.getIdentifier().getText());
if (!ENV_FUNCTIONS.containsKey(functionName)) return false;
PsiElement resolve = ref.resolve();
GoFunctionOrMethodDeclaration declaration = ObjectUtils.tryCast(resolve, GoFunctionOrMethodDeclaration.class);
if (!GoInspectionUtil.isInSdkPackage(declaration, "os")) return false;*/
String functionName = ref.getText().toLowerCase();
if (!ENV_FUNCTIONS.containsKey(functionName)) return null;
int position = ENV_FUNCTIONS.get(functionName);
if (callExpression.getArgumentList().getExpressionList().size() < position + 1) return null;
GoExpression expr = callExpression.getArgumentList().getExpressionList().get(position);
if (!(expr instanceof GoStringLiteral)) return null;
return (GoStringLiteral) expr;
}
}

View File

@@ -0,0 +1,78 @@
package ru.adelf.idea.dotenv.grammars;
import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
import ru.adelf.idea.dotenv.psi.DotEnvTypes;
import com.intellij.psi.TokenType;
import static ru.adelf.idea.dotenv.psi.DotEnvTypes.*;
%%
%class _DotEnvLexer
%implements FlexLexer
%unicode
%function advance
%type IElementType
CRLF=\R
WHITE_SPACE=[\ \t\f]
FIRST_VALUE_CHARACTER=[^ \n\f\r\"\'\\\#] | "\\".
VALUE_CHARACTER=[^\r\n\#]
ANY_CHARACTER=[^\r\n]
QUOTE_VALUE_CHARACTER=[^\\\"]
QUOTED_SYMBOL=[\\][^]
SINGLE_QUOTE_VALUE_CHARACTER=[^\\\']
END_OF_LINE_COMMENT=("#")[^\r\n]*
SEPARATOR=[:=]
KEY_CHARACTER=[^:=\ \n\t\f\\] | "\\ "
QUOTE=[\"]
SINGLE_QUOTE=[\']
COMMENT=["#"]
EXPORT_PREFIX=[eE][xX][pP][oO][rR][tT](" ")+
%state WAITING_KEY
%state WAITING_VALUE
%state WAITING_QUOTED_VALUE
%state WAITING_SINGLE_QUOTED_VALUE
%state WAITING_COMMENT
%%
<YYINITIAL> {END_OF_LINE_COMMENT} { yybegin(YYINITIAL); return DotEnvTypes.COMMENT; }
<YYINITIAL> {KEY_CHARACTER}+ { yybegin(YYINITIAL); return DotEnvTypes.KEY_CHARS; }
<YYINITIAL> {SEPARATOR} { yybegin(WAITING_VALUE); return DotEnvTypes.SEPARATOR; }
<YYINITIAL> {EXPORT_PREFIX} { yybegin(WAITING_KEY); return DotEnvTypes.EXPORT; }
<WAITING_KEY> {KEY_CHARACTER}+ { yybegin(YYINITIAL); return DotEnvTypes.KEY_CHARS; }
<WAITING_VALUE> {CRLF}({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; }
<WAITING_VALUE> {WHITE_SPACE}+ { yybegin(WAITING_VALUE); return TokenType.WHITE_SPACE; }
<WAITING_VALUE> {QUOTE} { yybegin(WAITING_QUOTED_VALUE); return DotEnvTypes.QUOTE; }
<WAITING_VALUE> {SINGLE_QUOTE} { yybegin(WAITING_SINGLE_QUOTED_VALUE); return DotEnvTypes.QUOTE; }
<WAITING_VALUE> {COMMENT} { yybegin(WAITING_COMMENT); return DotEnvTypes.COMMENT; }
<WAITING_VALUE> {FIRST_VALUE_CHARACTER}{VALUE_CHARACTER}* { yybegin(YYINITIAL); return DotEnvTypes.VALUE_CHARS; }
<WAITING_QUOTED_VALUE> {QUOTE} { yybegin(WAITING_COMMENT); return DotEnvTypes.QUOTE; }
<WAITING_QUOTED_VALUE> ({QUOTE_VALUE_CHARACTER}|{QUOTED_SYMBOL})+ { yybegin(WAITING_QUOTED_VALUE); return DotEnvTypes.VALUE_CHARS; }
<WAITING_SINGLE_QUOTED_VALUE> {SINGLE_QUOTE} { yybegin(WAITING_COMMENT); return DotEnvTypes.QUOTE; }
<WAITING_SINGLE_QUOTED_VALUE> ({SINGLE_QUOTE_VALUE_CHARACTER}|{QUOTED_SYMBOL})+ { yybegin(WAITING_SINGLE_QUOTED_VALUE); return DotEnvTypes.VALUE_CHARS; }
<WAITING_COMMENT> {CRLF}({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; }
<WAITING_COMMENT> {ANY_CHARACTER}+ { yybegin(WAITING_COMMENT); return DotEnvTypes.COMMENT; }
({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; }
[^] { return TokenType.BAD_CHARACTER; }

View File

@@ -0,0 +1,9 @@
package ru.adelf.idea.dotenv.grammars;
import com.intellij.lexer.FlexAdapter;
public class DotEnvLexerAdapter extends FlexAdapter {
public DotEnvLexerAdapter() {
super(new _DotEnvLexer(null));
}
}

View File

@@ -0,0 +1,27 @@
{
parserClass="ru.adelf.idea.dotenv.parser.DotEnvParser"
extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
psiClassPrefix="DotEnv"
psiImplClassSuffix="Impl"
psiPackage="ru.adelf.idea.dotenv.psi"
psiImplPackage="ru.adelf.idea.dotenv.psi.impl"
elementTypeHolderClass="ru.adelf.idea.dotenv.psi.DotEnvTypes"
elementTypeClass="ru.adelf.idea.dotenv.psi.DotEnvElementType"
tokenTypeClass="ru.adelf.idea.dotenv.psi.DotEnvTokenType"
psiImplUtilClass="ru.adelf.idea.dotenv.psi.DotEnvPsiUtil"
}
dotEnvFile ::= item_*
private item_ ::= (EXPORT? property|COMMENT|CRLF)
key ::= KEY_CHARS
value ::= VALUE_CHARS+ | QUOTE VALUE_CHARS* QUOTE?
property ::= (key SEPARATOR value? COMMENT?) | key COMMENT? {mixin="ru.adelf.idea.dotenv.psi.DotEnvNamedElementImpl"
implements="ru.adelf.idea.dotenv.psi.DotEnvNamedElement" methods=[getKeyText getValueText getName setName getNameIdentifier]}

View File

@@ -0,0 +1,81 @@
package ru.adelf.idea.dotenv.indexing;
import com.intellij.util.indexing.*;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.DotEnvSettings;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesProvider;
import ru.adelf.idea.dotenv.models.KeyValuePsiElement;
import ru.adelf.idea.dotenv.util.EnvironmentVariablesProviderUtil;
import java.util.HashMap;
import java.util.Map;
public class DotEnvKeyValuesIndex extends FileBasedIndexExtension<String, String> {
public static final ID<String, String> KEY = ID.create("ru.adelf.idea.php.dotenv.keyValues");
@NotNull
@Override
public ID<String, String> getName() {
return KEY;
}
@NotNull
@Override
public DataIndexer<String, String, FileContent> getIndexer() {
return fileContent -> {
final Map<String, String> map = new HashMap<>();
boolean storeValues = DotEnvSettings.getInstance().storeValues;
for (EnvironmentVariablesProvider provider : EnvironmentVariablesProviderUtil.PROVIDERS) {
for (KeyValuePsiElement keyValueElement : provider.getElements(fileContent.getPsiFile())) {
if (storeValues) {
map.put(keyValueElement.getKey(), keyValueElement.getShortValue());
} else {
map.put(keyValueElement.getKey(), "");
}
}
}
return map;
};
}
@NotNull
@Override
public KeyDescriptor<String> getKeyDescriptor() {
return EnumeratorStringDescriptor.INSTANCE;
}
@NotNull
@Override
public DataExternalizer<String> getValueExternalizer() {
return EnumeratorStringDescriptor.INSTANCE;
}
@NotNull
@Override
public FileBasedIndex.InputFilter getInputFilter() {
return file -> {
for (EnvironmentVariablesProvider provider : EnvironmentVariablesProviderUtil.PROVIDERS) {
if (provider.acceptFile(file).isAccepted()) return true;
}
return false;
};
}
@Override
public boolean dependsOnFileContent() {
return true;
}
@Override
public int getVersion() {
return 7;
}
}

View File

@@ -0,0 +1,68 @@
package ru.adelf.idea.dotenv.inspections;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvPsiElementsVisitor;
import ru.adelf.idea.dotenv.models.KeyValuePsiElement;
import ru.adelf.idea.dotenv.psi.DotEnvFile;
import java.util.*;
public class DuplicateKeyInspection extends LocalInspectionTool {
// Change the display name within the plugin.xml
// This needs to be here as otherwise the tests will throw errors.
@NotNull
@Override
public String getDisplayName() {
return "Duplicate key";
}
@Override
public boolean runForWholeFile() {
return true;
}
@Nullable
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
if(!(file instanceof DotEnvFile)) {
return null;
}
return analyzeFile(file, manager, isOnTheFly).getResultsArray();
}
@NotNull
private ProblemsHolder analyzeFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
DotEnvPsiElementsVisitor visitor = new DotEnvPsiElementsVisitor();
file.acceptChildren(visitor);
ProblemsHolder problemsHolder = new ProblemsHolder(manager, file, isOnTheFly);
Map<String, PsiElement> existingKeys = new HashMap<>();
Set<PsiElement> markedElements = new HashSet<>();
for(KeyValuePsiElement keyValue : visitor.getCollectedItems()) {
final String key = keyValue.getKey();
if(existingKeys.containsKey(key)) {
problemsHolder.registerProblem(keyValue.getElement(), "Duplicate key");
PsiElement markedElement = existingKeys.get(key);
if(!markedElements.contains(markedElement)) {
problemsHolder.registerProblem(markedElement, "Duplicate key");
markedElements.add(markedElement);
}
} else {
existingKeys.put(key, keyValue.getElement());
}
}
return problemsHolder;
}
}

View File

@@ -0,0 +1,92 @@
package ru.adelf.idea.dotenv.inspections;
import com.intellij.codeInspection.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.TokenType;
import com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvFactory;
import ru.adelf.idea.dotenv.psi.DotEnvFile;
import ru.adelf.idea.dotenv.psi.DotEnvTypes;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ExtraBlankLineInspection extends LocalInspectionTool {
// Change the display name within the plugin.xml
// This needs to be here as otherwise the tests will throw errors.
@NotNull
@Override
public String getDisplayName() {
return "Extra blank line";
}
@Override
public boolean runForWholeFile() {
return true;
}
@Nullable
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
if (!(file instanceof DotEnvFile)) {
return null;
}
return analyzeFile(file, manager, isOnTheFly).getResultsArray();
}
@NotNull
private ProblemsHolder analyzeFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
ProblemsHolder problemsHolder = new ProblemsHolder(manager, file, isOnTheFly);
PsiTreeUtil.findChildrenOfType(file, PsiWhiteSpaceImpl.class).forEach(whiteSpace -> {
Pattern pattern = Pattern.compile("\r\n|\r|\n");
Matcher matcher = pattern.matcher(whiteSpace.getText());
int count = 0;
while (matcher.find())
count++;
if (count > 2) {
problemsHolder.registerProblem(whiteSpace,
"Only one extra line allowed between properties",
new RemoveExtraBlankLineQuickFix());
}
});
return problemsHolder;
}
private static class RemoveExtraBlankLineQuickFix implements LocalQuickFix {
@NotNull
@Override
public String getName() {
return "Remove extra blank line";
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
try {
PsiElement psiElement = descriptor.getPsiElement();
PsiElement newPsiElement = DotEnvFactory.createFromText(project, TokenType.WHITE_SPACE, "\n\n");
psiElement.replace(newPsiElement);
} catch (IncorrectOperationException e) {
Logger.getInstance(ExtraBlankLineInspection.class).error(e);
}
}
@NotNull
public String getFamilyName() {
return getName();
}
}
}

View File

@@ -0,0 +1,80 @@
package ru.adelf.idea.dotenv.inspections;
import com.intellij.codeInspection.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvFactory;
import ru.adelf.idea.dotenv.psi.DotEnvFile;
import ru.adelf.idea.dotenv.psi.DotEnvTypes;
import ru.adelf.idea.dotenv.psi.impl.DotEnvKeyImpl;
public class IncorrectDelimiterInspection extends LocalInspectionTool {
// Change the display name within the plugin.xml
// This needs to be here as otherwise the tests will throw errors.
@NotNull
@Override
public String getDisplayName() {
return "Incorrect delimiter";
}
@Override
public boolean runForWholeFile() {
return true;
}
@Nullable
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
if (!(file instanceof DotEnvFile)) {
return null;
}
return analyzeFile(file, manager, isOnTheFly).getResultsArray();
}
@NotNull
private ProblemsHolder analyzeFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
ProblemsHolder problemsHolder = new ProblemsHolder(manager, file, isOnTheFly);
PsiTreeUtil.findChildrenOfType(file, DotEnvKeyImpl.class).forEach(key -> {
if (key.getText().contains("-")) {
problemsHolder.registerProblem(key, "Expected: '_' Found: '-'"/*, new ReplaceDelimiterQuickFix()*/);
}
});
return problemsHolder;
}
private static class ReplaceDelimiterQuickFix implements LocalQuickFix {
@NotNull
@Override
public String getName() {
return "Replace delimiter";
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
try {
PsiElement psiElement = descriptor.getPsiElement();
PsiElement newPsiElement = DotEnvFactory.createFromText(project, DotEnvTypes.KEY,
psiElement.getText().replace("-","_")+"=dummy");
psiElement.replace(newPsiElement);
} catch (IncorrectOperationException e) {
Logger.getInstance(IncorrectDelimiterInspection.class).error(e);
}
}
@NotNull
public String getFamilyName() {
return getName();
}
}
}

View File

@@ -0,0 +1,49 @@
package ru.adelf.idea.dotenv.inspections;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.psi.DotEnvFile;
import ru.adelf.idea.dotenv.psi.DotEnvKey;
public class LeadingCharacterInspection extends LocalInspectionTool {
// Change the display name within the plugin.xml
// This needs to be here as otherwise the tests will throw errors.
@NotNull
@Override
public String getDisplayName() {
return "Invalid leading character";
}
@Nullable
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
if (!(file instanceof DotEnvFile)) {
return null;
}
return analyzeFile(file, manager, isOnTheFly).getResultsArray();
}
@NotNull
private ProblemsHolder analyzeFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
ProblemsHolder problemsHolder = new ProblemsHolder(manager, file, isOnTheFly);
PsiTreeUtil.findChildrenOfType(file, DotEnvKey.class).forEach(dotEnvKey -> {
// Also accepts lower case chars as keys with lower case chars are handled by another inspection
// same for dash (-> IncorrectDelimiter
if (!dotEnvKey.getText().matches("[A-Za-z_-].*")){
problemsHolder.registerProblem(dotEnvKey,
"Invalid first char for a key. Only A-Z and '_' are allowed.");
}
});
return problemsHolder;
}
}

View File

@@ -0,0 +1,78 @@
package ru.adelf.idea.dotenv.inspections;
import com.intellij.codeInspection.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvFactory;
import ru.adelf.idea.dotenv.psi.DotEnvFile;
import ru.adelf.idea.dotenv.psi.DotEnvKey;
import ru.adelf.idea.dotenv.psi.DotEnvTypes;
public class LowercaseKeyInspection extends LocalInspectionTool {
// Change the display name within the plugin.xml
// This needs to be here as otherwise the tests will throw errors.
@NotNull
@Override
public String getDisplayName() {
return "Key uses lowercase chars";
}
@Nullable
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
if (!(file instanceof DotEnvFile)) {
return null;
}
return analyzeFile(file, manager, isOnTheFly).getResultsArray();
}
@NotNull
private ProblemsHolder analyzeFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
ProblemsHolder problemsHolder = new ProblemsHolder(manager, file, isOnTheFly);
PsiTreeUtil.findChildrenOfType(file, DotEnvKey.class).forEach(dotEnvKey -> {
if (dotEnvKey.getText().matches(".*[a-z].*")) {
problemsHolder.registerProblem(dotEnvKey,
"Key uses lowercase chars. Only keys with uppercase chars are allowed."/*,
new ForceUppercaseQuickFix()*/
);
}
});
return problemsHolder;
}
private static class ForceUppercaseQuickFix implements LocalQuickFix {
@NotNull
@Override
public String getName() {
return "Change to uppercase";
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
try {
PsiElement psiElement = descriptor.getPsiElement();
PsiElement newPsiElement = DotEnvFactory.createFromText(project, DotEnvTypes.KEY,
psiElement.getText().toUpperCase() +"=dummy");
psiElement.replace(newPsiElement);
} catch (IncorrectOperationException e) {
Logger.getInstance(IncorrectDelimiterInspection.class).error(e);
}
}
@NotNull
public String getFamilyName() {
return getName();
}
}
}

View File

@@ -0,0 +1,98 @@
package ru.adelf.idea.dotenv.inspections;
import com.intellij.codeInspection.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvFactory;
import ru.adelf.idea.dotenv.psi.DotEnvFile;
import ru.adelf.idea.dotenv.psi.DotEnvProperty;
import ru.adelf.idea.dotenv.psi.DotEnvTypes;
import java.util.regex.Pattern;
public class SpaceAroundSeparatorInspection extends LocalInspectionTool {
// Change the display name within the plugin.xml
// This needs to be here as otherwise the tests will throw errors.
@NotNull
@Override
public String getDisplayName() {
return "Extra spaces surrounding '='";
}
@Nullable
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
if (!(file instanceof DotEnvFile)) {
return null;
}
return analyzeFile(file, manager, isOnTheFly).getResultsArray();
}
@NotNull
private ProblemsHolder analyzeFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
ProblemsHolder problemsHolder = new ProblemsHolder(manager, file, isOnTheFly);
PsiTreeUtil.findChildrenOfType(file, DotEnvProperty.class).forEach(dotEnvProperty -> {
String key = dotEnvProperty.getKey().getText();
String value = dotEnvProperty.getValue() != null ? dotEnvProperty.getValue().getText() : "";
String separator = dotEnvProperty.getText()
.replaceFirst("^" + Pattern.quote(key) , "")
.replaceFirst(Pattern.quote(value) + "$", "");
if (separator.matches("([ \t]+=.*)|(.*=[ \t]+)")) {
problemsHolder.registerProblem(dotEnvProperty,
new TextRange(
dotEnvProperty.getKey().getText().length(),
dotEnvProperty.getKey().getText().length() + separator.length()
),
"Extra spaces surrounding '='",
new RemoveSpaceAroundSeparatorQuickFix()
);
}
});
return problemsHolder;
}
private static class RemoveSpaceAroundSeparatorQuickFix implements LocalQuickFix {
@NotNull
@Override
public String getName() {
return "Remove spaces surrounding '='";
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
try {
DotEnvProperty dotEnvProperty = (DotEnvProperty) descriptor.getPsiElement();
String key = dotEnvProperty.getKey().getText();
String value = dotEnvProperty.getValue() != null ? dotEnvProperty.getValue().getText() : "";
PsiElement newPsiElement = DotEnvFactory.createFromText(
project,
DotEnvTypes.PROPERTY,
key + "=" + value
);
dotEnvProperty.replace(newPsiElement);
} catch (IncorrectOperationException e) {
Logger.getInstance(ExtraBlankLineInspection.class).error(e);
}
}
@NotNull
public String getFamilyName() {
return getName();
}
}
}

View File

@@ -0,0 +1,109 @@
package ru.adelf.idea.dotenv.inspections;
import com.intellij.codeInspection.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvFactory;
import ru.adelf.idea.dotenv.psi.DotEnvFile;
import ru.adelf.idea.dotenv.psi.DotEnvTypes;
import ru.adelf.idea.dotenv.psi.DotEnvValue;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class SpaceInsideNonQuotedInspection extends LocalInspectionTool {
// Change the display name within the plugin.xml
// This needs to be here as otherwise the tests will throw errors.
@NotNull
@Override
public String getDisplayName() {
return "Space inside non-quoted value";
}
private AddQuotesQuickFix addQuotesQuickFix = new AddQuotesQuickFix();
@Override
public boolean runForWholeFile() {
return true;
}
@Nullable
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
if (!(file instanceof DotEnvFile)) {
return null;
}
return analyzeFile(file, manager, isOnTheFly).getResultsArray();
}
@NotNull
private ProblemsHolder analyzeFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
ProblemsHolder problemsHolder = new ProblemsHolder(manager, file, isOnTheFly);
PsiTreeUtil.findChildrenOfType(file, DotEnvValue.class).forEach(dotEnvValue -> {
// first child VALUE_CHARS -> non quoted value
// first child QUOTE -> quoted value
if(dotEnvValue.getFirstChild().getNode().getElementType() == DotEnvTypes.VALUE_CHARS) {
if (dotEnvValue.getText().trim().contains(" ")) {
problemsHolder.registerProblem(dotEnvValue, "Space inside allowed only for quoted values", addQuotesQuickFix);
}
}
});
return problemsHolder;
}
private static class AddQuotesQuickFix implements LocalQuickFix {
@NotNull
@Override
public String getName() {
return "Add quotes";
}
/**
* Adds quotes to DotEnvValue element
*
* @param project The project that contains the file being edited.
* @param descriptor A problem found by this inspection.
*/
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
// counting each quote type " AND '. The quickfix will use the most common quote type.
String quote;
Supplier<Stream<DotEnvValue>> supplier = () -> PsiTreeUtil.findChildrenOfType(descriptor.getPsiElement().getContainingFile(), DotEnvValue.class)
.stream()
.filter(dotEnvValue -> dotEnvValue.getFirstChild().getNode().getElementType() == DotEnvTypes.QUOTE);
long total = supplier.get().count();
long doubleQuoted = supplier.get().filter(dotEnvValue -> dotEnvValue.getFirstChild().getText().contains("\"")).count();
long singleQuoted = total - doubleQuoted;
if (doubleQuoted > singleQuoted) {
quote = "\"";
} else {
quote = "'";
}
try {
DotEnvValue valueElement = (DotEnvValue) descriptor.getPsiElement();
PsiElement newValueElement = DotEnvFactory.createFromText(project, DotEnvTypes.VALUE, "DUMMY=" + quote + valueElement.getText() + quote);
valueElement.getNode().getTreeParent().replaceChild(valueElement.getNode(), newValueElement.getNode());
} catch (IncorrectOperationException e) {
Logger.getInstance(SpaceInsideNonQuotedInspection.class).error(e);
}
}
@NotNull
public String getFamilyName() {
return getName();
}
}
}

View File

@@ -0,0 +1,97 @@
package ru.adelf.idea.dotenv.inspections;
import com.intellij.codeInspection.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.TokenType;
import com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvFactory;
import ru.adelf.idea.dotenv.psi.DotEnvFile;
import ru.adelf.idea.dotenv.psi.DotEnvTypes;
import ru.adelf.idea.dotenv.psi.DotEnvValue;
import ru.adelf.idea.dotenv.psi.impl.DotEnvValueImpl;
public class TrailingWhitespaceInspection extends LocalInspectionTool {
// Change the display name within the plugin.xml
// This needs to be here as otherwise the tests will throw errors.
@NotNull
@Override
public String getDisplayName() {
return "Value has trailing whitespace";
}
@Nullable
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
if (!(file instanceof DotEnvFile)) {
return null;
}
return analyzeFile(file, manager, isOnTheFly).getResultsArray();
}
@NotNull
private ProblemsHolder analyzeFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
ProblemsHolder problemsHolder = new ProblemsHolder(manager, file, isOnTheFly);
PsiTreeUtil.findChildrenOfType(file, DotEnvValue.class).forEach(dotEnvValue -> {
if (dotEnvValue.getText().matches(".*[ \\t]+")) {
problemsHolder.registerProblem(dotEnvValue,
new TextRange(dotEnvValue.getText().stripTrailing().length(), dotEnvValue.getText().length()),
"Line has trailing whitespace.",
new TrailingWhitespaceInspection.RemoveTrailingWhitespaceQuickFix()
);
}
});
PsiTreeUtil.findChildrenOfType(file, PsiWhiteSpaceImpl.class).forEach(whiteSpace -> {
if (whiteSpace.getText().matches("\\s*[ \\t]\\n\\s*")) {
problemsHolder.registerProblem(whiteSpace,
"Line has trailing whitespace.",
new TrailingWhitespaceInspection.RemoveTrailingWhitespaceQuickFix()
);
}
});
return problemsHolder;
}
private static class RemoveTrailingWhitespaceQuickFix implements LocalQuickFix {
@NotNull
@Override
public String getName() {
return "Remove trailing whitespace";
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
try {
PsiElement psiElement = descriptor.getPsiElement();
if (psiElement instanceof DotEnvValueImpl) {
PsiElement newPsiElement = DotEnvFactory.createFromText(project, DotEnvTypes.VALUE,
"DUMMY_KEY=" + psiElement.getText().stripTrailing());
psiElement.replace(newPsiElement);
} else if (psiElement instanceof PsiWhiteSpaceImpl) {
PsiElement newPsiElement = DotEnvFactory.createFromText(project, TokenType.WHITE_SPACE,
"DUMMY_KEY='VALUE'" + psiElement.getText().replaceAll("[ \\t]*\\n", "\n"));
psiElement.replace(newPsiElement);
}
} catch (IncorrectOperationException e) {
Logger.getInstance(IncorrectDelimiterInspection.class).error(e);
}
}
@NotNull
public String getFamilyName() {
return getName();
}
}
}

View File

@@ -0,0 +1,25 @@
package ru.adelf.idea.dotenv.java;
import com.intellij.codeInsight.completion.CompletionConfidence;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.util.ThreeState;
import org.jetbrains.annotations.NotNull;
public class JavaCompletionConfidence extends CompletionConfidence {
@NotNull
@Override
public ThreeState shouldSkipAutopopup(@NotNull PsiElement contextElement, @NotNull PsiFile psiFile, int offset) {
PsiElement literal = contextElement.getContext();
if(!(literal instanceof PsiLiteralExpression)) {
return ThreeState.UNSURE;
}
if(JavaPsiHelper.isEnvStringLiteral((PsiLiteralExpression) literal)) {
return ThreeState.NO;
}
return ThreeState.UNSURE;
}
}

View File

@@ -0,0 +1,82 @@
package ru.adelf.idea.dotenv.java;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionProvider;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvSettings;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import ru.adelf.idea.dotenv.common.BaseEnvCompletionProvider;
public class JavaEnvCompletionContributor extends BaseEnvCompletionProvider implements GotoDeclarationHandler {
public JavaEnvCompletionContributor() {
extend(CompletionType.BASIC, PlatformPatterns.psiElement().withParent(PsiLiteralExpression.class), new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) {
PsiElement psiElement = completionParameters.getOriginalPosition();
if (psiElement == null || !DotEnvSettings.getInstance().completionEnabled) {
return;
}
if (getStringLiteral(psiElement) == null) {
return;
}
fillCompletionResultSet(completionResultSet, psiElement.getProject());
}
});
}
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) {
if (psiElement == null) {
return PsiElement.EMPTY_ARRAY;
}
PsiLiteralExpression stringLiteral = getStringLiteral(psiElement);
if (stringLiteral == null) {
return PsiElement.EMPTY_ARRAY;
}
Object value = stringLiteral.getValue();
if (!(value instanceof String)) {
return PsiElement.EMPTY_ARRAY;
}
return EnvironmentVariablesApi.getKeyDeclarations(psiElement.getProject(), (String) value);
}
@Nullable
private PsiLiteralExpression getStringLiteral(@NotNull PsiElement psiElement) {
PsiElement parent = psiElement.getParent();
if (!(parent instanceof PsiLiteralExpression)) {
return null;
}
if (!JavaPsiHelper.isEnvStringLiteral((PsiLiteralExpression) parent)) {
return null;
}
return (PsiLiteralExpression) parent;
}
@Nullable
@Override
public String getActionText(@NotNull DataContext dataContext) {
return null;
}
}

View File

@@ -0,0 +1,39 @@
package ru.adelf.idea.dotenv.java;
import com.intellij.psi.JavaRecursiveElementWalkingVisitor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.PsiMethodCallExpression;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
import java.util.HashSet;
class JavaEnvironmentCallsVisitor extends JavaRecursiveElementWalkingVisitor {
final private Collection<KeyUsagePsiElement> collectedItems = new HashSet<>();
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
if (JavaPsiHelper.isEnvMethodCall(expression)) {
PsiElement[] parameters = expression.getArgumentList().getExpressions();
if (parameters.length == 0) return;
if (!(parameters[0] instanceof PsiLiteralExpression)) return;
Object value = ((PsiLiteralExpression) parameters[0]).getValue();
if (value instanceof String) {
collectedItems.add(new KeyUsagePsiElement((String) value, parameters[0]));
}
}
super.visitMethodCallExpression(expression);
}
@NotNull
Collection<KeyUsagePsiElement> getCollectedItems() {
return collectedItems;
}
}

View File

@@ -0,0 +1,25 @@
package ru.adelf.idea.dotenv.java;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class JavaEnvironmentClasses {
public static boolean isDirectMethodCall(String methodName) {
return methodName.equals("getenv") || methodName.equals("getEnv");
}
@Nullable
public static List<String> getClassNames(String methodName) {
switch (methodName) {
case "get":
return Arrays.asList("Dotenv", "DotEnv");
case "getProperty":
return Collections.singletonList("System");
}
return null;
}
}

View File

@@ -0,0 +1,32 @@
package ru.adelf.idea.dotenv.java;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesUsagesProvider;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
import java.util.Collections;
public class JavaEnvironmentVariablesUsagesProvider implements EnvironmentVariablesUsagesProvider {
@Override
public boolean acceptFile(VirtualFile file) {
return file.getFileType().equals(JavaFileType.INSTANCE);
}
@NotNull
@Override
public Collection<KeyUsagePsiElement> getUsages(PsiFile psiFile) {
if (psiFile instanceof PsiJavaFile) {
JavaEnvironmentCallsVisitor visitor = new JavaEnvironmentCallsVisitor();
psiFile.acceptChildren(visitor);
return visitor.getCollectedItems();
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,69 @@
package ru.adelf.idea.dotenv.java;
import com.intellij.psi.*;
import java.util.List;
class JavaPsiHelper {
/**
* Checks that this element environment string
*
* @param literal Checking psi element
*/
static boolean isEnvStringLiteral(PsiLiteralExpression literal) {
PsiElement parent = literal.getParent();
if (parent instanceof PsiExpressionList) {
PsiExpression[] expressions = ((PsiExpressionList) parent).getExpressions();
if (expressions.length < 1) return false;
if (expressions[0] != literal) return false;
PsiElement methodCall = parent.getParent();
if (!(methodCall instanceof PsiMethodCallExpression)) return false;
return isEnvMethodCall((PsiMethodCallExpression) methodCall);
}
return false;
}
/**
* Checks whether this function reference is reference for env functions, like env or getenv
*
* @param methodCallExpression Checking reference
* @return true if condition filled
*/
static boolean isEnvMethodCall(PsiMethodCallExpression methodCallExpression) {
PsiElement nameElement = methodCallExpression.getMethodExpression().getReferenceNameElement();
if (nameElement == null) {
return false;
}
String methodName = nameElement.getText();
if (JavaEnvironmentClasses.isDirectMethodCall(methodName)) {
return true;
}
List<String> classNames = JavaEnvironmentClasses.getClassNames(methodName);
if (classNames == null) {
return false;
}
for (ResolveResult result : methodCallExpression.getMethodExpression().multiResolve(true)) {
if (result.getElement() instanceof PsiMethod) {
PsiClass psiClass = ((PsiMethod) result.getElement()).getContainingClass();
if (psiClass != null && classNames.contains(psiClass.getName())) {
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,61 @@
/*
package ru.adelf.idea.dotenv.js;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionProvider;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvSettings;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import ru.adelf.idea.dotenv.common.BaseEnvCompletionProvider;
public class JsEnvCompletionProvider extends BaseEnvCompletionProvider implements GotoDeclarationHandler {
public JsEnvCompletionProvider() {
extend(CompletionType.BASIC, PlatformPatterns.psiElement(), new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) {
PsiElement psiElement = completionParameters.getOriginalPosition();
if (psiElement == null || !DotEnvSettings.getInstance(psiElement.getProject()).completionEnabled) {
return;
}
if (!JsPsiHelper.checkPsiElement(psiElement)) {
return;
}
fillCompletionResultSet(completionResultSet, psiElement.getProject());
}
});
}
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) {
if (psiElement == null) {
return PsiElement.EMPTY_ARRAY;
}
if (!JsPsiHelper.checkPsiElement(psiElement)) {
return PsiElement.EMPTY_ARRAY;
}
return EnvironmentVariablesApi.getKeyDeclarations(psiElement.getProject(), psiElement.getText());
}
@Nullable
@Override
public String getActionText(@NotNull DataContext dataContext) {
return null;
}
}*/

View File

@@ -0,0 +1,35 @@
/*
package ru.adelf.idea.dotenv.js;
import com.intellij.lang.javascript.psi.JSReferenceExpression;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiRecursiveElementVisitor;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
import java.util.HashSet;
class JsEnvironmentCallsVisitor extends PsiRecursiveElementVisitor {
final private Collection<KeyUsagePsiElement> collectedItems = new HashSet<>();
@Override
public void visitElement(PsiElement element) {
if(element instanceof JSReferenceExpression) {
String possibleKey = JsPsiHelper.checkReferenceExpression((JSReferenceExpression) element);
if(possibleKey != null) {
collectedItems.add(new KeyUsagePsiElement(possibleKey, element));
}
}
super.visitElement(element);
}
@NotNull
Collection<KeyUsagePsiElement> getCollectedItems() {
return collectedItems;
}
}
*/

View File

@@ -0,0 +1,36 @@
/*
package ru.adelf.idea.dotenv.js;
import com.intellij.lang.javascript.JavaScriptFileType;
import com.intellij.lang.javascript.TypeScriptFileType;
import com.intellij.lang.javascript.psi.JSFile;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesUsagesProvider;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
import java.util.Collections;
public class JsEnvironmentVariablesUsagesProvider implements EnvironmentVariablesUsagesProvider {
@Override
public boolean acceptFile(VirtualFile file) {
return (file.getFileType().equals(JavaScriptFileType.INSTANCE) || file.getFileType().equals(TypeScriptFileType.INSTANCE))
&& !file.getPath().contains("/node_modules/");
}
@NotNull
@Override
public Collection<KeyUsagePsiElement> getUsages(PsiFile psiFile) {
if(psiFile instanceof JSFile) {
JsEnvironmentCallsVisitor visitor = new JsEnvironmentCallsVisitor();
psiFile.acceptChildren(visitor);
return visitor.getCollectedItems();
}
return Collections.emptyList();
}
}
*/

View File

@@ -0,0 +1,42 @@
/*
package ru.adelf.idea.dotenv.js;
import com.intellij.lang.javascript.psi.JSReferenceExpression;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class JsPsiHelper {
static boolean checkPsiElement(@NotNull PsiElement psiElement) {
if(!(psiElement instanceof LeafPsiElement)) {
return false;
}
IElementType elementType = ((LeafPsiElement) psiElement).getElementType();
if(!elementType.toString().equals("JS:IDENTIFIER")) {
return false;
}
PsiElement parent = psiElement.getParent();
if(!(parent instanceof JSReferenceExpression)) {
return false;
}
return ((JSReferenceExpression) parent).getCanonicalText().startsWith("process.env");
}
@Nullable
static String checkReferenceExpression(@NotNull JSReferenceExpression referenceExpression) {
String text = referenceExpression.getCanonicalText();
if(!text.startsWith("process.env.") || text.length() < 13) {
return null;
}
return text.substring(12);
}
}
*/

View File

@@ -0,0 +1,25 @@
package ru.adelf.idea.dotenv.kotlin;
import com.intellij.codeInsight.completion.CompletionConfidence;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.ThreeState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry;
public class KotlinCompletionConfidence extends CompletionConfidence {
@NotNull
@Override
public ThreeState shouldSkipAutopopup(@NotNull PsiElement contextElement, @NotNull PsiFile psiFile, int offset) {
PsiElement literal = contextElement.getContext();
if (!(literal instanceof KtLiteralStringTemplateEntry)) {
return ThreeState.UNSURE;
}
if (KotlinPsiHelper.isEnvStringLiteral((KtLiteralStringTemplateEntry) literal)) {
return ThreeState.NO;
}
return ThreeState.UNSURE;
}
}

View File

@@ -0,0 +1,76 @@
package ru.adelf.idea.dotenv.kotlin;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionProvider;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry;
import ru.adelf.idea.dotenv.DotEnvSettings;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import ru.adelf.idea.dotenv.common.BaseEnvCompletionProvider;
public class KotlinEnvCompletionContributor extends BaseEnvCompletionProvider implements GotoDeclarationHandler {
public KotlinEnvCompletionContributor() {
extend(CompletionType.BASIC, PlatformPatterns.psiElement().withParent(KtLiteralStringTemplateEntry.class), new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) {
PsiElement psiElement = completionParameters.getOriginalPosition();
if (psiElement == null || !DotEnvSettings.getInstance().completionEnabled) {
return;
}
if (getStringLiteral(psiElement) == null) {
return;
}
fillCompletionResultSet(completionResultSet, psiElement.getProject());
}
});
}
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) {
if (psiElement == null) {
return PsiElement.EMPTY_ARRAY;
}
KtLiteralStringTemplateEntry stringLiteral = getStringLiteral(psiElement);
if (stringLiteral == null) {
return PsiElement.EMPTY_ARRAY;
}
return EnvironmentVariablesApi.getKeyDeclarations(psiElement.getProject(), stringLiteral.getText());
}
@Nullable
private KtLiteralStringTemplateEntry getStringLiteral(@NotNull PsiElement psiElement) {
PsiElement parent = psiElement.getParent();
if (!(parent instanceof KtLiteralStringTemplateEntry)) {
return null;
}
if (!KotlinPsiHelper.isEnvStringLiteral((KtLiteralStringTemplateEntry) parent)) {
return null;
}
return (KtLiteralStringTemplateEntry) parent;
}
@Nullable
@Override
public String getActionText(@NotNull DataContext dataContext) {
return null;
}
}

View File

@@ -0,0 +1,33 @@
package ru.adelf.idea.dotenv.kotlin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.psi.KtArrayAccessExpression;
import org.jetbrains.kotlin.psi.KtCallExpression;
import org.jetbrains.kotlin.psi.KtTreeVisitor;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Set;
class KotlinEnvironmentCallsVisitor extends KtTreeVisitor<Set<KeyUsagePsiElement>> {
@Override
public Void visitCallExpression(@NotNull KtCallExpression expression, Set<KeyUsagePsiElement> data) {
KeyUsagePsiElement keyUsage = KotlinPsiHelper.getKeyUsageFromCall(expression);
if (keyUsage != null) {
data.add(keyUsage);
}
return super.visitCallExpression(expression, data);
}
@Override
public Void visitArrayAccessExpression(@NotNull KtArrayAccessExpression expression, Set<KeyUsagePsiElement> data) {
KeyUsagePsiElement keyUsage = KotlinPsiHelper.getKeyUsageFromArrayAccess(expression);
if (keyUsage != null) {
data.add(keyUsage);
}
return super.visitArrayAccessExpression(expression, data);
}
}

View File

@@ -0,0 +1,36 @@
package ru.adelf.idea.dotenv.kotlin;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.KotlinFileType;
import org.jetbrains.kotlin.psi.KtFile;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesUsagesProvider;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class KotlinEnvironmentVariablesUsagesProvider implements EnvironmentVariablesUsagesProvider {
@Override
public boolean acceptFile(VirtualFile file) {
return file.getFileType().equals(KotlinFileType.INSTANCE);
}
@NotNull
@Override
public Collection<KeyUsagePsiElement> getUsages(PsiFile psiFile) {
if (psiFile instanceof KtFile) {
Set<KeyUsagePsiElement> result = new HashSet<>();
KotlinEnvironmentCallsVisitor visitor = new KotlinEnvironmentCallsVisitor();
((KtFile) psiFile).acceptChildren(visitor, result);
return result;
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,178 @@
package ru.adelf.idea.dotenv.kotlin;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.psi.*;
import ru.adelf.idea.dotenv.java.JavaEnvironmentClasses;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Arrays;
import java.util.List;
class KotlinPsiHelper {
/**
* Checks that this element environment string
*
* @param literal Checking psi element
*/
static boolean isEnvStringLiteral(KtLiteralStringTemplateEntry literal) {
if (!(literal.getParent() instanceof KtStringTemplateExpression)) {
return false;
}
PsiElement parent = literal.getParent().getParent();
if (parent instanceof KtValueArgument) {
return isMethodCallLiteral((KtValueArgument) parent);
}
if (parent instanceof KtContainerNode) {
return isArrayAccessLiteral((KtContainerNode) parent);
}
return false;
}
private static boolean isMethodCallLiteral(KtValueArgument valueArgument) {
PsiElement valueArgumentList = valueArgument.getParent();
if (!(valueArgumentList instanceof KtValueArgumentList)) {
return false;
}
if (((KtValueArgumentList) valueArgumentList).getArguments().get(0) != valueArgument) {
return false;
}
PsiElement methodCall = valueArgumentList.getParent();
if (!(methodCall instanceof KtCallExpression)) return false;
return isEnvMethodCall((KtCallExpression) methodCall);
}
private static boolean isArrayAccessLiteral(KtContainerNode containerNode) {
if (!(containerNode.getParent() instanceof KtArrayAccessExpression)) {
return false;
}
return isEnvArrayAccess((KtArrayAccessExpression) containerNode.getParent());
}
/**
* Checks whether this function reference is reference for env functions, like env or getenv
*
* @param methodCallExpression Checking reference
* @return true if condition filled
*/
static boolean isEnvMethodCall(KtCallExpression methodCallExpression) {
PsiElement nameElement = methodCallExpression.getCalleeExpression();
if (!(nameElement instanceof KtNameReferenceExpression)) {
return false;
}
String methodName = ((KtNameReferenceExpression) nameElement).getReferencedName();
if (JavaEnvironmentClasses.isDirectMethodCall(methodName)) {
return true;
}
List<String> classNames = JavaEnvironmentClasses.getClassNames(methodName);
if (classNames == null) {
return false;
}
return checkReferences(methodCallExpression.getCalleeExpression(), classNames);
}
@Nullable
static KeyUsagePsiElement getKeyUsageFromCall(@NotNull KtCallExpression expression) {
if (!isEnvMethodCall(expression)) {
return null;
}
KtValueArgumentList valueArgumentList = expression.getValueArgumentList();
if (valueArgumentList == null || valueArgumentList.getArguments().isEmpty()) {
return null;
}
KtValueArgument valueArgument = valueArgumentList.getArguments().get(0);
if (valueArgument == null) {
return null;
}
return getKeyUsageFromStringTemplate(valueArgument.getFirstChild());
}
/**
* Checks whether this array access is environment call
*
* @param arrayAccess Checking array
* @return true if condition filled
*/
static boolean isEnvArrayAccess(KtArrayAccessExpression arrayAccess) {
List<String> classNames = JavaEnvironmentClasses.getClassNames("get");
if (classNames == null) {
return false;
}
return checkReferences(arrayAccess, classNames);
}
@Nullable
static KeyUsagePsiElement getKeyUsageFromArrayAccess(@NotNull KtArrayAccessExpression expression) {
if (!isEnvArrayAccess(expression)) {
return null;
}
List<KtExpression> indexExpressions = expression.getIndexExpressions();
if (indexExpressions.isEmpty()) {
return null;
}
return getKeyUsageFromStringTemplate(indexExpressions.get(0));
}
@Nullable
private static KeyUsagePsiElement getKeyUsageFromStringTemplate(PsiElement element) {
if (!(element instanceof KtStringTemplateExpression)) {
return null;
}
KtLiteralStringTemplateEntry literal = PsiTreeUtil.findChildOfType(element, KtLiteralStringTemplateEntry.class);
if (literal == null) {
return null;
}
return new KeyUsagePsiElement(literal.getText(), literal);
}
private static boolean checkReferences(PsiElement element, List<String> classNames) {
return Arrays.stream(element.getReferences()).anyMatch(psiReference -> {
PsiElement method = psiReference.resolve();
if (method instanceof KtNamedFunction) {
KtClass ktClass = PsiTreeUtil.getParentOfType(method, KtClass.class);
return ktClass != null && classNames.contains(ktClass.getName());
} else if (method instanceof PsiMethod) {
// Maybe it's a Java reference?
PsiClass psiClass = ((PsiMethod) method).getContainingClass();
return psiClass != null && classNames.contains(psiClass.getName());
}
return false;
});
}
}

View File

@@ -0,0 +1,27 @@
package ru.adelf.idea.dotenv.models;
import org.jetbrains.annotations.NotNull;
/**
* Environment key and value
*/
public class EnvironmentKeyValue {
private final String key;
private final String value;
public EnvironmentKeyValue(@NotNull String key, @NotNull String value) {
this.key = key;
this.value = value;
}
@NotNull
public String getKey() {
return key;
}
@NotNull
public String getValue() {
return value;
}
}

View File

@@ -0,0 +1,25 @@
package ru.adelf.idea.dotenv.models;
import com.intellij.psi.PsiElement;
/**
* Environment key usage PsiElement representation with key
*/
public class KeyUsagePsiElement {
private final String key;
private final PsiElement element;
public KeyUsagePsiElement(String key, PsiElement element) {
this.key = key;
this.element = element;
}
public String getKey() {
return key;
}
public PsiElement getElement() {
return element;
}
}

View File

@@ -0,0 +1,40 @@
package ru.adelf.idea.dotenv.models;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
/**
* Environment key and value PsiElement representation with key and value values.
*/
public class KeyValuePsiElement {
private final String key;
private final String value;
private final PsiElement element;
public KeyValuePsiElement(String key, String value, PsiElement element) {
this.key = key;
this.value = value;
this.element = element;
}
public String getKey() {
return key.trim();
}
public String getShortValue() {
if (value.indexOf('\n') != -1) {
return clearString(value.substring(0, value.indexOf('\n'))) + "...";
}
return value.trim();
}
private String clearString(String s) {
return StringUtil.trim(s.trim(), ch -> ch != '\\').trim();
}
public PsiElement getElement() {
return element;
}
}

View File

@@ -0,0 +1,78 @@
package ru.adelf.idea.dotenv.php;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionProvider;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvSettings;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import ru.adelf.idea.dotenv.common.BaseEnvCompletionProvider;
public class PhpEnvCompletionContributor extends BaseEnvCompletionProvider implements GotoDeclarationHandler {
public PhpEnvCompletionContributor() {
extend(CompletionType.BASIC, PlatformPatterns.psiElement().withParent(StringLiteralExpression.class), new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) {
PsiElement psiElement = completionParameters.getOriginalPosition();
if (psiElement == null || !DotEnvSettings.getInstance().completionEnabled) {
return;
}
if (getStringLiteral(psiElement) == null) {
return;
}
fillCompletionResultSet(completionResultSet, psiElement.getProject());
}
});
}
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) {
if (psiElement == null) {
return PsiElement.EMPTY_ARRAY;
}
StringLiteralExpression stringLiteral = getStringLiteral(psiElement);
if (stringLiteral == null) {
return PsiElement.EMPTY_ARRAY;
}
return EnvironmentVariablesApi.getKeyDeclarations(psiElement.getProject(), stringLiteral.getContents());
}
@Nullable
private StringLiteralExpression getStringLiteral(@NotNull PsiElement psiElement) {
PsiElement parent = psiElement.getParent();
if (!(parent instanceof StringLiteralExpression)) {
return null;
}
if (!PhpPsiHelper.isEnvStringLiteral((StringLiteralExpression) parent)) {
return null;
}
return (StringLiteralExpression) parent;
}
@Nullable
@Override
public String getActionText(@NotNull DataContext dataContext) {
return null;
}
}

View File

@@ -0,0 +1,63 @@
package ru.adelf.idea.dotenv.php;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiRecursiveElementVisitor;
import com.jetbrains.php.lang.psi.elements.ArrayAccessExpression;
import com.jetbrains.php.lang.psi.elements.FunctionReference;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
import java.util.HashSet;
class PhpEnvironmentCallsVisitor extends PsiRecursiveElementVisitor {
final private Collection<KeyUsagePsiElement> collectedItems = new HashSet<>();
@Override
public void visitElement(@NotNull PsiElement element) {
if(element instanceof FunctionReference) {
this.visitFunction((FunctionReference) element);
}
if(element instanceof ArrayAccessExpression) {
this.visitArrayAccess((ArrayAccessExpression) element);
}
super.visitElement(element);
}
private void visitFunction(FunctionReference expression) {
if(!PhpPsiHelper.isEnvFunction(expression)) return;
PsiElement[] parameters = expression.getParameters();
if(parameters.length == 0) return;
if(!(parameters[0] instanceof StringLiteralExpression)) return;
String key = ((StringLiteralExpression)parameters[0]).getContents();
collectedItems.add(new KeyUsagePsiElement(key, parameters[0]));
}
private void visitArrayAccess(ArrayAccessExpression expression) {
if(!PhpPsiHelper.isEnvArrayCall(expression)) return;
if(expression.getIndex() == null) return;
PsiElement indexLiteral = expression.getIndex().getValue();
if(!(indexLiteral instanceof StringLiteralExpression)) return;
String key = ((StringLiteralExpression)indexLiteral).getContents();
collectedItems.add(new KeyUsagePsiElement(key, indexLiteral));
}
@NotNull
Collection<KeyUsagePsiElement> getCollectedItems() {
return collectedItems;
}
}

View File

@@ -0,0 +1,32 @@
package ru.adelf.idea.dotenv.php;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.jetbrains.php.lang.PhpFileType;
import com.jetbrains.php.lang.psi.PhpFile;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesUsagesProvider;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
import java.util.Collections;
public class PhpEnvironmentVariablesUsagesProvider implements EnvironmentVariablesUsagesProvider {
@Override
public boolean acceptFile(VirtualFile file) {
return file.getFileType().equals(PhpFileType.INSTANCE);
}
@NotNull
@Override
public Collection<KeyUsagePsiElement> getUsages(PsiFile psiFile) {
if(psiFile instanceof PhpFile) {
PhpEnvironmentCallsVisitor visitor = new PhpEnvironmentCallsVisitor();
psiFile.acceptChildren(visitor);
return visitor.getCollectedItems();
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,21 @@
package ru.adelf.idea.dotenv.php;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.impl.FileTypeOverrider;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.php.lang.PhpFileType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@SuppressWarnings("UnstableApiUsage")
public class PhpFileTypeOverrider implements FileTypeOverrider {
@Nullable
@Override
public FileType getOverriddenFileType(@NotNull VirtualFile virtualFile) {
if (virtualFile.getName().startsWith(".env") && virtualFile.getName().endsWith(".php")) {
return PhpFileType.INSTANCE;
}
return null;
}
}

View File

@@ -0,0 +1,91 @@
package ru.adelf.idea.dotenv.php;
import com.intellij.psi.PsiElement;
import com.jetbrains.php.lang.psi.elements.*;
import java.util.Arrays;
import java.util.List;
class PhpPsiHelper {
public static final List<String> FUNCTIONS = Arrays.asList("getenv", "env");
public static final List<String> ARRAY_NAMES = Arrays.asList("_ENV", "_SERVER");
/**
* Checks that this element environment string
*
* @param literal Checking psi element
*/
static boolean isEnvStringLiteral(StringLiteralExpression literal) {
PsiElement parent = literal.getParent();
if (parent instanceof ParameterList) {
return isFunctionParameter(literal, 0, FUNCTIONS);
}
if (parent instanceof ArrayIndex) {
PsiElement arrayAccess = parent.getParent();
if (arrayAccess instanceof ArrayAccessExpression) {
return isEnvArrayCall((ArrayAccessExpression) arrayAccess);
}
}
return false;
}
/**
* Checks whether this function reference is reference for env functions, like env or getenv
* @param functionReference Checking reference
* @return true if condition filled
*/
static boolean isEnvFunction(FunctionReference functionReference) {
String name = functionReference.getName();
return (name != null && FUNCTIONS.contains(name));
}
/**
* Checks whether this array access is environment call
* @param arrayAccess Checking array
* @return true if condition filled
*/
static boolean isEnvArrayCall(ArrayAccessExpression arrayAccess) {
PhpPsiElement variable = arrayAccess.getValue();
if (!(variable instanceof Variable)) return false;
return (variable.getName() != null && ARRAY_NAMES.contains(variable.getName()));
}
@SuppressWarnings("SameParameterValue")
private static boolean isFunctionParameter(PsiElement psiElement, int parameterIndex, List<String> functions) {
PsiElement variableContext = psiElement.getContext();
if(!(variableContext instanceof ParameterList)) {
return false;
} else {
ParameterList parameterList = (ParameterList) variableContext;
PsiElement context = parameterList.getContext();
if(!(context instanceof FunctionReference)) {
return false;
} else {
FunctionReference methodReference = (FunctionReference) context;
String name = methodReference.getName();
return (name != null && functions.contains(name) && getParameterIndex(parameterList, psiElement) == parameterIndex);
}
}
}
private static int getParameterIndex(ParameterList parameterList, PsiElement parameter) {
PsiElement[] parameters = parameterList.getParameters();
for(int i = 0; i < parameters.length; i = i + 1) {
if(parameters[i].equals(parameter)) {
return i;
}
}
return -1;
}
}

View File

@@ -0,0 +1,94 @@
package ru.adelf.idea.dotenv.php;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionProvider;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlToken;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvSettings;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import ru.adelf.idea.dotenv.common.BaseEnvCompletionProvider;
import java.util.Arrays;
import java.util.List;
public class PhpunitEnvCompletionContributor extends BaseEnvCompletionProvider implements GotoDeclarationHandler {
public static final List<String> TAGS = Arrays.asList("server", "env");
public PhpunitEnvCompletionContributor() {
extend(CompletionType.BASIC, PlatformPatterns.psiElement(XmlToken.class).withParent(XmlAttributeValue.class), new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) {
PsiElement psiElement = completionParameters.getOriginalPosition();
if (psiElement == null || !DotEnvSettings.getInstance().completionEnabled) {
return;
}
if (getXmlAttributeValue(psiElement) == null) {
return;
}
fillCompletionResultSet(completionResultSet, psiElement.getProject());
}
});
}
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) {
if (psiElement == null) {
return PsiElement.EMPTY_ARRAY;
}
XmlAttributeValue stringLiteral = getXmlAttributeValue(psiElement);
if (stringLiteral == null) {
return PsiElement.EMPTY_ARRAY;
}
return EnvironmentVariablesApi.getKeyDeclarations(psiElement.getProject(), stringLiteral.getValue());
}
@Nullable
private XmlAttributeValue getXmlAttributeValue(@NotNull PsiElement psiElement) {
PsiElement attributeValue = psiElement.getParent();
if (!(attributeValue instanceof XmlAttributeValue)) return null;
PsiElement attribute = attributeValue.getParent();
if (!(attribute instanceof XmlAttribute)) return null;
if (!((XmlAttribute) attribute).getName().equals("name")) return null;
if (!(attribute.getParent() instanceof XmlTag)) return null;
XmlTag tag = (XmlTag) attribute.getParent();
if (!TAGS.contains(tag.getName())) return null;
if (tag.getParentTag() == null || !tag.getParentTag().getName().equals("php")) return null;
return (XmlAttributeValue) attributeValue;
}
@Nullable
@Override
public String getActionText(@NotNull DataContext dataContext) {
return null;
}
}

View File

@@ -0,0 +1,53 @@
package ru.adelf.idea.dotenv.php;
import com.intellij.ide.highlighter.XmlFileType;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlDocument;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesUsagesProvider;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
public class PhpunitEnvironmentVariablesUsagesProvider implements EnvironmentVariablesUsagesProvider {
@Override
public boolean acceptFile(VirtualFile file) {
return file.getFileType().equals(XmlFileType.INSTANCE) && file.getName().equals("phpunit.xml");
}
@NotNull
@Override
public Collection<KeyUsagePsiElement> getUsages(PsiFile psiFile) {
if (!(psiFile instanceof XmlFile)) return Collections.emptyList();
if (!(psiFile.getFirstChild() instanceof XmlDocument)) return Collections.emptyList();
XmlTag rootTag = ((XmlDocument) psiFile.getFirstChild()).getRootTag();
if (rootTag == null) return Collections.emptyList();
XmlTag phpTag = rootTag.findFirstSubTag("php");
if (phpTag == null) return Collections.emptyList();
Collection<KeyUsagePsiElement> collectedItems = new HashSet<>();
for (XmlTag tag : phpTag.getSubTags()) {
if (tag.getName().equals("server") || tag.getName().equals("env")) {
XmlAttribute attribute = tag.getAttribute("name");
if (attribute != null && attribute.getValueElement() != null) {
collectedItems.add(new KeyUsagePsiElement(attribute.getValue(), attribute.getValueElement()));
}
}
}
return collectedItems;
}
}

View File

@@ -0,0 +1,18 @@
package ru.adelf.idea.dotenv.psi;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFileFactory;
import ru.adelf.idea.dotenv.DotEnvFileType;
class DotEnvElementFactory {
static DotEnvProperty createProperty(Project project, String name) {
final DotEnvFile file = createFile(project, name);
return (DotEnvProperty) file.getFirstChild();
}
private static DotEnvFile createFile(Project project, String text) {
String name = "dummy.env";
return (DotEnvFile) PsiFileFactory.getInstance(project).
createFileFromText(name, DotEnvFileType.INSTANCE, text);
}
}

View File

@@ -0,0 +1,11 @@
package ru.adelf.idea.dotenv.psi;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.*;
import ru.adelf.idea.dotenv.DotEnvLanguage;
public class DotEnvElementType extends IElementType {
public DotEnvElementType(@NotNull @NonNls String debugName) {
super(debugName, DotEnvLanguage.INSTANCE);
}
}

View File

@@ -0,0 +1,31 @@
package ru.adelf.idea.dotenv.psi;
import com.intellij.extapi.psi.PsiFileBase;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.psi.FileViewProvider;
import ru.adelf.idea.dotenv.*;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
public class DotEnvFile extends PsiFileBase {
public DotEnvFile(@NotNull FileViewProvider viewProvider) {
super(viewProvider, DotEnvLanguage.INSTANCE);
}
@NotNull
@Override
public FileType getFileType() {
return DotEnvFileType.INSTANCE;
}
@Override
public String toString() {
return ".env file";
}
@Override
public Icon getIcon(int flags) {
return super.getIcon(flags);
}
}

View File

@@ -0,0 +1,7 @@
package ru.adelf.idea.dotenv.psi;
import com.intellij.psi.PsiNameIdentifierOwner;
public interface DotEnvNamedElement extends PsiNameIdentifierOwner
{
}

View File

@@ -0,0 +1,11 @@
package ru.adelf.idea.dotenv.psi;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
public abstract class DotEnvNamedElementImpl extends ASTWrapperPsiElement implements DotEnvNamedElement {
public DotEnvNamedElementImpl(@NotNull ASTNode node) {
super(node);
}
}

View File

@@ -0,0 +1,45 @@
package ru.adelf.idea.dotenv.psi;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.stream.Collectors;
public class DotEnvPsiUtil {
public static String getKeyText(DotEnvProperty element) {
// IMPORTANT: Convert embedded escaped spaces to simple spaces
return element.getKey().getText().replaceAll("\\\\ ", " ");
}
public static String getValueText(DotEnvProperty element) {
ASTNode valueNode = element.getNode().findChildByType(DotEnvTypes.VALUE);
if (valueNode != null) {
return Arrays.stream(valueNode.getChildren(TokenSet.create(DotEnvTypes.VALUE_CHARS)))
.map(ASTNode::getText)
.collect(Collectors.joining(""));
} else {
return "";
}
}
public static String getName(DotEnvProperty element) {
return getKeyText(element);
}
public static PsiElement setName(DotEnvProperty element, @NotNull String newName) {
ASTNode keyNode = element.getNode().findChildByType(DotEnvTypes.KEY);
if (keyNode != null) {
DotEnvProperty property = DotEnvElementFactory.createProperty(element.getProject(), newName);
ASTNode newKeyNode = property.getFirstChild().getNode();
element.getNode().replaceChild(keyNode, newKeyNode);
}
return element;
}
public static PsiElement getNameIdentifier(DotEnvProperty element) {
return element.getKey();
}
}

View File

@@ -0,0 +1,17 @@
package ru.adelf.idea.dotenv.psi;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.DotEnvLanguage;
public class DotEnvTokenType extends IElementType {
public DotEnvTokenType(@NotNull @NonNls String debugName) {
super(debugName, DotEnvLanguage.INSTANCE);
}
@Override
public String toString() {
return "DotEnvTokenType." + super.toString();
}
}

View File

@@ -0,0 +1,104 @@
package ru.adelf.idea.dotenv.python;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionProvider;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.adelf.idea.dotenv.DotEnvSettings;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import ru.adelf.idea.dotenv.common.BaseEnvCompletionProvider;
public class PythonEnvCompletionProvider extends BaseEnvCompletionProvider implements GotoDeclarationHandler {
public PythonEnvCompletionProvider() {
extend(CompletionType.BASIC, PlatformPatterns.psiElement(), new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) {
PsiElement psiElement = completionParameters.getOriginalPosition();
if (psiElement == null || !DotEnvSettings.getInstance().completionEnabled) {
return;
}
if (getStringLiteral(psiElement) == null) {
return;
}
fillCompletionResultSet(completionResultSet, psiElement.getProject());
}
});
}
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) {
if (psiElement == null) {
return PsiElement.EMPTY_ARRAY;
}
PyStringLiteralExpression stringLiteral = getStringLiteral(psiElement);
if (stringLiteral == null) {
return PsiElement.EMPTY_ARRAY;
}
return EnvironmentVariablesApi.getKeyDeclarations(psiElement.getProject(), stringLiteral.getStringValue());
}
@Nullable
private PyStringLiteralExpression getStringLiteral(@NotNull PsiElement psiElement) {
PsiElement parent = psiElement.getParent();
if (!(parent instanceof PyStringLiteralExpression)) {
return null;
}
if (parent.getParent() == null) {
return null;
}
PsiElement candidate = parent.getParent().getParent();
if (candidate instanceof PyCallExpression) {
PyCallExpression callExpression = (PyCallExpression) candidate;
if (PythonPsiHelper.checkGetMethodCall(callExpression)
&& callExpression.getArgumentList() != null
&& callExpression.getArgumentList().getArguments().length > 0
&& callExpression.getArgumentList().getArguments()[0].isEquivalentTo(parent)) {
return (PyStringLiteralExpression) parent;
}
return null;
}
if (candidate instanceof PyAssignmentStatement) {
PyExpression assignedValue = ((PyAssignmentStatement) candidate).getAssignedValue();
if (assignedValue instanceof PySubscriptionExpression) {
if (PythonPsiHelper.checkIndexCall((PySubscriptionExpression) assignedValue)) {
return (PyStringLiteralExpression) parent;
}
return null;
}
}
return null;
}
@Nullable
@Override
public String getActionText(@NotNull DataContext dataContext) {
return null;
}
}

View File

@@ -0,0 +1,55 @@
package ru.adelf.idea.dotenv.python;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiRecursiveElementVisitor;
import com.jetbrains.python.psi.PyCallExpression;
import com.jetbrains.python.psi.PyStringLiteralExpression;
import com.jetbrains.python.psi.PySubscriptionExpression;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
import java.util.HashSet;
class PythonEnvironmentCallsVisitor extends PsiRecursiveElementVisitor {
final private Collection<KeyUsagePsiElement> collectedItems = new HashSet<>();
@Override
public void visitElement(PsiElement element) {
if(element instanceof PyCallExpression) {
this.visitCall((PyCallExpression) element);
}
if(element instanceof PySubscriptionExpression) {
this.visitIndex((PySubscriptionExpression) element);
}
super.visitElement(element);
}
private void visitCall(PyCallExpression expression) {
if(PythonPsiHelper.checkGetMethodCall(expression)
&& expression.getArgumentList() != null
&& expression.getArgumentList().getArguments().length > 0
&& expression.getArgumentList().getArguments()[0] instanceof PyStringLiteralExpression) {
PyStringLiteralExpression stringLiteral = (PyStringLiteralExpression) expression.getArgumentList().getArguments()[0];
collectedItems.add(new KeyUsagePsiElement(stringLiteral.getStringValue(), stringLiteral));
}
}
private void visitIndex(PySubscriptionExpression expression) {
if(PythonPsiHelper.checkIndexCall(expression) &&
expression.getIndexExpression() instanceof PyStringLiteralExpression) {
PyStringLiteralExpression stringLiteral = (PyStringLiteralExpression) expression.getIndexExpression();
collectedItems.add(new KeyUsagePsiElement(stringLiteral.getStringValue(), stringLiteral));
}
}
@NotNull
Collection<KeyUsagePsiElement> getCollectedItems() {
return collectedItems;
}
}

View File

@@ -0,0 +1,32 @@
package ru.adelf.idea.dotenv.python;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.psi.PyFile;
import org.jetbrains.annotations.NotNull;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesUsagesProvider;
import ru.adelf.idea.dotenv.models.KeyUsagePsiElement;
import java.util.Collection;
import java.util.Collections;
public class PythonEnvironmentVariablesUsagesProvider implements EnvironmentVariablesUsagesProvider {
@Override
public boolean acceptFile(VirtualFile file) {
return file.getFileType().equals(PythonFileType.INSTANCE);
}
@NotNull
@Override
public Collection<KeyUsagePsiElement> getUsages(PsiFile psiFile) {
if(psiFile instanceof PyFile) {
PythonEnvironmentCallsVisitor visitor = new PythonEnvironmentCallsVisitor();
psiFile.acceptChildren(visitor);
return visitor.getCollectedItems();
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,51 @@
package ru.adelf.idea.dotenv.python;
import com.intellij.psi.util.QualifiedName;
import com.jetbrains.python.psi.PyCallExpression;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyReferenceExpression;
import com.jetbrains.python.psi.PySubscriptionExpression;
class PythonPsiHelper {
/**
* Checks os.environ.get("") call
* @param callExpression checking element
* @return true if
*/
static boolean checkGetMethodCall(PyCallExpression callExpression) {
PyExpression callee = callExpression.getCallee();
if(!(callee instanceof PyReferenceExpression)) {
return false;
}
QualifiedName qualifiedName = ((PyReferenceExpression) (callee)).asQualifiedName();
if(qualifiedName == null) {
return false;
}
String name = qualifiedName.toString();
return name != null && (name.equals("os.environ.get") || name.equals("os.getenv"));
}
/**
* Checks os.environ[""] call
* @param subscription checking element
* @return true if
*/
static boolean checkIndexCall(PySubscriptionExpression subscription) {
QualifiedName qualifiedName = subscription.asQualifiedName();
if(qualifiedName == null) {
return false;
}
String name = qualifiedName.toString();
return name != null && name.equals("os.environ.__getitem__");
}
}

View File

@@ -0,0 +1,102 @@
package ru.adelf.idea.dotenv.ruby;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionProvider;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.ruby.ruby.lang.psi.basicTypes.stringLiterals.RStringLiteral;
import org.jetbrains.plugins.ruby.ruby.lang.psi.expressions.RArrayIndexing;
import org.jetbrains.plugins.ruby.ruby.lang.psi.variables.RConstant;
import ru.adelf.idea.dotenv.DotEnvSettings;
import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi;
import ru.adelf.idea.dotenv.common.BaseEnvCompletionProvider;
import java.util.Objects;
public class RubyEnvCompletionProvider extends BaseEnvCompletionProvider implements GotoDeclarationHandler {
public RubyEnvCompletionProvider() {
extend(CompletionType.BASIC, PlatformPatterns.psiElement(), new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) {
PsiElement psiElement = completionParameters.getOriginalPosition();
if (psiElement == null || !DotEnvSettings.getInstance().completionEnabled) {
return;
}
if (getStringLiteral(psiElement) == null) {
return;
}
fillCompletionResultSet(completionResultSet, psiElement.getProject());
}
});
}
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) {
if (psiElement == null) {
return PsiElement.EMPTY_ARRAY;
}
RStringLiteral stringLiteral = getStringLiteral(psiElement);
if (stringLiteral == null) {
return PsiElement.EMPTY_ARRAY;
}
return EnvironmentVariablesApi.getKeyDeclarations(psiElement.getProject(), stringLiteral.getContent());
}
@Nullable
private RStringLiteral getStringLiteral(@NotNull PsiElement psiElement) {
PsiElement parent = psiElement.getParent();
if (!(parent instanceof RStringLiteral)) {
return null;
}
if (parent.getParent() == null) {
return null;
}
PsiElement array = parent.getParent().getParent();
if (!(array instanceof RArrayIndexing)) {
return null;
}
PsiElement receiver = ((RArrayIndexing) array).getReceiver();
if (!(receiver instanceof RConstant)) {
return null;
}
if (receiver.getFirstChild() == null) {
return null;
}
if (!Objects.equals(receiver.getFirstChild().getText(), "ENV")) {
return null;
}
return (RStringLiteral) parent;
}
@Nullable
@Override
public String getActionText(@NotNull DataContext dataContext) {
return null;
}
}

Some files were not shown because too many files have changed in this diff Show More