diff --git a/.idea/libraries/Groovy.xml b/.idea/libraries/Groovy.xml index e396e496812e..3df0b5fbd6bc 100644 --- a/.idea/libraries/Groovy.xml +++ b/.idea/libraries/Groovy.xml @@ -1,46 +1,11 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/bin/idea.properties b/bin/idea.properties index a9cd05389fd5..9e8524cd8523 100644 --- a/bin/idea.properties +++ b/bin/idea.properties @@ -1,4 +1,4 @@ -# Use ${idea.home} macro to specify location relative to IDE installation home. +# Use ${idea.home.path} macro to specify location relative to IDE installation home. # Use ${xxx} where xxx is any Java property (including defined in previous lines of this file) to refer to its value. # Note for Windows users: please make sure you're using forward slashes (e.g. c:/idea/system). diff --git a/build/conf/classVersions.txt b/build/conf/classVersions.txt index 7ca31d1d871c..377431bd105c 100644 --- a/build/conf/classVersions.txt +++ b/build/conf/classVersions.txt @@ -36,6 +36,9 @@ 1.5 => plugins/Groovy/lib/groovy_rt.jar 1.5 => lib/annotations.jar +1.7 => lib/groovy-all-2.3.7.jar!org/codehaus/groovy/runtime/WritablePath.class +1.7 => lib/groovy-all-2.3.7.jar!org/codehaus/groovy/runtime/NioGroovyMethods.class + 1.6 => plugins/gradle/lib/gradle-jps-plugin.jar 1.3 => plugins/junit/lib/junit-rt.jar \ No newline at end of file diff --git a/build/conf/ideaCE64-launcher.properties b/build/conf/ideaCE64-launcher.properties index 2277afab3863..0567cc288251 100644 --- a/build/conf/ideaCE64-launcher.properties +++ b/build/conf/ideaCE64-launcher.properties @@ -4,5 +4,5 @@ IDS_APP_TITLE=__IDE-NAME__ Launcher IDS_VM_OPTIONS_PATH=%USERPROFILE%\\.__PRODUCT_PATHS_SELECTOR__ IDC_WINLAUNCHER=__IDE-NAME___LAUNCHER IDS_PROPS_ENV_VAR=__IDE-NAME___PROPERTIES -IDS_VM_OPTIONS_ENV_VAR=__IDE-NAME___VM_OPTIONS +IDS_VM_OPTIONS_ENV_VAR=__IDE-NAME__64_VM_OPTIONS IDS_ERROR_LAUNCHING_APP=Error launching __IDE-NAME__ diff --git a/build/lib/gant/README_Install.txt b/build/lib/gant/README_Install.txt index 179af7832043..5f6cc4ae2aa6 100644 --- a/build/lib/gant/README_Install.txt +++ b/build/lib/gant/README_Install.txt @@ -1,27 +1,30 @@ Gant -- A Groovy way of scripting Ant tasks. + This is Gant, a Groovy way of working with Ant tasks -- no more XML :-) The method of installation depends on whether you have downloaded a tarball or -zipfile distribution, or you have a Bazaar branch -- or a Subversion checkout, -or even a Git clone -- of the source. +zipfile distribution, or you have a Git clone -- or even a Bazaar branch -- of +the source. + Distribution ------------ The Gant distributions contain a ready-made install directory hierarchy. Untar the tarball or unzip the zipfile to the location where you want the Gant -installation to reside. A directory with the name structured +installation to reside. A directory with the name structured gant- will be created in the location specified for the untar or unzip. There are a number of distinct distributions: - 1. Requires a separate Groovy installation. There are builds: - a. compiled against Groovy 1.6.7; and - b. compiled against Groovy 1.7-rc-2. + 1. Requires a separate Groovy installation. There are builds: + a. compiled against Groovy 1.7.10; and + b. compiled against Groovy 1.8.6; and + c. compiled against Groovy 2.0.0-beta-2 - 2. Self-contained, includes all dependent jars. + 2. Self-contained, includes all dependent jars. You might like to set up an environment variable GANT_HOME set to the directory created by the untar or unzip, though this is not essential, it is @@ -30,33 +33,31 @@ just an efficiency. The script $GANT_HOME/bin/gant for systems with a Posix shell, or $GANT_HOME/bin/gant.bat on Windows is the mechanism for launching a Gant run. -Distributions 1a and 1b only include the direct Gant materials. The Maven +Distributions 1a, 1b and 1c only include the direct Gant materials. The Maven target set depends on use of the Maven Ant tasks, and the Ivy tool depends on the Ivy jar, these will have to be downloaded and installed into $GANT_HOME/lib unless they are already available on on your CLASSPATH. -Using a Bazaar Branch ---------------------- -You first need to get a source tree. Bazaar is the version control system -used for developing Gant. For reasons that are unlikely to become apparent -quickly, the master branch is actually held in the Subversion repository at -Codehaus. However the effective mainline is a branch on Launchpad. So to get -a branch: +Using a Git Clone +----------------- - bzr branch lp:gant Gant_Trunk +Gant's mainline is a Git repository on GitHub, see -or + https://github.com/Gant/Gant - bzr branch http://svn.codehaus.org/gant/gant/trunk Gant_Trunk +you should fork this on GitHub and then clone to give you a local repository. -(If you are going to actively develop Gant, you almost certainly want to have -a shared repository in which this mirror branch is kept so that you can then -make feature branches from it.) +The repository on Codehaus at: + + git://git.codehaus.org/gant.git + +is an administrative clone of the GitHub mainline and should not be used in +normal circumstances. Gradle is used as the build system for Gant, so you will need to set the gant_installPath property in ~/.gradle/gradle.properties so you can install -Gant. So for example: +Gant. So for example: gant_installPath = ${System.properties.'user.home'}/lib/JavaPackages/gant-trunk @@ -64,22 +65,45 @@ Then you type: ./gradlew :gant:install -and all the necessary magic happens. The first time you use the Gradle +and all the necessary magic happens. The first time you use the Gradle Wrapper, it will connect to the Internet to download the various jars that -comprise Gradle. This takes a while. However this is only needed the first +comprise Gradle. This takes a while. However this is only needed the first time, thereafter it uses the version you downloaded. - You probably want to set the GROOVY_HOME environment variable to point at the Groovy installation that the Gant installation is to work with. + +Using a Bazaar Branch +--------------------- + +For anyone prefering to use Bazaar rather than Git, there is an automated +bridge of the master branch of the Git clone on Launchpad. + +To get a branch: + + bzr branch lp:gant Gant + +or if you want to use bzr-git directly: + + bzr branch git://github.com/Gant/Gant.git Gant + +(If you are going to actively develop Gant, you almost certainly want to have +a shared repository in which this mirror branch is kept so that you can then +make feature branches from it.) + +All the information in the previous section about Gradle and building Gant +apply when using Bazaar. + + Contact ------- If you have any problems using Gant, or have any ideas for improvements, please make use of the Gant users mailing list: user@gant.codehaus.org -Russel Winder +Russel Winder + ;;; Local Variables: *** ;;; fill-column: 78 *** diff --git a/build/lib/gant/bin/gant b/build/lib/gant/bin/gant index 3389ea635a30..327e4542ac51 100755 --- a/build/lib/gant/bin/gant +++ b/build/lib/gant/bin/gant @@ -2,7 +2,7 @@ # Gant -- A Groovy way of scripting Ant tasks. # -# Copyright © 2006-9 Russel Winder +# Copyright © 2006–2010, 2013 Russel Winder # # 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 @@ -14,12 +14,9 @@ # implied. See the License for the specific language governing permissions and limitations under the # License. # -# Author : Russel Winder +# Author : Russel Winder -# Gant initiation script for Linux and UNIX -- stand alone version. - -# This version does not require a GROOVY_HOME to be set since it has all the necessary jars in the -# $GANT_HOME/lib directory. +# Gant initiation script for Linux and UNIX -- requires Groovy version. # Solaris 10 does not have readlink as standard -- though some people will have the version from # Sunfreeware which is the same as the version on Ubuntu, Cygwin, MSYS, etc. Mac OS X has a version of @@ -27,9 +24,9 @@ # # This function attempts to just do the right thing. -doReadLink ( ) { +doReadLink() { case `uname` in - Darwin ) + Darwin) gantLocation=`dirname $1` gantPath=`readlink "$1"` if [ "$gantPath" = "" ] @@ -46,7 +43,7 @@ doReadLink ( ) { cd $currentDirectory fi ;; - SunOS ) + SunOS) readlinkPath=`which readlink` if [ `expr "$readlinkPath" : '\([^ ]*\)'` = "no" ] then @@ -56,7 +53,7 @@ doReadLink ( ) { readlinkValue=`readlink -f $1` fi ;; - * ) + *) readlinkValue=`readlink -f $1` ;; esac @@ -77,9 +74,20 @@ then GANT_HOME=`dirname $GANT_HOME` fi -# Force GROOVY_HOME to be GANT_HOME so that the startGroovy code does the right thing. +# If GROOVY_HOME is not set, deduce a path -- this is needed in order to discover the location of the +# startGroovy script. Assume the executable is in $GROOVY_HOME/bin. -GROOVY_HOME="$GANT_HOME" +if [ -z "$GROOVY_HOME" ] +then + GROOVY_HOME=`which groovy` + if [ -h $GROOVY_HOME ] + then + doReadLink $GROOVY_HOME GROOVY_HOME + GROOVY_HOME=$readlinkValue + fi + GROOVY_HOME=`dirname "$GROOVY_HOME"` + GROOVY_HOME=`dirname "$GROOVY_HOME"` +fi # If ANT_HOME is not set, deduce a path -- this is needed in order to discover the location of the jars # associated with the Ant installation. Assume the executable is in $ANT_HOME/bin. diff --git a/build/lib/gant/bin/gant.bat b/build/lib/gant/bin/gant.bat index e77c247d8734..ccd28602158e 100755 --- a/build/lib/gant/bin/gant.bat +++ b/build/lib/gant/bin/gant.bat @@ -14,7 +14,7 @@ @rem implied. See the License for the specific language governing permissions and limitations under the @rem License. @rem -@rem Author : Russel Winder +@rem Author : Russel Winder @rem Gant initiation script for Windows. @@ -27,12 +27,20 @@ if "%DIRNAME%" == "" set DIRNAME=.\ @rem If GANT_HOME is not set, deduce a path. if not "%GANT_HOME%" == "" goto endSetGantHome - set GANT_HOME=%DIRNAME%.. + set GANT_HOME="%DIRNAME%.." :endSetGantHome -@rem Force GROOVY_HOME to be GANT_HOME so that the startGroovy code does the right thing. +@rem If GROOVY_HOME is not set, deduce a path -- this is needed in order to discover the location of the +@rem startGroovy script. -set GROOVY_HOME=%GANT_HOME% +if not "%GROOVY_HOME%" == "" goto endSetGroovyHome + for %%P in ( %PATH% ) do if exist %%P\groovy.exe set GROOVY_HOME=%%P\.. + if not "%GROOVY_HOME%" == "" goto endSetGroovyHome + for %%P in ( %PATH% ) do if exist %%P\groovy.bat set GROOVY_HOME=%%P\.. + if not "%GROOVY_HOME%" == "" goto endSetGroovyHome + call :environmentVariableError GROOVY_HOME + goto :EOF +:endSetGroovyHome @rem If ANT_HOME is not set, deduce a path -- this is needed in order to discover the location of the jars @rem asscoiated with the Ant installation. @@ -46,15 +54,15 @@ if not "%ANT_HOME%" == "" goto endSetAntHome set PROGNAME=gant.bat set GROOVY_SCRIPT_NAME=gant.bat -set STARTER_CONF=%GANT_HOME%\conf\gant-starter.conf +set STARTER_CONF="%GANT_HOME%\conf\gant-starter.conf" set JAVA_OPTS=%JAVA_OPTS% -Dgant.home="%GANT_HOME%" -Dant.home="%ANT_HOME%" -%GANT_HOME%\bin\startGroovy.bat %DIRNAME% gant.Gant lib %* +"%GANT_HOME%\bin\startGroovy.bat" "%DIRNAME%" gant.Gant %* @rem End local scope for the variables with windows NT shell if "%OS%" == "Windows_NT" endlocal -exit /B %ERRORLEVEL% +%COMSPEC% /C exit /B %ERRORLEVEL% :environmentVariableError echo. diff --git a/build/lib/gant/bin/startGroovy b/build/lib/gant/bin/startGroovy deleted file mode 100755 index 9e9ccf35b11b..000000000000 --- a/build/lib/gant/bin/startGroovy +++ /dev/null @@ -1,272 +0,0 @@ -# -*- mode:sh -*- - -############################################################################## -## ## -## Groovy JVM Bootstrap for UN*X ## -## ## -############################################################################## - -## -## $Revision$ -## $Date$ -## - -PROGNAME=`basename "$0"` - -#DIRNAME=`dirname "$0"` - -SCRIPT_PATH="$0" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "${PROGNAME}: $*" -} - -die ( ) { - warn "$*" - exit 1 -} - -earlyInit ( ) { - return -} - -lateInit ( ) { - return -} - -GROOVY_STARTUP="$HOME/.groovy/startup" -if [ -r "$GROOVY_STARTUP" ] ; then - . "$GROOVY_STARTUP" -fi - -earlyInit - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -if [ "$1" = "-cp" -o "$1" = "-classpath" ] ; then - CP=$2 - shift 2 -fi - -# Attempt to set JAVA_HOME if it's not already set. -if [ -z "$JAVA_HOME" ] ; then - if $darwin ; then - [ -z "$JAVA_HOME" -a -d "/Library/Java/Home" ] && export JAVA_HOME="/Library/Java/Home" - [ -z "$JAVA_HOME" -a -d "/System/Library/Frameworks/JavaVM.framework/Home" ] && export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home" - else - javaExecutable="`which javac`" - [ -z "$javaExecutable" -o "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ] && die "JAVA_HOME not set and cannot find javac to deduce location, please set JAVA_HOME." - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - [ `expr "$readLink" : '\([^ ]*\)'` = "no" ] && die "JAVA_HOME not set and readlink not available, please set JAVA_HOME." - javaExecutable="`readlink -f \"$javaExecutable\"`" - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - - fi -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$GROOVY_HOME" ] && GROOVY_HOME=`cygpath --unix "$GROOVY_HOME"` - [ -n "$JAVACMD" ] && JAVACMD=`cygpath --unix "$JAVACMD"` - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CP" ] && CP=`cygpath --path --unix "$CP"` -else - if [ -n "$GROOVY_HOME" -a `expr "$GROOVY_HOME":'\/$'` ] ; then - GROOVY_HOME=`echo $GROOVY_HOME | sed -e 's/\/$//'` - fi -fi - -# For MSYS, ensure paths are in appropriate format. -if $msys -then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`( cd "$JAVA_HOME" ; pwd )` -fi - -# Attempt to set GROOVY_HOME if it is not already set. -if [ -z "$GROOVY_HOME" -o ! -d "$GROOVY_HOME" ] ; then - # Resolve links: $0 may be a link to groovy's home. - 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\"`/.." - GROOVY_HOME="`pwd -P`" - cd "$SAVED" -fi - -# Set the default Groovy config if no specific one is mentioned. -if [ -z "$GROOVY_CONF" ] ; then - GROOVY_CONF="$GROOVY_HOME/conf/groovy-starter.conf" -fi -STARTER_CLASSPATH="$GROOVY_HOME/lib/groovy-all-2.2.1.jar" - -# Create the final classpath. Setting a classpath using the -cp or -classpath option means not to use the -# global classpath. Groovy behaves then the same as the java interpreter -if [ -n "$CP" ] ; then - CP="$CP":. -elif [ -n "$CLASSPATH" ] ; then - CP="$CLASSPATH":. -else - CP=. -fi - -# Determine the Java command to use to start the JVM. -if [ -z "$JAVACMD" ] ; then - 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 - else - JAVACMD="java" - fi -fi -if [ ! -x "$JAVACMD" ] ; then - die "JAVA_HOME is not defined correctly, can not execute: $JAVACMD" -fi -if [ -z "$JAVA_HOME" ] ; then - warn "JAVA_HOME environment variable is not set" -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "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 businessSystem maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# Setup Profiler -useprofiler=false -if [ "$PROFILER" != "" ] ; then - if [ -r "$PROFILER" ] ; then - . $PROFILER - useprofiler=true - else - die "Profiler file not found: $PROFILER" - fi -fi - -# For Darwin, use classes.jar for TOOLS_JAR -TOOLS_JAR="$JAVA_HOME/lib/tools.jar" -#if $darwin; then -# TOOLS_JAR="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Classes/classes.jar" -#fi - -# For Darwin, add GROOVY_APP_NAME to the JAVA_OPTS as -Xdock:name -if $darwin; then - JAVA_OPTS="$JAVA_OPTS -Xdock:name=$GROOVY_APP_NAME" -# we may also want to set -Xdock:image -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - GROOVY_HOME=`cygpath --mixed "$GROOVY_HOME"` - JAVA_HOME=`cygpath --mixed "$JAVA_HOME"` - GROOVY_CONF=`cygpath --mixed "$GROOVY_CONF"` - CP=`cygpath --path --mixed "$CP"` - TOOLS_JAR=`cygpath --mixed "$TOOLS_JAR"` - STARTER_CLASSPATH=`cygpath --path --mixed "$STARTER_CLASSPATH"` - - # 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 [ "$GROOVY_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GROOVY_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" -` - if [ $CHECK -ne 0 ] ; then - 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 - -startGroovy ( ) { - CLASS=$1 - shift - # Start the Profiler or the JVM - if $useprofiler ; then - runProfiler - else - exec "$JAVACMD" $JAVA_OPTS \ - -classpath "$STARTER_CLASSPATH" \ - -Dscript.name="$SCRIPT_PATH" \ - -Dprogram.name="$PROGNAME" \ - -Dgroovy.starter.conf="$GROOVY_CONF" \ - -Dgroovy.home="$GROOVY_HOME" \ - -Dtools.jar="$TOOLS_JAR" \ - $STARTER_MAIN_CLASS \ - --main $CLASS \ - --conf "$GROOVY_CONF" \ - --classpath "$CP" \ - "$@" - fi -} - -STARTER_MAIN_CLASS=org.codehaus.groovy.tools.GroovyStarter - -lateInit diff --git a/build/lib/gant/bin/startGroovy.bat b/build/lib/gant/bin/startGroovy.bat index d2b1436b0bed..5f4540b22c7e 100755 --- a/build/lib/gant/bin/startGroovy.bat +++ b/build/lib/gant/bin/startGroovy.bat @@ -10,7 +10,7 @@ @rem @rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +if "%OS%"=="Windows_NT" setlocal enabledelayedexpansion set DIRNAME=%~1 shift @@ -18,9 +18,6 @@ shift set CLASS=%~1 shift -set JARSPATH=%~1 -shift - if exist "%USERPROFILE%/.groovy/preinit.bat" call "%USERPROFILE%/.groovy/preinit.bat" @rem Determine the command interpreter to execute the "CD" later @@ -36,7 +33,24 @@ if exist "%SystemRoot%\command\find.exe" set FIND_EXE="%SystemRoot%\command\find :check_JAVA_HOME @rem Make sure we have a valid JAVA_HOME if not "%JAVA_HOME%" == "" goto have_JAVA_HOME -for %%P in (%PATH%) do if exist %%P\..\bin\java.exe set JAVA_HOME=%%P\.. +set PATHTMP=%PATH% +:loop +for /f "delims=; tokens=1*" %%i in ("!PATHTMP!") do ( + if exist "%%i\..\bin\java.exe" ( + set "JAVA_HOME=%%i\.." + goto found_JAVA_HOME + ) + set PATHTMP=%%j + goto loop +) +goto check_default_JAVA_EXE + +:found_JAVA_HOME +@rem Remove trailing \bin\.. from JAVA_HOME +if "%JAVA_HOME:~-7%"=="\bin\.." SET "JAVA_HOME=%JAVA_HOME:~0,-7%" +set JAVA_EXE=%JAVA_HOME%\bin\java.exe + +:check_default_JAVA_EXE if not "%JAVA_HOME%" == "" goto valid_JAVA_HOME java -version 2>NUL if not ERRORLEVEL 1 goto default_JAVA_EXE @@ -170,8 +184,8 @@ if "x1" == "x%_SKIP%" goto skip_1 rem now unescape -q, -s, -d set _ARG=%_ARG:-s=*% -set _ARG=%_ARG:-d=-% set _ARG=%_ARG:-q="% +set _ARG=%_ARG:-d=-% set CMD_LINE_ARGS=%CMD_LINE_ARGS% %_ARG% set _ARG= @@ -203,7 +217,7 @@ set CMD_LINE_ARGS=%$ :execute @rem Setup the command line -set STARTER_CLASSPATH=%GROOVY_HOME%\%JARSPATH%\groovy-all-2.2.1.jar;%STARTER_CLASSPATH% +set STARTER_CLASSPATH=%GROOVY_HOME%\lib\groovy-all-2.3.0.jar if exist "%USERPROFILE%/.groovy/init.bat" call "%USERPROFILE%/.groovy/init.bat" @@ -221,19 +235,19 @@ set CP=%CLASSPATH%;%CP% :after_cp set STARTER_MAIN_CLASS=org.codehaus.groovy.tools.GroovyStarter -if "%STARTER_CONF%" == "" set STARTER_CONF=%GROOVY_HOME%\conf\groovy-starter.conf +set STARTER_CONF=%GROOVY_HOME%\conf\groovy-starter.conf -if "%JAVA_OPTS%" == "" set JAVA_OPTS="-Xmx128m" -set JAVA_OPTS=%JAVA_OPTS% -Dprogram.name="%PROGNAME%" -set JAVA_OPTS=%JAVA_OPTS% -Dgroovy.home="%GROOVY_HOME%" -if not "%TOOLS_JAR%" == "" set JAVA_OPTS=%JAVA_OPTS% -Dtools.jar="%TOOLS_JAR%" -set JAVA_OPTS=%JAVA_OPTS% -Dgroovy.starter.conf="%STARTER_CONF%" -set JAVA_OPTS=%JAVA_OPTS% -Dscript.name="%GROOVY_SCRIPT_NAME%" +set GROOVY_OPTS="-Xmx128m" +set GROOVY_OPTS=%GROOVY_OPTS% -Dprogram.name="%PROGNAME%" +set GROOVY_OPTS=%GROOVY_OPTS% -Dgroovy.home="%GROOVY_HOME%" +if not "%TOOLS_JAR%" == "" set GROOVY_OPTS=%GROOVY_OPTS% -Dtools.jar="%TOOLS_JAR%" +set GROOVY_OPTS=%GROOVY_OPTS% -Dgroovy.starter.conf="%STARTER_CONF%" +set GROOVY_OPTS=%GROOVY_OPTS% -Dscript.name="%GROOVY_SCRIPT_NAME%" if exist "%USERPROFILE%/.groovy/postinit.bat" call "%USERPROFILE%/.groovy/postinit.bat" @rem Execute Groovy -"%JAVA_EXE%" %JAVA_OPTS% -classpath "%STARTER_CLASSPATH%" %STARTER_MAIN_CLASS% --main %CLASS% --conf "%STARTER_CONF%" --classpath "%CP%" %CMD_LINE_ARGS% +"%JAVA_EXE%" %GROOVY_OPTS% %JAVA_OPTS% -classpath "%STARTER_CLASSPATH%" %STARTER_MAIN_CLASS% --main %CLASS% --conf "%STARTER_CONF%" --classpath "%CP%" %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell @@ -241,5 +255,4 @@ if "%OS%"=="Windows_NT" endlocal @rem Optional pause the batch file if "%GROOVY_BATCH_PAUSE%" == "on" pause - -exit /B %ERRORLEVEL% +%COMSPEC% /C exit /B %ERRORLEVEL% diff --git a/build/lib/gant/conf/gant-starter.conf b/build/lib/gant/conf/gant-starter.conf index 154d5fe1fb94..75aef0043c9a 100644 --- a/build/lib/gant/conf/gant-starter.conf +++ b/build/lib/gant/conf/gant-starter.conf @@ -1,6 +1,6 @@ # Gant -- A Groovy way of scripting Ant tasks. # -# Copyright © 2008 Russel Winder +# Copyright © 2008, 2010, 2013 Russel Winder # # 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 @@ -12,17 +12,17 @@ # implied. See the License for the specific language governing permissions and limitations under the # License. # -# Author : Russel Winder +# Author : Russel Winder # Load user specific libraries that are Gant specific. load !{user.home}/.gant/lib/*.jar - + # Load user specific libraries that are Ant specific. load !{user.home}/.ant/lib/*.jar - + # Load user specific libraries that are for Groovy. load !{user.home}/.groovy/lib/*.jar - + # Load required libraries load !{gant.home}/lib/*.jar diff --git a/build/lib/gant/gant_groovy2.1-1.9.10-sources.jar b/build/lib/gant/gant_groovy2.1-1.9.10-sources.jar deleted file mode 100644 index 776f88897d0b..000000000000 Binary files a/build/lib/gant/gant_groovy2.1-1.9.10-sources.jar and /dev/null differ diff --git a/build/lib/gant/lib/gant_groovy2.1-1.9.10.jar b/build/lib/gant/lib/gant-1.9.11_groovy-2.3.0.jar similarity index 56% rename from build/lib/gant/lib/gant_groovy2.1-1.9.10.jar rename to build/lib/gant/lib/gant-1.9.11_groovy-2.3.0.jar index 8f8fc1e009f2..f3d98426914a 100644 Binary files a/build/lib/gant/lib/gant_groovy2.1-1.9.10.jar and b/build/lib/gant/lib/gant-1.9.11_groovy-2.3.0.jar differ diff --git a/build/scripts/dist.gant b/build/scripts/dist.gant index 59e7c9a0beb0..d61a0b8d2899 100644 --- a/build/scripts/dist.gant +++ b/build/scripts/dist.gant @@ -85,6 +85,8 @@ target('default': 'The default target') { system_selector: "IdeaIC${p("component.version.major")}", tools_jar: true], home, null, paths, true) + String macZip = "$paths.artifacts/ideaIC-${snapshot}.mac.zip" + notifyArtifactBuilt(macZip) } target('build-dist-jars' : 'Target to build jars from locally compiled classes') { @@ -99,6 +101,8 @@ target('build-dist-jars' : 'Target to build jars from locally compiled classes') system_selector: "IdeaIC${p("component.version.major")}", tools_jar: true], home, null, paths) + String macZip = "$paths.artifacts/ideaIC-${snapshot}.mac.zip" + notifyArtifactBuilt(macZip) } private void indexSearchableOptions() { @@ -152,7 +156,6 @@ def layoutAll(Map args, String home, String out, Paths _paths = null, buildJps = String macZip = "$paths.artifacts/idea${args.buildNumber}.mac.zip" buildMacZip(macAppRoot, macZip, [paths.distAll], paths.distMac) - notifyArtifactBuilt(macZip) buildTarGz("idea-${args.buildNumber}", "$paths.artifacts/idea${args.buildNumber}.tar", [paths.distAll, paths.distUnix]) } diff --git a/build/scripts/libLicenses.gant b/build/scripts/libLicenses.gant index 4437ffa0a753..99d0257e0906 100644 --- a/build/scripts/libLicenses.gant +++ b/build/scripts/libLicenses.gant @@ -133,7 +133,7 @@ binding.setVariable("generateLicensesTable", {String filePath, Set usedM // projectBuilder.info(" $lib.name") //} - lines.sort(String.CASE_INSENSITIVE_ORDER) + lines.sort(true, String.CASE_INSENSITIVE_ORDER) File file = new File(filePath) file.parentFile.mkdirs() FileWriter out = new FileWriter(file) @@ -175,13 +175,13 @@ libraryLicense(name: "Apache Lucene", libraryName: "lucene-core-2.4.1.jar", vers libraryLicense(name: "Apache Sanselan", libraryName: "Sanselan", version: "0.98", license: "Apache 2.0", url: "http://commons.apache.org/sanselan/") libraryLicense(name: "Automaton", libraryName: "automaton.jar", version: "1.11", license: "BSD", url: "http://www.brics.dk/automaton/", licenseUrl: "http://www.opensource.org/licenses/bsd-license.php") libraryLicense(name: "DTDParser", version: "1.13", license: "LGPL", url: "http://sourceforge.net/projects/dtdparser/", licenseUrl: "http://www.opensource.org/licenses/lgpl-2.1") -libraryLicense(name: "Gant", version: "1.9.8", libraryName: "gant_groovy2.0-1.9.8.jar", license: "Apache 2.0", url: "http://gant.codehaus.org/", licenseUrl: "http://gant.codehaus.org/Licence") +libraryLicense(name: "Gant", version: "1.9.8", libraryName: "gant-1.9.11_groovy-2.3.0.jar", license: "Apache 2.0", url: "http://gant.codehaus.org/", licenseUrl: "http://gant.codehaus.org/Licence") libraryLicense(name: "sqljet", version: "bundled with SVNKit", libraryName: "sqljet.jar", license: "link (commercial license)", url: "http://sqljet.com", licenseUrl: "http://svnkit.com/license.html") libraryLicense(name: "svnkit-javahl", version: "bundled with SVNKit", libraryName: "svnkit-javahl.jar", license: "link (commercial license)", url: "http://www.svnkit.com/", licenseUrl: "http://svnkit.com/license.html") libraryLicense(name: "Antlr", libraryName: "antlr.jar", version: "3.4", license: "BSD", url: "http://www.antlr.org", licenseUrl: "http://www.antlr.org/license.html") libraryLicense(name: "Guava", version: "17.0", license: "Apache 2.0", url: "http://code.google.com/p/guava-libraries/", licenseUrl: "http://ant.apache.org/license.html") libraryLicense(name: "Guava", libraryName: "guava-tools", version: "14.0.1", license: "Apache 2.0", url: "http://code.google.com/p/guava-libraries/", licenseUrl: "http://ant.apache.org/license.html") -libraryLicense(name: "Groovy", version: "2.2.1", license: "Apache 2.0", url: "http://groovy.codehaus.org/") +libraryLicense(name: "Groovy", version: "2.3.7", license: "Apache 2.0", url: "http://groovy.codehaus.org/") libraryLicense(name: "Gson", version: "2.2.4", libraryName: "gson", license: "Apache 2.0", url: "http://code.google.com/p/google-gson/") libraryLicense(name: "ini4j", libraryName: "ini4j-0.5.2-patched", version: "0.5.2 (with a patch by JetBrains)", license: "Apache 2.0", url: "http://ini4j.sourceforge.net/", attachedTo: "git4idea") libraryLicense(name: "ISO RELAX", libraryName: "isorelax.jar", license: "MIT License", url: "http://sourceforge.net/projects/iso-relax/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html") @@ -278,7 +278,6 @@ libraryLicense(name: "json-smart", libraryName: "json-smart-1.1.1.jar", version: libraryLicense(name: "fxg-utils", libraryName: "fxg-utils", version: "4.9.1", license: "Apache 2.0", url: "http://flex.apache.org", licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0") libraryLicense(name: "jayatana", libraryName: "jayatana", version: "1.2.4", license: "MIT License", url: "https://code.google.com/p/java-swing-ayatana/", licenseUrl: "http://opensource.org/licenses/mit-license.php") libraryLicense(name: "ForkJoin", version: "", license: "Creative Commons Public Domain Dedication", url: "http://gee.cs.oswego.edu/dl/concurrency-interest/index.html", licenseUrl: "http://creativecommons.org/publicdomain/zero/1.0/") -libraryLicense(name: "ANTLR 4 Runtime", libraryName: "antlr-runtime-4.1.jar", version: "4.1", license: "BSD", url: "http://www.antlr.org", licenseUrl: "http://www.antlr.org/license.html") libraryLicense(name: "minlog", libraryName: "minlog-1.2.jar", version: "1.2", license: "BSD", url: "https://github.com/EsotericSoftware/minlog", licenseUrl: "http://opensource.org/licenses/BSD-3-Clause") libraryLicense(name: "ReflectASM", libraryName: "reflectasm-1.0.7.jar", version: "1.0.7", license: "BSD", url: "https://github.com/EsotericSoftware/reflectasm", licenseUrl: "http://opensource.org/licenses/BSD-3-Clause") libraryLicense(name: "Objenesis", libraryName: "objenesis-1.2.jar", version: "1.2", license: "Apache 2.0", url: "http://objenesis.org/", licenseUrl: "http://apache.org/licenses/LICENSE-2.0") @@ -290,4 +289,4 @@ jetbrainsLibrary("CoverageReport") jetbrainsLibrary("JPS") jetbrainsLibrary("Maven Embedder") jetbrainsLibrary("tcServiceMessages") -jetbrainsLibrary("optimizedFileManager.jar") \ No newline at end of file +jetbrainsLibrary("optimizedFileManager.jar") diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java index 93673cceb2ff..8f16bc164433 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,7 +53,7 @@ public class AdjustArrayRangeAction extends DebuggerAction { return; } - XValue container = node.getValueContainer(); + final XValue container = node.getValueContainer(); if (!(container instanceof JavaValue)) { return; } @@ -79,17 +79,14 @@ public class AdjustArrayRangeAction extends DebuggerAction { public void contextAction() throws Exception { final Renderer lastRenderer = descriptor.getLastRenderer(); if (lastRenderer instanceof ArrayRenderer) { - descriptor.setRenderer(clonedRenderer); - refreshViews(node); - //selectedNode.setRenderer(clonedRenderer); + ((JavaValue)container).setRenderer(clonedRenderer, node); } else if (lastRenderer instanceof CompoundNodeRenderer) { final CompoundNodeRenderer compoundRenderer = (CompoundNodeRenderer)lastRenderer; final ChildrenRenderer childrenRenderer = compoundRenderer.getChildrenRenderer(); if (childrenRenderer instanceof ExpressionChildrenRenderer) { ExpressionChildrenRenderer.setPreferableChildrenRenderer(descriptor, clonedRenderer); - refreshViews(node); - //selectedNode.calcRepresentation(); + ((JavaValue)container).reBuild(node); } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JVMNameUtil.java b/java/debugger/impl/src/com/intellij/debugger/engine/JVMNameUtil.java index 16f0647ecec1..04694c844592 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JVMNameUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JVMNameUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -249,13 +249,13 @@ public class JVMNameUtil { } public static JVMName getJVMQualifiedName(PsiClass psiClass) { - if (!PsiUtil.isLocalOrAnonymousClass(psiClass)) { - final String name = getNonAnonymousClassName(psiClass); - if (name != null) { - return getJVMRawText(name); - } + final String name = getNonAnonymousClassName(psiClass); + if (name != null) { + return getJVMRawText(name); + } + else { + return new JVMClassAt(SourcePosition.createFromElement(psiClass)); } - return new JVMClassAt(SourcePosition.createFromElement(psiClass)); } @Nullable @@ -264,17 +264,18 @@ public class JVMNameUtil { if (psiClass == null) { return null; } - if (!PsiUtil.isLocalOrAnonymousClass(psiClass)) { - final String name = getNonAnonymousClassName(psiClass); - if (name != null) { - return getJVMRawText(name); - } + final String name = getNonAnonymousClassName(psiClass); + if (name != null) { + return getJVMRawText(name); } return new JVMClassAt(pos); } @Nullable public static String getNonAnonymousClassName(PsiClass aClass) { + if (PsiUtil.isLocalOrAnonymousClass(aClass)) { + return null; + } String name = aClass.getName(); if (name == null) { return null; diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java index 0d33050e1f38..b58e35e2ab78 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java @@ -540,6 +540,11 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV public void setRenderer(NodeRenderer nodeRenderer, final XValueNodeImpl node) { DebuggerManagerThreadImpl.assertIsManagerThread(); myValueDescriptor.setRenderer(nodeRenderer); + reBuild(node); + } + + public void reBuild(final XValueNodeImpl node) { + DebuggerManagerThreadImpl.assertIsManagerThread(); myCurrentChildrenStart = 0; node.getTree().getLaterInvocator().offer(new Runnable() { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java index 1c3fef202b77..e6122f0a38b0 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/PositionManagerImpl.java @@ -84,7 +84,8 @@ public class PositionManagerImpl implements PositionManager { return; } - if (!classHasName(psiClass)) { + String className = JVMNameUtil.getNonAnonymousClassName(psiClass); + if (className == null) { final PsiClass parent = JVMNameUtil.getTopLevelParentClass(psiClass); if (parent == null) { @@ -107,7 +108,7 @@ public class PositionManagerImpl implements PositionManager { }); } else { - waitPrepareFor.set(JVMNameUtil.getNonAnonymousClassName(psiClass)); + waitPrepareFor.set(className); waitRequestor.set(requestor); } } @@ -118,10 +119,6 @@ public class PositionManagerImpl implements PositionManager { return myDebugProcess.getRequestsManager().createClassPrepareRequest(waitRequestor.get(), waitPrepareFor.get()); } - private static boolean classHasName(PsiClass psiClass) { - return !PsiUtil.isLocalOrAnonymousClass(psiClass) && JVMNameUtil.getNonAnonymousClassName(psiClass) != null; - } - public SourcePosition getSourcePosition(final Location location) throws NoDataException { DebuggerManagerThreadImpl.assertIsManagerThread(); if(location == null) { @@ -244,7 +241,8 @@ public class PositionManagerImpl implements PositionManager { final PsiClass psiClass = JVMNameUtil.getClassAt(position); if (psiClass != null) { classAtPositionRef.set(psiClass); - if (!classHasName(psiClass)) { + String className = JVMNameUtil.getNonAnonymousClassName(psiClass); + if (className == null) { isLocalOrAnonymous.set(Boolean.TRUE); final PsiClass topLevelClass = JVMNameUtil.getTopLevelParentClass(psiClass); if (topLevelClass != null) { @@ -259,10 +257,7 @@ public class PositionManagerImpl implements PositionManager { } } else { - final String className = JVMNameUtil.getNonAnonymousClassName(psiClass); - if (className != null) { - baseClassNameRef.set(className); - } + baseClassNameRef.set(className); } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java index 89cd2bd89ef5..c7e682f200a5 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java @@ -671,7 +671,7 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils { @NotNull @Override public Navigatable createNavigatable(@NotNull Project project) { - return XSourcePositionImpl.createOpenFileDescriptor(project, this); + return XSourcePositionImpl.doCreateOpenFileDescriptor(project, this); } } diff --git a/java/idea-ui/idea-ui.iml b/java/idea-ui/idea-ui.iml index 4a549c503788..2a6932c91115 100644 --- a/java/idea-ui/idea-ui.iml +++ b/java/idea-ui/idea-ui.iml @@ -13,6 +13,6 @@ + - - + \ No newline at end of file diff --git a/java/idea-ui/src/com/intellij/openapi/vcs/checkout/NewProjectFromVCSGroup.java b/java/idea-ui/src/com/intellij/openapi/vcs/checkout/NewProjectFromVCSGroup.java new file mode 100644 index 000000000000..2c8d75704317 --- /dev/null +++ b/java/idea-ui/src/com/intellij/openapi/vcs/checkout/NewProjectFromVCSGroup.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.intellij.openapi.vcs.checkout; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.CheckoutProvider; +import com.intellij.openapi.vcs.VcsKey; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.platform.PlatformProjectOpenProcessor; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +public class NewProjectFromVCSGroup extends CheckoutActionGroup { + + @NotNull + @Override + protected AnAction createAction(CheckoutProvider provider) { + return new CheckoutAction(provider) { + @Override + protected CheckoutProvider.Listener getListener(Project project) { + return new CheckoutProvider.Listener() { + @Override + public void directoryCheckedOut(File directory, VcsKey vcs) { + final VirtualFile dir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(directory); + if (dir != null) { + PlatformProjectOpenProcessor.getInstance().doOpenProject(dir, null, false); + } + } + + @Override + public void checkoutCompleted() { + + } + }; + } + }; + } +} diff --git a/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectCheckoutListener.java b/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectCheckoutListener.java index f7bc32e0be00..2621e2461659 100644 --- a/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectCheckoutListener.java +++ b/java/idea-ui/src/com/intellij/openapi/vcs/checkout/ProjectCheckoutListener.java @@ -32,6 +32,10 @@ import java.io.FilenameFilter; * @author yole */ public class ProjectCheckoutListener implements CheckoutListener { + + public ProjectCheckoutListener() { + } + @Override public boolean processCheckedOutDirectory(Project project, File directory) { File[] files = directory.listFiles((FilenameFilter) new GlobFilenameFilter("*" + ProjectFileType.DOT_DEFAULT_EXTENSION)); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java index 46175c5d55a5..ebd364b26965 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -137,11 +137,13 @@ public class AnnotationsHighlightUtil { formatReference(nameRef), JavaHighlightUtil.formatType(expectedType)); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(value).descriptionAndTooltip(description).create(); } + if (value instanceof PsiArrayInitializerMemberValue) { if (expectedType instanceof PsiArrayType) return null; String description = JavaErrorMessages.message("annotation.illegal.array.initializer", JavaHighlightUtil.formatType(expectedType)); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(value).descriptionAndTooltip(description).create(); } + if (value instanceof PsiExpression) { PsiExpression expr = (PsiExpression)value; PsiType type = expr.getType(); @@ -160,8 +162,7 @@ public class AnnotationsHighlightUtil { String description = JavaErrorMessages.message("annotation.incompatible.types", JavaHighlightUtil.formatType(type), JavaHighlightUtil.formatType(expectedType)); - final HighlightInfo info = - HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(value).descriptionAndTooltip(description).create(); + HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(value).descriptionAndTooltip(description).create(); QuickFixAction.registerQuickFixAction(info, QuickFixFactory.getInstance().createSurroundWithQuotesAnnotationParameterValueFix(value, expectedType)); return info; } @@ -185,8 +186,7 @@ public class AnnotationsHighlightUtil { String containedElementFQN = contained == null ? null : contained.getQualifiedName(); if (containedElementFQN != null) { - PsiClass container = annotationType; - String containerName = container.getQualifiedName(); + String containerName = annotationType.getQualifiedName(); if (isAnnotationRepeatedTwice(owner, containedElementFQN)) { String description = JavaErrorMessages.message("annotation.container.wrong.place", containerName); return annotationError(annotationToCheck, description); @@ -197,8 +197,8 @@ public class AnnotationsHighlightUtil { String description = JavaErrorMessages.message("annotation.duplicate.annotation"); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create(); } - PsiAnnotation metaAnno = PsiImplUtil.findAnnotation(annotationType.getModifierList(), CommonClassNames.JAVA_LANG_ANNOTATION_REPEATABLE); + PsiAnnotation metaAnno = PsiImplUtil.findAnnotation(annotationType.getModifierList(), CommonClassNames.JAVA_LANG_ANNOTATION_REPEATABLE); if (metaAnno == null) { String explanation = JavaErrorMessages.message("annotation.non.repeatable", annotationType.getQualifiedName()); String description = JavaErrorMessages.message("annotation.duplicate.explained", explanation); @@ -223,15 +223,6 @@ public class AnnotationsHighlightUtil { } } - for (PsiAnnotation annotation : owner.getAnnotations()) { - if (annotation == annotationToCheck) continue; - PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement(); - if (nameRef == null) continue; - PsiElement aClass = nameRef.resolve(); - if (!resolved.equals(aClass)) continue; - - } - return null; } @@ -252,15 +243,14 @@ public class AnnotationsHighlightUtil { return contained; } - private static boolean isAnnotationRepeatedTwice(@NotNull PsiAnnotationOwner owner, @NotNull String qualifiedName) { + private static boolean isAnnotationRepeatedTwice(@NotNull PsiAnnotationOwner owner, @Nullable String qualifiedName) { int count = 0; for (PsiAnnotation annotation : owner.getAnnotations()) { PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement(); if (nameRef == null) continue; PsiElement resolved = nameRef.resolve(); - if (!(resolved instanceof PsiClass) || !qualifiedName.equals(((PsiClass)resolved).getQualifiedName())) continue; - count++; - if (count == 2) return true; + if (!(resolved instanceof PsiClass) || !Comparing.equal(qualifiedName, ((PsiClass)resolved).getQualifiedName())) continue; + if (++count == 2) return true; } return false; } @@ -721,7 +711,7 @@ public class AnnotationsHighlightUtil { if (field instanceof PsiEnumConstant) { String name = ((PsiEnumConstant)field).getName(); try { - return RetentionPolicy.valueOf(RetentionPolicy.class, name); + return Enum.valueOf(RetentionPolicy.class, name); } catch (Exception e) { LOG.warn("Unknown policy: " + name); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java index 6832f9d226db..de9876b0e81d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java @@ -32,6 +32,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; +import com.intellij.psi.impl.PsiClassImplUtil; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.search.searches.ReferencesSearch; @@ -346,17 +347,21 @@ public class GenericsHighlightUtil { Map inheritedClasses = new HashMap(); final TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass); return checkInterfaceMultipleInheritance(aClass, + aClass, PsiSubstitutor.EMPTY, inheritedClasses, new HashSet(), textRange); } private static HighlightInfo checkInterfaceMultipleInheritance(PsiClass aClass, + PsiElement place, PsiSubstitutor derivedSubstitutor, Map inheritedClasses, Set visited, TextRange textRange) { final PsiClassType[] superTypes = aClass.getSuperTypes(); for (PsiClassType superType : superTypes) { + superType = PsiClassImplUtil.correctType(superType, place.getResolveScope()); + if (superType == null) continue; final PsiClassType.ClassResolveResult result = superType.resolveGenerics(); final PsiClass superClass = result.getElement(); if (superClass == null || visited.contains(superClass)) continue; @@ -381,7 +386,7 @@ public class GenericsHighlightUtil { } inheritedClasses.put(superClass, superTypeSubstitutor); visited.add(superClass); - final HighlightInfo highlightInfo = checkInterfaceMultipleInheritance(superClass, superTypeSubstitutor, inheritedClasses, visited, textRange); + final HighlightInfo highlightInfo = checkInterfaceMultipleInheritance(superClass, place, superTypeSubstitutor, inheritedClasses, visited, textRange); visited.remove(superClass); if (highlightInfo != null) return highlightInfo; @@ -1185,6 +1190,23 @@ public class GenericsHighlightUtil { return null; } + public static HighlightInfo checkEnumWithoutConstantsCantHaveAbstractMethods(final PsiClass aClass) { + if (!aClass.isEnum()) return null; + for (PsiField field : aClass.getFields()) { + if (field instanceof PsiEnumConstant) { + return null; + } + } + for (PsiMethod method : aClass.getMethods()) { + if (method.hasModifierProperty(PsiModifier.ABSTRACT)) { + final String description = "Enum declaration without enum constants cannot have abstract methods"; + final TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass); + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create(); + } + } + return null; + } + public static HighlightInfo checkSelectStaticClassFromParameterizedType(final PsiElement resolved, final PsiJavaCodeReferenceElement ref) { if (resolved instanceof PsiClass && ((PsiClass)resolved).hasModifierProperty(PsiModifier.STATIC)) { final PsiElement qualifier = ref.getQualifier(); @@ -1329,5 +1351,60 @@ public class GenericsHighlightUtil { } return null; } + + public static HighlightInfo areSupersAccessible(@NotNull PsiClass aClass) { + return areSupersAccessible(aClass, aClass.getResolveScope(), HighlightNamesUtil.getClassDeclarationTextRange(aClass)); + } + + public static HighlightInfo areSupersAccessible(@NotNull PsiClass aClass, PsiElement ref) { + return areSupersAccessible(aClass, ref.getResolveScope(), ref.getTextRange()); + } + + private static HighlightInfo areSupersAccessible(@NotNull PsiClass aClass, + GlobalSearchScope resolveScope, + TextRange range) { + final JavaPsiFacade factory = JavaPsiFacade.getInstance(aClass.getProject()); + for (PsiClassType superType : aClass.getSuperTypes()) { + final String notAccessibleErrorMessage = isSuperTypeAccessible(superType, new HashSet(), resolveScope, factory); + if (notAccessibleErrorMessage != null) { + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .descriptionAndTooltip(notAccessibleErrorMessage) + .range(range) + .create(); + } + } + return null; + } + + @Nullable + private static String isSuperTypeAccessible(PsiType superType, + HashSet classes, + GlobalSearchScope resolveScope, + JavaPsiFacade factory) { + final PsiClass aClass = PsiUtil.resolveClassInType(superType); + if (aClass != null && classes.add(aClass)) { + final String qualifiedName = aClass.getQualifiedName(); + if (qualifiedName != null && factory.findClass(qualifiedName, resolveScope) == null) { + return "Cannot access " + HighlightUtil.formatClass(aClass); + } + + if (superType instanceof PsiClassType) { + for (PsiType psiType : ((PsiClassType)superType).getParameters()) { + final String notAccessibleMessage = isSuperTypeAccessible(psiType, classes, resolveScope, factory); + if (notAccessibleMessage != null) { + return notAccessibleMessage; + } + } + } + + for (PsiClassType type : aClass.getSuperTypes()) { + final String notAccessibleMessage = isSuperTypeAccessible(type, classes, resolveScope, factory); + if (notAccessibleMessage != null) { + return notAccessibleMessage; + } + } + } + return null; + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index 83ec3affc36b..8d913644794f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -34,6 +34,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; +import com.intellij.psi.impl.PsiSuperMethodImplUtil; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.util.*; @@ -1147,25 +1148,22 @@ public class HighlightMethodUtil { PsiClass aClass = method.getContainingClass(); if (aClass == null) return null; - PsiClass superClass = aClass.getSuperClass(); - PsiMethod superMethod = superClass == null - ? null - : MethodSignatureUtil.findMethodBySignature(superClass, method, true); - - boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC); - HighlightInfo highlightInfo = checkStaticMethodOverride(aClass, method, isStatic,superClass, superMethod,containingFile); - if (highlightInfo != null) return highlightInfo; - if (!isStatic) { - // all methods in interface are instance, so no possible errors in this case + final HierarchicalMethodSignature methodSignature = PsiSuperMethodImplUtil.getHierarchicalMethodSignature(method); + final List superSignatures = methodSignature.getSuperSignatures(); + if (superSignatures.isEmpty()) { return null; } - PsiClass[] interfaces = aClass.getInterfaces(); - for (PsiClass aInterfaces : interfaces) { - superClass = aInterfaces; - superMethod = MethodSignatureUtil.findMethodInSuperClassBySignatureInDerived(aClass, superClass, method.getSignature(PsiSubstitutor.EMPTY), true); - highlightInfo = checkStaticMethodOverride(aClass, method, true, superClass, superMethod,containingFile); - if (highlightInfo != null) return highlightInfo; + + boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC); + for (HierarchicalMethodSignature signature : superSignatures) { + final PsiMethod superMethod = signature.getMethod(); + final PsiClass superClass = superMethod.getContainingClass(); + if (superClass == null) continue; + final HighlightInfo highlightInfo = checkStaticMethodOverride(aClass, method, isStatic, superClass, superMethod,containingFile); + if (highlightInfo != null) { + return highlightInfo; } + } return null; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 7c9c1d9e9f63..2791ed73bbda 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -381,8 +381,10 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh super.visitClass(aClass); if (aClass instanceof PsiSyntheticClass) return; if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkInterfaceMultipleInheritance(aClass)); + if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.areSupersAccessible(aClass)); if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkDuplicateTopLevelClass(aClass)); if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkEnumMustNotBeLocal(aClass)); + if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkEnumWithoutConstantsCantHaveAbstractMethods(aClass)); if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkImplicitThisReferenceBeforeSuper(aClass, myJavaSdkVersion)); if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkClassAndPackageConflict(aClass)); if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkPublicClassInRightFile(aClass)); @@ -1220,7 +1222,14 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh } if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkAccessStaticFieldFromEnumConstructor(expression, result)); if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkClassReferenceAfterQualifier(expression, resolved)); - myHolder.add(HighlightUtil.checkUnqualifiedSuperInDefaultMethod(myLanguageLevel, expression, expression.getQualifierExpression())); + final PsiExpression qualifierExpression = expression.getQualifierExpression(); + myHolder.add(HighlightUtil.checkUnqualifiedSuperInDefaultMethod(myLanguageLevel, expression, qualifierExpression)); + if (!myHolder.hasErrorResults() && qualifierExpression != null) { + final PsiClass psiClass = PsiUtil.resolveClassInType(qualifierExpression.getType()); + if (psiClass != null) { + myHolder.add(GenericsHighlightUtil.areSupersAccessible(psiClass, qualifierExpression)); + } + } } @Override diff --git a/java/java-impl/src/com/intellij/application/options/CodeStyleGenerationConfigurable.java b/java/java-impl/src/com/intellij/application/options/CodeStyleGenerationConfigurable.java index 91b40d12a837..5f0ee4f20e85 100644 --- a/java/java-impl/src/com/intellij/application/options/CodeStyleGenerationConfigurable.java +++ b/java/java-impl/src/com/intellij/application/options/CodeStyleGenerationConfigurable.java @@ -25,6 +25,7 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.ui.JavaVisibilityPanel; import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.ToolbarDecorator; @@ -67,7 +68,7 @@ public class CodeStyleGenerationConfigurable implements Configurable { mySettings = settings; myMembersOrderList = new MembersOrderList(); myPanel.setBorder(IdeBorderFactory.createEmptyBorder(2, 2, 2, 2)); - myJavaVisibilityPanel = new JavaVisibilityPanel(false, true); + myJavaVisibilityPanel = new JavaVisibilityPanel(false, true, RefactoringBundle.message("default.visibility.border.title")); } public JComponent createComponent() { diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java index d68e683de6ce..44d50a239da8 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java @@ -128,6 +128,11 @@ public class JavaVariableInplaceIntroducer extends AbstractJavaInplaceIntroducer return "IntroduceVariable"; } + @Override + protected String getRefactoringId() { + return "refactoring.extractVariable"; + } + @Override protected void restoreState(@NotNull PsiVariable psiField) { if (myDeleteSelf) return; @@ -159,6 +164,15 @@ public class JavaVariableInplaceIntroducer extends AbstractJavaInplaceIntroducer } } + @Override + protected PsiExpression getBeforeExpr() { + final PsiVariable variable = getVariable(); + if (variable != null) { + return variable.getInitializer(); + } + return super.getBeforeExpr(); + } + @Override protected void performIntroduce() { final PsiVariable psiVariable = getVariable(); diff --git a/java/java-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteJavaCallerChooser.java b/java/java-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteJavaCallerChooser.java index 7a3ec7efc825..85bbd0fe4436 100644 --- a/java/java-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteJavaCallerChooser.java +++ b/java/java-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteJavaCallerChooser.java @@ -105,7 +105,7 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser { /** * @return parameter if it is used inside method only as argument in nodeMethod call at parameterIndex */ - static PsiParameter isTheOnlyOneParameterUsage(PsiElement call, int parameterIndex, final PsiMethod nodeMethod) { + static PsiParameter isTheOnlyOneParameterUsage(PsiElement call, final int parameterIndex, final PsiMethod nodeMethod) { if (call instanceof PsiCallExpression) { final PsiExpressionList argumentList = ((PsiCallExpression)call).getArgumentList(); if (argumentList != null) { @@ -129,6 +129,7 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser { if (parameter != null && !parameter.isVarArgs()) { final PsiElement scope = parameter.getDeclarationScope(); if (scope instanceof PsiMethod && ((PsiMethod)scope).findDeepestSuperMethods().length == 0) { + final int scopeParamIdx = ((PsiMethod)scope).getParameterList().getParameterIndex(parameter); final Ref ref = new Ref(false); if (ReferencesSearch.search(parameter, new LocalSearchScope(scope)).forEach(new Processor() { @Override @@ -139,11 +140,11 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser { while (parent != null) { final PsiMethod resolved = parent.resolveMethod(); if (scope.equals(resolved)) { - if (usedInQualifier(element, parent)) return false; + if (usedInQualifier(element, parent, scopeParamIdx)) return false; return true; } if (nodeMethod.equals(resolved)) { - if (usedInQualifier(element, parent)) return false; + if (usedInQualifier(element, parent, parameterIndex)) return false; ref.set(true); return true; } @@ -154,7 +155,7 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser { return true; } - private boolean usedInQualifier(PsiElement element, PsiCallExpression parent) { + private boolean usedInQualifier(PsiElement element, PsiCallExpression parent, int parameterIndex) { PsiExpression qualifier = null; if (parent instanceof PsiMethodCallExpression) { qualifier = ((PsiMethodCallExpression)parent).getMethodExpression(); @@ -162,7 +163,13 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser { else if (parent instanceof PsiNewExpression) { qualifier = ((PsiNewExpression)parent).getQualifier(); } - return PsiTreeUtil.isAncestor(qualifier, element, true); + + if (PsiTreeUtil.isAncestor(qualifier, element, true)) { + return true; + } + + final PsiExpressionList list = parent.getArgumentList(); + return list != null && !PsiTreeUtil.isAncestor(list.getExpressions()[parameterIndex], element, false); } }) && ref.get()) { return parameter; diff --git a/java/java-impl/src/com/intellij/refactoring/ui/JavaVisibilityPanel.java b/java/java-impl/src/com/intellij/refactoring/ui/JavaVisibilityPanel.java index 414c44212367..641e6d92d9d9 100644 --- a/java/java-impl/src/com/intellij/refactoring/ui/JavaVisibilityPanel.java +++ b/java/java-impl/src/com/intellij/refactoring/ui/JavaVisibilityPanel.java @@ -46,7 +46,13 @@ public class JavaVisibilityPanel extends VisibilityPanelBase { private final JRadioButton myRbPublic; public JavaVisibilityPanel(boolean hasAsIs, final boolean hasEscalate) { - setBorder(IdeBorderFactory.createTitledBorder(RefactoringBundle.message("visibility.border.title"), true, + this(hasAsIs, hasEscalate, RefactoringBundle.message("visibility.border.title")); + } + + public JavaVisibilityPanel(boolean hasAsIs, + final boolean hasEscalate, + String visibilityTitle) { + setBorder(IdeBorderFactory.createTitledBorder(visibilityTitle, true, new Insets(IdeBorderFactory.TITLED_BORDER_TOP_INSET, UIUtil.DEFAULT_HGAP, IdeBorderFactory.TITLED_BORDER_BOTTOM_INSET, diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index 667e808bfc8d..f2b71bf36955 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -823,8 +823,9 @@ public final class PsiUtil extends PsiUtilCore { @Nullable public static PsiMember findEnclosingConstructorOrInitializer(PsiElement expression) { - PsiMember parent = PsiTreeUtil.getParentOfType(expression, PsiClassInitializer.class, PsiEnumConstantInitializer.class, PsiMethod.class); + PsiMember parent = PsiTreeUtil.getParentOfType(expression, PsiClassInitializer.class, PsiEnumConstantInitializer.class, PsiMethod.class, PsiField.class); if (parent instanceof PsiMethod && !((PsiMethod)parent).isConstructor()) return null; + if (parent instanceof PsiField && parent.hasModifierProperty(PsiModifier.STATIC)) return null; return parent; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java index c410b37dcb91..0e572d7f4f9f 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java @@ -33,10 +33,7 @@ import com.intellij.psi.scope.NameHint; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.scope.processor.FilterScopeProcessor; import com.intellij.psi.scope.processor.MethodResolverProcessor; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.LocalSearchScope; -import com.intellij.psi.search.PackageScope; -import com.intellij.psi.search.SearchScope; +import com.intellij.psi.search.*; import com.intellij.psi.util.*; import com.intellij.ui.IconDeferrer; import com.intellij.ui.RowIcon; @@ -61,7 +58,7 @@ import java.util.*; */ public class PsiClassImplUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiClassImplUtil"); - private static final Key> MAP_IN_CLASS_KEY = Key.create("MAP_KEY"); + private static final Key, PsiClass>> MAP_IN_CLASS_KEY = Key.create("MAP_KEY"); private static final String VALUES_METHOD = "values"; private static final String VALUE_OF_METHOD = "valueOf"; @@ -215,13 +212,13 @@ public class PsiClassImplUtil { public enum MemberType {CLASS, FIELD, METHOD} private static Map>> getMap(@NotNull PsiClass aClass, @NotNull MemberType type) { - ParameterizedCachedValue value = getValues(aClass); - return value.getValue(aClass).get(type); + ParameterizedCachedValue, PsiClass> value = getValues(aClass); + return value.getValue(aClass).get(aClass.getResolveScope()).get(type); } @NotNull - private static ParameterizedCachedValue getValues(@NotNull PsiClass aClass) { - ParameterizedCachedValue value = aClass.getUserData(MAP_IN_CLASS_KEY); + private static ParameterizedCachedValue, PsiClass> getValues(@NotNull PsiClass aClass) { + ParameterizedCachedValue, PsiClass> value = aClass.getUserData(MAP_IN_CLASS_KEY); if (value == null) { value = CachedValuesManager.getManager(aClass.getProject()).createParameterizedCachedValue(ByNameCachedValueProvider.INSTANCE, false); //Do not cache for nonphysical elements @@ -361,9 +358,11 @@ public class PsiClassImplUtil { private static class MembersMap extends ConcurrentFactoryMap>>> { private final PsiClass myPsiClass; + private final GlobalSearchScope myResolveScope; - public MembersMap(PsiClass psiClass) { + public MembersMap(PsiClass psiClass, GlobalSearchScope scope) { myPsiClass = psiClass; + myResolveScope = scope; } @Nullable @@ -397,17 +396,24 @@ public class PsiClassImplUtil { }; processDeclarationsInClassNotCached(myPsiClass, processor, ResolveState.initial(), null, null, myPsiClass, false, - PsiUtil.getLanguageLevel(myPsiClass)); + PsiUtil.getLanguageLevel(myPsiClass), myResolveScope); return map; } } - private static class ByNameCachedValueProvider implements ParameterizedCachedValueProvider { + private static class ByNameCachedValueProvider implements ParameterizedCachedValueProvider, PsiClass> { private static final ByNameCachedValueProvider INSTANCE = new ByNameCachedValueProvider(); @Override - public CachedValueProvider.Result compute(@NotNull PsiClass myClass) { - return new CachedValueProvider.Result(new MembersMap(myClass), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); + public CachedValueProvider.Result> compute(@NotNull final PsiClass myClass) { + final Map map = new ConcurrentFactoryMap() { + @Nullable + @Override + protected MembersMap create(GlobalSearchScope resolveScope) { + return new MembersMap(myClass, resolveScope); + } + }; + return CachedValueProvider.Result.create(map, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); } } @@ -438,6 +444,18 @@ public class PsiClassImplUtil { @NotNull PsiElement place, @NotNull LanguageLevel languageLevel, boolean isRaw) { + return processDeclarationsInClass(aClass, processor, state, visited, last, place, languageLevel, isRaw, place.getResolveScope()); + } + + private static boolean processDeclarationsInClass(@NotNull PsiClass aClass, + @NotNull final PsiScopeProcessor processor, + @NotNull ResolveState state, + @Nullable Set visited, + PsiElement last, + @NotNull PsiElement place, + @NotNull LanguageLevel languageLevel, + boolean isRaw, + @NotNull GlobalSearchScope resolveScope) { if (last instanceof PsiTypeParameterList || last instanceof PsiModifierList) { return true; //TypeParameterList and ModifierList do not see our declarations } @@ -446,16 +464,17 @@ public class PsiClassImplUtil { PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY); isRaw = isRaw || PsiUtil.isRawSubstitutor(aClass, substitutor); - ParameterizedCachedValue cache = getValues(aClass); //aClass.getUserData(MAP_IN_CLASS_KEY); + ParameterizedCachedValue, PsiClass> cache = getValues(aClass); //aClass.getUserData(MAP_IN_CLASS_KEY); boolean upToDate = cache.hasUpToDateValue(); if (/*true || */upToDate) { final NameHint nameHint = processor.getHint(NameHint.KEY); if (nameHint != null) { String name = nameHint.getName(state); - return processCachedMembersByName(aClass, processor, state, visited, last, place, isRaw, substitutor, cache.getValue(aClass), name,languageLevel); + return processCachedMembersByName(aClass, processor, state, visited, last, place, isRaw, substitutor, + cache.getValue(aClass).get(resolveScope), name,languageLevel); } } - return processDeclarationsInClassNotCached(aClass, processor, state, visited, last, place, isRaw, languageLevel); + return processDeclarationsInClassNotCached(aClass, processor, state, visited, last, place, isRaw, languageLevel, resolveScope); } private static boolean processCachedMembersByName(@NotNull PsiClass aClass, @@ -628,7 +647,8 @@ public class PsiClassImplUtil { PsiElement last, @NotNull PsiElement place, boolean isRaw, - @NotNull LanguageLevel languageLevel) { + @NotNull LanguageLevel languageLevel, + @NotNull GlobalSearchScope resolveScope) { if (visited == null) visited = new THashSet(); if (!visited.add(aClass)) return true; processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass); @@ -686,7 +706,50 @@ public class PsiClassImplUtil { } return last instanceof PsiReferenceList || processSuperTypes(aClass, processor, visited, last, place, state, isRaw, factory, - languageLevel); + languageLevel, resolveScope); + } + + @Nullable + public static PsiClassType correctType(PsiClassType originalType, final GlobalSearchScope resolveScope) { + final PsiClassType.ClassResolveResult originalResolveResult = originalType.resolveGenerics(); + PsiClass superClass = originalResolveResult.getElement(); + if (superClass == null) { + return originalType; + } + + String qualifiedName = superClass.getQualifiedName(); + if (qualifiedName != null && !PsiSearchScopeUtil.isInScope(resolveScope, superClass)) { + PsiClass originalSuperClass = superClass; + PsiSubstitutor originalSubstitutor = originalResolveResult.getSubstitutor(); + superClass = JavaPsiFacade.getInstance(superClass.getProject()).findClass(qualifiedName, resolveScope); + if (superClass == null) { + return null; + } + + PsiTypeParameter[] typeParameters = superClass.getTypeParameters(); + PsiTypeParameter[] originalTypeParameters = originalSuperClass.getTypeParameters(); + if (typeParameters.length != originalTypeParameters.length) return null; + + PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; + for (int i = 0; i < originalTypeParameters.length; i++) { + PsiType originalSubstitute = originalSubstitutor.substitute(originalTypeParameters[i]); + if (originalSubstitute != null) { + PsiType substitute = originalSubstitute.accept(new PsiTypeVisitor() { + @Nullable + @Override + public PsiType visitClassType(PsiClassType classType) { + return correctType(classType, resolveScope); + } + }); + if (substitute == null) return null; + + substitutor = substitutor.put(typeParameters[i], substitute); + } + } + return JavaPsiFacade.getElementFactory(superClass.getProject()).createType(superClass, substitutor); + } + + return originalType; } private static boolean processSuperTypes(@NotNull PsiClass aClass, @@ -697,15 +760,19 @@ public class PsiClassImplUtil { @NotNull ResolveState state, boolean isRaw, @NotNull PsiElementFactory factory, - @NotNull LanguageLevel languageLevel) { + @NotNull LanguageLevel languageLevel, GlobalSearchScope resolveScope) { boolean resolved = false; - for (final PsiClassType superType : aClass.getSuperTypes()) { + for (PsiClassType superType : aClass.getSuperTypes()) { + superType = correctType(superType, resolveScope); + if (superType == null) continue; + final PsiClassType.ClassResolveResult superTypeResolveResult = superType.resolveGenerics(); PsiClass superClass = superTypeResolveResult.getElement(); if (superClass == null) continue; PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(superClass, superTypeResolveResult.getSubstitutor(), aClass, state.get(PsiSubstitutor.KEY), factory, languageLevel); - if (!processDeclarationsInClass(superClass, processor, state.put(PsiSubstitutor.KEY, finalSubstitutor), visited, last, place, languageLevel, isRaw)) { + if (!processDeclarationsInClass(superClass, processor, state.put(PsiSubstitutor.KEY, finalSubstitutor), visited, last, place, + languageLevel, isRaw, resolveScope)) { resolved = true; } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiSuperMethodImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiSuperMethodImplUtil.java index e5ae3926e4ca..e853e12ef577 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiSuperMethodImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiSuperMethodImplUtil.java @@ -24,6 +24,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.impl.source.HierarchicalMethodSignatureImpl; import com.intellij.psi.impl.source.PsiClassImpl; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.DeepestSuperMethodsSearch; import com.intellij.psi.search.searches.SuperMethodsSearch; import com.intellij.psi.util.*; @@ -45,7 +46,7 @@ public class PsiSuperMethodImplUtil { @NotNull @Override public Map fun(PsiClass dom) { - return buildMethodHierarchy(dom, null, PsiSubstitutor.EMPTY, true, new THashSet(), false); + return buildMethodHierarchy(dom, null, PsiSubstitutor.EMPTY, true, new THashSet(), false, dom.getResolveScope()); } }); private static final PsiCacheKey>, PsiClass> SIGNATURES_BY_NAME_KEY = PsiCacheKey @@ -56,7 +57,7 @@ public class PsiSuperMethodImplUtil { @Nullable @Override protected Map create(String methodName) { - return buildMethodHierarchy(psiClass, methodName, PsiSubstitutor.EMPTY, true, new THashSet(), false); + return buildMethodHierarchy(psiClass, methodName, PsiSubstitutor.EMPTY, true, new THashSet(), false, psiClass.getResolveScope()); } }; } @@ -131,7 +132,8 @@ public class PsiSuperMethodImplUtil { @NotNull PsiSubstitutor substitutor, final boolean includePrivates, @NotNull final Set visited, - boolean isInRawContext) { + boolean isInRawContext, + GlobalSearchScope resolveScope) { ProgressManager.checkCanceled(); Map result = new LinkedHashMap(); final Map> sameParameterErasureMethods = new THashMap>(MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY); @@ -189,6 +191,8 @@ public class PsiSuperMethodImplUtil { } for (PsiClassType superType : aClass.getSuperTypes()) { + superType = PsiClassImplUtil.correctType(superType, resolveScope); + if (superType == null) continue; //super class doesn't belong to resolve scope PsiClassType.ClassResolveResult superTypeResolveResult = superType.resolveGenerics(); PsiClass superClass = superTypeResolveResult.getElement(); if (superClass == null) continue; @@ -197,7 +201,7 @@ public class PsiSuperMethodImplUtil { PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(superClass, superSubstitutor, substitutor, isInRawContext); final boolean isInRawContextSuper = (isInRawContext || PsiUtil.isRawSubstitutor(superClass, superSubstitutor)) && superClass.getTypeParameters().length != 0; - Map superResult = buildMethodHierarchy(superClass, nameHint, finalSubstitutor, false, visited, isInRawContextSuper); + Map superResult = buildMethodHierarchy(superClass, nameHint, finalSubstitutor, false, visited, isInRawContextSuper, resolveScope); visited.remove(superClass); List> flattened = new ArrayList>(); @@ -303,9 +307,11 @@ public class PsiSuperMethodImplUtil { if (superClass != null) { if (superClass.isInterface() || CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) { - if (superMethod.hasModifierProperty(PsiModifier.STATIC)) { + if (superMethod.hasModifierProperty(PsiModifier.STATIC) || + superMethod.hasModifierProperty(PsiModifier.DEFAULT) && hierarchicalMethodSignature.getMethod().hasModifierProperty(PsiModifier.STATIC)) { return false; } + if (superMethod.hasModifierProperty(PsiModifier.DEFAULT) || hierarchicalMethodSignature.getMethod().hasModifierProperty(PsiModifier.DEFAULT)) { return !InheritanceUtil.isInheritorOrSelf(superClass, containingClass, true); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiAnnotationImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiAnnotationImpl.java index bdc3a2f1003b..0bf175361425 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiAnnotationImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiAnnotationImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -132,7 +132,10 @@ public class PsiAnnotationImpl extends JavaStubPsiElement imp } else if (parent instanceof PsiJavaCodeReferenceElement) { PsiElement ctx = PsiTreeUtil.skipParentsOfType(parent, PsiJavaCodeReferenceElement.class); - if (ctx instanceof PsiReferenceList || ctx instanceof PsiNewExpression || ctx instanceof PsiTypeElement) { + if (ctx instanceof PsiReferenceList || + ctx instanceof PsiNewExpression || + ctx instanceof PsiTypeElement || + ctx instanceof PsiAnonymousClass) { return new PsiClassReferenceType((PsiJavaCodeReferenceElement)parent, null); } } diff --git a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java index 099678b627b2..955dacb29828 100644 --- a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java +++ b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java @@ -534,6 +534,13 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ return Specifics.FIRST; } + if (method1.hasModifierProperty(PsiModifier.DEFAULT) && method2.hasModifierProperty(PsiModifier.STATIC)) { + return Specifics.FIRST; + } + + if (method2.hasModifierProperty(PsiModifier.DEFAULT) && method1.hasModifierProperty(PsiModifier.STATIC)) { + return Specifics.SECOND; + } } if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && myArgumentsList instanceof PsiExpressionList && (typeParameters1.length == 0 || typeParameters2.length == 0)) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/EnumInstanceFieldInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/EnumInstanceFieldInitializer.java new file mode 100644 index 000000000000..f2cee6e11287 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/EnumInstanceFieldInitializer.java @@ -0,0 +1,13 @@ +enum MyEnumTest { + FOO; + MyEnumTest Foo = FOO; + + { + MyEnumTest foo = FOO; + } + static MyEnumTest Bar = FOO; + + MyEnumTest() { + System.out.println(Bar); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/EnumWithoutConstants.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/EnumWithoutConstants.java new file mode 100644 index 000000000000..7278ce8c03ae --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/EnumWithoutConstants.java @@ -0,0 +1,4 @@ +enum MyEnumTest { + ; + public abstract void m(); +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/typeAnnotations.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/typeAnnotations.java index 01f7486ea89a..c1fbcaf07cd6 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/typeAnnotations.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/typeAnnotations.java @@ -59,6 +59,7 @@ class Outer { { new @TA Object(); new @TA ArrayList(); + new @TA Runnable() { public void run() { } }.run(); ArrayList var = new @TA ArrayList(); new @TA Outer().new @TA Middle(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/PreferDefaultMethodsOverStatic.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/PreferDefaultMethodsOverStatic.java new file mode 100644 index 000000000000..5fde9bc9bbf8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/PreferDefaultMethodsOverStatic.java @@ -0,0 +1,18 @@ +class Test { + interface I { + default void f() {} + } + + interface J { + static void f() {} + } + + static class IJ implements I, J {} + static class JI implements J, I {} + + public static void main(String[] args) { + new IJ(). f(); + new JI(). f(); + J.f(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/multipleJdks/java6/p/StaticCallOnChildWithNotAccessibleParent.java b/java/java-tests/testData/codeInsight/multipleJdks/java6/p/StaticCallOnChildWithNotAccessibleParent.java new file mode 100644 index 000000000000..d42bcd1c4e40 --- /dev/null +++ b/java/java-tests/testData/codeInsight/multipleJdks/java6/p/StaticCallOnChildWithNotAccessibleParent.java @@ -0,0 +1,7 @@ +package p; + +class Sample { + public static void main(String[] args) { + ChildClass.foo(); + } +} diff --git a/java/java-tests/testData/codeInsight/multipleJdks/java7/p/DeclaredTypeOfVariableImplementsUnknownType.java b/java/java-tests/testData/codeInsight/multipleJdks/java7/p/DeclaredTypeOfVariableImplementsUnknownType.java new file mode 100644 index 000000000000..1ae112f77d57 --- /dev/null +++ b/java/java-tests/testData/codeInsight/multipleJdks/java7/p/DeclaredTypeOfVariableImplementsUnknownType.java @@ -0,0 +1,10 @@ +package p; +abstract class B { + void f(A a) { + a.foo(); + } + + void f(java.util.List a) { + a.get(0).foo(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/multipleJdks/java7/p/NoOverriding.java b/java/java-tests/testData/codeInsight/multipleJdks/java7/p/NoOverriding.java new file mode 100644 index 000000000000..219a93539fc9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/multipleJdks/java7/p/NoOverriding.java @@ -0,0 +1,14 @@ +package p; + +abstract class B extends A { + public static String getOrDefault(Object key, String defaultValue) { + return null; + } +} + +abstract class C extends A { + @Override + public String getOrDefault(Object key, String defaultValue) { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/multipleJdks/java7/p/StaticCallOnChildWithNotAccessibleParent.java b/java/java-tests/testData/codeInsight/multipleJdks/java7/p/StaticCallOnChildWithNotAccessibleParent.java new file mode 100644 index 000000000000..c09ff65de6e8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/multipleJdks/java7/p/StaticCallOnChildWithNotAccessibleParent.java @@ -0,0 +1,3 @@ +package p; + +public class ChildClass extends BaseClass {} diff --git a/java/java-tests/testData/codeInsight/multipleJdks/java7/p/SuperclassImplementsGenericsOfUnknownType.java b/java/java-tests/testData/codeInsight/multipleJdks/java7/p/SuperclassImplementsGenericsOfUnknownType.java index c435a69e8588..1a2d7a699ee3 100644 --- a/java/java-tests/testData/codeInsight/multipleJdks/java7/p/SuperclassImplementsGenericsOfUnknownType.java +++ b/java/java-tests/testData/codeInsight/multipleJdks/java7/p/SuperclassImplementsGenericsOfUnknownType.java @@ -1,2 +1,3 @@ package p; -abstract class B extends A{} \ No newline at end of file +abstract class B extends A{} +abstract class C extends A1{} diff --git a/java/java-tests/testData/codeInsight/multipleJdks/java8/p/DeclaredTypeOfVariableImplementsUnknownType.java b/java/java-tests/testData/codeInsight/multipleJdks/java8/p/DeclaredTypeOfVariableImplementsUnknownType.java new file mode 100644 index 000000000000..07e72962683a --- /dev/null +++ b/java/java-tests/testData/codeInsight/multipleJdks/java8/p/DeclaredTypeOfVariableImplementsUnknownType.java @@ -0,0 +1,7 @@ +package p; +import java.util.stream.Stream; +import java.util.List; + +public abstract class A implements List> { + public void foo() {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/multipleJdks/java8/p/NoOverriding.java b/java/java-tests/testData/codeInsight/multipleJdks/java8/p/NoOverriding.java new file mode 100644 index 000000000000..477a992f88cc --- /dev/null +++ b/java/java-tests/testData/codeInsight/multipleJdks/java8/p/NoOverriding.java @@ -0,0 +1,5 @@ +package p; +import java.util.Map; + +public abstract class A implements Map {} + diff --git a/java/java-tests/testData/codeInsight/multipleJdks/java8/p/StaticCallOnChildWithNotAccessibleParent.java b/java/java-tests/testData/codeInsight/multipleJdks/java8/p/StaticCallOnChildWithNotAccessibleParent.java new file mode 100644 index 000000000000..9882c3076072 --- /dev/null +++ b/java/java-tests/testData/codeInsight/multipleJdks/java8/p/StaticCallOnChildWithNotAccessibleParent.java @@ -0,0 +1,6 @@ +package p; + +public class BaseClass { + public static void foo() { + } +} diff --git a/java/java-tests/testData/codeInsight/multipleJdks/java8/p/SuperclassImplementsGenericsOfUnknownType.java b/java/java-tests/testData/codeInsight/multipleJdks/java8/p/SuperclassImplementsGenericsOfUnknownType.java index a0859d35946b..15bf832b83e4 100644 --- a/java/java-tests/testData/codeInsight/multipleJdks/java8/p/SuperclassImplementsGenericsOfUnknownType.java +++ b/java/java-tests/testData/codeInsight/multipleJdks/java8/p/SuperclassImplementsGenericsOfUnknownType.java @@ -3,4 +3,5 @@ import java.util.stream.Stream; import java.util.List; public abstract class A implements List> { -} \ No newline at end of file +} +abstract class A1 extends A {} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/safeDelete/NoDeepDeleteParameterUsedInNextArgumentExpression.java b/java/java-tests/testData/refactoring/safeDelete/NoDeepDeleteParameterUsedInNextArgumentExpression.java new file mode 100644 index 000000000000..2d8c46d19ae2 --- /dev/null +++ b/java/java-tests/testData/refactoring/safeDelete/NoDeepDeleteParameterUsedInNextArgumentExpression.java @@ -0,0 +1,7 @@ +class Test { + void foo(String s, String p) {} + + void bar(String s, String p) { + foo(s, s.substring(0)); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/safeDelete/NoDeepDeleteParameterUsedInNextArgumentExpression_after.java b/java/java-tests/testData/refactoring/safeDelete/NoDeepDeleteParameterUsedInNextArgumentExpression_after.java new file mode 100644 index 000000000000..4c649c4dc527 --- /dev/null +++ b/java/java-tests/testData/refactoring/safeDelete/NoDeepDeleteParameterUsedInNextArgumentExpression_after.java @@ -0,0 +1,7 @@ +class Test { + void foo(String p) {} + + void bar(String s, String p) { + foo(s.substring(0)); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java index d7c1ca165d31..17239efb9a16 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/MultipleJdksHighlightingTest.java @@ -34,6 +34,7 @@ import java.util.Calendar; public class MultipleJdksHighlightingTest extends UsefulTestCase { private CodeInsightTestFixture myFixture; + private Module myJava6Module; private Module myJava7Module; private Module myJava8Module; @@ -47,6 +48,7 @@ public class MultipleJdksHighlightingTest extends UsefulTestCase { super.tearDown(); myFixture.tearDown(); myFixture = null; + myJava6Module = null; myJava7Module = null; myJava8Module = null; } @@ -57,12 +59,24 @@ public class MultipleJdksHighlightingTest extends UsefulTestCase { TestFixtureBuilder projectBuilder = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getName()); myFixture = JavaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(projectBuilder.getFixture()); myFixture.setTestDataPath(PathManagerEx.getTestDataPath() + "/codeInsight/multipleJdks"); - final JavaModuleFixtureBuilder[] builders = new JavaModuleFixtureBuilder[2]; + final JavaModuleFixtureBuilder[] builders = new JavaModuleFixtureBuilder[3]; builders[0] = projectBuilder.addModule(JavaModuleFixtureBuilder.class); builders[1] = projectBuilder.addModule(JavaModuleFixtureBuilder.class); + builders[2] = projectBuilder.addModule(JavaModuleFixtureBuilder.class); myFixture.setUp(); - myJava7Module = builders[0].getFixture().getModule(); - myJava8Module = builders[1].getFixture().getModule(); + myJava6Module = builders[0].getFixture().getModule(); + myJava7Module = builders[1].getFixture().getModule(); + myJava8Module = builders[2].getFixture().getModule(); + ModuleRootModificationUtil.updateModel(myJava6Module, new Consumer() { + @Override + public void consume(ModifiableRootModel model) { + model.addModuleOrderEntry(myJava7Module); + model.setSdk(IdeaTestUtil.getMockJdk17()); + String contentUrl = VfsUtilCore.pathToUrl(myFixture.getTempDirPath()) + "/java6"; + model.addContentEntry(contentUrl).addSourceFolder(contentUrl, false); + } + }); + ModuleRootModificationUtil.updateModel(myJava7Module, new Consumer() { @Override public void consume(ModifiableRootModel model) { @@ -87,7 +101,6 @@ public class MultipleJdksHighlightingTest extends UsefulTestCase { doTest(); } - @Bombed(month = Calendar.FEBRUARY, day = 20) public void testWrongSuperInLibrary() throws Exception { final String name = getTestName(false); for (Module module : new Module[] {myJava7Module, myJava8Module}) { @@ -102,43 +115,55 @@ public class MultipleJdksHighlightingTest extends UsefulTestCase { myFixture.configureByFile("java8/p/" + name + ".java"); myFixture.checkHighlighting(); } - - @Bombed(month = Calendar.FEBRUARY, day = 20) + public void testWrongComparator() throws Exception { doTestWithoutLibrary(); } - @Bombed(month = Calendar.FEBRUARY, day = 20) public void testGenericComparator() throws Exception { doTestWithoutLibrary(); } - @Bombed(month = Calendar.FEBRUARY, day = 20) public void testGenericCallableWithDifferentTypeArgs() throws Exception { doTestWithoutLibrary(); } - @Bombed(month = Calendar.FEBRUARY, day = 20) public void testSuperclassImplementsUnknownType() throws Exception { doTestWithoutLibrary(); } + + public void testDeclaredTypeOfVariableImplementsUnknownType() throws Exception { + doTestWithoutLibrary(); + } - @Bombed(month = Calendar.FEBRUARY, day = 20) public void testSuperclassImplementsGenericsOfUnknownType() throws Exception { doTestWithoutLibrary(); } - @Bombed(month = Calendar.FEBRUARY, day = 20) public void testSuperMethodNotExist() throws Exception { doTestWithoutLibrary(); } + public void testNoOverriding() throws Exception { + doTestWithoutLibrary(); + } + + public void testStaticCallOnChildWithNotAccessibleParent() throws Exception { + doTest3Modules(); + } + private void doTestWithoutLibrary() { final String name = getTestName(false); myFixture.configureByFiles("java7/p/" + name + ".java", "java8/p/" + name + ".java"); myFixture.checkHighlighting(); } + private void doTest3Modules() { + final String name = getTestName(false); + myFixture.configureByFiles("java6/p/" + name + ".java", "java7/p/" + name + ".java", "java8/p/" + name + ".java"); + myFixture.checkHighlighting(); + } + private void doTest() { final String name = getTestName(false); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk6Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk6Test.java index 0ed6303bfdad..225b44dc5b04 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk6Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk6Test.java @@ -68,6 +68,8 @@ public class LightAdvHighlightingJdk6Test extends LightDaemonAnalyzerTestCase { public void testUnsupportedFeatures7() { doTest(false, false); } public void testEnumInitializers() { doTest(false, false); } public void testEnumSynthetics() { doTest(false, false); } + public void testEnumWithoutConstants() { doTest(false, false); } + public void testEnumInstanceFieldInitializer() { doTest(false, false); } public void testIDEA79251() { doTest(false, false); } public void testIDEA65473() { doTest(false, false); } public void testIDEA61415() { doTest(false, false); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java index 3e3852503280..dada81fb3971 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java @@ -79,6 +79,10 @@ public class OverloadResolutionTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testPreferDefaultMethodsOverStatic() throws Exception { + doTest(); + } + private void doTest() { doTest(true); } diff --git a/java/java-tests/testSrc/com/intellij/refactoring/SafeDeleteTest.java b/java/java-tests/testSrc/com/intellij/refactoring/SafeDeleteTest.java index 6d24883fad47..5cf1fc5876df 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/SafeDeleteTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/SafeDeleteTest.java @@ -92,6 +92,10 @@ public class SafeDeleteTest extends MultiFileTestCase { doSingleFileTest(); } + public void testNoDeepDeleteParameterUsedInNextArgumentExpression() throws Exception { + doSingleFileTest(); + } + public void testToDeepDeleteParameterOverriders() throws Exception { doSingleFileTest(); } diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java index b175aea4046e..9fdc4c29cb0e 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java @@ -740,18 +740,19 @@ public class JavaMatchingVisitor extends JavaElementVisitor { PsiElement[] typeparams = null; if (el2 instanceof PsiJavaCodeReferenceElement) { - typeparams = ((PsiJavaCodeReferenceElement)el2).getParameterList().getTypeParameterElements(); + final PsiReferenceParameterList parameterList = ((PsiJavaCodeReferenceElement)el2).getParameterList(); + if (parameterList != null) { + typeparams = parameterList.getTypeParameterElements(); + } } else if (el2 instanceof PsiTypeParameter) { el2 = ((PsiTypeParameter)el2).getNameIdentifier(); } - else if (el2 instanceof PsiClass && ((PsiClass)el2).hasTypeParameters() - ) { + else if (el2 instanceof PsiClass && ((PsiClass)el2).hasTypeParameters()) { typeparams = ((PsiClass)el2).getTypeParameters(); el2 = ((PsiClass)el2).getNameIdentifier(); } - else if (el2 instanceof PsiMethod && ((PsiMethod)el2).hasTypeParameters() - ) { + else if (el2 instanceof PsiMethod && ((PsiMethod)el2).hasTypeParameters()) { typeparams = ((PsiMethod)_type2).getTypeParameters(); el2 = ((PsiMethod)_type2).getNameIdentifier(); } diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/ReadPredicate.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/ReadPredicate.java index ce25b998ba9b..a5967d5ca661 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/ReadPredicate.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/predicates/ReadPredicate.java @@ -10,19 +10,21 @@ import com.intellij.structuralsearch.impl.matcher.MatchUtils; */ public final class ReadPredicate extends MatchPredicate { public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) { + PsiElement parent = matchedNode.getParent(); if (matchedNode instanceof PsiIdentifier) { - matchedNode = matchedNode.getParent(); + matchedNode = parent; + parent = matchedNode.getParent(); } - if (matchedNode instanceof PsiReferenceExpression && - ( !(matchedNode.getParent() instanceof PsiMethodCallExpression) && - ( !(matchedNode.getParent() instanceof PsiAssignmentExpression) || - ((PsiAssignmentExpression)matchedNode.getParent()).getLExpression() != matchedNode - ) - ) && - MatchUtils.getReferencedElement(matchedNode) instanceof PsiVariable - ) { - return true; + if (!(matchedNode instanceof PsiReferenceExpression) || parent instanceof PsiMethodCallExpression) { + return false; } - return false; + if (parent instanceof PsiAssignmentExpression) { + final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent; + if (assignmentExpression.getLExpression() == matchedNode && + assignmentExpression.getOperationTokenType() == JavaTokenType.EQ) { + return false; + } + } + return MatchUtils.getReferencedElement(matchedNode) instanceof PsiVariable; } } diff --git a/jps/model-api/src/org/jetbrains/jps/model/java/JdkVersionDetector.java b/jps/model-api/src/org/jetbrains/jps/model/java/JdkVersionDetector.java index 9cca7dc6ea69..b4d2875313c1 100644 --- a/jps/model-api/src/org/jetbrains/jps/model/java/JdkVersionDetector.java +++ b/jps/model-api/src/org/jetbrains/jps/model/java/JdkVersionDetector.java @@ -30,6 +30,13 @@ public abstract class JdkVersionDetector { return JpsServiceManager.getInstance().getService(JdkVersionDetector.class); } + /** + * Returns java version for JDK located at {@code homePath} in format like
+ * java version "1.8.0_40"
+ * by running 'java -version' command + * @param homePath path to JDK home directory + * @return version string of {@code null} if version cannot be determined + */ @Nullable public abstract String detectJdkVersion(String homePath); diff --git a/jps/standalone-builder/jps-standalone-builder.iml b/jps/standalone-builder/jps-standalone-builder.iml index 13b8839d7b3d..a0cedffff0d8 100644 --- a/jps/standalone-builder/jps-standalone-builder.iml +++ b/jps/standalone-builder/jps-standalone-builder.iml @@ -16,7 +16,7 @@ - + diff --git a/lib/groovy-all-2.2.1.jar b/lib/groovy-all-2.2.1.jar deleted file mode 100644 index 8d151271695d..000000000000 Binary files a/lib/groovy-all-2.2.1.jar and /dev/null differ diff --git a/lib/groovy-all-2.3.7.jar b/lib/groovy-all-2.3.7.jar new file mode 100644 index 000000000000..11497a36356a Binary files /dev/null and b/lib/groovy-all-2.3.7.jar differ diff --git a/lib/required_for_dist.txt b/lib/required_for_dist.txt index 68f6e741a578..dc14bae97971 100644 --- a/lib/required_for_dist.txt +++ b/lib/required_for_dist.txt @@ -15,7 +15,7 @@ fluent-hc-4.3.6.jar freemarker.jar httpmime-4.3.6.jar ecj-4.4.jar -groovy-all-2.2.1.jar +groovy-all-2.3.7.jar gson-2.3.jar guava-17.0.jar hamcrest-core-1.3.jar diff --git a/lib/src/groovy-all-2.3.7-sources.jar b/lib/src/groovy-all-2.3.7-sources.jar new file mode 100644 index 000000000000..7c9d3c8dcf8a Binary files /dev/null and b/lib/src/groovy-all-2.3.7-sources.jar differ diff --git a/lib/src/groovy-src-2.2.1.zip b/lib/src/groovy-src-2.2.1.zip deleted file mode 100644 index c11ab96cddcb..000000000000 Binary files a/lib/src/groovy-src-2.2.1.zip and /dev/null differ diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/NetService.java b/platform/built-in-server/src/org/jetbrains/builtInWebServer/NetService.java index fcbbd8045125..6027e89cfbab 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/NetService.java +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/NetService.java @@ -20,6 +20,7 @@ import com.intellij.util.Consumer; import com.intellij.util.net.NetUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.concurrency.AsyncPromise; import org.jetbrains.io.NettyUtil; import javax.swing.*; @@ -84,8 +85,10 @@ public abstract class NetService implements Disposable { @Override public void run() { if (!result.isRejected()) { + AsyncPromise promise = new AsyncPromise(); try { - connectToProcess(result, port, processHandler, processListener); + connectToProcess(promise, port, processHandler, processListener); + promise.notify(result); } catch (Throwable e) { result.setRejected(); @@ -121,11 +124,11 @@ public abstract class NetService implements Disposable { @Nullable protected abstract OSProcessHandler createProcessHandler(@NotNull Project project, int port) throws ExecutionException; - protected void connectToProcess(@NotNull AsyncResult asyncResult, + protected void connectToProcess(@NotNull AsyncPromise promise, int port, @NotNull OSProcessHandler processHandler, @NotNull Consumer errorOutputConsumer) { - asyncResult.setDone(processHandler); + promise.setResult(processHandler); } protected abstract void closeProcessConnections(); diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/SingleConnectionNetService.java b/platform/built-in-server/src/org/jetbrains/builtInWebServer/SingleConnectionNetService.java index 300f44bf4281..e3f1dcc7584c 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/SingleConnectionNetService.java +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/SingleConnectionNetService.java @@ -2,12 +2,12 @@ package org.jetbrains.builtInWebServer; import com.intellij.execution.process.OSProcessHandler; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.AsyncResult; import com.intellij.util.Consumer; import com.intellij.util.net.NetUtils; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import org.jetbrains.annotations.NotNull; +import org.jetbrains.concurrency.AsyncPromise; import org.jetbrains.io.NettyUtil; import java.net.InetSocketAddress; @@ -22,12 +22,12 @@ public abstract class SingleConnectionNetService extends NetService { protected abstract void configureBootstrap(@NotNull Bootstrap bootstrap, @NotNull Consumer errorOutputConsumer); @Override - protected void connectToProcess(@NotNull AsyncResult asyncResult, int port, @NotNull OSProcessHandler processHandler, @NotNull Consumer errorOutputConsumer) { + protected void connectToProcess(@NotNull AsyncPromise promise, int port, @NotNull OSProcessHandler processHandler, @NotNull Consumer errorOutputConsumer) { Bootstrap bootstrap = NettyUtil.oioClientBootstrap(); configureBootstrap(bootstrap, errorOutputConsumer); - processChannel = NettyUtil.connectClient(bootstrap, new InetSocketAddress(NetUtils.getLoopbackAddress(), port), asyncResult); + processChannel = NettyUtil.connect(bootstrap, new InetSocketAddress(NetUtils.getLoopbackAddress(), port), promise); if (processChannel != null) { - asyncResult.setDone(processHandler); + promise.setResult(processHandler); } } diff --git a/platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiChannelHandler.java b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiChannelHandler.java similarity index 97% rename from platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiChannelHandler.java rename to platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiChannelHandler.java index 42accb022188..977d33e641da 100644 --- a/platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiChannelHandler.java +++ b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiChannelHandler.java @@ -1,4 +1,4 @@ -package org.jetbrains.fastCgi; +package org.jetbrains.io.fastCgi; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtilRt; @@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.io.Responses; import org.jetbrains.io.SimpleChannelInboundHandlerAdapter; -import static org.jetbrains.fastCgi.FastCgiService.LOG; +import static org.jetbrains.io.fastCgi.FastCgiService.LOG; @ChannelHandler.Sharable public class FastCgiChannelHandler extends SimpleChannelInboundHandlerAdapter { diff --git a/platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiConstants.java b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiConstants.java similarity index 71% rename from platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiConstants.java rename to platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiConstants.java index df41bda38987..23a85dead1f9 100644 --- a/platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiConstants.java +++ b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiConstants.java @@ -1,4 +1,4 @@ -package org.jetbrains.fastCgi; +package org.jetbrains.io.fastCgi; public final class FastCgiConstants { public static final int HEADER_LENGTH = 8; diff --git a/platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiDecoder.java b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiDecoder.java similarity index 97% rename from platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiDecoder.java rename to platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiDecoder.java index 57b2326477e8..cb48cc18c185 100644 --- a/platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiDecoder.java +++ b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiDecoder.java @@ -1,4 +1,4 @@ -package org.jetbrains.fastCgi; +package org.jetbrains.io.fastCgi; import com.intellij.util.Consumer; import gnu.trove.TIntObjectHashMap; @@ -10,7 +10,7 @@ import io.netty.util.CharsetUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.io.Decoder; -import static org.jetbrains.fastCgi.FastCgiService.LOG; +import static org.jetbrains.io.fastCgi.FastCgiService.LOG; public class FastCgiDecoder extends Decoder { private enum State { diff --git a/platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiRequest.java b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiRequest.java similarity index 99% rename from platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiRequest.java rename to platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiRequest.java index 4ac48e273204..e92d20eebffc 100644 --- a/platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiRequest.java +++ b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiRequest.java @@ -1,4 +1,4 @@ -package org.jetbrains.fastCgi; +package org.jetbrains.io.fastCgi; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; diff --git a/platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiResponse.java b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiResponse.java similarity index 90% rename from platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiResponse.java rename to platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiResponse.java index dbad397489c4..e249f7152c7c 100644 --- a/platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiResponse.java +++ b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiResponse.java @@ -1,4 +1,4 @@ -package org.jetbrains.fastCgi; +package org.jetbrains.io.fastCgi; import io.netty.buffer.ByteBuf; diff --git a/platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiService.java b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiService.java similarity index 99% rename from platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiService.java rename to platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiService.java index aef0c3f0770e..8849e6cb7a76 100644 --- a/platform/built-in-server/src/org/jetbrains/fastCgi/FastCgiService.java +++ b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.fastCgi; +package org.jetbrains.io.fastCgi; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java index a45b2deb4b86..401ab76370e0 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java @@ -32,20 +32,26 @@ import com.intellij.psi.impl.PsiManagerEx; import com.intellij.psi.impl.source.tree.MarkersHolderFileViewProvider; import com.intellij.psi.util.PsiUtilCore; import com.intellij.reference.SoftReference; -import com.intellij.util.containers.UnsafeWeakList; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.Set; public class SmartPointerManagerImpl extends SmartPointerManager { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl"); private final Project myProject; - private final Object lock = new Object(); - private final Key> POINTERS_KEY; + private static final Object lock = new Object(); + private static final ReferenceQueue ourQueue = new ReferenceQueue(); + private final Key> POINTERS_KEY; private final Key POINTERS_ARE_FASTENED_KEY; public SmartPointerManagerImpl(Project project) { @@ -54,23 +60,33 @@ public class SmartPointerManagerImpl extends SmartPointerManager { POINTERS_ARE_FASTENED_KEY = Key.create("SMART_POINTERS_ARE_FASTENED for "+project); } + private static void processQueue() { + while (true) { + PointerReference reference = (PointerReference)ourQueue.poll(); + if (reference == null) break; + synchronized (lock) { + Set pointers = reference.file.getUserData(reference.key); + if (pointers != null) { + pointers.remove(reference); + if (pointers.isEmpty()) { + reference.file.putUserData(reference.key, null); + } + } + } + } + } + public void fastenBelts(@NotNull VirtualFile file, int offset, @Nullable RangeMarker[] cachedRangeMarkers) { ApplicationManager.getApplication().assertIsDispatchThread(); + processQueue(); synchronized (lock) { - List pointers = getPointers(file); - if (pointers == null) return; + List pointers = getStrongPointers(file); + if (pointers.isEmpty()) return; if (getAndFasten(file)) return; - if (pointers.isEmpty()) { - file.putUserData(POINTERS_KEY, null); - } - else { - // pointers might change in fastenBelt() - List strongPointers = ((UnsafeWeakList)pointers).toStrongList(); - for (SmartPointerEx pointer : strongPointers) { - pointer.fastenBelt(offset, cachedRangeMarkers); - } + for (SmartPointerEx pointer : pointers) { + pointer.fastenBelt(offset, cachedRangeMarkers); } PsiFile psiFile = ((PsiManagerEx)PsiManager.getInstance(myProject)).getFileManager().getCachedPsiFile(file); @@ -103,21 +119,15 @@ public class SmartPointerManagerImpl extends SmartPointerManager { public void unfastenBelts(@NotNull VirtualFile file, int offset) { ApplicationManager.getApplication().assertIsDispatchThread(); + processQueue(); synchronized (lock) { - List pointers = getPointers(file); - if (pointers == null) return; + List pointers = getStrongPointers(file); + if (pointers.isEmpty()) return; if (!getAndUnfasten(file)) return; - if (pointers.isEmpty()) { - file.putUserData(POINTERS_KEY, null); - } - else { - // pointers might change in unfastenBelt() - List strongPointers = ((UnsafeWeakList)pointers).toStrongList(); - for (SmartPointerEx pointer : strongPointers) { - pointer.unfastenBelt(offset); - } + for (SmartPointerEx pointer : pointers) { + pointer.unfastenBelt(offset); } PsiFile psiFile = ((PsiManagerEx)PsiManager.getInstance(myProject)).getFileManager().getCachedPsiFile(file); @@ -147,6 +157,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager { PsiUtilCore.ensureValid(element); LOG.error("Invalid element:" + element); } + processQueue(); SmartPointerEx pointer = getCachedPointer(element); if (pointer != null) { containingFile = containingFile == null ? element.getContainingFile() : containingFile; @@ -197,12 +208,12 @@ public class SmartPointerManagerImpl extends SmartPointerManager { private void initPointer(@NotNull SmartPointerEx pointer, @NotNull VirtualFile containingFile) { synchronized (lock) { - List pointers = getPointers(containingFile); + Set pointers = getPointers(containingFile); if (pointers == null) { - pointers = new UnsafeWeakList(); // we synchronise access anyway + pointers = ContainerUtil.newTroveSet(); // we synchronise access anyway containingFile.putUserData(POINTERS_KEY, pointers); } - pointers.add(pointer); + pointers.add(new PointerReference(pointer, containingFile, ourQueue, POINTERS_KEY)); if (areBeltsFastened(containingFile)) { pointer.fastenBelt(0, null); @@ -222,26 +233,45 @@ public class SmartPointerManagerImpl extends SmartPointerManager { } PsiFile containingFile = pointer.getContainingFile(); if (containingFile == null) return false; - List pointers = getPointers(containingFile.getViewProvider().getVirtualFile()); + Set pointers = getPointers(containingFile.getViewProvider().getVirtualFile()); if (pointers == null) return false; SmartPointerElementInfo info = ((SmartPsiElementPointerImpl)pointer).getElementInfo(); info.cleanup(); - return pointers.remove(pointer); + + for (Iterator iterator = pointers.iterator(); iterator.hasNext(); ) { + if (pointer == iterator.next().get()) { + iterator.remove(); + return true; + } + } + return false; } } } return false; } - private List getPointers(@NotNull VirtualFile containingFile) { + @Nullable + private Set getPointers(@NotNull VirtualFile containingFile) { return containingFile.getUserData(POINTERS_KEY); } + @NotNull + private List getStrongPointers(@NotNull VirtualFile containingFile) { + Set refs = getPointers(containingFile); + if (refs == null) return Collections.emptyList(); + + List result = ContainerUtil.newArrayList(); + for (PointerReference reference : refs) { + ContainerUtil.addIfNotNull(result, reference.get()); + } + return result; + } + @TestOnly public int getPointersNumber(@NotNull PsiFile containingFile) { synchronized (lock) { - List pointers = getPointers(containingFile.getViewProvider().getVirtualFile()); - return pointers == null ? 0 : ((UnsafeWeakList)pointers).toStrongList().size(); + return getStrongPointers(containingFile.getViewProvider().getVirtualFile()).size(); } } @@ -263,4 +293,19 @@ public class SmartPointerManagerImpl extends SmartPointerManager { public boolean pointToTheSameElement(@NotNull SmartPsiElementPointer pointer1, @NotNull SmartPsiElementPointer pointer2) { return SmartPsiElementPointerImpl.pointsToTheSameElementAs(pointer1, pointer2); } + + private static class PointerReference extends WeakReference { + private final VirtualFile file; + private final Key> key; + + public PointerReference(SmartPointerEx pointer, + VirtualFile containingFile, + ReferenceQueue queue, + Key> key) { + super(pointer, queue); + file = containingFile; + this.key = key; + } + } + } diff --git a/platform/diff-api/src/com/intellij/diff/util/Side.java b/platform/diff-api/src/com/intellij/diff/util/Side.java index f24d5d04dec9..6b23d409f484 100644 --- a/platform/diff-api/src/com/intellij/diff/util/Side.java +++ b/platform/diff-api/src/com/intellij/diff/util/Side.java @@ -70,7 +70,7 @@ public enum Side { } @NotNull - public T selectN(@NotNull T left, @NotNull T right) { + public T selectNotNull(@NotNull T left, @NotNull T right) { return isLeft() ? left : right; } @@ -86,7 +86,7 @@ public enum Side { } @NotNull - public T selectN(@NotNull T[] array) { + public T selectNotNull(@NotNull T[] array) { assert array.length == 2; return array[myIndex]; } @@ -98,7 +98,7 @@ public enum Side { } @NotNull - public T selectN(@NotNull List list) { + public T selectNotNull(@NotNull List list) { assert list.size() == 2; return list.get(myIndex); } @@ -109,7 +109,7 @@ public enum Side { } @NotNull - public T selectN(@NotNull Couple region) { + public T selectNotNull(@NotNull Couple region) { return isLeft() ? region.first : region.second; } diff --git a/platform/diff-api/src/com/intellij/diff/util/ThreeSide.java b/platform/diff-api/src/com/intellij/diff/util/ThreeSide.java index 4defc2458c4a..9ac735d67101 100644 --- a/platform/diff-api/src/com/intellij/diff/util/ThreeSide.java +++ b/platform/diff-api/src/com/intellij/diff/util/ThreeSide.java @@ -48,7 +48,7 @@ public enum ThreeSide { } @NotNull - public T selectN(@NotNull T left, @NotNull T base, @NotNull T right) { + public T selectNotNull(@NotNull T left, @NotNull T base, @NotNull T right) { if (myIndex == 0) return left; if (myIndex == 1) return base; if (myIndex == 2) return right; @@ -67,7 +67,7 @@ public enum ThreeSide { } @NotNull - public T selectN(@NotNull T[] array) { + public T selectNotNull(@NotNull T[] array) { assert array.length == 3; return array[myIndex]; } @@ -79,7 +79,7 @@ public enum ThreeSide { } @NotNull - public T selectN(@NotNull List list) { + public T selectNotNull(@NotNull List list) { assert list.size() == 3; return list.get(myIndex); } diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/MergeUtil.java b/platform/diff-impl/src/com/intellij/diff/comparison/MergeUtil.java index 297345cf1f8b..88ea7ea4fabd 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/MergeUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/MergeUtil.java @@ -241,7 +241,7 @@ public class MergeUtil { @NotNull private Interval getRange(@NotNull ThreeSide side) { - return side.selectN(myLeft, myBase, myRight); + return side.selectNotNull(myLeft, myBase, myRight); } public int getStartLine(@NotNull ThreeSide side) { diff --git a/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java b/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java index bd28e91bb79a..8f3f8dbc9935 100644 --- a/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java +++ b/platform/diff-impl/src/com/intellij/diff/impl/DiffRequestProcessor.java @@ -583,6 +583,11 @@ public abstract class DiffRequestProcessor implements Disposable { protected class MyNextDifferenceAction extends NextDifferenceAction { @Override public void update(@NotNull AnActionEvent e) { + if (!ActionPlaces.DIFF_TOOLBAR.equals(e.getPlace())) { + e.getPresentation().setEnabledAndVisible(true); + return; + } + PrevNextDifferenceIterable iterable = DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.getData(e.getDataContext()); if (iterable != null && iterable.canGoNext()) { e.getPresentation().setEnabled(true); @@ -606,6 +611,8 @@ public abstract class DiffRequestProcessor implements Disposable { return; } + if (!isNavigationEnabled() || !hasNextChange()) return; + if (myIterationState != IterationState.NEXT) { // TODO: provide "change" word in chain UserData - for tests/etc if (iterable != null) iterable.notify("Press again to go to the next file"); @@ -620,6 +627,11 @@ public abstract class DiffRequestProcessor implements Disposable { protected class MyPrevDifferenceAction extends PrevDifferenceAction { @Override public void update(@NotNull AnActionEvent e) { + if (!ActionPlaces.DIFF_TOOLBAR.equals(e.getPlace())) { + e.getPresentation().setEnabledAndVisible(true); + return; + } + PrevNextDifferenceIterable iterable = DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.getData(e.getDataContext()); if (iterable != null && iterable.canGoPrev()) { e.getPresentation().setEnabled(true); @@ -643,6 +655,8 @@ public abstract class DiffRequestProcessor implements Disposable { return; } + if (!isNavigationEnabled() || !hasNextChange()) return; + if (myIterationState != IterationState.PREV) { if (iterable != null) iterable.notify("Press again to go to the previous file"); myIterationState = IterationState.PREV; diff --git a/platform/diff-impl/src/com/intellij/diff/tools/binary/BinaryDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/binary/BinaryDiffViewer.java index 1212b1e98cb2..2c41f656e79c 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/binary/BinaryDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/binary/BinaryDiffViewer.java @@ -388,7 +388,7 @@ public class BinaryDiffViewer extends ListenerDiffViewerBase { FileEditor editor = getCurrentEditor(); if (editor == null) return null; - DiffContent content = getCurrentSide().selectN(request.getContents()); + DiffContent content = getCurrentSide().selectNotNull(request.getContents()); return content.getOpenFileDescriptor(); } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideDiffViewer.java index 4d33882cdedc..f28c0624a14a 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideDiffViewer.java @@ -1009,7 +1009,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase { myShouldScroll = !doScrollToContext(myNavigationContext); } if (myShouldScroll && myCaretPosition != null && myCaretPosition.length == 2) { - LogicalPosition twosidePosition = myMasterSide.selectN(myCaretPosition); + LogicalPosition twosidePosition = myMasterSide.selectNotNull(myCaretPosition); int onesideLine = transferLineToOneside(myMasterSide, twosidePosition.line); LogicalPosition position = new LogicalPosition(onesideLine, twosidePosition.column); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideEditorHighlighter.java b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideEditorHighlighter.java index a35ddc59cb51..26e72197bad7 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideEditorHighlighter.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideEditorHighlighter.java @@ -69,7 +69,7 @@ public class OnesideEditorHighlighter implements EditorHighlighter { offset = base.getStartOffset(); } - HighlighterIterator it = range.getSide().selectN(it1, it2); + HighlighterIterator it = range.getSide().selectNotNull(it1, it2); while (!it.atEnd() && changed.getStartOffset() >= it.getEnd()) { it.advance(); } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideFragmentBuilder.java b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideFragmentBuilder.java index 3cd29271d342..07ba5cc7f2b7 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideFragmentBuilder.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideFragmentBuilder.java @@ -145,7 +145,7 @@ public class OnesideFragmentBuilder { } private void appendText(@NotNull Side side, int offset1, int offset2, int lines, int startLine1, int startLine2) { - Document document = side.selectN(myDocument1, myDocument2); + Document document = side.selectNotNull(myDocument1, myDocument2); int newline = document.getTextLength() > offset2 + 1 ? 1 : 0; TextRange base = new TextRange(myBuilder.length(), myBuilder.length() + offset2 - offset1 + newline); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffChange.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffChange.java index 4b91729c3733..b70286d2e074 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffChange.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffChange.java @@ -18,6 +18,7 @@ package com.intellij.diff.tools.simple; import com.intellij.diff.fragments.DiffFragment; import com.intellij.diff.fragments.LineFragment; import com.intellij.diff.util.*; +import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.editor.Document; @@ -227,9 +228,9 @@ public class SimpleDiffChange { final Document document1 = myEditor1.getDocument(); final Document document2 = myEditor2.getDocument(); - DiffUtil.applyModification(sourceSide.other().selectN(document1, document2), + DiffUtil.applyModification(sourceSide.other().selectNotNull(document1, document2), getStartLine(sourceSide.other()), getEndLine(sourceSide.other()), - sourceSide.selectN(document1, document2), + sourceSide.selectNotNull(document1, document2), getStartLine(sourceSide), getEndLine(sourceSide)); destroyHighlighter(); @@ -245,9 +246,9 @@ public class SimpleDiffChange { final Document document1 = myEditor1.getDocument(); final Document document2 = myEditor2.getDocument(); - DiffUtil.applyModification(sourceSide.other().selectN(document1, document2), + DiffUtil.applyModification(sourceSide.other().selectNotNull(document1, document2), getEndLine(sourceSide.other()), getEndLine(sourceSide.other()), - sourceSide.selectN(document1, document2), + sourceSide.selectNotNull(document1, document2), getStartLine(sourceSide), getEndLine(sourceSide)); destroyHighlighter(); @@ -261,7 +262,7 @@ public class SimpleDiffChange { private MyGutterOperation createOperation(@NotNull Side side) { assert myEditor1 != null && myEditor2 != null; int offset = side.getStartOffset(myFragment); - EditorEx editor = side.selectN(myEditor1, myEditor2); + EditorEx editor = side.selectNotNull(myEditor1, myEditor2); RangeHighlighter highlighter = editor.getMarkupModel().addRangeHighlighter(offset, offset, HighlighterLayer.ADDITIONAL_SYNTAX, null, @@ -302,8 +303,8 @@ public class SimpleDiffChange { myCtrlPressed = myViewer.getModifierProvider().isCtrlPressed(); myShiftPressed = myViewer.getModifierProvider().isShiftPressed(); - boolean isEditable = DiffUtil.isEditable(mySide.selectN(myEditor1, myEditor2)); - boolean isOtherEditable = DiffUtil.isEditable(mySide.other().selectN(myEditor1, myEditor2)); + boolean isEditable = DiffUtil.isEditable(mySide.selectNotNull(myEditor1, myEditor2)); + boolean isOtherEditable = DiffUtil.isEditable(mySide.other().selectNotNull(myEditor1, myEditor2)); if (myCtrlPressed && myShiftPressed) return null; if ((myShiftPressed || !isOtherEditable) && isEditable) { @@ -318,7 +319,7 @@ public class SimpleDiffChange { @Nullable private GutterIconRenderer createApplyRenderer(@NotNull final Side side) { - return createIconRenderer(side, DiffIcons.getReplaceIcon(Side.RIGHT), new Runnable() { + return createIconRenderer(side, AllIcons.Diff.Arrow, new Runnable() { @Override public void run() { replaceChange(side); @@ -328,7 +329,7 @@ public class SimpleDiffChange { @Nullable private GutterIconRenderer createAppendRenderer(@NotNull final Side side) { - return createIconRenderer(side, DiffIcons.getAppendIcon(Side.RIGHT), new Runnable() { + return createIconRenderer(side, AllIcons.Diff.ArrowLeftDown, new Runnable() { @Override public void run() { appendChange(side); @@ -338,7 +339,7 @@ public class SimpleDiffChange { @Nullable private GutterIconRenderer createRevertRenderer(@NotNull final Side side) { - return createIconRenderer(side.other(), DiffIcons.getRevertIcon(Side.RIGHT), new Runnable() { + return createIconRenderer(side.other(), AllIcons.Diff.Remove, new Runnable() { @Override public void run() { replaceChange(side.other()); @@ -349,7 +350,7 @@ public class SimpleDiffChange { @Nullable private GutterIconRenderer createIconRenderer(@NotNull final Side sourceSide, @NotNull final Icon icon, @NotNull final Runnable perform) { assert myEditor1 != null && myEditor2 != null; - if (!DiffUtil.isEditable(sourceSide.other().selectN(myEditor1, myEditor2))) return null; + if (!DiffUtil.isEditable(sourceSide.other().selectNotNull(myEditor1, myEditor2))) return null; return new GutterIconRenderer() { @NotNull @Override @@ -357,6 +358,8 @@ public class SimpleDiffChange { return icon; } + public boolean isNavigateAction() { return true; } + @Nullable @Override public AnAction getClickAction() { @@ -369,7 +372,7 @@ public class SimpleDiffChange { if (!myIsValid) return; - DiffUtil.executeWriteCommand(sourceSide.other().selectN(document1, document2), project, "Replace change", new Runnable() { + DiffUtil.executeWriteCommand(sourceSide.other().selectNotNull(document1, document2), project, "Replace change", new Runnable() { @Override public void run() { perform.run(); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java index 0b0c191a393f..f8e35d04d250 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java @@ -32,11 +32,11 @@ import com.intellij.diff.tools.util.FoldingModelSupport.SimpleFoldingModel; import com.intellij.diff.tools.util.base.HighlightPolicy; import com.intellij.diff.tools.util.twoside.TwosideTextDiffViewer; import com.intellij.diff.util.DiffDividerDrawUtil; -import com.intellij.diff.util.DiffIcons; import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy; import com.intellij.diff.util.DiffUtil; import com.intellij.diff.util.DiffUtil.DocumentData; import com.intellij.diff.util.Side; +import com.intellij.icons.AllIcons; import com.intellij.ide.IdeEventQueue; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -608,7 +608,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { return; } - Editor modifiedEditor = side.other(myModifyOpposite).selectN(myEditor1, myEditor2); + Editor modifiedEditor = side.other(myModifyOpposite).selectNotNull(myEditor1, myEditor2); if (!DiffUtil.isEditable(modifiedEditor)) { e.getPresentation().setEnabledAndVisible(false); return; @@ -626,7 +626,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { final Side side = Side.fromLeft(editor == myEditor1); final List selectedChanges = getSelectedChanges(side); - Editor modifiedEditor = side.other(myModifyOpposite).selectN(myEditor1, myEditor2); + Editor modifiedEditor = side.other(myModifyOpposite).selectNotNull(myEditor1, myEditor2); String title = e.getPresentation().getText() + " selected changes"; DiffUtil.executeWriteCommand(modifiedEditor.getDocument(), e.getProject(), title, new Runnable() { @Override @@ -663,13 +663,13 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { private class ReplaceSelectedChangesAction extends ApplySelectedChangesActionBase { public ReplaceSelectedChangesAction() { - super("Replace", null, DiffIcons.getReplaceIcon(null), true); + super("Replace", null, AllIcons.Diff.Arrow, true); } @NotNull @Override protected Icon getIcon(@NotNull Side side) { - return DiffIcons.getReplaceIcon(side); + return side.isLeft() ? AllIcons.Diff.Arrow : AllIcons.Diff.ArrowRight; } @Override @@ -682,13 +682,13 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { private class AppendSelectedChangesAction extends ApplySelectedChangesActionBase { public AppendSelectedChangesAction() { - super("Insert", null, DiffIcons.getAppendIcon(null), true); + super("Insert", null, AllIcons.Diff.ArrowLeftDown, true); } @NotNull @Override protected Icon getIcon(@NotNull Side side) { - return DiffIcons.getAppendIcon(side); + return side.isLeft() ? AllIcons.Diff.ArrowLeftDown : AllIcons.Diff.ArrowRightDown; } @Override @@ -701,13 +701,13 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { private class RevertSelectedChangesAction extends ApplySelectedChangesActionBase { public RevertSelectedChangesAction() { - super("Revert", null, DiffIcons.getRevertIcon(null), false); + super("Revert", null, AllIcons.Diff.Remove, false); } @NotNull @Override protected Icon getIcon(@NotNull Side side) { - return DiffIcons.getRevertIcon(side); + return AllIcons.Diff.Remove; } @Override diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffChange.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffChange.java index 12cad991df6d..8bae87e66ec7 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffChange.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffChange.java @@ -81,7 +81,7 @@ public class SimpleThreesideDiffChange { // private void createHighlighter(@NotNull ThreeSide side) { - Editor editor = side.selectN(myEditors); + Editor editor = side.selectNotNull(myEditors); Document document = editor.getDocument(); TextDiffType type = getDiffType(); @@ -208,7 +208,7 @@ public class SimpleThreesideDiffChange { private static CharSequence getRangeContent(@NotNull MergeLineFragment fragment, @NotNull List editors, @NotNull ThreeSide side) { - DocumentEx document = side.selectN(editors).getDocument(); + DocumentEx document = side.selectNotNull(editors).getDocument(); int line1 = fragment.getStartLine(side); int line2 = fragment.getEndLine(side); return DiffUtil.getLinesContent(document, line1, line2); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java index 2bd11c1f3351..2cc638633ee6 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java @@ -391,7 +391,7 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer { @NotNull @Override protected SyncScrollSupport.SyncScrollable getSyncScrollable(@NotNull Side side) { - return side.selectN(mySyncScrollable1, mySyncScrollable2); + return side.selectNotNull(mySyncScrollable1, mySyncScrollable2); } @NotNull @@ -548,15 +548,15 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer { @Override protected void processHelper(@NotNull ScrollHelper helper) { - ThreeSide left = mySide.selectN(ThreeSide.LEFT, ThreeSide.BASE); - ThreeSide right = mySide.selectN(ThreeSide.BASE, ThreeSide.RIGHT); + ThreeSide left = mySide.selectNotNull(ThreeSide.LEFT, ThreeSide.BASE); + ThreeSide right = mySide.selectNotNull(ThreeSide.BASE, ThreeSide.RIGHT); if (!helper.process(0, 0)) return; for (SimpleThreesideDiffChange diffChange : myDiffChanges) { if (!helper.process(diffChange.getStartLine(left), diffChange.getStartLine(right))) return; if (!helper.process(diffChange.getEndLine(left), diffChange.getEndLine(right))) return; } - helper.process(left.selectN(myEditors).getDocument().getLineCount(), right.selectN(myEditors).getDocument().getLineCount()); + helper.process(left.selectNotNull(myEditors).getDocument().getLineCount(), right.selectNotNull(myEditors).getDocument().getLineCount()); } } @@ -569,8 +569,8 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer { @Override public void process(@NotNull Handler handler) { - ThreeSide left = mySide.selectN(ThreeSide.LEFT, ThreeSide.BASE); - ThreeSide right = mySide.selectN(ThreeSide.BASE, ThreeSide.RIGHT); + ThreeSide left = mySide.selectNotNull(ThreeSide.LEFT, ThreeSide.BASE); + ThreeSide right = mySide.selectNotNull(ThreeSide.BASE, ThreeSide.RIGHT); for (SimpleThreesideDiffChange diffChange : myDiffChanges) { if (!diffChange.getType().isChange(mySide)) continue; @@ -596,8 +596,8 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer { public void paint(@NotNull Graphics g, @NotNull Component divider) { Graphics2D gg = getDividerGraphics(g, divider); - Editor editor1 = mySide.selectN(myEditors.get(0), myEditors.get(1)); - Editor editor2 = mySide.selectN(myEditors.get(1), myEditors.get(2)); + Editor editor1 = mySide.selectNotNull(myEditors.get(0), myEditors.get(1)); + Editor editor2 = mySide.selectNotNull(myEditors.get(1), myEditors.get(2)); //DividerPolygonUtil.paintSimplePolygons(gg, divider.getWidth(), editor1, editor2, myPaintable); DiffDividerDrawUtil.paintPolygons(gg, divider.getWidth(), editor1, editor2, myPaintable); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java b/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java index de41870c03b8..f6ee8536f745 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java @@ -264,7 +264,7 @@ public class FoldingModelSupport { } public void paintOnDivider(@NotNull Graphics2D gg, @NotNull Component divider, @NotNull Side side) { - MyPaintable paintable = side.selectN(myPaintable1, myPaintable2); + MyPaintable paintable = side.selectNotNull(myPaintable1, myPaintable2); paintable.paintOnDivider(gg, divider); } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/threeside/ThreesideTextDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/util/threeside/ThreesideTextDiffViewer.java index 6762d3c5efbe..c4a89d7fc6e8 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/threeside/ThreesideTextDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/threeside/ThreesideTextDiffViewer.java @@ -254,7 +254,7 @@ public abstract class ThreesideTextDiffViewer extends TextDiffViewerBase { @NotNull public EditorEx getCurrentEditor() { - return myCurrentSide.selectN(myEditors); + return myCurrentSide.selectNotNull(myEditors); } @NotNull @@ -274,7 +274,7 @@ public abstract class ThreesideTextDiffViewer extends TextDiffViewerBase { @CalledInAwt protected void scrollToLine(@NotNull ThreeSide side, int line) { - Editor editor = side.selectN(myEditors); + Editor editor = side.selectNotNull(myEditors); DiffUtil.scrollEditor(editor, line); myCurrentSide = side; } @@ -301,7 +301,7 @@ public abstract class ThreesideTextDiffViewer extends TextDiffViewerBase { protected OpenFileDescriptor getOpenFileDescriptor() { EditorEx editor = getCurrentEditor(); - DocumentContent content = getCurrentSide().selectN(myActualContents); + DocumentContent content = getCurrentSide().selectNotNull(myActualContents); int offset = editor.getCaretModel().getOffset(); return content.getOpenFileDescriptor(offset); @@ -363,8 +363,8 @@ public abstract class ThreesideTextDiffViewer extends TextDiffViewerBase { DiffContent[] contents = myRequest.getContents(); String[] titles = myRequest.getContentTitles(); - DiffRequest request = new SimpleDiffRequest(myRequest.getTitle(), mySide1.selectN(contents), mySide2.selectN(contents), - mySide1.selectN(titles), mySide1.selectN(titles)); + DiffRequest request = new SimpleDiffRequest(myRequest.getTitle(), mySide1.selectNotNull(contents), mySide2.selectNotNull(contents), + mySide1.selectNotNull(titles), mySide1.selectNotNull(titles)); DiffManager.getInstance().showDiff(myProject, request, new DiffDialogHints(null, myPanel)); } } diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffIcons.java b/platform/diff-impl/src/com/intellij/diff/util/DiffIcons.java deleted file mode 100644 index 1fea0e06add1..000000000000 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffIcons.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * 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 - * - * http://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. - */ -package com.intellij.diff.util; - -import com.intellij.icons.AllIcons; -import com.intellij.util.IconUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; - -public class DiffIcons { - public final static Icon REPLACE_LEFT = IconUtil.flip(AllIcons.Diff.Arrow, true); - public final static Icon REPLACE_RIGHT = AllIcons.Diff.Arrow; - - // TODO: different icon - public final static Icon APPEND_LEFT = IconUtil.flip(AllIcons.Diff.Arrow, true); - public final static Icon APPEND_RIGHT = AllIcons.Diff.Arrow; - - public final static Icon REVERT_LEFT = AllIcons.Diff.Remove; - public final static Icon REVERT_RIGHT = AllIcons.Diff.Remove; - - @NotNull - public static Icon getReplaceIcon(@Nullable Side side) { - if (side == null) side = Side.LEFT; - return side.selectN(REPLACE_LEFT, REPLACE_RIGHT); - } - - @NotNull - public static Icon getAppendIcon(@Nullable Side side) { - if (side == null) side = Side.LEFT; - return side.selectN(APPEND_LEFT, APPEND_RIGHT); - } - - @NotNull - public static Icon getRevertIcon(@Nullable Side side) { - if (side == null) side = Side.LEFT; - return side.selectN(REVERT_LEFT, REVERT_RIGHT); - } -} diff --git a/platform/icons/src/diff/arrow@2x_dark.png b/platform/icons/src/diff/arrow@2x_dark.png index db6d35cadcd5..5f3b65f83663 100644 Binary files a/platform/icons/src/diff/arrow@2x_dark.png and b/platform/icons/src/diff/arrow@2x_dark.png differ diff --git a/platform/icons/src/diff/arrowLeftDown.png b/platform/icons/src/diff/arrowLeftDown.png new file mode 100644 index 000000000000..e01b1b4206bd Binary files /dev/null and b/platform/icons/src/diff/arrowLeftDown.png differ diff --git a/platform/icons/src/diff/arrowLeftDown@2x.png b/platform/icons/src/diff/arrowLeftDown@2x.png new file mode 100644 index 000000000000..39397c4c65c8 Binary files /dev/null and b/platform/icons/src/diff/arrowLeftDown@2x.png differ diff --git a/platform/icons/src/diff/arrowLeftDown@2x_dark.png b/platform/icons/src/diff/arrowLeftDown@2x_dark.png new file mode 100644 index 000000000000..f7d0791c2f1b Binary files /dev/null and b/platform/icons/src/diff/arrowLeftDown@2x_dark.png differ diff --git a/platform/icons/src/diff/arrowLeftDown_dark.png b/platform/icons/src/diff/arrowLeftDown_dark.png new file mode 100644 index 000000000000..e54c525c98e6 Binary files /dev/null and b/platform/icons/src/diff/arrowLeftDown_dark.png differ diff --git a/platform/icons/src/diff/arrowRight.png b/platform/icons/src/diff/arrowRight.png new file mode 100644 index 000000000000..f7d76849a23a Binary files /dev/null and b/platform/icons/src/diff/arrowRight.png differ diff --git a/platform/icons/src/diff/arrowRight@2x.png b/platform/icons/src/diff/arrowRight@2x.png new file mode 100644 index 000000000000..299b7d315bc2 Binary files /dev/null and b/platform/icons/src/diff/arrowRight@2x.png differ diff --git a/platform/icons/src/diff/arrowRight@2x_dark.png b/platform/icons/src/diff/arrowRight@2x_dark.png new file mode 100644 index 000000000000..7c83eae20f87 Binary files /dev/null and b/platform/icons/src/diff/arrowRight@2x_dark.png differ diff --git a/platform/icons/src/diff/arrowRightDown.png b/platform/icons/src/diff/arrowRightDown.png new file mode 100644 index 000000000000..7f005f1d68fc Binary files /dev/null and b/platform/icons/src/diff/arrowRightDown.png differ diff --git a/platform/icons/src/diff/arrowRightDown@2x.png b/platform/icons/src/diff/arrowRightDown@2x.png new file mode 100644 index 000000000000..d116ce1447dd Binary files /dev/null and b/platform/icons/src/diff/arrowRightDown@2x.png differ diff --git a/platform/icons/src/diff/arrowRightDown@2x_dark.png b/platform/icons/src/diff/arrowRightDown@2x_dark.png new file mode 100644 index 000000000000..9c0c3a5b6d1d Binary files /dev/null and b/platform/icons/src/diff/arrowRightDown@2x_dark.png differ diff --git a/platform/icons/src/diff/arrowRightDown_dark.png b/platform/icons/src/diff/arrowRightDown_dark.png new file mode 100644 index 000000000000..6df2507058f8 Binary files /dev/null and b/platform/icons/src/diff/arrowRightDown_dark.png differ diff --git a/platform/icons/src/diff/arrowRight_dark.png b/platform/icons/src/diff/arrowRight_dark.png new file mode 100644 index 000000000000..6d1d8e054343 Binary files /dev/null and b/platform/icons/src/diff/arrowRight_dark.png differ diff --git a/platform/icons/src/diff/arrow_dark.png b/platform/icons/src/diff/arrow_dark.png index f6ee4dca52e6..7bebe9b18758 100644 Binary files a/platform/icons/src/diff/arrow_dark.png and b/platform/icons/src/diff/arrow_dark.png differ diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionContributor.java b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionContributor.java index 2329e07f7401..b6485d6c5432 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionContributor.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionContributor.java @@ -22,6 +22,7 @@ import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.KeyedExtensionCollector; import com.intellij.openapi.util.Pair; @@ -234,7 +235,9 @@ public abstract class CompletionContributor { return ApplicationManager.getApplication().runReadAction(new Computable>() { @Override public List compute() { - return forLanguage(PsiUtilCore.getLanguageAtOffset(parameters.getPosition().getContainingFile(), parameters.getOffset())); + PsiElement position = parameters.getPosition(); + List all = forLanguage(PsiUtilCore.getLanguageAtOffset(position.getContainingFile(), parameters.getOffset())); + return DumbService.getInstance(position.getProject()).filterByDumbAwareness(all); } }); } diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionService.java b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionService.java index 282082a0d4a4..cfc2e27a8179 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionService.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionService.java @@ -17,7 +17,6 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.project.DumbService; import com.intellij.openapi.util.Key; import com.intellij.psi.Weigher; import com.intellij.util.Consumer; @@ -74,11 +73,9 @@ public abstract class CompletionService { @Nullable final CompletionContributor from, final Consumer consumer) { final List contributors = CompletionContributor.forParameters(parameters); - final boolean dumb = DumbService.getInstance(parameters.getPosition().getProject()).isDumb(); for (int i = contributors.indexOf(from) + 1; i < contributors.size(); i++) { final CompletionContributor contributor = contributors.get(i); - if (dumb && !DumbService.isDumbAware(contributor)) continue; final CompletionResultSet result = createResultSet(parameters, consumer, contributor); contributor.fillCompletionVariants(parameters, result); diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/EditorNotificationInfo.java b/platform/lang-api/src/com/intellij/psi/codeStyle/EditorNotificationInfo.java new file mode 100644 index 000000000000..59315c245631 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/EditorNotificationInfo.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.intellij.psi.codeStyle; + +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.Collections; +import java.util.List; + +public class EditorNotificationInfo { + + private String myTitle; + private Icon myIcon; + private List myLabelsWithActions = ContainerUtil.newArrayList(); + + public EditorNotificationInfo(@NotNull String title, + @NotNull LabelWithAction firstLabel, + @Nullable LabelWithAction... otherLabels) + { + myTitle = title; + myLabelsWithActions.add(firstLabel); + if (otherLabels != null) { + Collections.addAll(myLabelsWithActions, otherLabels); + } + } + + public EditorNotificationInfo(@NotNull String title, + @NotNull Icon icon, + @NotNull LabelWithAction firstLabel, + @Nullable LabelWithAction... otherLabels) + { + this(title, firstLabel, otherLabels); + myIcon = icon; + } + + @NotNull + public List getLabelAndActions() { + return myLabelsWithActions; + } + + @Nullable + public Icon getIcon() { + return myIcon; + } + + @NotNull + public String getTitle() { + return myTitle; + } + +} + + diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/FileIndentOptionsProvider.java b/platform/lang-api/src/com/intellij/psi/codeStyle/FileIndentOptionsProvider.java index 3003d23e4f06..6c06a9216027 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/FileIndentOptionsProvider.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/FileIndentOptionsProvider.java @@ -16,9 +16,11 @@ package com.intellij.psi.codeStyle; import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; +import com.intellij.ui.EditorNotificationPanel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,7 +29,7 @@ import javax.swing.*; /** * @author Rustam Vishnyakov */ -public abstract class FileIndentOptionsProvider { +public abstract class FileIndentOptionsProvider { public final static ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.fileIndentOptionsProvider"); /** @@ -48,36 +50,18 @@ public abstract class FileIndentOptionsProvider { } /** - * @return A name which will be used to notify a user on overwritten indent options. If the option is null, no notification + * @return information used to create user notification in editor. If the option is null, no notification * will be shown. */ @Nullable - public String getDisplayName() { + public EditorNotificationInfo getNotificationInfo(@NotNull Project project, + @NotNull VirtualFile file, + @NotNull FileEditor fileEditor, + @NotNull CommonCodeStyleSettings.IndentOptions user, + @NotNull CommonCodeStyleSettings.IndentOptions detected) { return null; } - - /** - * @return The icon to be displayed in a notification message, can be null (no icon). - */ - @Nullable - public Icon getIcon() { - return null; - } - - /** - * @return True if the provider can be disabled (default is false). - */ - public boolean canBeDisabled() { - return false; - } - - /** - * Disables the provider. - * @param project The project to disable the provider for. - */ - public void disable(@NotNull Project project) { - } - + /** * Tells if there should not be any notification for this specific file. * @param file The file to check. diff --git a/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCAnswerFileTypeFactory.java b/platform/lang-api/src/com/intellij/psi/codeStyle/LabelWithAction.java similarity index 57% rename from python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCAnswerFileTypeFactory.java rename to platform/lang-api/src/com/intellij/psi/codeStyle/LabelWithAction.java index 4585963375a3..b39afc320102 100644 --- a/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCAnswerFileTypeFactory.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/LabelWithAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.edu.coursecreator; +package com.intellij.psi.codeStyle; - -import com.intellij.openapi.fileTypes.FileTypeConsumer; -import com.intellij.openapi.fileTypes.FileTypeFactory; -import com.jetbrains.python.PythonFileType; import org.jetbrains.annotations.NotNull; -public class PyCCAnswerFileTypeFactory extends FileTypeFactory { - @Override - public void createFileTypes(@NotNull FileTypeConsumer fileTypeConsumer) { - fileTypeConsumer.consume(PythonFileType.INSTANCE, "answer"); +public class LabelWithAction { + + public final String label; + public final Runnable action; + + public LabelWithAction(@NotNull String label, @NotNull Runnable action) { + this.label = label; + this.action = action; } } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java index 749a0038e048..dfa91e3bed53 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java @@ -75,8 +75,6 @@ public class WrappingAndBracesPanel extends OptionTableWithPreviewPanel { addOption("METHOD_CALL_CHAIN_WRAP", WRAPPING_CALL_CHAIN, WRAP_OPTIONS, WRAP_VALUES); addOption("ALIGN_MULTILINE_CHAINED_METHODS", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_CALL_CHAIN); - addOption("ALIGN_GROUP_FIELD_DECLARATIONS", ApplicationBundle.message("wrapping.align.in.columns"), WRAPPING_FIELDS_VARIABLES_GROUPS); - addOption("IF_BRACE_FORCE", ApplicationBundle.message("wrapping.force.braces"), WRAPPING_IF_STATEMENT, BRACE_OPTIONS, BRACE_VALUES); addOption("ELSE_ON_NEW_LINE", ApplicationBundle.message("wrapping.else.on.new.line"), WRAPPING_IF_STATEMENT); addOption("SPECIAL_ELSE_IF_TREATMENT", ApplicationBundle.message("wrapping.special.else.if.braces.treatment"), WRAPPING_IF_STATEMENT); @@ -111,7 +109,9 @@ public class WrappingAndBracesPanel extends OptionTableWithPreviewPanel { addOption("ASSIGNMENT_WRAP", WRAPPING_ASSIGNMENT, WRAP_OPTIONS, WRAP_VALUES); addOption("ALIGN_MULTILINE_ASSIGNMENT", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_ASSIGNMENT); addOption("PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE", ApplicationBundle.message("wrapping.assignment.sign.on.next.line"), WRAPPING_ASSIGNMENT); - addOption("ALIGN_CONSECUTIVE_VARIABLE_DECLARATIONS", ApplicationBundle.message("wrapping.assignment.align.consecutive.variable.declarations"), WRAPPING_ASSIGNMENT); + + addOption("ALIGN_GROUP_FIELD_DECLARATIONS", ApplicationBundle.message("wrapping.align.fields.in.columns"), WRAPPING_FIELDS_VARIABLES_GROUPS); + addOption("ALIGN_CONSECUTIVE_VARIABLE_DECLARATIONS", ApplicationBundle.message("wrapping.align.variables.in.columns"), WRAPPING_FIELDS_VARIABLES_GROUPS); addOption("TERNARY_OPERATION_WRAP", WRAPPING_TERNARY_OPERATION, WRAP_OPTIONS, WRAP_VALUES); addOption("ALIGN_MULTILINE_TERNARY_OPERATION", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_TERNARY_OPERATION); diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/OptionsPanelImpl.java b/platform/lang-impl/src/com/intellij/application/options/colors/OptionsPanelImpl.java index 537d630fff95..ebcdc9e5487b 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/OptionsPanelImpl.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/OptionsPanelImpl.java @@ -16,10 +16,15 @@ package com.intellij.application.options.colors; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.CollectionListModel; import com.intellij.ui.ListScrollingUtil; +import com.intellij.ui.ListSpeedSearch; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.components.JBList; import com.intellij.util.EventDispatcher; +import com.intellij.util.containers.ContainerUtil; import javax.swing.*; import javax.swing.event.ListSelectionEvent; @@ -27,11 +32,16 @@ import javax.swing.event.ListSelectionListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.HashSet; -import java.util.Set; +import java.util.*; public class OptionsPanelImpl extends JPanel implements OptionsPanel { - private final JList myOptionsList; + private static final Comparator ATTR_COMPARATOR = new Comparator() { + @Override + public int compare(EditorSchemeAttributeDescriptor o1, EditorSchemeAttributeDescriptor o2) { + return StringUtil.naturalCompare(o1.toString(), o2.toString()); + } + }; + private final JBList myOptionsList; private final ColorAndFontDescriptionPanel myOptionsPanel; private final ColorAndFontOptions myOptions; @@ -39,9 +49,12 @@ public class OptionsPanelImpl extends JPanel implements OptionsPanel { private final String myCategoryName; private final EventDispatcher myDispatcher = EventDispatcher.create(ColorAndFontSettingsListener.class); + private final CollectionListModel myListModel; - public OptionsPanelImpl(ColorAndFontDescriptionPanel optionsPanel, ColorAndFontOptions options, SchemesPanel schemesProvider, - String categoryName) { + public OptionsPanelImpl(ColorAndFontDescriptionPanel optionsPanel, + ColorAndFontOptions options, + SchemesPanel schemesProvider, + String categoryName) { super(new BorderLayout()); myOptions = options; mySchemesProvider = schemesProvider; @@ -54,7 +67,9 @@ public class OptionsPanelImpl extends JPanel implements OptionsPanel { } }); - myOptionsList = new JBList(); + myListModel = new CollectionListModel(); + myOptionsList = new JBList(myListModel); + new ListSpeedSearch(myOptionsList); myOptionsList.addListSelectionListener(new ListSelectionListener() { @Override @@ -75,7 +90,6 @@ public class OptionsPanelImpl extends JPanel implements OptionsPanel { } }); - myOptionsList.setModel(new DefaultListModel()); myOptionsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myOptionsList); @@ -109,16 +123,15 @@ public class OptionsPanelImpl extends JPanel implements OptionsPanel { private void fillOptionsList() { int selIndex = myOptionsList.getSelectedIndex(); - DefaultListModel listModel = (DefaultListModel)myOptionsList.getModel(); - listModel.removeAllElements(); + myListModel.removeAll(); - EditorSchemeAttributeDescriptor[] descriptions = myOptions.getCurrentDescriptions(); - - for (EditorSchemeAttributeDescriptor description : descriptions) { - if (description.getGroup().equals(myCategoryName)) { - listModel.addElement(description); - } + ArrayList list = ContainerUtil.newArrayList(); + for (EditorSchemeAttributeDescriptor description : myOptions.getCurrentDescriptions()) { + if (!description.getGroup().equals(myCategoryName)) continue; + list.add(description); } + Collections.sort(list, ATTR_COMPARATOR); + myListModel.add(list); if (selIndex >= 0) { myOptionsList.setSelectedIndex(selIndex); } @@ -142,54 +155,38 @@ public class OptionsPanelImpl extends JPanel implements OptionsPanel { } @Override - public Runnable showOption(final String option) { - final String lowerCaseOption = option.toLowerCase(); - DefaultListModel model = (DefaultListModel)myOptionsList.getModel(); - - for (int i = 0; i < model.size(); i++) { - Object o = model.get(i); - if (o instanceof EditorSchemeAttributeDescriptor) { - String type = ((EditorSchemeAttributeDescriptor)o).getType(); - if (type.toLowerCase().contains(lowerCaseOption) || - o.toString().toLowerCase().contains(lowerCaseOption)) { - final int i1 = i; - return new Runnable() { - @Override - public void run() { - ListScrollingUtil.selectItem(myOptionsList, i1); - } - }; - - } + public Runnable showOption(String attribute) { + final int index = getAttributeIndex(attribute); + return index < 0 ? null : new Runnable() { + @Override + public void run() { + ListScrollingUtil.selectItem(myOptionsList, index); } - } + }; + } - return null; + private int getAttributeIndex(final String option) { + return ContainerUtil.indexOf(myListModel.getItems(), new Condition() { + @Override + public boolean value(EditorSchemeAttributeDescriptor o) { + return StringUtil.naturalCompare(o.getType(), option) == 0; + } + }); } @Override public void applyChangesToScheme() { Object selectedValue = myOptionsList.getSelectedValue(); if (selectedValue instanceof ColorAndFontDescription) { - myOptionsPanel.apply((ColorAndFontDescription)selectedValue,myOptions.getSelectedScheme()); + myOptionsPanel.apply((ColorAndFontDescription)selectedValue, myOptions.getSelectedScheme()); } - } @Override - public void selectOption(final String typeToSelect) { - DefaultListModel model = (DefaultListModel)myOptionsList.getModel(); - - for (int i = 0; i < model.size(); i++) { - Object o = model.get(i); - if (o instanceof EditorSchemeAttributeDescriptor) { - if (typeToSelect.equals(((EditorSchemeAttributeDescriptor)o).getType())) { - ListScrollingUtil.selectItem(myOptionsList, i); - return; - } - } - } - + public void selectOption(String attribute) { + int index = getAttributeIndex(attribute); + if (index < 0) return; + ListScrollingUtil.selectItem(myOptionsList, index); } @Override diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java index 44368e996ca4..1d95b055d70b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java @@ -185,12 +185,21 @@ public class CtrlMouseHandler extends AbstractProjectComponent { } MouseEvent mouseEvent = e.getMouseEvent(); + Point prevLocation = myPrevMouseLocation; + myPrevMouseLocation = mouseEvent.getLocationOnScreen(); if (isMouseOverTooltip(mouseEvent.getLocationOnScreen()) - || ScreenUtil.isMovementTowards(myPrevMouseLocation, mouseEvent.getLocationOnScreen(), getHintBounds())) { - myPrevMouseLocation = mouseEvent.getLocationOnScreen(); + || ScreenUtil.isMovementTowards(prevLocation, mouseEvent.getLocationOnScreen(), getHintBounds())) { + return; + } + cancelPreviousTooltip(); + + myStoredModifiers = mouseEvent.getModifiers(); + BrowseMode browseMode = getBrowseMode(myStoredModifiers); + + if (browseMode == BrowseMode.None) { + disposeHighlighter(); return; } - myPrevMouseLocation = mouseEvent.getLocationOnScreen(); Editor editor = e.getEditor(); if (editor.getProject() != null && editor.getProject() != myProject) return; @@ -208,12 +217,7 @@ public class CtrlMouseHandler extends AbstractProjectComponent { int selStart = editor.getSelectionModel().getSelectionStart(); int selEnd = editor.getSelectionModel().getSelectionEnd(); - myStoredModifiers = mouseEvent.getModifiers(); - BrowseMode browseMode = getBrowseMode(myStoredModifiers); - - cancelPreviousTooltip(); - - if (browseMode == BrowseMode.None || offset >= selStart && offset < selEnd) { + if (offset >= selStart && offset < selEnd) { disposeHighlighter(); return; } diff --git a/platform/lang-impl/src/com/intellij/execution/console/RunIdeConsoleAction.java b/platform/lang-impl/src/com/intellij/execution/console/RunIdeConsoleAction.java index 0f1e722acb08..723a9b690ab7 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/RunIdeConsoleAction.java +++ b/platform/lang-impl/src/com/intellij/execution/console/RunIdeConsoleAction.java @@ -28,6 +28,7 @@ import com.intellij.ide.scratch.ScratchFileService; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ex.util.EditorUtil; @@ -50,6 +51,7 @@ import com.intellij.util.PathUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.ide.PooledThreadExecutor; import javax.script.*; import javax.swing.*; @@ -58,6 +60,7 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.util.List; import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; /** * @author gregsh @@ -68,12 +71,22 @@ public class RunIdeConsoleAction extends DumbAwareAction { private static final Key> DESCRIPTOR_KEY = Key.create("DESCRIPTOR_KEY"); private static final Key HISTORY_CONTROLLER_KEY = Key.create("HISTORY_CONTROLLER_KEY"); + private static final Logger LOG = Logger.getInstance(RunIdeConsoleAction.class); static class Engines { - static final Map ourEngines = ContainerUtil.newLinkedHashMap(); - static { - for (ScriptEngineFactory factory : new ScriptEngineManager().getEngineFactories()) { - ourEngines.put(factory.getLanguageName(), factory); - } + static final List ourEngines = new CopyOnWriteArrayList(); + static { + PooledThreadExecutor.INSTANCE.submit( + new Runnable() { + @Override + public void run() { + try { + ourEngines.addAll(new ScriptEngineManager().getEngineFactories()); + } + catch (Throwable e) { + LOG.error(e); + } + } + }); } } @@ -86,11 +99,11 @@ public class RunIdeConsoleAction extends DumbAwareAction { @Override public void actionPerformed(AnActionEvent e) { if (Engines.ourEngines.size() == 1) { - runConsole(e, Engines.ourEngines.values().iterator().next()); + runConsole(e, Engines.ourEngines.get(0)); } else { DefaultActionGroup actions = new DefaultActionGroup( - ContainerUtil.map(Engines.ourEngines.values(), new NotNullFunction() { + ContainerUtil.map(Engines.ourEngines, new NotNullFunction() { @NotNull @Override public AnAction fun(final ScriptEngineFactory engine) { @@ -119,7 +132,8 @@ public class RunIdeConsoleAction extends DumbAwareAction { FileEditorManager.getInstance(project).openFile(virtualFile, true); } } - catch (IOException ignored) { + catch (IOException ex) { + LOG.error(ex); } } @@ -137,7 +151,7 @@ public class RunIdeConsoleAction extends DumbAwareAction { @Nullable private static ScriptEngine findScriptEngine(@NotNull VirtualFile file) { - for (ScriptEngineFactory factory : Engines.ourEngines.values()) { + for (ScriptEngineFactory factory : Engines.ourEngines) { if (factory.getExtensions().contains(file.getExtension())) { return factory.getScriptEngine(); } @@ -149,7 +163,6 @@ public class RunIdeConsoleAction extends DumbAwareAction { @NotNull VirtualFile file, @NotNull Editor editor, @NotNull ScriptEngine engine) { - TextRange selectedRange = EditorUtil.getSelectionInAnyMode(editor); Document document = editor.getDocument(); if (selectedRange.getLength() == 0) { diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/NewScratchFileAction.java b/platform/lang-impl/src/com/intellij/ide/scratch/NewScratchFileAction.java index 05a0a7bf5996..57c4b14ddaa1 100644 --- a/platform/lang-impl/src/com/intellij/ide/scratch/NewScratchFileAction.java +++ b/platform/lang-impl/src/com/intellij/ide/scratch/NewScratchFileAction.java @@ -66,10 +66,6 @@ import java.util.concurrent.TimeUnit; public class NewScratchFileAction extends DumbAwareAction { public static final int MAX_VISIBLE_SIZE = 20; - public NewScratchFileAction() { - super("New Scratch File...", null, null); - } - @Override public void update(@NotNull AnActionEvent e) { e.getPresentation().setEnabledAndVisible(isEnabled(e)); diff --git a/platform/lang-impl/src/com/intellij/internal/DumpLookupElementWeights.java b/platform/lang-impl/src/com/intellij/internal/DumpLookupElementWeights.java index 6350642c772e..8ae1cc85f72f 100644 --- a/platform/lang-impl/src/com/intellij/internal/DumpLookupElementWeights.java +++ b/platform/lang-impl/src/com/intellij/internal/DumpLookupElementWeights.java @@ -66,7 +66,8 @@ public class DumpLookupElementWeights extends AnAction implements DumbAware { final Map strings = lookup.getRelevanceStrings(); List sb = new ArrayList(); for (LookupElement item : lookup.getItems()) { - String weight = strings.get(item).toString(); + StringBuilder builder = strings.get(item); + String weight = builder == null ? "null" : builder.toString(); final String s = item.getLookupString() + "\t" + weight; sb.add(s); } diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/actions/NamedElementDuplicateHandler.java b/platform/lang-impl/src/com/intellij/openapi/editor/actions/NamedElementDuplicateHandler.java index ae12790519dc..baeb0d5bed09 100644 --- a/platform/lang-impl/src/com/intellij/openapi/editor/actions/NamedElementDuplicateHandler.java +++ b/platform/lang-impl/src/com/intellij/openapi/editor/actions/NamedElementDuplicateHandler.java @@ -16,6 +16,7 @@ package com.intellij.openapi.editor.actions; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.VisualPosition; @@ -25,8 +26,12 @@ import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; -import com.intellij.psi.*; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiNameIdentifierOwner; import com.intellij.util.text.CharArrayUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -40,6 +45,11 @@ public class NamedElementDuplicateHandler extends EditorWriteActionHandler { myOriginal = original; } + @Override + protected boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) { + return myOriginal.isEnabled(editor, caret, dataContext); + } + @Override public void executeWriteAction(Editor editor, DataContext dataContext) { Project project = editor.getProject(); diff --git a/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectableIndentOptionsProvider.java b/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectableIndentOptionsProvider.java index 36677c4cdb8d..1687817f5a22 100644 --- a/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectableIndentOptionsProvider.java +++ b/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectableIndentOptionsProvider.java @@ -15,16 +15,19 @@ */ package com.intellij.psi.codeStyle.autodetect; +import com.intellij.ide.actions.ShowSettingsUtilImpl; import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CodeStyleSettingsManager; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings; -import com.intellij.psi.codeStyle.FileIndentOptionsProvider; +import com.intellij.psi.codeStyle.*; import com.intellij.testFramework.LightVirtualFile; +import com.intellij.ui.EditorNotifications; import com.intellij.util.containers.WeakList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,6 +41,7 @@ import java.util.List; public class DetectableIndentOptionsProvider extends FileIndentOptionsProvider { private boolean myIsEnabledInTest; private final List myAcceptedFiles = new WeakList(); + private final List myDisabledFiles = new WeakList(); @Nullable @Override @@ -60,7 +64,7 @@ public class DetectableIndentOptionsProvider extends FileIndentOptionsProvider { return myIsEnabledInTest; } VirtualFile vFile = file.getVirtualFile(); - if (vFile == null || vFile instanceof LightVirtualFile) return false; + if (vFile == null || vFile instanceof LightVirtualFile || myDisabledFiles.contains(vFile)) return false; return settings.AUTODETECT_INDENTS; } @@ -72,21 +76,83 @@ public class DetectableIndentOptionsProvider extends FileIndentOptionsProvider { @Nullable @Override - public String getDisplayName() { - return ApplicationBundle.message("code.style.indents.detector.display.name"); + public EditorNotificationInfo getNotificationInfo(@NotNull final Project project, + @NotNull final VirtualFile file, + @NotNull final FileEditor fileEditor, + @NotNull CommonCodeStyleSettings.IndentOptions userOptions, + @NotNull CommonCodeStyleSettings.IndentOptions detectedOptions) + { + NotificationLabels labels = getNotificationLabels(userOptions, detectedOptions); + final Editor editor = fileEditor instanceof TextEditor ? ((TextEditor)fileEditor).getEditor() : null; + if (labels == null || editor == null) return null; + + LabelWithAction okAction = new LabelWithAction( + ApplicationBundle.message("code.style.indents.detector.accept"), + new Runnable() { + @Override + public void run() { + setAccepted(file); + EditorNotifications.getInstance(project).updateAllNotifications(); + } + } + ); + + LabelWithAction disableForSingleFile = new LabelWithAction( + labels.revertToOldSettingsLabel, + new Runnable() { + @Override + public void run() { + disableForFile(file); + if (editor instanceof EditorEx) { + ((EditorEx)editor).reinitSettings(); + } + EditorNotifications.getInstance(project).updateAllNotifications(); + } + } + ); + + LabelWithAction showSettings = new LabelWithAction( + ApplicationBundle.message("code.style.indents.detector.show.settings"), + new Runnable() { + @Override + public void run() { + ShowSettingsUtilImpl.showSettingsDialog(project, "preferences.sourceCode", + ApplicationBundle.message("settings.code.style.general.autodetect.indents")); + EditorNotifications.getInstance(project).updateAllNotifications(); + } + } + ); + + return new EditorNotificationInfo(labels.title, okAction, disableForSingleFile, showSettings); } - @Override - public boolean canBeDisabled() { - return true; + @Nullable + private static NotificationLabels getNotificationLabels(@NotNull CommonCodeStyleSettings.IndentOptions userOptions, + @NotNull CommonCodeStyleSettings.IndentOptions detectedOptions) { + if (userOptions.USE_TAB_CHARACTER) { + if (!detectedOptions.USE_TAB_CHARACTER) { + return new NotificationLabels(ApplicationBundle.message("code.style.space.indent.detected", detectedOptions.INDENT_SIZE), + ApplicationBundle.message("code.style.detector.use.tabs")); + } + } + else { + String restoreToSpaces = ApplicationBundle.message("code.style.detector.use.spaces", userOptions.INDENT_SIZE); + if (detectedOptions.USE_TAB_CHARACTER) { + return new NotificationLabels(ApplicationBundle.message("code.style.tab.usage.detected", userOptions.INDENT_SIZE), + restoreToSpaces); + } + if (userOptions.INDENT_SIZE != detectedOptions.INDENT_SIZE) { + return new NotificationLabels(ApplicationBundle.message("code.style.different.indent.size.detected", detectedOptions.INDENT_SIZE, userOptions.INDENT_SIZE), + restoreToSpaces); + } + } + return null; } - @Override - public void disable(@NotNull Project project) { - CodeStyleSettingsManager.getSettings(project).AUTODETECT_INDENTS = false; - myAcceptedFiles.clear(); + private void disableForFile(@NotNull VirtualFile file) { + myDisabledFiles.add(file); } - + @Override public void setAccepted(@NotNull VirtualFile file) { myAcceptedFiles.add(file); @@ -96,4 +162,14 @@ public class DetectableIndentOptionsProvider extends FileIndentOptionsProvider { public boolean isAcceptedWithoutWarning(@NotNull VirtualFile file) { return myAcceptedFiles.contains(file); } + + private static class NotificationLabels { + public final String title; + public final String revertToOldSettingsLabel; + + public NotificationLabels(@NotNull String title, @NotNull String revertToOldSettingsLabel) { + this.title = title; + this.revertToOldSettingsLabel = revertToOldSettingsLabel; + } + } } diff --git a/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectedIndentOptionsNotificationProvider.java b/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectedIndentOptionsNotificationProvider.java index 81b0485d5881..b0be0cee284f 100644 --- a/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectedIndentOptionsNotificationProvider.java +++ b/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectedIndentOptionsNotificationProvider.java @@ -15,10 +15,8 @@ */ package com.intellij.psi.codeStyle.autodetect; -import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.project.Project; @@ -27,10 +25,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CodeStyleSettingsManager; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings; -import com.intellij.psi.codeStyle.FileIndentOptionsProvider; +import com.intellij.psi.codeStyle.*; import com.intellij.ui.EditorNotificationPanel; import com.intellij.ui.EditorNotifications; import com.intellij.util.Processor; @@ -73,41 +68,17 @@ public class DetectedIndentOptionsNotificationProvider extends EditorNotificatio } }); final FileIndentOptionsProvider provider = indentOptionsProviderRef.get(); - if (provider != null && - !provider.isAcceptedWithoutWarning(file) && - provider.getDisplayName() != null && - !userOptions.equals(detectedOptions)) { - final EditorNotificationPanel panel = - new EditorNotificationPanel() - .text(ApplicationBundle.message("code.style.indents.detector.message", provider.getDisplayName(), - getOptionDiffInfoString(userOptions, detectedOptions))); - if (provider.getIcon() != null) { - panel.icon(provider.getIcon()); + EditorNotificationInfo info = provider != null && !provider.isAcceptedWithoutWarning(file) && !userOptions.equals(detectedOptions) + ? provider.getNotificationInfo(project, file, fileEditor, userOptions, detectedOptions) + : null; + + if (info != null) { + EditorNotificationPanel panel = new EditorNotificationPanel().text(info.getTitle()); + if (info.getIcon() != null) { + panel.icon(info.getIcon()); } - panel.createActionLabel( - ApplicationBundle.message("code.style.indents.detector.accept"), - new Runnable() { - @Override - public void run() { - provider.setAccepted(file); - EditorNotifications.getInstance(project).updateAllNotifications(); - } - } - ); - if (provider.canBeDisabled()) { - panel.createActionLabel( - ApplicationBundle.message("code.style.indents.detector.disable"), - new Runnable() { - @Override - public void run() { - provider.disable(project); - if (editor instanceof EditorEx) { - ((EditorEx)editor).reinitSettings(); - } - EditorNotifications.getInstance(project).updateAllNotifications(); - } - } - ); + for (LabelWithAction action : info.getLabelAndActions()) { + panel.createActionLabel(action.label, action.action); } return panel; } @@ -116,26 +87,4 @@ public class DetectedIndentOptionsNotificationProvider extends EditorNotificatio } return null; } - - @NotNull - private static String getOptionDiffInfoString(CommonCodeStyleSettings.IndentOptions user, - CommonCodeStyleSettings.IndentOptions detected) { - StringBuilder sb = new StringBuilder(); - if (user.INDENT_SIZE != detected.INDENT_SIZE) { - sb.append("indent size=").append(detected.INDENT_SIZE); - } - if (user.TAB_SIZE != detected.TAB_SIZE) { - if (sb.length() > 0) sb.append(", "); - sb.append("tab size=").append(detected.TAB_SIZE); - } - if (user.USE_TAB_CHARACTER != detected.USE_TAB_CHARACTER) { - if (sb.length() > 0) sb.append(", "); - sb.append(detected.USE_TAB_CHARACTER ? "tabs" : "no tabs"); - } - if (user.SMART_TABS != detected.SMART_TABS) { - if (sb.length() > 0) sb.append(", "); - sb.append(detected.SMART_TABS ? "smart tabs" : "no smart tabs"); - } - return sb.toString(); - } } diff --git a/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java index 158550e8a397..c16bae772c01 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java @@ -161,7 +161,7 @@ public abstract class BaseRefactoringProcessor implements Runnable { @Override public void run() { try { - refUsages.set(ApplicationManager.getApplication().runReadAction(new Computable() { + refUsages.set(DumbService.getInstance(myProject).runReadActionInSmartMode(new Computable() { @Override public UsageInfo[] compute() { return findUsages(); diff --git a/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/AbstractInplaceIntroducer.java b/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/AbstractInplaceIntroducer.java index ea0b8bad56e0..b0b6f8da971a 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/AbstractInplaceIntroducer.java +++ b/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/AbstractInplaceIntroducer.java @@ -531,7 +531,16 @@ public abstract class AbstractInplaceIntroducer 0, "TextWidth: "+textImageWidth+"; startLine:" + startLine + "; endLine:" + endLine + ";"); - final FoldingModelEx foldingModel = editor.getFoldingModel(); - final boolean isFoldingEnabled = foldingModel.isFoldingEnabled(); + FoldingModelEx foldingModel = editor.getFoldingModel(); + boolean isFoldingEnabled = foldingModel.isFoldingEnabled(); if (!showFolding) { foldingModel.setFoldingEnabled(false); } Point p1 = editor.logicalPositionToXY(new LogicalPosition(startLine, 0)); Point p2 = editor.logicalPositionToXY(new LogicalPosition(Math.max(endLine, startLine + 1), 0)); - final int y1 = p1.y; + int y1 = p1.y; int y2 = p2.y; final int textImageHeight = y2 - y1 == 0 ? editor.getLineHeight() : y2 - y1; LOG.assertTrue(textImageHeight > 0, "Height: " + textImageHeight + "; startLine:" + startLine + "; endLine:" + endLine + "; p1:" + p1 + "; p2:" + p2); - final int savedScrollOffset = editor.getScrollingModel().getHorizontalScrollOffset(); + int savedScrollOffset = editor.getScrollingModel().getHorizontalScrollOffset(); if (savedScrollOffset > 0) { editor.getScrollingModel().scrollHorizontally(0); } + final BufferedImage textImage = UIUtil.createImage(textImageWidth, textImageHeight, BufferedImage.TYPE_INT_RGB); + Graphics textGraphics = textImage.getGraphics(); + UISettings.setupAntialiasing(textGraphics); + + final JComponent rowHeader; + final BufferedImage markersImage; + final int markersImageWidth; + + if (showGutter) { + rowHeader = editor.getGutterComponentEx(); + markersImageWidth = Math.max(1, rowHeader.getWidth()); + + markersImage = UIUtil.createImage(markersImageWidth, textImageHeight, BufferedImage.TYPE_INT_RGB); + Graphics markerGraphics = markersImage.getGraphics(); + UISettings.setupAntialiasing(markerGraphics); + + markerGraphics.translate(0, -y1); + markerGraphics.setClip(0, y1, rowHeader.getWidth(), textImageHeight); + markerGraphics.setColor(getBackgroundColor(editor)); + markerGraphics.fillRect(0, y1, rowHeader.getWidth(), textImageHeight); + rowHeader.paint(markerGraphics); + } + else { + markersImageWidth = 0; + rowHeader = null; + markersImage = null; + } + + textGraphics.translate(0, -y1); + textGraphics.setClip(0, y1, textImageWidth, textImageHeight); + final boolean wasVisible = editor.setCaretVisible(false); + editor.getContentComponent().paint(textGraphics); + if (wasVisible) { + editor.setCaretVisible(true); + } + + if (!showFolding) { + foldingModel.setFoldingEnabled(isFoldingEnabled); + } + + if (savedScrollOffset > 0) { + editor.getScrollingModel().scrollHorizontally(savedScrollOffset); + } + JComponent component = new JComponent() { - - private Graphics2D componentGraphics; - private JComponent rowHeader; - private BufferedImage markersImage; - private int markersImageWidth; - private BufferedImage textImage; - - private void updateImages() { - - if ((getGraphics()).equals(componentGraphics)) { - return; - } - - componentGraphics = (Graphics2D)getGraphics(); - - textImage = UIUtil.createImageForGraphics(componentGraphics, - textImageWidth, textImageHeight, BufferedImage.TYPE_INT_RGB); - Graphics textGraphics = textImage.getGraphics(); - UISettings.setupAntialiasing(textGraphics); - - if (showGutter) { - rowHeader = editor.getGutterComponentEx(); - markersImageWidth = Math.max(1, rowHeader.getWidth()); - - markersImage = UIUtil.createImageForGraphics(componentGraphics, markersImageWidth, textImageHeight, BufferedImage.TYPE_INT_RGB); - Graphics markerGraphics = markersImage.getGraphics(); - UISettings.setupAntialiasing(markerGraphics); - - markerGraphics.translate(0, -y1); - markerGraphics.setClip(0, y1, rowHeader.getWidth(), textImageHeight); - markerGraphics.setColor(getBackgroundColor(editor)); - markerGraphics.fillRect(0, y1, rowHeader.getWidth(), textImageHeight); - rowHeader.paint(markerGraphics); - } - else { - markersImageWidth = 0; - rowHeader = null; - markersImage = null; - } - - textGraphics.translate(0, -y1); - textGraphics.setClip(0, y1, textImageWidth, textImageHeight); - final boolean wasVisible = editor.setCaretVisible(false); - editor.getContentComponent().paint(textGraphics); - if (wasVisible) { - editor.setCaretVisible(true); - } - - if (!showFolding) { - foldingModel.setFoldingEnabled(isFoldingEnabled); - } - - if (savedScrollOffset > 0) { - editor.getScrollingModel().scrollHorizontally(savedScrollOffset); - } - } - @Override public Dimension getPreferredSize() { return new Dimension(textImageWidth + markersImageWidth, textImageHeight); } - @Override - public void addNotify() { - super.addNotify(); - updateImages(); - } - @Override protected void paintComponent(Graphics graphics) { if (markersImage != null) { diff --git a/platform/platform-impl/src/com/intellij/execution/process/RunnerMediator.java b/platform/platform-impl/src/com/intellij/execution/process/RunnerMediator.java index 8b177279e786..649835c3a544 100644 --- a/platform/platform-impl/src/com/intellij/execution/process/RunnerMediator.java +++ b/platform/platform-impl/src/com/intellij/execution/process/RunnerMediator.java @@ -31,12 +31,13 @@ import java.io.PrintWriter; * @author traff */ public class RunnerMediator { - public static final Logger LOG = Logger.getInstance("#com.intellij.execution.process.RunnerMediator"); + private static final Logger LOG = Logger.getInstance("#com.intellij.execution.process.RunnerMediator"); private static final char IAC = (char)5; private static final char BRK = (char)3; private static final char C = (char)5; private static final String STANDARD_RUNNERW = "runnerw.exe"; + private static final String IDEA_RUNNERW = "IDEA_RUNNERW"; /** * Creates default runner mediator @@ -79,28 +80,32 @@ public class RunnerMediator { @Nullable private static String getRunnerPath() { - if (SystemInfo.isWindows) { - final String path = System.getenv("IDEA_RUNNERW"); - if (path != null && new File(path).exists()) { - return path; - } - File runnerw = new File(PathManager.getBinPath(), STANDARD_RUNNERW); - if (runnerw.exists()) { - return runnerw.getPath(); - } - return null; - } - else { + if (!SystemInfo.isWindows) { throw new IllegalStateException("There is no need of runner under unix based OS"); } + final String path = System.getenv(IDEA_RUNNERW); + if (path != null) { + if (new File(path).exists()) { + return path; + } + LOG.warn("Cannot locate " + STANDARD_RUNNERW + " by " + IDEA_RUNNERW + " environment variable (" + path + ")"); + } + File runnerw = new File(PathManager.getBinPath(), STANDARD_RUNNERW); + if (runnerw.exists()) { + return runnerw.getPath(); + } + LOG.warn("Cannot locate " + STANDARD_RUNNERW + " by default path (" + runnerw.getAbsolutePath() + ")"); + return null; } - static void injectRunnerCommand(@NotNull GeneralCommandLine commandLine) { + static boolean injectRunnerCommand(@NotNull GeneralCommandLine commandLine) { final String path = getRunnerPath(); if (path != null) { commandLine.getParametersList().addAt(0, commandLine.getExePath()); commandLine.setExePath(path); + return true; } + return false; } /** diff --git a/platform/platform-impl/src/com/intellij/execution/process/RunnerWinProcess.java b/platform/platform-impl/src/com/intellij/execution/process/RunnerWinProcess.java index 8e142c45c1a4..833591c75ce0 100644 --- a/platform/platform-impl/src/com/intellij/execution/process/RunnerWinProcess.java +++ b/platform/platform-impl/src/com/intellij/execution/process/RunnerWinProcess.java @@ -35,13 +35,12 @@ public class RunnerWinProcess extends ProcessWrapper { } @NotNull - public static RunnerWinProcess create(@NotNull GeneralCommandLine commandLine) throws ExecutionException { + public static Process create(@NotNull GeneralCommandLine commandLine) throws ExecutionException { if (!SystemInfo.isWindows) { throw new RuntimeException(RunnerWinProcess.class.getSimpleName() + " works on Windows only!"); } - RunnerMediator.injectRunnerCommand(commandLine); + boolean success = RunnerMediator.injectRunnerCommand(commandLine); Process process = commandLine.createProcess(); - return new RunnerWinProcess(process); + return success ? new RunnerWinProcess(process) : process; } - } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/actions/MergeOperations.java b/platform/platform-impl/src/com/intellij/openapi/diff/actions/MergeOperations.java index 2d41d5ab8eb7..1f7072d09a32 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/actions/MergeOperations.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/actions/MergeOperations.java @@ -131,7 +131,7 @@ public class MergeOperations { private static Operation insertOperation(TextRange range, int offset, Document document, Document otherDocument) { return new Operation(DiffBundle.message("merge.editor.insert.operation.name"), - AllIcons.Diff.Arrow, + AllIcons.Diff.ArrowRightDown, otherDocument, insertModification(range, document, offset, otherDocument)); } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java index 82b1029c0452..aeb362bf68fe 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java @@ -149,6 +149,10 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable { LOG.assertTrue(!myAdditionTools.contains(tool)); } + public ArrayList getAdditionTools() { + return myAdditionTools; + } + @Override public MarkupEditorFilter getDiffEditorFilter() { return DIFF_EDITOR_FILTER; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHeaderComponent.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHeaderComponent.java index 4686b7caa652..b50b7af5489b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHeaderComponent.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHeaderComponent.java @@ -17,10 +17,8 @@ package com.intellij.openapi.editor.impl; import com.intellij.ide.ui.UISettings; import com.intellij.openapi.util.SystemInfo; -import com.intellij.ui.Gray; import com.intellij.ui.JBColor; import com.intellij.ui.border.CustomLineBorder; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -44,24 +42,4 @@ public class EditorHeaderComponent extends JPanel { g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); super.paint(g); } - - @Override - protected void paintComponent(@NotNull Graphics g) { - super.paintComponent(g); - if (UIUtil.isUnderGTKLookAndFeel()) return; - - paintGradient(g, this); - } - - private void paintGradient(Graphics g, JComponent c) { - Color GRADIENT_C1 = isBackgroundSet() ? getBackground() : new JBColor(getBackground(), JBColor.background()); - Color GRADIENT_C2 = new JBColor(new Color(Math.max(0, GRADIENT_C1.getRed() - 0x18), Math.max(0, GRADIENT_C1.getGreen() - 0x18), - Math.max(0, GRADIENT_C1.getBlue() - 0x18)), Gray._75); - - Graphics2D g2d = (Graphics2D)g; - - g2d.setPaint(UIUtil.getGradientPaint(0, 0, GRADIENT_C1, 0, c.getHeight(), GRADIENT_C2)); - g2d.fillRect(0, 0, c.getWidth(), c.getHeight() - 1); - g2d.setPaint(null); - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.java index 2f6c6dfd2ec5..b9a61567620d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.java @@ -24,6 +24,7 @@ import com.intellij.openapi.keymap.ex.KeymapManagerEx; import com.intellij.openapi.options.BaseSchemeProcessor; import com.intellij.openapi.options.SchemesManager; import com.intellij.openapi.options.SchemesManagerFactory; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; @@ -154,7 +155,13 @@ public class KeymapManagerImpl extends KeymapManagerEx implements PersistentStat @Override public String getActionBinding(String actionId) { - return myBoundShortcuts.get(actionId); + Set visited = null; + String id = actionId, next; + while ((next = myBoundShortcuts.get(id)) != null) { + if (visited == null) visited = ContainerUtil.newHashSet(); + if (!visited.add(id = next)) break; + } + return Comparing.equal(id, actionId) ? null : id; } @Override diff --git a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java index c5e94cbf32d3..fcb584bf709f 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java @@ -703,6 +703,7 @@ public class AbstractPopup implements JBPopup { } assert ApplicationManager.getApplication().isDispatchThread(); + assert myState == State.INIT : "Popup was already shown. Recreate a new instance to show again."; debugState("show popup", State.INIT); myState = State.SHOWING; @@ -1348,9 +1349,10 @@ public class AbstractPopup implements JBPopup { Runnable runFinal = new Runnable() { @Override public void run() { + //noinspection SSBasedInspection + SwingUtilities.invokeLater(myFinalRunnable); //noinspection SSBasedInspection SwingUtilities.invokeLater(typeAheadDone.createSetDoneRunnable()); - myFinalRunnable.run(); myFinalRunnable = null; } }; diff --git a/platform/platform-impl/src/org/jetbrains/io/NettyUtil.java b/platform/platform-impl/src/org/jetbrains/io/NettyUtil.java index 4515a50efa48..68c4872a5925 100644 --- a/platform/platform-impl/src/org/jetbrains/io/NettyUtil.java +++ b/platform/platform-impl/src/org/jetbrains/io/NettyUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.jetbrains.io; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.ActionCallback; import com.intellij.util.SystemProperties; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.BootstrapUtil; @@ -34,6 +33,8 @@ import io.netty.handler.codec.http.cors.CorsConfig; import io.netty.handler.codec.http.cors.CorsHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.concurrency.AsyncPromise; +import org.jetbrains.concurrency.Promise; import org.jetbrains.ide.PooledThreadExecutor; import java.io.IOException; @@ -43,8 +44,6 @@ import java.net.Socket; import java.util.Random; public final class NettyUtil { - private static final Logger LOG = Logger.getInstance(NettyUtil.class); - public static final int MAX_CONTENT_LENGTH = 100 * 1024 * 1024; public static final int DEFAULT_CONNECT_ATTEMPT_COUNT = 20; @@ -68,72 +67,86 @@ public final class NettyUtil { } @Nullable - public static Channel connectClient(@NotNull Bootstrap bootstrap, @NotNull InetSocketAddress remoteAddress, @Nullable ActionCallback asyncResult) { - return connect(bootstrap, remoteAddress, asyncResult, DEFAULT_CONNECT_ATTEMPT_COUNT); + public static Channel connect(@NotNull Bootstrap bootstrap, @NotNull InetSocketAddress remoteAddress, @Nullable AsyncPromise promise) { + return connect(bootstrap, remoteAddress, promise, DEFAULT_CONNECT_ATTEMPT_COUNT); } @Nullable - public static Channel connect(@NotNull Bootstrap bootstrap, @NotNull InetSocketAddress remoteAddress, @Nullable ActionCallback promise, int maxAttemptCount) { + public static Channel connect(@NotNull Bootstrap bootstrap, @NotNull InetSocketAddress remoteAddress, @Nullable AsyncPromise promise, int maxAttemptCount) { try { - int attemptCount = 0; - - if (bootstrap.group() instanceof NioEventLoopGroup) { - while (true) { - ChannelFuture future = bootstrap.connect(remoteAddress).awaitUninterruptibly(); - if (future.isSuccess()) { - return future.channel(); - } - else if (++attemptCount < maxAttemptCount) { - //noinspection BusyWait - Thread.sleep(attemptCount * MIN_START_TIME); - } - else { - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - Throwable cause = future.cause(); - if (promise != null) { - if (cause != null && cause.getMessage() == null) { - LOG.warn(cause); - } - promise.reject("Cannot connect: " + (cause == null ? "unknown error" : cause.getMessage())); - } - return null; - } - } - } - - Socket socket; - while (true) { - try { - //noinspection SocketOpenedButNotSafelyClosed - socket = new Socket(remoteAddress.getAddress(), remoteAddress.getPort()); - break; - } - catch (IOException e) { - if (++attemptCount < maxAttemptCount) { - //noinspection BusyWait - Thread.sleep(attemptCount * MIN_START_TIME); - } - else { - if (promise != null) { - promise.reject("Cannot connect: " + e.getMessage()); - } - return null; - } - } - } - - OioSocketChannel channel = new OioSocketChannel(socket); - BootstrapUtil.initAndRegister(channel, bootstrap).awaitUninterruptibly(); - return channel; + return doConnect(bootstrap, remoteAddress, promise, maxAttemptCount); } catch (Throwable e) { if (promise != null) { - promise.reject("Cannot connect: " + e.getMessage()); + promise.setError(e); } return null; } } + @Nullable + private static Channel doConnect(@NotNull Bootstrap bootstrap, @NotNull InetSocketAddress remoteAddress, @Nullable AsyncPromise promise, int maxAttemptCount) + throws Throwable { + int attemptCount = 0; + + if (bootstrap.group() instanceof NioEventLoopGroup) { + while (true) { + ChannelFuture future = bootstrap.connect(remoteAddress).awaitUninterruptibly(); + if (future.isSuccess()) { + return future.channel(); + } + else if (maxAttemptCount == -1) { + //noinspection BusyWait + Thread.sleep(300); + attemptCount++; + } + else if (++attemptCount < maxAttemptCount) { + //noinspection BusyWait + Thread.sleep(attemptCount * MIN_START_TIME); + } + else { + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") + Throwable cause = future.cause(); + if (promise != null) { + //noinspection ThrowableResultOfMethodCallIgnored + promise.setError(cause == null ? Promise.createError("Cannot connect: unknown error") : cause); + } + return null; + } + } + } + + Socket socket; + while (true) { + try { + //noinspection SocketOpenedButNotSafelyClosed + socket = new Socket(remoteAddress.getAddress(), remoteAddress.getPort()); + break; + } + catch (IOException e) { + if (maxAttemptCount == -1) { + //noinspection BusyWait + Thread.sleep(300); + attemptCount++; + } + else if (++attemptCount < maxAttemptCount) { + //noinspection BusyWait + Thread.sleep(attemptCount * MIN_START_TIME); + } + else { + if (promise != null) { + promise.setError(e); + } + return null; + } + } + } + + OioSocketChannel channel = new OioSocketChannel(socket); + BootstrapUtil.initAndRegister(channel, bootstrap).awaitUninterruptibly(); + return channel; + } + private static boolean isAsWarning(Throwable throwable) { String message = throwable.getMessage(); if (message == null) { diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index e74333c58c50..df4aa168e8fa 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -575,6 +575,10 @@ action.ShowModulesDependencies.text=Analyze _Module Dependencies... action.ShowModulesDependencies.description=Show dependencies between modules in project action.Unscramble.text=Analyze _Stacktrace... action.Unscramble.description=Open console with the navigatable stacktrace +action.IdeScriptingConsole.text=IDE Scripting Console +action.IdeScriptingConsole.description=Open IDE scripting console +action.NewScratchFile.text=New Scratch File... +action.NewScratchFile.description=Creates new scratch file with chosen language group.RefactoringMenu.text=_Refactor action.RenameElement.text=_Rename... action.RenameElement.description=Rename the selected symbol and correct all references diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index fc6522fcb747..57275fd67331 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -165,7 +165,8 @@ wrapping.brace.placement.next.line.each.shifted=Next line, each shifted wrapping.align.when.multiline=Align when multiline wrapping.align.parenthesised.when.multiline=Align parenthesised when multiline -wrapping.align.in.columns=Align in columns +wrapping.align.fields.in.columns=Align fields in columns +wrapping.align.variables.in.columns=Align variables in columns wrapping.else.on.new.line='else' on new line wrapping.while.on.new.line='while' on new line @@ -226,7 +227,7 @@ checkbox.align.multiline.method.parameters=Method parameters checkbox.align.multiline.call.arguments=Call arguments checkbox.align.multiline.extends.list=Extends list checkbox.align.multiline.throws.list=Throws list -checkbox.align.multiline.fields.groups=Field groups +checkbox.align.multiline.fields.groups=Group declarations checkbox.align.multiline.parenthesized.expression=Parenthesized expression checkbox.align.multiline.binary.operation=Binary operation checkbox.align.multiline.ternary.operation=Ternary operation @@ -685,7 +686,12 @@ reformat.changed.text.file.too.big.notification.text=file {0} is too big or ther bulk.reformat.prepare.progress.text=Preparing... bulk.reformat.process.progress.text=Processing... -code.style.indents.detector.display.name=Automatic indent options detector -code.style.indents.detector.message=Indent options for the file are overwritten by {0} ({1}) -code.style.indents.detector.accept=OK -code.style.indents.detector.disable=Disable for all files \ No newline at end of file +code.style.different.indent.size.detected=This file is indented with {0} spaces instead of {1} +code.style.tab.usage.detected=This file is indented with tabs instead of {0} spaces +code.style.space.indent.detected=This file is indented with {0} spaces instead of the tab character + +code.style.detector.use.spaces=Indent with {0} spaces +code.style.detector.use.tabs=Indent with tab + +code.style.indents.detector.show.settings=Show Settings +code.style.indents.detector.accept=OK \ No newline at end of file diff --git a/platform/platform-resources-en/src/messages/OptionsBundle.properties b/platform/platform-resources-en/src/messages/OptionsBundle.properties index 53085b78104e..761cb2400667 100644 --- a/platform/platform-resources-en/src/messages/OptionsBundle.properties +++ b/platform/platform-resources-en/src/messages/OptionsBundle.properties @@ -209,7 +209,7 @@ settings.panel.title=Settings configurable.group.root.settings.display.name=ROOT GROUP configurable.group.appearance.settings.display.name=Appearance \\& Behavior configurable.group.appearance.settings.description=\ - Personalize IntelliJ appearance and behavior: change themes and font size, tune the keymap,\ + Personalize IDE appearance and behavior: change themes and font size, tune the keymap,\ configure plugins and system settings, such as password policies, HTTP proxy, updates and more. configurable.group.editor.settings.display.name=Editor configurable.group.editor.settings.description=\ diff --git a/platform/platform-resources-en/src/messages/RefactoringBundle.properties b/platform/platform-resources-en/src/messages/RefactoringBundle.properties index 2b63d9ab4638..b3e85fae875f 100644 --- a/platform/platform-resources-en/src/messages/RefactoringBundle.properties +++ b/platform/platform-resources-en/src/messages/RefactoringBundle.properties @@ -71,6 +71,7 @@ visibility.protected=Pr&otected visibility.private=Pri&vate visibility.package.local=Pac&kage local visibility.border.title=Visibility +default.visibility.border.title=Default Visibility visibility.combo.title=&Visibility: moveInstanceMethod.select.an.instance.parameter=Select an &instance parameter: destination.package=Destination &package: diff --git a/platform/platform-resources/src/idea/Keymap_Eclipse.xml b/platform/platform-resources/src/idea/Keymap_Eclipse.xml index e3570998c8a6..7a4459867a97 100644 --- a/platform/platform-resources/src/idea/Keymap_Eclipse.xml +++ b/platform/platform-resources/src/idea/Keymap_Eclipse.xml @@ -47,7 +47,7 @@ - + diff --git a/platform/platform-resources/src/idea/Keymap_EclipseMac.xml b/platform/platform-resources/src/idea/Keymap_EclipseMac.xml index 8ac008b05436..7cf1977d5db3 100644 --- a/platform/platform-resources/src/idea/Keymap_EclipseMac.xml +++ b/platform/platform-resources/src/idea/Keymap_EclipseMac.xml @@ -43,7 +43,7 @@ - + diff --git a/platform/platform-resources/src/idea/Keymap_Netbeans.xml b/platform/platform-resources/src/idea/Keymap_Netbeans.xml index fc4c8addf0ae..eb406143e64e 100644 --- a/platform/platform-resources/src/idea/Keymap_Netbeans.xml +++ b/platform/platform-resources/src/idea/Keymap_Netbeans.xml @@ -83,7 +83,7 @@ - + diff --git a/platform/platform-resources/src/idea/LangActions.xml b/platform/platform-resources/src/idea/LangActions.xml index eb649da603af..9db13bc456b1 100644 --- a/platform/platform-resources/src/idea/LangActions.xml +++ b/platform/platform-resources/src/idea/LangActions.xml @@ -342,7 +342,7 @@ - + diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceMapDecoder.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceMapDecoder.java index 2e7a982ee1d8..c3ff56116ed1 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceMapDecoder.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceMapDecoder.java @@ -49,9 +49,10 @@ public final class SourceMapDecoder { public interface SourceResolverFactory { @NotNull - SourceResolver create(@NotNull List sourcesUrl, @Nullable List sourcesContent); + SourceResolver create(@NotNull List sourceUrls, @Nullable List sourceContents); } + @Nullable public static SourceMap decode(@NotNull String contents, @NotNull SourceResolverFactory sourceResolverFactory) throws IOException { if (contents.isEmpty()) { throw new IOException("source map contents cannot be empty"); diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceResolver.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceResolver.java index 665039507ace..240ea8288562 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceResolver.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceResolver.java @@ -21,7 +21,7 @@ import java.util.List; public class SourceResolver { private final List rawSources; - @Nullable private final List sourcesContent; + @Nullable private final List sourceContents; final Url[] canonicalizedSources; private final ObjectIntHashMap canonicalizedSourcesMap; @@ -30,15 +30,15 @@ public class SourceResolver { // absoluteLocalPathToSourceIndex contains canonical paths too, but this map contains only used (specified in the source map) path private String[] sourceIndexToAbsoluteLocalPath; - public SourceResolver(@NotNull List sourcesUrl, boolean trimFileScheme, @Nullable Url baseFileUrl, @Nullable List sourcesContent) { - rawSources = sourcesUrl; - this.sourcesContent = sourcesContent; - canonicalizedSources = new Url[sourcesUrl.size()]; + public SourceResolver(@NotNull List sourceUrls, boolean trimFileScheme, @Nullable Url baseFileUrl, @Nullable List sourceContents) { + rawSources = sourceUrls; + this.sourceContents = sourceContents; + canonicalizedSources = new Url[sourceUrls.size()]; canonicalizedSourcesMap = SystemInfo.isFileSystemCaseSensitive ? new ObjectIntHashMap(canonicalizedSources.length) : new ObjectIntHashMap(canonicalizedSources.length, Urls.getCaseInsensitiveUrlHashingStrategy()); - for (int i = 0; i < sourcesUrl.size(); i++) { - String rawSource = sourcesUrl.get(i); + for (int i = 0; i < sourceUrls.size(); i++) { + String rawSource = sourceUrls.get(i); Url url = canonicalizeUrl(rawSource, baseFileUrl, trimFileScheme, i); canonicalizedSources[i] = url; canonicalizedSourcesMap.put(url, i); @@ -50,7 +50,7 @@ public class SourceResolver { } // see canonicalizeUri kotlin impl and https://trac.webkit.org/browser/trunk/Source/WebCore/inspector/front-end/ParsedURL.js completeURL - private Url canonicalizeUrl(@NotNull String url, @Nullable Url baseUrl, boolean trimFileScheme, int sourceIndex) { + protected Url canonicalizeUrl(@NotNull String url, @Nullable Url baseUrl, boolean trimFileScheme, int sourceIndex) { if (trimFileScheme && url.startsWith(StandardFileSystems.FILE_PROTOCOL_PREFIX)) { return Urls.newLocalFileUrl(FileUtil.toCanonicalPath(VfsUtilCore.toIdeaUrl(url, true).substring(StandardFileSystems.FILE_PROTOCOL_PREFIX.length()), '/')); } @@ -115,12 +115,12 @@ public class SourceResolver { @Nullable public String getSourceContent(@NotNull MappingEntry entry) { - if (ContainerUtil.isEmpty(sourcesContent)) { + if (ContainerUtil.isEmpty(sourceContents)) { return null; } int index = entry.getSource(); - return index < 0 || index >= sourcesContent.size() ? null : sourcesContent.get(index); + return index < 0 || index >= sourceContents.size() ? null : sourceContents.get(index); } @Nullable diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ValueManager.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ValueManager.java index ca04171af5eb..b5eb802e8dec 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ValueManager.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ValueManager.java @@ -16,6 +16,9 @@ import java.util.concurrent.atomic.AtomicInteger; * Currently WIP implementation doesn't keep such map due to protocol issue. But V8 does. */ public abstract class ValueManager { + public static final RuntimeException OBSOLETE_CONTEXT_ERROR = Promise.createError("Obsolete context"); + public static final Promise OBSOLETE_CONTEXT_PROMISE = Promise.reject(OBSOLETE_CONTEXT_ERROR); + private final AtomicInteger cacheStamp = new AtomicInteger(); private volatile boolean obsolete; @@ -61,7 +64,8 @@ public abstract class ValueManager { @NotNull public static Promise reject() { - return Promise.reject("Obsolete context"); + //noinspection unchecked + return (Promise)OBSOLETE_CONTEXT_PROMISE; } @NotNull diff --git a/platform/script-debugger/debugger-ui/src/com/jetbrains/javascript/debugger/FileUrlMapper.java b/platform/script-debugger/debugger-ui/src/com/jetbrains/javascript/debugger/FileUrlMapper.java index b079afcddcd1..952dd91a57a2 100644 --- a/platform/script-debugger/debugger-ui/src/com/jetbrains/javascript/debugger/FileUrlMapper.java +++ b/platform/script-debugger/debugger-ui/src/com/jetbrains/javascript/debugger/FileUrlMapper.java @@ -11,13 +11,16 @@ import com.intellij.util.containers.ObjectIntHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.List; public abstract class FileUrlMapper { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.jetbrains.fileUrlMapper"); @NotNull - public abstract List getUrls(@NotNull VirtualFile file, @NotNull Project project, @Nullable String currentAuthority); + public List getUrls(@NotNull VirtualFile file, @NotNull Project project, @Nullable String currentAuthority) { + return Collections.emptyList(); + } /** * Optional to implement, useful if default navigation position to source file is not equals to 0:0 (java file for example) diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/RejectErrorReporter.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/RejectErrorReporter.java index 8aaac1ef0c63..4e0e2feeed3b 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/RejectErrorReporter.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/RejectErrorReporter.java @@ -5,6 +5,7 @@ import com.intellij.xdebugger.XDebugSession; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.concurrency.Promise; +import org.jetbrains.debugger.values.ValueManager; import org.jetbrains.rpc.CommandProcessor; public final class RejectErrorReporter implements Consumer { @@ -25,6 +26,8 @@ public final class RejectErrorReporter implements Consumer { if (!(error instanceof Promise.MessageError)) { CommandProcessor.LOG.error(error); } - session.reportError((description == null ? "" : description + ": ") + error.getMessage()); + if (error != ValueManager.OBSOLETE_CONTEXT_ERROR) { + session.reportError((description == null ? "" : description + ": ") + error.getMessage()); + } } } \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/RemoteVmConnection.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/RemoteVmConnection.java index ccdd8aad0fb7..d1feb617949f 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/RemoteVmConnection.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/connection/RemoteVmConnection.java @@ -3,7 +3,6 @@ package org.jetbrains.debugger.connection; import com.intellij.ide.browsers.WebBrowser; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.util.ActionCallback; import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.components.JBList; import com.intellij.util.Consumer; @@ -35,9 +34,13 @@ public abstract class RemoteVmConnection extends VmConnection { } @NotNull - public abstract Bootstrap createBootstrap(@NotNull InetSocketAddress address, @NotNull AsyncPromise result); + public abstract Bootstrap createBootstrap(@NotNull InetSocketAddress address, @NotNull AsyncPromise promise); - public void open(@NotNull final InetSocketAddress address) { + public void open(@NotNull InetSocketAddress address) { + open(address, false); + } + + public void open(@NotNull final InetSocketAddress address, final boolean waitForever) { setState(ConnectionStatus.WAITING_FOR_CONNECTION, "Connecting to " + address.getHostName() + ":" + address.getPort()); final Future future = ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { @Override @@ -54,12 +57,12 @@ public abstract class RemoteVmConnection extends VmConnection { } }); - ActionCallback callback = new ActionCallback(); - NettyUtil.connectClient(createBootstrap(address, result), address, callback); - callback.doWhenRejected(new Consumer() { + AsyncPromise connectionPromise = new AsyncPromise(); + NettyUtil.connect(createBootstrap(address, result), address, connectionPromise, waitForever ? -1 : NettyUtil.DEFAULT_CONNECT_ATTEMPT_COUNT); + connectionPromise.rejected(new Consumer() { @Override - public void consume(String error) { - result.setError(Promise.createError(error)); + public void consume(Throwable error) { + result.setError(error); } }); diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java index 6398627faff1..23250d8fe2fe 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java @@ -1233,6 +1233,21 @@ public class StructuralSearchTest extends StructuralSearchTestCase { "find sym finds declaration", 2, findMatchesCount(s133_2, s134, true) ); + final String in = "class C {" + + " {" + + " int i = 0;" + + " i += 1;" + + " i = 3;" + + " int j = i;" + + " i();" + + " }" + + " void i() {}" + + "}"; + final String pattern1 = "'_:[read]"; + assertEquals("Find reads of symbol (including operator assignment)", 2, findMatchesCount(in, pattern1)); + + final String pattern2 = "'_:[write && regex( i )]"; + assertEquals("Find writes of symbol", 3, findMatchesCount(in, pattern2)); } public void testSearchGenerics() { @@ -1900,14 +1915,14 @@ public class StructuralSearchTest extends StructuralSearchTestCase { assertEquals( "fields of class read", - findMatchesCount(s117,s118_2), - 2 + 2, + findMatchesCount(s117,s118_2) ); assertEquals( "fields of class written", - findMatchesCount(s117,s118_3), - 2 + 2, + findMatchesCount(s117,s118_3) ); final String s119 = "try { a.b(); } catch(IOException e) { c(); } catch(Exception ex) { d(); }"; diff --git a/platform/testFramework/testSrc/com/intellij/openapi/keymap/KeymapsTestCase.java b/platform/testFramework/testSrc/com/intellij/openapi/keymap/KeymapsTestCase.java index 8546c74392fc..7a2b52530d58 100644 --- a/platform/testFramework/testSrc/com/intellij/openapi/keymap/KeymapsTestCase.java +++ b/platform/testFramework/testSrc/com/intellij/openapi/keymap/KeymapsTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -262,7 +262,7 @@ public abstract class KeymapsTestCase extends PlatformTestCase { { "shift control F6", "PreviousTab", "ChangeTypeSignature"}, { "shift control F11", "ToggleBookmark", "FocusTracer"}, { "shift control G", "FindUsagesInFile", "ClassTemplateNavigation", "GoToClass"}, - { "shift control I", "QuickImplementations", "Debugger.Inspect"}, + { "shift control I", "QuickImplementations", "XDebugger.Inspect"}, { "shift control LEFT", "EditorPreviousWordWithSelection", "ResizeToolWindowLeft", }, { "shift control RIGHT", "EditorNextWordWithSelection", "ResizeToolWindowRight", }, { "shift control UP", "ResizeToolWindowUp", "MethodUp"}, @@ -297,7 +297,7 @@ public abstract class KeymapsTestCase extends PlatformTestCase { { "shift alt F9", "ChooseDebugConfiguration", "ValidateXml", "ValidateJsp"}, { "shift control DOWN", "EditorDuplicate", "ResizeToolWindowDown", }, { "shift control ENTER", "EditorChooseLookupItemCompleteStatement", "EditorCompleteStatement", "Console.Jpa.GenerateSql"}, - { "shift control F7", "HighlightUsagesInFile", "Debugger.NewWatch"}, + { "shift control F7", "HighlightUsagesInFile", "XDebugger.NewWatch"}, { "shift control UP", "EditorDuplicate", "ResizeToolWindowUp", }, { "shift control alt P", "Print", "Graph.Print"}, { "shift control K", "HippieCompletion", "Vcs.Push"}, diff --git a/platform/util/src/com/intellij/icons/AllIcons.java b/platform/util/src/com/intellij/icons/AllIcons.java index 49b548c4971c..fde46ce43706 100644 --- a/platform/util/src/com/intellij/icons/AllIcons.java +++ b/platform/util/src/com/intellij/icons/AllIcons.java @@ -311,6 +311,9 @@ public class AllIcons { public static class Diff { public static final Icon ApplyNotConflicts = IconLoader.getIcon("/diff/applyNotConflicts.png"); // 16x16 public static final Icon Arrow = IconLoader.getIcon("/diff/arrow.png"); // 11x11 + public static final Icon ArrowLeftDown = IconLoader.getIcon("/diff/arrowLeftDown.png"); // 11x11 + public static final Icon ArrowRight = IconLoader.getIcon("/diff/arrowRight.png"); // 11x11 + public static final Icon ArrowRightDown = IconLoader.getIcon("/diff/arrowRightDown.png"); // 11x11 public static final Icon BranchDiff = IconLoader.getIcon("/diff/branchDiff.png"); // 16x16 public static final Icon CurrentLine = IconLoader.getIcon("/diff/currentLine.png"); // 16x16 public static final Icon Diff = IconLoader.getIcon("/diff/Diff.png"); // 16x16 diff --git a/platform/util/src/com/intellij/openapi/application/PathManager.java b/platform/util/src/com/intellij/openapi/application/PathManager.java index 5b855cb0137d..8b2e4eccd337 100644 --- a/platform/util/src/com/intellij/openapi/application/PathManager.java +++ b/platform/util/src/com/intellij/openapi/application/PathManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,39 +28,47 @@ import gnu.trove.THashSet; import org.apache.log4j.Appender; import org.apache.oro.text.regex.PatternMatcher; import org.jdom.Document; -import org.jetbrains.annotations.*; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.picocontainer.PicoContainer; import java.io.*; import java.net.URL; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static com.intellij.util.SystemProperties.getUserHome; public class PathManager { - @NonNls public static final String PROPERTIES_FILE = "idea.properties.file"; - @NonNls public static final String PROPERTY_SYSTEM_PATH = "idea.system.path"; - @NonNls public static final String PROPERTY_CONFIG_PATH = "idea.config.path"; - @NonNls public static final String PROPERTY_PLUGINS_PATH = "idea.plugins.path"; - @NonNls public static final String PROPERTY_HOME_PATH = "idea.home.path"; - @NonNls public static final String PROPERTY_LOG_PATH = "idea.log.path"; - @NonNls public static final String PROPERTY_PATHS_SELECTOR = "idea.paths.selector"; - @NonNls public static final String DEFAULT_OPTIONS_FILE_NAME = "other"; + public static final String PROPERTIES_FILE = "idea.properties.file"; + public static final String PROPERTY_HOME_PATH = "idea.home.path"; + public static final String PROPERTY_CONFIG_PATH = "idea.config.path"; + public static final String PROPERTY_SYSTEM_PATH = "idea.system.path"; + public static final String PROPERTY_PLUGINS_PATH = "idea.plugins.path"; + public static final String PROPERTY_LOG_PATH = "idea.log.path"; + public static final String PROPERTY_PATHS_SELECTOR = "idea.paths.selector"; + public static final String DEFAULT_OPTIONS_FILE_NAME = "other"; - @NonNls private static final String LIB_FOLDER = "lib"; - @NonNls private static final String PLUGINS_FOLDER = "plugins"; - @NonNls private static final String BIN_FOLDER = "bin"; - @NonNls private static final String LOG_DIRECTORY = "log"; - @NonNls private static final String CONFIG_FOLDER = "config"; - @NonNls private static final String OPTIONS_FOLDER = "options"; - @NonNls private static final String SYSTEM_FOLDER = "system"; - @NonNls private static final String PATHS_SELECTOR = System.getProperty(PROPERTY_PATHS_SELECTOR); + private static final String PROPERTY_HOME = "idea.home"; // reduced variant of PROPERTY_HOME_PATH, now deprecated - @NonNls private static String ourHomePath; - @NonNls private static String ourSystemPath; - @NonNls private static String ourConfigPath; - @NonNls private static String ourPluginsPath; - @NonNls private static String ourLogPath; + private static final String LIB_FOLDER = "lib"; + private static final String PLUGINS_FOLDER = "plugins"; + private static final String BIN_FOLDER = "bin"; + private static final String LOG_DIRECTORY = "log"; + private static final String CONFIG_FOLDER = "config"; + private static final String OPTIONS_FOLDER = "options"; + private static final String SYSTEM_FOLDER = "system"; + private static final String PATHS_SELECTOR = System.getProperty(PROPERTY_PATHS_SELECTOR); + + private static final Pattern PROPERTY_REF = Pattern.compile("\\$\\{(.+?)}"); + + private static String ourHomePath; + private static String ourConfigPath; + private static String ourSystemPath; + private static String ourPluginsPath; + private static String ourLogPath; // IDE installation paths @@ -68,7 +76,7 @@ public class PathManager { public static String getHomePath() { if (ourHomePath != null) return ourHomePath; - String fromProperty = System.getProperty(PROPERTY_HOME_PATH); + String fromProperty = System.getProperty(PROPERTY_HOME_PATH, System.getProperty(PROPERTY_HOME)); if (fromProperty != null) { ourHomePath = getAbsolutePath(fromProperty); if (!new File(ourHomePath).isDirectory()) { @@ -260,7 +268,7 @@ public class PathManager { * Attempts to detect classpath entry which contains given resource. */ @Nullable - public static String getResourceRoot(@NotNull Class context, @NonNls String path) { + public static String getResourceRoot(@NotNull Class context, String path) { URL url = context.getResource(path); if (url == null) { url = ClassLoader.getSystemResource(path.substring(1)); @@ -272,10 +280,9 @@ public class PathManager { * Attempts to extract classpath entry part from passed URL. */ @Nullable - @NonNls private static String extractRoot(URL resourceURL, String resourcePath) { if (!(StringUtil.startsWithChar(resourcePath, '/') || StringUtil.startsWithChar(resourcePath, '\\'))) { - //noinspection HardCodedStringLiteral,UseOfSystemOutOrSystemErr + //noinspection UseOfSystemOutOrSystemErr System.err.println("precondition failed: " + resourcePath); return null; } @@ -298,7 +305,7 @@ public class PathManager { } if (resultPath == null) { - //noinspection HardCodedStringLiteral,UseOfSystemOutOrSystemErr + //noinspection UseOfSystemOutOrSystemErr System.err.println("cannot extract: " + resourcePath + " from " + resourceURL); return null; } @@ -319,7 +326,7 @@ public class PathManager { } public static void loadProperties() { - String[] propFiles = new String[]{ + String[] propFiles = { System.getProperty(PROPERTIES_FILE), getUserPropertiesPath() + "/idea.properties", getHomePath() + "/bin/idea.properties", @@ -334,11 +341,6 @@ public class PathManager { try { Map properties = FileUtil.loadProperties(fis); - String home = properties.get("idea.home"); - if (home != null && ourHomePath == null) { - ourHomePath = getAbsolutePath(substituteVars(home)); - } - Properties sysProperties = System.getProperties(); for (String key : properties.keySet()) { if (sysProperties.getProperty(key, null) == null) { // load the property from the property file only if it is not defined yet @@ -352,7 +354,7 @@ public class PathManager { } } catch (IOException e) { - //noinspection HardCodedStringLiteral,UseOfSystemOutOrSystemErr + //noinspection UseOfSystemOutOrSystemErr System.err.println("Problem reading from property file: " + propFile.getPath()); } } @@ -362,24 +364,43 @@ public class PathManager { @Contract("null -> null") public static String substituteVars(String s) { - final String ideaHomePath = getHomePath(); - return substituteVars(s, ideaHomePath); + return substituteVars(s, getHomePath()); } @Contract("null, _ -> null") public static String substituteVars(String s, String ideaHomePath) { if (s == null) return null; + if (s.startsWith("..")) { s = ideaHomePath + File.separatorChar + BIN_FOLDER + File.separatorChar + s; } - s = StringUtil.replace(s, "${idea.home}", ideaHomePath); - final Properties props = System.getProperties(); - final Set keys = props.keySet(); - for (final Object key1 : keys) { - String key = (String)key1; - String value = props.getProperty(key); - s = StringUtil.replace(s, "${" + key + "}", value); + + Matcher m = PROPERTY_REF.matcher(s); + while (m.find()) { + String key = m.group(1); + String value = System.getProperty(key); + + if (value == null) { + if (PROPERTY_HOME_PATH.equals(key) || PROPERTY_HOME.equals(key)) { + value = ideaHomePath; + } + else if (PROPERTY_CONFIG_PATH.equals(key)) { + value = getConfigPath(); + } + else if (PROPERTY_SYSTEM_PATH.equals(key)) { + value = getSystemPath(); + } + } + + if (value == null) { + //noinspection UseOfSystemOutOrSystemErr + System.err.println("Unknown property: " + key); + value = ""; + } + + s = m.replaceAll(value); } + return s; } @@ -466,7 +487,7 @@ public class PathManager { // outdated stuff /** @deprecated use {@link #getPluginsPath()} (to remove in IDEA 14) */ - @SuppressWarnings("UnusedDeclaration") @NonNls public static final String PLUGINS_DIRECTORY = PLUGINS_FOLDER; + @SuppressWarnings("UnusedDeclaration") public static final String PLUGINS_DIRECTORY = PLUGINS_FOLDER; /** @deprecated use {@link #getPreInstalledPluginsPath()} (to remove in IDEA 14) */ @SuppressWarnings({"UnusedDeclaration", "MethodNamesDifferingOnlyByCase", "SpellCheckingInspection"}) diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 1591a33967d3..5156c8111d4c 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -2056,7 +2056,7 @@ public class UIUtil { } @Nullable - public static Component findParentByCondition(@NotNull JComponent c, Condition condition) { + public static Component findParentByCondition(@NotNull Component c, Condition condition) { Component eachParent = c; while (eachParent != null) { if (condition.value(eachParent)) return eachParent; diff --git a/platform/util/testSrc/com/intellij/openapi/application/PathManagerTest.java b/platform/util/testSrc/com/intellij/openapi/application/PathManagerTest.java new file mode 100644 index 000000000000..cf8aee5fab5b --- /dev/null +++ b/platform/util/testSrc/com/intellij/openapi/application/PathManagerTest.java @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.intellij.openapi.application; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.Random; + +import static org.junit.Assert.assertEquals; + +public class PathManagerTest { + private static final String TEST_RPOP = "__ij_subst_test__"; + private static final String TEST_VALUE = "__" + new Random().nextInt(1000) + "__"; + + @Before + public void setUp() throws Exception { + System.setProperty(TEST_RPOP, TEST_VALUE); + } + + @After + public void tearDown() throws Exception { + System.clearProperty(TEST_RPOP); + } + + @Test + public void testVarSubstitution() { + assertEquals("", PathManager.substituteVars("")); + assertEquals("abc", PathManager.substituteVars("abc")); + assertEquals("a$b$c", PathManager.substituteVars("a$b$c")); + + assertEquals("/" + TEST_VALUE + "/" + TEST_VALUE + "/", PathManager.substituteVars("/${" + TEST_RPOP + "}/${" + TEST_RPOP + "}/")); + + assertEquals(PathManager.getHomePath() + "/build.txt", PathManager.substituteVars("${idea.home.path}/build.txt")); + assertEquals(PathManager.getHomePath() + "/build.txt", PathManager.substituteVars("${idea.home}/build.txt")); + + assertEquals("/opt/idea/build.txt", PathManager.substituteVars("${idea.home.path}/build.txt", "/opt/idea")); + + String config = System.clearProperty(PathManager.PROPERTY_CONFIG_PATH); + try { + assertEquals(PathManager.getConfigPath() + "/opts", PathManager.substituteVars("${idea.config.path}/opts")); + } + finally { + if (config != null) { + System.setProperty(PathManager.PROPERTY_CONFIG_PATH, config); + } + } + + String system = System.clearProperty(PathManager.PROPERTY_SYSTEM_PATH); + try { + assertEquals(PathManager.getSystemPath() + "/logs2", PathManager.substituteVars("${idea.system.path}/logs2")); + } + finally { + if (system != null) { + System.setProperty(PathManager.PROPERTY_CONFIG_PATH, system); + } + } + + assertEquals(PathManager.getBinPath() + File.separator + "../license", PathManager.substituteVars("../license")); + + assertEquals("//", PathManager.substituteVars("/${unknown_property_ignore_the_error}/")); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java index f44497106063..05398a8edfba 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/CacheChangeProcessor.java @@ -122,9 +122,9 @@ public abstract class CacheChangeProcessor extends DiffRequestProcessor { ChangeListManager.getInstance(myProject).invokeAfterUpdate(new Runnable() { @Override public void run() { - refresh(); + refresh(); // TODO: this could cause diff init in 'hide' state } - }, InvokeAfterUpdateMode.BACKGROUND_CANCELLABLE, "", ModalityState.current()); + }, InvokeAfterUpdateMode.SILENT, "", ModalityState.current()); return new LoadingDiffRequest(ChangeDiffRequestProducer.getRequestTitle(change)); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/migrate/MigrateDiffTool.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/migrate/MigrateDiffTool.java index 19e2af8d3b8a..9d8914d03ff1 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/migrate/MigrateDiffTool.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/migrate/MigrateDiffTool.java @@ -15,6 +15,9 @@ */ package com.intellij.openapi.vcs.changes.actions.migrate; +import com.intellij.diff.DiffDialogHints; +import com.intellij.diff.DiffManager; +import com.intellij.diff.chains.DiffRequestChain; import com.intellij.openapi.Disposable; import com.intellij.openapi.diff.DiffRequest; import com.intellij.openapi.diff.DiffTool; @@ -24,9 +27,6 @@ import com.intellij.openapi.diff.impl.external.BinaryDiffTool; import com.intellij.openapi.diff.impl.external.DiffManagerImpl; import com.intellij.openapi.diff.impl.external.FrameDiffTool; import com.intellij.openapi.ui.WindowWrapper; -import com.intellij.diff.DiffDialogHints; -import com.intellij.diff.DiffManager; -import com.intellij.diff.chains.DiffRequestChain; import org.jetbrains.annotations.NotNull; import java.awt.*; @@ -51,6 +51,10 @@ public class MigrateDiffTool implements DiffTool { if (request.getHints().contains(MigrateToNewDiffUtil.DO_NOT_TRY_MIGRATE)) return false; if (request.getOnOkRunnable() != null) return false; if (!DiffManagerImpl.INTERNAL_DIFF.canShow(request) && !BinaryDiffTool.INSTANCE.canShow(request)) return false; + for (DiffTool tool : DiffManagerImpl.getInstanceEx().getAdditionTools()) { + if (tool == this) continue; + if (tool.canShow(request)) return false; + } return true; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/checkout/CheckoutAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/checkout/CheckoutAction.java index fbedef497159..ff41a13a19fd 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/checkout/CheckoutAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/checkout/CheckoutAction.java @@ -18,10 +18,9 @@ package com.intellij.openapi.vcs.checkout; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.vcs.CheckoutProvider; import com.intellij.openapi.vcs.ProjectLevelVcsManager; @@ -35,7 +34,11 @@ public class CheckoutAction extends AnAction implements DumbAware { public void actionPerformed(AnActionEvent e) { Project project = e.getData(CommonDataKeys.PROJECT); project = (project == null) ? ProjectManager.getInstance().getDefaultProject() : project; - myProvider.doCheckout(project, ProjectLevelVcsManager.getInstance(project).getCompositeCheckoutListener()); + myProvider.doCheckout(project, getListener(project)); + } + + protected CheckoutProvider.Listener getListener(Project project) { + return ProjectLevelVcsManager.getInstance(project).getCompositeCheckoutListener(); } public void update(AnActionEvent e) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/checkout/CheckoutActionGroup.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/checkout/CheckoutActionGroup.java index 926e5cced906..aef225849eec 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/checkout/CheckoutActionGroup.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/checkout/CheckoutActionGroup.java @@ -35,8 +35,14 @@ public class CheckoutActionGroup extends ComputableActionGroup.Simple { Arrays.sort(providers, new CheckoutProvider.CheckoutProviderComparator()); AnAction[] children = new AnAction[providers.length]; for (int i = 0; i < providers.length; i++) { - children[i] = new CheckoutAction(providers[i]); + CheckoutProvider provider = providers[i]; + children[i] = createAction(provider); } return children; } + + @NotNull + protected AnAction createAction(CheckoutProvider provider) { + return new CheckoutAction(provider); + } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/IntelliSortChooserAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/IntelliSortChooserAction.java index 3365dae4b986..940b28191eb6 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/IntelliSortChooserAction.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/IntelliSortChooserAction.java @@ -60,7 +60,7 @@ class IntelliSortChooserAction extends DumbAwareAction { public void update(AnActionEvent e) { super.update(e); VcsLogUi logUI = e.getData(VcsLogDataKeys.VCS_LOG_UI); - e.getPresentation().setEnabledAndVisible(logUI != null); + e.getPresentation().setEnabled(logUI != null); if (logUI != null) { String description = "IntelliSort: " + logUI.getBekType().getName(); e.getPresentation().setDescription(description); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XSourcePositionImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XSourcePositionImpl.java index fd68ef40752c..8bd98149e612 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XSourcePositionImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XSourcePositionImpl.java @@ -128,7 +128,7 @@ public class XSourcePositionImpl implements XSourcePosition { } @NotNull - private static OpenFileDescriptor doCreateOpenFileDescriptor(@NotNull Project project, @NotNull XSourcePosition position) { + public static OpenFileDescriptor doCreateOpenFileDescriptor(@NotNull Project project, @NotNull XSourcePosition position) { return position.getOffset() != -1 ? new OpenFileDescriptor(project, position.getFile(), position.getOffset()) : new OpenFileDescriptor(project, position.getFile(), position.getLine(), 0); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java index a538babfa138..7de88fe936da 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java @@ -42,6 +42,7 @@ import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.net.HttpConfigurable; import com.intellij.util.text.CharArrayUtil; import groovy.lang.GroovyObject; +import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling; import org.gradle.tooling.ProjectConnection; import org.gradle.tooling.model.DomainObjectSet; import org.gradle.tooling.model.GradleModuleVersion; @@ -382,7 +383,8 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver ProjectImportAction.class, // gradle-tooling-extension-impl jar ModelBuildScriptClasspathBuilderImpl.class, - GsonBuilder.class + GsonBuilder.class, + ShortTypeHandling.class ); } diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java index 6cd21373c189..4878e88d007f 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java @@ -21,18 +21,16 @@ import com.intellij.openapi.application.Result; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.externalSystem.model.ProjectSystemId; import com.intellij.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings; -import com.intellij.openapi.externalSystem.service.execution.ExternalSystemJdkUtil; import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings; import com.intellij.openapi.externalSystem.settings.ExternalSystemSettingsListenerAdapter; import com.intellij.openapi.externalSystem.test.ExternalSystemImportingTestCase; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; -import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TestDialog; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.PathUtil; import com.intellij.util.containers.ContainerUtil; import org.gradle.util.GradleVersion; import org.gradle.wrapper.GradleWrapperMain; @@ -189,17 +187,8 @@ public abstract class GradleImportingTestCase extends ExternalSystemImportingTes createProjectSubFile("gradle/wrapper/gradle-wrapper.properties", writer.toString()); } + @NotNull private static File wrapperJar() { - URI location; - try { - location = GradleWrapperMain.class.getProtectionDomain().getCodeSource().getLocation().toURI(); - } - catch (URISyntaxException e) { - throw new RuntimeException(e); - } - if (!location.getScheme().equals("file")) { - throw new RuntimeException(String.format("Cannot determine classpath for wrapper JAR from codebase '%s'.", location)); - } - return new File(location.getPath()); + return new File(PathUtil.getJarPathForClass(GradleWrapperMain.class)); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java index ab6750a99585..2918126327b9 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java @@ -118,7 +118,9 @@ public class GrListOrMapInfo implements ConstructorCallInfo { PsiClassType type = myReference.getConstructedClassType(); if (type == null) return GroovyResolveResult.EMPTY_ARRAY; - return new GroovyResolveResult[]{new GroovyResolveResultImpl(type.resolveGenerics())}; + final GroovyResolveResult result = GroovyResolveResultImpl.from(type.resolveGenerics()); + if (result == GroovyResolveResult.EMPTY_RESULT) return GroovyResolveResult.EMPTY_ARRAY; + return new GroovyResolveResult[]{result}; } @NotNull diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorReference.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorReference.java index 9fc78670c92f..315488a085b1 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorReference.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/findUsages/LiteralConstructorReference.java @@ -32,7 +32,10 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrSafeCastExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider; import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl; @@ -104,7 +107,7 @@ public class LiteralConstructorReference extends PsiReferenceBase.Poly /*println evt.text*/}] as ProcessAdapter + def listener = [onTextAvailable: { ProcessEvent evt, type -> /*println evt.text*/ }] as ProcessAdapter runConfiguration(DefaultDebugExecutor, listener, runner, configuration); } try { @@ -105,7 +107,8 @@ class GroovyDebuggerTest extends GroovyCompilerTestCase { if (!handler.waitFor(ourTimeout)) { if (handler instanceof OSProcessHandler) { OSProcessManager.instance.killProcessTree(handler.process) - } else { + } + else { println "can't terminate $handler" } fail('too long waiting for process termination') @@ -202,7 +205,6 @@ println 2 //4 eval 'call(2)', '-1' eval 'call(foo)', '-1' } - } public void testStaticContext() { @@ -230,7 +232,6 @@ class B { eval 'owner.name', 'B' eval 'this.name', 'B' } - } public void "test closures in instance context with delegation"() { @@ -253,7 +254,6 @@ def getFoo() { 13 } eval 'this.foo', '13' eval 'foo', '13' } - } public void testClassOutOfSourceRoots() { @@ -336,7 +336,7 @@ foo() } } - public void "test_navigation_outside_source"() { + public void "test navigation outside source"() { def module1 = addModule("module1", false) def module2 = addModule("module2", true) addGroovyLibrary(module1) @@ -388,6 +388,32 @@ public static void main(String[] args) { } } + public void "test evaluation within trait method"() { + def file = myFixture.addFileToProject 'Foo.groovy', ''' +trait Introspector { // 1 + def whoAmI() { + this // 3 + } +} + +class FooT implements Introspector { + def a = 1 + def b = 3 + + String toString() { 'fooInstance' } +} + +new FooT().whoAmI() +''' + addBreakpoint 'Foo.groovy', 3 + runDebugger file, { + waitForBreakpoint() + eval 'a', '1' + eval 'b', '3' + eval 'this', 'fooInstance' + } + } + private def addBreakpoint(String fileName, int line) { VirtualFile file = null edt { @@ -417,16 +443,16 @@ public static void main(String[] args) { } }); def finished = semaphore.waitFor(ourTimeout); - assert finished : 'Too long debugger actions' + assert finished: 'Too long debugger actions' int i = 0 def suspendManager = debugProcess.suspendManager - while (i++ < ourTimeout/10 && !suspendManager.pausedContext && !debugProcess.processHandler.processTerminated) { + while (i++ < ourTimeout / 10 && !suspendManager.pausedContext && !debugProcess.processHandler.processTerminated) { Thread.sleep(10) } def context = suspendManager.pausedContext - assert context : "too long process, terminated=$debugProcess.processHandler.processTerminated" + assert context: "too long process, terminated=$debugProcess.processHandler.processTerminated" return context } @@ -460,7 +486,7 @@ public static void main(String[] args) { } }) def finished = semaphore.waitFor(ourTimeout) - assert finished : 'Too long debugger action' + assert finished: 'Too long debugger action' return result } @@ -473,10 +499,10 @@ public static void main(String[] args) { managed { ctx = evaluationContext() item.setContext(ctx) - item.updateRepresentation(ctx, { } as DescriptorLabelListener) + item.updateRepresentation(ctx, {} as DescriptorLabelListener) semaphore.up() } - assert semaphore.waitFor(ourTimeout): "too long evaluation: $item.label $item.evaluateException" + assert semaphore.waitFor(ourTimeout): "too long evaluation: $item.label $item.evaluateException" String result = managed { def e = item.evaluateException @@ -492,5 +518,4 @@ public static void main(String[] args) { final SuspendContextImpl suspendContext = debugProcess.suspendManager.pausedContext new EvaluationContextImpl(suspendContext, suspendContext.frameProxy, suspendContext.frameProxy.thisObject()) } - } diff --git a/plugins/hg4idea/testData/repo/dot_hg/hgrc b/plugins/hg4idea/testData/repo/dot_hg/hgrc index 0c817f1bf6a4..88df1f2698d0 100644 --- a/plugins/hg4idea/testData/repo/dot_hg/hgrc +++ b/plugins/hg4idea/testData/repo/dot_hg/hgrc @@ -3,3 +3,6 @@ username = John Doe [merge-patterns] ** = internal:merge + +[extensions] +sparse=! diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java index a245114ad5e6..57f35272177d 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java @@ -200,6 +200,8 @@ public class MavenResourceCompilerConfigurationGenerator { } private static void generateManifest(@NotNull MavenProject mavenProject, @NotNull Module module) { + if(mavenProject.isAggregator()) return; + try { String jdkVersion = null; Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java index 8a5f6e2ba0fa..e377e14f5d00 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java @@ -167,6 +167,7 @@ public class RedmineRepository extends NewBaseRepositoryImpl { URIBuilder builder = new URIBuilder(getRestApiUrl("issues.json")) .addParameter("offset", String.valueOf(offset)) .addParameter("limit", String.valueOf(limit)) + .addParameter("sort", "updated_on:desc") .addParameter("status_id", withClosed ? "*" : "open") .addParameter("assigned_to_id", "me"); // If project was not chosen, all available issues still fetched. Such behavior may seems strange to user. diff --git a/python/build/pycharm64_community_launcher.properties b/python/build/pycharm64_community_launcher.properties index 7470a38aa539..11ca3241ed51 100644 --- a/python/build/pycharm64_community_launcher.properties +++ b/python/build/pycharm64_community_launcher.properties @@ -4,6 +4,6 @@ IDS_APP_TITLE=__IDE-NAME__ Launcher IDS_VM_OPTIONS_PATH=%USERPROFILE%\\.__PRODUCT_PATHS_SELECTOR__ IDC_WINLAUNCHER=__IDE-NAME___LAUNCHER IDS_PROPS_ENV_VAR=__IDE-NAME___PROPERTIES -IDS_VM_OPTIONS_ENV_VAR=__IDE-NAME___VM_OPTIONS +IDS_VM_OPTIONS_ENV_VAR=__IDE-NAME__64_VM_OPTIONS IDS_ERROR_LAUNCHING_APP=Error launching __IDE-NAME__ IDS_VM_OPTIONS=-Didea.platform.prefix=PyCharmCore -Didea.no.jre.check=true -Didea.paths.selector=__PRODUCT_PATHS_SELECTOR__ diff --git a/python/edu/course-creator-python/resources/META-INF/plugin.xml b/python/edu/course-creator-python/resources/META-INF/plugin.xml index b9148d8f1de2..1b015984d136 100644 --- a/python/edu/course-creator-python/resources/META-INF/plugin.xml +++ b/python/edu/course-creator-python/resources/META-INF/plugin.xml @@ -24,10 +24,13 @@ - + + + + diff --git a/python/edu/course-creator-python/resources/fileTemplates/internal/task.answer.ft b/python/edu/course-creator-python/resources/fileTemplates/internal/task.answer.py.ft similarity index 100% rename from python/edu/course-creator-python/resources/fileTemplates/internal/task.answer.ft rename to python/edu/course-creator-python/resources/fileTemplates/internal/task.answer.py.ft diff --git a/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java b/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java index 82477feb2636..d5f73de1e69e 100644 --- a/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java +++ b/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java @@ -64,6 +64,7 @@ public class PyCCProjectGenerator extends PythonProjectGenerator implements Dire final CCProjectService service = CCProjectService.getInstance(project); final Course course = new Course(name, author, description); + course.setLanguage("Python"); service.setCourse(course); final PsiDirectory projectDir = PsiManager.getInstance(project).findDirectory(baseDir); diff --git a/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyStudyLanguageManager.java b/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyStudyLanguageManager.java new file mode 100644 index 000000000000..1b711f818b90 --- /dev/null +++ b/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyStudyLanguageManager.java @@ -0,0 +1,34 @@ +package com.jetbrains.edu.coursecreator; + +import com.intellij.ide.fileTemplates.FileTemplate; +import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PyStudyLanguageManager implements StudyLanguageManager { + @Nullable + @Override + public String getDefaultTaskFileExtension() { + return "py"; + } + + @Nullable + @Override + public FileTemplate getTaskFileTemplateForExtension(@NotNull final Project project, String extension) { + if (!extension.equals("py")) { + return null; + } + return getInternalTemplateByName(project, "task.answer.py"); + } + + @Nullable + @Override + public FileTemplate getTestsTemplate(@NotNull final Project project) { + return getInternalTemplateByName(project, "tests.py"); + } + + private static FileTemplate getInternalTemplateByName(@NotNull final Project project, String name) { + return FileTemplateManager.getInstance(project).getInternalTemplate(name); + } +} diff --git a/python/educational/course-creator/resources/META-INF/plugin.xml b/python/educational/course-creator/resources/META-INF/plugin.xml index 6fb2817bff05..f15d2598fae9 100644 --- a/python/educational/course-creator/resources/META-INF/plugin.xml +++ b/python/educational/course-creator/resources/META-INF/plugin.xml @@ -37,6 +37,11 @@ + + + + + diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/StudyLanguageManager.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/StudyLanguageManager.java new file mode 100644 index 000000000000..bdef802bab94 --- /dev/null +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/StudyLanguageManager.java @@ -0,0 +1,20 @@ +package com.jetbrains.edu.coursecreator; + +import com.intellij.ide.fileTemplates.FileTemplate; +import com.intellij.lang.LanguageExtension; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface StudyLanguageManager { + LanguageExtension INSTANCE = new LanguageExtension("Edu.StudyLanguageManager"); + + @Nullable + String getDefaultTaskFileExtension(); + + @Nullable + FileTemplate getTaskFileTemplateForExtension(@NotNull final Project project, String extension); + + @Nullable + FileTemplate getTestsTemplate(@NotNull final Project project); +} diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java index 3741d52096fc..f34aacba345f 100644 --- a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java @@ -15,6 +15,7 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.io.ZipUtil; @@ -120,7 +121,7 @@ public class CCCreateCourseArchive extends DumbAwareAction { file = userFileDir.findChild(name); assert file != null; - String answerFileName = file.getNameWithoutExtension() + ".answer"; + String answerFileName = file.getNameWithoutExtension() + ".answer." + file.getExtension(); VirtualFile answerFile = answerFileDir.findChild(answerFileName); if (answerFile == null) { return; @@ -143,7 +144,7 @@ public class CCCreateCourseArchive extends DumbAwareAction { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - document.replaceString(0, document.getTextLength(), answerDocument.getText()); + document.replaceString(0, document.getTextLength(), answerDocument.getCharsSequence()); } }); } @@ -156,6 +157,17 @@ public class CCCreateCourseArchive extends DumbAwareAction { final AnswerPlaceholder answerPlaceholder = taskFile.getTaskWindows().get(i); replaceTaskWindow(project, document, answerPlaceholder); } + CommandProcessor.getInstance().executeCommand(project, new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + FileDocumentManager.getInstance().saveDocument(document); + } + }); + } + }, "x", "qwe"); document.removeDocumentListener(listener); } @@ -204,7 +216,8 @@ public class CCCreateCourseArchive extends DumbAwareAction { @Override public boolean accept(File pathname) { String name = pathname.getName(); - return !name.contains(".answer") && !name.contains("__pycache__") && !name.contains("_windows") && !name.contains(".pyc"); + String nameWithoutExtension = FileUtil.getNameWithoutExtension(pathname); + return !nameWithoutExtension.endsWith(".answer") && !name.contains("__pycache__") && !name.contains("_windows") && !name.contains(".pyc"); } }, null); } diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java index cd2c0791f881..fefa795f4ff6 100644 --- a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java @@ -7,11 +7,13 @@ import com.intellij.ide.fileTemplates.FileTemplateUtil; import com.intellij.ide.util.DirectoryChooserUtil; import com.intellij.ide.util.DirectoryUtil; import com.intellij.ide.util.EditorHelper; +import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; @@ -22,12 +24,16 @@ import com.intellij.psi.PsiElement; import com.intellij.util.PlatformIcons; import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.coursecreator.StudyLanguageManager; import com.jetbrains.edu.coursecreator.format.Course; import com.jetbrains.edu.coursecreator.format.Lesson; import com.jetbrains.edu.coursecreator.format.Task; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class CCCreateTask extends DumbAwareAction { + private static final Logger LOG = Logger.getInstance(CCCreateTask.class.getName()); + public CCCreateTask() { super("Task", "Create new Task", PlatformIcons.DIRECTORY_CLOSED_ICON); } @@ -67,42 +73,64 @@ public class CCCreateTask extends DumbAwareAction { public void run() { final PsiDirectory taskDirectory = DirectoryUtil.createSubdirectories("task" + (size + 1), lessonDir, "\\/"); if (taskDirectory != null) { + Language language = Language.findLanguageByID(course.getLanguage()); + if (language == null) { + return; + } + final StudyLanguageManager studyLanguageManager = StudyLanguageManager.INSTANCE.forLanguage(language); CCUtils.markDirAsSourceRoot(taskDirectory.getVirtualFile(), project); - final FileTemplate template = FileTemplateManager.getInstance(project).getInternalTemplate("task.html"); - final FileTemplate testsTemplate = FileTemplateManager.getInstance(project).getInternalTemplate("tests"); - final FileTemplate taskTemplate = FileTemplateManager.getInstance(project).getInternalTemplate("task.answer"); - try { - final PsiElement taskFile = FileTemplateUtil.createFromTemplate(template, "task.html", null, taskDirectory); - final PsiElement testsFile = FileTemplateUtil.createFromTemplate(testsTemplate, "tests.py", null, taskDirectory); - final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, "file1", null, taskDirectory); - final Task task = new Task(taskName); - task.addTaskFile("file1.py", size + 1); - task.setIndex(size + 1); - lesson.addTask(task, taskDirectory); - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - FileEditorManager fileEditorManager = FileEditorManager.getInstance(project); - for (VirtualFile virtualFile : fileEditorManager.getOpenFiles()) { - fileEditorManager.closeFile(virtualFile); - } - if (view != null) { - EditorHelper.openInEditor(testsFile, false); - EditorHelper.openInEditor(taskPyFile, false); - view.selectElement(taskFile); - EditorHelper.openInEditor(taskFile, false); - } + createFromTemplateAndOpen(taskDirectory, studyLanguageManager.getTestsTemplate(project), view); + createFromTemplateAndOpen(taskDirectory, FileTemplateManager.getInstance(project).getInternalTemplate("task.html"), view); + String defaultExtension = studyLanguageManager.getDefaultTaskFileExtension(); + String taskFileName = null; + if (defaultExtension != null) { + FileTemplate taskFileTemplate = studyLanguageManager.getTaskFileTemplateForExtension(project, + defaultExtension); + createFromTemplateAndOpen(taskDirectory, taskFileTemplate, view); + if (taskFileTemplate != null) { + taskFileName = taskFileTemplate.getName(); + } + } + + final Task task = new Task(taskName); + task.setIndex(size + 1); + lesson.addTask(task, taskDirectory); + if (taskFileName != null) { + task.addTaskFile(taskFileName, size + 1); + } + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + FileEditorManager fileEditorManager = FileEditorManager.getInstance(project); + for (VirtualFile virtualFile : fileEditorManager.getOpenFiles()) { + fileEditorManager.closeFile(virtualFile); } - }); - } - catch (Exception ignored) { - } + } + }); } } }); } + private static void createFromTemplateAndOpen(@NotNull final PsiDirectory taskDirectory, + @Nullable final FileTemplate template, + @Nullable IdeView view) { + if (template == null) { + return; + } + try { + final PsiElement file = FileTemplateUtil.createFromTemplate(template, template.getName(), null, taskDirectory); + if (view != null) { + EditorHelper.openInEditor(file, false); + view.selectElement(file); + } + } + catch (Exception e) { + LOG.error(e); + } + } + @Override public void update(@NotNull AnActionEvent event) { if (!CCProjectService.setCCActionAvailable(event)) { diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTaskFile.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTaskFile.java index 6296ee839af4..73b50d39b3be 100644 --- a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTaskFile.java +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTaskFile.java @@ -3,26 +3,33 @@ package com.jetbrains.edu.coursecreator.actions; import com.intellij.icons.AllIcons; import com.intellij.ide.IdeView; import com.intellij.ide.fileTemplates.FileTemplate; -import com.intellij.ide.fileTemplates.FileTemplateManager; import com.intellij.ide.fileTemplates.FileTemplateUtil; +import com.intellij.ide.projectView.ProjectView; import com.intellij.ide.util.DirectoryChooserUtil; import com.intellij.ide.util.EditorHelper; +import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.jetbrains.edu.coursecreator.CCProjectService; +import com.jetbrains.edu.coursecreator.StudyLanguageManager; import com.jetbrains.edu.coursecreator.format.Course; import com.jetbrains.edu.coursecreator.format.Lesson; import com.jetbrains.edu.coursecreator.format.Task; +import com.jetbrains.edu.coursecreator.ui.CreateTaskFileDialog; import org.jetbrains.annotations.NotNull; +import static com.intellij.openapi.ui.DialogWrapper.OK_EXIT_CODE; + public class CCCreateTaskFile extends DumbAwareAction { public CCCreateTaskFile() { @@ -30,7 +37,7 @@ public class CCCreateTaskFile extends DumbAwareAction { } @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(final AnActionEvent e) { final IdeView view = e.getData(LangDataKeys.IDE_VIEW); final Project project = e.getData(CommonDataKeys.PROJECT); @@ -50,26 +57,48 @@ public class CCCreateTaskFile extends DumbAwareAction { final int index = task.getTaskFiles().size() + 1; String generatedName = "file" + index; - final String taskFileName = Messages.showInputDialog("Name:", "Task File Name", null, generatedName, null); + CreateTaskFileDialog dialog = new CreateTaskFileDialog(project, generatedName); + dialog.show(); + if (dialog.getExitCode() != OK_EXIT_CODE) { + return; + } + final String taskFileName = dialog.getFileName(); if (taskFileName == null) return; - + FileType type = dialog.getFileType(); + if (type == null) { + return; + } + Language language = Language.findLanguageByID(course.getLanguage()); + if (language == null) { + return; + } + final StudyLanguageManager studyLanguageManager = StudyLanguageManager.INSTANCE.forLanguage(language); + final String extension = type.getDefaultExtension(); ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - final FileTemplate taskTemplate = FileTemplateManager.getInstance(project).getInternalTemplate("task.answer"); - try { - final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, taskFileName, null, taskDir); - task.addTaskFile(taskFileName + ".py", index); + final FileTemplate taskTemplate = studyLanguageManager.getTaskFileTemplateForExtension(project, extension); + final String answerFileName = taskFileName + ".answer." + extension; + try { + if (taskTemplate == null) { + VirtualFile file = taskDir.getVirtualFile().createChildData(this, answerFileName); + ProjectView.getInstance(project).select(file, file, false); + FileEditorManager.getInstance(project).openFile(file, true); + } + else { + final PsiElement taskFile = FileTemplateUtil.createFromTemplate(taskTemplate, answerFileName, null, taskDir); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - EditorHelper.openInEditor(taskPyFile, false); - view.selectElement(taskPyFile); + EditorHelper.openInEditor(taskFile, false); + view.selectElement(taskFile); } }); } - catch (Exception ignored) { - } + task.addTaskFile(taskFileName + "." + extension, index); + } + catch (Exception ignored) { + } } }); } diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/format/Course.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/format/Course.java index d01f879f98f7..211aa517c54d 100644 --- a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/format/Course.java +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/format/Course.java @@ -13,6 +13,8 @@ public class Course { @Expose private String name; @Expose private String author; + @Expose private String language; + private Map myLessonsMap = new HashMap(); public Map getLessonsMap() { @@ -81,4 +83,12 @@ public class Course { public void setLessonsMap(Map lessonsMap) { myLessonsMap = lessonsMap; } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } } diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateTaskFileDialog.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateTaskFileDialog.java new file mode 100644 index 000000000000..40abda126c58 --- /dev/null +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateTaskFileDialog.java @@ -0,0 +1,80 @@ +package com.jetbrains.edu.coursecreator.ui; + +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypeManager; +import com.intellij.openapi.fileTypes.impl.FileTypeRenderer; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.ui.DoubleClickListener; +import com.intellij.ui.ListScrollingUtil; +import com.intellij.ui.components.JBList; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.event.MouseEvent; + +public class CreateTaskFileDialog extends DialogWrapper { + private JPanel myPanel; + private JBList myList; + private JTextField myTextField; + + @SuppressWarnings("unchecked") + public CreateTaskFileDialog(@Nullable Project project, String generatedFileName) { + super(project); + FileType[] fileTypes = FileTypeManager.getInstance().getRegisteredFileTypes(); + + DefaultListModel model = new DefaultListModel(); + for (FileType type : fileTypes) { + if (!type.isReadOnly() && !type.getDefaultExtension().isEmpty()) { + model.addElement(type); + } + } + myList.setModel(model); + myTextField.setText(generatedFileName); + setTitle("Create New Task File"); + init(); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + myList.setCellRenderer(new FileTypeRenderer()); + + new DoubleClickListener() { + @Override + protected boolean onDoubleClick(MouseEvent e) { + doOKAction(); + return true; + } + }.installOn(myList); + + myList.getSelectionModel().addListSelectionListener( + new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + //TODO: do smth to check validness + } + } + ); + + ListScrollingUtil.selectItem(myList, FileTypeManager.getInstance().getFileTypeByExtension("py")); + return myPanel; + } + + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + return myTextField; + } + + public String getFileName() { + return myTextField.getText(); + } + + public FileType getFileType() { + return (FileType)myList.getSelectedValue(); + } +} diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateTaskFilePanel.form b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateTaskFilePanel.form new file mode 100644 index 000000000000..84061a2aca12 --- /dev/null +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateTaskFilePanel.form @@ -0,0 +1,62 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/helpers/pydev/pydev_ipython/matplotlibtools.py b/python/helpers/pydev/pydev_ipython/matplotlibtools.py index 664f6ab06b33..234a93710d60 100644 --- a/python/helpers/pydev/pydev_ipython/matplotlibtools.py +++ b/python/helpers/pydev/pydev_ipython/matplotlibtools.py @@ -61,16 +61,13 @@ def is_interactive_backend(backend): return matplotlib.is_interactive() -def patch_use(interpreter=None): +def patch_use(enable_gui_function): """ Patch matplotlib function 'use' """ matplotlib = sys.modules['matplotlib'] def patched_use(*args, **kwargs): matplotlib.real_use(*args, **kwargs) gui, backend = find_gui_and_backend() - if interpreter: - interpreter.enableGui(gui) - else: - do_enable_gui(gui) + enable_gui_function(gui) setattr(matplotlib, "real_use", getattr(matplotlib, "use")) setattr(matplotlib, "use", patched_use) @@ -86,27 +83,24 @@ def patch_is_interactive(): setattr(matplotlib, "is_interactive", patched_is_interactive) -def activate_matplotlib(interpreter=None): - """Set interactive to True for interactive backends.""" - def activate_matplotlib_inner(): - matplotlib = sys.modules['matplotlib'] - gui, backend = find_gui_and_backend() - is_interactive = is_interactive_backend(backend) - if is_interactive: - if interpreter: - interpreter.enableGui(gui) - else: - do_enable_gui(gui) - if not matplotlib.is_interactive(): - sys.stdout.write("Backend %s is interactive backend. Turning interactive mode on.\n" % backend) - matplotlib.interactive(True) - else: - if matplotlib.is_interactive(): - sys.stdout.write("Backend %s is non-interactive backend. Turning interactive mode off.\n" % backend) - matplotlib.interactive(False) - patch_use(interpreter) - patch_is_interactive() - return activate_matplotlib_inner +def activate_matplotlib(enable_gui_function): + """Set interactive to True for interactive backends. + enable_gui_function - Function which enables gui, should be run in the main thread. + """ + matplotlib = sys.modules['matplotlib'] + gui, backend = find_gui_and_backend() + is_interactive = is_interactive_backend(backend) + if is_interactive: + enable_gui_function(gui) + if not matplotlib.is_interactive(): + sys.stdout.write("Backend %s is interactive backend. Turning interactive mode on.\n" % backend) + matplotlib.interactive(True) + else: + if matplotlib.is_interactive(): + sys.stdout.write("Backend %s is non-interactive backend. Turning interactive mode off.\n" % backend) + matplotlib.interactive(False) + patch_use(enable_gui_function) + patch_is_interactive() def flag_calls(func): diff --git a/python/helpers/pydev/pydevconsole.py b/python/helpers/pydev/pydevconsole.py index dfface32307c..ad6443df81e7 100644 --- a/python/helpers/pydev/pydevconsole.py +++ b/python/helpers/pydev/pydevconsole.py @@ -176,7 +176,9 @@ def process_exec_queue(interpreter): from pydev_import_hook import import_hook_manager from pydev_ipython.matplotlibtools import activate_matplotlib, activate_pylab, activate_pyplot - import_hook_manager.add_module_name("matplotlib", activate_matplotlib(interpreter)) + import_hook_manager.add_module_name("matplotlib", lambda: activate_matplotlib(interpreter.enableGui)) + # enable_gui_function in activate_matplotlib should be called in main thread. That's why we call + # interpreter.enableGui which put it into the interpreter's exec_queue and executes it in the main thread. import_hook_manager.add_module_name("pylab", activate_pylab) import_hook_manager.add_module_name("pyplot", activate_pyplot) diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index 4d8f109e8ac7..6ab83b956790 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -499,8 +499,11 @@ class PyDB: set_return_control_callback(return_control) from pydev_import_hook import import_hook_manager - from pydev_ipython.matplotlibtools import activate_matplotlib, activate_pylab, activate_pyplot - import_hook_manager.add_module_name("matplotlib", activate_matplotlib()) + from pydev_ipython.matplotlibtools import activate_matplotlib, activate_pylab, activate_pyplot, do_enable_gui + import_hook_manager.add_module_name("matplotlib", lambda: activate_matplotlib(do_enable_gui)) + # enable_gui_function in activate_matplotlib should be called in main thread. Unlike integrated console, + # in the debug console we have no interpreter instance with exec_queue, but we run this code in the main + # thread and can call it directly. import_hook_manager.add_module_name("pylab", activate_pylab) import_hook_manager.add_module_name("pyplot", activate_pyplot) self.use_hooks_in_debug_console = True @@ -547,12 +550,6 @@ class PyDB: try: while True: int_cmd = queue.get(False) - if int_cmd.canBeExecutedBy(curr_thread_id): - PydevdLog(2, "processing internal command ", str(int_cmd)) - int_cmd.doIt(self) - else: - PydevdLog(2, "NOT processing internal command ", str(int_cmd)) - cmdsToReadd.append(int_cmd) if not self.use_hooks_in_debug_console and isinstance(int_cmd, InternalConsoleExec): # patch matplotlib if only debug console was started @@ -562,6 +559,14 @@ class PyDB: sys.stderr.write("Matplotlib support in debug console failed\n") pydev_log.error("Error in matplotlib init %s\n" % sys.exc_info()[0]) + if int_cmd.canBeExecutedBy(curr_thread_id): + PydevdLog(2, "processing internal command ", str(int_cmd)) + int_cmd.doIt(self) + else: + PydevdLog(2, "NOT processing internal command ", str(int_cmd)) + cmdsToReadd.append(int_cmd) + + except _queue.Empty: #@UndefinedVariable for int_cmd in cmdsToReadd: queue.put(int_cmd) diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index 03014b095ff9..08238498648c 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -852,7 +852,14 @@ remote.interpreter.configure.temp.files.path.label=PyCharm helpers path: # Message we display for inspection if user uses custom class type members that do not exist custom.type.mimic.name=Dynamic class based on {0} + # Values for command argument value validation -commandsWithArgs.validation.badValue=Argument can't have this value -commandsWithArgs.validation.excess=Excess argument -commandsWithArgs.enterCommand.label=Enter command here \ No newline at end of file +commandLine.validation.badCommand=Unknown command +commandLine.validation.argMissing=Required argument value is missing +commandLine.validation.argBadValue=Argument can't have this value +commandLine.validation.excessArg=Excess argument value +# And for labels +commandLine.subText.key.complete=Use {0} to complete selected variant +commandLine.subText.key.suggestions=Use {0} to view available values +commandLine.subText.key.executeUnknown=Click {0} to execute +commandLine.subText.key.executeCommand=Click {0} to execute "{1}" diff --git a/python/src/com/jetbrains/python/optParse/WordWithPosition.java b/python/src/com/jetbrains/python/WordWithPosition.java similarity index 91% rename from python/src/com/jetbrains/python/optParse/WordWithPosition.java rename to python/src/com/jetbrains/python/WordWithPosition.java index cdfdb27e8ed9..35f10c3a2e81 100644 --- a/python/src/com/jetbrains/python/optParse/WordWithPosition.java +++ b/python/src/com/jetbrains/python/WordWithPosition.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.python.optParse; +package com.jetbrains.python; import com.intellij.util.Range; import org.jetbrains.annotations.NotNull; @@ -33,6 +33,7 @@ public final class WordWithPosition extends Range { /** * Creates word with beam (it has start, but it is infinite) + * * @param word word * @param from start */ @@ -50,6 +51,15 @@ public final class WordWithPosition extends Range { myWord = word; } + /** + * Creates instance with certain text and range (start/end) + * @param word text + * @param range range + */ + public WordWithPosition(@NotNull final String word, @NotNull final Range range) { + this(word, range.getFrom(), range.getTo()); + } + @NotNull public String getText() { return myWord; @@ -121,7 +131,7 @@ public final class WordWithPosition extends Range { * @return parse result */ @NotNull - static List splitText(@NotNull final String text) { + public static List splitText(@NotNull final String text) { // TODO: Rewrite using regex or scanner? int position = 0; int wordStart = -1; diff --git a/python/src/com/jetbrains/python/commandInterface/CommandInterfacePresenter.java b/python/src/com/jetbrains/python/commandInterface/CommandInterfacePresenter.java index 79eb731c4481..1eeecac550e5 100644 --- a/python/src/com/jetbrains/python/commandInterface/CommandInterfacePresenter.java +++ b/python/src/com/jetbrains/python/commandInterface/CommandInterfacePresenter.java @@ -27,9 +27,8 @@ public interface CommandInterfacePresenter extends Presenter { /** * Called by view when user types new text or text changed by some other reason * - * @param inForcedTextMode text changed not by user but by calling {@link com.jetbrains.python.commandInterface.CommandInterfaceView#forceText(String)} */ - void textChanged(boolean inForcedTextMode); + void textChanged(); /** * Called by view when user requests for completion (like tab) @@ -39,14 +38,13 @@ public interface CommandInterfacePresenter extends Presenter { void completionRequested(@Nullable String valueFromSuggestionList); /** - * Called by view when user asks for suggestions (CTRL+Space) + * Called by view when user asks for suggestions (like CTRL+Space) */ void suggestionRequested(); /** - * Called by view when user wants to execute command (Enter is presed) + * Called by view when user wants to execute command (like enter) * - * @param valueFromSuggestionList value selected from suggestion list (if any selected) */ - void executionRequested(@Nullable String valueFromSuggestionList); + void executionRequested(); } diff --git a/python/src/com/jetbrains/python/commandInterface/CommandInterfaceView.java b/python/src/com/jetbrains/python/commandInterface/CommandInterfaceView.java index 3377431712c3..317d4caeed49 100644 --- a/python/src/com/jetbrains/python/commandInterface/CommandInterfaceView.java +++ b/python/src/com/jetbrains/python/commandInterface/CommandInterfaceView.java @@ -15,7 +15,8 @@ */ package com.jetbrains.python.commandInterface; -import com.jetbrains.python.optParse.WordWithPosition; +import com.intellij.util.Range; +import com.jetbrains.python.WordWithPosition; import com.jetbrains.python.suggestionList.SuggestionsBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,6 +31,13 @@ import java.util.List; */ public interface CommandInterfaceView { + /** + * Special place in command line that represents "after the last character" place. + * To be used in methods like {@link #setInfoAndErrors(java.util.Collection, java.util.Collection)} to mark it. + */ + @NotNull + Range AFTER_LAST_CHARACTER_RANGE = new Range(Integer.MAX_VALUE, Integer.MAX_VALUE); + /** * Launches view */ @@ -39,43 +47,34 @@ public interface CommandInterfaceView { * Suggests user some elements (for completion reason) * * @param suggestions what to suggest (see {@link com.jetbrains.python.suggestionList.SuggestionsBuilder}) - * @param absolute display list in its main position, or directly near the text - * @param toSelect word to select if list (if any) + * @param absolute display list in its main position, or directly near the caret + * @param toSelect word to select in list (if any) */ void displaySuggestions(@NotNull SuggestionsBuilder suggestions, boolean absolute, @Nullable String toSelect); - /** - * Emphasize errors (like red line and special message). - * - * @param errors list of errors (coordinates and error message. Message may be empty not to display any text) - * @param specialErrorPlace if you want to underline special place, you may provide it here - */ - void showErrors(@NotNull final List errors, @Nullable SpecialErrorPlace specialErrorPlace); /** - * Change text to the one provided + * Each time caret meets certain place, view should check whether some subtext has to be displayed. + * There are 2 types of subtext to be displayed: + *
    + *
  1. Suggestion Text: View says something like "click FOO to see list of suggestions". Only presenter knows exact places where + * suggestions are available, so it should provide them
  2. + *
  3. Default text: In all other cases view displays default text (if available).
  4. + *
+ *

+ * Presenter provides view list of special places * - * @param newText text to display + * @param defaultSubText default text + * @param suggestionAvailablePlaces list of places where suggestions are available in format [from, to]. */ - void forceText(@NotNull String newText); - - /** - * Display text in sub part (like hint) - * - * @param subText text to display - */ - void setSubText(@NotNull String subText); + void configureSubTexts(@Nullable String defaultSubText, + @NotNull List> suggestionAvailablePlaces); /** * Hide suggestion list */ void removeSuggestions(); - /** - * Displays baloon with message right under the last letter. - * - * @param message text to display - */ /** * @return text, entered by user @@ -83,41 +82,40 @@ public interface CommandInterfaceView { @NotNull String getText(); + /** - * Enlarges view to make it as big as required to display appropriate number of chars + * When caret meets certain place, view may display some info and some errors. + * Errors, how ever, may always be emphasized (with something like red line). + * This function configures view with pack of ranges and texts to display. + * Special place {@link #AFTER_LAST_CHARACTER_RANGE} may also be used. + * Each place is described as start-end position (in chars) where it should be enabled. + * Each call removes previously enabled information. * - * @param widthInChars number of chars + * @param errors places to be marked as errors with error text. + * @param infoBalloons places to display info balloon + * @see #AFTER_LAST_CHARACTER_RANGE */ - void setPreferredWidthInChars(int widthInChars); + void setInfoAndErrors(@NotNull final Collection infoBalloons, @NotNull final Collection errors); + /** - * Displays help balloon when cursor meets certain place. - * Each balloon is described as start-end position (in chars) where it should be enabled - * and test to display. - * Caution: Each call removes previuos balloons! + * Inserts text after caret moving next chars to the right * - * @param balloons list of balloons to display (i.e. you want to text 'foo' be displayed when user sets cursor on position - * from 1 to 3, so you add 'foo',1,4 here) + * @param text text to insert */ - void setBalloons(@NotNull final Collection balloons); + void insertTextAfterCaret(@NotNull String text); /** - * @return true if current caret position is on the word (no on whitespace) + * Replaces current text with another one. + * + * @param from from + * @param to to + * @param newText text to replace */ - boolean isCaretOnWord(); - + void replaceText(final int from, final int to, @NotNull String newText); /** - * Special place that may be underlined + * @return position of caret (in chars) */ - enum SpecialErrorPlace { - /** - * Whole text (from start to end) - */ - WHOLE_TEXT, - /** - * Only after last character - */ - AFTER_LAST_CHAR - } + int getCaretPosition(); } diff --git a/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ChunkAndInfo.java b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ChunkAndInfo.java new file mode 100644 index 000000000000..92a6910fdd5a --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ChunkAndInfo.java @@ -0,0 +1,83 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.chunkDriverBasedPresenter; + +import com.jetbrains.python.WordWithPosition; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Chunk and its info pair. Chunk may be null, while chunk info always present. + * @author Ilya.Kazakevich + */ +final class ChunkAndInfo implements Comparable { + @Nullable + private final WordWithPosition myChunk; + @NotNull + private final ChunkInfo myChunkInfo; + + ChunkAndInfo(@Nullable final WordWithPosition chunk, @NotNull final ChunkInfo chunkInfo) { + myChunk = chunk; + myChunkInfo = chunkInfo; + } + + /** + * @return chunk (word). may be null + */ + @Nullable + WordWithPosition getChunk() { + return myChunk; + } + + /** + * @return chunk info. + */ + @NotNull + ChunkInfo getChunkInfo() { + return myChunkInfo; + } + + @Override + public int compareTo(@NotNull final ChunkAndInfo o) { + if (myChunk == null && o.myChunk == null) { + return 0; + } + if (myChunk == null) { + return 1; + } + if (o.myChunk == null) { + return -1; + } + return myChunk.getFrom().compareTo(o.myChunk.getFrom()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ChunkAndInfo info = (ChunkAndInfo)o; + + if (myChunk != null ? !myChunk.equals(info.myChunk) : info.myChunk != null) return false; + + return true; + } + + @Override + public int hashCode() { + return myChunk != null ? myChunk.hashCode() : 0; + } +} diff --git a/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ChunkDriver.java b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ChunkDriver.java new file mode 100644 index 000000000000..12740a530052 --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ChunkDriver.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.chunkDriverBasedPresenter; + +import com.jetbrains.python.WordWithPosition; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Driver that knows how to parse pack of chunks into chunk info. + * + * @author Ilya.Kazakevich + */ +public interface ChunkDriver { + /** + * Parses chunks into pack of chunks. There always should be chunk+1 chunkInfos (one for the tail like + * {@link com.jetbrains.python.commandInterface.CommandInterfaceView#AFTER_LAST_CHARACTER_RANGE}). + * So, at least one chunk info should also exist! + * + * @param chunks chunks (parts of command line) + * @return parse info with chunks info. Warning: do not return less chunk infos than chunks provided. That leads to runtime error + */ + @NotNull + ParseInfo parse(@NotNull List chunks); +} diff --git a/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ChunkDriverBasedPresenter.java b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ChunkDriverBasedPresenter.java new file mode 100644 index 000000000000..946b2718d20c --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ChunkDriverBasedPresenter.java @@ -0,0 +1,247 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.chunkDriverBasedPresenter; + +import com.intellij.util.Range; +import com.jetbrains.python.WordWithPosition; +import com.jetbrains.python.commandInterface.CommandInterfacePresenterAdapter; +import com.jetbrains.python.commandInterface.CommandInterfaceView; +import com.jetbrains.python.suggestionList.SuggestionsBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +// TODO: Test + +/** + * Presenter that uses {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver} to parse pack of chunks + * to obtain {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo}. + * Each chunk may be paired with certain chunk info. Such info tells presenter whether this chunk has info, error, suggestions and so on. + * If caret situated far from chunks, then next neariest chunk should be found (see {@link #findNearestChunkAndInfo()}. + * + * + * @author Ilya.Kazakevich + * @see com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo + */ +public final class ChunkDriverBasedPresenter extends CommandInterfacePresenterAdapter { + + @NotNull + private final ChunkDriver myChunkDriver; + @NotNull + private final SortedSet myChunkAndInfos = new TreeSet(); + @Nullable + private Runnable myExecutor; + + public ChunkDriverBasedPresenter(@NotNull final CommandInterfaceView view, + @NotNull final ChunkDriver chunkDriver) { + super(view); + myChunkDriver = chunkDriver; + } + + @Override + public void launch() { + super.launch(); + reparseText(true); + } + + @Override + public void textChanged() { + reparseText(false); + } + + private void reparseText(final boolean skipSuggestions) { + final List chunks = WordWithPosition.splitText(myView.getText()); + final ParseInfo parseInfo = myChunkDriver.parse(chunks); + myExecutor = parseInfo.getExecutor(); + final List chunkInfos = parseInfo.getChunkInfo(); + assert chunkInfos.size() >= chunks.size() : "Driver did not return enough chunks"; + assert !chunkInfos.isEmpty() : "At least one chunk info should exist"; + myChunkAndInfos.clear(); + for (int i = 0; i < chunkInfos.size(); i++) { + final ChunkInfo chunkInfo = chunkInfos.get(i); + final WordWithPosition chunk = chunks.size() > i ? chunks.get(i) : null; + myChunkAndInfos.add(new ChunkAndInfo(chunk, chunkInfo)); + } + + + // configure Errors And Balloons + + final Collection infoBalloons = new ArrayList(); + final Collection errorBalloons = new ArrayList(); + + + for (final ChunkAndInfo chunkInfoPair : myChunkAndInfos) { + final ChunkInfo chunkInfo = chunkInfoPair.getChunkInfo(); + Range chunk = chunkInfoPair.getChunk(); + if (chunk == null) { + // After the last! + chunk = CommandInterfaceView.AFTER_LAST_CHARACTER_RANGE; + } + final String error = chunkInfo.getError(); + if (error != null) { + errorBalloons.add(new WordWithPosition(error, chunk)); + } + final String info = chunkInfo.getInfoBalloon(); + if (info != null) { + infoBalloons.add(new WordWithPosition(info, chunk)); + } + } + + myView.setInfoAndErrors(infoBalloons, errorBalloons); + + + if (!skipSuggestions) { + configureSuggestion(false); + } + + // Configure subtexts + final List> placesWhereSuggestionAvailable = new ArrayList>(); + for (final ChunkAndInfo chunkAndInfo : myChunkAndInfos) { + Range chunk = chunkAndInfo.getChunk(); + // If some place has suggestions, then add it + if (chunkAndInfo.getChunkInfo().getSuggestions() != null) { + if (chunk == null) { + // If there is no such chunk, that means we are after the last character, so use "special case" here + //noinspection ReuseOfLocalVariable + chunk = CommandInterfaceView.AFTER_LAST_CHARACTER_RANGE; + } + placesWhereSuggestionAvailable.add(chunk); + } + } + myView.configureSubTexts(parseInfo.getStatusText(), placesWhereSuggestionAvailable); + } + + @Override + public void suggestionRequested() { + configureSuggestion(true); // Show or hide + } + + + /** + * Displays suggestions if needed. + * + * @param requestedExplicitly is suggesions where requested by user explicitly or not + */ + private void configureSuggestion(final boolean requestedExplicitly) { + myView.removeSuggestions(); + final ChunkAndInfo chunkAndInfo = findNearestChunkAndInfo(); + final ChunkInfo chunkInfo = chunkAndInfo.getChunkInfo(); + final WordWithPosition chunk = chunkAndInfo.getChunk(); + + final SuggestionInfo suggestionInfo = chunkInfo.getSuggestions(); + if (suggestionInfo == null || (!suggestionInfo.isShowSuggestionsAutomatically() && !requestedExplicitly)) { + return; + } + final List suggestions = new ArrayList(suggestionInfo.getSuggestions()); + if (chunk != null && !requestedExplicitly) { + filterLeaveOnlyMatching(suggestions, chunk.getText()); + } + // TODO: Place to add history + if (!suggestions.isEmpty()) { + // No need to display empty suggestions + myView + .displaySuggestions(new SuggestionsBuilder(suggestions), suggestionInfo.isShowAbsolute(), (chunk == null ? null : chunk.getText())); + } + } + + + /** + * Filters collection of suggestions leaving only those starts with certain text. + * @param suggestions list to filter + * @param textToMatch leave only parts that start with this param + */ + private static void filterLeaveOnlyMatching(@NotNull final Iterable suggestions, @NotNull final String textToMatch) { + // TODO: use guava instead? + final Iterator iterator = suggestions.iterator(); + while (iterator.hasNext()) { + if (!iterator.next().startsWith(textToMatch)) { + iterator.remove(); + } + } + } + + /** + * Searches for the nearest chunk and info to use. It may or may not find chunk, but it should always provide some chunk info. + * + * @return nearest chunk info and, probably, chunk. + */ + @NotNull + private ChunkAndInfo findNearestChunkAndInfo() { + final int caretPosition = myView.getCaretPosition(); + + for (final ChunkAndInfo chunkAndInfo : myChunkAndInfos) { + final Range range = chunkAndInfo.getChunk(); + if (range != null && range.isWithin(caretPosition)) { + return chunkAndInfo; + } + if (range != null && range.getFrom() > caretPosition) { + return new ChunkAndInfo(null, chunkAndInfo.getChunkInfo()); + } + } + + return new ChunkAndInfo(null, myChunkAndInfos.last().getChunkInfo()); + } + + + @Override + public void completionRequested(@Nullable final String valueFromSuggestionList) { + final ChunkAndInfo chunkAndInfo = findNearestChunkAndInfo(); + final WordWithPosition chunk = chunkAndInfo.getChunk(); + + if (valueFromSuggestionList != null) { + // Just insert it + if (chunk != null) { // If caret is on the chunk itself + myView.replaceText(chunk.getFrom(), chunk.getTo(), valueFromSuggestionList); + } + else { + myView.insertTextAfterCaret(valueFromSuggestionList); + } + return; + } + + //User did not provide text no insert, do our best to find one + + final ChunkInfo chunkInfo = chunkAndInfo.getChunkInfo(); + final SuggestionInfo suggestionInfo = chunkInfo.getSuggestions(); + if (suggestionInfo == null) { + return; // No suggestion available for this chunk + } + final List suggestions = new ArrayList(suggestionInfo.getSuggestions()); + if (chunk != null) { + filterLeaveOnlyMatching(suggestions, chunk.getText()); + } + if (suggestions.size() == 1) { + // Exclusive! + if (chunk != null) { + myView.replaceText(chunk.getFrom(), chunk.getTo(), suggestions.get(0)); + } + else { + myView.insertTextAfterCaret(suggestions.get(0)); + } + } + } + + @Override + public void executionRequested() { + if (myExecutor == null) { + // TODO: Display error somehow + } + else { + myExecutor.run(); + } + } +} diff --git a/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ChunkInfo.java b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ChunkInfo.java new file mode 100644 index 000000000000..4397751ed011 --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ChunkInfo.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.chunkDriverBasedPresenter; + +import org.jetbrains.annotations.Nullable; + +/** + * Information about certain place in text, provided by chunk driver. + * + * @author Ilya.Kazakevich + */ +public final class ChunkInfo { + @Nullable + private final String myInfoBalloon; + @Nullable + private final String myError; + @Nullable + private final SuggestionInfo mySuggestions; + + + public ChunkInfo(@Nullable final String infoBalloon, + @Nullable final String error) { + this(infoBalloon, error, null); + } + + + /** + * + * @param infoBalloon Info balloon to display when caret meets this place (null if display nothing) + * @param error Error balloon to display when caret meets this place and underline text as error (null if no error) + * @param suggestions list of suggestions available in this place (if any) + */ + public ChunkInfo(@Nullable final String infoBalloon, + @Nullable final String error, + @Nullable final SuggestionInfo suggestions) { + myInfoBalloon = infoBalloon; + myError = error; + mySuggestions = suggestions; + } + + + /** + * + * @return Info balloon to display when caret meets this place (null if display nothing) + */ + @Nullable + public String getInfoBalloon() { + return myInfoBalloon; + } + + /** + * + * @return Error balloon to display when caret meets this place and underline text as error (null if no error) + */ + @Nullable + public String getError() { + return myError; + } + + /** + * + * @return list of suggestions available in this place (if any) + */ + @Nullable + public SuggestionInfo getSuggestions() { + return mySuggestions; + } +} diff --git a/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ParseInfo.java b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ParseInfo.java new file mode 100644 index 000000000000..005c4dae6150 --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/ParseInfo.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.chunkDriverBasedPresenter; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Pack of {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo} for certain chunks and other parsing info. + * + * @author Ilya.Kazakevich + */ +public final class ParseInfo { + @Nullable + private final String myStatusText; + @NotNull + private final List myChunkInfo = new ArrayList(); + @Nullable + private final Runnable myExecutor; + + /** + * @param chunkInfo Chunk info should match chunks in 1-to-1 manner: + * If "chunk1 chunk2 chunk3" were provided, you then need to return list where first chunkInfo matches first chunk ets. + * And there also should be one more chunkInfo for tail. + * @param statusText Status text {@link com.jetbrains.python.commandInterface.CommandInterfaceView view} may display. + * @param executor Engine to process command-line execution + */ + public ParseInfo(@NotNull final Collection chunkInfo, + @Nullable final String statusText, + @Nullable final Runnable executor) { + myStatusText = statusText; + myChunkInfo.addAll(chunkInfo); + myExecutor = executor; + } + + /** + * Simple parse info with out of status text and executor + * + * @param chunkInfo Chunk info (See {@link #ParseInfo(java.util.Collection, String, Runnable)} + * @see #ParseInfo(java.util.Collection, String, Runnable) + */ + public ParseInfo(@NotNull final Collection chunkInfo) { + this(chunkInfo, null, null); + } + + /** + * @return Status text {@link com.jetbrains.python.commandInterface.CommandInterfaceView view} may display. + */ + @Nullable + String getStatusText() { + return myStatusText; + } + + /** + * @return Engine to process command-line execution + */ + @Nullable + Runnable getExecutor() { + return myExecutor; + } + + /** + * @return Chunk info should match chunks in 1-to-1 manner, and there also should be one more chunkInfo for tail ( + * see {@link #ParseInfo(java.util.Collection, String, Runnable) ctor} manual) + */ + @NotNull + List getChunkInfo() { + return Collections.unmodifiableList(myChunkInfo); + } +} diff --git a/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/SuggestionInfo.java b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/SuggestionInfo.java new file mode 100644 index 000000000000..59bea9abd12f --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/SuggestionInfo.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.chunkDriverBasedPresenter; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Information about suggestions + * + * @author Ilya.Kazakevich + */ +public final class SuggestionInfo { + @NotNull + private final List mySuggestions = new ArrayList(); + private final boolean myShowSuggestionsAutomatically; + private final boolean myShowAbsolute; + + /** + * @param showSuggestionsAutomatically true it suggestions should be displayed even if user did not ask for that + * @param showAbsolute show suggestions at the absolute position (not relative to caret). + * @param suggestions List of suggestions to display + */ + public SuggestionInfo(final boolean showSuggestionsAutomatically, + final boolean showAbsolute, + @NotNull final Collection suggestions) { + myShowSuggestionsAutomatically = showSuggestionsAutomatically; + myShowAbsolute = showAbsolute; + mySuggestions.addAll(suggestions); + } + + /** + * @return List of suggestions to display + */ + @NotNull + public List getSuggestions() { + return Collections.unmodifiableList(mySuggestions); + } + + /** + * @return true it suggestions should be displayed even if user did not ask for that + */ + public boolean isShowSuggestionsAutomatically() { + return myShowSuggestionsAutomatically; + } + + /** + * @return show suggestions at the absolute position (not relative to caret). + */ + public boolean isShowAbsolute() { + return myShowAbsolute; + } +} diff --git a/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/package-info.java b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/package-info.java new file mode 100644 index 000000000000..9281bc7f630c --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/chunkDriverBasedPresenter/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ + +/** + * {@link com.jetbrains.python.commandInterface.CommandInterfacePresenter} implementation based on ideas of chunk + * and {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver}. + * This presenter explodes command line into several parts or chunks. + * Chunks then passed to {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver driver} and it returns + * all information it has about each chunk. Presenter uses this information to display chunks correctly using view. + * To use this package, {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver} should be implemented. + * + * See {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriverBasedPresenter} as entry point + * + * + * @author Ilya.Kazakevich + */ +package com.jetbrains.python.commandInterface.chunkDriverBasedPresenter; \ No newline at end of file diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/Argument.java b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/Argument.java similarity index 96% rename from python/src/com/jetbrains/python/commandInterface/commandsWithArgs/Argument.java rename to python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/Argument.java index 5826a45a4dd4..fc3f64192c17 100644 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/Argument.java +++ b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/Argument.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.python.commandInterface.commandsWithArgs; +package com.jetbrains.python.commandInterface.commandBasedChunkDriver; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/ArgumentsInfo.java b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/ArgumentsInfo.java similarity index 51% rename from python/src/com/jetbrains/python/commandInterface/commandsWithArgs/ArgumentsInfo.java rename to python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/ArgumentsInfo.java index 594b7ac37a70..ad43da45adcd 100644 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/ArgumentsInfo.java +++ b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/ArgumentsInfo.java @@ -13,39 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.python.commandInterface.commandsWithArgs; +package com.jetbrains.python.commandInterface.commandBasedChunkDriver; -import org.jetbrains.annotations.NotNull; +import com.intellij.openapi.util.Pair; import org.jetbrains.annotations.Nullable; -import java.util.List; - /** - * Information about command {@link com.jetbrains.python.commandInterface.commandsWithArgs.Argument arguments} and their value + * Information about command {@link Argument arguments} and their value * validation. - * Check optparse manual, package info and {@link com.jetbrains.python.commandInterface.commandsWithArgs.Argument} + * Check optparse manual, package info and {@link Argument} * manual for more info about arguments. * * @author Ilya.Kazakevich */ public interface ArgumentsInfo { /** - * Returns argument by its position. + * Returns argument by its position. It also returns hint whether argument is required or not. * * @param argumentPosition argument position - * @return null if no argument value is available at this position. Returns argument otherwise. + * @return null if no argument value is available at this position. + * Returns argument otherwise. Boolean here should tell you if argument is required (command is invalid with out of it) or optional ( + * it is acceptible, but command can work with our of it) */ @Nullable - Argument getArgument(int argumentPosition); - - - /** - * Validates argument values. - * Values should be provided as list. I.e. for

my_command foo bar
there should be list of "foo, bar". - * - * @param argumentValuesToCheck values to check - * @return validation result - */ - @NotNull - ArgumentsValuesValidationInfo validateArgumentValues(@NotNull final List argumentValuesToCheck); + Pair getArgument(int argumentPosition); } diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/Command.java b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/Command.java similarity index 69% rename from python/src/com/jetbrains/python/commandInterface/commandsWithArgs/Command.java rename to python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/Command.java index fb139df02ca2..694021964fea 100644 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/Command.java +++ b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/Command.java @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.python.commandInterface.commandsWithArgs; +package com.jetbrains.python.commandInterface.commandBasedChunkDriver; +import com.intellij.openapi.module.Module; +import com.jetbrains.python.commandLineParser.CommandLineParseResult; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,8 +42,16 @@ public interface Command { /** - * @return Information about command positional, unnamed {@link com.jetbrains.python.commandInterface.commandsWithArgs.Argument arguments} (not options!) + * @return Information about command positional, unnamed {@link Argument arguments} (not options!) */ @NotNull ArgumentsInfo getArgumentsInfo(); + + /** + * Execute command + * + * @param module module to execute command against + * @param commandLine command's command line + */ + void execute(@NotNull final Module module, @NotNull final CommandLineParseResult commandLine); } diff --git a/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/CommandBasedChunkDriver.java b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/CommandBasedChunkDriver.java new file mode 100644 index 000000000000..02fd1d3194c0 --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/CommandBasedChunkDriver.java @@ -0,0 +1,187 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.commandBasedChunkDriver; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.util.Pair; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.WordWithPosition; +import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver; +import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo; +import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ParseInfo; +import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.SuggestionInfo; +import com.jetbrains.python.commandLineParser.CommandLineParseResult; +import com.jetbrains.python.commandLineParser.CommandLineParser; +import com.jetbrains.python.commandLineParser.CommandLinePartType; +import com.jetbrains.python.commandLineParser.MalformedCommandLineException; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/** + * Chunk driver that uses pack of commands. + * + * @author Ilya.Kazakevich + */ +public final class CommandBasedChunkDriver implements ChunkDriver { + @NotNull + private final CommandLineParser myCommandLineParser; + @NotNull + private final Map myCommands = new TreeMap(); // To sort commands by name + @NotNull + private final Module myModule; + + /** + * @param commandLineParser parser to use + * @param module module parsing takes place in + * @param commands available commands + */ + public CommandBasedChunkDriver(@NotNull final CommandLineParser commandLineParser, + @NotNull final Module module, + @NotNull final Collection commands) { + myCommandLineParser = commandLineParser; + for (final Command command : commands) { + myCommands.put(command.getName(), command); + } + myModule = module; + } + + @Override + @NotNull + public ParseInfo parse(@NotNull final List chunks) { + // TODO: Refactor to add command first to prevent copy/paste + if (chunks.isEmpty()) { + return createBadCommandInfo(chunks.size()); + } + + try { + final CommandLineParseResult commandLine = myCommandLineParser.parse(chunks); + final Command command = myCommands.get(commandLine.getCommand().getText()); + if (command == null) { + // Bad command inserted + return createBadCommandInfo(chunks.size()); + } + + // Command exists, lets check its arguments + + // TODO: Support options as well + + // First, validate values + final ArgumentsInfo commandArgumentsInfo = command.getArgumentsInfo(); + + + final List chunkInfo = new ArrayList(); + // First chunk iscommand and it seems to be ok + chunkInfo.add(new ChunkInfo(null, null, new SuggestionInfo(false, true, myCommands.keySet()))); + + + // Now add balloons, info and suggestions + for (int i = 0; i < commandLine.getParts().size(); i++) { + final Pair argumentPair = commandArgumentsInfo.getArgument(i); + if (argumentPair == null) { // Excess argument! + chunkInfo.add(new ChunkInfo(null, PyBundle.message("commandLine.validation.excessArg"))); + continue; + } + final Argument argument = argumentPair.getSecond(); + final List availableValues = argument.getAvailableValues(); + final Pair part = commandLine.getParts().get(i); + if (part.first != CommandLinePartType.ARGUMENT) { + // Only arguments are supported now, so we have nothing to say about this chunk + chunkInfo.add(new ChunkInfo(null, null)); + } + final String argumentValue = part.second.getText(); + String errorMessage = null; + if (availableValues != null && !availableValues.contains(argumentValue)) { + // Bad value + errorMessage = PyBundle.message("commandLine.validation.argBadValue"); + } + // Argument seems to be ok. We suggest values automatically only if value is bad + chunkInfo.add(new ChunkInfo(argument.getHelpText(), errorMessage, + (availableValues != null ? new SuggestionInfo(errorMessage != null, false, availableValues) : null))); + } + + + final Pair nextArgumentPair = commandArgumentsInfo.getArgument(commandLine.getParts().size()); + if (nextArgumentPair != null) { + // Next arg exists + final Argument nextArgument = nextArgumentPair.getSecond(); + final List availableValues = nextArgument.getAvailableValues(); + // Only add error if required + final String error = nextArgumentPair.first ? PyBundle.message("commandLine.validation.argMissing") : null; + final ChunkInfo lastArgInfo = + new ChunkInfo(nextArgument.getHelpText(), error, + (availableValues != null ? new SuggestionInfo(false, false, availableValues) : null)); + chunkInfo.add(lastArgInfo); + } + else { + // Looks like all arguments are satisfied. Adding empty chunk to prevent completion etc. + // This is a hack, but with out of it last chunkinfo will always be used, even 200 chars after last place + chunkInfo.add(new ChunkInfo(null, null)); + } + + assert chunkInfo.size() >= chunks.size() : "Contract broken: not enough chunks"; + + return new ParseInfo(chunkInfo, command.getHelp(), new MyExecutor(command, commandLine)); + } + catch (final MalformedCommandLineException ignored) { + // Junk enetered! + return createBadCommandInfo(chunks.size()); + } + } + + + /** + * Creates parse info signaling command is bad or junk + * + * @param numberOfChunks number of chunks provided by user (we must return chunk info for each chunk + 1, accroding to contract) + * @return parse info to return + */ + @NotNull + private ParseInfo createBadCommandInfo(final int numberOfChunks) { + final List result = new ArrayList(); + // We know that first chunk command line, but we can't say anything about outher chunks except they are bad. + // How ever, we must say something according to contract (number of infos should be equal or greater than number of chunks) + result + .add(new ChunkInfo(null, PyBundle.message("commandLine.validation.badCommand"), new SuggestionInfo(true, true, myCommands.keySet()))); + for (int i = 1; i < numberOfChunks; i++) { + result.add( + new ChunkInfo(null, PyBundle.message("commandLine.validation.badCommand"))); + } + + return new ParseInfo(result); + } + + + /** + * Adapter that executes command using {@link Command#execute(com.intellij.openapi.module.Module, com.jetbrains.python.commandLineParser.CommandLineParseResult)} + */ + private class MyExecutor implements Runnable { + @NotNull + private final Command myCommand; + @NotNull + private final CommandLineParseResult myCommandLine; + + MyExecutor(@NotNull final Command command, @NotNull final CommandLineParseResult line) { + myCommand = command; + myCommandLine = line; + } + + @Override + public void run() { + myCommand.execute(myModule, myCommandLine); + } + } +} diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/KnownArgumentsInfo.java b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/KnownArgumentsInfo.java similarity index 73% rename from python/src/com/jetbrains/python/commandInterface/commandsWithArgs/KnownArgumentsInfo.java rename to python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/KnownArgumentsInfo.java index 2e8762d10001..4c3a50295f78 100644 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/KnownArgumentsInfo.java +++ b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/KnownArgumentsInfo.java @@ -13,18 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.python.commandInterface.commandsWithArgs; +package com.jetbrains.python.commandInterface.commandBasedChunkDriver; import com.google.common.base.Preconditions; -import com.intellij.util.containers.hash.HashMap; -import com.jetbrains.python.commandInterface.commandsWithArgs.ArgumentsValuesValidationInfo.ArgumentValueError; +import com.intellij.openapi.util.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Map; /** * In some special cases we have insight about command arguments. @@ -61,6 +59,15 @@ public final class KnownArgumentsInfo implements ArgumentsInfo { */ private final int myMaxArguments; + /** + * For command with fixed number of arguments. In this case all arguments are fixed and required! + * + * @param arguments arguments this command have + */ + public KnownArgumentsInfo(@NotNull final Collection arguments) { + this(arguments, arguments.size(), arguments.size()); + } + /** * For commands with infinite number of values last argument accepts (my_command VAL1 VAL2 .. VALN) * @@ -91,36 +98,20 @@ public final class KnownArgumentsInfo implements ArgumentsInfo { @Nullable @Override - public Argument getArgument(final int argumentPosition) { + public Pair getArgument(final int argumentPosition) { + if (argumentPosition >= myMaxArguments) { + return null; + } + final boolean optional = argumentPosition >= myMinArguments; + if (myArguments.size() > argumentPosition) { - return myArguments.get(argumentPosition); + return Pair.create(!optional, myArguments.get(argumentPosition)); } // We may need last one - if (argumentPosition <= myMaxArguments) { - return myArguments.get(myArguments.size() - 1); + if (argumentPosition < myMaxArguments) { + return Pair.create(false, myArguments.get(myArguments.size() - 1)); } return null; } - - @NotNull - @Override - public ArgumentsValuesValidationInfo validateArgumentValues(@NotNull final List argumentValuesToCheck) { - final Map errors = new HashMap(); - - for (int i = 0; i < argumentValuesToCheck.size(); i++) { - final String userValue = argumentValuesToCheck.get(i); - final Argument argument = getArgument(i); - if (argument == null) { - errors.put(i, ArgumentValueError.EXCESS); - continue; - } - final List availableValues = argument.getAvailableValues(); - if (availableValues != null && !availableValues.contains(userValue)) { - errors.put(i, ArgumentValueError.BAD_VALUE); - } - } - - return new ArgumentsValuesValidationInfo(errors, argumentValuesToCheck.size() < myMinArguments); - } } diff --git a/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/NoArgumentsInfo.java b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/NoArgumentsInfo.java new file mode 100644 index 000000000000..9aaa0d5503b2 --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/NoArgumentsInfo.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.commandBasedChunkDriver; + +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.Nullable; + +/** + * TODO: Redundant?! + * Case when command has no arguments (for sure!) + * + * @author Ilya.Kazakevich + */ +public final class NoArgumentsInfo implements ArgumentsInfo { + /** + * Instance to use when command has no arguments + */ + public static final ArgumentsInfo INSTANCE = new NoArgumentsInfo(); + + private NoArgumentsInfo() { + } + + @Nullable + @Override + public Pair getArgument(final int argumentPosition) { + return null; + } +} diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/UnknownArgumentsInfo.java b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/UnknownArgumentsInfo.java similarity index 72% rename from python/src/com/jetbrains/python/commandInterface/commandsWithArgs/UnknownArgumentsInfo.java rename to python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/UnknownArgumentsInfo.java index 3199c3431d15..04889e4109c9 100644 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/UnknownArgumentsInfo.java +++ b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/UnknownArgumentsInfo.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.python.commandInterface.commandsWithArgs; +package com.jetbrains.python.commandInterface.commandBasedChunkDriver; +import com.intellij.openapi.util.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; - /** * For many commands we know nothing about arguments but their help text. * This strategy is for this case @@ -40,15 +39,10 @@ public final class UnknownArgumentsInfo implements ArgumentsInfo { myHelp = allArgumentsHelpText; } + @Nullable @Override - public Argument getArgument(final int argumentPosition) { - return new Argument(myHelp); // We can't say argument does not exist. - } - - @NotNull - @Override - public ArgumentsValuesValidationInfo validateArgumentValues(@NotNull final List argumentValuesToCheck) { - return ArgumentsValuesValidationInfo.NO_ERROR; // Actually, we have no idea + public Pair getArgument(final int argumentPosition) { + return Pair.create(false, new Argument(myHelp)); } } diff --git a/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/package-info.java b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/package-info.java new file mode 100644 index 000000000000..cc72ae21e259 --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/commandBasedChunkDriver/package-info.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ + +/** + * {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver} implementation based on idea of + * {@link com.jetbrains.python.commandInterface.commandBasedChunkDriver.Command command} and its {@link com.jetbrains.python.commandInterface.commandBasedChunkDriver.Argument arguments}. + * + * See {@link com.jetbrains.python.commandInterface.commandBasedChunkDriver.CommandBasedChunkDriver} as entry point. + * It parses command line using {@link com.jetbrains.python.commandLineParser.CommandLineParser} and finds matching command and arguments + * provided by user + * + * @author Ilya.Kazakevich + */ +package com.jetbrains.python.commandInterface.commandBasedChunkDriver; \ No newline at end of file diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/ArgumentsValuesValidationInfo.java b/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/ArgumentsValuesValidationInfo.java deleted file mode 100644 index 239074741896..000000000000 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/ArgumentsValuesValidationInfo.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * 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 - * - * http://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. - */ -package com.jetbrains.python.commandInterface.commandsWithArgs; - -import com.intellij.util.containers.hash.HashMap; -import org.jetbrains.annotations.NotNull; - -import java.util.Collections; -import java.util.Map; - -/** - * Information about {@link com.jetbrains.python.commandInterface.commandsWithArgs.Argument arguments} values validation - * - * @author Ilya.Kazakevich - */ -public final class ArgumentsValuesValidationInfo { - /** - * Validation with out of any error - */ - @NotNull - static final ArgumentsValuesValidationInfo - NO_ERROR = new ArgumentsValuesValidationInfo(Collections.emptyMap(), false); - - private final Map myPositionOfErrorArguments = new HashMap(); - private final boolean myNotEnoughArguments; - - /** - * @param positionOfErrorArguments map of [argument_position, its_value_error] - * @param notEnoughArguments true if not enough arguments values provided (i.e. some required arg missed) - */ - ArgumentsValuesValidationInfo(@NotNull final Map positionOfErrorArguments, - final boolean notEnoughArguments) { - myPositionOfErrorArguments.putAll(positionOfErrorArguments); - myNotEnoughArguments = notEnoughArguments; - } - - /** - * @return map of [argument_position, its_value_error] - */ - @NotNull - Map getPositionOfErrorArguments() { - return Collections.unmodifiableMap(myPositionOfErrorArguments); - } - - /** - * @return if not enough argument values provided (i.e. some required arg missed) - */ - boolean isNotEnoughArguments() { - return myNotEnoughArguments; - } - - /** - * Type of argument value error. - */ - enum ArgumentValueError { - /** - * This argument is redundant - */ - EXCESS, - /** - * Argument has bad value - */ - BAD_VALUE - } -} diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/CommandAdapter.java b/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/CommandAdapter.java deleted file mode 100644 index 51d0a42942f0..000000000000 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/CommandAdapter.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * 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 - * - * http://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. - */ -package com.jetbrains.python.commandInterface.commandsWithArgs; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Simple command implementation - * - * @author Ilya.Kazakevich - */ -public class CommandAdapter implements Command { - @NotNull - private final String myName; - @Nullable - private final String myHelp; - @NotNull - private final ArgumentsInfo myArgumentsInfo; - - /** - * @param help help text - * @param name command name - * @param argumentsInfo arguments info - */ - public CommandAdapter(@NotNull final String name, @Nullable final String help, @NotNull ArgumentsInfo argumentsInfo) { - myName = name; - myHelp = help; - myArgumentsInfo = argumentsInfo; - } - - /** - * @return command name - */ - @Override - @NotNull - public final String getName() { - return myName; - } - - - @Override - @Nullable - public final String getHelp() { - return myHelp; - } - - - @NotNull - @Override - public final ArgumentsInfo getArgumentsInfo() { - return myArgumentsInfo; - } -} diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/CommandExecutionInfo.java b/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/CommandExecutionInfo.java deleted file mode 100644 index b7e533b7da67..000000000000 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/CommandExecutionInfo.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * 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 - * - * http://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. - */ -package com.jetbrains.python.commandInterface.commandsWithArgs; - -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.ArrayUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Arrays; -import java.util.List; - -/** - * Information about command and its arguments to save in history / pass to execution etc. - * - * @author Ilya.Kazakevich - */ -public class CommandExecutionInfo { - /** - * Command and arguments separator. - */ - private static final String SEPARATOR = " "; - @NotNull - private final String myCommandName; - @NotNull - private final String[] myArguments; - - /** - * @param commandName command - * @param arguments its arguments - */ - public CommandExecutionInfo(@NotNull final String commandName, @NotNull final String... arguments) { - myCommandName = commandName; - myArguments = arguments.clone(); - } - - /** - * @return command - */ - @NotNull - public String getCommandName() { - return myCommandName; - } - - /** - * @return command arguments - */ - @NotNull - public String[] getArguments() { - return myArguments.clone(); - } - - /** - * @return command in format "command arg1 arg2". Opposite to {@link #fromString(String)} - * @see #fromString(String) - */ - @NotNull - public String toString() { - // TODO: What if command or argument has space in it? Escape somehow! - return StringUtil.join(ArrayUtil.mergeArrays(new String[]{myCommandName}, myArguments), SEPARATOR); - } - - /** - * @param stringToUnserialize string created by {@link #toString()} - * @return command parsed from string - * @see #toString() - */ - @Nullable - public static CommandExecutionInfo fromString(@NotNull final String stringToUnserialize) { - // TODO: What if command or argument has space in it? Escape somehow! - final List strings = StringUtil.split(stringToUnserialize, SEPARATOR); - if (strings.isEmpty()) { - return null; - } - return new CommandExecutionInfo(strings.get(0), ArrayUtil.toStringArray(strings.subList(1, strings.size()))); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof CommandExecutionInfo)) return false; - - CommandExecutionInfo info = (CommandExecutionInfo)o; - - if (!Arrays.equals(myArguments, info.myArguments)) return false; - if (!myCommandName.equals(info.myCommandName)) return false; - - return true; - } - - @Override - public int hashCode() { - int result = myCommandName.hashCode(); - result = 31 * result + Arrays.hashCode(myArguments); - return result; - } -} diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/CommandInterfacePresenterCommandBased.java b/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/CommandInterfacePresenterCommandBased.java deleted file mode 100644 index 0090a9e13d65..000000000000 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/CommandInterfacePresenterCommandBased.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * 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 - * - * http://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. - */ -package com.jetbrains.python.commandInterface.commandsWithArgs; - -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.ArrayUtil; -import com.jetbrains.python.commandInterface.CommandInterfacePresenterAdapter; -import com.jetbrains.python.commandInterface.CommandInterfaceView; -import com.jetbrains.python.commandInterface.CommandInterfaceView.SpecialErrorPlace; -import com.jetbrains.python.optParse.MalformedCommandLineException; -import com.jetbrains.python.optParse.ParsedCommandLine; -import com.jetbrains.python.optParse.WordWithPosition; -import com.jetbrains.python.suggestionList.SuggestionsBuilder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -/** - * Command-line interface presenter that is command-based - * - * @param Command type - * @author Ilya.Kazakevich - */ -public class CommandInterfacePresenterCommandBased extends CommandInterfacePresenterAdapter { - /** - * [name] -> command. Linked is used to preserve order. - */ - private final Map myCommands = new LinkedHashMap(); - /** - * currenly used strategy (see interface for more info) - */ - private Strategy myStrategy; - - - /** - * @param view view - * @param commands available commands - */ - public CommandInterfacePresenterCommandBased(@NotNull final CommandInterfaceView view, - @NotNull final Iterable commands) { - super(view); - for (final C command : commands) { - myCommands.put(command.getName(), command); - } - } - - /** - * @param view view - * @param commands available commands - */ - public CommandInterfacePresenterCommandBased(@NotNull final CommandInterfaceView view, - @NotNull final C... commands) { - this(view, Arrays.asList(commands)); - } - - @Override - public void launch() { - /*myView.setPreferredWidthInChars(getMaximumCommandWithArgsLength());*/ - super.launch(); - myStrategy = new NoCommandStrategy(this); - } - - @Override - public void textChanged(final boolean inForcedTextMode) { - configureStrategy(); - myView.setSubText(myStrategy.getSubText()); - final Pair> errorInfo = myStrategy.getErrorInfo(); - myView.showErrors(errorInfo.getSecond(), errorInfo.first); - myView.setBalloons(myStrategy.getBalloonsToShow()); - - final SuggestionInfo suggestionInfo = myStrategy.getSuggestionInfo(); - final List suggestions = new ArrayList(suggestionInfo.getSuggestions()); - - final String lastPart = getLastPart(); - if ((lastPart != null) && myStrategy.isUnknownTextExists()) { - //Filter to starts from - final Iterator iterator = suggestions.iterator(); - while (iterator.hasNext()) { - final String textToCheck = iterator.next(); - - if (!textToCheck.startsWith(lastPart)) { - iterator.remove(); - } - } - } - - if (!suggestionInfo.myShowOnlyWhenRequested && !suggestions.isEmpty()) { - final SuggestionsBuilder suggestionsBuilder = getBuilderWithHistory(); - suggestionsBuilder.add(suggestions); - - myView - .displaySuggestions(suggestionsBuilder, suggestionInfo.myAbsolute, null); - } - else { - myView.removeSuggestions(); - } - } - - /** - * @return builder that already has history in its prefix group (see {@link com.jetbrains.python.suggestionList.SuggestionsBuilder}) - */ - @NotNull - private SuggestionsBuilder getBuilderWithHistory() { - return new SuggestionsBuilder(); - - // TODO: Uncomment when history would be fixed - /*final SuggestionsBuilder suggestionsBuilder = new SuggestionsBuilder(); - final List history = getHistory(); - final Collection historyCommands = new LinkedHashSet(); - for (final CommandExecutionInfo info : history) { - historyCommands.add(info.toString()); - } - - if (!historyCommands.isEmpty()) { - // TODO: Later implement folding by name - suggestionsBuilder.changeGroup(false); - suggestionsBuilder - .add(ArrayUtil.toStringArray(historyCommands)); - suggestionsBuilder.changeGroup(true); - } - - return suggestionsBuilder;*/ - } - - /** - * @return execution info from history. It is empty by default, child should implement it. - */ - @NotNull - protected List getHistory() { - return Collections.emptyList(); - } - - /** - * @return command that entered in box, or null of just entered - */ - @Nullable - protected CommandExecutionInfo getCommandToExecute() { - return myStrategy.getCommandToExecute(); - } - - /** - * Finds and sets appropriate strategy - */ - private void configureStrategy() { - final ParsedCommandLine line = getParsedCommandLine(); - if (line != null) { - final Command command = myCommands.get(line.getCommand().getText()); - if (command != null) { - myStrategy = new InCommandStrategy(command, line, this); - return; - } - } - myStrategy = new NoCommandStrategy(this); // No command or bad command found - } - - @Override - public void completionRequested(@Nullable final String valueFromSuggestionList) { - if (valueFromSuggestionList != null) { - final SuggestionInfo suggestionInfo = myStrategy.getSuggestionInfo(); - if (suggestionInfo.getSuggestions().contains(valueFromSuggestionList)) { - final ParsedCommandLine commandLine = getParsedCommandLine(); - final List words = commandLine != null ? commandLine.getAsWords() : new ArrayList(); - if (!words.isEmpty() && myView.isCaretOnWord()) { - words.remove(words.size() - 1); - } - words.add(valueFromSuggestionList); - myView.forceText(StringUtil.join(words, " ")); - } - } - myView.removeSuggestions(); - } - - @Override - public void suggestionRequested() { - final SuggestionInfo suggestionInfo = myStrategy.getSuggestionInfo(); - final List suggestions = suggestionInfo.getSuggestions(); - if (!suggestions.isEmpty()) { - final SuggestionsBuilder suggestionsBuilder = getBuilderWithHistory(); - suggestionsBuilder.add(suggestions); - myView.displaySuggestions(suggestionsBuilder, suggestionInfo.myAbsolute, null); - } - } - - @Override - public void executionRequested(@Nullable final String valueFromSuggestionList) { - - } - - /** - * @return [command_name => command] all available commands - */ - @NotNull - protected final Map getCommands() { - return Collections.unmodifiableMap(myCommands); - } - - /** - * @return parsed commandline entered by user - */ - @Nullable - final ParsedCommandLine getParsedCommandLine() { - try { - return new ParsedCommandLine(myView.getText()); - } - catch (final MalformedCommandLineException ignored) { - return null; - } - } - - - /** - * @return last part of splitted text (if any). I.e. "foo bar spam" will return "spam" - */ - @Nullable - final String getLastPart() { - final ParsedCommandLine commandLine = getParsedCommandLine(); - if (commandLine == null || commandLine.getAsWords().isEmpty()) { - return null; - } - final List words = commandLine.getAsWords(); - return words.get(words.size() - 1); - } - - /** - * @return view - */ - @NotNull - CommandInterfaceView getView() { - return myView; - } - -} diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/InCommandStrategy.java b/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/InCommandStrategy.java deleted file mode 100644 index 1d3be479937a..000000000000 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/InCommandStrategy.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * 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 - * - * http://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. - */ -package com.jetbrains.python.commandInterface.commandsWithArgs; - -import com.intellij.openapi.util.Pair; -import com.intellij.util.ArrayUtil; -import com.jetbrains.python.PyBundle; -import com.jetbrains.python.commandInterface.CommandInterfaceView; -import com.jetbrains.python.commandInterface.CommandInterfaceView.SpecialErrorPlace; -import com.jetbrains.python.commandInterface.commandsWithArgs.ArgumentsValuesValidationInfo.ArgumentValueError; -import com.jetbrains.python.optParse.ParsedCommandLine; -import com.jetbrains.python.optParse.WordWithPosition; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Strategy implementation for case when user entered command - * - * @author Ilya.Kazakevich - */ -final class InCommandStrategy extends Strategy { - @NotNull - private final List myArguments = new ArrayList(); - @NotNull - private final Command myCommand; - @NotNull - private final ParsedCommandLine myCommandLine; - - /** - * @param command command enrtered by user - * @param presenter presenter - */ - InCommandStrategy(@NotNull final Command command, - @NotNull final ParsedCommandLine commandLine, - @NotNull final CommandInterfacePresenterCommandBased presenter) { - super(presenter); - myArguments.addAll(WordWithPosition.fetchText(commandLine.getArguments())); - myCommand = command; - myCommandLine = commandLine; - } - - @NotNull - @Override - public String getSubText() { - final String help = myCommand.getHelp(); - if (help != null) { - return help; - } - return "Place to display help"; - } - - @NotNull - @Override - SuggestionInfo getSuggestionInfo() { - final Argument nextArgument = myCommand.getArgumentsInfo().getArgument(myCommandLine.getArguments().size()); - if (nextArgument != null) {// TODO: Check options! - // If next arg exists - final List availableValues = nextArgument.getAvailableValues(); - if (availableValues != null) { // If has available values - return new SuggestionInfo(false, false, availableValues); - } - } - return new SuggestionInfo(false, false, Collections.emptyList()); - } - - @NotNull - @Override - List getBalloonsToShow() { - // Display argument balloons right from command end to last argument end - final ArgumentsInfo argumentsInfo = myCommand.getArgumentsInfo(); - final List arguments = myCommandLine.getArguments(); - if (arguments.isEmpty()) { - // If no arguments provided, then display first argument popup right after command - final Argument firstArgument = argumentsInfo.getArgument(0); - if (firstArgument == null) { - return Collections.emptyList(); // Looks like no argument required - } - return Collections - .singletonList(new WordWithPosition(firstArgument.getHelpText(), myCommandLine.getCommand().getTo() + 1)); - } - - final List result = new ArrayList(arguments.size()); - for (int i = 0; i < arguments.size(); i++) { - final WordWithPosition argEnteredByUser = arguments.get(i); - final Argument argument = argumentsInfo.getArgument(i); - if (argument != null) { - // Display argument help - result.add(argEnteredByUser.copyWithDifferentText(argument.getHelpText())); - } - } - return result; - } - - @Override - boolean isUnknownTextExists() { - if (myCommandLine.getAsWords().isEmpty()) { - return false; // Command only - } - final String lastPart = myPresenter.getLastPart(); - return ((lastPart != null) && !getSuggestionInfo().getSuggestions().contains(lastPart)); - } - - @Nullable - @Override - CommandExecutionInfo getCommandToExecute() { - return new CommandExecutionInfo(myCommand.getName(), ArrayUtil.toStringArray(myArguments)); - } - - @NotNull - @Override - Pair> getErrorInfo() { - final List userProvidedValues = myCommandLine.getArguments(); - SpecialErrorPlace specialError = null; - final List errors = new ArrayList(); - - final ArgumentsValuesValidationInfo validation = - myCommand.getArgumentsInfo().validateArgumentValues(WordWithPosition.fetchText(userProvidedValues)); - if (validation.isNotEnoughArguments()) { - specialError = SpecialErrorPlace.AFTER_LAST_CHAR; - } - for (final Entry errorEntry : validation.getPositionOfErrorArguments().entrySet()) { - final String errorText = (errorEntry.getValue() == ArgumentValueError.BAD_VALUE ? - PyBundle.message("commandsWithArgs.validation.badValue") : - PyBundle.message("commandsWithArgs.validation.excess") ); - errors.add(userProvidedValues.get(errorEntry.getKey()).copyWithDifferentText(errorText)); - } - - - return Pair.create(specialError, errors); - } -} diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/NoArgumentsInfo.java b/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/NoArgumentsInfo.java deleted file mode 100644 index d08e8ccb16f8..000000000000 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/NoArgumentsInfo.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * 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 - * - * http://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. - */ -package com.jetbrains.python.commandInterface.commandsWithArgs; - -import com.google.common.collect.ContiguousSet; -import com.google.common.collect.DiscreteDomain; -import com.google.common.collect.Range; -import com.intellij.util.containers.hash.HashMap; -import com.jetbrains.python.commandInterface.commandsWithArgs.ArgumentsValuesValidationInfo.ArgumentValueError; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Map; - -/** - * Case when command has no arguments (for sure!) - * - * @author Ilya.Kazakevich - */ -public final class NoArgumentsInfo implements ArgumentsInfo { - /** - * Instance to use when command has no arguments - */ - public static final ArgumentsInfo INSTANCE = new NoArgumentsInfo(); - - private NoArgumentsInfo() { - } - - @Nullable - @Override - public Argument getArgument(final int argumentPosition) { - return null; - } - - @NotNull - @Override - public ArgumentsValuesValidationInfo validateArgumentValues(@NotNull final List argumentValuesToCheck) { - if (argumentValuesToCheck.isEmpty()) { - return ArgumentsValuesValidationInfo.NO_ERROR; - } - final Map errors = - new HashMap(); - for (final int errorPosition : ContiguousSet.create(Range.closedOpen(0, argumentValuesToCheck.size()), DiscreteDomain.integers())) { - errors.put(errorPosition, ArgumentValueError.EXCESS); - } - return new ArgumentsValuesValidationInfo(errors, false); - } -} diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/NoCommandStrategy.java b/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/NoCommandStrategy.java deleted file mode 100644 index 16ea46fe9fc6..000000000000 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/NoCommandStrategy.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * 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 - * - * http://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. - */ -package com.jetbrains.python.commandInterface.commandsWithArgs; - -import com.intellij.openapi.util.Pair; -import com.jetbrains.python.PyBundle; -import com.jetbrains.python.commandInterface.CommandInterfaceView.SpecialErrorPlace; -import com.jetbrains.python.optParse.WordWithPosition; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Strategy implementation for case when no command parsed - * - * @author Ilya.Kazakevich - */ -final class NoCommandStrategy extends Strategy { - - private static final Pair> - NO_ERROR = Pair.create(null, Collections.emptyList()); - - NoCommandStrategy(@NotNull final CommandInterfacePresenterCommandBased presenter) { - super(presenter); - } - - @NotNull - @Override - String getSubText() { - return PyBundle.message("commandsWithArgs.enterCommand.label"); - } - - @NotNull - @Override - SuggestionInfo getSuggestionInfo() { - return new SuggestionInfo(true, isTextBoxEmpty(), new ArrayList(myPresenter.getCommands().keySet())); - } - - @Override - boolean isUnknownTextExists() { - return !myPresenter.getView().getText().isEmpty(); - } - - - @NotNull - @Override - Pair> getErrorInfo() { - // No error if textbox empty, but mark everything as error if some text entered: it is junk (it can't be command, - // InCommand strategy were selected otherwise) - return isTextBoxEmpty() ? NO_ERROR : Pair.create(SpecialErrorPlace.WHOLE_TEXT, Collections.emptyList()); - } - - private boolean isTextBoxEmpty() { - return myPresenter.getView().getText().isEmpty(); - } - - @Nullable - @Override - CommandExecutionInfo getCommandToExecute() { - return null; - } -} diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/Strategy.java b/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/Strategy.java deleted file mode 100644 index 231cf0207c17..000000000000 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/Strategy.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * 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 - * - * http://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. - */ -package com.jetbrains.python.commandInterface.commandsWithArgs; - -import com.intellij.openapi.util.Pair; -import com.jetbrains.python.commandInterface.CommandInterfaceView.SpecialErrorPlace; -import com.jetbrains.python.optParse.WordWithPosition; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.List; - -/** - * Strategy that helps {@link com.jetbrains.python.commandInterface.commandsWithArgs.CommandInterfacePresenterCommandBased} - * to deal with 2 states: when no text entered (or some junk enetered) or command name entered - * - * @author Ilya.Kazakevich - */ -abstract class Strategy { - @NotNull - protected final CommandInterfacePresenterCommandBased myPresenter; - - /** - * @param presenter presenter - */ - protected Strategy(@NotNull final CommandInterfacePresenterCommandBased presenter) { - myPresenter = presenter; - } - - /** - * @return sub text to display - */ - @NotNull - abstract String getSubText(); - - /** - * @return suggestions - */ - @NotNull - abstract SuggestionInfo getSuggestionInfo(); - - // TODO: Merge baloon and error (actually the same) - @NotNull - List getBalloonsToShow() { - return Collections.emptyList(); - } - - /** - * @return command that entered in box, or null of just entered - */ - @Nullable - abstract CommandExecutionInfo getCommandToExecute(); - - - /** - * @return errors - */ - @NotNull - abstract Pair> getErrorInfo(); - - /** - * @return if text entered by user contains some unknown commands - */ - abstract boolean isUnknownTextExists(); -} diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/SuggestionInfo.java b/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/SuggestionInfo.java deleted file mode 100644 index c761d8d34619..000000000000 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/SuggestionInfo.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * 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 - * - * http://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. - */ -package com.jetbrains.python.commandInterface.commandsWithArgs; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Information abouyt suggestion, provided by {@link com.jetbrains.python.commandInterface.commandsWithArgs.Strategy} - * - * @author Ilya.Kazakevich - */ -@SuppressWarnings("PackageVisibleField") -// No do not need to hide field: everything is internal API in package, anyway -final class SuggestionInfo { - /** - * Suggestions - */ - private final List mySuggestions = new ArrayList(); - /** - * Display them at absolute location or relative to last letter - */ - final boolean myAbsolute; - /** - * Show then any time, or only when user requests them - */ - final boolean myShowOnlyWhenRequested; - - /** - * @param absolute Display them at absolute location or relative to last letter - * @param showOnlyWhenRequested Show then any time, or only when user requests them - * @param suggestions Suggestions - */ - SuggestionInfo(final boolean absolute, - final boolean showOnlyWhenRequested, - @NotNull final List suggestions) { - myAbsolute = absolute; - myShowOnlyWhenRequested = showOnlyWhenRequested; - mySuggestions.addAll(suggestions); - } - - /** - * @return suggestions - */ - @NotNull - List getSuggestions() { - return Collections.unmodifiableList(mySuggestions); - } -} diff --git a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/package-info.java b/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/package-info.java deleted file mode 100644 index 2198b00dea60..000000000000 --- a/python/src/com/jetbrains/python/commandInterface/commandsWithArgs/package-info.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * 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 - * - * http://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. - */ - -/** - *

Optparse-based commandline interface presenter

- *

- * Command-line like interface presenter that uses conception of command and its arguments. - * See {@link com.jetbrains.python.commandInterface.commandsWithArgs.CommandInterfacePresenterCommandBased} - * and its arguments: {@link com.jetbrains.python.commandInterface.commandsWithArgs.Argument}. - * - * It supports optparse terminology, so - * read it first and use {@link com.jetbrains.python.optParse} package - *

- *

Arguments and validation

- *

- * Optparse arguments are positional and unnamed. - * Each {@link com.jetbrains.python.commandInterface.commandsWithArgs.Command command} provides - * {@link com.jetbrains.python.commandInterface.commandsWithArgs.ArgumentsInfo arguments info}. - * It can be used to obtain information about argument (like list of possible values) and it also used to validate argument values, - * provided by user. In most cases we have no idea about arguments: due to optparse limitations only help test is available. - * But sometimes we do know (like when args are documented). - * Different strategies exist, so be sure to check {@link com.jetbrains.python.commandInterface.commandsWithArgs.ArgumentsInfo} children - *

- * - * - * @see com.jetbrains.python.optParse - * @author Ilya.Kazakevich - */ -package com.jetbrains.python.commandInterface.commandsWithArgs; \ No newline at end of file diff --git a/python/src/com/jetbrains/python/commandInterface/package-info.java b/python/src/com/jetbrains/python/commandInterface/package-info.java index 19c0b13ac979..7f1581af3bc7 100644 --- a/python/src/com/jetbrains/python/commandInterface/package-info.java +++ b/python/src/com/jetbrains/python/commandInterface/package-info.java @@ -19,7 +19,7 @@ * It has several abilities, including (but not limited): *
    *
  1. Suggestion box
  2. - *
  3. Error marking
  4. + *
  5. Error emphasising
  6. *
  7. Popups
  8. *
  9. AutoCompletion
  10. *
@@ -31,9 +31,15 @@ * *

* There is also swing-based view implementation in {@link com.jetbrains.python.commandInterface.swingView} - * and presenter implementation based on idea of commands with arguments. See {@link com.jetbrains.python.commandInterface.commandsWithArgs} + * and presenter implementation based on idea of commands with arguments. See {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter} *

* + *

+ * Presenter and View talk to each other only in term of text and chars. Presenter knows nothing about pixels and should never + * assume view have certain pixel size. + *

+ * + * * * @author Ilya.Kazakevich */ diff --git a/python/src/com/jetbrains/python/commandInterface/swingView/CommandInterfaceViewSwingImpl.java b/python/src/com/jetbrains/python/commandInterface/swingView/CommandInterfaceViewSwingImpl.java index 3071a8dc8665..955d07ee6580 100644 --- a/python/src/com/jetbrains/python/commandInterface/swingView/CommandInterfaceViewSwingImpl.java +++ b/python/src/com/jetbrains/python/commandInterface/swingView/CommandInterfaceViewSwingImpl.java @@ -17,16 +17,18 @@ package com.jetbrains.python.commandInterface.swingView; import com.google.common.base.Preconditions; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; -import com.intellij.openapi.fileEditor.impl.EditorWindow; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.*; import com.intellij.openapi.ui.popup.Balloon.Position; import com.intellij.ui.JBColor; import com.intellij.ui.awt.RelativePoint; +import com.intellij.util.Range; +import com.intellij.util.containers.HashSet; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.WordWithPosition; import com.jetbrains.python.commandInterface.CommandInterfacePresenter; import com.jetbrains.python.commandInterface.CommandInterfaceView; -import com.jetbrains.python.optParse.WordWithPosition; import com.jetbrains.python.suggestionList.SuggestionList; import com.jetbrains.python.suggestionList.SuggestionsBuilder; import org.jetbrains.annotations.NotNull; @@ -37,19 +39,31 @@ import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import javax.swing.text.BadLocationException; import java.awt.*; -import java.awt.event.*; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Set; /** - * Command-interface view implementation based on Swing + * Command-interface view implementation based on Swing. + * It uses balloons to display errors and infos, drop-down for suggestions and also underlines errors * * @author Ilya.Kazakevich */ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements CommandInterfaceView, DocumentListener, CaretListener { private static final JBColor ERROR_COLOR = JBColor.RED; + + /** + * We need to track balloons, so we have field with callback + */ + @NotNull + private final BalloonManager myBalloonManager = new BalloonManager(); /** * Pop-up we displayed in */ @@ -68,27 +82,38 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com * Lower (sub) label */ private JLabel mySubLabel; + /** + * "Suggestion area". Suggestion status is displayed when caret meets this area + */ + private final List> myPlacesWhereSuggestionsAvailable = new ArrayList>(); @NotNull private final CommandInterfacePresenter myPresenter; /** * List to display suggestions */ @NotNull - private final SuggestionList myList; + private final SuggestionList mySuggestionList; /** * Displayed when there is no text */ @Nullable private final String myPlaceHolderText; + /** - * Flag that indicates we are in "test forced" mode: current text set by presenter, not by user + * Information balloons that should be displayed when caret meets their boundaries. */ - private boolean myInForcedTextMode; - // TODO: Doc - @NotNull - private final List myBalloons = new ArrayList(); @NotNull + private final List myInfoBalloons = new ArrayList(); + /** + * Error balloons that should be displayed when caret meets their boundaries. + * Errors are always underlined, but balloons are displayed only if caret meets error + */ private final List myErrorBalloons = new ArrayList(); + /** + * Default subtext to display when caret is out of {@link #myPlacesWhereSuggestionsAvailable "suggestion" area} + */ + @Nullable + private String myDefaultSubText; /** * @param presenter our presenter @@ -112,18 +137,11 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com myMainTextField.setFocusable(true); - final EditorWindow window = FileEditorManagerEx.getInstanceEx(project).getCurrentWindow(); - final int windowSize; - if (window != null) { - windowSize = window.getSize().width; - } - else { - windowSize = 0; // Windows size is unknown - } + final int windowWidth = FileEditorManagerEx.getInstanceEx(project).getComponent().getRootPane().getWidth() - 10; // Little gap myMainTextField - .setPreferredWidthInPx(windowSize); - myList = new SuggestionList(new MySuggestionListListener()); + .setPreferredWidthInPx(windowWidth); + mySuggestionList = new SuggestionList(new MySuggestionListListener()); } @@ -145,7 +163,12 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com } }); myMainTextField.setFocusTraversalKeysEnabled(false); - myMainTextField.addKeyListener(new MyKeyListener()); + myMainTextField.addKeyListener(new MyKeyListener()); // Up/down arrows are not handles with actions + + // Register all available actions + for (final KeyStrokeInfo strokeInfo : KeyStrokeInfo.values()) { + strokeInfo.register(myPresenter, mySuggestionList, myMainTextField); + } myMainPopUp.showInFocusCenter(); } @@ -154,32 +177,18 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com public void displaySuggestions(@NotNull final SuggestionsBuilder suggestions, final boolean absolute, @Nullable final String toSelect) { int left = 0; - // Display text right after line ends if not in "absolute" mode + // Display text right after caret if (!absolute) { - left = myMainTextField.getTextEndPosition(); + left = myMainTextField.getTextCaretPositionInPx(); } - myList.showSuggestions(suggestions, new RelativePoint(myPanel, new Point(left, myPanel.getHeight())), toSelect); + mySuggestionList.showSuggestions(suggestions, new RelativePoint(myPanel, new Point(left, myPanel.getHeight())), toSelect); + configureAppropriateStatus(); } - @Override public void onClosed(final LightweightWindowEvent event) { super.onClosed(event); - myList.close(); - } - - @Override - public final void showErrors(@NotNull final List errors, @Nullable final SpecialErrorPlace specialErrorPlace) { - for (final WordWithPosition error : errors) { - myMainTextField.underlineText(ERROR_COLOR, error.getFrom(), error.getTo()); - } - if (specialErrorPlace != null) { - myMainTextField.underlineText(specialErrorPlace, ERROR_COLOR); - } - synchronized (myErrorBalloons) { - myErrorBalloons.clear(); - myErrorBalloons.addAll(errors); - } + mySuggestionList.close(); } @@ -188,6 +197,7 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com processDocumentChange(); } + @Override public void removeUpdate(final DocumentEvent e) { processDocumentChange(); @@ -195,7 +205,7 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com private void processDocumentChange() { myMainTextField.hideUnderline(); - myPresenter.textChanged(myInForcedTextMode); + myPresenter.textChanged(); } @Override @@ -204,75 +214,155 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com } @Override - public void forceText(@NotNull final String newText) { - myInForcedTextMode = true; - myMainTextField.setText(newText); - myInForcedTextMode = false; + public void removeSuggestions() { + mySuggestionList.close(); + configureAppropriateStatus(); } - @Override - public void removeSuggestions() { - myList.close(); - } @Override public final void caretUpdate(final CaretEvent e) { - // TODO: Stupid copy/paste, fix by method extract // When caret moved, we need to check if balloon has to be displayed - synchronized (myErrorBalloons) { - showBaloons(myErrorBalloons, Position.below, MessageType.ERROR); + displayBalloonsIfRequired(); + configureAppropriateStatus(); + } + + private void configureAppropriateStatus() { + if (!mySuggestionList.isClosed()) { + // Tell user she may use TAB to complete + mySubLabel.setText(PyBundle.message("commandLine.subText.key.complete", KeyStrokeInfo.COMPLETION.getText())); + return; } - synchronized (myBalloons) { - showBaloons(myBalloons, Position.above, MessageType.INFO); + + // If we are in "suggestion available" place -- tell it + for (final Range range : myPlacesWhereSuggestionsAvailable) { + final boolean specialCaseAfterLastChar = isAfterLastCharRange(range) && getCaretPosition() == myMainTextField.getText().length(); + if (range.isWithin(getCaretPosition()) || specialCaseAfterLastChar) { + mySubLabel.setText(PyBundle.message("commandLine.subText.key.suggestions", KeyStrokeInfo.SUGGESTION.getText())); + return; + } + } + + // We may simply tell user she may execute command + if (myDefaultSubText != null) { + mySubLabel.setText(PyBundle.message("commandLine.subText.key.executeCommand", KeyStrokeInfo.EXECUTION.getText(), myDefaultSubText)); + } + else { + mySubLabel.setText(PyBundle.message("commandLine.subText.key.executeUnknown", KeyStrokeInfo.EXECUTION.getText())); } } - // TODO: Doc - private void showBaloons(@NotNull final List balloons, - @NotNull final Position popUpPosition, - @NotNull final MessageType messageType) { + + private void displayBalloonsIfRequired() { + synchronized (myErrorBalloons) { + if (mySuggestionList.isClosed()) { // No need to display error popups when suggestion list is displayed. It intersects. + showBalloons(myErrorBalloons, Position.below, MessageType.ERROR); + } + } + synchronized (myInfoBalloons) { + showBalloons(myInfoBalloons, Position.above, MessageType.INFO); + } + } + + /** + * Displays some balloons + * + * @param balloons balloons to display + * @param popUpPosition where ti display them. Only {@link Position#above} and {@link Position#below} are supported! + * @param messageType may be {@link MessageType#ERROR} or {@link MessageType#INFO} for example + */ + private void showBalloons(@NotNull final List balloons, + @NotNull final Position popUpPosition, + @NotNull final MessageType messageType) { Preconditions.checkArgument(popUpPosition == Position.above || popUpPosition == Position.below, "Only above or below is supported"); for (final WordWithPosition balloon : balloons) { if (balloon.getText().isEmpty()) { continue; // Can't be displayed if empty } final int caretPosition = myMainTextField.getCaretPosition(); - if (caretPosition >= balloon.getFrom() && caretPosition <= balloon.getTo()) { + if ((caretPosition >= balloon.getFrom() && caretPosition <= balloon.getTo())) { final int top = (popUpPosition == Position.above ? 0 : myMainTextField.getHeight() * 2); // Display below a little bit lower - final RelativePoint point = new RelativePoint(myMainTextField, new Point(myMainTextField.getTextCursorPosition(), top)); + final RelativePoint point = new RelativePoint(myMainTextField, new Point(myMainTextField.getTextCaretPositionInPx(), top)); final Balloon balloonToShow = JBPopupFactory.getInstance().createBalloonBuilder(new JLabel(balloon.getText())).setFillColor(messageType.getPopupBackground()) .createBalloon(); balloonToShow.setAnimationEnabled(false); + myBalloonManager.registerBalloon(balloonToShow); balloonToShow.show(point, popUpPosition); } } } @Override - public final boolean isCaretOnWord() { - final int caretPosition = myMainTextField.getCaretPosition(); - if (caretPosition == 0) { - return false; // At the beginning of the line + public final void setInfoAndErrors(@NotNull final Collection infoBalloons, + @NotNull final Collection errors) { + synchronized (myInfoBalloons) { + myInfoBalloons.clear(); + myInfoBalloons.addAll(infoBalloons); } - return !Character.isWhitespace(myMainTextField.getText().toCharArray()[caretPosition - 1]); + synchronized (myErrorBalloons) { + myErrorBalloons.clear(); + myErrorBalloons.addAll(errors); + } + for (final WordWithPosition error : errors) { + if (isAfterLastCharRange(error)) { + // In "special" case we use last char + myMainTextField.underlineText(ERROR_COLOR, myMainTextField.getText().length(), myMainTextField.getText().length() + 1); + } + else { + myMainTextField.underlineText(ERROR_COLOR, error.getFrom(), error.getTo()); + } + } + } + + /** + * Checks if some range is special case {@link #AFTER_LAST_CHARACTER_RANGE}. + * + * @param range range to check + * @return true if special case + */ + private static boolean isAfterLastCharRange(@NotNull final Range range) { + return AFTER_LAST_CHARACTER_RANGE.getFrom().equals(range.getFrom()) && AFTER_LAST_CHARACTER_RANGE.getTo().equals(range.getTo()); } @Override - public void setBalloons(@NotNull final Collection balloons) { - synchronized (myBalloons) { - myBalloons.clear(); - myBalloons.addAll(balloons); + public final void insertTextAfterCaret(@NotNull final String text) { + try { + myMainTextField.getDocument().insertString(myMainTextField.getCaretPosition(), text, null); + } + catch (final BadLocationException e) { + // TODO: Display error somehow! + e.printStackTrace(); } } @Override - public void setSubText(@NotNull final String subText) { - mySubLabel.setText(subText); + public final void replaceText(final int from, final int to, @NotNull final String newText) { + myMainTextField.select(from, to); + myMainTextField.replaceSelection(newText); + myBalloonManager.closeAllBalloons(); + myPresenter.textChanged(); + displayBalloonsIfRequired(); // This crunch but we need to recalculate balloons in this case (position is changed!) } + @Override + public final int getCaretPosition() { + return myMainTextField.getCaretPosition(); + } + + + @Override + public final void configureSubTexts(@Nullable final String defaultSubText, + @NotNull final List> suggestionAvailablePlaces) { + synchronized (myPlacesWhereSuggestionsAvailable) { + myPlacesWhereSuggestionsAvailable.clear(); + myPlacesWhereSuggestionsAvailable.addAll(suggestionAvailablePlaces); + myDefaultSubText = defaultSubText; + } + configureAppropriateStatus(); + } /** * Reacts on keys, pressed by user @@ -281,21 +371,13 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com @Override public void keyPressed(final KeyEvent e) { super.keyPressed(e); + final int keyCode = e.getKeyCode(); if (keyCode == KeyEvent.VK_UP) { - myList.moveSelection(true); + mySuggestionList.moveSelection(true); } else if (keyCode == KeyEvent.VK_DOWN) { - myList.moveSelection(false); - } - else if (keyCode == KeyEvent.VK_ENTER) { - myPresenter.executionRequested(myList.getValue()); - } - else if (keyCode == KeyEvent.VK_TAB) { - myPresenter.completionRequested(myList.getValue()); - } - else if ((keyCode == KeyEvent.VK_SPACE) && (e.getModifiersEx() == InputEvent.CTRL_DOWN_MASK)) { - myPresenter.suggestionRequested(); + mySuggestionList.moveSelection(false); } } } @@ -306,10 +388,6 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com return myMainTextField.getText(); } - @Override - public void setPreferredWidthInChars(final int widthInChars) { - myMainTextField.setPreferredWidthInChars(widthInChars); - } /** * Listener for suggestion list @@ -322,4 +400,35 @@ public class CommandInterfaceViewSwingImpl extends JBPopupAdapter implements Com removeSuggestions(); } } + + /** + * Keeps tracks for baloons to close all of them in case of text inserion + */ + private static final class BalloonManager extends JBPopupAdapter { + @NotNull + private final Set myCurrentBaloons = new HashSet(); + + void registerBalloon(final Balloon balloon) { + synchronized (myCurrentBaloons) { + myCurrentBaloons.add(balloon); + balloon.addListener(this); + } + } + + @Override + public void onClosed(final LightweightWindowEvent event) { + synchronized (myCurrentBaloons) { + myCurrentBaloons.remove(event.asBalloon()); + } + super.onClosed(event); + } + + void closeAllBalloons() { + synchronized (myCurrentBaloons) { + for (final Balloon balloon : myCurrentBaloons) { + balloon.dispose(); + } + } + } + } } diff --git a/python/src/com/jetbrains/python/commandInterface/swingView/CompletionKeyStrokeAction.java b/python/src/com/jetbrains/python/commandInterface/swingView/CompletionKeyStrokeAction.java new file mode 100644 index 000000000000..8ed0c5af06ed --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/swingView/CompletionKeyStrokeAction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.swingView; + +import java.awt.event.ActionEvent; + +/** + * "Complete current command or argument" action + * + * @author Ilya.Kazakevich + */ +final class CompletionKeyStrokeAction extends KeyStrokeAction { + CompletionKeyStrokeAction() { + super(KeyStrokeInfo.COMPLETION); + } + + @Override + public void actionPerformed(final ActionEvent e) { + myPresenter.completionRequested(mySuggestionList.getValue()); + } +} diff --git a/python/src/com/jetbrains/python/commandInterface/swingView/ExecutionKeyStrokeAction.java b/python/src/com/jetbrains/python/commandInterface/swingView/ExecutionKeyStrokeAction.java new file mode 100644 index 000000000000..7f258822a968 --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/swingView/ExecutionKeyStrokeAction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.swingView; + +import java.awt.event.ActionEvent; + +/** + * "Execute command" action + * + * @author Ilya.Kazakevich + */ +final class ExecutionKeyStrokeAction extends KeyStrokeAction { + ExecutionKeyStrokeAction() { + super(KeyStrokeInfo.EXECUTION); + } + + @Override + public void actionPerformed(final ActionEvent e) { + myPresenter.executionRequested(); + } +} diff --git a/python/src/com/jetbrains/python/commandInterface/swingView/KeyStrokeAction.java b/python/src/com/jetbrains/python/commandInterface/swingView/KeyStrokeAction.java new file mode 100644 index 000000000000..a801580a5bf6 --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/swingView/KeyStrokeAction.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.swingView; + +import com.jetbrains.python.commandInterface.CommandInterfacePresenter; +import com.jetbrains.python.suggestionList.SuggestionList; +import org.jetbrains.annotations.NotNull; + +import javax.swing.text.TextAction; + +/** + * Action that should be taken for certain {@link javax.swing.KeyStroke} (wrapped in {@link com.jetbrains.python.commandInterface.swingView.KeyStrokeInfo}). + * You need to call {@link #configure(com.jetbrains.python.commandInterface.CommandInterfacePresenter, com.jetbrains.python.suggestionList.SuggestionList)} + * to enable one. + * + * @author Ilya.Kazakevich + */ +@SuppressWarnings({"InstanceVariableMayNotBeInitialized", "NonSerializableFieldInSerializableClass"}) // Will never serialize +abstract class KeyStrokeAction extends TextAction { + @NotNull + private final String myName; + @NotNull + private final KeyStrokeInfo myStroke; + + protected CommandInterfacePresenter myPresenter; + protected SuggestionList mySuggestionList; + + /** + * @param stroke key stroke to bind this info to + */ + KeyStrokeAction(@NotNull final KeyStrokeInfo stroke) { + super(stroke.name()); + myName = stroke.name(); + myStroke = stroke; + } + + + /** + * Configures action. + * + * @param presenter presenter to be used for call back. + * @param suggestionList list of suggestions to be used for call back + * @return name of this action to add to {@link javax.swing.InputMap} + */ + @NotNull + final String configure(@NotNull final CommandInterfacePresenter presenter, @NotNull final SuggestionList suggestionList) { + myPresenter = presenter; + mySuggestionList = suggestionList; + return myName; + } + + /** + * @return stroke bound to this action + */ + @NotNull + final KeyStrokeInfo getStroke() { + return myStroke; + } +} diff --git a/python/src/com/jetbrains/python/commandInterface/swingView/KeyStrokeInfo.java b/python/src/com/jetbrains/python/commandInterface/swingView/KeyStrokeInfo.java new file mode 100644 index 000000000000..dcf8278e6612 --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/swingView/KeyStrokeInfo.java @@ -0,0 +1,99 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.swingView; + +import com.intellij.openapi.keymap.KeymapUtil; +import com.jetbrains.python.commandInterface.CommandInterfacePresenter; +import com.jetbrains.python.suggestionList.SuggestionList; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +/** + * Key strokes to be used with view. + * Strokes paired with action. You need to register each action via {@link javax.swing.InputMap} + * + * @author Ilya.Kazakevich + */ +enum KeyStrokeInfo { + /** + * "Execute command" keystroke + */ + EXECUTION(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)), + /** + * "Complete current command or argument" keystroke + */ + COMPLETION(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0)), + /** + * "Display suggestions" keystroke. + */ + SUGGESTION(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_MASK)),; + + /** + * List of actions. Each action should be bound to some {@link com.jetbrains.python.commandInterface.swingView.KeyStrokeInfo} + */ + @NotNull + private static final KeyStrokeAction[] ACTIONS = { + new CompletionKeyStrokeAction(), + new ExecutionKeyStrokeAction(), + new SuggestionKeyStrokeAction()}; + + private final KeyStroke myStroke; + + KeyStrokeInfo(@NotNull final KeyStroke stroke) { + myStroke = stroke; + } + + /** + * Registers action and binds it appropriate stroke. Call if for all instances to make all actions available. + * + * @param presenter presenter to be used as call back + * @param suggestionList suggestion list to be used as call back + * @param source Component with {@link javax.swing.InputMap} and {@link javax.swing.ActionMap} (swing view itself) + */ + void register(@NotNull final CommandInterfacePresenter presenter, + @NotNull final SuggestionList suggestionList, + @NotNull final JComponent source) { + final KeyStrokeAction action = getAction(); + final String strokeName = action.configure(presenter, suggestionList); + source.getInputMap().put(myStroke, strokeName); + source.getActionMap().put(strokeName, action); + } + + /** + * @return Human-readable name of this action (to display it to user) + */ + @NotNull + String getText() { + return KeymapUtil.getKeystrokeText(myStroke); + } + + + /** + * @return action paired with stroke + */ + @NotNull + private KeyStrokeAction getAction() { + for (final KeyStrokeAction action : ACTIONS) { + if (action.getStroke() == this) { + return action; + } + } + throw new IllegalStateException("Failed to find action for " + name()); + } +} diff --git a/python/src/com/jetbrains/python/commandInterface/swingView/SmartTextField.java b/python/src/com/jetbrains/python/commandInterface/swingView/SmartTextField.java index 0a247bbcec81..2e7ff1a07352 100644 --- a/python/src/com/jetbrains/python/commandInterface/swingView/SmartTextField.java +++ b/python/src/com/jetbrains/python/commandInterface/swingView/SmartTextField.java @@ -17,13 +17,9 @@ package com.jetbrains.python.commandInterface.swingView; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorFontType; -import com.intellij.openapi.util.Pair; import com.intellij.util.Range; import com.intellij.util.ui.StatusText; -import com.jetbrains.python.commandInterface.CommandInterfaceView; -import com.jetbrains.python.commandInterface.CommandInterfaceView.SpecialErrorPlace; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -47,11 +43,6 @@ public class SmartTextField extends JTextField { @NotNull private final Collection myUnderlineInfo = new ArrayList(); private int myPreferredWidth; - /** - * (color, special_place) tuple to underline special place, or null if no underline required - */ - @Nullable - private Pair mySpecialUnderlinePlace; public SmartTextField() { setFont(EditorColorsManager.getInstance().getGlobalScheme().getFont(EditorFontType.CONSOLE_PLAIN)); @@ -66,15 +57,9 @@ public class SmartTextField extends JTextField { synchronized (myUnderlineInfo) { for (final UnderlineInfo underlineInfo : myUnderlineInfo) { g.setColor(underlineInfo.myColor); + // To prevent too long underlines: last char should really be last underline(g, underlineInfo.getFrom(), underlineInfo.getTo()); } - if (mySpecialUnderlinePlace != null) { - final SpecialErrorPlace place = mySpecialUnderlinePlace.second; - g.setColor(mySpecialUnderlinePlace.first); - final int endPosition = getTextEndPosition(); - final int from = (place == SpecialErrorPlace.WHOLE_TEXT ? 0 : endPosition - getColumnWidth()); - underline(g, from, endPosition); - } } } @@ -90,17 +75,11 @@ public class SmartTextField extends JTextField { g.drawLine(from + getColumnWidth(), verticalPosition, to + getColumnWidth(), verticalPosition); } - /** - * @return place (in px) where entered text ends. - */ - int getTextEndPosition() { - return (getText().length() + 1) * getColumnWidth(); - } /** * @return place (in px) where caret. */ - int getTextCursorPosition() { + int getTextCaretPositionInPx() { return (getCaretPosition() + 1) * getColumnWidth(); } @@ -139,19 +118,9 @@ public class SmartTextField extends JTextField { void hideUnderline() { synchronized (myUnderlineInfo) { myUnderlineInfo.clear(); - mySpecialUnderlinePlace = null; } } - /** - * Sets appropriate width in chars - * - * @param widthInChars num of chars - */ - void setPreferredWidthInChars(final int widthInChars) { - setColumns(widthInChars); - } - /** * Sets appropriate width in pixels * @@ -161,18 +130,6 @@ public class SmartTextField extends JTextField { myPreferredWidth = width; } - /** - * Display underline in special place - * - * @param color color to underline - * @param specialUnderlinePlace special place to underline - */ - void underlineText(@NotNull final SpecialErrorPlace specialUnderlinePlace, - @NotNull final Color color) { - synchronized (myUnderlineInfo) { - mySpecialUnderlinePlace = Pair.create(color, specialUnderlinePlace); - } - } /** * Wrapper to display placeholder diff --git a/python/src/com/jetbrains/python/commandInterface/swingView/SuggestionKeyStrokeAction.java b/python/src/com/jetbrains/python/commandInterface/swingView/SuggestionKeyStrokeAction.java new file mode 100644 index 000000000000..088ff8c3fe27 --- /dev/null +++ b/python/src/com/jetbrains/python/commandInterface/swingView/SuggestionKeyStrokeAction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandInterface.swingView; + +import java.awt.event.ActionEvent; + +/** + * "Suggestion request" action + * + * @author Ilya.Kazakevich + */ +final class SuggestionKeyStrokeAction extends KeyStrokeAction { + SuggestionKeyStrokeAction() { + super(KeyStrokeInfo.SUGGESTION); + } + + @Override + public void actionPerformed(final ActionEvent e) { + myPresenter.suggestionRequested(); + } +} diff --git a/python/src/com/jetbrains/python/commandLineParser/CommandLineParseResult.java b/python/src/com/jetbrains/python/commandLineParser/CommandLineParseResult.java new file mode 100644 index 000000000000..a2a9756b0c26 --- /dev/null +++ b/python/src/com/jetbrains/python/commandLineParser/CommandLineParseResult.java @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandLineParser; + +import com.intellij.openapi.util.Pair; +import com.jetbrains.python.WordWithPosition; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Command line parse result. + * It consists of command itself and its parts. + * Each part may be {@link com.jetbrains.python.commandLineParser.CommandLinePartType#ARGUMENT argument} or + * {@link com.jetbrains.python.commandLineParser.CommandLinePartType#OPTION option} or something else. + * + * @author Ilya.Kazakevich + */ +public final class CommandLineParseResult { + @NotNull + private final List> myParts = new ArrayList>(); + @NotNull + private final WordWithPosition myCommand; + + CommandLineParseResult( + @NotNull final WordWithPosition command, + @NotNull final Collection> parts) { + myCommand = command; + myParts.addAll(parts); + } + + /** + * @return command (i.e. "startapp" in "startapp my_app") + */ + @NotNull + public WordWithPosition getCommand() { + return myCommand; + } + + /** + * @return list of parts in format [part_type, value]. + * For example (rm my_folder): [{@link com.jetbrains.python.commandLineParser.CommandLinePartType#ARGUMENT argument}, my_folder] + */ + @NotNull + public List> getParts() { + return Collections.unmodifiableList(myParts); + } + + /** + * @return all command line parts with out of part information (just words and positions). + * Note tha command itself is not part, only args and options are + * @see #getParts() + */ + @NotNull + public Collection getPartsNoType() { + final Collection result = new ArrayList(); + for (final Pair part : myParts) { + result.add(part.second); + } + return result; + } +} diff --git a/python/src/com/jetbrains/python/commandLineParser/CommandLineParser.java b/python/src/com/jetbrains/python/commandLineParser/CommandLineParser.java new file mode 100644 index 000000000000..b3813d8dd616 --- /dev/null +++ b/python/src/com/jetbrains/python/commandLineParser/CommandLineParser.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandLineParser; + +import com.jetbrains.python.WordWithPosition; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Engine to parse command line. It understands how options and arguments are coded in certain commandline. + * It supportd {@link com.jetbrains.python.WordWithPosition} telling you exactly with part of + * command line is command or argument. That helps you to underline or emphisize some parts. + * + * @author Ilya.Kazakevich + */ +public interface CommandLineParser { + /** + * @param commandLineParts command line splitted into words. + * @return command line information + * @throws MalformedCommandLineException in case of bad commandline + */ + @NotNull + CommandLineParseResult parse(@NotNull List commandLineParts) throws MalformedCommandLineException; +} diff --git a/python/src/com/jetbrains/python/commandLineParser/CommandLinePartType.java b/python/src/com/jetbrains/python/commandLineParser/CommandLinePartType.java new file mode 100644 index 000000000000..0f2359e15087 --- /dev/null +++ b/python/src/com/jetbrains/python/commandLineParser/CommandLinePartType.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ +package com.jetbrains.python.commandLineParser; + +/** + * Types of command line parts. + * + * @author Ilya.Kazakevich + */ +public enum CommandLinePartType { + /** + * Argument (or positional, or unnamed argument) something that has only value. Like "my_folder" in "rm my_folder" + */ + ARGUMENT, + /** + * Option is named but optional parameter. Like "-l" in "ls -l". + */ + OPTION, + /** + * Some part of command line that {@link com.jetbrains.python.commandLineParser.CommandLineParser} does not understand + */ + UNKNOWN +} diff --git a/python/src/com/jetbrains/python/optParse/MalformedCommandLineException.java b/python/src/com/jetbrains/python/commandLineParser/MalformedCommandLineException.java similarity index 94% rename from python/src/com/jetbrains/python/optParse/MalformedCommandLineException.java rename to python/src/com/jetbrains/python/commandLineParser/MalformedCommandLineException.java index 3e2185230360..8f69e96bf96e 100644 --- a/python/src/com/jetbrains/python/optParse/MalformedCommandLineException.java +++ b/python/src/com/jetbrains/python/commandLineParser/MalformedCommandLineException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.python.optParse; +package com.jetbrains.python.commandLineParser; import org.jetbrains.annotations.NotNull; diff --git a/python/src/com/jetbrains/python/optParse/ParsedCommandLine.java b/python/src/com/jetbrains/python/commandLineParser/OptParseCommandLineParser.java similarity index 56% rename from python/src/com/jetbrains/python/optParse/ParsedCommandLine.java rename to python/src/com/jetbrains/python/commandLineParser/OptParseCommandLineParser.java index f4cab570ee45..ddcd0822e53c 100644 --- a/python/src/com/jetbrains/python/optParse/ParsedCommandLine.java +++ b/python/src/com/jetbrains/python/commandLineParser/OptParseCommandLineParser.java @@ -13,11 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.python.optParse; +package com.jetbrains.python.commandLineParser; +import com.intellij.openapi.util.Pair; +import com.jetbrains.python.WordWithPosition; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; // TODO: Support options and their arguments @@ -36,23 +41,17 @@ import java.util.*; * * @author Ilya.Kazakevich */ -public final class ParsedCommandLine { +public final class OptParseCommandLineParser implements CommandLineParser { @NotNull - private final WordWithPosition myCommand; - @NotNull - private final List myArguments = new ArrayList(); - - /** - * @param commandLine command line to parse - * @throws MalformedCommandLineException if malformed commandline can't be parsed - */ - public ParsedCommandLine(@NotNull final String commandLine) throws MalformedCommandLineException { - final Deque parts = new LinkedList(WordWithPosition.splitText(commandLine)); + @Override + public CommandLineParseResult parse(@NotNull final List commandLineParts) throws MalformedCommandLineException { + final Deque parts = new ArrayDeque(commandLineParts); if (parts.isEmpty()) { throw new MalformedCommandLineException("No command provided"); } - myCommand = parts.pop(); - if (myCommand.getText().startsWith("-")) { + final WordWithPosition command = parts.pop(); + final List> resultParts = new ArrayList>(); + if (command.getText().startsWith("-")) { throw new MalformedCommandLineException("Command can't start with option prefix"); } @@ -60,46 +59,13 @@ public final class ParsedCommandLine { for (final WordWithPosition part : parts) { if (part.getText().startsWith("-")) { // This is option! + resultParts.add(Pair.create(CommandLinePartType.OPTION, part)); } else { // TODO: Check optopn argument! - myArguments.add(part); + resultParts.add(Pair.create(CommandLinePartType.ARGUMENT, part)); } } + return new CommandLineParseResult(command, resultParts); } - - - /** - * @return command (i.e. "startapp" in "startapp my_app") - */ - @NotNull - public WordWithPosition getCommand() { - return myCommand; - } - - /** - * @return all arguments (not options or option arguments!) - */ - @NotNull - public List getArguments() { - return Collections.unmodifiableList(myArguments); - } - - /** - * @return all parts for command line as simple words - */ - @NotNull - public List getAsWords() { - final List result = new ArrayList(); - - result.add(myCommand.getText()); - - for (final WordWithPosition argument : myArguments) { - result.add(argument.getText()); - } - // TODO: Add options as well - return result; - } - - // TODO: Add options, arguments and option arguments } diff --git a/python/src/com/jetbrains/python/commandLineParser/package-info.java b/python/src/com/jetbrains/python/commandLineParser/package-info.java new file mode 100644 index 000000000000..bbdd192167f4 --- /dev/null +++ b/python/src/com/jetbrains/python/commandLineParser/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * 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 + * + * http://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. + */ + +/** + * Engine to parse command-line. + * Command line consists of command itself, {@link com.jetbrains.python.commandLineParser.CommandLinePartType#ARGUMENT arguments} + * and {@link com.jetbrains.python.commandLineParser.CommandLinePartType#OPTION options}. + * Use need to split command line into {@link com.jetbrains.python.WordWithPosition chunks}, pass them to + * {@link com.jetbrains.python.commandLineParser.CommandLineParser parser} and obtain {@link com.jetbrains.python.commandLineParser.CommandLineParseResult}. + * + * Not like any other parsers, this package supports {@link com.jetbrains.python.WordWithPosition} telling you exactly with part of + * command line is command or argument. That helps you to underline or emphisize some parts. + * + * + * @author Ilya.Kazakevich + */ +package com.jetbrains.python.commandLineParser; \ No newline at end of file diff --git a/python/src/com/jetbrains/python/formatter/PyPreFormatProcessor.java b/python/src/com/jetbrains/python/formatter/PyPreFormatProcessor.java index ee2c4abb7939..f2c60e9db582 100644 --- a/python/src/com/jetbrains/python/formatter/PyPreFormatProcessor.java +++ b/python/src/com/jetbrains/python/formatter/PyPreFormatProcessor.java @@ -16,12 +16,12 @@ package com.jetbrains.python.formatter; import com.intellij.lang.ASTNode; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiComment; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; +import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.impl.source.codeStyle.PreFormatProcessor; @@ -31,6 +31,9 @@ import com.jetbrains.python.psi.PyElementGenerator; import com.jetbrains.python.psi.PyRecursiveElementVisitor; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; + /** * @author traff */ @@ -58,6 +61,7 @@ public class PyPreFormatProcessor implements PreFormatProcessor { private final Project myProject; private final CodeStyleSettings mySettings; private final PyCodeStyleSettings myPyCodeStyleSettings; + private final List> myCommentReplacements = new ArrayList>(); private TextRange myRange; private int myDelta = 0; @@ -72,34 +76,62 @@ public class PyPreFormatProcessor implements PreFormatProcessor { return range; } myRange = range; - element.accept(this); + final PsiDocumentManager manager = PsiDocumentManager.getInstance(myProject); + final Document document = manager.getDocument(element.getContainingFile()); + if (document != null) { + manager.doPostponedOperationsAndUnblockDocument(document); + try { + // collect all comments + element.accept(this); + for (Couple pair : myCommentReplacements) { + pair.getFirst().replace(pair.getSecond()); + } + } + finally { + manager.commitDocument(document); + } + } return TextRange.create(range.getStartOffset(), range.getEndOffset() + myDelta); } @Override - public void visitComment(PsiComment element) { - if (!myRange.contains(element.getTextRange())) { + public void visitComment(PsiComment comment) { + if (!myRange.contains(comment.getTextRange())) { return; } - String text = element.getText(); - int commentStart = text.indexOf('#'); - if (commentStart != -1 && (commentStart + 1) < text.length()) { - char charAfterDash = text.charAt(commentStart + 1); - if (charAfterDash == '!' && element.getTextRange().getStartOffset() == 0) { - return; //shebang + final String origText = comment.getText(); + final int commentStart = origText.indexOf('#'); + if (commentStart != -1 && (commentStart + 1) < origText.length()) { + final char charAfterDash = origText.charAt(commentStart + 1); + if (charAfterDash == '!' && comment.getTextRange().getStartOffset() == 0) { + return; // shebang } if (charAfterDash == '#' || charAfterDash == ':') { + return; // doc comment + } + final String commentTextWithoutDash = origText.substring(commentStart + 1); + final String newText; + if (isTrailingComment(comment)) { + newText = "# " + StringUtil.trimLeading(commentTextWithoutDash); + } + else if (!StringUtil.isWhiteSpace(charAfterDash)) { + newText = "# " + commentTextWithoutDash; + } + else { return; } - String commentText = StringUtil.trimLeading(text.substring(commentStart + 1)); - - String newText = "# " + commentText; - if (!newText.equals(text)) { - myDelta += newText.length() - text.length(); - element.replace( - PyElementGenerator.getInstance(myProject).createFromText(LanguageLevel.getDefault(), PsiComment.class, newText)); + if (!newText.equals(origText)) { + myDelta += newText.length() - origText.length(); + final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(myProject); + final PsiComment newComment = elementGenerator.createFromText(LanguageLevel.forElement(comment), PsiComment.class, newText); + myCommentReplacements.add(Couple.of(comment, newComment)); } } } } + + private static boolean isTrailingComment(@NotNull PsiComment comment) { + final PsiElement prevElement = comment.getPrevSibling(); + return prevElement != null && (!(prevElement instanceof PsiWhiteSpace) || !prevElement.textContains('\n')); + } } diff --git a/python/src/com/jetbrains/python/optParse/package-info.java b/python/src/com/jetbrains/python/optParse/package-info.java deleted file mode 100644 index 3be946b72036..000000000000 --- a/python/src/com/jetbrains/python/optParse/package-info.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * 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 - * - * http://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. - */ - -/** - *

- * An optparse module twin, that parses command line. - * Unlike any other GNU/Posix parsers, it knows how to:

- *
    - *
  1. Parse options and args with out of any knowledge about required args
  2. - *
  3. Provide actual places in command line where exactly such args or opts exist./li> - *
- *

- * Be sure to read optparse manual - * (epecially terminology) part. - *

- *

Package entry point is {@link com.jetbrains.python.optParse.ParsedCommandLine}

- * @author Ilya.Kazakevich - */ -package com.jetbrains.python.optParse; \ No newline at end of file diff --git a/python/src/com/jetbrains/python/suggestionList/SuggestionList.java b/python/src/com/jetbrains/python/suggestionList/SuggestionList.java index c43f23809806..d209235bec74 100644 --- a/python/src/com/jetbrains/python/suggestionList/SuggestionList.java +++ b/python/src/com/jetbrains/python/suggestionList/SuggestionList.java @@ -166,7 +166,14 @@ public class SuggestionList { return null; // Nothing is selected if list is invisible } final Object value = myList.getSelectedValue(); - return ((value == null) ? "" : getElement(value).mySuggestion.getText()); + return ((value == null) ? null : getElement(value).mySuggestion.getText()); + } + + /** + * @return true if no suggestion list is displayed now. + */ + public final synchronized boolean isClosed() { + return myListPopUp == null || myListPopUp.isDisposed(); } /** diff --git a/python/testData/formatter/commentedCodeFragmentIgnored.py b/python/testData/formatter/commentedCodeFragmentIgnored.py new file mode 100644 index 000000000000..64d378e829aa --- /dev/null +++ b/python/testData/formatter/commentedCodeFragmentIgnored.py @@ -0,0 +1,6 @@ +# This normal comment won't be touched as well +# for a in [1, 2, 3]: +# if a == 1: +# print 'x' +# elif a == 2: +# print 'y' \ No newline at end of file diff --git a/python/testData/formatter/commentedCodeFragmentIgnored_after.py b/python/testData/formatter/commentedCodeFragmentIgnored_after.py new file mode 100644 index 000000000000..64d378e829aa --- /dev/null +++ b/python/testData/formatter/commentedCodeFragmentIgnored_after.py @@ -0,0 +1,6 @@ +# This normal comment won't be touched as well +# for a in [1, 2, 3]: +# if a == 1: +# print 'x' +# elif a == 2: +# print 'y' \ No newline at end of file diff --git a/python/testData/formatter/docCommentIgnored.py b/python/testData/formatter/docCommentIgnored.py new file mode 100644 index 000000000000..08a8959972a7 --- /dev/null +++ b/python/testData/formatter/docCommentIgnored.py @@ -0,0 +1,4 @@ +#: :type: dict of (str, C) +d = {'foo': C()} +#: :type: list of str +xs = ['bar'] \ No newline at end of file diff --git a/python/testData/formatter/docCommentIgnored_after.py b/python/testData/formatter/docCommentIgnored_after.py new file mode 100644 index 000000000000..08a8959972a7 --- /dev/null +++ b/python/testData/formatter/docCommentIgnored_after.py @@ -0,0 +1,4 @@ +#: :type: dict of (str, C) +d = {'foo': C()} +#: :type: list of str +xs = ['bar'] \ No newline at end of file diff --git a/python/testData/formatter/doubleHashCommentIgnored.py b/python/testData/formatter/doubleHashCommentIgnored.py new file mode 100644 index 000000000000..f1501b360660 --- /dev/null +++ b/python/testData/formatter/doubleHashCommentIgnored.py @@ -0,0 +1,6 @@ +########### +## +## MANUAL +## + +print('Spam') \ No newline at end of file diff --git a/python/testData/formatter/doubleHashCommentIgnored_after.py b/python/testData/formatter/doubleHashCommentIgnored_after.py new file mode 100644 index 000000000000..f1501b360660 --- /dev/null +++ b/python/testData/formatter/doubleHashCommentIgnored_after.py @@ -0,0 +1,6 @@ +########### +## +## MANUAL +## + +print('Spam') \ No newline at end of file diff --git a/python/testData/formatter/shebang.py b/python/testData/formatter/shebangCommentIgnored.py similarity index 100% rename from python/testData/formatter/shebang.py rename to python/testData/formatter/shebangCommentIgnored.py diff --git a/python/testData/formatter/shebang_after.py b/python/testData/formatter/shebangCommentIgnored_after.py similarity index 100% rename from python/testData/formatter/shebang_after.py rename to python/testData/formatter/shebangCommentIgnored_after.py diff --git a/python/testData/formatter/trailingComment.py b/python/testData/formatter/trailingComment.py new file mode 100644 index 000000000000..59839dfe2b29 --- /dev/null +++ b/python/testData/formatter/trailingComment.py @@ -0,0 +1,3 @@ +# Whole-line commend that starts with whitespace. +if True: #Trailing comment with no whitespace + print 'Spam' # Trailing comment with excess whitespace \ No newline at end of file diff --git a/python/testData/formatter/trailingComment_after.py b/python/testData/formatter/trailingComment_after.py new file mode 100644 index 000000000000..ca734099069c --- /dev/null +++ b/python/testData/formatter/trailingComment_after.py @@ -0,0 +1,3 @@ +# Whole-line commend that starts with whitespace. +if True: # Trailing comment with no whitespace + print 'Spam' # Trailing comment with excess whitespace \ No newline at end of file diff --git a/python/testData/formatter/whitespaceInsertedAfterHashSignInMultilineComment.py b/python/testData/formatter/whitespaceInsertedAfterHashSignInMultilineComment.py new file mode 100644 index 000000000000..814f4774b27a --- /dev/null +++ b/python/testData/formatter/whitespaceInsertedAfterHashSignInMultilineComment.py @@ -0,0 +1,4 @@ +#some +#invalid +#pep-8 +#comment \ No newline at end of file diff --git a/python/testData/formatter/whitespaceInsertedAfterHashSignInMultilineComment_after.py b/python/testData/formatter/whitespaceInsertedAfterHashSignInMultilineComment_after.py new file mode 100644 index 000000000000..80d188f7dd63 --- /dev/null +++ b/python/testData/formatter/whitespaceInsertedAfterHashSignInMultilineComment_after.py @@ -0,0 +1,4 @@ +# some +# invalid +# pep-8 +# comment \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index a9db8eb0f4de..ff821d6b524f 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -401,10 +401,6 @@ public class PyFormatterTest extends PyTestCase { doTest(); } - public void testShebang() { //PY-12775 - doTest(); - } - public void testParenthesisAroundGeneratorExpression() { doTest(); } @@ -483,13 +479,43 @@ public class PyFormatterTest extends PyTestCase { // PY-12748 public void testIndentCommentariesInsideFromImportStatement() { - doTest(); + doTest(); } public void testClosingParenthesisInFromImportStatementWithNoHangingIndent() { doTest(); } + // PY-12932 + public void testCommentedCodeFragmentIgnored() { + doTest(); + } + + // PY-12932 + public void testTrailingComment() { + doTest(); + } + + // PY-12938 + public void testDoubleHashCommentIgnored() { + doTest(); + } + + // PY-12938 + public void testDocCommentIgnored() { + doTest(); + } + + // PY-12775 + public void testShebangCommentIgnored() { + doTest(); + } + + // PY-13232 + public void testWhitespaceInsertedAfterHashSignInMultilineComment() { + doTest(); + } + /** * This test merely checks that call to {@link com.intellij.psi.codeStyle.CodeStyleManager#reformat(com.intellij.psi.PsiElement)} * is possible for Python sources. diff --git a/resources/src/idea/RichPlatformActions.xml b/resources/src/idea/RichPlatformActions.xml index adb93ecf9502..3ba8f55e5aaa 100644 --- a/resources/src/idea/RichPlatformActions.xml +++ b/resources/src/idea/RichPlatformActions.xml @@ -56,6 +56,7 @@ + diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlCharFilter.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlCharFilter.java index 8f708b7805f3..0568e5812bce 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/XmlCharFilter.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlCharFilter.java @@ -70,15 +70,12 @@ public class XmlCharFilter extends CharFilter { public static boolean isWithinTag(Lookup lookup) { if (isInXmlContext(lookup)) { PsiElement psiElement = lookup.getPsiElement(); - final PsiElement parentElement = psiElement.getParent() != null ? psiElement.getParent():null; - String s; - return parentElement != null && - ( parentElement instanceof XmlTag || - ( parentElement instanceof PsiErrorElement && - parentElement.getParent() instanceof XmlDocument - ) || - ((parentElement instanceof XmlDocument || parentElement instanceof XmlText) && - ((s = psiElement.getText()).equals("<") || s.equals("\"")))); + final PsiElement parentElement = psiElement != null ? psiElement.getParent() : null; + if (parentElement instanceof XmlTag) return true; + if (parentElement instanceof PsiErrorElement && parentElement.getParent() instanceof XmlDocument) return true; + + return (parentElement instanceof XmlDocument || parentElement instanceof XmlText) && + (psiElement.textMatches("<") || psiElement.textMatches("\"")); } return false; } diff --git a/xml/impl/src/com/intellij/codeInsight/editorActions/XmlTagNameSynchronizer.java b/xml/impl/src/com/intellij/codeInsight/editorActions/XmlTagNameSynchronizer.java index aa80aee4d8f9..5cfbc8e5e7b0 100644 --- a/xml/impl/src/com/intellij/codeInsight/editorActions/XmlTagNameSynchronizer.java +++ b/xml/impl/src/com/intellij/codeInsight/editorActions/XmlTagNameSynchronizer.java @@ -17,6 +17,8 @@ package com.intellij.codeInsight.editorActions; import com.intellij.application.options.editor.WebEditorOptions; import com.intellij.codeInsight.completion.XmlTagInsertHandler; +import com.intellij.codeInsight.lookup.LookupManager; +import com.intellij.codeInsight.lookup.impl.LookupImpl; import com.intellij.codeInspection.htmlInspections.RenameTagBeginOrEndIntentionAction; import com.intellij.lang.Language; import com.intellij.lang.html.HTMLLanguage; @@ -287,8 +289,7 @@ public class XmlTagNameSynchronizer extends CommandAdapter implements Applicatio myState = State.APPLYING; final Document document = myEditor.getDocument(); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override + final Runnable apply = new Runnable() { public void run() { for (Couple couple : myMarkers) { final RangeMarker leader = couple.first; @@ -297,6 +298,17 @@ public class XmlTagNameSynchronizer extends CommandAdapter implements Applicatio document.replaceString(support.getStartOffset(), support.getEndOffset(), name); } } + }; + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + final LookupImpl lookup = (LookupImpl)LookupManager.getActiveLookup(myEditor); + if (lookup != null) { + lookup.performGuardedChange(apply); + } else { + apply.run(); + } + } }); myState = State.TRACKING; diff --git a/xml/tests/src/com/intellij/codeInsight/completion/XmlSyncTagCommunityTest.java b/xml/tests/src/com/intellij/codeInsight/completion/XmlSyncTagCommunityTest.java index ed8f50cf3280..9df16cda1e24 100644 --- a/xml/tests/src/com/intellij/codeInsight/completion/XmlSyncTagCommunityTest.java +++ b/xml/tests/src/com/intellij/codeInsight/completion/XmlSyncTagCommunityTest.java @@ -82,6 +82,12 @@ public class XmlSyncTagCommunityTest extends XmlSyncTagTest { ""); } + public void testCompletionWithLookupAfterTyping() { + doTestCompletion(">", "e", + ""); + assertNotNull(myFixture.getLookup()); + } + public void testUndo() { doTest(">", "v", ""); myFixture.performEditorAction(IdeActions.ACTION_UNDO);