Skip to content

Add compiler section in Project Setting page #1335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/project-settings/assets/classpath/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,3 @@
.inactive {
opacity: 0.67;
}

.hidden {
visibility: hidden;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

import { VSCodeCheckbox, VSCodeDataGrid, VSCodeDataGridCell, VSCodeDataGridRow, VSCodeDivider, VSCodeDropdown, VSCodeLink, VSCodeOption } from "@vscode/webview-ui-toolkit/react";
import React, { Dispatch, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updateCompilerSettings, updateAvailableComplianceLevels } from "./compilerConfigurationViewSlice";
import { CompilerRequest } from "../../vscode/utils";
import { VmInstall } from "../../../types";
import { updateActiveSection } from "../../mainpage/features/commonSlice";
import { updateActiveTab } from "../../classpath/features/classpathConfigurationViewSlice";

const CompilerConfigurationView = (): JSX.Element | null => {

const dispatch: Dispatch<any> = useDispatch();

const projects: any[] = useSelector((state: any) => state.commonConfig.data.projects);
const activeProjectIndex: number = useSelector((state: any) => state.commonConfig.ui.activeProjectIndex);
const complianceLevel: string = useSelector((state: any) => state.compilerConfig.data.complianceLevel[activeProjectIndex]);
const sourceLevel: string = useSelector((state: any) => state.compilerConfig.data.sourceLevel[activeProjectIndex]);
const targetLevel: string = useSelector((state: any) => state.compilerConfig.data.targetLevel[activeProjectIndex]);

// Find the version of current JDK to determine the max compliance level that can be set.
const vmInstalls: VmInstall[] = useSelector((state: any) => state.classpathConfig.data.vmInstalls);
const activeVmInstallPath: string = useSelector((state: any) => state.classpathConfig.data.activeVmInstallPath[activeProjectIndex]);
const activeVmInstall: VmInstall | undefined = vmInstalls.find((vmInstall: VmInstall) => vmInstall.path === activeVmInstallPath);
let currentJdkComplianceLevel: number = Number.MAX_SAFE_INTEGER;
if (activeVmInstall?.version) {
currentJdkComplianceLevel = parseJavaVersion(activeVmInstall.version);
}

const useRelease: boolean = useSelector((state: any) => state.compilerConfig.data.useRelease[activeProjectIndex]);
const enablePreview: boolean = useSelector((state: any) => state.compilerConfig.data.enablePreview[activeProjectIndex]);
const generateDebugInfo: boolean = useSelector((state: any) => state.compilerConfig.data.generateDebugInfo[activeProjectIndex]);
const storeMethodParamNames: boolean = useSelector((state: any) => state.compilerConfig.data.storeMethodParamNames[activeProjectIndex]);
const availableComplianceLevels: string[] = useSelector((state: any) => state.compilerConfig.ui.availableComplianceLevels);

// Release flag only supported on Java 9+.
const showReleaseFlag: boolean = currentJdkComplianceLevel >= 9;

// Hide the preview checkbox if the current JDK version is not latest & preview flag is already disabled.
const showPreviewFlag: boolean = !!(availableComplianceLevels.find((level) => {
return Number(level) > currentJdkComplianceLevel;
}) === undefined) || enablePreview;

const showSourceTargetWarning: boolean = !useRelease && (sourceLevel !== "" && targetLevel !== "" && Number(sourceLevel) > Number(targetLevel));
const showJdkLevelWarning: boolean = Number(complianceLevel) > currentJdkComplianceLevel ||
Number(sourceLevel) > currentJdkComplianceLevel ||
Number(targetLevel) > currentJdkComplianceLevel;

const onMessage = (event: any) => {
const message = event.data;
if (message.command === "compiler.onDidGetAvailableComplianceLevels") {
dispatch(updateAvailableComplianceLevels({
availableComplianceLevels: message.complianceLevels
}));
} else if (message.command === "compiler.onDidGetCompilerSettings") {
dispatch(updateCompilerSettings({
activeProjectIndex,
useRelease: message.useRelease,
enablePreview: message.enablePreview,
complianceLevel: message.complianceLevel,
sourceLevel: message.sourceLevel,
targetLevel: message.targetLevel,
generateDebugInfo: message.generateDebugInfo,
storeMethodParamNames: message.storeMethodParamNames
}));
}
};

useEffect(() => {
window.addEventListener("message", onMessage);
if (availableComplianceLevels?.length === 0) {
CompilerRequest.onWillGetAvailableComplianceLevels();
}
if (sourceLevel === "") {
CompilerRequest.onWillGetCompilerSettings(projects[activeProjectIndex].rootPath);
}
return () => {
window.removeEventListener("message", onMessage);
}
}, []);

const jdkLevels = (selectedLevel: string, label: string, onClick: (value: string) => void) => {
return availableComplianceLevels.map((level) => {

return (
<VSCodeOption
className="setting-section-option"
key={`${label}-${level}`}
value={level}
selected={level === selectedLevel}
onClick={() => onClick(level)}
>
<span>{level}</span>
</VSCodeOption>
);
});
};

const onClickUseRelease = (e: any) => {
dispatch(updateCompilerSettings({
activeProjectIndex,
useRelease: e.target.checked
}));
};

const onClickEnablePreview = (e: any) => {
dispatch(updateCompilerSettings({
activeProjectIndex,
enablePreview: e.target.checked
}));
};

const onClickComplianceLevel = (value: string) => {
dispatch(updateCompilerSettings({
activeProjectIndex,
complianceLevel: value
}));
};

const onClickSourceLevel = (value: string) => {
dispatch(updateCompilerSettings({
activeProjectIndex,
sourceLevel: value
}));
};

const onClickTargetLevel = (value: string) => {
dispatch(updateCompilerSettings({
activeProjectIndex,
targetLevel: value
}));
};

const onClickGenerateDebugInfo = (e: any) => {
dispatch(updateCompilerSettings({
activeProjectIndex,
generateDebugInfo: e.target.checked
}));
};

const onClickStoreMethodParamNames = (e: any) => {
dispatch(updateCompilerSettings({
activeProjectIndex,
storeMethodParamNames: e.target.checked
}));
};

const onClickChangeJdk = () => {
dispatch(updateActiveSection("classpath"));
dispatch(updateActiveTab("jdk"));
};

return (
<div className="setting-section">
<div className={showReleaseFlag ? "" : "invisible"}>
<VSCodeCheckbox checked={useRelease} onClick={onClickUseRelease}>Use '--release' option for cross-compilation (Java 9 and later)</VSCodeCheckbox>
</div>
<div>
<VSCodeDataGrid gridTemplateColumns="40% 60%">
<VSCodeDataGridRow className={showReleaseFlag && useRelease ? "" : "invisible"}>
<VSCodeDataGridCell className="flex-center pl-0 pr-0" gridColumn="1">
<span>Bytecode version:</span>
</VSCodeDataGridCell>
<VSCodeDataGridCell className="flex-center pl-0 pr-0" gridColumn="2">
<VSCodeDropdown value={complianceLevel}>
{jdkLevels(complianceLevel, "compliance", onClickComplianceLevel)}
</VSCodeDropdown>
</VSCodeDataGridCell>
</VSCodeDataGridRow>
<VSCodeDataGridRow className={showReleaseFlag && useRelease ? "invisible" : ""}>
<VSCodeDataGridCell className="flex-center pl-0 pr-0" gridColumn="1">
<span>Source compatibility:</span>
</VSCodeDataGridCell>
<VSCodeDataGridCell className="flex-center pl-0 pr-0" gridColumn="2">
<VSCodeDropdown value={sourceLevel}>
{jdkLevels(sourceLevel, "source", onClickSourceLevel)}
</VSCodeDropdown>
</VSCodeDataGridCell>
</VSCodeDataGridRow>
<VSCodeDataGridRow className={showReleaseFlag && useRelease ? "invisible" : ""}>
<VSCodeDataGridCell className="flex-center pl-0 pr-0" gridColumn="1">
<span>Target compatibility:</span>
</VSCodeDataGridCell>
<VSCodeDataGridCell className="flex-center pl-0 pr-0" gridColumn="2">
<VSCodeDropdown value={targetLevel}>
{jdkLevels(targetLevel, "target", onClickTargetLevel)}
</VSCodeDropdown>
</VSCodeDataGridCell>
</VSCodeDataGridRow>
</VSCodeDataGrid>
</div>
<div className={`mt-2 mb-2 ${showSourceTargetWarning ? "" : "invisible"}`}>
<span className="setting-section-warning">
Target compatibility must be equal or greater than source compatibility.
</span>
</div>
<div className={`mt-2 mb-2 ${showJdkLevelWarning ? "" : "invisible"}`}>
<span className="setting-section-warning">
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.
</span>
</div>
<div className={showPreviewFlag ? "" : "invisible"}>
<VSCodeCheckbox checked={enablePreview} onClick={onClickEnablePreview}>Enable preview features</VSCodeCheckbox>
</div>
<VSCodeDivider className="mt-3"/>
<h4 className="mt-3 mb-3">Class File Generation</h4>
<div>
<VSCodeCheckbox checked={generateDebugInfo} onClick={onClickGenerateDebugInfo}>Generate debugging information</VSCodeCheckbox>
</div>
<div>
<VSCodeCheckbox checked={storeMethodParamNames} onClick={onClickStoreMethodParamNames}>Store information about method parameters</VSCodeCheckbox>
</div>
</div>)
}

function parseJavaVersion(version: string): number {
if (!version.includes(".")) {
return Number(version);
} else if (version.startsWith("1.")) {
return Number("1." + version.split(".")[1]);
} else {
return Number(version.split(".")[0]);
}
}

export default CompilerConfigurationView;
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

import { createSlice } from "@reduxjs/toolkit";

export const compilerConfigurationViewSlice = createSlice({
name: "compilerConfig",
initialState: {
ui: {
availableComplianceLevels: [] as string[],
},
data: {
useRelease: [] as boolean[],
enablePreview: [] as boolean[],
complianceLevel: [] as string[],
sourceLevel: [] as string[],
targetLevel: [] as string[],
generateDebugInfo: [] as boolean[],
storeMethodParamNames: [] as boolean[],
},
},
reducers: {
initializeCompilerData: (state, action) => {
const projectNum = action.payload.projectsNum;
state.data.useRelease = Array(projectNum).fill(false);
state.data.enablePreview = Array(projectNum).fill(false);
state.data.complianceLevel = Array(projectNum).fill("");
state.data.sourceLevel = Array(projectNum).fill("");
state.data.targetLevel = Array(projectNum).fill("");
state.data.generateDebugInfo = Array(projectNum).fill(false);
state.data.storeMethodParamNames = Array(projectNum).fill(false);
state.ui.availableComplianceLevels = [];
},
updateAvailableComplianceLevels: (state, action) => {
state.ui.availableComplianceLevels = action.payload.availableComplianceLevels;
},
updateCompilerSettings: (state, action) => {
const activeProjectIndex = action.payload.activeProjectIndex;
if (action.payload.useRelease !== undefined) {
state.data.useRelease[activeProjectIndex] = action.payload.useRelease;
}
if (action.payload.enablePreview !== undefined) {
state.data.enablePreview[activeProjectIndex] = action.payload.enablePreview;
}
if (action.payload.complianceLevel !== undefined) {
state.data.complianceLevel[activeProjectIndex] = action.payload.complianceLevel;
}
if (action.payload.sourceLevel !== undefined) {
state.data.sourceLevel[activeProjectIndex] = action.payload.sourceLevel;
}
if (action.payload.targetLevel !== undefined) {
state.data.targetLevel[activeProjectIndex] = action.payload.targetLevel;
}
if (action.payload.generateDebugInfo !== undefined) {
state.data.generateDebugInfo[activeProjectIndex] = action.payload.generateDebugInfo;
}
if (action.payload.storeMethodParamNames !== undefined) {
state.data.storeMethodParamNames[activeProjectIndex] = action.payload.storeMethodParamNames;
}
},
},
});

export const {
initializeCompilerData,
updateAvailableComplianceLevels,
updateCompilerSettings,
} = compilerConfigurationViewSlice.actions;

export default compilerConfigurationViewSlice.reducer;
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import SideBar from "./component/SideBar";
import MavenConfigurationView from "../../maven/features/MavenConfigurationView";
import { SectionId } from "../../../types";
import { initializeMavenData } from "../../maven/features/mavenConfigurationViewSlice";
import CompilerConfigurationView from "../../compiler/features/CompilerConfigurationView";
import { initializeCompilerData } from "../../compiler/features/compilerConfigurationViewSlice";

const ProjectSettingView = (): JSX.Element => {
const activeSection: string = useSelector((state: any) => state.commonConfig.ui.activeSection);
Expand All @@ -29,6 +31,8 @@ const ProjectSettingView = (): JSX.Element => {
const getSectionContent = () => {
if (activeSection === SectionId.Classpath) {
return <ClasspathConfigurationView />;
} else if (activeSection === SectionId.Compiler) {
return <CompilerConfigurationView />;
} else if (activeSection === SectionId.Maven) {
return <MavenConfigurationView />;
}
Expand All @@ -51,8 +55,12 @@ const ProjectSettingView = (): JSX.Element => {
}
}
} else if (data.command === "main.onDidListProjects") {
dispatch(initializeClasspathData({ projectsNum: data.projectInfo?.length }));
dispatch(initializeMavenData({ projectsNum: data.projectInfo?.length }));
const length = data.projectInfo?.length;
if (length) {
dispatch(initializeClasspathData({ projectsNum: length }));
dispatch(initializeCompilerData({ projectsNum: length }));
dispatch(initializeMavenData({ projectsNum: length }));
}
dispatch(listProjects(data.projectInfo));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ClasspathEntry, ProjectInfo } from "../../../../types";
import { useDispatch, useSelector } from "react-redux";
import { ProjectType } from "../../../../../utils/webview";
import { updateLoadingState } from "../../../classpath/features/classpathConfigurationViewSlice";
import { ClasspathRequest, MavenRequest } from "../../../vscode/utils";
import { ClasspathRequest, CompilerRequest, MavenRequest } from "../../../vscode/utils";

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

Expand All @@ -19,6 +19,13 @@ const Footer = (): JSX.Element => {
const projectType: ProjectType[] = useSelector((state: any) => state.commonConfig.data.projectType);
const libraries: ClasspathEntry[][] = useSelector((state: any) => state.classpathConfig.data.libraries);
const activeProfiles: string[] = useSelector((state: any) => state.mavenConfig.data.activeProfiles);
const useRelease: boolean[] = useSelector((state: any) => state.compilerConfig.data.useRelease);
const enablePreview: boolean[] = useSelector((state: any) => state.compilerConfig.data.enablePreview);
const complianceLevel: string[] = useSelector((state: any) => state.compilerConfig.data.complianceLevel);
const sourceLevel: string[] = useSelector((state: any) => state.compilerConfig.data.sourceLevel);
const targetLevel: string[] = useSelector((state: any) => state.compilerConfig.data.targetLevel);
const generateDebugInfo: boolean[] = useSelector((state: any) => state.compilerConfig.data.generateDebugInfo);
const storeMethodParamNames: boolean[] = useSelector((state: any) => state.compilerConfig.data.storeMethodParamNames);
const loadingState: boolean = useSelector((state: any) => state.classpathConfig.loadingState);

const dispatch: Dispatch<any> = useDispatch();
Expand All @@ -39,6 +46,20 @@ const Footer = (): JSX.Element => {
MavenRequest.onWillUpdateSelectProfiles(projects[i].rootPath, activeProfiles[i]);
}
}

// compiler section
for (let i = 0; i < projects.length; i++) {
CompilerRequest.onWillUpdateCompilerSettings(
projects[i].rootPath,
useRelease[i],
enablePreview[i],
complianceLevel[i],
sourceLevel[i],
targetLevel[i],
generateDebugInfo[i],
storeMethodParamNames[i]
);
}
};

const onDidChangeLoadingState = (event: OnDidChangeLoadingStateEvent) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ const SideBar = (): JSX.Element => {
<div className={`section-link ${activeSection === SectionId.Classpath ? "section-link-active" : ""} mb-1`} onClick={() => onClickNavBarItem(SectionId.Classpath)}>
Classpath
</div>
<div className={`section-link ${activeSection === SectionId.Compiler ? "section-link-active" : ""} mb-1`} onClick={() => onClickNavBarItem(SectionId.Compiler)}>
Compiler
</div>
{
projectType === ProjectType.Maven && (
<div className={`section-link ${activeSection === SectionId.Maven ? "section-link-active" : ""} mb-1`} onClick={() => onClickNavBarItem(SectionId.Maven)}>
Expand Down
Loading
Loading