├── .editorconfig
├── .gitattributes
├── .gitignore
├── .idea
├── .gitignore
├── checkstyle-idea.xml
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── encodings.xml
├── inspectionProfiles
│ └── profiles_settings.xml
├── misc.xml
└── vcs.xml
├── LICENSE
├── README.md
├── build.gradle
├── checkstyle.xml
├── gradlew
├── gradlew.bat
├── screenshots
└── 1.gif
├── settings.gradle
└── src
└── main
├── java
└── com
│ └── jsdelivr
│ └── pluginintellij
│ ├── InsertModel.java
│ ├── JsDelivrPackageSearch.java
│ ├── actions
│ ├── OpenGithub.java
│ ├── OpenJsDelivr.java
│ └── OpenNpm.java
│ ├── packagefile
│ ├── FileInput.java
│ ├── FileThread.java
│ ├── PackageFiles.java
│ └── remotetypes
│ │ ├── ApiFilesError.java
│ │ ├── ApiPackageFile.java
│ │ └── ApiPackageFiles.java
│ ├── packageinsert
│ ├── InsertOptions.java
│ └── TextEdit.java
│ ├── packagename
│ ├── AlgoliaSearch.java
│ ├── ListNameItem.java
│ ├── NameInput.java
│ ├── NameThread.java
│ └── remotetypes
│ │ ├── AlgoliaPackage.java
│ │ └── AlgoliaRepository.java
│ ├── packageversion
│ └── VersionInput.java
│ └── ui
│ ├── DefaultListItem.java
│ ├── IJsDelivrListItem.java
│ ├── JsDelivrInput.java
│ ├── JsDelivrList.java
│ └── JsDelivrPopup.java
└── resources
└── META-INF
├── plugin.xml
└── pluginIcon.svg
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | trim_trailing_whitespace = true
8 | indent_style = tab
9 | indent_size = 4
10 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle/
2 | .local/
3 | .sync/
4 | build/
5 | gradle/
6 | out/
7 | local.properties
8 | plugin-intellij.iml
9 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
3 |
4 | # Sensitive or high-churn files
5 | /uiDesigner.xml
6 |
7 | # Gradle
8 | /gradle.xml
9 | /libraries
10 |
11 | # Gradle and Maven with auto-import
12 | /artifacts
13 | /compiler.xml
14 | /jarRepositories.xml
15 | /modules.xml
16 | /*.iml
17 | /modules
18 | *.iml
19 | *.ipr
20 |
--------------------------------------------------------------------------------
/.idea/checkstyle-idea.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Open Software License ("OSL") v. 3.0
2 |
3 | This Open Software License (the "License") applies to any original work of
4 | authorship (the "Original Work") whose owner (the "Licensor") has placed the
5 | following licensing notice adjacent to the copyright notice for the Original
6 | Work:
7 |
8 | Licensed under the Open Software License version 3.0
9 |
10 | 1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free,
11 | non-exclusive, sublicensable license, for the duration of the copyright, to do
12 | the following:
13 |
14 | a) to reproduce the Original Work in copies, either alone or as part of a
15 | collective work;
16 |
17 | b) to translate, adapt, alter, transform, modify, or arrange the Original
18 | Work, thereby creating derivative works ("Derivative Works") based upon the
19 | Original Work;
20 |
21 | c) to distribute or communicate copies of the Original Work and Derivative
22 | Works to the public, with the proviso that copies of Original Work or
23 | Derivative Works that You distribute or communicate shall be licensed under
24 | this Open Software License;
25 |
26 | d) to perform the Original Work publicly; and
27 |
28 | e) to display the Original Work publicly.
29 |
30 | 2) Grant of Patent License. Licensor grants You a worldwide, royalty-free,
31 | non-exclusive, sublicensable license, under patent claims owned or controlled
32 | by the Licensor that are embodied in the Original Work as furnished by the
33 | Licensor, for the duration of the patents, to make, use, sell, offer for sale,
34 | have made, and import the Original Work and Derivative Works.
35 |
36 | 3) Grant of Source Code License. The term "Source Code" means the preferred
37 | form of the Original Work for making modifications to it and all available
38 | documentation describing how to modify the Original Work. Licensor agrees to
39 | provide a machine-readable copy of the Source Code of the Original Work along
40 | with each copy of the Original Work that Licensor distributes. Licensor
41 | reserves the right to satisfy this obligation by placing a machine-readable
42 | copy of the Source Code in an information repository reasonably calculated to
43 | permit inexpensive and convenient access by You for as long as Licensor
44 | continues to distribute the Original Work.
45 |
46 | 4) Exclusions From License Grant. Neither the names of Licensor, nor the names
47 | of any contributors to the Original Work, nor any of their trademarks or
48 | service marks, may be used to endorse or promote products derived from this
49 | Original Work without express prior permission of the Licensor. Except as
50 | expressly stated herein, nothing in this License grants any license to
51 | Licensor's trademarks, copyrights, patents, trade secrets or any other
52 | intellectual property. No patent license is granted to make, use, sell, offer
53 | for sale, have made, or import embodiments of any patent claims other than the
54 | licensed claims defined in Section 2. No license is granted to the trademarks
55 | of Licensor even if such marks are included in the Original Work. Nothing in
56 | this License shall be interpreted to prohibit Licensor from licensing under
57 | terms different from this License any Original Work that Licensor otherwise
58 | would have a right to license.
59 |
60 | 5) External Deployment. The term "External Deployment" means the use,
61 | distribution, or communication of the Original Work or Derivative Works in any
62 | way such that the Original Work or Derivative Works may be used by anyone
63 | other than You, whether those works are distributed or communicated to those
64 | persons or made available as an application intended for use over a network.
65 | As an express condition for the grants of license hereunder, You must treat
66 | any External Deployment by You of the Original Work or a Derivative Work as a
67 | distribution under section 1(c).
68 |
69 | 6) Attribution Rights. You must retain, in the Source Code of any Derivative
70 | Works that You create, all copyright, patent, or trademark notices from the
71 | Source Code of the Original Work, as well as any notices of licensing and any
72 | descriptive text identified therein as an "Attribution Notice." You must cause
73 | the Source Code for any Derivative Works that You create to carry a prominent
74 | Attribution Notice reasonably calculated to inform recipients that You have
75 | modified the Original Work.
76 |
77 | 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
78 | the copyright in and to the Original Work and the patent rights granted herein
79 | by Licensor are owned by the Licensor or are sublicensed to You under the
80 | terms of this License with the permission of the contributor(s) of those
81 | copyrights and patent rights. Except as expressly stated in the immediately
82 | preceding sentence, the Original Work is provided under this License on an "AS
83 | IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without
84 | limitation, the warranties of non-infringement, merchantability or fitness for
85 | a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK
86 | IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this
87 | License. No license to the Original Work is granted by this License except
88 | under this disclaimer.
89 |
90 | 8) Limitation of Liability. Under no circumstances and under no legal theory,
91 | whether in tort (including negligence), contract, or otherwise, shall the
92 | Licensor be liable to anyone for any indirect, special, incidental, or
93 | consequential damages of any character arising as a result of this License or
94 | the use of the Original Work including, without limitation, damages for loss
95 | of goodwill, work stoppage, computer failure or malfunction, or any and all
96 | other commercial damages or losses. This limitation of liability shall not
97 | apply to the extent applicable law prohibits such limitation.
98 |
99 | 9) Acceptance and Termination. If, at any time, You expressly assented to this
100 | License, that assent indicates your clear and irrevocable acceptance of this
101 | License and all of its terms and conditions. If You distribute or communicate
102 | copies of the Original Work or a Derivative Work, You must make a reasonable
103 | effort under the circumstances to obtain the express assent of recipients to
104 | the terms of this License. This License conditions your rights to undertake
105 | the activities listed in Section 1, including your right to create Derivative
106 | Works based upon the Original Work, and doing so without honoring these terms
107 | and conditions is prohibited by copyright law and international treaty.
108 | Nothing in this License is intended to affect copyright exceptions and
109 | limitations (including "fair use" or "fair dealing"). This License shall
110 | terminate immediately and You may no longer exercise any of the rights granted
111 | to You by this License upon your failure to honor the conditions in Section
112 | 1(c).
113 |
114 | 10) Termination for Patent Action. This License shall terminate automatically
115 | and You may no longer exercise any of the rights granted to You by this
116 | License as of the date You commence an action, including a cross-claim or
117 | counterclaim, against Licensor or any licensee alleging that the Original Work
118 | infringes a patent. This termination provision shall not apply for an action
119 | alleging patent infringement by combinations of the Original Work with other
120 | software or hardware.
121 |
122 | 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this
123 | License may be brought only in the courts of a jurisdiction wherein the
124 | Licensor resides or in which Licensor conducts its primary business, and under
125 | the laws of that jurisdiction excluding its conflict-of-law provisions. The
126 | application of the United Nations Convention on Contracts for the
127 | International Sale of Goods is expressly excluded. Any use of the Original
128 | Work outside the scope of this License or after its termination shall be
129 | subject to the requirements and penalties of copyright or patent law in the
130 | appropriate jurisdiction. This section shall survive the termination of this
131 | License.
132 |
133 | 12) Attorneys' Fees. In any action to enforce the terms of this License or
134 | seeking damages relating thereto, the prevailing party shall be entitled to
135 | recover its costs and expenses, including, without limitation, reasonable
136 | attorneys' fees and costs incurred in connection with such action, including
137 | any appeal of such action. This section shall survive the termination of this
138 | License.
139 |
140 | 13) Miscellaneous. If any provision of this License is held to be
141 | unenforceable, such provision shall be reformed only to the extent necessary
142 | to make it enforceable.
143 |
144 | 14) Definition of "You" in This License. "You" throughout this License,
145 | whether in upper or lower case, means an individual or a legal entity
146 | exercising rights under, and complying with all of the terms of, this License.
147 | For legal entities, "You" includes any entity that controls, is controlled by,
148 | or is under common control with you. For purposes of this definition,
149 | "control" means (i) the power, direct or indirect, to cause the direction or
150 | management of such entity, whether by contract or otherwise, or (ii) ownership
151 | of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
152 | ownership of such entity.
153 |
154 | 15) Right to Use. You may use the Original Work in all ways not otherwise
155 | restricted or conditioned by this License or by law, and Licensor promises not
156 | to interfere with or be responsible for such uses by You.
157 |
158 | 16) Modification of This License. This License is Copyright © 2005 Lawrence
159 | Rosen. Permission is granted to copy, distribute, or communicate this License
160 | without modification. Nothing in this License permits You to modify this
161 | License as applied to the Original Work or to Derivative Works. However, You
162 | may modify the text of this License and copy, distribute or communicate your
163 | modified version (the "Modified License") and apply it to other original works
164 | of authorship subject to the following conditions: (i) You may not indicate in
165 | any way that your Modified License is the "Open Software License" or "OSL" and
166 | you may not use those names in the name of your Modified License; (ii) You
167 | must replace the notice specified in the first paragraph above with the notice
168 | "Licensed under " or with a notice of your own
169 | that is not confusingly similar to the notice in this License; and (iii) You
170 | may not claim that your original works are open source software unless your
171 | Modified License has been approved by Open Source Initiative (OSI) and You
172 | comply with its license review and certification process.
173 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # plugin-intellij
2 |
3 | Quickly insert any npm package from [jsDelivr CDN](https://www.jsdelivr.com).
4 |
5 | Use a keyboard shortcut (Alt + A by default) or choose "Add jsDelivr package"
6 | from the context menu to bring up the search window.
7 |
8 | 
9 |
10 | ## Installation
11 |
12 | In your IDE, go to `File -> Settings -> Plugins`, click `Browse repositories`,
13 | and search for `jsDelivr`.
14 |
15 | ## Features
16 |
17 | - Insert just the URL, HTML code, or HTML + SRI.
18 | - Offers [minified versions](https://www.jsdelivr.com/features#minify) of all CSS/JS files.
19 | - Quick open (press in the search results list):
20 | - F1 - jsDelivr package page
21 | - F2 - npm package page
22 | - F3 - GitHub page
23 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'org.jetbrains.intellij' version '1.17.3'
4 | }
5 |
6 | version '1.0.10'
7 |
8 | project.sourceCompatibility = JavaVersion.VERSION_17
9 | project.targetCompatibility = JavaVersion.VERSION_17
10 |
11 | repositories {
12 | mavenCentral()
13 | }
14 |
15 | dependencies {
16 | implementation 'com.algolia:algoliasearch-core:3.16.9'
17 | implementation 'com.algolia:algoliasearch-apache:3.16.9'
18 | runtimeOnly 'com.github.cliftonlabs:json-simple:3.1.0'
19 | implementation 'com.github.zafarkhaja:java-semver:0.9.0'
20 | testImplementation group: 'junit', name: 'junit', version: '4.13.1'
21 | }
22 |
23 | intellij {
24 | version = '2022.2'
25 | updateSinceUntilBuild = false
26 | }
27 |
--------------------------------------------------------------------------------
/checkstyle.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
68 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
93 |
94 |
95 |
97 |
98 |
99 |
100 |
102 |
103 |
104 |
105 |
107 |
108 |
109 |
110 |
111 |
112 |
114 |
115 |
116 |
117 |
119 |
120 |
121 |
122 |
124 |
125 |
126 |
127 |
129 |
131 |
133 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
155 |
156 |
157 |
158 |
159 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
--------------------------------------------------------------------------------
/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 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || 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 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
147 | # shellcheck disable=SC3045
148 | MAX_FD=$( ulimit -H -n ) ||
149 | warn "Could not query maximum file descriptor limit"
150 | esac
151 | case $MAX_FD in #(
152 | '' | soft) :;; #(
153 | *)
154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
155 | # shellcheck disable=SC3045
156 | ulimit -n "$MAX_FD" ||
157 | warn "Could not set maximum file descriptor limit to $MAX_FD"
158 | esac
159 | fi
160 |
161 | # Collect all arguments for the java command, stacking in reverse order:
162 | # * args from the command line
163 | # * the main class name
164 | # * -classpath
165 | # * -D...appname settings
166 | # * --module-path (only if needed)
167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
168 |
169 | # For Cygwin or MSYS, switch paths to Windows format before running java
170 | if "$cygwin" || "$msys" ; then
171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
173 |
174 | JAVACMD=$( cygpath --unix "$JAVACMD" )
175 |
176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
177 | for arg do
178 | if
179 | case $arg in #(
180 | -*) false ;; # don't mess with options #(
181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
182 | [ -e "$t" ] ;; #(
183 | *) false ;;
184 | esac
185 | then
186 | arg=$( cygpath --path --ignore --mixed "$arg" )
187 | fi
188 | # Roll the args list around exactly as many times as the number of
189 | # args, so each arg winds up back in the position where it started, but
190 | # possibly modified.
191 | #
192 | # NB: a `for` loop captures its iteration list before it begins, so
193 | # changing the positional parameters here affects neither the number of
194 | # iterations, nor the values presented in `arg`.
195 | shift # remove old arg
196 | set -- "$@" "$arg" # push replacement arg
197 | done
198 | fi
199 |
200 | # Collect all arguments for the java command;
201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
202 | # shell script including quotes and variable substitutions, so put them in
203 | # double quotes to make sure that they get re-expanded; and
204 | # * put everything else in single quotes, so that it's not re-expanded.
205 |
206 | set -- \
207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
208 | -classpath "$CLASSPATH" \
209 | org.gradle.wrapper.GradleWrapperMain \
210 | "$@"
211 |
212 | # Stop when "xargs" is not available.
213 | if ! command -v xargs >/dev/null 2>&1
214 | then
215 | die "xargs is not available"
216 | fi
217 |
218 | # Use "xargs" to parse quoted args.
219 | #
220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
221 | #
222 | # In Bash we could simply go:
223 | #
224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
225 | # set -- "${ARGS[@]}" "$@"
226 | #
227 | # but POSIX shell has neither arrays nor command substitution, so instead we
228 | # post-process each arg (as a line of input to sed) to backslash-escape any
229 | # character that might be a shell metacharacter, then use eval to reverse
230 | # that process (while maintaining the separation between arguments), and wrap
231 | # the whole thing up as a single "set" statement.
232 | #
233 | # This will of course break if any of these variables contains a newline or
234 | # an unmatched quote.
235 | #
236 |
237 | eval "set -- $(
238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
239 | xargs -n1 |
240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
241 | tr '\n' ' '
242 | )" '"$@"'
243 |
244 | exec "$JAVACMD" "$@"
245 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/screenshots/1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsdelivr/plugin-intellij/50bc4c4b81929d5d52c077b3e10297f523f9bf00/screenshots/1.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'plugin-intellij'
2 |
3 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/InsertModel.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij;
2 |
3 | import com.jsdelivr.pluginintellij.packagefile.remotetypes.ApiPackageFile;
4 |
5 | public class InsertModel {
6 | public String name;
7 | public String version;
8 | public ApiPackageFile file;
9 |
10 | public InsertModel(String name, String version, ApiPackageFile file) {
11 | this.name = name;
12 | this.version = version;
13 | this.file = file;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/JsDelivrPackageSearch.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij;
2 |
3 | import com.intellij.openapi.actionSystem.ActionUpdateThread;
4 | import com.intellij.openapi.actionSystem.AnAction;
5 | import com.intellij.openapi.actionSystem.AnActionEvent;
6 | import com.intellij.openapi.actionSystem.CommonDataKeys;
7 | import com.intellij.openapi.editor.Editor;
8 | import com.intellij.openapi.project.Project;
9 | import com.jsdelivr.pluginintellij.packagename.NameInput;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | import java.awt.*;
13 |
14 | public class JsDelivrPackageSearch extends AnAction {
15 | private Editor editor;
16 | private Project project;
17 |
18 | @Override
19 | public void update(AnActionEvent event) {
20 | project = event.getProject();
21 | editor = event.getData(CommonDataKeys.EDITOR);
22 | event.getPresentation().setVisible(project != null && editor != null);
23 | }
24 |
25 | @Override
26 | public void actionPerformed(AnActionEvent event) {
27 | editor = event.getData(CommonDataKeys.EDITOR);
28 |
29 | if (editor == null) {
30 | return;
31 | }
32 |
33 | project = event.getProject();
34 |
35 | Font editorFont = editor.getContentComponent().getFont();
36 | new NameInput(editor, project).setFont(new Font(editorFont.getFontName(), Font.PLAIN, editorFont.getSize() + 1));
37 | }
38 |
39 | @Override
40 | public @NotNull ActionUpdateThread getActionUpdateThread() {
41 | return ActionUpdateThread.EDT;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/actions/OpenGithub.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.actions;
2 |
3 | import com.intellij.openapi.actionSystem.ActionUpdateThread;
4 | import com.intellij.openapi.actionSystem.AnAction;
5 | import com.intellij.openapi.actionSystem.AnActionEvent;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | public class OpenGithub extends AnAction {
9 | @Override
10 | public void actionPerformed(AnActionEvent event) {
11 |
12 | }
13 |
14 | @Override
15 | public @NotNull ActionUpdateThread getActionUpdateThread() {
16 | return ActionUpdateThread.EDT;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/actions/OpenJsDelivr.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.actions;
2 |
3 | import com.intellij.openapi.actionSystem.ActionUpdateThread;
4 | import com.intellij.openapi.actionSystem.AnAction;
5 | import com.intellij.openapi.actionSystem.AnActionEvent;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | public class OpenJsDelivr extends AnAction {
9 | @Override
10 | public void actionPerformed(AnActionEvent event) {
11 |
12 | }
13 |
14 | @Override
15 | public @NotNull ActionUpdateThread getActionUpdateThread() {
16 | return ActionUpdateThread.EDT;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/actions/OpenNpm.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.actions;
2 |
3 | import com.intellij.openapi.actionSystem.ActionUpdateThread;
4 | import com.intellij.openapi.actionSystem.AnAction;
5 | import com.intellij.openapi.actionSystem.AnActionEvent;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | public class OpenNpm extends AnAction {
9 | @Override
10 | public void actionPerformed(AnActionEvent event) {
11 |
12 | }
13 |
14 | @Override
15 | public @NotNull ActionUpdateThread getActionUpdateThread() {
16 | return ActionUpdateThread.EDT;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packagefile/FileInput.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packagefile;
2 |
3 | import com.intellij.openapi.editor.Editor;
4 | import com.intellij.openapi.project.Project;
5 | import com.jsdelivr.pluginintellij.InsertModel;
6 | import com.jsdelivr.pluginintellij.packagefile.remotetypes.ApiPackageFile;
7 | import com.jsdelivr.pluginintellij.packagefile.remotetypes.ApiPackageFiles;
8 | import com.jsdelivr.pluginintellij.packageinsert.InsertOptions;
9 | import com.jsdelivr.pluginintellij.packagename.AlgoliaSearch;
10 | import com.jsdelivr.pluginintellij.packagename.NameThread;
11 | import com.jsdelivr.pluginintellij.ui.DefaultListItem;
12 | import com.jsdelivr.pluginintellij.ui.JsDelivrInput;
13 |
14 | import java.util.List;
15 |
16 | public class FileInput extends JsDelivrInput {
17 | private static final String messageLoading = "Loading...";
18 | private static final String messageNotFound = "File not found";
19 | private static final String messagePlaceholder = "Package file";
20 |
21 | private String pkgName;
22 | private String pkgVersion;
23 |
24 | String pkgFile;
25 | List files;
26 | ApiPackageFiles apiPackageFiles;
27 |
28 | public FileInput(Editor editor, Project project, String pkgName, String pkgVersion) {
29 | super(editor, project, messagePlaceholder);
30 |
31 | this.pkgName = pkgName;
32 | this.pkgVersion = pkgVersion;
33 |
34 | list.getDefaultModel().clear();
35 | list.setEmptyText(messageLoading);
36 |
37 | FileThread fileThread = new FileThread(pkgName, pkgVersion, this);
38 | Thread thread = new Thread(fileThread);
39 | thread.start();
40 | }
41 |
42 | public FileInput(Editor editor, Project project, String pkgName, String pkgVersion, String pkgFile) {
43 | super(editor, project, messagePlaceholder);
44 |
45 | this.pkgName = pkgName;
46 | this.pkgVersion = pkgVersion;
47 | this.pkgFile = pkgFile;
48 |
49 | list.getDefaultModel().clear();
50 | list.setEmptyText(messageLoading);
51 |
52 | FileThread fileThread = new FileThread(pkgName, pkgVersion, this);
53 | Thread thread = new Thread(fileThread);
54 | thread.start();
55 | }
56 |
57 | @Override
58 | protected void updateAutocomplete(String text) {
59 | list.getDefaultModel().clear();
60 | list.setEmptyText(messageLoading);
61 |
62 | for (String file : files) {
63 | String woMin = text;
64 |
65 | if (text.endsWith(".min.js")) {
66 | woMin = text.substring(0, text.lastIndexOf(".min.js")) + ".js";
67 | } else if (text.endsWith(".min.css")) {
68 | woMin = text.substring(0, text.lastIndexOf(".min.css")) + ".css";
69 | }
70 |
71 | if ((file.contains(text) || file.contains(woMin)) && (file.endsWith(".js") || file.endsWith(".css"))) {
72 | if (file.contains(text) && !list.contains(file.replaceFirst("/", ""))) {
73 | list.getDefaultModel().addElement(new DefaultListItem(file.replaceFirst("/", "")));
74 | }
75 |
76 | String tmp = file.replaceFirst("/", "");
77 |
78 | if (tmp.endsWith(".min.js") || tmp.endsWith(".min.css")) {
79 | if (!list.contains(tmp)) {
80 | list.getDefaultModel().addElement(new DefaultListItem(tmp));
81 | }
82 | }
83 |
84 | if (tmp.endsWith(".js") && !tmp.endsWith(".min.js")) {
85 | tmp = tmp.substring(0, tmp.lastIndexOf(".js")) + ".min.js";
86 |
87 | if (!list.contains(tmp)) {
88 | list.getDefaultModel().addElement(new DefaultListItem(tmp));
89 | }
90 | }
91 |
92 | if (tmp.endsWith(".css") && !tmp.endsWith(".min.css")) {
93 | tmp = tmp.substring(0, tmp.lastIndexOf(".css")) + ".min.css";
94 |
95 | if (!list.contains(tmp)) {
96 | list.getDefaultModel().addElement(new DefaultListItem(tmp));
97 | }
98 | }
99 | }
100 | }
101 |
102 | if (list.getDefaultModel().isEmpty()) {
103 | list.setEmptyText(messageNotFound);
104 | }
105 | }
106 |
107 | @Override
108 | protected boolean inputComplete(String text) {
109 | list.getDefaultModel().clear();
110 | list.setEmptyText(messageLoading);
111 |
112 | ApiPackageFile file = null;
113 | boolean generatedMin = false;
114 | text = "/" + text;
115 |
116 | if (apiPackageFiles.files == null || apiPackageFiles.files.length == 0) {
117 | list.setEmptyText(messageNotFound);
118 | return false;
119 | }
120 |
121 | for (ApiPackageFile tmp : apiPackageFiles.files) {
122 | if (tmp.name.equals(text)) {
123 | file = tmp;
124 | break;
125 | }
126 | }
127 |
128 | if (file == null) {
129 | String notMin;
130 |
131 | if (text.endsWith(".min.js")) {
132 | notMin = text.substring(0, text.lastIndexOf(".min")) + ".js";
133 | } else if (text.endsWith(".min.css")) {
134 | notMin = text.substring(0, text.lastIndexOf(".min")) + ".css";
135 | } else {
136 | list.setEmptyText("File not found");
137 | return false;
138 | }
139 |
140 | for (ApiPackageFile tmp : apiPackageFiles.files) {
141 | if (tmp.name.equals(notMin)) {
142 | file = tmp;
143 | generatedMin = true;
144 | break;
145 | }
146 | }
147 | }
148 |
149 | if (file == null) {
150 | list.setEmptyText("File not found");
151 | return false;
152 | }
153 |
154 | file.name = text;
155 |
156 | new InsertOptions(editor, project, new InsertModel(pkgName, pkgVersion, file), generatedMin).setFont(getFont());
157 | return true;
158 | }
159 |
160 | @Override
161 | protected void previousInput() {
162 | list.getDefaultModel().clear();
163 | list.setEmptyText("Loading...");
164 |
165 | NameThread thread = new NameThread(new AlgoliaSearch(), pkgName, pkgVersion, this, true);
166 | Thread thr = new Thread(thread);
167 | thr.start();
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packagefile/FileThread.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packagefile;
2 |
3 | import com.intellij.openapi.application.ApplicationManager;
4 | import com.jsdelivr.pluginintellij.packagefile.remotetypes.ApiPackageFile;
5 | import com.jsdelivr.pluginintellij.ui.JsDelivrInput;
6 |
7 | import java.util.ArrayList;
8 |
9 | public class FileThread implements Runnable {
10 | private String pkgName;
11 | private String pkgVersion;
12 | private FileInput fileInput;
13 |
14 | FileThread(String pkgName, String pkgVersion, FileInput fileInput) {
15 | this.pkgName = pkgName;
16 | this.pkgVersion = pkgVersion;
17 | this.fileInput = fileInput;
18 | }
19 |
20 | @Override
21 | public void run() {
22 | fileInput.loading = true;
23 | fileInput.apiPackageFiles = PackageFiles.getFiles(pkgName, pkgVersion);
24 |
25 | ApplicationManager.getApplication().invokeLater(() -> {
26 | ApplicationManager.getApplication().runWriteAction(() -> {
27 | fileInput.files = new ArrayList<>();
28 |
29 | if (fileInput.apiPackageFiles != null) {
30 | for (ApiPackageFile file : fileInput.apiPackageFiles.files) {
31 | fileInput.files.add(file.name);
32 | }
33 |
34 | if (fileInput.pkgFile == null) {
35 | fileInput.updateAutocomplete(fileInput.inputField.getText().equals(fileInput.placeholder) ? "" : fileInput.inputField.getText());
36 | JsDelivrInput.list.resetSelection();
37 | } else {
38 | fileInput.inputField.setText(fileInput.pkgFile);
39 | fileInput.inputField.setForeground(fileInput.editor.getContentComponent().getForeground());
40 | fileInput.updateAutocomplete(fileInput.pkgFile);
41 | JsDelivrInput.list.resetSelection();
42 | }
43 | } else if (PackageFiles.error != null) {
44 | JsDelivrInput.list.setEmptyText(PackageFiles.error.message);
45 | } else {
46 | JsDelivrInput.list.setEmptyText("Package not found");
47 | }
48 | });
49 | });
50 |
51 | fileInput.loading = false;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packagefile/PackageFiles.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packagefile;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.jsdelivr.pluginintellij.packagefile.remotetypes.ApiFilesError;
5 | import com.jsdelivr.pluginintellij.packagefile.remotetypes.ApiPackageFiles;
6 | import org.apache.http.HttpResponse;
7 | import org.apache.http.client.HttpClient;
8 | import org.apache.http.client.methods.HttpGet;
9 | import org.apache.http.impl.client.HttpClients;
10 | import org.apache.http.util.EntityUtils;
11 |
12 | import java.net.URI;
13 |
14 | class PackageFiles {
15 | static ApiPackageFiles pkgFiles;
16 | static ApiFilesError error;
17 |
18 | private static final String jsDelivrPackageFilesEndpoint = "https://data.jsdelivr.com/v1/package/npm/";
19 | private static final String userAgentHeader = "jsDelivr intelliJ plugin/" + PackageFiles.class.getPackage().getImplementationVersion() + " (https://github.com/jsdelivr/plugin-intellij)";
20 |
21 | static ApiPackageFiles getFiles(String packageName, String packageVersion) {
22 | String response;
23 |
24 | try {
25 | HttpClient client = HttpClients.custom().setUserAgent(userAgentHeader).build();
26 | HttpGet httpGet = new HttpGet();
27 | httpGet.setURI(new URI(jsDelivrPackageFilesEndpoint + packageName + "@" + packageVersion + "/flat"));
28 | HttpResponse resp = client.execute(httpGet);
29 |
30 | response = EntityUtils.toString(resp.getEntity());
31 |
32 | if (resp.getStatusLine().getStatusCode() == 200) {
33 | return new ObjectMapper().readValue(response, ApiPackageFiles.class);
34 | } else if (resp.getStatusLine().getStatusCode() == 403) {
35 | ApiFilesError err = new ApiFilesError();
36 | err.status = 0;
37 | err.message = "Package size exceeded the limit of 50 MB";
38 | error = err;
39 | } else if (resp.getStatusLine().getStatusCode() == 404) {
40 | error = new ObjectMapper().readValue(response, ApiFilesError.class);
41 | } else {
42 | ApiFilesError err = new ApiFilesError();
43 | err.status = 0;
44 | err.message = "Unknown API error occurred";
45 | error = err;
46 | }
47 | } catch (Exception exception) {
48 | pkgFiles = null;
49 | error = null;
50 | }
51 |
52 | return null;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packagefile/remotetypes/ApiFilesError.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packagefile.remotetypes;
2 |
3 | public class ApiFilesError {
4 | public int status;
5 | public String message;
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packagefile/remotetypes/ApiPackageFile.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packagefile.remotetypes;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 |
5 | @JsonIgnoreProperties(ignoreUnknown = true)
6 | public class ApiPackageFile {
7 | public String name;
8 | public String hash;
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packagefile/remotetypes/ApiPackageFiles.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packagefile.remotetypes;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 |
5 | @JsonIgnoreProperties(ignoreUnknown = true)
6 | public class ApiPackageFiles {
7 | public ApiPackageFile[] files;
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packageinsert/InsertOptions.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packageinsert;
2 |
3 | import com.intellij.openapi.editor.Editor;
4 | import com.intellij.openapi.project.Project;
5 | import com.jsdelivr.pluginintellij.InsertModel;
6 | import com.jsdelivr.pluginintellij.packagefile.FileInput;
7 | import com.jsdelivr.pluginintellij.ui.DefaultListItem;
8 | import com.jsdelivr.pluginintellij.ui.JsDelivrList;
9 | import com.jsdelivr.pluginintellij.ui.JsDelivrPopup;
10 |
11 | import java.awt.*;
12 | import java.awt.event.KeyEvent;
13 |
14 | public class InsertOptions {
15 | private static final String jsDelivrUrl = "https://cdn.jsdelivr.net/npm/";
16 |
17 | private static final String messageInsertUrl = "Insert URL";
18 | private static final String messageInsertHtml = "Insert HTML";
19 | private static final String messageInsertHtmlSri = "Insert HTML + SRI";
20 |
21 | private JsDelivrList list;
22 | private JsDelivrPopup popup;
23 | private InsertModel insertModel;
24 | private Editor editor;
25 | private Project project;
26 | private Font font;
27 |
28 | public InsertOptions(Editor editor, Project project, InsertModel insertModel, boolean generatedMin) {
29 | this.editor = editor;
30 | this.insertModel = insertModel;
31 | this.project = project;
32 |
33 | list = new JsDelivrList(s -> {
34 | inputComplete(s.toString());
35 | popup.closePopup();
36 | return null;
37 | });
38 |
39 | list.getDefaultModel().addElement(new DefaultListItem(messageInsertUrl));
40 | list.getDefaultModel().addElement(new DefaultListItem(messageInsertHtml));
41 |
42 | if (!generatedMin) {
43 | list.getDefaultModel().addElement(new DefaultListItem(messageInsertHtmlSri));
44 | }
45 |
46 | popup = new JsDelivrPopup(editor, ke -> {
47 | if (ke.getID() == KeyEvent.KEY_RELEASED) {
48 | if (ke.getKeyCode() == KeyEvent.VK_TAB && ke.isShiftDown()) {
49 | new FileInput(editor, project, insertModel.name, insertModel.version, insertModel.file.name.replaceFirst("/", "")).setFont(font);
50 | popup.closePopup();
51 | } else if (ke.getKeyCode() == KeyEvent.VK_ENTER || ke.getKeyCode() == KeyEvent.VK_TAB) {
52 | inputComplete(list.getSelectedItem().toString());
53 | popup.closePopup();
54 | }
55 | }
56 |
57 | list.keyEvent(ke);
58 | return false;
59 | }, list.getPane());
60 |
61 | list.resetSelection();
62 | }
63 |
64 | private void inputComplete(String text) {
65 | if (text.equals(messageInsertUrl)) {
66 | new TextEdit(editor, project).insertText(getUrl());
67 | } else if (text.equals(messageInsertHtml)) {
68 | if (insertModel.file.name.endsWith(".js")) {
69 | new TextEdit(editor, project).insertText("");
70 | } else {
71 | new TextEdit(editor, project).insertText("");
72 | }
73 | } else if (text.equals(messageInsertHtmlSri)) {
74 | if (insertModel.file.name.endsWith(".js")) {
75 | new TextEdit(editor, project).insertText("");
76 | } else {
77 | new TextEdit(editor, project).insertText("");
78 | }
79 | }
80 | }
81 |
82 | private String getUrl() {
83 | return jsDelivrUrl + insertModel.name + "@" + insertModel.version + insertModel.file.name;
84 | }
85 |
86 | public void setFont(Font font) {
87 | list.setFont(font);
88 | this.font = font;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packageinsert/TextEdit.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packageinsert;
2 |
3 | import com.intellij.openapi.command.WriteCommandAction;
4 | import com.intellij.openapi.editor.Document;
5 | import com.intellij.openapi.editor.Editor;
6 | import com.intellij.openapi.editor.SelectionModel;
7 | import com.intellij.openapi.project.Project;
8 |
9 | class TextEdit {
10 | private Editor editor;
11 | private Project project;
12 |
13 | TextEdit(Editor editor, Project project) {
14 | this.editor = editor;
15 | this.project = project;
16 | }
17 |
18 | void insertText(String text) {
19 | final Document document = editor.getDocument();
20 | final SelectionModel selectionModel = editor.getSelectionModel();
21 | final int start = selectionModel.getSelectionStart();
22 | final int end = selectionModel.getSelectionEnd();
23 | WriteCommandAction.runWriteCommandAction(project, () -> document.replaceString(start, end, text));
24 | selectionModel.removeSelection();
25 | editor.getCaretModel().moveToOffset(editor.getCaretModel().getOffset() + text.length());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packagename/AlgoliaSearch.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packagename;
2 |
3 | import com.algolia.search.DefaultSearchClient;
4 | import com.algolia.search.SearchClient;
5 | import com.algolia.search.SearchConfig;
6 | import com.algolia.search.SearchIndex;
7 | import com.algolia.search.models.indexing.Query;
8 | import com.algolia.search.models.indexing.SearchResult;
9 | import com.jsdelivr.pluginintellij.packagename.remotetypes.AlgoliaPackage;
10 |
11 | import java.util.Collections;
12 | import java.util.List;
13 |
14 | public class AlgoliaSearch {
15 | private static final String appId = "OFCNCOG2CU";
16 | private static final String apiKey = "f54e21fa3a2a0160595bb058179bfb1e";
17 | private static final String indexName = "npm-search";
18 |
19 | private SearchIndex index;
20 |
21 | public AlgoliaSearch() {
22 | java.security.Security.setProperty("networkaddress.cache.ttl", "60");
23 | SearchConfig config = new SearchConfig.Builder(appId, apiKey).build();
24 | SearchClient client = DefaultSearchClient.create(config);
25 | index = client.initIndex(indexName, AlgoliaPackage.class);
26 | }
27 |
28 | List search(String packageName) {
29 | try {
30 | SearchResult result = index.search(new Query(packageName));
31 | return result.getHits();
32 | } catch (Exception e) {
33 | e.printStackTrace();
34 | }
35 |
36 | return Collections.emptyList();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packagename/ListNameItem.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packagename;
2 |
3 | import com.jsdelivr.pluginintellij.packagename.remotetypes.AlgoliaRepository;
4 | import com.jsdelivr.pluginintellij.ui.IJsDelivrListItem;
5 |
6 | public class ListNameItem implements IJsDelivrListItem {
7 | private String name;
8 | private AlgoliaRepository repository;
9 |
10 | ListNameItem(String name, AlgoliaRepository repository) {
11 | this.name = name;
12 | this.repository = repository;
13 | }
14 |
15 | public String getRepositoryUrl() {
16 | return this.repository != null ? this.repository.getUrl() : null;
17 | }
18 |
19 | public String toString() {
20 | return name;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packagename/NameInput.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packagename;
2 |
3 | import com.intellij.openapi.actionSystem.ActionManager;
4 | import com.intellij.openapi.actionSystem.AnAction;
5 | import com.intellij.openapi.actionSystem.Shortcut;
6 | import com.intellij.openapi.editor.Editor;
7 | import com.intellij.openapi.keymap.KeymapUtil;
8 | import com.intellij.openapi.project.Project;
9 | import com.jsdelivr.pluginintellij.ui.JsDelivrInput;
10 |
11 | import javax.swing.*;
12 | import java.awt.*;
13 | import java.awt.event.KeyEvent;
14 | import java.io.IOException;
15 | import java.net.URI;
16 | import java.net.URISyntaxException;
17 |
18 | public class NameInput extends JsDelivrInput {
19 | private static final String messageLoading = "Loading...";
20 | private static final String messagePlaceholder = "Package name";
21 |
22 | private AlgoliaSearch algoliaSearch;
23 |
24 | Thread thread = null;
25 |
26 | public NameInput(Editor editor, Project project) {
27 | super(editor, project, messagePlaceholder);
28 |
29 | this.algoliaSearch = new AlgoliaSearch();
30 | updateAutocomplete("");
31 | list.resetSelection();
32 | }
33 |
34 | public NameInput(Editor editor, Project project, String name) {
35 | super(editor, project, messagePlaceholder);
36 |
37 | this.algoliaSearch = new AlgoliaSearch();
38 | inputField.setText(name);
39 | inputField.setForeground(editor.getContentComponent().getForeground());
40 | updateAutocomplete(name);
41 | list.resetSelection();
42 | }
43 |
44 | @Override
45 | protected void updateAutocomplete(String text) {
46 | list.getDefaultModel().clear();
47 | list.setEmptyText(messageLoading);
48 |
49 | NameThread thread = new NameThread(algoliaSearch, text, this, false);
50 | Thread thr = new Thread(thread);
51 | thr.start();
52 | }
53 |
54 | @Override
55 | protected boolean inputComplete(String text) {
56 | list.getDefaultModel().clear();
57 | list.setEmptyText(messageLoading);
58 |
59 | NameThread pkgNameThr = new NameThread(algoliaSearch, text, this, true);
60 |
61 | if (thread != null) {
62 | thread.interrupt();
63 | }
64 |
65 | thread = new Thread(pkgNameThr);
66 | thread.start();
67 |
68 | return false;
69 | }
70 |
71 | @Override
72 | protected void previousInput() {
73 | // no op
74 | }
75 |
76 | @Override
77 | protected void onKeyEvent(KeyEvent keyEvent) {
78 | if (keyEvent.getID() == KeyEvent.KEY_RELEASED) {
79 | if (isShortcutPressed("com.jsdelivr.pluginintellij.actions.OpenJsDelivr", keyEvent)) {
80 | if (list.getSelectedItem() != null && !list.getSelectedItem().toString().isEmpty()) {
81 | openBrowser("https://www.jsdelivr.com/package/npm/" + list.getSelectedItem().toString());
82 | }
83 | }
84 |
85 | if (isShortcutPressed("com.jsdelivr.pluginintellij.actions.OpenNpm", keyEvent)) {
86 | if (list.getSelectedItem() != null && !list.getSelectedItem().toString().isEmpty()) {
87 | openBrowser("https://www.npmjs.com/package/" + list.getSelectedItem().toString());
88 | }
89 | }
90 |
91 | if (isShortcutPressed("com.jsdelivr.pluginintellij.actions.OpenGithub", keyEvent) && list.getSelectedItem() != null) {
92 | String url = ((ListNameItem) list.getSelectedItem()).getRepositoryUrl();
93 |
94 | if (url != null && !url.isEmpty()) {
95 | openBrowser(url);
96 | }
97 | }
98 | }
99 | }
100 |
101 | private boolean isShortcutPressed(String action, KeyEvent ke) {
102 | AnAction jsdelivrSite = ActionManager.getInstance().getAction(action);
103 |
104 | for (Shortcut shortcut : jsdelivrSite.getShortcutSet().getShortcuts()) {
105 | KeyStroke stroke = KeyStroke.getKeyStroke(getKeyStrokeString(shortcut));
106 |
107 | if (ke.getKeyCode() == stroke.getKeyCode()) {
108 | if ((stroke.getModifiers() & KeyEvent.CTRL_DOWN_MASK) != 0 && !ke.isControlDown()) {
109 | continue;
110 | }
111 |
112 | if ((stroke.getModifiers() & KeyEvent.ALT_DOWN_MASK) != 0 && !ke.isAltDown()) {
113 | continue;
114 | }
115 |
116 | if ((stroke.getModifiers() & KeyEvent.SHIFT_DOWN_MASK) != 0 && !ke.isShiftDown()) {
117 | continue;
118 | }
119 |
120 | return true;
121 | }
122 | }
123 |
124 | return false;
125 | }
126 |
127 | private void openBrowser(String website) {
128 | if (Desktop.isDesktopSupported()) {
129 | Desktop desktop = Desktop.getDesktop();
130 | try {
131 | desktop.browse(new URI(website));
132 | } catch (IOException | URISyntaxException e) {
133 | e.printStackTrace();
134 | }
135 | } else {
136 | Runtime runtime = Runtime.getRuntime();
137 | try {
138 | runtime.exec(new String[]{"xdg-open", website});
139 | } catch (IOException e) {
140 | e.printStackTrace();
141 | }
142 | }
143 | }
144 |
145 | private String getKeyStrokeString(Shortcut shortcut) {
146 | return KeymapUtil.getShortcutText(shortcut)
147 | .replaceAll("\\+", " ")
148 | .replace("Ctrl", "ctrl")
149 | .replace("Alt", "alt")
150 | .replace("Shift", "shift");
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packagename/NameThread.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packagename;
2 |
3 | import com.intellij.openapi.application.ApplicationManager;
4 | import com.jsdelivr.pluginintellij.packagename.remotetypes.AlgoliaPackage;
5 | import com.jsdelivr.pluginintellij.packageversion.VersionInput;
6 | import com.jsdelivr.pluginintellij.ui.JsDelivrInput;
7 |
8 | import java.util.List;
9 |
10 | public class NameThread implements Runnable {
11 | private static final String messageNotFound = "Package not found";
12 |
13 | private volatile List packages;
14 | private AlgoliaSearch algoliaSearch;
15 | private String text;
16 | private String version;
17 | private NameInput nameInput;
18 | private JsDelivrInput jsDelivrInput;
19 | private boolean complete;
20 |
21 | NameThread(AlgoliaSearch algoliaSearch, String text, NameInput nameInput, boolean complete) {
22 | this.algoliaSearch = algoliaSearch;
23 | this.text = text;
24 | this.nameInput = nameInput;
25 | this.complete = complete;
26 | this.jsDelivrInput = nameInput;
27 | }
28 |
29 | public NameThread(AlgoliaSearch algoliaSearch, String text, String version, JsDelivrInput nameInput, boolean complete) {
30 | this.algoliaSearch = algoliaSearch;
31 | this.text = text;
32 | this.complete = complete;
33 | this.jsDelivrInput = nameInput;
34 | this.version = version;
35 | }
36 |
37 | @Override
38 | public void run() {
39 | jsDelivrInput.loading = true;
40 | packages = algoliaSearch.search(text);
41 |
42 | if (complete) {
43 | ApplicationManager.getApplication().invokeLater(() -> {
44 | ApplicationManager.getApplication().runWriteAction(this::runComplete);
45 | });
46 | } else {
47 | ApplicationManager.getApplication().invokeLater(() -> {
48 | ApplicationManager.getApplication().runWriteAction(() -> {
49 | synchronized (this) {
50 | if (packages == null || packages.isEmpty()) {
51 | JsDelivrInput.list.setEmptyText(messageNotFound);
52 | jsDelivrInput.loading = false;
53 | return;
54 | }
55 |
56 | JsDelivrInput.list.getDefaultModel().clear();
57 |
58 | for (AlgoliaPackage pkg : packages) {
59 | JsDelivrInput.list.getDefaultModel().addElement(new ListNameItem(pkg.getName(), pkg.getRepository()));
60 | }
61 |
62 | JsDelivrInput.list.resetSelection();
63 | }
64 | });
65 | });
66 | }
67 |
68 | jsDelivrInput.loading = false;
69 |
70 | if (nameInput != null) {
71 | nameInput.thread = null;
72 | }
73 | }
74 |
75 | private void runComplete() {
76 | synchronized (this) {
77 | AlgoliaPackage chosenPackage = null;
78 |
79 | if (packages == null || packages.isEmpty()) {
80 | JsDelivrInput.list.getDefaultModel().clear();
81 | JsDelivrInput.list.setEmptyText(messageNotFound);
82 | jsDelivrInput.loading = false;
83 | return;
84 | }
85 |
86 | for (AlgoliaPackage pkg : packages) {
87 | if (text.equals(pkg.getName())) {
88 | chosenPackage = pkg;
89 | }
90 | }
91 |
92 | if (chosenPackage == null) {
93 | JsDelivrInput.list.getDefaultModel().clear();
94 | JsDelivrInput.list.setEmptyText(messageNotFound);
95 | jsDelivrInput.loading = false;
96 | return;
97 | }
98 |
99 | if (version == null) {
100 | new VersionInput(jsDelivrInput.editor, jsDelivrInput.project, chosenPackage).setFont(jsDelivrInput.getFont());
101 | } else {
102 | new VersionInput(jsDelivrInput.editor, jsDelivrInput.project, chosenPackage, version).setFont(jsDelivrInput.getFont());
103 | }
104 | jsDelivrInput.popup.closePopup();
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packagename/remotetypes/AlgoliaPackage.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packagename.remotetypes;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | public class AlgoliaPackage {
8 | private String name;
9 | private List versions;
10 | private String version;
11 | private AlgoliaRepository repository;
12 |
13 | public String getName() {
14 | return name;
15 | }
16 |
17 | public AlgoliaPackage setName(String name) {
18 | this.name = name;
19 | return this;
20 | }
21 |
22 | public AlgoliaRepository getRepository() {
23 | return repository;
24 | }
25 |
26 | public AlgoliaPackage setRepository(AlgoliaRepository repository) {
27 | this.repository = repository;
28 | return this;
29 | }
30 |
31 | public String getVersion() {
32 | return version;
33 | }
34 |
35 | public AlgoliaPackage setVersion(String version) {
36 | this.version = version;
37 | return this;
38 | }
39 |
40 | public List getVersions() {
41 | return versions;
42 | }
43 |
44 | public AlgoliaPackage setVersions(Map versions) {
45 | this.versions = new ArrayList<>(versions.keySet());
46 | return this;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packagename/remotetypes/AlgoliaRepository.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packagename.remotetypes;
2 |
3 | public class AlgoliaRepository {
4 | private String url;
5 |
6 | public String getUrl() {
7 | return url;
8 | }
9 |
10 | public AlgoliaRepository setUrl(String url) {
11 | this.url = url;
12 | return this;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/packageversion/VersionInput.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.packageversion;
2 |
3 | import com.github.zafarkhaja.semver.Version;
4 | import com.intellij.openapi.editor.Editor;
5 | import com.intellij.openapi.project.Project;
6 | import com.jsdelivr.pluginintellij.packagefile.FileInput;
7 | import com.jsdelivr.pluginintellij.packagename.NameInput;
8 | import com.jsdelivr.pluginintellij.packagename.remotetypes.AlgoliaPackage;
9 | import com.jsdelivr.pluginintellij.ui.DefaultListItem;
10 | import com.jsdelivr.pluginintellij.ui.JsDelivrInput;
11 |
12 | import java.util.ArrayList;
13 | import java.util.Comparator;
14 | import java.util.List;
15 |
16 | public class VersionInput extends JsDelivrInput {
17 | private static final String messageLoading = "Loading...";
18 | private static final String messageNotFound = "Version not found";
19 | private static final String messagePlaceholder = "Package version";
20 |
21 | private AlgoliaPackage pkg;
22 | private Comparator versionAscending = Comparator.reverseOrder();
23 |
24 | public VersionInput(Editor editor, Project project, AlgoliaPackage pkg) {
25 | super(editor, project, messagePlaceholder);
26 |
27 | loading = true;
28 |
29 | this.pkg = pkg;
30 | updateAutocomplete("");
31 | list.resetSelection();
32 |
33 | loading = false;
34 | }
35 |
36 | public VersionInput(Editor editor, Project project, AlgoliaPackage pkg, String version) {
37 | super(editor, project, messagePlaceholder);
38 |
39 | loading = true;
40 |
41 | this.pkg = pkg;
42 | inputField.setText(version);
43 | inputField.setForeground(editor.getContentComponent().getForeground());
44 | updateAutocomplete(version);
45 | list.resetSelection();
46 |
47 | loading = false;
48 | }
49 |
50 | @Override
51 | protected void updateAutocomplete(String text) {
52 | List list = new ArrayList<>();
53 |
54 | JsDelivrInput.list.setEmptyText(messageLoading);
55 |
56 | if (pkg.getVersions() == null) {
57 | JsDelivrInput.list.setEmptyText(messageNotFound);
58 | }
59 |
60 | for (String version : pkg.getVersions()) {
61 | if (version.startsWith(text)) {
62 | try {
63 | list.add(Version.valueOf(version));
64 | } catch (Exception e) {
65 | System.out.println("Unrecognized version " + version);
66 | }
67 | }
68 | }
69 |
70 | if (list.isEmpty()) {
71 | JsDelivrInput.list.setEmptyText(messageNotFound);
72 | }
73 |
74 | list.sort(versionAscending);
75 | JsDelivrInput.list.getDefaultModel().clear();
76 |
77 | for (Version version : list) {
78 | JsDelivrInput.list.getDefaultModel().addElement(new DefaultListItem(version.toString()));
79 | }
80 | }
81 |
82 | @Override
83 | protected boolean inputComplete(String text) {
84 | list.getDefaultModel().clear();
85 | list.setEmptyText(messageLoading);
86 |
87 | if (!pkg.getVersions().contains(text)) {
88 | list.setEmptyText(messageNotFound);
89 | return false;
90 | }
91 |
92 | new FileInput(editor, project, pkg.getName(), text).setFont(getFont());
93 | return true;
94 | }
95 |
96 | @Override
97 | protected void previousInput() {
98 | new NameInput(editor, project, pkg.getName()).setFont(getFont());
99 | popup.closePopup();
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/ui/DefaultListItem.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.ui;
2 |
3 | public class DefaultListItem implements IJsDelivrListItem {
4 | private String value;
5 |
6 | public DefaultListItem(String value) {
7 | this.value = value;
8 | }
9 |
10 | public String toString() {
11 | return value;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/ui/IJsDelivrListItem.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.ui;
2 |
3 | public interface IJsDelivrListItem {
4 | String toString();
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/ui/JsDelivrInput.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.ui;
2 |
3 | import com.intellij.openapi.application.ApplicationManager;
4 | import com.intellij.openapi.editor.Editor;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.ui.JBColor;
7 | import com.intellij.ui.components.JBTextField;
8 |
9 | import javax.swing.event.DocumentEvent;
10 | import javax.swing.event.DocumentListener;
11 | import java.awt.*;
12 | import java.awt.event.*;
13 |
14 | public abstract class JsDelivrInput implements FocusListener {
15 | public String placeholder;
16 | public JBTextField inputField;
17 | public Editor editor;
18 | public Project project;
19 | public JsDelivrPopup popup;
20 | public boolean loading = false;
21 | public static JsDelivrList list;
22 |
23 | private Font font;
24 |
25 | public JsDelivrInput(Editor editor, Project project, String placeholder) {
26 | this.editor = editor;
27 | this.project = project;
28 | this.placeholder = placeholder;
29 |
30 | inputField = new JBTextField();
31 | inputField.addMouseListener(new JsDelivrMouseListener());
32 | inputField.getDocument().addDocumentListener(new PasteListener());
33 |
34 | list = new JsDelivrList(s -> {
35 | inputComplete(s.toString());
36 | popup.closePopup();
37 | return null;
38 | });
39 |
40 | list.setFont(getFont());
41 | list.setMaximumSize(JsDelivrPopup.popupDim);
42 | popup = new JsDelivrPopup(editor, new JsDelivrKeyDispatcher(), inputField, list.getPane());
43 |
44 | inputField.requestFocus();
45 | inputField.setForeground(JBColor.GRAY);
46 | inputField.setText(placeholder);
47 | inputField.setCaretPosition(0);
48 | }
49 |
50 | /**
51 | * Update autocomplete hints in variable list.
52 | *
53 | * @param text Text in input field
54 | */
55 | protected abstract void updateAutocomplete(String text);
56 |
57 | /**
58 | * Continue to next stage of input process.
59 | *
60 | * @param text Text in input field
61 | * @return Completion success
62 | */
63 | protected abstract boolean inputComplete(String text);
64 |
65 | /**
66 | * Go to previous stage of input process.
67 | */
68 | protected abstract void previousInput();
69 |
70 | /**
71 | * Support custom key event actions.
72 | *
73 | * @param keyEvent key event
74 | */
75 | protected void onKeyEvent(KeyEvent keyEvent) {
76 |
77 | }
78 |
79 | @Override
80 | public void focusLost(FocusEvent event) {
81 | popup.closePopup();
82 | }
83 |
84 | @Override
85 | public void focusGained(FocusEvent event) {
86 | // no op
87 | }
88 |
89 | public Font getFont() {
90 | return this.font;
91 | }
92 |
93 | public void setFont(Font font) {
94 | list.setFont(font);
95 | inputField.setFont(font);
96 | this.font = font;
97 | }
98 |
99 | class JsDelivrKeyDispatcher implements KeyEventDispatcher {
100 | boolean shift = false;
101 | boolean tab = false;
102 | private String lastUpdate = "";
103 |
104 | private boolean isAllowedWhenInputEmpty(KeyEvent event) {
105 | return event.getKeyCode() != KeyEvent.VK_DELETE
106 | && event.getKeyCode() != KeyEvent.VK_BACK_SPACE
107 | && event.getKeyCode() != KeyEvent.VK_LEFT
108 | && event.getKeyCode() != KeyEvent.VK_RIGHT;
109 | }
110 |
111 | private boolean isAllowedWhenListEmpty(KeyEvent event) {
112 | return event.getKeyCode() != KeyEvent.VK_ENTER
113 | && event.getKeyCode() != KeyEvent.VK_TAB
114 | && event.getKeyCode() != KeyEvent.VK_UP
115 | && event.getKeyCode() != KeyEvent.VK_DOWN;
116 | }
117 |
118 | private boolean mustResetPlaceholder(KeyEvent event) {
119 | return event.getKeyCode() == KeyEvent.VK_SHIFT
120 | || event.isActionKey()
121 | || event.getKeyCode() == KeyEvent.VK_TAB
122 | || event.getKeyCode() == KeyEvent.VK_ALT
123 | || event.getKeyCode() == KeyEvent.VK_CONTROL
124 | || event.getKeyCode() == KeyEvent.VK_CAPS_LOCK
125 | || event.getKeyCode() == KeyEvent.VK_NUM_LOCK
126 | || event.getKeyCode() == KeyEvent.VK_DELETE
127 | || event.getKeyCode() == KeyEvent.VK_BACK_SPACE
128 | || (event.getKeyCode() == KeyEvent.VK_DELETE && event.isShiftDown());
129 | }
130 |
131 | @Override
132 | public boolean dispatchKeyEvent(KeyEvent ke) {
133 | if ((!isAllowedWhenInputEmpty(ke) && inputField.getText().equals(placeholder))
134 | || (!isAllowedWhenListEmpty(ke) && list.getDefaultModel().isEmpty())
135 | ) {
136 | ke.consume();
137 | return true;
138 | }
139 |
140 | if (ke.getID() == KeyEvent.KEY_PRESSED) {
141 | // input was empty
142 | if (inputField.getText().equals(placeholder)) {
143 | // user pressed a symbol, input field will get rid of placeholder
144 | if (isAllowedWhenInputEmpty(ke) && !mustResetPlaceholder(ke)) {
145 | inputField.setForeground(editor.getContentComponent().getForeground());
146 | inputField.setText("");
147 | // user pressed an action key, must reset placeholder so it doesn't flicker
148 | } else {
149 | inputField.setText(placeholder);
150 | inputField.setCaretPosition(0);
151 | inputField.setForeground(JBColor.GRAY);
152 | }
153 | }
154 |
155 | // update shift + tab combo state
156 | if (ke.getKeyCode() == KeyEvent.VK_SHIFT) {
157 | shift = true;
158 | ke.consume();
159 | } else if (ke.getKeyCode() == KeyEvent.VK_TAB) {
160 | tab = true;
161 | ke.consume();
162 | }
163 | }
164 |
165 | if (ke.getID() == KeyEvent.KEY_RELEASED) {
166 | // shift + tab was pressed, going to previous input
167 | if ((ke.getKeyCode() == KeyEvent.VK_TAB && (shift || ke.isShiftDown())) || (ke.getKeyCode() == KeyEvent.VK_SHIFT && tab)) {
168 | shift = ke.getKeyCode() == KeyEvent.VK_TAB;
169 | tab = ke.getKeyCode() == KeyEvent.VK_SHIFT;
170 |
171 | ke.consume();
172 |
173 | if (!loading) {
174 | previousInput();
175 | }
176 | // released tab or enter, going to next input
177 | } else if (ke.getKeyCode() == KeyEvent.VK_ENTER || ke.getKeyCode() == KeyEvent.VK_TAB) {
178 | tab = tab && ke.getKeyCode() != KeyEvent.VK_TAB;
179 | ke.consume();
180 |
181 | // if nothing was selected, reset the placeholder to avoid flickering
182 | if (list.getDefaultModel().isEmpty()) {
183 | inputField.setText(placeholder);
184 | inputField.setForeground(JBColor.GRAY);
185 | inputField.setCaretPosition(0);
186 | return false;
187 | }
188 |
189 | // go to next input if list is not loading
190 | if (inputComplete(list.getSelectedItem().toString()) && !loading) {
191 | popup.closePopup();
192 | }
193 | // updating shift state
194 | } else if (ke.getKeyCode() == KeyEvent.VK_SHIFT) {
195 | shift = false;
196 | ke.consume();
197 | // if a key that doesn't suit any of the above was released (except up & down arrow keys)
198 | } else if (ke.getKeyCode() != KeyEvent.VK_UP && ke.getKeyCode() != KeyEvent.VK_DOWN) {
199 | // update input field (for example if backspace was pressed and no text is left in input)
200 | if (inputField.getText().equals("")) {
201 | inputField.setForeground(JBColor.GRAY);
202 | inputField.setText(placeholder);
203 | inputField.setCaretPosition(0);
204 | updateAutocomplete("");
205 | list.resetSelection();
206 | lastUpdate = "";
207 | } else if ((!mustResetPlaceholder(ke) || ke.getKeyCode() == KeyEvent.VK_BACK_SPACE || ke.getKeyCode() == KeyEvent.VK_DELETE) && !inputField.getText().equals(placeholder)) {
208 | // if input changed, update list
209 | if (!inputField.getText().equals(lastUpdate)) {
210 | lastUpdate = inputField.getText();
211 | updateAutocomplete(inputField.getText());
212 | list.resetSelection();
213 | }
214 | }
215 | }
216 | }
217 |
218 | // run custom handlers which may be implemented in subclasses
219 | onKeyEvent(ke);
220 |
221 | // pass up & down arrow keys to list
222 | list.keyEvent(ke);
223 |
224 | return false;
225 | }
226 | }
227 |
228 | class PasteListener implements DocumentListener {
229 | @Override
230 | public void insertUpdate(DocumentEvent event) {
231 | ApplicationManager.getApplication().invokeLater(() -> {
232 | ApplicationManager.getApplication().runWriteAction(() -> {
233 | if (inputField.getText().contains(placeholder) && inputField.getText().length() > placeholder.length()) {
234 | inputField.setForeground(editor.getContentComponent().getForeground());
235 | inputField.setText(inputField.getText().substring(0, event.getLength()));
236 | }
237 | });
238 | });
239 | }
240 |
241 | @Override
242 | public void removeUpdate(DocumentEvent event) {
243 | // no op
244 | }
245 |
246 | @Override
247 | public void changedUpdate(DocumentEvent event) {
248 | // no op
249 | }
250 | }
251 |
252 | class JsDelivrMouseListener implements MouseListener {
253 | @Override
254 | public void mouseClicked(MouseEvent event) {
255 | if (inputField.getText().equals(placeholder)) {
256 | event.consume();
257 | inputField.select(0, 0);
258 | }
259 | }
260 |
261 | @Override
262 | public void mousePressed(MouseEvent event) {
263 | if (inputField.getText().equals(placeholder)) {
264 | event.consume();
265 | inputField.select(0, 0);
266 | }
267 | }
268 |
269 | @Override
270 | public void mouseReleased(MouseEvent event) {
271 | if (inputField.getText().equals(placeholder)) {
272 | event.consume();
273 | inputField.select(0, 0);
274 | }
275 | }
276 |
277 | @Override
278 | public void mouseEntered(MouseEvent event) {
279 | // no op
280 | }
281 |
282 | @Override
283 | public void mouseExited(MouseEvent event) {
284 | // no op
285 | }
286 | }
287 | }
288 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/ui/JsDelivrList.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.ui;
2 |
3 | import com.intellij.ui.components.JBList;
4 | import com.intellij.ui.components.JBScrollPane;
5 |
6 | import javax.swing.*;
7 | import java.awt.*;
8 | import java.awt.event.KeyEvent;
9 | import java.awt.event.MouseAdapter;
10 | import java.awt.event.MouseEvent;
11 | import java.awt.event.MouseMotionAdapter;
12 | import java.util.Collections;
13 | import java.util.function.Function;
14 | import java.util.stream.Collectors;
15 |
16 | public class JsDelivrList extends JBList {
17 | private Font font;
18 | private JBScrollPane pane;
19 |
20 | public JsDelivrList(Function onItemSelected) {
21 | super(new DefaultListModel<>());
22 |
23 | setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
24 | setSelectedIndex(0);
25 | setVisibleRowCount(5);
26 | setEmptyText("Loading...");
27 |
28 | addMouseListener(new MouseAdapter() {
29 | @Override
30 | public void mouseClicked(MouseEvent mouseEvent) {
31 | JBList lst = (JBList) mouseEvent.getSource();
32 | int index = lst.locationToIndex(mouseEvent.getPoint());
33 |
34 | if (index >= 0) {
35 | IJsDelivrListItem object = (IJsDelivrListItem) lst.getModel().getElementAt(index);
36 | onItemSelected.apply(object);
37 | }
38 | }
39 | });
40 |
41 | addMouseMotionListener(new MouseMotionAdapter() {
42 | @Override
43 | public void mouseMoved(MouseEvent event) {
44 | JBList lst = (JBList) event.getSource();
45 | int index = lst.locationToIndex(event.getPoint());
46 |
47 | if (index >= 0) {
48 | lst.setSelectedIndex(index);
49 | }
50 | }
51 | });
52 |
53 | pane = new JBScrollPane(this);
54 | pane.setBorder(null);
55 | }
56 |
57 | public Font getFont() {
58 | return this.font;
59 | }
60 |
61 | public void setFont(Font font) {
62 | super.setFont(font);
63 | this.font = font;
64 | }
65 |
66 | public JBScrollPane getPane() {
67 | return pane;
68 | }
69 |
70 | public IJsDelivrListItem getSelectedItem() {
71 | if (getSelectedValue() == null) {
72 | return null;
73 | }
74 |
75 | return getSelectedValue();
76 | }
77 |
78 | public boolean contains(String item) {
79 | return Collections.list(this.getDefaultModel().elements()).stream().anyMatch(i -> i.toString().equals(item));
80 | }
81 |
82 | public void resetSelection() {
83 | if (!isEmpty()) {
84 | setSelectedIndex(0);
85 | }
86 | }
87 |
88 | public void keyEvent(KeyEvent ke) {
89 | if (ke.getID() == KeyEvent.KEY_PRESSED) {
90 | if (ke.getKeyCode() == KeyEvent.VK_DOWN) {
91 | setSelectedIndex(getSelectedIndex() + 1);
92 | } else if (ke.getKeyCode() == KeyEvent.VK_UP) {
93 | setSelectedIndex(getSelectedIndex() - 1);
94 | }
95 | }
96 |
97 | ensureIndexIsVisible(getSelectedIndex());
98 | }
99 |
100 | public DefaultListModel getDefaultModel() {
101 | return (DefaultListModel) getModel();
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/com/jsdelivr/pluginintellij/ui/JsDelivrPopup.java:
--------------------------------------------------------------------------------
1 | package com.jsdelivr.pluginintellij.ui;
2 |
3 | import com.intellij.openapi.editor.Editor;
4 | import com.intellij.openapi.ui.popup.*;
5 | import com.intellij.util.ui.FormBuilder;
6 |
7 | import javax.swing.*;
8 | import java.awt.*;
9 |
10 | public class JsDelivrPopup {
11 | JBPopup popup;
12 | Editor editor;
13 |
14 | public static final Dimension popupDim = new Dimension(200, 62);
15 |
16 | public JsDelivrPopup(Editor editor, KeyEventDispatcher keyDispatcher, JComponent... components) {
17 | this.editor = editor;
18 |
19 | FormBuilder formBuilder = FormBuilder.createFormBuilder();
20 |
21 | for (JComponent component : components) {
22 | formBuilder.addComponent(component);
23 | }
24 |
25 | ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(formBuilder.getPanel(), null);
26 | popup = builder.setModalContext(true).setRequestFocus(true).setResizable(true).setMovable(true).createPopup();
27 |
28 | popup.setMinimumSize(popupDim);
29 |
30 | // Under caret position
31 | popup.showInBestPositionFor(editor);
32 | KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(keyDispatcher);
33 |
34 | popup.addListener(new JBPopupListener() {
35 | @Override
36 | public void onClosed(LightweightWindowEvent event) {
37 | if (keyDispatcher != null) {
38 | KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(keyDispatcher);
39 | }
40 | }
41 | });
42 | }
43 |
44 | public void closePopup() {
45 | if (popup != null) {
46 | popup.cancel();
47 | popup.dispose();
48 | popup = null;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | jsDelivr package search
3 |
4 | com.jsdelivr.pluginintellij.JsDelivrPackageSearch
5 |
6 | https://www.jsdelivr.com
8 | ]]>
9 |
10 | v1.0.10
12 | Resolve deprecation warnings in latest IntelliJ versions.
13 |
14 | v1.0.9
15 | Resolve deprecation warnings in IntelliJ 2024.1.
16 |
17 | v1.0.8
18 | Update dependencies.
19 |
20 | v1.0.7
21 | Update IntelliJ platform to 2020.1, add plugin logo.
22 |
23 | v1.0.6
24 | Update IntelliJ platform to 2019.3.
25 |
26 | v1.0.5
27 | Update IntelliJ platform to 2019.2.
28 |
29 | v1.0.4
30 | Fix handling of invalid semver versions.
31 |
32 | v1.0.3
33 | Update IntelliJ platform to 2019.1.
34 |
35 | v1.0.2
36 | Add keyboard shortcuts to quickly open jsDelivr, npm, or GitHub page of the selected package.
37 |
38 | v1.0.1
39 | Fix pasting into empty input.
40 |
41 | v1.0.0
42 | Initial release.
43 | ]]>
44 |
45 | 1.0.7
46 |
47 |
48 | jsDelivr
49 |
50 | com.intellij.modules.lang
51 |
52 |
53 |
56 |
57 |
58 |
59 |
62 |
63 |
64 |
66 |
67 |
68 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/pluginIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
34 |
--------------------------------------------------------------------------------