Skip to content

Commit df19a62

Browse files
authored
Add compiler section in Project Setting page (#1335)
1 parent b287a6a commit df19a62

File tree

15 files changed

+536
-24
lines changed

15 files changed

+536
-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,228 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import { VSCodeCheckbox, VSCodeDataGrid, VSCodeDataGridCell, VSCodeDataGridRow, VSCodeDivider, VSCodeDropdown, VSCodeLink, VSCodeOption } from "@vscode/webview-ui-toolkit/react";
5+
import React, { Dispatch, useEffect } from "react";
6+
import { useDispatch, useSelector } from "react-redux";
7+
import { updateCompilerSettings, updateAvailableComplianceLevels } from "./compilerConfigurationViewSlice";
8+
import { CompilerRequest } from "../../vscode/utils";
9+
import { VmInstall } from "../../../types";
10+
import { updateActiveSection } from "../../mainpage/features/commonSlice";
11+
import { updateActiveTab } from "../../classpath/features/classpathConfigurationViewSlice";
12+
13+
const CompilerConfigurationView = (): JSX.Element | null => {
14+
15+
const dispatch: Dispatch<any> = useDispatch();
16+
17+
const projects: any[] = useSelector((state: any) => state.commonConfig.data.projects);
18+
const activeProjectIndex: number = useSelector((state: any) => state.commonConfig.ui.activeProjectIndex);
19+
const complianceLevel: string = useSelector((state: any) => state.compilerConfig.data.complianceLevel[activeProjectIndex]);
20+
const sourceLevel: string = useSelector((state: any) => state.compilerConfig.data.sourceLevel[activeProjectIndex]);
21+
const targetLevel: string = useSelector((state: any) => state.compilerConfig.data.targetLevel[activeProjectIndex]);
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+
const useRelease: boolean = useSelector((state: any) => state.compilerConfig.data.useRelease[activeProjectIndex]);
33+
const enablePreview: boolean = useSelector((state: any) => state.compilerConfig.data.enablePreview[activeProjectIndex]);
34+
const generateDebugInfo: boolean = useSelector((state: any) => state.compilerConfig.data.generateDebugInfo[activeProjectIndex]);
35+
const storeMethodParamNames: boolean = useSelector((state: any) => state.compilerConfig.data.storeMethodParamNames[activeProjectIndex]);
36+
const availableComplianceLevels: string[] = useSelector((state: any) => state.compilerConfig.ui.availableComplianceLevels);
37+
38+
// Release flag only supported on Java 9+.
39+
const showReleaseFlag: boolean = currentJdkComplianceLevel >= 9;
40+
41+
// Hide the preview checkbox if the current JDK version is not latest & preview flag is already disabled.
42+
const showPreviewFlag: boolean = !!(availableComplianceLevels.find((level) => {
43+
return Number(level) > currentJdkComplianceLevel;
44+
}) === undefined) || enablePreview;
45+
46+
const showSourceTargetWarning: boolean = !useRelease && (sourceLevel !== "" && targetLevel !== "" && Number(sourceLevel) > Number(targetLevel));
47+
const showJdkLevelWarning: boolean = Number(complianceLevel) > currentJdkComplianceLevel ||
48+
Number(sourceLevel) > currentJdkComplianceLevel ||
49+
Number(targetLevel) > currentJdkComplianceLevel;
50+
51+
const onMessage = (event: any) => {
52+
const message = event.data;
53+
if (message.command === "compiler.onDidGetAvailableComplianceLevels") {
54+
dispatch(updateAvailableComplianceLevels({
55+
availableComplianceLevels: message.complianceLevels
56+
}));
57+
} else if (message.command === "compiler.onDidGetCompilerSettings") {
58+
dispatch(updateCompilerSettings({
59+
activeProjectIndex,
60+
useRelease: message.useRelease,
61+
enablePreview: message.enablePreview,
62+
complianceLevel: message.complianceLevel,
63+
sourceLevel: message.sourceLevel,
64+
targetLevel: message.targetLevel,
65+
generateDebugInfo: message.generateDebugInfo,
66+
storeMethodParamNames: message.storeMethodParamNames
67+
}));
68+
}
69+
};
70+
71+
useEffect(() => {
72+
window.addEventListener("message", onMessage);
73+
if (availableComplianceLevels?.length === 0) {
74+
CompilerRequest.onWillGetAvailableComplianceLevels();
75+
}
76+
if (sourceLevel === "") {
77+
CompilerRequest.onWillGetCompilerSettings(projects[activeProjectIndex].rootPath);
78+
}
79+
return () => {
80+
window.removeEventListener("message", onMessage);
81+
}
82+
}, []);
83+
84+
const jdkLevels = (selectedLevel: string, label: string, onClick: (value: string) => void) => {
85+
return availableComplianceLevels.map((level) => {
86+
87+
return (
88+
<VSCodeOption
89+
className="setting-section-option"
90+
key={`${label}-${level}`}
91+
value={level}
92+
selected={level === selectedLevel}
93+
onClick={() => onClick(level)}
94+
>
95+
<span>{level}</span>
96+
</VSCodeOption>
97+
);
98+
});
99+
};
100+
101+
const onClickUseRelease = (e: any) => {
102+
dispatch(updateCompilerSettings({
103+
activeProjectIndex,
104+
useRelease: e.target.checked
105+
}));
106+
};
107+
108+
const onClickEnablePreview = (e: any) => {
109+
dispatch(updateCompilerSettings({
110+
activeProjectIndex,
111+
enablePreview: e.target.checked
112+
}));
113+
};
114+
115+
const onClickComplianceLevel = (value: string) => {
116+
dispatch(updateCompilerSettings({
117+
activeProjectIndex,
118+
complianceLevel: value
119+
}));
120+
};
121+
122+
const onClickSourceLevel = (value: string) => {
123+
dispatch(updateCompilerSettings({
124+
activeProjectIndex,
125+
sourceLevel: value
126+
}));
127+
};
128+
129+
const onClickTargetLevel = (value: string) => {
130+
dispatch(updateCompilerSettings({
131+
activeProjectIndex,
132+
targetLevel: value
133+
}));
134+
};
135+
136+
const onClickGenerateDebugInfo = (e: any) => {
137+
dispatch(updateCompilerSettings({
138+
activeProjectIndex,
139+
generateDebugInfo: e.target.checked
140+
}));
141+
};
142+
143+
const onClickStoreMethodParamNames = (e: any) => {
144+
dispatch(updateCompilerSettings({
145+
activeProjectIndex,
146+
storeMethodParamNames: e.target.checked
147+
}));
148+
};
149+
150+
const onClickChangeJdk = () => {
151+
dispatch(updateActiveSection("classpath"));
152+
dispatch(updateActiveTab("jdk"));
153+
};
154+
155+
return (
156+
<div className="setting-section">
157+
<div className={showReleaseFlag ? "" : "invisible"}>
158+
<VSCodeCheckbox checked={useRelease} onClick={onClickUseRelease}>Use '--release' option for cross-compilation (Java 9 and later)</VSCodeCheckbox>
159+
</div>
160+
<div>
161+
<VSCodeDataGrid gridTemplateColumns="40% 60%">
162+
<VSCodeDataGridRow className={showReleaseFlag && useRelease ? "" : "invisible"}>
163+
<VSCodeDataGridCell className="flex-center pl-0 pr-0" gridColumn="1">
164+
<span>Bytecode version:</span>
165+
</VSCodeDataGridCell>
166+
<VSCodeDataGridCell className="flex-center pl-0 pr-0" gridColumn="2">
167+
<VSCodeDropdown value={complianceLevel}>
168+
{jdkLevels(complianceLevel, "compliance", onClickComplianceLevel)}
169+
</VSCodeDropdown>
170+
</VSCodeDataGridCell>
171+
</VSCodeDataGridRow>
172+
<VSCodeDataGridRow className={showReleaseFlag && useRelease ? "invisible" : ""}>
173+
<VSCodeDataGridCell className="flex-center pl-0 pr-0" gridColumn="1">
174+
<span>Source compatibility:</span>
175+
</VSCodeDataGridCell>
176+
<VSCodeDataGridCell className="flex-center pl-0 pr-0" gridColumn="2">
177+
<VSCodeDropdown value={sourceLevel}>
178+
{jdkLevels(sourceLevel, "source", onClickSourceLevel)}
179+
</VSCodeDropdown>
180+
</VSCodeDataGridCell>
181+
</VSCodeDataGridRow>
182+
<VSCodeDataGridRow className={showReleaseFlag && useRelease ? "invisible" : ""}>
183+
<VSCodeDataGridCell className="flex-center pl-0 pr-0" gridColumn="1">
184+
<span>Target compatibility:</span>
185+
</VSCodeDataGridCell>
186+
<VSCodeDataGridCell className="flex-center pl-0 pr-0" gridColumn="2">
187+
<VSCodeDropdown value={targetLevel}>
188+
{jdkLevels(targetLevel, "target", onClickTargetLevel)}
189+
</VSCodeDropdown>
190+
</VSCodeDataGridCell>
191+
</VSCodeDataGridRow>
192+
</VSCodeDataGrid>
193+
</div>
194+
<div className={`mt-2 mb-2 ${showSourceTargetWarning ? "" : "invisible"}`}>
195+
<span className="setting-section-warning">
196+
Target compatibility must be equal or greater than source compatibility.
197+
</span>
198+
</div>
199+
<div className={`mt-2 mb-2 ${showJdkLevelWarning ? "" : "invisible"}`}>
200+
<span className="setting-section-warning">
201+
Please make sure to have a compatible JDK configured (currently {currentJdkComplianceLevel}). You can change the JDK under the <VSCodeLink href="" onClick={() => onClickChangeJdk()}>JDK Runtime</VSCodeLink> tab.
202+
</span>
203+
</div>
204+
<div className={showPreviewFlag ? "" : "invisible"}>
205+
<VSCodeCheckbox checked={enablePreview} onClick={onClickEnablePreview}>Enable preview features</VSCodeCheckbox>
206+
</div>
207+
<VSCodeDivider className="mt-3"/>
208+
<h4 className="mt-3 mb-3">Class File Generation</h4>
209+
<div>
210+
<VSCodeCheckbox checked={generateDebugInfo} onClick={onClickGenerateDebugInfo}>Generate debugging information</VSCodeCheckbox>
211+
</div>
212+
<div>
213+
<VSCodeCheckbox checked={storeMethodParamNames} onClick={onClickStoreMethodParamNames}>Store information about method parameters</VSCodeCheckbox>
214+
</div>
215+
</div>)
216+
}
217+
218+
function parseJavaVersion(version: string): number {
219+
if (!version.includes(".")) {
220+
return Number(version);
221+
} else if (version.startsWith("1.")) {
222+
return Number("1." + version.split(".")[1]);
223+
} else {
224+
return Number(version.split(".")[0]);
225+
}
226+
}
227+
228+
export default CompilerConfigurationView;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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+
updateCompilerSettings: (state, action) => {
38+
const activeProjectIndex = action.payload.activeProjectIndex;
39+
if (action.payload.useRelease !== undefined) {
40+
state.data.useRelease[activeProjectIndex] = action.payload.useRelease;
41+
}
42+
if (action.payload.enablePreview !== undefined) {
43+
state.data.enablePreview[activeProjectIndex] = action.payload.enablePreview;
44+
}
45+
if (action.payload.complianceLevel !== undefined) {
46+
state.data.complianceLevel[activeProjectIndex] = action.payload.complianceLevel;
47+
}
48+
if (action.payload.sourceLevel !== undefined) {
49+
state.data.sourceLevel[activeProjectIndex] = action.payload.sourceLevel;
50+
}
51+
if (action.payload.targetLevel !== undefined) {
52+
state.data.targetLevel[activeProjectIndex] = action.payload.targetLevel;
53+
}
54+
if (action.payload.generateDebugInfo !== undefined) {
55+
state.data.generateDebugInfo[activeProjectIndex] = action.payload.generateDebugInfo;
56+
}
57+
if (action.payload.storeMethodParamNames !== undefined) {
58+
state.data.storeMethodParamNames[activeProjectIndex] = action.payload.storeMethodParamNames;
59+
}
60+
},
61+
},
62+
});
63+
64+
export const {
65+
initializeCompilerData,
66+
updateAvailableComplianceLevels,
67+
updateCompilerSettings,
68+
} = compilerConfigurationViewSlice.actions;
69+
70+
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
}
@@ -51,8 +55,12 @@ const ProjectSettingView = (): JSX.Element => {
5155
}
5256
}
5357
} else if (data.command === "main.onDidListProjects") {
54-
dispatch(initializeClasspathData({ projectsNum: data.projectInfo?.length }));
55-
dispatch(initializeMavenData({ projectsNum: data.projectInfo?.length }));
58+
const length = data.projectInfo?.length;
59+
if (length) {
60+
dispatch(initializeClasspathData({ projectsNum: length }));
61+
dispatch(initializeCompilerData({ projectsNum: length }));
62+
dispatch(initializeMavenData({ projectsNum: length }));
63+
}
5664
dispatch(listProjects(data.projectInfo));
5765
}
5866
}

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)