terminal: initial "Got it" popup shown near highlighted command, fix warnings (IDEA-259451)

GitOrigin-RevId: 4d7d1a8398363e6e25b9bcefdc2832ffd9c79292
This commit is contained in:
Sergey Simonchik
2021-03-23 01:07:56 +03:00
committed by intellij-monorepo-bot
parent af4bd9a9cd
commit dbdf4c717e
3 changed files with 84 additions and 41 deletions

View File

@@ -41,7 +41,7 @@ settings.terminal.application.settings=Application Settings
settings.terminal.shell.executable.path.browseFolder.description=Shell executable path
smart_command_execution.notification.title=Run Commands using IDE
smart_command_execution.notification.text=Press {0} to run the highlighted action using the relevant IDE feature instead of the terminal. Press {1} for debug. Press Enter to run the command in the terminal as usual. You can turn this behavior on/off in {2} | Tools | Terminal. <a href="{3}"/>Got it!</a>
smart_command_execution.notification.text=Press {0} to run the highlighted command<br/>using the corresponding IDE feature.<p/>Press {1} to debug a process.<p><a href='Configure'>Configure...</a>
settings.terminal.smart.command.handling=Run Commands using IDE
toolwindow.stripe.Terminal=Terminal

View File

@@ -69,7 +69,7 @@ public class ShellTerminalWidget extends JBTerminalWidget {
}
}
else {
myShellCommandHandlerHelper.processKeyPressed();
myShellCommandHandlerHelper.processKeyPressed(e);
}
});
}

View File

@@ -6,23 +6,30 @@ import com.intellij.execution.Executor;
import com.intellij.execution.ExecutorRegistry;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.notification.*;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.KeyboardShortcut;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Experiments;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.terminal.TerminalShellCommandHandler;
import com.intellij.ui.GotItMessage;
import com.intellij.ui.HyperlinkAdapter;
import com.intellij.ui.JBColor;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.Alarm;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.PositionTracker;
import com.jediterm.terminal.TerminalColor;
import com.jediterm.terminal.TextStyle;
import com.jediterm.terminal.TtyConnector;
@@ -38,6 +45,7 @@ import org.jetbrains.plugins.terminal.shellCommandRunner.TerminalRunSmartCommand
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.util.Arrays;
@@ -47,21 +55,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
public final class TerminalShellCommandHandlerHelper {
private static final Logger LOG = Logger.getInstance(TerminalShellCommandHandler.class);
@NonNls private static final String TERMINAL_CUSTOM_COMMANDS_GOT_IT = "TERMINAL_CUSTOM_COMMANDS_GOT_IT";
@NonNls private static final String GOT_IT = "got_it";
@NonNls private static final String FEATURE_ID = "terminal.shell.command.handling";
private static Experiments ourExperiments;
private static final NotificationGroup ourToolWindowGroup =
NotificationGroup.toolWindowGroup("Terminal", TerminalToolWindowFactory.TOOL_WINDOW_ID);
private final ShellTerminalWidget myWidget;
private final Alarm myAlarm;
private volatile String myWorkingDirectory;
private volatile Boolean myHasRunningCommands;
private PropertiesComponent myPropertiesComponent;
private final SingletonNotificationManager mySingletonNotificationManager =
new SingletonNotificationManager(ourToolWindowGroup, NotificationType.INFORMATION, null);
private final AtomicBoolean myKeyPressed = new AtomicBoolean(false);
private TerminalLineIntervalHighlighting myCurrentHighlighting;
private TerminalLineIntervalHighlighting myCommandHighlighting;
private Disposable myNotificationDisposable;
TerminalShellCommandHandlerHelper(@NotNull ShellTerminalWidget widget) {
myWidget = widget;
@@ -79,10 +83,12 @@ public final class TerminalShellCommandHandlerHelper {
Disposer.register(myWidget, () -> widget.getTerminalTextBuffer().removeModelListener(listener));
}
public void processKeyPressed() {
public void processKeyPressed(KeyEvent e) {
if (isFeatureEnabled()) {
myKeyPressed.set(true);
scheduleCommandHighlighting();
if (e.getKeyCode() == KeyEvent.VK_ESCAPE && e.getModifiersEx() == 0 && hideNotification()) {
e.consume();
}
}
}
@@ -104,48 +110,81 @@ public final class TerminalShellCommandHandlerHelper {
private void highlightMatchedCommand(@NotNull Project project) {
if (!isEnabledForProject()) {
setHighlighting(null);
setCommandHighlighting(null);
return;
}
String command = myWidget.getTypedShellCommand().trim();
TerminalLineIntervalHighlighting commandHighlighting = null;
if (TerminalShellCommandHandler.Companion.matches(project, getWorkingDirectory(), !hasRunningCommands(), command)) {
commandHighlighting = doHighlight(command);
}
setHighlighting(commandHighlighting);
TerminalLineIntervalHighlighting commandHighlighting = highlightCommand(project, command);
setCommandHighlighting(commandHighlighting);
//show notification
if (getPropertiesComponent().getBoolean(TERMINAL_CUSTOM_COMMANDS_GOT_IT, false)) {
return;
}
if (commandHighlighting != null) {
String title = TerminalBundle.message("smart_command_execution.notification.title");
String content = TerminalBundle.message("smart_command_execution.notification.text",
KeymapUtil.getFirstKeyboardShortcutText(getRunAction()),
KeymapUtil.getFirstKeyboardShortcutText(getDebugAction()),
ShowSettingsUtil.getSettingsMenuName(),
GOT_IT);
NotificationListener.Adapter listener = new NotificationListener.Adapter() {
@Override
protected void hyperlinkActivated(@NotNull Notification notification, @NotNull HyperlinkEvent e) {
if (GOT_IT.equals(e.getDescription())) {
getPropertiesComponent().setValue(TERMINAL_CUSTOM_COMMANDS_GOT_IT, true, false);
}
}
};
mySingletonNotificationManager.notify(title, content, project, listener);
}
ApplicationManager.getApplication().invokeLater(() -> {
showOrHideNotification(commandHighlighting);
}, ModalityState.stateForComponent(myWidget.getTerminalPanel()));
}
private synchronized void setHighlighting(@Nullable TerminalLineIntervalHighlighting highlighting) {
TerminalLineIntervalHighlighting oldHighlighting = myCurrentHighlighting;
private synchronized void setCommandHighlighting(@Nullable TerminalLineIntervalHighlighting commandHighlighting) {
TerminalLineIntervalHighlighting oldHighlighting = myCommandHighlighting;
if (oldHighlighting != null) {
oldHighlighting.dispose();
myWidget.getTerminalPanel().repaint();
}
myCurrentHighlighting = highlighting;
myCommandHighlighting = commandHighlighting;
}
private boolean hideNotification() {
boolean shown = myNotificationDisposable != null && !Disposer.isDisposed(myNotificationDisposable);
if (shown) {
Disposer.dispose(myNotificationDisposable);
}
myNotificationDisposable = null;
return shown;
}
private void showOrHideNotification(@Nullable TerminalLineIntervalHighlighting commandHighlighting) {
if (commandHighlighting == null) {
hideNotification();
return;
}
if (myNotificationDisposable != null && !Disposer.isDisposed(myNotificationDisposable)) {
return;
}
String title = TerminalBundle.message("smart_command_execution.notification.title");
String content = TerminalBundle.message("smart_command_execution.notification.text",
KeymapUtil.getFirstKeyboardShortcutText(getRunAction()),
KeymapUtil.getFirstKeyboardShortcutText(getDebugAction()));
GotItMessage message = GotItMessage.createMessage(title, content);
myNotificationDisposable = Disposer.newDisposable();
Disposer.register(myWidget.getTerminalPanel(), myNotificationDisposable);
message.setDisposable(myNotificationDisposable);
message.setShowCallout(true);
message.setHyperlinkListener(new HyperlinkAdapter() {
@Override
protected void hyperlinkActivated(HyperlinkEvent e) {
ShowSettingsUtil.getInstance().showSettingsDialog(myWidget.getProject(), TerminalOptionsConfigurable.class);
}
});
message.setCallback(() -> {
hideNotification();
getPropertiesComponent().setValue(TERMINAL_CUSTOM_COMMANDS_GOT_IT, true);
});
JComponent component = myWidget.getTerminalPanel();
message.show(new PositionTracker<>(component) {
@Override
public RelativePoint recalculateLocation(@NotNull Balloon balloon) {
RelativePoint point = RelativePoint.getNorthWestOf(component);
Rectangle bounds = myWidget.getTerminalPanel().getBounds(commandHighlighting);
if (bounds != null) {
point.getPoint().translate(bounds.x + bounds.width / 2, bounds.y + bounds.height);
}
return point;
}
}, Balloon.Position.below);
}
private boolean isEnabledForProject() {
@@ -181,8 +220,11 @@ public final class TerminalShellCommandHandlerHelper {
return hasRunningCommands;
}
private @Nullable TerminalLineIntervalHighlighting doHighlight(@NotNull String command) {
if (command.length() == 0) {
private @Nullable TerminalLineIntervalHighlighting highlightCommand(@NotNull Project project, @NotNull String command) {
if (command.isEmpty()) {
return null;
}
if (!TerminalShellCommandHandler.Companion.matches(project, getWorkingDirectory(), !hasRunningCommands(), command)) {
return null;
}
return myWidget.processTerminalBuffer(textBuffer -> {
@@ -209,6 +251,7 @@ public final class TerminalShellCommandHandlerHelper {
onShellCommandExecuted();
return false;
}
myKeyPressed.set(true);
String command = myWidget.getTypedShellCommand().trim();
if (LOG.isDebugEnabled()) {
LOG.debug("typed shell command to execute: " + command);
@@ -274,7 +317,7 @@ public final class TerminalShellCommandHandlerHelper {
final KeyboardShortcut eventShortcut = new KeyboardShortcut(KeyStroke.getKeyStrokeForEvent(e), null);
AnAction action = getRunAction();
return action instanceof TerminalRunSmartCommandAction
&& Arrays.stream(action.getShortcutSet().getShortcuts()).anyMatch(sc -> sc.isKeyboard() && sc.startsWith(eventShortcut))
&& ContainerUtil.exists(action.getShortcutSet().getShortcuts(), sc -> sc.isKeyboard() && sc.startsWith(eventShortcut))
? ((TerminalRunSmartCommandAction)action)
: null;
}
@@ -283,7 +326,7 @@ public final class TerminalShellCommandHandlerHelper {
final KeyboardShortcut eventShortcut = new KeyboardShortcut(KeyStroke.getKeyStrokeForEvent(e), null);
AnAction action = getDebugAction();
return action instanceof TerminalDebugSmartCommandAction
&& Arrays.stream(action.getShortcutSet().getShortcuts()).anyMatch(sc -> sc.isKeyboard() && sc.startsWith(eventShortcut))
&& ContainerUtil.exists(action.getShortcutSet().getShortcuts(), sc -> sc.isKeyboard() && sc.startsWith(eventShortcut))
? ((TerminalDebugSmartCommandAction)action)
: null;
}