Repository: qczone/switch2cursor Branch: main Commit: 26020292c8ad Files: 17 Total size: 37.6 KB Directory structure: gitextract_xo5h46fx/ ├── .gitignore ├── LICENSE ├── README.md ├── README_zh.md ├── build.gradle.kts ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src/ └── main/ ├── kotlin/ │ └── com/ │ └── github/ │ └── qczone/ │ └── switch2cursor/ │ ├── actions/ │ │ ├── OpenFileInCursorAction.kt │ │ └── OpenProjectInCursorAction.kt │ ├── settings/ │ │ ├── AppSettingsConfigurable.kt │ │ └── AppSettingsState.kt │ └── utils/ │ └── WindowUtils.kt └── resources/ └── META-INF/ └── plugin.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .gradle/ .idea/ .vscode/ build/ *.DS_Store bin/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2025 qczone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Switch2Cursor [中文文档](README_zh.md) > 💡 Recommended to use with [Switch2IDEA](https://github.com/qczone/switch2idea) in Cursor [![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) [![Downloads](https://img.shields.io/jetbrains/plugin/d/26309-switch2cursor?style=for-the-badge&logo=intellij-idea)](https://plugins.jetbrains.com/plugin/26309-switch2cursor) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge)](LICENSE) ## 🔍 Introduction A JetBrains IDE plugin that enhances development efficiency by enabling seamless switching between JetBrains IDE and Cursor ![Switch2Cursor Demo](images/switch-show.gif) ## 🌟 Features - 🚀 Seamless Editor Switching - One-click switch between JetBrains IDE and Cursor - Automatically positions to the same cursor location (line and column) - Perfectly maintains editing context without interrupting workflow - ⌨️ Convenient Shortcut Support - macOS: - `Option+Shift+P` - Open project in Cursor - `Option+Shift+O` - Open current file in Cursor - Windows: - `Alt+Shift+P` - Open project in Cursor - `Alt+Shift+O` - Open current file in Cursor - 🔧 Multiple Access Methods - Keyboard shortcuts - Editor context menu - IDE tools menu ## 🛠️ Installation Guide ### Method 1: Install via JetBrains Marketplace 1. Open IDE → `Settings` → `Plugins` → `Marketplace` 2. Search for switch2cursor 3. Click `Install` to complete installation 4. Click `OK` to apply changes ### Method 2: Local Installation 1. Download the latest plugin package from [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/26309-switch2cursor) 2. IDE → `Settings` → `Plugins` → `⚙️`→ `Install Plugin from Disk...` 3. Select the downloaded plugin package 4. Click `OK` to apply changes ## 🚀 Usage Guide ### Basic Usage #### Open Project - Shortcuts: - macOS: `Option+Shift+P` - Windows: `Alt+Shift+P` - Context Menu: Right-click in project view → `Open Project In Cursor` - Tools Menu: `Tools` → `Open Project In Cursor` #### Open Current File - Shortcuts: - macOS: `Option+Shift+O` - Windows: `Alt+Shift+O` - Context Menu: Right-click in editor → `Open File In Cursor` - Tools Menu: `Tools` → `Open File In Cursor` ### Configuration - In `Settings/Preferences` → `Tools` → `Switch2Cursor`: - Set Cursor executable path (default is "cursor") - Customize shortcuts through Keymap settings ### Requirements - [Cursor](https://cursor.com) installed - Compatible with all JetBrains IDEs (version 2022.3 and above) ## 🧑‍💻 Developer Guide ### Build Project ```bash # Clone repository git clone https://github.com/qczone/switch2cursor.git # Build plugin cd switch2cursor ./gradlew buildPlugin # Plugin package will be generated in build/distributions/ directory ``` ### Contributing 1. Fork this repository 2. Create a feature branch 3. Commit your changes 4. Push to the branch 5. Submit a Pull Request ## 🙋 FAQ ### 1. Why doesn't the shortcut/menu click switch to Cursor after installation? Check if the correct Cursor executable path is configured in `Settings` → `Tools` → `Switch2Cursor` ### 2. Which IDEs are supported? Supports all JetBrains IDEs, including: IntelliJ IDEA, PyCharm, WebStorm, GoLand, RustRover, Android Studio, etc. ### 3. Which versions are supported? The plugin is developed based on JDK 17 and currently only supports JetBrains IDE version 2022.3 and above ### 4. How to modify plugin shortcuts? Modify in `Settings` → `Keymap` → `Plugins` → `Switch2Cursor` ## 📄 License This project is licensed under the [MIT License](LICENSE) ## 📮 Feedback If you encounter any issues or have suggestions, please provide feedback through: - [Submit GitHub Issue](https://github.com/qczone/switch2cursor/issues) ## 🌟 Star History [![Star History Chart](https://api.star-history.com/svg?repos=qczone/switch2cursor&type=Date)](https://star-history.com/#qczone/switch2cursor&Date) ================================================ FILE: README_zh.md ================================================ # Switch2Cursor [English](README.md) > 💡 推荐在 Cursor 中配合 [Switch2IDEA](https://github.com/qczone/switch2idea) 使用 [![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) [![Downloads](https://img.shields.io/jetbrains/plugin/d/26309-switch2cursor?style=for-the-badge&logo=intellij-idea)](https://plugins.jetbrains.com/plugin/26309-switch2cursor) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge)](LICENSE) ## 🔍 项目简介 一个提升开发效率的 JetBrains IDE 插件,让你在 JetBrains IDE 和 Cursor 之间实现丝滑切换 ![Switch2Cursor演示](images/switch-show.gif) ## 🌟 功能特性 - 🚀 无缝编辑器切换 - 在 JetBrains IDE 和 Cursor 之间一键切换 - 自动定位到相同的光标位置(行号和列号) - 完美保持编辑上下文,不中断思路 - ⌨️ 便捷的快捷键支持 - macOS: - `Option+Shift+P` - 在 Cursor 中打开整个项目 - `Option+Shift+O` - 在 Cursor 中打开当前文件 - Windows: - `Alt+Shift+P` - 在 Cursor 中打开整个项目 - `Alt+Shift+O` - 在 Cursor 中打开当前文件 - 🔧 多样化的访问方式 - 快捷键操作 - 编辑器右键菜单 - IDE 工具菜单 ## 🛠️ 安装指南 ### 方式一:通过 JetBrains 插件市场安装 1. 打开 IDE → `Settings` → `Plugins` → `Marketplace` 2. 搜索 switch2cursor 3. 点击 `Install` 完成安装 4. 点击 `OK` 生效 ### 方式二:本地安装 1. 从 [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/26309-switch2cursor) 下载最新版插件包 2. IDE → `Settings` → `Plugins` → `⚙️`→ `Install Plugin from Disk...` 3. 选择下载的插件包 4. 点击 `OK` 生效 ## 🚀 使用说明 ### 基础使用 #### 打开项目 - 快捷键: - macOS: `Option+Shift+P` - Windows: `Alt+Shift+P` - 右键菜单:在项目视图中右键 → `Open Project In Cursor` - 工具菜单:`Tools` → `Open Project In Cursor` #### 打开当前文件 - 快捷键: - macOS: `Option+Shift+O` - Windows: `Alt+Shift+O` - 右键菜单:在编辑器中右键 → `Open File In Cursor` - 工具菜单:`Tools` → `Open File In Cursor` ### 配置 - 在 `Settings/Preferences` → `Tools` → `Switch2Cursor` 中: - 设置 Cursor 可执行文件路径(默认为 "cursor") - 通过 Keymap 设置自定义快捷键 ### 环境要求 - 已安装 [Cursor](https://cursor.com) - 兼容所有 JetBrains IDE(2022.3 及以上版本) ## 🧑‍💻 开发者指南 ### 项目构建 ```bash # 克隆仓库 git clone https://github.com/qczone/switch2cursor.git # 构建插件 cd switch2cursor ./gradlew buildPlugin # 生成插件包在 build/distributions/ 目录下 ``` ### 贡献代码 1. Fork 本仓库 2. 创建特性分支 3. 提交修改 4. 推送分支 5. 提交 Pull Request ## 🙋 常见问题 ### 1. 为什么安装之后快捷键/点击菜单不会跳转到 Cursor? 检查是否在 `Settings` → `Tools` → `Switch2Cursor` 中配置了正确的 Cursor 可执行文件路径 ### 2. 都支持哪些 IDE? 支持所有 JetBrains 系列的 IDE,如:IntelliJ IDEA、PyCharm、WebStorm、GoLand、RustRover、Android Studio 等 ### 3. 都支持哪些版本? 插件基于 JDK 17 开发,目前仅支持 JetBrains IDE 2022.3 及以上版本 ### 4. 如何修改插件的快捷键? 在 `Settings` → `Keymap` → `Plugins` → `Switch2Cursor` 中修改 ## 📄 许可证 本项目采用 [MIT License](LICENSE) 开源协议 ## 📮 问题反馈 如果遇到问题或有建议,请通过以下方式反馈: - [提交 GitHub Issue](https://github.com/qczone/switch2cursor/issues) ## 🌟 Star 历史 [![Star History Chart](https://api.star-history.com/svg?repos=qczone/switch2cursor&type=Date)](https://star-history.com/#qczone/switch2cursor&Date) ================================================ FILE: build.gradle.kts ================================================ plugins { id("java") id("org.jetbrains.kotlin.jvm") version "1.9.25" id("org.jetbrains.intellij") version "1.17.4" } group = "com.github.qczone" version = "1.0.3" repositories { mavenCentral() } // Configure Gradle IntelliJ Plugin // Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html intellij { version.set("2022.3") type.set("IC") // Target IDE Platform pluginName.set("Switch2Cursor") updateSinceUntilBuild.set(true) sameSinceUntilBuild.set(false) plugins.set(listOf(/* Plugin Dependencies */)) } tasks { // Set the JVM compatibility versions withType { sourceCompatibility = "17" targetCompatibility = "17" } withType { kotlinOptions.jvmTarget = "17" } patchPluginXml { sinceBuild.set("223") untilBuild.set("") } signPlugin { certificateChain.set(System.getenv("CERTIFICATE_CHAIN")) privateKey.set(System.getenv("PRIVATE_KEY")) password.set(System.getenv("PRIVATE_KEY_PASSWORD")) } publishPlugin { token.set(System.getenv("PUBLISH_TOKEN")) } } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ # Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib kotlin.stdlib.default.dependency=false # Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html org.gradle.configuration-cache=false # Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html org.gradle.caching=false ================================================ FILE: gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original 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 POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" APP_BASE_NAME=${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 "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # 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 ;; #( MSYS* | 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" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in # double quotes to make sure that they get re-expanded; and # * put everything else in single quotes, so that it's not re-expanded. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @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 ================================================ FILE: settings.gradle.kts ================================================ pluginManagement { repositories { mavenCentral() gradlePluginPortal() } } rootProject.name = "switch2cursor" ================================================ FILE: src/main/kotlin/com/github/qczone/switch2cursor/actions/OpenFileInCursorAction.kt ================================================ package com.github.qczone.switch2cursor.actions import com.github.qczone.switch2cursor.settings.AppSettingsState import com.github.qczone.switch2cursor.utils.WindowUtils import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.actionSystem.ActionUpdateThread class OpenFileInCursorAction : AnAction() { private val logger = Logger.getInstance(OpenFileInCursorAction::class.java) override fun getActionUpdateThread(): ActionUpdateThread { return ActionUpdateThread.BGT } override fun actionPerformed(e: AnActionEvent) { val project: Project = e.project ?: return val virtualFile: VirtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE) ?: return val editor: Editor? = e.getData(CommonDataKeys.EDITOR) val line = editor?.caretModel?.logicalPosition?.line?.plus(1) ?: 1 val column = editor?.caretModel?.logicalPosition?.column?.plus(1) ?: 1 val filePath = virtualFile.path val settings = AppSettingsState.getInstance() val cursorPath = settings.cursorPath val command = when { System.getProperty("os.name").lowercase().contains("mac") -> { arrayOf("open", "-a", "$cursorPath", "cursor://file$filePath:$line:$column") } System.getProperty("os.name").lowercase().contains("windows") -> { arrayOf("cmd", "/c", "$cursorPath", "--goto", "$filePath:$line:$column") } else -> { arrayOf(cursorPath, "--goto", "$filePath:$line:$column") } } try { logger.info("Executing command: ${command.joinToString(" ")}") ProcessBuilder(*command).start() } catch (ex: Exception) { logger.error("Failed to execute cursor command: ${ex.message}", ex) com.intellij.openapi.ui.Messages.showErrorDialog( project, """ ${ex.message} Please check: 1. Cursor path is correctly configured in Settings > Tools > Switch2Cursor 2. Cursor is properly installed on your system 3. The configured path points to a valid Cursor executable """.trimIndent(), "Error" ) } WindowUtils.activeWindow() } override fun update(e: AnActionEvent) { val project = e.project val virtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE) e.presentation.isEnabledAndVisible = project != null && virtualFile != null && !virtualFile.isDirectory } } ================================================ FILE: src/main/kotlin/com/github/qczone/switch2cursor/actions/OpenProjectInCursorAction.kt ================================================ package com.github.qczone.switch2cursor.actions import com.github.qczone.switch2cursor.settings.AppSettingsState import com.github.qczone.switch2cursor.utils.WindowUtils import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.openapi.actionSystem.ActionUpdateThread class OpenProjectInCursorAction : AnAction() { private val logger = Logger.getInstance(OpenProjectInCursorAction::class.java) override fun getActionUpdateThread(): ActionUpdateThread { return ActionUpdateThread.BGT } override fun actionPerformed(e: AnActionEvent) { val project: Project = e.project ?: return val projectPath = project.basePath ?: return val settings = AppSettingsState.getInstance() val cursorPath = settings.cursorPath val command = when { System.getProperty("os.name").lowercase().contains("mac") -> { arrayOf("open", "-a", "$cursorPath", projectPath) } System.getProperty("os.name").lowercase().contains("windows") -> { arrayOf("cmd", "/c", "$cursorPath", projectPath) } else -> { arrayOf(cursorPath, projectPath) } } try { logger.info("Executing command: ${command.joinToString(" ")}") ProcessBuilder(*command).start() } catch (ex: Exception) { logger.error("Failed to execute cursor command: ${ex.message}", ex) com.intellij.openapi.ui.Messages.showErrorDialog( project, """ ${ex.message} Please check: 1. Cursor path is correctly configured in Settings > Tools > Switch2Cursor 2. Cursor is properly installed on your system 3. The configured path points to a valid Cursor executable """.trimIndent(), "Error" ) } WindowUtils.activeWindow() } override fun update(e: AnActionEvent) { val project = e.project e.presentation.isEnabledAndVisible = project != null } } ================================================ FILE: src/main/kotlin/com/github/qczone/switch2cursor/settings/AppSettingsConfigurable.kt ================================================ package com.github.qczone.switch2cursor.settings import com.intellij.openapi.options.Configurable import javax.swing.JComponent import javax.swing.JPanel import javax.swing.JTextField import com.intellij.ui.components.JBLabel import com.intellij.util.ui.FormBuilder class AppSettingsConfigurable : Configurable { private var mySettingsComponent: AppSettingsComponent? = null override fun getDisplayName(): String = "Open In Cursor" override fun createComponent(): JComponent { mySettingsComponent = AppSettingsComponent() return mySettingsComponent!!.panel } override fun isModified(): Boolean { val settings = AppSettingsState.getInstance() return mySettingsComponent!!.cursorPath != settings.cursorPath } override fun apply() { val settings = AppSettingsState.getInstance() settings.cursorPath = mySettingsComponent!!.cursorPath } override fun reset() { val settings = AppSettingsState.getInstance() mySettingsComponent!!.cursorPath = settings.cursorPath } override fun disposeUIResources() { mySettingsComponent = null } } class AppSettingsComponent { val panel: JPanel private val cursorPathText = JTextField() init { panel = FormBuilder.createFormBuilder() .addLabeledComponent(JBLabel("Cursor Path: "), cursorPathText, 1, false) .addComponentFillVertically(JPanel(), 0) .panel } var cursorPath: String get() = cursorPathText.text set(value) { cursorPathText.text = value } } ================================================ FILE: src/main/kotlin/com/github/qczone/switch2cursor/settings/AppSettingsState.kt ================================================ package com.github.qczone.switch2cursor.settings import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.PersistentStateComponent import com.intellij.openapi.components.State import com.intellij.openapi.components.Storage import com.intellij.util.xmlb.XmlSerializerUtil @State( name = "com.github.qczone.switch2cursor.settings.AppSettingsState", storages = [Storage("Switch2CursorSettings.xml")] ) class AppSettingsState : PersistentStateComponent { var cursorPath: String = "cursor" override fun getState(): AppSettingsState = this override fun loadState(state: AppSettingsState) { XmlSerializerUtil.copyBean(state, this) } companion object { fun getInstance(): AppSettingsState = ApplicationManager.getApplication().getService(AppSettingsState::class.java) } } ================================================ FILE: src/main/kotlin/com/github/qczone/switch2cursor/utils/WindowUtils.kt ================================================ package com.github.qczone.switch2cursor.utils import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.diagnostic.Logger object WindowUtils { private val logger = Logger.getInstance(WindowUtils::class.java) fun activeWindow() { if (!SystemInfo.isWindows) { return } try { 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) }""" logger.info("Executing PowerShell command: $command") val processBuilder = ProcessBuilder("powershell", "-command", command) processBuilder.redirectErrorStream(true) val process = processBuilder.start() val output = process.inputStream.bufferedReader().use { it.readText() } logger.info("Command output: $output") val exitCode = process.waitFor() logger.info("Command completed with exit code: $exitCode") if (exitCode != 0) { logger.error("Command failed with exit code: $exitCode") } } catch (e: Exception) { logger.error("Failed to activate Cursor window", e) } } } ================================================ FILE: src/main/resources/META-INF/plugin.xml ================================================ ]> com.github.qczone.switch2cursor Switch2Cursor qczone Switch2Cursor is a JetBrains IDE plugin that enables seamless switching between IDE and Cursor while maintaining precise cursor position.

Key Features

  • Open files in Cursor with one click
  • Maintain exact cursor position (line and column)
  • Support opening entire projects in Cursor
  • Convenient shortcuts (customizable):
    • Alt+Shift+O - Open current file
    • Alt+Shift+P - Open current project
  • Multiple access methods: shortcuts, context menu, tools menu

Usage

  • Open current file:
    • Press Alt+Shift+O
    • Right-click in editor → Open File In Cursor
    • Tools menu → Open File In Cursor
  • Open project:
    • Press Alt+Shift+P
    • Right-click in project view → Open Project In Cursor
    • Tools menu → Open Project In Cursor

Configuration

  • Go to Settings/Preferences → Tools → Switch2Cursor
  • Set Cursor executable path (default is "cursor")
  • Customize shortcuts in Keymap settings

Requirements

  • Cursor Editor installed (https://cursor.sh)
  • Compatible with all JetBrains IDEs
  • Supported IDE versions: 2022.3 and above

Switch2Cursor 是一个 JetBrains IDE 插件,可以让你在 IDE 和 Cursor 之间无缝切换,并保持精确的光标位置。

主要特性

  • 一键在 Cursor 中打开文件
  • 精确保持光标位置(行号和列号)
  • 支持在 Cursor 中打开整个项目
  • 便捷的快捷键(可自定义):
    • Alt+Shift+O - 打开当前文件
    • Alt+Shift+P - 打开当前项目
  • 多种访问方式:快捷键、右键菜单、工具菜单

使用方法

  • 打开当前文件:
    • 按下 Alt+Shift+O
    • 在编辑器中右键 → Open File In Cursor
    • 工具菜单 → Open File In Cursor
  • 打开项目:
    • 按下 Alt+Shift+P
    • 在项目视图中右键 → Open Project In Cursor
    • 工具菜单 → Open Project In Cursor

配置说明

  • 进入 Settings/Preferences → Tools → Switch2Cursor
  • 设置 Cursor 可执行文件路径(默认为 "cursor")
  • 通过 Keymap 设置自定义快捷键

系统要求

  • 已安装 Cursor Editor (https://cursor.sh)
  • 兼容所有 JetBrains IDE
  • 支持的 IDE 版本:2022.3 及以上
]]>
com.intellij.modules.platform 1.0.3
  • ⚡️ Update action update thread to background thread for better performance

  • ⚡️ 将动作更新线程改为后台线程以提升性能

1.0.2

  • ⚡️ Support JetBrains IDEs from version 2022.3
  • 🐛 Fix DirectoryChooserPopupMenu registration issue

  • ⚡️ 支持 JetBrains IDEs 2022.3 开始以及以后的版本
  • 🐛 修复 DirectoryChooserPopupMenu 注册问题

1.0.1

  • 🐛 Fix the issue of not being able to activate the Cursor window on Windows

  • 🐛 修复在 Windows 上无法激活 Cursor 窗口的问题

1.0.0

  • ✨ Support opening files and projects in Cursor
  • 🔧 Configure Cursor executable path
  • ⌨️ Access via shortcuts, context menu and toolbar

  • ✨ 支持在 Cursor 中打开文件和项目
  • 🔧 支持配置 Cursor 可执行文件路径
  • ⌨️ 支持快捷键、右键菜单和工具栏访问
]]>