[
  {
    "path": ".gitignore",
    "content": ".gradle/\n.idea/\n.vscode/\nbuild/\n*.DS_Store\nbin/"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2025 qczone\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Switch2Cursor\n\n[中文文档](README_zh.md)\n\n> 💡 Recommended to use with [Switch2IDEA](https://github.com/qczone/switch2idea) in Cursor\n\n\n[![JetBrains Plugins](https://img.shields.io/jetbrains/plugin/v/26309-switch2cursor?label=JetBrains%20Marketplace&style=for-the-badge&logo=intellij-idea)](https://plugins.jetbrains.com/plugin/26309-switch2cursor)\n[![Downloads](https://img.shields.io/jetbrains/plugin/d/26309-switch2cursor?style=for-the-badge&logo=intellij-idea)](https://plugins.jetbrains.com/plugin/26309-switch2cursor)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge)](LICENSE)\n\n## 🔍 Introduction\nA JetBrains IDE plugin that enhances development efficiency by enabling seamless switching between JetBrains IDE and Cursor\n\n![Switch2Cursor Demo](images/switch-show.gif)\n\n## 🌟 Features\n\n- 🚀 Seamless Editor Switching\n  - One-click switch between JetBrains IDE and Cursor\n  - Automatically positions to the same cursor location (line and column)\n  - Perfectly maintains editing context without interrupting workflow\n\n- ⌨️ Convenient Shortcut Support\n  - macOS:\n    - `Option+Shift+P` - Open project in Cursor\n    - `Option+Shift+O` - Open current file in Cursor\n  - Windows:\n    - `Alt+Shift+P` - Open project in Cursor\n    - `Alt+Shift+O` - Open current file in Cursor\n\n- 🔧 Multiple Access Methods\n  - Keyboard shortcuts\n  - Editor context menu\n  - IDE tools menu\n\n## 🛠️ Installation Guide\n\n### Method 1: Install via JetBrains Marketplace\n1. Open IDE → `Settings` → `Plugins` → `Marketplace`\n2. Search for switch2cursor\n3. Click `Install` to complete installation\n4. Click `OK` to apply changes\n\n### Method 2: Local Installation\n1. Download the latest plugin package from [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/26309-switch2cursor)\n2. IDE → `Settings` → `Plugins` → `⚙️`→ `Install Plugin from Disk...`\n3. Select the downloaded plugin package\n4. Click `OK` to apply changes\n\n\n## 🚀 Usage Guide\n\n### Basic Usage\n\n#### Open Project\n- Shortcuts:\n  - macOS: `Option+Shift+P` \n  - Windows: `Alt+Shift+P`\n- Context Menu: Right-click in project view → `Open Project In Cursor`\n- Tools Menu: `Tools` → `Open Project In Cursor`\n\n#### Open Current File\n- Shortcuts:\n  - macOS: `Option+Shift+O` \n  - Windows: `Alt+Shift+O`\n- Context Menu: Right-click in editor → `Open File In Cursor`\n- Tools Menu: `Tools` → `Open File In Cursor`\n\n### Configuration\n- In `Settings/Preferences` → `Tools` → `Switch2Cursor`:\n  - Set Cursor executable path (default is \"cursor\")\n  - Customize shortcuts through Keymap settings\n\n### Requirements\n- [Cursor](https://cursor.com) installed\n- Compatible with all JetBrains IDEs (version 2022.3 and above)\n\n## 🧑‍💻 Developer Guide\n\n### Build Project\n```bash\n# Clone repository\ngit clone https://github.com/qczone/switch2cursor.git\n\n# Build plugin\ncd switch2cursor\n./gradlew buildPlugin  \n# Plugin package will be generated in build/distributions/ directory\n```\n\n### Contributing\n1. Fork this repository\n2. Create a feature branch\n3. Commit your changes\n4. Push to the branch\n5. Submit a Pull Request\n\n## 🙋 FAQ \n\n### 1. Why doesn't the shortcut/menu click switch to Cursor after installation?\nCheck if the correct Cursor executable path is configured in `Settings` → `Tools` → `Switch2Cursor`\n\n### 2. Which IDEs are supported?\nSupports all JetBrains IDEs, including: IntelliJ IDEA, PyCharm, WebStorm, GoLand, RustRover, Android Studio, etc.\n\n### 3. Which versions are supported?\nThe plugin is developed based on JDK 17 and currently only supports JetBrains IDE version 2022.3 and above\n\n### 4. How to modify plugin shortcuts?\nModify in `Settings` → `Keymap` → `Plugins` → `Switch2Cursor`\n\n## 📄 License\nThis project is licensed under the [MIT License](LICENSE)\n\n\n## 📮 Feedback\nIf you encounter any issues or have suggestions, please provide feedback through:\n- [Submit GitHub Issue](https://github.com/qczone/switch2cursor/issues) \n\n## 🌟 Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=qczone/switch2cursor&type=Date)](https://star-history.com/#qczone/switch2cursor&Date)"
  },
  {
    "path": "README_zh.md",
    "content": "# Switch2Cursor\n\n[English](README.md)\n\n> 💡 推荐在 Cursor 中配合 [Switch2IDEA](https://github.com/qczone/switch2idea) 使用\n\n\n[![JetBrains Plugins](https://img.shields.io/jetbrains/plugin/v/26309-switch2cursor?label=JetBrains%20Marketplace&style=for-the-badge&logo=intellij-idea)](https://plugins.jetbrains.com/plugin/26309-switch2cursor)\n[![Downloads](https://img.shields.io/jetbrains/plugin/d/26309-switch2cursor?style=for-the-badge&logo=intellij-idea)](https://plugins.jetbrains.com/plugin/26309-switch2cursor)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge)](LICENSE)\n\n## 🔍 项目简介\n一个提升开发效率的 JetBrains IDE 插件，让你在 JetBrains IDE 和 Cursor 之间实现丝滑切换\n\n![Switch2Cursor演示](images/switch-show.gif)\n\n## 🌟 功能特性\n\n- 🚀 无缝编辑器切换\n  - 在 JetBrains IDE 和 Cursor 之间一键切换\n  - 自动定位到相同的光标位置（行号和列号）\n  - 完美保持编辑上下文，不中断思路\n\n- ⌨️ 便捷的快捷键支持\n  - macOS:\n    - `Option+Shift+P` - 在 Cursor 中打开整个项目\n    - `Option+Shift+O` - 在 Cursor 中打开当前文件\n  - Windows:\n    - `Alt+Shift+P` - 在 Cursor 中打开整个项目\n    - `Alt+Shift+O` - 在 Cursor 中打开当前文件\n\n- 🔧 多样化的访问方式\n  - 快捷键操作\n  - 编辑器右键菜单\n  - IDE 工具菜单\n\n## 🛠️ 安装指南\n\n### 方式一：通过 JetBrains 插件市场安装\n1. 打开 IDE → `Settings` → `Plugins` → `Marketplace`\n2. 搜索 switch2cursor\n3. 点击 `Install` 完成安装\n4. 点击 `OK` 生效\n\n### 方式二：本地安装\n1. 从 [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/26309-switch2cursor) 下载最新版插件包\n2. IDE → `Settings` → `Plugins` → `⚙️`→ `Install Plugin from Disk...`\n3. 选择下载的插件包\n4. 点击 `OK` 生效\n\n\n## 🚀 使用说明\n\n### 基础使用\n\n#### 打开项目\n- 快捷键：\n  - macOS: `Option+Shift+P` \n  - Windows: `Alt+Shift+P`\n- 右键菜单：在项目视图中右键 → `Open Project In Cursor`\n- 工具菜单：`Tools` → `Open Project In Cursor`\n\n#### 打开当前文件\n- 快捷键：\n  - macOS: `Option+Shift+O` \n  - Windows: `Alt+Shift+O`\n- 右键菜单：在编辑器中右键 → `Open File In Cursor`\n- 工具菜单：`Tools` → `Open File In Cursor`\n\n### 配置\n- 在 `Settings/Preferences` → `Tools` → `Switch2Cursor` 中：\n  - 设置 Cursor 可执行文件路径（默认为 \"cursor\"）\n  - 通过 Keymap 设置自定义快捷键\n\n### 环境要求\n- 已安装 [Cursor](https://cursor.com)\n- 兼容所有 JetBrains IDE（2022.3 及以上版本）\n\n## 🧑‍💻 开发者指南\n\n### 项目构建\n```bash\n# 克隆仓库\ngit clone https://github.com/qczone/switch2cursor.git\n\n# 构建插件\ncd switch2cursor\n./gradlew buildPlugin  \n# 生成插件包在 build/distributions/ 目录下\n```\n\n### 贡献代码\n1. Fork 本仓库\n2. 创建特性分支\n3. 提交修改\n4. 推送分支\n5. 提交 Pull Request\n\n## 🙋 常见问题 \n\n### 1. 为什么安装之后快捷键/点击菜单不会跳转到 Cursor？\n检查是否在 `Settings` → `Tools` → `Switch2Cursor` 中配置了正确的 Cursor 可执行文件路径\n\n### 2. 都支持哪些 IDE？\n支持所有 JetBrains 系列的 IDE，如：IntelliJ IDEA、PyCharm、WebStorm、GoLand、RustRover、Android Studio 等\n\n### 3. 都支持哪些版本？\n插件基于 JDK 17 开发，目前仅支持 JetBrains IDE 2022.3 及以上版本\n\n### 4. 如何修改插件的快捷键？\n在 `Settings` → `Keymap` → `Plugins` → `Switch2Cursor` 中修改\n\n## 📄 许可证\n本项目采用 [MIT License](LICENSE) 开源协议\n\n\n## 📮 问题反馈\n如果遇到问题或有建议，请通过以下方式反馈：\n- [提交 GitHub Issue](https://github.com/qczone/switch2cursor/issues)\n\n## 🌟 Star 历史\n\n[![Star History Chart](https://api.star-history.com/svg?repos=qczone/switch2cursor&type=Date)](https://star-history.com/#qczone/switch2cursor&Date)"
  },
  {
    "path": "build.gradle.kts",
    "content": "plugins {\n    id(\"java\")\n    id(\"org.jetbrains.kotlin.jvm\") version \"1.9.25\"\n    id(\"org.jetbrains.intellij\") version \"1.17.4\"\n}\n\ngroup = \"com.github.qczone\"\nversion = \"1.0.3\"\n\n\nrepositories {\n    mavenCentral()\n}\n\n// Configure Gradle IntelliJ Plugin\n// Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html\nintellij {\n    version.set(\"2022.3\")\n    type.set(\"IC\") // Target IDE Platform\n    pluginName.set(\"Switch2Cursor\")\n    updateSinceUntilBuild.set(true)\n    sameSinceUntilBuild.set(false)\n\n    plugins.set(listOf(/* Plugin Dependencies */))\n}\n\ntasks {\n    // Set the JVM compatibility versions\n    withType<JavaCompile> {\n        sourceCompatibility = \"17\"\n        targetCompatibility = \"17\"\n    }\n    withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {\n        kotlinOptions.jvmTarget = \"17\"\n    }\n\n    patchPluginXml {\n        sinceBuild.set(\"223\")\n        untilBuild.set(\"\")\n    }\n\n    signPlugin {\n        certificateChain.set(System.getenv(\"CERTIFICATE_CHAIN\"))\n        privateKey.set(System.getenv(\"PRIVATE_KEY\"))\n        password.set(System.getenv(\"PRIVATE_KEY_PASSWORD\"))\n    }\n\n    publishPlugin {\n        token.set(System.getenv(\"PUBLISH_TOKEN\"))\n    }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.11-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib\nkotlin.stdlib.default.dependency=false\n# Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html\norg.gradle.configuration-cache=false\n# Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html\norg.gradle.caching=false"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\nAPP_HOME=$( cd \"${APP_HOME:-./}\" && pwd -P ) || exit\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=${0##*/}\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n# Collect all arguments for the java command;\n#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of\n#     shell script including quotes and variable substitutions, so put them in\n#     double quotes to make sure that they get re-expanded; and\n#   * put everything else in single quotes, so that it's not re-expanded.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto execute\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto execute\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "pluginManagement {\n    repositories {\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\n\nrootProject.name = \"switch2cursor\""
  },
  {
    "path": "src/main/kotlin/com/github/qczone/switch2cursor/actions/OpenFileInCursorAction.kt",
    "content": "package com.github.qczone.switch2cursor.actions\n\nimport com.github.qczone.switch2cursor.settings.AppSettingsState\nimport com.github.qczone.switch2cursor.utils.WindowUtils\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.actionSystem.CommonDataKeys\nimport com.intellij.openapi.diagnostic.Logger\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\n\nclass OpenFileInCursorAction : AnAction() {\n    private val logger = Logger.getInstance(OpenFileInCursorAction::class.java)\n\n    override fun getActionUpdateThread(): ActionUpdateThread {\n        return ActionUpdateThread.BGT\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val project: Project = e.project ?: return\n        val virtualFile: VirtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE) ?: return\n        \n        val editor: Editor? = e.getData(CommonDataKeys.EDITOR)\n        \n        val line = editor?.caretModel?.logicalPosition?.line?.plus(1) ?: 1\n        val column = editor?.caretModel?.logicalPosition?.column?.plus(1) ?: 1\n        \n        val filePath = virtualFile.path\n        val settings = AppSettingsState.getInstance()\n        val cursorPath = settings.cursorPath\n        \n        val command = when {\n            System.getProperty(\"os.name\").lowercase().contains(\"mac\") -> {\n                arrayOf(\"open\", \"-a\", \"$cursorPath\", \"cursor://file$filePath:$line:$column\")\n            }\n            System.getProperty(\"os.name\").lowercase().contains(\"windows\") -> {\n                arrayOf(\"cmd\", \"/c\", \"$cursorPath\", \"--goto\", \"$filePath:$line:$column\")\n            }\n            else -> {\n                arrayOf(cursorPath, \"--goto\", \"$filePath:$line:$column\")\n            }\n        }\n        \n        try {\n            logger.info(\"Executing command: ${command.joinToString(\" \")}\")\n            ProcessBuilder(*command).start()\n        } catch (ex: Exception) {\n            logger.error(\"Failed to execute cursor command: ${ex.message}\", ex)\n            com.intellij.openapi.ui.Messages.showErrorDialog(\n                project,\n                \"\"\"\n                ${ex.message}\n                \n                Please check:\n                1. Cursor path is correctly configured in Settings > Tools > Switch2Cursor\n                2. Cursor is properly installed on your system\n                3. The configured path points to a valid Cursor executable\n                \"\"\".trimIndent(),\n                \"Error\"\n            )\n        }\n\n        WindowUtils.activeWindow()\n    }\n\n    override fun update(e: AnActionEvent) {\n        val project = e.project\n        val virtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE)\n        \n        e.presentation.isEnabledAndVisible = project != null && \n                                           virtualFile != null && \n                                           !virtualFile.isDirectory\n    }\n} "
  },
  {
    "path": "src/main/kotlin/com/github/qczone/switch2cursor/actions/OpenProjectInCursorAction.kt",
    "content": "package com.github.qczone.switch2cursor.actions\n\nimport com.github.qczone.switch2cursor.settings.AppSettingsState\nimport com.github.qczone.switch2cursor.utils.WindowUtils\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.diagnostic.Logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\n\nclass OpenProjectInCursorAction : AnAction() {\n    private val logger = Logger.getInstance(OpenProjectInCursorAction::class.java)\n\n    override fun getActionUpdateThread(): ActionUpdateThread {\n        return ActionUpdateThread.BGT\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val project: Project = e.project ?: return\n        val projectPath = project.basePath ?: return\n\n        val settings = AppSettingsState.getInstance()\n        val cursorPath = settings.cursorPath\n\n        val command = when {\n            System.getProperty(\"os.name\").lowercase().contains(\"mac\") -> {\n                arrayOf(\"open\", \"-a\", \"$cursorPath\", projectPath)\n            }\n\n            System.getProperty(\"os.name\").lowercase().contains(\"windows\") -> {\n                arrayOf(\"cmd\", \"/c\", \"$cursorPath\", projectPath)\n            }\n\n            else -> {\n                arrayOf(cursorPath, projectPath)\n            }\n        }\n        try {\n            logger.info(\"Executing command: ${command.joinToString(\" \")}\")\n            ProcessBuilder(*command).start()\n        } catch (ex: Exception) {\n            logger.error(\"Failed to execute cursor command: ${ex.message}\", ex)\n            com.intellij.openapi.ui.Messages.showErrorDialog(\n                project,\n                \"\"\"\n                ${ex.message}\n                \n                Please check:\n                1. Cursor path is correctly configured in Settings > Tools > Switch2Cursor\n                2. Cursor is properly installed on your system\n                3. The configured path points to a valid Cursor executable\n                \"\"\".trimIndent(),\n                \"Error\"\n            )\n        }\n\n        WindowUtils.activeWindow()\n    }\n\n    override fun update(e: AnActionEvent) {\n        val project = e.project\n        e.presentation.isEnabledAndVisible = project != null\n    }\n} "
  },
  {
    "path": "src/main/kotlin/com/github/qczone/switch2cursor/settings/AppSettingsConfigurable.kt",
    "content": "package com.github.qczone.switch2cursor.settings\n\nimport com.intellij.openapi.options.Configurable\nimport javax.swing.JComponent\nimport javax.swing.JPanel\nimport javax.swing.JTextField\nimport com.intellij.ui.components.JBLabel\nimport com.intellij.util.ui.FormBuilder\n\nclass AppSettingsConfigurable : Configurable {\n    private var mySettingsComponent: AppSettingsComponent? = null\n\n    override fun getDisplayName(): String = \"Open In Cursor\"\n\n    override fun createComponent(): JComponent {\n        mySettingsComponent = AppSettingsComponent()\n        return mySettingsComponent!!.panel\n    }\n\n    override fun isModified(): Boolean {\n        val settings = AppSettingsState.getInstance()\n        return mySettingsComponent!!.cursorPath != settings.cursorPath\n    }\n\n    override fun apply() {\n        val settings = AppSettingsState.getInstance()\n        settings.cursorPath = mySettingsComponent!!.cursorPath\n    }\n\n    override fun reset() {\n        val settings = AppSettingsState.getInstance()\n        mySettingsComponent!!.cursorPath = settings.cursorPath\n    }\n\n    override fun disposeUIResources() {\n        mySettingsComponent = null\n    }\n}\n\nclass AppSettingsComponent {\n    val panel: JPanel\n    private val cursorPathText = JTextField()\n\n    init {\n        panel = FormBuilder.createFormBuilder()\n            .addLabeledComponent(JBLabel(\"Cursor Path: \"), cursorPathText, 1, false)\n            .addComponentFillVertically(JPanel(), 0)\n            .panel\n    }\n\n    var cursorPath: String\n        get() = cursorPathText.text\n        set(value) {\n            cursorPathText.text = value\n        }\n} "
  },
  {
    "path": "src/main/kotlin/com/github/qczone/switch2cursor/settings/AppSettingsState.kt",
    "content": "package com.github.qczone.switch2cursor.settings\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.components.PersistentStateComponent\nimport com.intellij.openapi.components.State\nimport com.intellij.openapi.components.Storage\nimport com.intellij.util.xmlb.XmlSerializerUtil\n\n@State(\n    name = \"com.github.qczone.switch2cursor.settings.AppSettingsState\",\n    storages = [Storage(\"Switch2CursorSettings.xml\")]\n)\nclass AppSettingsState : PersistentStateComponent<AppSettingsState> {\n    var cursorPath: String = \"cursor\"\n\n    override fun getState(): AppSettingsState = this\n\n    override fun loadState(state: AppSettingsState) {\n        XmlSerializerUtil.copyBean(state, this)\n    }\n\n    companion object {\n        fun getInstance(): AppSettingsState = ApplicationManager.getApplication().getService(AppSettingsState::class.java)\n    }\n} "
  },
  {
    "path": "src/main/kotlin/com/github/qczone/switch2cursor/utils/WindowUtils.kt",
    "content": "package com.github.qczone.switch2cursor.utils\n\nimport com.intellij.openapi.util.SystemInfo\nimport com.intellij.openapi.diagnostic.Logger\n\nobject WindowUtils {\n\n    private val logger = Logger.getInstance(WindowUtils::class.java)\n\n    fun activeWindow() {\n        if (!SystemInfo.isWindows) {\n            return\n        }\n        try {\n            val command = \"\"\"Get-Process | Where-Object { ${'$'}_.ProcessName -eq '\"Cursor\"' -and ${'$'}_.MainWindowTitle -match '\"Cursor\"' } | Sort-Object { ${'$'}_.StartTime } -Descending | Select-Object -First 1 | ForEach-Object { (New-Object -ComObject WScript.Shell).AppActivate(${'$'}_.Id) }\"\"\"\n            logger.info(\"Executing PowerShell command: $command\")\n            \n            val processBuilder = ProcessBuilder(\"powershell\", \"-command\", command)\n            processBuilder.redirectErrorStream(true)\n            \n            val process = processBuilder.start()\n            val output = process.inputStream.bufferedReader().use { it.readText() }\n            logger.info(\"Command output: $output\")\n            \n            val exitCode = process.waitFor()\n            logger.info(\"Command completed with exit code: $exitCode\")\n            \n            if (exitCode != 0) {\n                logger.error(\"Command failed with exit code: $exitCode\")\n            }\n        } catch (e: Exception) {\n            logger.error(\"Failed to activate Cursor window\", e)\n        }\n    }\n}"
  },
  {
    "path": "src/main/resources/META-INF/plugin.xml",
    "content": "<!-- Plugin Configuration File. Read more: https://plugins.jetbrains.com/docs/intellij/plugin-configuration-file.html --><!DOCTYPE idea-plugin [\n    <!ELEMENT idea-plugin (id|name|vendor|description|depends|extensions|actions|change-notes)*>\n    <!ELEMENT id (#PCDATA)>\n    <!ELEMENT name (#PCDATA)>\n    <!ELEMENT vendor (#PCDATA)>\n    <!ATTLIST vendor\n        email CDATA #REQUIRED\n        url CDATA #REQUIRED>\n    <!ELEMENT description (#PCDATA)>\n    <!ELEMENT depends (#PCDATA)>\n    <!ELEMENT extensions (applicationService|applicationConfigurable)*>\n    <!ATTLIST extensions\n        defaultExtensionNs CDATA #REQUIRED>\n    <!ELEMENT applicationService (#PCDATA)>\n    <!ATTLIST applicationService\n        serviceImplementation CDATA #REQUIRED>\n    <!ELEMENT applicationConfigurable (#PCDATA)>\n    <!ATTLIST applicationConfigurable\n        parentId CDATA #REQUIRED\n        instance CDATA #REQUIRED\n        id CDATA #REQUIRED\n        displayName CDATA #REQUIRED>\n    <!ELEMENT actions (action)*>\n    <!ELEMENT action (add-to-group|keyboard-shortcut)*>\n    <!ATTLIST action\n        id CDATA #REQUIRED\n        class CDATA #REQUIRED\n        text CDATA #REQUIRED\n        description CDATA #REQUIRED>\n    <!ELEMENT add-to-group (#PCDATA)>\n    <!ATTLIST add-to-group\n        anchor CDATA #REQUIRED\n        group-id CDATA #REQUIRED>\n    <!ELEMENT keyboard-shortcut (#PCDATA)>\n    <!ATTLIST keyboard-shortcut\n        keymap CDATA #REQUIRED\n        first-keystroke CDATA #REQUIRED>\n    <!ELEMENT change-notes (#PCDATA)>\n    ]>\n<idea-plugin>\n    <!-- Unique identifier of the plugin. It should be FQN. It cannot be changed between the plugin versions. -->\n    <id>com.github.qczone.switch2cursor</id>\n\n    <!-- Public plugin name should be written in Title Case.\n         Guidelines: https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-name -->\n    <name>Switch2Cursor</name>\n\n    <!-- A displayed Vendor name or Organization ID displayed on the Plugins Page. -->\n    <vendor email=\"quchaozhong@gmail.com\" url=\"https://github.com/qczone\">qczone</vendor>\n\n    <!-- Description of the plugin displayed on the Plugin Page and IDE Plugin Manager.\n         Simple HTML elements (text formatting, paragraphs, and lists) can be added inside of <![CDATA[ ]]> tag.\n         Guidelines: https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-description -->\n    <description><![CDATA[\n    <p>Switch2Cursor is a JetBrains IDE plugin that enables seamless switching between IDE and Cursor while maintaining precise cursor position.</p>\n\n    <h2>Key Features</h2>\n    <ul>\n        <li>Open files in Cursor with one click</li>\n        <li>Maintain exact cursor position (line and column)</li>\n        <li>Support opening entire projects in Cursor</li>\n        <li>Convenient shortcuts (customizable):\n            <ul>\n                <li>Alt+Shift+O - Open current file</li>\n                <li>Alt+Shift+P - Open current project</li>\n            </ul>\n        </li>\n        <li>Multiple access methods: shortcuts, context menu, tools menu</li>\n    </ul>\n\n    <h2>Usage</h2>\n    <ul>\n        <li>Open current file:\n            <ul>\n                <li>Press Alt+Shift+O</li>\n                <li>Right-click in editor → Open File In Cursor</li>\n                <li>Tools menu → Open File In Cursor</li>\n            </ul>\n        </li>\n        <li>Open project:\n            <ul>\n                <li>Press Alt+Shift+P</li>\n                <li>Right-click in project view → Open Project In Cursor</li>\n                <li>Tools menu → Open Project In Cursor</li>\n            </ul>\n        </li>\n    </ul>\n\n    <h2>Configuration</h2>\n    <ul>\n        <li>Go to Settings/Preferences → Tools → Switch2Cursor</li>\n        <li>Set Cursor executable path (default is \"cursor\")</li>\n        <li>Customize shortcuts in Keymap settings</li>\n    </ul>\n\n    <h2>Requirements</h2>\n    <ul>\n        <li>Cursor Editor installed (<a href=\"https://cursor.sh\">https://cursor.sh</a>)</li>\n        <li>Compatible with all JetBrains IDEs</li>\n        <li>Supported IDE versions: 2022.3 and above</li>\n    </ul>\n\n    <hr/>\n\n    <p>Switch2Cursor 是一个 JetBrains IDE 插件，可以让你在 IDE 和 Cursor 之间无缝切换，并保持精确的光标位置。</p>\n\n    <h2>主要特性</h2>\n    <ul>\n        <li>一键在 Cursor 中打开文件</li>\n        <li>精确保持光标位置（行号和列号）</li>\n        <li>支持在 Cursor 中打开整个项目</li>\n        <li>便捷的快捷键（可自定义）:\n            <ul>\n                <li>Alt+Shift+O - 打开当前文件</li>\n                <li>Alt+Shift+P - 打开当前项目</li>\n            </ul>\n        </li>\n        <li>多种访问方式：快捷键、右键菜单、工具菜单</li>\n    </ul>\n\n    <h2>使用方法</h2>\n    <ul>\n        <li>打开当前文件:\n            <ul>\n                <li>按下 Alt+Shift+O</li>\n                <li>在编辑器中右键 → Open File In Cursor</li>\n                <li>工具菜单 → Open File In Cursor</li>\n            </ul>\n        </li>\n        <li>打开项目:\n            <ul>\n                <li>按下 Alt+Shift+P</li>\n                <li>在项目视图中右键 → Open Project In Cursor</li>\n                <li>工具菜单 → Open Project In Cursor</li>\n            </ul>\n        </li>\n    </ul>\n\n    <h2>配置说明</h2>\n    <ul>\n        <li>进入 Settings/Preferences → Tools → Switch2Cursor</li>\n        <li>设置 Cursor 可执行文件路径（默认为 \"cursor\"）</li>\n        <li>通过 Keymap 设置自定义快捷键</li>\n    </ul>\n\n    <h2>系统要求</h2>\n    <ul>\n        <li>已安装 Cursor Editor (<a href=\"https://cursor.sh\">https://cursor.sh</a>)</li>\n        <li>兼容所有 JetBrains IDE</li>\n        <li>支持的 IDE 版本：2022.3 及以上</li>\n    </ul>\n    ]]></description>\n\n    <!-- Product and plugin compatibility requirements.\n         Read more: https://plugins.jetbrains.com/docs/intellij/plugin-compatibility.html -->\n    <depends>com.intellij.modules.platform</depends>\n\n    <!-- Extension points defined by the plugin.\n         Read more: https://plugins.jetbrains.com/docs/intellij/plugin-extension-points.html -->\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <applicationService serviceImplementation=\"com.github.qczone.switch2cursor.settings.AppSettingsState\"/>\n        <applicationConfigurable\n            parentId=\"tools\"\n            instance=\"com.github.qczone.switch2cursor.settings.AppSettingsConfigurable\"\n            id=\"com.github.qczone.switch2cursor.settings.AppSettingsConfigurable\"\n            displayName=\"Switch2Cursor\"/>\n    </extensions>\n\n    <actions>\n        <action id=\"OpenFileInCursor\" class=\"com.github.qczone.switch2cursor.actions.OpenFileInCursorAction\"\n            text=\"Open File In Cursor\" description=\"Open current file in Cursor\">\n            <add-to-group group-id=\"ToolsMenu\" anchor=\"first\"/>\n            <add-to-group group-id=\"EditorPopupMenu\" anchor=\"first\"/>\n            <add-to-group group-id=\"ProjectViewPopupMenu\" anchor=\"first\"/>\n            <keyboard-shortcut keymap=\"$default\" first-keystroke=\"alt shift O\"/>\n        </action>\n\n        <action id=\"OpenProjectInCursor\" class=\"com.github.qczone.switch2cursor.actions.OpenProjectInCursorAction\"\n            text=\"Open Project In Cursor\" description=\"Open current project in Cursor\">\n            <add-to-group group-id=\"ToolsMenu\" anchor=\"first\"/>\n            <add-to-group group-id=\"ProjectViewPopupMenu\" anchor=\"first\"/>\n            <keyboard-shortcut keymap=\"$default\" first-keystroke=\"alt shift P\"/>\n        </action>\n    </actions>\n\n    <change-notes><![CDATA[\n        <h3>1.0.3</h3>\n        <ul>\n            <li>⚡️ Update action update thread to background thread for better performance</li>\n        </ul>\n\n        <hr/>\n\n        <ul>\n            <li>⚡️ 将动作更新线程改为后台线程以提升性能</li>\n        </ul>\n\n        <h3>1.0.2</h3>\n        <ul>\n            <li>⚡️ Support JetBrains IDEs from version 2022.3</li>\n            <li>🐛 Fix DirectoryChooserPopupMenu registration issue</li>\n        </ul>\n\n        <hr/>\n\n        <ul>\n            <li>⚡️ 支持 JetBrains IDEs 2022.3 开始以及以后的版本</li>\n            <li>🐛 修复 DirectoryChooserPopupMenu 注册问题</li>\n        </ul>\n        <h3>1.0.1</h3>\n        <ul>\n            <li>🐛 Fix the issue of not being able to activate the Cursor window on Windows</li>\n        </ul>\n\n        <hr/>\n        \n        <ul>\n            <li>🐛 修复在 Windows 上无法激活 Cursor 窗口的问题</li>\n        </ul>\n        \n        <h3>1.0.0</h3>\n        <ul>\n            <li>✨ Support opening files and projects in Cursor</li>\n            <li>🔧 Configure Cursor executable path</li>\n            <li>⌨️ Access via shortcuts, context menu and toolbar</li>\n        </ul>\n\n        <hr/>\n\n        <ul>\n            <li>✨ 支持在 Cursor 中打开文件和项目</li>\n            <li>🔧 支持配置 Cursor 可执行文件路径</li>\n            <li>⌨️ 支持快捷键、右键菜单和工具栏访问</li>\n        </ul>\n    ]]></change-notes>\n</idea-plugin>"
  }
]