Skip to content

Commit a858acd

Browse files
committed
Add compiler section in Project Setting page
1 parent 9961fa6 commit a858acd

File tree

15 files changed

+530
-24
lines changed

15 files changed

+530
-24
lines changed

src/project-settings/assets/classpath/style.scss

-4
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,3 @@
135135
.inactive {
136136
opacity: 0.67;
137137
}
138-
139-
.hidden {
140-
visibility: hidden;
141-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import { VSCodeCheckbox, VSCodeDivider, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react";
5+
import React, { Dispatch, useEffect } from "react";
6+
import { useDispatch, useSelector } from "react-redux";
7+
import { loadCompilerSettings, updateAvailableComplianceLevels, updateComplianceLevel, updateEnablePreview, updateGenerateDebugInfo, updateSourceLevel, updateStoreMethodParamNames, updateTargetLevel, updateUseRelease } from "./compilerConfigurationViewSlice";
8+
import { CompilerRequest } from "../../vscode/utils";
9+
import { VmInstall } from "../../../types";
10+
11+
const CompilerConfigurationView = (): JSX.Element | null => {
12+
const projects: any[] = useSelector((state: any) => state.commonConfig.data.projects);
13+
const activeProjectIndex: number = useSelector((state: any) => state.commonConfig.ui.activeProjectIndex);
14+
const useRelease: boolean = useSelector((state: any) => state.compilerConfig.data.useRelease[activeProjectIndex]);
15+
const enablePreview: boolean = useSelector((state: any) => state.compilerConfig.data.enablePreview[activeProjectIndex]);
16+
let complianceLevel: string = useSelector((state: any) => state.compilerConfig.data.complianceLevel[activeProjectIndex]);
17+
let sourceLevel: string = useSelector((state: any) => state.compilerConfig.data.sourceLevel[activeProjectIndex]);
18+
let targetLevel: string = useSelector((state: any) => state.compilerConfig.data.targetLevel[activeProjectIndex]);
19+
const generateDebugInfo: boolean = useSelector((state: any) => state.compilerConfig.data.generateDebugInfo[activeProjectIndex]);
20+
const storeMethodParamNames: boolean = useSelector((state: any) => state.compilerConfig.data.storeMethodParamNames[activeProjectIndex]);
21+
const availableComplianceLevels: string[] = useSelector((state: any) => state.compilerConfig.ui.availableComplianceLevels);
22+
23+
// Find the version of current JDK to determine the max compliance level that can be set.
24+
const vmInstalls: VmInstall[] = useSelector((state: any) => state.classpathConfig.data.vmInstalls);
25+
const activeVmInstallPath: string = useSelector((state: any) => state.classpathConfig.data.activeVmInstallPath[activeProjectIndex]);
26+
const activeVmInstall: VmInstall | undefined = vmInstalls.find((vmInstall: VmInstall) => vmInstall.path === activeVmInstallPath);
27+
let currentJdkComplianceLevel: number = Number.MAX_SAFE_INTEGER;
28+
if (activeVmInstall?.version) {
29+
currentJdkComplianceLevel = parseJavaVersion(activeVmInstall.version);
30+
}
31+
32+
// compliance/source/target level cannot be higher than current jdk level.
33+
if (Number(complianceLevel) > currentJdkComplianceLevel) {
34+
complianceLevel = currentJdkComplianceLevel.toString();
35+
}
36+
if (Number(sourceLevel) > currentJdkComplianceLevel) {
37+
sourceLevel = currentJdkComplianceLevel.toString();
38+
}
39+
if (Number(targetLevel) > currentJdkComplianceLevel) {
40+
targetLevel = currentJdkComplianceLevel.toString();
41+
}
42+
43+
// Release flag only supported on Java 9+.
44+
const showReleaseFlag: boolean = currentJdkComplianceLevel >= 9;
45+
46+
// Hide the preview checkbox if the current JDK version is not latest & preview flag is already disabled.
47+
const showPreviewFlag: boolean = !!(availableComplianceLevels.find((level) => {
48+
return Number(level) > currentJdkComplianceLevel;
49+
}) === undefined) || enablePreview;
50+
51+
const showSourceTargetWarning: boolean = !useRelease && (sourceLevel !== "" && targetLevel !== "" && Number(sourceLevel) > Number(targetLevel));
52+
53+
const dispatch: Dispatch<any> = useDispatch();
54+
55+
const onMessage = (event: any) => {
56+
const message = event.data;
57+
if (message.command === "compiler.onDidGetAvailableComplianceLevels") {
58+
dispatch(updateAvailableComplianceLevels({
59+
availableComplianceLevels: message.complianceLevels
60+
}));
61+
} else if (message.command === "compiler.onDidGetCompilerSettings") {
62+
dispatch(loadCompilerSettings({
63+
activeProjectIndex,
64+
useRelease: message.useRelease,
65+
enablePreview: message.enablePreview,
66+
complianceLevel: message.complianceLevel,
67+
sourceLevel: message.sourceLevel,
68+
targetLevel: message.targetLevel,
69+
generateDebugInfo: message.generateDebugInfo,
70+
storeMethodParamNames: message.storeMethodParamNames
71+
}));
72+
}
73+
};
74+
75+
useEffect(() => {
76+
window.addEventListener("message", onMessage);
77+
if (availableComplianceLevels?.length === 0) {
78+
CompilerRequest.onWillGetAvailableComplianceLevels();
79+
}
80+
if (sourceLevel === "") {
81+
CompilerRequest.onWillGetCompilerSettings(projects[activeProjectIndex].rootPath);
82+
}
83+
return () => {
84+
window.removeEventListener("message", onMessage);
85+
}
86+
}, []);
87+
88+
const jdkLevels = (selectedLevel: string, label: string, onClick: (value: string) => void) => {
89+
return availableComplianceLevels.map((level) => {
90+
if (Number(level) > currentJdkComplianceLevel) {
91+
return null;
92+
}
93+
94+
return (
95+
<VSCodeOption
96+
className="setting-section-option"
97+
key={`${label}-${level}`}
98+
value={level}
99+
selected={level === selectedLevel}
100+
onClick={() => onClick(level)}
101+
>
102+
<span>{level}</span>
103+
</VSCodeOption>
104+
);
105+
}).filter((option) => option !== null);
106+
};
107+
108+
const onClickUseRelease = (e: any) => {
109+
dispatch(updateUseRelease({
110+
activeProjectIndex,
111+
useRelease: e.target.checked
112+
}));
113+
};
114+
115+
const onClickEnablePreview = (e: any) => {
116+
dispatch(updateEnablePreview({
117+
activeProjectIndex,
118+
enablePreview: e.target.checked
119+
}));
120+
};
121+
122+
const onClickComplianceLevel = (value: string) => {
123+
dispatch(updateComplianceLevel({
124+
activeProjectIndex,
125+
complianceLevel: value
126+
}));
127+
};
128+
129+
const onClickSourceLevel = (value: string) => {
130+
dispatch(updateSourceLevel({
131+
activeProjectIndex,
132+
sourceLevel: value
133+
}));
134+
};
135+
136+
const onClickTargetLevel = (value: string) => {
137+
dispatch(updateTargetLevel({
138+
activeProjectIndex,
139+
targetLevel: value
140+
}));
141+
};
142+
143+
const onClickGenerateDebugInfo = (e: any) => {
144+
dispatch(updateGenerateDebugInfo({
145+
activeProjectIndex,
146+
generateDebugInfo: e.target.checked
147+
}));
148+
};
149+
150+
const onClickStoreMethodParamNames = (e: any) => {
151+
dispatch(updateStoreMethodParamNames({
152+
activeProjectIndex,
153+
storeMethodParamNames: e.target.checked
154+
}));
155+
};
156+
157+
return (
158+
<div className="setting-section">
159+
<h4 className="mt-3 mb-3">JDK Compliance</h4>
160+
<div className={showReleaseFlag ? "" : "invisible"}>
161+
<VSCodeCheckbox checked={useRelease} onClick={onClickUseRelease}>Use '--release' option</VSCodeCheckbox>
162+
</div>
163+
<div className={`flex-center mt-1 mb-2 ${showReleaseFlag && useRelease ? "" : "invisible"}`}>
164+
<span className="mr-1">Compliance level:</span>
165+
<VSCodeDropdown value={complianceLevel}>
166+
{jdkLevels(complianceLevel, "compliance", onClickComplianceLevel)}
167+
</VSCodeDropdown>
168+
</div>
169+
<div className={`flex-center mt-1 mb-2 ${showReleaseFlag && useRelease ? "invisible" : ""}`}>
170+
<span className="mr-1">Source compatibility:</span>
171+
<VSCodeDropdown value={sourceLevel}>
172+
{jdkLevels(sourceLevel, "source", onClickSourceLevel)}
173+
</VSCodeDropdown>
174+
</div>
175+
<div className={`flex-center mb-2 ${showReleaseFlag && useRelease ? "invisible" : ""}`}>
176+
<span className="mr-1">Target compatibility:</span>
177+
<VSCodeDropdown value={targetLevel}>
178+
{jdkLevels(targetLevel, "target", onClickTargetLevel)}
179+
</VSCodeDropdown>
180+
</div>
181+
<div className={`mb-2 ${showSourceTargetWarning ? "" : "invisible"}`}>
182+
<span className="setting-section-warning">
183+
Target compatibility must be equal or greater than source compatibility.
184+
</span>
185+
</div>
186+
<div className={showPreviewFlag ? "" : "invisible"}>
187+
<VSCodeCheckbox checked={enablePreview} onClick={onClickEnablePreview}>Enable preview features</VSCodeCheckbox>
188+
</div>
189+
<VSCodeDivider className="mt-3"/>
190+
<h4 className="mt-3 mb-3">Class File Generation</h4>
191+
<div>
192+
<VSCodeCheckbox checked={generateDebugInfo} onClick={onClickGenerateDebugInfo}>Generate debugging information</VSCodeCheckbox>
193+
</div>
194+
<div>
195+
<VSCodeCheckbox checked={storeMethodParamNames} onClick={onClickStoreMethodParamNames}>Store information about method parameters</VSCodeCheckbox>
196+
</div>
197+
</div>)
198+
}
199+
200+
function parseJavaVersion(version: string): number {
201+
if (!version.includes(".")) {
202+
return Number(version);
203+
} else if (version.startsWith("1.")) {
204+
return Number("1." + version.split(".")[1]);
205+
} else {
206+
return Number(version.split(".")[0]);
207+
}
208+
}
209+
210+
export default CompilerConfigurationView;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import { createSlice } from "@reduxjs/toolkit";
5+
6+
export const compilerConfigurationViewSlice = createSlice({
7+
name: "compilerConfig",
8+
initialState: {
9+
ui: {
10+
availableComplianceLevels: [] as string[],
11+
},
12+
data: {
13+
useRelease: [] as boolean[],
14+
enablePreview: [] as boolean[],
15+
complianceLevel: [] as string[],
16+
sourceLevel: [] as string[],
17+
targetLevel: [] as string[],
18+
generateDebugInfo: [] as boolean[],
19+
storeMethodParamNames: [] as boolean[],
20+
},
21+
},
22+
reducers: {
23+
initializeCompilerData: (state, action) => {
24+
const projectNum = action.payload.projectsNum;
25+
state.data.useRelease = Array(projectNum).fill(false);
26+
state.data.enablePreview = Array(projectNum).fill(false);
27+
state.data.complianceLevel = Array(projectNum).fill("");
28+
state.data.sourceLevel = Array(projectNum).fill("");
29+
state.data.targetLevel = Array(projectNum).fill("");
30+
state.data.generateDebugInfo = Array(projectNum).fill(false);
31+
state.data.storeMethodParamNames = Array(projectNum).fill(false);
32+
state.ui.availableComplianceLevels = [];
33+
},
34+
updateAvailableComplianceLevels: (state, action) => {
35+
state.ui.availableComplianceLevels = action.payload.availableComplianceLevels;
36+
},
37+
loadCompilerSettings: (state, action) => {
38+
const activeProjectIndex = action.payload.activeProjectIndex;
39+
state.data.useRelease[activeProjectIndex] = action.payload.useRelease;
40+
state.data.enablePreview[activeProjectIndex] = action.payload.enablePreview;
41+
state.data.complianceLevel[activeProjectIndex] = action.payload.complianceLevel;
42+
state.data.sourceLevel[activeProjectIndex] = action.payload.sourceLevel;
43+
state.data.targetLevel[activeProjectIndex] = action.payload.targetLevel;
44+
state.data.generateDebugInfo[activeProjectIndex] = action.payload.generateDebugInfo;
45+
state.data.storeMethodParamNames[activeProjectIndex] = action.payload.storeMethodParamNames;
46+
},
47+
updateUseRelease: (state, action) => {
48+
const activeProjectIndex = action.payload.activeProjectIndex;
49+
state.data.useRelease[activeProjectIndex] = action.payload.useRelease;
50+
},
51+
updateEnablePreview: (state, action) => {
52+
const activeProjectIndex = action.payload.activeProjectIndex;
53+
state.data.enablePreview[activeProjectIndex] = action.payload.enablePreview;
54+
},
55+
updateComplianceLevel: (state, action) => {
56+
const activeProjectIndex = action.payload.activeProjectIndex;
57+
state.data.complianceLevel[activeProjectIndex] = action.payload.complianceLevel;
58+
},
59+
updateSourceLevel: (state, action) => {
60+
const activeProjectIndex = action.payload.activeProjectIndex;
61+
state.data.sourceLevel[activeProjectIndex] = action.payload.sourceLevel;
62+
},
63+
updateTargetLevel: (state, action) => {
64+
const activeProjectIndex = action.payload.activeProjectIndex;
65+
state.data.targetLevel[activeProjectIndex] = action.payload.targetLevel;
66+
},
67+
updateGenerateDebugInfo: (state, action) => {
68+
const activeProjectIndex = action.payload.activeProjectIndex;
69+
state.data.generateDebugInfo[activeProjectIndex] = action.payload.generateDebugInfo;
70+
},
71+
updateStoreMethodParamNames: (state, action) => {
72+
const activeProjectIndex = action.payload.activeProjectIndex;
73+
state.data.storeMethodParamNames[activeProjectIndex] = action.payload.storeMethodParamNames;
74+
},
75+
},
76+
});
77+
78+
export const {
79+
initializeCompilerData,
80+
updateAvailableComplianceLevels,
81+
loadCompilerSettings,
82+
updateUseRelease,
83+
updateEnablePreview,
84+
updateComplianceLevel,
85+
updateSourceLevel,
86+
updateTargetLevel,
87+
updateGenerateDebugInfo,
88+
updateStoreMethodParamNames,
89+
} = compilerConfigurationViewSlice.actions;
90+
91+
export default compilerConfigurationViewSlice.reducer;

src/project-settings/assets/mainpage/features/ProjectSettingView.tsx

+10-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import SideBar from "./component/SideBar";
1414
import MavenConfigurationView from "../../maven/features/MavenConfigurationView";
1515
import { SectionId } from "../../../types";
1616
import { initializeMavenData } from "../../maven/features/mavenConfigurationViewSlice";
17+
import CompilerConfigurationView from "../../compiler/features/CompilerConfigurationView";
18+
import { initializeCompilerData } from "../../compiler/features/compilerConfigurationViewSlice";
1719

1820
const ProjectSettingView = (): JSX.Element => {
1921
const activeSection: string = useSelector((state: any) => state.commonConfig.ui.activeSection);
@@ -29,6 +31,8 @@ const ProjectSettingView = (): JSX.Element => {
2931
const getSectionContent = () => {
3032
if (activeSection === SectionId.Classpath) {
3133
return <ClasspathConfigurationView />;
34+
} else if (activeSection === SectionId.Compiler) {
35+
return <CompilerConfigurationView />;
3236
} else if (activeSection === SectionId.Maven) {
3337
return <MavenConfigurationView />;
3438
}
@@ -53,8 +57,12 @@ const ProjectSettingView = (): JSX.Element => {
5357
}
5458
}
5559
} else if (data.command === "main.onDidListProjects") {
56-
dispatch(initializeClasspathData({ projectsNum: data.projectInfo?.length }));
57-
dispatch(initializeMavenData({ projectsNum: data.projectInfo?.length }));
60+
const length = data.projectInfo?.length;
61+
if (length) {
62+
dispatch(initializeClasspathData({ projectsNum: length }));
63+
dispatch(initializeCompilerData({ projectsNum: length }));
64+
dispatch(initializeMavenData({ projectsNum: length }));
65+
}
5866
dispatch(listProjects(data.projectInfo));
5967
}
6068
}

src/project-settings/assets/mainpage/features/component/Footer.tsx

+22-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { ClasspathEntry, ProjectInfo } from "../../../../types";
88
import { useDispatch, useSelector } from "react-redux";
99
import { ProjectType } from "../../../../../utils/webview";
1010
import { updateLoadingState } from "../../../classpath/features/classpathConfigurationViewSlice";
11-
import { ClasspathRequest, MavenRequest } from "../../../vscode/utils";
11+
import { ClasspathRequest, CompilerRequest, MavenRequest } from "../../../vscode/utils";
1212

1313
const Footer = (): JSX.Element => {
1414

@@ -19,6 +19,13 @@ const Footer = (): JSX.Element => {
1919
const projectType: ProjectType[] = useSelector((state: any) => state.commonConfig.data.projectType);
2020
const libraries: ClasspathEntry[][] = useSelector((state: any) => state.classpathConfig.data.libraries);
2121
const activeProfiles: string[] = useSelector((state: any) => state.mavenConfig.data.activeProfiles);
22+
const useRelease: boolean[] = useSelector((state: any) => state.compilerConfig.data.useRelease);
23+
const enablePreview: boolean[] = useSelector((state: any) => state.compilerConfig.data.enablePreview);
24+
const complianceLevel: string[] = useSelector((state: any) => state.compilerConfig.data.complianceLevel);
25+
const sourceLevel: string[] = useSelector((state: any) => state.compilerConfig.data.sourceLevel);
26+
const targetLevel: string[] = useSelector((state: any) => state.compilerConfig.data.targetLevel);
27+
const generateDebugInfo: boolean[] = useSelector((state: any) => state.compilerConfig.data.generateDebugInfo);
28+
const storeMethodParamNames: boolean[] = useSelector((state: any) => state.compilerConfig.data.storeMethodParamNames);
2229
const loadingState: boolean = useSelector((state: any) => state.classpathConfig.loadingState);
2330

2431
const dispatch: Dispatch<any> = useDispatch();
@@ -39,6 +46,20 @@ const Footer = (): JSX.Element => {
3946
MavenRequest.onWillUpdateSelectProfiles(projects[i].rootPath, activeProfiles[i]);
4047
}
4148
}
49+
50+
// compiler section
51+
for (let i = 0; i < projects.length; i++) {
52+
CompilerRequest.onWillUpdateCompilerSettings(
53+
projects[i].rootPath,
54+
useRelease[i],
55+
enablePreview[i],
56+
complianceLevel[i],
57+
sourceLevel[i],
58+
targetLevel[i],
59+
generateDebugInfo[i],
60+
storeMethodParamNames[i]
61+
);
62+
}
4263
};
4364

4465
const onDidChangeLoadingState = (event: OnDidChangeLoadingStateEvent) => {

src/project-settings/assets/mainpage/features/component/SideBar.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ const SideBar = (): JSX.Element => {
3232
<div className={`section-link ${activeSection === SectionId.Classpath ? "section-link-active" : ""} mb-1`} onClick={() => onClickNavBarItem(SectionId.Classpath)}>
3333
Classpath
3434
</div>
35+
<div className={`section-link ${activeSection === SectionId.Compiler ? "section-link-active" : ""} mb-1`} onClick={() => onClickNavBarItem(SectionId.Compiler)}>
36+
Compiler
37+
</div>
3538
{
3639
projectType === ProjectType.Maven && (
3740
<div className={`section-link ${activeSection === SectionId.Maven ? "section-link-active" : ""} mb-1`} onClick={() => onClickNavBarItem(SectionId.Maven)}>

0 commit comments

Comments
 (0)