1
+ // Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2
+ // Licensed under the GNU Affero General Public License (AGPL).
3
+ // See License-AGPL.txt in the project root for license information.
4
+
5
+ package io.gitpod.jetbrains.remote.inspections
6
+
7
+ import com.intellij.codeInspection.LocalInspectionTool
8
+ import com.intellij.codeInspection.ProblemsHolder
9
+ import com.intellij.diagnostic.VMOptions
10
+ import com.intellij.openapi.util.BuildNumber
11
+ import com.intellij.psi.PsiElementVisitor
12
+ import com.intellij.psi.PsiFile
13
+ import io.gitpod.jetbrains.remote.quickfixes.AddVMOptionsQuickFix
14
+ import io.gitpod.jetbrains.remote.quickfixes.ApplyVMOptionsQuickFix
15
+ import io.gitpod.jetbrains.remote.quickfixes.ReplaceVMOptionsQuickFix
16
+ import io.gitpod.jetbrains.remote.utils.GitpodConfig.YamlKey
17
+ import io.gitpod.jetbrains.remote.utils.GitpodConfig.defaultXmxMiB
18
+ import io.gitpod.jetbrains.remote.utils.GitpodConfig.getJetBrainsProductName
19
+ import io.gitpod.jetbrains.remote.utils.GitpodConfig.gitpodYamlFile
20
+ import org.jetbrains.yaml.YAMLUtil
21
+ import org.jetbrains.yaml.psi.YAMLFile
22
+ import org.jetbrains.yaml.psi.YAMLKeyValue
23
+
24
+ class GitpodConfigInspection : LocalInspectionTool () {
25
+
26
+ private val runtimeXmxMiB = Runtime .getRuntime().maxMemory().shr(20 )
27
+
28
+ override fun buildVisitor (holder : ProblemsHolder , isOnTheFly : Boolean ): PsiElementVisitor {
29
+ return object : PsiElementVisitor () {
30
+ override fun visitFile (file : PsiFile ) {
31
+ if (file.name != gitpodYamlFile || file !is YAMLFile ) return
32
+ val productCode = BuildNumber .currentVersion().productCode
33
+ val productName = getJetBrainsProductName(productCode) ? : return
34
+ val keyValue = YAMLUtil .getQualifiedKeyInFile(file, YamlKey .jetbrains, productName, YamlKey .vmOptions)
35
+ if (keyValue == null ) {
36
+ holder.registerProblem(
37
+ file,
38
+ " IDE's max heap size (-Xmx) is ${runtimeXmxMiB} m, but not configured in $gitpodYamlFile " ,
39
+ AddVMOptionsQuickFix (productName, runtimeXmxMiB)
40
+ )
41
+ return
42
+ }
43
+ val configuredXmxMiB = getUserConfiguredXmxValue(keyValue)
44
+ if (configuredXmxMiB == null && runtimeXmxMiB != defaultXmxMiB) {
45
+ val description = " IDE's max heap size (-Xmx) is ${runtimeXmxMiB} m, but not configured in $gitpodYamlFile "
46
+ val fix = if (VMOptions .canWriteOptions()) {
47
+ ApplyVMOptionsQuickFix (" Apply default -Xmx${defaultXmxMiB} m" , defaultXmxMiB)
48
+ } else {
49
+ ReplaceVMOptionsQuickFix (runtimeXmxMiB)
50
+ }
51
+ holder.registerProblem(file, description, fix)
52
+ return
53
+ }
54
+ if (configuredXmxMiB != null && configuredXmxMiB != runtimeXmxMiB) {
55
+ val description = " IDE's max heap size (-Xmx) is ${runtimeXmxMiB} m, but -Xmx${configuredXmxMiB} m configured in $gitpodYamlFile "
56
+ val fix = if (VMOptions .canWriteOptions()) {
57
+ ApplyVMOptionsQuickFix (" Apply -Xmx${configuredXmxMiB} m configured in $gitpodYamlFile " , runtimeXmxMiB)
58
+ } else {
59
+ ReplaceVMOptionsQuickFix (runtimeXmxMiB)
60
+ }
61
+ holder.registerProblem(file, description, fix)
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ private fun getUserConfiguredXmxValue (vmOptionsKeyValue : YAMLKeyValue ): Long? {
68
+ val vmOptions = vmOptionsKeyValue.valueText.trim().split(" \\ s" .toRegex())
69
+ // the rightmost option is the one to take effect
70
+ val finalXmx = vmOptions.lastOrNull { it.startsWith(" -Xmx" ) } ? : return null
71
+ val xmxValue = finalXmx.substringAfter(" -Xmx" )
72
+ return try {
73
+ VMOptions .parseMemoryOption(xmxValue).shr(20 )
74
+ } catch (e: IllegalArgumentException ) {
75
+ // ignore invalid user configuration
76
+ null
77
+ }
78
+ }
79
+ }
0 commit comments