diff --git a/build.gradle.kts b/build.gradle.kts index c6b5a1d8..a2191761 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -57,10 +57,13 @@ dependencies { implementation("com.theokanning.openai-gpt3-java:service:0.14.0") implementation("com.vladsch.flexmark:flexmark:0.64.8") implementation("com.vladsch.flexmark:flexmark-ext-tables:0.64.8") + implementation("com.vladsch.flexmark:flexmark-html2md-converter:0.64.8") + implementation("com.googlecode.lanterna:lanterna:3.1.1") + implementation("com.vladsch.flexmark:flexmark-html2md-converter:0.64.8") testImplementation("org.testcontainers:couchbase:1.19.7") - testImplementation("org.testcontainers:junit-jupiter:1.19.7") + } @@ -71,7 +74,7 @@ intellij { version.set("2024.1") type.set("IC") // Target IDE Platform - plugins.set(listOf("com.intellij.java")) + plugins.set(listOf("com.intellij.java", "org.jetbrains.plugins.terminal")) } tasks { diff --git a/src/main/java/com/couchbase/intellij/listener/FileConfigInitializer.java b/src/main/java/com/couchbase/intellij/listener/FileConfigInitializer.java index 1c38df30..80c8c70b 100644 --- a/src/main/java/com/couchbase/intellij/listener/FileConfigInitializer.java +++ b/src/main/java/com/couchbase/intellij/listener/FileConfigInitializer.java @@ -22,35 +22,17 @@ public static void start(String toolsPath) throws Exception { String configPath = toolsPath + File.separator + "config"; createFolder(configPath); - //initCBShell(configPath); initExplain(toolsPath, configPath); initJSDependencies(toolsPath, configPath); } -//NOTE: LEAVE THIS CODE COMMENTED FOR NOW -// public static void initCBShell(String configPath) throws Exception { -// String path = configPath + File.separator + "cbshell"; -// Path dest = Paths.get(path); -// -// if (!Files.exists(dest)) { -// Log.debug("Copying CBShell autopass.sh script"); -// createFolder(path); -// copyFile("/tools/cbshell.zip", Paths.get(configPath + File.separator + "cbshell.zip")); -// unzipFile(configPath + File.separator + "cbshell.zip", configPath); -// makeFilesExecutable(new File(path)); -// } else { -// Log.debug("The script for cbshell is already copied to the config"); -// } -// -// CBFolders.getInstance().setCbShellPath(path); -// } - public static void initExplain(String toolsPath, String configPath) throws Exception { String path = configPath + File.separator + "explain"; if (!EXPLAIN_VERSION.equals(getPropertyValue(toolsPath, EXPLAIN_KEY))) { - Log.info("A new version of Couchbase Explain is available. Removing local version and downloading the new one"); + Log.info( + "A new version of Couchbase Explain is available. Removing local version and downloading the new one"); DependenciesUtil.deleteFolder(path); } @@ -84,7 +66,8 @@ public static void initJSDependencies(String toolsPath, String configPath) throw String path = configPath + File.separator + "js_dependencies"; if (!JS_DEPENDENCIES_VERSION.equals(getPropertyValue(toolsPath, JS_DEPENDENCIES_KEY))) { - Log.info("A new version of Couchbase JS Dependencies is available. Removing local version and downloading the new one"); + Log.info( + "A new version of Couchbase JS Dependencies is available. Removing local version and downloading the new one"); DependenciesUtil.deleteFolder(path); } diff --git a/src/main/java/com/couchbase/intellij/workbench/CbShellEmulator.java b/src/main/java/com/couchbase/intellij/workbench/CbShellEmulator.java new file mode 100644 index 00000000..0c67aee0 --- /dev/null +++ b/src/main/java/com/couchbase/intellij/workbench/CbShellEmulator.java @@ -0,0 +1,302 @@ +package com.couchbase.intellij.workbench; + + +import com.couchbase.intellij.tools.CBTools; +import com.googlecode.lanterna.TerminalPosition; +import com.googlecode.lanterna.TerminalSize; +import com.googlecode.lanterna.graphics.TextGraphics; +import com.googlecode.lanterna.input.KeyStroke; +import com.googlecode.lanterna.screen.Screen; +import com.googlecode.lanterna.screen.TerminalScreen; +import com.googlecode.lanterna.terminal.swing.SwingTerminal; + +import javax.swing.*; +import java.awt.*; +import java.io.*; + +public class CbShellEmulator extends JPanel { + private Screen screen; + private Process cliProcess; + private BufferedReader cliOutputReader; + private PrintWriter cliInputWriter; + + public CbShellEmulator() { + setLayout(new BorderLayout()); + + try { + // Initialize the SwingTerminal and set a preferred size + SwingTerminal terminal = new SwingTerminal(); + terminal.setPreferredSize(new Dimension(800, 600)); // Set a preferred size for the terminal + screen = new TerminalScreen(terminal); + + // Add the terminal directly to the JPanel + add(terminal, BorderLayout.CENTER); + + // Revalidate and repaint to ensure the component is laid out properly + revalidate(); + repaint(); + + + // Delay screen start to ensure layout is done + SwingUtilities.invokeLater(() -> { + try { + screen.startScreen(); + screen.setCursorPosition(null); // Hide the cursor + + // Start a thread to handle terminal input and output + new Thread(this::handleTerminal).start(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void startCLIProcess() { + try { + // Start the CLI process + String cliPath = CBTools.getTool(CBTools.Type.SHELL).getPath(); + ProcessBuilder builder = new ProcessBuilder(cliPath); + cliProcess = builder.start(); + + // Set up streams to capture output and send input + cliOutputReader = new BufferedReader(new InputStreamReader(cliProcess.getInputStream())); + cliInputWriter = new PrintWriter(new OutputStreamWriter(cliProcess.getOutputStream()), true); + + // Start a thread to read the CLI output and display it in the Lanterna terminal + new Thread(this::readCLIOutput).start(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void readCLIOutput() { + try { + String line; + while ((line = cliOutputReader.readLine()) != null) { + appendToTerminal(line); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void appendToTerminal(String line) { + try { + TextGraphics textGraphics = screen.newTextGraphics(); + TerminalPosition currentPosition = screen.getCursorPosition(); + if (currentPosition == null) { + currentPosition = new TerminalPosition(0, 0); + } + + // Move the cursor to the next line if needed + TerminalSize screenSize = screen.getTerminalSize(); + if (currentPosition.getRow() >= screenSize.getRows() - 1) { + screen.scrollLines(0, screenSize.getRows(), 1); // Scroll terminal upwards + currentPosition = new TerminalPosition(0, screenSize.getRows() - 1); + } + + textGraphics.putString(currentPosition, line); + screen.refresh(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void handleTerminal() { + try { + while (true) { + // Read input from the user using Lanterna's KeyStroke + KeyStroke keyStroke = screen.readInput(); + if (keyStroke != null && keyStroke.getCharacter() != null) { + char typedChar = keyStroke.getCharacter(); + + // Send the input to the CLI process + cliInputWriter.print(typedChar); + cliInputWriter.flush(); + + // Display the typed character on the terminal + appendToTerminal(String.valueOf(typedChar)); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void dispose() { + try { + if (cliProcess != null) { + cliProcess.destroy(); + } + if (screen != null) { + screen.stopScreen(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} + + +//import com.couchbase.intellij.tools.CBTools; +//import com.intellij.openapi.project.Project; +//import com.intellij.openapi.util.Disposer; +//import com.intellij.terminal.JBTerminalSystemSettingsProviderBase; +//import com.intellij.terminal.JBTerminalWidget; +//import com.jediterm.core.util.TermSize; +//import com.jediterm.terminal.ProcessTtyConnector; +//import com.jediterm.terminal.TtyConnector; +// +//import javax.swing.*; +//import java.awt.*; +//import java.io.BufferedReader; +//import java.io.IOException; +//import java.io.InputStream; +//import java.io.InputStreamReader; +//import java.nio.charset.StandardCharsets; +// +//public class CbShellEmulator { +// +// private final JBTerminalWidget terminalWidget; +// private Process process; +// private StringBuilder inputBuffer = new StringBuilder(); +// +// +// public CbShellEmulator(Project project, JPanel parentPanel) { +// // Create terminal widget with system settings +// JBTerminalSystemSettingsProviderBase settingsProvider = new JBTerminalSystemSettingsProviderBase(); +// +// // Initialize JBTerminalWidget, passing the required parameters +// terminalWidget = new JBTerminalWidget(project, settingsProvider, Disposer.newDisposable()); +// +// // Add the terminal widget to your parent panel +// parentPanel.add(terminalWidget.getComponent(), BorderLayout.CENTER); +// terminalWidget.getComponent().requestFocusInWindow(); +// +// // Start your CLI process and connect to the terminal widget +// startCliProcess(); +// } +// +// public void requestFocus() { +// System.out.println("---------chamou"); +// SwingUtilities.invokeLater(() -> terminalWidget.getComponent().requestFocusInWindow()); +// } +// +// private void startCliProcess() { +// try { +// ProcessBuilder builder = new ProcessBuilder(CBTools.getTool(CBTools.Type.SHELL).getPath()); +// process = builder.start(); +// +// new Thread(() -> readStream(process.getInputStream())).start(); +// new Thread(() -> readStream(process.getErrorStream())).start(); +// +// TtyConnector ttyConnector = new ProcessTtyConnector(process, StandardCharsets.UTF_8) { +// @Override +// public String getName() { +// return "cbshell_terminal"; +// } +// +// @Override +// public void resize(TermSize termSize) { +// System.out.println( +// "Terminal resized to: " + termSize.getColumns() + " columns, " + termSize.getRows() + " rows"); +// } +// +// @Override +// public void write(byte[] bytes) throws IOException { +// String typed = new String(bytes, StandardCharsets.UTF_8); +// +// for (char ch : typed.toCharArray()) { +// switch (ch) { +// case '\b': // Backspace key +// handleBackspace(); +// break; +// case 127: // Delete key (ASCII 127) +// handleDelete(); +// break; +// case '\n': // Enter key +// case '\r': // Carriage return +// sendInputToProcess(); +// break; +// default: +// addCharacterToBuffer(ch); +// break; +// } +// } +// } +// +// private void handleBackspace() { +// if (inputBuffer.length() > 0) { +// // Remove the last character from the buffer +// inputBuffer.deleteCharAt(inputBuffer.length() - 1); +// // Simulate backspace effect: move cursor back, overwrite with space, then move back again +// SwingUtilities.invokeLater(() -> terminalWidget.writePlainMessage("\b \b")); +// } +// } +// +// private void handleDelete() { +// if (inputBuffer.length() > 0) { +// // Remove the last character from the input buffer +// inputBuffer.deleteCharAt(inputBuffer.length() - 1); +// +// SwingUtilities.invokeLater(() -> { +// // Calculate the length of the current line (including the deleted character) +// int lineLength = inputBuffer.length() + 1; // +1 for the deleted character +// +// // Create a string of spaces to overwrite the current line +// String clearLine = " ".repeat(lineLength); +// +// // Write the string of spaces to clear the current content +// terminalWidget.writePlainMessage(clearLine); +// +// // Now, rewrite the updated content of the buffer +// terminalWidget.writePlainMessage("\b" + inputBuffer.toString()); +// }); +// } +// } +// +// private void sendInputToProcess() throws IOException { +// // Send the buffered input to the process +// process.getOutputStream().write(inputBuffer.toString().getBytes(StandardCharsets.UTF_8)); +// process.getOutputStream().flush(); +// inputBuffer.setLength(0); // Clear the buffer after sending +// } +// +// private void addCharacterToBuffer(char ch) { +// // Add the character to the input buffer +// inputBuffer.append(ch); +// // Echo the character to the terminal +// SwingUtilities.invokeLater(() -> terminalWidget.writePlainMessage(Character.toString(ch))); +// } +// }; +// +// terminalWidget.start(ttyConnector); +// +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// +// +// public void dispose() { +// Disposer.dispose(terminalWidget); +// if (process != null) { +// process.destroy(); +// } +// } +// +// private void readStream(InputStream inputStream) { +// try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { +// String line; +// while ((line = reader.readLine()) != null) { +// System.out.println("Process output: " + line); // Log the output for debugging +// terminalWidget.writePlainMessage(line + "\n"); +// } +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +//} diff --git a/src/main/java/com/couchbase/intellij/workbench/QueryResultToolWindowFactory.java b/src/main/java/com/couchbase/intellij/workbench/QueryResultToolWindowFactory.java index 12bb2609..9f1d9fdc 100644 --- a/src/main/java/com/couchbase/intellij/workbench/QueryResultToolWindowFactory.java +++ b/src/main/java/com/couchbase/intellij/workbench/QueryResultToolWindowFactory.java @@ -86,6 +86,8 @@ public class QueryResultToolWindowFactory implements ToolWindowFactory { private List cachedResults; + private CbShellEmulator emulator; + public QueryResultToolWindowFactory() { instance = this; @@ -249,12 +251,24 @@ public void selectionChanged(TabInfo oldSelection, TabInfo newSelection) { TabInfo outputTab = new TabInfo(getConsolePanel()).setText("Log"); TabInfo queryResultTab = new TabInfo(queryResultPanel).setText("Query Result"); + TabInfo cbShell = new TabInfo(getCbShellPanel()).setText("CB Shell"); tabs.addTab(outputTab); tabs.addTab(queryResultTab); + tabs.addTab(cbShell); tabs.select(queryResultTab, true); + tabs.addListener(new TabsListener() { + @Override + public void selectionChanged(TabInfo oldSelection, TabInfo newSelection) { + if (newSelection == cbShell) { + emulator.startCLIProcess(); + + } + } + }); + ContentFactory contentFactory = ContentFactory.getInstance(); Content content = contentFactory.createContent(tabs.getComponent(), "", false); @@ -288,6 +302,20 @@ private void addSQLPPMenuItemListener(JMenuItem menuItem, String title, Supplier }); } + + public JPanel getCbShellPanel() { + + + JPanel consolePanel = new JPanel(new BorderLayout()); +// consolePanel.add(panel, BorderLayout.NORTH); +// consolePanel.add(console.getComponent(), BorderLayout.CENTER); + + emulator = new CbShellEmulator(); + consolePanel.add(emulator, BorderLayout.CENTER); + + return consolePanel; + } + public JPanel getConsolePanel() { ConsoleView console = Log.getView();