├── .gitignore ├── CHANGELOG.md ├── DeveloperGuide.md ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── authenticate.png ├── code_context.png ├── confirm.png ├── install_plugin.png ├── settings.png ├── statusbar.png ├── tool_window.png └── view_total.png ├── settings.gradle └── src ├── main ├── java │ └── ai │ │ └── deepcode │ │ └── jbplugin │ │ ├── DeepCodeConsoleToolWindowFactory.java │ │ ├── DeepCodeIcons.java │ │ ├── DeepCodeNotifications.java │ │ ├── DeepCodeStartupActivity.java │ │ ├── DeepCodeStatusBarWidgetProvider.java │ │ ├── DeepCodeToolWindowFactory.java │ │ ├── actions │ │ ├── AnalyseCurrentFileAction.java │ │ ├── AnalyseProjectAction.java │ │ ├── DeepCodeIntentionAction.java │ │ ├── InvalidateCachesAction.java │ │ ├── SeeResultsInBrowserAction.java │ │ └── ShowSettingsAction.java │ │ ├── annotators │ │ └── DeepCodeExternalAnnotator.java │ │ ├── core │ │ ├── AnalysisData.java │ │ ├── BulkMode.java │ │ ├── DCLogger.java │ │ ├── DeepCodeIgnoreInfoHolder.java │ │ ├── DeepCodeParams.java │ │ ├── DeepCodeUtils.java │ │ ├── HashContentUtils.java │ │ ├── LoginUtils.java │ │ ├── MyBulkFileListener.java │ │ ├── MyProjectManagerListener.java │ │ ├── PDU.java │ │ └── RunUtils.java │ │ └── ui │ │ ├── AllTodosTreeBuilder.java │ │ ├── AllTodosTreeStructure.java │ │ ├── ChangeListTodosPanel.java │ │ ├── ChangeListTodosTreeBuilder.java │ │ ├── ChangeListTodosTreeStructure.java │ │ ├── CurrentFileTodosPanel.java │ │ ├── CurrentFileTodosTreeBuilder.java │ │ ├── CurrentFileTodosTreeStructure.java │ │ ├── CustomChangelistTodoTreeStructure.java │ │ ├── DeepCodeDirAndModuleComparator.java │ │ ├── FileTree.java │ │ ├── HighlightedRegionProvider.java │ │ ├── MultiLineTodoLocalityDetector.java │ │ ├── MultiLineTodoRenderer.java │ │ ├── ScopeBasedTodosPanel.java │ │ ├── ScopeBasedTodosTreeBuilder.java │ │ ├── ScopeBasedTodosTreeStructure.java │ │ ├── SetTodoFilterAction.java │ │ ├── SmartTodoItemPointer.java │ │ ├── SmartTodoItemPointerComparator.java │ │ ├── ToDoSettings.java │ │ ├── ToDoSummary.java │ │ ├── TodoCompositeRenderer.java │ │ ├── TodoNodeVisitor.java │ │ ├── TodoPanel.java │ │ ├── TodoPanelSettings.java │ │ ├── TodoTreeBuilder.java │ │ ├── TodoTreeBuilderFactory.java │ │ ├── TodoTreeStructure.java │ │ ├── config │ │ ├── DeepCodeConfigEntry.java │ │ ├── DeepCodeConfigForm.form │ │ └── DeepCodeConfigForm.java │ │ ├── configurable │ │ ├── FilterDialog.java │ │ ├── FiltersTableModel.java │ │ ├── PatternDialog.java │ │ ├── PatternsTableModel.java │ │ ├── TodoConfigurable.java │ │ └── TodoPatternTableCellRenderer.java │ │ ├── myTodoView.java │ │ ├── nodes │ │ ├── BaseToDoNode.java │ │ ├── MarkerItemImpl.java │ │ ├── ModuleToDoNode.java │ │ ├── SingleFileToDoNode.java │ │ ├── SuggestionNode.java │ │ ├── SummaryNode.java │ │ ├── ToDoRootNode.java │ │ ├── TodoDirNode.java │ │ ├── TodoFileNode.java │ │ ├── TodoItemNode.java │ │ └── TodoTreeHelper.java │ │ └── utils │ │ └── DeepCodeUIUtils.java └── resources │ ├── META-INF │ ├── plugin.xml │ ├── pluginIcon.svg │ └── pluginIcon_dark.svg │ └── icons │ ├── DeepCodeLogo.svg │ └── DeepCodeLogo_dark.svg └── test ├── .dcignore ├── java └── ai │ └── deepcode │ └── jbplugin │ ├── MyBasePlatformTestCase.java │ ├── MyPlatformTestCase.java │ ├── TestAnnotatorForCPP.java │ ├── TestAnnotatorForJava.java │ ├── TestAnnotatorForJavaScript.java │ ├── TestLoginProcess.java │ └── core │ └── TestInnerCaches.java ├── resources └── log4j.properties └── testData ├── AnnotatorTest.cpp ├── AnnotatorTest.java ├── AnnotatorTest.js └── AnnotatorTest_ValidCPP.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # Gradle related 26 | .gradle 27 | /build -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Latest update 🗞️ (start here first 👇) 2 | 3 | - Dear developers 👋, thank you for your support and feedback! 4 | - With the DeepCode acquisition by [Snyk](https://snyk.io) we will be starting a new journey, a better one, towards helping you write robust and secure application code. The DeepCode plugin will be replaced by [Snyk's JetBrains plugin](https://plugins.jetbrains.com/plugin/10972-snyk-vulnerability-scanner) with includes DeepCode's functionality and more. 5 | - If you want to read more about it, here is the [official announcement](https://www.deepcode.ai/). We will start sunsetting the official DeepCode API in August, 2021. In the mean time we will do one last update of the JetBrains plugin with this message to ensure you are up to date. 6 | - We invite you to join us and start using the new Snyk plugin! We are excited and looking forward to helping you build stable and secure applications 🚀 7 | 8 | ## [1.2.14] - 2021-06 9 | 1.2.14 - fix deprecation deadline and links at plugin replacement announcement;
10 | 11 | ## [1.2.13] - 2021-05 12 | 1.2.13 - 2021.1 compatibility and plugin replacement announcement;
13 | 14 | ## [1.2.12] - 2019-10-05 15 | 1.2.12 - Upcoming 2020.3 compatibility and bug fixes; 16 | 17 | ## [1.2.11] - 2019-09-16 18 | 1.2.11 - Fix bug with IndexOutOfBound Exception for wrongly positioned suggestion/marker; 19 | - Fix bug with multiple analyses processes on project opening. 20 | 21 | ## [1.2.10] - 2019-09-14 22 | - Update modes added: 23 | * Interactive (on any source file change); 24 | * On Save (when source file saved on disk); 25 | * On Demand (only if explicitly invoked). 26 | 27 | ## [1.2.9] - 2019-09-08 28 | - Status Bar widget with summary info added; 29 | - .dcignore parsing fixed. 30 | 31 | ## [1.2.8] - 2019-09-03 32 | - Improvements: 33 | * markers for each suggestion are shown in the List and highlighted in Preview 34 | * more informative Progress indicator messages 35 | 36 | - Bug fixes: 37 | * fix missing re-analyse in some corner cases 38 | * fix missing/"too many" login/consent requests 39 | * fix ignoring action (quick fix) been broken in some cases 40 | * fix logger to not pollute IntelliJ general log (see readme.md how to collect log information) 41 | 42 | ## [1.2.0] - 2019-06-20 43 | - Moving common logic into java-client 44 | 45 | ## [1.1.2] - 2019-06-19 46 | - Fix bug parsing of trailing "**" in .dcignore 47 | 48 | ## [1.1.1] - 2019-06-16 49 | - Speedup of large projects analysis and suggestions highlighting; various internal fixes and refactoring 50 | 51 | ## [1.1.0] - 2019-06-01 52 | - Speedup of large projects analysis and suggestions highlighting 53 | - Various internal fixes and refactoring 54 | 55 | ## [1.0.3] - 2019-05-28 56 | - Fix multiple rescan requests and add ignoring events while rescan running 57 | - Fix cache invalidation in Bulk mode for files update 58 | - CheckBundle after uploadFiles to ensure no missingFiles left 59 | 60 | ## [1.0.2] - 2019-05-25 61 | - Bugfixing and better support for refactoring 62 | 63 | ## [1.0.1] - 2019-05-22 64 | - Updated Java-client and added support for Java 8, required for Android Studio 65 | 66 | ## [1.0.0] - 2019-05-21 67 | ### Added 68 | - First public beta release 69 | -------------------------------------------------------------------------------- /DeveloperGuide.md: -------------------------------------------------------------------------------- 1 | # Latest update 🗞️ (start here first 👇) 2 | 3 | - Dear developers 👋, thank you for your support and feedback! 4 | - With the DeepCode acquisition by [Snyk](https://snyk.io) we will be starting a new journey, a better one, towards helping you write robust and secure application code. The DeepCode plugin will be replaced by [Snyk's JetBrains plugin](https://plugins.jetbrains.com/plugin/10972-snyk-vulnerability-scanner) with includes DeepCode's functionality and more. 5 | - If you want to read more about it, here is the [official announcement](https://www.deepcode.ai/). We will start sunsetting the official DeepCode API in August, 2021. In the mean time we will do one last update of the JetBrains plugin with this message to ensure you are up to date. 6 | - We invite you to join us and start using the new Snyk plugin! We are excited and looking forward to helping you build stable and secure applications 🚀 7 | 8 | ### Build 9 | 10 | - Clone this repository: `git clone https://github.com/DeepCodeAI/jetbrains-plugin.git` 11 | - Switch to the dev branch: `git checkout origin/dev` 12 | - Install Java 11 or higher (https://adoptopenjdk.net/) and/or make sure JAVA_HOME is pointing to Java 11 installation path of the JDK; 13 | - Check if java is correctly installed (and your java version) with `java -version` command; 14 | - Place `java-client-{X.X.X}-all.jar` into `..\libs` dir (see [java-client](https://github.com/DeepCodeAI/java-client) repository for instruction how to build it); 15 | 16 | **Important note: Starting version 1.2.0 use of java-client version 2.0 or above required!** 17 | 18 | See below correspondent `java-client` version requirements: 19 | 20 | | jetbrains-plugin | java-client | 21 | |------------------|-------------| 22 | | 1.2.0 | 2.0.0 | 23 | | 1.2.2 | 2.0.4 | 24 | | 1.2.3 | 2.0.5 | 25 | | 1.2.4 | 2.0.6 | 26 | | 1.2.5 | 2.0.8 | 27 | | 1.2.7 | 2.0.12 | 28 | | 1.2.8 | 2.0.14 | 29 | | 1.2.10 | 2.0.16 | 30 | | 1.2.11 | 2.0.17 | 31 | | 1.2.12 | 2.0.18 | 32 | | 1.2.13 | 2.0.18 | 33 | | 1.2.14 | 2.0.18 | 34 | 35 | **Important note: For backward compatibility build MUST be run against Intellij Idea 2019.2 instance!** 36 | - Run `source gradlew buildPlugin` 37 | - Look for resulting ZIP file at `..\build\distributions` 38 | 39 | ### Run tests 40 | 41 | - environment variable with __already logged__ Token need to be declared: 42 | 43 | `DEEPCODE_API_KEY` - logged at https://www.deepcode.ai Token 44 | 45 | - Run gradle test task: `source gradlew test --stacktrace --scan` 46 | 47 | ### See log output 48 | 49 | To see plugin's log output in idea.log (related to testing Idea instance), please enable Debug level logging 50 | (in testing Idea instance) by open `Help -> Diagnostic Tools -> Debug Log Settings...` and type there `DeepCode` (case-sensitive!). 51 | 52 | ### Useful links 53 | - IntelliJ Platform SDK [documentation](https://www.jetbrains.org/intellij/sdk/docs/intro/welcome.html) 54 | - JetBrains Marketplace [documentation](https://plugins.jetbrains.com/docs/marketplace/about-marketplace.html) 55 | - [Community SDK Forum](https://intellij-support.jetbrains.com/hc/en-us/community/topics/200366979-IntelliJ-IDEA-Open-API-and-Plugin-Development) 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 DeepCodeAI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import org.jetbrains.intellij.tasks.RunIdeTask 2 | 3 | plugins { 4 | id 'java' 5 | id 'org.jetbrains.intellij' version '0.4.19' 6 | } 7 | 8 | //group 'org.example' 9 | 10 | version '1.2.14' 11 | sourceCompatibility = 1.8 12 | 13 | repositories { 14 | mavenCentral() 15 | } 16 | 17 | dependencies { 18 | compile fileTree("$rootProject.projectDir/libs") 19 | 20 | testCompile group: 'junit', name: 'junit', version: '4.12' 21 | } 22 | 23 | // See https://github.com/JetBrains/gradle-intellij-plugin/ 24 | intellij { 25 | pluginName "DeepCode-$project.name" 26 | version '2019.2' 27 | // plugins += 'java' 28 | // plugins += 'PsiViewer:192-SNAPSHOT' 29 | 30 | } 31 | patchPluginXml { 32 | sinceBuild 192 33 | untilBuild 212 34 | 35 | changeNotes """ 36 | Dear developers, thank you for your support and feedback!
37 | With the DeepCode acquisition by Snyk we will be starting a new journey, a better one, towards helping you write robust and secure application code. The DeepCode plugin will be replaced by Snyk's JetBrains plugin with includes DeepCode's functionality and more.
38 | If you want to read more about it, here is the official announcement. We will start sunsetting the official DeepCode API in August, 2021. In the mean time we will do one last update of the JetBrains plugin with this message to ensure you are up to date.
39 | We invite you to join us and start using the new Snyk plugin! We are excited and looking forward to helping you build stable and secure applications 🚀

40 | 41 | 1.2.14 - fix deprecation deadline and links at plugin replacement announcement
42 | 1.2.13 - 2021.1 compatibility and plugin replacement announcement
43 | 1.2.12 - Upcoming 2020.3 compatibility and bug fixes
44 | 1.2.11 - Fix bugs and performance optimisation
45 | 1.2.10 - Update modes added:
46 | * Interactive (on any source file change);
47 | * On Save (when source file saved on disk);
48 | * On Demand (only if explicitly invoked).
49 | 1.2.9 - Status Bar widget with summary info added; .dcignore parsing fixed.
50 | 1.2.8 - Improvements:
51 | * markers for each suggestion are shown in the List and highlighted in Preview
52 | * more informative Progress indicator messages
53 | - Bug fixes:
54 | * fix missing re-analyse in some corner cases
55 | * fix missing/"too many" login/consent requests
56 | * fix ignoring action (quick fix) been broken in some cases
57 | * fix logger to not pollute IntelliJ general log (see readme.md)
58 | 1.1.2 - Fix bug parsing of trailing "**" in .dcignore
59 | 1.1.1 - Fix errors symbolic presentation; fix exception when analysis results not available.
60 | 1.1.0 - Speedup of large projects analysis and suggestions highlighting; various internal fixes and refactoring
61 | 1.0.4 - Bugfixing internals
62 | 1.0.3 - Bugfixing and optimization of bulk file changes
63 | 1.0.2 - Bugfixing and better support for refactoring
64 | 1.0.1 - Updated Java-client and added support for Java 8, required for Android Studio
65 | 1.0.0 - First public beta release.
66 | """ 67 | } 68 | 69 | runIde { 70 | jvmArgs '-Xmx2G' 71 | } 72 | 73 | task runIC_201(type: RunIdeTask){ 74 | jvmArgs '-Xmx2G' 75 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\IDEA-C\\ch-1\\201.7846.76' 76 | } 77 | 78 | task runIC_202(type: RunIdeTask){ 79 | jvmArgs '-Xmx2G' 80 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\IDEA-C\\ch-0\\202.7319.50' 81 | } 82 | 83 | task runIC_203(type: RunIdeTask){ 84 | jvmArgs '-Xmx2G' 85 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\IDEA-C\\ch-3\\203.5600.34' 86 | } 87 | 88 | task runIC_211(type: RunIdeTask){ 89 | jvmArgs '-Xmx2G' 90 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\IDEA-C\\ch-3\\211.6693.111' 91 | } 92 | 93 | task runIC_193(type: RunIdeTask){ 94 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\IDEA-C\\ch-0\\193.6911.18' 95 | } 96 | 97 | task runIU_192(type: RunIdeTask){ 98 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\IDEA-U\\ch-1\\192.7142.36' 99 | } 100 | 101 | task runPC_193(type: RunIdeTask){ 102 | jvmArgs '-Xmx2G' 103 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\PyCharm-C\\ch-0\\193.5233.109' 104 | } 105 | 106 | task runCLion_193(type: RunIdeTask){ 107 | jvmArgs '-Xmx2G' 108 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\CLion\\ch-0\\193.6494.38' 109 | } 110 | 111 | task runCLion_192(type: RunIdeTask){ 112 | jvmArgs '-Xmx2G' 113 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\CLion\\ch-2\\192.7142.39' 114 | } 115 | 116 | task run_WS_201(type: RunIdeTask){ 117 | jvmArgs '-Xmx2G' 118 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\WebStorm\\ch-0\\201.6668.106' 119 | } 120 | 121 | task run_WS_192(type: RunIdeTask){ 122 | jvmArgs '-Xmx2G' 123 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\WebStorm\\ch-1\\192.7142.35' 124 | } 125 | 126 | task run_PC_201(type: RunIdeTask){ 127 | jvmArgs '-Xmx2G' 128 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\PyCharm-C\\ch-0\\201.7223.92' 129 | } 130 | 131 | task run_AS_40(type: RunIdeTask){ 132 | jvmArgs '-Xmx2G' 133 | jbrVersion '8u202b1483.24' 134 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\AndroidStudio\\ch-0\\193.6453388' 135 | } 136 | 137 | task run_AS_36(type: RunIdeTask){ 138 | jvmArgs '-Xmx2G' 139 | jbrVersion '8u202b1483.24' 140 | ideDirectory 'C:\\Users\\artem\\AppData\\Local\\JetBrains\\Toolbox\\apps\\AndroidStudio\\ch-1\\192.6392135' 141 | } 142 | 143 | tasks.withType(RunIdeTask).forEach {task -> task.dependsOn(prepareSandbox)} 144 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepCodeAI/jetbrains-plugin/e228308541c5d0bfb13156f219095859af33f683/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Mar 20 09:57:06 MSK 2020 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /images/authenticate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepCodeAI/jetbrains-plugin/e228308541c5d0bfb13156f219095859af33f683/images/authenticate.png -------------------------------------------------------------------------------- /images/code_context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepCodeAI/jetbrains-plugin/e228308541c5d0bfb13156f219095859af33f683/images/code_context.png -------------------------------------------------------------------------------- /images/confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepCodeAI/jetbrains-plugin/e228308541c5d0bfb13156f219095859af33f683/images/confirm.png -------------------------------------------------------------------------------- /images/install_plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepCodeAI/jetbrains-plugin/e228308541c5d0bfb13156f219095859af33f683/images/install_plugin.png -------------------------------------------------------------------------------- /images/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepCodeAI/jetbrains-plugin/e228308541c5d0bfb13156f219095859af33f683/images/settings.png -------------------------------------------------------------------------------- /images/statusbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepCodeAI/jetbrains-plugin/e228308541c5d0bfb13156f219095859af33f683/images/statusbar.png -------------------------------------------------------------------------------- /images/tool_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepCodeAI/jetbrains-plugin/e228308541c5d0bfb13156f219095859af33f683/images/tool_window.png -------------------------------------------------------------------------------- /images/view_total.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepCodeAI/jetbrains-plugin/e228308541c5d0bfb13156f219095859af33f683/images/view_total.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'jetbrains-plugin' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/DeepCodeConsoleToolWindowFactory.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin; 2 | 3 | import ai.deepcode.javaclient.core.MyTextRange; 4 | import ai.deepcode.jbplugin.core.AnalysisData; 5 | import ai.deepcode.jbplugin.core.DeepCodeUtils; 6 | import ai.deepcode.javaclient.core.SuggestionForFile; 7 | import com.intellij.execution.impl.ConsoleViewUtil; 8 | import com.intellij.openapi.Disposable; 9 | import com.intellij.openapi.editor.Document; 10 | import com.intellij.openapi.editor.EditorFactory; 11 | import com.intellij.openapi.editor.ex.EditorEx; 12 | import com.intellij.openapi.project.Project; 13 | import com.intellij.openapi.ui.SimpleToolWindowPanel; 14 | import com.intellij.openapi.util.Disposer; 15 | import com.intellij.openapi.util.TextRange; 16 | import com.intellij.openapi.wm.ToolWindow; 17 | import com.intellij.openapi.wm.ToolWindowFactory; 18 | import com.intellij.psi.PsiFile; 19 | import com.intellij.ui.content.Content; 20 | import com.intellij.ui.content.ContentFactory; 21 | import com.intellij.ui.content.ContentManager; 22 | import com.intellij.ui.scale.JBUIScale; 23 | import com.intellij.util.ui.AbstractLayoutManager; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | 28 | import javax.swing.*; 29 | import java.awt.*; 30 | import java.util.List; 31 | 32 | public class DeepCodeConsoleToolWindowFactory implements ToolWindowFactory, Disposable { 33 | private static final Logger LOG = LoggerFactory.getLogger("DeepCodeToolWindowFactory"); 34 | private static PsiFile myCurrentFile; 35 | 36 | @Override 37 | public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { 38 | final ContentManager contentManager = toolWindow.getContentManager(); 39 | final ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); 40 | 41 | // Current File panel 42 | JPanel editorPanel = 43 | new JPanel( 44 | new AbstractLayoutManager() { 45 | private int getOffset() { 46 | return JBUIScale.scale(4); 47 | } 48 | 49 | @Override 50 | public Dimension preferredLayoutSize(Container parent) { 51 | Dimension size = parent.getComponent(0).getPreferredSize(); 52 | return new Dimension(size.width + getOffset(), size.height); 53 | } 54 | 55 | @Override 56 | public void layoutContainer(Container parent) { 57 | int offset = getOffset(); 58 | parent 59 | .getComponent(0) 60 | .setBounds(offset, 0, parent.getWidth() - offset, parent.getHeight()); 61 | } 62 | }) { 63 | }; 64 | 65 | editorPanel.add(CurrentFileEditor.get(project).getComponent()); 66 | 67 | // todo CurrentFilePanel 68 | final SimpleToolWindowPanel currentFilePanel = new SimpleToolWindowPanel(true); 69 | currentFilePanel.setContent(editorPanel); 70 | Content currentFileContent = 71 | contentFactory.createContent(currentFilePanel, "Current File", false); 72 | contentManager.addContent(currentFileContent); 73 | 74 | // Update CurrentFile Panel if file was edited 75 | /* 76 | PsiManager.getInstance(project) 77 | .addPsiTreeChangeListener( 78 | new PsiTreeChangeAdapter() { 79 | @Override 80 | public void childrenChanged(@NotNull PsiTreeChangeEvent event) { 81 | PsiFile file = event.getFile(); 82 | if (file != null && file.equals(myCurrentFile)) { 83 | DeepCodeUtils.getInstance().asyncUpdateCurrentFilePanel(file); 84 | } 85 | } 86 | }); 87 | */ 88 | 89 | } 90 | 91 | /** 92 | * Perform additional initialization routine here. 93 | * 94 | * @param toolWindow 95 | */ 96 | @Override 97 | public void init(@NotNull ToolWindow toolWindow) {} 98 | 99 | /** Usually not invoked directly, see class javadoc. */ 100 | @Override 101 | public void dispose() {} 102 | 103 | // ------------------------------------------------------------------------------ 104 | 105 | private static class CurrentFileEditor { 106 | 107 | private static EditorEx myEditor; 108 | 109 | public static EditorEx get(@NotNull Project project) { 110 | if (myEditor == null) { 111 | myEditor = ConsoleViewUtil.setupConsoleEditor(project, false, false); 112 | Disposer.register( 113 | project, 114 | () -> { 115 | EditorFactory.getInstance().releaseEditor(myEditor); 116 | }); 117 | } 118 | return myEditor; 119 | } 120 | } 121 | 122 | public static void updateCurrentFilePanel(@NotNull PsiFile psiFile) { 123 | myCurrentFile = psiFile; 124 | Project project = psiFile.getProject(); 125 | cleanMessages(project); 126 | if (!DeepCodeUtils.getInstance().isSupportedFileFormat(psiFile)) return; 127 | List suggestions = AnalysisData.getInstance().getAnalysis(psiFile); 128 | for (SuggestionForFile suggestion : suggestions) { 129 | printMessage(project, suggestion.getMessage()); 130 | for (MyTextRange range : suggestion.getRanges()) { 131 | printMessage(project, " " + range); 132 | } 133 | } 134 | } 135 | 136 | private static void printMessage(Project project, String message) { 137 | EditorEx editor = CurrentFileEditor.get(project); 138 | if (editor.isDisposed()) { 139 | return; 140 | } 141 | Document document = editor.getDocument(); 142 | if (document.getTextLength() >= 0) { 143 | document.insertString(document.getTextLength(), message + "\n"); 144 | } 145 | } 146 | 147 | private static void cleanMessages(Project project) { 148 | EditorEx editor = CurrentFileEditor.get(project); 149 | if (editor.isDisposed()) { 150 | return; 151 | } 152 | Document document = editor.getDocument(); 153 | if (document.getTextLength() >= 0) { 154 | document.setText(""); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/DeepCodeIcons.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin; 2 | 3 | import com.intellij.openapi.util.IconLoader; 4 | 5 | import javax.swing.*; 6 | 7 | public class DeepCodeIcons { 8 | public static final Icon LOGO = IconLoader.getIcon("/icons/DeepCodeLogo.svg"); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/DeepCodeStartupActivity.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin; 2 | 3 | import ai.deepcode.jbplugin.core.*; 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.project.ProjectManager; 7 | import com.intellij.openapi.startup.StartupActivity; 8 | import com.intellij.openapi.vfs.VirtualFileManager; 9 | import com.intellij.util.messages.MessageBusConnection; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class DeepCodeStartupActivity implements StartupActivity { 13 | 14 | private static boolean listenersActivated = false; 15 | 16 | @Override 17 | public void runActivity(@NotNull Project project) { 18 | if (DeepCodeParams.getInstance().isPluginDeprecated()) { 19 | DeepCodeNotifications.showPluginDeprecationAnnouncement(project); 20 | // Unfortunately, we can't disable plugin here programmatically: https://intellij-support.jetbrains.com/hc/en-us/community/posts/360009820960/comments/360002292200 21 | // as workaround will return `isLogged() = false` to stop requesting disabled API 22 | } 23 | 24 | if (!listenersActivated) { 25 | final MessageBusConnection messageBusConnection = 26 | ApplicationManager.getApplication().getMessageBus().connect(); 27 | messageBusConnection.subscribe(VirtualFileManager.VFS_CHANGES, new MyBulkFileListener()); 28 | messageBusConnection.subscribe(ProjectManager.TOPIC, new MyProjectManagerListener(project)); 29 | listenersActivated = true; 30 | } 31 | if (DeepCodeParams.getInstance().isFirstStart()) { 32 | DeepCodeNotifications.showTutorialRequest(project); 33 | } 34 | if (DeepCodeParams.getInstance().needToShowReplacementMessage()) { 35 | DeepCodeNotifications.showPluginReplacementAnnouncement(project); 36 | } 37 | // Keep commented - for DEBUG ONLY !!!!!!!!!!!!!!!!! 38 | //PropertiesComponent.getInstance(project).setValue("consentGiven", false); 39 | 40 | AnalysisData.getInstance().resetCachesAndTasks(project); 41 | // Initial logging if needed. 42 | if (LoginUtils.getInstance().isLogged(project, true)) { 43 | RunUtils.getInstance().asyncAnalyseProjectAndUpdatePanel(project); 44 | } 45 | // Keep commented - for DEBUG ONLY !!!!!!!!!!!!!!!!! 46 | //throw new NullPointerException(); 47 | 48 | /* 49 | // Update CurrentFile Panel if file Tab was changed in Editor 50 | MessageBusConnection connection = project.getMessageBus().connect(); 51 | connection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new MyEditorManagerListener()); 52 | */ 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/DeepCodeStatusBarWidgetProvider.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin; 2 | 3 | import ai.deepcode.jbplugin.ui.utils.DeepCodeUIUtils; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.wm.*; 6 | import com.intellij.util.Consumer; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import javax.swing.*; 11 | import java.awt.event.MouseEvent; 12 | 13 | public class DeepCodeStatusBarWidgetProvider implements StatusBarWidgetProvider { 14 | 15 | public static void updateWidget(@NotNull Project project) { 16 | StatusBar statusBar = WindowManager.getInstance().getStatusBar(project); 17 | if (statusBar != null) { 18 | StatusBarWidget widget = statusBar.getWidget("DeepCodeAnalysisStatus"); 19 | if (widget instanceof DeepCodeStatusBarWidget) 20 | ((DeepCodeStatusBarWidget) widget).update(); 21 | } 22 | } 23 | 24 | @Nullable 25 | @Override 26 | public StatusBarWidget getWidget(@NotNull Project project) { 27 | return new DeepCodeStatusBarWidget(project); 28 | } 29 | 30 | @NotNull 31 | @Override 32 | public String getAnchor() { 33 | return StatusBar.Anchors.before(StatusBar.StandardWidgets.POSITION_PANEL); 34 | } 35 | 36 | public static class DeepCodeStatusBarWidget 37 | implements StatusBarWidget, StatusBarWidget.IconPresentation { 38 | private final Project project; 39 | private StatusBar myStatusBar; 40 | private Icon myCurrentIcon = DeepCodeUIUtils.EMPTY_EWI_ICON; 41 | private String myToolTipText = "DeepCode"; 42 | 43 | public DeepCodeStatusBarWidget(@NotNull Project project) { 44 | this.project = project; 45 | update(); 46 | } 47 | 48 | private void update() { 49 | myCurrentIcon = DeepCodeUIUtils.getSummaryIcon(project); 50 | myToolTipText = DeepCodeUIUtils.addErrWarnInfoCounts(project, "DeepCode", true, null); 51 | if (myStatusBar != null) myStatusBar.updateWidget(ID()); 52 | } 53 | 54 | @NotNull 55 | @Override 56 | public String ID() { 57 | return "DeepCodeAnalysisStatus"; 58 | } 59 | 60 | @Nullable 61 | @Override 62 | public WidgetPresentation getPresentation(@NotNull StatusBarWidget.PlatformType type) { 63 | return this; 64 | } 65 | 66 | @Override 67 | public void install(@NotNull StatusBar statusBar) { 68 | myStatusBar = statusBar; 69 | } 70 | 71 | @Override 72 | public void dispose() {} 73 | 74 | @Nullable 75 | @Override 76 | public Icon getIcon() { 77 | return myCurrentIcon; 78 | } 79 | 80 | @Nullable 81 | @Override 82 | public String getTooltipText() { 83 | return myToolTipText; 84 | } 85 | 86 | @Nullable 87 | @Override 88 | public Consumer getClickConsumer() { 89 | return mouseEvent -> { 90 | ToolWindow myToolWindow = ToolWindowManager.getInstance(project).getToolWindow("DeepCode"); 91 | myToolWindow.show(null); 92 | }; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/DeepCodeToolWindowFactory.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin; 2 | 3 | import ai.deepcode.jbplugin.ui.myTodoView; 4 | import ai.deepcode.jbplugin.ui.utils.DeepCodeUIUtils; 5 | import com.intellij.openapi.components.ServiceManager; 6 | import com.intellij.openapi.project.DumbService; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.openapi.wm.ToolWindow; 9 | import com.intellij.openapi.wm.ToolWindowFactory; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class DeepCodeToolWindowFactory implements ToolWindowFactory { 13 | @Override 14 | public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { 15 | DumbService.getInstance(project) 16 | .runWhenSmart( 17 | () -> ServiceManager.getService(project, myTodoView.class).initToolWindow(toolWindow)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/actions/AnalyseCurrentFileAction.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.actions; 2 | 3 | import ai.deepcode.jbplugin.DeepCodeNotifications; 4 | import ai.deepcode.jbplugin.core.LoginUtils; 5 | import ai.deepcode.jbplugin.core.RunUtils; 6 | import ai.deepcode.jbplugin.ui.myTodoView; 7 | import ai.deepcode.jbplugin.core.DeepCodeUtils; 8 | import com.intellij.openapi.actionSystem.AnAction; 9 | import com.intellij.openapi.actionSystem.AnActionEvent; 10 | import com.intellij.openapi.actionSystem.CommonDataKeys; 11 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 12 | import com.intellij.openapi.components.ServiceManager; 13 | import com.intellij.openapi.project.Project; 14 | import com.intellij.psi.PsiFile; 15 | import org.jetbrains.annotations.NotNull; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | // TODO Remove it? 20 | public class AnalyseCurrentFileAction extends AnAction { 21 | private static final Logger LOG = LoggerFactory.getLogger("DeepCode.AnalyseCurrentFileAction"); 22 | 23 | @Override 24 | public void update(AnActionEvent e) { 25 | // perform action if and only if EDITOR != null 26 | boolean enabled = e.getData(CommonDataKeys.EDITOR) != null; 27 | e.getPresentation().setEnabled(enabled); 28 | } 29 | 30 | @Override 31 | public void actionPerformed(@NotNull AnActionEvent event) { 32 | final PsiFile psiFile = event.getRequiredData(PlatformDataKeys.PSI_FILE); 33 | Project project = psiFile.getProject(); 34 | 35 | if (!LoginUtils.getInstance().isLogged(project, true)) { 36 | // DeepCodeNotifications.reShowLastNotification(); 37 | return; 38 | } 39 | 40 | if (!DeepCodeUtils.getInstance().isSupportedFileFormat(psiFile)) { 41 | DeepCodeNotifications.showInfo( 42 | String.format( 43 | "Files with `%1$s` extension are not supported yet.", 44 | psiFile.getVirtualFile().getExtension()), 45 | project); 46 | return; 47 | } 48 | //RunUtils.asyncUpdateCurrentFilePanel(psiFile); 49 | ServiceManager.getService(project, myTodoView.class).refresh(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/actions/AnalyseProjectAction.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.actions; 2 | 3 | import ai.deepcode.jbplugin.core.*; 4 | import com.intellij.openapi.actionSystem.AnAction; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 7 | import com.intellij.openapi.project.Project; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class AnalyseProjectAction extends AnAction { 11 | 12 | @Override 13 | public void actionPerformed(@NotNull AnActionEvent e) { 14 | final Project project = e.getRequiredData(PlatformDataKeys.PROJECT); 15 | DCLogger.getInstance().logInfo("Re-Analyse Project requested for: " + project); 16 | // fixme: ?? background task here to avoid potential freeze due to MUTEX lock 17 | AnalysisData.getInstance().resetCachesAndTasks(project); 18 | if (LoginUtils.getInstance().isLogged(project, true)) { 19 | RunUtils.getInstance().asyncAnalyseProjectAndUpdatePanel(project); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/actions/InvalidateCachesAction.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.actions; 2 | 3 | import ai.deepcode.jbplugin.ui.myTodoView; 4 | import ai.deepcode.jbplugin.core.AnalysisData; 5 | import com.intellij.openapi.actionSystem.AnAction; 6 | import com.intellij.openapi.actionSystem.AnActionEvent; 7 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 8 | import com.intellij.openapi.components.ServiceManager; 9 | import com.intellij.openapi.project.Project; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class InvalidateCachesAction extends AnAction { 13 | 14 | @Override 15 | public void actionPerformed(@NotNull AnActionEvent e) { 16 | final Project project = e.getRequiredData(PlatformDataKeys.PROJECT); 17 | AnalysisData.getInstance().resetCachesAndTasks(project); 18 | ServiceManager.getService(project, myTodoView.class).refresh(); 19 | /* 20 | ActionManager.getInstance() 21 | .getAction("ai.deepcode.jbplugin.ToolsMenu.AnalyseProjectAction") 22 | .actionPerformed(e); 23 | */ 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/actions/SeeResultsInBrowserAction.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.actions; 2 | 3 | import ai.deepcode.jbplugin.core.AnalysisData; 4 | import com.intellij.ide.BrowserUtil; 5 | import com.intellij.openapi.actionSystem.AnAction; 6 | import com.intellij.openapi.actionSystem.AnActionEvent; 7 | import com.intellij.openapi.project.Project; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class SeeResultsInBrowserAction extends AnAction { 11 | 12 | @Override 13 | public void update(@NotNull AnActionEvent e) { 14 | final Project project = e.getProject(); 15 | if (project==null) return; 16 | final boolean urlExist = !AnalysisData.getInstance().getAnalysisUrl(project).isEmpty(); 17 | e.getPresentation().setEnabled(urlExist); 18 | } 19 | 20 | @Override 21 | public void actionPerformed(@NotNull AnActionEvent e) { 22 | final Project project = e.getProject(); 23 | if (project==null) return; 24 | final String analysisUrl = AnalysisData.getInstance().getAnalysisUrl(project); 25 | if (!analysisUrl.isEmpty()) BrowserUtil.open(analysisUrl); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/actions/ShowSettingsAction.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.actions; 2 | 3 | import ai.deepcode.jbplugin.ui.config.DeepCodeConfigEntry; 4 | import com.intellij.openapi.actionSystem.AnAction; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import com.intellij.openapi.options.ShowSettingsUtil; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class ShowSettingsAction extends AnAction { 10 | 11 | @Override 12 | public void actionPerformed(@NotNull AnActionEvent e) { 13 | ShowSettingsUtil.getInstance().showSettingsDialog(e.getProject(), DeepCodeConfigEntry.class); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/annotators/DeepCodeExternalAnnotator.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.annotators; 2 | 3 | import ai.deepcode.javaclient.core.MyTextRange; 4 | import ai.deepcode.jbplugin.actions.DeepCodeIntentionAction; 5 | import ai.deepcode.jbplugin.core.DCLogger; 6 | import ai.deepcode.jbplugin.core.PDU; 7 | import ai.deepcode.jbplugin.core.AnalysisData; 8 | import ai.deepcode.jbplugin.core.DeepCodeUtils; 9 | import ai.deepcode.javaclient.core.SuggestionForFile; 10 | import com.intellij.lang.annotation.Annotation; 11 | import com.intellij.lang.annotation.AnnotationHolder; 12 | import com.intellij.lang.annotation.ExternalAnnotator; 13 | import com.intellij.openapi.editor.Editor; 14 | import com.intellij.openapi.progress.ProgressManager; 15 | import com.intellij.openapi.util.TextRange; 16 | import com.intellij.psi.PsiFile; 17 | import org.jetbrains.annotations.NotNull; 18 | import org.jetbrains.annotations.Nullable; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | import java.util.Collections; 23 | import java.util.List; 24 | 25 | /** 26 | * We should use ExternalAnnotator to work on files level while ordinary Annotators works on 27 | * elements level. 28 | */ 29 | public class DeepCodeExternalAnnotator extends ExternalAnnotator> { 30 | 31 | private static final Logger LOG = LoggerFactory.getLogger("DeepCode.Annotator"); 32 | 33 | /* 34 | @Nullable 35 | @Override 36 | public PsiFile collectInformation(@NotNull PsiFile psiFile) { 37 | DCLogger.getInstance().info("collectInformation(@NotNull PsiFile psiFile) for " + psiFile); 38 | return psiFile; 39 | } 40 | 41 | */ 42 | @Override 43 | @Nullable 44 | public PsiFile collectInformation( 45 | @NotNull PsiFile psiFile, @NotNull Editor editor, boolean hasErrors) { 46 | // DCLogger.getInstance().info("collectInformation(@NotNull PsiFile psiFile, @NotNull Editor 47 | // editor, boolean hasErrors) for " + psiFile); 48 | return psiFile; 49 | // return collectInformation(psiFile); 50 | } 51 | 52 | @Nullable 53 | @Override 54 | public List doAnnotate(PsiFile psiFile) { 55 | if (!DeepCodeUtils.getInstance().isSupportedFileFormat(psiFile)) return Collections.emptyList(); 56 | final long annotatorId = System.currentTimeMillis(); 57 | DCLogger.getInstance() 58 | .logInfo("Annotator (" + annotatorId + ") requested for file: " + psiFile.getName()); 59 | AnalysisData.getInstance() 60 | .waitForUpdateAnalysisFinish(psiFile.getProject(), ProgressManager.getInstance().getProgressIndicator()); 61 | ProgressManager.checkCanceled(); 62 | List suggestions = AnalysisData.getInstance().getAnalysis(psiFile); 63 | DCLogger.getInstance() 64 | .logInfo( 65 | "Annotator (" + annotatorId + ") suggestions gotten for file: " + psiFile.getName()); 66 | ProgressManager.checkCanceled(); 67 | 68 | return suggestions; 69 | /* 70 | // https://youtrack.jetbrains.com/issue/IDEA-239960 71 | return ProgressManager.getInstance() 72 | .runProcess( 73 | () -> AnalysisData.getAnalysis(psiFile), 74 | new BackgroundableProcessIndicator( 75 | psiFile.getProject(), 76 | "DeepCode: Analysing " + psiFile.getName() + " file... ", 77 | () -> true, 78 | "Stop", 79 | "Stop file analysis", 80 | true)); 81 | */ 82 | } 83 | 84 | @SuppressWarnings("deprecation") // later move to .newAnnotation introduced in 2020.1 85 | @Override 86 | public void apply( 87 | @NotNull PsiFile psiFile, 88 | List suggestions, 89 | @NotNull AnnotationHolder holder) { 90 | if (suggestions == null) return; 91 | for (SuggestionForFile suggestion : suggestions) { 92 | final String message = "DeepCode: " + suggestion.getMessage(); 93 | Annotation annotation; 94 | for (MyTextRange myTextRange : suggestion.getRanges()) { 95 | final TextRange range = PDU.toTextRange(myTextRange); 96 | switch (suggestion.getSeverity()) { 97 | case 1: 98 | annotation = holder.createWeakWarningAnnotation(range, message); 99 | break; 100 | case 2: 101 | annotation = holder.createWarningAnnotation(range, message); 102 | break; 103 | case 3: 104 | annotation = holder.createErrorAnnotation(range, message); 105 | break; 106 | default: 107 | annotation = holder.createInfoAnnotation(range, message); 108 | break; 109 | } 110 | annotation.registerFix( 111 | new DeepCodeIntentionAction(psiFile, range, suggestion.getRule(), false)); 112 | annotation.registerFix( 113 | new DeepCodeIntentionAction(psiFile, range, suggestion.getRule(), true)); 114 | /* 115 | holder 116 | .newAnnotation(severity, "DeepCode: " + suggestion.getMessage()) 117 | .range(range) 118 | .create(); 119 | */ 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/core/AnalysisData.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.core; 2 | 3 | import ai.deepcode.javaclient.core.*; 4 | import ai.deepcode.jbplugin.DeepCodeStatusBarWidgetProvider; 5 | import com.intellij.psi.PsiFile; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.Collection; 9 | 10 | public final class AnalysisData extends AnalysisDataBase { 11 | 12 | private static final AnalysisData INSTANCE = new AnalysisData(); 13 | 14 | public static AnalysisData getInstance() { 15 | return INSTANCE; 16 | } 17 | 18 | private AnalysisData() { 19 | super( 20 | PDU.getInstance(), 21 | HashContentUtils.getInstance(), 22 | DeepCodeParams.getInstance(), 23 | DCLogger.getInstance()); 24 | } 25 | 26 | @Override 27 | protected void updateUIonFilesRemovalFromCache(@NotNull Collection files) { 28 | PDU.toPsiFiles(files).stream() 29 | .map(PsiFile::getProject) 30 | .distinct() 31 | .forEach(DeepCodeStatusBarWidgetProvider::updateWidget); 32 | // code from T0D0 already have listener for updates 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/core/BulkMode.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.core; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | public class BulkMode { 10 | private static final Map mapProject2RequestsCounter = new ConcurrentHashMap<>(); 11 | 12 | private static int getBulkRequestsCount(@NotNull Project project) { 13 | return mapProject2RequestsCounter.computeIfAbsent(project, p -> 0); 14 | } 15 | 16 | /** No events for individual files should be processed */ 17 | public static boolean isActive(@NotNull Project project) { 18 | return getBulkRequestsCount(project) > 0; 19 | } 20 | 21 | public static void set(@NotNull Project project) { 22 | final int counter = getBulkRequestsCount(project) + 1; 23 | if (counter == 1) { 24 | // cancel all running tasks first 25 | RunUtils.getInstance().cancelRunningIndicators(project); 26 | } 27 | DCLogger.getInstance().logInfo("BulkMode ON with " + counter + " total requests"); 28 | mapProject2RequestsCounter.put(project, counter); 29 | } 30 | 31 | public static void unset(@NotNull Project project) { 32 | final int counter = getBulkRequestsCount(project) - 1; 33 | if (counter >= 0) { 34 | mapProject2RequestsCounter.put(project, counter); 35 | DCLogger.getInstance().logInfo("BulkMode OFF with " + counter + " total requests"); 36 | } else { 37 | DCLogger.getInstance().logWarn("BulkMode OFF request with already " + counter + " total requests"); 38 | } 39 | } 40 | 41 | public static void forceUnset(@NotNull Project project) { 42 | mapProject2RequestsCounter.put(project, 0); 43 | DCLogger.getInstance().logInfo("BulkMode OFF forced"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/core/DCLogger.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.core; 2 | 3 | import ai.deepcode.javaclient.core.DCLoggerBase; 4 | import com.intellij.openapi.application.Application; 5 | import com.intellij.openapi.application.ApplicationManager; 6 | import com.intellij.openapi.diagnostic.Logger; 7 | import com.intellij.openapi.progress.ProgressIndicator; 8 | import com.intellij.openapi.progress.ProgressManager; 9 | import com.intellij.openapi.project.DumbService; 10 | import com.intellij.openapi.project.Project; 11 | import com.intellij.openapi.project.ProjectManager; 12 | 13 | public class DCLogger extends DCLoggerBase { 14 | 15 | private static final DCLogger INSTANCE = new DCLogger(); 16 | 17 | public static DCLogger getInstance() { 18 | return INSTANCE; 19 | } 20 | 21 | private DCLogger() { 22 | super( 23 | () -> Logger.getInstance("DeepCode")::debug, 24 | () -> Logger.getInstance("DeepCode")::warn, 25 | () -> Logger.getInstance("DeepCode").isDebugEnabled(), 26 | () -> true); 27 | } 28 | 29 | @Override 30 | protected String getExtraInfo() { 31 | String currentThread = " [" + Thread.currentThread().getName() + "] "; 32 | 33 | final Application application = ApplicationManager.getApplication(); 34 | String rwAccess = (application.isReadAccessAllowed() ? "R" : "-"); 35 | rwAccess += (application.isWriteAccessAllowed() ? "W" : "-"); 36 | 37 | // fixme presume we work with one project only 38 | final Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); 39 | String mode = 40 | (openProjects.length != 0) 41 | ? DumbService.getInstance(openProjects[0]).isDumb() ? "D" : "S" 42 | : "X"; 43 | 44 | final ProgressIndicator currentProgressIndicator = 45 | ProgressManager.getInstance().getProgressIndicator(); 46 | String progressIndicator = 47 | (currentProgressIndicator == null) 48 | ? "" 49 | : "\n" + "ProgressIndicator [" + currentProgressIndicator.toString() + "]"; 50 | 51 | return rwAccess + mode + currentThread + progressIndicator; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/core/DeepCodeIgnoreInfoHolder.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.core; 2 | 3 | import ai.deepcode.javaclient.core.DeepCodeIgnoreInfoHolderBase; 4 | import com.intellij.openapi.vfs.VfsUtil; 5 | import com.intellij.openapi.vfs.VirtualFile; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | class DeepCodeIgnoreInfoHolder extends DeepCodeIgnoreInfoHolderBase { 9 | 10 | private static final DeepCodeIgnoreInfoHolder INSTANCE = new DeepCodeIgnoreInfoHolder(); 11 | 12 | public static DeepCodeIgnoreInfoHolder getInstance() { 13 | return INSTANCE; 14 | } 15 | 16 | private DeepCodeIgnoreInfoHolder() { 17 | super(HashContentUtils.getInstance()); 18 | } 19 | 20 | @Override 21 | protected String getFilePath(@NotNull Object file) { 22 | return PDU.toPsiFile(file).getVirtualFile().getPath(); 23 | } 24 | 25 | /* 26 | @Override 27 | protected boolean inScope(@NotNull Object dcignoreFile, @NotNull Object fileToCheck) { 28 | final VirtualFile dcignoreDir = PDU.toPsiFile(dcignoreFile).getVirtualFile().getParent(); 29 | return VfsUtil.isAncestor(dcignoreDir, PDU.toPsiFile(fileToCheck).getVirtualFile(), true); 30 | } 31 | */ 32 | 33 | @Override 34 | protected String getFileName(@NotNull Object file) { 35 | return PDU.toPsiFile(file).getVirtualFile().getName(); 36 | } 37 | 38 | @Override 39 | protected Object getProjectOfFile(@NotNull Object file) { 40 | return PDU.toPsiFile(file).getProject(); 41 | } 42 | 43 | @Override 44 | protected String getDirPath(@NotNull Object file) { 45 | return PDU.toPsiFile(file).getVirtualFile().getParent().getPath(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/core/DeepCodeParams.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.core; 2 | 3 | import ai.deepcode.javaclient.DeepCodeRestApi; 4 | import ai.deepcode.javaclient.core.DeepCodeParamsBase; 5 | import com.intellij.ide.util.PropertiesComponent; 6 | import com.intellij.openapi.application.ApplicationNamesInfo; 7 | import com.intellij.openapi.project.Project; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.Calendar; 11 | 12 | public class DeepCodeParams extends DeepCodeParamsBase { 13 | 14 | private static final DeepCodeParams INSTANCE = new DeepCodeParams(); 15 | 16 | public static DeepCodeParams getInstance() { 17 | return INSTANCE; 18 | } 19 | 20 | public static enum UpdateMode { 21 | INTERACTIVE_MODE, 22 | ON_SAVE_MODE, 23 | MANUAL_MODE 24 | } 25 | 26 | private UpdateMode updateMode; 27 | 28 | // TODO https://www.jetbrains.org/intellij/sdk/docs/basics/persisting_sensitive_data.html 29 | private final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(); 30 | 31 | private DeepCodeParams() { 32 | super( 33 | PropertiesComponent.getInstance().getBoolean("isEnable", true), 34 | PropertiesComponent.getInstance().getValue("apiUrl", "https://www.deepcode.ai/"), 35 | PropertiesComponent.getInstance().getBoolean("useLinter"), 36 | PropertiesComponent.getInstance().getInt("minSeverity", 1), 37 | PropertiesComponent.getInstance().getValue("sessionToken", ""), 38 | PropertiesComponent.getInstance().getValue("loginUrl", ""), 39 | ApplicationNamesInfo.getInstance().getProductName()); 40 | DeepCodeRestApi.setBaseUrl(getApiUrl()); 41 | 42 | String pastIdeProductName = propertiesComponent.getValue("ideProductName", ""); 43 | String ideProductName = getIdeProductName(); 44 | if (!pastIdeProductName.equals(ideProductName)) { 45 | clearLoginParams(); 46 | propertiesComponent.setValue("ideProductName", ideProductName); 47 | } 48 | updateMode = 49 | UpdateMode.valueOf( 50 | propertiesComponent.getValue("updateMode", UpdateMode.INTERACTIVE_MODE.name())); 51 | } 52 | 53 | @Override 54 | public void setSessionToken(String sessionToken) { 55 | super.setSessionToken(sessionToken); 56 | propertiesComponent.setValue("sessionToken", sessionToken); 57 | } 58 | 59 | @Override 60 | public void setLoginUrl(String loginUrl) { 61 | super.setLoginUrl(loginUrl); 62 | propertiesComponent.setValue("loginUrl", loginUrl); 63 | } 64 | 65 | @Override 66 | public void setUseLinter(boolean useLinter) { 67 | super.setUseLinter(useLinter); 68 | propertiesComponent.setValue("useLinter", useLinter); 69 | } 70 | 71 | @Override 72 | public void setMinSeverity(int minSeverity) { 73 | super.setMinSeverity(minSeverity); 74 | propertiesComponent.setValue("minSeverity", String.valueOf(minSeverity)); 75 | } 76 | 77 | @Override 78 | public void setApiUrl(@NotNull String apiUrl) { 79 | super.setApiUrl(apiUrl); 80 | propertiesComponent.setValue("apiUrl", apiUrl); 81 | } 82 | 83 | @Override 84 | public void setEnable(boolean isEnable) { 85 | super.setEnable(isEnable); 86 | propertiesComponent.setValue("isEnable", isEnable, true); 87 | } 88 | 89 | @Override 90 | public boolean consentGiven(@NotNull Object projectO) { 91 | Project project = PDU.toProject(projectO); 92 | return PropertiesComponent.getInstance(project).getBoolean("consentGiven"); 93 | } 94 | 95 | @Override 96 | public void setConsentGiven(@NotNull Object projectO) { 97 | Project project = PDU.toProject(projectO); 98 | PropertiesComponent.getInstance(project).setValue("consentGiven", true); 99 | } 100 | 101 | public boolean isFirstStart() { 102 | boolean result = PropertiesComponent.getInstance().getBoolean("deepcode.isFirstStart", true); 103 | if (result) PropertiesComponent.getInstance().setValue("deepcode.isFirstStart", false, true); 104 | return result; 105 | } 106 | 107 | public boolean needToShowReplacementMessage() { 108 | int lastMonthShown = PropertiesComponent.getInstance().getInt("deepcode.lastMonthReplacementMessageShownFor", 0); 109 | int currentMonth = Calendar.getInstance().get(Calendar.MONTH); 110 | int currentYear = Calendar.getInstance().get(Calendar.YEAR); 111 | final boolean result = lastMonthShown != currentMonth || (currentYear == 2021 && currentMonth >= Calendar.AUGUST); 112 | if (result) PropertiesComponent.getInstance().setValue("deepcode.lastMonthReplacementMessageShownFor", currentMonth, 0); 113 | return result; 114 | } 115 | 116 | public boolean isPluginDeprecated() { 117 | int currentDayOfMonth = Calendar.getInstance().get(Calendar.DAY_OF_MONTH); 118 | int currentMonth = Calendar.getInstance().get(Calendar.MONTH); 119 | int currentYear = Calendar.getInstance().get(Calendar.YEAR); 120 | return currentYear >= 2021 && currentMonth >= Calendar.AUGUST && currentDayOfMonth > 7; 121 | } 122 | 123 | public UpdateMode getUpdateMode() { 124 | return updateMode; 125 | } 126 | 127 | public void setUpdateMode(UpdateMode updateMode) { 128 | this.updateMode = updateMode; 129 | propertiesComponent.setValue("updateMode", updateMode.name()); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/core/DeepCodeUtils.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.core; 2 | 3 | import ai.deepcode.javaclient.core.DeepCodeUtilsBase; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.project.ProjectUtil; 6 | import com.intellij.openapi.vcs.changes.ChangeListManager; 7 | import com.intellij.openapi.vfs.VirtualFile; 8 | import com.intellij.psi.PsiDirectory; 9 | import com.intellij.psi.PsiFile; 10 | import com.intellij.psi.PsiManager; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.*; 14 | 15 | public final class DeepCodeUtils extends DeepCodeUtilsBase { 16 | 17 | private static final DeepCodeUtils INSTANCE = new DeepCodeUtils(); 18 | private final DCLogger dcLogger = DCLogger.getInstance(); 19 | 20 | private DeepCodeUtils() { 21 | super( 22 | AnalysisData.getInstance(), 23 | DeepCodeParams.getInstance(), 24 | DeepCodeIgnoreInfoHolder.getInstance(), 25 | DCLogger.getInstance()); 26 | } 27 | 28 | public static DeepCodeUtilsBase getInstance() { 29 | return INSTANCE; 30 | } 31 | 32 | @Override 33 | protected Collection allProjectFiles(@NotNull Object projectO) { 34 | Project project = PDU.toProject(projectO); 35 | return RunUtils.computeInReadActionInSmartMode( 36 | project, 37 | () -> { 38 | final PsiManager psiManager = PsiManager.getInstance(project); 39 | final VirtualFile projectDir = ProjectUtil.guessProjectDir(project); 40 | if (projectDir == null) { 41 | dcLogger.logWarn("Project directory not found for: " + project); 42 | return Collections.emptyList(); 43 | } 44 | final PsiDirectory prjDirectory = psiManager.findDirectory(projectDir); 45 | if (prjDirectory == null) { 46 | dcLogger.logWarn("Project directory not found for: " + project); 47 | return Collections.emptyList(); 48 | } 49 | return PDU.toObjects(getFilesRecursively(prjDirectory)); 50 | }); 51 | } 52 | 53 | private List getFilesRecursively(@NotNull PsiDirectory psiDirectory) { 54 | List psiFileList = new ArrayList<>(Arrays.asList(psiDirectory.getFiles())); 55 | for (PsiDirectory subDir : psiDirectory.getSubdirectories()) { 56 | psiFileList.addAll(getFilesRecursively(subDir)); 57 | } 58 | return psiFileList; 59 | } 60 | 61 | @Override 62 | protected long getFileLength(@NotNull Object file) { 63 | return PDU.toPsiFile(file).getVirtualFile().getLength(); 64 | } 65 | 66 | @Override 67 | protected String getFileExtention(@NotNull Object file) { 68 | return PDU.toPsiFile(file).getVirtualFile().getExtension(); 69 | } 70 | 71 | @Override 72 | protected boolean isGitIgnored(@NotNull Object file) { 73 | PsiFile psiFile = PDU.toPsiFile(file); 74 | final VirtualFile virtualFile = psiFile.getVirtualFile(); 75 | if (virtualFile == null) return false; 76 | return (ChangeListManager.getInstance(psiFile.getProject()).isIgnoredFile(virtualFile)); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/core/HashContentUtils.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.core; 2 | 3 | import ai.deepcode.javaclient.core.HashContentUtilsBase; 4 | import ai.deepcode.javaclient.core.PlatformDependentUtilsBase; 5 | import com.intellij.psi.PsiFile; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | public class HashContentUtils extends HashContentUtilsBase { 9 | 10 | private static final HashContentUtils INSTANCE = new HashContentUtils(PDU.getInstance()); 11 | 12 | private HashContentUtils(@NotNull PlatformDependentUtilsBase platformDependentUtils) { 13 | super(platformDependentUtils); 14 | } 15 | 16 | public static HashContentUtils getInstance() { 17 | return INSTANCE; 18 | } 19 | 20 | @NotNull 21 | public String doGetFileContent(@NotNull Object file) { 22 | PsiFile psiFile = PDU.toPsiFile(file); 23 | final String fileContent = 24 | RunUtils.computeInReadActionInSmartMode( 25 | psiFile.getProject(), () -> getPsiFileText(psiFile)); 26 | return fileContent != null ? fileContent : ""; 27 | } 28 | 29 | /** Should be run inside Read action !!! */ 30 | @NotNull 31 | private static String getPsiFileText(@NotNull PsiFile psiFile) { 32 | if (!psiFile.isValid()) { 33 | DCLogger.getInstance().logWarn("Invalid PsiFile: " + psiFile); 34 | return ""; 35 | } 36 | // psiFile.getText() is NOT expensive as it's goes to VirtualFileContent.getText() 37 | return psiFile.getText(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/core/LoginUtils.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.core; 2 | 3 | import ai.deepcode.javaclient.core.LoginUtilsBase; 4 | import com.intellij.openapi.application.ApplicationInfo; 5 | import com.intellij.openapi.application.ApplicationNamesInfo; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public class LoginUtils extends LoginUtilsBase { 9 | 10 | private static final LoginUtils INSTANCE = new LoginUtils(); 11 | 12 | public static LoginUtils getInstance() { 13 | return INSTANCE; 14 | } 15 | 16 | private LoginUtils() { 17 | super( 18 | PDU.getInstance(), 19 | DeepCodeParams.getInstance(), 20 | AnalysisData.getInstance(), 21 | DCLogger.getInstance()); 22 | } 23 | 24 | private static final String userAgent = 25 | "JetBrains-" 26 | + ApplicationNamesInfo.getInstance().getProductName() 27 | + "-" 28 | + ApplicationInfo.getInstance().getFullVersion(); 29 | 30 | @Override 31 | protected String getUserAgent() { 32 | return userAgent; 33 | } 34 | 35 | @Override 36 | public boolean isLogged(@Nullable Object project, boolean userActionNeeded) { 37 | return !DeepCodeParams.getInstance().isPluginDeprecated() && super.isLogged(project, userActionNeeded); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/core/MyProjectManagerListener.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.core; 2 | 3 | import com.intellij.openapi.editor.Document; 4 | import com.intellij.openapi.fileEditor.FileDocumentManager; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.project.ProjectManagerListener; 7 | import com.intellij.openapi.vfs.VirtualFile; 8 | import com.intellij.psi.PsiFile; 9 | import com.intellij.psi.PsiManager; 10 | import com.intellij.psi.PsiTreeChangeAdapter; 11 | import com.intellij.psi.PsiTreeChangeEvent; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.Collections; 15 | 16 | /** Add PsiTree change Listener to clear caches for file if it was changed. */ 17 | public class MyProjectManagerListener implements ProjectManagerListener { 18 | 19 | public MyProjectManagerListener(@NotNull Project project) { 20 | projectOpened(project); 21 | } 22 | 23 | @Override 24 | public void projectOpened(@NotNull Project project) { 25 | if (AnalysisData.getInstance().addProjectToCache(project)) { 26 | // EditorFactory.getEventMulticaster.addDocumentListener BulkAwareDocumentListener 27 | PsiManager.getInstance(project).addPsiTreeChangeListener(new MyPsiTreeChangeAdapter()); 28 | } 29 | } 30 | 31 | @Override 32 | public void projectClosing(@NotNull Project project) { 33 | RunUtils.getInstance() 34 | .runInBackground( 35 | project, 36 | "Project closing: " + project.getName(), 37 | (progress) -> { 38 | // lets all running ProgressIndicators release MUTEX first 39 | RunUtils.getInstance().cancelRunningIndicators(project); 40 | AnalysisData.getInstance().removeProjectFromCaches(project); 41 | }); 42 | } 43 | 44 | private static class MyPsiTreeChangeAdapter extends PsiTreeChangeAdapter { 45 | @Override 46 | public void beforeChildrenChange(@NotNull PsiTreeChangeEvent event) { 47 | final PsiFile psiFile = event.getFile(); 48 | if (psiFile == null || BulkMode.isActive(psiFile.getProject())) return; 49 | if (AnalysisData.getInstance().isFileInCache(psiFile)) { 50 | // ?? immediate delete for visual updates in Panel, annotations, etc. 51 | // should be done in background to wait MUTEX released in case of currently running update 52 | RunUtils.getInstance() 53 | .runInBackground( 54 | psiFile.getProject(), 55 | "Cache updating...", 56 | (progress) -> { 57 | AnalysisData.getInstance().removeFilesFromCache(Collections.singleton(psiFile)); 58 | // to postpone Annotator and show ? in UI 59 | AnalysisData.getInstance().setUpdateInProgress(psiFile.getProject()); 60 | }); 61 | } 62 | /* 63 | if (DeepCodeUtils.isSupportedFileFormat(psiFile)) { 64 | // should be done in background to wait MUTEX released in case of currently running update 65 | RunUtils.runInBackground( 66 | psiFile.getProject(), 67 | () -> { 68 | // to prevent updating already done by MyBulkFileListener 69 | if (AnalysisData.isHashChanged(psiFile)) { 70 | AnalysisData.removeFilesFromCache(Collections.singleton(psiFile)); 71 | } 72 | }); 73 | } 74 | */ 75 | } 76 | 77 | @Override 78 | public void childrenChanged(@NotNull PsiTreeChangeEvent event) { 79 | final PsiFile psiFile = event.getFile(); 80 | if (psiFile == null) return; 81 | final Project project = psiFile.getProject(); 82 | if (BulkMode.isActive(project)) return; 83 | if (DeepCodeParams.getInstance().getUpdateMode() 84 | != DeepCodeParams.UpdateMode.INTERACTIVE_MODE) return; 85 | 86 | if (DeepCodeUtils.getInstance().isSupportedFileFormat(psiFile)) { 87 | RunUtils.getInstance() 88 | .runInBackgroundCancellable( 89 | psiFile, 90 | "Analyzing changes in " + psiFile.getName(), 91 | (progress) -> { 92 | if (AnalysisData.getInstance().isFileInCache(psiFile)) { 93 | // should be already deleted at beforeChildrenChange in most cases, 94 | // but in case of update finished between beforeChildrenChange and now. 95 | AnalysisData.getInstance().removeFilesFromCache(Collections.singleton(psiFile)); 96 | } 97 | RunUtils.getInstance() 98 | .updateCachedAnalysisResults( 99 | project, Collections.singleton(psiFile), progress); 100 | }); 101 | } 102 | 103 | if (DeepCodeIgnoreInfoHolder.getInstance().is_dcignoreFile(psiFile)) { 104 | DeepCodeIgnoreInfoHolder.getInstance().update_dcignoreFileContent(psiFile); 105 | // delayed to prevent unnecessary updates in case of continuous typing by user 106 | RunUtils.getInstance() 107 | .rescanInBackgroundCancellableDelayed(project, PDU.DEFAULT_DELAY, false); 108 | } 109 | // .gitignore content delay to be parsed https://youtrack.jetbrains.com/issue/IDEA-239773 110 | final VirtualFile virtualFile = psiFile.getVirtualFile(); 111 | if (virtualFile != null && virtualFile.getName().equals(".gitignore")) { 112 | final Document document = psiFile.getViewProvider().getDocument(); 113 | if (document != null) { 114 | FileDocumentManager.getInstance().saveDocument(document); 115 | // delayed to let git update it meta-info 116 | RunUtils.getInstance() 117 | .rescanInBackgroundCancellableDelayed(project, PDU.DEFAULT_DELAY, false); 118 | } 119 | } 120 | } 121 | 122 | @Override 123 | public void beforeChildRemoval(@NotNull PsiTreeChangeEvent event) { 124 | PsiFile psiFile = (event.getChild() instanceof PsiFile) ? (PsiFile) event.getChild() : null; 125 | if (psiFile == null) return; 126 | final Project project = psiFile.getProject(); 127 | if (BulkMode.isActive(project)) return; 128 | if (DeepCodeParams.getInstance().getUpdateMode() 129 | != DeepCodeParams.UpdateMode.INTERACTIVE_MODE) return; 130 | 131 | if (DeepCodeIgnoreInfoHolder.getInstance().is_ignoreFile(psiFile)) { 132 | DeepCodeIgnoreInfoHolder.getInstance().remove_dcignoreFileContent(psiFile); 133 | // ??? small delay to prevent duplicated delete with MyBulkFileListener 134 | RunUtils.getInstance() 135 | .rescanInBackgroundCancellableDelayed(project, PDU.DEFAULT_DELAY_SMALL, false); 136 | } 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/core/RunUtils.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.core; 2 | 3 | import ai.deepcode.javaclient.core.RunUtilsBase; 4 | import com.intellij.openapi.application.ReadAction; 5 | import com.intellij.openapi.progress.ProgressIndicator; 6 | import com.intellij.openapi.progress.ProgressManager; 7 | import com.intellij.openapi.progress.Task; 8 | import com.intellij.openapi.project.DumbService; 9 | import com.intellij.openapi.project.Project; 10 | import com.intellij.openapi.project.ProjectManager; 11 | import com.intellij.openapi.util.Computable; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.Collection; 15 | import java.util.function.Consumer; 16 | 17 | public class RunUtils extends RunUtilsBase { 18 | 19 | private static final RunUtils INSTANCE = new RunUtils(); 20 | 21 | public static RunUtils getInstance() { 22 | return INSTANCE; 23 | } 24 | 25 | private RunUtils() { 26 | super( 27 | PDU.getInstance(), 28 | HashContentUtils.getInstance(), 29 | AnalysisData.getInstance(), 30 | DeepCodeUtils.getInstance(), 31 | DCLogger.getInstance()); 32 | } 33 | 34 | public static T computeInReadActionInSmartMode( 35 | @NotNull Project project, @NotNull final Computable computation) { 36 | // DCLogger.getInstance().info("computeInReadActionInSmartMode requested"); 37 | final DumbService dumbService = 38 | ReadAction.compute(() -> project.isDisposed() ? null : DumbService.getInstance(project)); 39 | if (dumbService == null) { 40 | DCLogger.getInstance().logWarn("dumbService == null"); 41 | return null; 42 | } 43 | return dumbService.runReadActionInSmartMode( 44 | () -> { 45 | // DCLogger.getInstance().info("computeInReadActionInSmartMode actually executing"); 46 | return computation.compute(); 47 | }); 48 | } 49 | 50 | /** 51 | * Should implement reuse of currently running parent DeepCode Progress if possible 52 | * 53 | * @return true if reuse been successful 54 | */ 55 | @Override 56 | protected boolean reuseCurrentProgress( 57 | @NotNull Object project, @NotNull String title, @NotNull Consumer progressConsumer) { 58 | final ProgressManager progressManager = ProgressManager.getInstance(); 59 | final ProgressIndicator progressIndicator = progressManager.getProgressIndicator(); 60 | if (getRunningProgresses(PDU.toProject(project)).contains(progressIndicator)) { 61 | final MyBackgroundable myBackgroundable = 62 | new MyBackgroundable(PDU.toProject(project), title, progressConsumer); 63 | progressManager.runProcessWithProgressAsynchronously(myBackgroundable, progressIndicator); 64 | return true; 65 | } 66 | return false; 67 | } 68 | 69 | /** Should implement background task creation with call of progressConsumer() inside Job.run() */ 70 | @Override 71 | protected void doBackgroundRun( 72 | @NotNull Object project, @NotNull String title, @NotNull Consumer progressConsumer) { 73 | ProgressManager.getInstance() 74 | .run(new MyBackgroundable(PDU.toProject(project), title, progressConsumer)); 75 | } 76 | 77 | @NotNull 78 | private static ProgressIndicator toProgress(@NotNull Object progress) { 79 | if (!(progress instanceof ProgressIndicator)) 80 | throw new IllegalArgumentException("progress should be ProgressIndicator instance"); 81 | return (ProgressIndicator) progress; 82 | } 83 | 84 | @Override 85 | protected void cancelProgress(@NotNull Object progress) { 86 | toProgress(progress).cancel(); 87 | } 88 | 89 | @Override 90 | protected void bulkModeForceUnset(@NotNull Object project) { 91 | // in case any indicator holds Bulk mode process 92 | BulkMode.forceUnset(PDU.toProject(project)); 93 | } 94 | 95 | @Override 96 | protected void bulkModeUnset(@NotNull Object project) { 97 | BulkMode.unset(PDU.toProject(project)); 98 | } 99 | 100 | @Override 101 | protected void updateAnalysisResultsUIPresentation(@NotNull Collection files) { 102 | // code from T0D0 already have listener for updates 103 | } 104 | 105 | private class MyBackgroundable extends Task.Backgroundable { 106 | private @NotNull final Project project; 107 | private @NotNull final Consumer consumer; 108 | 109 | public MyBackgroundable( 110 | @NotNull Project project, @NotNull String title, @NotNull Consumer consumer) { 111 | super(project, "DeepCode: " + title); 112 | this.project = project; 113 | this.consumer = consumer; 114 | } 115 | 116 | @Override 117 | public void run(@NotNull ProgressIndicator indicator) { 118 | indicator.setIndeterminate(false); 119 | consumer.accept(indicator); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/AllTodosTreeBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2019 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import com.intellij.openapi.project.Project; 20 | import org.jetbrains.annotations.ApiStatus; 21 | import org.jetbrains.annotations.NotNull; 22 | 23 | import javax.swing.*; 24 | import javax.swing.tree.DefaultTreeModel; 25 | 26 | public class AllTodosTreeBuilder extends TodoTreeBuilder { 27 | /** 28 | * @deprecated To remove in 2020.1 29 | */ 30 | @ApiStatus.ScheduledForRemoval(inVersion = "2020.1") 31 | @Deprecated 32 | public AllTodosTreeBuilder(JTree tree, DefaultTreeModel treeModel, Project project) { 33 | this(tree, project); 34 | } 35 | 36 | public AllTodosTreeBuilder(JTree tree, Project project) { 37 | super(tree, project); 38 | } 39 | 40 | @Override 41 | @NotNull 42 | protected TodoTreeStructure createTreeStructure() { 43 | return new AllTodosTreeStructure(myProject); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/AllTodosTreeStructure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2009 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import ai.deepcode.jbplugin.ui.nodes.ToDoRootNode; 20 | import com.intellij.ide.util.treeView.AbstractTreeNode; 21 | import com.intellij.openapi.project.Project; 22 | import com.intellij.psi.PsiFile; 23 | 24 | /** 25 | * @author Vladimir Kondratyev 26 | */ 27 | public class AllTodosTreeStructure extends TodoTreeStructure { 28 | public AllTodosTreeStructure(final Project project) { 29 | super(project); 30 | } 31 | 32 | @Override 33 | public boolean accept(final PsiFile psiFile) { 34 | final boolean 35 | accept = psiFile.isValid() 36 | /* 37 | && ( 38 | myTodoFilter != null && myTodoFilter.accept(mySearchHelper, psiFile) || 39 | (myTodoFilter == null && mySearchHelper.getTodoItemsCount(psiFile) > 0) 40 | ) 41 | */ 42 | ; 43 | return accept; 44 | } 45 | 46 | @Override 47 | public boolean getIsPackagesShown() { 48 | return myArePackagesShown; 49 | } 50 | 51 | @Override 52 | Object getFirstSelectableElement() { 53 | return ((ToDoRootNode)myRootElement).getSummaryNode(); 54 | } 55 | 56 | @Override 57 | protected AbstractTreeNode createRootElement() { 58 | return new ToDoRootNode(myProject, new Object(), 59 | myBuilder, mySummaryElement); 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/ChangeListTodosPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2014 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import com.intellij.openapi.project.Project; 20 | import com.intellij.openapi.vcs.changes.Change; 21 | import com.intellij.openapi.vcs.changes.ChangeList; 22 | import com.intellij.openapi.vcs.changes.ChangeListAdapter; 23 | import com.intellij.openapi.vcs.changes.ChangeListManager; 24 | import com.intellij.ui.AppUIUtil; 25 | import com.intellij.ui.content.Content; 26 | import com.intellij.util.Alarm; 27 | 28 | import java.util.Collection; 29 | 30 | public abstract class ChangeListTodosPanel extends TodoPanel{ 31 | private final Alarm myAlarm; 32 | 33 | public ChangeListTodosPanel(Project project,TodoPanelSettings settings, Content content){ 34 | super(project,settings,false,content); 35 | ChangeListManager.getInstance(project).addChangeListListener(new MyChangeListManagerListener(), this); 36 | myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this); 37 | } 38 | 39 | private final class MyChangeListManagerListener extends ChangeListAdapter { 40 | @Override 41 | public void defaultListChanged(final ChangeList oldDefaultList, final ChangeList newDefaultList) { 42 | rebuildWithAlarm(myAlarm); 43 | AppUIUtil.invokeOnEdt(() -> setDisplayName(myTodoView.getTabNameForChangeList(newDefaultList.getName()))); 44 | } 45 | 46 | @Override 47 | public void changeListRenamed(final ChangeList list, final String oldName) { 48 | AppUIUtil.invokeOnEdt(() -> setDisplayName(myTodoView.getTabNameForChangeList(list.getName()))); 49 | } 50 | 51 | @Override 52 | public void changesMoved(final Collection changes, final ChangeList fromList, final ChangeList toList) { 53 | rebuildWithAlarm(myAlarm); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/ChangeListTodosTreeBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2016 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import com.intellij.openapi.project.Project; 20 | import org.jetbrains.annotations.NotNull; 21 | 22 | import javax.swing.*; 23 | 24 | public class ChangeListTodosTreeBuilder extends TodoTreeBuilder{ 25 | public ChangeListTodosTreeBuilder(JTree tree, Project project){ 26 | super(tree, project); 27 | } 28 | 29 | @Override 30 | @NotNull 31 | protected TodoTreeStructure createTreeStructure(){ 32 | return new ChangeListTodosTreeStructure(myProject); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/ChangeListTodosTreeStructure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2009 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import ai.deepcode.jbplugin.ui.nodes.ToDoRootNode; 20 | import com.intellij.ide.util.treeView.AbstractTreeNode; 21 | import com.intellij.openapi.project.Project; 22 | import com.intellij.openapi.vcs.FilePath; 23 | import com.intellij.openapi.vcs.FileStatus; 24 | import com.intellij.openapi.vcs.changes.Change; 25 | import com.intellij.openapi.vcs.changes.ChangeListManager; 26 | import com.intellij.openapi.vcs.changes.ContentRevision; 27 | import com.intellij.openapi.vfs.VirtualFile; 28 | import com.intellij.psi.PsiFile; 29 | import com.intellij.vcsUtil.VcsUtil; 30 | 31 | import java.util.Collection; 32 | 33 | public class ChangeListTodosTreeStructure extends TodoTreeStructure { 34 | public ChangeListTodosTreeStructure(Project project) { 35 | super(project); 36 | } 37 | 38 | @Override 39 | public boolean accept(final PsiFile psiFile) { 40 | if (!psiFile.isValid()) return false; 41 | 42 | VirtualFile file = psiFile.getVirtualFile(); 43 | ChangeListManager listManager = ChangeListManager.getInstance(myProject); 44 | 45 | FileStatus status = listManager.getStatus(file); 46 | if (status == FileStatus.NOT_CHANGED) return false; 47 | 48 | FilePath filePath = VcsUtil.getFilePath(file); 49 | final Collection changes = listManager.getDefaultChangeList().getChanges(); 50 | for (Change change : changes) { 51 | ContentRevision afterRevision = change.getAfterRevision(); 52 | if (afterRevision != null && afterRevision.getFile().equals(filePath)) { 53 | return (myTodoFilter != null && myTodoFilter.accept(mySearchHelper, psiFile) || 54 | (myTodoFilter == null && mySearchHelper.getTodoItemsCount(psiFile) > 0)); 55 | } 56 | } 57 | return false; 58 | } 59 | 60 | @Override 61 | public boolean getIsPackagesShown() { 62 | return myArePackagesShown; 63 | } 64 | 65 | @Override 66 | Object getFirstSelectableElement() { 67 | return ((ToDoRootNode)myRootElement).getSummaryNode(); 68 | } 69 | 70 | @Override 71 | protected AbstractTreeNode createRootElement() { 72 | return new ToDoRootNode(myProject, new Object(), myBuilder, mySummaryElement); 73 | } 74 | } -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/CurrentFileTodosPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2016 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ai.deepcode.jbplugin.ui; 17 | 18 | import com.intellij.openapi.application.ApplicationManager; 19 | import com.intellij.openapi.fileEditor.FileEditorManager; 20 | import com.intellij.openapi.fileEditor.FileEditorManagerEvent; 21 | import com.intellij.openapi.fileEditor.FileEditorManagerListener; 22 | import com.intellij.openapi.project.DumbService; 23 | import com.intellij.openapi.project.Project; 24 | import com.intellij.openapi.vfs.VirtualFile; 25 | import com.intellij.psi.PsiFile; 26 | import com.intellij.psi.PsiManager; 27 | import com.intellij.ui.content.Content; 28 | import org.jetbrains.annotations.NotNull; 29 | 30 | abstract class CurrentFileTodosPanel extends TodoPanel { 31 | CurrentFileTodosPanel(Project project, TodoPanelSettings settings, Content content) { 32 | super(project, settings, true, content); 33 | 34 | VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles(); 35 | setFile(files.length == 0 ? null : PsiManager.getInstance(myProject).findFile(files[0]), true); 36 | // It's important to remove this listener. It prevents invocation of setFile method after the tree builder is disposed 37 | project.getMessageBus().connect(this).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerListener() { 38 | @Override 39 | public void selectionChanged(@NotNull FileEditorManagerEvent e) { 40 | VirtualFile file = e.getNewFile(); 41 | final PsiFile psiFile = file != null && file.isValid() ? PsiManager.getInstance(myProject).findFile(file) : null; 42 | // This invokeLater is required. The problem is setFile does a commit to PSI, but setFile is 43 | // invoked inside PSI change event. It causes an Exception like "Changes to PSI are not allowed inside event processing" 44 | DumbService.getInstance(myProject).smartInvokeLater(() -> setFile(psiFile, false)); 45 | } 46 | }); 47 | } 48 | 49 | private void setFile(PsiFile file, boolean initialUpdate) { 50 | // setFile method is invoked in LaterInvocator so PsiManager 51 | // can be already disposed, so we need to check this before using it. 52 | if (myProject == null || PsiManager.getInstance(myProject).isDisposed()) { 53 | return; 54 | } 55 | 56 | if (file != null && getSelectedFile() == file) return; 57 | 58 | CurrentFileTodosTreeBuilder builder = (CurrentFileTodosTreeBuilder)myTodoTreeBuilder; 59 | builder.setFile(file); 60 | if (myTodoTreeBuilder.isUpdatable() || initialUpdate) { 61 | Object selectableElement = builder.getTodoTreeStructure().getFirstSelectableElement(); 62 | if (selectableElement != null) { 63 | builder.select(selectableElement); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/CurrentFileTodosTreeBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2009 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import com.intellij.openapi.project.Project; 20 | import com.intellij.openapi.vfs.VirtualFile; 21 | import com.intellij.psi.PsiFile; 22 | import org.jetbrains.annotations.NotNull; 23 | 24 | import javax.swing.*; 25 | import java.util.HashSet; 26 | import java.util.Set; 27 | 28 | /** 29 | * @author Vladimir Kondratyev 30 | */ 31 | public class CurrentFileTodosTreeBuilder extends TodoTreeBuilder { 32 | public CurrentFileTodosTreeBuilder(JTree tree, Project project){ 33 | super(tree, project); 34 | } 35 | 36 | @Override 37 | @NotNull 38 | protected TodoTreeStructure createTreeStructure(){ 39 | return new CurrentFileTodosTreeStructure(myProject); 40 | } 41 | 42 | @Override 43 | void rebuildCache(){ 44 | Set files = new HashSet<>(); 45 | CurrentFileTodosTreeStructure treeStructure=(CurrentFileTodosTreeStructure)getTodoTreeStructure(); 46 | PsiFile psiFile=treeStructure.getFile(); 47 | if(treeStructure.accept(psiFile)){ 48 | files.add(psiFile.getVirtualFile()); 49 | } 50 | super.rebuildCache(files); 51 | } 52 | 53 | /** 54 | * @see ai.deepcode.jbplugin.ui.CurrentFileTodosTreeStructure#setFile 55 | */ 56 | public void setFile(PsiFile file){ 57 | CurrentFileTodosTreeStructure treeStructure=(CurrentFileTodosTreeStructure)getTodoTreeStructure(); 58 | treeStructure.setFile(file); 59 | rebuildCache(); 60 | updateTree(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/CurrentFileTodosTreeStructure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2009 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import ai.deepcode.jbplugin.ui.nodes.SingleFileToDoNode; 20 | import ai.deepcode.jbplugin.ui.nodes.ToDoRootNode; 21 | import com.intellij.ide.util.treeView.AbstractTreeNode; 22 | import com.intellij.ide.util.treeView.NodeDescriptor; 23 | import com.intellij.openapi.diagnostic.Logger; 24 | import com.intellij.openapi.project.Project; 25 | import com.intellij.openapi.vfs.VirtualFile; 26 | import com.intellij.psi.PsiFile; 27 | import com.intellij.psi.PsiManager; 28 | 29 | public final class CurrentFileTodosTreeStructure extends TodoTreeStructure{ 30 | private static final Logger LOG = Logger.getInstance(CurrentFileTodosTreeStructure.class); 31 | private static final Object[] ourEmptyArray=new Object[]{}; 32 | 33 | /** 34 | * Current {@code VirtualFile} for which the structure is built. If {@code myFile} is {@code null} 35 | * then the structure is empty (contains only root node). 36 | */ 37 | private PsiFile myFile; 38 | 39 | public CurrentFileTodosTreeStructure(Project project){ 40 | super(project); 41 | } 42 | 43 | @Override 44 | protected void validateCache(){ 45 | super.validateCache(); 46 | if(myFile!=null && !myFile.isValid()){ 47 | VirtualFile vFile=myFile.getVirtualFile(); 48 | if(vFile.isValid()){ 49 | myFile=PsiManager.getInstance(myProject).findFile(vFile); 50 | }else{ 51 | myFile=null; 52 | } 53 | } 54 | } 55 | 56 | PsiFile getFile(){ 57 | return myFile; 58 | } 59 | 60 | /** 61 | * Sets {@code file} for which the structure is built. Alter this method is invoked caches should 62 | * be validated. 63 | */ 64 | public void setFile(PsiFile file){ 65 | myFile=file; 66 | myRootElement = createRootElement(); 67 | } 68 | 69 | @Override 70 | public boolean accept(PsiFile psiFile){ 71 | if(myFile==null||!myFile.equals(psiFile)||!myFile.isValid()){ 72 | return false; 73 | } 74 | return (myTodoFilter!=null&&myTodoFilter.accept(mySearchHelper,psiFile))|| 75 | (myTodoFilter==null&&mySearchHelper.getTodoItemsCount(psiFile)>0); 76 | } 77 | 78 | @Override 79 | boolean isAutoExpandNode(NodeDescriptor descriptor){ 80 | Object element=descriptor.getElement(); 81 | if (element instanceof AbstractTreeNode) { 82 | element = ((AbstractTreeNode)element).getValue(); 83 | } 84 | if(element==myFile){ 85 | return true; 86 | }else{ 87 | return element == getRootElement() || element == mySummaryElement; 88 | } 89 | } 90 | 91 | @Override 92 | Object getFirstSelectableElement(){ 93 | if (myRootElement instanceof SingleFileToDoNode){ 94 | return ((SingleFileToDoNode)myRootElement).getFileNode(); 95 | } else { 96 | return null; 97 | } 98 | } 99 | 100 | @Override 101 | public boolean getIsPackagesShown() { 102 | return myArePackagesShown; 103 | } 104 | 105 | @Override 106 | protected AbstractTreeNode createRootElement() { 107 | if (!accept(myFile)) { 108 | return new ToDoRootNode(myProject, new Object(), myBuilder, mySummaryElement); 109 | } else { 110 | return new SingleFileToDoNode(myProject, myFile, myBuilder); 111 | } 112 | 113 | } 114 | } -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/CustomChangelistTodoTreeStructure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2011 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ai.deepcode.jbplugin.ui; 17 | 18 | import ai.deepcode.jbplugin.ui.nodes.ToDoRootNode; 19 | import com.intellij.ide.util.treeView.AbstractTreeNode; 20 | import com.intellij.openapi.project.Project; 21 | import com.intellij.psi.PsiFile; 22 | import com.intellij.psi.search.PsiTodoSearchHelper; 23 | 24 | /** 25 | * @author irengrig 26 | */ 27 | public class CustomChangelistTodoTreeStructure extends TodoTreeStructure { 28 | private final PsiTodoSearchHelper mySearchHelper; 29 | 30 | public CustomChangelistTodoTreeStructure(Project project, PsiTodoSearchHelper searchHelper) { 31 | super(project); 32 | mySearchHelper = searchHelper; 33 | } 34 | 35 | @Override 36 | public boolean accept(final PsiFile psiFile) { 37 | if (! psiFile.isValid()) return false; 38 | return mySearchHelper.getTodoItemsCount(psiFile) > 0; 39 | } 40 | 41 | @Override 42 | public boolean getIsPackagesShown() { 43 | return myArePackagesShown; 44 | } 45 | 46 | @Override 47 | Object getFirstSelectableElement() { 48 | return ((ToDoRootNode)myRootElement).getSummaryNode(); 49 | } 50 | 51 | @Override 52 | protected AbstractTreeNode createRootElement() { 53 | return new ToDoRootNode(myProject, new Object(), myBuilder, mySummaryElement); 54 | } 55 | 56 | @Override 57 | public PsiTodoSearchHelper getSearchHelper() { 58 | return mySearchHelper; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/DeepCodeDirAndModuleComparator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 2 | // license that can be found in the LICENSE file. 3 | 4 | package ai.deepcode.jbplugin.ui; 5 | 6 | import ai.deepcode.jbplugin.ui.nodes.TodoFileNode; 7 | import ai.deepcode.jbplugin.core.DeepCodeUtils; 8 | import com.intellij.ide.projectView.ProjectViewNode; 9 | import ai.deepcode.jbplugin.ui.nodes.ModuleToDoNode; 10 | import com.intellij.ide.util.treeView.NodeDescriptor; 11 | import com.intellij.psi.PsiFile; 12 | 13 | import java.util.Collections; 14 | import java.util.Comparator; 15 | 16 | public final class DeepCodeDirAndModuleComparator implements Comparator { 17 | public static final DeepCodeDirAndModuleComparator INSTANCE = 18 | new DeepCodeDirAndModuleComparator(); 19 | 20 | private DeepCodeDirAndModuleComparator() {} 21 | 22 | @Override 23 | public int compare(NodeDescriptor obj1, NodeDescriptor obj2) { 24 | 25 | final int weight1 = obj1.getWeight(); 26 | final int weight2 = obj2.getWeight(); 27 | if (weight1 != weight2) return weight1 - weight2; 28 | 29 | if (obj1 instanceof TodoFileNode && obj2 instanceof TodoFileNode) { 30 | PsiFile file1 = ((TodoFileNode) obj1).getValue(); 31 | PsiFile file2 = ((TodoFileNode) obj2).getValue(); 32 | DeepCodeUtils.ErrorsWarningsInfos ewi1 = DeepCodeUtils.getInstance().getEWI(Collections.singleton(file1)); 33 | DeepCodeUtils.ErrorsWarningsInfos ewi2 = DeepCodeUtils.getInstance().getEWI(Collections.singleton(file2)); 34 | return (ewi1.getErrors() != ewi2.getErrors()) 35 | ? ewi2.getErrors() - ewi1.getErrors() 36 | : (ewi1.getWarnings() != ewi2.getWarnings()) 37 | ? ewi2.getWarnings() - ewi1.getWarnings() 38 | : ewi2.getInfos() - ewi1.getInfos(); 39 | 40 | } else if (obj1 instanceof ProjectViewNode && obj2 instanceof ProjectViewNode) { 41 | return ((ProjectViewNode) obj1) 42 | .getTitle() 43 | .compareToIgnoreCase(((ProjectViewNode) obj2).getTitle()); 44 | } 45 | if (obj1 instanceof ModuleToDoNode && obj2 instanceof ModuleToDoNode) { 46 | return ((ModuleToDoNode) obj1) 47 | .getValue() 48 | .getName() 49 | .compareToIgnoreCase(((ModuleToDoNode) obj2).getValue().getName()); 50 | } else if (obj1 instanceof ModuleToDoNode) { 51 | return -1; 52 | } else if (obj2 instanceof ModuleToDoNode) { 53 | return 1; 54 | } else { 55 | throw new IllegalArgumentException( 56 | obj1.getClass().getName() + "," + obj2.getClass().getName()); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/HighlightedRegionProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2009 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import com.intellij.ui.HighlightedRegion; 20 | 21 | /** 22 | * @author Vladimir Kondratyev 23 | */ 24 | public interface HighlightedRegionProvider{ 25 | Iterable getHighlightedRegions(); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/MultiLineTodoLocalityDetector.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package ai.deepcode.jbplugin.ui; 3 | 4 | import com.intellij.codeInsight.daemon.ChangeLocalityDetector; 5 | import com.intellij.ide.todo.TodoConfiguration; 6 | import com.intellij.openapi.util.text.StringUtil; 7 | import com.intellij.psi.PsiComment; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.psi.PsiWhiteSpace; 10 | import com.intellij.psi.util.PsiTreeUtil; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | public class MultiLineTodoLocalityDetector implements ChangeLocalityDetector { 15 | @Nullable 16 | @Override 17 | public PsiElement getChangeHighlightingDirtyScopeFor(@NotNull PsiElement changedElement) { 18 | if (! TodoConfiguration.getInstance().isMultiLine()) return null; 19 | if (changedElement instanceof PsiWhiteSpace) { 20 | PsiElement prevLeaf = PsiTreeUtil.prevLeaf(changedElement); 21 | return prevLeaf instanceof PsiComment ? PsiTreeUtil.findCommonParent(changedElement, prevLeaf) : null; 22 | } 23 | else if (changedElement instanceof PsiComment) { 24 | PsiComment commentAbove = findAdjacentComment(changedElement, true); 25 | PsiComment commentBelow = findAdjacentComment(changedElement, false); 26 | if (commentAbove == null && commentBelow == null) return null; 27 | return PsiTreeUtil.findCommonParent(changedElement, commentAbove, commentBelow); 28 | } 29 | else { 30 | return null; 31 | } 32 | } 33 | 34 | private static PsiComment findAdjacentComment(PsiElement element, boolean above) { 35 | PsiElement currentElement = element; 36 | PsiComment lastComment = null; 37 | boolean nextLine = false; 38 | while (true) { 39 | currentElement = above ? PsiTreeUtil.prevLeaf(currentElement) : PsiTreeUtil.nextLeaf(currentElement); 40 | if (currentElement == null) break; 41 | String elementText = currentElement.getText(); 42 | if (elementText == null) break; 43 | int newLines = StringUtil.countNewLines(elementText); 44 | if (newLines == 0 && !nextLine) continue; 45 | if (currentElement instanceof PsiComment) lastComment = (PsiComment)currentElement; 46 | if (newLines > (nextLine ? 0 : 1)) break; 47 | nextLine = true; 48 | } 49 | return lastComment; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/MultiLineTodoRenderer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package ai.deepcode.jbplugin.ui; 3 | 4 | import com.intellij.ide.IdeBundle; 5 | import ai.deepcode.jbplugin.ui.nodes.TodoItemNode; 6 | import com.intellij.ui.HighlightableCellRenderer; 7 | import com.intellij.ui.HighlightedRegion; 8 | 9 | import javax.swing.*; 10 | import javax.swing.tree.DefaultMutableTreeNode; 11 | import javax.swing.tree.TreeCellRenderer; 12 | import java.awt.*; 13 | import java.util.List; 14 | 15 | public class MultiLineTodoRenderer extends JPanel implements TreeCellRenderer { 16 | private static final int MAX_DISPLAYED_LINES = 10; 17 | 18 | private final HighlightableCellRenderer myPrefixRenderer; 19 | private final HighlightableCellRenderer[] myLineRenderers = new HighlightableCellRenderer[MAX_DISPLAYED_LINES]; 20 | private final JLabel myMoreLabel; 21 | 22 | public MultiLineTodoRenderer() { 23 | super(new GridBagLayout()); 24 | GridBagConstraints c = new GridBagConstraints(); 25 | add(myPrefixRenderer = new HighlightableCellRenderer(), c); 26 | c.gridx = 1; 27 | c.weightx = 1; 28 | c.fill = GridBagConstraints.HORIZONTAL; 29 | c.anchor = GridBagConstraints.WEST; 30 | for (int i = 0; i < MAX_DISPLAYED_LINES; i++) { 31 | c.gridy = i; 32 | add(myLineRenderers[i] = new HighlightableCellRenderer(), c); 33 | } 34 | c.gridy++; 35 | add(myMoreLabel = new JLabel(IdeBundle.message("node.todo.more.items")), c); 36 | } 37 | 38 | @Override 39 | public Component getTreeCellRendererComponent(JTree tree, 40 | Object value, 41 | boolean selected, 42 | boolean expanded, 43 | boolean leaf, 44 | int row, 45 | boolean hasFocus) { 46 | TodoItemNode node = (TodoItemNode)((DefaultMutableTreeNode)value).getUserObject(); 47 | String text = value.toString(); 48 | int parenPos = text.indexOf(')'); 49 | int contentStartPos = (parenPos >= 0 && parenPos < (text.length() - 1)) ? parenPos + 2 : 0; 50 | myPrefixRenderer.getTreeCellRendererComponent(tree, text.substring(0, contentStartPos), selected, expanded, leaf, row, hasFocus); 51 | myPrefixRenderer.setIcon(node.getIcon()); 52 | 53 | List additionalLines = node.getAdditionalLines(); 54 | for (int i = 0; i < MAX_DISPLAYED_LINES; i++) { 55 | if (i > additionalLines.size()) { 56 | myLineRenderers[i].setVisible(false); 57 | } 58 | else { 59 | myLineRenderers[i].setVisible(true); 60 | myLineRenderers[i].getTreeCellRendererComponent(tree, i == 0 ? text.substring(contentStartPos) : additionalLines.get(i - 1), 61 | selected, expanded, leaf, row, hasFocus); 62 | HighlightedRegionProvider provider = i == 0 ? node : additionalLines.get(i - 1); 63 | for (HighlightedRegion region : provider.getHighlightedRegions()) { 64 | myLineRenderers[i].addHighlighter(region.startOffset - (i == 0 ? contentStartPos : 0), 65 | region.endOffset - (i == 0 ? contentStartPos : 0), 66 | region.textAttributes); 67 | } 68 | } 69 | } 70 | myMoreLabel.setVisible(additionalLines.size() >= MAX_DISPLAYED_LINES); 71 | return this; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/ScopeBasedTodosPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2016 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import com.intellij.ide.util.PropertiesComponent; 20 | import com.intellij.ide.util.scopeChooser.ScopeChooserCombo; 21 | import com.intellij.openapi.project.Project; 22 | import com.intellij.openapi.util.Disposer; 23 | import com.intellij.ui.content.Content; 24 | import com.intellij.util.Alarm; 25 | import com.intellij.util.ui.JBUI; 26 | 27 | import javax.swing.*; 28 | import java.awt.*; 29 | import java.awt.event.ActionEvent; 30 | import java.awt.event.ActionListener; 31 | 32 | public class ScopeBasedTodosPanel extends TodoPanel { 33 | private static final String SELECTED_SCOPE = "TODO_SCOPE"; 34 | private final Alarm myAlarm; 35 | private ScopeChooserCombo myScopes; 36 | 37 | public ScopeBasedTodosPanel(final Project project, TodoPanelSettings settings, Content content){ 38 | super(project,settings,false,content); 39 | myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this); 40 | myScopes.getChildComponent().addActionListener(new ActionListener() { 41 | @Override 42 | public void actionPerformed(ActionEvent e) { 43 | rebuildWithAlarm(ScopeBasedTodosPanel.this.myAlarm); 44 | PropertiesComponent.getInstance(myProject).setValue(SELECTED_SCOPE, myScopes.getSelectedScopeName(), null); 45 | } 46 | }); 47 | rebuildWithAlarm(myAlarm); 48 | } 49 | 50 | @Override 51 | protected JComponent createCenterComponent() { 52 | JPanel panel = new JPanel(new BorderLayout()); 53 | final JComponent component = super.createCenterComponent(); 54 | panel.add(component, BorderLayout.CENTER); 55 | String preselect = PropertiesComponent.getInstance(myProject).getValue(SELECTED_SCOPE); 56 | myScopes = new ScopeChooserCombo(myProject, false, true, preselect); 57 | Disposer.register(this, myScopes); 58 | 59 | myScopes.setCurrentSelection(false); 60 | myScopes.setUsageView(false); 61 | 62 | JPanel chooserPanel = new JPanel(new GridBagLayout()); 63 | final JLabel scopesLabel = new JLabel("Scope:"); 64 | scopesLabel.setDisplayedMnemonic('S'); 65 | scopesLabel.setLabelFor(myScopes); 66 | final GridBagConstraints gc = 67 | new GridBagConstraints(GridBagConstraints.RELATIVE, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, 68 | JBUI.insets(2, 8, 2, 4), 0, 0); 69 | chooserPanel.add(scopesLabel, gc); 70 | gc.insets = JBUI.insets(2); 71 | chooserPanel.add(myScopes, gc); 72 | 73 | gc.fill = GridBagConstraints.HORIZONTAL; 74 | gc.weightx = 1; 75 | chooserPanel.add(Box.createHorizontalBox(), gc); 76 | panel.add(chooserPanel, BorderLayout.NORTH); 77 | return panel; 78 | } 79 | 80 | @Override 81 | protected TodoTreeBuilder createTreeBuilder(JTree tree, Project project) { 82 | ScopeBasedTodosTreeBuilder builder = new ScopeBasedTodosTreeBuilder(tree, project, myScopes); 83 | builder.init(); 84 | return builder; 85 | } 86 | } -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/ScopeBasedTodosTreeBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2016 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import com.intellij.ide.util.scopeChooser.ScopeChooserCombo; 20 | import com.intellij.openapi.project.Project; 21 | import org.jetbrains.annotations.NotNull; 22 | 23 | import javax.swing.*; 24 | 25 | public class ScopeBasedTodosTreeBuilder extends TodoTreeBuilder{ 26 | private final ScopeChooserCombo myScopes; 27 | 28 | public ScopeBasedTodosTreeBuilder(JTree tree, Project project, ScopeChooserCombo scopes){ 29 | super(tree, project); 30 | myScopes = scopes; 31 | } 32 | 33 | @Override 34 | @NotNull 35 | protected TodoTreeStructure createTreeStructure(){ 36 | return new ScopeBasedTodosTreeStructure(myProject, myScopes); 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/ScopeBasedTodosTreeStructure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2012 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import ai.deepcode.jbplugin.ui.nodes.ToDoRootNode; 20 | import com.intellij.ide.util.scopeChooser.ScopeChooserCombo; 21 | import com.intellij.ide.util.treeView.AbstractTreeNode; 22 | import com.intellij.openapi.project.Project; 23 | import com.intellij.openapi.vfs.VirtualFile; 24 | import com.intellij.psi.PsiFile; 25 | import com.intellij.psi.search.SearchScope; 26 | 27 | public class ScopeBasedTodosTreeStructure extends TodoTreeStructure { 28 | private final ScopeChooserCombo myScopes; 29 | 30 | public ScopeBasedTodosTreeStructure(Project project, ScopeChooserCombo scopes) { 31 | super(project); 32 | myScopes = scopes; 33 | } 34 | 35 | @Override 36 | public boolean accept(final PsiFile psiFile) { 37 | if (!psiFile.isValid()) return false; 38 | 39 | SearchScope scope = myScopes.getSelectedScope(); 40 | VirtualFile file = psiFile.getVirtualFile(); 41 | boolean isAffected = scope != null && file != null && scope.contains(file); 42 | return isAffected && (myTodoFilter != null && myTodoFilter.accept(mySearchHelper, psiFile) || 43 | (myTodoFilter == null && mySearchHelper.getTodoItemsCount(psiFile) > 0)); 44 | } 45 | 46 | @Override 47 | public boolean getIsPackagesShown() { 48 | return myArePackagesShown; 49 | } 50 | 51 | @Override 52 | Object getFirstSelectableElement() { 53 | return ((ToDoRootNode)myRootElement).getSummaryNode(); 54 | } 55 | 56 | @Override 57 | protected AbstractTreeNode createRootElement() { 58 | return new ToDoRootNode(myProject, new Object(), myBuilder, mySummaryElement); 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/SetTodoFilterAction.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package ai.deepcode.jbplugin.ui; 3 | 4 | import com.intellij.ConfigurableFactory; 5 | import com.intellij.icons.AllIcons; 6 | import com.intellij.ide.IdeBundle; 7 | import com.intellij.ide.todo.TodoConfiguration; 8 | import com.intellij.ide.todo.TodoFilter; 9 | import com.intellij.openapi.actionSystem.*; 10 | import com.intellij.openapi.actionSystem.ex.CustomComponentAction; 11 | import com.intellij.openapi.actionSystem.impl.ActionButton; 12 | import com.intellij.openapi.options.ShowSettingsUtil; 13 | import com.intellij.openapi.project.Project; 14 | import com.intellij.openapi.util.Comparing; 15 | import com.intellij.util.Consumer; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | import javax.swing.*; 19 | 20 | /** 21 | * @author irengrig 22 | * moved from inner class 23 | */ 24 | public class SetTodoFilterAction extends AnAction implements CustomComponentAction { 25 | private final Project myProject; 26 | private final TodoPanelSettings myToDoSettings; 27 | private final Consumer myTodoFilterConsumer; 28 | 29 | public SetTodoFilterAction(final Project project, final TodoPanelSettings toDoSettings, final Consumer todoFilterConsumer) { 30 | super(IdeBundle.message("action.filter.todo.items"), null, AllIcons.General.Filter); 31 | myProject = project; 32 | myToDoSettings = toDoSettings; 33 | myTodoFilterConsumer = todoFilterConsumer; 34 | } 35 | 36 | @Override 37 | public void actionPerformed(@NotNull AnActionEvent e) { 38 | Presentation presentation = e.getPresentation(); 39 | JComponent button = presentation.getClientProperty(COMPONENT_KEY); 40 | DefaultActionGroup group = createPopupActionGroup(myProject, myToDoSettings, myTodoFilterConsumer); 41 | ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.TODO_VIEW_TOOLBAR, 42 | group); 43 | popupMenu.getComponent().show(button, button.getWidth(), 0); 44 | } 45 | 46 | @NotNull 47 | @Override 48 | public JComponent createCustomComponent(@NotNull Presentation presentation, @NotNull String place) { 49 | return new ActionButton(this, presentation, place, ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE); 50 | } 51 | 52 | public static DefaultActionGroup createPopupActionGroup(final Project project, 53 | final TodoPanelSettings settings, 54 | Consumer todoFilterConsumer) { 55 | TodoFilter[] filters = TodoConfiguration.getInstance().getTodoFilters(); 56 | DefaultActionGroup group = new DefaultActionGroup(); 57 | group.add(new TodoFilterApplier(IdeBundle.message("action.todo.show.all"), 58 | IdeBundle.message("action.description.todo.show.all"), null, settings, todoFilterConsumer)); 59 | for (TodoFilter filter : filters) { 60 | group.add(new TodoFilterApplier(filter.getName(), null, filter, settings, todoFilterConsumer)); 61 | } 62 | group.addSeparator(); 63 | group.add( 64 | new AnAction(IdeBundle.message("action.todo.edit.filters"), 65 | IdeBundle.message("action.todo.edit.filters"), AllIcons.General.Settings) { 66 | @Override 67 | public void actionPerformed(@NotNull AnActionEvent e) { 68 | final ShowSettingsUtil util = ShowSettingsUtil.getInstance(); 69 | util.editConfigurable(project, ConfigurableFactory.Companion.getInstance().getTodoConfigurable(project)); 70 | } 71 | } 72 | ); 73 | return group; 74 | } 75 | 76 | private static class TodoFilterApplier extends ToggleAction { 77 | private final TodoFilter myFilter; 78 | private final TodoPanelSettings mySettings; 79 | private final Consumer myTodoFilterConsumer; 80 | 81 | /** 82 | * @param text action's text. 83 | * @param description action's description. 84 | * @param filter filter to be applied. {@code null} value means "empty" filter. 85 | * @param settings 86 | * @param todoFilterConsumer 87 | */ 88 | TodoFilterApplier(String text, 89 | String description, 90 | TodoFilter filter, 91 | TodoPanelSettings settings, 92 | Consumer todoFilterConsumer) { 93 | super(null, description, null); 94 | mySettings = settings; 95 | myTodoFilterConsumer = todoFilterConsumer; 96 | getTemplatePresentation().setText(text, false); 97 | myFilter = filter; 98 | } 99 | 100 | @Override 101 | public void update(@NotNull AnActionEvent e) { 102 | super.update(e); 103 | if (myFilter != null) { 104 | e.getPresentation().setEnabled(!myFilter.isEmpty()); 105 | } 106 | } 107 | 108 | @Override 109 | public boolean isSelected(@NotNull AnActionEvent e) { 110 | return Comparing.equal(myFilter != null ? myFilter.getName() : null, mySettings.todoFilterName); 111 | } 112 | 113 | @Override 114 | public void setSelected(@NotNull AnActionEvent e, boolean state) { 115 | if (state) { 116 | myTodoFilterConsumer.consume(myFilter); 117 | //setTodoFilter(myFilter); 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/SmartTodoItemPointer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 2 | // license that can be found in the LICENSE file. 3 | 4 | package ai.deepcode.jbplugin.ui; 5 | 6 | import com.intellij.openapi.editor.Document; 7 | import com.intellij.openapi.editor.RangeMarker; 8 | import com.intellij.openapi.util.TextRange; 9 | import com.intellij.psi.search.TodoItem; 10 | import com.intellij.util.containers.ContainerUtil; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.List; 14 | 15 | /** @author Vladimir Kondratyev */ 16 | public final class SmartTodoItemPointer { 17 | private final TodoItem myTodoItem; 18 | private final Document myDocument; 19 | private final RangeMarker myRangeMarker; 20 | private final List myAdditionalRangeMarkers; 21 | 22 | public SmartTodoItemPointer(@NotNull TodoItem todoItem, @NotNull Document document) { 23 | myTodoItem = todoItem; 24 | myDocument = document; 25 | TextRange textRange = myTodoItem.getTextRange(); 26 | myRangeMarker = document.createRangeMarker(textRange); 27 | myAdditionalRangeMarkers = 28 | ContainerUtil.map(todoItem.getAdditionalTextRanges(), document::createRangeMarker); 29 | } 30 | 31 | public TodoItem getTodoItem() { 32 | return myTodoItem; 33 | } 34 | 35 | public Document getDocument() { 36 | return myDocument; 37 | } 38 | 39 | public RangeMarker getRangeMarker() { 40 | return myRangeMarker; 41 | } 42 | 43 | @NotNull 44 | public List getAdditionalRangeMarkers() { 45 | return myAdditionalRangeMarkers; 46 | } 47 | 48 | public boolean equals(Object obj) { 49 | if (!(obj instanceof SmartTodoItemPointer)) { 50 | return false; 51 | } 52 | SmartTodoItemPointer pointer = (SmartTodoItemPointer) obj; 53 | if (!(myTodoItem.getFile().equals(pointer.myTodoItem.getFile()) 54 | && myRangeMarker.getStartOffset() == pointer.myRangeMarker.getStartOffset() 55 | && myRangeMarker.getEndOffset() == pointer.myRangeMarker.getEndOffset() 56 | && myTodoItem.getPattern().equals(pointer.myTodoItem.getPattern()) 57 | && myAdditionalRangeMarkers.size() == pointer.myAdditionalRangeMarkers.size())) { 58 | return false; 59 | } 60 | for (int i = 0; i < myAdditionalRangeMarkers.size(); i++) { 61 | RangeMarker m1 = myAdditionalRangeMarkers.get(i); 62 | RangeMarker m2 = pointer.myAdditionalRangeMarkers.get(i); 63 | if (m1.getStartOffset() != m2.getStartOffset() || m1.getEndOffset() != m2.getEndOffset()) { 64 | return false; 65 | } 66 | } 67 | return true; 68 | } 69 | 70 | public int hashCode() { 71 | return myTodoItem.getFile().hashCode(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/SmartTodoItemPointerComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2009 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import ai.deepcode.jbplugin.ui.nodes.TodoItemNode; 20 | import com.intellij.openapi.util.TextRange; 21 | 22 | import java.util.Comparator; 23 | 24 | /** 25 | * @author Vladimir Kondratyev 26 | */ 27 | public final class SmartTodoItemPointerComparator implements Comparator { 28 | public static final SmartTodoItemPointerComparator ourInstance=new SmartTodoItemPointerComparator(); 29 | 30 | private SmartTodoItemPointerComparator(){} 31 | 32 | @Override 33 | public int compare(TodoItemNode obj1,TodoItemNode obj2){ 34 | TextRange range1= obj1.getValue().getTodoItem().getTextRange(); 35 | TextRange range2= obj2.getValue().getTodoItem().getTextRange(); 36 | return range1.getStartOffset()-range2.getStartOffset(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/ToDoSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2009 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | public interface ToDoSettings { 20 | boolean getIsPackagesShown(); 21 | boolean isModulesShown(); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/ToDoSummary.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2009 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | /** 20 | * @author Vladimir Kondratyev 21 | */ 22 | public class ToDoSummary{ 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/TodoCompositeRenderer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package ai.deepcode.jbplugin.ui; 3 | 4 | import ai.deepcode.jbplugin.ui.nodes.SummaryNode; 5 | import ai.deepcode.jbplugin.ui.nodes.TodoItemNode; 6 | import com.intellij.ide.util.treeView.NodeDescriptor; 7 | import com.intellij.ide.util.treeView.NodeRenderer; 8 | import com.intellij.ui.HighlightableCellRenderer; 9 | import com.intellij.ui.HighlightedRegion; 10 | import com.intellij.util.ui.UIUtil; 11 | 12 | import javax.swing.*; 13 | import javax.swing.tree.DefaultMutableTreeNode; 14 | import javax.swing.tree.TreeCellRenderer; 15 | import java.awt.*; 16 | 17 | /** 18 | * todo: replace this highlightable crap with regular NodeRenderer 19 | * @author Vladimir Kondratyev 20 | */ 21 | final class TodoCompositeRenderer implements TreeCellRenderer { 22 | private final NodeRenderer myNodeRenderer; 23 | private final HighlightableCellRenderer myColorTreeCellRenderer; 24 | private final MultiLineTodoRenderer myMultiLineRenderer; 25 | 26 | TodoCompositeRenderer() { 27 | myNodeRenderer = new NodeRenderer(); 28 | myColorTreeCellRenderer = new HighlightableCellRenderer(); 29 | myMultiLineRenderer = new MultiLineTodoRenderer(); 30 | } 31 | 32 | @Override 33 | public Component getTreeCellRendererComponent(JTree tree, Object obj, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { 34 | Component result; 35 | 36 | Object userObject = ((DefaultMutableTreeNode)obj).getUserObject(); 37 | /* 38 | if (userObject instanceof SummaryNode) { 39 | myNodeRenderer.getTreeCellRendererComponent(tree, userObject.toString(), selected, expanded, leaf, row, hasFocus); 40 | myNodeRenderer.setFont(UIUtil.getTreeFont().deriveFont(Font.BOLD)); 41 | myNodeRenderer.setIcon(null); 42 | result = myNodeRenderer; 43 | */ 44 | if (userObject instanceof TodoItemNode && !((TodoItemNode)userObject).getAdditionalLines().isEmpty()) { 45 | myMultiLineRenderer.getTreeCellRendererComponent(tree, obj, selected, expanded, leaf, row, hasFocus); 46 | result = myMultiLineRenderer; 47 | } 48 | else if (userObject instanceof HighlightedRegionProvider) { 49 | HighlightedRegionProvider regionProvider = (HighlightedRegionProvider)userObject; 50 | myColorTreeCellRenderer.getTreeCellRendererComponent(tree, obj, selected, expanded, leaf, row, hasFocus); 51 | for (HighlightedRegion region : regionProvider.getHighlightedRegions()) { 52 | myColorTreeCellRenderer.addHighlighter(region.startOffset, region.endOffset, region.textAttributes); 53 | } 54 | if (userObject instanceof SummaryNode) { 55 | myColorTreeCellRenderer.setFont(UIUtil.getTreeFont().deriveFont(Font.BOLD)); 56 | myColorTreeCellRenderer.setIcon(null); 57 | } else if (userObject instanceof NodeDescriptor) { 58 | NodeDescriptor descriptor = (NodeDescriptor)userObject; 59 | myColorTreeCellRenderer.setIcon(descriptor.getIcon()); 60 | } 61 | result = myColorTreeCellRenderer; 62 | } 63 | else { 64 | result = myNodeRenderer.getTreeCellRendererComponent(tree, obj, selected, expanded, leaf, row, hasFocus); 65 | } 66 | 67 | if (result instanceof JComponent) { 68 | ((JComponent)result).setOpaque(!selected); 69 | } 70 | 71 | return result; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/TodoNodeVisitor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package ai.deepcode.jbplugin.ui; 3 | 4 | import com.intellij.ide.projectView.ProjectViewNode; 5 | import ai.deepcode.jbplugin.ui.nodes.BaseToDoNode; 6 | import ai.deepcode.jbplugin.ui.nodes.SummaryNode; 7 | import ai.deepcode.jbplugin.ui.nodes.ToDoRootNode; 8 | import ai.deepcode.jbplugin.ui.nodes.TodoTreeHelper; 9 | import com.intellij.ide.util.treeView.AbstractTreeNode; 10 | import com.intellij.openapi.vfs.VirtualFile; 11 | import com.intellij.ui.tree.AbstractTreeNodeVisitor; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.function.Supplier; 15 | 16 | 17 | class TodoNodeVisitor extends AbstractTreeNodeVisitor { 18 | private final VirtualFile myFile; 19 | 20 | TodoNodeVisitor(@NotNull Supplier supplier, VirtualFile file) { 21 | super(supplier, null); 22 | myFile = file; 23 | } 24 | 25 | @Override 26 | protected boolean contains(@NotNull AbstractTreeNode node, @NotNull Object element) { 27 | if (node instanceof SummaryNode || node instanceof ToDoRootNode) return true; 28 | if (node instanceof ProjectViewNode) { 29 | if (myFile == null) { 30 | return TodoTreeHelper.getInstance(node.getProject()).contains((ProjectViewNode)node, element); 31 | } 32 | } 33 | return node instanceof BaseToDoNode && ((BaseToDoNode)node).contains(element) || 34 | node instanceof ProjectViewNode && ((ProjectViewNode)node).contains(myFile); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/TodoPanelSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 3 | */ 4 | package ai.deepcode.jbplugin.ui; 5 | 6 | import com.intellij.util.xmlb.annotations.OptionTag; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class TodoPanelSettings { 10 | @OptionTag(tag = "are-packages-shown", nameAttribute = "") 11 | public boolean arePackagesShown; 12 | @OptionTag(tag = "are-modules-shown", nameAttribute = "") 13 | public boolean areModulesShown; 14 | @OptionTag(tag = "flatten-packages", nameAttribute = "") 15 | public boolean areFlattenPackages; 16 | @OptionTag(tag = "is-autoscroll-to-source", nameAttribute = "") 17 | public boolean isAutoScrollToSource; 18 | @OptionTag(tag = "todo-filter", nameAttribute = "", valueAttribute = "name") 19 | public String todoFilterName; 20 | @OptionTag(tag = "is-preview-enabled", nameAttribute = "") 21 | public boolean showPreview; 22 | 23 | public TodoPanelSettings() { 24 | } 25 | 26 | public TodoPanelSettings(@NotNull TodoPanelSettings s) { 27 | arePackagesShown = s.arePackagesShown; 28 | areModulesShown = s.areModulesShown; 29 | areFlattenPackages = s.areFlattenPackages; 30 | isAutoScrollToSource = s.isAutoScrollToSource; 31 | todoFilterName = s.todoFilterName; 32 | showPreview = s.showPreview; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/TodoTreeBuilderFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2011 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ai.deepcode.jbplugin.ui; 17 | 18 | import com.intellij.openapi.project.Project; 19 | 20 | import javax.swing.*; 21 | 22 | /** 23 | * @author irengrig 24 | */ 25 | public interface TodoTreeBuilderFactory { 26 | TodoTreeBuilder createTreeBuilder(JTree tree,Project project); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/TodoTreeStructure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2009 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui; 18 | 19 | import ai.deepcode.jbplugin.core.AnalysisData; 20 | import com.intellij.ide.projectView.TreeStructureProvider; 21 | import com.intellij.ide.todo.TodoFilter; 22 | import com.intellij.ide.util.treeView.AbstractTreeNode; 23 | import com.intellij.ide.util.treeView.AbstractTreeStructureBase; 24 | import com.intellij.ide.util.treeView.NodeDescriptor; 25 | import com.intellij.openapi.project.Project; 26 | import com.intellij.openapi.util.ActionCallback; 27 | import com.intellij.psi.PsiDocumentManager; 28 | import com.intellij.psi.PsiFile; 29 | import com.intellij.psi.search.PsiTodoSearchHelper; 30 | import org.jetbrains.annotations.NotNull; 31 | 32 | import java.util.Collections; 33 | import java.util.List; 34 | 35 | /** 36 | * @author Vladimir Kondratyev 37 | */ 38 | public abstract class TodoTreeStructure extends AbstractTreeStructureBase implements ToDoSettings{ 39 | protected TodoTreeBuilder myBuilder; 40 | protected AbstractTreeNode myRootElement; 41 | protected final ToDoSummary mySummaryElement; 42 | 43 | private boolean myFlattenPackages; 44 | protected boolean myArePackagesShown; 45 | private boolean myAreModulesShown; 46 | 47 | 48 | protected final PsiTodoSearchHelper mySearchHelper; 49 | /** 50 | * Current {@code TodoFilter}. If no filter is set then this field is {@code null}. 51 | */ 52 | protected TodoFilter myTodoFilter; 53 | 54 | public TodoTreeStructure(Project project){ 55 | super(project); 56 | myArePackagesShown=true; 57 | mySummaryElement=new ToDoSummary(); 58 | mySearchHelper= PsiTodoSearchHelper.SERVICE.getInstance(project); 59 | } 60 | 61 | final void setTreeBuilder(TodoTreeBuilder builder){ 62 | myBuilder=builder; 63 | myRootElement=createRootElement(); 64 | } 65 | 66 | protected abstract AbstractTreeNode createRootElement(); 67 | 68 | public abstract boolean accept(PsiFile psiFile); 69 | 70 | /** 71 | * Validate whole the cache 72 | */ 73 | protected void validateCache(){ 74 | } 75 | 76 | public final boolean isPackagesShown(){ 77 | return myArePackagesShown; 78 | } 79 | 80 | final void setShownPackages(boolean state){ 81 | myArePackagesShown=state; 82 | } 83 | 84 | public final boolean areFlattenPackages(){ 85 | return myFlattenPackages; 86 | } 87 | 88 | public final void setFlattenPackages(boolean state){ 89 | myFlattenPackages=state; 90 | } 91 | 92 | /** 93 | * Sets new {@code TodoFilter}. {@code null} is acceptable value. It means 94 | * that there is no any filtration of TodoItem>/code>s. 95 | */ 96 | final void setTodoFilter(TodoFilter todoFilter){ 97 | myTodoFilter=todoFilter; 98 | } 99 | 100 | /** 101 | * @return first element that can be selected in the tree. The method can returns {@code null}. 102 | */ 103 | abstract Object getFirstSelectableElement(); 104 | 105 | /** 106 | * @return number of {@code TodoItem}s located in the file. 107 | */ 108 | public final int getTodoItemCount(PsiFile psiFile){ 109 | int count=0; 110 | if(psiFile != null){ 111 | count = (int) AnalysisData.getInstance().getAnalysis(psiFile).stream().flatMap(s -> s.getRanges().stream()).count(); 112 | /* 113 | if(myTodoFilter!=null){ 114 | for(Iterator i=myTodoFilter.iterator();i.hasNext();){ 115 | TodoPattern pattern=(TodoPattern)i.next(); 116 | count+=getSearchHelper().getTodoItemsCount(psiFile,pattern); 117 | } 118 | }else{ 119 | count=getSearchHelper().getTodoItemsCount(psiFile); 120 | } 121 | */ 122 | } 123 | return count; 124 | } 125 | 126 | boolean isAutoExpandNode(NodeDescriptor descriptor){ 127 | Object element=descriptor.getElement(); 128 | if (element instanceof AbstractTreeNode) { 129 | element = ((AbstractTreeNode)element).getValue(); 130 | } 131 | return element == getRootElement() || element == mySummaryElement && (myAreModulesShown || myArePackagesShown); 132 | } 133 | 134 | @Override 135 | public final void commit() { 136 | PsiDocumentManager.getInstance(myProject).commitAllDocuments(); 137 | } 138 | 139 | @Override 140 | public boolean hasSomethingToCommit() { 141 | return PsiDocumentManager.getInstance(myProject).hasUncommitedDocuments(); 142 | } 143 | 144 | @NotNull 145 | @Override 146 | public ActionCallback asyncCommit() { 147 | return asyncCommitDocuments(myProject); 148 | } 149 | 150 | @NotNull 151 | @Override 152 | public final Object getRootElement(){ 153 | return myRootElement; 154 | } 155 | 156 | public boolean getIsFlattenPackages() { 157 | return myFlattenPackages; 158 | } 159 | 160 | public PsiTodoSearchHelper getSearchHelper() { 161 | return mySearchHelper; 162 | } 163 | 164 | public TodoFilter getTodoFilter() { 165 | return myTodoFilter; 166 | } 167 | 168 | @Override 169 | public List getProviders() { 170 | return Collections.emptyList(); 171 | } 172 | 173 | void setShownModules(boolean state) { 174 | myAreModulesShown = state; 175 | } 176 | 177 | @Override 178 | public boolean isModulesShown() { 179 | return myAreModulesShown; 180 | } 181 | } -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/config/DeepCodeConfigEntry.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.ui.config; 2 | 3 | import ai.deepcode.jbplugin.DeepCodeNotifications; 4 | import ai.deepcode.jbplugin.core.*; 5 | import com.intellij.openapi.options.Configurable; 6 | import com.intellij.openapi.options.ConfigurationException; 7 | import org.jetbrains.annotations.Nls; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import javax.swing.*; 11 | 12 | public class DeepCodeConfigEntry implements Configurable { 13 | private DeepCodeConfigForm myForm; 14 | 15 | // Default values 16 | private final String defaultBaseUrl = "https://www.deepcode.ai/"; 17 | private final String defaultTokenId = ""; 18 | 19 | @Override 20 | public @Nls(capitalization = Nls.Capitalization.Title) String getDisplayName() { 21 | return "DeepCode Settings"; 22 | } 23 | 24 | @Override 25 | public @Nullable String getHelpTopic() { 26 | return "Configuration for the DeepCode plugin"; 27 | } 28 | 29 | @Override 30 | public @Nullable JComponent createComponent() { 31 | myForm = new DeepCodeConfigForm(); 32 | reset(); 33 | return myForm.getRoot(); 34 | } 35 | 36 | /** 37 | * Indicates whether the Swing form was modified or not. This method is called very often, so it 38 | * should not take a long time. 39 | * 40 | * @return {@code true} if the settings were modified, {@code false} otherwise 41 | */ 42 | @Override 43 | public boolean isModified() { 44 | return (myForm != null) 45 | && (!myForm.getBaseURL().equals(DeepCodeParams.getInstance().getApiUrl()) 46 | || !myForm.getTokenID().equals(DeepCodeParams.getInstance().getSessionToken()) 47 | || !(myForm.isPluginEnabled() == DeepCodeParams.getInstance().isEnable()) 48 | || !(myForm.isLintersEnabled() == DeepCodeParams.getInstance().useLinter()) 49 | || myForm.getMinSeverityLevel() != DeepCodeParams.getInstance().getMinSeverity() 50 | || myForm.getUpdateMode() != DeepCodeParams.getInstance().getUpdateMode()); 51 | } 52 | 53 | @Override 54 | public void disposeUIResources() { 55 | myForm = null; 56 | } 57 | 58 | /** 59 | * Stores the settings from the Swing form to the configurable component. This method is called on 60 | * EDT upon user's request. 61 | * 62 | * @throws ConfigurationException if values cannot be applied 63 | */ 64 | @Override 65 | public void apply() throws ConfigurationException { 66 | if (myForm == null) return; 67 | boolean tokenChanged, urlChanged, severityChanged, linterChanged, enableChanged = false; 68 | if (tokenChanged = 69 | !myForm.getTokenID().equals(DeepCodeParams.getInstance().getSessionToken())) { 70 | DeepCodeParams.getInstance().setSessionToken(myForm.getTokenID()); 71 | DeepCodeParams.getInstance().setLoginUrl(""); 72 | } 73 | if (urlChanged = !myForm.getBaseURL().equals(DeepCodeParams.getInstance().getApiUrl())) { 74 | DeepCodeParams.getInstance().setApiUrl(myForm.getBaseURL()); 75 | } 76 | if (severityChanged = 77 | myForm.getMinSeverityLevel() != DeepCodeParams.getInstance().getMinSeverity()) { 78 | DeepCodeParams.getInstance().setMinSeverity(myForm.getMinSeverityLevel()); 79 | } 80 | if (linterChanged = myForm.isLintersEnabled() != DeepCodeParams.getInstance().useLinter()) { 81 | DeepCodeParams.getInstance().setUseLinter(myForm.isLintersEnabled()); 82 | } 83 | if (enableChanged = myForm.isPluginEnabled() != DeepCodeParams.getInstance().isEnable()) { 84 | DeepCodeParams.getInstance().setEnable(myForm.isPluginEnabled()); 85 | } 86 | if (tokenChanged || urlChanged || severityChanged || linterChanged || enableChanged) { 87 | AnalysisData.getInstance().resetCachesAndTasks(null); 88 | DeepCodeNotifications.expireShownLoginNotifications(); 89 | if (LoginUtils.getInstance().isLogged(null, true)) { 90 | RunUtils.getInstance().asyncAnalyseProjectAndUpdatePanel(null); 91 | } 92 | } 93 | if (myForm.getUpdateMode() != DeepCodeParams.getInstance().getUpdateMode()) { 94 | DeepCodeParams.getInstance().setUpdateMode(myForm.getUpdateMode()); 95 | } 96 | } 97 | 98 | @Override 99 | public void reset() { 100 | if (myForm == null) return; 101 | myForm.setBaseURL(DeepCodeParams.getInstance().getApiUrl()); 102 | myForm.setTokenID(DeepCodeParams.getInstance().getSessionToken()); 103 | myForm.setAddLinters(DeepCodeParams.getInstance().useLinter()); 104 | myForm.setMinSeverityLevel(DeepCodeParams.getInstance().getMinSeverity()); 105 | myForm.setUpdateMode(DeepCodeParams.getInstance().getUpdateMode()); 106 | myForm.enablePlugin(DeepCodeParams.getInstance().isEnable()); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/config/DeepCodeConfigForm.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
108 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/config/DeepCodeConfigForm.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.ui.config; 2 | 3 | import ai.deepcode.jbplugin.core.DeepCodeParams; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import javax.swing.*; 7 | 8 | public class DeepCodeConfigForm { 9 | private JPanel rootPanel; 10 | private JTextField baseURL; 11 | private JTextField tokenID; 12 | private JCheckBox addLinters; 13 | private JComboBox minSeverityLevel; 14 | private JCheckBox enableDeepCodePlugin; 15 | private JComboBox updateMode; 16 | 17 | public DeepCodeConfigForm() { 18 | minSeverityLevel.setModel( 19 | new DefaultComboBoxModel( 20 | new String[] {"Infos, Warnings and Errors", "Warnings and Errors", "Errors only"})); 21 | updateMode.setModel( 22 | new DefaultComboBoxModel( 23 | new String[] { 24 | "Interactive (on any source file change)", 25 | "On Save (when source file saved on disk)", 26 | "On Demand (only if explicitly invoked)" 27 | })); 28 | updateMode.setSelectedIndex(1); 29 | minSeverityLevel.setSelectedIndex(0); 30 | } 31 | 32 | public JPanel getRoot() { 33 | return rootPanel; 34 | } 35 | 36 | @NotNull 37 | public String getBaseURL() { 38 | return baseURL.getText(); 39 | } 40 | 41 | public void setBaseURL(String baseURL) { 42 | this.baseURL.setText(baseURL); 43 | } 44 | 45 | public String getTokenID() { 46 | return tokenID.getText(); 47 | } 48 | 49 | public void setTokenID(String tokenID) { 50 | this.tokenID.setText(tokenID); 51 | } 52 | 53 | public boolean isLintersEnabled() { 54 | return addLinters.getModel().isSelected(); 55 | } 56 | 57 | public void setAddLinters(boolean addLinters) { 58 | this.addLinters.getModel().setSelected(addLinters); 59 | } 60 | 61 | public int getMinSeverityLevel() { 62 | return minSeverityLevel.getSelectedIndex() + 1; 63 | } 64 | 65 | public void setMinSeverityLevel(int minSeverityLevel) { 66 | this.minSeverityLevel.setSelectedIndex(minSeverityLevel - 1); 67 | } 68 | 69 | public DeepCodeParams.UpdateMode getUpdateMode() { 70 | return DeepCodeParams.UpdateMode.values()[updateMode.getSelectedIndex()]; 71 | } 72 | 73 | public void setUpdateMode(DeepCodeParams.UpdateMode mode) { 74 | this.updateMode.setSelectedIndex(mode.ordinal()); 75 | } 76 | 77 | public boolean isPluginEnabled() { 78 | return enableDeepCodePlugin.getModel().isSelected(); 79 | } 80 | 81 | public void enablePlugin(boolean enableDeepCodePlugin) { 82 | this.enableDeepCodePlugin.getModel().setSelected(enableDeepCodePlugin); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/configurable/FilterDialog.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package ai.deepcode.jbplugin.ui.configurable; 3 | 4 | import com.intellij.ide.IdeBundle; 5 | import com.intellij.ide.todo.TodoFilter; 6 | import com.intellij.openapi.ui.DialogWrapper; 7 | import com.intellij.openapi.ui.ValidationInfo; 8 | import com.intellij.psi.search.TodoPattern; 9 | import com.intellij.ui.CheckBoxList; 10 | import com.intellij.ui.IdeBorderFactory; 11 | import com.intellij.ui.ScrollPaneFactory; 12 | import com.intellij.ui.components.JBTextField; 13 | import com.intellij.util.ui.FormBuilder; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | import javax.swing.*; 17 | import java.awt.*; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | class FilterDialog extends DialogWrapper { 22 | private final TodoFilter myFilter; 23 | private final int myFilterIndex; 24 | private final List myFilters; 25 | 26 | private final JTextField myNameField; 27 | private final JScrollPane myPatternsScrollPane; 28 | 29 | /** 30 | * @param parent parent component. 31 | * @param filter filter to be edited. 32 | * @param filterIndex index of {@code filter} in the {@code filters}. This parameter is 33 | * needed to not compare filter with itself when validating. 34 | * @param filters all already configured filters. This parameter is used to 35 | * @param patterns all patterns available in this filter. 36 | */ 37 | FilterDialog(Component parent, TodoFilter filter, int filterIndex, List filters, List patterns) { 38 | super(parent, true); 39 | setTitle(IdeBundle.message("title.add.todo.filter")); 40 | myFilter = filter; 41 | myFilterIndex = filterIndex; 42 | myFilters = filters; 43 | myNameField = new JBTextField(filter.getName()); 44 | CheckBoxList patternsList = new CheckBoxList<>(); 45 | patternsList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 46 | patternsList.setCheckBoxListListener((int index, boolean value) -> { 47 | if (value) { 48 | myFilter.addTodoPattern(patternsList.getItemAt(index)); 49 | } 50 | else { 51 | myFilter.removeTodoPattern(patternsList.getItemAt(index)); 52 | } 53 | }); 54 | for (TodoPattern pattern : patterns) { 55 | patternsList.addItem(pattern, pattern.getPatternString(), myFilter.contains(pattern)); 56 | } 57 | if (patternsList.getItemsCount() > 0) { 58 | patternsList.setSelectedIndex(0); 59 | } 60 | myPatternsScrollPane = ScrollPaneFactory.createScrollPane(patternsList); 61 | myPatternsScrollPane.setMinimumSize(new Dimension(300, -1)); 62 | init(); 63 | } 64 | 65 | @NotNull 66 | @Override 67 | protected List doValidateAll() { 68 | List result = new ArrayList<>(); 69 | String filterName = getNewFilterName(); 70 | if (filterName.isEmpty()) { 71 | result.add(new ValidationInfo(IdeBundle.message("error.filter.name.should.be.specified"), myNameField)); 72 | } 73 | else { 74 | for (int i = 0; i < myFilters.size(); i++) { 75 | TodoFilter filter = myFilters.get(i); 76 | if (myFilterIndex != i && filterName.equals(filter.getName())) { 77 | result.add(new ValidationInfo(IdeBundle.message("error.filter.with.the.same.name.already.exists"), myNameField)); 78 | } 79 | } 80 | } 81 | if (myFilter.isEmpty()) { 82 | result.add(new ValidationInfo(IdeBundle.message("error.filter.should.contain.at.least.one.pattern"), myPatternsScrollPane)); 83 | } 84 | return result; 85 | } 86 | 87 | @NotNull 88 | private String getNewFilterName() { 89 | return myNameField.getText().trim(); 90 | } 91 | 92 | @Override 93 | protected void doOKAction() { 94 | myFilter.setName(getNewFilterName()); 95 | super.doOKAction(); 96 | } 97 | 98 | @Override 99 | protected String getHelpId() { 100 | return "reference.idesettings.todo.editfilter"; 101 | } 102 | 103 | @Override 104 | public JComponent getPreferredFocusedComponent() { 105 | return myNameField; 106 | } 107 | 108 | @Override 109 | protected JComponent createCenterPanel() { 110 | JPanel patternListPanel = new JPanel(new BorderLayout()); 111 | patternListPanel.setBorder(IdeBorderFactory.createTitledBorder(IdeBundle.message("group.todo.filter.patterns"))); 112 | patternListPanel.add(myPatternsScrollPane, BorderLayout.CENTER); 113 | return FormBuilder.createFormBuilder() 114 | .addLabeledComponent(IdeBundle.message("label.todo.filter.name"), myNameField) 115 | .addComponentFillVertically(patternListPanel, 0).getPanel(); 116 | } 117 | } -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/configurable/FiltersTableModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2016 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui.configurable; 18 | 19 | import com.intellij.ide.IdeBundle; 20 | import com.intellij.ide.todo.TodoFilter; 21 | import com.intellij.psi.search.TodoPattern; 22 | import com.intellij.util.ui.ItemRemovable; 23 | 24 | import javax.swing.table.AbstractTableModel; 25 | import java.util.Iterator; 26 | import java.util.List; 27 | 28 | final class FiltersTableModel extends AbstractTableModel implements ItemRemovable { 29 | private final String[] ourColumnNames = new String[]{ 30 | IdeBundle.message("column.todo.filters.name"), 31 | IdeBundle.message("column.todo.filter.patterns") 32 | }; 33 | private final Class[] ourColumnClasses = new Class[]{String.class, String.class}; 34 | 35 | private final List myFilters; 36 | 37 | FiltersTableModel(List filters) { 38 | myFilters = filters; 39 | } 40 | 41 | @Override 42 | public String getColumnName(int column) { 43 | return ourColumnNames[column]; 44 | } 45 | 46 | @Override 47 | public Class getColumnClass(int column) { 48 | return ourColumnClasses[column]; 49 | } 50 | 51 | @Override 52 | public int getColumnCount() { 53 | return 2; 54 | } 55 | 56 | @Override 57 | public int getRowCount() { 58 | return myFilters.size(); 59 | } 60 | 61 | @Override 62 | public Object getValueAt(int row, int column) { 63 | TodoFilter filter = myFilters.get(row); 64 | switch (column) { 65 | case 0: { // "Name" column 66 | return filter.getName(); 67 | } 68 | case 1: { 69 | StringBuilder sb = new StringBuilder(); 70 | for (Iterator i = filter.iterator(); i.hasNext(); ) { 71 | TodoPattern pattern = (TodoPattern)i.next(); 72 | sb.append(pattern.getPatternString()); 73 | if (i.hasNext()) { 74 | sb.append(" | "); 75 | } 76 | } 77 | return sb.toString(); 78 | } 79 | default: { 80 | throw new IllegalArgumentException(); 81 | } 82 | } 83 | } 84 | 85 | @Override 86 | public void removeRow(int index) { 87 | myFilters.remove(index); 88 | fireTableRowsDeleted(index, index); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/configurable/PatternDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2017 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui.configurable; 18 | 19 | import com.intellij.application.options.colors.ColorAndFontDescription; 20 | import com.intellij.application.options.colors.ColorAndFontDescriptionPanel; 21 | import com.intellij.application.options.colors.TextAttributesDescription; 22 | import com.intellij.icons.AllIcons; 23 | import com.intellij.ide.IdeBundle; 24 | import com.intellij.openapi.editor.colors.EditorColorsManager; 25 | import com.intellij.openapi.editor.markup.TextAttributes; 26 | import com.intellij.openapi.ui.ComboBox; 27 | import com.intellij.openapi.ui.DialogWrapper; 28 | import com.intellij.openapi.ui.ValidationInfo; 29 | import com.intellij.psi.search.TodoAttributes; 30 | import com.intellij.psi.search.TodoAttributesUtil; 31 | import com.intellij.psi.search.TodoPattern; 32 | import com.intellij.ui.SimpleListCellRenderer; 33 | import com.intellij.ui.components.JBCheckBox; 34 | import com.intellij.ui.components.JBTextField; 35 | import com.intellij.util.ui.FormBuilder; 36 | import org.jetbrains.annotations.NotNull; 37 | 38 | import javax.swing.*; 39 | import java.awt.*; 40 | import java.awt.event.ActionEvent; 41 | import java.awt.event.ActionListener; 42 | import java.util.Collections; 43 | import java.util.List; 44 | 45 | class PatternDialog extends DialogWrapper { 46 | private final TodoPattern myPattern; 47 | 48 | private final ComboBox myIconComboBox; 49 | private final JBCheckBox myCaseSensitiveCheckBox; 50 | private final JBTextField myPatternStringField; 51 | private final ColorAndFontDescriptionPanel myColorAndFontDescriptionPanel; 52 | private final ColorAndFontDescription myColorAndFontDescription; 53 | private final JBCheckBox myUsedDefaultColorsCheckBox; 54 | private final int myPatternIndex; 55 | private final List myExistingPatterns; 56 | 57 | PatternDialog(Component parent, TodoPattern pattern, int patternIndex, List existingPatterns) { 58 | super(parent, true); 59 | myPatternIndex = patternIndex; 60 | myExistingPatterns = existingPatterns; 61 | setTitle(IdeBundle.message("title.add.todo.pattern")); 62 | setResizable(false); 63 | 64 | final TodoAttributes attrs = pattern.getAttributes(); 65 | myPattern = pattern; 66 | myIconComboBox = new ComboBox<>(new Icon[]{AllIcons.General.TodoDefault, AllIcons.General.TodoQuestion, 67 | AllIcons.General.TodoImportant}); 68 | myIconComboBox.setSelectedItem(attrs.getIcon()); 69 | myIconComboBox.setRenderer(SimpleListCellRenderer.create((label, value, index) -> { 70 | label.setIcon(value); 71 | label.setText(" "); 72 | })); 73 | myCaseSensitiveCheckBox = new JBCheckBox(IdeBundle.message("checkbox.case.sensitive"), pattern.isCaseSensitive()); 74 | myPatternStringField = new JBTextField(pattern.getPatternString()); 75 | 76 | // use default colors check box 77 | myUsedDefaultColorsCheckBox = new JBCheckBox(IdeBundle.message("checkbox.todo.use.default.colors")); 78 | myUsedDefaultColorsCheckBox.setSelected(!attrs.shouldUseCustomTodoColor()); 79 | 80 | myColorAndFontDescriptionPanel = new ColorAndFontDescriptionPanel(); 81 | 82 | TextAttributes attributes = myPattern.getAttributes().getCustomizedTextAttributes(); 83 | myColorAndFontDescription = new TextAttributesDescription("null", null, attributes, null, 84 | EditorColorsManager.getInstance().getGlobalScheme(), null, null) { 85 | @Override 86 | public boolean isErrorStripeEnabled() { 87 | return true; 88 | } 89 | 90 | @Override 91 | public boolean isEditable() { 92 | return true; 93 | } 94 | }; 95 | myColorAndFontDescriptionPanel.reset(myColorAndFontDescription); 96 | 97 | updateCustomColorsPanel(); 98 | myUsedDefaultColorsCheckBox.addActionListener(new ActionListener() { 99 | @Override 100 | public void actionPerformed(ActionEvent e) { 101 | updateCustomColorsPanel(); 102 | } 103 | }); 104 | init(); 105 | } 106 | 107 | private void updateCustomColorsPanel() { 108 | if (useCustomTodoColor()) { 109 | // restore controls 110 | myColorAndFontDescriptionPanel.reset(myColorAndFontDescription); 111 | } 112 | else { 113 | // disable controls 114 | myColorAndFontDescriptionPanel.resetDefault(); 115 | } 116 | } 117 | 118 | @Override 119 | public JComponent getPreferredFocusedComponent() { 120 | return myPatternStringField; 121 | } 122 | 123 | @Override 124 | protected void doOKAction() { 125 | myPattern.setPatternString(myPatternStringField.getText().trim()); 126 | myPattern.setCaseSensitive(myCaseSensitiveCheckBox.isSelected()); 127 | 128 | final TodoAttributes attrs = myPattern.getAttributes(); 129 | attrs.setIcon((Icon)myIconComboBox.getSelectedItem()); 130 | attrs.setUseCustomTodoColor(useCustomTodoColor(), TodoAttributesUtil.getDefaultColorSchemeTextAttributes()); 131 | 132 | if (useCustomTodoColor()) { 133 | myColorAndFontDescriptionPanel.apply(myColorAndFontDescription, null); 134 | } 135 | super.doOKAction(); 136 | } 137 | 138 | @NotNull 139 | @Override 140 | protected List doValidateAll() { 141 | String patternString = myPatternStringField.getText().trim(); 142 | if (patternString.isEmpty()) { 143 | return Collections.singletonList(new ValidationInfo(IdeBundle.message("error.pattern.should.be.specified"), myPatternStringField)); 144 | } 145 | for (int i = 0; i < myExistingPatterns.size(); i++) { 146 | TodoPattern pattern = myExistingPatterns.get(i); 147 | if (myPatternIndex != i && patternString.equals(pattern.getPatternString())) { 148 | return Collections.singletonList(new ValidationInfo(IdeBundle.message("error.same.pattern.already.exists"), myPatternStringField)); 149 | } 150 | } 151 | return super.doValidateAll(); 152 | } 153 | 154 | private boolean useCustomTodoColor() { 155 | return !myUsedDefaultColorsCheckBox.isSelected(); 156 | } 157 | 158 | @Override 159 | protected JComponent createCenterPanel() { 160 | return FormBuilder.createFormBuilder() 161 | .addLabeledComponent(IdeBundle.message("label.todo.pattern"), myPatternStringField) 162 | .addLabeledComponent(IdeBundle.message("label.todo.icon"), myIconComboBox) 163 | .addComponent(myCaseSensitiveCheckBox) 164 | .addComponent(myUsedDefaultColorsCheckBox) 165 | .addComponent(myColorAndFontDescriptionPanel) 166 | .getPanel(); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/configurable/PatternsTableModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2009 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui.configurable; 18 | 19 | import com.intellij.psi.search.TodoPattern; 20 | import com.intellij.util.ui.ItemRemovable; 21 | import com.intellij.ide.IdeBundle; 22 | 23 | import javax.swing.*; 24 | import javax.swing.table.AbstractTableModel; 25 | import java.util.List; 26 | 27 | final class PatternsTableModel extends AbstractTableModel implements ItemRemovable{ 28 | private final String[] ourColumnNames=new String[]{ 29 | IdeBundle.message("column.todo.patterns.icon"), 30 | IdeBundle.message("column.todo.patterns.case.sensitive"), 31 | IdeBundle.message("column.todo.patterns.pattern") 32 | }; 33 | private final Class[] ourColumnClasses=new Class[]{Icon.class,Boolean.class,String.class}; 34 | 35 | private final List myPatterns; 36 | 37 | PatternsTableModel(List patterns){ 38 | myPatterns=patterns; 39 | } 40 | 41 | @Override 42 | public String getColumnName(int column){ 43 | return ourColumnNames[column]; 44 | } 45 | 46 | @Override 47 | public Class getColumnClass(int column){ 48 | return ourColumnClasses[column]; 49 | } 50 | 51 | @Override 52 | public int getColumnCount(){ 53 | return 3; 54 | } 55 | 56 | @Override 57 | public int getRowCount(){ 58 | return myPatterns.size(); 59 | } 60 | 61 | @Override 62 | public boolean isCellEditable(int rowIndex, int columnIndex) { 63 | return columnIndex == 1; 64 | } 65 | 66 | @Override 67 | public Object getValueAt(int row,int column){ 68 | TodoPattern pattern=myPatterns.get(row); 69 | switch(column){ 70 | case 0:{ // "Icon" column 71 | return pattern.getAttributes().getIcon(); 72 | }case 1:{ // "Case Sensitive" column 73 | return pattern.isCaseSensitive()?Boolean.TRUE:Boolean.FALSE; 74 | }case 2:{ // "Pattern" column 75 | return pattern.getPatternString(); 76 | }default:{ 77 | throw new IllegalArgumentException(); 78 | } 79 | } 80 | } 81 | 82 | @Override 83 | public void setValueAt(Object value,int row,int column){ 84 | TodoPattern pattern=myPatterns.get(row); 85 | switch(column){ 86 | case 0:{ 87 | pattern.getAttributes().setIcon((Icon)value); 88 | break; 89 | }case 1:{ 90 | pattern.setCaseSensitive(((Boolean)value).booleanValue()); 91 | break; 92 | }case 2:{ 93 | pattern.setPatternString(((String)value).trim()); 94 | break; 95 | }default:{ 96 | throw new IllegalArgumentException(); 97 | } 98 | } 99 | } 100 | 101 | @Override 102 | public void removeRow(int index){ 103 | myPatterns.remove(index); 104 | fireTableRowsDeleted(index,index); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/configurable/TodoPatternTableCellRenderer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | 3 | package ai.deepcode.jbplugin.ui.configurable; 4 | 5 | import com.intellij.psi.search.TodoPattern; 6 | import com.intellij.ui.JBColor; 7 | import com.intellij.util.ui.UIUtil; 8 | 9 | import javax.swing.*; 10 | import javax.swing.table.DefaultTableCellRenderer; 11 | import java.awt.*; 12 | import java.util.List; 13 | 14 | final class TodoPatternTableCellRenderer extends DefaultTableCellRenderer { 15 | private final List myPatterns; 16 | 17 | TodoPatternTableCellRenderer(List patterns) { 18 | myPatterns = patterns; 19 | } 20 | 21 | @Override 22 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 23 | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 24 | TodoPattern pattern = myPatterns.get(row); 25 | if (isSelected) { 26 | setForeground(UIUtil.getTableSelectionForeground(true)); 27 | } 28 | else { 29 | setForeground(pattern.getPattern() != null ? UIUtil.getTableForeground() : JBColor.RED); 30 | } 31 | return this; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/nodes/BaseToDoNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2009 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.deepcode.jbplugin.ui.nodes; 18 | 19 | import ai.deepcode.jbplugin.ui.ToDoSettings; 20 | import ai.deepcode.jbplugin.ui.TodoTreeBuilder; 21 | import ai.deepcode.jbplugin.ui.TodoTreeStructure; 22 | import com.intellij.ide.util.treeView.AbstractTreeNode; 23 | import com.intellij.openapi.project.Project; 24 | import com.intellij.openapi.vfs.VirtualFile; 25 | import org.jetbrains.annotations.NotNull; 26 | 27 | public abstract class BaseToDoNode extends AbstractTreeNode { 28 | protected final ToDoSettings myToDoSettings; 29 | protected final TodoTreeBuilder myBuilder; 30 | 31 | protected BaseToDoNode(Project project, @NotNull Value value, TodoTreeBuilder builder) { 32 | super(project, value); 33 | myBuilder = builder; 34 | myToDoSettings = myBuilder.getTodoTreeStructure(); 35 | } 36 | 37 | public boolean contains(VirtualFile file) { 38 | return false; 39 | } 40 | 41 | public boolean contains(Object element) { 42 | return false; 43 | } 44 | 45 | protected TodoTreeStructure getTreeStructure() { 46 | return myBuilder.getTodoTreeStructure(); 47 | } 48 | 49 | public abstract int getFileCount(Value val); 50 | 51 | public abstract int getTodoItemCount(Value val); 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/nodes/MarkerItemImpl.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.ui.nodes; 2 | 3 | import com.intellij.psi.PsiFile; 4 | import com.intellij.psi.impl.search.TodoItemImpl; 5 | import com.intellij.psi.search.TodoPattern; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.Collections; 9 | 10 | /** Marks Marker (for suggestion) item */ 11 | public class MarkerItemImpl extends TodoItemImpl { 12 | 13 | private final String message; 14 | 15 | public MarkerItemImpl( 16 | @NotNull PsiFile file, int startOffset, int endOffset, @NotNull TodoPattern pattern, String message) { 17 | super(file, startOffset, endOffset, pattern, Collections.emptyList()); 18 | this.message = message; 19 | } 20 | 21 | public String getMessage() { 22 | return message; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/nodes/ModuleToDoNode.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | 3 | package ai.deepcode.jbplugin.ui.nodes; 4 | 5 | import ai.deepcode.jbplugin.core.RunUtils; 6 | import ai.deepcode.jbplugin.ui.TodoTreeBuilder; 7 | import ai.deepcode.jbplugin.ui.TodoTreeStructure; 8 | import com.intellij.ide.IdeBundle; 9 | import com.intellij.ide.projectView.PresentationData; 10 | import com.intellij.ide.util.treeView.AbstractTreeNode; 11 | import com.intellij.openapi.module.Module; 12 | import com.intellij.openapi.module.ModuleType; 13 | import com.intellij.openapi.module.ModuleUtilCore; 14 | import com.intellij.openapi.project.Project; 15 | import com.intellij.openapi.roots.ModuleRootManager; 16 | import com.intellij.openapi.ui.Queryable; 17 | import com.intellij.openapi.vfs.VirtualFile; 18 | import com.intellij.psi.PsiElement; 19 | import com.intellij.psi.PsiFile; 20 | import com.intellij.psi.search.TodoItem; 21 | import org.jetbrains.annotations.NotNull; 22 | import org.jetbrains.annotations.Nullable; 23 | 24 | import java.util.ArrayList; 25 | import java.util.Collection; 26 | import java.util.Iterator; 27 | 28 | public class ModuleToDoNode extends BaseToDoNode { 29 | 30 | public ModuleToDoNode(Project project, @NotNull Module value, TodoTreeBuilder builder) { 31 | super(project, value, builder); 32 | } 33 | 34 | @Override 35 | @NotNull 36 | public Collection> getChildren() { 37 | ArrayList> children = new ArrayList<>(); 38 | if (myToDoSettings.getIsPackagesShown()) { 39 | TodoTreeHelper.getInstance(getProject()).addPackagesToChildren(children, getValue(), myBuilder); 40 | } 41 | else { 42 | for (Iterator i = myBuilder.getAllFiles(); i.hasNext();) { 43 | final PsiFile psiFile = (PsiFile)i.next(); 44 | if (psiFile == null) { // skip invalid PSI files 45 | continue; 46 | } 47 | final VirtualFile virtualFile = psiFile.getVirtualFile(); 48 | final boolean isInContent = ModuleRootManager.getInstance(getValue()).getFileIndex().isInContent(virtualFile); 49 | if (!isInContent) continue; 50 | TodoFileNode fileNode = new TodoFileNode(getProject(), psiFile, myBuilder, false); 51 | if (getTreeStructure().accept(psiFile) && !children.contains(fileNode)) { 52 | children.add(fileNode); 53 | } 54 | } 55 | } 56 | return children; 57 | 58 | } 59 | 60 | @Override 61 | public boolean contains(Object element) { 62 | if (element instanceof TodoItem) { 63 | Module module = ModuleUtilCore.findModuleForFile(((TodoItem)element).getFile()); 64 | return super.canRepresent(module); 65 | } 66 | 67 | if (element instanceof PsiElement) { 68 | Module module = ModuleUtilCore.findModuleForPsiElement((PsiElement)element); 69 | return super.canRepresent(module); 70 | } 71 | return super.canRepresent(element); 72 | } 73 | 74 | private TodoTreeStructure getStructure() { 75 | return myBuilder.getTodoTreeStructure(); 76 | } 77 | 78 | @Override 79 | public void update(@NotNull PresentationData presentation) { 80 | String newName = getValue().getName(); 81 | int todoItemCount = getTodoItemCount(getValue()); 82 | presentation.setLocationString(IdeBundle.message("node.todo.group", todoItemCount)); 83 | presentation.setIcon(ModuleType.get(getValue()).getIcon()); 84 | presentation.setPresentableText(newName); 85 | } 86 | 87 | /* 88 | @Override 89 | public String getTestPresentation() { 90 | return "Module"; 91 | } 92 | */ 93 | 94 | @Override 95 | public String toTestString(@Nullable Queryable.PrintInfo printInfo) { 96 | return "Module"; 97 | } 98 | 99 | @Override 100 | public int getFileCount(Module module) { 101 | Iterator iterator = myBuilder.getFiles(module); 102 | int count = 0; 103 | while (iterator.hasNext()) { 104 | PsiFile psiFile = iterator.next(); 105 | if (getStructure().accept(psiFile)) { 106 | count++; 107 | } 108 | } 109 | return count; 110 | } 111 | 112 | @Override 113 | public int getTodoItemCount(final Module val) { 114 | Iterator iterator = myBuilder.getFiles(val); 115 | int count = 0; 116 | while (iterator.hasNext()) { 117 | final PsiFile psiFile = iterator.next(); 118 | count += RunUtils.computeInReadActionInSmartMode( 119 | psiFile.getProject(), 120 | () -> getTreeStructure().getTodoItemCount(psiFile)); 121 | } 122 | return count; 123 | } 124 | 125 | @Override 126 | public int getWeight() { 127 | return 1; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/nodes/SingleFileToDoNode.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | 3 | package ai.deepcode.jbplugin.ui.nodes; 4 | 5 | import com.intellij.ide.projectView.PresentationData; 6 | import ai.deepcode.jbplugin.ui.TodoTreeBuilder; 7 | import com.intellij.ide.util.treeView.AbstractTreeNode; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.psi.PsiFile; 10 | import com.intellij.psi.search.TodoItem; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Collection; 15 | import java.util.Collections; 16 | 17 | public class SingleFileToDoNode extends BaseToDoNode{ 18 | private final TodoFileNode myFileNode; 19 | 20 | public SingleFileToDoNode(Project project, @NotNull PsiFile value, TodoTreeBuilder builder) { 21 | super(project, value, builder); 22 | myFileNode = new TodoFileNode(getProject(), value, myBuilder, true); 23 | } 24 | 25 | @Override 26 | @NotNull 27 | public Collection> getChildren() { 28 | return new ArrayList<>(Collections.singleton(myFileNode)); 29 | } 30 | 31 | @Override 32 | public void update(@NotNull PresentationData presentation) { 33 | } 34 | 35 | @Override 36 | public boolean canRepresent(Object element) { 37 | return false; 38 | } 39 | 40 | @Override 41 | public boolean contains(Object element) { 42 | if (element instanceof TodoItem) { 43 | return super.canRepresent(((TodoItem)element).getFile()); 44 | } 45 | return super.canRepresent(element); 46 | } 47 | 48 | public Object getFileNode() { 49 | return myFileNode; 50 | } 51 | 52 | @Override 53 | public int getFileCount(final PsiFile val) { 54 | return 1; 55 | } 56 | 57 | @Override 58 | public int getTodoItemCount(final PsiFile val) { 59 | return getTreeStructure().getTodoItemCount(val); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/nodes/ToDoRootNode.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | 3 | package ai.deepcode.jbplugin.ui.nodes; 4 | 5 | import com.intellij.ide.projectView.PresentationData; 6 | import ai.deepcode.jbplugin.ui.ToDoSummary; 7 | import ai.deepcode.jbplugin.ui.TodoTreeBuilder; 8 | import com.intellij.ide.util.treeView.AbstractTreeNode; 9 | import com.intellij.openapi.project.Project; 10 | import com.intellij.openapi.ui.Queryable; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Collection; 16 | import java.util.Collections; 17 | 18 | public class ToDoRootNode extends BaseToDoNode{ 19 | private final SummaryNode mySummaryNode; 20 | 21 | public ToDoRootNode(Project project, Object value, TodoTreeBuilder builder, @NotNull ToDoSummary summary) { 22 | super(project, value, builder); 23 | mySummaryNode = createSummaryNode(summary); 24 | } 25 | 26 | protected SummaryNode createSummaryNode(@NotNull ToDoSummary summary) { 27 | return new SummaryNode(getProject(), summary, myBuilder); 28 | } 29 | 30 | @Override 31 | @NotNull 32 | public Collection> getChildren() { 33 | return new ArrayList<>(Collections.singleton(mySummaryNode)); 34 | } 35 | 36 | @Override 37 | public void update(@NotNull PresentationData presentation) { 38 | } 39 | 40 | public Object getSummaryNode() { 41 | return mySummaryNode; 42 | } 43 | 44 | /* 45 | @Override 46 | public String getTestPresentation() { 47 | return "Root"; 48 | } 49 | */ 50 | 51 | @Nullable 52 | @Override 53 | public String toTestString(@Nullable Queryable.PrintInfo printInfo) { 54 | return "Root"; 55 | } 56 | 57 | @Override 58 | public int getFileCount(final Object val) { 59 | return mySummaryNode.getFileCount(null); 60 | } 61 | 62 | @Override 63 | public int getTodoItemCount(final Object val) { 64 | return mySummaryNode.getTodoItemCount(null); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/nodes/TodoDirNode.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | 3 | package ai.deepcode.jbplugin.ui.nodes; 4 | 5 | import com.intellij.ide.IdeBundle; 6 | import com.intellij.ide.projectView.PresentationData; 7 | import com.intellij.ide.projectView.ViewSettings; 8 | import com.intellij.ide.projectView.impl.ProjectRootsUtil; 9 | import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode; 10 | import ai.deepcode.jbplugin.ui.TodoTreeBuilder; 11 | import ai.deepcode.jbplugin.ui.TodoTreeStructure; 12 | import com.intellij.ide.util.treeView.AbstractTreeNode; 13 | import com.intellij.openapi.project.IndexNotReadyException; 14 | import com.intellij.openapi.project.Project; 15 | import com.intellij.openapi.roots.ProjectRootManager; 16 | import com.intellij.openapi.vfs.VirtualFile; 17 | import com.intellij.psi.PsiDirectory; 18 | import com.intellij.psi.PsiFile; 19 | import com.intellij.psi.impl.file.SourceRootIconProvider; 20 | import org.jetbrains.annotations.NotNull; 21 | 22 | import java.util.Collection; 23 | import java.util.Iterator; 24 | 25 | public final class TodoDirNode extends PsiDirectoryNode { 26 | private final TodoTreeBuilder myBuilder; 27 | 28 | 29 | public TodoDirNode(Project project, 30 | @NotNull PsiDirectory directory, 31 | TodoTreeBuilder builder) { 32 | super(project, directory, ViewSettings.DEFAULT); 33 | myBuilder = builder; 34 | } 35 | 36 | @Override 37 | protected void updateImpl(@NotNull PresentationData data) { 38 | super.updateImpl(data); 39 | int fileCount = getFileCount(getValue()); 40 | if (getValue() == null || !getValue().isValid() || fileCount == 0) { 41 | setValue(null); 42 | return; 43 | } 44 | 45 | VirtualFile directory = getValue().getVirtualFile(); 46 | boolean isProjectRoot = !ProjectRootManager.getInstance(getProject()).getFileIndex().isInContent(directory); 47 | String newName = isProjectRoot || getStructure().getIsFlattenPackages() ? getValue().getVirtualFile().getPresentableUrl() : getValue().getName(); 48 | 49 | int todoItemCount = getTodoItemCount(getValue()); 50 | data.setLocationString(IdeBundle.message("node.todo.group", todoItemCount)); 51 | data.setPresentableText(newName); 52 | } 53 | 54 | @Override 55 | protected void setupIcon(PresentationData data, PsiDirectory psiDirectory) { 56 | final VirtualFile virtualFile = psiDirectory.getVirtualFile(); 57 | if (ProjectRootsUtil.isModuleContentRoot(virtualFile, psiDirectory.getProject())) { 58 | data.setIcon(new SourceRootIconProvider.DirectoryProvider().getIcon(psiDirectory, 0)); 59 | } else { 60 | super.setupIcon(data, psiDirectory); 61 | } 62 | } 63 | 64 | private TodoTreeStructure getStructure() { 65 | return myBuilder.getTodoTreeStructure(); 66 | } 67 | 68 | @Override 69 | public Collection getChildrenImpl() { 70 | return TodoTreeHelper.getInstance(getProject()).getDirectoryChildren(getValue(), myBuilder, getSettings().isFlattenPackages()); 71 | } 72 | 73 | public int getFileCount(PsiDirectory directory) { 74 | Iterator iterator = myBuilder.getFiles(directory); 75 | int count = 0; 76 | try { 77 | while (iterator.hasNext()) { 78 | PsiFile psiFile = iterator.next(); 79 | if (getStructure().accept(psiFile)) { 80 | count++; 81 | } 82 | } 83 | } 84 | catch (IndexNotReadyException e) { 85 | return count; 86 | } 87 | return count; 88 | } 89 | 90 | public int getTodoItemCount(PsiDirectory directory) { 91 | if (TodoTreeHelper.getInstance(getProject()).skipDirectory(directory)) { 92 | return 0; 93 | } 94 | int count = 0; 95 | Iterator iterator = myBuilder.getFiles(directory); 96 | while (iterator.hasNext()) { 97 | PsiFile psiFile = iterator.next(); 98 | count += getStructure().getTodoItemCount(psiFile); 99 | } 100 | return count; 101 | } 102 | 103 | @Override 104 | public int getWeight() { 105 | return 2; 106 | } 107 | 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/nodes/TodoFileNode.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 2 | // license that can be found in the LICENSE file. 3 | 4 | package ai.deepcode.jbplugin.ui.nodes; 5 | 6 | import ai.deepcode.jbplugin.core.PDU; 7 | import ai.deepcode.jbplugin.ui.HighlightedRegionProvider; 8 | import ai.deepcode.jbplugin.ui.TodoTreeBuilder; 9 | import ai.deepcode.jbplugin.ui.utils.DeepCodeUIUtils; 10 | import ai.deepcode.jbplugin.core.AnalysisData; 11 | import ai.deepcode.jbplugin.core.DeepCodeUtils; 12 | import com.intellij.ide.IdeBundle; 13 | import com.intellij.ide.projectView.PresentationData; 14 | import com.intellij.ide.projectView.ViewSettings; 15 | import com.intellij.ide.projectView.impl.nodes.PsiFileNode; 16 | import com.intellij.ide.util.treeView.AbstractTreeNode; 17 | import com.intellij.openapi.project.IndexNotReadyException; 18 | import com.intellij.openapi.project.Project; 19 | import com.intellij.psi.PsiFile; 20 | import com.intellij.ui.HighlightedRegion; 21 | import com.intellij.util.containers.ContainerUtil; 22 | import org.jetbrains.annotations.NotNull; 23 | 24 | import java.util.Collection; 25 | import java.util.Collections; 26 | import java.util.List; 27 | import java.util.stream.Collectors; 28 | 29 | public final class TodoFileNode extends PsiFileNode implements HighlightedRegionProvider { 30 | private final List myHighlightedRegions; 31 | 32 | private final TodoTreeBuilder myBuilder; 33 | private final boolean mySingleFileMode; 34 | 35 | public TodoFileNode( 36 | Project project, @NotNull PsiFile file, TodoTreeBuilder treeBuilder, boolean singleFileMode) { 37 | super(project, file, ViewSettings.DEFAULT); 38 | myBuilder = treeBuilder; 39 | mySingleFileMode = singleFileMode; 40 | myHighlightedRegions = ContainerUtil.createConcurrentList(); 41 | } 42 | 43 | @Override 44 | public Collection getChildrenImpl() { 45 | try { 46 | if (!mySingleFileMode) { 47 | return createGeneralList(); 48 | } 49 | return createListForSingleFile(); 50 | } catch (IndexNotReadyException e) { 51 | return Collections.emptyList(); 52 | } 53 | } 54 | 55 | // fixme: same as createGeneralList 56 | private Collection createListForSingleFile() { 57 | return createGeneralList(); 58 | } 59 | 60 | private Collection createGeneralList() { 61 | PsiFile psiFile = getValue(); 62 | return AnalysisData.getInstance().getAnalysis(psiFile).stream() 63 | .map(suggestion -> new SuggestionNode(getProject(), psiFile, myBuilder, suggestion)) 64 | .sorted((o1, o2) -> o2.getValue().getSeverity() - o1.getValue().getSeverity()) 65 | .collect(Collectors.toList()); 66 | } 67 | 68 | @Override 69 | protected void updateImpl(@NotNull PresentationData data) { 70 | super.updateImpl(data); 71 | PsiFile psiFile = getValue(); 72 | String newName = PDU.getInstance().getDeepCodedFilePath(psiFile); 73 | final int length = newName.length(); 74 | if (length > 100) { 75 | newName = "..." + newName.substring(length - 97, length); 76 | } 77 | /* 78 | if (myBuilder.getTodoTreeStructure().isPackagesShown()) { 79 | newName = getValue().getName(); 80 | } else { 81 | newName = 82 | mySingleFileMode ? getValue().getName() : getValue().getVirtualFile().getPresentableUrl(); 83 | } 84 | */ 85 | String message = 86 | DeepCodeUIUtils.addErrWarnInfoCounts( 87 | Collections.singleton(psiFile), newName, false, myHighlightedRegions); 88 | data.setPresentableText(message); 89 | 90 | //todo remove?, not shown 91 | int todoItemCount; 92 | try { 93 | todoItemCount = myBuilder.getTodoTreeStructure().getTodoItemCount(getValue()); 94 | } catch (IndexNotReadyException e) { 95 | return; 96 | } 97 | if (todoItemCount > 0) { 98 | data.setLocationString(IdeBundle.message("node.todo.items", todoItemCount)); 99 | } 100 | } 101 | 102 | @Override 103 | public int getWeight() { 104 | return 4; 105 | } 106 | 107 | @Override 108 | public Iterable getHighlightedRegions() { 109 | return myHighlightedRegions; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/ai/deepcode/jbplugin/ui/utils/DeepCodeUIUtils.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.ui.utils; 2 | 3 | import ai.deepcode.jbplugin.core.AnalysisData; 4 | import ai.deepcode.jbplugin.core.DCLogger; 5 | import ai.deepcode.jbplugin.core.DeepCodeUtils; 6 | import ai.deepcode.jbplugin.core.PDU; 7 | import com.intellij.execution.process.ConsoleHighlighter; 8 | import com.intellij.icons.AllIcons; 9 | import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; 10 | import com.intellij.openapi.editor.colors.EditorColorsManager; 11 | import com.intellij.openapi.editor.colors.TextAttributesKey; 12 | import com.intellij.openapi.editor.markup.TextAttributes; 13 | import com.intellij.openapi.project.Project; 14 | import com.intellij.psi.PsiFile; 15 | import com.intellij.ui.HighlightedRegion; 16 | import com.intellij.ui.RowIcon; 17 | import com.intellij.ui.scale.JBUIScale; 18 | import com.intellij.util.IconUtil; 19 | import org.jetbrains.annotations.NotNull; 20 | import org.jetbrains.annotations.Nullable; 21 | 22 | import javax.swing.*; 23 | import java.awt.*; 24 | import java.util.ArrayList; 25 | import java.util.Collection; 26 | import java.util.List; 27 | 28 | public class DeepCodeUIUtils { 29 | 30 | private DeepCodeUIUtils() {} 31 | 32 | private static final TextAttributes ERROR_ATTRIBUTES = createAttributes(ConsoleHighlighter.RED); 33 | private static final TextAttributes WARNING_ATTRIBUTES = 34 | createAttributes(ConsoleHighlighter.YELLOW); 35 | private static final TextAttributes INFO_ATTRIBUTES = createAttributes(ConsoleHighlighter.BLUE); 36 | 37 | private static TextAttributes createAttributes(TextAttributesKey textAttributesKey) { 38 | return EditorColorsManager.getInstance() 39 | .getGlobalScheme() 40 | .getAttributes(textAttributesKey) 41 | .clone(); 42 | } 43 | 44 | private static TextAttributes createAttributes(Color frgColor) { 45 | TextAttributes result = 46 | EditorColorsManager.getInstance() 47 | .getGlobalScheme() 48 | .getAttributes(DefaultLanguageHighlighterColors.IDENTIFIER) 49 | .clone(); 50 | result.setForegroundColor(frgColor); 51 | return result; 52 | } 53 | 54 | public static String addErrWarnInfoCounts( 55 | @NotNull Project project, 56 | String originalMsg, 57 | boolean withTextEWI, 58 | @Nullable List regionsToUpdate) { 59 | return addErrWarnInfoCounts( 60 | PDU.toPsiFiles(AnalysisData.getInstance().getAllFilesWithSuggestions(project)), 61 | originalMsg, 62 | withTextEWI, 63 | regionsToUpdate); 64 | } 65 | 66 | public static String addErrWarnInfoCounts( 67 | @NotNull Collection psiFiles, 68 | String originalMsg, 69 | boolean withTextEWI, 70 | @Nullable List regionsToUpdate) { 71 | if (regionsToUpdate == null) { 72 | regionsToUpdate = new ArrayList<>(); // i.e. ignore 73 | } 74 | DeepCodeUtils.ErrorsWarningsInfos ewi = 75 | DeepCodeUtils.getInstance().getEWI(PDU.toObjects(psiFiles)); 76 | int errors = ewi.getErrors(); 77 | int warnings = ewi.getWarnings(); 78 | int infos = ewi.getInfos(); 79 | if (errors == 0 && infos == 0 && warnings == 0) return originalMsg; 80 | 81 | String result = originalMsg + ": "; 82 | regionsToUpdate.clear(); 83 | if (errors != 0) { 84 | int oldLength = result.length(); 85 | result += "\u24BE " + errors + (withTextEWI ? " errors" : "") + " "; 86 | regionsToUpdate.add(new HighlightedRegion(oldLength, result.length(), ERROR_ATTRIBUTES)); 87 | } 88 | if (warnings != 0) { 89 | int oldLength = result.length(); 90 | result += "\u26A0 " + warnings + (withTextEWI ? " warnings" : "") + " "; 91 | regionsToUpdate.add(new HighlightedRegion(oldLength, result.length(), WARNING_ATTRIBUTES)); 92 | } 93 | if (infos != 0) { 94 | int oldLength = result.length(); 95 | result += "\u24D8 " + infos + (withTextEWI ? " informational" : "") + " "; 96 | regionsToUpdate.add(new HighlightedRegion(oldLength, result.length(), INFO_ATTRIBUTES)); 97 | } 98 | return result; 99 | } 100 | 101 | private static final float fontToScale = JBUIScale.scale(12f); 102 | 103 | private static Icon scaleIcon(Icon sourceIcon) { 104 | return IconUtil.scaleByFont(sourceIcon, null, fontToScale - 2); 105 | } 106 | 107 | private static final Icon errorGray = scaleIcon(AllIcons.Nodes.WarningIntroduction); 108 | private static final Icon errorColor = scaleIcon(AllIcons.General.Error); 109 | private static final Icon warningGray = scaleIcon(AllIcons.General.ShowWarning); 110 | private static final Icon warningColor = scaleIcon(AllIcons.General.Warning); 111 | private static final Icon infoGray = scaleIcon(AllIcons.General.Note); 112 | private static final Icon infoColor = scaleIcon(AllIcons.General.Information); 113 | 114 | public static final Icon EMPTY_EWI_ICON = 115 | new RowIcon( 116 | errorGray, 117 | IconUtil.textToIcon("?", new JLabel(), fontToScale), 118 | warningGray, 119 | IconUtil.textToIcon("?", new JLabel(), fontToScale), 120 | infoGray, 121 | IconUtil.textToIcon("?", new JLabel(), fontToScale)); 122 | 123 | public static Icon getSummaryIcon(@NotNull Project project) { 124 | if (AnalysisData.getInstance().isProjectNOTAnalysed(project) 125 | || AnalysisData.getInstance().isUpdateAnalysisInProgress(project)) { 126 | DCLogger.getInstance().logInfo("EMPTY icon set"); 127 | return EMPTY_EWI_ICON; 128 | } 129 | 130 | DeepCodeUtils.ErrorsWarningsInfos ewi = 131 | DeepCodeUtils.getInstance() 132 | .getEWI(AnalysisData.getInstance().getAllFilesWithSuggestions(project)); 133 | int errors = ewi.getErrors(); 134 | int warnings = ewi.getWarnings(); 135 | int infos = ewi.getInfos(); 136 | DCLogger.getInstance().logInfo("error=" + errors + " warning=" + warnings + " info=" + infos); 137 | 138 | return new RowIcon( 139 | (errors != 0) ? errorColor : errorGray, 140 | number2ColoredIcon(errors, (errors != 0) ? ERROR_ATTRIBUTES.getForegroundColor() : null), 141 | (warnings != 0) ? warningColor : warningGray, 142 | number2ColoredIcon( 143 | warnings, (warnings != 0) ? WARNING_ATTRIBUTES.getForegroundColor() : null), 144 | (infos != 0) ? infoColor : infoGray, 145 | number2ColoredIcon(infos, (infos != 0) ? INFO_ATTRIBUTES.getForegroundColor() : null)); 146 | } 147 | 148 | private static Icon number2ColoredIcon(int number, @Nullable Color color) { 149 | Icon greyIcon = IconUtil.textToIcon(String.valueOf(number), new JLabel(), fontToScale); 150 | return greyIcon; // (color == null) ? greyIcon : IconUtil.colorize(greyIcon, color); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 18 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 18 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/resources/icons/DeepCodeLogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 18 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/resources/icons/DeepCodeLogo_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 18 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/test/.dcignore: -------------------------------------------------------------------------------- 1 | testData -------------------------------------------------------------------------------- /src/test/java/ai/deepcode/jbplugin/MyBasePlatformTestCase.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin; 2 | 3 | import ai.deepcode.jbplugin.core.DeepCodeParams; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.testFramework.fixtures.BasePlatformTestCase; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | * See: https://www.jetbrains.org/intellij/sdk/docs/basics/testing_plugins/testing_plugins.html See: 10 | * https://www.jetbrains.org/intellij/sdk/docs/tutorials/writing_tests_for_plugins.html 11 | */ 12 | public abstract class MyBasePlatformTestCase extends BasePlatformTestCase { 13 | protected Project project; 14 | 15 | @Override 16 | protected void setUp() throws Exception { 17 | super.setUp(); 18 | LoggerFactory.getLogger(this.getClass()).info("-------------------MyBasePlatformTestCase.setUp--------------------\n"); 19 | project = myFixture.getProject(); 20 | DeepCodeParams.getInstance().setUpdateMode(DeepCodeParams.UpdateMode.INTERACTIVE_MODE); 21 | } 22 | 23 | // !!! Will works only with already logged sessionToken 24 | protected static final String loggedToken = System.getenv("DEEPCODE_API_KEY"); 25 | 26 | @Override 27 | protected String getTestDataPath() { 28 | return "src/test/testData"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/ai/deepcode/jbplugin/MyPlatformTestCase.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin; 2 | 3 | import com.intellij.testFramework.PlatformTestCase; 4 | 5 | /** 6 | * See: https://www.jetbrains.org/intellij/sdk/docs/basics/testing_plugins/testing_plugins.html See: 7 | * https://www.jetbrains.org/intellij/sdk/docs/tutorials/writing_tests_for_plugins.html 8 | */ 9 | public abstract class MyPlatformTestCase extends PlatformTestCase { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/ai/deepcode/jbplugin/TestAnnotatorForCPP.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin; 2 | 3 | import ai.deepcode.jbplugin.core.DeepCodeParams; 4 | import ai.deepcode.jbplugin.core.PDU; 5 | import com.intellij.psi.PsiFile; 6 | import org.slf4j.LoggerFactory; 7 | 8 | public class TestAnnotatorForCPP extends MyBasePlatformTestCase { 9 | 10 | public void testHighlighting_CPP() { 11 | LoggerFactory.getLogger(this.getClass()).info("-------------------testHighlighting_CPP--------------------"); 12 | DeepCodeParams.getInstance().setSessionToken(loggedToken); 13 | DeepCodeParams.getInstance().setConsentGiven(project); 14 | PsiFile file = myFixture.configureByFile("AnnotatorTest.cpp"); 15 | // fixme: delay to let annotators do the job 16 | PDU.getInstance().delay(2000, null); 17 | myFixture.checkHighlighting(true, true, true, false); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/ai/deepcode/jbplugin/TestAnnotatorForJava.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin; 2 | 3 | import ai.deepcode.jbplugin.core.DeepCodeParams; 4 | import ai.deepcode.jbplugin.core.PDU; 5 | import org.slf4j.LoggerFactory; 6 | 7 | public class TestAnnotatorForJava extends MyBasePlatformTestCase { 8 | 9 | public void testHighlighting_Java() { 10 | LoggerFactory.getLogger(this.getClass()).info("-------------------testHighlighting_Java--------------------"); 11 | DeepCodeParams.getInstance().setSessionToken(loggedToken); 12 | DeepCodeParams.getInstance().setConsentGiven(project); 13 | myFixture.configureByFile("AnnotatorTest.java"); 14 | //fixme: delay to let annotators do the job 15 | PDU.getInstance().delay(2000, null); 16 | myFixture.checkHighlighting(true, true, true, true); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/ai/deepcode/jbplugin/TestAnnotatorForJavaScript.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin; 2 | 3 | import ai.deepcode.jbplugin.core.DeepCodeParams; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public class TestAnnotatorForJavaScript extends MyBasePlatformTestCase { 7 | 8 | public void testHighlighting_JavaScript() { 9 | LoggerFactory.getLogger(this.getClass()).info("-------------------testHighlighting_JavaScript--------------------"); 10 | DeepCodeParams.getInstance().setSessionToken(loggedToken); 11 | DeepCodeParams.getInstance().setConsentGiven(project); 12 | myFixture.configureByFile("AnnotatorTest.js"); 13 | myFixture.checkHighlighting(true, true, true, false); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/ai/deepcode/jbplugin/TestLoginProcess.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin; 2 | 3 | import ai.deepcode.jbplugin.core.DeepCodeParams; 4 | import ai.deepcode.jbplugin.core.LoginUtils; 5 | import ai.deepcode.jbplugin.core.RunUtils; 6 | import com.intellij.ide.util.PropertiesComponent; 7 | import org.slf4j.LoggerFactory; 8 | 9 | public class TestLoginProcess extends MyBasePlatformTestCase { 10 | 11 | public void testMalformedToken() { 12 | LoggerFactory.getLogger(this.getClass()).info("-------------------testMalformedToken--------------------"); 13 | DeepCodeParams.getInstance().setSessionToken("blablabla"); 14 | assertFalse( 15 | "Login with malformed Token should fail", 16 | LoginUtils.getInstance().isLogged(project, false)); 17 | } 18 | 19 | public void testNotLoggedToken() { 20 | LoggerFactory.getLogger(this.getClass()).info("-------------------testNotLoggedToken--------------------"); 21 | // need to run as a background process due to synchronized execution (??) in test environment. 22 | RunUtils.getInstance().runInBackground( 23 | project, 24 | "New Login Request", 25 | (progress) -> LoginUtils.getInstance().requestNewLogin(project, false)); 26 | assertFalse( 27 | "Login with newly requested but not yet logged token should fail", 28 | LoginUtils.getInstance().isLogged(project, false)); 29 | } 30 | 31 | public void testNotGivenConsent() { 32 | LoggerFactory.getLogger(this.getClass()).info("-------------------testNotGivenConsent--------------------"); 33 | DeepCodeParams.getInstance().setSessionToken(loggedToken); 34 | PropertiesComponent.getInstance(project).setValue("consentGiven", false); 35 | assertFalse( 36 | "Login without Consent should fail", LoginUtils.getInstance().isLogged(project, false)); 37 | } 38 | 39 | public void testLoggedTokenAndGivenConsent() { 40 | LoggerFactory.getLogger(this.getClass()).info("-------------------testLoggedTokenAndGivenConsent--------------------"); 41 | DeepCodeParams.getInstance().setSessionToken(loggedToken); 42 | DeepCodeParams.getInstance().setConsentGiven(project); 43 | assertTrue( 44 | "Login with logged Token and confirmed Consent should pass", 45 | LoginUtils.getInstance().isLogged(project, false)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/ai/deepcode/jbplugin/core/TestInnerCaches.java: -------------------------------------------------------------------------------- 1 | package ai.deepcode.jbplugin.core; 2 | 3 | import ai.deepcode.jbplugin.MyBasePlatformTestCase; 4 | import com.intellij.psi.PsiFile; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.util.Collections; 8 | import java.util.Set; 9 | 10 | public class TestInnerCaches extends MyBasePlatformTestCase { 11 | PsiFile psiTestFile; 12 | 13 | @Override 14 | protected void setUp() throws Exception { 15 | super.setUp(); 16 | LoggerFactory.getLogger(this.getClass()).info("-------------------TestInnerCaches.setUp--------------------"); 17 | 18 | DeepCodeParams.getInstance().setSessionToken(loggedToken); 19 | DeepCodeParams.getInstance().setConsentGiven(project); 20 | 21 | myFixture.configureByFile("AnnotatorTest_ValidCPP.cpp"); 22 | psiTestFile = myFixture.getFile(); 23 | 24 | RunUtils.getInstance().runInBackground( 25 | project, 26 | "Test analysis", 27 | (progress) -> 28 | AnalysisData.getInstance() 29 | .updateCachedResultsForFiles( 30 | project, 31 | Collections.singleton(psiTestFile), 32 | Collections.emptyList(), 33 | progress)); 34 | 35 | AnalysisData.getInstance().waitForUpdateAnalysisFinish(project, null); 36 | // RunUtils.delay(1000); 37 | } 38 | 39 | public void testProjectInCache() { 40 | LoggerFactory.getLogger(this.getClass()).info("-------------------testProjectInCache--------------------"); 41 | //delay to let caches update process finish 42 | PDU.getInstance().delay(2000, null); 43 | final Set allCachedProject = AnalysisData.getInstance().getAllCachedProject(); 44 | assertTrue( 45 | "Current Project should be in cache.", 46 | allCachedProject.size() == 1 && allCachedProject.contains(project)); 47 | } 48 | 49 | public void testFileInCache() { 50 | LoggerFactory.getLogger(this.getClass()).info("-------------------testFileInCache--------------------"); 51 | assertTrue("Test file is not in cache", AnalysisData.getInstance().isFileInCache(psiTestFile)); 52 | final Set filesWithSuggestions = 53 | AnalysisData.getInstance().getAllFilesWithSuggestions(project); 54 | assertFalse("List of Files with suggestions is empty", filesWithSuggestions.isEmpty()); 55 | assertTrue("Test file has no suggestions in cache", filesWithSuggestions.contains(psiTestFile)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger option 2 | log4j.rootLogger=INFO, file, stdout 3 | 4 | # Direct log messages to stdout 5 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 6 | log4j.appender.stdout.Target=System.out 7 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 8 | log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1} - %m%n 9 | 10 | # Direct log messages to a log file 11 | log4j.appender.file=org.apache.log4j.RollingFileAppender 12 | log4j.appender.file.File=jetbrains-plugin-test.log 13 | log4j.appender.file.MaxFileSize=10MB 14 | log4j.appender.file.MaxBackupIndex=10 15 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 16 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1} - %m%n 17 | -------------------------------------------------------------------------------- /src/test/testData/AnnotatorTest.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | ReffedClientGraph* to_unref = nullptr; 3 | if (to_unref != nullptr) {} 4 | } 5 | -------------------------------------------------------------------------------- /src/test/testData/AnnotatorTest.java: -------------------------------------------------------------------------------- 1 | public class AnnotatorTest { 2 | public static void delay(long millis) { 3 | try { 4 | Thread.sleep(millis); 5 | } catch (InterruptedException e) { 6 | e.printStackTrace(); 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/testData/AnnotatorTest.js: -------------------------------------------------------------------------------- 1 | // Example 1 2 | var a = "prop" in 42; // other issue 3 | 4 | // Example 2 5 | function Foo() { } 6 | var x = new Foo(); 7 | var b = x instanceof "string"; // here is the issue that behaves strange 8 | -------------------------------------------------------------------------------- /src/test/testData/AnnotatorTest_ValidCPP.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | ReffedClientGraph* to_unref = nullptr; 3 | if (to_unref != nullptr) {} 4 | } 5 | --------------------------------------------------------------------------------