diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..1b76843 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,225 @@ +# GitHub Actions Workflow created for testing and preparing the plugin release in following steps: +# - validate Gradle Wrapper, +# - run test and verifyPlugin tasks, +# - run buildPlugin task and prepare artifact for the further tests, +# - run IntelliJ Plugin Verifier, +# - create a draft release. +# +# Workflow is triggered on push and pull_request events. +# +# Docs: +# - GitHub Actions: https://help.github.com/en/actions +# - IntelliJ Plugin Verifier GitHub Action: https://github.com/ChrisCarini/intellij-platform-plugin-verifier-action +# +## JBIJPPTPL + +name: Build +on: [push, pull_request] +jobs: + + # Run Gradle Wrapper Validation Action to verify the wrapper's checksum + gradleValidation: + name: Gradle Wrapper + runs-on: ubuntu-latest + steps: + + # Check out current repository + - name: Fetch Sources + uses: actions/checkout@v2 + + # Validate wrapper + - name: Gradle Wrapper Validation + uses: gradle/wrapper-validation-action@v1 + + # Run verifyPlugin and test Gradle tasks + test: + name: Test + needs: gradleValidation + runs-on: ubuntu-latest + steps: + + # Setup Java 1.8 environment for the next steps + - name: Setup Java + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + # Check out current repository + - name: Fetch Sources + uses: actions/checkout@v2 + + # Cache Gradle dependencies + - name: Setup Gradle Dependencies Cache + uses: actions/cache@v2 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-caches-${{ hashFiles('**/*.gradle', '**/*.gradle.kts') }} + + # Cache Gradle Wrapper + - name: Setup Gradle Wrapper Cache + uses: actions/cache@v2 + with: + path: ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }} + + # Run detekt, ktlint and tests + - name: Run Linters and Test + run: ./gradlew check + + # Run verifyPlugin Gradle task + - name: Verify Plugin + run: ./gradlew verifyPlugin + + # Build plugin with buildPlugin Gradle task and provide the artifact for the next workflow jobs + # Requires test job to be passed + build: + name: Build + needs: test + runs-on: ubuntu-latest + outputs: + name: ${{ steps.properties.outputs.name }} + version: ${{ steps.properties.outputs.version }} + changelog: ${{ steps.properties.outputs.changelog }} + artifact: ${{ steps.properties.outputs.artifact }} + steps: + + # Setup Java 1.8 environment for the next steps + - name: Setup Java + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + # Check out current repository + - name: Fetch Sources + uses: actions/checkout@v2 + + # Cache Gradle Dependencies + - name: Setup Gradle Dependencies Cache + uses: actions/cache@v2 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-caches-${{ hashFiles('**/*.gradle', '**/*.gradle.kts') }} + + # Cache Gradle Wrapper + - name: Setup Gradle Wrapper Cache + uses: actions/cache@v2 + with: + path: ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }} + + # Set environment variables + - name: Export Properties + id: properties + shell: bash + run: | + PROPERTIES="$(./gradlew properties --console=plain -q)" + VERSION="$(echo "$PROPERTIES" | grep "^version:" | cut -f2- -d ' ')" + NAME="$(echo "$PROPERTIES" | grep "^pluginName_:" | cut -f2- -d ' ')" + CHANGELOG="$(./gradlew getChangelog --unreleased --no-header --console=plain -q)" + CHANGELOG="${CHANGELOG//'%'/'%25'}" + CHANGELOG="${CHANGELOG//$'\n'/'%0A'}" + CHANGELOG="${CHANGELOG//$'\r'/'%0D'}" + ARTIFACT="${NAME}-${VERSION}.zip" + + echo "::set-output name=version::$VERSION" + echo "::set-output name=name::$NAME" + echo "::set-output name=changelog::$CHANGELOG" + echo "::set-output name=artifact::$ARTIFACT" + + # Build artifact using buildPlugin Gradle task + - name: Build Plugin + run: ./gradlew buildPlugin + + # Upload plugin artifact to make it available in the next jobs + - name: Upload artifact + uses: actions/upload-artifact@v1 + with: + name: plugin-artifact + path: ./build/distributions/${{ needs.build.outputs.artifact }} + + # Verify built plugin using IntelliJ Plugin Verifier tool + # Requires build job to be passed + verify: + name: Verify + needs: build + runs-on: ubuntu-latest + steps: + + # Download plugin artifact provided by the previous job + - name: Download Artifact + uses: actions/download-artifact@v1 + with: + name: plugin-artifact + + # Run IntelliJ Plugin Verifier action using GitHub Action + - name: Verify Plugin + id: verify + uses: ChrisCarini/intellij-platform-plugin-verifier-action@v0.0.2 + with: + plugin-location: plugin-artifact/*.zip + ide-versions: | + ideaIC:2019.3 + ideaIC:2020.1 + ideaIC:2020.2 + + # Print the output of the verify step + - name: Print Logs + if: ${{ always() }} + env: + OUTPUT_LOG: ${{ steps.verify.outputs.verification-output-log-filename }} + run: | + echo "The verifier log file [$OUTPUT_LOG] contents : " ; + cat $OUTPUT_LOG + + # Prepare a draft release for GitHub Releases page for the manual verification + # If accepted and published, release workflow would be triggered + releaseDraft: + name: Release Draft + needs: [build, verify] + runs-on: ubuntu-latest + steps: + + # Check out current repository + - name: Fetch Sources + uses: actions/checkout@v2 + + # Remove old release drafts by using the curl request for the available releases with draft flag + - name: Remove Old Release Drafts + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + curl -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/repos/$GITHUB_REPOSITORY/releases \ + | tr '\r\n' ' ' \ + | jq '.[] | select(.draft == true) | .id' \ + | xargs -I '{}' \ + curl -X DELETE -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/repos/$GITHUB_REPOSITORY/releases/{} + + # Create new release draft - which is not publicly visible and requires manual acceptance + - name: Create Release Draft + id: createDraft + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ needs.build.outputs.version }} + release_name: v${{ needs.build.outputs.version }} + body: ${{ needs.build.outputs.changelog }} + draft: true + + # Download plugin artifact provided by the previous job + - name: Download Artifact + uses: actions/download-artifact@v1 + with: + name: plugin-artifact + + # Upload artifact as a release asset + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.createDraft.outputs.upload_url }} + asset_path: ./plugin-artifact/${{ needs.build.outputs.artifact }} + asset_name: ${{ needs.build.outputs.artifact }} + asset_content_type: application/zip diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..382fa91 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,70 @@ +# GitHub Actions Workflow created for handling the release process based on the draft release prepared +# with the Build workflow. Running the publishPlugin task requires the PUBLISH_TOKEN secret provided. + +name: Release +on: + release: + types: [prereleased, released] + +jobs: + + # Prepare and publish the plugin to the Marketplace repository + release: + name: Publish Plugin + runs-on: ubuntu-latest + steps: + + # Setup Java 1.8 environment for the next steps + - name: Setup Java + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + # Check out current repository + - name: Fetch Sources + uses: actions/checkout@v2 + with: + ref: ${{ github.event.release.tag_name }} + + # Publish the plugin to the Marketplace + - name: Publish Plugin + env: + PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} + run: ./gradlew publishPlugin + + # Patch changelog, commit and push to the current repository + changelog: + name: Update Changelog + needs: release + runs-on: ubuntu-latest + steps: + + # Setup Java 1.8 environment for the next steps + - name: Setup Java + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + # Check out current repository + - name: Fetch Sources + uses: actions/checkout@v2 + with: + ref: ${{ github.event.release.tag_name }} + + # Update Unreleased section with the current version + - name: Patch Changelog + run: ./gradlew patchChangelog + + # Commit patched Changelog + - name: Commit files + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git commit -m "Update changelog" -a + + # Push changes + - name: Push changes + uses: ad-m/github-push-action@master + with: + branch: main + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2404953 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +gen/ +build/ +.gradle/ +.idea/ +target/ +out/ +.project +.classpath +/bootstrap.xml +/font-awesome.xml +*.*~ +*.iml +**/*.iml +*.ipl +**/*.ipl +*.iwl +**/*.iwl +/logs +*.log +config.properties diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7c34880 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ + + +# Changelog + +## [Unreleased] +### Changed +- Migrated the plugin to gradle + +### Fixed +- Fixed issues with deprecated APIs + +## [0.1.1] +### Added +- Basic support diff --git a/README.md b/README.md index 5b2049c..393967b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ -This plugin helps you to run http-requests from an IDEA's text editor +# Rest Client -#### Features -- Supports GET, POST, PUT, DELETE, PATCH requests +![Build](https://github.com/danblack/idea-rest-client/workflows/Build/badge.svg) +[![Version](https://img.shields.io/jetbrains/plugin/v/9232-http-editor-client.svg)](https://plugins.jetbrains.com/plugin/9232-http-editor-client) +[![Downloads](https://img.shields.io/jetbrains/plugin/d/9232-http-editor-client.svg)](https://plugins.jetbrains.com/plugin/9232-http-editor-client) + + +Allows making http requests using IDEA text editor + +## Features +- Supports GET, POST, PUT, DELETE, PATCH, HEAD & OPTIONS requests - Multiline request parameters - Response auto format (based on response Content-Type header) - Customized colors and fonts @@ -10,7 +17,7 @@ This plugin helps you to run http-requests from an IDEA's text editor ![demo](doc/demo.png) -#### YouTube demos +## YouTube demos * [Simple demo](https://www.youtube.com/watch?v=AliJaGmXlxc) @@ -90,3 +97,17 @@ http://localhost:8080/mirror-params?one=1 one: 1 five: 5 ``` + + + +## Installation + +- Using IDE built-in plugin system: + + Preferences > Plugins > Marketplace > Search for "%NAME%" > + Install Plugin + +- Manually: + + Download the [latest release](https://github.com/%REPOSITORY%/releases/latest) and install it manually using + Preferences > Plugins > ⚙️ > Install plugin from disk... diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..f3b3ed5 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,137 @@ +import org.jetbrains.changelog.closure +import org.jetbrains.changelog.markdownToHTML +import org.jetbrains.grammarkit.tasks.GenerateLexer +import org.jetbrains.grammarkit.tasks.GenerateParser + +plugins { + // plugin development + id("org.jetbrains.intellij") version "0.4.26" + // for parsing grammer files & generate lexer & grammer + id("org.jetbrains.grammarkit") version "2020.2.1" + // for patching plugin.xml with the content from CHANGELOG.md file + id("org.jetbrains.changelog") version "0.5.0" + + java + idea + + // Plugin which can check for Gradle dependencies, use the help/dependencyUpdates task. + id("com.github.ben-manes.versions") version "0.28.0" + // Plugin which can update Gradle dependencies, use the help/useLatestVersions task. + id("se.patrikerdes.use-latest-versions") version "0.2.14" + + // Used to debug in a different IDE + maven + id("de.undercouch.download") version "4.0.4" +} + +// Import variables from gradle.properties file +val pluginGroup: String by project +// `pluginName_` variable ends with `_` because of the collision with Kotlin magic getter in the `intellij` closure. +// Read more about the issue: https://github.com/JetBrains/intellij-platform-plugin-template/issues/29 +val pluginName_: String by project +val pluginVersion: String by project +val pluginSinceBuild: String by project +val pluginUntilBuild: String by project + +val platformType: String by project +val platformVersion: String by project +val platformDownloadSources: String by project + +group = pluginGroup +version = pluginVersion + +repositories { + mavenCentral() + mavenLocal() +} + +dependencies { + implementation("org.jetbrains:annotations:20.1.0") + + // lombok + compileOnly("org.projectlombok:lombok:1.18.12") + annotationProcessor("org.projectlombok:lombok:1.18.12") + testCompileOnly("org.projectlombok:lombok:1.18.12") + testAnnotationProcessor("org.projectlombok:lombok:1.18.12") + compileOnly("org.jetbrains:annotations:20.1.0") +} + +// Configure gradle-intellij-plugin plugin. +// Read more: https://github.com/JetBrains/gradle-intellij-plugin +intellij { + pluginName = pluginName_ + version = platformVersion + type = platformType + downloadSources = platformDownloadSources.toBoolean() + updateSinceUntilBuild = true +} + +val generateRestLexer = task("generateRestLexer") { + source = "src/main/grammars/Rest.flex" + targetDir = "gen/ru/basecode/ide/rest/plugin/grammar/" + targetClass = "_RestLexer" + purgeOldFiles = true +} + +val generateRestParser = task("generateRestParser") { + source = "src/main/grammars/Rest.bnf" + targetRoot = "gen" + pathToParser = "ru/basecode/ide/rest/plugin/parser/_RestParser.java" + pathToPsiRoot = "ru/basecode/ide/rest/plugin/psi" + purgeOldFiles = true +} + +tasks { + compileJava { + dependsOn(generateRestLexer, generateRestParser) + sourceCompatibility = JavaVersion.VERSION_1_8.toString() + targetCompatibility = JavaVersion.VERSION_1_8.toString() + options.compilerArgs = listOf("-Xlint:deprecation") + } + + patchPluginXml { + version(pluginVersion) + sinceBuild(pluginSinceBuild) + untilBuild(pluginUntilBuild) + + // Extract the section from README.md and provide for the plugin's manifest + pluginDescription( + closure { + File("./README.md").readText().lines().run { + val start = "" + val end = "" + + if (!containsAll(listOf(start, end))) { + throw GradleException("Plugin description section not found in README.md file:\n$start ... $end") + } + subList(indexOf(start) + 1, indexOf(end)) + }.joinToString("\n").run { markdownToHTML(this) } + } + ) + + // Get the latest available change notes from the changelog file + changeNotes( + closure { + changelog.getLatest().toHTML() + } + ) + } + + publishPlugin { + dependsOn("patchChangelog") + token(System.getenv("PUBLISH_TOKEN")) + channels(pluginVersion.split('-').getOrElse(1) { "default" }.split('.').first()) + } +} + +sourceSets { + main { + java.srcDirs("gen") + } +} + +idea { + module { + generatedSourceDirs.add(file("gen")) + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..a69aab4 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,12 @@ +# IntelliJ Platform Artifacts Repositories +# -> https://www.jetbrains.org/intellij/sdk/docs/reference_guide/intellij_artifacts.html + +pluginGroup = ru.basecode.ide.rest.plugin +pluginName_ = Rest Client +pluginVersion = 0.2.0 +pluginSinceBuild = 202.6397.94 +pluginUntilBuild = + +platformType = IC +platformVersion = 2020.2 +platformDownloadSources = true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..12d38de --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/resources/1.html b/resources/1.html deleted file mode 100644 index af723ef..0000000 --- a/resources/1.html +++ /dev/null @@ -1,12 +0,0 @@ - - - -
-
-
-
- - \ No newline at end of file diff --git a/resources/1.rest b/resources/1.rest deleted file mode 100644 index 77f7110..0000000 --- a/resources/1.rest +++ /dev/null @@ -1,10 +0,0 @@ - ->>> RESPONSE - - -
-
-
-
- - \ No newline at end of file diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml deleted file mode 100644 index c6ceb1a..0000000 --- a/resources/META-INF/plugin.xml +++ /dev/null @@ -1,61 +0,0 @@ - - ru.basecode.ide.rest.plugin - Simple Rest Client - 1.0 - Denis Chernyshov - - - - - - - - - - - - - - ru.basecode.ide.rest.plugin.RestClientPlugin - - - - - - ru.basecode.ide.rest.plugin.RestProjectComponent - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..477afc6 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,12 @@ +rootProject.name = "idea-rest-client" +pluginManagement { + repositories { + maven{ + url = uri("https://jetbrains.bintray.com/intellij-plugin-service") + } + maven { + url = uri("https://plugins.gradle.org/m2/") + } + mavenCentral() + } +} diff --git a/src/icon/optimise.sh b/src/icon/optimise.sh new file mode 100755 index 0000000..95e28b0 --- /dev/null +++ b/src/icon/optimise.sh @@ -0,0 +1,3 @@ +svgo --folder=. --output=../main/resources/META-INF +# Remove custom style attribute `-inkscape-font-specification:'Londrina Sketch, Normal';` manually +# TODO: do a simple sed? diff --git a/src/icon/pluginIcon.svg b/src/icon/pluginIcon.svg new file mode 100644 index 0000000..b973ed5 --- /dev/null +++ b/src/icon/pluginIcon.svg @@ -0,0 +1,258 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/icon/pluginIcon_dark.svg b/src/icon/pluginIcon_dark.svg new file mode 100644 index 0000000..0add319 --- /dev/null +++ b/src/icon/pluginIcon_dark.svg @@ -0,0 +1,254 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/icon/restFileType.svg b/src/icon/restFileType.svg new file mode 100644 index 0000000..0402e00 --- /dev/null +++ b/src/icon/restFileType.svg @@ -0,0 +1,157 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + R + + + diff --git a/src/ru/basecode/ide/rest/plugin/Rest.bnf b/src/main/grammars/Rest.bnf similarity index 85% rename from src/ru/basecode/ide/rest/plugin/Rest.bnf rename to src/main/grammars/Rest.bnf index 44d9220..d8d0ab7 100644 --- a/src/ru/basecode/ide/rest/plugin/Rest.bnf +++ b/src/main/grammars/Rest.bnf @@ -1,5 +1,5 @@ { - parserClass="ru.basecode.ide.rest.plugin.parser.RestParser" + parserClass="ru.basecode.ide.rest.plugin.parser._RestParser" extends="com.intellij.extapi.psi.ASTWrapperPsiElement" @@ -13,7 +13,7 @@ tokenTypeClass="ru.basecode.ide.rest.plugin.psi.RestTokenType" } -restFile ::= item_* +restPsiFile ::= item_* //private item_ ::= (WHITE_SPACE|COMMENT|OPTION|PARAM|HEADER|URL|METHOD|SEPARATOR|REQUEST_BODY_LINE|RESPONSE_BODY_LINE|CRLF|BAD_CHARACTER)+ @@ -44,12 +44,12 @@ e_header ::= HEADER headers ::= e_header {e_header|COMMENT|ws}* request_body ::= REQUEST_BODY_LINE {REQUEST_BODY_LINE|COMMENT|ws}* { - mixin="ru.basecode.ide.rest.plugin.psi.impl.RestElementImpl" + mixin="ru.basecode.ide.rest.plugin.psi.impl.RestWrapperPsiElement" } response ::= comments? headers? comments? response_body? response_body ::= RESPONSE_BODY_LINE {RESPONSE_BODY_LINE|COMMENT|ws}* { - mixin="ru.basecode.ide.rest.plugin.psi.impl.RestElementImpl" + mixin="ru.basecode.ide.rest.plugin.psi.impl.RestWrapperPsiElement" } diff --git a/src/ru/basecode/ide/rest/plugin/Rest.flex b/src/main/grammars/Rest.flex similarity index 95% rename from src/ru/basecode/ide/rest/plugin/Rest.flex rename to src/main/grammars/Rest.flex index 32fcfa1..b09c9e0 100644 --- a/src/ru/basecode/ide/rest/plugin/Rest.flex +++ b/src/main/grammars/Rest.flex @@ -1,4 +1,4 @@ -package ru.basecode.ide.rest.plugin; +package ru.basecode.ide.rest.plugin.grammar; import com.intellij.lexer.FlexLexer; import com.intellij.psi.tree.IElementType; @@ -7,7 +7,7 @@ import com.intellij.psi.TokenType; %% -%class RestLexer +%class _RestLexer %implements FlexLexer %unicode %function advance @@ -15,7 +15,7 @@ import com.intellij.psi.TokenType; %eof{ return; %eof} -HTTP_METHOD = ("GET" | "POST" | "PUT" | "PATCH" | "DELETE") +HTTP_METHOD = ("GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "HEAD") OPTION = "--" {LINE}? HEADER = "@" {LINE}? PARAM = "&" {LINE}? diff --git a/src/main/java/ru/basecode/ide/rest/plugin/RestLanguage.java b/src/main/java/ru/basecode/ide/rest/plugin/RestLanguage.java new file mode 100644 index 0000000..7f068a9 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/RestLanguage.java @@ -0,0 +1,11 @@ +package ru.basecode.ide.rest.plugin; + +import com.intellij.lang.Language; + +public class RestLanguage extends Language { + public static final Language INSTANCE = new RestLanguage(); + + public RestLanguage() { + super("Rest"); + } +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/action/SendAction.java b/src/main/java/ru/basecode/ide/rest/plugin/action/SendAction.java new file mode 100644 index 0000000..756e370 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/action/SendAction.java @@ -0,0 +1,159 @@ +package ru.basecode.ide.rest.plugin.action; + +import com.intellij.icons.AllIcons; +import com.intellij.ide.ActivityTracker; +import com.intellij.lang.Language; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.CustomShortcutSet; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.keymap.Keymap; +import com.intellij.openapi.keymap.KeymapManagerListener; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.basecode.ide.rest.plugin.file.RestPsiFile; +import ru.basecode.ide.rest.plugin.http.HttpRequest; +import ru.basecode.ide.rest.plugin.http.HttpResponse; +import ru.basecode.ide.rest.plugin.http.RequestExecutor; +import ru.basecode.ide.rest.plugin.psi.RestESeparator; +import ru.basecode.ide.rest.plugin.psi.RestRequest; +import ru.basecode.ide.rest.plugin.psi.RestRequestParser; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.function.Supplier; + +import static com.intellij.openapi.keymap.KeymapManagerListener.TOPIC; +import static ru.basecode.ide.rest.plugin.misc.Util.format; +import static ru.basecode.ide.rest.plugin.misc.Util.isSuitable; + +public class SendAction extends AnAction { + private static final Logger log = Logger.getInstance(SendAction.class); + + public SendAction() { + super(AllIcons.Actions.Execute); + } + + @Override + public void update(@NotNull AnActionEvent e) { + final RequestExecutor executor = RequestExecutor.getOrDefault(e); + if (executor == null) { + e.getPresentation().setEnabled(false); + } else { + super.update(e); + e.getPresentation().setEnabled(executor.isWaiting()); + } + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + final RequestExecutor executor = RequestExecutor.getOrDefault(e); + if (executor == null || executor.isRunning()) { + return; + } + final Project project = e.getRequiredData(CommonDataKeys.PROJECT); + final VirtualFile file = e.getRequiredData(CommonDataKeys.VIRTUAL_FILE); + if (!isSuitable(project, file)) { + return; + } + final Document document = e.getRequiredData(CommonDataKeys.EDITOR).getDocument(); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + RestPsiFile restPsiFile = getRestFile(project, document); + HttpRequest request = getRequest(restPsiFile); + if (restPsiFile == null || request == null) { + return; + } + try { + long startTime = System.currentTimeMillis(); + String start = "# Executing request...\n# URL: " + request.getUrl() + "\n# Start time: " + + LocalDateTime.now(); + writeResponse(project, document, () -> restPsiFile, start); + HttpResponse response = executor.execute(request); + writeResponse(project, document, () -> restPsiFile, "# Reading response..."); + String headers = "\n# " + response.getStatus() + "\n" + getHeaders(response); + long duration = System.currentTimeMillis() - startTime; + final StringBuilder psiFileContent = new StringBuilder(); + psiFileContent.append("\n# Duration: ").append(duration).append(" ms\n# URL: ").append(request.getUrl()).append("\n").append(headers); + if(response.getBody() != null) { + String formattedResponseBody = getFormattedResponse(project, response.getContentType(), response.getBody()); + psiFileContent.append(formattedResponseBody); + } + writeResponse(project, document, () -> restPsiFile, psiFileContent.toString()); + } catch (Exception ex) { + writeResponse(project, document, () -> restPsiFile, "# Error: " + ex.getMessage()); + } finally { + // Ask activity tracker to update all action statuses + ActivityTracker.getInstance().inc(); + } + }); + } + + private String getHeaders(HttpResponse response) { + StringBuilder sb = new StringBuilder("\n"); + for (String header : response.getHeaders()) { + sb.append("@").append(header).append("\n"); + } + return sb.append("\n").toString(); + } + + private String getFormattedResponse(Project project, @NotNull String contentType, @NotNull String responseBody) { + Collection langList = Language.findInstancesByMimeType(contentType); + if (!langList.isEmpty()) { + return format(project, langList.iterator().next(), responseBody); + } + return responseBody; + } + + @Nullable + private HttpRequest getRequest(RestPsiFile restPsiFile) { + return ApplicationManager.getApplication().runReadAction((Computable) () -> { + if (restPsiFile.getRequest() == null) { + return null; + } + return RestRequestParser.parse(restPsiFile.getRequest()); + }); + } + + private void writeResponse(Project project, Document doc, Supplier file, + String text) { + WriteCommandAction.runWriteCommandAction(project, () -> { + int responsePosition; + String separatorString; + RestPsiFile restPsiFile = file.get(); + RestESeparator separator = restPsiFile.getSeparator(); + if (separator != null && separator.isValid()) { + responsePosition = separator.getTextOffset(); + separatorString = ""; + } else { + RestRequest request = restPsiFile.getRequest(); + responsePosition = request.getTextOffset() + request.getTextLength(); + separatorString = "\n"; + } + String sb = separatorString + "%%%\n" + text; + doc.replaceString(responsePosition, doc.getTextLength(), sb.replace("\r", "")); + }); + } + + private RestPsiFile getRestFile(Project project, Document document) { + return ApplicationManager.getApplication().runReadAction((Computable) () -> { + if (project.isOpen()) { + PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document); + if (psiFile != null && psiFile.isValid() && psiFile instanceof RestPsiFile) { + return (RestPsiFile) psiFile; + } + } + return null; + }); + } + +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/action/StopAction.java b/src/main/java/ru/basecode/ide/rest/plugin/action/StopAction.java new file mode 100644 index 0000000..e2875e1 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/action/StopAction.java @@ -0,0 +1,44 @@ +package ru.basecode.ide.rest.plugin.action; + +import com.intellij.icons.AllIcons; +import com.intellij.ide.ActivityTracker; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.diagnostic.Logger; +import org.jetbrains.annotations.NotNull; +import ru.basecode.ide.rest.plugin.http.RequestExecutor; + +/** + * @author danblack + */ +public class StopAction extends AnAction { + private static final Logger log = Logger.getInstance(StopAction.class); + + public StopAction() { + super(AllIcons.Actions.Suspend); + } + + @Override + public void update(@NotNull AnActionEvent e) { +// log.error(e); + final RequestExecutor executor = RequestExecutor.getOrDefault(e); + if (executor == null) { + e.getPresentation().setEnabled(false); + } else { + super.update(e); + e.getPresentation().setEnabled(executor.isRunning()); + } + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { +// log.error(e); + final RequestExecutor executor = RequestExecutor.getOrDefault(e); + if (executor != null && executor.isRunning()) { + executor.stop(); + // Ask activity tracker to update all action statuses + ActivityTracker.getInstance().inc(); + } + } + +} diff --git a/src/ru/basecode/ide/rest/plugin/RestCodeStyleSettings.java b/src/main/java/ru/basecode/ide/rest/plugin/codestyle/RestCodeStyleSettings.java similarity index 56% rename from src/ru/basecode/ide/rest/plugin/RestCodeStyleSettings.java rename to src/main/java/ru/basecode/ide/rest/plugin/codestyle/RestCodeStyleSettings.java index dcbd449..199911e 100644 --- a/src/ru/basecode/ide/rest/plugin/RestCodeStyleSettings.java +++ b/src/main/java/ru/basecode/ide/rest/plugin/codestyle/RestCodeStyleSettings.java @@ -1,4 +1,4 @@ -package ru.basecode.ide.rest.plugin; +package ru.basecode.ide.rest.plugin.codestyle; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CustomCodeStyleSettings; @@ -7,7 +7,7 @@ * @author danblack */ public class RestCodeStyleSettings extends CustomCodeStyleSettings { - protected RestCodeStyleSettings(CodeStyleSettings container) { - super("RestCodeStyleSettings", container); - } + protected RestCodeStyleSettings(CodeStyleSettings container) { + super("RestCodeStyleSettings", container); + } } diff --git a/src/main/java/ru/basecode/ide/rest/plugin/codestyle/RestCodeStyleSettingsProvider.java b/src/main/java/ru/basecode/ide/rest/plugin/codestyle/RestCodeStyleSettingsProvider.java new file mode 100644 index 0000000..c6c8fa7 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/codestyle/RestCodeStyleSettingsProvider.java @@ -0,0 +1,55 @@ +package ru.basecode.ide.rest.plugin.codestyle; + +import com.intellij.application.options.CodeStyleAbstractConfigurable; +import com.intellij.application.options.CodeStyleAbstractPanel; +import com.intellij.application.options.TabbedLanguageCodeStylePanel; +import com.intellij.psi.codeStyle.CodeStyleConfigurable; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsProvider; +import com.intellij.psi.codeStyle.CustomCodeStyleSettings; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.basecode.ide.rest.plugin.RestLanguage; + +/** + * @author danblack + */ +public class RestCodeStyleSettingsProvider extends CodeStyleSettingsProvider { + @Override + public @NotNull CodeStyleConfigurable createConfigurable(@NotNull CodeStyleSettings settings, + @NotNull CodeStyleSettings modelSettings) { + return new CodeStyleAbstractConfigurable(settings, modelSettings, + RestLanguage.INSTANCE.getDisplayName()) { + + @Nullable + @Override + public String getHelpTopic() { + return null; + } + + @Override + protected CodeStyleAbstractPanel createPanel(CodeStyleSettings settings) { + return new RestTabbedLanguageCodeStylePanel(getCurrentSettings(), settings); + } + }; + } + + @Nullable + @Override + public String getConfigurableDisplayName() { + return RestLanguage.INSTANCE.getDisplayName(); + } + + @Nullable + @Override + public CustomCodeStyleSettings createCustomSettings(CodeStyleSettings settings) { + return new RestCodeStyleSettings(settings); + } + + private static class RestTabbedLanguageCodeStylePanel extends TabbedLanguageCodeStylePanel { + protected RestTabbedLanguageCodeStylePanel(CodeStyleSettings currentSettings, + CodeStyleSettings settings) { + super(RestLanguage.INSTANCE, currentSettings, settings); + } + } +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/editor/RestClientEditor.java b/src/main/java/ru/basecode/ide/rest/plugin/editor/RestClientEditor.java new file mode 100644 index 0000000..a12219f --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/editor/RestClientEditor.java @@ -0,0 +1,200 @@ +package ru.basecode.ide.rest.plugin.editor; + +import com.intellij.codeHighlighting.BackgroundEditorHighlighter; +import com.intellij.ide.ActivityTracker; +import com.intellij.ide.structureView.StructureViewBuilder; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.CustomShortcutSet; +import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorLocation; +import com.intellij.openapi.fileEditor.FileEditorState; +import com.intellij.openapi.fileEditor.FileEditorStateLevel; +import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl; +import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider; +import com.intellij.openapi.keymap.Keymap; +import com.intellij.openapi.keymap.KeymapManager; +import com.intellij.openapi.keymap.KeymapManagerListener; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ui.JBEmptyBorder; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.basecode.ide.rest.plugin.http.RequestExecutor; + +import javax.swing.*; +import java.awt.*; +import java.beans.PropertyChangeListener; + +import static com.intellij.openapi.keymap.KeymapManagerListener.TOPIC; + +public class RestClientEditor implements FileEditor { + + private final PsiAwareTextEditorImpl editor; + private final ActionToolbarImpl toolbar; + private final JPanel panel; + + public RestClientEditor(@NotNull Project project, @NotNull VirtualFile file, @NotNull TextEditorProvider provider) { + editor = new PsiAwareTextEditorImpl(project, file, provider); + toolbar = createToolbarFromGroupId("RestClient.Toolbar.Left"); + panel = new JPanel(new BorderLayout()); + panel.add(toolbar, BorderLayout.NORTH); + panel.add(editor.getComponent(), BorderLayout.CENTER); + } + + @Override + public @NotNull JComponent getComponent() { + return panel; + } + + @NotNull + private static ActionToolbarImpl createToolbarFromGroupId(@SuppressWarnings("SameParameterValue") @NotNull String groupId) { + final ActionManager actionManager = ActionManager.getInstance(); + + if (!actionManager.isGroup(groupId)) { + throw new IllegalStateException(groupId + " should have been a group"); + } + final ActionGroup group = ((ActionGroup)actionManager.getAction(groupId)); + final ActionToolbarImpl editorToolbar = + ((ActionToolbarImpl)actionManager.createActionToolbar(ActionPlaces.EDITOR_TOOLBAR, group, true)); + editorToolbar.setOpaque(false); + editorToolbar.setBorder(new JBEmptyBorder(0, 2, 0, 2)); +// editorToolbar.setForceMinimumSize(true); + + return editorToolbar; + } + + @Override + public @Nullable JComponent getPreferredFocusedComponent() { + return editor.getPreferredFocusedComponent(); + } + + @Override + public @NotNull FileEditorState getState(@NotNull FileEditorStateLevel level) { + return editor.getState(level); + } + + @Override + public void setState(@NotNull FileEditorState state) { + editor.setState(state); + } + + @Override + public boolean isModified() { + return editor.isModified(); + } + + @Override + public boolean isValid() { + return editor.isValid(); + } + + @Override + public @NonNls @NotNull String getName() { + return editor.getName(); + } + + @Override + public void selectNotify() { + editor.selectNotify(); + toolbar.getActions().forEach(this::subscribeToKeyboardShortcuts); + } + + @Override + public void deselectNotify() { + editor.deselectNotify(); + toolbar.getActions().forEach(this::unsubscribeFromKeyboardShortcuts); + } + + @Override + public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) { + editor.addPropertyChangeListener(listener); + } + + @Override + public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) { + editor.removePropertyChangeListener(listener); + } + + @Override + public @Nullable BackgroundEditorHighlighter getBackgroundHighlighter() { + return editor.getBackgroundHighlighter(); + } + + @Override + public @Nullable FileEditorLocation getCurrentLocation() { + return editor.getCurrentLocation(); + } + + @Override + public @Nullable StructureViewBuilder getStructureViewBuilder() { + return editor.getStructureViewBuilder(); + } + + @Override + public void dispose() { + toolbar.getActions().forEach(this::unsubscribeFromKeyboardShortcuts); + Disposer.dispose(editor); + } + + @Override + public @Nullable T getUserData(@NotNull Key key) { + return editor.getUserData(key); + } + + @Override + public void putUserData(@NotNull Key key, @Nullable T value) { + editor.putUserData(key, value); + } + + private void subscribeToKeyboardShortcuts(AnAction action) { + ApplicationManager.getApplication().getMessageBus().connect(this) + .subscribe(TOPIC, new KeymapManagerListener() { + @Override + public void shortcutChanged(@NotNull Keymap keymap, @NotNull String actionId) { + unsubscribeFromKeyboardShortcuts(action); + registerShortCuts(action, keymap); + } + + @Override + public void keymapAdded(@NotNull Keymap keymap) { + unsubscribeFromKeyboardShortcuts(action); + registerShortCuts(action, keymap); + } + + @Override + public void keymapRemoved(@NotNull Keymap keymap) { + unsubscribeFromKeyboardShortcuts(action); + registerShortCuts(action, keymap); + } + + @Override + public void activeKeymapChanged(@Nullable Keymap keymap) { + unsubscribeFromKeyboardShortcuts(action); + if (keymap != null) { + registerShortCuts(action, keymap); + } + } + }); + registerShortCuts(action, KeymapManager.getInstance().getActiveKeymap()); + } + + private void registerShortCuts(@NotNull AnAction action, @NotNull Keymap keymap) { + action.registerCustomShortcutSet(editor.getComponent(), null); + } + + private void unsubscribeFromKeyboardShortcuts(@NotNull AnAction action) { + final RequestExecutor executor = RequestExecutor.getOrDefault(editor.getEditor()); + executor.stop(); + // Ask activity tracker to update all action statuses + ActivityTracker.getInstance().inc(); + action.unregisterCustomShortcutSet(editor.getComponent()); + } +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/editor/RestClientEditorProvider.java b/src/main/java/ru/basecode/ide/rest/plugin/editor/RestClientEditorProvider.java new file mode 100644 index 0000000..20bf82e --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/editor/RestClientEditorProvider.java @@ -0,0 +1,61 @@ +package ru.basecode.ide.rest.plugin.editor; + +import com.intellij.ide.scratch.ScratchUtil; +import com.intellij.lang.LanguageUtil; +import com.intellij.openapi.fileEditor.AsyncFileEditorProvider; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorPolicy; +import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorProvider; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import ru.basecode.ide.rest.plugin.RestLanguage; +import ru.basecode.ide.rest.plugin.file.RestFileType; + +public class RestClientEditorProvider implements AsyncFileEditorProvider, DumbAware { + + private final PsiAwareTextEditorProvider editorProvider; + + public RestClientEditorProvider() { + editorProvider = new PsiAwareTextEditorProvider(); + } + + @Override + public boolean accept(@NotNull Project project, @NotNull VirtualFile file) { + final FileType fileType = file.getFileType(); + + final boolean isRestType = fileType == RestFileType.INSTANCE || ScratchUtil.isScratch(file) + && LanguageUtil.getLanguageForPsi(project, file) == RestLanguage.INSTANCE; + + return isRestType && editorProvider.accept(project, file); + } + + @Override + public @NotNull FileEditor createEditor(@NotNull Project project, + @NotNull VirtualFile file) { + return createEditorAsync(project, file).build(); + } + + @Override + public @NotNull @NonNls String getEditorTypeId() { + return RestClientEditor.class.getCanonicalName(); + } + + @Override + public @NotNull FileEditorPolicy getPolicy() { + return FileEditorPolicy.HIDE_DEFAULT_EDITOR; + } + + @Override + public @NotNull Builder createEditorAsync(@NotNull Project project, @NotNull VirtualFile file) { + return new Builder() { + @Override + public FileEditor build() { + return new RestClientEditor(project, file, RestClientEditorProvider.this.editorProvider); + } + }; + } +} diff --git a/src/ru/basecode/ide/rest/plugin/RestCommenter.java b/src/main/java/ru/basecode/ide/rest/plugin/editor/RestCommenter.java similarity index 93% rename from src/ru/basecode/ide/rest/plugin/RestCommenter.java rename to src/main/java/ru/basecode/ide/rest/plugin/editor/RestCommenter.java index 137691b..b47019e 100644 --- a/src/ru/basecode/ide/rest/plugin/RestCommenter.java +++ b/src/main/java/ru/basecode/ide/rest/plugin/editor/RestCommenter.java @@ -1,4 +1,4 @@ -package ru.basecode.ide.rest.plugin; +package ru.basecode.ide.rest.plugin.editor; import com.intellij.lang.Commenter; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/ru/basecode/ide/rest/plugin/file/RestFileType.java b/src/main/java/ru/basecode/ide/rest/plugin/file/RestFileType.java new file mode 100644 index 0000000..b6c338e --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/file/RestFileType.java @@ -0,0 +1,45 @@ +package ru.basecode.ide.rest.plugin.file; + +import com.intellij.icons.AllIcons; +import com.intellij.openapi.fileTypes.LanguageFileType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.basecode.ide.rest.plugin.RestLanguage; + +import javax.swing.*; + +import static ru.basecode.ide.rest.plugin.misc.Icons.REST_FILE_TYPE; + +public class RestFileType extends LanguageFileType { + public static final String REST_FILE_TYPE_TITLE = "Rest file"; + + public final static LanguageFileType INSTANCE = new RestFileType(); + + protected RestFileType() { + super(RestLanguage.INSTANCE); + } + + @NotNull + @Override + public String getName() { + return REST_FILE_TYPE_TITLE; + } + + @NotNull + @Override + public String getDescription() { + return "Rest file"; + } + + @NotNull + @Override + public String getDefaultExtension() { + return "rest"; + } + + @Nullable + @Override + public Icon getIcon() { + return REST_FILE_TYPE; + } +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/file/RestPsiFile.java b/src/main/java/ru/basecode/ide/rest/plugin/file/RestPsiFile.java new file mode 100644 index 0000000..9907720 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/file/RestPsiFile.java @@ -0,0 +1,41 @@ +package ru.basecode.ide.rest.plugin.file; + +import com.intellij.extapi.psi.PsiFileBase; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.psi.FileViewProvider; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import ru.basecode.ide.rest.plugin.RestLanguage; +import ru.basecode.ide.rest.plugin.psi.RestESeparator; +import ru.basecode.ide.rest.plugin.psi.RestRequest; + +public class RestPsiFile extends PsiFileBase { + + public RestPsiFile(@NotNull FileViewProvider viewProvider) { + super(viewProvider, RestLanguage.INSTANCE); + } + + public RestRequest getRequest() { + for (PsiElement element : getChildren()) { + if (element instanceof RestRequest) { + return (RestRequest) element; + } + } + return null; + } + + public RestESeparator getSeparator() { + for (PsiElement element : getChildren()) { + if (element instanceof RestESeparator) { + return (RestESeparator) element; + } + } + return null; + } + + @NotNull + @Override + public FileType getFileType() { + return RestFileType.INSTANCE; + } +} diff --git a/src/ru/basecode/ide/rest/plugin/RestLexerAdapter.java b/src/main/java/ru/basecode/ide/rest/plugin/grammar/RestLexerAdapter.java similarity index 56% rename from src/ru/basecode/ide/rest/plugin/RestLexerAdapter.java rename to src/main/java/ru/basecode/ide/rest/plugin/grammar/RestLexerAdapter.java index 1ac1842..3829d3b 100644 --- a/src/ru/basecode/ide/rest/plugin/RestLexerAdapter.java +++ b/src/main/java/ru/basecode/ide/rest/plugin/grammar/RestLexerAdapter.java @@ -1,15 +1,12 @@ -package ru.basecode.ide.rest.plugin; +package ru.basecode.ide.rest.plugin.grammar; import com.intellij.lexer.FlexAdapter; import com.intellij.lexer.MergingLexerAdapter; import com.intellij.psi.tree.TokenSet; import ru.basecode.ide.rest.plugin.psi.RestTypes; -/** - * - */ public class RestLexerAdapter extends MergingLexerAdapter { - public RestLexerAdapter() { - super(new FlexAdapter(new RestLexer(null)), TokenSet.create(RestTypes.BAD_CHARACTER)); - } + public RestLexerAdapter() { + super(new FlexAdapter(new _RestLexer(null)), TokenSet.create(RestTypes.BAD_CHARACTER)); + } } diff --git a/src/main/java/ru/basecode/ide/rest/plugin/grammar/RestParserDefinition.java b/src/main/java/ru/basecode/ide/rest/plugin/grammar/RestParserDefinition.java new file mode 100644 index 0000000..d144cac --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/grammar/RestParserDefinition.java @@ -0,0 +1,73 @@ +package ru.basecode.ide.rest.plugin.grammar; + +import com.intellij.lang.ASTNode; +import com.intellij.lang.Language; +import com.intellij.lang.ParserDefinition; +import com.intellij.lang.PsiParser; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.project.Project; +import com.intellij.psi.FileViewProvider; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.TokenType; +import com.intellij.psi.tree.IFileElementType; +import com.intellij.psi.tree.TokenSet; +import org.jetbrains.annotations.NotNull; +import ru.basecode.ide.rest.plugin.RestLanguage; +import ru.basecode.ide.rest.plugin.file.RestPsiFile; +import ru.basecode.ide.rest.plugin.parser._RestParser; +import ru.basecode.ide.rest.plugin.psi.RestTypes; + +public class RestParserDefinition implements ParserDefinition { + + public static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE); + + public static final IFileElementType FILE = + new IFileElementType(Language.findInstance(RestLanguage.class)); + + @NotNull + @Override + public Lexer createLexer(Project project) { + return new RestLexerAdapter(); + } + + @Override + public PsiParser createParser(Project project) { + return new _RestParser(); + } + + @Override + public IFileElementType getFileNodeType() { + return FILE; + } + + @NotNull + @Override + public TokenSet getWhitespaceTokens() { + return WHITE_SPACES; + } + + @NotNull + @Override + public TokenSet getCommentTokens() { + return TokenSet.EMPTY; + } + + @NotNull + @Override + public TokenSet getStringLiteralElements() { + return TokenSet.EMPTY; + } + + @NotNull + @Override + public PsiElement createElement(ASTNode node) { + return RestTypes.Factory.createElement(node); + } + + @Override + public PsiFile createFile(FileViewProvider viewProvider) { + return new RestPsiFile(viewProvider); + } + +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/highlighter/RestColorSettingsPage.java b/src/main/java/ru/basecode/ide/rest/plugin/highlighter/RestColorSettingsPage.java new file mode 100644 index 0000000..a823b95 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/highlighter/RestColorSettingsPage.java @@ -0,0 +1,75 @@ +package ru.basecode.ide.rest.plugin.highlighter; + +import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.openapi.fileTypes.SyntaxHighlighter; +import com.intellij.openapi.options.colors.AttributesDescriptor; +import com.intellij.openapi.options.colors.ColorDescriptor; +import com.intellij.openapi.options.colors.ColorSettingsPage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.basecode.ide.rest.plugin.RestLanguage; + +import javax.swing.*; +import java.util.Map; + +/** + * @author danblack + */ +public class RestColorSettingsPage implements ColorSettingsPage { + + private static final AttributesDescriptor[] DESCRIPTORS = + new AttributesDescriptor[] {new AttributesDescriptor("Comment", RestHighlighter.COMMENT), + new AttributesDescriptor("Method", RestHighlighter.METHOD), + new AttributesDescriptor("Url", RestHighlighter.URL), + new AttributesDescriptor("Header", RestHighlighter.HEADER), + new AttributesDescriptor("Option", RestHighlighter.OPTION), + new AttributesDescriptor("Param", RestHighlighter.PARAM), + new AttributesDescriptor("Error", RestHighlighter.BAD_CHARACTER), + new AttributesDescriptor("Separator", RestHighlighter.SEPARATOR), + new AttributesDescriptor("Body", RestHighlighter.BODY),}; + + @NotNull + @Override + public AttributesDescriptor[] getAttributeDescriptors() { + return DESCRIPTORS; + } + + @NotNull + @Override + public ColorDescriptor[] getColorDescriptors() { + return ColorDescriptor.EMPTY_ARRAY; + } + + @NotNull + @Override + public String getDisplayName() { + return RestLanguage.INSTANCE.getDisplayName(); + } + + @Nullable + @Override + public Icon getIcon() { + return null; + } + + @NotNull + @Override + public SyntaxHighlighter getHighlighter() { + return new RestHighlighter(); + } + + @NotNull + @Override + public String getDemoText() { + return "# This is a comment\n" + "-- option = value\n" + "POST\n" + "http://site.com\n" + + "¶m=value\n" + "@Content-type: application/xml\n" + "\n" + + "This is a request body\n" + "\n" + "%%%\n" + "@Content-length: 100\n" + + "\n" + "This is a response body\n" + ""; + } + + @Nullable + @Override + public Map getAdditionalHighlightingTagToDescriptorMap() { + return null; + } +} diff --git a/src/ru/basecode/ide/rest/plugin/RestHighlighter.java b/src/main/java/ru/basecode/ide/rest/plugin/highlighter/RestHighlighter.java similarity index 95% rename from src/ru/basecode/ide/rest/plugin/RestHighlighter.java rename to src/main/java/ru/basecode/ide/rest/plugin/highlighter/RestHighlighter.java index cd4ffe4..e04ae96 100644 --- a/src/ru/basecode/ide/rest/plugin/RestHighlighter.java +++ b/src/main/java/ru/basecode/ide/rest/plugin/highlighter/RestHighlighter.java @@ -1,4 +1,4 @@ -package ru.basecode.ide.rest.plugin; +package ru.basecode.ide.rest.plugin.highlighter; import com.intellij.lexer.Lexer; import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; @@ -7,13 +7,11 @@ import com.intellij.psi.TokenType; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; +import ru.basecode.ide.rest.plugin.grammar.RestLexerAdapter; import ru.basecode.ide.rest.plugin.psi.RestTypes; import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey; -/** - * - */ public class RestHighlighter extends SyntaxHighlighterBase { @@ -65,6 +63,6 @@ public TextAttributesKey[] getTokenHighlights(IElementType tokenType) { } else if (tokenType.equals(TokenType.BAD_CHARACTER)) { return BAD_CHAR_KEYS; } - return EMPTY; + return TextAttributesKey.EMPTY_ARRAY; } } diff --git a/src/ru/basecode/ide/rest/plugin/RestHighlighterFactory.java b/src/main/java/ru/basecode/ide/rest/plugin/highlighter/RestHighlighterFactory.java similarity index 53% rename from src/ru/basecode/ide/rest/plugin/RestHighlighterFactory.java rename to src/main/java/ru/basecode/ide/rest/plugin/highlighter/RestHighlighterFactory.java index e8686a5..63c3647 100644 --- a/src/ru/basecode/ide/rest/plugin/RestHighlighterFactory.java +++ b/src/main/java/ru/basecode/ide/rest/plugin/highlighter/RestHighlighterFactory.java @@ -1,4 +1,4 @@ -package ru.basecode.ide.rest.plugin; +package ru.basecode.ide.rest.plugin.highlighter; import com.intellij.openapi.fileTypes.SyntaxHighlighter; import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory; @@ -6,16 +6,12 @@ import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; -/** - * - */ public class RestHighlighterFactory extends SyntaxHighlighterFactory { + private final RestHighlighter restHighlighter = new RestHighlighter(); - private final RestHighlighter restHighlighter = new RestHighlighter(); - - @NotNull - @Override - public SyntaxHighlighter getSyntaxHighlighter(Project project, VirtualFile virtualFile) { - return restHighlighter; - } + @NotNull + @Override + public SyntaxHighlighter getSyntaxHighlighter(Project project, VirtualFile virtualFile) { + return restHighlighter; + } } diff --git a/src/main/java/ru/basecode/ide/rest/plugin/http/HttpRequest.java b/src/main/java/ru/basecode/ide/rest/plugin/http/HttpRequest.java new file mode 100644 index 0000000..a9b3fa3 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/http/HttpRequest.java @@ -0,0 +1,60 @@ +package ru.basecode.ide.rest.plugin.http; + +import lombok.Builder; +import lombok.Value; + +import java.util.List; + +/** + * @author danblack + */ +@Value +@Builder +public class HttpRequest { + + Method method; + String url; + List
headers; + String body; + + public HttpRequest(Method method, String url, List
headers, String body) { + this.method = method; + this.url = url; + this.headers = headers; + this.body = body; + } + + @Override + public String toString() { + return "Request{" + "method=" + method + ", url='" + url + '\'' + ", headers=" + headers + + ", body='" + body + '\'' + '}'; + } + + public static enum Method { + GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD + } + + + @Value + @Builder + public static class Header { + String name; + String value; + + public Header(String name, String value) { + this.name = name; + this.value = value; + } + } + + + @Value + @Builder + public static class Params { + int timeout; + + public Params(int timeout) { + this.timeout = timeout; + } + } +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/http/HttpResponse.java b/src/main/java/ru/basecode/ide/rest/plugin/http/HttpResponse.java new file mode 100644 index 0000000..24005f2 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/http/HttpResponse.java @@ -0,0 +1,20 @@ +package ru.basecode.ide.rest.plugin.http; + +import lombok.Builder; +import lombok.Value; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author danblack + */ +@Value +@Builder +public class HttpResponse { + String status; + List headers; + String contentType; + @Nullable + String body; +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/http/RequestExecutor.java b/src/main/java/ru/basecode/ide/rest/plugin/http/RequestExecutor.java new file mode 100644 index 0000000..00f2608 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/http/RequestExecutor.java @@ -0,0 +1,148 @@ +package ru.basecode.ide.rest.plugin.http; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.UserDataHolder; +import org.apache.http.Header; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.util.EntityUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * @author danblack + */ +public class RequestExecutor { + + private static final Key EXECUTOR_KEY = new Key<>("ExecutorKey"); + + private static final Charset DEFAULT_CHARSET = UTF_8; + private final HttpClient httpClient = createHttpClient(); + private State state = State.WAITING; + private HttpRequestBase httpRequest; + + @Nullable + public static RequestExecutor getOrDefault(AnActionEvent event) { + // Its possible for the event that triggered this might not have an editor associated + final Editor editor = event.getData(CommonDataKeys.EDITOR); + if (editor != null) { + return getOrDefault(editor); + } + return null; + } + + @NotNull + public static RequestExecutor getOrDefault(UserDataHolder dataHolder) { + RequestExecutor executor = dataHolder.getUserData(EXECUTOR_KEY); + if (executor == null) { + executor = new RequestExecutor(); + dataHolder.putUserData(EXECUTOR_KEY, executor); + } + return executor; + } + + public void stop() { + if (httpRequest != null) { + httpRequest.abort(); + } + } + + private CloseableHttpClient createHttpClient() { + try { + SSLContextBuilder contextBuilder = SSLContextBuilder.create(); + contextBuilder.loadTrustMaterial(null, (x509Certificates, s) -> true); + SSLConnectionSocketFactory sslSocketFactory = + new SSLConnectionSocketFactory(contextBuilder.build(), NoopHostnameVerifier.INSTANCE); + return HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build(); + } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { + throw new RuntimeException(e); + } + } + + public HttpResponse execute(HttpRequest request) throws IOException { + //@formatter:off + state = State.RUNNING; + try { + httpRequest = Requests.createHttpRequest(request); + org.apache.http.HttpResponse response = httpClient.execute(httpRequest); + if (response.getEntity() != null) { + return HttpResponse.builder() + .status(response.getStatusLine().toString()) + .headers(getHeaders(response)) + .contentType(getContentType(response)) + .body(EntityUtils.toString(response.getEntity(), DEFAULT_CHARSET)) + .build(); + } else { + return HttpResponse.builder() + .status(response.getStatusLine().toString()) + .headers(getHeaders(response)) + .contentType(getContentType(response)) + .build(); + } + } finally { + state = State.WAITING; + } + //@formatter:on + } + + private String getContentType(org.apache.http.HttpResponse response) { + Header header = response.getFirstHeader("Content-Type"); + if (header != null) { + String contentType = header.getValue(); + int semicolonIndex = contentType.indexOf(";"); + if (semicolonIndex >= 0) { + return contentType.substring(0, semicolonIndex); + } else { + return contentType; + } + } + return null; + } + + private List getHeaders(org.apache.http.HttpResponse response) { + Header[] allHeaders = response.getAllHeaders(); + if (allHeaders != null) { + ArrayList result = new ArrayList<>(); + for (Header header : allHeaders) { + result.add(header.getName() + ": " + header.getValue()); + } + return result; + } + return Collections.emptyList(); + } + + public boolean isRunning() { + return state == State.RUNNING; + } + + public boolean isWaiting() { + return state == State.WAITING; + } + + public State getState() { + return state; + } + + public enum State { + WAITING, RUNNING, STOPPING + } +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/http/Requests.java b/src/main/java/ru/basecode/ide/rest/plugin/http/Requests.java new file mode 100644 index 0000000..4888509 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/http/Requests.java @@ -0,0 +1,102 @@ +package ru.basecode.ide.rest.plugin.http; + +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpOptions; +import org.apache.http.client.methods.HttpPatch; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.StringEntity; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.function.Function; + +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.DELETE; +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.GET; +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.HEAD; +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.OPTIONS; +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.PATCH; +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.POST; +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.PUT; + +/** + * @author danblack + */ +public class Requests { + + private static final Charset DEFAULT_CHARSET = Charset.defaultCharset(); + + public static HttpRequestBase createHttpRequest(HttpRequest request) + throws UnsupportedEncodingException { + if (request.getMethod() == GET) { + HttpGet httpGet = new HttpGet(request.getUrl()); + fillHeaders(request, httpGet); + return httpGet; + } + + if (request.getMethod() == POST) { + return create(HttpPost::new, request); + } + + if (request.getMethod() == PUT) { + return create(HttpPut::new, request); + } + + if (request.getMethod() == PATCH) { + return create(HttpPatch::new, request); + } + + if (request.getMethod() == OPTIONS) { + final HttpOptions httpOptiopns = new HttpOptions(request.getUrl()); + fillHeaders(request, httpOptiopns); + return httpOptiopns; + } + + if (request.getMethod() == HEAD) { + final HttpHead httpHead = new HttpHead(request.getUrl()); + fillHeaders(request, httpHead); + return httpHead; + } + + if (request.getMethod() == DELETE) { + HttpDelete httpDelete = new HttpDelete(request.getUrl()); + fillHeaders(request, httpDelete); + return httpDelete; + } + + throw new IllegalStateException(); + } + + public static HttpEntityEnclosingRequestBase create( + Function constructor, HttpRequest request) + throws UnsupportedEncodingException { + HttpEntityEnclosingRequestBase httpRequest = constructor.apply(request.getUrl()); + fillHeaders(request, httpRequest); + fillBody(request, httpRequest); + return httpRequest; + } + + private static void fillBody(HttpRequest request, HttpEntityEnclosingRequestBase httpRequest) { + String body = request.getBody(); + if (body != null) { + httpRequest.setEntity(new StringEntity(body, DEFAULT_CHARSET)); + for (HttpRequest.Header header : request.getHeaders()) { + if ("Content-Type".equals(header.getName())) { + if (!header.getValue().contains("charset")) { + // TODO: Not sure what the author wanted to do + } + } + } + } + } + + private static void fillHeaders(HttpRequest request, HttpRequestBase httpRequest) { + for (HttpRequest.Header header : request.getHeaders()) { + httpRequest.addHeader(header.getName(), header.getValue()); + } + } +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/injector/RestHostInjector.java b/src/main/java/ru/basecode/ide/rest/plugin/injector/RestHostInjector.java new file mode 100644 index 0000000..973e8ec --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/injector/RestHostInjector.java @@ -0,0 +1,70 @@ +package ru.basecode.ide.rest.plugin.injector; + +import com.intellij.lang.Language; +import com.intellij.lang.injection.MultiHostInjector; +import com.intellij.lang.injection.MultiHostRegistrar; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiLanguageInjectionHost; +import org.jetbrains.annotations.NotNull; +import ru.basecode.ide.rest.plugin.psi.RestEHeader; +import ru.basecode.ide.rest.plugin.psi.RestHeaders; +import ru.basecode.ide.rest.plugin.psi.RestRequest; +import ru.basecode.ide.rest.plugin.psi.RestRequestBody; +import ru.basecode.ide.rest.plugin.psi.RestResponse; +import ru.basecode.ide.rest.plugin.psi.RestResponseBody; +import ru.basecode.ide.rest.plugin.psi.impl.RestWrapperPsiElement; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class RestHostInjector implements MultiHostInjector { + @Override + public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, + @NotNull final PsiElement context) { + RestHeaders headers = null; + if (context instanceof RestResponseBody) { + headers = ((RestResponse) context.getParent()).getHeaders(); + } else if (context instanceof RestRequestBody) { + headers = ((RestRequest) context.getParent()).getHeaders(); + } + if (headers != null) { + String contentType = getContentType(headers); + if (contentType != null) { + Collection langList = Language.findInstancesByMimeType(contentType); + if (!langList.isEmpty()) { + registrar.startInjecting(langList.iterator().next()) + .addPlace(null, null, (PsiLanguageInjectionHost) context, + TextRange.create(0, context.getTextLength())).doneInjecting(); + } + } + } + } + + private String getContentType(@NotNull RestHeaders headers) { + if (headers.isValid()) { + for (RestEHeader eHeader : headers.getEHeaderList()) { + String header = eHeader.getText(); + if (header.startsWith("@Content-Type")) { + int colonIndex = header.indexOf(":"); + if (colonIndex >= 0) { + String contentType = header.substring(colonIndex + 1); + int semicolonIndex = contentType.indexOf(";"); + if (semicolonIndex >= 0) { + return contentType.substring(0, semicolonIndex).trim(); + } + return contentType.trim(); + } + } + } + } + return null; + } + + @NotNull + @Override + public List> elementsToInjectIn() { + return Collections.singletonList(RestWrapperPsiElement.class); + } +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/misc/Icons.java b/src/main/java/ru/basecode/ide/rest/plugin/misc/Icons.java new file mode 100644 index 0000000..5d6e56f --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/misc/Icons.java @@ -0,0 +1,13 @@ +package ru.basecode.ide.rest.plugin.misc; + +import com.intellij.openapi.util.IconLoader; +import lombok.NoArgsConstructor; + +import javax.swing.*; + +import static lombok.AccessLevel.PRIVATE; + +@NoArgsConstructor(access = PRIVATE) +public class Icons { + public static Icon REST_FILE_TYPE = IconLoader.getIcon("/META-INF/restFileType.svg"); +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/misc/Util.java b/src/main/java/ru/basecode/ide/rest/plugin/misc/Util.java new file mode 100644 index 0000000..f88b895 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/misc/Util.java @@ -0,0 +1,38 @@ +package ru.basecode.ide.rest.plugin.misc; + +import com.intellij.lang.Language; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.impl.http.HttpVirtualFile; +import com.intellij.psi.FileViewProvider; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.impl.PsiFileFactoryImpl; +import lombok.NoArgsConstructor; +import org.jetbrains.annotations.NotNull; +import ru.basecode.ide.rest.plugin.RestLanguage; + +import static lombok.AccessLevel.PRIVATE; + +@NoArgsConstructor(access = PRIVATE) +public class Util { + public static boolean isSuitable(@NotNull Project project, @NotNull VirtualFile file) { + if (file instanceof HttpVirtualFile) { + return false; + } + final FileViewProvider provider = PsiManager.getInstance(project).findViewProvider(file); + return provider != null && RestLanguage.INSTANCE == provider.getBaseLanguage(); + } + + public static String format(final Project project, @NotNull Language language, + @NotNull String responseBody) { + return WriteCommandAction.runWriteCommandAction(project, (Computable) () -> { + final PsiFile psiFile = PsiFileFactoryImpl.getInstance(project).createFileFromText("virtual", language, responseBody); + CodeStyleManager.getInstance(project).reformatText(psiFile, 0, psiFile.getTextLength()); + return psiFile.getText(); + }); + } +} diff --git a/src/ru/basecode/ide/rest/plugin/psi/RestElement.java b/src/main/java/ru/basecode/ide/rest/plugin/psi/RestElement.java similarity index 93% rename from src/ru/basecode/ide/rest/plugin/psi/RestElement.java rename to src/main/java/ru/basecode/ide/rest/plugin/psi/RestElement.java index 6f5f1a1..2d095a3 100644 --- a/src/ru/basecode/ide/rest/plugin/psi/RestElement.java +++ b/src/main/java/ru/basecode/ide/rest/plugin/psi/RestElement.java @@ -2,8 +2,5 @@ import com.intellij.psi.NavigatablePsiElement; -/** - * - */ public interface RestElement extends NavigatablePsiElement { } diff --git a/src/ru/basecode/ide/rest/plugin/psi/RestElementType.java b/src/main/java/ru/basecode/ide/rest/plugin/psi/RestElementType.java similarity index 67% rename from src/ru/basecode/ide/rest/plugin/psi/RestElementType.java rename to src/main/java/ru/basecode/ide/rest/plugin/psi/RestElementType.java index 04e3089..788613b 100644 --- a/src/ru/basecode/ide/rest/plugin/psi/RestElementType.java +++ b/src/main/java/ru/basecode/ide/rest/plugin/psi/RestElementType.java @@ -5,11 +5,8 @@ import org.jetbrains.annotations.NotNull; import ru.basecode.ide.rest.plugin.RestLanguage; -/** - * - */ public class RestElementType extends IElementType { - public RestElementType(@NotNull @NonNls String debugName) { - super(debugName, RestLanguage.INSTANCE); - } + public RestElementType(@NotNull @NonNls String debugName) { + super(debugName, RestLanguage.INSTANCE); + } } diff --git a/src/main/java/ru/basecode/ide/rest/plugin/psi/RestRequestParser.java b/src/main/java/ru/basecode/ide/rest/plugin/psi/RestRequestParser.java new file mode 100644 index 0000000..fc43817 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/psi/RestRequestParser.java @@ -0,0 +1,140 @@ +package ru.basecode.ide.rest.plugin.psi; + +import com.google.common.net.UrlEscapers; +import org.jetbrains.annotations.NotNull; +import ru.basecode.ide.rest.plugin.http.HttpRequest; +import ru.basecode.ide.rest.plugin.http.HttpRequest.Method; +import ru.basecode.ide.rest.plugin.psi.RestEHeader; +import ru.basecode.ide.rest.plugin.psi.RestEMethod; +import ru.basecode.ide.rest.plugin.psi.RestEParam; +import ru.basecode.ide.rest.plugin.psi.RestEUrl; +import ru.basecode.ide.rest.plugin.psi.RestHeaders; +import ru.basecode.ide.rest.plugin.psi.RestParams; +import ru.basecode.ide.rest.plugin.psi.RestRequest; +import ru.basecode.ide.rest.plugin.psi.RestRequestBody; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.DELETE; +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.GET; +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.HEAD; +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.OPTIONS; +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.PATCH; +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.POST; +import static ru.basecode.ide.rest.plugin.http.HttpRequest.Method.PUT; + +/** + * @author danblack + */ +public class RestRequestParser { + + private static final Function encoder = + v -> UrlEscapers.urlFormParameterEscaper().escape(v); + + private static Method getMethod(RestRequest request) { + RestEMethod method = request.getEMethod(); + if (method != null && method.isValid()) { + switch (method.getText()) { + case "GET": + return GET; + case "PUT": + return PUT; + case "POST": + return POST; + case "DELETE": + return DELETE; + case "PATCH": + return PATCH; + case "OPTIONS": + return OPTIONS; + case "HEAD": + return HEAD; + } + throw new IllegalStateException(); + } + return GET; + } + + public static HttpRequest parse(RestRequest request) { + Method method = getMethod(request); + String url = getUrl(request); + String body = getBody(request); + List headers = getHeaders(request); + return HttpRequest.builder().method(method).url(url).headers(headers).body(body).build(); + } + + private static List getHeaders(RestRequest request) { + ArrayList result = new ArrayList<>(); + RestHeaders headers = request.getHeaders(); + if (headers != null) { + for (RestEHeader header : headers.getEHeaderList()) { + String text = header.getText(); + int i = text.indexOf("@"); + if (i >= 0) { + i = text.indexOf(":"); + if (i >= 0) { + String name = text.substring(1, i).trim(); + String value = text.substring(i + 1).trim(); + result.add(new HttpRequest.Header(name, value)); + } + } + } + } + return result; + } + + private static String getBody(RestRequest request) { + RestRequestBody body = request.getRequestBody(); + if (body != null && body.isValid()) { + return body.getText(); + } + return null; + } + + @NotNull + private static String getUrl(RestRequest request) { + RestEUrl url = request.getEUrl(); + if (url.isValid()) { + String urlText = url.getText(); + boolean markExists = urlText.contains("?"); + StringBuilder sb = new StringBuilder(urlText); + RestParams params = request.getParams(); + if (params != null && params.isValid()) { + if (!markExists) { + sb.append('?'); + } + for (RestEParam param : params.getEParamList()) { + sb.append(param.getText()); + } + } + return encode(sb.toString()); + } + throw new IllegalStateException(""); + } + + static String encode(String url) { + StringBuilder sb = new StringBuilder(url.length()); + int qpos = url.indexOf("?"); + if (qpos == -1) { + return url; + } + sb.append(url.substring(0, qpos + 1)); + url = url.substring(qpos + 1); + String delimiter = ""; + for (String paramEntry : url.split("&")) { + sb.append(delimiter); + int epos = paramEntry.indexOf("="); + if (epos == -1) { + sb.append(UrlEscapers.urlFormParameterEscaper().escape(paramEntry.trim())); + } else { + String paramName = paramEntry.substring(0, epos).trim(); + String paramValue = paramEntry.substring(epos + 1).trim(); + sb.append(encoder.apply(paramName)).append("=").append(encoder.apply(paramValue)); + } + delimiter = "&"; + } + return sb.toString(); + } +} diff --git a/src/ru/basecode/ide/rest/plugin/psi/RestTokenType.java b/src/main/java/ru/basecode/ide/rest/plugin/psi/RestTokenType.java similarity index 67% rename from src/ru/basecode/ide/rest/plugin/psi/RestTokenType.java rename to src/main/java/ru/basecode/ide/rest/plugin/psi/RestTokenType.java index bb8254c..21c729a 100644 --- a/src/ru/basecode/ide/rest/plugin/psi/RestTokenType.java +++ b/src/main/java/ru/basecode/ide/rest/plugin/psi/RestTokenType.java @@ -5,11 +5,8 @@ import org.jetbrains.annotations.NotNull; import ru.basecode.ide.rest.plugin.RestLanguage; -/** - * - */ public class RestTokenType extends IElementType { - public RestTokenType(@NotNull @NonNls String debugName) { - super(debugName, RestLanguage.INSTANCE); - } + public RestTokenType(@NotNull @NonNls String debugName) { + super(debugName, RestLanguage.INSTANCE); + } } diff --git a/src/main/java/ru/basecode/ide/rest/plugin/psi/impl/ResponseLiteralEscaper.java b/src/main/java/ru/basecode/ide/rest/plugin/psi/impl/ResponseLiteralEscaper.java new file mode 100644 index 0000000..bf21a12 --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/psi/impl/ResponseLiteralEscaper.java @@ -0,0 +1,35 @@ +package ru.basecode.ide.rest.plugin.psi.impl; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.LiteralTextEscaper; +import org.jetbrains.annotations.NotNull; + +public class ResponseLiteralEscaper extends LiteralTextEscaper { + public ResponseLiteralEscaper(RestWrapperPsiElement host) { + super(host); + } + + @Override + public boolean decode(@NotNull final TextRange rangeInsideHost, @NotNull StringBuilder outChars) { + outChars + .append(myHost.getText(), rangeInsideHost.getStartOffset(), rangeInsideHost.getEndOffset()); + return true; + } + + @Override + public int getOffsetInHost(int offsetInDecoded, @NotNull final TextRange rangeInsideHost) { + int offset = offsetInDecoded + rangeInsideHost.getStartOffset(); + if (offset < rangeInsideHost.getStartOffset()) { + offset = rangeInsideHost.getStartOffset(); + } + if (offset > rangeInsideHost.getEndOffset()) { + offset = rangeInsideHost.getEndOffset(); + } + return offset; + } + + @Override + public boolean isOneLine() { + return true; + } +} diff --git a/src/main/java/ru/basecode/ide/rest/plugin/psi/impl/RestWrapperPsiElement.java b/src/main/java/ru/basecode/ide/rest/plugin/psi/impl/RestWrapperPsiElement.java new file mode 100644 index 0000000..de45c0e --- /dev/null +++ b/src/main/java/ru/basecode/ide/rest/plugin/psi/impl/RestWrapperPsiElement.java @@ -0,0 +1,31 @@ +package ru.basecode.ide.rest.plugin.psi.impl; + +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import com.intellij.lang.ASTNode; +import com.intellij.psi.LiteralTextEscaper; +import com.intellij.psi.PsiLanguageInjectionHost; +import org.jetbrains.annotations.NotNull; + +public class RestWrapperPsiElement extends ASTWrapperPsiElement + implements PsiLanguageInjectionHost { + + public RestWrapperPsiElement(@NotNull ASTNode node) { + super(node); + } + + @Override + public boolean isValidHost() { + return true; + } + + @Override + public PsiLanguageInjectionHost updateText(@NotNull String text) { + return this; + } + + @NotNull + @Override + public LiteralTextEscaper createLiteralTextEscaper() { + return new ResponseLiteralEscaper(this); + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml new file mode 100644 index 0000000..6b36e36 --- /dev/null +++ b/src/main/resources/META-INF/plugin.xml @@ -0,0 +1,70 @@ + + ru.basecode.ide.rest.plugin + Rest Client + Denis Chernyshov + + + + + + + + + com.intellij.modules.platform + + com.intellij.modules.lang + com.intellij.modules.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/pluginIcon.svg b/src/main/resources/META-INF/pluginIcon.svg new file mode 100644 index 0000000..dcd617e --- /dev/null +++ b/src/main/resources/META-INF/pluginIcon.svg @@ -0,0 +1,14 @@ + + + + + + + diff --git a/src/main/resources/META-INF/pluginIcon_dark.svg b/src/main/resources/META-INF/pluginIcon_dark.svg new file mode 100644 index 0000000..acbae6a --- /dev/null +++ b/src/main/resources/META-INF/pluginIcon_dark.svg @@ -0,0 +1,14 @@ + + + + + + + diff --git a/src/main/resources/META-INF/restFileType.svg b/src/main/resources/META-INF/restFileType.svg new file mode 100644 index 0000000..683c7a8 --- /dev/null +++ b/src/main/resources/META-INF/restFileType.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + R + + + + diff --git a/resources/META-INF/schema.xml b/src/main/resources/META-INF/schema.xml similarity index 100% rename from resources/META-INF/schema.xml rename to src/main/resources/META-INF/schema.xml diff --git a/src/ru/basecode/ide/rest/plugin/HeaderPanelComponent.java b/src/ru/basecode/ide/rest/plugin/HeaderPanelComponent.java deleted file mode 100644 index 4132d3e..0000000 --- a/src/ru/basecode/ide/rest/plugin/HeaderPanelComponent.java +++ /dev/null @@ -1,135 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.ActionPlaces; -import com.intellij.openapi.actionSystem.CustomShortcutSet; -import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.TextEditor; -import com.intellij.openapi.keymap.Keymap; -import com.intellij.openapi.keymap.KeymapManager; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Key; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -/** - * @author danblack - */ -public class HeaderPanelComponent implements Disposable { - - private static final Key HEADER_PANEL_KEY = new Key<>("HEADER_PANEL_KEY"); - - public static void dispose(@NotNull Editor editor) { - HeaderPanelComponent component = editor.getUserData(HEADER_PANEL_KEY); - if (component != null) { - Disposer.dispose(component); - } - } - - public static boolean attached(Editor editor) { - return editor.getUserData(HEADER_PANEL_KEY) != null; - } - - private final ActionToolbarImpl panel; - private final RequestExecutor requestExecutor; - private final RunAction runAction; - private final StopAction stopAction; - private final Editor editor; - private TextEditor textEditor; - private Keymap keymap; - - private final Keymap.Listener shortcutChangeListener = actionId -> { - if (actionId.equals(RunAction.ID) || actionId.equals(StopAction.ID)) { - unregisterShortCuts(); - registerShortCuts(); - } - }; - - public HeaderPanelComponent(TextEditor textEditor) { - this.textEditor = textEditor; - editor = textEditor.getEditor(); - editor.putUserData(HEADER_PANEL_KEY, this); - - requestExecutor = new RequestExecutor(); - - runAction = new RunAction(requestExecutor); - stopAction = new StopAction(requestExecutor); - - panel = createPanel(createActionGroup()); - getFileEditorManager().addTopComponent(textEditor, panel); - - registerKeymapManagerListener(); - registerShortCuts(); - } - - @Override - public void dispose() { - if (textEditor != null) { - getFileEditorManager().removeTopComponent(textEditor, panel); - unregisterShortCuts(); - editor.putUserData(HEADER_PANEL_KEY, null); - textEditor = null; - } - } - - private FileEditorManager getFileEditorManager() { - return FileEditorManager.getInstance(editor.getProject()); - } - - @NotNull - private DefaultActionGroup createActionGroup() { - DefaultActionGroup group = new DefaultActionGroup("rest-request", false); - group.add(runAction); - group.add(stopAction); - return group; - } - - private ActionToolbarImpl createPanel(DefaultActionGroup group) { - ActionToolbarImpl result = (ActionToolbarImpl) ActionManager.getInstance().createActionToolbar(ActionPlaces.EDITOR_TOOLBAR, group, true); - result.setForceMinimumSize(true); - return result; - } - - private void registerShortCuts() { - Objects.requireNonNull(editor, "Editor required"); - - KeymapManager keymapManager = KeymapManager.getInstance(); - Keymap newKeymap = keymapManager.getActiveKeymap(); - - if (!Objects.equals(keymap, newKeymap)) { - if (keymap != null) { - unregisterShortCuts(); - keymap.removeShortcutChangeListener(shortcutChangeListener); - } - keymap = newKeymap; - keymap.addShortcutChangeListener(shortcutChangeListener); // does not work - } - - runAction.registerCustomShortcutSet( - new CustomShortcutSet(keymap.getShortcuts(RunAction.ID)), - editor.getComponent()); - - stopAction.registerCustomShortcutSet( - new CustomShortcutSet(keymap.getShortcuts(StopAction.ID)), - editor.getComponent()); - } - - private void registerKeymapManagerListener() { - KeymapManager keymapManager = KeymapManager.getInstance(); - keymapManager.addKeymapManagerListener(keymap -> { - unregisterShortCuts(); - registerShortCuts(); - }, this); - } - - private void unregisterShortCuts() { - Objects.requireNonNull(editor, "Editor required"); - runAction.unregisterCustomShortcutSet(editor.getComponent()); - stopAction.unregisterCustomShortcutSet(editor.getComponent()); - } -} diff --git a/src/ru/basecode/ide/rest/plugin/Request.java b/src/ru/basecode/ide/rest/plugin/Request.java deleted file mode 100644 index 5ee16b1..0000000 --- a/src/ru/basecode/ide/rest/plugin/Request.java +++ /dev/null @@ -1,159 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import java.util.List; - -/** - * @author danblack - */ -public class Request { - - public static class Header { - private final String name; - private final String value; - - public Header(String name, String value) { - this.name = name; - this.value = value; - } - - public String getName() { - return name; - } - - public String getValue() { - return value; - } - - @Override - public String toString() { - return "Header{" + - "name='" + name + '\'' + - ", value='" + value + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Header header = (Header) o; - - if (name != null ? !name.equals(header.name) : header.name != null) return false; - return value != null ? value.equals(header.value) : header.value == null; - - } - - @Override - public int hashCode() { - int result = name != null ? name.hashCode() : 0; - result = 31 * result + (value != null ? value.hashCode() : 0); - return result; - } - } - - enum Method { - GET, - POST, - PUT, - PATCH, - DELETE - } - - public static class Params { - private final int timeout; - - public Params(int timeout) { - this.timeout = timeout; - } - - public int getTimeout() { - return timeout; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Params params = (Params) o; - - return timeout == params.timeout; - - } - - @Override - public int hashCode() { - return timeout; - } - - @Override - - public String toString() { - return "Params{" + - "timeout=" + timeout + - '}'; - } - } - - private final Method method; - private final String url; - private final List
headers; - private final String body; - - public Request(Method method, String url, List
headers, String body) { - this.method = method; - this.url = url; - this.headers = headers; - this.body = body; - } - - public Method getMethod() { - return method; - } - - public String getUrl() { - return url; - } - - public List
getHeaders() { - return headers; - } - - public String getBody() { - return body; - } - - @Override - public String toString() { - return "Request{" + - "method=" + method + - ", url='" + url + '\'' + - ", headers=" + headers + - ", body='" + body + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Request request = (Request) o; - - if (method != request.method) return false; - if (url != null ? !url.equals(request.url) : request.url != null) return false; - if (headers != null ? !headers.equals(request.headers) : request.headers != null) return false; - return body != null ? body.equals(request.body) : request.body == null; - - } - - @Override - public int hashCode() { - int result = method != null ? method.hashCode() : 0; - result = 31 * result + (url != null ? url.hashCode() : 0); - result = 31 * result + (headers != null ? headers.hashCode() : 0); - result = 31 * result + (body != null ? body.hashCode() : 0); - return result; - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RequestExecutor.java b/src/ru/basecode/ide/rest/plugin/RequestExecutor.java deleted file mode 100644 index 904d862..0000000 --- a/src/ru/basecode/ide/rest/plugin/RequestExecutor.java +++ /dev/null @@ -1,123 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.UserDataHolder; -import org.apache.http.Header; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.ssl.SSLContextBuilder; -import org.apache.http.util.EntityUtils; -import ru.basecode.ide.rest.plugin.http.Response; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * @author danblack - */ -public class RequestExecutor { - - private static final Key EXECUTOR_KEY = new Key<>("ExecutorKey"); - private State state = State.WAITING; - - enum State { - WAITING, - RUNNING, - STOPPING - } - - private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); - - private final HttpClient httpClient = createHttpClient(); - - public static RequestExecutor getInstance(UserDataHolder dataHolder) { - RequestExecutor executor = dataHolder.getUserData(EXECUTOR_KEY); - if (executor == null) { - executor = new RequestExecutor(); - dataHolder.putUserData(EXECUTOR_KEY, executor); - } - return executor; - } - - private HttpRequestBase httpRequest; - - public void stop() { - httpRequest.abort(); - } - - private CloseableHttpClient createHttpClient() { - try { - SSLContextBuilder contextBuilder = SSLContextBuilder.create(); - contextBuilder.loadTrustMaterial(null, (x509Certificates, s) -> true); - SSLConnectionSocketFactory sslSocketFactory = - new SSLConnectionSocketFactory(contextBuilder.build(), NoopHostnameVerifier.INSTANCE); - return HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build(); - } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { - throw new RuntimeException(e); - } - } - - public Response execute(Request request) throws IOException { - state = State.RUNNING; - try { - httpRequest = Requests.createHttpRequest(request); - HttpResponse response = httpClient.execute(httpRequest); - return new Response( - response.getStatusLine().toString(), - getHeaders(response), - getContentType(response), - EntityUtils.toString(response.getEntity(), DEFAULT_CHARSET)); - } finally { - state = State.WAITING; - } - } - - private String getContentType(HttpResponse response) { - Header header = response.getFirstHeader("Content-Type"); - if (header != null) { - String contentType = header.getValue(); - int semicolonIndex = contentType.indexOf(";"); - if (semicolonIndex >= 0) { - return contentType.substring(0, semicolonIndex); - } else { - return contentType; - } - } - return null; - } - - private List getHeaders(HttpResponse response) { - Header[] allHeaders = response.getAllHeaders(); - if (allHeaders != null) { - ArrayList result = new ArrayList<>(); - for (Header header : allHeaders) { - result.add(header.getName() + ": " + header.getValue()); - } - return result; - } - return Collections.emptyList(); - } - - public boolean isRunning() { - return state == State.RUNNING; - } - - public boolean isWaiting() { - return state == State.WAITING; - } - - public State getState() { - return state; - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RequestParser.java b/src/ru/basecode/ide/rest/plugin/RequestParser.java deleted file mode 100644 index 69e06e3..0000000 --- a/src/ru/basecode/ide/rest/plugin/RequestParser.java +++ /dev/null @@ -1,118 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.google.common.net.UrlEscapers; -import org.jetbrains.annotations.NotNull; -import ru.basecode.ide.rest.plugin.psi.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -/** - * @author danblack - */ -public class RequestParser { - - private static final Function encoder = v -> UrlEscapers.urlFormParameterEscaper().escape(v); - - private static Request.Method getMethod(RestRequest request) { - RestEMethod method = request.getEMethod(); - if (method != null && method.isValid()) { - switch (method.getText()) { - case "GET": - return Request.Method.GET; - case "PUT": - return Request.Method.PUT; - case "POST": - return Request.Method.POST; - case "DELETE": - return Request.Method.DELETE; - case "PATCH": - return Request.Method.PATCH; - } - throw new IllegalStateException(); - } - return Request.Method.GET; - } - - public static Request parse(RestRequest request) { - Request.Method method = getMethod(request); - String url = getUrl(request); - String body = getBody(request); - List headers = getHeaders(request); - return new Request(method, url, headers, body); - } - - private static List getHeaders(RestRequest request) { - ArrayList result = new ArrayList<>(); - RestHeaders headers = request.getHeaders(); - if (headers != null) { - for (RestEHeader header : headers.getEHeaderList()) { - String text = header.getText(); - int i = text.indexOf("@"); - if (i >= 0) { - i = text.indexOf(":"); - if (i >= 0) { - String name = text.substring(1, i).trim(); - String value = text.substring(i + 1).trim(); - result.add(new Request.Header(name, value)); - } - } - } - } - return result; - } - - private static String getBody(RestRequest request) { - RestRequestBody body = request.getRequestBody(); - if (body != null && body.isValid()) { - return body.getText(); - } - return null; - } - - @NotNull - private static String getUrl(RestRequest request) { - RestEUrl url = request.getEUrl(); - if (url.isValid()) { - String urlText = url.getText(); - boolean markExists = urlText.contains("?"); - StringBuilder sb = new StringBuilder(urlText); - RestParams params = request.getParams(); - if (params != null && params.isValid()) { - if (!markExists) { - sb.append('?'); - } - for (RestEParam param : params.getEParamList()) { - sb.append(param.getText()); - } - } - return encode(sb.toString()); - } - throw new IllegalStateException(""); - } - - static String encode(String url) { - StringBuilder sb = new StringBuilder(url.length()); - int qpos = url.indexOf("?"); - if (qpos == -1) { - return url; - } - sb.append(url.substring(0, qpos + 1)); - url = url.substring(qpos + 1); - String delimiter = ""; - for (String paramEntry : url.split("&")) { - sb.append(delimiter); - int epos = paramEntry.indexOf("="); - if (epos == -1) { - sb.append(UrlEscapers.urlFormParameterEscaper().escape(paramEntry.trim())); - } else { - String paramName = paramEntry.substring(0, epos).trim(); - String paramValue = paramEntry.substring(epos + 1).trim(); - sb.append(encoder.apply(paramName)).append("=").append(encoder.apply(paramValue)); - } - delimiter = "&"; - } - return sb.toString(); - } -} diff --git a/src/ru/basecode/ide/rest/plugin/Requests.java b/src/ru/basecode/ide/rest/plugin/Requests.java deleted file mode 100644 index 644a7d8..0000000 --- a/src/ru/basecode/ide/rest/plugin/Requests.java +++ /dev/null @@ -1,74 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import org.apache.http.client.methods.*; -import org.apache.http.entity.StringEntity; - -import java.io.UnsupportedEncodingException; -import java.nio.charset.Charset; -import java.util.function.Function; - -/** - * @author danblack - */ -public class Requests { - - private static final Charset DEFAULT_CHARSET = Charset.defaultCharset(); - - public static HttpRequestBase createHttpRequest(Request request) throws UnsupportedEncodingException { - if (request.getMethod() == Request.Method.GET) { - HttpGet httpGet = new HttpGet(request.getUrl()); - fillHeaders(request, httpGet); - return httpGet; - } - - if (request.getMethod() == Request.Method.POST) { - return create(HttpPost::new, request); - } - - if (request.getMethod() == Request.Method.PUT) { - return create(HttpPut::new, request); - } - - if (request.getMethod() == Request.Method.PATCH) { - return create(HttpPatch::new, request); - } - - if (request.getMethod() == Request.Method.DELETE) { - HttpDelete httpDelete = new HttpDelete(request.getUrl()); - fillHeaders(request, httpDelete); - return httpDelete; - } - - throw new IllegalStateException(); - } - - public static HttpEntityEnclosingRequestBase create(Function constructor, - Request request) - throws UnsupportedEncodingException { - HttpEntityEnclosingRequestBase httpRequest = constructor.apply(request.getUrl()); - fillHeaders(request, httpRequest); - fillBody(request, httpRequest); - return httpRequest; - } - - private static void fillBody(Request request, HttpEntityEnclosingRequestBase httpRequest) - throws UnsupportedEncodingException { - String body = request.getBody(); - if (body != null) { - httpRequest.setEntity(new StringEntity(body, DEFAULT_CHARSET)); - for (Request.Header header : request.getHeaders()) { - if ("Content-Type".equals(header.getName())) { - if (!header.getValue().contains("charset")) { - - } - } - } - } - } - - private static void fillHeaders(Request request, HttpRequestBase httpRequest) { - for (Request.Header header : request.getHeaders()) { - httpRequest.addHeader(header.getName(), header.getValue()); - } - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RestClientPlugin.java b/src/ru/basecode/ide/rest/plugin/RestClientPlugin.java deleted file mode 100644 index 762d538..0000000 --- a/src/ru/basecode/ide/rest/plugin/RestClientPlugin.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.openapi.components.ApplicationComponent; -import org.jetbrains.annotations.NotNull; - -/** - * @author danblack - */ -public class RestClientPlugin implements ApplicationComponent { - - @NotNull - @Override - public String getComponentName() { - return "Rest client plugin"; - } - - @Override - public void initComponent() { - } - - @Override - public void disposeComponent() { - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RestCodeStyleSettingsProvider.java b/src/ru/basecode/ide/rest/plugin/RestCodeStyleSettingsProvider.java deleted file mode 100644 index c5fbd14..0000000 --- a/src/ru/basecode/ide/rest/plugin/RestCodeStyleSettingsProvider.java +++ /dev/null @@ -1,53 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.application.options.CodeStyleAbstractConfigurable; -import com.intellij.application.options.CodeStyleAbstractPanel; -import com.intellij.application.options.TabbedLanguageCodeStylePanel; -import com.intellij.openapi.options.Configurable; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CodeStyleSettingsProvider; -import com.intellij.psi.codeStyle.CustomCodeStyleSettings; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @author danblack - */ -public class RestCodeStyleSettingsProvider extends CodeStyleSettingsProvider { - @NotNull - @Override - public Configurable createSettingsPage(CodeStyleSettings settings, CodeStyleSettings originalSettings) { - return new CodeStyleAbstractConfigurable(settings, originalSettings, "Rest") { - - @Nullable - @Override - public String getHelpTopic() { - return null; - } - - @Override - protected CodeStyleAbstractPanel createPanel(CodeStyleSettings settings) { - return new RestTabbedLanguageCodeStylePanel(getCurrentSettings(), settings); - } - }; - } - - @Nullable - @Override - public String getConfigurableDisplayName() { - return "Rest"; - } - - @Nullable - @Override - public CustomCodeStyleSettings createCustomSettings(CodeStyleSettings settings) { - return new RestCodeStyleSettings(settings); - } - - private static class RestTabbedLanguageCodeStylePanel extends TabbedLanguageCodeStylePanel { - - protected RestTabbedLanguageCodeStylePanel(CodeStyleSettings currentSettings, CodeStyleSettings settings) { - super(RestLanguage.INSTANCE, currentSettings, settings); - } - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RestColorSettingsPage.java b/src/ru/basecode/ide/rest/plugin/RestColorSettingsPage.java deleted file mode 100644 index e93e0a0..0000000 --- a/src/ru/basecode/ide/rest/plugin/RestColorSettingsPage.java +++ /dev/null @@ -1,85 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.openapi.editor.colors.TextAttributesKey; -import com.intellij.openapi.fileTypes.SyntaxHighlighter; -import com.intellij.openapi.options.colors.AttributesDescriptor; -import com.intellij.openapi.options.colors.ColorDescriptor; -import com.intellij.openapi.options.colors.ColorSettingsPage; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.util.Map; - -/** - * @author danblack - */ -public class RestColorSettingsPage implements ColorSettingsPage { - - private static final AttributesDescriptor[] DESCRIPTORS = new AttributesDescriptor[]{ - new AttributesDescriptor("Comment", RestHighlighter.COMMENT), - new AttributesDescriptor("Method", RestHighlighter.METHOD), - new AttributesDescriptor("Url", RestHighlighter.URL), - new AttributesDescriptor("Header", RestHighlighter.HEADER), - new AttributesDescriptor("Option", RestHighlighter.OPTION), - new AttributesDescriptor("Param", RestHighlighter.PARAM), - new AttributesDescriptor("Error", RestHighlighter.BAD_CHARACTER), - new AttributesDescriptor("Separator", RestHighlighter.SEPARATOR), - new AttributesDescriptor("Body", RestHighlighter.BODY), - }; - - @NotNull - @Override - public AttributesDescriptor[] getAttributeDescriptors() { - return DESCRIPTORS; - } - - @NotNull - @Override - public ColorDescriptor[] getColorDescriptors() { - return ColorDescriptor.EMPTY_ARRAY; - } - - @NotNull - @Override - public String getDisplayName() { - return "Rest"; - } - - @Nullable - @Override - public Icon getIcon() { - return null; - } - - @NotNull - @Override - public SyntaxHighlighter getHighlighter() { - return new RestHighlighter(); - } - - @NotNull - @Override - public String getDemoText() { - return "# This is a comment\n" + - "-- option = value\n" + - "POST\n" + - "http://site.com\n" + - "¶m=value\n" + - "@Content-type: application/xml\n" + - "\n" + - "This is a request body\n" + - "\n" + - "%%%\n" + - "@Content-length: 100\n" + - "\n" + - "This is a response body\n" + - ""; - } - - @Nullable - @Override - public Map getAdditionalHighlightingTagToDescriptorMap() { - return null; - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RestFile.java b/src/ru/basecode/ide/rest/plugin/RestFile.java deleted file mode 100644 index 6924faa..0000000 --- a/src/ru/basecode/ide/rest/plugin/RestFile.java +++ /dev/null @@ -1,43 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.extapi.psi.PsiFileBase; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.psi.FileViewProvider; -import com.intellij.psi.PsiElement; -import org.jetbrains.annotations.NotNull; -import ru.basecode.ide.rest.plugin.psi.RestESeparator; -import ru.basecode.ide.rest.plugin.psi.RestRequest; - -/** - * - */ -public class RestFile extends PsiFileBase { - - protected RestFile(@NotNull FileViewProvider viewProvider) { - super(viewProvider, RestLanguage.INSTANCE); - } - - public RestRequest getRequest() { - for (PsiElement element : getChildren()) { - if (element instanceof RestRequest) { - return (RestRequest) element; - } - } - return null; - } - - public RestESeparator getSeparator() { - for (PsiElement element : getChildren()) { - if (element instanceof RestESeparator) { - return (RestESeparator) element; - } - } - return null; - } - - @NotNull - @Override - public FileType getFileType() { - return RestFileType.INSTANCE; - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RestFileType.java b/src/ru/basecode/ide/rest/plugin/RestFileType.java deleted file mode 100644 index 6b146ff..0000000 --- a/src/ru/basecode/ide/rest/plugin/RestFileType.java +++ /dev/null @@ -1,44 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.icons.AllIcons; -import com.intellij.openapi.fileTypes.LanguageFileType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; - -/** - * - */ -public class RestFileType extends LanguageFileType { - - public final static LanguageFileType INSTANCE = new RestFileType(); - - protected RestFileType() { - super(RestLanguage.INSTANCE); - } - - @NotNull - @Override - public String getName() { - return "Rest file"; - } - - @NotNull - @Override - public String getDescription() { - return "Rest language file"; - } - - @NotNull - @Override - public String getDefaultExtension() { - return "rest"; - } - - @Nullable - @Override - public Icon getIcon() { - return AllIcons.General.Web; - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RestFileTypeFactory.java b/src/ru/basecode/ide/rest/plugin/RestFileTypeFactory.java deleted file mode 100644 index eb90609..0000000 --- a/src/ru/basecode/ide/rest/plugin/RestFileTypeFactory.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.openapi.fileTypes.FileTypeConsumer; -import com.intellij.openapi.fileTypes.FileTypeFactory; -import org.jetbrains.annotations.NotNull; - -/** - * - */ -public class RestFileTypeFactory extends FileTypeFactory { - @Override - public void createFileTypes(@NotNull FileTypeConsumer consumer) { - consumer.consume(RestFileType.INSTANCE, "rest"); - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RestHostInjector.java b/src/ru/basecode/ide/rest/plugin/RestHostInjector.java deleted file mode 100644 index 3f6a5e7..0000000 --- a/src/ru/basecode/ide/rest/plugin/RestHostInjector.java +++ /dev/null @@ -1,69 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.lang.Language; -import com.intellij.lang.injection.MultiHostInjector; -import com.intellij.lang.injection.MultiHostRegistrar; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiLanguageInjectionHost; -import com.intellij.psi.impl.source.tree.injected.MultiHostRegistrarImpl; -import org.jetbrains.annotations.NotNull; -import ru.basecode.ide.rest.plugin.psi.*; -import ru.basecode.ide.rest.plugin.psi.impl.RestElementImpl; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * - */ -public class RestHostInjector implements MultiHostInjector { - @Override - public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull final PsiElement context) { - final MultiHostRegistrarImpl r = (MultiHostRegistrarImpl) registrar; - RestHeaders headers = null; - if (context instanceof RestResponseBody) { - headers = ((RestResponse) context.getParent()).getHeaders(); - } else if (context instanceof RestRequestBody) { - headers = ((RestRequest) context.getParent()).getHeaders(); - } - if (headers != null) { - String contentType = getContentType(headers); - if (contentType != null) { - Collection langList = Language.findInstancesByMimeType(contentType); - if (!langList.isEmpty()) { - r.startInjecting(langList.iterator().next()) - .addPlace(null, null, (PsiLanguageInjectionHost) context, TextRange.create(0, context.getTextLength())) - .doneInjecting(); - } - } - } - } - - private String getContentType(@NotNull RestHeaders headers) { - if (headers.isValid()) { - for (RestEHeader eHeader : headers.getEHeaderList()) { - String header = eHeader.getText(); - if (header.startsWith("@Content-Type")) { - int colonIndex = header.indexOf(":"); - if (colonIndex >= 0) { - String contentType = header.substring(colonIndex + 1); - int semicolonIndex = contentType.indexOf(";"); - if (semicolonIndex >= 0) { - return contentType.substring(0, semicolonIndex).trim(); - } - return contentType.trim(); - } - } - } - } - return null; - } - - @NotNull - @Override - public List> elementsToInjectIn() { - return Collections.singletonList(RestElementImpl.class); - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RestLanguage.java b/src/ru/basecode/ide/rest/plugin/RestLanguage.java deleted file mode 100644 index b489cec..0000000 --- a/src/ru/basecode/ide/rest/plugin/RestLanguage.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.lang.Language; - -/** - * - */ -public class RestLanguage extends Language { - - public static final Language INSTANCE = new RestLanguage(); - - public RestLanguage() { - super("Rest"); - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RestLexer.java b/src/ru/basecode/ide/rest/plugin/RestLexer.java deleted file mode 100644 index 0f06ce6..0000000 --- a/src/ru/basecode/ide/rest/plugin/RestLexer.java +++ /dev/null @@ -1,579 +0,0 @@ -/* The following code was generated by JFlex 1.4.3 on 04.04.16 22:28 */ - -package ru.basecode.ide.rest.plugin; - -import com.intellij.lexer.FlexLexer; -import com.intellij.psi.tree.IElementType; -import ru.basecode.ide.rest.plugin.psi.RestTypes; -import com.intellij.psi.TokenType; - - -/** - * This class is a scanner generated by - * JFlex 1.4.3 - * on 04.04.16 22:28 from the specification file - * /Users/zoom/Development/ide/projects/idea-rest-client/src/ru/basecode/ide/rest/plugin/Rest.flex - */ -class RestLexer implements FlexLexer { - /** initial size of the lookahead buffer */ - private static final int ZZ_BUFFERSIZE = 16384; - - /** lexical states */ - public static final int S_RESP_HEADER = 12; - public static final int S_PARAM = 8; - public static final int S_RESP_BODY = 14; - public static final int S_REQ_HEADER = 10; - public static final int S_METHOD = 2; - public static final int S_URL = 6; - public static final int YYINITIAL = 0; - public static final int S_REQ_BODY = 16; - public static final int S_OPTION = 4; - - /** - * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l - * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l - * at the beginning of a line - * l is of the form l = 2*k, k a non negative integer - */ - private static final int ZZ_LEXSTATE[] = { - 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, - 8, 8 - }; - - /** - * Translates characters to character classes - */ - private static final String ZZ_CMAP_PACKED = - "\11\0\1\31\1\27\1\0\1\31\1\30\22\0\1\31\2\0\1\20"+ - "\1\0\1\32\1\17\6\0\1\15\1\0\1\26\12\0\1\25\5\0"+ - "\1\16\1\10\1\0\1\11\1\13\1\2\1\0\1\1\1\12\3\0"+ - "\1\14\2\0\1\5\1\4\2\0\1\6\1\3\1\7\22\0\1\21"+ - "\7\0\1\23\2\0\1\24\1\22\uff8b\0"; - - /** - * Translates characters to character classes - */ - private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); - - /** - * Translates DFA states to action switch labels. - */ - private static final int [] ZZ_ACTION = zzUnpackAction(); - - private static final String ZZ_ACTION_PACKED_0 = - "\11\0\5\1\1\2\1\1\2\3\1\4\1\1\1\5"+ - "\1\6\1\7\1\4\1\5\1\10\1\11\1\12\1\13"+ - "\4\0\1\14\1\0\1\15\1\0\1\5\1\16\4\0"+ - "\1\17\3\0\1\17\4\0\1\20"; - - private static int [] zzUnpackAction() { - int [] result = new int[53]; - int offset = 0; - offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); - return result; - } - - private static int zzUnpackAction(String packed, int offset, int [] result) { - int i = 0; /* index in packed string */ - int j = offset; /* index in unpacked array */ - int l = packed.length(); - while (i < l) { - int count = packed.charAt(i++); - int value = packed.charAt(i++); - do result[j++] = value; while (--count > 0); - } - return j; - } - - - /** - * Translates a state to a row index in the transition table - */ - private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); - - private static final String ZZ_ROWMAP_PACKED_0 = - "\0\0\0\33\0\66\0\121\0\154\0\207\0\242\0\275"+ - "\0\330\0\363\0\u010e\0\u0129\0\u0144\0\u015f\0\u017a\0\u0195"+ - "\0\363\0\u01b0\0\u01cb\0\u01e6\0\u0201\0\u021c\0\u0237\0\u0252"+ - "\0\u026d\0\u0288\0\u02a3\0\u02be\0\u02d9\0\u02f4\0\u030f\0\u032a"+ - "\0\u0345\0\u0360\0\u037b\0\u0396\0\u03b1\0\u03cc\0\363\0\u03e7"+ - "\0\u0402\0\u041d\0\u0438\0\u0453\0\u046e\0\u0489\0\u04a4\0\u04bf"+ - "\0\u04da\0\u04f5\0\u0510\0\u052b\0\u0546"; - - private static int [] zzUnpackRowMap() { - int [] result = new int[53]; - int offset = 0; - offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); - return result; - } - - private static int zzUnpackRowMap(String packed, int offset, int [] result) { - int i = 0; /* index in packed string */ - int j = offset; /* index in unpacked array */ - int l = packed.length(); - while (i < l) { - int high = packed.charAt(i++) << 16; - result[j++] = high | packed.charAt(i++); - } - return j; - } - - /** - * The transition table of the DFA - */ - private static final int [] ZZ_TRANS = zzUnpackTrans(); - - private static final String ZZ_TRANS_PACKED_0 = - "\1\12\1\13\2\12\1\14\6\12\1\15\1\12\1\16"+ - "\2\12\1\17\1\20\5\12\1\21\1\22\1\23\21\12"+ - "\1\17\1\20\5\12\1\21\1\22\1\23\2\12\1\13"+ - "\2\12\1\14\6\12\1\15\1\12\1\24\2\12\1\17"+ - "\1\20\5\12\1\21\1\22\1\23\1\12\16\25\1\26"+ - "\1\27\1\17\6\25\1\21\1\22\1\30\1\31\16\25"+ - "\1\26\1\32\1\17\6\25\1\21\1\22\1\30\1\31"+ - "\16\25\1\33\1\25\1\17\6\25\1\21\1\22\1\30"+ - "\1\31\16\34\1\33\1\34\1\17\6\34\1\21\1\22"+ - "\1\23\1\34\27\35\1\21\1\22\1\23\1\35\27\25"+ - "\1\21\1\22\1\30\1\31\35\0\1\36\35\0\1\37"+ - "\1\0\1\36\1\40\24\0\1\41\45\0\1\42\15\0"+ - "\27\17\2\0\2\17\22\0\1\43\37\0\1\21\34\0"+ - "\1\23\16\0\1\44\15\0\27\25\2\0\2\25\27\26"+ - "\2\0\2\26\27\27\2\0\2\27\31\0\1\30\1\45"+ - "\27\25\2\0\1\25\1\46\27\32\2\0\2\32\27\33"+ - "\2\0\2\33\27\34\2\0\2\34\27\35\2\0\2\35"+ - "\3\0\1\47\35\0\1\36\27\0\1\50\43\0\1\51"+ - "\16\0\27\42\2\0\2\42\22\0\1\52\10\0\27\44"+ - "\2\0\2\44\32\0\1\53\27\25\2\0\1\25\1\54"+ - "\11\0\1\55\23\0\1\56\53\0\1\57\41\0\1\60"+ - "\27\25\2\0\1\54\1\25\12\0\1\47\23\0\1\61"+ - "\53\0\1\62\1\63\36\0\1\60\3\0\1\47\55\0"+ - "\1\63\33\0\1\64\32\0\1\65\4\0\27\65\2\0"+ - "\2\65"; - - private static int [] zzUnpackTrans() { - int [] result = new int[1377]; - int offset = 0; - offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); - return result; - } - - private static int zzUnpackTrans(String packed, int offset, int [] result) { - int i = 0; /* index in packed string */ - int j = offset; /* index in unpacked array */ - int l = packed.length(); - while (i < l) { - int count = packed.charAt(i++); - int value = packed.charAt(i++); - value--; - do result[j++] = value; while (--count > 0); - } - return j; - } - - - /* error codes */ - private static final int ZZ_UNKNOWN_ERROR = 0; - private static final int ZZ_NO_MATCH = 1; - private static final int ZZ_PUSHBACK_2BIG = 2; - private static final char[] EMPTY_BUFFER = new char[0]; - private static final int YYEOF = -1; - private static java.io.Reader zzReader = null; // Fake - - /* error messages for the codes above */ - private static final String ZZ_ERROR_MSG[] = { - "Unkown internal scanner error", - "Error: could not match input", - "Error: pushback value was too large" - }; - - /** - * ZZ_ATTRIBUTE[aState] contains the attributes of state aState - */ - private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); - - private static final String ZZ_ATTRIBUTE_PACKED_0 = - "\11\0\1\11\6\1\1\11\14\1\4\0\1\1\1\0"+ - "\1\1\1\0\1\1\1\11\4\0\1\1\3\0\1\1"+ - "\4\0\1\1"; - - private static int [] zzUnpackAttribute() { - int [] result = new int[53]; - int offset = 0; - offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); - return result; - } - - private static int zzUnpackAttribute(String packed, int offset, int [] result) { - int i = 0; /* index in packed string */ - int j = offset; /* index in unpacked array */ - int l = packed.length(); - while (i < l) { - int count = packed.charAt(i++); - int value = packed.charAt(i++); - do result[j++] = value; while (--count > 0); - } - return j; - } - - /** the current state of the DFA */ - private int zzState; - - /** the current lexical state */ - private int zzLexicalState = YYINITIAL; - - /** this buffer contains the current text to be matched and is - the source of the yytext() string */ - private CharSequence zzBuffer = ""; - - /** this buffer may contains the current text array to be matched when it is cheap to acquire it */ - private char[] zzBufferArray; - - /** the textposition at the last accepting state */ - private int zzMarkedPos; - - /** the textposition at the last state to be included in yytext */ - private int zzPushbackPos; - - /** the current text position in the buffer */ - private int zzCurrentPos; - - /** startRead marks the beginning of the yytext() string in the buffer */ - private int zzStartRead; - - /** endRead marks the last character in the buffer, that has been read - from input */ - private int zzEndRead; - - /** - * zzAtBOL == true <=> the scanner is currently at the beginning of a line - */ - private boolean zzAtBOL = true; - - /** zzAtEOF == true <=> the scanner is at the EOF */ - private boolean zzAtEOF; - - /** denotes if the user-EOF-code has already been executed */ - private boolean zzEOFDone; - - - /** - * Creates a new scanner - * - * @param in the java.io.Reader to read input from. - */ - RestLexer(java.io.Reader in) { - this.zzReader = in; - } - - - /** - * Unpacks the compressed character translation table. - * - * @param packed the packed character translation table - * @return the unpacked character translation table - */ - private static char [] zzUnpackCMap(String packed) { - char [] map = new char[0x10000]; - int i = 0; /* index in packed string */ - int j = 0; /* index in unpacked array */ - while (i < 92) { - int count = packed.charAt(i++); - char value = packed.charAt(i++); - do map[j++] = value; while (--count > 0); - } - return map; - } - - public final int getTokenStart(){ - return zzStartRead; - } - - public final int getTokenEnd(){ - return getTokenStart() + yylength(); - } - - public void reset(CharSequence buffer, int start, int end,int initialState){ - zzBuffer = buffer; - zzBufferArray = com.intellij.util.text.CharArrayUtil.fromSequenceWithoutCopying(buffer); - zzCurrentPos = zzMarkedPos = zzStartRead = start; - zzPushbackPos = 0; - zzAtEOF = false; - zzAtBOL = true; - zzEndRead = end; - yybegin(initialState); - } - - /** - * Refills the input buffer. - * - * @return false, iff there was new input. - * - * @exception java.io.IOException if any I/O-Error occurs - */ - private boolean zzRefill() throws java.io.IOException { - return true; - } - - - /** - * Returns the current lexical state. - */ - public final int yystate() { - return zzLexicalState; - } - - - /** - * Enters a new lexical state - * - * @param newState the new lexical state - */ - public final void yybegin(int newState) { - zzLexicalState = newState; - } - - - /** - * Returns the text matched by the current regular expression. - */ - public final CharSequence yytext() { - return zzBuffer.subSequence(zzStartRead, zzMarkedPos); - } - - - /** - * Returns the character at position pos from the - * matched text. - * - * It is equivalent to yytext().charAt(pos), but faster - * - * @param pos the position of the character to fetch. - * A value from 0 to yylength()-1. - * - * @return the character at position pos - */ - public final char yycharat(int pos) { - return zzBufferArray != null ? zzBufferArray[zzStartRead+pos]:zzBuffer.charAt(zzStartRead+pos); - } - - - /** - * Returns the length of the matched text region. - */ - public final int yylength() { - return zzMarkedPos-zzStartRead; - } - - - /** - * Reports an error that occured while scanning. - * - * In a wellformed scanner (no or only correct usage of - * yypushback(int) and a match-all fallback rule) this method - * will only be called with things that "Can't Possibly Happen". - * If this method is called, something is seriously wrong - * (e.g. a JFlex bug producing a faulty scanner etc.). - * - * Usual syntax/scanner level error handling should be done - * in error fallback rules. - * - * @param errorCode the code of the errormessage to display - */ - private void zzScanError(int errorCode) { - String message; - try { - message = ZZ_ERROR_MSG[errorCode]; - } - catch (ArrayIndexOutOfBoundsException e) { - message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; - } - - throw new Error(message); - } - - - /** - * Pushes the specified amount of characters back into the input stream. - * - * They will be read again by then next call of the scanning method - * - * @param number the number of characters to be read again. - * This number must not be greater than yylength()! - */ - public void yypushback(int number) { - if ( number > yylength() ) - zzScanError(ZZ_PUSHBACK_2BIG); - - zzMarkedPos -= number; - } - - - /** - * Contains user EOF-code, which will be executed exactly once, - * when the end of file is reached - */ - private void zzDoEOF() { - if (!zzEOFDone) { - zzEOFDone = true; - - } - } - - - /** - * Resumes scanning until the next regular expression is matched, - * the end of input is encountered or an I/O-Error occurs. - * - * @return the next token - * @exception java.io.IOException if any I/O-Error occurs - */ - public IElementType advance() throws java.io.IOException { - int zzInput; - int zzAction; - - // cached fields: - int zzCurrentPosL; - int zzMarkedPosL; - int zzEndReadL = zzEndRead; - CharSequence zzBufferL = zzBuffer; - char[] zzBufferArrayL = zzBufferArray; - char [] zzCMapL = ZZ_CMAP; - - int [] zzTransL = ZZ_TRANS; - int [] zzRowMapL = ZZ_ROWMAP; - int [] zzAttrL = ZZ_ATTRIBUTE; - - while (true) { - zzMarkedPosL = zzMarkedPos; - - zzAction = -1; - - zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; - - zzState = ZZ_LEXSTATE[zzLexicalState]; - - - zzForAction: { - while (true) { - - if (zzCurrentPosL < zzEndReadL) - zzInput = (zzBufferArrayL != null ? zzBufferArrayL[zzCurrentPosL++] : zzBufferL.charAt(zzCurrentPosL++)); - else if (zzAtEOF) { - zzInput = YYEOF; - break zzForAction; - } - else { - // store back cached positions - zzCurrentPos = zzCurrentPosL; - zzMarkedPos = zzMarkedPosL; - boolean eof = zzRefill(); - // get translated positions and possibly new buffer - zzCurrentPosL = zzCurrentPos; - zzMarkedPosL = zzMarkedPos; - zzBufferL = zzBuffer; - zzEndReadL = zzEndRead; - if (eof) { - zzInput = YYEOF; - break zzForAction; - } - else { - zzInput = (zzBufferArrayL != null ? zzBufferArrayL[zzCurrentPosL++] : zzBufferL.charAt(zzCurrentPosL++)); - } - } - int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; - if (zzNext == -1) break zzForAction; - zzState = zzNext; - - int zzAttributes = zzAttrL[zzState]; - if ( (zzAttributes & 1) == 1 ) { - zzAction = zzState; - zzMarkedPosL = zzCurrentPosL; - if ( (zzAttributes & 8) == 8 ) break zzForAction; - } - - } - } - - // store back cached position - zzMarkedPos = zzMarkedPosL; - - switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { - case 7: - { yybegin(S_PARAM); return RestTypes.PARAM; - } - case 17: break; - case 4: - { return RestTypes.WHITE_SPACE; - } - case 18: break; - case 11: - { return RestTypes.RESPONSE_BODY_LINE; - } - case 19: break; - case 9: - { return RestTypes.HEADER; - } - case 20: break; - case 14: - { yybegin(S_METHOD); return RestTypes.METHOD; - } - case 21: break; - case 2: - { return RestTypes.COMMENT; - } - case 22: break; - case 1: - { return RestTypes.BAD_CHARACTER; - } - case 23: break; - case 15: - { yybegin(S_RESP_HEADER); return RestTypes.SEPARATOR; - } - case 24: break; - case 16: - { yybegin(S_URL); return RestTypes.URL; - } - case 25: break; - case 3: - { return RestTypes.CRLF; - } - case 26: break; - case 10: - { yybegin(S_RESP_BODY); return RestTypes.RESPONSE_BODY_LINE; - } - case 27: break; - case 6: - { yybegin(S_REQ_HEADER); return RestTypes.HEADER; - } - case 28: break; - case 12: - { yybegin(S_OPTION); return RestTypes.OPTION; - } - case 29: break; - case 8: - { return RestTypes.PARAM; - } - case 30: break; - case 13: - { return RestTypes.OPTION; - } - case 31: break; - case 5: - { yybegin(S_REQ_BODY); return RestTypes.REQUEST_BODY_LINE; - } - case 32: break; - default: - if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { - zzAtEOF = true; - zzDoEOF(); - return null; - } - else { - zzScanError(ZZ_NO_MATCH); - } - } - } - } - - -} diff --git a/src/ru/basecode/ide/rest/plugin/RestParserDefinition.java b/src/ru/basecode/ide/rest/plugin/RestParserDefinition.java deleted file mode 100644 index a069674..0000000 --- a/src/ru/basecode/ide/rest/plugin/RestParserDefinition.java +++ /dev/null @@ -1,78 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.lang.ASTNode; -import com.intellij.lang.Language; -import com.intellij.lang.ParserDefinition; -import com.intellij.lang.PsiParser; -import com.intellij.lexer.Lexer; -import com.intellij.openapi.project.Project; -import com.intellij.psi.FileViewProvider; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.TokenType; -import com.intellij.psi.tree.IFileElementType; -import com.intellij.psi.tree.TokenSet; -import org.jetbrains.annotations.NotNull; -import ru.basecode.ide.rest.plugin.parser.RestParser; -import ru.basecode.ide.rest.plugin.psi.RestTypes; - -/** - * - */ -public class RestParserDefinition implements ParserDefinition { - - public static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE); - - public static final IFileElementType FILE = - new IFileElementType(Language.findInstance(RestLanguage.class)); - - @NotNull - @Override - public Lexer createLexer(Project project) { - return new RestLexerAdapter(); - } - - @Override - public PsiParser createParser(Project project) { - return new RestParser(); - } - - @Override - public IFileElementType getFileNodeType() { - return FILE; - } - - @NotNull - @Override - public TokenSet getWhitespaceTokens() { - return WHITE_SPACES; - } - - @NotNull - @Override - public TokenSet getCommentTokens() { - return TokenSet.EMPTY; - } - - @NotNull - @Override - public TokenSet getStringLiteralElements() { - return TokenSet.EMPTY; - } - - @NotNull - @Override - public PsiElement createElement(ASTNode node) { - return RestTypes.Factory.createElement(node); - } - - @Override - public PsiFile createFile(FileViewProvider viewProvider) { - return new RestFile(viewProvider); - } - - @Override - public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode left, ASTNode right) { - return null; - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RestProjectComponent.java b/src/ru/basecode/ide/rest/plugin/RestProjectComponent.java deleted file mode 100644 index 949b047..0000000 --- a/src/ru/basecode/ide/rest/plugin/RestProjectComponent.java +++ /dev/null @@ -1,95 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.openapi.components.AbstractProjectComponent; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.*; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.openapi.vfs.VirtualFilePropertyEvent; -import com.intellij.openapi.vfs.impl.http.HttpVirtualFile; -import com.intellij.psi.FileViewProvider; -import com.intellij.psi.PsiManager; -import com.intellij.util.messages.MessageBusConnection; -import org.jetbrains.annotations.NotNull; - -/** - * @author danblack - */ -public class RestProjectComponent extends AbstractProjectComponent { - - protected RestProjectComponent(Project project) { - super(project); - } - - @Override - public void initComponent() { - super.initComponent(); - registerOpenFileHandler(myProject.getMessageBus().connect(myProject)); - registerFileLanguageHandler(); - } - - private void registerFileLanguageHandler() { - VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { - @Override - public void propertyChanged(@NotNull VirtualFilePropertyEvent event) { - if (VirtualFile.PROP_NAME.equals(event.getPropertyName())) { - FileEditorManager fileEditorManager = FileEditorManager.getInstance(myProject); - VirtualFile file = event.getFile(); - if (fileEditorManager.isFileOpen(file)) { - refreshComponent(fileEditorManager, file); - } - } - } - }, myProject); - } - - private void registerOpenFileHandler(MessageBusConnection connection) { - connection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerAdapter() { - @Override - public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - refreshComponent(source, file); - } - }); - } - - public static boolean isSuitable(@NotNull Project project, @NotNull VirtualFile file) { - if (file instanceof HttpVirtualFile) { - return false; - } - final FileViewProvider provider = PsiManager.getInstance(project).findViewProvider(file); - return provider != null && RestLanguage.INSTANCE == provider.getBaseLanguage(); - } - - - private void detachComponent(@NotNull FileEditorManager fileEditorManager, @NotNull VirtualFile file) { - for (FileEditor fileEditor : fileEditorManager.getAllEditors(file)) { - if (fileEditor instanceof TextEditor) { - HeaderPanelComponent.dispose(((TextEditor) fileEditor).getEditor()); - } - } - } - - private void refreshComponent(@NotNull final FileEditorManager fileEditorManager, @NotNull VirtualFile file) { - if (isSuitable(fileEditorManager.getProject(), file)) { - attachComponent(fileEditorManager, file); - } else { - detachComponent(fileEditorManager, file); - } - } - - private void attachComponent(@NotNull FileEditorManager fileEditorManager, @NotNull VirtualFile file) { - for (FileEditor fileEditor : fileEditorManager.getAllEditors(file)) { - if (fileEditor instanceof TextEditor) { - TextEditor textEditor = (TextEditor) fileEditor; - Editor editor = textEditor.getEditor(); - if (!HeaderPanelComponent.attached(editor)) { - HeaderPanelComponent component = new HeaderPanelComponent(textEditor); - Disposer.register(fileEditor, () -> Disposer.dispose(component)); - } - } - } - } -} diff --git a/src/ru/basecode/ide/rest/plugin/RunAction.java b/src/ru/basecode/ide/rest/plugin/RunAction.java deleted file mode 100644 index 328da31..0000000 --- a/src/ru/basecode/ide/rest/plugin/RunAction.java +++ /dev/null @@ -1,153 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.icons.AllIcons; -import com.intellij.lang.Language; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.WriteCommandAction; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.psi.impl.PsiFileFactoryImpl; -import ru.basecode.ide.rest.plugin.http.Response; -import ru.basecode.ide.rest.plugin.psi.RestESeparator; -import ru.basecode.ide.rest.plugin.psi.RestRequest; - -import java.time.LocalDateTime; -import java.util.Collection; -import java.util.function.Supplier; - -/** - * - */ -public class RunAction extends AnAction { - - public static final String ID = "rest.action.run"; - - private final RequestExecutor executor; - - @Override - public void update(AnActionEvent e) { - if (executor == null) { - e.getPresentation().setEnabled(false); - } else { - super.update(e); - e.getPresentation().setEnabled(executor.isWaiting()); - } - } - - public RunAction() { - this(null); - } - - public RunAction(RequestExecutor executor) { - super("Execute http request", "", AllIcons.Actions.Execute); - this.executor = executor; - } - - private static void writeResponse(Project project, Document doc, Supplier file, String text) { - WriteCommandAction.runWriteCommandAction(project, () -> { - int responsePosition; - String separatorString; - RestFile restFile = file.get(); - RestESeparator separator = restFile.getSeparator(); - if (separator != null && separator.isValid()) { - responsePosition = separator.getTextOffset(); - separatorString = ""; - } else { - RestRequest request = restFile.getRequest(); - responsePosition = request.getTextOffset() + request.getTextLength(); - separatorString = "\n"; - } - String sb = separatorString + "%%%\n" + text; - doc.replaceString(responsePosition, doc.getTextLength(), sb.replace("\r", "")); - }); - } - - @Override - public void actionPerformed(AnActionEvent e) { - if (executor == null || executor.isRunning()) { - return; - } - final Project project = e.getRequiredData(CommonDataKeys.PROJECT); - final VirtualFile file = e.getRequiredData(CommonDataKeys.VIRTUAL_FILE); - if (!RestProjectComponent.isSuitable(project, file)) { - return; - } - final Document document = e.getRequiredData(CommonDataKeys.EDITOR).getDocument(); - ApplicationManager.getApplication().executeOnPooledThread((Runnable) () -> { - RestFile restFile = getRestFile(project, document); - if (restFile == null) { - return; - } - Request request = getRequest(restFile); - try { - long startTime = System.currentTimeMillis(); - String start = "# Executing request...\n# URL: " + request.getUrl() + "\n# Start time: " + LocalDateTime.now(); - writeResponse(project, document, () -> restFile, start); - Response response = executor.execute(request); - writeResponse(project, document, () -> restFile, "# Reading response..."); - String headers = "\n# " + response.getStatus() + "\n" + getHeaders(response); - String text = getFormattedResponse(project, response.getContentType(), response.getBody()); - long duration = System.currentTimeMillis() - startTime; - writeResponse(project, document, () -> restFile, "\n# Duration: " + duration + " ms\n# URL: " + request.getUrl() + "\n" + headers + text); - } catch (Exception e1) { - writeResponse(project, document, () -> restFile, "# Error: " + e1.getMessage()); - } - }); - } - - private String getHeaders(Response response) { - StringBuilder sb = new StringBuilder("\n"); - for (String header : response.getHeaders()) { - sb.append("@").append(header).append("\n"); - } - return sb.append("\n").toString(); - } - - private String getFormattedResponse(Project project, String contentType, String text) { - Collection langList = Language.findInstancesByMimeType(contentType); - if (!langList.isEmpty()) { - return format(project, langList.iterator().next(), text); - } - return text; - } - - private Request getRequest(RestFile restFile) { - return ApplicationManager.getApplication().runReadAction( - (Computable) () -> RequestParser.parse(restFile.getRequest())); - } - - private static RestFile getRestFile(Project project, Document document) { - return ApplicationManager.getApplication().runReadAction((Computable) () -> { - if (project.isOpen()) { - PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document); - if (psiFile != null && psiFile.isValid() && psiFile instanceof RestFile) { - return (RestFile) psiFile; - } - } - return null; - }); - } - - public static String format(final Project project, Language language, String text) { - return WriteCommandAction.runWriteCommandAction(project, (Computable) () -> { - long startTime = System.currentTimeMillis(); - final PsiFile psiFile; - try { - psiFile = PsiFileFactoryImpl.getInstance(project).createFileFromText("virtual", language, text); - CodeStyleManager.getInstance(project).reformatText(psiFile, 0, psiFile.getTextLength()); - } finally { -// System.out.println(System.currentTimeMillis() - startTime); - } - - return psiFile.getText(); - }); - } -} diff --git a/src/ru/basecode/ide/rest/plugin/StopAction.java b/src/ru/basecode/ide/rest/plugin/StopAction.java deleted file mode 100644 index a97fd9d..0000000 --- a/src/ru/basecode/ide/rest/plugin/StopAction.java +++ /dev/null @@ -1,42 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import com.intellij.icons.AllIcons; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; - -/** - * @author danblack - */ -public class StopAction extends AnAction { - - public static final String ID = "rest.action.stop"; - - private final RequestExecutor executor; - - @Override - public void update(AnActionEvent e) { - if (executor == null) { - e.getPresentation().setEnabled(false); - } else { - super.update(e); - e.getPresentation().setEnabled(executor.isRunning()); - } - } - - public StopAction() { - this(null); - } - - public StopAction(RequestExecutor executor) { - super(AllIcons.Actions.Suspend); - this.executor = executor; - } - - @Override - public void actionPerformed(AnActionEvent e) { - if (executor != null && executor.isRunning()) { - executor.stop(); - } - } - -} diff --git a/src/ru/basecode/ide/rest/plugin/http/Response.java b/src/ru/basecode/ide/rest/plugin/http/Response.java deleted file mode 100644 index ade10f8..0000000 --- a/src/ru/basecode/ide/rest/plugin/http/Response.java +++ /dev/null @@ -1,37 +0,0 @@ -package ru.basecode.ide.rest.plugin.http; - -import java.util.List; - -/** - * @author danblack - */ -public class Response { - - private final String status; - private final List headers; - private final String contentType; - private final String body; - - public Response(String status, List headers, String contentType, String body) { - this.status = status; - this.headers = headers; - this.contentType = contentType; - this.body = body; - } - - public String getBody() { - return body; - } - - public List getHeaders() { - return headers; - } - - public String getContentType() { - return contentType; - } - - public String getStatus() { - return status; - } -} diff --git a/src/ru/basecode/ide/rest/plugin/psi/impl/ResponseLiteralEscaper.java b/src/ru/basecode/ide/rest/plugin/psi/impl/ResponseLiteralEscaper.java deleted file mode 100644 index 38f2570..0000000 --- a/src/ru/basecode/ide/rest/plugin/psi/impl/ResponseLiteralEscaper.java +++ /dev/null @@ -1,33 +0,0 @@ -package ru.basecode.ide.rest.plugin.psi.impl; - -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.LiteralTextEscaper; -import org.jetbrains.annotations.NotNull; - -/** - * - */ -public class ResponseLiteralEscaper extends LiteralTextEscaper { - public ResponseLiteralEscaper(RestElementImpl host) { - super(host); - } - - @Override - public boolean decode(@NotNull final TextRange rangeInsideHost, @NotNull StringBuilder outChars) { - outChars.append(myHost.getText(), rangeInsideHost.getStartOffset(), rangeInsideHost.getEndOffset()); - return true; - } - - @Override - public int getOffsetInHost(int offsetInDecoded, @NotNull final TextRange rangeInsideHost) { - int offset = offsetInDecoded + rangeInsideHost.getStartOffset(); - if (offset < rangeInsideHost.getStartOffset()) offset = rangeInsideHost.getStartOffset(); - if (offset > rangeInsideHost.getEndOffset()) offset = rangeInsideHost.getEndOffset(); - return offset; - } - - @Override - public boolean isOneLine() { - return true; - } -} diff --git a/src/ru/basecode/ide/rest/plugin/psi/impl/RestElementImpl.java b/src/ru/basecode/ide/rest/plugin/psi/impl/RestElementImpl.java deleted file mode 100644 index e765cd6..0000000 --- a/src/ru/basecode/ide/rest/plugin/psi/impl/RestElementImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -package ru.basecode.ide.rest.plugin.psi.impl; - -import com.intellij.extapi.psi.ASTWrapperPsiElement; -import com.intellij.lang.ASTNode; -import com.intellij.psi.LiteralTextEscaper; -import com.intellij.psi.PsiLanguageInjectionHost; -import org.jetbrains.annotations.NotNull; - -/** - * - */ -public class RestElementImpl extends ASTWrapperPsiElement implements PsiLanguageInjectionHost { - - public RestElementImpl(@NotNull ASTNode node) { - super(node); - } - - @Override - public boolean isValidHost() { - return true; - } - - @Override - public PsiLanguageInjectionHost updateText(@NotNull String text) { - return this; - } - - @NotNull - @Override - public LiteralTextEscaper createLiteralTextEscaper() { - return new ResponseLiteralEscaper(this); - } -} diff --git a/src/test/java/ru/basecode/ide/rest/plugin/psi/RestRequestParserTest.java b/src/test/java/ru/basecode/ide/rest/plugin/psi/RestRequestParserTest.java new file mode 100644 index 0000000..ae32fe7 --- /dev/null +++ b/src/test/java/ru/basecode/ide/rest/plugin/psi/RestRequestParserTest.java @@ -0,0 +1,45 @@ +package ru.basecode.ide.rest.plugin.psi; + +import org.junit.Assert; +import org.junit.Test; +import ru.basecode.ide.rest.plugin.psi.RestRequestParser; + +/** + * @author danblack + */ +public class RestRequestParserTest extends Assert{ + + @Test + public void shouldNotChangeTheUrl() { + assertEquals("param", RestRequestParser.encode("param")); + assertEquals("?param", RestRequestParser.encode("?param")); + assertEquals("?param=", RestRequestParser.encode("?param=")); + assertEquals("http://www.site.com?param", RestRequestParser.encode("http://www.site.com?param")); + assertEquals("http://www.site.com?param=", RestRequestParser.encode("http://www.site.com?param=")); + assertEquals("http://www.site.com?param=value", RestRequestParser.encode("http://www.site.com?param=value")); + assertEquals("http://www.site.com?param=value", RestRequestParser.encode("http://www.site.com?param=value")); + assertEquals("http://www.site.com?param=value¶m2=value2", RestRequestParser.encode("http://www.site.com?param=value¶m2=value2")); + } + + @Test + public void shouldTrimSpacesInParamName() { + assertEquals("?param", RestRequestParser.encode("?param ")); + assertEquals("?param", RestRequestParser.encode("? param ")); + assertEquals("?param", RestRequestParser.encode("? param")); + assertEquals("?param=", RestRequestParser.encode("?param =")); + assertEquals("?param=", RestRequestParser.encode("? param =")); + assertEquals("?param=", RestRequestParser.encode("? param=")); + } + + @Test + public void shouldEncodeSpaces() { + assertEquals("?param=1+2", RestRequestParser.encode("?param=1 2")); + assertEquals("?p+aram=1+2", RestRequestParser.encode("?p aram=1 2")); + } + + @Test + public void shouldEncodeSymbols() { + assertEquals("?param=%D1%8B%D1%91", RestRequestParser.encode("?param=ыё")); + } + +} diff --git a/test/ru/basecode/ide/rest/plugin/RequestParserTest_only_body.txt b/src/test/resources/RequestParserTest_only_body.txt similarity index 100% rename from test/ru/basecode/ide/rest/plugin/RequestParserTest_only_body.txt rename to src/test/resources/RequestParserTest_only_body.txt diff --git a/test/ru/basecode/ide/rest/plugin/RequestParserTest_only_client_params.txt b/src/test/resources/RequestParserTest_only_client_params.txt similarity index 100% rename from test/ru/basecode/ide/rest/plugin/RequestParserTest_only_client_params.txt rename to src/test/resources/RequestParserTest_only_client_params.txt diff --git a/test/ru/basecode/ide/rest/plugin/RequestParserTest_only_headers.txt b/src/test/resources/RequestParserTest_only_headers.txt similarity index 100% rename from test/ru/basecode/ide/rest/plugin/RequestParserTest_only_headers.txt rename to src/test/resources/RequestParserTest_only_headers.txt diff --git a/test/ru/basecode/ide/rest/plugin/RequestParserTest_only_method.txt b/src/test/resources/RequestParserTest_only_method.txt similarity index 100% rename from test/ru/basecode/ide/rest/plugin/RequestParserTest_only_method.txt rename to src/test/resources/RequestParserTest_only_method.txt diff --git a/test/ru/basecode/ide/rest/plugin/RequestParserTest_only_url.txt b/src/test/resources/RequestParserTest_only_url.txt similarity index 100% rename from test/ru/basecode/ide/rest/plugin/RequestParserTest_only_url.txt rename to src/test/resources/RequestParserTest_only_url.txt diff --git a/test/ru/basecode/ide/rest/plugin/RequestParserTest_url_with_params.txt b/src/test/resources/RequestParserTest_url_with_params.txt similarity index 100% rename from test/ru/basecode/ide/rest/plugin/RequestParserTest_url_with_params.txt rename to src/test/resources/RequestParserTest_url_with_params.txt diff --git a/test/ru/basecode/ide/rest/plugin/RequestParserTest.java b/test/ru/basecode/ide/rest/plugin/RequestParserTest.java deleted file mode 100644 index e4c700b..0000000 --- a/test/ru/basecode/ide/rest/plugin/RequestParserTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package ru.basecode.ide.rest.plugin; - -import org.junit.Assert; -import org.junit.Test; - -/** - * @author danblack - */ -public class RequestParserTest extends Assert{ - - @Test - public void shouldNotChangeTheUrl() { - assertEquals("param", RequestParser.encode("param")); - assertEquals("?param", RequestParser.encode("?param")); - assertEquals("?param=", RequestParser.encode("?param=")); - assertEquals("http://www.site.com?param", RequestParser.encode("http://www.site.com?param")); - assertEquals("http://www.site.com?param=", RequestParser.encode("http://www.site.com?param=")); - assertEquals("http://www.site.com?param=value", RequestParser.encode("http://www.site.com?param=value")); - assertEquals("http://www.site.com?param=value", RequestParser.encode("http://www.site.com?param=value")); - assertEquals("http://www.site.com?param=value¶m2=value2", RequestParser.encode("http://www.site.com?param=value¶m2=value2")); - } - - @Test - public void shouldTrimSpacesInParamName() { - assertEquals("?param", RequestParser.encode("?param ")); - assertEquals("?param", RequestParser.encode("? param ")); - assertEquals("?param", RequestParser.encode("? param")); - assertEquals("?param=", RequestParser.encode("?param =")); - assertEquals("?param=", RequestParser.encode("? param =")); - assertEquals("?param=", RequestParser.encode("? param=")); - } - - @Test - public void shouldEncodeSpaces() { - assertEquals("?param=1+2", RequestParser.encode("?param=1 2")); - assertEquals("?p+aram=1+2", RequestParser.encode("?p aram=1 2")); - } - - @Test - public void shouldEncodeSymbols() { - assertEquals("?param=%D1%8B%D1%91", RequestParser.encode("?param=ыё")); - } - -} \ No newline at end of file