- добавлена работа с терминалом по ssh
This commit is contained in:
parent
792ea03808
commit
d54e9dd058
@ -14,6 +14,7 @@ dependencies {
|
||||
// IntelliJ Platform Gradle Plugin Dependencies Extension - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html
|
||||
intellijPlatform {
|
||||
intellijIdeaCommunity("2024.1.7")
|
||||
bundledPlugin("org.jetbrains.plugins.terminal")
|
||||
testFramework(TestFrameworkType.Platform)
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
package com.example.plugin;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.wm.ToolWindow;
|
||||
import com.intellij.openapi.wm.ToolWindowManager;
|
||||
import com.intellij.terminal.JBTerminalWidget;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.intellij.ui.content.ContentFactory;
|
||||
import com.jcraft.jsch.ChannelShell;
|
||||
import com.jediterm.terminal.TtyConnector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.terminal.JBTerminalSystemSettingsProvider;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class OpenRemoteTerminalAction extends AnAction {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = e.getProject();
|
||||
if (project == null) return;
|
||||
|
||||
String serverId = ProjectServerMapping.getInstance()
|
||||
.getServerIdForProject(project.getBasePath());
|
||||
|
||||
if (serverId == null) {
|
||||
Messages.showErrorDialog("No server associated with this project.", "Error");
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<SshServer> maybeServer =
|
||||
SshServerManager.getInstance().findServer(serverId);
|
||||
|
||||
if (maybeServer.isEmpty()) {
|
||||
Messages.showErrorDialog("Server not found.", "Error");
|
||||
return;
|
||||
}
|
||||
|
||||
SshServer server = maybeServer.get();
|
||||
|
||||
try {
|
||||
ChannelShell shellChannel =
|
||||
SftpSessionManager.getInstance(project)
|
||||
.getShellChannel(server);
|
||||
|
||||
TtyConnector connector = new SshTtyConnector(shellChannel);
|
||||
|
||||
ToolWindow terminalToolWindow =
|
||||
ToolWindowManager.getInstance(project).getToolWindow("Terminal");
|
||||
|
||||
if (terminalToolWindow == null) {
|
||||
Messages.showErrorDialog("Terminal tool window not found.", "Error");
|
||||
return;
|
||||
}
|
||||
|
||||
Disposable disposable =
|
||||
Disposer.newDisposable(project, "Remote SSH Terminal");
|
||||
|
||||
JBTerminalWidget widget =
|
||||
new JBTerminalWidget(
|
||||
project,
|
||||
new JBTerminalSystemSettingsProvider(),
|
||||
disposable
|
||||
);
|
||||
|
||||
Content content =
|
||||
ContentFactory.getInstance()
|
||||
.createContent(widget, "Remote: " + server.name, false);
|
||||
|
||||
content.setDisposer(disposable);
|
||||
terminalToolWindow.getContentManager().addContent(content);
|
||||
terminalToolWindow.getContentManager().setSelectedContent(content, true);
|
||||
|
||||
terminalToolWindow.activate(() -> widget.start(connector), true);
|
||||
|
||||
} catch (Exception ex) {
|
||||
Messages.showErrorDialog(
|
||||
"Failed to open terminal: " + ex.getMessage(),
|
||||
"Error"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,11 +4,11 @@ import com.intellij.openapi.project.Project;
|
||||
import com.jcraft.jsch.*;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class SftpSessionManager {
|
||||
private final ConcurrentHashMap<String, ChannelSftp> channels = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, ChannelShell> shellChannels = new ConcurrentHashMap<>();
|
||||
private final Object lock = new Object();
|
||||
|
||||
public ChannelSftp getChannel(SshServer server) throws Exception {
|
||||
@ -20,32 +20,59 @@ public class SftpSessionManager {
|
||||
channel = channels.get(server.id);
|
||||
if (channel != null && channel.isConnected() && !channel.isClosed()) return channel;
|
||||
|
||||
JSch jsch = new JSch();
|
||||
Session session = jsch.getSession(server.user, server.host, server.port);
|
||||
session.setPassword(SshServerManager.getInstance().getPassword(server.id));
|
||||
java.util.Properties config = new java.util.Properties();
|
||||
config.put("StrictHostKeyChecking", "no");
|
||||
session.setConfig(config);
|
||||
session.connect(5000);
|
||||
|
||||
Session session = getSession(server);
|
||||
ChannelSftp newChannel = (ChannelSftp) session.openChannel("sftp");
|
||||
newChannel.connect(3000);
|
||||
|
||||
sessions.put(server.id, session);
|
||||
channels.put(server.id, newChannel);
|
||||
return newChannel;
|
||||
}
|
||||
}
|
||||
|
||||
public ChannelShell getShellChannel(SshServer server) throws Exception {
|
||||
ChannelShell shell = shellChannels.get(server.id);
|
||||
if (shell != null && shell.isConnected() && !shell.isClosed()) {
|
||||
return shell;
|
||||
}
|
||||
synchronized (lock) {
|
||||
shell = shellChannels.get(server.id);
|
||||
if (shell != null && shell.isConnected() && !shell.isClosed()) return shell;
|
||||
|
||||
Session session = getSession(server);
|
||||
ChannelShell newShell = (ChannelShell) session.openChannel("shell");
|
||||
newShell.connect(5000);
|
||||
shellChannels.put(server.id, newShell);
|
||||
return newShell;
|
||||
}
|
||||
}
|
||||
|
||||
private Session getSession(SshServer server) throws JSchException {
|
||||
Session session = sessions.get(server.id);
|
||||
if (session == null || !session.isConnected()) {
|
||||
JSch jsch = new JSch();
|
||||
session = jsch.getSession(server.user, server.host, server.port);
|
||||
session.setPassword(SshServerManager.getInstance().getPassword(server.id));
|
||||
java.util.Properties config = new java.util.Properties();
|
||||
config.put("StrictHostKeyChecking", "no");
|
||||
session.setConfig(config);
|
||||
session.connect(5000);
|
||||
sessions.put(server.id, session);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
synchronized (lock) {
|
||||
for (ChannelShell sh : shellChannels.values()) {
|
||||
if (sh.isConnected()) sh.disconnect();
|
||||
}
|
||||
shellChannels.clear();
|
||||
for (ChannelSftp ch : channels.values()) {
|
||||
if (ch.isConnected()) ch.disconnect();
|
||||
}
|
||||
channels.clear();
|
||||
for (Session s : sessions.values()) {
|
||||
if (s.isConnected()) s.disconnect();
|
||||
}
|
||||
channels.clear();
|
||||
sessions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
79
src/main/java/com/example/plugin/SshTtyConnector.java
Normal file
79
src/main/java/com/example/plugin/SshTtyConnector.java
Normal file
@ -0,0 +1,79 @@
|
||||
package com.example.plugin;
|
||||
|
||||
import com.jcraft.jsch.ChannelShell;
|
||||
import com.jediterm.terminal.TtyConnector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class SshTtyConnector implements TtyConnector {
|
||||
private final ChannelShell channel;
|
||||
private final InputStream inputStream;
|
||||
private final OutputStream outputStream;
|
||||
private final Reader reader;
|
||||
|
||||
public SshTtyConnector(ChannelShell channel) {
|
||||
this.channel = channel;
|
||||
try {
|
||||
this.inputStream = channel.getInputStream();
|
||||
this.outputStream = channel.getOutputStream();
|
||||
this.reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return channel.isConnected() && !channel.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (channel.isConnected()) {
|
||||
channel.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getName() {
|
||||
return "SSH Remote";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(char[] buf, int offset, int length) throws IOException {
|
||||
return reader.read(buf, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String string) throws IOException {
|
||||
write(string.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] bytes) throws IOException {
|
||||
outputStream.write(bytes);
|
||||
outputStream.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(@NotNull Dimension size) {
|
||||
channel.setPtySize(size.width, size.height, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int waitFor() throws InterruptedException {
|
||||
while (isConnected()) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
return channel.getExitStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ready() throws IOException {
|
||||
return reader.ready();
|
||||
}
|
||||
}
|
||||
@ -28,15 +28,14 @@
|
||||
]]>
|
||||
</description>
|
||||
<depends>com.intellij.modules.platform</depends>
|
||||
<depends>org.jetbrains.plugins.terminal</depends>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<projectConfigurable instance="com.example.plugin.SshServerConfigurable"
|
||||
displayName="SSH Remote Project"/>
|
||||
|
||||
<applicationService serviceImplementation="com.example.plugin.SshServerManager"/>
|
||||
<applicationService serviceImplementation="com.example.plugin.ProjectServerMapping"/>
|
||||
<projectService serviceImplementation="com.example.plugin.SftpSessionManager"/>
|
||||
<applicationService serviceImplementation="com.example.plugin.ProjectMappingService"/>
|
||||
<postStartupActivity implementation="com.example.plugin.SshSyncStartupActivity"/>
|
||||
</extensions>
|
||||
|
||||
@ -61,6 +60,10 @@
|
||||
class="com.example.plugin.RemoteCommandAction"
|
||||
text="Run Remote Command"
|
||||
description="Execute command on the associated server"/>
|
||||
<action id="SshRemote.OpenTerminal"
|
||||
class="com.example.plugin.OpenRemoteTerminalAction"
|
||||
text="Open Remote Terminal"
|
||||
description="Open integrated terminal connected to the remote server"/>
|
||||
<add-to-group group-id="ToolsMenu" anchor="last"/>
|
||||
</group>
|
||||
</actions>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user