├── CONTRIBUTING.md ├── gradle.properties ├── doc └── images │ ├── add-repo.gif │ ├── plugins.gif │ ├── settings.png │ ├── wildcard.gif │ ├── jetbrains.png │ ├── dependencies.gif │ ├── plugins.kts.gif │ ├── classname-query.gif │ ├── dependencies.kts.gif │ ├── smart-type-completion.png │ └── settings-maven-repositories.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── resources │ │ ├── icons │ │ │ └── repo.png │ │ └── META-INF │ │ │ └── plugin.xml │ ├── java │ │ └── cn │ │ │ └── bestwu │ │ │ └── gdph │ │ │ └── config │ │ │ ├── package-info.java │ │ │ ├── ConfigurationView.java │ │ │ └── ConfigurationView.form │ └── kotlin │ │ └── cn │ │ └── bestwu │ │ └── gdph │ │ ├── MyDynamicPluginListener.kt │ │ ├── search │ │ ├── GradleArtifactSearcher.kt │ │ ├── ArtifactInfo.kt │ │ ├── GradlePluginsSearcher.kt │ │ ├── AbstractArtifactSearcher.kt │ │ ├── MavenCentralSearcher.kt │ │ ├── AliyunSearcher.kt │ │ ├── NexusSearcher.kt │ │ ├── SearchParam.kt │ │ ├── ArtifactorySearcher.kt │ │ └── Util.kt │ │ ├── OpenMavenCentralProvider.kt │ │ ├── AbstractGDPHProvider.kt │ │ ├── config │ │ ├── Settings.kt │ │ └── GDPHConfigurable.kt │ │ ├── AbstractGradlePluginsCompletionContributor.kt │ │ ├── kotlin │ │ ├── KtsOpenMavenCentralProvider.kt │ │ ├── GradleKtsPluginsCompletionContributor.kt │ │ └── GradleKtsDependenciesCompletionContributor.kt │ │ ├── GradlePluginsCompletionContributor.kt │ │ ├── Util.kt │ │ └── GradleDependenciesCompletionContributor.kt └── test │ ├── kotlin │ └── test │ │ ├── gdph │ │ ├── completion │ │ │ ├── NoneCompletionTest.kt │ │ │ ├── PluginsCompletionTest.kt │ │ │ ├── DependenciesKtsCompletionTest.kt │ │ │ └── DependenciesCompletionTest.kt │ │ ├── SearchTest.kt │ │ └── ResultTest.kt │ │ └── CodeInsightTestBase.kt │ └── resources │ └── result.json ├── settings.gradle.kts ├── .gitignore ├── .travis.yml ├── .gradletasknamecache ├── README_CN.md ├── README.md ├── gradlew.bat ├── CODE_OF_CONDUCT.md ├── gradlew └── LICENSE /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.stdlib.default.dependency = false 2 | -------------------------------------------------------------------------------- /doc/images/add-repo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/doc/images/add-repo.gif -------------------------------------------------------------------------------- /doc/images/plugins.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/doc/images/plugins.gif -------------------------------------------------------------------------------- /doc/images/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/doc/images/settings.png -------------------------------------------------------------------------------- /doc/images/wildcard.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/doc/images/wildcard.gif -------------------------------------------------------------------------------- /doc/images/jetbrains.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/doc/images/jetbrains.png -------------------------------------------------------------------------------- /doc/images/dependencies.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/doc/images/dependencies.gif -------------------------------------------------------------------------------- /doc/images/plugins.kts.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/doc/images/plugins.kts.gif -------------------------------------------------------------------------------- /doc/images/classname-query.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/doc/images/classname-query.gif -------------------------------------------------------------------------------- /doc/images/dependencies.kts.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/doc/images/dependencies.kts.gif -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/icons/repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/src/main/resources/icons/repo.png -------------------------------------------------------------------------------- /doc/images/smart-type-completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/doc/images/smart-type-completion.png -------------------------------------------------------------------------------- /doc/images/settings-maven-repositories.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/top-bettercode/gradle-dependencies-plugins-helper-plugin/HEAD/doc/images/settings-maven-repositories.png -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenLocal() 4 | maven("https://maven.aliyun.com/repository/gradle-plugin/") 5 | gradlePluginPortal() 6 | mavenCentral() 7 | } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.5-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### wu template 2 | .gradle 3 | /build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | .intellijPlatform 6 | ### STS ### 7 | .apt_generated 8 | .classpath 9 | .factorypath 10 | .project 11 | .settings 12 | .springBeans 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | out 20 | classes 21 | 22 | ### NetBeans ### 23 | nbproject/private/ 24 | build/ 25 | nbbuild/ 26 | dist/ 27 | nbdist/ 28 | .nb-gradle/ 29 | -------------------------------------------------------------------------------- /src/main/java/cn/bestwu/gdph/config/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 | /** 18 | * 配置界面 19 | * 20 | * @author Peter Wu 21 | * @since 0.0.2 22 | */ 23 | package cn.bestwu.gdph.config; -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | 3 | cache: 4 | directories: 5 | - $HOME/.gradle/caches 6 | 7 | language: java 8 | 9 | matrix: 10 | include: 11 | - jdk: oraclejdk8 12 | env: IDEA_VERSION="2016.1" 13 | - jdk: oraclejdk8 14 | env: IDEA_VERSION="2016.2" 15 | - jdk: oraclejdk8 16 | env: IDEA_VERSION="2016.3" 17 | - jdk: oraclejdk8 18 | env: IDEA_VERSION="2017.1" 19 | - jdk: oraclejdk8 20 | env: IDEA_VERSION="2017.2" 21 | - jdk: oraclejdk8 22 | env: IDEA_VERSION="2017.3" 23 | - jdk: oraclejdk8 24 | env: IDEA_VERSION="2018.1" 25 | - jdk: oraclejdk8 26 | env: IDEA_VERSION="2018.2" 27 | - jdk: oraclejdk8 28 | env: IDEA_VERSION="2018.3" 29 | # - jdk: oraclejdk8 30 | # env: IDEA_VERSION="LATEST-EAP-SNAPSHOT" 31 | 32 | install: ./gradlew -P ideaVersion=$IDEA_VERSION assemble 33 | 34 | script: ./gradlew -P ideaVersion=$IDEA_VERSION build 35 | 36 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/MyDynamicPluginListener.kt: -------------------------------------------------------------------------------- 1 | package cn.bestwu.gdph 2 | 3 | /** 4 | * 5 | * @author Peter Wu 6 | */ 7 | import com.intellij.ide.plugins.DynamicPluginListener 8 | import com.intellij.ide.plugins.IdeaPluginDescriptor 9 | 10 | class MyDynamicPluginListener : DynamicPluginListener { 11 | 12 | override fun beforePluginLoaded(pluginDescriptor: IdeaPluginDescriptor) { 13 | // 插件加载前的操作 14 | println("Plugin is about to be loaded: ${pluginDescriptor.pluginId}") 15 | } 16 | 17 | override fun pluginLoaded(pluginDescriptor: IdeaPluginDescriptor) { 18 | // 插件加载后的操作 19 | println("Plugin loaded: ${pluginDescriptor.pluginId}") 20 | } 21 | 22 | override fun beforePluginUnload(pluginDescriptor: IdeaPluginDescriptor, isUpdate: Boolean) { 23 | // 插件卸载前的操作 24 | println("Plugin is about to be unloaded: ${pluginDescriptor.pluginId}") 25 | // 例如,取消长时间运行的任务 26 | cancelLongRunningTasks() 27 | } 28 | 29 | override fun pluginUnloaded(pluginDescriptor: IdeaPluginDescriptor, isUpdate: Boolean) { 30 | // 插件卸载后的操作 31 | println("Plugin unloaded: ${pluginDescriptor.pluginId}") 32 | // 例如,清除缓存或清理 UserDataHolder 数据 33 | clearUserData() 34 | } 35 | 36 | private fun cancelLongRunningTasks() { 37 | // 实现取消长时间运行的任务 38 | } 39 | 40 | private fun clearUserData() { 41 | // 实现清理 UserDataHolder 数据 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /.gradletasknamecache: -------------------------------------------------------------------------------- 1 | assemble 2 | build 3 | buildDependents 4 | buildNeeded 5 | classes 6 | clean 7 | jar 8 | testClasses 9 | init 10 | wrapper 11 | javadoc 12 | buildEnvironment 13 | components 14 | dependencies 15 | dependencyInsight 16 | dependentComponents 17 | help 18 | kotlinDslAccessorsReport 19 | model 20 | projects 21 | properties 22 | tasks 23 | buildPlugin 24 | patchPluginXml 25 | prepareSandbox 26 | prepareTestingSandbox 27 | publishPlugin 28 | runIde 29 | verifyPlugin 30 | check 31 | test 32 | compileJava 33 | compileKotlin 34 | compileTestJava 35 | compileTestKotlin 36 | inspectClassesForKotlinIC 37 | instrumentCode 38 | instrumentTestCode 39 | kotlinSourcesJar 40 | mainClasses 41 | postInstrumentCode 42 | postInstrumentTestCode 43 | prepareKotlinBuildScriptModel 44 | processResources 45 | processTestResources 46 | Pattern: 47 | Pattern: 48 | Pattern: 49 | assemble 50 | build 51 | buildDependents 52 | buildNeeded 53 | classes 54 | clean 55 | jar 56 | testClasses 57 | init 58 | wrapper 59 | javadoc 60 | buildEnvironment 61 | components 62 | dependencies 63 | dependencyInsight 64 | dependentComponents 65 | help 66 | kotlinDslAccessorsReport 67 | model 68 | projects 69 | properties 70 | tasks 71 | buildPlugin 72 | patchPluginXml 73 | prepareSandbox 74 | prepareTestingSandbox 75 | publishPlugin 76 | runIde 77 | verifyPlugin 78 | check 79 | test 80 | compileJava 81 | compileKotlin 82 | compileTestJava 83 | compileTestKotlin 84 | inspectClassesForKotlinIC 85 | instrumentCode 86 | instrumentTestCode 87 | kotlinSourcesJar 88 | mainClasses 89 | postInstrumentCode 90 | postInstrumentTestCode 91 | prepareKotlinBuildScriptModel 92 | processResources 93 | processTestResources 94 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # GradleDependenciesAndPluginsHelper 2 | 3 | Gradle plugins/dependencies 坐标自动补全IntelliJ IDEA插件 [插件地址](https://plugins.jetbrains.com/plugin/10033-gradle-dependencies-and-plugins-helper) 4 | 5 | 6 | ### 功能 7 | 8 | * 使用IntelliJ IDEA Smart Type Completion功能自动补全Gradle脚本中dependencies及plugins的依赖库坐标 9 | 10 | ![](doc/images/plugins.gif) 11 | 12 | ![](doc/images/dependencies.gif) 13 | 14 | * 支持Gradle groovy脚本(*.build)及kotlin脚本(*.build.kts) 15 | 16 | ![](doc/images/plugins.kts.gif) 17 | 18 | ![](doc/images/dependencies.kts.gif) 19 | 20 | 21 | * 默认使用[mavenCentral](https://search.maven.org)搜索dependencies,使用[Gradle Plugins](https://plugins.gradle.org)搜索plugins 22 | 23 | * 支持通配符* 24 | 25 | ![](doc/images/wildcard.gif) 26 | 27 | * 支持通过类名搜索依赖库坐标 28 | 29 | 在dependencies区域使用 "c:"(classname) 或 "fc:"( fully-qualified classname )搜索依赖库坐标 30 | 31 | 例子: 32 | 33 | compile("fc:org.junit.Test") 34 | 35 | compile("c:Junit") 36 | 37 | ![](doc/images/classname-query.gif) 38 | 39 | * 可选添加特定maven仓库到repositories.Use `Show Intention Actions` action (`Alt + Enter` or ⌥⏎) and choose `Add specified repository to repositories.` 40 | 41 | ![](doc/images/add-repo.gif) 42 | 43 | ### 设置 44 | 45 | * 使用前确定Smart Type Completion功能开启 46 | 47 | ![](doc/images/smart-type-completion.png) 48 | 49 | * 可选使用 `Use Nexus2 Repository Search`(Nexus2),`Use Artifactory Repository Search` 搜索 50 | 51 | ![](doc/images/settings.png) 52 | 53 | ## License 54 | 55 | Apache License Version 2.0 56 | 57 | ## IDE Support 58 | 59 | Thanks to JetBrains for offering IDE support to develop this Open Source project. 60 | 61 | [![JetBrains](doc/images/jetbrains.png)](https://jb.gg/OpenSource) 62 | -------------------------------------------------------------------------------- /src/test/kotlin/test/gdph/completion/NoneCompletionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 test.gdph.completion 18 | 19 | import test.CodeInsightTestBase 20 | 21 | /** 22 | * 23 | * @author Peter Wu 24 | * @since 25 | */ 26 | class NoneCompletionTest : CodeInsightTestBase() { 27 | 28 | //build.gradle dependencies 29 | fun testEndNone() { 30 | completionCheckResult(gradleKtsFileName, """plugins { 31 | kotlin("jvm$caret""", 32 | """plugins { 33 | kotlin("jvm""" 34 | ) 35 | } 36 | 37 | fun testEndNone1() { 38 | completionCheckResult(gradleFileName, """plugins { 39 | kotlin("jvm") version "1.1.50" 40 | } 41 | 42 | repo$caret """, 43 | """plugins { 44 | kotlin("jvm") version "1.1.50" 45 | } 46 | 47 | repo """ 48 | ) 49 | } 50 | 51 | fun testEndNone2() { 52 | completionCheckResult(gradleFileName, """plugins { 53 | kotlin("jvm") version "1.1.50" 54 | } 55 | 56 | repo$caret""", 57 | """plugins { 58 | kotlin("jvm") version "1.1.50" 59 | } 60 | 61 | repo""" 62 | ) 63 | } 64 | 65 | //build.gradle dependencies 66 | fun testNone() { 67 | completionCheckResult(gradleFileName, caret, "") 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GradleDependenciesAndPluginsHelper 2 | [![Version](http://phpstorm.espend.de/badge/10033/version)](https://plugins.jetbrains.com/plugin/10033-gradle-dependencies-and-plugins-helper) 3 | [![Downloads](http://phpstorm.espend.de/badge/10033/downloads)](https://plugins.jetbrains.com/plugin/10033-gradle-dependencies-and-plugins-helper) 4 | [![License](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) 5 | 6 | [中文说明](README_CN.md) 7 | 8 | This is an IntelliJ IDEA plugin for searching dependencies/plugins from [mavenCentral](https://search.maven.org)/[GradlePlugins](https://plugins.gradle.org/) inside Gradle projects. 9 | 10 | Inspired by [https://github.com/siosio/GradleDependenciesHelperPlugin](https://github.com/siosio/GradleDependenciesHelperPlugin). 11 | 12 | ## Features 13 | 14 | * Use `Smart Type Completion` in dependencies/plugins script block. 15 | 16 | ![](doc/images/plugins.gif) 17 | 18 | ![](doc/images/dependencies.gif) 19 | 20 | * Support *.gradle,*.gradle.kts. 21 | 22 | ![](doc/images/plugins.kts.gif) 23 | 24 | ![](doc/images/dependencies.kts.gif) 25 | 26 | * Use [mavenCentral](https://search.maven.org) for Gradle dependencies queries, Use [Gradle Plugins](https://plugins.gradle.org) for Gradle plugins queries. 27 | * Support wildcard query * . 28 | 29 | ![](doc/images/wildcard.gif) 30 | 31 | * Support search by classname in mavenCentral search. 32 | 33 | use "c:"(classname) or "fc:"( fully-qualified classname ) in dependencies script block. 34 | 35 | example: 36 | 37 | compile("fc:org.junit.Test") 38 | 39 | compile("c:Junit") 40 | 41 | 42 | ![](doc/images/classname-query.gif) 43 | 44 | * Add specified repository to repositories.Use `Show Intention Actions` action (`Alt + Enter` or ⌥⏎) and choose `Add specified repository to repositories.` 45 | 46 | ![](doc/images/add-repo.gif) 47 | 48 | ## Settings 49 | 50 | * Make sure `Smart Type Completion` is on. 51 | 52 | ![](doc/images/smart-type-completion.png) 53 | 54 | * Support `Use Nexus2 Repository Search`(Nexus2),`Use Artifactory Repository Search` options. 55 | 56 | ![](doc/images/settings.png) 57 | 58 | ## License 59 | 60 | Apache License Version 2.0 61 | 62 | ## IDE Support 63 | 64 | Thanks to JetBrains for offering IDE support to develop this Open Source project. 65 | 66 | [![JetBrains](doc/images/jetbrains.png)](https://jb.gg/OpenSource) -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/search/GradleArtifactSearcher.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.search 18 | 19 | import cn.bestwu.gdph.config.Settings 20 | import com.intellij.openapi.project.Project 21 | 22 | object GradleArtifactSearcher { 23 | 24 | fun searchByClassName(searchParam: ClassNameSearchParam, project: Project): Collection { 25 | if (searchParam.src.isBlank()) { 26 | return emptyList() 27 | } 28 | val settings = Settings.getInstance() 29 | return when { 30 | settings.useNexus -> NexusSearcher.searchByClassName(searchParam, project) 31 | settings.useArtifactory -> ArtifactorySearcher.searchByClassName(searchParam, project) 32 | else -> MavenCentralSearcher.searchByClassName(searchParam, project) 33 | } 34 | } 35 | 36 | fun search(searchParam: SearchParam, project: Project): Collection { 37 | if (searchParam.src.isBlank()) { 38 | return emptyList() 39 | } 40 | val settings = Settings.getInstance() 41 | val result: Collection = when { 42 | settings.useAli -> AliyunSearcher.search(searchParam, project) 43 | settings.useNexus -> NexusSearcher.search(searchParam, project) 44 | settings.useArtifactory -> ArtifactorySearcher.search(searchParam, project) 45 | else -> MavenCentralSearcher.search(searchParam, project) 46 | } 47 | return if (searchParam.fg && searchParam.fa) { 48 | result 49 | } else { 50 | val newResult = result.distinctBy { it.id } 51 | return if (newResult.size > 1) 52 | newResult 53 | else 54 | result 55 | } 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/OpenMavenCentralProvider.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph 18 | 19 | import com.intellij.psi.PsiElement 20 | import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrCommandArgumentListImpl 21 | import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.literals.GrLiteralImpl 22 | import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.literals.GrStringContentImpl 23 | 24 | class OpenMavenCentralProvider : AbstractGDPHProvider() { 25 | 26 | override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? { 27 | if (element is GrLiteralImpl || element is GrStringContentImpl) { 28 | return dependenciesDoc(element) ?: pluginsDoc(element) 29 | } 30 | return null 31 | } 32 | 33 | private fun pluginsDoc(element: PsiElement): String? { 34 | var e = element 35 | do { 36 | e = e.parent ?: return null 37 | } while ("plugins" != e.firstChild.text) 38 | var parent = element.parent 39 | var searchText = trim(element.text) 40 | 41 | if (parent is GrCommandArgumentListImpl) { 42 | parent = parent.parent 43 | } 44 | val parentText = parent.text 45 | if (parent != null) { 46 | if (parentText.contains("version")) { 47 | searchText = parentText.replace(AbstractGradlePluginsCompletionContributor.versionRegex, "$1").trim() 48 | } 49 | } 50 | return pluginsDoc(searchText) 51 | } 52 | 53 | private fun dependenciesDoc(element: PsiElement): String? { 54 | var e = element 55 | do { 56 | e = e.parent ?: return null 57 | } while ("dependencies" != e.firstChild.text && "imports" != e.firstChild.text) 58 | return dependenciesDoc(trim(element.text)) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/search/ArtifactInfo.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.search 18 | 19 | /** 20 | * 21 | * @author Peter Wu 22 | * @since 23 | */ 24 | class ArtifactInfo(groupId: String, artifactId: String, version: String, repoType: String, repo: String, val isSpecifiedRepo: Boolean = false, val className: String = "") : 25 | Comparable { 26 | val groupId: String = groupId.trim() 27 | val artifactId: String = artifactId.trim() 28 | var version: String = version.trim() 29 | set(value) { 30 | field = value 31 | this.gav = "${this.id}${if (this.version.isBlank()) "" else ":${this.version}"}" 32 | } 33 | val repo: String = repo.trim() 34 | val repoType = repoType.trim() 35 | val id: String = "${this.groupId}${if (this.artifactId.isBlank()) "" else ":${this.artifactId}"}" 36 | var gav: String 37 | 38 | override fun equals(other: Any?): Boolean { 39 | if (this === other) return true 40 | if (other !is ArtifactInfo) return false 41 | 42 | if (gav != other.gav) return false 43 | 44 | return true 45 | } 46 | 47 | override fun hashCode(): Int { 48 | return gav.hashCode() 49 | } 50 | 51 | override fun toString(): String { 52 | return "ArtifactInfo(groupId='$groupId', artifactId='$artifactId', version='$version', repo='$repo', repoType='$repoType', id='$id', gav='$gav')" 53 | } 54 | 55 | init { 56 | this.gav = "${this.id}${if (this.version.isBlank()) "" else ":${this.version}"}" 57 | } 58 | 59 | override fun compareTo(other: ArtifactInfo): Int { 60 | val g = this.groupId.compareTo(other.groupId) 61 | return if (g == 0) { 62 | val a = this.artifactId.compareTo(other.artifactId) 63 | if (a == 0) 64 | compareVersion(other.version, this.version) 65 | else 66 | a 67 | } else g 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/search/GradlePluginsSearcher.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.search 18 | 19 | import org.jsoup.Jsoup 20 | 21 | object GradlePluginsSearcher { 22 | private val pluginsCaches = HashMap>() 23 | private val pluginVersionsCaches = HashMap>() 24 | const val SPLIT_RULE = "#" 25 | 26 | fun searchPlugins(text: String, page: Int = 0): List { 27 | var result = pluginsCaches[text] 28 | if (result != null) { 29 | return result 30 | } 31 | val connect = 32 | Jsoup.connect("https://plugins.gradle.org/search?term=${text.trim()}${if (page > 0) "&page=$page" else ""}") 33 | connect.timeout(5000) 34 | val elements = connect.get().select("#search-results tbody tr") 35 | result = elements.mapNotNull { 36 | val pluginId = it.select(".plugin-id a").text() 37 | if (pluginId.isBlank()) { 38 | return@mapNotNull null 39 | } 40 | pluginId + SPLIT_RULE + it.select(".latest-version").text() 41 | } 42 | if (result.isNotEmpty()) { 43 | pluginsCaches[text] = result 44 | } 45 | return result 46 | } 47 | 48 | fun searchPluginVersions(text: String): List { 49 | var result = pluginVersionsCaches[text] 50 | result = if (result == null) { 51 | ArrayList() 52 | } else { 53 | return result 54 | } 55 | val connect = Jsoup.connect("https://plugins.gradle.org/plugin/${text.trim()}") 56 | connect.timeout(5000) 57 | val plugin = connect.get() 58 | result.add( 59 | plugin.select(".version-info h3").text() 60 | .replace("^Version (.*) \\(latest\\)$".toRegex(), "$1") 61 | ) 62 | val elements = plugin.select(".other-versions li") 63 | elements.mapTo(result) { it.select("a").text() } 64 | if (result.isNotEmpty()) { 65 | pluginVersionsCaches[text] = result 66 | } 67 | return result 68 | } 69 | 70 | } 71 | 72 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/AbstractGDPHProvider.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph 18 | 19 | import com.intellij.lang.documentation.DocumentationProvider 20 | import com.intellij.psi.PsiElement 21 | import com.intellij.psi.PsiManager 22 | 23 | abstract class AbstractGDPHProvider : DocumentationProvider { 24 | override fun getUrlFor(element: PsiElement?, originalElement: PsiElement?): List? { 25 | return null 26 | } 27 | 28 | override fun getQuickNavigateInfo(element: PsiElement?, originalElement: PsiElement?): String? { 29 | return null 30 | } 31 | 32 | override fun getDocumentationElementForLookupItem( 33 | psiManager: PsiManager?, 34 | `object`: Any?, 35 | element: PsiElement? 36 | ): PsiElement? { 37 | return null 38 | } 39 | 40 | override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? { 41 | return null 42 | } 43 | 44 | override fun getDocumentationElementForLink( 45 | psiManager: PsiManager?, 46 | link: String?, 47 | context: PsiElement? 48 | ): PsiElement? { 49 | return null 50 | } 51 | 52 | 53 | protected fun pluginsDoc(searchText: String): String { 54 | return "search in GradlePlugins
" + 55 | "show in GradlePlugins
" 56 | } 57 | 58 | protected fun dependenciesDoc(dependency: String): String { 59 | val mavenUrl = split(dependency).let { 60 | if (it.size >= 2) { 61 | when { 62 | "c" == it[0] -> "https://search.maven.org/#search|gav|1|c:$quot${it[1]}$quot" 63 | "fc" == it[0] -> "https://search.maven.org/#search|gav|1|fc:$quot${it[1]}$quot" 64 | else -> "https://search.maven.org/#search|gav|1|g:$quot${it[0]}$quot AND a:$quot${it[1]}$quot" 65 | } 66 | } else { 67 | "https://search.maven.org/#search|gav|1|g:$quot${it[0]}$quot" 68 | } 69 | } 70 | return "search in mavenCentral
" 71 | } 72 | 73 | 74 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/search/AbstractArtifactSearcher.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.search 18 | 19 | import com.intellij.openapi.project.Project 20 | import java.util.* 21 | 22 | /** 23 | * 24 | * @author Peter Wu 25 | * @since 26 | */ 27 | abstract class AbstractArtifactSearcher { 28 | companion object { 29 | internal val artifactsCaches = HashMap>() 30 | } 31 | 32 | abstract val cache: Boolean 33 | abstract val key: String 34 | protected abstract fun doSearch(searchParam: SearchParam, project: Project): Collection 35 | protected abstract fun doSearchByClassName(searchParam: ClassNameSearchParam, project: Project): Collection 36 | 37 | protected open fun handleEmptyResult(searchParam: SearchParam, project: Project): Collection { 38 | return emptySet() 39 | } 40 | 41 | protected open fun handleEmptyResultByClassName(searchParam: ClassNameSearchParam, project: Project): Collection { 42 | return emptySet() 43 | } 44 | 45 | fun search(searchParam: SearchParam, project: Project): Collection { 46 | val cacheKey = "$key$searchParam" 47 | if (cache) { 48 | val existResult = artifactsCaches[cacheKey] 49 | if (existResult != null) { 50 | return existResult 51 | } 52 | } 53 | val result = doSearch(searchParam, project) 54 | return if (result.isEmpty()) { 55 | handleEmptyResult(searchParam, project) 56 | } else { 57 | if (cache) 58 | artifactsCaches[cacheKey] = result 59 | result 60 | } 61 | } 62 | 63 | fun searchByClassName(searchParam: ClassNameSearchParam, project: Project): Collection { 64 | val cacheKey = "$key$searchParam" 65 | val existResult = artifactsCaches[cacheKey] 66 | if (existResult != null) { 67 | return existResult 68 | } 69 | val result = doSearchByClassName(searchParam, project) 70 | return if (result.isEmpty()) { 71 | handleEmptyResultByClassName(searchParam, project) 72 | } else { 73 | artifactsCaches[cacheKey] = result 74 | result 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/config/Settings.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.config 18 | 19 | import com.intellij.openapi.Disposable 20 | import com.intellij.openapi.application.ApplicationManager 21 | import com.intellij.openapi.components.PersistentStateComponent 22 | import com.intellij.openapi.components.Storage 23 | 24 | @com.intellij.openapi.components.State( 25 | name = "GDPHSettings", 26 | storages = [(Storage("gdph.settings.xml"))] 27 | ) 28 | class Settings( 29 | var useAli: Boolean = Settings.useAli, 30 | var aliRepo: String = Settings.aliRepo, 31 | var useNexus: Boolean = Settings.useNexus, 32 | var nexusSearchUrl: String = Settings.nexusSearchUrl, 33 | var useArtifactory: Boolean = Settings.useArtifactory, 34 | var artifactoryUrl: String = Settings.artifactoryUrl, 35 | var artiRepos: String = Settings.artiRepos, 36 | var artifactoryUsername: String = Settings.artifactoryUsername, 37 | var artifactoryPassword: String = Settings.artifactoryPassword 38 | ) : PersistentStateComponent, Disposable { 39 | 40 | override fun loadState(state: Settings) { 41 | this.useAli = state.useAli 42 | this.aliRepo = state.aliRepo 43 | this.useNexus = state.useNexus 44 | this.nexusSearchUrl = state.nexusSearchUrl 45 | this.useArtifactory = state.useArtifactory 46 | this.artiRepos = state.artiRepos 47 | this.artifactoryUsername = state.artifactoryUsername 48 | this.artifactoryPassword = state.artifactoryPassword 49 | this.artifactoryUrl = state.artifactoryUrl 50 | } 51 | 52 | override fun getState(): Settings { 53 | return this 54 | } 55 | 56 | override fun dispose() { 57 | 58 | } 59 | 60 | companion object { 61 | const val useAli: Boolean = false 62 | const val aliRepo: String = "central" 63 | const val useNexus: Boolean = false 64 | const val nexusSearchUrl: String = "https://oss.sonatype.org" 65 | const val useArtifactory: Boolean = false 66 | const val artifactoryUrl: String = "https://oss.jfrog.org" 67 | const val artiRepos: String = "" 68 | const val artifactoryUsername: String = "" 69 | const val artifactoryPassword: String = "" 70 | 71 | fun getInstance(): Settings { 72 | return ApplicationManager.getApplication().getService(Settings::class.java) 73 | } 74 | } 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/test/kotlin/test/gdph/completion/PluginsCompletionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 test.gdph.completion 18 | 19 | import cn.bestwu.gdph.search.GradlePluginsSearcher 20 | import test.CodeInsightTestBase 21 | 22 | 23 | /** 24 | * 25 | * @author Peter Wu 26 | * @since 27 | */ 28 | class PluginsCompletionTest : CodeInsightTestBase() { 29 | 30 | companion object { 31 | 32 | private val pluginsAfter: (String) -> String = { 33 | """plugins { 34 | id 'org.jetbrains.intellij' version '$it' 35 | }""" 36 | } 37 | private val pluginsKtsAfter: (String) -> String = { 38 | """plugins { 39 | id("org.jetbrains.intellij") version "$it" 40 | }""" 41 | } 42 | private val pluginsKtsKotlinAfter: (String) -> String = { 43 | """plugins { 44 | kotlin("jvm") version "$it" 45 | }""" 46 | } 47 | } 48 | 49 | // gradle.build plugins 50 | fun testStdPluginId() { 51 | completionCheckResult(gradleFileName, """plugins { 52 | id 'intellij$caret' 53 | }""", pluginsAfter, "org.jetbrains.intellij${GradlePluginsSearcher.SPLIT_RULE}") 54 | } 55 | 56 | fun testStdPluginVersion() { 57 | completionCheckResult(gradleFileName, """plugins { 58 | id 'org.jetbrains.intellij' version '$caret' 59 | }""", pluginsAfter, "") 60 | } 61 | 62 | //build.gradle.kts plugins 63 | fun testKtsPluginId() { 64 | completionCheckResult(gradleKtsFileName, """plugins { 65 | id("intellij$caret") 66 | }""", pluginsKtsAfter, "org.jetbrains.intellij${GradlePluginsSearcher.SPLIT_RULE}") 67 | } 68 | 69 | fun testKtsPluginVersion() { 70 | completionCheckResult(gradleKtsFileName, """plugins { 71 | id("org.jetbrains.intellij") version "$caret" 72 | }""", pluginsKtsAfter, "") 73 | } 74 | 75 | //build.gradle.kts plugins kotlin("") 76 | fun testKtsKotlinPluginId() { 77 | completionCheckResult(gradleKtsFileName, """plugins { 78 | kotlin("$caret") 79 | }""", pluginsKtsKotlinAfter, "jvm${GradlePluginsSearcher.SPLIT_RULE}") 80 | } 81 | 82 | fun testKtsKotlinPluginVersion() { 83 | completionCheckResult(gradleKtsFileName, """plugins { 84 | kotlin("jvm") version "$caret" 85 | }""", pluginsKtsKotlinAfter, "") 86 | } 87 | 88 | fun testKtsKotlinPluginVersion2() { 89 | completionCheckResult(gradleKtsFileName, """plugins { 90 | kotlin("jvm","$caret") 91 | }""", { 92 | """plugins { 93 | kotlin("jvm","$it") 94 | }""" 95 | }, "") 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /src/test/kotlin/test/gdph/completion/DependenciesKtsCompletionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 test.gdph.completion 18 | 19 | import test.CodeInsightTestBase 20 | 21 | /** 22 | * 23 | * @author Peter Wu 24 | * @since 25 | */ 26 | class DependenciesKtsCompletionTest : CodeInsightTestBase() { 27 | 28 | //build.gradle.kts dependencies 29 | fun testKtsDependencies() { 30 | completionCheckResult(gradleKtsFileName, """dependencies{ 31 | compile("kotlin-reflect$caret") 32 | }""", {"""dependencies{ 33 | compile("org.jetbrains.kotlin:kotlin-reflect") 34 | }"""}, "org.jetbrains.kotlin:kotlin-reflect") 35 | } 36 | 37 | fun testKtsDependenciesVersion() { 38 | completionCheckResult(gradleKtsFileName, """dependencies{ 39 | compile("org.jetbrains.kotlin:kotlin-reflect$caret") 40 | }""", {"""dependencies{ 41 | compile("org.jetbrains.kotlin:kotlin-reflect:$it") 42 | }"""}, "org.jetbrains.kotlin:kotlin-reflect:") 43 | } 44 | 45 | //build.gradle.kts dependencies kotlin() 46 | fun testKtsDependenciesKotlin() { 47 | completionCheckResult(gradleKtsFileName, """dependencies{ 48 | compile(kotlin("stdlib-$caret")) 49 | }""", {"""dependencies{ 50 | compile(kotlin("stdlib-jre8")) 51 | }"""}, "stdlib-jre8") 52 | } 53 | 54 | fun testKtsDependenciesKotlinVersion() { 55 | completionCheckResult(gradleKtsFileName, """dependencies{ 56 | compile(kotlin("stdlib-jre8","$caret")) 57 | }""", {"""dependencies{ 58 | compile(kotlin("stdlib-jre8","$it")) 59 | }"""}, "") 60 | } 61 | 62 | //build.gradle.kts dependencies * 63 | fun testKtsDependenciesWildcard() { 64 | completionCheckResult(gradleKtsFileName, """dependencies{ 65 | testCompile("kotlin*junit$caret") 66 | }""", {"""dependencies{ 67 | testCompile("org.jetbrains.kotlin:kotlin-test-junit") 68 | }"""}, "org.jetbrains.kotlin:kotlin-test-junit") 69 | } 70 | 71 | //build.gradle.kts dependencies by className 72 | fun testDependenciesByFullyQualifiedClassName() { 73 | completionCheckResult(gradleFileName, """dependencies{ 74 | compile("fc:feign.Client$caret") 75 | }""", {"""dependencies{ 76 | compile("com.netflix.feign:feign-core:$it") 77 | }"""}, "com.netflix.feign:feign-core:") 78 | } 79 | fun testDependenciesByClassName() { 80 | completionCheckResult(gradleFileName, """dependencies{ 81 | compile("c:feign$caret") 82 | }""", {"""dependencies{ 83 | compile("com.netflix.feign:feign-core:$it") 84 | }"""}, "com.netflix.feign:feign-core:") 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /src/test/kotlin/test/gdph/SearchTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 | @file:Suppress("UNCHECKED_CAST") 18 | 19 | package test.gdph 20 | 21 | import cn.bestwu.gdph.quot 22 | import cn.bestwu.gdph.search.* 23 | import groovy.json.JsonSlurper 24 | import org.junit.Test 25 | import java.util.* 26 | 27 | /** 28 | * 29 | * @author Peter Wu 30 | * @since 31 | */ 32 | class NexusSearcherTest { 33 | 34 | @Test 35 | fun doSearchByClassName() { 36 | // val url = "https://oss.sonatype.org/service/local/lucene/search?cn=com.sun.jna.examples.win32.W32API.HWND" 37 | val url = "https://oss.sonatype.org/service/local/lucene/search?cn=org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" 38 | val connection = getConnection(url) 39 | connection.setRequestProperty("Accept", "application/json") 40 | val stream = connection.inputStream 41 | val jsonResult = (JsonSlurper().parse(stream) as Map<*, *>)["data"] as List> 42 | jsonResult.forEach { 43 | val repo = if (it["latestReleaseRepositoryId"] != null) { 44 | it["latestReleaseRepositoryId"] 45 | } else { 46 | it["latestSnapshotRepositoryId"] 47 | } 48 | println(it.keys) 49 | println(it["groupId"] as String + ":" + it["artifactId"] as String + ":" + (if (it["latestRelease"] == null) it["version"] else it["latestRelease"]) as String + ":" + repo as String) 50 | } 51 | } 52 | } 53 | 54 | class MavenCentralSearcherTest { 55 | 56 | @Test 57 | fun doSearchByClassName() { 58 | val result: LinkedHashSet = LinkedHashSet() 59 | val url = "https://search.maven.org/solrsearch/select?q=fc:${quot}org.springframework.data.domain.Page$quot&core=gav&rows=1000&wt=json" 60 | System.err.println(url) 61 | val connection = getConnection(url) 62 | val stream = connection.inputStream 63 | val text = stream.bufferedReader().readText() 64 | System.err.println(text) 65 | regex.findAll(text).forEach { 66 | val artifactInfo = MavenCentralSearcher.artifactInfo(it.groupValues[1], it.groupValues[2], it.groupValues[3]) 67 | val exist = result.find { r -> r.id == artifactInfo.id } 68 | if (exist != null) { 69 | if (compareVersion(exist.version, artifactInfo.version) < 0) { 70 | exist.version = artifactInfo.version 71 | } 72 | } else { 73 | result.add(artifactInfo) 74 | } 75 | } 76 | println(result) 77 | } 78 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/AbstractGradlePluginsCompletionContributor.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph 18 | 19 | import cn.bestwu.gdph.search.GradlePluginsSearcher 20 | import com.intellij.codeInsight.completion.CompletionContributor 21 | import com.intellij.codeInsight.completion.CompletionInitializationContext 22 | import com.intellij.codeInsight.completion.CompletionSorter 23 | import com.intellij.codeInsight.lookup.LookupElement 24 | import com.intellij.codeInsight.lookup.LookupElementWeigher 25 | 26 | abstract class AbstractGradlePluginsCompletionContributor : CompletionContributor() { 27 | companion object { 28 | const val PLUGINS_EXTENSION = "plugins" 29 | val versionRegex = "^id *\\(? *[\"'](.*)[\"'] *\\)? *version.*$".toRegex() 30 | val idRegex = "^id *\\(? *[\"'](.*)[\"'] *\\)?.*$".toRegex() 31 | 32 | fun completionSorter(searchResult: List): CompletionSorter { 33 | return CompletionSorter.emptySorter() 34 | .weigh(object : LookupElementWeigher("gradleDependencyWeigher") { 35 | override fun weigh(element: LookupElement): Comparable<*> { 36 | return VersionComparator(searchResult.indexOf(element.lookupString)) 37 | } 38 | }) 39 | } 40 | 41 | fun searchResultFix( 42 | searchResult: List, 43 | allText: String, 44 | page: Int = 0 45 | ): List { 46 | if (searchResult.isNotEmpty()) { 47 | return searchResultFix1(searchResult, allText, page).ifEmpty { searchResult } 48 | } 49 | return searchResult 50 | } 51 | 52 | private fun searchResultFix1( 53 | searchResult: List, 54 | allText: String, 55 | page: Int = 0 56 | ): List { 57 | if (searchResult.isNotEmpty()) { 58 | val result = searchResult.filter { it.startsWith(allText) } 59 | return result.ifEmpty { 60 | searchResultFix1( 61 | GradlePluginsSearcher.searchPlugins( 62 | allText.substringBeforeLast( 63 | "." 64 | ), page + 1 65 | ), allText, page + 1 66 | ) 67 | } 68 | } 69 | return searchResult 70 | } 71 | } 72 | 73 | 74 | override fun duringCompletion(context: CompletionInitializationContext) = 75 | contributorDuringCompletion(context) 76 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.intellij.modules.platform 3 | com.intellij.modules.lang 4 | com.intellij.modules.java 5 | org.intellij.groovy 6 | com.intellij.gradle 7 | org.jetbrains.kotlin 8 | 9 |
12 | Features 13 |
    14 |
  • Use Smart Type Completion in dependencies/plugins script block.
  • 15 |
  • Support *.gradle,*.gradle.kts.
  • 16 |
  • Use jcenter API for Gradle dependencies queries,Use Gradle Plugins Search for Gradle 17 | plugins queries. 18 |
  • 19 |
  • Support wildcard query *.
  • 20 |
  • Support search by classname in mavenCentral search.
    21 | use "c:"(classname) or "fc:"( fully-qualified classname ) in dependencies script block.
    22 | example:
    23 |   compile("fc:org.junit.Test")
    24 |   compile("c:Junit") 25 |
  • 26 |
  • `Use Maven Central Repository Search`,`Use Nexus2 Repository Search`(Nexus2),`Use 27 | Artifactory Repository Search` options. 28 |
  • 29 |
  • Add specified repository to repositories.
  • 30 |
31 | ]]>
32 | 33 | 34 | 35 | 36 | 37 | 42 | 45 | 48 | 49 | 52 | 55 | 56 | 59 | 60 | 62 | 63 | 66 | 68 | 69 | 70 | 71 | 74 | 75 | 76 |
77 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/search/MavenCentralSearcher.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.search 18 | 19 | import cn.bestwu.gdph.addArtifactInfo 20 | import cn.bestwu.gdph.quot 21 | import com.intellij.openapi.project.Project 22 | import java.util.* 23 | 24 | /** 25 | * 26 | * @author Peter Wu 27 | * @since 28 | */ 29 | object MavenCentralSearcher : AbstractArtifactSearcher() { 30 | 31 | override val cache: Boolean 32 | get() = true 33 | override val key: String 34 | get() = "maven:" 35 | 36 | fun artifactInfo(groupId: String, artifactId: String, version: String = "", className: String = ""): ArtifactInfo = ArtifactInfo(groupId, artifactId, version, "mavenCentral", "mavenCentral()", false, className) 37 | 38 | override fun doSearch(searchParam: SearchParam, project: Project): Collection { 39 | val url = "https://search.maven.org/solrsearch/select?q=${searchParam.toMq()}&rows=50&core=gav&wt=json" 40 | val connection = getConnection(url) 41 | val text = getResponseText(connection, project) ?: return emptySet() 42 | val result = TreeSet() 43 | regex.findAll(text).forEach { 44 | val groupId = it.groupValues[1] 45 | val artifactId = it.groupValues[2] 46 | val version = it.groupValues[3] 47 | val artifactInfo = artifactInfo(groupId = groupId, artifactId = if (searchParam.groupId.isNotBlank() && searchParam.artifactId.isBlank() && !searchParam.fg && groupId != searchParam.groupId) "" else artifactId) 48 | if (artifactInfo.id == searchParam.toId()) { 49 | artifactInfo.version = version 50 | } 51 | result.add(artifactInfo) 52 | } 53 | return result 54 | } 55 | 56 | override fun doSearchByClassName(searchParam: ClassNameSearchParam, project: Project): Collection { 57 | val url = "https://search.maven.org/solrsearch/select?q=${searchParam.k}:$quot${searchParam.q}$quot&core=gav&rows=1000&wt=json" 58 | val connection = getConnection(url) 59 | val text = getResponseText(connection, project) ?: return emptySet() 60 | val result = TreeSet() 61 | regex.findAll(text).forEach { 62 | val groupId = it.groupValues[1] 63 | val artifactId = it.groupValues[2] 64 | val version = it.groupValues[3] 65 | val artifactInfo = artifactInfo(groupId = groupId, artifactId = artifactId, version = version, className = "") 66 | result.addArtifactInfo(artifactInfo) 67 | } 68 | return result 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at piterwu@outlook.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/search/AliyunSearcher.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.search 18 | 19 | import cn.bestwu.gdph.addArtifactInfo 20 | import cn.bestwu.gdph.config.Settings 21 | import com.intellij.openapi.project.Project 22 | import java.util.* 23 | 24 | @Suppress("UNCHECKED_CAST") 25 | /** 26 | * 27 | * @author Peter Wu 28 | * @since 29 | */ 30 | object AliyunSearcher : AbstractArtifactSearcher() { 31 | 32 | override val cache: Boolean 33 | get() = true 34 | override val key: String 35 | get() = "aliyun.${Settings.getInstance().aliRepo}:" 36 | 37 | override fun doSearch(searchParam: SearchParam, project: Project): Collection { 38 | val url = "https://maven.aliyun.com${searchParam.toAliQ()}&repoId=${Settings.getInstance().aliRepo}" 39 | val connection = getConnection(url) 40 | var jsonResult = getResponseJson(connection, project) ?: return emptySet() 41 | val result = TreeSet() 42 | jsonResult = ((jsonResult as Map<*, *>)["object"] as? List>) ?: return emptySet() 43 | jsonResult.forEach { 44 | val groupId = it["groupId"] as String 45 | val onlyGroup = searchParam.artifactId.isBlank() && !searchParam.fg && searchParam.groupId.isNotBlank() 46 | val artifactId = if (onlyGroup) "" else it["artifactId"] as String 47 | val version = if (onlyGroup) "" else it["version"] as String 48 | val repositoryId = it["repositoryId"] as String 49 | val artifactInfo = ArtifactInfo(groupId, artifactId, version, "$repositoryId by aliyun", "", false, "") 50 | val id = artifactInfo.id + version 51 | if (!artifactInfo.groupId.startsWith(".") && !artifactInfo.groupId.startsWith("#") && !artifactInfo.groupId.startsWith("%") && !id.contains("#") && !id.contains("%")) { 52 | if (searchParam.fa && searchParam.fg) 53 | result.add(artifactInfo) 54 | else 55 | result.addArtifactInfo(artifactInfo) 56 | } 57 | } 58 | return result 59 | } 60 | 61 | override fun handleEmptyResult(searchParam: SearchParam, project: Project): Collection { 62 | val settings = Settings.getInstance() 63 | return when { 64 | settings.useNexus -> NexusSearcher.search(searchParam, project) 65 | settings.useArtifactory -> ArtifactorySearcher.search(searchParam, project) 66 | else -> MavenCentralSearcher.search(searchParam, project) 67 | } 68 | } 69 | 70 | override fun doSearchByClassName(searchParam: ClassNameSearchParam, project: Project): Collection { 71 | return emptySet() 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /src/test/kotlin/test/gdph/completion/DependenciesCompletionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 test.gdph.completion 18 | 19 | import test.CodeInsightTestBase 20 | 21 | /** 22 | * 23 | * @author Peter Wu 24 | * @since 25 | */ 26 | class DependenciesCompletionTest : CodeInsightTestBase() { 27 | 28 | //build.gradle dependencies 29 | fun testStdDependencies() { 30 | completionCheckResult(gradleFileName, """dependencies { 31 | compile 'kotlin-reflect$caret' 32 | }""", { 33 | """dependencies { 34 | compile 'org.jetbrains.kotlin:kotlin-reflect' 35 | }""" 36 | }, "org.jetbrains.kotlin:kotlin-reflect") 37 | } 38 | 39 | fun testStdDependenciesVersion() { 40 | completionCheckResult(gradleFileName, """dependencies { 41 | compile 'org.jetbrains.kotlin:kotlin-reflect$caret' 42 | }""", { 43 | """dependencies { 44 | compile 'org.jetbrains.kotlin:kotlin-reflect:$it' 45 | }""" 46 | }, "org.jetbrains.kotlin:kotlin-reflect:") 47 | } 48 | 49 | //build.gradle dependencies map notation 50 | 51 | fun testDependenciesMapGroup() { 52 | completionCheckResult(gradleFileName, """dependencies { 53 | compile group:'kotlin$caret' 54 | }""", { 55 | """dependencies { 56 | compile group:'org.jetbrains.kotlin' 57 | }""" 58 | }, "org.jetbrains.kotlin") 59 | } 60 | 61 | fun testDependenciesMapName() { 62 | completionCheckResult(gradleFileName, """dependencies { 63 | compile group:'org.jetbrains.kotlin',name:'kotlin-stdlib-$caret' 64 | }""", { 65 | """dependencies { 66 | compile group:'org.jetbrains.kotlin',name:'kotlin-stdlib-jre8' 67 | }""" 68 | }, "kotlin-stdlib-jre8") 69 | } 70 | 71 | fun testDependenciesMapVersion() { 72 | completionCheckResult(gradleFileName, """dependencies { 73 | compile group:'org.jetbrains.kotlin',name:'kotlin-reflect',version:'$caret' 74 | }""", { 75 | """dependencies { 76 | compile group:'org.jetbrains.kotlin',name:'kotlin-reflect',version:'$it' 77 | }""" 78 | }, "") 79 | } 80 | 81 | 82 | //build.gradle dependencies * 83 | fun testDependenciesWildcard() { 84 | completionCheckResult(gradleFileName, """dependencies{ 85 | testCompile 'kotlin*junit$caret' 86 | }""", { 87 | """dependencies{ 88 | testCompile 'org.jetbrains.kotlin:kotlin-test-junit' 89 | }""" 90 | }, "org.jetbrains.kotlin:kotlin-test-junit") 91 | } 92 | 93 | //build.gradle dependencies by className 94 | fun testDependenciesByFullyQualifiedClassName() { 95 | completionCheckResult(gradleFileName, """dependencies{ 96 | compile 'fc:feign.Client$caret' 97 | }""", { 98 | """dependencies{ 99 | compile 'com.netflix.feign:feign-core:$it' 100 | }""" 101 | }, "com.netflix.feign:feign-core:") 102 | } 103 | 104 | fun testDependenciesByClassName() { 105 | completionCheckResult(gradleFileName, """dependencies{ 106 | compile 'c:feign$caret' 107 | }""", { 108 | """dependencies{ 109 | compile 'com.netflix.feign:feign-core:$it' 110 | }""" 111 | }, "com.netflix.feign:feign-core:") 112 | } 113 | 114 | 115 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/kotlin/KtsOpenMavenCentralProvider.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.kotlin 18 | 19 | import cn.bestwu.gdph.AbstractGDPHProvider 20 | import cn.bestwu.gdph.AbstractGradlePluginsCompletionContributor 21 | import cn.bestwu.gdph.trim 22 | import com.intellij.psi.PsiElement 23 | import org.jetbrains.kotlin.psi.KtParenthesizedExpression 24 | import org.jetbrains.kotlin.psi.KtStringTemplateExpression 25 | import org.jetbrains.kotlin.psi.KtValueArgumentList 26 | 27 | class KtsOpenMavenCentralProvider : AbstractGDPHProvider() { 28 | 29 | 30 | override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? { 31 | if (element is KtValueArgumentList || element is KtStringTemplateExpression) { 32 | return dependenciesDoc(element) ?: pluginsDoc(element) 33 | } 34 | return null 35 | } 36 | 37 | private fun pluginsDoc(element: PsiElement): String? { 38 | var e = element 39 | do { 40 | e = e.parent ?: return null 41 | } while ("plugins" != e.firstChild.text) 42 | var parent: PsiElement? = element.parent 43 | var searchText = trim(element.text) 44 | 45 | if (parent is KtParenthesizedExpression) { 46 | parent = parent.parent 47 | } 48 | if (parent != null) { 49 | val parentText = parent.text 50 | when { 51 | parentText.contains("version") -> searchText = 52 | if (parentText.startsWith("kotlin")) { 53 | parentText.replace( 54 | GradleKtsPluginsCompletionContributor.Util.kotlinRegex, 55 | "${GradleKtsPluginsCompletionContributor.KOTLIN_PREFIX}$1" 56 | ).trim() 57 | } else { 58 | parentText.replace(AbstractGradlePluginsCompletionContributor.versionRegex, "$1") 59 | .trim() 60 | } 61 | parentText.startsWith("kotlin") -> { 62 | searchText = searchText.replace("^(.*?)\".*$".toRegex(), "$1") 63 | searchText = "${GradleKtsPluginsCompletionContributor.KOTLIN_PREFIX}$searchText" 64 | } 65 | parent.parent.parent.text.startsWith("kotlin") -> { 66 | searchText = 67 | trim(element.parent.parent.text).replace("^(.*?)\".*$".toRegex(), "$1") 68 | searchText = "${GradleKtsPluginsCompletionContributor.KOTLIN_PREFIX}$searchText" 69 | } 70 | } 71 | } 72 | return pluginsDoc(searchText) 73 | } 74 | 75 | private fun dependenciesDoc(element: PsiElement): String? { 76 | var e = element 77 | do { 78 | e = e.parent ?: return null 79 | } while ("dependencies" != e.firstChild.text && "imports" != e.firstChild.text) 80 | var dependency = trim(element.text) 81 | if (element.parent.text.startsWith("kotlin") || element.parent.parent.parent.text.startsWith( 82 | "kotlin" 83 | ) 84 | ) { 85 | dependency = "${GradleKtsDependenciesCompletionContributor.KOTLIN_PREFIX}$dependency" 86 | } 87 | 88 | return dependenciesDoc(dependency) 89 | } 90 | 91 | 92 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/config/GDPHConfigurable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.config 18 | 19 | import com.intellij.openapi.extensions.BaseExtensionPointName 20 | import com.intellij.openapi.options.Configurable 21 | import com.intellij.openapi.options.ConfigurationException 22 | import org.jetbrains.annotations.Nls 23 | import javax.swing.JComponent 24 | 25 | 26 | class GDPHConfigurable : Configurable, Configurable.WithEpDependencies { 27 | 28 | private var view: ConfigurationView? = null 29 | 30 | @Nls 31 | override fun getDisplayName(): String { 32 | return "Gradle Dependencies Helper" 33 | } 34 | 35 | override fun getHelpTopic(): String { 36 | return "Configure the default settings for the Gradle Dependencies Helper" 37 | } 38 | 39 | override fun createComponent(): JComponent? { 40 | if (view == null) { 41 | view = ConfigurationView() 42 | } 43 | 44 | // Reset on click. 45 | view!!.resetButton.addActionListener { 46 | view!!.useAli = Settings.useAli 47 | view!!.aliRepo = Settings.aliRepo 48 | view!!.useNexus = Settings.useNexus 49 | view!!.nexusSearchUrl = Settings.nexusSearchUrl 50 | view!!.useArtifactory = Settings.useArtifactory 51 | view!!.artifactoryUrl = Settings.artifactoryUrl 52 | view!!.artifactoryUsername = Settings.artifactoryUsername 53 | view!!.artifactoryPassword = Settings.artifactoryPassword 54 | view!!.artiRepos = Settings.artiRepos 55 | } 56 | 57 | reset() 58 | 59 | return view!!.dpPanel 60 | } 61 | 62 | override fun isModified(): Boolean { 63 | val settings = Settings.getInstance() 64 | return settings.useAli != view!!.useAli || settings.aliRepo != view!!.aliRepo 65 | ||settings.useNexus != view!!.useNexus || settings.nexusSearchUrl != view!!.nexusSearchUrl 66 | || settings.useArtifactory != view!!.useArtifactory || settings.artifactoryUrl != view!!.artifactoryUrl || settings.artiRepos != view!!.artiRepos 67 | || settings.artifactoryUsername != view!!.artifactoryUsername || settings.artifactoryPassword != view!!.artifactoryPassword 68 | } 69 | 70 | @Throws(ConfigurationException::class) 71 | override fun apply() { 72 | val settings = Settings.getInstance() 73 | settings.useAli = view!!.useAli 74 | settings.aliRepo = view!!.aliRepo 75 | settings.useNexus = view!!.useNexus 76 | settings.nexusSearchUrl = view!!.nexusSearchUrl 77 | settings.useArtifactory = view!!.useArtifactory 78 | settings.artifactoryUrl = view!!.artifactoryUrl 79 | settings.artiRepos = view!!.artiRepos 80 | settings.artifactoryUsername = view!!.artifactoryUsername 81 | settings.artifactoryPassword = view!!.artifactoryPassword 82 | } 83 | 84 | override fun reset() { 85 | val settings = Settings.getInstance() 86 | view!!.useAli = settings.useAli 87 | view!!.aliRepo = settings.aliRepo 88 | view!!.useNexus = settings.useNexus 89 | view!!.nexusSearchUrl = settings.nexusSearchUrl 90 | view!!.useArtifactory = settings.useArtifactory 91 | view!!.artifactoryUrl = settings.artifactoryUrl 92 | view!!.artifactoryUsername = settings.artifactoryUsername 93 | view!!.artifactoryPassword = settings.artifactoryPassword 94 | view!!.artiRepos = settings.artiRepos 95 | } 96 | 97 | override fun disposeUIResources() { 98 | view = null 99 | } 100 | 101 | override fun getDependencies(): MutableCollection> { 102 | return mutableListOf() 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/test/kotlin/test/CodeInsightTestBase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 test 18 | 19 | import cn.bestwu.gdph.config.Settings 20 | import com.intellij.codeInsight.actions.CodeInsightAction 21 | import com.intellij.codeInsight.completion.CompletionType 22 | import com.intellij.codeInsight.lookup.Lookup 23 | import com.intellij.codeInsight.lookup.LookupElement 24 | import com.intellij.openapi.fileTypes.FileType 25 | import com.intellij.psi.impl.source.PostprocessReformattingAspect 26 | import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase 27 | 28 | /** 29 | * 30 | * @author Peter Wu 31 | * @since 32 | */ 33 | abstract class CodeInsightTestBase : LightJavaCodeInsightFixtureTestCase() { 34 | companion object { 35 | const val gradleFileName = "build.gradle" 36 | const val gradleKtsFileName = "build.gradle.kts" 37 | const val caret = "" 38 | 39 | private const val intention = "Add specified repository to repositories" 40 | } 41 | 42 | override fun setUp() { 43 | super.setUp() 44 | Settings.getInstance().useNexus = false 45 | } 46 | 47 | protected fun completionCheckResult(fileName: String, before: String, after: (String) -> String, selectItem: String) { 48 | myFixture.configureByText(fileName, before) 49 | myFixture.complete(CompletionType.SMART, 1) 50 | val selectVersion = selectLookupItem(selectItem) 51 | val afterStr = after(selectVersion) 52 | println("result:\n$afterStr") 53 | myFixture.checkResult(afterStr) 54 | } 55 | 56 | protected fun completionCheckResult(fileName: String, before: String, after: String) { 57 | myFixture.configureByText(fileName, before) 58 | myFixture.complete(CompletionType.SMART, 1) 59 | myFixture.checkResult(after) 60 | } 61 | 62 | protected fun actionCheckResult(fileType: FileType, before: String, after: String, action: CodeInsightAction) { 63 | myFixture.configureByText(fileType, before) 64 | action.actionPerformedImpl(project,editor) 65 | myFixture.checkResult(after) 66 | } 67 | 68 | // protected fun completionCheckResultByFile(before: String, after: String, selectItem: String) { 69 | // myFixture.configureByFiles(before) 70 | // myFixture.complete(CompletionType.SMART, 1) 71 | // if (selectItem.isNotBlank()) { 72 | // selectLookupItem(selectItem) 73 | // } 74 | // myFixture.checkResultByFile(after) 75 | // } 76 | 77 | private fun selectLookupItem(selectItem: String): String { 78 | val lookupElements = myFixture.lookupElements 79 | assertNotNull("Lookup is empty", lookupElements) 80 | val toSelect: LookupElement? = lookupElements!!.firstOrNull { 81 | println(it.lookupString) 82 | it.lookupString.startsWith(selectItem) 83 | } 84 | assertNotNull("$selectItem not found in lookup", toSelect) 85 | myFixture.lookup.currentItem = toSelect 86 | myFixture.type(Lookup.NORMAL_SELECT_CHAR) 87 | return toSelect!!.lookupString.split("[#:]".toRegex()).last() 88 | } 89 | 90 | 91 | protected fun intentionCheckResult(fileName: String, given: String, expected: String) { 92 | myFixture.configureByText(fileName, given) 93 | val list = myFixture.filterAvailableIntentions(intention) 94 | assert(list.size == 1) 95 | myFixture.launchAction(list.first()) 96 | PostprocessReformattingAspect.getInstance(project).doPostponedFormatting() 97 | myFixture.checkResult(expected) 98 | } 99 | 100 | protected fun intentionEmpty(fileName: String, given: String) { 101 | myFixture.configureByText(fileName, given) 102 | val list = myFixture.filterAvailableIntentions(intention) 103 | assert(list.size == 0) 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /src/main/java/cn/bestwu/gdph/config/ConfigurationView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.config; 18 | 19 | import cn.bestwu.gdph.search.UtilKt; 20 | import java.util.List; 21 | import javax.swing.JButton; 22 | import javax.swing.JCheckBox; 23 | import javax.swing.JComboBox; 24 | import javax.swing.JPanel; 25 | import javax.swing.JTextField; 26 | 27 | 28 | public class ConfigurationView { 29 | 30 | private JButton resetButton; 31 | private JCheckBox useNexus; 32 | private JTextField nexusSearchUrl; 33 | private JPanel dpPanel; 34 | private JTextField artifactoryUrl; 35 | private JCheckBox useArtifactory; 36 | private JTextField artiRepos; 37 | private javax.swing.JPasswordField artifactoryPassword; 38 | private JTextField artifactoryUsername; 39 | private JComboBox aliRepo; 40 | private JCheckBox useAli; 41 | 42 | public ConfigurationView() { 43 | useNexus.addActionListener( 44 | actionEvent -> nexusSearchUrl.setEnabled(useNexus.isSelected()) 45 | ); 46 | useAli.addActionListener( 47 | actionEvent -> aliRepo.setEnabled(useAli.isSelected()) 48 | ); 49 | useArtifactory.addActionListener( 50 | actionEvent -> { 51 | artifactoryUrl.setEnabled(useArtifactory.isSelected()); 52 | artiRepos.setEnabled(useArtifactory.isSelected()); 53 | artifactoryUsername.setEnabled(useArtifactory.isSelected()); 54 | artifactoryPassword.setEnabled(useArtifactory.isSelected()); 55 | } 56 | ); 57 | aliRepo.removeAllItems(); 58 | aliRepo.addItem("all"); 59 | List aliRepos = UtilKt.getAliRepos(); 60 | for (String repo : aliRepos) { 61 | aliRepo.addItem(repo); 62 | } 63 | } 64 | 65 | public JPanel getDpPanel() { 66 | return dpPanel; 67 | } 68 | 69 | public String getAliRepo() { 70 | return aliRepo.getSelectedItem().toString(); 71 | } 72 | 73 | public void setAliRepo(String aliRepo) { 74 | this.aliRepo.setSelectedItem(aliRepo); 75 | } 76 | 77 | public boolean getUseAli() { 78 | return useAli.isSelected(); 79 | } 80 | 81 | public void setUseAli(boolean useAli) { 82 | this.useAli.setSelected(useAli); 83 | aliRepo.setEnabled(useAli); 84 | } 85 | 86 | public String getNexusSearchUrl() { 87 | return nexusSearchUrl.getText().trim(); 88 | } 89 | 90 | public void setNexusSearchUrl(String nexusSearchUrl) { 91 | this.nexusSearchUrl.setText(nexusSearchUrl); 92 | } 93 | 94 | 95 | public boolean getUseNexus() { 96 | return useNexus.isSelected(); 97 | } 98 | 99 | public void setUseNexus(boolean selected) { 100 | useNexus.setSelected(selected); 101 | nexusSearchUrl.setEnabled(selected); 102 | } 103 | 104 | 105 | public String getArtifactoryUrl() { 106 | return artifactoryUrl.getText().trim(); 107 | } 108 | 109 | public void setArtifactoryUrl(String artifactoryUrl) { 110 | this.artifactoryUrl.setText(artifactoryUrl); 111 | } 112 | 113 | public String getArtiRepos() { 114 | return artiRepos.getText().trim(); 115 | } 116 | 117 | public void setArtiRepos(String artiRepos) { 118 | this.artiRepos.setText(artiRepos); 119 | } 120 | 121 | public String getArtifactoryPassword() { 122 | return new String(artifactoryPassword.getPassword()); 123 | } 124 | 125 | public void setArtifactoryPassword(String artifactoryPassword) { 126 | this.artifactoryPassword.setText(artifactoryPassword); 127 | } 128 | 129 | public String getArtifactoryUsername() { 130 | return artifactoryUsername.getText(); 131 | } 132 | 133 | public void setArtifactoryUsername(String artifactoryUsername) { 134 | this.artifactoryUsername.setText(artifactoryUsername); 135 | } 136 | 137 | public boolean getUseArtifactory() { 138 | return useArtifactory.isSelected(); 139 | } 140 | 141 | public void setUseArtifactory(boolean useArtifactory) { 142 | this.useArtifactory.setSelected(useArtifactory); 143 | artifactoryUrl.setEnabled(useArtifactory); 144 | artiRepos.setEnabled(useArtifactory); 145 | artifactoryUsername.setEnabled(useArtifactory); 146 | artifactoryPassword.setEnabled(useArtifactory); 147 | } 148 | 149 | public JButton getResetButton() { 150 | return resetButton; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/GradlePluginsCompletionContributor.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph 18 | 19 | import cn.bestwu.gdph.search.GradlePluginsSearcher 20 | import com.intellij.codeInsight.completion.CompletionParameters 21 | import com.intellij.codeInsight.completion.CompletionProvider 22 | import com.intellij.codeInsight.completion.CompletionResultSet 23 | import com.intellij.codeInsight.completion.CompletionType 24 | import com.intellij.codeInsight.lookup.LookupElementBuilder 25 | import com.intellij.icons.AllIcons 26 | import com.intellij.patterns.PlatformPatterns 27 | import com.intellij.patterns.PlatformPatterns.psiElement 28 | import com.intellij.patterns.StandardPatterns.string 29 | import com.intellij.psi.PsiElement 30 | import com.intellij.util.ProcessingContext 31 | import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral 32 | import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.path.GrMethodCallExpressionImpl 33 | import java.net.SocketTimeoutException 34 | 35 | 36 | class GradlePluginsCompletionContributor : AbstractGradlePluginsCompletionContributor() { 37 | 38 | init { 39 | extend( 40 | CompletionType.SMART, 41 | psiElement(PsiElement::class.java) 42 | .and( 43 | psiElement().inFile( 44 | PlatformPatterns.psiFile().withName(string().endsWith(".gradle")) 45 | ) 46 | ) 47 | .withParent(GrLiteral::class.java) 48 | .withAncestor( 49 | 10, psiElement(GrMethodCallExpressionImpl::class.java) 50 | .withText(string().startsWith(PLUGINS_EXTENSION)) 51 | ), CompletionPluginsCompletionProvider() 52 | ) 53 | 54 | } 55 | 56 | 57 | private class CompletionPluginsCompletionProvider : CompletionProvider() { 58 | override fun addCompletions( 59 | params: CompletionParameters, 60 | context: ProcessingContext, 61 | result: CompletionResultSet 62 | ) { 63 | try { 64 | val parent = params.position.parent?.parent?.parent 65 | result.stopHere() 66 | val isVersion = parent != null && parent.text.contains("version") 67 | var allText = "" 68 | val text = parent!!.text 69 | val searchText = if (isVersion) { 70 | text.replace(versionRegex, "$1") 71 | } else { 72 | allText = 73 | text.replace(idRegex, "$1").substringBefore("IntellijIdeaRulezzz ").trim() 74 | allText.substringBeforeLast(".") 75 | } 76 | val searchResult: List 77 | var completionResultSet = result 78 | if (isVersion) { 79 | searchResult = GradlePluginsSearcher.searchPluginVersions(searchText) 80 | completionResultSet = result.withRelevanceSorter(completionSorter(searchResult)) 81 | } else { 82 | if (searchText.length < 2) { 83 | return 84 | } 85 | searchResult = 86 | searchResultFix(GradlePluginsSearcher.searchPlugins(searchText), allText) 87 | } 88 | 89 | 90 | searchResult.forEach { 91 | val lookupElementBuilder = if (isVersion) LookupElementBuilder.create(it) 92 | .withIcon(AllIcons.Nodes.PpLib) 93 | .withInsertHandler(insertHandler) else LookupElementBuilder.create(it) 94 | .withPresentableText(it.replace(GradlePluginsSearcher.SPLIT_RULE, ":")) 95 | .withIcon(AllIcons.Nodes.PpLib).withInsertHandler(insertHandler) 96 | completionResultSet.addElement(lookupElementBuilder) 97 | } 98 | } catch (e: SocketTimeoutException) { 99 | val url = "https://plugins.gradle.org/search" 100 | browseNotification(params.position.project, "Request timeout", url) 101 | } 102 | } 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/search/NexusSearcher.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.search 18 | 19 | import cn.bestwu.gdph.config.Settings 20 | import com.intellij.openapi.project.Project 21 | import java.util.* 22 | 23 | @Suppress("UNCHECKED_CAST") 24 | /** 25 | * 26 | * @author Peter Wu 27 | * @since 28 | */ 29 | object NexusSearcher : AbstractArtifactSearcher() { 30 | 31 | override val cache: Boolean 32 | get() = false 33 | override val key: String 34 | get() = "nexus:" 35 | 36 | private fun artifactInfo(groupId: String, artifactId: String, version: String = "", repo: String, className: String = ""): ArtifactInfo { 37 | val owner = Settings.getInstance().nexusSearchUrl.replace("^.*?\\.(.*?)\\..*$".toRegex(), "$1 nexus").trim() 38 | return ArtifactInfo(groupId, artifactId, version, "$repo${if (owner.isNotBlank() && !(repo == "jcenter" && owner == "bintray")) " by $owner" else ""}", "${Settings.getInstance().nexusSearchUrl}/$repo", true, className) 39 | } 40 | 41 | override fun doSearch(searchParam: SearchParam, project: Project): Collection { 42 | val nexusSearchUrl = Settings.getInstance().nexusSearchUrl 43 | val url = "$nexusSearchUrl/service/local/lucene/search?${searchParam.toNq()}" 44 | val connection = getConnection(url) 45 | connection.setRequestProperty("Accept", "application/json") 46 | var jsonResult = getResponseJson(connection, project) ?: return emptySet() 47 | jsonResult = (jsonResult as Map<*, *>)["data"] as List> 48 | if (searchParam.fg) 49 | jsonResult = jsonResult.filter { it["groupId"] == searchParam.groupId } 50 | val result = TreeSet() 51 | jsonResult.forEach { 52 | val repo = if (it["latestReleaseRepositoryId"] != null) { 53 | it["latestReleaseRepositoryId"] 54 | } else { 55 | it["latestSnapshotRepositoryId"] 56 | } 57 | val artifactInfo = artifactInfo(it["groupId"] as String, if (searchParam.artifactId.isBlank() && !searchParam.fg && searchParam.groupId.isNotBlank() && searchParam.groupId != it["groupId"]) "" else it["artifactId"] as String, "", repo as String) 58 | if (artifactInfo.id == searchParam.toId() && artifactInfo.artifactId.isNotBlank()) { 59 | artifactInfo.version = it["version"] as String 60 | result.add(artifactInfo) 61 | } else if (!searchParam.fa) { 62 | result.add(artifactInfo) 63 | } 64 | } 65 | 66 | return result 67 | } 68 | 69 | override fun handleEmptyResult(searchParam: SearchParam, project: Project): Collection { 70 | val settings = Settings.getInstance() 71 | return when { 72 | settings.useArtifactory -> ArtifactorySearcher.search(searchParam, project) 73 | else -> MavenCentralSearcher.search(searchParam, project) 74 | } 75 | } 76 | 77 | override fun doSearchByClassName(searchParam: ClassNameSearchParam, project: Project): Collection { 78 | val nexusSearchUrl = Settings.getInstance().nexusSearchUrl 79 | val url = "$nexusSearchUrl/service/local/lucene/search?cn=${searchParam.q}" 80 | val connection = getConnection(url) 81 | connection.setRequestProperty("Accept", "application/json") 82 | var jsonResult = getResponseJson(connection, project) ?: return emptySet() 83 | jsonResult = (jsonResult as Map<*, *>)["data"] as List> 84 | val result = TreeSet() 85 | jsonResult.mapTo(result) { 86 | val repo = if (it["latestReleaseRepositoryId"] != null) { 87 | it["latestReleaseRepositoryId"] 88 | } else { 89 | it["latestSnapshotRepositoryId"] 90 | } 91 | artifactInfo(it["groupId"] as String, it["artifactId"] as String, (if (it["latestRelease"] == null) it["version"] else it["latestRelease"]) as String, repo as String) 92 | } 93 | return result 94 | } 95 | 96 | override fun handleEmptyResultByClassName(searchParam: ClassNameSearchParam, project: Project): Collection { 97 | val settings = Settings.getInstance() 98 | return when { 99 | settings.useArtifactory -> ArtifactorySearcher.searchByClassName(searchParam, project) 100 | else -> MavenCentralSearcher.searchByClassName(searchParam, project) 101 | } 102 | } 103 | 104 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/search/SearchParam.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.search 18 | 19 | import cn.bestwu.gdph.quot 20 | import cn.bestwu.gdph.split 21 | 22 | /** 23 | * 24 | * @author Peter Wu 25 | * @since 26 | */ 27 | interface ISearchParam { 28 | val docUrl: String 29 | val docText: String 30 | val src: String 31 | } 32 | 33 | class ClassNameSearchParam(override val src: String) : ISearchParam { 34 | override val docUrl: String 35 | override val docText: String 36 | val k: String 37 | val q: String 38 | 39 | init { 40 | when { 41 | src.startsWith("c:", true) -> { 42 | k = "c" 43 | q = src.substringAfter("c:").trim() 44 | docUrl = "https://search.maven.org/#search|gav|1|c:$quot$q$quot" 45 | docText = "search in mavenCentral" 46 | } 47 | 48 | src.startsWith("fc:", true) -> { 49 | k = "fc" 50 | q = src.substringAfter("fc:").trim() 51 | docUrl = "https://search.maven.org/#search|gav|1|fc:$quot$q$quot" 52 | docText = "search in mavenCentral" 53 | } 54 | 55 | else -> { 56 | k = "" 57 | q = "" 58 | docUrl = "" 59 | docText = "" 60 | } 61 | } 62 | } 63 | 64 | override fun toString(): String { 65 | return "ClassNameSearchParam(src='$src', docUrl='$docUrl', k='$k', q='$q')" 66 | } 67 | 68 | 69 | } 70 | 71 | class SearchParam(val groupId: String, val artifactId: String, val fg: Boolean, val fa: Boolean, src: String = "") : ISearchParam { 72 | override val src: String = if (src.isBlank()) "$groupId${if (fg) ":" else ""}${artifactId.ifBlank { "" }}${if (fa) ":" else ""}" else src.trim() 73 | override val docUrl: String 74 | override val docText: String 75 | 76 | private fun fullQuery(fullname: Boolean, name: String) = if (fullname) name else "*$name*" 77 | private fun halfQuery(fullname: Boolean, name: String) = if (fullname) name else "$name*" 78 | 79 | fun toId() = if (groupId.isBlank()) src else "$groupId${if (artifactId.isBlank()) "" else ":$artifactId"}" 80 | 81 | /** 82 | * jcenter params 83 | */ 84 | fun toQ() = if (groupId.isBlank()) "q=*$src*" else "g=${fullQuery(fg, groupId)}${if (artifactId.isBlank()) "" else "&a=${fullQuery(fa, artifactId)}"}" 85 | 86 | /** 87 | * artifactory params 88 | */ 89 | fun toAQ() = if (groupId.isBlank()) "/api/search/artifact?name=$src" else "/api/search/gavc?g=${fullQuery(fg, groupId)}${if (artifactId.isBlank()) "" else "&a=${fullQuery(fa, artifactId)}"}" 90 | 91 | /** 92 | * ali params 93 | */ 94 | fun toAliQ() = if (groupId.isBlank()) "/artifact/aliyunMaven/searchArtifactByWords?queryTerm=$src&_input_charset=utf-8" else "/artifact/aliyunMaven/searchArtifactByGav?groupId=${fullQuery(fg, groupId)}${if (artifactId.isBlank()) "&artifactId=" else "&artifactId=${fullQuery(fa, artifactId)}"}&version=&_input_charset=utf-8" 95 | 96 | /** 97 | * maven center params 98 | */ 99 | fun toMq() = if (groupId.isBlank()) src else "g:$groupId${if (fg) "" else "*"}${ 100 | if (artifactId.isBlank()) { 101 | "" 102 | } else "+AND+a:$artifactId${if (fa) "" else "*"}" 103 | }" 104 | 105 | /** 106 | * nexus params 107 | */ 108 | fun toNq() = if (groupId.isBlank()) "q=$src" else "g=${halfQuery(fg, groupId)}${if (artifactId.isBlank()) "" else "&a=${halfQuery(fa, artifactId)}"}" 109 | 110 | override fun toString(): String { 111 | return "SearchParam(groupId='$groupId', artifactId='$artifactId', fg=$fg, fa=$fa, src='$src', docUrl='$docUrl')" 112 | } 113 | 114 | init { 115 | docUrl = "https://search.maven.org/solrsearch/select?q=${toMq()}" 116 | docText = "search in mavenCentral" 117 | } 118 | } 119 | 120 | fun toSearchParam(src: String): SearchParam { 121 | val list = split(src) 122 | return when { 123 | list.size in (2..3) -> { 124 | val groupId = list[0].trim() 125 | val artifactId = list[1].trim() 126 | val fa = src.count { it == ':' } > 1 && artifactId.isNotBlank() 127 | SearchParam(groupId, artifactId, groupId.isNotBlank(), fa) 128 | } 129 | 130 | src.contains(":") -> SearchParam(src.trim(), "", fg = true, fa = false) 131 | else -> SearchParam("", "", fg = false, fa = false, src = src.trim()) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/test/kotlin/test/gdph/ResultTest.kt: -------------------------------------------------------------------------------- 1 | package test.gdph 2 | 3 | import cn.bestwu.gdph.AbstractGradlePluginsCompletionContributor 4 | import cn.bestwu.gdph.kotlin.GradleKtsPluginsCompletionContributor 5 | import cn.bestwu.gdph.search.ArtifactInfo 6 | import cn.bestwu.gdph.search.compareVersion 7 | import cn.bestwu.gdph.search.regex 8 | import org.junit.Assert 9 | import org.junit.Assert.assertEquals 10 | import org.junit.Test 11 | import java.io.File 12 | 13 | /** 14 | * 15 | * @author Peter Wu 16 | * @since 17 | */ 18 | class ParseResultTest { 19 | 20 | @Test 21 | fun regexVersion() { 22 | val regex = AbstractGradlePluginsCompletionContributor.versionRegex 23 | assertEquals("org.springframework.boot", "id(\"org.springframework.boot\") version( \"1.5.6.RELEASE\")".replace(regex, "$1")) 24 | assertEquals("org.springframework.boot", "id \"org.springframework.boot\" version( \"1.5.6.RELEASE\")".replace(regex, "$1")) 25 | assertEquals("org.springframework.boot", "id \"org.springframework.boot\" version \"1.5.6.RELEASE\"".replace(regex, "$1")) 26 | assertEquals("org.springframework.boot", "id('org.springframework.boot') version( '1.5.6.RELEASE')".replace(regex, "$1")) 27 | assertEquals("org.springframework.boot", "id 'org.springframework.boot' version( '1.5.6.RELEASE')".replace(regex, "$1")) 28 | assertEquals("org.springframework.boot", "id 'org.springframework.boot' version '1.5.6.RELEASE'".replace(regex, "$1")) 29 | assertEquals("org.springframework.boot", "id(\"org.springframework.boot\") version (\"1.5.6.RELEASE\")".replace(regex, "$1")) 30 | assertEquals("org.springframework.boot", "id(\"org.springframework.boot\") version \"1.5.6.RELEASE\"".replace(regex, "$1")) 31 | } 32 | 33 | @Test 34 | fun grRegex() { 35 | assertEquals("org.springframework.boot:spr", "org.springframework.boot:sprIntellijIdeaRulezzz ing-boot-dependencies".substringBefore("IntellijIdeaRulezzz ")) 36 | } 37 | 38 | @Test 39 | fun sortVersion() { 40 | val versions = arrayListOf( 41 | "1.3.0.ALPHA", 42 | "1.3.0.SNAPSHOTS", 43 | "1.3.0.RELEASE", 44 | "1.3.0.BETA", 45 | "1.3.0.RC1", 46 | "1.3.0.RC2", 47 | "1.3.0.M3", 48 | "1.3.0.M4", 49 | "1.3.0.M", 50 | "1.3.0", 51 | "1.3.0.1", 52 | "1.3.1", 53 | "1.3.0.M2", 54 | "1.3.0.M5", 55 | "1.3.0.M1" 56 | ) 57 | versions.sortWith { o1, o2 -> 58 | compareVersion(o1, o2) 59 | } 60 | assertEquals(arrayListOf( 61 | "1.3.0", 62 | "1.3.0.SNAPSHOTS", 63 | "1.3.0.ALPHA", 64 | "1.3.0.BETA", 65 | "1.3.0.M", 66 | "1.3.0.M1", 67 | "1.3.0.M2", 68 | "1.3.0.M3", 69 | "1.3.0.M4", 70 | "1.3.0.M5", 71 | "1.3.0.RC1", 72 | "1.3.0.RC2", 73 | "1.3.0.RELEASE", 74 | "1.3.0.1", 75 | "1.3.1" 76 | ), versions) 77 | } 78 | 79 | @Test 80 | fun sortCloudVersion() { 81 | val versions = arrayListOf( 82 | "Finchley", 83 | "Edgware", 84 | "Edgware.M1", 85 | "Angel.SR6", 86 | "Dalston.SR3", 87 | "Brixton.SR7", 88 | "Camden.SR7", 89 | "Finchley.M2" 90 | ) 91 | versions.sortWith { o1, o2 -> 92 | compareVersion(o2, o1) 93 | } 94 | assertEquals(arrayListOf( 95 | "Finchley.M2", 96 | "Finchley", 97 | "Edgware.M1", 98 | "Edgware", 99 | "Dalston.SR3", 100 | "Camden.SR7", 101 | "Brixton.SR7", 102 | "Angel.SR6" 103 | ), versions) 104 | } 105 | 106 | @Test 107 | fun mavenRegex() { 108 | 109 | regex.findAll(File(this::class.java.getResource("/result.json")?.path ?: "").readText()).forEach { 110 | val artifactInfo = ArtifactInfo(it.groupValues[1], it.groupValues[2], it.groupValues[3], "mavenCentral", "Apache") 111 | Assert.assertTrue(artifactInfo.version.isNotBlank()) 112 | Assert.assertTrue(artifactInfo.artifactId.isNotBlank()) 113 | Assert.assertTrue(artifactInfo.groupId.isNotBlank()) 114 | } 115 | } 116 | 117 | 118 | // @Test 119 | // fun local() { 120 | // Assert.assertTrue(Locale.getDefault() == Locale.CHINA) 121 | // Assert.assertFalse(Locale.getDefault() == Locale.CHINESE) 122 | // Assert.assertTrue(Locale.getDefault() == Locale.SIMPLIFIED_CHINESE) 123 | // } 124 | 125 | @Test 126 | fun testkotlinRegex() { 127 | assertEquals("org.jetbrains.kotlin.jvm", "kotlin(\"jvm\", \"1.1.4\")".replace(GradleKtsPluginsCompletionContributor.Util.kotlinRegex, "${GradleKtsPluginsCompletionContributor.KOTLIN_PREFIX}$1")) 128 | assertEquals("org.jetbrains.kotlin.jvm", "kotlin(\"jvm\") version (\"IntellijIdeaRulezzz\$\")".replace(GradleKtsPluginsCompletionContributor.Util.kotlinRegex, "${GradleKtsPluginsCompletionContributor.KOTLIN_PREFIX}$1")) 129 | assertEquals("org.jetbrains.kotlin.jvm", "kotlin(\"jvm\") version \"IntellijIdeaRulezzz\$\"".replace(GradleKtsPluginsCompletionContributor.Util.kotlinRegex, "${GradleKtsPluginsCompletionContributor.KOTLIN_PREFIX}$1")) 130 | } 131 | 132 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/Util.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph 18 | 19 | import cn.bestwu.gdph.search.ArtifactInfo 20 | import cn.bestwu.gdph.search.GradlePluginsSearcher 21 | import cn.bestwu.gdph.search.compareVersion 22 | import com.intellij.codeInsight.completion.CompletionInitializationContext 23 | import com.intellij.codeInsight.completion.CompletionType 24 | import com.intellij.codeInsight.completion.InsertHandler 25 | import com.intellij.codeInsight.lookup.LookupElement 26 | import com.intellij.notification.BrowseNotificationAction 27 | import com.intellij.notification.NotificationGroup 28 | import com.intellij.notification.NotificationType 29 | import com.intellij.notification.Notifications 30 | import com.intellij.openapi.project.Project 31 | import com.intellij.openapi.util.IconLoader 32 | import com.intellij.util.ReflectionUtil 33 | import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil.removeQuotes 34 | import java.net.URLEncoder 35 | 36 | internal fun split(dependency: String) = Regex(":").split(dependency) 37 | internal fun trim(dependency: String) = removeQuotes(dependency).trim('(', ')') 38 | val quot = URLEncoder.encode("\"", "UTF-8")!! 39 | 40 | internal var repoIcon = IconLoader.getIcon( 41 | "/icons/repo.png", 42 | ReflectionUtil.getGrandCallerClass()!! 43 | ) 44 | 45 | internal fun browseNotification(project: Project, text: String, url: String, title: String = text) { 46 | val notificationGroup = NotificationGroup.findRegisteredGroup("GradleDependencies")!! 47 | val notification = notificationGroup.createNotification(title, "", NotificationType.WARNING) 48 | notification.addAction(BrowseNotificationAction(text, url)) 49 | Notifications.Bus.notify(notification, project) 50 | } 51 | 52 | 53 | internal fun show( 54 | project: Project, 55 | content: String, 56 | title: String = "", 57 | type: NotificationType = NotificationType.INFORMATION 58 | ) { 59 | val notificationGroup = NotificationGroup.findRegisteredGroup("GradleDependencies")!! 60 | val notification = notificationGroup.createNotification(title, content, type) 61 | Notifications.Bus.notify(notification, project) 62 | } 63 | 64 | internal fun MutableSet.addArtifactInfo(artifactInfo: ArtifactInfo) { 65 | val exist = find { r -> r.id == artifactInfo.id } 66 | if (exist != null && artifactInfo.version.isNotBlank() && exist.version.isNotBlank()) { 67 | if (compareVersion(exist.version, artifactInfo.version) < 0) { 68 | exist.version = artifactInfo.version 69 | } 70 | } else { 71 | add(artifactInfo) 72 | } 73 | } 74 | 75 | internal class VersionComparator(private val index: Int) : Comparable { 76 | override fun compareTo(other: VersionComparator): Int = this.index - other.index 77 | } 78 | 79 | private val stopChars = arrayOf('"', '\'') 80 | internal var insertHandler: InsertHandler = InsertHandler { context, _ -> 81 | context.commitDocument() 82 | val text = context.document.text 83 | var idStart = context.startOffset 84 | do { 85 | idStart-- 86 | if (idStart == -1 || '\n' == text[idStart]) { 87 | return@InsertHandler 88 | } 89 | } while (!stopChars.contains(text[idStart])) 90 | idStart++ 91 | 92 | var idEnd = context.tailOffset 93 | if (idEnd >= text.length) { 94 | if (text.indexOf('#') != -1) 95 | context.document.replaceString(text.indexOf('#'), idEnd, "") 96 | return@InsertHandler 97 | } 98 | while (!stopChars.contains(text[idEnd])) { 99 | idEnd++ 100 | if (idEnd == text.length || '\n' == text[idEnd]) { 101 | return@InsertHandler 102 | } 103 | } 104 | val quote = text[idEnd] 105 | var lookupString = text.substring(context.startOffset, context.tailOffset) 106 | val list = lookupString.split(GradlePluginsSearcher.SPLIT_RULE) 107 | if (list.size == 2) { 108 | lookupString = list[0] 109 | 110 | var tailEnd = idEnd 111 | var tailStart = idEnd + 1 112 | while (tailEnd < text.length && '\n' != text[tailEnd]) { 113 | tailEnd++ 114 | if (tailEnd < text.length && ')' == text[tailEnd]) { 115 | tailStart = tailEnd + 1 116 | } 117 | } 118 | context.document.replaceString(tailStart, tailEnd, " version $quote${list[1]}$quote") 119 | } 120 | context.document.replaceString(idStart, idEnd, lookupString) 121 | } 122 | 123 | 124 | internal fun contributorDuringCompletion(context: CompletionInitializationContext) { 125 | if (context.completionType == CompletionType.SMART) { 126 | val offset = context.caret.offset 127 | val text = context.editor.document.charsSequence 128 | var idEnd = offset 129 | if (text.isBlank()) { 130 | return 131 | } 132 | while (idEnd < text.length && !stopChars.contains(text[idEnd])) { 133 | idEnd++ 134 | if (idEnd == text.length || '\n' == text[idEnd]) { 135 | return 136 | } 137 | } 138 | context.offsetMap.addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, idEnd) 139 | } 140 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/search/ArtifactorySearcher.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.search 18 | 19 | import cn.bestwu.gdph.addArtifactInfo 20 | import cn.bestwu.gdph.config.Settings 21 | import com.intellij.openapi.project.Project 22 | import java.net.HttpURLConnection 23 | import java.util.* 24 | 25 | /** 26 | * 27 | * @author Peter Wu 28 | * @since 29 | */ 30 | @Suppress("UNCHECKED_CAST") 31 | object ArtifactorySearcher : AbstractArtifactSearcher() { 32 | 33 | override val cache: Boolean 34 | get() = true 35 | override val key: String 36 | get() = "artifactory.${Settings.getInstance().artiRepos}:" 37 | 38 | override fun doSearch(searchParam: SearchParam, project: Project): Collection { 39 | val settings = Settings.getInstance() 40 | val result = TreeSet() 41 | if (searchParam.fa && searchParam.fg) { 42 | val url = "${settings.artifactoryUrl}/api/search/versions?g=${searchParam.groupId}&a=${searchParam.artifactId}&repos=${settings.artiRepos}" 43 | val connection = httpURLConnection(url) 44 | var jsonResult = getResponseJson(connection, project) 45 | if (jsonResult != null) { 46 | jsonResult = (jsonResult as Map<*, *>)["results"] as List> 47 | jsonResult.forEach { 48 | val version = it["version"] as String 49 | val artifactInfo = ArtifactInfo(searchParam.groupId, searchParam.artifactId, version, "artifactory", "", false, "") 50 | result.add(artifactInfo) 51 | } 52 | return result 53 | } 54 | } 55 | val url = "${settings.artifactoryUrl}${searchParam.toAQ()}&repos=${settings.artiRepos}" 56 | val connection = httpURLConnection(url) 57 | var jsonResult = getResponseJson(connection, project) ?: return result 58 | jsonResult = (jsonResult as Map<*, *>)["results"] as List> 59 | jsonResult.forEach { 60 | val artifactInfo = (it["uri"] as String).toArtifactInfo { 61 | searchParam.artifactId.isBlank() && !searchParam.fg && searchParam.groupId.isNotBlank() 62 | } 63 | if (searchParam.fa && searchParam.fg) 64 | result.add(artifactInfo) 65 | else 66 | result.addArtifactInfo(artifactInfo) 67 | } 68 | 69 | return result 70 | } 71 | 72 | override fun handleEmptyResult(searchParam: SearchParam, project: Project): Collection { 73 | return when { 74 | else -> MavenCentralSearcher.search(searchParam, project) 75 | } 76 | } 77 | 78 | override fun doSearchByClassName(searchParam: ClassNameSearchParam, project: Project): Collection { 79 | val settings = Settings.getInstance() 80 | val url = "${settings.artifactoryUrl}/api/search/archive?name=${searchParam.q.substringAfterLast(".")}.class&repos=${settings.artiRepos}" 81 | val connection = httpURLConnection(url) 82 | var jsonResult = getResponseJson(connection, project) ?: return emptySet() 83 | jsonResult = (jsonResult as Map<*, *>)["results"] as List> 84 | val result = TreeSet() 85 | jsonResult.forEach { 86 | val className = it["entry"] as String 87 | (it["archiveUris"] as List).forEach { uri -> 88 | val artifactInfo = uri.toArtifactInfo(className) 89 | result.addArtifactInfo(artifactInfo) 90 | } 91 | } 92 | return result 93 | } 94 | 95 | private fun httpURLConnection(url: String): HttpURLConnection { 96 | val settings = Settings.getInstance() 97 | return if (settings.artifactoryUsername.isNotBlank() && settings.artifactoryPassword.isNotBlank()) { 98 | getConnection(url, mapOf("Authorization" to listOf("Basic " + Base64.getEncoder().encodeToString("${settings.artifactoryUsername}:${settings.artifactoryPassword}".toByteArray())))) 99 | } else 100 | getConnection(url) 101 | } 102 | 103 | private fun String.toArtifactInfo(className: String = "", onlyGroupFn: (String) -> Boolean = { false }): ArtifactInfo { 104 | ///artifactory/api/storage/third-party-releases-local/org/apache/jackrabbit/jackrabbit-core/1.2.3/jackrabbit-core-1.2.3.jar 105 | var artifactString = this.substringAfter("/api/storage/") 106 | val repo = artifactString.substringBefore("/") 107 | artifactString = artifactString.substringAfter("/").substringBeforeLast("/") 108 | var version = artifactString.substringAfterLast("/") 109 | artifactString = artifactString.substringBeforeLast("/") 110 | val groupId = artifactString.substringBeforeLast("/").replace("/", ".") 111 | val onlyGroup = onlyGroupFn(groupId) 112 | val artifactId = if (onlyGroup) "" else artifactString.substringAfterLast("/") 113 | if (onlyGroup) { 114 | version = "" 115 | } 116 | val owner = "artifactory" 117 | return ArtifactInfo(groupId, artifactId, version, "$repo${if (owner.isNotBlank()) " by $owner" else ""}", "${Settings.getInstance().nexusSearchUrl}/$repo", true, className) 118 | } 119 | 120 | override fun handleEmptyResultByClassName(searchParam: ClassNameSearchParam, project: Project): Collection { 121 | return MavenCentralSearcher.searchByClassName(searchParam, project) 122 | } 123 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/search/Util.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.search 18 | 19 | import cn.bestwu.gdph.browseNotification 20 | import cn.bestwu.gdph.show 21 | import com.intellij.notification.NotificationType 22 | import com.intellij.openapi.project.Project 23 | import groovy.json.JsonSlurper 24 | import java.io.InputStream 25 | import java.net.HttpURLConnection 26 | import java.net.URI 27 | import java.util.* 28 | 29 | /** 30 | * 31 | * @author Peter Wu 32 | * @since 33 | */ 34 | val regex = Regex("\\{\"id\":\"(.*?):(.*?):(.*?)\",") 35 | 36 | fun getConnection(spec: String, requestProperties: Map> = mapOf()): HttpURLConnection { 37 | val url = URI(spec).toURL() 38 | val httpURLConnection = url.openConnection() as HttpURLConnection 39 | httpURLConnection.connectTimeout = 4000 40 | httpURLConnection.readTimeout = 4000 41 | requestProperties.forEach { (t, u) -> 42 | u.forEach { 43 | httpURLConnection.addRequestProperty(t, it) 44 | } 45 | } 46 | return httpURLConnection 47 | } 48 | 49 | private fun getResponse(connection: HttpURLConnection, project: Project): InputStream? { 50 | try { 51 | if (connection.responseCode != 200) { 52 | val responseText = (connection.errorStream?.bufferedReader()?.readText() 53 | ?: connection.inputStream.bufferedReader().readText()) 54 | if (connection.responseCode != 404 && (connection.responseCode != 400 || !responseText.contains("This REST API is available only in Artifactory Pro"))) 55 | show(project, "response:$responseText.", "No dependencies found", NotificationType.WARNING) 56 | return null 57 | } 58 | return connection.inputStream 59 | } catch (e: Exception) { 60 | val url = connection.url.toString() 61 | browseNotification(project, "Request timeout", url) 62 | return null 63 | } 64 | } 65 | 66 | internal fun getResponseText(connection: HttpURLConnection, project: Project): String? { 67 | try { 68 | val stream = getResponse(connection, project) ?: return null 69 | return stream.bufferedReader().readText() 70 | } catch (e: Exception) { 71 | val url = connection.url.toString() 72 | browseNotification(project, "Request timeout", url) 73 | return null 74 | } 75 | } 76 | 77 | internal fun getResponseJson(connection: HttpURLConnection, project: Project): Any? { 78 | try { 79 | val stream = getResponse(connection, project) ?: return null 80 | return JsonSlurper().parse(stream) 81 | } catch (e: Exception) { 82 | val url = connection.url.toString() 83 | browseNotification(project, "Request timeout", url) 84 | return null 85 | } 86 | } 87 | 88 | 89 | internal fun getAliRepos(): List { 90 | try { 91 | val connection = getConnection("https://maven.aliyun.com/repo/list?_input_charset=utf-8") 92 | if (connection.responseCode == 200) { 93 | val inputStream = connection.inputStream 94 | val jsonResult = JsonSlurper().parse(inputStream) 95 | @Suppress("UNCHECKED_CAST") 96 | return ((jsonResult as Map<*, *>)["object"] as List>).map { it["repoId"].toString() } 97 | } 98 | } catch (_: Exception) { 99 | } 100 | return emptyList() 101 | } 102 | 103 | 104 | internal val versionTails = arrayOf("SNAPSHOTS", "ALPHA", "BETA", "M", "RC", "RELEASE") 105 | internal val versionTailRegex = "^([A-Za-z]+?)(\\d*)$".toRegex() 106 | 107 | /** 108 | * 比较版本信息 109 | 110 | * @param version1 版本1 111 | * * 112 | * @param version2 版本2 113 | * * 114 | * @return int 115 | */ 116 | fun compareVersion(version1: String, version2: String): Int { 117 | if (version1 == version2) { 118 | return 0 119 | } 120 | val separator = "[.-]" 121 | val version1s = version1.split(separator.toRegex()).toMutableList() 122 | val version2s = version2.split(separator.toRegex()).toMutableList() 123 | 124 | if (version1s.size < version2s.size) { 125 | version1s.addAll(List(version2s.size - version1s.size) { "" }) 126 | } else { 127 | version2s.addAll(List(version1s.size - version2s.size) { "" }) 128 | } 129 | val length = version1s.size 130 | 131 | for (i in 0 until length) { 132 | val toIntOrNull2 = version2s[i].toIntOrNull() 133 | val toIntOrNull1 = version1s[i].toIntOrNull() 134 | if (toIntOrNull1 == null && toIntOrNull2 != null) 135 | return -1 136 | else if (toIntOrNull1 != null && toIntOrNull2 == null) 137 | return 1 138 | var v2 = toIntOrNull2 139 | ?: versionTails.indexOf( 140 | version2s[i].replace(versionTailRegex, "$1").uppercase(Locale.getDefault()) 141 | ) 142 | var v1 = toIntOrNull1 143 | ?: versionTails.indexOf( 144 | version1s[i].replace(versionTailRegex, "$1").uppercase(Locale.getDefault()) 145 | ) 146 | if (v1 != -1 && v1 == v2 && toIntOrNull1 == null) { 147 | v2 = version2s[i].replace(versionTailRegex, "$2").toIntOrNull() ?: 0 148 | v1 = version1s[i].replace(versionTailRegex, "$2").toIntOrNull() ?: 0 149 | } 150 | if (v1 == -1 || v2 == -1) { 151 | val result = version1s[i].compareTo(version2s[i]) 152 | if (result != 0) { 153 | return result 154 | } 155 | } 156 | if (v2 > v1) { 157 | return -1 158 | } else if (v2 < v1) { 159 | return 1 160 | } 161 | // 相等 比较下一组值 162 | } 163 | return 0 164 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/kotlin/GradleKtsPluginsCompletionContributor.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.kotlin 18 | 19 | import cn.bestwu.gdph.AbstractGradlePluginsCompletionContributor 20 | import cn.bestwu.gdph.browseNotification 21 | import cn.bestwu.gdph.contributorDuringCompletion 22 | import cn.bestwu.gdph.insertHandler 23 | import cn.bestwu.gdph.search.GradlePluginsSearcher 24 | import com.intellij.codeInsight.completion.* 25 | import com.intellij.codeInsight.lookup.LookupElementBuilder 26 | import com.intellij.icons.AllIcons 27 | import com.intellij.patterns.PlatformPatterns 28 | import com.intellij.patterns.StandardPatterns 29 | import com.intellij.psi.PsiElement 30 | import com.intellij.util.ProcessingContext 31 | import org.jetbrains.kotlin.psi.KtCallExpression 32 | import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry 33 | import org.jetbrains.kotlin.psi.KtParenthesizedExpression 34 | import java.net.SocketTimeoutException 35 | 36 | class GradleKtsPluginsCompletionContributor : AbstractGradlePluginsCompletionContributor() { 37 | object Util { 38 | val kotlinRegex = "^kotlin\\(\"(.*?)\".*$".toRegex() 39 | } 40 | 41 | companion object { 42 | const val KOTLIN_PREFIX = "org.jetbrains.kotlin." 43 | } 44 | 45 | init { 46 | extend( 47 | CompletionType.SMART, 48 | PlatformPatterns.psiElement(PsiElement::class.java) 49 | .and( 50 | PlatformPatterns.psiElement().inFile( 51 | PlatformPatterns.psiFile() 52 | .withName(StandardPatterns.string().endsWith(".gradle.kts")) 53 | ) 54 | ) 55 | .withParent(KtLiteralStringTemplateEntry::class.java) 56 | .withAncestor( 57 | 15, PlatformPatterns.psiElement(KtCallExpression::class.java) 58 | .withText(StandardPatterns.string().startsWith(PLUGINS_EXTENSION)) 59 | ), CompletionPluginsCompletionProvider() 60 | ) 61 | } 62 | 63 | override fun duringCompletion(context: CompletionInitializationContext) = 64 | contributorDuringCompletion(context) 65 | 66 | private class CompletionPluginsCompletionProvider : CompletionProvider() { 67 | override fun addCompletions( 68 | params: CompletionParameters, 69 | context: ProcessingContext, 70 | result: CompletionResultSet 71 | ) { 72 | try { 73 | var parent = params.position.parent?.parent?.parent 74 | result.stopHere() 75 | if (parent is KtParenthesizedExpression) { 76 | parent = parent.parent 77 | } 78 | val isVersion = parent != null && parent.text.contains("version") 79 | val text = parent!!.text.trim('"') 80 | var allText = "" 81 | var searchText = if (isVersion) { 82 | if (text.startsWith("kotlin")) { 83 | text.replace(Util.kotlinRegex, "$KOTLIN_PREFIX$1") 84 | } else 85 | text.replace(versionRegex, "$1") 86 | } else { 87 | allText = 88 | text.replace(idRegex, "$1").substringBefore("IntellijIdeaRulezzz$").trim() 89 | allText.substringBeforeLast(".") 90 | } 91 | val isKotlin: Boolean 92 | if (!isVersion && parent.parent?.parent is KtCallExpression && parent.parent.parent.firstChild.text == "kotlin") { 93 | isKotlin = true 94 | searchText = KOTLIN_PREFIX 95 | allText = KOTLIN_PREFIX + allText 96 | } else { 97 | isKotlin = false 98 | } 99 | 100 | val searchResult: List 101 | var completionResultSet = result 102 | if (isVersion) { 103 | searchResult = GradlePluginsSearcher.searchPluginVersions(searchText) 104 | completionResultSet = result.withRelevanceSorter(completionSorter(searchResult)) 105 | } else { 106 | if (searchText.length < 2) { 107 | return 108 | } 109 | searchResult = 110 | searchResultFix(GradlePluginsSearcher.searchPlugins(searchText), allText) 111 | } 112 | 113 | searchResult.forEach { 114 | val lookupElementBuilder = 115 | if (isKotlin) { 116 | LookupElementBuilder.create(it.substringAfter(KOTLIN_PREFIX)) 117 | .withPresentableText( 118 | it.replace( 119 | GradlePluginsSearcher.SPLIT_RULE, 120 | ":" 121 | ) 122 | ).withIcon(AllIcons.Nodes.PpLib).withInsertHandler(insertHandler) 123 | } else { 124 | if (isVersion) LookupElementBuilder.create(it) 125 | .withIcon(AllIcons.Nodes.PpLib) 126 | .withInsertHandler(insertHandler) else LookupElementBuilder.create( 127 | it 128 | ).withPresentableText(it.replace(GradlePluginsSearcher.SPLIT_RULE, ":")) 129 | .withIcon(AllIcons.Nodes.PpLib).withInsertHandler(insertHandler) 130 | } 131 | completionResultSet.addElement(lookupElementBuilder) 132 | } 133 | } catch (e: SocketTimeoutException) { 134 | val url = "https://plugins.gradle.org/search" 135 | browseNotification(params.position.project, "Request timeout", url) 136 | } 137 | } 138 | 139 | 140 | } 141 | } -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/kotlin/GradleKtsDependenciesCompletionContributor.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph.kotlin 18 | 19 | import cn.bestwu.gdph.* 20 | import cn.bestwu.gdph.search.* 21 | import com.intellij.codeInsight.completion.* 22 | import com.intellij.codeInsight.lookup.LookupElement 23 | import com.intellij.codeInsight.lookup.LookupElementBuilder 24 | import com.intellij.codeInsight.lookup.LookupElementWeigher 25 | import com.intellij.icons.AllIcons 26 | import com.intellij.patterns.PatternCondition 27 | import com.intellij.patterns.PlatformPatterns 28 | import com.intellij.patterns.PsiElementPattern 29 | import com.intellij.patterns.StandardPatterns 30 | import com.intellij.psi.PsiElement 31 | import com.intellij.psi.util.PsiTreeUtil 32 | import com.intellij.util.ProcessingContext 33 | import org.jetbrains.kotlin.psi.KtCallExpression 34 | import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry 35 | 36 | class GradleKtsDependenciesCompletionContributor : CompletionContributor() { 37 | 38 | init { 39 | // group:name:version notation 40 | // e.g.: 41 | // compile('junit:junit:4.11') 42 | extend(CompletionType.SMART, Util.IN_METHOD_DEPENDENCY_NOTATION, object : CompletionProvider() { 43 | override fun addCompletions(params: CompletionParameters, 44 | context: ProcessingContext, 45 | result: CompletionResultSet) { 46 | result.stopHere() 47 | val text = trim(params.originalPosition?.text ?: "") 48 | val prefix = params.position.text.substringBefore("IntellijIdeaRulezzz").trim() 49 | val iSearchParam: ISearchParam 50 | var isKotlin = false 51 | var isVersion = false 52 | var searchResult = if (text.startsWith("c:", true) || text.startsWith("fc:", true)) { 53 | val classNameSearchParam = ClassNameSearchParam(text) 54 | if (classNameSearchParam.q.length < 2) 55 | return 56 | iSearchParam = classNameSearchParam 57 | GradleArtifactSearcher.searchByClassName(classNameSearchParam, params.position.project) 58 | } else { 59 | val searchParam = if (text.contains(":") && !prefix.contains(":")) { 60 | SearchParam(prefix, "", fg = false, fa = false) 61 | } else { 62 | val parent = params.position.parent.parent.parent 63 | val pText = parent.parent.parent.text 64 | if (pText.startsWith("kotlin")) { 65 | isKotlin = true 66 | if ("(" != parent.prevSibling.text) { 67 | isVersion = true 68 | SearchParam(KOTLIN_GROUP, pText.replace(GradleKtsPluginsCompletionContributor.Util.kotlinRegex, "$KOTLIN_ARTIFACT_PREFIX$1").trim(), fg = true, fa = true) 69 | } else { 70 | SearchParam(KOTLIN_GROUP, "$KOTLIN_ARTIFACT_PREFIX$prefix", fg = true, fa = false) 71 | } 72 | } else 73 | toSearchParam(prefix) 74 | } 75 | if (searchParam.src.length < 2) 76 | return 77 | iSearchParam = searchParam 78 | GradleArtifactSearcher.search(searchParam, params.position.project) 79 | } 80 | if (searchResult.isEmpty()) { 81 | browseNotification(params.position.project, iSearchParam.docText, iSearchParam.docUrl, "No dependencies found") 82 | } 83 | if (isKotlin) { 84 | if (!isVersion) 85 | searchResult.forEach { it.version = "" } 86 | searchResult = searchResult.filter { it.gav.startsWith(KOTLIN_PREFIX) }.toSet() 87 | } 88 | var completionResultSet = if (isVersion) result.withRelevanceSorter( 89 | CompletionSorter.emptySorter().weigh(object : LookupElementWeigher("gradleDependencyWeigher") { 90 | override fun weigh(element: LookupElement): Comparable<*> { 91 | return VersionComparator(searchResult.indexOfFirst { it.version == element.lookupString }) 92 | } 93 | }) 94 | ) else result.withRelevanceSorter( 95 | CompletionSorter.emptySorter().weigh(object : LookupElementWeigher("gradleDependencyWeigher") { 96 | override fun weigh(element: LookupElement): Comparable<*> { 97 | return VersionComparator(searchResult.indexOfFirst { it.gav == (if (isKotlin) KOTLIN_PREFIX + element.lookupString else element.lookupString) }) 98 | } 99 | }) 100 | ) 101 | if (iSearchParam is ClassNameSearchParam) { 102 | completionResultSet = completionResultSet.withPrefixMatcher(PrefixMatcher.ALWAYS_TRUE) 103 | } 104 | searchResult.forEach { 105 | val lookupElementBuilder = 106 | if (isKotlin) { 107 | if (isVersion) 108 | LookupElementBuilder.create(it.version).withPresentableText(it.version).withIcon(AllIcons.Nodes.PpLib).withTypeText(it.repoType, repoIcon, true).withInsertHandler(insertHandler) 109 | else 110 | LookupElementBuilder.create(it.gav.substringAfter(KOTLIN_PREFIX)).withPresentableText(it.gav).withTailText(it.className).withIcon(AllIcons.Nodes.PpLib).withTypeText(it.repoType, repoIcon, true).withInsertHandler(insertHandler) 111 | } else 112 | LookupElementBuilder.create("${it.gav}${if (it.artifactId.isBlank()) ":" else ""}").withPresentableText(it.gav).withTailText(it.className).withIcon(AllIcons.Nodes.PpLib).withTypeText(it.repoType, repoIcon, true).withInsertHandler(insertHandler) 113 | completionResultSet.addElement(lookupElementBuilder) 114 | } 115 | } 116 | }) 117 | } 118 | 119 | override fun duringCompletion(context: CompletionInitializationContext) = contributorDuringCompletion(context) 120 | 121 | object Util { 122 | private val DEPENDENCIES_CALL_PATTERN = PlatformPatterns.psiElement() 123 | .inside(true, PlatformPatterns.psiElement(KtCallExpression::class.java).with( 124 | object : PatternCondition("withInvokedExpressionText") { 125 | override fun accepts(expression: KtCallExpression, context: ProcessingContext): Boolean { 126 | if (checkExpression(expression)) return true 127 | return checkExpression(PsiTreeUtil.getParentOfType(expression, KtCallExpression::class.java)) 128 | } 129 | 130 | private fun checkExpression(expression: KtCallExpression?): Boolean { 131 | if (expression == null) return false 132 | val grExpression = expression.firstChild ?: return false 133 | return DEPENDENCIES_SCRIPT_BLOCK == grExpression.text || "imports" == grExpression.text 134 | } 135 | })) 136 | val IN_METHOD_DEPENDENCY_NOTATION: PsiElementPattern.Capture = PlatformPatterns.psiElement() 137 | .withParent(KtLiteralStringTemplateEntry::class.java) 138 | .and(PlatformPatterns.psiElement().inFile(PlatformPatterns.psiFile().withName(StandardPatterns.string().endsWith(".gradle.kts")))) 139 | .and(DEPENDENCIES_CALL_PATTERN) 140 | } 141 | 142 | companion object { 143 | private const val DEPENDENCIES_SCRIPT_BLOCK = "dependencies" 144 | const val KOTLIN_PREFIX = "org.jetbrains.kotlin:kotlin-" 145 | const val KOTLIN_ARTIFACT_PREFIX = "kotlin-" 146 | const val KOTLIN_GROUP = "org.jetbrains.kotlin" 147 | 148 | } 149 | } 150 | 151 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /src/test/resources/result.json: -------------------------------------------------------------------------------- 1 | {"responseHeader":{"status":0,"QTime":22,"params":{"fl":"id,g,a,v,p,ec,timestamp,tags","sort":"score desc,timestamp desc,g asc,a asc,v desc","hl.snippets":"3","indent":"off","q":"fc:\"feign.Client\"","core":"gav","hl.fl":"fch","wt":"json","hl":"true","rows":"30","version":"2.2"}},"response":{"numFound":111,"start":0,"docs":[{"id":"io.github.openfeign:feign-core:9.5.1","g":"io.github.openfeign","a":"feign-core","v":"9.5.1","p":"jar","timestamp":1501638223000,"tags":["feign","core"],"ec":["-sources.jar","-javadoc.jar",".jar","-tests.jar",".pom"]},{"id":"io.github.openfeign:feign-core:9.5.0","g":"io.github.openfeign","a":"feign-core","v":"9.5.0","p":"jar","timestamp":1494057013000,"tags":["feign","core"],"ec":["-sources.jar","-javadoc.jar",".jar","-tests.jar",".pom"]},{"id":"io.github.openfeign:feign-core:9.4.0","g":"io.github.openfeign","a":"feign-core","v":"9.4.0","p":"jar","timestamp":1478630957000,"tags":["feign","core"],"ec":["-sources.jar","-javadoc.jar","-tests.jar",".jar",".pom"]},{"id":"io.github.openfeign:feign-core:9.3.1","g":"io.github.openfeign","a":"feign-core","v":"9.3.1","p":"jar","timestamp":1472535150000,"tags":["feign","core"],"ec":["-sources.jar","-javadoc.jar",".jar","-tests.jar",".pom"]},{"id":"io.github.openfeign:feign-core:9.3.0","g":"io.github.openfeign","a":"feign-core","v":"9.3.0","p":"jar","timestamp":1471611424000,"tags":["feign","core"],"ec":["-sources.jar","-javadoc.jar","-tests.jar",".jar",".pom"]},{"id":"io.github.openfeign:feign-core:9.2.0","g":"io.github.openfeign","a":"feign-core","v":"9.2.0","p":"jar","timestamp":1471520866000,"tags":["feign","core"],"ec":["-javadoc.jar","-sources.jar","-tests.jar",".jar",".pom"]},{"id":"io.github.openfeign:feign-core:9.1.0","g":"io.github.openfeign","a":"feign-core","v":"9.1.0","p":"jar","timestamp":1470666876000,"tags":["feign","core"],"ec":["-javadoc.jar","-sources.jar",".jar","-tests.jar",".pom"]},{"id":"io.github.openfeign:feign-core:9.0.0","g":"io.github.openfeign","a":"feign-core","v":"9.0.0","p":"jar","timestamp":1468470864000,"tags":["feign","core"],"ec":["-javadoc.jar","-sources.jar","-tests.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:6.1.3","g":"com.netflix.feign","a":"feign-core","v":"6.1.3","p":"jar","timestamp":1408469279000,"tags":["feign","developed","core","netflix"],"ec":["-javadoc.jar","-sources.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:6.1.2","g":"com.netflix.feign","a":"feign-core","v":"6.1.2","p":"jar","timestamp":1396371784000,"tags":["feign","developed","core","netflix"],"ec":["-sources.jar","-javadoc.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:6.1.1","g":"com.netflix.feign","a":"feign-core","v":"6.1.1","p":"jar","timestamp":1392757904000,"tags":["feign","developed","core","netflix"],"ec":["-javadoc.jar","-sources.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:6.0.1","g":"com.netflix.feign","a":"feign-core","v":"6.0.1","p":"jar","timestamp":1384465993000,"tags":["feign","developed","core","netflix"],"ec":["-javadoc.jar","-sources.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:5.4.1","g":"com.netflix.feign","a":"feign-core","v":"5.4.1","p":"jar","timestamp":1384463562000,"tags":["feign","developed","core","netflix"],"ec":["-sources.jar","-javadoc.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:6.0.0","g":"com.netflix.feign","a":"feign-core","v":"6.0.0","p":"jar","timestamp":1383799714000,"tags":["feign","developed","core","netflix"],"ec":["-javadoc.jar","-sources.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:5.4.0","g":"com.netflix.feign","a":"feign-core","v":"5.4.0","p":"jar","timestamp":1383768943000,"tags":["feign","developed","core","netflix"],"ec":["-javadoc.jar","-sources.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:5.3.0","g":"com.netflix.feign","a":"feign-core","v":"5.3.0","p":"jar","timestamp":1379981203000,"tags":["feign","developed","core","netflix"],"ec":["-sources.jar","-javadoc.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:5.2.0","g":"com.netflix.feign","a":"feign-core","v":"5.2.0","p":"jar","timestamp":1379889972000,"tags":["feign","developed","core","netflix"],"ec":["-javadoc.jar","-sources.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:5.1.0","g":"com.netflix.feign","a":"feign-core","v":"5.1.0","p":"jar","timestamp":1379781976000,"tags":["feign","developed","core","netflix"],"ec":["-sources.jar","-javadoc.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:5.0.1","g":"com.netflix.feign","a":"feign-core","v":"5.0.1","p":"jar","timestamp":1379462737000,"tags":["feign","developed","core","netflix"],"ec":["-javadoc.jar","-sources.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:5.0.0","g":"com.netflix.feign","a":"feign-core","v":"5.0.0","p":"jar","timestamp":1379451293000,"tags":["feign","developed","core","netflix"],"ec":["-sources.jar","-javadoc.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:2.0.1","g":"com.netflix.feign","a":"feign-core","v":"2.0.1","p":"jar","timestamp":1373844458000,"tags":["feign","developed","core","netflix"],"ec":["-javadoc.jar","-sources.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:2.0.0","g":"com.netflix.feign","a":"feign-core","v":"2.0.0","p":"jar","timestamp":1372698348000,"tags":["feign","developed","core","netflix"],"ec":["-javadoc.jar","-sources.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:1.1.1","g":"com.netflix.feign","a":"feign-core","v":"1.1.1","p":"jar","timestamp":1372691784000,"tags":["feign","developed","core","netflix"],"ec":["-sources.jar","-javadoc.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:1.0.0","g":"com.netflix.feign","a":"feign-core","v":"1.0.0","p":"jar","timestamp":1372296642000,"tags":["feign","developed","core","netflix"],"ec":["-sources.jar","-javadoc.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:8.18.0","g":"com.netflix.feign","a":"feign-core","v":"8.18.0","p":"jar","timestamp":1468464572000,"tags":["feign","core"],"ec":["-javadoc.jar","-sources.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:8.17.0","g":"com.netflix.feign","a":"feign-core","v":"8.17.0","p":"jar","timestamp":1465085598000,"tags":["feign","core"],"ec":["-javadoc.jar","-sources.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:8.16.2","g":"com.netflix.feign","a":"feign-core","v":"8.16.2","p":"jar","timestamp":1461394026000,"tags":["feign","core"],"ec":["-sources.jar","-javadoc.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:8.16.1","g":"com.netflix.feign","a":"feign-core","v":"8.16.1","p":"jar","timestamp":1461335244000,"tags":["feign","core"],"ec":["-sources.jar","-javadoc.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:8.16.0","g":"com.netflix.feign","a":"feign-core","v":"8.16.0","p":"jar","timestamp":1459559673000,"tags":["feign","core"],"ec":["-sources.jar","-javadoc.jar",".jar",".pom"]},{"id":"com.netflix.feign:feign-core:8.15.1","g":"com.netflix.feign","a":"feign-core","v":"8.15.1","p":"jar","timestamp":1457492349000,"tags":["feign","core"],"ec":["-javadoc.jar","-sources.jar",".jar",".pom"]}]},"highlighting":{"io.github.openfeign:feign-core:9.5.1":{"fch":["feign<\/em>.client<\/em>.TrustingSSLSocketFactory","feign<\/em>.client<\/em>.DefaultClientTest","feign<\/em>.Client<\/em>"]},"io.github.openfeign:feign-core:9.5.0":{"fch":["feign<\/em>.client<\/em>.TrustingSSLSocketFactory","feign<\/em>.client<\/em>.DefaultClientTest","feign<\/em>.Client<\/em>"]},"io.github.openfeign:feign-core:9.4.0":{"fch":["feign<\/em>.client<\/em>.TrustingSSLSocketFactory","feign<\/em>.client<\/em>.DefaultClientTest","feign<\/em>.Client<\/em>"]},"io.github.openfeign:feign-core:9.3.1":{"fch":["feign<\/em>.client<\/em>.TrustingSSLSocketFactory","feign<\/em>.client<\/em>.DefaultClientTest","feign<\/em>.Client<\/em>"]},"io.github.openfeign:feign-core:9.3.0":{"fch":["feign<\/em>.client<\/em>.DefaultClientTest","feign<\/em>.client<\/em>.TrustingSSLSocketFactory","feign<\/em>.Client<\/em>"]},"io.github.openfeign:feign-core:9.2.0":{"fch":["feign<\/em>.client<\/em>.TrustingSSLSocketFactory","feign<\/em>.client<\/em>.DefaultClientTest","feign<\/em>.Client<\/em>"]},"io.github.openfeign:feign-core:9.1.0":{"fch":["feign<\/em>.client<\/em>.DefaultClientTest","feign<\/em>.client<\/em>.TrustingSSLSocketFactory","feign<\/em>.Client<\/em>"]},"io.github.openfeign:feign-core:9.0.0":{"fch":["feign<\/em>.client<\/em>.DefaultClientTest","feign<\/em>.client<\/em>.TrustingSSLSocketFactory","feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:6.1.3":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:6.1.2":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:6.1.1":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:6.0.1":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:5.4.1":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:6.0.0":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:5.4.0":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:5.3.0":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:5.2.0":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:5.1.0":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:5.0.1":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:5.0.0":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:2.0.1":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:2.0.0":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:1.1.1":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:1.0.0":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:8.18.0":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:8.17.0":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:8.16.2":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:8.16.1":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:8.16.0":{"fch":["feign<\/em>.Client<\/em>"]},"com.netflix.feign:feign-core:8.15.1":{"fch":["feign<\/em>.Client<\/em>"]}}} -------------------------------------------------------------------------------- /src/main/java/cn/bestwu/gdph/config/ConfigurationView.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 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/main/kotlin/cn/bestwu/gdph/GradleDependenciesCompletionContributor.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Peter Wu 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 cn.bestwu.gdph 18 | 19 | import cn.bestwu.gdph.search.* 20 | import com.intellij.codeInsight.completion.* 21 | import com.intellij.codeInsight.lookup.LookupElement 22 | import com.intellij.codeInsight.lookup.LookupElementBuilder 23 | import com.intellij.codeInsight.lookup.LookupElementWeigher 24 | import com.intellij.icons.AllIcons 25 | import com.intellij.patterns.ElementPattern 26 | import com.intellij.patterns.PatternCondition 27 | import com.intellij.patterns.PlatformPatterns.psiElement 28 | import com.intellij.patterns.PlatformPatterns.psiFile 29 | import com.intellij.patterns.PsiElementPattern 30 | import com.intellij.patterns.StandardPatterns.string 31 | import com.intellij.psi.PsiElement 32 | import com.intellij.psi.util.PsiTreeUtil 33 | import com.intellij.util.ProcessingContext 34 | import org.jetbrains.plugins.gradle.util.GradleConstants 35 | import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument 36 | import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral 37 | import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringContent 38 | import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression 39 | import org.jetbrains.plugins.groovy.lang.psi.api.util.GrNamedArgumentsOwner 40 | import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.literals.GrLiteralImpl 41 | 42 | class GradleDependenciesCompletionContributor : CompletionContributor() { 43 | 44 | init { 45 | // map-style notation: 46 | // e.g.: 47 | // compile group: 'com.google.code.guice', name: 'guice', version: '1.0' 48 | // runtime([group:'junit', name:'junit-dep', version:'4.7']) 49 | // compile(group:'junit', name:'junit-dep', version:'4.7') 50 | extend(CompletionType.SMART, Util.IN_MAP_DEPENDENCY_NOTATION, object : CompletionProvider() { 51 | override fun addCompletions(params: CompletionParameters, 52 | context: ProcessingContext, 53 | result: CompletionResultSet) { 54 | val parent = params.position.parent?.parent 55 | if (parent !is GrNamedArgument || parent.parent !is GrNamedArgumentsOwner) { 56 | return 57 | } 58 | result.stopHere() 59 | val searchText = CompletionUtil.findReferenceOrAlphanumericPrefix(params).trim() 60 | val searchParam: SearchParam = when (parent.labelName) { 61 | GROUP_LABEL -> SearchParam(searchText, "", fg = false, fa = false) 62 | NAME_LABEL -> { 63 | val groupId = Util.findNamedArgumentValue(parent.parent as GrNamedArgumentsOwner, GROUP_LABEL) 64 | ?: return 65 | SearchParam(groupId, searchText, fg = groupId.isNotBlank(), fa = false) 66 | } 67 | 68 | VERSION_LABEL -> { 69 | val namedArgumentsOwner = parent.parent as GrNamedArgumentsOwner 70 | val groupId = Util.findNamedArgumentValue(namedArgumentsOwner, GROUP_LABEL) 71 | ?: return 72 | val artifactId = Util.findNamedArgumentValue(namedArgumentsOwner, NAME_LABEL) 73 | ?: return 74 | SearchParam(groupId, artifactId, fg = groupId.isNotBlank(), fa = artifactId.isNotBlank()) 75 | } 76 | 77 | else -> return 78 | } 79 | if (searchParam.toId().length < 2) 80 | return 81 | val searchResult = GradleArtifactSearcher.search(searchParam, params.position.project) 82 | if (searchResult.isEmpty()) { 83 | browseNotification(params.position.project, searchParam.docText, searchParam.docUrl, "No dependencies found") 84 | } 85 | 86 | var completionResultSet = result 87 | when (parent.labelName) { 88 | GROUP_LABEL -> { 89 | searchResult.forEach { 90 | completionResultSet.addElement(LookupElementBuilder.create(it.groupId).withIcon(AllIcons.Nodes.PpLib).withTypeText(it.repoType, repoIcon, true).withInsertHandler(insertHandler)) 91 | } 92 | } 93 | 94 | NAME_LABEL -> { 95 | searchResult.forEach { 96 | completionResultSet.addElement(LookupElementBuilder.create(it.artifactId).withIcon(AllIcons.Nodes.PpLib).withTypeText(it.repoType, repoIcon, true).withInsertHandler(insertHandler)) 97 | } 98 | } 99 | 100 | VERSION_LABEL -> { 101 | completionResultSet = result.withRelevanceSorter( 102 | CompletionSorter.emptySorter().weigh(object : LookupElementWeigher("gradleDependencyWeigher") { 103 | override fun weigh(element: LookupElement): Comparable<*> { 104 | return VersionComparator(searchResult.indexOfFirst { it.version == element.lookupString }) 105 | } 106 | }) 107 | ) 108 | searchResult.forEach { 109 | completionResultSet.addElement(LookupElementBuilder.create(it.version).withIcon(AllIcons.Nodes.PpLib).withTypeText(it.repoType, repoIcon, true).withInsertHandler(insertHandler)) 110 | } 111 | } 112 | 113 | else -> return 114 | } 115 | 116 | 117 | } 118 | }) 119 | 120 | // group:name:version notation 121 | // e.g.: 122 | // compile 'junit:junit:4.11' 123 | // compile('junit:junit:4.11') 124 | extend(CompletionType.SMART, Util.IN_METHOD_DEPENDENCY_NOTATION, object : CompletionProvider() { 125 | override fun addCompletions(params: CompletionParameters, 126 | context: ProcessingContext, 127 | result: CompletionResultSet) { 128 | val parent = params.position.parent 129 | if (parent !is GrLiteral && parent !is GrStringContent) return 130 | result.stopHere() 131 | val prefix: String = if (parent is GrStringContent) { 132 | params.position.text.substringBefore("IntellijIdeaRulezzz ").trim() 133 | } else 134 | CompletionUtil.findReferenceOrAlphanumericPrefix(params).trim() 135 | val text = trim(params.originalPosition?.text ?: "") 136 | val iSearchParam: ISearchParam 137 | val searchResult = if (text.startsWith("c:", true) || text.startsWith("fc:", true)) { 138 | val classNameSearchParam = ClassNameSearchParam(text) 139 | if (classNameSearchParam.q.length < 2) 140 | return 141 | iSearchParam = classNameSearchParam 142 | GradleArtifactSearcher.searchByClassName(classNameSearchParam, params.position.project) 143 | } else { 144 | val searchParam = if (text.contains(":") && !prefix.contains(":")) { 145 | SearchParam(prefix, "", fg = false, fa = false) 146 | } else { 147 | toSearchParam(prefix) 148 | } 149 | if (searchParam.src.length < 2) 150 | return 151 | iSearchParam = searchParam 152 | GradleArtifactSearcher.search(searchParam, params.position.project) 153 | } 154 | if (searchResult.isEmpty()) { 155 | browseNotification(params.position.project, iSearchParam.docText, iSearchParam.docUrl, "No dependencies found") 156 | } 157 | 158 | var completionResultSet = result.withRelevanceSorter( 159 | CompletionSorter.emptySorter().weigh(object : LookupElementWeigher("gradleDependencyWeigher") { 160 | override fun weigh(element: LookupElement): Comparable<*> { 161 | return VersionComparator(searchResult.indexOfFirst { it.gav == element.lookupString }) 162 | } 163 | }) 164 | ) 165 | if (iSearchParam is ClassNameSearchParam) { 166 | completionResultSet = completionResultSet.withPrefixMatcher(PrefixMatcher.ALWAYS_TRUE) 167 | } 168 | searchResult.forEach { 169 | completionResultSet.addElement(LookupElementBuilder.create("${it.gav}${if (it.artifactId.isBlank()) ":" else ""}").withPresentableText(it.gav).withTailText(it.className).withIcon(AllIcons.Nodes.PpLib).withTypeText(it.repoType, repoIcon, true).withInsertHandler(insertHandler)) 170 | } 171 | } 172 | }) 173 | } 174 | 175 | override fun duringCompletion(context: CompletionInitializationContext) = contributorDuringCompletion(context) 176 | 177 | object Util { 178 | private val GRADLE_FILE_PATTERN: ElementPattern = psiElement() 179 | .inFile(psiFile().withName(string().endsWith('.' + GradleConstants.EXTENSION))) 180 | 181 | fun findNamedArgumentValue(namedArgumentsOwner: GrNamedArgumentsOwner?, label: String): String? { 182 | val namedArgument = namedArgumentsOwner?.findNamedArgument(label) ?: return null 183 | return (namedArgument.expression as? GrLiteralImpl)?.value?.toString()?.trim() 184 | } 185 | 186 | private val DEPENDENCIES_CALL_PATTERN = psiElement() 187 | .inside(true, psiElement(GrMethodCallExpression::class.java).with( 188 | object : PatternCondition("withInvokedExpressionText") { 189 | override fun accepts(expression: GrMethodCallExpression, context: ProcessingContext): Boolean { 190 | if (checkExpression(expression)) return true 191 | return checkExpression(PsiTreeUtil.getParentOfType(expression, GrMethodCallExpression::class.java)) 192 | } 193 | 194 | private fun checkExpression(expression: GrMethodCallExpression?): Boolean { 195 | if (expression == null) return false 196 | val grExpression = expression.invokedExpression 197 | return DEPENDENCIES_SCRIPT_BLOCK == grExpression.text || "imports" == grExpression.text 198 | } 199 | })) 200 | val IN_MAP_DEPENDENCY_NOTATION: PsiElementPattern.Capture = psiElement() 201 | .and(GRADLE_FILE_PATTERN) 202 | .withParent(GrLiteral::class.java) 203 | .withSuperParent(2, psiElement(GrNamedArgument::class.java)) 204 | .and(DEPENDENCIES_CALL_PATTERN) 205 | val IN_METHOD_DEPENDENCY_NOTATION: PsiElementPattern.Capture = psiElement() 206 | .and(GRADLE_FILE_PATTERN) 207 | .withParent(GrLiteral::class.java) 208 | .and(DEPENDENCIES_CALL_PATTERN) 209 | } 210 | 211 | companion object { 212 | private const val GROUP_LABEL = "group" 213 | private const val NAME_LABEL = "name" 214 | private const val VERSION_LABEL = "version" 215 | private const val DEPENDENCIES_SCRIPT_BLOCK = "dependencies" 216 | 217 | } 218 | 219 | 220 | } 221 | 222 | --------------------------------------------------------------------------------