diff --git a/src/main/java/org/sonarlint/intellij/SonarLintIcons.kt b/src/main/java/org/sonarlint/intellij/SonarLintIcons.kt index 22d2b703e..fdacee209 100644 --- a/src/main/java/org/sonarlint/intellij/SonarLintIcons.kt +++ b/src/main/java/org/sonarlint/intellij/SonarLintIcons.kt @@ -82,6 +82,8 @@ object SonarLintIcons { val RESOLVED = getIcon("/images/resolved.svg") @JvmField val FOCUS = getIcon("/images/focus.svg") + @JvmField + val WALKTHROUGH_ICON = getIcon("/images/sonarqube-for-ide-mark.png") private val BUG_ICONS = mapOf( IssueSeverity.BLOCKER to getIcon("/images/bug/bugBlocker.svg"), diff --git a/src/main/java/org/sonarlint/intellij/documentation/SonarLintDocumentation.kt b/src/main/java/org/sonarlint/intellij/documentation/SonarLintDocumentation.kt index 48302df76..9bd982d1b 100644 --- a/src/main/java/org/sonarlint/intellij/documentation/SonarLintDocumentation.kt +++ b/src/main/java/org/sonarlint/intellij/documentation/SonarLintDocumentation.kt @@ -36,6 +36,7 @@ object SonarLintDocumentation { const val RULE_SECTION_LINK = "$BASE_DOCS_URL/using-sonarlint/rules/#rule-selection" const val FILE_EXCLUSION_LINK = "$BASE_DOCS_URL/using-sonarlint/file-exclusions" const val AI_FIX_SUGGESTIONS_LINK = "$BASE_DOCS_URL/using-sonarlint/investigating-issues/#ai-generated-fix-suggestions" + const val COMMUNITY_LINK = "https://community.sonarsource.com/c/sl/fault/6" } object SonarQube { diff --git a/src/main/java/org/sonarlint/intellij/telemetry/LinkTelemetry.kt b/src/main/java/org/sonarlint/intellij/telemetry/LinkTelemetry.kt index 4a41be01e..c4dff86e8 100644 --- a/src/main/java/org/sonarlint/intellij/telemetry/LinkTelemetry.kt +++ b/src/main/java/org/sonarlint/intellij/telemetry/LinkTelemetry.kt @@ -24,6 +24,7 @@ import org.sonarlint.intellij.common.util.SonarLintUtils import org.sonarlint.intellij.documentation.SonarLintDocumentation import org.sonarlint.intellij.documentation.SonarLintDocumentation.Intellij.AI_FIX_SUGGESTIONS_LINK import org.sonarlint.intellij.documentation.SonarLintDocumentation.Intellij.BASE_DOCS_URL +import org.sonarlint.intellij.documentation.SonarLintDocumentation.Intellij.COMMUNITY_LINK import org.sonarlint.intellij.documentation.SonarLintDocumentation.Intellij.RULE_SECTION_LINK enum class LinkTelemetry( @@ -36,6 +37,7 @@ enum class LinkTelemetry( SONARQUBE_EDITIONS_DOWNLOADS("sonarQubeEditionsDownloads", SonarLintDocumentation.Marketing.SONARQUBE_EDITIONS_DOWNLOADS_LINK), RULE_SELECTION_PAGE("rulesSelectionDocs", RULE_SECTION_LINK), AI_FIX_SUGGESTIONS_PAGE("aiFixSuggestionsDocs", AI_FIX_SUGGESTIONS_LINK), + COMMUNITY_PAGE("communityReportPage", COMMUNITY_LINK), BASE_DOCS_PAGE("baseDocs", BASE_DOCS_URL); fun browseWithTelemetry() { diff --git a/src/main/java/org/sonarlint/intellij/ui/ConnectWithYourTeamPage.java b/src/main/java/org/sonarlint/intellij/ui/ConnectWithYourTeamPage.java deleted file mode 100644 index dc946b313..000000000 --- a/src/main/java/org/sonarlint/intellij/ui/ConnectWithYourTeamPage.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * SonarLint for IntelliJ IDEA - * Copyright (C) 2015-2024 SonarSource - * sonarlint@sonarsource.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonarlint.intellij.ui; - -import com.intellij.openapi.options.ShowSettingsUtil; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.wm.ToolWindowManager; -import com.intellij.ui.HyperlinkAdapter; -import com.intellij.util.ui.UIUtil; -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.GridBagConstraints; -import java.util.Objects; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JEditorPane; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.SwingConstants; -import javax.swing.event.HyperlinkEvent; -import org.sonarlint.intellij.config.project.SonarLintProjectConfigurable; - -import static org.sonarlint.intellij.telemetry.LinkTelemetry.AI_FIX_SUGGESTIONS_PAGE; -import static org.sonarlint.intellij.ui.SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE; - -public class ConnectWithYourTeamPage { - private final JPanel connectWithYourTeamPagePanel; - - public ConnectWithYourTeamPage(Project project, JButton connectWithYourTeamNextButton, JButton connectWithYourTeamBackButton) { - var font = UIUtil.getLabelFont(); - connectWithYourTeamPagePanel = new JPanel(new BorderLayout()); - - var icon = new ImageIcon(Objects.requireNonNull(getClass().getResource("/images/sonarqube-for-ide-mark.png"))); - var connectWithYourTeamImageLabel = new JLabel(icon); - - var connectWithYourTeamStepLabel = new JLabel("Step 3/4", SwingConstants.LEFT); - connectWithYourTeamStepLabel.setFont(new Font(SonarLintWalkthroughToolWindow.FONT, Font.PLAIN, 14)); - - var connectWithYourTeamLabel = new JLabel("Connect with your team"); - connectWithYourTeamLabel.setFont(new Font(SonarLintWalkthroughToolWindow.FONT, Font.BOLD, 16)); - var connectWithYourTeamText = createConnectWithYourTeamPageText(font, project); - - var connectWithYourTeamScrollPane = new JScrollPane(connectWithYourTeamText); - connectWithYourTeamScrollPane.setBorder(null); - connectWithYourTeamScrollPane.setPreferredSize(new Dimension(SonarLintWalkthroughToolWindow.WIDTH, - SonarLintWalkthroughToolWindow.HEIGHT)); - - var connectWithYourTeamBackButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - connectWithYourTeamBackButtonPanel.add(connectWithYourTeamBackButton); - - var connectWithYourTeamNextButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - connectWithYourTeamNextButtonPanel.add(connectWithYourTeamNextButton); - - createConnectWithYourTeamLayout(connectWithYourTeamStepLabel, connectWithYourTeamLabel, connectWithYourTeamScrollPane, - connectWithYourTeamBackButtonPanel, connectWithYourTeamNextButtonPanel, connectWithYourTeamPagePanel, connectWithYourTeamImageLabel); - } - - public JPanel getPanel() { - return connectWithYourTeamPagePanel; - } - - private static void createConnectWithYourTeamLayout(JLabel stepLabel, JLabel label, JScrollPane pane, - JPanel backButtonPanel, JPanel nextButtonPanel, - JPanel page, JLabel imageLabel) { - var gbc = new GridBagConstraints(); - - var centerPanel = SonarLintWalkthroughUtils.createCenterPanel(stepLabel, label, pane, gbc); - - gbc.anchor = GridBagConstraints.SOUTHWEST; - SonarLintWalkthroughUtils.provideCommonButtonConstraints(gbc); - centerPanel.add(backButtonPanel, gbc); - - gbc.anchor = GridBagConstraints.SOUTHEAST; - centerPanel.add(nextButtonPanel, gbc); - - page.add(imageLabel, BorderLayout.NORTH); - page.add(centerPanel, BorderLayout.CENTER); - } - - private static JEditorPane createConnectWithYourTeamPageText(Font font, Project project) { - var descriptionPane = new JEditorPane(SonarLintWalkthroughToolWindow.EDITOR_PANE_TYPE, - "" + - "Apply the same set of rules as your team by using " + SONARQUBE_FOR_IDE + " in Connected Mode with SonarQube Cloud or SonarQube " + - "Server" + - ".

" + - "With connected mode, benefit from advanced analysis like Taint Vulnerabilities and open " + - "issues and AI fix suggestions from SonarQube Server or Cloud in the IDE.

" + - "Already using SonarQube Cloud or Server? Set up a connection."); - - descriptionPane.setEditable(false); - descriptionPane.setOpaque(false); - - descriptionPane.addHyperlinkListener(new HyperlinkAdapter() { - @Override - protected void hyperlinkActivated(HyperlinkEvent e) { - if ("#setupConnection".equals(e.getDescription())) { - var configurable = new SonarLintProjectConfigurable(project); - ShowSettingsUtil.getInstance().editConfigurable(project, configurable); - } else if ("#taintVulnerabilities".equals(e.getDescription())) { - var sonarqubeToolWindow = ToolWindowManager.getInstance(project).getToolWindow(SONARQUBE_FOR_IDE); - if (sonarqubeToolWindow != null) { - if (!sonarqubeToolWindow.isVisible()) { - sonarqubeToolWindow.activate(null); - } - var taintVulnerabilitiesContent = sonarqubeToolWindow.getContentManager().findContent("Taint Vulnerabilities"); - if (taintVulnerabilitiesContent != null) { - sonarqubeToolWindow.getContentManager().setSelectedContent(taintVulnerabilitiesContent); - } - } - } else { - AI_FIX_SUGGESTIONS_PAGE.browseWithTelemetry(); - } - } - }); - return descriptionPane; - } -} diff --git a/src/main/java/org/sonarlint/intellij/ui/LearnAsYouCodePage.java b/src/main/java/org/sonarlint/intellij/ui/LearnAsYouCodePage.java deleted file mode 100644 index 66c172d52..000000000 --- a/src/main/java/org/sonarlint/intellij/ui/LearnAsYouCodePage.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * SonarLint for IntelliJ IDEA - * Copyright (C) 2015-2024 SonarSource - * sonarlint@sonarsource.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonarlint.intellij.ui; - -import com.intellij.openapi.options.ShowSettingsUtil; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.wm.ToolWindowManager; -import com.intellij.ui.HyperlinkAdapter; -import com.intellij.util.ui.UIUtil; -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.GridBagConstraints; -import java.util.Objects; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JEditorPane; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.SwingConstants; -import javax.swing.event.HyperlinkEvent; -import org.sonarlint.intellij.config.global.SonarLintGlobalConfigurable; - -import static org.sonarlint.intellij.ui.SonarLintWalkthroughToolWindow.EDITOR_PANE_TYPE; -import static org.sonarlint.intellij.ui.SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE; - -public class LearnAsYouCodePage { - private final JPanel learnAsYouCodePagePanel; - - public LearnAsYouCodePage(Project project, JButton learnAsYouCodePageNextButton, JButton learnAsYouCodePageBackButton) { - var font = UIUtil.getLabelFont(); - learnAsYouCodePagePanel = new JPanel(new BorderLayout()); - - var icon = new ImageIcon(Objects.requireNonNull(getClass().getResource("/images/sonarqube-for-ide-mark.png"))); - var learnAsYouCodeImageLabel = new JLabel(icon); - - var learnAsYouCodeStepLabel = new JLabel("Step 2/4", SwingConstants.LEFT); - learnAsYouCodeStepLabel.setFont(new Font(SonarLintWalkthroughToolWindow.FONT, Font.PLAIN, 14)); - - var learnAsYouCodePageLabel = new JLabel("Learn as you code"); - learnAsYouCodePageLabel.setFont(new Font(SonarLintWalkthroughToolWindow.FONT, Font.BOLD, 16)); - var learnAsYouCodeText = createLearnAsYouCodePageText(font, project); - - var learnAsYouCodeScrollPane = new JScrollPane(learnAsYouCodeText); - learnAsYouCodeScrollPane.setBorder(null); - learnAsYouCodeScrollPane.setPreferredSize(new Dimension(SonarLintWalkthroughToolWindow.WIDTH, SonarLintWalkthroughToolWindow.HEIGHT)); - - var learnAsYouCodePageBackButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - learnAsYouCodePageBackButtonPanel.add(learnAsYouCodePageBackButton); - - var learnAsYouCodePageNextButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - learnAsYouCodePageNextButtonPanel.add(learnAsYouCodePageNextButton); - - createLearnAsYouCodePageLayout(learnAsYouCodeStepLabel, learnAsYouCodePageLabel, learnAsYouCodeScrollPane, - learnAsYouCodePageBackButtonPanel, learnAsYouCodePageNextButtonPanel, learnAsYouCodePagePanel, learnAsYouCodeImageLabel); - } - - public JPanel getPanel() { - return learnAsYouCodePagePanel; - } - - private static void createLearnAsYouCodePageLayout(JLabel stepLabel, JLabel label, JScrollPane pane, - JPanel backButtonPanel, JPanel nextButtonPanel, - JPanel page, JLabel imageLabel) { - var gbc = new GridBagConstraints(); - - var centerPanel = SonarLintWalkthroughUtils.createCenterPanel(stepLabel, label, pane, gbc); - - gbc.anchor = GridBagConstraints.SOUTHWEST; - SonarLintWalkthroughUtils.provideCommonButtonConstraints(gbc); - centerPanel.add(backButtonPanel, gbc); - - gbc.anchor = GridBagConstraints.SOUTHEAST; - centerPanel.add(nextButtonPanel, gbc); - - page.add(imageLabel, BorderLayout.NORTH); - page.add(centerPanel, BorderLayout.CENTER); - } - - private static JEditorPane createLearnAsYouCodePageText(Font font, Project project) { - var descriptionPane = new JEditorPane(EDITOR_PANE_TYPE, "" + - "Check the Current File view: When " + SONARQUBE_FOR_IDE + " found something, click on the issue to " + - "get the rule " + - "description and an example of compliant code.

" + - "Some rules offer quick fixes when you hover over the issue location.

" + - "Finally you can disable rules in the settings."); - - descriptionPane.setEditable(false); - descriptionPane.setOpaque(false); - - descriptionPane.addHyperlinkListener(new HyperlinkAdapter() { - @Override - protected void hyperlinkActivated(HyperlinkEvent e) { - if ("#currentFile".equals(e.getDescription())) { - var sonarqubeToolWindow = ToolWindowManager.getInstance(project).getToolWindow(SONARQUBE_FOR_IDE); - if (sonarqubeToolWindow != null) { - if (!sonarqubeToolWindow.isVisible()) { - sonarqubeToolWindow.activate(null); - } - var currentFileContent = sonarqubeToolWindow.getContentManager().findContent("Current File"); - if (currentFileContent != null) { - sonarqubeToolWindow.getContentManager().setSelectedContent(currentFileContent); - } - } - } else if ("#settings".equals(e.getDescription())) { - ShowSettingsUtil.getInstance().showSettingsDialog(project, SonarLintGlobalConfigurable.class); - } - } - }); - return descriptionPane; - } -} diff --git a/src/main/java/org/sonarlint/intellij/ui/ReachOutToUsPage.java b/src/main/java/org/sonarlint/intellij/ui/ReachOutToUsPage.java deleted file mode 100644 index 4324cce60..000000000 --- a/src/main/java/org/sonarlint/intellij/ui/ReachOutToUsPage.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * SonarLint for IntelliJ IDEA - * Copyright (C) 2015-2024 SonarSource - * sonarlint@sonarsource.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonarlint.intellij.ui; - -import com.intellij.ide.BrowserUtil; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.wm.ToolWindowManager; -import com.intellij.ui.HyperlinkAdapter; -import com.intellij.util.ui.UIUtil; -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.GridBagConstraints; -import java.util.Objects; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JEditorPane; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.SwingConstants; -import javax.swing.event.HyperlinkEvent; - -import static org.sonarlint.intellij.telemetry.LinkTelemetry.BASE_DOCS_PAGE; - -public class ReachOutToUsPage { - private final JPanel reachOutToUsPagePanel; - - public ReachOutToUsPage(Project project, JButton reachOutToUsBackButton, JButton closeButton) { - var font = UIUtil.getLabelFont(); - reachOutToUsPagePanel = new JPanel(new BorderLayout()); - - var icon = new ImageIcon(Objects.requireNonNull(getClass().getResource("/images/sonarqube-for-ide-mark.png"))); - var reachOutToUsImageLabel = new JLabel(icon); - - var reachOutToUsStepLabel = new JLabel("Step 4/4", SwingConstants.LEFT); - reachOutToUsStepLabel.setFont(new Font(SonarLintWalkthroughToolWindow.FONT, Font.PLAIN, 14)); - - var reachOutToUsLabel = new JLabel("Reach out to us"); - reachOutToUsLabel.setFont(new Font(SonarLintWalkthroughToolWindow.FONT, Font.BOLD, 16)); - - var reachOutToUsDescription = createReachOutToUsPageText(font, project); - - var reachOutToUsPane = new JScrollPane(reachOutToUsDescription); - reachOutToUsPane.setBorder(null); - reachOutToUsPane.setPreferredSize(new Dimension(SonarLintWalkthroughToolWindow.WIDTH, 100)); - - var reachOutToUsBackButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - reachOutToUsBackButtonPanel.add(reachOutToUsBackButton); - - var closeButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - closeButtonPanel.add(closeButton); - - createReachOutToUsPageLayout(reachOutToUsStepLabel, project, reachOutToUsLabel, reachOutToUsPane, reachOutToUsBackButtonPanel, - closeButtonPanel, reachOutToUsPagePanel, reachOutToUsImageLabel); - } - - public JPanel getPanel() { - return reachOutToUsPagePanel; - } - - private static void createReachOutToUsPageLayout(JLabel stepLabel, Project project, JLabel pageLabel, JScrollPane scrollPane, - JPanel backButtonPanel, JPanel closeButtonPanel, JPanel panel, JLabel imageLabel) { - var gbc = new GridBagConstraints(); - - var page4CenterPanel = SonarLintWalkthroughUtils.createCenterPanel(stepLabel, pageLabel, scrollPane, gbc); - - gbc.anchor = GridBagConstraints.SOUTHWEST; - SonarLintWalkthroughUtils.provideCommonButtonConstraints(gbc); - page4CenterPanel.add(backButtonPanel, gbc); - - gbc.anchor = GridBagConstraints.SOUTHEAST; - page4CenterPanel.add(closeButtonPanel, gbc); - - panel.add(imageLabel, BorderLayout.NORTH); - panel.add(page4CenterPanel, BorderLayout.CENTER); - } - - private static JEditorPane createReachOutToUsPageText(Font font, Project project) { - var descriptionPane = new JEditorPane(SonarLintWalkthroughToolWindow.EDITOR_PANE_TYPE, - "" + - "You suspect any issue with " + SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE + "? Check the log view" + - ".
" + - "Share the verbose logs with us via Community forum in case of problem. We will be happy to help " + - "you debug.

" + - "Learn more about " + SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE + " through our docs."); - - descriptionPane.setEditable(false); - descriptionPane.setOpaque(false); - descriptionPane.addHyperlinkListener(new HyperlinkAdapter() { - @Override - public void hyperlinkActivated(HyperlinkEvent e) { - if ("#logView".equals(e.getDescription())) { - var sonarqubeToolWindow = ToolWindowManager.getInstance(project).getToolWindow(SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE); - if (sonarqubeToolWindow != null) { - if (!sonarqubeToolWindow.isVisible()) { - sonarqubeToolWindow.activate(null); - } - var currentFileContent = sonarqubeToolWindow.getContentManager().findContent("Log"); - if (currentFileContent != null) { - sonarqubeToolWindow.getContentManager().setSelectedContent(currentFileContent); - } - } - } else if ("#communityForum".equals(e.getDescription())) { - BrowserUtil.browse("https://community.sonarsource.com/c/sl/fault/6"); - } else if ("#docs".equals(e.getDescription())) { - BASE_DOCS_PAGE.browseWithTelemetry(); - } - } - }); - - return descriptionPane; - } -} diff --git a/src/main/java/org/sonarlint/intellij/ui/SonarLintWalkthroughToolWindow.java b/src/main/java/org/sonarlint/intellij/ui/SonarLintWalkthroughToolWindow.java deleted file mode 100644 index 9e1678b3f..000000000 --- a/src/main/java/org/sonarlint/intellij/ui/SonarLintWalkthroughToolWindow.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * SonarLint for IntelliJ IDEA - * Copyright (C) 2015-2024 SonarSource - * sonarlint@sonarsource.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonarlint.intellij.ui; - -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.wm.ToolWindow; -import com.intellij.openapi.wm.ToolWindowFactory; -import com.intellij.openapi.wm.ToolWindowManager; -import com.intellij.ui.content.ContentFactory; -import java.awt.CardLayout; -import java.awt.Dimension; -import javax.swing.JButton; -import javax.swing.JPanel; - -public class SonarLintWalkthroughToolWindow implements ToolWindowFactory, DumbAware { - - protected static final String SONARQUBE_FOR_IDE = "SonarQube for IDE"; - protected static final String PAGE_2 = "Page 2"; - protected static final String PAGE_3 = "Page 3"; - protected static final String FONT = "Arial"; - protected static final String EDITOR_PANE_TYPE = "text/html"; - protected static final String PREVIOUS = "Previous"; - protected static final int WIDTH = 300; - protected static final int HEIGHT = 200; - - @Override - public void createToolWindowContent(Project project, ToolWindow toolWindow) { - var mainPanel = new JPanel(new CardLayout()); - mainPanel.setPreferredSize(new Dimension(WIDTH, HEIGHT)); - - var welcomePageNextButton = new JButton("Next: Learn as you code"); - var learnAsYouCodePageNextButton = new JButton("Next: Connect with your team"); - var learnAsYouCodePageBackButton = new JButton(PREVIOUS); - var connectWithYourTeamBackButton = new JButton(PREVIOUS); - var connectWithYourTeamNextButton = new JButton("Next: Reach out to us"); - var closeButton = new JButton("Close"); - var reachOutToUsBackButton = new JButton(PREVIOUS); - - var welcomePage = new WelcomePage(project, welcomePageNextButton).getPanel(); - var learnAsYouCodePage = new LearnAsYouCodePage(project, learnAsYouCodePageNextButton, learnAsYouCodePageBackButton).getPanel(); - var connectWithYourTeamPage = - new ConnectWithYourTeamPage(project, connectWithYourTeamNextButton, connectWithYourTeamBackButton).getPanel(); - var reachOutToUsPage = new ReachOutToUsPage(project, reachOutToUsBackButton, closeButton).getPanel(); - - mainPanel.add(welcomePage, "Page 1"); - mainPanel.add(learnAsYouCodePage, PAGE_2); - mainPanel.add(connectWithYourTeamPage, PAGE_3); - mainPanel.add(reachOutToUsPage, "Page 4"); - - addButtonActionListeners(welcomePageNextButton, mainPanel, learnAsYouCodePageBackButton, learnAsYouCodePageNextButton, - connectWithYourTeamBackButton, connectWithYourTeamNextButton, reachOutToUsBackButton, closeButton, project); - - var contentFactory = ContentFactory.getInstance(); - var content = contentFactory.createContent(mainPanel, "", false); - toolWindow.getContentManager().addContent(content); - } - - private static void addButtonActionListeners(JButton welcomePageNextButton, JPanel mainPanel, JButton learnAsYouCodePageBackButton, - JButton learnAsYouCodePageNextButton, JButton connectWithYourTeamBackButton, JButton connectWithYourTeamNextButton, - JButton reachOutToUsBackButton, JButton closeButton, Project project) { - welcomePageNextButton.addActionListener(e -> { - var cl = (CardLayout) (mainPanel.getLayout()); - cl.show(mainPanel, PAGE_2); - }); - - learnAsYouCodePageBackButton.addActionListener(e -> { - var cl = (CardLayout) (mainPanel.getLayout()); - cl.show(mainPanel, "Page 1"); - }); - - learnAsYouCodePageNextButton.addActionListener(e -> { - var cl = (CardLayout) (mainPanel.getLayout()); - cl.show(mainPanel, PAGE_3); - }); - - connectWithYourTeamBackButton.addActionListener(e -> { - var cl = (CardLayout) (mainPanel.getLayout()); - cl.show(mainPanel, PAGE_2); - }); - - connectWithYourTeamNextButton.addActionListener(e -> { - var cl = (CardLayout) (mainPanel.getLayout()); - cl.show(mainPanel, "Page 4"); - }); - - reachOutToUsBackButton.addActionListener(e -> { - var cl = (CardLayout) (mainPanel.getLayout()); - cl.show(mainPanel, PAGE_3); - }); - - closeButton.addActionListener(e -> { - var toolWindow = ToolWindowManager.getInstance(project).getToolWindow("Welcome to SonarQube for IDE"); - if (toolWindow != null) { - toolWindow.hide(null); - } - }); - } -} - diff --git a/src/main/java/org/sonarlint/intellij/ui/SonarLintWalkthroughUtils.java b/src/main/java/org/sonarlint/intellij/ui/SonarLintWalkthroughUtils.java deleted file mode 100644 index 892ebc7c3..000000000 --- a/src/main/java/org/sonarlint/intellij/ui/SonarLintWalkthroughUtils.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SonarLint for IntelliJ IDEA - * Copyright (C) 2015-2024 SonarSource - * sonarlint@sonarsource.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonarlint.intellij.ui; - -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import javax.swing.BorderFactory; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import org.jetbrains.annotations.NotNull; - -public class SonarLintWalkthroughUtils { - - public static @NotNull JPanel createCenterPanel(JLabel stepLabel, JLabel pageLabel, JScrollPane scrollPane, GridBagConstraints gbc) { - var centerPanel = new JPanel(new GridBagLayout()); - pageLabel.setBorder(BorderFactory.createEmptyBorder(2, 8, 2, 0)); - stepLabel.setBorder(BorderFactory.createEmptyBorder(2, 8, 2, 0)); - - gbc.gridx = 0; - gbc.gridy = 0; - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.NONE; - centerPanel.add(stepLabel, gbc); - - gbc.gridx = 0; - gbc.gridy = 1; - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.NONE; - centerPanel.add(pageLabel, gbc); - - gbc.gridy = 2; - gbc.fill = GridBagConstraints.BOTH; - gbc.weightx = 1.0; - gbc.weighty = 1.0; - centerPanel.add(scrollPane, gbc); - - return centerPanel; - } - - public static void provideCommonButtonConstraints(GridBagConstraints gbc) { - gbc.gridy = 3; - gbc.fill = GridBagConstraints.NONE; - gbc.weightx = 0; - gbc.weighty = 0; - } -} diff --git a/src/main/java/org/sonarlint/intellij/ui/WelcomePage.java b/src/main/java/org/sonarlint/intellij/ui/WelcomePage.java deleted file mode 100644 index a3054874f..000000000 --- a/src/main/java/org/sonarlint/intellij/ui/WelcomePage.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * SonarLint for IntelliJ IDEA - * Copyright (C) 2015-2024 SonarSource - * sonarlint@sonarsource.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonarlint.intellij.ui; - -import com.intellij.openapi.project.Project; -import com.intellij.openapi.wm.ToolWindowManager; -import com.intellij.ui.HyperlinkAdapter; -import com.intellij.util.ui.UIUtil; -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.GridBagConstraints; -import java.util.Objects; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JEditorPane; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.SwingConstants; -import javax.swing.event.HyperlinkEvent; - -import static org.sonarlint.intellij.documentation.SonarLintDocumentation.Intellij.RULE_SECTION_LINK; -import static org.sonarlint.intellij.telemetry.LinkTelemetry.RULE_SELECTION_PAGE; - -public class WelcomePage { - private final JPanel welcomePagePanel; - - public WelcomePage(Project project, JButton welcomePageNextButton) { - var font = UIUtil.getLabelFont(); - welcomePagePanel = new JPanel(new BorderLayout()); - - var icon = new ImageIcon(Objects.requireNonNull(getClass().getResource("/images/sonarqube-for-ide-mark.png"))); - var welcomeImageLabel = new JLabel(icon); - - var welcomeStepLabel = new JLabel("Step 1/4", SwingConstants.LEFT); - welcomeStepLabel.setFont(new Font(SonarLintWalkthroughToolWindow.FONT, Font.PLAIN, 14)); - - var titleLabel = new JLabel("Get started", SwingConstants.LEFT); - titleLabel.setFont(new Font(SonarLintWalkthroughToolWindow.FONT, Font.BOLD, 16)); - var welcomePageText = createWelcomePageText(font, project); - - var welcomePageScrollPane = new JScrollPane(welcomePageText); - welcomePageScrollPane.setBorder(null); - welcomePageScrollPane.setPreferredSize(new Dimension(70, 100)); - - var welcomePageNextButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - welcomePageNextButtonPanel.add(welcomePageNextButton); - - createWelcomePageLayout(welcomeStepLabel, titleLabel, welcomePageScrollPane, welcomePageNextButtonPanel, welcomePagePanel, - welcomeImageLabel); - } - - public JPanel getPanel() { - return welcomePagePanel; - } - - private static void createWelcomePageLayout(JLabel stepLabel, JLabel titleLabel, JScrollPane welcomePageScrollPane, - JPanel welcomePageNextButtonPanel, JPanel welcomePagePanel, JLabel welcomePageImageLabel) { - var gbc = new GridBagConstraints(); - - var centerPanel = SonarLintWalkthroughUtils.createCenterPanel(stepLabel, titleLabel, welcomePageScrollPane, gbc); - - gbc.anchor = GridBagConstraints.SOUTHEAST; - SonarLintWalkthroughUtils.provideCommonButtonConstraints(gbc); - centerPanel.add(welcomePageNextButtonPanel, gbc); - - welcomePagePanel.add(welcomePageImageLabel, BorderLayout.NORTH); - welcomePagePanel.add(centerPanel, BorderLayout.CENTER); - } - - private static JEditorPane createWelcomePageText(Font font, Project project) { - var descriptionPane = new JEditorPane(SonarLintWalkthroughToolWindow.EDITOR_PANE_TYPE, - "" + - SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE + " supports the analysis of 15+ languages including Python, Java, Javascript, " + - "IaC" + - " domains along with secrets " + - "detection. " + - "Learn more.

" + - "Detect issues while you code in an open files or run the analysis on more file in the report view" + - ".

" + - "Open a file and start your clean code journey."); - - descriptionPane.setEditable(false); - descriptionPane.setOpaque(false); - - descriptionPane.addHyperlinkListener(new HyperlinkAdapter() { - @Override - protected void hyperlinkActivated(HyperlinkEvent e) { - if ("#reportView".equals(e.getDescription())) { - var sonarqubeToolWindow = ToolWindowManager.getInstance(project).getToolWindow(SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE); - if (sonarqubeToolWindow != null) { - if (!sonarqubeToolWindow.isVisible()) { - sonarqubeToolWindow.activate(null); - } - var reportContent = sonarqubeToolWindow.getContentManager().findContent("Report"); - if (reportContent != null) { - sonarqubeToolWindow.getContentManager().setSelectedContent(reportContent); - } - } - } else { - RULE_SELECTION_PAGE.browseWithTelemetry(); - } - } - }); - return descriptionPane; - } -} diff --git a/src/main/java/org/sonarlint/intellij/ui/walkthrough/ConnectWithYourTeamPanel.kt b/src/main/java/org/sonarlint/intellij/ui/walkthrough/ConnectWithYourTeamPanel.kt new file mode 100644 index 000000000..f141fd49c --- /dev/null +++ b/src/main/java/org/sonarlint/intellij/ui/walkthrough/ConnectWithYourTeamPanel.kt @@ -0,0 +1,124 @@ +/* + * SonarLint for IntelliJ IDEA + * Copyright (C) 2015-2024 SonarSource + * sonarlint@sonarsource.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonarlint.intellij.ui.walkthrough + +import com.intellij.openapi.options.ShowSettingsUtil +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.ToolWindow +import com.intellij.openapi.wm.ToolWindowManager +import com.intellij.ui.HyperlinkAdapter +import com.intellij.util.ui.JBUI +import com.intellij.util.ui.SwingHelper +import com.intellij.util.ui.UIUtil +import java.awt.BorderLayout +import java.awt.Dimension +import java.awt.FlowLayout +import java.awt.Font +import javax.swing.JButton +import javax.swing.JEditorPane +import javax.swing.JLabel +import javax.swing.JPanel +import javax.swing.JScrollPane +import javax.swing.SwingConstants +import javax.swing.event.HyperlinkEvent +import org.sonarlint.intellij.SonarLintIcons +import org.sonarlint.intellij.config.project.SonarLintProjectConfigurable +import org.sonarlint.intellij.telemetry.LinkTelemetry +import org.sonarlint.intellij.ui.walkthrough.SonarLintWalkthroughUtils.addCenterPanel + +private const val TAINT_VULNERABILITIES = "Taint Vulnerabilities" + +class ConnectWithYourTeamPanel(project: Project, connectWithYourTeamNextButton: JButton?, connectWithYourTeamBackButton: JButton?) : JPanel(BorderLayout()) { + + init { + val font = UIUtil.getLabelFont() + + val connectWithYourTeamImageLabel = JLabel(SonarLintIcons.WALKTHROUGH_ICON) + + val connectWithYourTeamStepLabel = JLabel("Step 3/4", SwingConstants.LEFT) + connectWithYourTeamStepLabel.font = Font(SonarLintWalkthroughToolWindow.FONT, Font.PLAIN, 14) + + val connectWithYourTeamLabel = JLabel("Connect with your team") + connectWithYourTeamLabel.font = Font(SonarLintWalkthroughToolWindow.FONT, Font.BOLD, 16) + val connectWithYourTeamText = createConnectWithYourTeamPageText(font, project) + + val connectWithYourTeamScrollPane = JScrollPane(connectWithYourTeamText) + connectWithYourTeamScrollPane.border = null + connectWithYourTeamScrollPane.preferredSize = Dimension( + SonarLintWalkthroughToolWindow.WIDTH, + SonarLintWalkthroughToolWindow.HEIGHT + ) + + val connectWithYourTeamBackButtonPanel = JPanel(FlowLayout(FlowLayout.LEFT)) + connectWithYourTeamBackButtonPanel.add(connectWithYourTeamBackButton) + + val connectWithYourTeamNextButtonPanel = JPanel(FlowLayout(FlowLayout.RIGHT)) + connectWithYourTeamNextButtonPanel.add(connectWithYourTeamNextButton) + + createConnectWithYourTeamLayout( + connectWithYourTeamStepLabel, connectWithYourTeamLabel, connectWithYourTeamScrollPane, + connectWithYourTeamBackButtonPanel, connectWithYourTeamNextButtonPanel, this, connectWithYourTeamImageLabel + ) + } + + companion object { + private fun createConnectWithYourTeamLayout( + stepLabel: JLabel, label: JLabel, pane: JScrollPane, + backButtonPanel: JPanel, nextButtonPanel: JPanel, + panel: JPanel, imageLabel: JLabel + ) { + addCenterPanel(stepLabel, label, pane, backButtonPanel, nextButtonPanel, panel, imageLabel) + } + + private fun createConnectWithYourTeamPageText(font: Font, project: Project): JEditorPane { + val descriptionPane = SwingHelper.createHtmlViewer(false, font, null, null) + descriptionPane.text = "Apply the same set of rules as your team by using " + SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE + + " in Connected Mode with SonarQube Cloud or SonarQube Server" + + ".

" + + "With connected mode, benefit from advanced analysis like Taint Vulnerabilities and open" + + " issues and AI fix suggestions from SonarQube (Server, Cloud) in the IDE.

" + + "Already using SonarQube (Server, Cloud)? Set up a connection" + descriptionPane.border = JBUI.Borders.empty(8, 8) + + descriptionPane.addHyperlinkListener(object : HyperlinkAdapter() { + override fun hyperlinkActivated(e: HyperlinkEvent) { + if ("#setupConnection" == e.description) { + val configurable = SonarLintProjectConfigurable(project) + ShowSettingsUtil.getInstance().editConfigurable(project, configurable) + } else if ("#taintVulnerabilities" == e.description) { + val sonarqubeToolWindow: ToolWindow? = ToolWindowManager.getInstance(project).getToolWindow(SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE) + if (sonarqubeToolWindow != null) { + if (!sonarqubeToolWindow.isVisible) { + sonarqubeToolWindow.activate(null) + } + val taintVulnerabilitiesContent = sonarqubeToolWindow.contentManager.findContent(TAINT_VULNERABILITIES) + if (taintVulnerabilitiesContent != null) { + sonarqubeToolWindow.contentManager.setSelectedContent(taintVulnerabilitiesContent) + } + } + } else { + LinkTelemetry.AI_FIX_SUGGESTIONS_PAGE.browseWithTelemetry() + } + } + }) + return descriptionPane + } + } +} diff --git a/src/main/java/org/sonarlint/intellij/ui/walkthrough/LearnAsYouCodePanel.kt b/src/main/java/org/sonarlint/intellij/ui/walkthrough/LearnAsYouCodePanel.kt new file mode 100644 index 000000000..496e66e4c --- /dev/null +++ b/src/main/java/org/sonarlint/intellij/ui/walkthrough/LearnAsYouCodePanel.kt @@ -0,0 +1,119 @@ +/* + * SonarLint for IntelliJ IDEA + * Copyright (C) 2015-2024 SonarSource + * sonarlint@sonarsource.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonarlint.intellij.ui.walkthrough + +import com.intellij.openapi.options.ShowSettingsUtil +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.ToolWindow +import com.intellij.openapi.wm.ToolWindowManager +import com.intellij.ui.HyperlinkAdapter +import com.intellij.util.ui.JBUI +import com.intellij.util.ui.SwingHelper +import com.intellij.util.ui.UIUtil +import java.awt.BorderLayout +import java.awt.Dimension +import java.awt.FlowLayout +import java.awt.Font +import java.util.Objects +import javax.swing.ImageIcon +import javax.swing.JButton +import javax.swing.JEditorPane +import javax.swing.JLabel +import javax.swing.JPanel +import javax.swing.JScrollPane +import javax.swing.SwingConstants +import javax.swing.event.HyperlinkEvent +import org.sonarlint.intellij.config.global.SonarLintGlobalConfigurable +import org.sonarlint.intellij.ui.walkthrough.SonarLintWalkthroughUtils.addCenterPanel + +private const val CURRENT_FILE = "Current File" + +class LearnAsYouCodePanel(project: Project, learnAsYouCodePageNextButton: JButton?, learnAsYouCodePageBackButton: JButton?) : JPanel(BorderLayout()) { + + init { + val font = UIUtil.getLabelFont() + + val learnAsYouCodeImageLabel = JLabel(ImageIcon(Objects.requireNonNull(javaClass.getResource("/images/sonarqube-for-ide-mark.png")))) + + val learnAsYouCodeStepLabel = JLabel("Step 2/4", SwingConstants.LEFT) + learnAsYouCodeStepLabel.font = Font(SonarLintWalkthroughToolWindow.FONT, Font.PLAIN, 14) + + val learnAsYouCodePageLabel = JLabel("Learn as you code") + learnAsYouCodePageLabel.font = Font(SonarLintWalkthroughToolWindow.FONT, Font.BOLD, 16) + val learnAsYouCodeText = createLearnAsYouCodePageText(font, project) + + val learnAsYouCodeScrollPane = JScrollPane(learnAsYouCodeText) + learnAsYouCodeScrollPane.border = null + learnAsYouCodeScrollPane.preferredSize = Dimension(SonarLintWalkthroughToolWindow.WIDTH, SonarLintWalkthroughToolWindow.HEIGHT) + + val learnAsYouCodePageBackButtonPanel = JPanel(FlowLayout(FlowLayout.LEFT)) + learnAsYouCodePageBackButtonPanel.add(learnAsYouCodePageBackButton) + + val learnAsYouCodePageNextButtonPanel = JPanel(FlowLayout(FlowLayout.RIGHT)) + learnAsYouCodePageNextButtonPanel.add(learnAsYouCodePageNextButton) + + createLearnAsYouCodePageLayout( + learnAsYouCodeStepLabel, learnAsYouCodePageLabel, learnAsYouCodeScrollPane, + learnAsYouCodePageBackButtonPanel, learnAsYouCodePageNextButtonPanel, this, learnAsYouCodeImageLabel + ) + } + + companion object { + private fun createLearnAsYouCodePageLayout( + stepLabel: JLabel, label: JLabel, pane: JScrollPane, + backButtonPanel: JPanel, nextButtonPanel: JPanel, + panel: JPanel, imageLabel: JLabel, + ) { + addCenterPanel(stepLabel, label, pane, backButtonPanel, nextButtonPanel, panel, imageLabel) + } + + private fun createLearnAsYouCodePageText(font: Font, project: Project): JEditorPane { + val descriptionPane = SwingHelper.createHtmlViewer(false, font, null, null) + descriptionPane.text = + "Check the Current File view: When " + SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE + " found something, click on the issue to " + + "get the rule " + + "description and an example of compliant code.

" + + "Some rules offer quick fixes when you hover over the issue location.

" + + "Finally you can disable rules in the settings." + descriptionPane.border = JBUI.Borders.empty(8, 8) + + descriptionPane.addHyperlinkListener(object : HyperlinkAdapter() { + override fun hyperlinkActivated(e: HyperlinkEvent) { + if ("#currentFile" == e.description) { + val sonarqubeToolWindow: ToolWindow? = + ToolWindowManager.getInstance(project).getToolWindow(SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE) + if (sonarqubeToolWindow != null) { + if (!sonarqubeToolWindow.isVisible) { + sonarqubeToolWindow.activate(null) + } + val currentFileContent = sonarqubeToolWindow.contentManager.findContent(CURRENT_FILE) + if (currentFileContent != null) { + sonarqubeToolWindow.contentManager.setSelectedContent(currentFileContent) + } + } + } else if ("#settings" == e.description) { + ShowSettingsUtil.getInstance().showSettingsDialog(project, SonarLintGlobalConfigurable::class.java) + } + } + }) + return descriptionPane + } + } +} diff --git a/src/main/java/org/sonarlint/intellij/ui/walkthrough/ReachOutToUsPanel.kt b/src/main/java/org/sonarlint/intellij/ui/walkthrough/ReachOutToUsPanel.kt new file mode 100644 index 000000000..0fc987596 --- /dev/null +++ b/src/main/java/org/sonarlint/intellij/ui/walkthrough/ReachOutToUsPanel.kt @@ -0,0 +1,121 @@ +/* + * SonarLint for IntelliJ IDEA + * Copyright (C) 2015-2024 SonarSource + * sonarlint@sonarsource.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonarlint.intellij.ui.walkthrough + +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.ToolWindowManager +import com.intellij.ui.HyperlinkAdapter +import com.intellij.util.ui.JBUI +import com.intellij.util.ui.SwingHelper +import com.intellij.util.ui.UIUtil +import java.awt.BorderLayout +import java.awt.Dimension +import java.awt.FlowLayout +import java.awt.Font +import java.util.Objects +import javax.swing.ImageIcon +import javax.swing.JButton +import javax.swing.JEditorPane +import javax.swing.JLabel +import javax.swing.JPanel +import javax.swing.JScrollPane +import javax.swing.SwingConstants +import javax.swing.event.HyperlinkEvent +import org.sonarlint.intellij.telemetry.LinkTelemetry +import org.sonarlint.intellij.ui.walkthrough.SonarLintWalkthroughUtils.addCenterPanel + +private const val LOG = "Log" + +class ReachOutToUsPanel(project: Project, reachOutToUsBackButton: JButton?, closeButton: JButton?) : JPanel(BorderLayout()) { + + init { + val font = UIUtil.getLabelFont() + + val icon = ImageIcon(Objects.requireNonNull(javaClass.getResource("/images/sonarqube-for-ide-mark.png"))) + val reachOutToUsImageLabel = JLabel(icon) + + val reachOutToUsStepLabel = JLabel("Step 4/4", SwingConstants.LEFT) + reachOutToUsStepLabel.font = Font(SonarLintWalkthroughToolWindow.FONT, Font.PLAIN, 14) + + val reachOutToUsLabel = JLabel("Reach out to us") + reachOutToUsLabel.font = Font(SonarLintWalkthroughToolWindow.FONT, Font.BOLD, 16) + + val reachOutToUsDescription = createReachOutToUsPageText(font, project) + + val reachOutToUsPane = JScrollPane(reachOutToUsDescription) + reachOutToUsPane.border = null + reachOutToUsPane.preferredSize = Dimension(SonarLintWalkthroughToolWindow.WIDTH, 100) + + val reachOutToUsBackButtonPanel = JPanel(FlowLayout(FlowLayout.LEFT)) + reachOutToUsBackButtonPanel.add(reachOutToUsBackButton) + + val closeButtonPanel = JPanel(FlowLayout(FlowLayout.RIGHT)) + closeButtonPanel.add(closeButton) + + createReachOutToUsPageLayout( + reachOutToUsStepLabel, reachOutToUsLabel, reachOutToUsPane, reachOutToUsBackButtonPanel, + closeButtonPanel, this, reachOutToUsImageLabel + ) + } + + companion object { + private fun createReachOutToUsPageLayout( + stepLabel: JLabel, pageLabel: JLabel, scrollPane: JScrollPane, + backButtonPanel: JPanel, closeButtonPanel: JPanel, panel: JPanel, imageLabel: JLabel, + ) { + addCenterPanel(stepLabel, pageLabel, scrollPane, backButtonPanel, closeButtonPanel, panel, imageLabel) + } + + private fun createReachOutToUsPageText(font: Font, project: Project): JEditorPane { + val descriptionPane = SwingHelper.createHtmlViewer(false, font, null, null) + descriptionPane.text = + "You suspect any issue with ${SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE}? Check the log view.
" + + "Share the verbose logs with us via Community forum in case of problem. We will be happy to help " + + "you debug.

" + + "Learn more about ${SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE} through our docs." + descriptionPane.border = JBUI.Borders.empty(8, 8) + + descriptionPane.addHyperlinkListener(object : HyperlinkAdapter() { + override fun hyperlinkActivated(e: HyperlinkEvent) { + when (e.description) { + "#logView" -> { + val sonarqubeToolWindow = + ToolWindowManager.getInstance(project).getToolWindow(SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE) + if (sonarqubeToolWindow != null) { + if (!sonarqubeToolWindow.isVisible) { + sonarqubeToolWindow.activate(null) + } + val currentFileContent = sonarqubeToolWindow.contentManager.findContent(LOG) + if (currentFileContent != null) { + sonarqubeToolWindow.contentManager.setSelectedContent(currentFileContent) + } + } + } + + "#communityForum" -> LinkTelemetry.COMMUNITY_PAGE.browseWithTelemetry() + "#docs" -> LinkTelemetry.BASE_DOCS_PAGE.browseWithTelemetry() + } + } + }) + + return descriptionPane + } + } +} diff --git a/src/main/java/org/sonarlint/intellij/ui/walkthrough/SonarLintWalkthroughToolWindow.kt b/src/main/java/org/sonarlint/intellij/ui/walkthrough/SonarLintWalkthroughToolWindow.kt new file mode 100644 index 000000000..38818b2c0 --- /dev/null +++ b/src/main/java/org/sonarlint/intellij/ui/walkthrough/SonarLintWalkthroughToolWindow.kt @@ -0,0 +1,120 @@ +/* + * SonarLint for IntelliJ IDEA + * Copyright (C) 2015-2024 SonarSource + * sonarlint@sonarsource.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonarlint.intellij.ui.walkthrough + +import com.intellij.openapi.project.DumbAware +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.ToolWindow +import com.intellij.openapi.wm.ToolWindowFactory +import com.intellij.openapi.wm.ToolWindowManager +import com.intellij.ui.content.ContentFactory +import java.awt.CardLayout +import java.awt.Dimension +import javax.swing.JButton +import javax.swing.JPanel + +private const val WELCOME_TO_IDE_TOOLWINDOW = "Welcome to SonarQube for IDE" + +class SonarLintWalkthroughToolWindow : ToolWindowFactory, DumbAware { + override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { + val mainPanel = JPanel(CardLayout()) + mainPanel.preferredSize = + Dimension(WIDTH, HEIGHT) + + val welcomePageNextButton = JButton("Next: Learn as you code") + val learnAsYouCodePageNextButton = JButton("Next: Connect with your team") + val learnAsYouCodePageBackButton = JButton(PREVIOUS) + val connectWithYourTeamBackButton = JButton(PREVIOUS) + val connectWithYourTeamNextButton = JButton("Next: Reach out to us") + val closeButton = JButton("Close") + val reachOutToUsBackButton = JButton(PREVIOUS) + + val welcomePanel = WelcomePanel(project, welcomePageNextButton) + val learnAsYouCodePanel = LearnAsYouCodePanel(project, learnAsYouCodePageNextButton, learnAsYouCodePageBackButton) + val connectWithYourTeamPage = ConnectWithYourTeamPanel(project, connectWithYourTeamNextButton, connectWithYourTeamBackButton) + val reachOutToUsPanel = ReachOutToUsPanel(project, reachOutToUsBackButton, closeButton) + + mainPanel.add(welcomePanel, "Page 1") + mainPanel.add(learnAsYouCodePanel, PAGE_2) + mainPanel.add(connectWithYourTeamPage, PAGE_3) + mainPanel.add(reachOutToUsPanel, "Page 4") + + addButtonActionListeners( + welcomePageNextButton, mainPanel, learnAsYouCodePageBackButton, learnAsYouCodePageNextButton, + connectWithYourTeamBackButton, connectWithYourTeamNextButton, reachOutToUsBackButton, closeButton, project + ) + + val contentFactory = ContentFactory.getInstance() + val content = contentFactory.createContent(mainPanel, "", false) + toolWindow.contentManager.addContent(content) + } + + companion object { + const val SONARQUBE_FOR_IDE: String = "SonarQube for IDE" + protected const val PAGE_2: String = "Page 2" + protected const val PAGE_3: String = "Page 3" + const val FONT: String = "Arial" + const val EDITOR_PANE_TYPE: String = "text/html" + protected const val PREVIOUS: String = "Previous" + const val WIDTH: Int = 300 + const val HEIGHT: Int = 200 + + private fun addButtonActionListeners( + welcomePageNextButton: JButton, mainPanel: JPanel, learnAsYouCodePageBackButton: JButton, + learnAsYouCodePageNextButton: JButton, connectWithYourTeamBackButton: JButton, connectWithYourTeamNextButton: JButton, + reachOutToUsBackButton: JButton, closeButton: JButton, project: Project, + ) { + welcomePageNextButton.addActionListener { + val cl = mainPanel.layout as CardLayout + cl.show(mainPanel, PAGE_2) + } + + learnAsYouCodePageBackButton.addActionListener { + val cl = mainPanel.layout as CardLayout + cl.show(mainPanel, "Page 1") + } + + learnAsYouCodePageNextButton.addActionListener { + val cl = mainPanel.layout as CardLayout + cl.show(mainPanel, PAGE_3) + } + + connectWithYourTeamBackButton.addActionListener { + val cl = mainPanel.layout as CardLayout + cl.show(mainPanel, PAGE_2) + } + + connectWithYourTeamNextButton.addActionListener { + val cl = mainPanel.layout as CardLayout + cl.show(mainPanel, "Page 4") + } + + reachOutToUsBackButton.addActionListener { + val cl = mainPanel.layout as CardLayout + cl.show(mainPanel, PAGE_3) + } + + closeButton.addActionListener { + ToolWindowManager.getInstance(project).getToolWindow(WELCOME_TO_IDE_TOOLWINDOW)?.hide(null) + } + } + } +} + diff --git a/src/main/java/org/sonarlint/intellij/ui/walkthrough/SonarLintWalkthroughUtils.kt b/src/main/java/org/sonarlint/intellij/ui/walkthrough/SonarLintWalkthroughUtils.kt new file mode 100644 index 000000000..967b495fa --- /dev/null +++ b/src/main/java/org/sonarlint/intellij/ui/walkthrough/SonarLintWalkthroughUtils.kt @@ -0,0 +1,90 @@ +/* + * SonarLint for IntelliJ IDEA + * Copyright (C) 2015-2024 SonarSource + * sonarlint@sonarsource.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonarlint.intellij.ui.walkthrough + +import java.awt.BorderLayout +import java.awt.GridBagConstraints +import java.awt.GridBagLayout +import javax.swing.BorderFactory +import javax.swing.JLabel +import javax.swing.JPanel +import javax.swing.JScrollPane + +object SonarLintWalkthroughUtils { + @JvmStatic + fun createCenterPanel(stepLabel: JLabel, pageLabel: JLabel, scrollPane: JScrollPane?, gbc: GridBagConstraints): JPanel { + val centerPanel = JPanel(GridBagLayout()) + pageLabel.border = BorderFactory.createEmptyBorder(2, 8, 2, 0) + stepLabel.border = BorderFactory.createEmptyBorder(2, 8, 2, 0) + + gbc.gridx = 0 + gbc.gridy = 0 + gbc.anchor = GridBagConstraints.WEST + gbc.fill = GridBagConstraints.NONE + centerPanel.add(stepLabel, gbc) + + gbc.gridx = 0 + gbc.gridy = 1 + gbc.anchor = GridBagConstraints.WEST + gbc.fill = GridBagConstraints.NONE + centerPanel.add(pageLabel, gbc) + + gbc.gridy = 2 + gbc.fill = GridBagConstraints.BOTH + gbc.weightx = 1.0 + gbc.weighty = 1.0 + centerPanel.add(scrollPane, gbc) + + return centerPanel + } + + @JvmStatic + fun provideCommonButtonConstraints(gbc: GridBagConstraints) { + gbc.gridy = 3 + gbc.fill = GridBagConstraints.NONE + gbc.weightx = 0.0 + gbc.weighty = 0.0 + } + + @JvmStatic + fun addCenterPanel( + stepLabel: JLabel, + pageLabel: JLabel, + scrollPane: JScrollPane, + lefButtonPanel: JPanel, + rightButtonPanel: JPanel, + panel: JPanel, + imageLabel: JLabel, + ) { + val gbc = GridBagConstraints() + + val centerPanel = createCenterPanel(stepLabel, pageLabel, scrollPane, gbc) + + gbc.anchor = GridBagConstraints.SOUTHWEST + provideCommonButtonConstraints(gbc) + centerPanel.add(lefButtonPanel, gbc) + + gbc.anchor = GridBagConstraints.SOUTHEAST + centerPanel.add(rightButtonPanel, gbc) + + panel.add(imageLabel, BorderLayout.NORTH) + panel.add(centerPanel, BorderLayout.CENTER) + } +} diff --git a/src/main/java/org/sonarlint/intellij/ui/walkthrough/WelcomePanel.kt b/src/main/java/org/sonarlint/intellij/ui/walkthrough/WelcomePanel.kt new file mode 100644 index 000000000..557814a2b --- /dev/null +++ b/src/main/java/org/sonarlint/intellij/ui/walkthrough/WelcomePanel.kt @@ -0,0 +1,126 @@ +/* + * SonarLint for IntelliJ IDEA + * Copyright (C) 2015-2024 SonarSource + * sonarlint@sonarsource.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonarlint.intellij.ui.walkthrough + +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.ToolWindowManager +import com.intellij.ui.HyperlinkAdapter +import com.intellij.util.ui.JBUI +import com.intellij.util.ui.SwingHelper +import com.intellij.util.ui.UIUtil +import java.awt.BorderLayout +import java.awt.Dimension +import java.awt.FlowLayout +import java.awt.Font +import java.awt.GridBagConstraints +import java.util.Objects +import javax.swing.ImageIcon +import javax.swing.JButton +import javax.swing.JEditorPane +import javax.swing.JLabel +import javax.swing.JPanel +import javax.swing.JScrollPane +import javax.swing.SwingConstants +import javax.swing.event.HyperlinkEvent +import org.sonarlint.intellij.documentation.SonarLintDocumentation.Intellij.RULE_SECTION_LINK +import org.sonarlint.intellij.telemetry.LinkTelemetry + +private const val REPORT = "Report" + +class WelcomePanel(project: Project, welcomePageNextButton: JButton?) : JPanel(BorderLayout()) { + + init { + val font = UIUtil.getLabelFont() + + val icon = ImageIcon(Objects.requireNonNull(javaClass.getResource("/images/sonarqube-for-ide-mark.png"))) + val welcomeImageLabel = JLabel(icon) + + val welcomeStepLabel = JLabel("Step 1/4", SwingConstants.LEFT) + welcomeStepLabel.font = Font(SonarLintWalkthroughToolWindow.FONT, Font.PLAIN, 14) + + val titleLabel = JLabel("Get started", SwingConstants.LEFT) + titleLabel.font = Font(SonarLintWalkthroughToolWindow.FONT, Font.BOLD, 16) + val welcomePageText = createWelcomePageText(font, project) + + val welcomePageScrollPane = JScrollPane(welcomePageText) + welcomePageScrollPane.border = null + welcomePageScrollPane.preferredSize = Dimension(70, 100) + + val welcomePageNextButtonPanel = JPanel(FlowLayout(FlowLayout.RIGHT)) + welcomePageNextButtonPanel.add(welcomePageNextButton) + + createWelcomePageLayout( + welcomeStepLabel, titleLabel, welcomePageScrollPane, welcomePageNextButtonPanel, this, + welcomeImageLabel + ) + } + + companion object { + private fun createWelcomePageLayout( + stepLabel: JLabel, titleLabel: JLabel, welcomePageScrollPane: JScrollPane, + welcomePageNextButtonPanel: JPanel, welcomePagePanel: JPanel, welcomePageImageLabel: JLabel, + ) { + val gbc = GridBagConstraints() + + val centerPanel = SonarLintWalkthroughUtils.createCenterPanel(stepLabel, titleLabel, welcomePageScrollPane, gbc) + + gbc.anchor = GridBagConstraints.SOUTHEAST + SonarLintWalkthroughUtils.provideCommonButtonConstraints(gbc) + centerPanel.add(welcomePageNextButtonPanel, gbc) + + welcomePagePanel.add(welcomePageImageLabel, BorderLayout.NORTH) + welcomePagePanel.add(centerPanel, BorderLayout.CENTER) + } + + private fun createWelcomePageText(font: Font, project: Project): JEditorPane { + val descriptionPane = SwingHelper.createHtmlViewer(false, font, null, null) + descriptionPane.text = SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE + " supports the analysis of 15+ languages including" + + " Python, Java, Javascript, IaC" + + " domains along with secrets " + + "detection. " + + "Learn more.

" + + "Detect issues while you code in an open files or run the analysis on more file in the report view" + + ".

" + + "Open a file and start your clean code journey." + descriptionPane.border = JBUI.Borders.empty(8, 8) + + descriptionPane.addHyperlinkListener(object : HyperlinkAdapter() { + override fun hyperlinkActivated(e: HyperlinkEvent) { + if ("#reportView" == e.description) { + val sonarqubeToolWindow = + ToolWindowManager.getInstance(project).getToolWindow(SonarLintWalkthroughToolWindow.SONARQUBE_FOR_IDE) + if (sonarqubeToolWindow != null) { + if (!sonarqubeToolWindow.isVisible) { + sonarqubeToolWindow.activate(null) + } + val reportContent = sonarqubeToolWindow.contentManager.findContent(REPORT) + if (reportContent != null) { + sonarqubeToolWindow.contentManager.setSelectedContent(reportContent) + } + } + } else { + LinkTelemetry.RULE_SELECTION_PAGE.browseWithTelemetry() + } + } + }) + return descriptionPane + } + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index c054345e6..5ec6d3c6a 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -204,7 +204,7 @@ - +