mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
Merge IJI-2443
GitOrigin-RevId: a68106cf773ceab89cc73135f697e172f65e6c45
This commit is contained in:
11
plugins/env-files-support/.gitignore
vendored
Normal file
11
plugins/env-files-support/.gitignore
vendored
Normal 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
|
||||
21
plugins/env-files-support/LICENSE
Normal file
21
plugins/env-files-support/LICENSE
Normal 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.
|
||||
70
plugins/env-files-support/build.gradle.kts
Normal file
70
plugins/env-files-support/build.gradle.kts
Normal 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
|
||||
}
|
||||
17
plugins/env-files-support/dotenv.iml
Normal file
17
plugins/env-files-support/dotenv.iml
Normal 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>
|
||||
BIN
plugins/env-files-support/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
plugins/env-files-support/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
plugins/env-files-support/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
plugins/env-files-support/gradle/wrapper/gradle-wrapper.properties
vendored
Normal 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
185
plugins/env-files-support/gradlew
vendored
Normal 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
89
plugins/env-files-support/gradlew.bat
vendored
Normal 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
|
||||
43
plugins/env-files-support/readme.md
Normal file
43
plugins/env-files-support/readme.md
Normal file
@@ -0,0 +1,43 @@
|
||||
IntelliJ IDEA .env files support plugin
|
||||
-------------
|
||||
|
||||
[](https://plugins.jetbrains.com/plugin/9525)
|
||||
[](https://plugins.jetbrains.com/plugin/9525)
|
||||
[](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
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
<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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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]}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}*/
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.adelf.idea.dotenv.psi;
|
||||
|
||||
import com.intellij.psi.PsiNameIdentifierOwner;
|
||||
|
||||
public interface DotEnvNamedElement extends PsiNameIdentifierOwner
|
||||
{
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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__");
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user