├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── build.gradle.kts ├── change-notes.html ├── description.html ├── gradle.properties ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── res ├── create_project.png ├── edit_configuration.png ├── logo256.png ├── menu.png ├── output_panel.png ├── problem.gif ├── project_configuration.png ├── quickstart.gif ├── run_configuration.png └── run_plugin.png ├── settings.gradle.kts └── src ├── main ├── kotlin │ └── io │ │ └── xmake │ │ ├── actions │ │ ├── BuildAction.kt │ │ ├── CleanAction.kt │ │ ├── CleanConfigurationAction.kt │ │ ├── QuickStartAction.kt │ │ ├── RebuildAction.kt │ │ ├── RunAction.kt │ │ ├── UpdateCmakeListsAction.kt │ │ └── UpdateCompileCommandsAction.kt │ │ ├── file │ │ ├── XMakeLuaFileChangeListener.kt │ │ ├── XMakeLuaFileType.kt │ │ └── XMakeLuaLanguage.kt │ │ ├── icons │ │ ├── XMakeIconProvider.kt │ │ └── XMakeIcons.kt │ │ ├── project │ │ ├── XMakeModuleConfigurationEditorProvider.kt │ │ ├── XMakeProjectToolkitConfigurable.kt │ │ ├── XMakeToolWindowFactory.kt │ │ ├── XMakeToolWindowOutputPanel.kt │ │ ├── XMakeToolWindowProblemPanel.kt │ │ ├── directory │ │ │ └── ui │ │ │ │ └── DirectoryBrowser.kt │ │ ├── target │ │ │ └── TargetManager.kt │ │ ├── toolkit │ │ │ ├── ActivatedToolkit.kt │ │ │ ├── Toolkit.kt │ │ │ ├── ToolkitChangedNotifier.kt │ │ │ ├── ToolkitHost.kt │ │ │ ├── ToolkitHostType.kt │ │ │ ├── ToolkitManager.kt │ │ │ └── ui │ │ │ │ ├── ToolkitComboBox.kt │ │ │ │ ├── ToolkitComboBoxRenderer.kt │ │ │ │ └── ToolkitListItem.kt │ │ └── wizard │ │ │ ├── NewProjectWizardDirectoryGeneratorAdapter.kt │ │ │ ├── XMakeGeneratorNewProjectWizard.kt │ │ │ ├── XMakeNewProjectWizardData.kt │ │ │ ├── XMakeProjectDirectoryGenerator.kt │ │ │ ├── XMakeProjectModuleBuilder.kt │ │ │ └── XMakeProjectWizardStep.kt │ │ ├── run │ │ ├── XMakeDefaultRunner.kt │ │ ├── XMakeRunConfiguration.kt │ │ ├── XMakeRunConfigurationEditor.kt │ │ ├── XMakeRunConfigurationProducer.kt │ │ ├── XMakeRunConfigurationType.kt │ │ └── XMakeRunner.kt │ │ ├── shared │ │ ├── XMakeConfiguration.kt │ │ └── XMakeProblem.kt │ │ └── utils │ │ ├── Command.kt │ │ ├── SystemUtils.kt │ │ ├── exception │ │ ├── XMakeRunConfigurationNotSetException.kt │ │ └── XMakeToolkitNotSetException.kt │ │ ├── execute │ │ ├── CommandEx.kt │ │ ├── Instruction.kt │ │ ├── SftpChannelEx.kt │ │ └── Sync.kt │ │ ├── extension │ │ ├── SshToolkitHostExtensionImpl.kt │ │ └── ToolkitHostExtension.kt │ │ ├── info │ │ ├── XMakeInfo.kt │ │ ├── XMakeInfoActivity.kt │ │ ├── XMakeInfoInstance.kt │ │ ├── XMakeInfoManager.kt │ │ └── XMakeInfoTypes.kt │ │ └── interact │ │ ├── LocalData.kt │ │ └── XMakeData.kt └── resources │ ├── META-INF │ ├── io.xmake-ssh.xml │ ├── plugin.xml │ └── pluginIcon.svg │ └── icons │ ├── xmake-dark-fill-translucent.svg │ ├── xmake-dark.svg │ └── xmake.svg └── test └── kotlin └── io └── xmake ├── TestData.kt └── utils └── interact └── InteractTest.kt /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | *.vsix 4 | .DS_Store 5 | *.swp 6 | *.swo 7 | *.jar 8 | build/ 9 | deps/ 10 | .gradle 11 | .idea 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | If you discover issues, have ideas for improvements or new features, or 4 | want to contribute a new module, please report them to the 5 | [issue tracker][1] of the repository or submit a pull request. Please, 6 | try to follow these guidelines when you do so. 7 | 8 | ## Issue reporting 9 | 10 | * Check that the issue has not already been reported. 11 | * Check that the issue has not already been fixed in the latest code 12 | (a.k.a. `master`). 13 | * Be clear, concise and precise in your description of the problem. 14 | * Open an issue with a descriptive title and a summary in grammatically correct, 15 | complete sentences. 16 | * Include any relevant code to the issue summary. 17 | 18 | ## Pull requests 19 | 20 | * Use a topic branch to easily amend a pull request later, if necessary. 21 | * Write good commit messages. 22 | * Use the same coding conventions as the rest of the project. 23 | * Ensure your edited codes with four spaces instead of TAB. 24 | * Please commit code to `dev` branch and we will merge into `master` branch in feature 25 | 26 | ## Financial contributions 27 | 28 | We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/xmake). 29 | Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed. 30 | 31 | ## Credits 32 | 33 | ### Backers 34 | 35 | Thank you to all our backers! [[Become a backer](https://opencollective.com/xmake#backer)] 36 | 37 | 38 | 39 | ### Sponsors 40 | 41 | Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/xmake#sponsor)) 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | # 贡献代码 55 | 56 | 如果你发现一些问题,或者想新增或者改进某些新特性,或者想贡献一个新的模块 57 | 那么你可以在[issues][1]上提交反馈,或者发起一个提交代码的请求(pull request). 58 | 59 | ## 问题反馈 60 | 61 | * 确认这个问题没有被反馈过 62 | * 确认这个问题最近还没有被修复,请先检查下 `master` 的最新提交 63 | * 请清晰详细地描述你的问题 64 | * 如果发现某些代码存在问题,请在issue上引用相关代码 65 | 66 | ## 安装环境 67 | 68 | #### 安装cnpm 69 | 70 | ```console 71 | $ npm install -g cnpm --registry=https://registry.npm.taobao.org 72 | ``` 73 | 74 | #### 创建空工程 75 | 76 | ```console 77 | $ cnpm install -g yo generator-code 78 | $ yo code 79 | ``` 80 | 81 | #### 创建发布者 82 | 83 | ```console 84 | $ cnpm install -g vsce 85 | $ vsce create-publisher (publisher name) 86 | $ vsce login (publisher name) 87 | ``` 88 | 89 | #### 构建发布 90 | 91 | ```console 92 | $ vsce package 93 | $ vsce publish [version] 94 | ``` 95 | 96 | ## 提交代码 97 | 98 | * 请先更新你的本地分支到最新,再进行提交代码请求,确保没有合并冲突 99 | * 编写友好可读的提交信息 100 | * 请使用余工程代码相同的代码规范 101 | * 确保提交的代码缩进是四个空格,而不是tab 102 | * 请提交代码到`dev`分支,如果通过,我们会在特定时间合并到`master`分支上 103 | * 为了规范化提交日志的格式,commit消息,不要用中文,请用英文描述 104 | 105 | [1]: https://github.com/xmake-io/xmake-vscode/issues 106 | 107 | ## 支持项目 108 | 109 | xmake项目属于个人开源项目,它的发展需要您的帮助,如果您愿意支持xmake项目的开发,欢迎为其捐赠,支持它的发展。 🙏 [[支持此项目](https://opencollective.com/xmake#backer)] 110 | 111 | 112 | 113 | ## 赞助项目 114 | 115 | 通过赞助支持此项目,您的logo和网站链接将显示在这里。[[赞助此项目](https://opencollective.com/xmake#sponsor)] 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 |

xmake-idea

7 | 8 |
9 | 10 | Version 11 | 12 | 13 | Downloads 14 | 15 | 16 | license 17 | 18 |
19 |
20 | 21 | license 22 | 23 | 24 | Reddit 25 | 26 | 27 | Gitter 28 | 29 | 30 | Telegram 31 | 32 | 33 | QQ 34 | 35 | 36 | Donate 37 | 38 |
39 | 40 |

A XMake integration in IntelliJ IDEA

41 |
42 | 43 | ## Introduction 44 | 45 | A XMake integration in IntelliJ IDEA. 46 | 47 | It is deeply integrated with [xmake](https://github.com/xmake-io/xmake) and Intellij-IDEA to provide a convenient and fast cross-platform c/c++ development and building. 48 | 49 | And It also support other Intellij-based platform, like Clion, Android Studio and etc. 50 | 51 | You need install [xmake](https://github.com/xmake-io/xmake) first and a project with `xmake.lua`. 52 | 53 | Please see [xmake-github](https://github.com/xmake-io/xmake) and [website](http://xmake.io) if you want to known more about xmake. 54 | 55 | ## Features 56 | 57 | * Quickstart 58 | * Create project 59 | * Project configuration 60 | * Run configuration 61 | * Menu tools 62 | * Tool windows 63 | * Build and run 64 | * Parse errors and goto file 65 | * C/C++ intellisense 66 | * Debug 67 | 68 | ## Quickstart 69 | 70 |
71 | 72 |
73 | 74 | ## Parse errors and goto file 75 | 76 |
77 | 78 |
79 | 80 | ## Output panel 81 | 82 | 83 | 84 | ## Create project 85 | 86 | 87 | 88 | ## Project configuration 89 | 90 | 91 | 92 | ## Run configuration 93 | 94 | 95 | 96 | ## Menu tools 97 | 98 |
99 | 100 |
101 | 102 | ## C/C++ intellisense 103 | 104 | > Only support CLion (>= 2020.1) 105 | 106 | 1. Click "Update compile commands" to create or update "compile_commands.json" file 107 | 2. Click "File > open..." to choose this file. 108 | 109 | ## Debug 110 | 111 | > Only support Clion (>= 2020.1) 112 | 113 | 1. Click "Update CmakeLists" to create or update "CmakeLists.txt" file. 114 | 2. Click "File > open..." to choose this file. 115 | 3. Choose "Run > Debug..." or "Run > Debug 'project name'" into debug mode. 116 | 117 | ## How to contribute? 118 | 119 | Due to limited personal time, I cannot maintain this plug-in all the time. If you encounter problems, you are welcome to download the plug-in source code to debug it yourself and open pr to contribute. 120 | 121 | ### Build this project 122 | 123 | Use IDEA Intellji open this project source code, and click `Build` button. 124 | 125 | ### Run and debug this project 126 | 127 | Open and edit `Run configuration`, and add a gradle run configuration, then write run arguments: `runIde --stacktrace` and save it. 128 | 129 | 130 | 131 | Select this run configuration and click run button to load it. 132 | 133 | 134 | 135 | For more details, please visit: [CONTRIBUTING](https://github.com/xmake-io/xmake-idea/blob/master/CONTRIBUTING.md) 136 | 137 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType 2 | 3 | fun properties(key: String) = project.findProperty(key).toString() 4 | 5 | // read local workspace file to string 6 | val localChangeNotes: String = file("${projectDir}/change-notes.html").readText(Charsets.UTF_8) 7 | val localDescription: String = file("${projectDir}/description.html").readText(Charsets.UTF_8) 8 | 9 | val type = mapOf( 10 | "IC" to "ideaIC", 11 | "IU" to "ideaIU", 12 | "CL" to "clion", 13 | "PY" to "pycharmPY" 14 | ) 15 | 16 | /* 17 | * Best practice: 18 | * Use CL for both building and running. 19 | * If you lack a license, use CLI for building and IC for running. 20 | * Specify the ideDir path as needed. 21 | * */ 22 | 23 | val buildIdeType: String = when (2) { 24 | 0 -> "IC" // SSH-related functions cannot be built by the Community version. 25 | 1 -> "IU" // To build with Ultimate version does not require a license. 26 | 2 -> "CL" // C/C++ intellij-sense is included. 27 | 3 -> "PY" 28 | else -> "IC" 29 | } 30 | 31 | val buildIdeVersion = "2025.1" 32 | 33 | val runIdeType: String = when (2) { 34 | 0 -> "IC" // You can build with the Ultimate version, but run with the Community version. 35 | 1 -> "IU" // It may require a license to run with the Ultimate version. 36 | 2 -> "CL" // It includes C/C++ related functions, along with functions in the Ultimate version. 37 | 3 -> "PY" 38 | else -> "IC" 39 | } 40 | 41 | val runIdeVersion = "2025.1" 42 | 43 | plugins { 44 | id("java") 45 | id("org.jetbrains.intellij.platform") version "2.5.0" 46 | id("org.jetbrains.kotlin.jvm") version "2.1.0" 47 | id("org.jetbrains.changelog") version "2.2.0" 48 | kotlin("plugin.serialization") version "2.1.0" 49 | } 50 | 51 | group = "io.xmake" 52 | 53 | repositories { 54 | maven("https://maven.aliyun.com/repository/public/") 55 | maven("https://oss.sonatype.org/content/repositories/snapshots/") 56 | mavenLocal() 57 | mavenCentral() 58 | gradlePluginPortal() 59 | intellijPlatform { 60 | defaultRepositories() 61 | } 62 | } 63 | 64 | intellijPlatform{ 65 | 66 | pluginConfiguration { 67 | version = properties("pluginVersion") 68 | changeNotes = localChangeNotes 69 | description = localDescription 70 | ideaVersion { 71 | sinceBuild = properties("pluginSinceBuild") 72 | untilBuild = properties("pluginUntilBuild") 73 | } 74 | } 75 | 76 | pluginVerification { 77 | ides{ 78 | select { 79 | types = listOf( 80 | IntelliJPlatformType.CLion, 81 | IntelliJPlatformType.IntellijIdeaUltimate, 82 | IntelliJPlatformType.IntellijIdeaCommunity 83 | ) 84 | sinceBuild = "243" 85 | untilBuild = "251.*" 86 | } 87 | } 88 | } 89 | } 90 | 91 | tasks { 92 | test { 93 | useJUnit() 94 | include("io/xmake/**/**") 95 | } 96 | } 97 | 98 | dependencies { 99 | implementation("org.jetbrains.kotlin:kotlin-stdlib:2.1.0") 100 | implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") 101 | testImplementation("io.mockk:mockk:1.13.12") 102 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") 103 | intellijPlatform { 104 | create(runIdeType, runIdeVersion) 105 | bundledPlugin("com.intellij.clion") 106 | } 107 | } 108 | 109 | val Project.dependencyCachePath 110 | get(): String { 111 | val cachePath = file("${rootProject.projectDir}/deps") 112 | if (!cachePath.exists()) { 113 | cachePath.mkdirs() 114 | } 115 | return cachePath.absolutePath 116 | } -------------------------------------------------------------------------------- /change-notes.html: -------------------------------------------------------------------------------- 1 | 1.4.6 2 | 5 | 1.4.5 6 | 9 | 1.4.3 10 | 13 | 1.4.2 14 | 17 | 1.4.1 18 | 21 | 1.4.0 22 | 25 | 1.3.9 26 | 29 | 1.3.8 30 | 33 | 1.3.7 34 | 37 | 1.3.6 38 | 41 | 1.3.5 42 | 45 | 1.3.4 46 | 50 | 1.3.3 51 | 56 | 1.3.2 57 | 60 | 1.3.1 61 | 64 | 1.3.0 65 | 68 | 1.2.3 69 | 72 | 1.2.2 73 | 76 | 1.2.1 77 | 80 | 1.2.0 81 | 84 | 1.1.9 85 | 88 | 1.1.8 89 | 92 | 1.1.7 93 | 96 | 1.1.6 97 | 100 | 1.1.5 101 | 104 | 1.1.4 105 | 108 | 1.1.3 109 | 112 | 1.1.2 113 | 116 | 1.1.1 117 | 120 | 1.1.0 121 | 124 | 1.0.9 125 | 128 | 1.0.8 129 | 132 | 1.0.7 133 | 136 | 1.0.6 137 | 140 | 1.0.5 141 | 144 | 1.0.4 145 | 148 | 1.0.3 149 | 152 | 1.0.2 153 | 156 | 1.0.1 157 | 166 | -------------------------------------------------------------------------------- /description.html: -------------------------------------------------------------------------------- 1 |
A XMake integration plugin in Intellij Platform
2 |

Source Code | Discord | Donate | XMake Homepage

3 | Features: 4 | 21 | Usage instruction:

22 | 1. New project
23 | New -> Project -> Select Xmake -> Select project template
24 |
25 | 2. Open existing project
26 | Open -> Select project folder with xmake.lua
27 |
28 | 3. Build project
29 | Menu -> Xmake -> Click 'Build Project'
30 |
31 | 4. Run Target
32 | Add run configuration -> Select Xmake -> Add run target 33 | Menu -> Xmake -> Click 'Run Target'
34 |

中国朋友可以加QQ群交流及反馈BUG: 343118190

-------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | pluginVersion=1.4.6 2 | pluginSinceBuild=243.21565.193 3 | pluginUntilBuild=251.* 4 | 5 | # Opt-out flag for bundling Kotlin standard library. 6 | # See https://kotlinlang.org/docs/reference/using-gradle.html#dependency-on-the-standard-library for details. 7 | kotlin.stdlib.default.dependency=false 8 | 9 | # proxy 10 | # systemProp.http.proxyHost=127.0.0.1 11 | # systemProp.http.proxyPort=7890 12 | # systemProp.https.proxyHost=127.0.0.1 13 | # systemProp.https.proxyPort=7890 14 | 15 | # testing in clion 16 | testInClion=false 17 | 18 | 19 | org.gradle.warning.mode=all 20 | kotlin.daemon.jvmargs=-Xmx2048m 21 | 22 | # Gradle Releases -> https://github.com/gradle/gradle/releases 23 | gradleVersion = 8.5 24 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /res/create_project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmake-io/xmake-idea/313a6fbaa3ac35ed35b03a7c1ea85749dc29135b/res/create_project.png -------------------------------------------------------------------------------- /res/edit_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmake-io/xmake-idea/313a6fbaa3ac35ed35b03a7c1ea85749dc29135b/res/edit_configuration.png -------------------------------------------------------------------------------- /res/logo256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmake-io/xmake-idea/313a6fbaa3ac35ed35b03a7c1ea85749dc29135b/res/logo256.png -------------------------------------------------------------------------------- /res/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmake-io/xmake-idea/313a6fbaa3ac35ed35b03a7c1ea85749dc29135b/res/menu.png -------------------------------------------------------------------------------- /res/output_panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmake-io/xmake-idea/313a6fbaa3ac35ed35b03a7c1ea85749dc29135b/res/output_panel.png -------------------------------------------------------------------------------- /res/problem.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmake-io/xmake-idea/313a6fbaa3ac35ed35b03a7c1ea85749dc29135b/res/problem.gif -------------------------------------------------------------------------------- /res/project_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmake-io/xmake-idea/313a6fbaa3ac35ed35b03a7c1ea85749dc29135b/res/project_configuration.png -------------------------------------------------------------------------------- /res/quickstart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmake-io/xmake-idea/313a6fbaa3ac35ed35b03a7c1ea85749dc29135b/res/quickstart.gif -------------------------------------------------------------------------------- /res/run_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmake-io/xmake-idea/313a6fbaa3ac35ed35b03a7c1ea85749dc29135b/res/run_configuration.png -------------------------------------------------------------------------------- /res/run_plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmake-io/xmake-idea/313a6fbaa3ac35ed35b03a7c1ea85749dc29135b/res/run_plugin.png -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "xmake-idea" -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/actions/BuildAction.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.actions 2 | 3 | import com.intellij.execution.process.ProcessAdapter 4 | import com.intellij.execution.process.ProcessEvent 5 | import com.intellij.execution.ui.ConsoleViewContentType 6 | import com.intellij.notification.NotificationGroupManager 7 | import com.intellij.notification.NotificationType 8 | import com.intellij.openapi.actionSystem.AnAction 9 | import com.intellij.openapi.actionSystem.AnActionEvent 10 | import io.xmake.project.xmakeConsoleView 11 | import io.xmake.shared.xmakeConfiguration 12 | import io.xmake.utils.SystemUtils 13 | import io.xmake.utils.exception.XMakeRunConfigurationNotSetException 14 | 15 | class BuildAction : AnAction() { 16 | 17 | override fun actionPerformed(e: AnActionEvent) { 18 | 19 | // the project 20 | val project = e.project ?: return 21 | 22 | // clear console first 23 | project.xmakeConsoleView.clear() 24 | 25 | try { 26 | // configure and build it 27 | val xmakeConfiguration = project.xmakeConfiguration 28 | if (xmakeConfiguration.changed) { 29 | SystemUtils.runvInConsole(project, xmakeConfiguration.configurationCommandLine) 30 | ?.addProcessListener(object : ProcessAdapter() { 31 | override fun processTerminated(e: ProcessEvent) { 32 | SystemUtils.runvInConsole(project, xmakeConfiguration.buildCommandLine, false, true, true) 33 | } 34 | }) 35 | xmakeConfiguration.changed = false 36 | } else { 37 | SystemUtils.runvInConsole(project, xmakeConfiguration.buildCommandLine, true, true, true) 38 | } 39 | } catch (e: XMakeRunConfigurationNotSetException) { 40 | project.xmakeConsoleView.print( 41 | "Please select a xmake run configuration first!\n", 42 | ConsoleViewContentType.ERROR_OUTPUT 43 | ) 44 | NotificationGroupManager.getInstance() 45 | .getNotificationGroup("XMake.NotificationGroup") 46 | .createNotification("Error with XMake Configuration", e.message ?: "", NotificationType.ERROR) 47 | .notify(project) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/actions/CleanAction.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.actions 2 | 3 | import com.intellij.execution.process.ProcessAdapter 4 | import com.intellij.execution.process.ProcessEvent 5 | import com.intellij.execution.ui.ConsoleViewContentType 6 | import com.intellij.notification.NotificationGroupManager 7 | import com.intellij.notification.NotificationType 8 | import com.intellij.openapi.actionSystem.AnAction 9 | import com.intellij.openapi.actionSystem.AnActionEvent 10 | import io.xmake.project.xmakeConsoleView 11 | import io.xmake.shared.xmakeConfiguration 12 | import io.xmake.utils.SystemUtils 13 | import io.xmake.utils.exception.XMakeRunConfigurationNotSetException 14 | 15 | class CleanAction : AnAction() { 16 | 17 | override fun actionPerformed(e: AnActionEvent) { 18 | 19 | // the project 20 | val project = e.project ?: return 21 | 22 | // clear console first 23 | project.xmakeConsoleView.clear() 24 | 25 | try { 26 | // configure and clean it 27 | val xmakeConfiguration = project.xmakeConfiguration 28 | if (xmakeConfiguration.changed) { 29 | SystemUtils.runvInConsole(project, xmakeConfiguration.configurationCommandLine) 30 | ?.addProcessListener(object : ProcessAdapter() { 31 | override fun processTerminated(e: ProcessEvent) { 32 | SystemUtils.runvInConsole(project, xmakeConfiguration.cleanCommandLine, false, false, true) 33 | } 34 | }) 35 | xmakeConfiguration.changed = false 36 | } else { 37 | SystemUtils.runvInConsole(project, xmakeConfiguration.cleanCommandLine, true, false, true) 38 | } 39 | } catch (e: XMakeRunConfigurationNotSetException) { 40 | project.xmakeConsoleView.print( 41 | "Please select a xmake run configuration first!\n", 42 | ConsoleViewContentType.ERROR_OUTPUT 43 | ) 44 | NotificationGroupManager.getInstance() 45 | .getNotificationGroup("XMake.NotificationGroup") 46 | .createNotification("Error with XMake Configuration", e.message ?: "", NotificationType.ERROR) 47 | .notify(project) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/actions/CleanConfigurationAction.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.actions 2 | 3 | import com.intellij.execution.ui.ConsoleViewContentType 4 | import com.intellij.notification.NotificationGroupManager 5 | import com.intellij.notification.NotificationType 6 | import com.intellij.openapi.actionSystem.AnAction 7 | import com.intellij.openapi.actionSystem.AnActionEvent 8 | import io.xmake.project.xmakeConsoleView 9 | import io.xmake.shared.xmakeConfiguration 10 | import io.xmake.utils.SystemUtils 11 | import io.xmake.utils.exception.XMakeRunConfigurationNotSetException 12 | 13 | class CleanConfigurationAction : AnAction() { 14 | 15 | override fun actionPerformed(e: AnActionEvent) { 16 | 17 | // the project 18 | val project = e.project ?: return 19 | 20 | // clear console first 21 | project.xmakeConsoleView.clear() 22 | 23 | try { 24 | // clear configure 25 | val xmakeConfiguration = project.xmakeConfiguration 26 | SystemUtils.runvInConsole(project, xmakeConfiguration.cleanConfigurationCommandLine, true, false, true) 27 | xmakeConfiguration.changed = false 28 | } catch (e: XMakeRunConfigurationNotSetException) { 29 | project.xmakeConsoleView.print( 30 | "Please select a xmake run configuration first!\n", 31 | ConsoleViewContentType.ERROR_OUTPUT 32 | ) 33 | NotificationGroupManager.getInstance() 34 | .getNotificationGroup("XMake.NotificationGroup") 35 | .createNotification("Error with XMake Configuration", e.message ?: "", NotificationType.ERROR) 36 | .notify(project) 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/actions/QuickStartAction.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.actions 2 | 3 | import com.intellij.execution.ui.ConsoleViewContentType 4 | import com.intellij.notification.NotificationGroupManager 5 | import com.intellij.notification.NotificationType 6 | import com.intellij.openapi.actionSystem.AnAction 7 | import com.intellij.openapi.actionSystem.AnActionEvent 8 | import io.xmake.project.xmakeConsoleView 9 | import io.xmake.shared.xmakeConfiguration 10 | import io.xmake.utils.SystemUtils 11 | import io.xmake.utils.exception.XMakeRunConfigurationNotSetException 12 | 13 | class QuickStartAction : AnAction() { 14 | 15 | override fun actionPerformed(e: AnActionEvent) { 16 | 17 | // the project 18 | val project = e.project ?: return 19 | 20 | // clear console first 21 | project.xmakeConsoleView.clear() 22 | 23 | try { 24 | // quick start 25 | SystemUtils.runvInConsole(project, project.xmakeConfiguration.quickStartCommandLine, true, false, true) 26 | } catch (e: XMakeRunConfigurationNotSetException) { 27 | project.xmakeConsoleView.print( 28 | "Please select a xmake run configuration first!\n", 29 | ConsoleViewContentType.ERROR_OUTPUT 30 | ) 31 | NotificationGroupManager.getInstance() 32 | .getNotificationGroup("XMake.NotificationGroup") 33 | .createNotification("Error with XMake Configuration", e.message ?: "", NotificationType.ERROR) 34 | .notify(project) 35 | } 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/actions/RebuildAction.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.actions 2 | 3 | import com.intellij.execution.process.ProcessAdapter 4 | import com.intellij.execution.process.ProcessEvent 5 | import com.intellij.execution.ui.ConsoleViewContentType 6 | import com.intellij.notification.NotificationGroupManager 7 | import com.intellij.notification.NotificationType 8 | import com.intellij.openapi.actionSystem.AnAction 9 | import com.intellij.openapi.actionSystem.AnActionEvent 10 | import io.xmake.project.xmakeConsoleView 11 | import io.xmake.shared.xmakeConfiguration 12 | import io.xmake.utils.SystemUtils 13 | import io.xmake.utils.exception.XMakeRunConfigurationNotSetException 14 | 15 | class RebuildAction : AnAction() { 16 | 17 | override fun actionPerformed(e: AnActionEvent) { 18 | 19 | // the project 20 | val project = e.project ?: return 21 | 22 | // clear console first 23 | project.xmakeConsoleView.clear() 24 | 25 | try { 26 | // configure and rebuild it 27 | val xmakeConfiguration = project.xmakeConfiguration 28 | if (xmakeConfiguration.changed) { 29 | SystemUtils.runvInConsole(project, xmakeConfiguration.configurationCommandLine) 30 | ?.addProcessListener(object : ProcessAdapter() { 31 | override fun processTerminated(e: ProcessEvent) { 32 | SystemUtils.runvInConsole(project, xmakeConfiguration.rebuildCommandLine, false, true, true) 33 | } 34 | }) 35 | xmakeConfiguration.changed = false 36 | } else { 37 | SystemUtils.runvInConsole(project, xmakeConfiguration.rebuildCommandLine, true, true, true) 38 | } 39 | } catch (e: XMakeRunConfigurationNotSetException) { 40 | project.xmakeConsoleView.print( 41 | "Please select a xmake run configuration first!\n", 42 | ConsoleViewContentType.ERROR_OUTPUT 43 | ) 44 | NotificationGroupManager.getInstance() 45 | .getNotificationGroup("XMake.NotificationGroup") 46 | .createNotification("Error with XMake Configuration", e.message ?: "", NotificationType.ERROR) 47 | .notify(project) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/actions/RunAction.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.actions 2 | 3 | import com.intellij.execution.process.ProcessAdapter 4 | import com.intellij.execution.process.ProcessEvent 5 | import com.intellij.execution.ui.ConsoleViewContentType 6 | import com.intellij.notification.NotificationGroupManager 7 | import com.intellij.notification.NotificationType 8 | import com.intellij.openapi.actionSystem.AnAction 9 | import com.intellij.openapi.actionSystem.AnActionEvent 10 | import io.xmake.project.xmakeConsoleView 11 | import io.xmake.shared.xmakeConfiguration 12 | import io.xmake.utils.SystemUtils 13 | import io.xmake.utils.exception.XMakeRunConfigurationNotSetException 14 | 15 | class RunAction : AnAction() { 16 | 17 | override fun actionPerformed(e: AnActionEvent) { 18 | 19 | // the project 20 | val project = e.project ?: return 21 | 22 | // clear console first 23 | project.xmakeConsoleView.clear() 24 | 25 | try { 26 | // configure and run it 27 | val xmakeConfiguration = project.xmakeConfiguration 28 | if (xmakeConfiguration.changed) { 29 | SystemUtils.runvInConsole(project, xmakeConfiguration.configurationCommandLine) 30 | ?.addProcessListener(object : ProcessAdapter() { 31 | override fun processTerminated(e: ProcessEvent) { 32 | SystemUtils.runvInConsole( 33 | project, 34 | xmakeConfiguration.configuration.runCommandLine, 35 | false, 36 | true, 37 | true 38 | ) 39 | } 40 | }) 41 | xmakeConfiguration.changed = false 42 | } else { 43 | SystemUtils.runvInConsole(project, xmakeConfiguration.configuration.runCommandLine, true, true, true) 44 | } 45 | 46 | } catch (e: XMakeRunConfigurationNotSetException) { 47 | project.xmakeConsoleView.print( 48 | "Please select a xmake run configuration first!\n", 49 | ConsoleViewContentType.ERROR_OUTPUT 50 | ) 51 | NotificationGroupManager.getInstance() 52 | .getNotificationGroup("XMake.NotificationGroup") 53 | .createNotification("Error with XMake Configuration", e.message ?: "", NotificationType.ERROR) 54 | .notify(project) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/actions/UpdateCmakeListsAction.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.actions 2 | 3 | import com.intellij.execution.process.ProcessAdapter 4 | import com.intellij.execution.process.ProcessEvent 5 | import com.intellij.execution.ui.ConsoleViewContentType 6 | import com.intellij.notification.NotificationGroupManager 7 | import com.intellij.notification.NotificationType 8 | import com.intellij.openapi.actionSystem.AnAction 9 | import com.intellij.openapi.actionSystem.AnActionEvent 10 | import io.xmake.project.toolkit.activatedToolkit 11 | import io.xmake.project.xmakeConsoleView 12 | import io.xmake.shared.xmakeConfiguration 13 | import io.xmake.utils.SystemUtils 14 | import io.xmake.utils.exception.XMakeRunConfigurationNotSetException 15 | import io.xmake.utils.execute.fetchGeneratedFile 16 | import io.xmake.utils.execute.syncBeforeFetch 17 | 18 | class UpdateCmakeListsAction : AnAction() { 19 | override fun actionPerformed(e: AnActionEvent) { 20 | // the project 21 | val project = e.project ?: return 22 | 23 | // clear console first 24 | project.xmakeConsoleView.clear() 25 | 26 | try { 27 | // configure and build it 28 | val xmakeConfiguration = project.xmakeConfiguration 29 | if (xmakeConfiguration.changed) { 30 | SystemUtils.runvInConsole(project, xmakeConfiguration.configurationCommandLine) 31 | ?.addProcessListener(object : ProcessAdapter() { 32 | override fun processTerminated(e: ProcessEvent) { 33 | syncBeforeFetch(project, project.activatedToolkit!!) 34 | 35 | SystemUtils.runvInConsole( 36 | project, 37 | xmakeConfiguration.updateCmakeListsCommandLine, 38 | false, 39 | true, 40 | true 41 | )?.addProcessListener( 42 | object : ProcessAdapter() { 43 | override fun processTerminated(e: ProcessEvent) { 44 | fetchGeneratedFile(project, project.activatedToolkit!!, "CMakeLists.txt") 45 | // Todo: Reload from disks after download from remote. 46 | } 47 | } 48 | ) 49 | } 50 | }) 51 | xmakeConfiguration.changed = false 52 | } else { 53 | SystemUtils.runvInConsole(project, xmakeConfiguration.updateCmakeListsCommandLine, false, true, true) 54 | ?.addProcessListener( 55 | object : ProcessAdapter() { 56 | override fun processTerminated(e: ProcessEvent) { 57 | fetchGeneratedFile(project, project.activatedToolkit!!, "CMakeLists.txt") 58 | } 59 | } 60 | ) 61 | } 62 | } catch (e: XMakeRunConfigurationNotSetException) { 63 | project.xmakeConsoleView.print( 64 | "Please select a xmake run configuration first!\n", 65 | ConsoleViewContentType.ERROR_OUTPUT 66 | ) 67 | NotificationGroupManager.getInstance() 68 | .getNotificationGroup("XMake.NotificationGroup") 69 | .createNotification("Error with XMake Configuration", e.message ?: "", NotificationType.ERROR) 70 | .notify(project) 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/actions/UpdateCompileCommandsAction.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.actions 2 | 3 | import com.intellij.execution.process.ProcessAdapter 4 | import com.intellij.execution.process.ProcessEvent 5 | import com.intellij.execution.ui.ConsoleViewContentType 6 | import com.intellij.notification.NotificationGroupManager 7 | import com.intellij.notification.NotificationType 8 | import com.intellij.openapi.actionSystem.AnAction 9 | import com.intellij.openapi.actionSystem.AnActionEvent 10 | import io.xmake.project.toolkit.activatedToolkit 11 | import io.xmake.project.xmakeConsoleView 12 | import io.xmake.shared.xmakeConfiguration 13 | import io.xmake.utils.SystemUtils 14 | import io.xmake.utils.exception.XMakeRunConfigurationNotSetException 15 | import io.xmake.utils.execute.fetchGeneratedFile 16 | import io.xmake.utils.execute.syncBeforeFetch 17 | 18 | class UpdateCompileCommandsAction : AnAction() { 19 | override fun actionPerformed(e: AnActionEvent) { 20 | // the project 21 | val project = e.project ?: return 22 | 23 | // clear console first 24 | project.xmakeConsoleView.clear() 25 | 26 | try { 27 | // configure and build it 28 | val xmakeConfiguration = project.xmakeConfiguration 29 | if (xmakeConfiguration.changed) { 30 | SystemUtils.runvInConsole(project, xmakeConfiguration.configurationCommandLine) 31 | ?.addProcessListener(object : ProcessAdapter() { 32 | override fun processTerminated(e: ProcessEvent) { 33 | syncBeforeFetch(project, project.activatedToolkit!!) 34 | 35 | SystemUtils.runvInConsole( 36 | project, 37 | xmakeConfiguration.updateCompileCommandsLine, 38 | false, 39 | true, 40 | true 41 | ) 42 | ?.addProcessListener( 43 | object : ProcessAdapter() { 44 | override fun processTerminated(e: ProcessEvent) { 45 | fetchGeneratedFile( 46 | project, 47 | project.activatedToolkit!!, 48 | "compile_commands.json" 49 | ) 50 | // Todo: Reload from disks after download from remote. 51 | } 52 | } 53 | ) 54 | } 55 | }) 56 | xmakeConfiguration.changed = false 57 | } else { 58 | SystemUtils.runvInConsole(project, xmakeConfiguration.updateCompileCommandsLine, false, true, true) 59 | ?.addProcessListener( 60 | object : ProcessAdapter() { 61 | override fun processTerminated(e: ProcessEvent) { 62 | fetchGeneratedFile(project, project.activatedToolkit!!, "compile_commands.json") 63 | } 64 | } 65 | ) 66 | } 67 | } catch (e: XMakeRunConfigurationNotSetException) { 68 | project.xmakeConsoleView.print( 69 | "Please select a xmake run configuration first!\n", 70 | ConsoleViewContentType.ERROR_OUTPUT 71 | ) 72 | NotificationGroupManager.getInstance() 73 | .getNotificationGroup("XMake.NotificationGroup") 74 | .createNotification("Error with XMake Configuration", e.message ?: "", NotificationType.ERROR) 75 | .notify(project) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/file/XMakeLuaFileChangeListener.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.file 2 | 3 | import com.intellij.openapi.fileEditor.FileDocumentManager 4 | import com.intellij.psi.PsiFile 5 | import com.intellij.psi.PsiTreeAnyChangeAbstractAdapter 6 | 7 | class XMakeLuaFileChangeListener : PsiTreeAnyChangeAbstractAdapter() { 8 | private val fileDocumentManager = FileDocumentManager.getInstance() 9 | 10 | override fun onChange(file: PsiFile?) { 11 | file?.let { 12 | if (XMakeLuaFileType.isFileOfType(file.virtualFile)) { 13 | println("${file.name} on change") 14 | fileDocumentManager.saveDocument(it.fileDocument) 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/file/XMakeLuaFileType.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.file 2 | 3 | import com.intellij.lang.Language 4 | import com.intellij.openapi.fileTypes.FileTypeManager 5 | import com.intellij.openapi.fileTypes.LanguageFileType 6 | import com.intellij.openapi.fileTypes.PlainTextLanguage 7 | import com.intellij.openapi.vfs.VirtualFile 8 | import io.xmake.icons.XMakeIcons 9 | import org.jetbrains.annotations.Contract 10 | import javax.swing.Icon 11 | 12 | object XMakeLuaFileType : LanguageFileType(XMakeLuaLanguage) { 13 | 14 | const val FILE_NAME: String = "xmake.lua" 15 | val INSTANCE: XMakeLuaFileType = XMakeLuaFileType 16 | 17 | override fun getName(): String { 18 | return FILE_NAME 19 | } 20 | 21 | override fun getDescription(): String { 22 | return "XMake Lua file" 23 | } 24 | 25 | override fun getDefaultExtension(): String { 26 | return "lua" 27 | } 28 | 29 | override fun getIcon(): Icon { 30 | return XMakeIcons.FILE 31 | } 32 | 33 | override fun getDisplayName(): String { 34 | return "XMake Lua" 35 | } 36 | 37 | 38 | private fun findLanguage(): Language { 39 | return Language.findLanguageByID("xmake.lua") ?: PlainTextLanguage.INSTANCE 40 | } 41 | 42 | @Contract("null->false") 43 | fun isFileOfType(file: VirtualFile?): Boolean { 44 | return file != null && FileTypeManager.getInstance().isFileOfType(file, INSTANCE) 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/file/XMakeLuaLanguage.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.file 2 | 3 | import com.intellij.lang.Language 4 | 5 | object XMakeLuaLanguage : Language("xmake.lua") { 6 | private fun readResolve(): Any = XMakeLuaLanguage 7 | val INSTANCE: XMakeLuaLanguage = XMakeLuaLanguage 8 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/icons/XMakeIconProvider.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.icons 2 | 3 | import com.intellij.ide.IconProvider 4 | import com.intellij.psi.PsiElement 5 | import javax.swing.Icon 6 | 7 | class XMakeIconProvider : IconProvider() { 8 | 9 | override fun getIcon(element: PsiElement, flags: Int): Icon? = when { 10 | element.containingFile?.name == "xmake.lua" -> XMakeIcons.FILE 11 | else -> null 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/icons/XMakeIcons.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.icons 2 | 3 | import com.intellij.icons.AllIcons 4 | import com.intellij.openapi.util.IconLoader 5 | import javax.swing.Icon 6 | 7 | object XMakeIcons { 8 | 9 | // logo icon 10 | val XMAKE = load("/icons/xmake.svg") 11 | 12 | // file icon 13 | val FILE = load("/icons/xmake.svg") 14 | 15 | // error icon 16 | val ERROR = AllIcons.General.Error 17 | 18 | // warning icon 19 | val WARNING = AllIcons.General.Warning 20 | 21 | private fun load(path: String): Icon = IconLoader.getIcon(path, XMakeIcons::class.java) 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/XMakeModuleConfigurationEditorProvider.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project 2 | 3 | import com.intellij.openapi.diagnostic.Logger 4 | import com.intellij.openapi.module.ModuleConfigurationEditor 5 | //import com.intellij.openapi.roots.ui.configuration.DefaultModuleConfigurationEditorFactory 6 | import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationEditorProvider 7 | import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationState 8 | 9 | class XMakeModuleConfigurationEditorProvider : ModuleConfigurationEditorProvider { 10 | 11 | override fun createEditors(moduleConfigurationState: ModuleConfigurationState): Array { 12 | 13 | var editors = arrayOf() 14 | // val factory = DefaultModuleConfigurationEditorFactory.getInstance() 15 | 16 | /* 17 | editors += factory.createModuleContentRootsEditor(moduleConfigurationState) 18 | editors += factory.createClasspathEditor(moduleConfigurationState) 19 | */ 20 | return editors 21 | } 22 | 23 | companion object { 24 | private val Log = Logger.getInstance(XMakeModuleConfigurationEditorProvider::class.java.getName()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/XMakeProjectToolkitConfigurable.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project 2 | 3 | import com.intellij.openapi.options.Configurable 4 | import com.intellij.openapi.ui.Messages 5 | import com.intellij.ui.ColoredListCellRenderer 6 | import com.intellij.ui.SimpleTextAttributes 7 | import com.intellij.ui.ToolbarDecorator 8 | import com.intellij.ui.components.JBList 9 | import com.intellij.ui.dsl.builder.Align 10 | import com.intellij.ui.dsl.builder.panel 11 | import io.xmake.project.toolkit.ToolkitManager 12 | import io.xmake.project.toolkit.ui.ToolkitListItem 13 | import javax.swing.DefaultListModel 14 | import javax.swing.JComponent 15 | import javax.swing.JList 16 | 17 | class XMakeProjectToolkitConfigurable : Configurable, Configurable.NoScroll { 18 | 19 | private val toolkitManager = ToolkitManager.getInstance() 20 | 21 | override fun createComponent(): JComponent { 22 | val registeredToolkit = toolkitManager.getRegisteredToolkits() 23 | val listModel = DefaultListModel().apply { registeredToolkit.forEach { 24 | addElement(ToolkitListItem.ToolkitItem(it).asRegistered()) } 25 | } 26 | val toolkitList = JBList(listModel).apply { 27 | 28 | cellRenderer = object : ColoredListCellRenderer() { 29 | override fun customizeCellRenderer( 30 | list: JList, 31 | value: ToolkitListItem?, 32 | index: Int, 33 | selected: Boolean, 34 | hasFocus: Boolean, 35 | ) { 36 | 37 | isOpaque = false 38 | icon = value?.icon 39 | val text = value?.let { value.text } ?: "" 40 | val secondaryText = value?.secondaryText 41 | val tertiaryText = value?.tertiaryText 42 | 43 | append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES) 44 | if (secondaryText != null) 45 | append(" - $secondaryText ", SimpleTextAttributes.REGULAR_ITALIC_ATTRIBUTES) 46 | if (tertiaryText != null) 47 | append(" $tertiaryText", SimpleTextAttributes.GRAYED_ATTRIBUTES) 48 | } 49 | } 50 | } 51 | val decorator = ToolbarDecorator.createDecorator(toolkitList) 52 | 53 | decorator.setRemoveAction { 54 | val toolkit = (toolkitList.selectedValue as ToolkitListItem.ToolkitItem).toolkit 55 | if (Messages.showYesNoDialog( 56 | "Unregister ${toolkit.name}?", 57 | "Unregister Toolkit", 58 | Messages.getQuestionIcon() 59 | ) == Messages.YES 60 | ) { 61 | toolkitManager.unregisterToolkit(toolkit) 62 | listModel.removeElement(toolkitList.selectedValue) 63 | } 64 | } 65 | 66 | return panel { 67 | row { 68 | cell(decorator.createPanel()).align(Align.FILL) 69 | } 70 | } 71 | } 72 | 73 | override fun isModified(): Boolean { 74 | return false 75 | } 76 | 77 | override fun apply() { 78 | 79 | } 80 | 81 | override fun getDisplayName(): String { 82 | return "Xmake Project Toolkit" 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/XMakeToolWindowFactory.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project 2 | 3 | import com.intellij.execution.ui.ConsoleView 4 | import com.intellij.openapi.project.Project 5 | import com.intellij.openapi.wm.ToolWindow 6 | import com.intellij.openapi.wm.ToolWindowFactory 7 | import com.intellij.openapi.wm.ToolWindowManager 8 | import com.intellij.ui.content.ContentFactory 9 | import io.xmake.shared.XMakeProblem 10 | 11 | class XMakeToolWindowFactory : ToolWindowFactory { 12 | override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { 13 | 14 | // add output tab/panel 15 | val toolwindowOutputPanel = XMakeToolWindowOutputPanel(project) 16 | val outputTab = ContentFactory.getInstance().createContent(toolwindowOutputPanel, "Output", false) 17 | toolWindow.contentManager.addContent(outputTab) 18 | 19 | // add problem tab/panel 20 | val toolwindowProblemPanel = XMakeToolWindowProblemPanel(project) 21 | val problemTab = ContentFactory.getInstance().createContent(toolwindowProblemPanel, "Problem", false) 22 | toolWindow.contentManager.addContent(problemTab) 23 | 24 | // show the output panel by default 25 | toolWindow.contentManager.setSelectedContent(outputTab) 26 | } 27 | } 28 | 29 | // the xmake tool windows 30 | val Project.xmakeToolWindow: ToolWindow? 31 | get() = ToolWindowManager.getInstance(this).getToolWindow("XMake") 32 | 33 | // the xmake output panel 34 | val Project.xmakeOutputPanel: XMakeToolWindowOutputPanel 35 | get() = this.xmakeToolWindow?.contentManager?.getContent(0)?.component as XMakeToolWindowOutputPanel 36 | 37 | // the xmake problem panel 38 | val Project.xmakeProblemPanel: XMakeToolWindowProblemPanel 39 | get() = this.xmakeToolWindow?.contentManager?.getContent(1)?.component as XMakeToolWindowProblemPanel 40 | 41 | // the xmake console view 42 | val Project.xmakeConsoleView: ConsoleView 43 | get() = this.xmakeOutputPanel.consoleView 44 | 45 | // the xmake problem list 46 | var Project.xmakeProblemList: List 47 | get() = this.xmakeProblemPanel.problems 48 | set(value) { 49 | this.xmakeProblemPanel.problems = value 50 | } 51 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/XMakeToolWindowOutputPanel.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project 2 | 3 | import com.intellij.execution.filters.TextConsoleBuilderFactory 4 | import com.intellij.execution.ui.ConsoleView 5 | import com.intellij.openapi.actionSystem.ActionManager 6 | import com.intellij.openapi.actionSystem.ActionToolbar 7 | import com.intellij.openapi.actionSystem.DefaultActionGroup 8 | import com.intellij.openapi.diagnostic.Logger 9 | import com.intellij.openapi.project.Project 10 | import com.intellij.openapi.ui.SimpleToolWindowPanel 11 | 12 | class XMakeToolWindowOutputPanel(// the project 13 | val project: Project 14 | ) : SimpleToolWindowPanel(false) { 15 | 16 | // the toolbar 17 | val toolbar: ActionToolbar = run { 18 | val actionManager = ActionManager.getInstance() 19 | actionManager.createActionToolbar( 20 | "XMake Toolbar", 21 | actionManager.getAction("XMake.Menu") as DefaultActionGroup, 22 | false 23 | ) 24 | } 25 | 26 | // the console view 27 | val consoleView: ConsoleView = run { 28 | val builder = TextConsoleBuilderFactory.getInstance().createBuilder(project) 29 | builder.setViewer(true) 30 | builder.console 31 | } 32 | 33 | init { 34 | 35 | // init toolbar 36 | setToolbar(toolbar.component) 37 | toolbar.targetComponent = this 38 | 39 | // init content 40 | setContent(consoleView.component) 41 | } 42 | 43 | // show panel 44 | fun showPanel() { 45 | val contentManager = project.xmakeToolWindow?.contentManager 46 | contentManager?.setSelectedContent(contentManager.getContent(0)!!) 47 | } 48 | 49 | companion object { 50 | private val Log = Logger.getInstance(XMakeToolWindowOutputPanel::class.java.getName()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/XMakeToolWindowProblemPanel.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project 2 | 3 | import com.intellij.execution.RunManager 4 | import com.intellij.openapi.actionSystem.ActionManager 5 | import com.intellij.openapi.actionSystem.ActionToolbar 6 | import com.intellij.openapi.actionSystem.DefaultActionGroup 7 | import com.intellij.openapi.diagnostic.Logger 8 | import com.intellij.openapi.editor.markup.EffectType 9 | import com.intellij.openapi.editor.markup.TextAttributes 10 | import com.intellij.openapi.fileEditor.FileEditorManager 11 | import com.intellij.openapi.fileEditor.OpenFileDescriptor 12 | import com.intellij.openapi.project.Project 13 | import com.intellij.openapi.ui.SimpleToolWindowPanel 14 | import com.intellij.openapi.vfs.LocalFileSystem 15 | import com.intellij.ui.ColoredListCellRenderer 16 | import com.intellij.ui.JBColor 17 | import com.intellij.ui.SimpleTextAttributes 18 | import com.intellij.ui.components.JBList 19 | import com.intellij.ui.components.JBScrollPane 20 | import com.intellij.ui.dsl.builder.AlignX 21 | import com.intellij.ui.dsl.builder.panel 22 | import io.xmake.icons.XMakeIcons 23 | import io.xmake.run.XMakeRunConfiguration 24 | import io.xmake.shared.XMakeProblem 25 | import java.awt.Font 26 | import java.awt.event.MouseAdapter 27 | import java.awt.event.MouseEvent 28 | import java.io.File 29 | import javax.swing.JList 30 | import javax.swing.ListSelectionModel 31 | 32 | class XMakeToolWindowProblemPanel(project: Project) : SimpleToolWindowPanel(false) { 33 | 34 | // the problems 35 | private var _problems: List = emptyList() 36 | var problems: List 37 | get() = _problems 38 | set(value) { 39 | // check(ApplicationManager.getApplication().isDispatchThread) 40 | _problems = value 41 | problemList.setListData(problems.toTypedArray()) 42 | } 43 | 44 | // the toolbar 45 | val toolbar: ActionToolbar = run { 46 | val actionManager = ActionManager.getInstance() 47 | actionManager.createActionToolbar("XMake Toolbar", actionManager.getAction("XMake.Menu") as DefaultActionGroup, false) 48 | } 49 | 50 | // the problem list 51 | private val problemList = JBList(emptyList()).apply { 52 | emptyText.text = "There are no compiling problems to display." 53 | selectionMode = ListSelectionModel.SINGLE_SELECTION 54 | cellRenderer = object : ColoredListCellRenderer() { 55 | override fun customizeCellRenderer(list: JList, value: XMakeProblem, index: Int, selected: Boolean, hasFocus: Boolean) { 56 | 57 | // get file path 58 | var file = value.file 59 | if (file === null) { 60 | return 61 | } 62 | 63 | // init icon 64 | if (value.kind == "warning") { 65 | icon = XMakeIcons.WARNING 66 | } else if (value.kind == "error") { 67 | icon = XMakeIcons.ERROR 68 | } else { 69 | icon = XMakeIcons.WARNING 70 | } 71 | 72 | // init tips 73 | toolTipText = value.message ?: "" 74 | 75 | // append text 76 | append("${file}(${value.line ?: "0"}): ${value.message ?: ""}", SimpleTextAttributes.REGULAR_ATTRIBUTES) 77 | } 78 | } 79 | } 80 | 81 | // the problem pane 82 | val problemPane = JBScrollPane(problemList).apply { 83 | border = null 84 | } 85 | 86 | // the content 87 | val content = panel { 88 | row { 89 | scrollCell(problemList) 90 | .align(AlignX.FILL) 91 | } 92 | } 93 | /* 94 | val content = panel { 95 | row { 96 | problemPane(CCFlags.push, CCFlags.grow) 97 | } 98 | } 99 | */ 100 | 101 | init { 102 | 103 | // init toolbar 104 | setToolbar(toolbar.component) 105 | toolbar.targetComponent = this 106 | 107 | // init content 108 | setContent(content) 109 | 110 | // init double click listener 111 | problemList.addMouseListener(object: MouseAdapter() { 112 | override fun mouseClicked(e: MouseEvent) { 113 | if (e.clickCount == 1 || e.clickCount == 2) { 114 | 115 | // get the clicked problem 116 | val index = problemList.locationToIndex(e.getPoint()) 117 | if (index < problems.size && problems[index].file !== null) { 118 | 119 | // get file path 120 | val problem = problems[index] 121 | var filename = problem.file 122 | if (File(filename).exists()) { 123 | filename = File(filename).absolutePath 124 | } else { 125 | filename = File( 126 | // Todo: Check if correct 127 | (RunManager.getInstance(project).selectedConfiguration?.configuration as XMakeRunConfiguration).runWorkingDir 128 | , filename).absolutePath 129 | } 130 | 131 | // open this file 132 | val file = LocalFileSystem.getInstance().findFileByPath(filename) 133 | if (file !== null) { 134 | 135 | // goto file:line 136 | val descriptor = OpenFileDescriptor(project, file, problem.line?.toInt() ?: 0, problem.column?.toInt() ?: 0) 137 | descriptor.navigate(true) 138 | 139 | // highlight line 140 | val editor = FileEditorManager.getInstance(project).selectedTextEditor 141 | if (editor !== null) { 142 | 143 | if (e.clickCount == 2 && editor.markupModel.allHighlighters.size > 0) { 144 | editor.markupModel.removeAllHighlighters() 145 | return 146 | } 147 | 148 | // get line offset 149 | var line = problem.line?.toInt() ?: 0 150 | if (line > 0) line -= 1 151 | 152 | // init box color 153 | var boxcolor = JBColor.GRAY 154 | if (problem.kind == "warning") { 155 | boxcolor = JBColor.YELLOW 156 | } else if (problem.kind == "error") { 157 | boxcolor = JBColor.RED 158 | } 159 | 160 | // draw box 161 | editor.markupModel.addLineHighlighter(line, -1, TextAttributes(null, null, boxcolor, EffectType.BOXED, Font.PLAIN)) 162 | } 163 | } 164 | } 165 | } 166 | } 167 | }) 168 | } 169 | 170 | companion object { 171 | private val Log = Logger.getInstance(XMakeToolWindowProblemPanel::class.java.getName()) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/directory/ui/DirectoryBrowser.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.directory.ui 2 | 3 | import com.intellij.execution.wsl.WSLDistribution 4 | import com.intellij.execution.wsl.ui.browseWslPath 5 | import com.intellij.openapi.diagnostic.logger 6 | import com.intellij.openapi.extensions.ExtensionPointName 7 | import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory 8 | import com.intellij.openapi.project.Project 9 | import com.intellij.openapi.ui.TextComponentAccessor 10 | import com.intellij.openapi.ui.TextFieldWithBrowseButton 11 | import io.xmake.project.toolkit.Toolkit 12 | import io.xmake.project.toolkit.ToolkitHost 13 | import io.xmake.project.toolkit.ToolkitHostType.* 14 | import io.xmake.utils.extension.ToolkitHostExtension 15 | import java.awt.event.ActionListener 16 | 17 | class DirectoryBrowser(val project: Project?) : TextFieldWithBrowseButton() { 18 | 19 | private val listeners = mutableSetOf() 20 | 21 | private val EP_NAME: ExtensionPointName = ExtensionPointName("io.xmake.toolkitHostExtension") 22 | 23 | private fun createLocalBrowseListener(): ActionListener { 24 | val fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor() 25 | val browseFolderListener = BrowseFolderActionListener( 26 | this, 27 | project, 28 | fileChooserDescriptor 29 | .withTitle("Working Directory") 30 | .withDescription("Select the working directory"), 31 | TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT 32 | ) 33 | return browseFolderListener 34 | } 35 | 36 | private fun createWslBrowseListener(target: WSLDistribution): ActionListener { 37 | val fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor() 38 | val wslBrowseFolderListener = ActionListener { 39 | browseWslPath(this, 40 | target, 41 | this, 42 | true, 43 | fileChooserDescriptor) 44 | } 45 | return wslBrowseFolderListener 46 | } 47 | 48 | fun addBrowserListenerByToolkit(toolkit: Toolkit){ 49 | addBrowserListenerByHostType(toolkit.host) 50 | } 51 | 52 | fun addBrowserListenerByHostType(host: ToolkitHost) { 53 | when (host.type) { 54 | LOCAL -> { 55 | val localBrowseListener = createLocalBrowseListener() 56 | addActionListener(localBrowseListener) 57 | listeners.add(localBrowseListener) 58 | Log.debug("addActionListener local: $localBrowseListener") 59 | } 60 | 61 | WSL -> { 62 | val wslBrowseListener = createWslBrowseListener(host.target as WSLDistribution) 63 | addActionListener(wslBrowseListener) 64 | listeners.add(wslBrowseListener) 65 | Log.debug("addActionListener wsl: $wslBrowseListener") 66 | } 67 | 68 | SSH -> { 69 | EP_NAME.extensions.first { it.KEY == "SSH" }.let { extension -> 70 | println("host: $host") 71 | val browseListener = with(extension) { createBrowseListener(host) } 72 | addActionListener(browseListener) 73 | listeners.add(browseListener) 74 | Log.debug("addActionListener ${extension.getHostType()}: $browseListener") 75 | } 76 | } 77 | } 78 | } 79 | 80 | fun removeBrowserAllListener() { 81 | listeners.onEach { 82 | removeActionListener(it) 83 | }.clear() 84 | } 85 | companion object{ 86 | private val Log = logger() 87 | } 88 | } 89 | 90 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/target/TargetManager.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.target 2 | 3 | import com.intellij.openapi.components.Service 4 | import com.intellij.openapi.components.serviceOrNull 5 | import com.intellij.openapi.diagnostic.logger 6 | import com.intellij.openapi.project.Project 7 | import io.xmake.project.toolkit.Toolkit 8 | import io.xmake.utils.execute.createProcess 9 | import io.xmake.utils.execute.probeXmakeTargetCommand 10 | import io.xmake.utils.execute.runProcess 11 | import kotlinx.coroutines.CoroutineScope 12 | import kotlinx.coroutines.Dispatchers 13 | import kotlinx.coroutines.runBlocking 14 | 15 | @Service(Service.Level.PROJECT) 16 | class TargetManager( 17 | private val project: Project, 18 | private val scope: CoroutineScope, 19 | ) { 20 | 21 | fun detectXMakeTarget(toolkit: Toolkit, workingDirectory: String): List = runBlocking(Dispatchers.IO) { 22 | val process = probeXmakeTargetCommand 23 | .withExePath(toolkit.path) 24 | .withWorkDirectory(workingDirectory) 25 | .also { Log.debug(it.commandLineString) } 26 | .createProcess(toolkit) 27 | val (stdout, exitCode) = runProcess(process) 28 | Log.debug("ExitCode: $exitCode Output: $stdout") 29 | val targets = stdout.getOrElse { "" }.trimEnd().split(Regex("\\r\\n|\\n|\\r")) 30 | Log.debug("Targets: $targets") 31 | return@runBlocking listOf("default", "all") + targets 32 | } 33 | 34 | companion object { 35 | fun getInstance(project: Project): TargetManager = project.serviceOrNull() ?: throw IllegalStateException() 36 | private val Log = logger() 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/toolkit/ActivatedToolkit.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.toolkit 2 | 3 | import com.intellij.execution.RunManager 4 | import com.intellij.openapi.project.Project 5 | import io.xmake.run.XMakeRunConfiguration 6 | 7 | val Project.activatedToolkit: Toolkit? 8 | get() = RunManager.getInstance(this).selectedConfiguration?.configuration.let { 9 | if (it is XMakeRunConfiguration) it.runToolkit else null 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/toolkit/Toolkit.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.toolkit 2 | 3 | import com.intellij.util.xmlb.annotations.Attribute 4 | import com.intellij.util.xmlb.annotations.Property 5 | import com.intellij.util.xmlb.annotations.Tag 6 | import com.intellij.util.xmlb.annotations.Transient 7 | import java.util.* 8 | 9 | @Tag("toolkit") 10 | data class Toolkit( 11 | @Attribute 12 | val name: String = "", 13 | @Property(surroundWithTag = false) 14 | val host: ToolkitHost = ToolkitHost(ToolkitHostType.LOCAL), 15 | @Attribute 16 | val path: String = "", 17 | @Attribute 18 | val version: String = "", 19 | ) { 20 | @Attribute 21 | val id: String = UUID.nameUUIDFromBytes((name+host.type.name+path+version).toByteArray()).toString() 22 | @get:Transient 23 | var isRegistered: Boolean = false 24 | @get:Transient 25 | var isValid: Boolean = true 26 | @get:Transient 27 | val isOnRemote: Boolean 28 | get() = with(host) { type == ToolkitHostType.WSL || type == ToolkitHostType.SSH } 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/toolkit/ToolkitChangedNotifier.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.toolkit 2 | 3 | import com.intellij.util.messages.Topic 4 | 5 | interface ToolkitChangedNotifier { 6 | 7 | fun toolkitChanged(toolkit: Toolkit?) 8 | 9 | companion object { 10 | @Topic.ProjectLevel 11 | val TOOLKIT_CHANGED_TOPIC: Topic = Topic.create( 12 | "toolkit changed", 13 | ToolkitChangedNotifier::class.java 14 | ) 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/toolkit/ToolkitHost.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.toolkit 2 | 3 | import com.intellij.execution.wsl.WSLDistribution 4 | import com.intellij.execution.wsl.WslDistributionManager 5 | import com.intellij.openapi.extensions.ExtensionPointName 6 | import com.intellij.openapi.project.Project 7 | import com.intellij.openapi.util.SystemInfo 8 | import com.intellij.util.xmlb.annotations.Attribute 9 | import com.intellij.util.xmlb.annotations.Tag 10 | import io.xmake.project.toolkit.ToolkitHostType.* 11 | import io.xmake.utils.extension.ToolkitHostExtension 12 | import kotlinx.coroutines.coroutineScope 13 | 14 | @Tag("toolkitHost") 15 | data class ToolkitHost( 16 | @Attribute 17 | val type: ToolkitHostType = LOCAL, 18 | ) { 19 | 20 | private val EP_NAME: ExtensionPointName = 21 | ExtensionPointName("io.xmake.toolkitHostExtension") 22 | 23 | constructor(type: ToolkitHostType, target: Any? = null) : this(type) { 24 | this.target = target 25 | this.id = when (type) { 26 | LOCAL -> SystemInfo.getOsName() 27 | WSL -> (target as WSLDistribution).id 28 | SSH -> EP_NAME.extensions.first { it.KEY == "SSH" }.getTargetId(target) 29 | } 30 | } 31 | 32 | @Transient 33 | var target: Any? = null 34 | 35 | @Attribute 36 | var id: String? = null 37 | 38 | suspend fun loadTarget(project: Project? = null) { 39 | when (type) { 40 | LOCAL -> {} 41 | WSL -> loadWslTarget() 42 | SSH -> { 43 | with(EP_NAME.extensions.first { it.KEY == "SSH" }) { 44 | loadTargetX(project) 45 | } 46 | } 47 | } 48 | } 49 | 50 | private suspend fun loadWslTarget() = coroutineScope { 51 | target = WslDistributionManager.getInstance().installedDistributions.find { it.id == id }!! 52 | } 53 | 54 | override fun toString(): String { 55 | return "ToolkitHost(type=$type, id=$id)" 56 | } 57 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/toolkit/ToolkitHostType.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.toolkit 2 | 3 | enum class ToolkitHostType { 4 | LOCAL, WSL, SSH, 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/toolkit/ToolkitManager.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.toolkit 2 | 3 | import com.intellij.execution.RunManager 4 | import com.intellij.execution.processTools.getBareExecutionResult 5 | import com.intellij.execution.wsl.WSLDistribution 6 | import com.intellij.execution.wsl.WSLUtil 7 | import com.intellij.execution.wsl.WslDistributionManager 8 | import com.intellij.openapi.components.* 9 | import com.intellij.openapi.diagnostic.logger 10 | import com.intellij.openapi.extensions.ExtensionPointName 11 | import com.intellij.openapi.project.Project 12 | import com.intellij.openapi.project.ProjectManager 13 | import com.intellij.openapi.util.SystemInfo 14 | import com.intellij.util.xmlb.annotations.XCollection 15 | import io.xmake.project.toolkit.ToolkitHostType.* 16 | import io.xmake.run.XMakeRunConfiguration 17 | import io.xmake.utils.execute.* 18 | import io.xmake.utils.extension.ToolkitHostExtension 19 | import kotlinx.coroutines.* 20 | import kotlinx.coroutines.flow.* 21 | import java.util.* 22 | 23 | @Service 24 | @State(name = "toolkits", storages = [Storage("xmakeToolkits.xml")]) 25 | class ToolkitManager(private val scope: CoroutineScope) : PersistentStateComponent { 26 | 27 | private val EP_NAME: ExtensionPointName = 28 | ExtensionPointName("io.xmake.toolkitHostExtension") 29 | 30 | val fetchedToolkitsSet = mutableSetOf() 31 | private lateinit var detectionJob: Job 32 | private lateinit var validateJob: Job 33 | private var storage: State = State() 34 | private val listenerList = mutableListOf() 35 | 36 | class State{ 37 | @XCollection(propertyElementName = "registeredToolKits") 38 | val registeredToolkits = mutableSetOf() 39 | var lastSelectedToolkitId: String? = null 40 | } 41 | 42 | interface ToolkitDetectedListener : EventListener { 43 | fun onToolkitDetected(e: ToolkitDetectEvent) 44 | fun onAllToolkitsDetected() 45 | } 46 | 47 | class ToolkitDetectEvent(source: Toolkit) : EventObject(source) 48 | 49 | init { 50 | scope.launch { 51 | // Cache the list of installed distributions 52 | WslDistributionManager.getInstance().installedDistributions 53 | } 54 | } 55 | 56 | private fun toolkitHostFlow(project: Project? = null): Flow = flow { 57 | val wslDistributions = scope.async { WslDistributionManager.getInstance().installedDistributions } 58 | 59 | emit(ToolkitHost(LOCAL).also { host -> Log.info("emit host: $host") }) 60 | 61 | if (WSLUtil.isSystemCompatible()) { 62 | wslDistributions.await().forEach { 63 | emit(ToolkitHost(WSL, it).also { host -> Log.info("emit host: $host") }) 64 | } 65 | } 66 | 67 | EP_NAME.extensions.filter { it.KEY == "SSH" }.forEach { 68 | it.getToolkitHosts(project).forEach { 69 | emit(it).also { host -> Log.info("emit host: $host") } 70 | } 71 | } 72 | } 73 | 74 | private fun detectToolkitLocation(host: ToolkitHost): Flow = flow { 75 | val process = probeXmakeLocCommand.let { 76 | when (host.type) { 77 | LOCAL -> (if (SystemInfo.isWindows) probeXmakeLocCommandOnWin else it).createLocalProcess() 78 | WSL -> it.createWslProcess(host.target as WSLDistribution) 79 | SSH -> with(EP_NAME.extensions.first { it.KEY == "SSH" }) { it.createProcess(host) } 80 | } 81 | } 82 | 83 | with(process.getBareExecutionResult()){ 84 | Log.info("Host: ${host.type} ExitCode: $exitCode Output: ${stdOut.toString(Charsets.UTF_8)}") 85 | val paths = stdOut.toString(Charsets.UTF_8) 86 | .split(Regex("\\r\\n|\\n|\\r")) 87 | .filterNot { it.isBlank() || it.contains("not found") } 88 | .distinct() 89 | paths.forEach { emit(it); Log.info("emit path on ${host.type}: $it") } 90 | } 91 | } 92 | 93 | private fun detectToolkitVersion(host: ToolkitHost, path: String): Flow = flow { 94 | val process = probeXmakeVersionCommand.withExePath(path).let { 95 | when (host.type) { 96 | LOCAL -> it.createLocalProcess() 97 | WSL -> it.createWslProcess(host.target as WSLDistribution) 98 | SSH -> with(EP_NAME.extensions.first { it.KEY == "SSH" }) { it.createProcess(host) } 99 | } 100 | } 101 | val (stdout, exitCode) = runProcess(process) 102 | val versionString = stdout.getOrElse { "" }.split(Regex(",")).first().split(" ").last() 103 | Log.info("ExitCode: $exitCode Version: $versionString") 104 | emit(versionString) 105 | } 106 | 107 | @OptIn(ExperimentalCoroutinesApi::class) 108 | fun detectXMakeToolkits(project: Project?) { 109 | detectionJob = scope.launch { 110 | val toolkitFlow = toolkitHostFlow(project) 111 | 112 | val pathFlow = toolkitFlow.flatMapMerge { host -> 113 | detectToolkitLocation(host).catch { 114 | Log.warn(it.message) 115 | }.flowOn(Dispatchers.IO).buffer() 116 | .distinctUntilChanged() 117 | .filterNot { it.isBlank() } 118 | .onEach { Log.info("output path: $it") } 119 | .map { path -> host to path } 120 | }.flowOn(Dispatchers.Default).buffer() 121 | 122 | val versionFlow = pathFlow.flatMapMerge { (host, path) -> 123 | Log.info("detecting version: host: $host, path: $path") 124 | detectToolkitVersion(host, path).catch { 125 | Log.warn(it.message) 126 | }.flowOn(Dispatchers.IO).buffer().filterNot { it.isBlank() }.map { versionString -> 127 | when (host.type) { 128 | LOCAL -> { 129 | val name = SystemInfo.getOsName() 130 | Toolkit(name, host, path, versionString) 131 | } 132 | 133 | WSL -> { 134 | val wslDistribution = host.target as WSLDistribution 135 | val name = wslDistribution.presentableName 136 | Toolkit(name, host, path, versionString) 137 | } 138 | 139 | SSH -> { 140 | EP_NAME.extensions.first { it.KEY == "SSH" } 141 | .createToolkit(host, path, versionString) 142 | } 143 | }.apply { this.isRegistered = true; this.isValid = true } 144 | } 145 | }.flowOn(Dispatchers.Default).buffer() 146 | 147 | versionFlow.collect { toolkit -> 148 | // Todo: Consider cache 149 | fetchedToolkitsSet.add(toolkit) 150 | listenerList.forEach { listener -> 151 | listener.onToolkitDetected(ToolkitDetectEvent(toolkit)) 152 | } 153 | Log.info("toolkit added: $toolkit") 154 | } 155 | listenerList.forEach { it.onAllToolkitsDetected() } 156 | } 157 | } 158 | 159 | fun cancelDetection() { 160 | scope.launch { 161 | detectionJob.cancel() 162 | } 163 | } 164 | 165 | // Todo: Validate toolkit. 166 | fun validateXMakeToolkit() { 167 | scope.launch { 168 | try { 169 | validateJob = launch { validateToolkits() } 170 | } catch (e: Exception) { 171 | Log.error("Error: ${e.message}") 172 | } 173 | } 174 | } 175 | 176 | // Todo: Validate toolkit. 177 | fun validateToolkits(){ 178 | 179 | } 180 | 181 | // Todo: Validate toolkit. 182 | fun cancelValidation(){ 183 | 184 | } 185 | 186 | fun addToolkitDetectedListener(listener: ToolkitDetectedListener) { 187 | listenerList.add(listener) 188 | } 189 | 190 | override fun getState(): State { 191 | return storage 192 | } 193 | 194 | override fun loadState(state: State) { 195 | state.registeredToolkits.forEach { 196 | registerToolkit(it) 197 | fetchedToolkitsSet.add(it) 198 | } 199 | this.storage = state 200 | } 201 | 202 | private fun loadToolkit(toolkit: Toolkit) { 203 | scope.launch(Dispatchers.IO) { 204 | toolkit.host.loadTarget() 205 | joinAll() 206 | } 207 | } 208 | 209 | fun registerToolkit(toolkit: Toolkit) { 210 | toolkit.isRegistered = true 211 | if (state.registeredToolkits.add(toolkit)){ 212 | loadToolkit(toolkit) 213 | } else { 214 | loadToolkit(findRegisteredToolkitById(toolkit.id)!!) 215 | } 216 | Log.info("load registered toolkit: ${toolkit.name}, ${toolkit.id}") 217 | } 218 | 219 | // Todo: Increase robustness of this method 220 | fun unregisterToolkit(toolkit: Toolkit) { 221 | if(state.registeredToolkits.remove(toolkit)) { 222 | ProjectManager.getInstance().openProjects.forEach { project -> 223 | RunManager.getInstance(project).allConfigurationsList.forEach { 224 | if (it is XMakeRunConfiguration) { 225 | if (it.runToolkit?.id == toolkit.id) 226 | it.runToolkit = null 227 | } 228 | } 229 | } 230 | } 231 | } 232 | 233 | fun findRegisteredToolkitById(id: String): Toolkit? { 234 | return state.registeredToolkits.find { it.id == id } 235 | } 236 | 237 | fun getRegisteredToolkits(): List { 238 | return state.registeredToolkits.filter { toolkit -> 239 | !toolkit.isOnRemote || 240 | EP_NAME.extensions.filter { it.KEY == "SSH" }.fold(true) { acc, sshExtension -> 241 | acc || sshExtension.filterRegistered()(toolkit) 242 | } 243 | } 244 | // .filterNot { (it.host.type == SSH && PlatformUtils.isCommunityEdition()) } 245 | } 246 | 247 | companion object { 248 | private val Log = logger() 249 | 250 | fun getInstance(): ToolkitManager = serviceOrNull() ?: throw IllegalStateException() 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/toolkit/ui/ToolkitComboBox.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.toolkit.ui 2 | 3 | import ai.grazie.utils.tryRunWithException 4 | import com.intellij.openapi.application.ApplicationManager 5 | import com.intellij.openapi.diagnostic.logger 6 | import com.intellij.openapi.project.ProjectManager 7 | import com.intellij.openapi.ui.ComboBox 8 | import com.intellij.openapi.ui.validation.DialogValidation 9 | import com.intellij.openapi.ui.validation.transformParameter 10 | import com.intellij.openapi.ui.validation.validationErrorIf 11 | import com.intellij.ui.PopupMenuListenerAdapter 12 | import com.intellij.ui.SortedComboBoxModel 13 | import io.xmake.project.toolkit.Toolkit 14 | import io.xmake.project.toolkit.ToolkitChangedNotifier 15 | import io.xmake.project.toolkit.ToolkitManager 16 | import java.awt.event.ItemEvent 17 | import java.util.* 18 | import javax.swing.event.PopupMenuEvent 19 | import kotlin.concurrent.timerTask 20 | import kotlin.reflect.KMutableProperty0 21 | 22 | class ToolkitComboBox(toolkitProperty: KMutableProperty0) : ComboBox( 23 | SortedComboBoxModel { o1, o2 -> o1 compareTo o2 } 24 | ) { 25 | 26 | private val service = ToolkitManager.getInstance() 27 | 28 | private var model: SortedComboBoxModel 29 | get() = super.getModel() as SortedComboBoxModel 30 | set(value) { super.setModel(value) } 31 | 32 | var activatedToolkit: Toolkit? by toolkitProperty 33 | private set 34 | 35 | override fun getItem(): ToolkitListItem? { 36 | return model.selectedItem ?: null 37 | } 38 | 39 | override fun setItem(anObject: ToolkitListItem?) { 40 | model.selectedItem = anObject 41 | } 42 | 43 | private val timer = Timer() 44 | private var timerTask: TimerTask? = null 45 | 46 | private fun debounce(delayMillis: Long = 50L, action: TimerTask.() -> Unit) { 47 | timerTask?.cancel() 48 | timerTask = timerTask(action) 49 | timer.schedule(timerTask, delayMillis) 50 | } 51 | 52 | init { 53 | model.apply { 54 | val initialToolkit = activatedToolkit 55 | Log.debug("ComboBox initial activated Toolkit: $initialToolkit") 56 | 57 | val initialListItem = if (initialToolkit != null) { 58 | if (initialToolkit.isValid) 59 | ToolkitListItem.ToolkitItem(initialToolkit) 60 | else 61 | ToolkitListItem.ToolkitItem(initialToolkit).asInvalid() 62 | } else { 63 | ToolkitListItem.NoneItem() 64 | } 65 | 66 | add(initialListItem) 67 | item = initialListItem 68 | } 69 | 70 | isSwingPopup = false 71 | maximumRowCount = 30 72 | renderer = ToolkitComboBoxRenderer(this) 73 | putClientProperty("ComboBox.jbPopup.supportUpdateModel", true) 74 | Log.debug("ComboBox Client Property: " + getClientProperty("ComboBox.jbPopup.supportUpdateModel")) 75 | 76 | } 77 | 78 | init { 79 | addPopupMenuListener(object : PopupMenuListenerAdapter() { 80 | override fun popupMenuWillBecomeVisible(e: PopupMenuEvent?) { 81 | super.popupMenuWillBecomeVisible(e) 82 | 83 | // todo: check whether safe or not 84 | val itemToolkit = (item as? ToolkitListItem.ToolkitItem)?.toolkit 85 | 86 | with(model) { 87 | clear() 88 | add(ToolkitListItem.NoneItem()) 89 | service.getRegisteredToolkits().forEach { 90 | add(ToolkitListItem.ToolkitItem(it).asRegistered()) 91 | } 92 | } 93 | 94 | tryRunWithException { 95 | firePropertyChange("model", false, true) 96 | } 97 | 98 | // to select configuration-level activated toolkit 99 | item = if (service.getRegisteredToolkits().isEmpty() || itemToolkit == null) { 100 | model.items.first() 101 | } else { 102 | model.items.find { it.id == itemToolkit.id }.let { 103 | if (it == null) { 104 | val invalidToolkitItem = ToolkitListItem.ToolkitItem(itemToolkit).asInvalid() 105 | model.items.add(0, invalidToolkitItem) 106 | invalidToolkitItem 107 | } else it 108 | } 109 | } 110 | 111 | val project = ProjectManager.getInstanceIfCreated()?.defaultProject 112 | service.detectXMakeToolkits(project) 113 | } 114 | 115 | override fun popupMenuWillBecomeInvisible(e: PopupMenuEvent?) { 116 | super.popupMenuWillBecomeInvisible(e) 117 | service.cancelDetection() 118 | } 119 | }) 120 | 121 | service.addToolkitDetectedListener(object : ToolkitManager.ToolkitDetectedListener { 122 | override fun onToolkitDetected(e: ToolkitManager.ToolkitDetectEvent) { 123 | val toolkit = e.source as Toolkit 124 | model.add(ToolkitListItem.ToolkitItem(toolkit)) 125 | debounce { 126 | tryRunWithException { 127 | firePropertyChange("model", false, true) 128 | } 129 | } 130 | } 131 | 132 | override fun onAllToolkitsDetected() {} 133 | }) 134 | 135 | addItemListener { it -> 136 | if (it.stateChange == ItemEvent.SELECTED) { 137 | val toolkitListItem = it.item as ToolkitListItem 138 | if (toolkitListItem is ToolkitListItem.ToolkitItem) { 139 | with(service) { 140 | val fetchedToolkit = fetchedToolkitsSet.find { it.id == toolkitListItem.id } 141 | 142 | if (fetchedToolkit != null) { 143 | // check whether registered or not 144 | getRegisteredToolkits().run { 145 | if (findRegisteredToolkitById(fetchedToolkit.id) == null) { 146 | registerToolkit(fetchedToolkit) 147 | } 148 | } 149 | } else { 150 | // selectedItem toolkit is not in toolkitSet 151 | } 152 | 153 | activatedToolkit = fetchedToolkit 154 | } 155 | } else { 156 | activatedToolkit = null 157 | } 158 | 159 | toolkitChangedListeners.forEach { listener -> 160 | listener.onToolkitChanged(activatedToolkit) 161 | } 162 | 163 | Log.info("activeToolkit: $activatedToolkit") 164 | Log.info("selected Item: " + (item?.text ?: "")) 165 | } 166 | } 167 | } 168 | 169 | private val toolkitChangedListeners = mutableListOf() 170 | 171 | interface ToolkitChangedListener : EventListener { 172 | fun onToolkitChanged(toolkit: Toolkit?) 173 | } 174 | 175 | fun addToolkitChangedListener(listener: ToolkitChangedListener) { 176 | toolkitChangedListeners.add(listener) 177 | } 178 | 179 | val publisher: ToolkitChangedNotifier = ApplicationManager.getApplication().messageBus 180 | .syncPublisher(ToolkitChangedNotifier.TOOLKIT_CHANGED_TOPIC) 181 | 182 | fun addToolkitChangedListener(action: (Toolkit?) -> Unit) { 183 | toolkitChangedListeners.add(object : ToolkitChangedListener { 184 | override fun onToolkitChanged(toolkit: Toolkit?) { 185 | publisher.toolkitChanged(toolkit) 186 | action(toolkit) 187 | } 188 | }) 189 | } 190 | 191 | fun removeToolkitChangedListener(listener: ToolkitChangedListener) { 192 | toolkitChangedListeners.remove(listener) 193 | } 194 | 195 | fun getToolkitChangedListeners(): List = toolkitChangedListeners 196 | 197 | companion object { 198 | private val Log = logger() 199 | 200 | fun DialogValidation.WithParameter<() -> Toolkit?>.forToolkitComboBox(): DialogValidation.WithParameter = 201 | transformParameter { ::activatedToolkit } 202 | 203 | val CHECK_NON_EMPTY_TOOLKIT: DialogValidation.WithParameter<() -> Toolkit?> = 204 | validationErrorIf("XMake toolkit is not set!") { it == null } 205 | } 206 | 207 | } 208 | 209 | 210 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/toolkit/ui/ToolkitComboBoxRenderer.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.toolkit.ui 2 | 3 | import com.intellij.icons.AllIcons 4 | import com.intellij.openapi.ui.asSequence 5 | import com.intellij.openapi.ui.popup.ListSeparator 6 | import com.intellij.ui.GroupedComboBoxRenderer 7 | import com.intellij.ui.SimpleColoredComponent 8 | import com.intellij.ui.SimpleTextAttributes 9 | import javax.swing.Icon 10 | import javax.swing.JComponent 11 | import javax.swing.JList 12 | 13 | class ToolkitComboBoxRenderer(component: JComponent) : GroupedComboBoxRenderer(component) { 14 | 15 | override fun isSeparatorVisible(list: JList?, value: ToolkitListItem?): Boolean { 16 | return list?.model?.asSequence()?.firstOrNull { it?.caption == value?.caption } == value 17 | } 18 | 19 | override fun separatorFor(value: ToolkitListItem?): ListSeparator { 20 | return ListSeparator(value?.caption ?: "") 21 | } 22 | 23 | override fun getText(item: ToolkitListItem?): String { 24 | return item?.text ?: "" 25 | } 26 | 27 | override fun getSecondaryText(item: ToolkitListItem?): String? { 28 | return item?.secondaryText 29 | } 30 | 31 | private fun getTertiaryText(item: ToolkitListItem?): String? { 32 | return item?.tertiaryText 33 | } 34 | 35 | override fun getIcon(item: ToolkitListItem?): Icon { 36 | return item?.icon?: AllIcons.General.Error 37 | } 38 | 39 | override fun getCaption(list: JList?, value: ToolkitListItem?): String? { 40 | return if (value?.isCaptionVisible == true) value.caption else null 41 | } 42 | 43 | override fun customize( 44 | item: SimpleColoredComponent, 45 | value: ToolkitListItem?, 46 | index: Int, 47 | isSelected: Boolean, 48 | cellHasFocus: Boolean, 49 | ) { 50 | item.icon = getIcon(value) 51 | val text = value?.let { getText(value) } ?: "" 52 | val secondaryText = getSecondaryText(value) 53 | val tertiaryText = getTertiaryText(value) 54 | 55 | item.append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES) 56 | if (secondaryText != null) 57 | item.append(" - $secondaryText ", SimpleTextAttributes.REGULAR_ITALIC_ATTRIBUTES) 58 | if (tertiaryText != null) 59 | item.append(" $tertiaryText", SimpleTextAttributes.GRAYED_ATTRIBUTES) 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/toolkit/ui/ToolkitListItem.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.toolkit.ui 2 | 3 | import com.intellij.execution.configurations.RuntimeConfigurationError 4 | import com.intellij.openapi.actionSystem.AnAction 5 | import io.xmake.icons.XMakeIcons 6 | import io.xmake.project.toolkit.Toolkit 7 | import javax.swing.Icon 8 | 9 | open class ToolkitListItem( 10 | val id: String, 11 | var text: String?, 12 | var secondaryText: String? = null, 13 | var tertiaryText: String? = null, 14 | var caption: String? = null, 15 | var isCaptionVisible: Boolean = false, 16 | var icon: Icon? = null, 17 | ) { 18 | 19 | infix operator fun compareTo(other: ToolkitListItem): Int { 20 | return if (this is ToolkitItem && other is ToolkitItem) { 21 | return this compareTo other 22 | } else if (this is NoneItem || other is NoneItem) { 23 | compareValuesBy(this, other) { it.id } 24 | } else { 25 | return compareValuesBy(this, other) { it.text } 26 | } 27 | } 28 | 29 | class NoneItem : ToolkitListItem(id = "", text = "None") 30 | 31 | open class ToolkitItem(val toolkit: Toolkit) : ToolkitListItem( 32 | toolkit.id, 33 | toolkit.path, 34 | toolkit.name, 35 | toolkit.version, 36 | toolkit.host.type.name, 37 | true, 38 | XMakeIcons.XMAKE 39 | ) { 40 | infix operator fun compareTo(other: ToolkitItem): Int { 41 | return compareValuesBy(this, other, 42 | { if (it.caption == "Registered") -1 else it.toolkit.host.type.ordinal }, 43 | { it.toolkit.host.type.ordinal }, 44 | { it.toolkit.path } 45 | ) 46 | } 47 | 48 | fun asRegistered(): ToolkitItem { 49 | if (this.toolkit.isRegistered) 50 | return this.apply { caption = "Registered" } 51 | else 52 | throw RuntimeConfigurationError("Toolkit is not registered!") 53 | } 54 | 55 | fun asInvalid(): ToolkitItem { 56 | return this.apply { tertiaryText = "Invalid" } 57 | } 58 | 59 | fun asCurrent(): ToolkitItem { 60 | return this.apply { caption = "Current" } 61 | } 62 | } 63 | 64 | enum class ActionRole { DOWNLOAD, ADD } 65 | 66 | class ActionItem( 67 | id: String, 68 | name: String, 69 | private val role: ActionRole, 70 | private val action: AnAction, 71 | ) : ToolkitListItem(id, name,) {} 72 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/wizard/NewProjectWizardDirectoryGeneratorAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 2 | 3 | /* 4 | * Modifications: 5 | * - Changed class from final to open 6 | * - Date: 2024-08-07 7 | * - Author: windchargerj 8 | * - Reference: [com.jetbrains.python.newProject.NewProjectWizardDirectoryGeneratorAdapter] 9 | */ 10 | 11 | package io.xmake.project.wizard 12 | 13 | import com.intellij.ide.util.projectWizard.AbstractNewProjectStep 14 | import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase 15 | import com.intellij.ide.util.projectWizard.WizardContext 16 | import com.intellij.ide.wizard.GeneratorNewProjectWizard 17 | import com.intellij.ide.wizard.NewProjectWizardStepPanel 18 | import com.intellij.openapi.module.Module 19 | import com.intellij.openapi.project.Project 20 | import com.intellij.openapi.ui.TextFieldWithBrowseButton 21 | import com.intellij.openapi.ui.VerticalFlowLayout 22 | import com.intellij.openapi.vfs.VirtualFile 23 | import com.intellij.platform.DirectoryProjectGeneratorBase 24 | import com.intellij.platform.GeneratorPeerImpl 25 | import com.intellij.platform.ProjectGeneratorPeer 26 | import javax.swing.Icon 27 | import javax.swing.JButton 28 | import javax.swing.JComponent 29 | import javax.swing.JPanel 30 | 31 | // Todo: Refactor to transform a project wizard to a [com.intellij.platform.DirectoryProjectGenerator] directly. 32 | 33 | /** 34 | * A base adapter class to turn a [GeneratorNewProjectWizard] into a 35 | * [com.intellij.platform.DirectoryProjectGenerator] and register as an extension point. 36 | * 37 | * @see NewProjectWizardProjectSettingsStep 38 | */ 39 | open class NewProjectWizardDirectoryGeneratorAdapter(val wizard: GeneratorNewProjectWizard) : 40 | DirectoryProjectGeneratorBase() { 41 | internal lateinit var panel: NewProjectWizardStepPanel 42 | 43 | @Suppress("DialogTitleCapitalization") 44 | override fun getName(): String = wizard.name 45 | override fun getLogo(): Icon = wizard.icon 46 | 47 | override fun generateProject(project: Project, baseDir: VirtualFile, settings: T, module: Module) { 48 | panel.step.setupProject(project) 49 | } 50 | 51 | override fun createPeer(): ProjectGeneratorPeer { 52 | val context = WizardContext(null) {} 53 | return object : GeneratorPeerImpl() { 54 | override fun getComponent(myLocationField: TextFieldWithBrowseButton, checkValid: Runnable): JComponent { 55 | panel = NewProjectWizardStepPanel(wizard.createStep(context)) 56 | return panel.component 57 | } 58 | } 59 | } 60 | } 61 | 62 | /** 63 | * A wizard-enabled project settings step that you should use for your [projectGenerator] in your 64 | * [AbstractNewProjectStep.Customization.createProjectSpecificSettingsStep] to provide the project wizard UI and actions. 65 | */ 66 | open class NewProjectWizardProjectSettingsStep(private val projectGenerator: NewProjectWizardDirectoryGeneratorAdapter) : 67 | ProjectSettingsStepBase(projectGenerator, null) { 68 | 69 | init { 70 | myCallback = AbstractNewProjectStep.AbstractCallback() 71 | } 72 | 73 | override fun createAndFillContentPanel(): JPanel = 74 | JPanel(VerticalFlowLayout()).apply { 75 | add(peer.getComponent(TextFieldWithBrowseButton()) {}) 76 | } 77 | 78 | override fun registerValidators() {} 79 | 80 | override fun getProjectLocation(): String = 81 | projectGenerator.panel.step.context.projectFileDirectory 82 | 83 | override fun getActionButton(): JButton = 84 | super.getActionButton().apply { 85 | addActionListener { 86 | projectGenerator.panel.apply() 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/wizard/XMakeGeneratorNewProjectWizard.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.wizard 2 | 3 | import com.intellij.ide.util.projectWizard.WizardContext 4 | import com.intellij.ide.wizard.GeneratorNewProjectWizard 5 | import com.intellij.ide.wizard.NewProjectWizardBaseStep 6 | import com.intellij.ide.wizard.NewProjectWizardChainStep.Companion.nextStep 7 | import com.intellij.ide.wizard.NewProjectWizardStep 8 | import com.intellij.ide.wizard.RootNewProjectWizardStep 9 | import io.xmake.icons.XMakeIcons 10 | import javax.swing.Icon 11 | 12 | class XMakeGeneratorNewProjectWizard : GeneratorNewProjectWizard { 13 | override val icon: Icon = XMakeIcons.XMAKE 14 | override val id: String = "xmake" 15 | override val name: String = "XMake" 16 | override fun createStep(context: WizardContext): NewProjectWizardStep { 17 | return RootNewProjectWizardStep(context) 18 | .nextStep(::NewProjectWizardBaseStep) 19 | .nextStep(::XMakeProjectWizardStep) 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/wizard/XMakeNewProjectWizardData.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.wizard 2 | 3 | import com.intellij.ide.wizard.NewProjectWizardBaseData 4 | import com.intellij.ide.wizard.NewProjectWizardStep 5 | import com.intellij.openapi.observable.properties.GraphProperty 6 | import com.intellij.openapi.util.Key 7 | import io.xmake.project.toolkit.Toolkit 8 | 9 | interface XMakeNewProjectWizardData : NewProjectWizardBaseData { 10 | 11 | val remotePathProperty: GraphProperty 12 | 13 | var remotePath: String 14 | 15 | val remoteContentEntryPath: String // canonical 16 | get() = "$remotePath/$name" 17 | 18 | val toolkitProperty: GraphProperty 19 | 20 | var toolkit: Toolkit? 21 | 22 | val languagesProperty: GraphProperty 23 | 24 | var language: String 25 | 26 | val kindsProperty: GraphProperty 27 | 28 | var kind: String 29 | 30 | override val contentEntryPath: String 31 | get() = "$path/$name" 32 | 33 | companion object { 34 | 35 | val KEY: Key = Key.create(XMakeNewProjectWizardData::class.java.name) 36 | 37 | @JvmStatic 38 | val NewProjectWizardStep.xmakeData: XMakeNewProjectWizardData? 39 | get() = data.getUserData(KEY) 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/wizard/XMakeProjectDirectoryGenerator.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.wizard 2 | 3 | import com.intellij.facet.ui.ValidationResult 4 | import com.intellij.ide.util.projectWizard.AbstractNewProjectStep 5 | import com.intellij.ide.util.projectWizard.CustomStepProjectGenerator 6 | import com.intellij.openapi.util.Disposer 7 | import com.intellij.openapi.wm.impl.welcomeScreen.AbstractActionWithPanel 8 | import com.intellij.platform.DirectoryProjectGenerator 9 | 10 | class XMakeProjectDirectoryGenerator : 11 | NewProjectWizardDirectoryGeneratorAdapter(XMakeGeneratorNewProjectWizard()), 12 | CustomStepProjectGenerator { 13 | 14 | private fun validate(): ValidationResult { 15 | return with(panel.component.validateAll()) { 16 | if (all { it.okEnabled }) ValidationResult.OK 17 | else find { !it.okEnabled }?.let { ValidationResult(it.message) } ?: ValidationResult("") 18 | } 19 | } 20 | 21 | override fun createStep( 22 | projectGenerator: DirectoryProjectGenerator?, 23 | callback: AbstractNewProjectStep.AbstractCallback?, 24 | ): AbstractActionWithPanel = object : NewProjectWizardProjectSettingsStep(this) { 25 | override fun registerValidators() { 26 | setErrorText(validate().errorMessage) 27 | panel.step.propertyGraph.afterPropagation { 28 | setErrorText(validate().errorMessage) 29 | } 30 | Disposer.register(this) { } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/project/wizard/XMakeProjectModuleBuilder.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.project.wizard 2 | 3 | import com.intellij.ide.wizard.GeneratorNewProjectWizardBuilderAdapter 4 | 5 | class XMakeProjectModuleBuilder : 6 | GeneratorNewProjectWizardBuilderAdapter(XMakeGeneratorNewProjectWizard()) 7 | 8 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/run/XMakeDefaultRunner.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.run 2 | 3 | import com.intellij.execution.configurations.RunProfileState 4 | import com.intellij.execution.configurations.RunnerSettings 5 | import com.intellij.execution.runners.AsyncProgramRunner 6 | import com.intellij.execution.runners.ExecutionEnvironment 7 | import com.intellij.execution.runners.executeState 8 | import com.intellij.execution.ui.RunContentDescriptor 9 | import org.jetbrains.concurrency.Promise 10 | import org.jetbrains.concurrency.resolvedPromise 11 | 12 | abstract class XMakeDefaultRunner : AsyncProgramRunner() { 13 | 14 | override fun execute(environment: ExecutionEnvironment, state: RunProfileState): Promise { 15 | return resolvedPromise(doExecute(state, environment)) 16 | } 17 | protected open fun doExecute(state: RunProfileState, environment: ExecutionEnvironment) : RunContentDescriptor? { 18 | return executeState(state, environment, this) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/run/XMakeRunConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.run 2 | 3 | import com.intellij.execution.Executor 4 | import com.intellij.execution.configuration.EnvironmentVariablesData 5 | import com.intellij.execution.configurations.* 6 | import com.intellij.execution.process.ProcessAdapter 7 | import com.intellij.execution.process.ProcessEvent 8 | import com.intellij.execution.runners.ExecutionEnvironment 9 | import com.intellij.openapi.diagnostic.Logger 10 | import com.intellij.openapi.options.SettingsEditor 11 | import com.intellij.openapi.project.Project 12 | import com.intellij.util.xmlb.XmlSerializer 13 | import com.intellij.util.xmlb.annotations.OptionTag 14 | import com.intellij.util.xmlb.annotations.Transient 15 | import io.xmake.project.toolkit.Toolkit 16 | import io.xmake.project.toolkit.ToolkitManager 17 | import io.xmake.project.xmakeConsoleView 18 | import io.xmake.shared.xmakeConfiguration 19 | import io.xmake.utils.SystemUtils 20 | import io.xmake.utils.info.XMakeInfoManager 21 | import io.xmake.utils.info.xmakeInfo 22 | import org.jdom.Element 23 | import kotlin.io.path.Path 24 | 25 | class XMakeRunConfiguration( 26 | project: Project, name: String, factory: ConfigurationFactory 27 | ) : LocatableConfigurationBase(project, factory, name), 28 | RunConfigurationWithSuppressedDefaultDebugAction { 29 | 30 | @OptionTag(tag = "activatedToolkit") 31 | var runToolkit: Toolkit? = null 32 | 33 | // the run target 34 | @OptionTag(tag = "target") 35 | var runTarget: String = "default" 36 | 37 | @OptionTag(tag = "platform") 38 | var runPlatform: String = if (platforms.contains(SystemUtils.platform())) SystemUtils.platform() else "default" 39 | 40 | @OptionTag(tag = "architecture") 41 | var runArchitecture: String = getArchitecturesByPlatform(runPlatform).firstOrNull() ?: "default" 42 | 43 | @OptionTag(tag = "toolchain") 44 | var runToolchain: String = toolchains.firstOrNull() ?: "default" 45 | 46 | @OptionTag(tag = "mode") 47 | var runMode: String = "release" 48 | 49 | // the run arguments 50 | @OptionTag(tag = "arguments") 51 | var runArguments: String = "" 52 | 53 | // the run environmen 54 | @get:Transient 55 | var runEnvironment: EnvironmentVariablesData = EnvironmentVariablesData.DEFAULT 56 | 57 | @OptionTag(tag = "workingDirectory") 58 | var runWorkingDir: String = "" 59 | 60 | @OptionTag(tag = "buildDirectory") 61 | var buildDirectory: String = "" 62 | 63 | @OptionTag(tag = "androidNDKDirectory") 64 | var androidNDKDirectory: String = "" 65 | 66 | @OptionTag(tag = "enableVerbose") 67 | var enableVerbose: Boolean = false 68 | 69 | @OptionTag(tag = "additionalConfiguration") 70 | var additionalConfiguration: String = "" 71 | 72 | // the run command line 73 | val runCommandLine: GeneralCommandLine 74 | get() { 75 | 76 | // make parameters 77 | val parameters = mutableListOf("run") 78 | if (runTarget == "all") { 79 | parameters.add("-a") 80 | } else if (runTarget != "" && runTarget != "default") { 81 | parameters.add(runTarget) 82 | } 83 | if (runArguments != "") { 84 | runArguments.split(" ").forEach { 85 | parameters.add(it) 86 | } 87 | } 88 | 89 | // make command line 90 | return project.xmakeConfiguration 91 | .makeCommandLine(parameters, runEnvironment) 92 | .withWorkDirectory(Path(runWorkingDir).toFile()) 93 | .withCharset(Charsets.UTF_8) 94 | } 95 | 96 | // save configuration 97 | override fun writeExternal(element: Element) { 98 | super.writeExternal(element) 99 | 100 | XmlSerializer.serializeInto(this, element) 101 | runEnvironment.writeExternal(element) 102 | } 103 | 104 | // load configuration 105 | override fun readExternal(element: Element) { 106 | super.readExternal(element) 107 | 108 | XmlSerializer.deserializeInto(this, element) 109 | runEnvironment = EnvironmentVariablesData.readExternal(element) 110 | runToolkit = runToolkit?.let { toolkit -> 111 | ToolkitManager.getInstance().findRegisteredToolkitById(toolkit.id) 112 | } 113 | // Todo: Optimize to avoid probing delay. 114 | XMakeInfoManager.getInstance(project).probeXMakeInfo(runToolkit) 115 | } 116 | 117 | override fun checkConfiguration() { 118 | if (runToolkit == null) { 119 | throw RuntimeConfigurationError("XMake toolkit is not set!") 120 | } 121 | 122 | // Todo: Check whether working directory is valid. 123 | if (runWorkingDir.isBlank()){ 124 | throw RuntimeConfigurationError("Working directory is not set!") 125 | } 126 | } 127 | 128 | override fun getConfigurationEditor(): SettingsEditor = 129 | XMakeRunConfigurationEditor(project, this) 130 | 131 | override fun getState(executor: Executor, environment: ExecutionEnvironment): RunProfileState? { 132 | 133 | // clear console first 134 | project.xmakeConsoleView.clear() 135 | 136 | // configure and run it 137 | val xmakeConfiguration = project.xmakeConfiguration 138 | if (xmakeConfiguration.changed) { 139 | SystemUtils.runvInConsole(project, xmakeConfiguration.configurationCommandLine) 140 | ?.addProcessListener(object : ProcessAdapter() { 141 | override fun processTerminated(e: ProcessEvent) { 142 | SystemUtils.runvInConsole(project, runCommandLine, false, true, true) 143 | } 144 | }) 145 | xmakeConfiguration.changed = false 146 | } else { 147 | SystemUtils.runvInConsole(project, runCommandLine, true, true, true) 148 | } 149 | 150 | // does not use builtin run console panel 151 | return null 152 | } 153 | 154 | val platforms: Array 155 | get() = project.xmakeInfo.architectures.keys.plus("default").toTypedArray() 156 | 157 | val toolchains: Array 158 | get() = project.xmakeInfo.toolchains.keys.plus("default").toTypedArray() 159 | 160 | val modes: Array 161 | get() = project.xmakeInfo.buildModes.map { it.substringAfter('.') }.toTypedArray() 162 | 163 | fun getArchitecturesByPlatform(platform: String): Array { 164 | return (project.xmakeInfo.architectures[platform]?.toTypedArray() ?: arrayOf("default")) 165 | } 166 | 167 | companion object { 168 | private val Log = Logger.getInstance(XMakeRunConfiguration::class.java.getName()) 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/run/XMakeRunConfigurationEditor.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.run 2 | 3 | import com.intellij.execution.configuration.EnvironmentVariablesTextFieldWithBrowseButton 4 | import com.intellij.openapi.diagnostic.Logger 5 | import com.intellij.openapi.options.SettingsEditor 6 | import com.intellij.openapi.project.Project 7 | import com.intellij.openapi.ui.ComboBox 8 | import com.intellij.ui.PopupMenuListenerAdapter 9 | import com.intellij.ui.RawCommandLineEditor 10 | import com.intellij.ui.components.CheckBox 11 | import com.intellij.ui.dsl.builder.AlignX 12 | import com.intellij.ui.dsl.builder.AlignY 13 | import com.intellij.ui.dsl.builder.RowLayout 14 | import com.intellij.ui.dsl.builder.panel 15 | import com.intellij.ui.layout.ComboBoxPredicate 16 | import io.xmake.project.directory.ui.DirectoryBrowser 17 | import io.xmake.project.target.TargetManager 18 | import io.xmake.project.toolkit.Toolkit 19 | import io.xmake.project.toolkit.ui.ToolkitComboBox 20 | import io.xmake.project.toolkit.ui.ToolkitListItem 21 | import io.xmake.shared.xmakeConfiguration 22 | import io.xmake.utils.execute.SyncDirection 23 | import io.xmake.utils.execute.transferFolderByToolkit 24 | import kotlinx.coroutines.CoroutineScope 25 | import kotlinx.coroutines.Dispatchers 26 | import kotlinx.coroutines.launch 27 | import java.awt.Dimension 28 | import javax.swing.DefaultComboBoxModel 29 | import javax.swing.JComponent 30 | import javax.swing.JPanel 31 | import javax.swing.event.PopupMenuEvent 32 | 33 | class XMakeRunConfigurationEditor( 34 | private val project: Project, 35 | private val runConfiguration: XMakeRunConfiguration, 36 | ) : SettingsEditor() { 37 | 38 | private val scope = CoroutineScope(Dispatchers.Default) 39 | 40 | private var toolkit: Toolkit? = runConfiguration.runToolkit 41 | private val toolkitComboBox = ToolkitComboBox(::toolkit) 42 | 43 | // the targets ui 44 | private val targetsModel = DefaultComboBoxModel() 45 | private val targetsComboBox = ComboBox(targetsModel).apply { item = runConfiguration.runTarget } 46 | 47 | private val platformsModel = DefaultComboBoxModel(runConfiguration.platforms) 48 | private val platformsComboBox = ComboBox(platformsModel).apply { item = runConfiguration.runPlatform } 49 | 50 | private val architecturesModel = 51 | DefaultComboBoxModel(runConfiguration.getArchitecturesByPlatform(runConfiguration.runPlatform)) 52 | private val architecturesComboBox = ComboBox(architecturesModel).apply { item = runConfiguration.runArchitecture } 53 | 54 | private val toolchainsModel = DefaultComboBoxModel(runConfiguration.toolchains) 55 | private val toolchainsComboBox = ComboBox(toolchainsModel).apply { item = runConfiguration.runToolchain } 56 | 57 | private val modesModel = DefaultComboBoxModel(runConfiguration.modes) 58 | private val modesComboBox = ComboBox(modesModel).apply { item = runConfiguration.runMode } 59 | 60 | private val runArguments = RawCommandLineEditor() 61 | 62 | private val environmentVariables = EnvironmentVariablesTextFieldWithBrowseButton() 63 | 64 | private val workingDirectoryBrowser = DirectoryBrowser(project).apply { text = runConfiguration.runWorkingDir } 65 | 66 | private val buildDirectoryBrowser = DirectoryBrowser(project).apply { text = runConfiguration.buildDirectory } 67 | 68 | private val androidNDKDirectoryBrowser = 69 | DirectoryBrowser(project).apply { text = runConfiguration.androidNDKDirectory } 70 | 71 | private var enableVerbose: Boolean = runConfiguration.enableVerbose 72 | 73 | private val enableVerboseCheckBox = CheckBox("Enable verbose output", enableVerbose) 74 | 75 | private val additionalConfiguration = RawCommandLineEditor() 76 | 77 | // reset editor from configuration 78 | override fun resetEditorFrom(configuration: XMakeRunConfiguration) { 79 | 80 | toolkit = configuration.runToolkit 81 | 82 | // reset targets 83 | targetsModel.removeAllElements() 84 | 85 | targetsModel.selectedItem = configuration.runTarget 86 | 87 | platformsComboBox.item = configuration.runPlatform 88 | 89 | architecturesComboBox.item = configuration.runArchitecture 90 | 91 | toolchainsComboBox.item = configuration.runToolchain 92 | 93 | modesComboBox.item = configuration.runMode 94 | 95 | // reset run arguments 96 | runArguments.text = configuration.runArguments 97 | 98 | // reset environment variables 99 | environmentVariables.data = configuration.runEnvironment 100 | 101 | workingDirectoryBrowser.text = configuration.runWorkingDir 102 | 103 | buildDirectoryBrowser.text = configuration.buildDirectory 104 | 105 | androidNDKDirectoryBrowser.text = configuration.androidNDKDirectory 106 | 107 | enableVerbose = configuration.enableVerbose 108 | 109 | additionalConfiguration.text = configuration.additionalConfiguration 110 | } 111 | 112 | // apply editor to configuration 113 | override fun applyEditorTo(configuration: XMakeRunConfiguration) { 114 | 115 | configuration.runToolkit = toolkit 116 | 117 | configuration.runTarget = (targetsModel.selectedItem ?: "").toString() 118 | 119 | configuration.runPlatform = platformsComboBox.item 120 | 121 | configuration.runArchitecture = architecturesComboBox.item 122 | 123 | configuration.runToolchain = toolchainsComboBox.item 124 | 125 | configuration.runMode = modesComboBox.item 126 | 127 | configuration.runArguments = runArguments.text 128 | 129 | configuration.runEnvironment = environmentVariables.data 130 | 131 | configuration.runWorkingDir = workingDirectoryBrowser.text 132 | 133 | configuration.buildDirectory = buildDirectoryBrowser.text 134 | 135 | configuration.androidNDKDirectory = androidNDKDirectoryBrowser.text 136 | 137 | configuration.enableVerbose = enableVerbose 138 | 139 | configuration.additionalConfiguration = additionalConfiguration.text 140 | 141 | project.xmakeConfiguration.changed = true 142 | } 143 | 144 | // create editor 145 | override fun createEditor(): JComponent = panel { 146 | 147 | row("Xmake Toolkit:") { 148 | cell(toolkitComboBox).align(AlignX.FILL).applyToComponent { 149 | // Todo: Store previously selected toolkit to restore it if not applied. 150 | addToolkitChangedListener { toolkit -> 151 | workingDirectoryBrowser.removeBrowserAllListener() 152 | buildDirectoryBrowser.removeBrowserAllListener() 153 | androidNDKDirectoryBrowser.removeBrowserAllListener() 154 | toolkit?.let { 155 | workingDirectoryBrowser.addBrowserListenerByToolkit(it) 156 | buildDirectoryBrowser.addBrowserListenerByToolkit(it) 157 | androidNDKDirectoryBrowser.addBrowserListenerByToolkit(it) 158 | } 159 | } 160 | activatedToolkit?.let { 161 | workingDirectoryBrowser.addBrowserListenerByToolkit(it) 162 | buildDirectoryBrowser.addBrowserListenerByToolkit(it) 163 | androidNDKDirectoryBrowser.addBrowserListenerByToolkit(it) 164 | } 165 | } 166 | } 167 | 168 | row { 169 | label("Configuration:").align(AlignY.TOP) 170 | panel { 171 | row { 172 | label("Platform:") 173 | } 174 | row { 175 | cell(platformsComboBox).applyToComponent { 176 | addItemListener { 177 | val architectures = runConfiguration.getArchitecturesByPlatform(selectedItem as String) 178 | with(architecturesModel) { 179 | removeAllElements() 180 | addAll(architectures.toMutableList()) 181 | selectedItem = architectures.first() 182 | } 183 | } 184 | }.align(AlignX.FILL) 185 | } 186 | }.resizableColumn() 187 | panel { 188 | row { 189 | label("Architecture:") 190 | } 191 | row { 192 | cell(architecturesComboBox).align(AlignX.FILL) 193 | } 194 | }.resizableColumn() 195 | panel { 196 | row { 197 | label("Toolchain:") 198 | } 199 | row { 200 | cell(toolchainsComboBox).align(AlignX.FILL) 201 | } 202 | }.resizableColumn() 203 | }.layout(RowLayout.PARENT_GRID) 204 | 205 | separator() 206 | 207 | row("Target:") { 208 | cell(targetsComboBox).applyToComponent { 209 | addPopupMenuListener(object : PopupMenuListenerAdapter() { 210 | override fun popupMenuWillBecomeVisible(e: PopupMenuEvent?) { 211 | super.popupMenuWillBecomeVisible(e) 212 | targetsModel.removeAllElements() 213 | with(runConfiguration) { 214 | if (runToolkit != null && runWorkingDir.isNotEmpty()) { 215 | TargetManager.getInstance(project) 216 | .detectXMakeTarget(runToolkit!!, runConfiguration.runWorkingDir).forEach { target -> 217 | targetsModel.addElement(target) 218 | } 219 | } 220 | } 221 | } 222 | }) 223 | }.align(AlignX.FILL).resizableColumn() 224 | label("Mode:").align(AlignX.FILL) 225 | cell(modesComboBox).align(AlignX.FILL) 226 | } 227 | 228 | row("Program arguments:") { 229 | cell(runArguments).align(AlignX.FILL) 230 | } 231 | // environmentVariables.label 232 | row("Environment variables") { 233 | cell(environmentVariables).align(AlignX.FILL) 234 | } 235 | 236 | row("Working directory") { 237 | cell(workingDirectoryBrowser).align(AlignX.FILL) 238 | } 239 | 240 | collapsibleGroup("Additional Configurations") { 241 | row("Build directory") { 242 | cell(buildDirectoryBrowser).align(AlignX.FILL) 243 | } 244 | 245 | row("Android NDK directory") { 246 | cell(androidNDKDirectoryBrowser).align(AlignX.FILL) 247 | } 248 | 249 | row("Additional Configuration") { 250 | cell(additionalConfiguration).align(AlignX.FILL) 251 | } 252 | 253 | row("") { 254 | cell(enableVerboseCheckBox) 255 | } 256 | } 257 | 258 | 259 | row("Sync Directory:") { 260 | button("Upload") { 261 | toolkitComboBox.activatedToolkit?.let { toolkit -> 262 | val workingDirectoryPath = workingDirectoryBrowser.text 263 | 264 | scope.launch(Dispatchers.IO) { 265 | if (toolkit.isOnRemote) { 266 | transferFolderByToolkit( 267 | project, 268 | toolkit, 269 | SyncDirection.UPSTREAM_TO_LOCAL, 270 | workingDirectoryPath, 271 | null 272 | ) 273 | } 274 | } 275 | } 276 | } 277 | }.visibleIf(ComboBoxPredicate(toolkitComboBox) { 278 | val toolkit = (it as? ToolkitListItem.ToolkitItem)?.toolkit 279 | toolkit?.isOnRemote ?: false 280 | }) 281 | } 282 | 283 | private fun JPanel.makeWide() { 284 | preferredSize = Dimension(1000, height) 285 | } 286 | 287 | companion object { 288 | 289 | // get log 290 | private val Log = Logger.getInstance(XMakeRunConfigurationEditor::class.java.getName()) 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/run/XMakeRunConfigurationProducer.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.run 2 | 3 | import com.intellij.execution.actions.ConfigurationContext 4 | import com.intellij.execution.actions.LazyRunConfigurationProducer 5 | import com.intellij.execution.configurations.ConfigurationFactory 6 | import com.intellij.openapi.diagnostic.Logger 7 | import com.intellij.openapi.util.Ref 8 | import com.intellij.psi.PsiElement 9 | 10 | class XMakeRunConfigurationProducer : LazyRunConfigurationProducer() { 11 | override fun getConfigurationFactory(): ConfigurationFactory { 12 | return XMakeRunConfigurationType.getInstance().factory 13 | } 14 | 15 | override fun isConfigurationFromContext( 16 | configuration: XMakeRunConfiguration, 17 | context: ConfigurationContext 18 | ): Boolean { 19 | 20 | Log.info("isConfigurationFromContext") 21 | return false 22 | } 23 | 24 | override fun setupConfigurationFromContext( 25 | configuration: XMakeRunConfiguration, 26 | context: ConfigurationContext, 27 | sourceElement: Ref 28 | ): Boolean { 29 | 30 | Log.info("setupConfigurationFromContext") 31 | return true 32 | } 33 | 34 | companion object { 35 | 36 | // get log 37 | private val Log = Logger.getInstance(XMakeRunConfigurationProducer::class.java.getName()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/run/XMakeRunConfigurationType.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.run 2 | 3 | import com.intellij.execution.BeforeRunTask 4 | import com.intellij.execution.configurations.ConfigurationFactory 5 | import com.intellij.execution.configurations.ConfigurationTypeBase 6 | import com.intellij.execution.configurations.ConfigurationTypeUtil 7 | import com.intellij.execution.configurations.RunConfiguration 8 | import com.intellij.openapi.diagnostic.Logger 9 | import com.intellij.openapi.project.Project 10 | import com.intellij.openapi.util.Key 11 | import io.xmake.icons.XMakeIcons 12 | 13 | class XMakeRunConfigurationType : ConfigurationTypeBase( 14 | "XMakeRunConfiguration", 15 | "XMake", 16 | "XMake run command configuration", 17 | XMakeIcons.XMAKE 18 | ) { 19 | init { 20 | addFactory(object : ConfigurationFactory(this) { 21 | override fun createTemplateConfiguration(project: Project): RunConfiguration = 22 | XMakeRunConfiguration(project, "XMake", this) 23 | 24 | override fun configureBeforeRunTaskDefaults( 25 | providerID: Key>>, 26 | task: BeforeRunTask> 27 | ) { 28 | 29 | // if (providerID == CompileStepBeforeRun.ID) { 30 | // // We don't use jps, so we don't need to execute `Make` task 31 | // // before run configuration is executed 32 | // task.isEnabled = false 33 | // } 34 | } 35 | 36 | // This value gets written to the config file. By default it defers to getName, however, 37 | // the value needs to be kept the same even if the display name changes in the future 38 | // in order to maintain compatibility with older configs. 39 | override fun getId() = "Start and Debug" 40 | }) 41 | } 42 | 43 | val factory: ConfigurationFactory get() = configurationFactories.single() 44 | 45 | companion object { 46 | fun getInstance(): XMakeRunConfigurationType = 47 | ConfigurationTypeUtil.findConfigurationType(XMakeRunConfigurationType::class.java) 48 | 49 | // get log 50 | private val Log = Logger.getInstance(XMakeRunConfigurationType::class.java.getName()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/run/XMakeRunner.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.run 2 | 3 | import com.intellij.execution.configurations.RunProfile 4 | import com.intellij.execution.configurations.RunProfileState 5 | import com.intellij.execution.executors.DefaultRunExecutor 6 | import com.intellij.execution.runners.ExecutionEnvironment 7 | import com.intellij.execution.ui.RunContentDescriptor 8 | import com.intellij.openapi.diagnostic.Logger 9 | 10 | open class XMakeRunner : XMakeDefaultRunner() { 11 | 12 | override fun canRun(executorId: String, profile: RunProfile): Boolean { 13 | return executorId == DefaultRunExecutor.EXECUTOR_ID && profile is XMakeRunConfiguration 14 | } 15 | 16 | override fun getRunnerId(): String = "XMakeRunner" 17 | 18 | override fun doExecute(state: RunProfileState, environment: ExecutionEnvironment): RunContentDescriptor? { 19 | val configuration = environment.runProfile 20 | return if (configuration is XMakeRunConfiguration) { 21 | super.doExecute(state, environment) 22 | } else { 23 | null 24 | } 25 | } 26 | companion object { 27 | // get log 28 | private val Log = Logger.getInstance(XMakeRunner::class.java.name) 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/shared/XMakeConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.shared 2 | 3 | import com.intellij.execution.RunManager 4 | import com.intellij.execution.configuration.EnvironmentVariablesData 5 | import com.intellij.execution.configurations.GeneralCommandLine 6 | import com.intellij.openapi.components.Service 7 | import com.intellij.openapi.diagnostic.Logger 8 | import com.intellij.openapi.project.Project 9 | import io.xmake.project.toolkit.activatedToolkit 10 | import io.xmake.run.XMakeRunConfiguration 11 | import io.xmake.utils.exception.XMakeRunConfigurationNotSetException 12 | 13 | @Service(Service.Level.PROJECT) 14 | class XMakeConfiguration(val project: Project) { 15 | 16 | val configuration: XMakeRunConfiguration 17 | get() { 18 | return RunManager.getInstance(project).selectedConfiguration?.configuration as? XMakeRunConfiguration 19 | ?: throw XMakeRunConfigurationNotSetException() 20 | } 21 | 22 | // the build command line 23 | val buildCommandLine: GeneralCommandLine 24 | get() { 25 | 26 | // make parameters 27 | val parameters = mutableListOf("-y") 28 | if (configuration.enableVerbose) { 29 | parameters.add("-v") 30 | } 31 | 32 | // make command line 33 | return makeCommandLine(parameters) 34 | } 35 | 36 | // the rebuild command line 37 | val rebuildCommandLine: GeneralCommandLine 38 | get() { 39 | 40 | // make parameters 41 | val parameters = mutableListOf("-r", "-y") 42 | if (configuration.enableVerbose) { 43 | parameters.add("-v") 44 | } 45 | 46 | // make command line 47 | return makeCommandLine(parameters) 48 | } 49 | 50 | // the clean command line 51 | val cleanCommandLine: GeneralCommandLine 52 | get() { 53 | 54 | // make parameters 55 | val parameters = mutableListOf("c") 56 | if (configuration.enableVerbose) { 57 | parameters.add("-v") 58 | } 59 | 60 | // make command line 61 | return makeCommandLine(parameters) 62 | } 63 | 64 | // the clean configuration command line 65 | val cleanConfigurationCommandLine: GeneralCommandLine 66 | get() { 67 | 68 | // make parameters 69 | val parameters = mutableListOf("f", "-c", "-y", "--policies=run.autobuild") 70 | if (configuration.enableVerbose) { 71 | parameters.add("-v") 72 | } 73 | if (configuration.buildDirectory != "") { 74 | parameters.add("-o") 75 | parameters.add(configuration.buildDirectory) 76 | } 77 | 78 | // make command line 79 | return makeCommandLine(parameters) 80 | } 81 | 82 | // the configuration command line 83 | val configurationCommandLine: GeneralCommandLine 84 | get() { 85 | 86 | // make parameters 87 | val parameters = 88 | mutableListOf( 89 | "f", 90 | "-y", 91 | "-m", 92 | configuration.runMode, 93 | "--policies=run.autobuild" 94 | ) 95 | if (configuration.runPlatform != "default") { 96 | parameters.addAll(listOf("-p", configuration.runPlatform)) 97 | } 98 | if (configuration.runArchitecture != "default") { 99 | parameters.addAll(listOf("-a", configuration.runArchitecture)) 100 | } 101 | if (configuration.runToolchain != "default" ) { 102 | parameters.add("--toolchain=${configuration.runToolchain}") 103 | } 104 | if (configuration.runPlatform == "android" && configuration.androidNDKDirectory != "") { 105 | parameters.add("--ndk=\"${configuration.androidNDKDirectory}\"") 106 | } 107 | if (configuration.enableVerbose) { 108 | parameters.add("-v") 109 | } 110 | if (configuration.buildDirectory != "") { 111 | parameters.add("-o") 112 | parameters.add(configuration.buildDirectory) 113 | } 114 | if (configuration.additionalConfiguration != "") { 115 | parameters.add(configuration.additionalConfiguration) 116 | } 117 | 118 | // make command line 119 | return makeCommandLine(parameters) 120 | } 121 | 122 | // the quick start command line 123 | val quickStartCommandLine: GeneralCommandLine 124 | get() { 125 | 126 | // make parameters 127 | val parameters = mutableListOf("f", "-y") 128 | if (configuration.enableVerbose) { 129 | parameters.add("-v") 130 | } 131 | 132 | // make command line 133 | return makeCommandLine(parameters) 134 | } 135 | 136 | val updateCmakeListsCommandLine: GeneralCommandLine 137 | get() = makeCommandLine(mutableListOf("project", "-k", "cmake", "-y")) 138 | 139 | val updateCompileCommandsLine: GeneralCommandLine 140 | get() = makeCommandLine(mutableListOf("project", "-k", "compile_commands")) 141 | 142 | 143 | // configuration is changed? 144 | var changed = true 145 | 146 | // make command line 147 | fun makeCommandLine( 148 | parameters: List, 149 | environmentVariables: EnvironmentVariablesData = EnvironmentVariablesData.DEFAULT 150 | ): GeneralCommandLine { 151 | 152 | // make command 153 | return GeneralCommandLine(project.activatedToolkit!!.path) 154 | .withParameters(parameters) 155 | .withCharset(Charsets.UTF_8) 156 | // Todo: Check if correct. 157 | .withWorkDirectory( 158 | configuration.runWorkingDir 159 | ) 160 | .withEnvironment(environmentVariables.envs) 161 | .withRedirectErrorStream(true) 162 | } 163 | 164 | /* // ensure state 165 | private fun ensureState() { 166 | if (configuration.runArchitecture == "" && architectures.isNotEmpty()) { 167 | configuration.runArchitecture = architectures[0] 168 | } 169 | }*/ 170 | 171 | companion object { 172 | // get log 173 | private val Log = Logger.getInstance(XMakeConfiguration::class.java.getName()) 174 | } 175 | } 176 | 177 | val Project.xmakeConfiguration: XMakeConfiguration 178 | get() = this.getService(XMakeConfiguration::class.java) 179 | ?: error("Failed to get XMakeConfiguration for $this") 180 | 181 | val Project.xmakeConfigurationOrNull: XMakeConfiguration? 182 | get() = this.getService(XMakeConfiguration::class.java) ?: null 183 | 184 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/shared/XMakeProblem.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.shared 2 | 3 | class XMakeProblem( 4 | val file: String? = null, 5 | val line: String? = "0", 6 | val column: String? = "0", 7 | val kind: String? = "error", 8 | val message: String? = "" 9 | ) { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/Command.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils 2 | 3 | import com.intellij.execution.configurations.GeneralCommandLine 4 | import com.intellij.execution.process.ProcessNotCreatedException 5 | import com.intellij.notification.NotificationGroupManager 6 | import com.intellij.notification.NotificationType 7 | import com.intellij.openapi.application.ApplicationManager 8 | import com.intellij.openapi.project.ProjectManager 9 | import io.xmake.project.toolkit.activatedToolkit 10 | import io.xmake.utils.exception.XMakeToolkitNotSetException 11 | import io.xmake.utils.execute.createProcess 12 | import io.xmake.utils.execute.runProcess 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.runBlocking 15 | import java.util.concurrent.Callable 16 | import java.util.concurrent.Executors 17 | import java.util.concurrent.Future 18 | 19 | /** 20 | * [ioRunv] 21 | * 22 | * don't use it easily, it will block the main thread 23 | * 24 | * @param argv the command arguments 25 | * @param workDir the working directory 26 | * @return a List, the all output of the command 27 | * 28 | * error: return empty list -> emptyList() 29 | */ 30 | fun ioRunv(argv: List, workDir: String? = null): List { 31 | val commandLine: GeneralCommandLine = GeneralCommandLine(argv) 32 | .withWorkDirectory(workDir) 33 | .withCharset(Charsets.UTF_8) 34 | .withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE) 35 | val project = ProjectManager.getInstance().defaultProject 36 | try { 37 | val activatedToolkit = project.activatedToolkit ?: throw XMakeToolkitNotSetException() 38 | val (result, exitCode) = runBlocking(Dispatchers.Default) { 39 | runProcess(commandLine.createProcess(activatedToolkit)) 40 | } 41 | return result.getOrDefault("").split(Regex("\\s+")) 42 | } catch (e: XMakeToolkitNotSetException) { 43 | NotificationGroupManager.getInstance() 44 | .getNotificationGroup("XMake") 45 | .createNotification("Error with XMake Toolkit", e.message ?: "", NotificationType.ERROR) 46 | .notify(project) 47 | return emptyList() 48 | } catch (e: ProcessNotCreatedException) { 49 | return emptyList() 50 | } 51 | } 52 | 53 | fun ioRunvInPool(argv: List, workDir: String? = null): List { 54 | val callable: Callable> = Callable { 55 | return@Callable ioRunv(argv, workDir) 56 | } 57 | return ApplicationManager.getApplication().executeOnPooledThread(callable).get() 58 | } 59 | 60 | /** 61 | * [ioRunvBackground] 62 | * 63 | * processing in background 64 | * @param argv the command arguments 65 | * @param workDir the working directory 66 | * @return Unit (void) 67 | * 68 | * error: pop up prompt in the lower right corner if the operation fails 69 | */ 70 | fun ioRunvBackground(argv: List, workDir: String? = null): Unit { 71 | TODO() 72 | } 73 | 74 | /** 75 | * [ioRunvNonBlock] 76 | * 77 | * will not block the main thread, will poll for return 78 | * 79 | * @param argv the command arguments 80 | * @param workDir the working directory 81 | * @return a List, the all output of the command 82 | * 83 | * error: return empty list -> emptyList() 84 | */ 85 | fun ioRunvNonBlock(argv: List, workDir: String? = null): List{ 86 | TODO() 87 | } 88 | 89 | fun ioRunvSingle(argv: List, workDir: String? = null): List { 90 | val call = Callable { 91 | return@Callable ioRunv(argv, workDir) 92 | } 93 | 94 | val executor = Executors.newSingleThreadExecutor() 95 | val commandOutput: Future> = executor.submit(call) 96 | val result = commandOutput.get() 97 | executor.shutdown() 98 | return result 99 | } 100 | 101 | /** 102 | * [vRunv] 103 | * 104 | * output on console 105 | * @param console the console 106 | * @param argv the command arguments 107 | * @param workDir the working directory 108 | * @return void 109 | */ 110 | inline fun vRunv(console: String/*TODO()*/, argv: List, workDir: String? = null) { 111 | TODO() 112 | } 113 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/SystemUtils.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils 2 | 3 | import com.intellij.execution.configurations.GeneralCommandLine 4 | import com.intellij.execution.process.ProcessNotCreatedException 5 | import com.intellij.notification.NotificationGroupManager 6 | import com.intellij.notification.NotificationType 7 | import com.intellij.openapi.project.Project 8 | import com.intellij.openapi.util.SystemInfo 9 | import com.intellij.openapi.vfs.VirtualFile 10 | import io.xmake.project.toolkit.activatedToolkit 11 | import io.xmake.shared.XMakeProblem 12 | import io.xmake.utils.exception.XMakeToolkitNotSetException 13 | import io.xmake.utils.execute.createProcess 14 | import io.xmake.utils.execute.runProcessWithHandler 15 | import kotlinx.coroutines.Dispatchers 16 | import kotlinx.coroutines.runBlocking 17 | import java.nio.file.Path 18 | import java.nio.file.Paths 19 | import java.util.regex.Pattern 20 | 21 | object SystemUtils { 22 | 23 | // get platform 24 | fun platform(): String = when { 25 | SystemInfo.isWindows -> "windows" 26 | SystemInfo.isMac -> "macosx" 27 | else -> "linux" 28 | } 29 | 30 | // parse problems for the given line 31 | fun parseProblem(info: String): XMakeProblem? { 32 | 33 | if (SystemInfo.isWindows) { 34 | 35 | // gbk => utf8 36 | val info_utf8 = String(info.toByteArray(), charset("UTF-8")) 37 | 38 | // parse problem info 39 | val pattern = Pattern.compile("(.*?)\\(([0-9]*)\\): (.*?) .*?: (.*)") 40 | val matcher = pattern.matcher(info_utf8) 41 | if (matcher.find()) { 42 | val file = matcher.group(1) 43 | val line = matcher.group(2) 44 | val kind = matcher.group(3) 45 | val message = matcher.group(4) 46 | return XMakeProblem(file, line, "0", kind, message) 47 | } 48 | 49 | } else { 50 | 51 | // parse problem info 52 | val pattern = Pattern.compile("^(error: )?(.*?):([0-9]*):([0-9]*): (.*?): (.*)\$") 53 | val matcher = pattern.matcher(info) 54 | if (matcher.find()) { 55 | val file = matcher.group(2) 56 | val line = matcher.group(3) 57 | val column = matcher.group(4) 58 | val kind = matcher.group(5) 59 | val message = matcher.group(6) 60 | return XMakeProblem(file, line, column, kind, message) 61 | } 62 | } 63 | return null 64 | } 65 | 66 | fun runvInConsole( 67 | project: Project, 68 | commandLine: GeneralCommandLine, 69 | showConsole: Boolean = true, 70 | showProblem: Boolean = false, 71 | showExitCode: Boolean = false 72 | ) = runProcessWithHandler(project, commandLine, showConsole, showProblem, showExitCode) { 73 | println("runvInConsole: ${it.workDirectory}") 74 | try { 75 | val activatedToolkit = project.activatedToolkit ?: throw XMakeToolkitNotSetException() 76 | runBlocking(Dispatchers.Default) { 77 | commandLine.createProcess(activatedToolkit) 78 | } 79 | } catch (e: XMakeToolkitNotSetException) { 80 | NotificationGroupManager.getInstance() 81 | .getNotificationGroup("XMake.NotificationGroup") 82 | .createNotification("Error with XMake Toolkit", e.message ?: "", NotificationType.ERROR) 83 | .notify(project) 84 | throw ProcessNotCreatedException(e.message ?: "", commandLine) 85 | } 86 | } 87 | 88 | } 89 | 90 | val VirtualFile.pathAsPath: Path get() = Paths.get(path) 91 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/exception/XMakeRunConfigurationNotSetException.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.exception 2 | 3 | import com.intellij.execution.ExecutionException 4 | 5 | class XMakeRunConfigurationNotSetException : ExecutionException("XMake configuration is not selected!") -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/exception/XMakeToolkitNotSetException.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.exception 2 | 3 | import com.intellij.execution.ExecutionException 4 | 5 | class XMakeToolkitNotSetException : ExecutionException("Toolkit is not set!") -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/execute/CommandEx.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.execute 2 | 3 | import com.intellij.execution.configurations.GeneralCommandLine 4 | import com.intellij.execution.process.* 5 | import com.intellij.execution.processTools.getResultStdoutStr 6 | import com.intellij.execution.ui.ConsoleViewContentType 7 | import com.intellij.execution.wsl.WSLCommandLineOptions 8 | import com.intellij.execution.wsl.WSLDistribution 9 | import com.intellij.execution.wsl.WslPath 10 | import com.intellij.execution.wsl.rootMappings 11 | import com.intellij.openapi.diagnostic.fileLogger 12 | import com.intellij.openapi.extensions.ExtensionPointName 13 | import com.intellij.openapi.project.Project 14 | import com.intellij.openapi.util.Key 15 | import com.intellij.util.io.awaitExit 16 | import io.xmake.project.toolkit.Toolkit 17 | import io.xmake.project.toolkit.ToolkitHostType.* 18 | import io.xmake.project.xmakeConsoleView 19 | import io.xmake.project.xmakeOutputPanel 20 | import io.xmake.project.xmakeProblemList 21 | import io.xmake.project.xmakeToolWindow 22 | import io.xmake.shared.XMakeProblem 23 | import io.xmake.utils.SystemUtils.parseProblem 24 | import io.xmake.utils.extension.ToolkitHostExtension 25 | import kotlinx.coroutines.Dispatchers 26 | import kotlinx.coroutines.runBlocking 27 | 28 | private val Log = fileLogger() 29 | 30 | private val EP_NAME: ExtensionPointName = 31 | ExtensionPointName("io.xmake.toolkitHostExtension") 32 | 33 | fun GeneralCommandLine.createLocalProcess(): Process{ 34 | return this 35 | .also { Log.info("commandOnLocal: ${this.commandLineString}") } 36 | .toProcessBuilder().start() 37 | } 38 | 39 | fun GeneralCommandLine.createWslProcess(wslDistribution: WSLDistribution, project: Project? = null): Process { 40 | val commandInWsl: GeneralCommandLine = wslDistribution.patchCommandLine( 41 | object : GeneralCommandLine(this) { 42 | init { 43 | parametersList.clearAll() 44 | } 45 | }, project, 46 | WSLCommandLineOptions().apply { 47 | wslDistribution.rootMappings 48 | isLaunchWithWslExe = true 49 | // remoteWorkingDirectory = workingDirectory?.toCanonicalPath() 50 | } 51 | ).apply { 52 | workDirectory?.let { 53 | withWorkDirectory(WslPath(wslDistribution.id, it.path).toWindowsUncPath()) 54 | } 55 | parametersList.replaceOrAppend(this@createWslProcess.exePath, this@createWslProcess.commandLineString) 56 | } 57 | return commandInWsl 58 | .also { Log.info("commandInWsl: ${commandInWsl.commandLineString}") } 59 | .toProcessBuilder().start() 60 | } 61 | 62 | fun GeneralCommandLine.createProcess(toolkit: Toolkit): Process { 63 | return with(toolkit) { 64 | Log.info("createProcessWithToolkit: $toolkit") 65 | when (host.type) { 66 | LOCAL -> { 67 | this@createProcess.createLocalProcess() 68 | } 69 | 70 | WSL -> { 71 | val wslDistribution = host.target as WSLDistribution 72 | this@createProcess.createWslProcess(wslDistribution) 73 | } 74 | 75 | SSH -> { 76 | with(EP_NAME.extensions.first { it.KEY == "SSH" }) { 77 | createProcess(toolkit.host) 78 | } 79 | } 80 | } 81 | } 82 | } 83 | 84 | suspend fun runProcess(process: Process): Pair, Int>{ 85 | val result = process.getResultStdoutStr() 86 | val exitCode = process.awaitExit() 87 | return Pair(result, exitCode) 88 | } 89 | 90 | fun runProcessWithHandler( 91 | project: Project, 92 | command: GeneralCommandLine, 93 | showConsole: Boolean = true, 94 | showProblem: Boolean = false, 95 | showExitCode: Boolean = false, 96 | createProcess: (GeneralCommandLine) -> Process, 97 | ): ProcessHandler? { 98 | 99 | val process = try { 100 | createProcess(command) 101 | } catch (e: ProcessNotCreatedException) { 102 | return null 103 | } 104 | val processHandler = KillableColoredProcessHandler(process, command.commandLineString, Charsets.UTF_8) 105 | var content = "" 106 | 107 | processHandler.addProcessListener(object : ProcessAdapter() { 108 | override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { 109 | super.onTextAvailable(event, outputType) 110 | project.xmakeConsoleView.print(event.text, ConsoleViewContentType.getConsoleViewType(outputType)) 111 | content += event.text 112 | } 113 | }) 114 | 115 | if (showConsole) { 116 | project.xmakeToolWindow?.show { 117 | project.xmakeOutputPanel.showPanel() 118 | } 119 | } 120 | 121 | if (showProblem) { 122 | processHandler.addProcessListener(object : ProcessAdapter() { 123 | override fun processTerminated(e: ProcessEvent) { 124 | runBlocking(Dispatchers.Default) { 125 | val problems = mutableListOf() 126 | println("Content: $content") 127 | content.split(Regex("\\r\\n|\\n|\\r")).forEach { 128 | val problem = parseProblem(it.trim()) 129 | if (problem !== null) { 130 | problems.add(problem) 131 | } 132 | } 133 | project.xmakeProblemList = problems 134 | } 135 | } 136 | }) 137 | } 138 | 139 | if (showExitCode) { 140 | ProcessTerminatedListener.attach(processHandler) 141 | } 142 | 143 | processHandler.startNotify() 144 | ProcessTerminatedListener.attach(processHandler, project) 145 | return processHandler 146 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/execute/Instruction.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.execute 2 | 3 | import com.intellij.execution.configurations.GeneralCommandLine 4 | import com.intellij.util.containers.map2Array 5 | 6 | val predefinedPath = mapOf( 7 | "windows" to arrayOf(), 8 | "unix" to arrayOf( 9 | // Todo: Add more paths 10 | "\${HOME}/.local/bin", 11 | "/usr/local/bin", 12 | "/usr/bin", 13 | "/opt/homebrew/bin" 14 | ) 15 | ) 16 | 17 | val probeEnvCommand = GeneralCommandLine("uname") 18 | .withParameters("-a") 19 | .withCharset(Charsets.UTF_8) 20 | 21 | val probeXmakeLocCommandOnWin = GeneralCommandLine("where.exe") 22 | .withParameters("xmake") 23 | .withCharset(Charsets.UTF_8) 24 | 25 | val probeXmakeLocCommand = GeneralCommandLine("which") 26 | .withParameters(*arrayOf("xmake") 27 | .plus(predefinedPath["unix"]?.map2Array { "$it/xmake" } ?: emptyArray()) 28 | ) 29 | .withCharset(Charsets.UTF_8) 30 | 31 | val probeXmakeVersionCommand 32 | get() = GeneralCommandLine() 33 | .withParameters("--version") 34 | .withCharset(Charsets.UTF_8) 35 | 36 | val probeXmakeTargetCommand = GeneralCommandLine() 37 | .withParameters("l") 38 | .withParameters("-c") 39 | .withParameters(""" 40 | import('core.project.config'); 41 | import('core.project.project'); 42 | config.load(); 43 | for name, _ in pairs((project.targets())) 44 | do print(name) 45 | end 46 | """.trimIndent()) 47 | 48 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/execute/SftpChannelEx.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.execute 2 | 3 | import com.intellij.openapi.diagnostic.logger 4 | import com.intellij.ssh.channels.SftpChannel 5 | import com.intellij.ssh.channels.isDir 6 | import com.intellij.util.io.systemIndependentPath 7 | import kotlin.io.path.Path 8 | 9 | val SftpChannel.Log by lazy { logger() } 10 | 11 | fun SftpChannel.rmRecur(path: String){ 12 | ls(path).forEach { 13 | if (it.attrs.isDir) { 14 | Log.info("recur: ${Path(path, it.name)}") 15 | rmRecur(Path(path, it.name).systemIndependentPath) 16 | } else { 17 | Log.info("rm: ${Path(path, it.name).systemIndependentPath}") 18 | rm(Path(path, it.name).systemIndependentPath) 19 | } 20 | } 21 | Log.info("rmdir: $path") 22 | rmdir(path) 23 | 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/execute/Sync.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.execute 2 | 3 | import com.intellij.execution.RunManager 4 | import com.intellij.execution.target.TargetEnvironment 5 | import com.intellij.execution.target.TargetProgressIndicatorAdapter 6 | import com.intellij.execution.wsl.WSLDistribution 7 | import com.intellij.execution.wsl.target.WslTargetEnvironment 8 | import com.intellij.execution.wsl.target.WslTargetEnvironmentConfiguration 9 | import com.intellij.execution.wsl.target.WslTargetEnvironmentRequest 10 | import com.intellij.openapi.application.EDT 11 | import com.intellij.openapi.application.invokeLater 12 | import com.intellij.openapi.application.runWriteAction 13 | import com.intellij.openapi.diagnostic.fileLogger 14 | import com.intellij.openapi.extensions.ExtensionPointName 15 | import com.intellij.openapi.progress.ProgressIndicator 16 | import com.intellij.openapi.progress.ProgressManager 17 | import com.intellij.openapi.progress.Task 18 | import com.intellij.openapi.progress.util.ProgressIndicatorBase 19 | import com.intellij.openapi.project.Project 20 | import com.intellij.openapi.project.guessProjectDir 21 | import com.intellij.openapi.util.io.toCanonicalPath 22 | import com.intellij.openapi.vfs.VirtualFileManager 23 | import io.xmake.project.toolkit.Toolkit 24 | import io.xmake.project.toolkit.ToolkitHost 25 | import io.xmake.project.toolkit.ToolkitHostType 26 | import io.xmake.run.XMakeRunConfiguration 27 | import io.xmake.utils.extension.ToolkitHostExtension 28 | import kotlinx.coroutines.CoroutineScope 29 | import kotlinx.coroutines.Dispatchers 30 | import kotlinx.coroutines.launch 31 | import kotlinx.coroutines.withContext 32 | import kotlin.io.path.Path 33 | import kotlin.io.path.isDirectory 34 | 35 | private val Log = fileLogger() 36 | 37 | private val EP_NAME: ExtensionPointName = ExtensionPointName("io.xmake.toolkitHostExtension") 38 | 39 | enum class SyncMode { 40 | SYNC_ONLY, 41 | FORCE_SYNC, 42 | } 43 | 44 | enum class SyncStatus { 45 | SUCCESS, 46 | FAILED, 47 | } 48 | 49 | enum class SyncDirection { LOCAL_TO_UPSTREAM, UPSTREAM_TO_LOCAL } 50 | 51 | fun SyncDirection.toBoolean(): Boolean = when (this) { 52 | SyncDirection.LOCAL_TO_UPSTREAM -> false 53 | SyncDirection.UPSTREAM_TO_LOCAL -> true 54 | } 55 | 56 | fun syncProjectByWslSync( 57 | scope: CoroutineScope, 58 | project: Project, 59 | host: ToolkitHost, 60 | direction: SyncDirection, 61 | directoryPath: String, 62 | relativePath: String? = null, 63 | ) { 64 | val wslDistribution = host.target as? WSLDistribution ?: throw IllegalArgumentException() 65 | 66 | ProgressManager.getInstance().runProcessWithProgressAsynchronously( 67 | object : Task.Backgroundable(project, "Sync directory", true) { 68 | override fun run(indicator: ProgressIndicator) { 69 | scope.launch { 70 | indicator.isIndeterminate = true 71 | 72 | /* for (i in 1..100) { 73 | if (indicator.isCanceled) { 74 | break 75 | } 76 | withContext(Dispatchers.EDT) { 77 | indicator.fraction = i / 100.0 78 | indicator.text = "Processing $i%" 79 | } 80 | }*/ 81 | 82 | val wslTargetEnvironmentRequest = WslTargetEnvironmentRequest( 83 | WslTargetEnvironmentConfiguration(wslDistribution) 84 | ).apply { 85 | downloadVolumes.add( 86 | TargetEnvironment.DownloadRoot( 87 | project.guessProjectDir()!!.toNioPath(), 88 | TargetEnvironment.TargetPath.Persistent(directoryPath) 89 | ) 90 | ) 91 | uploadVolumes.add( 92 | TargetEnvironment.UploadRoot( 93 | project.guessProjectDir()!!.toNioPath(), 94 | TargetEnvironment.TargetPath.Persistent(directoryPath), 95 | ).also { println(it.targetRootPath) }.apply { 96 | this.volumeData 97 | } 98 | ) 99 | shouldCopyVolumes = true 100 | } 101 | 102 | val wslTargetEnvironment = WslTargetEnvironment( 103 | wslTargetEnvironmentRequest, 104 | wslDistribution 105 | ) 106 | 107 | when (direction) { 108 | SyncDirection.LOCAL_TO_UPSTREAM -> { 109 | wslTargetEnvironment.uploadVolumes.forEach { root, volume -> 110 | println("upload: ${root.localRootPath}, ${root.targetRootPath}") 111 | volume.upload(relativePath ?: "", TargetProgressIndicatorAdapter(indicator)) 112 | } 113 | } 114 | 115 | SyncDirection.UPSTREAM_TO_LOCAL -> { 116 | wslTargetEnvironment.downloadVolumes.forEach { root, volume -> 117 | volume.download(relativePath ?: "", indicator) 118 | } 119 | } 120 | } 121 | 122 | withContext(Dispatchers.EDT) { 123 | runWriteAction { 124 | VirtualFileManager.getInstance().syncRefresh() 125 | } 126 | } 127 | 128 | } 129 | } 130 | 131 | override fun onCancel() {} 132 | 133 | override fun onFinished() {} 134 | }, 135 | ProgressIndicatorBase() 136 | ) 137 | } 138 | 139 | private val scope = CoroutineScope(Dispatchers.IO) 140 | 141 | fun transferFolderByToolkit( 142 | project: Project, 143 | toolkit: Toolkit, 144 | direction: SyncDirection, 145 | directoryPath: String = (RunManager.getInstance(project).selectedConfiguration?.configuration as XMakeRunConfiguration).runWorkingDir, 146 | relativePath: String? = null, 147 | ) { 148 | 149 | when (toolkit.host.type) { 150 | ToolkitHostType.LOCAL -> { 151 | invokeLater { 152 | runWriteAction { 153 | VirtualFileManager.getInstance().syncRefresh() 154 | } 155 | } 156 | } 157 | ToolkitHostType.WSL -> { 158 | syncProjectByWslSync( 159 | scope, 160 | project, 161 | toolkit.host, 162 | direction, 163 | directoryPath, 164 | relativePath?.let { if (Path(it).isDirectory()) relativePath else null } 165 | ) 166 | } 167 | ToolkitHostType.SSH -> { 168 | val path = relativePath?.let { Path(directoryPath).resolve(relativePath).toCanonicalPath() } 169 | ?: directoryPath 170 | EP_NAME.extensions.first { it.KEY == "SSH" } 171 | .syncProject(scope, project, toolkit.host, direction, path) 172 | } 173 | } 174 | } 175 | 176 | fun syncBeforeFetch(project: Project, toolkit: Toolkit) { 177 | transferFolderByToolkit(project, toolkit, SyncDirection.LOCAL_TO_UPSTREAM, relativePath = null) 178 | } 179 | 180 | fun fetchGeneratedFile(project: Project, toolkit: Toolkit, fileRelatedPath: String) { 181 | transferFolderByToolkit(project, toolkit, SyncDirection.UPSTREAM_TO_LOCAL, relativePath = fileRelatedPath) 182 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/extension/SshToolkitHostExtensionImpl.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.extension 2 | 3 | import ai.grazie.utils.tryRunWithException 4 | import com.intellij.execution.configurations.GeneralCommandLine 5 | import com.intellij.openapi.application.EDT 6 | import com.intellij.openapi.application.runWriteAction 7 | import com.intellij.openapi.diagnostic.logger 8 | import com.intellij.openapi.progress.ProgressIndicator 9 | import com.intellij.openapi.progress.ProgressManager 10 | import com.intellij.openapi.progress.Task 11 | import com.intellij.openapi.progress.util.ProgressIndicatorBase 12 | import com.intellij.openapi.project.Project 13 | import com.intellij.openapi.project.guessProjectDir 14 | import com.intellij.openapi.roots.ProjectRootManager 15 | import com.intellij.openapi.util.text.Formats 16 | import com.intellij.openapi.vfs.VirtualFileManager 17 | import com.intellij.ssh.* 18 | import com.intellij.ssh.channels.SftpChannel 19 | import com.intellij.ssh.config.unified.SshConfig 20 | import com.intellij.ssh.config.unified.SshConfigManager 21 | import com.intellij.ssh.interaction.PlatformSshPasswordProvider 22 | import com.intellij.ssh.ui.sftpBrowser.RemoteBrowserDialog 23 | import com.intellij.ssh.ui.sftpBrowser.SftpRemoteBrowserProvider 24 | import io.xmake.project.directory.ui.DirectoryBrowser 25 | import io.xmake.project.toolkit.Toolkit 26 | import io.xmake.project.toolkit.ToolkitHost 27 | import io.xmake.project.toolkit.ToolkitHostType 28 | import io.xmake.utils.execute.SyncDirection 29 | import io.xmake.utils.execute.rmRecur 30 | import kotlinx.coroutines.* 31 | import java.awt.event.ActionListener 32 | import java.io.File 33 | import kotlin.io.path.Path 34 | 35 | class SshToolkitHostExtensionImpl : ToolkitHostExtension { 36 | 37 | override val KEY: String = "SSH" 38 | 39 | private val sshConfigManager = SshConfigManager.getInstance(null) 40 | 41 | override fun getHostType(): String { 42 | return "SSH" 43 | } 44 | 45 | override fun getToolkitHosts(project: Project?): List { 46 | return sshConfigManager.configs.map { 47 | ToolkitHost(ToolkitHostType.SSH, it) 48 | } 49 | } 50 | 51 | override fun filterRegistered(): (Toolkit) -> Boolean { 52 | return { it.isOnRemote } 53 | } 54 | 55 | override fun createToolkit(host: ToolkitHost, path: String, version: String): Toolkit { 56 | val sshConfig = (host.target as? SshConfig) ?: throw IllegalArgumentException() 57 | val name = sshConfig.presentableShortName 58 | return Toolkit(name, host, path, version) 59 | } 60 | 61 | override fun syncProject( 62 | scope: CoroutineScope, 63 | project: Project, 64 | host: ToolkitHost, 65 | direction: SyncDirection, 66 | remoteDirectory: String, 67 | ) { 68 | val sshConfig = (host.target as? SshConfig) ?: throw IllegalArgumentException() 69 | 70 | ProgressManager.getInstance().runProcessWithProgressAsynchronously( 71 | object : Task.Backgroundable(project, "Sync directory", true) { 72 | override fun run(indicator: ProgressIndicator) { 73 | val commandString = SftpChannelConfig.SftpCommand.detectSftpCommandString 74 | Log.info("Command: $commandString") 75 | 76 | val builder = ConnectionBuilder(sshConfig.host) 77 | .withSshPasswordProvider(PlatformSshPasswordProvider(sshConfig.copyToCredentials())) 78 | 79 | val sourceRoots = ProjectRootManager.getInstance(project).contentRoots 80 | Log.info("Source roots: $sourceRoots") 81 | Log.info("guessProjectDir: " + project.guessProjectDir()) 82 | 83 | scope.launch { 84 | val sftpChannel = builder.openFailSafeSftpChannel() 85 | Log.info("sftpChannel.home" + sftpChannel.home) 86 | 87 | when (direction) { 88 | SyncDirection.LOCAL_TO_UPSTREAM -> { 89 | 90 | Log.runCatching { 91 | tryRunWithException> { 92 | sftpChannel.ls( 93 | remoteDirectory 94 | ) 95 | }.also { Log.info("before: $it") } 96 | sftpChannel.rmRecur(remoteDirectory) 97 | Log.info("after: " + sftpChannel.ls("Project")) 98 | } 99 | 100 | sftpChannel.uploadFileOrDir( 101 | File(project.guessProjectDir()?.path ?: ""), 102 | remoteDir = remoteDirectory, relativePath = "/", 103 | progressTracker = object : SftpProgressTracker { 104 | override val isCanceled: Boolean 105 | get() = false 106 | // TODO("Not yet implemented") 107 | 108 | override fun onBytesTransferred(count: Long) { 109 | println("onBytesTransferred(${Formats.formatFileSize(count)})") 110 | } 111 | 112 | override fun onFileCopied(file: File) { 113 | println("onFileCopied($file)") 114 | } 115 | }, filesFilter = { file -> 116 | mutableListOf(".xmake", ".idea", "build", ".gitignore") 117 | .all { 118 | !file.startsWith( 119 | Path( 120 | project.guessProjectDir()?.path ?: "", 121 | it 122 | ).toFile() 123 | ) 124 | } 125 | }, persistExecutableBit = true 126 | ) 127 | } 128 | 129 | SyncDirection.UPSTREAM_TO_LOCAL -> { 130 | sftpChannel.downloadFileOrDir(remoteDirectory, project.guessProjectDir()?.path ?: "") 131 | } 132 | } 133 | sftpChannel.close() 134 | 135 | withContext(Dispatchers.EDT) { 136 | runWriteAction { 137 | VirtualFileManager.getInstance().syncRefresh() 138 | } 139 | } 140 | 141 | } 142 | } 143 | 144 | override fun onCancel() {} 145 | 146 | override fun onFinished() {} 147 | }, 148 | ProgressIndicatorBase() 149 | ) 150 | } 151 | 152 | override suspend fun ToolkitHost.loadTargetX(project: Project?) = coroutineScope { 153 | target = SshConfigManager.getInstance(project).findConfigById(id!!)!! 154 | } 155 | 156 | override fun getTargetId(target: Any?): String { 157 | val sshConfig = target as? SshConfig ?: throw IllegalArgumentException() 158 | return sshConfig.id 159 | } 160 | 161 | override fun DirectoryBrowser.createBrowseListener(host: ToolkitHost): ActionListener { 162 | val sshConfig = host.target as? SshConfig ?: throw IllegalArgumentException() 163 | 164 | val sftpChannel = runBlocking(Dispatchers.Default) { 165 | ConnectionBuilder(sshConfig.host) 166 | .withSshPasswordProvider(PlatformSshPasswordProvider(sshConfig.copyToCredentials())) 167 | .openFailSafeSftpChannel() 168 | } 169 | val sftpRemoteBrowserProvider = SftpRemoteBrowserProvider(sftpChannel) 170 | val remoteBrowseFolderListener = ActionListener { 171 | text = RemoteBrowserDialog( 172 | sftpRemoteBrowserProvider, 173 | project, 174 | true, 175 | withCreateDirectoryButton = true 176 | ).apply { showAndGet() }.getResult() 177 | } 178 | return remoteBrowseFolderListener 179 | } 180 | 181 | override fun GeneralCommandLine.createProcess(host: ToolkitHost): Process { 182 | 183 | val sshConfig = host.target as? SshConfig ?: throw IllegalArgumentException() 184 | 185 | val builder = ConnectionBuilder(sshConfig.host) 186 | .withSshPasswordProvider(PlatformSshPasswordProvider(sshConfig.copyToCredentials())) 187 | 188 | val command = GeneralCommandLine("sh").withParameters("-c") 189 | .withParameters(this.commandLineString) 190 | .withWorkDirectory(workDirectory) 191 | .withCharset(charset) 192 | .withEnvironment(environment) 193 | .withInput(inputFile) 194 | .withRedirectErrorStream(isRedirectErrorStream) 195 | 196 | return builder 197 | .also { Log.info("commandOnRemote: ${command.commandLineString}") } 198 | .processBuilder(command) 199 | .start() 200 | } 201 | 202 | companion object { 203 | private val Log = logger() 204 | } 205 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/extension/ToolkitHostExtension.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.extension 2 | 3 | import com.intellij.execution.configurations.GeneralCommandLine 4 | import com.intellij.openapi.project.Project 5 | import io.xmake.project.directory.ui.DirectoryBrowser 6 | import io.xmake.project.toolkit.Toolkit 7 | import io.xmake.project.toolkit.ToolkitHost 8 | import io.xmake.utils.execute.SyncDirection 9 | import kotlinx.coroutines.CoroutineScope 10 | import java.awt.event.ActionListener 11 | 12 | interface ToolkitHostExtension { 13 | val KEY: String 14 | 15 | fun getHostType(): String 16 | 17 | fun getToolkitHosts(project: Project? = null): List 18 | 19 | fun filterRegistered(): (Toolkit) -> Boolean 20 | 21 | fun createToolkit(host: ToolkitHost, path: String, version: String): Toolkit 22 | 23 | fun syncProject( 24 | scope: CoroutineScope, 25 | project: Project, 26 | host: ToolkitHost, 27 | direction: SyncDirection, 28 | remoteDirectory: String, 29 | ) 30 | 31 | fun getTargetId(target: Any? = null): String 32 | 33 | suspend fun ToolkitHost.loadTargetX(project: Project? = null) 34 | 35 | fun DirectoryBrowser.createBrowseListener(host: ToolkitHost): ActionListener 36 | 37 | fun GeneralCommandLine.createProcess(host: ToolkitHost): Process 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/info/XMakeInfo.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.info 2 | 3 | import com.intellij.openapi.diagnostic.logger 4 | import kotlinx.serialization.json.Json 5 | 6 | class XMakeInfo { 7 | 8 | var apis: XMakeApis? = null 9 | var architectures: XMakeArchitectures = emptyMap() 10 | var buildModes: XMakeBuildModes = emptyList() 11 | var envs: XMakeEnvs = emptyMap() 12 | var packages: XMakePackages = emptyList() 13 | var platforms: XMakePlatforms = emptyList() 14 | var policies: XMakePolicies = emptyMap() 15 | var rules: XMakeRules = emptyList() 16 | var targets: XMakeTargets = emptyList() 17 | var toolchains: XMakeToolchains = emptyMap() 18 | 19 | fun parseApis(apiString: String): XMakeApis? { 20 | return Json.decodeFromString(apiString) 21 | } 22 | 23 | fun parseArchitectures(archString: String): XMakeArchitectures { 24 | return Json.decodeFromString(archString) 25 | } 26 | 27 | fun parseBuildModes(buildModeString: String): XMakeBuildModes { 28 | return Json.decodeFromString(buildModeString) 29 | } 30 | 31 | fun parseEnvs(envString: String): XMakeEnvs { 32 | // Todo 33 | return emptyMap() 34 | } 35 | 36 | fun parsePackages(packageString: String): XMakePackages { 37 | // Todo 38 | return emptyList() 39 | } 40 | 41 | fun parsePlatforms(platformString: String): XMakePlatforms { 42 | return Json.decodeFromString(platformString) 43 | } 44 | 45 | fun parsePolicies(policyString: String): XMakePolicies { 46 | // Todo 47 | return emptyMap() 48 | } 49 | 50 | fun parseRules(ruleString: String): XMakeRules { 51 | return Json.decodeFromString(ruleString) 52 | } 53 | 54 | fun parseTargets(targetString: String): XMakeTargets { 55 | return Json.decodeFromString(targetString) 56 | } 57 | 58 | fun parseToolchains(toolchainString: String): XMakeToolchains { 59 | return toolchainString.split("\n").associate { 60 | it.split(" ", limit = 2).map { s -> 61 | s.replace(Regex("\u001B\\[[0-9;]*[A-Za-z]"), "") 62 | }.let { (toolchain, description) -> 63 | toolchain to description 64 | } 65 | }.also { Log.info("Parsed XMake Supported Toolchains: ${it.keys}") } 66 | } 67 | 68 | companion object { 69 | val Log = logger() 70 | 71 | } 72 | } 73 | 74 | -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/info/XMakeInfoActivity.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.info 2 | 3 | import com.intellij.openapi.application.ApplicationManager 4 | import com.intellij.openapi.project.Project 5 | import com.intellij.openapi.startup.ProjectActivity 6 | import io.xmake.project.toolkit.Toolkit 7 | import io.xmake.project.toolkit.ToolkitChangedNotifier 8 | 9 | class XMakeInfoActivity : ProjectActivity { 10 | override suspend fun execute(project: Project) { 11 | ApplicationManager.getApplication().messageBus.connect() 12 | .subscribe( 13 | ToolkitChangedNotifier.TOOLKIT_CHANGED_TOPIC, 14 | object : ToolkitChangedNotifier { 15 | override fun toolkitChanged(toolkit: Toolkit?) { 16 | XMakeInfoManager.getInstance(project).probeXMakeInfo(toolkit) 17 | } 18 | } 19 | ) 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/info/XMakeInfoInstance.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.info 2 | 3 | import com.intellij.openapi.project.Project 4 | 5 | val Project.xmakeInfo: XMakeInfo 6 | get() = XMakeInfoManager.getInstance(this).xmakeInfo -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/info/XMakeInfoManager.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.info 2 | 3 | import com.intellij.execution.configurations.GeneralCommandLine 4 | import com.intellij.openapi.components.Service 5 | import com.intellij.openapi.components.serviceOrNull 6 | import com.intellij.openapi.diagnostic.logger 7 | import com.intellij.openapi.project.Project 8 | import io.xmake.project.toolkit.Toolkit 9 | import io.xmake.utils.execute.createProcess 10 | import io.xmake.utils.execute.runProcess 11 | import kotlinx.coroutines.CoroutineScope 12 | import kotlinx.coroutines.launch 13 | 14 | @Service(Service.Level.PROJECT) 15 | class XMakeInfoManager(val project: Project, private val scope: CoroutineScope) { 16 | 17 | val xmakeInfo: XMakeInfo = XMakeInfo() 18 | 19 | // Todo 20 | val cachedXMakeInfoMap: MutableMap = mutableMapOf() 21 | 22 | fun probeXMakeInfo(toolkit: Toolkit?) { 23 | scope.launch { 24 | toolkit?.let { 25 | val apisString = runProcess( 26 | GeneralCommandLine( 27 | "xmake show -l apis --json".split(" ") 28 | ).createProcess(it) 29 | ).first.getOrDefault("") 30 | 31 | val architecturesString = runProcess( 32 | GeneralCommandLine( 33 | "xmake show -l architectures --json".split(" ") 34 | ).createProcess(it) 35 | ).first.getOrDefault("") 36 | 37 | val buildModesString = runProcess( 38 | GeneralCommandLine( 39 | "xmake show -l buildmodes --json".split(" ") 40 | ).createProcess(it) 41 | ).first.getOrDefault("") 42 | 43 | val envsString = runProcess( 44 | GeneralCommandLine( 45 | "xmake show -l envs --json".split(" ") 46 | ).createProcess(it) 47 | ).first.getOrDefault("") 48 | 49 | val packagesString = runProcess( 50 | GeneralCommandLine( 51 | "xmake show -l packages --json".split(" ") 52 | ).createProcess(it) 53 | ).first.getOrDefault("") 54 | 55 | val platformsString = runProcess( 56 | GeneralCommandLine( 57 | "xmake show -l platforms --json".split(" ") 58 | ).createProcess(it) 59 | ).first.getOrDefault("") 60 | 61 | val policiesString = runProcess( 62 | GeneralCommandLine( 63 | "xmake show -l policies --json".split(" ") 64 | ).createProcess(it) 65 | ).first.getOrDefault("") 66 | 67 | val rulesString = runProcess( 68 | GeneralCommandLine( 69 | "xmake show -l rules --json".split(" ") 70 | ).createProcess(it) 71 | ).first.getOrDefault("") 72 | 73 | val targetsString = runProcess( 74 | GeneralCommandLine( 75 | "xmake show -l targets --json".split(" ") 76 | ).createProcess(it) 77 | ).first.getOrDefault("") 78 | 79 | val toolchainsString = runProcess( 80 | GeneralCommandLine( 81 | "xmake show -l toolchains --json".split(" ") 82 | ).createProcess(it) 83 | ).first.getOrDefault("") 84 | 85 | with(xmakeInfo) { 86 | apis = parseApis(apisString) 87 | architectures = parseArchitectures(architecturesString) 88 | buildModes = parseBuildModes(buildModesString) 89 | platforms = parsePlatforms(platformsString) 90 | policies = parsePolicies(policiesString) 91 | rules = parseRules(rulesString) 92 | // targets = parseTargets(targetsString) 93 | toolchains = parseToolchains(toolchainsString) 94 | 95 | Log.info( 96 | "XMake Info: " + 97 | "$apis, " + 98 | "$architectures, " + 99 | "$platforms, " + 100 | "$policies, " + 101 | "$rules, " + 102 | "$targets, " + 103 | "$toolchains" 104 | ) 105 | } 106 | 107 | println(xmakeInfo) 108 | 109 | } 110 | } 111 | } 112 | 113 | companion object { 114 | val Log = logger() 115 | fun getInstance(project: Project): XMakeInfoManager = project.serviceOrNull() ?: throw IllegalStateException() 116 | } 117 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/info/XMakeInfoTypes.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.info 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class XMakeApis( 8 | @SerialName("description_builtin_apis") 9 | val descriptionBuiltinApis: List, 10 | @SerialName("description_builtin_module_apis") 11 | val descriptionBuiltinModuleApis: List, 12 | @SerialName("script_instance_apis") 13 | val scriptInstanceApis: List, 14 | @SerialName("script_extension_module_apis") 15 | val scriptExtensionModuleApis: List, 16 | @SerialName("description_scope_apis") 17 | val descriptionScopeApis: List, 18 | @SerialName("script_builtin_apis") 19 | val scriptBuiltinApis: List, 20 | @SerialName("script_builtin_module_apis") 21 | val scriptBuiltinModuleApis: List, 22 | ) 23 | typealias XMakeArchitectures = Map> 24 | typealias XMakeBuildModes = List 25 | typealias XMakeEnvs = Map 26 | typealias XMakePackages = List 27 | typealias XMakePlatforms = List 28 | typealias XMakePolicies = Map 29 | typealias XMakeRules = List 30 | typealias XMakeTargets = List 31 | typealias XMakeToolchains = Map -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/interact/LocalData.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.interact 2 | 3 | 4 | val kSysEnv: MutableMap? 5 | get() = System.getenv() 6 | 7 | val kLineSep: String 8 | get() = System.lineSeparator() 9 | 10 | val kSysLang: String 11 | get() = System.getProperty("user.language") 12 | 13 | val kSysThreadCount: Int 14 | get() = Runtime.getRuntime().availableProcessors() -------------------------------------------------------------------------------- /src/main/kotlin/io/xmake/utils/interact/XMakeData.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.interact 2 | 3 | import io.xmake.utils.ioRunvInPool 4 | 5 | /* 6 | * ioTemp: return of ioRunv() 7 | */ 8 | 9 | /** 10 | * [kXMakeFind] 11 | * 12 | * @return a boolean, true if xmake is found 13 | */ 14 | val kXMakeFind:Boolean 15 | get() = ioRunvInPool(listOf("xmake", "--version")).isNotEmpty() 16 | 17 | /** 18 | * [kXMakeVersion] 19 | * 20 | * @return string of xmake version 21 | * 22 | * like: "2.8.6 HEAD.211710b67" 23 | * 24 | * error: return empty string -> "" 25 | */ 26 | val kXMakeVersion:String 27 | get() { 28 | val ioTemp: List = ioRunvInPool(listOf("xmake", "--version")) 29 | if (ioTemp.isNotEmpty()) { 30 | val verTemp = ioTemp[0].split(' ')[1] 31 | return verTemp.substring(1, verTemp.length - 1).replace('+', ' ') 32 | } 33 | return "" 34 | } 35 | 36 | val kXMakeInstallDir:String 37 | get() = TODO() 38 | 39 | /** 40 | * [kPlatList] 41 | * 42 | * @return a list of plat string 43 | * 44 | * error: return empty list -> emptyList() 45 | */ 46 | val kPlatList: List 47 | get() { 48 | //TODO("Add: xmake full path as input") 49 | val ioTemp: List = ioRunvInPool(listOf("xmake", "f", "config")) 50 | if (ioTemp.isEmpty()) return emptyList() 51 | 52 | val ret: List 53 | val regex: Regex = Regex("""-\s(\w+)""") 54 | val searchBegin: String = "-p PLAT" 55 | val searchEnd: String = "-a ARCH" 56 | var indexBegin: Int = 0 57 | var indexEnd: Int = 0 58 | 59 | for (index in ioTemp.indices) { 60 | if (!ioTemp[index].contains(searchBegin)) { 61 | continue 62 | } else { 63 | indexBegin = index 64 | break 65 | } 66 | } 67 | 68 | for (index in indexBegin until ioTemp.size) { 69 | if (!ioTemp[index].contains(searchEnd)) { 70 | continue 71 | } else { 72 | indexEnd = index 73 | break 74 | } 75 | } 76 | 77 | val subListTemp = ioTemp.subList(indexBegin + 1, indexEnd) 78 | ret = subListTemp.map { 79 | regex.find(it)?.groups?.get(1)?.value ?: "" 80 | } 81 | return ret 82 | } 83 | 84 | /** 85 | * [kPlatArchMap] 86 | * 87 | * @return a map of plat and arch list 88 | * 89 | * error: return empty map -> emptyMap() 90 | */ 91 | val kPlatArchMap:Map> 92 | get() { 93 | //TODO("Add: xmake full path as input") 94 | val ioTemp: List = ioRunvInPool(listOf("xmake", "f", "config")) 95 | if (ioTemp.isEmpty()) return emptyMap() 96 | 97 | val ret: Map> 98 | val regex: Regex = Regex("""-\s(\w+): (.+)""") 99 | val searchBegin: String = "-a ARCH" 100 | val searchEnd: String = "-m MODE" 101 | var indexBegin: Int = 0 102 | var indexEnd: Int = 0 103 | 104 | for (index in ioTemp.indices) { 105 | if (!ioTemp[index].contains(searchBegin)) { 106 | continue 107 | } else { 108 | indexBegin = index 109 | break 110 | } 111 | } 112 | 113 | for (index in indexBegin until ioTemp.size) { 114 | if (!ioTemp[index].contains(searchEnd)) { 115 | continue 116 | } else { 117 | indexEnd = index 118 | break 119 | } 120 | } 121 | 122 | val subListTemp: List = ioTemp.subList(indexBegin + 1, indexEnd) 123 | ret = subListTemp.associate { 124 | val matchResult = regex.find(it) 125 | val plat = matchResult?.groups?.get(1)?.value ?: "" 126 | val arch = matchResult?.groups?.get(2)?.value ?: "" 127 | plat to arch.split(' ') 128 | } 129 | return ret 130 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/io.xmake-ssh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | io.xmake 4 | XMake 5 | xmake.io 6 | 7 | 8 | 10 | 11 | 12 | 13 | com.intellij.modules.platform 14 | com.intellij.modules.lang 15 | com.intellij.modules.xml 16 | com.intellij.modules.xdebugger 17 | 18 | 19 | com.intellij.modules.ssh 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 60 | 61 | 62 | 63 | 64 | 65 | 70 | 71 | 72 | 75 | 80 | 85 | 90 | 95 | 96 | 97 | 102 | 103 | 108 | 113 | 114 | 115 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | file_type_xmake 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/resources/icons/xmake-dark-fill-translucent.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 21 | 22 | 23 | 25 | 26 | 27 | 29 | 30 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/resources/icons/xmake-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 10 | 11 | 12 | 15 | 16 | 17 | 19 | 20 | 21 | 23 | 24 | 25 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/main/resources/icons/xmake.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | file_type_xmake 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/test/kotlin/io/xmake/TestData.kt: -------------------------------------------------------------------------------- 1 | package io.xmake 2 | 3 | import java.io.BufferedReader 4 | import java.io.File 5 | import java.io.IOException 6 | import java.io.InputStreamReader 7 | 8 | object TestData { 9 | // get from local xmake 10 | val xmakeVersion: String = testIORunv(listOf("xmake", "--version")) 11 | val xmakeFHelp: String= testIORunv(listOf("xmake", "f", "-h")) 12 | 13 | private fun testIORunv(argv: List, workDir: String? = null): String { 14 | var result = "" 15 | var bufferReader: BufferedReader? = null 16 | try { 17 | val processBuilder = ProcessBuilder(argv) 18 | if (workDir !== null) { 19 | processBuilder.directory(File(workDir)) 20 | } 21 | processBuilder.environment().put("COLORTERM", "nocolor") 22 | val process = processBuilder.start() 23 | bufferReader = BufferedReader(InputStreamReader(process.getInputStream())) 24 | var line: String? = bufferReader.readLine() 25 | while (line != null) { 26 | result += line + "\n" 27 | line = bufferReader.readLine() 28 | } 29 | if (process.waitFor() != 0) 30 | result = "" 31 | } catch (e: IOException) { 32 | e.printStackTrace() 33 | } finally { 34 | if (bufferReader != null) { 35 | try { 36 | bufferReader.close() 37 | } catch (e: Exception) { 38 | e.printStackTrace() 39 | } 40 | } 41 | } 42 | return result 43 | } 44 | } -------------------------------------------------------------------------------- /src/test/kotlin/io/xmake/utils/interact/InteractTest.kt: -------------------------------------------------------------------------------- 1 | package io.xmake.utils.interact 2 | 3 | import com.intellij.testFramework.fixtures.BasePlatformTestCase 4 | import io.mockk.every 5 | import io.mockk.junit4.MockKRule 6 | import io.mockk.mockkStatic 7 | import io.xmake.TestData 8 | import io.xmake.utils.ioRunv 9 | import org.junit.Before 10 | import org.junit.Rule 11 | import org.junit.Test 12 | import org.junit.runner.RunWith 13 | import org.junit.runners.JUnit4 14 | 15 | @RunWith(JUnit4::class) 16 | class XMakeVersion:BasePlatformTestCase(){ 17 | val xmakeVersion = TestData.xmakeVersion 18 | 19 | @get:Rule 20 | val mockkRule = MockKRule(this) 21 | 22 | @Before 23 | fun before() { 24 | mockkStatic(::ioRunv) 25 | every { ioRunv(any()) } returns xmakeVersion.split("\n") 26 | } 27 | 28 | @Test 29 | fun `Test kXMakeFind`() { 30 | val testCase: Boolean = true 31 | val result: Boolean = kXMakeFind 32 | assertEquals(testCase, result) 33 | } 34 | 35 | @Test 36 | fun `Test kXMakeVersion`() { 37 | val testCase: String = "2.8.6 HEAD.211710b67" 38 | val result: String = kXMakeVersion 39 | assertEquals(testCase, result) 40 | } 41 | 42 | } 43 | 44 | @RunWith(JUnit4::class) 45 | class XMakeFH:BasePlatformTestCase() { 46 | val xmakeFHelp = TestData.xmakeFHelp 47 | 48 | @get:Rule 49 | val mockkRule = MockKRule(this) 50 | 51 | @Before 52 | fun before() { 53 | mockkStatic(::ioRunv) 54 | every { ioRunv(any()) } returns xmakeFHelp.split("\n") 55 | } 56 | 57 | @Test 58 | fun `Test kPlatList`() { 59 | val testCase: List = listOf( 60 | "android", 61 | "appletvos", 62 | "applexros", 63 | "bsd", 64 | "cross", 65 | "cygwin", 66 | "haiku", 67 | "iphoneos", 68 | "linux", 69 | "macosx", 70 | "mingw", 71 | "msys", 72 | "wasm", 73 | "watchos", 74 | "windows", 75 | ) 76 | val result: List = kPlatList 77 | assertEquals(testCase, result) 78 | } 79 | 80 | @Test 81 | fun `Test kPlatArchMap`(){ 82 | val testCase: Map> = mapOf( 83 | "android" to "armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mip64".split(' '), 84 | "appletvos" to "arm64 armv7 armv7s i386 x86_64".split(' '), 85 | "applexros" to "arm64 armv7 armv7s i386 x86_64".split(' '), 86 | "bsd" to "i386 x86_64".split(' '), 87 | "cross" to "i386 x86_64 arm arm64 mips mips64 riscv riscv64 s390x ppc ppc64 sh4".split(' '), 88 | "cygwin" to "i386 x86_64".split(' '), 89 | "haiku" to "i386 x86_64".split(' '), 90 | "iphoneos" to "arm64 x86_64".split(' '), 91 | "linux" to "i386 x86_64 armv7 armv7s arm64-v8a mips mips64 mipsel mips64el loongarch64".split(' '), 92 | "macosx" to "x86_64 arm64".split(' '), 93 | "mingw" to "i386 x86_64 arm arm64".split(' '), 94 | "msys" to "i386 x86_64".split(' '), 95 | "wasm" to "wasm32 wasm64".split(' '), 96 | "watchos" to "armv7k i386".split(' '), 97 | "windows" to "x86 x64 arm64".split(' '), 98 | ) 99 | val result: Map> = kPlatArchMap 100 | assertEquals(testCase, result) 101 | } 102 | } --------------------------------------------------------------------------------