Skip to content

Commit

Permalink
terminal: provide a quick way to open installed shells or WSLs (IDEA-…
Browse files Browse the repository at this point in the history
…268890)

GitOrigin-RevId: 92d6408d22b36cabb14dc2f26db63b9cc9477f31
  • Loading branch information
segrey authored and intellij-monorepo-bot committed Jun 14, 2021
1 parent d64f96b commit f073431
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ action.Terminal.SmartCommandExecution.Run.description=Launches the highlighted a
action.Terminal.SmartCommandExecution.Debug.text=Debug highlighted command using IDE
action.Terminal.SmartCommandExecution.Debug.description=Performs the highlighted action using the relevant IDE feature under debugger
action.RenameSession.newSessionName.label=Enter new session name:
action.NewPredefinedSession.label=New Predefined Session

action.Terminal.ClearBuffer.text=Clear Terminal

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.terminal;

import com.intellij.execution.configurations.PathEnvironmentVariableUtil;
import com.intellij.execution.wsl.WSLDistribution;
import com.intellij.execution.wsl.WslDistributionManager;
import com.intellij.icons.AllIcons;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.NlsActions;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.popup.PopupState;
import com.intellij.util.EnvironmentUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.JBUI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

public class TerminalNewPredefinedSessionAction extends DumbAwareAction {

private final PopupState<JBPopup> myPopupState = PopupState.forPopup();

public TerminalNewPredefinedSessionAction() {
super(TerminalBundle.messagePointer("action.NewPredefinedSession.label"));
getTemplatePresentation().setIcon(AllIcons.Toolbar.Expand);
}

@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = e.getProject();
if (project == null || myPopupState.isRecentlyHidden()) return;
RelativePoint popupPoint = getPreferredPopupPoint(e);
ApplicationManager.getApplication().executeOnPooledThread(() -> {
List<OpenShellAction> shells = detectShells();
List<OpenShellAction> wsl = listOpenWslShellActions();
ApplicationManager.getApplication().invokeLater(() -> {
ListPopup popup = createPopup(shells, wsl, e.getDataContext());
if (popupPoint != null) {
popup.show(popupPoint);
}
else {
popup.showInFocusCenter();
}
myPopupState.prepareToShow(popup);
});
});
}

private static @Nullable RelativePoint getPreferredPopupPoint(@NotNull AnActionEvent e) {
InputEvent inputEvent = e.getInputEvent();
if (inputEvent instanceof MouseEvent) {
Component comp = inputEvent.getComponent();
if (comp instanceof AnActionHolder) {
return new RelativePoint(comp.getParent(), new Point(comp.getX() + JBUI.scale(3), comp.getY() + comp.getHeight() + JBUI.scale(3)));
}
}
return null;
}

private static @NotNull ListPopup createPopup(@NotNull List<OpenShellAction> shells,
@NotNull List<OpenShellAction> wsl,
@NotNull DataContext dataContext) {
DefaultActionGroup group = new DefaultActionGroup();
group.addAll(shells);
group.addAll(wsl);
if (!wsl.isEmpty()) {
group.addSeparator();
}
group.add(new TerminalSettingsAction());
return JBPopupFactory.getInstance().createActionGroupPopup(null, group, dataContext,
false, true, true, null, -1, null);
}

private static @NotNull List<OpenShellAction> listOpenWslShellActions() {
if (WSLDistribution.findWslExe() == null) return List.of();
List<WSLDistribution> distributions = WslDistributionManager.getInstance().getInstalledDistributions();
return ContainerUtil.map(distributions, (d) -> {
return new OpenShellAction(() -> d.getMsId(), List.of("wsl.exe", "-d", d.getMsId()), AllIcons.RunConfigurations.Wsl);
});
}

private static @NotNull List<OpenShellAction> detectShells() {
List<OpenShellAction> actions = new ArrayList<>();
if (SystemInfo.isUnix) {
ContainerUtil.addIfNotNull(actions, create("/bin/bash", List.of(), "Bash"));
if (Files.exists(Path.of("/usr/local/bin/zsh"))) {
ContainerUtil.addIfNotNull(actions, create("/usr/local/bin/zsh", List.of(), "Zsh"));
}
else {
ContainerUtil.addIfNotNull(actions, create("/usr/bin/zsh", List.of(), "Zsh"));
}
ContainerUtil.addIfNotNull(actions, create("/usr/bin/fish", List.of(), "Fish"));
}
else if (SystemInfo.isWindows) {
File powershell = PathEnvironmentVariableUtil.findInPath("powershell.exe");
if (powershell != null && StringUtil.startsWithIgnoreCase(powershell.getAbsolutePath(), "C:\\Windows\\System32\\WindowsPowerShell\\")) {
ContainerUtil.addIfNotNull(actions, create(powershell.getAbsolutePath(), List.of(), "Windows PowerShell"));
}
File cmd = PathEnvironmentVariableUtil.findInPath("cmd.exe");
if (cmd != null && StringUtil.startsWithIgnoreCase(cmd.getAbsolutePath(), "C:\\Windows\\System32\\")) {
ContainerUtil.addIfNotNull(actions, create(cmd.getAbsolutePath(), List.of(), "Command Prompt"));
}
File pwsh = PathEnvironmentVariableUtil.findInPath("pwsh.exe");
if (pwsh != null && StringUtil.startsWithIgnoreCase(pwsh.getAbsolutePath(), "C:\\Program Files\\PowerShell\\")) {
ContainerUtil.addIfNotNull(actions, create(pwsh.getAbsolutePath(), List.of(), "PowerShell"));
}
File gitBash = new File("C:\\Program Files\\Git\\bin\\bash.exe");
if (gitBash.isFile()) {
ContainerUtil.addIfNotNull(actions, create(gitBash.getAbsolutePath(), List.of(), "Git Bash"));
}
String cmderRoot = EnvironmentUtil.getValue("CMDER_ROOT");
if (cmderRoot != null && cmd != null && StringUtil.startsWithIgnoreCase(cmd.getAbsolutePath(), "C:\\Windows\\System32\\")) {
File cmderInitBat = new File(cmderRoot, "vendor\\init.bat");
if (cmderInitBat.isFile()) {
ContainerUtil.addIfNotNull(actions, create(cmd.getAbsolutePath(), List.of("/k", cmderInitBat.getAbsolutePath()), "Cmder"));
}
}
}
return actions;
}

private static @Nullable OpenShellAction create(@NotNull String shellPath, @NotNull List<String> shellOptions, @NlsSafe String presentableName) {
if (Files.exists(Path.of(shellPath))) {
return new OpenShellAction(() -> presentableName, ContainerUtil.concat(List.of(shellPath), shellOptions), null);
}
return null;
}

private static class TerminalSettingsAction extends DumbAwareAction {

private TerminalSettingsAction() {
super(IdeBundle.message("action.text.settings"), null, AllIcons.General.Settings);
}

@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = e.getProject();
if (project != null) {
ShowSettingsUtil.getInstance().showSettingsDialog(project, TerminalOptionsConfigurable.class);
}
}
}

private static class OpenShellAction extends DumbAwareAction {

private final List<String> myCommand;
private final Supplier<@NlsActions.ActionText String> myPresentableName;

private OpenShellAction(@NotNull Supplier<@NlsActions.ActionText String> presentableName, @NotNull List<String> command, @Nullable Icon icon) {
super(presentableName, icon);
myPresentableName = presentableName;
myCommand = command;
}

@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = e.getProject();
if (project != null) {
LocalTerminalDirectRunner runner = new LocalTerminalDirectRunner(project) {
@Override
public @NotNull List<String> getInitialCommand(@NotNull Map<String, String> envs) {
return myCommand;
}
};
TerminalTabState tabState = new TerminalTabState();
tabState.myTabName = myPresentableName.get();
TerminalView.getInstance(project).createNewSession(runner, tabState);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ void initToolWindow(@NotNull ToolWindowEx toolWindow) {
public void actionPerformed(@NotNull AnActionEvent e) {
newTab(toolWindow, null);
}
});
},
new TerminalNewPredefinedSessionAction()
);
toolWindow.setTabDoubleClickActions(Collections.singletonList(new RenameTerminalSessionAction()));
toolWindow.setToHideOnEmptyContent(true);

Expand Down

0 comments on commit f073431

Please sign in to comment.