├── .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 |
19 |
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 |
3 | [NEW]: update pluginUntilBuild
4 |
5 | 1.4.5
6 |
7 | [NEW]: and xmake language
8 |
9 | 1.4.3
10 |
11 | [NEW]: update APIs
12 |
13 | 1.4.2
14 |
15 | [FIX]: fix toolkit detect
16 |
17 | 1.4.1
18 |
19 | [FIX]: fix more compatibility issues
20 |
21 | 1.4.0
22 |
23 | [FIX]: Bug fix for compatibility issues
24 |
25 | 1.3.9
26 |
27 | [NEW]: Improve run configuration
28 |
29 | 1.3.8
30 |
31 | [ADD]: Add new feature of xmake toolkit
32 |
33 | 1.3.7
34 |
35 | [FIX]: Improve xmake icon and remove some deprecated apis
36 |
37 | 1.3.6
38 |
39 | [FIX]: Add xmake path
40 |
41 | 1.3.5
42 |
43 | [FIX]: Fix additional configuration
44 |
45 | 1.3.4
46 |
47 | [FIX]: Add test set
48 | [FIX]: Fix multithreading issues
49 |
50 | 1.3.3
51 |
52 | [FIX]: Update version
53 | [FIX]: Upgrade ui api version
54 | [FIX]: Refactoring ioRunv()
55 |
56 | 1.3.2
57 |
58 | [FIX]: Remove -w option
59 |
60 | 1.3.1
61 |
62 | [FIX]: Upgrade Kotlin UI DSL version 1 to 2
63 |
64 | 1.3.0
65 |
66 | [FIX]: Update version
67 |
68 | 1.2.3
69 |
70 | [FIX]: Update version
71 |
72 | 1.2.2
73 |
74 | [FIX]: Fix requires
75 |
76 | 1.2.1
77 |
78 | [FIX]: Update version
79 |
80 | 1.2.0
81 |
82 | [FIX]: Improve new progrect
83 |
84 | 1.1.9
85 |
86 | [FIX]: Support future Clion version
87 |
88 | 1.1.8
89 |
90 | [FIX]: Update Clion version
91 |
92 | 1.1.7
93 |
94 | [FIX]: Update Clion version
95 |
96 | 1.1.6
97 |
98 | [FIX]: Improve configuration
99 |
100 | 1.1.5
101 |
102 | [FIX]: Fix create project
103 |
104 | 1.1.4
105 |
106 | [FIX]: Fix deprecated api warning again
107 |
108 | 1.1.3
109 |
110 | [FIX]: Fix deprecated api warning
111 |
112 | 1.1.2
113 |
114 | [FIX]: Fix bug for windows
115 |
116 | 1.1.1
117 |
118 | [NEW]: Support Clion 213.x
119 |
120 | 1.1.0
121 |
122 | [NEW]: Add icon for CMakelists and compile_commands
123 |
124 | 1.0.9
125 |
126 | [NEW]: Support to generate CMakelists and compile_commands
127 |
128 | 1.0.8
129 |
130 | [FIX]: Improve compatibility to support more versions
131 |
132 | 1.0.7
133 |
134 | [FIX]: Improve compatibility and add usage instruction
135 |
136 | 1.0.6
137 |
138 | [NEW]: Support latest CLion and IDEA Intellij
139 |
140 | 1.0.5
141 |
142 | [FIX]: Fix conflict issues with java
143 |
144 | 1.0.4
145 |
146 | [NEW]: Modify rebuild icon
147 |
148 | 1.0.3
149 |
150 | [NEW]: Improve exit code
151 |
152 | 1.0.2
153 |
154 | [NEW]: Create project (c/c++/rust/go/dlang/swift/objc)
155 |
156 | 1.0.1
157 |
158 | [NEW]: Quickstart
159 | [NEW]: Project configuration
160 | [NEW]: Run configuration
161 | [NEW]: Menu tools
162 | [NEW]: Tool windows
163 | [NEW]: Build and run
164 | [NEW]: Goto error and waring problems
165 |
166 |
--------------------------------------------------------------------------------
/description.html:
--------------------------------------------------------------------------------
1 | A XMake integration plugin in Intellij Platform
2 | Source Code | Discord | Donate | XMake Homepage
3 | Features:
4 |
5 | xmake
6 |
12 | Quickstart
13 | Create project
14 | Project configuration
15 | Run configuration
16 | Menu tools
17 | Tool windows
18 | Build and run
19 | Goto error and waring problems
20 |
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 |
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 | }
--------------------------------------------------------------------------------