Skip to content

Commit

Permalink
Merge branch 'master' into remove-chat-control-buttons
Browse files Browse the repository at this point in the history
  • Loading branch information
yanir-codota committed Oct 19, 2023
2 parents 781cc0e + 4a649df commit 88efcbe
Show file tree
Hide file tree
Showing 30 changed files with 338 additions and 119 deletions.
2 changes: 1 addition & 1 deletion .chatversion
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.8.0
v2.13.0
27 changes: 20 additions & 7 deletions Common/src/main/java/com/tabnineCommon/binary/BinaryRun.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PermanentInstallationID;
import com.intellij.util.PlatformUtils;
import com.intellij.util.text.SemVer;
import com.tabnineCommon.binary.exceptions.NoValidBinaryToRunException;
import com.tabnineCommon.binary.exceptions.TabNineDeadException;
import com.tabnineCommon.binary.fetch.BinaryVersion;
import com.tabnineCommon.binary.fetch.BinaryVersionFetcher;
import com.tabnineCommon.config.Config;
import com.tabnineCommon.general.StaticConfig;
Expand All @@ -24,6 +26,9 @@
import org.jetbrains.annotations.Nullable;

public class BinaryRun {

static final SemVer TLS_CONFIG_MIN_SUPPORTED_VERSION = SemVer.parseFromText("4.22.0");

private final BinaryVersionFetcher binaryFetcher;

public BinaryRun(BinaryVersionFetcher binaryFetcher) {
Expand All @@ -33,29 +38,30 @@ public BinaryRun(BinaryVersionFetcher binaryFetcher) {
@NotNull
public List<String> generateRunCommand(@Nullable Map<String, Object> additionalMetadata)
throws NoValidBinaryToRunException {
List<String> command = new ArrayList<>(singletonList(binaryFetcher.fetchBinary()));
BinaryVersion binaryVersion = binaryFetcher.fetchBinary();
List<String> command = new ArrayList<>(singletonList(binaryVersion.getVersionFullPath()));

command.addAll(getBinaryConstantParameters(additionalMetadata));
command.addAll(getBinaryConstantParameters(binaryVersion, additionalMetadata));

return command;
}

public Process reportUninstall(@Nullable Map<String, Object> additionalMetadata)
throws NoValidBinaryToRunException, TabNineDeadException {
String fullLocation = binaryFetcher.fetchBinary();
List<String> command = new ArrayList<>(asList(fullLocation, UNINSTALLING_FLAG));
BinaryVersion binary = binaryFetcher.fetchBinary();
List<String> command = new ArrayList<>(asList(binary.getVersionFullPath(), UNINSTALLING_FLAG));

command.addAll(getBinaryConstantParameters(additionalMetadata));
command.addAll(getBinaryConstantParameters(binary, additionalMetadata));

try {
return new ProcessBuilder(command).start();
} catch (IOException e) {
throw new TabNineDeadException(e, fullLocation);
throw new TabNineDeadException(e, binary.getVersionFullPath());
}
}

private ArrayList<String> getBinaryConstantParameters(
@Nullable Map<String, Object> additionalMetadata) {
BinaryVersion binaryVersion, @Nullable Map<String, Object> additionalMetadata) {
ArrayList<String> constantParameters = new ArrayList<>();
if (ApplicationManager.getApplication() != null
&& !ApplicationManager.getApplication().isUnitTestMode()) {
Expand Down Expand Up @@ -85,6 +91,13 @@ private ArrayList<String> getBinaryConstantParameters(
"--cloud2_url=" + cmdSanitize(StaticConfig.getTabnineEnterpriseHost().get()));
}

if (SemVer.parseFromText(binaryVersion.getVersion())
.isGreaterOrEqualThan(TLS_CONFIG_MIN_SUPPORTED_VERSION)) {
constantParameters.add("--tls_config");
constantParameters.add(
String.format("insecure=%b", StaticConfig.getIgnoreCertificateErrors()));
}

if (additionalMetadata != null) {
additionalMetadata.forEach(
(key, value) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ public BinaryVersionFetcher(
* @return path of the binary to run
* @throws SecurityException, NoExistingBinaryException if something went wrong
*/
public String fetchBinary() throws NoValidBinaryToRunException {
public BinaryVersion fetchBinary() throws NoValidBinaryToRunException {
Optional<BinaryVersion> bootstrappedVersion =
BootstrapperSupport.bootstrapVersion(
localBinaryVersions, binaryRemoteSource, bundleDownloader);
if (bootstrappedVersion.isPresent()) {
Logger.getInstance(getClass())
.info(format("found local bootstrapped version %s", bootstrappedVersion.get()));
return bootstrappedVersion.get().getVersionFullPath();
return bootstrappedVersion.get();
}
Logger.getInstance(getClass())
.warn("couldn't get bootstrapped version. fallback to legacy code!");
Expand All @@ -57,15 +57,14 @@ public String fetchBinary() throws NoValidBinaryToRunException {
preferredBetaVersion.get().getVersion()));

if (!isBadVersion(preferredBetaVersion.get())) {
return preferredBetaVersion.get().getVersionFullPath();
return preferredBetaVersion.get();
}
}

return binaryRemoteSource
.fetchPreferredVersion()
.map(getLocalOrDownload(versions))
.orElseGet(versions.stream()::findFirst)
.map(BinaryVersion::getVersionFullPath)
.orElseThrow(NoValidBinaryToRunException::new);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.tabnineCommon.binary.requests.config

import com.google.gson.annotations.SerializedName
import com.tabnineCommon.binary.BinaryRequest

enum class ChatCommunicationKind {
@SerializedName("forward")
Forward
}
class ChatCommunicatorAddressRequest(val kind: ChatCommunicationKind) : BinaryRequest<ChatCommunicatorAddressResponse> {
override fun response(): Class<ChatCommunicatorAddressResponse> {
return ChatCommunicatorAddressResponse::class.java
}

override fun serialize(): Any {
return mapOf("ChatCommunicatorAddress" to this)
}

override fun validate(response: ChatCommunicatorAddressResponse): Boolean {
return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.tabnineCommon.binary.requests.config

import com.tabnineCommon.binary.BinaryResponse

data class ChatCommunicatorAddressResponse(val address: String) : BinaryResponse
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@ public enum Capability {
@SerializedName("plugin.feature.force_registration")
FORCE_REGISTRATION,
@SerializedName("plugin.feature.tabnine_chat")
TABNINE_CHAT
TABNINE_CHAT,
@SerializedName("chat_url_from_binary")
CHAT_URL_FROM_BINARY,
}
60 changes: 32 additions & 28 deletions Common/src/main/java/com/tabnineCommon/chat/ChatBrowser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,35 @@ import java.awt.datatransfer.StringSelection
import java.nio.file.Paths
import java.util.concurrent.atomic.AtomicBoolean

class ChatBrowser(messagesRouter: ChatMessagesRouter, project: Project) {
class ChatBrowser private constructor(project: Project) {
var jbCefBrowser: JBCefBrowser
private val browserLoadedListeners = mutableMapOf<String, () -> Unit>()
private val isLoaded = AtomicBoolean(false)

companion object {
fun getInstance(project: Project): ChatBrowser? {
return project.getUserData(Consts.BROWSER_PROJECT_KEY)
}
}
private val chatAppStartupListeners = mutableMapOf<String, () -> Unit>()
private val isChatAppAlive = AtomicBoolean(false)

init {
this.jbCefBrowser = initializeBrowser(project, messagesRouter)
this.jbCefBrowser = initializeBrowser(project)
}

project.putUserData(Consts.BROWSER_PROJECT_KEY, this)
companion object {
fun getInstance(project: Project): ChatBrowser {
val existingBrowser = project.getUserData(Consts.BROWSER_PROJECT_KEY)
if (existingBrowser != null) {
return existingBrowser
}
val newBrowser = ChatBrowser(project)
project.putUserData(Consts.BROWSER_PROJECT_KEY, newBrowser)
return newBrowser
}
}

private fun initializeBrowser(
project: Project,
messagesRouter: ChatMessagesRouter
): JBCefBrowser {
val browser = createBrowser()
val postMessageListener = JBCefJSQuery.create(browser)
val copyCodeListener = JBCefJSQuery.create(browser)
postMessageListener.addHandler {
handleIncomingMessage(it, project, browser, messagesRouter)
handleIncomingMessage(it, project, browser)
return@addHandler null
}
copyCodeListener.addHandler {
Expand All @@ -55,20 +58,21 @@ class ChatBrowser(messagesRouter: ChatMessagesRouter, project: Project) {
cefLoadHandler(browser, postMessageListener, copyCodeListener),
browser.cefBrowser
)
loadChatOnto(browser)
loadChatOnto(browser, project)

return browser
}

fun isLoaded(): Boolean {
return isLoaded.get()
fun isChatAppAlive(): Boolean {
return isChatAppAlive.get()
}

fun registerBrowserLoadedListener(id: String, listener: () -> Unit) {
browserLoadedListeners[id] = listener
fun registerChatAppStartupListener(id: String, project: Project, listener: () -> Unit) {
Logger.getInstance(javaClass).debug("Registering chat app startup $id for project ${project.name}")
chatAppStartupListeners[id] = listener
}

private fun loadChatOnto(browser: JBCefBrowser) {
private fun loadChatOnto(browser: JBCefBrowser, project: Project) {
val devServerUrl = System.getenv("TABNINE_CHAT_DEV_SERVER_URL")

if (devServerUrl != null) {
Expand All @@ -77,7 +81,7 @@ class ChatBrowser(messagesRouter: ChatMessagesRouter, project: Project) {
return
}

Logger.getInstance(javaClass).info("Running Tabnine Chat")
Logger.getInstance(javaClass).info("Running Tabnine Chat for project ${project.name}")

try {
val destination = Paths.get(StaticConfig.getBaseDirectory().toString(), "chat")
Expand All @@ -93,10 +97,16 @@ class ChatBrowser(messagesRouter: ChatMessagesRouter, project: Project) {
it: String,
project: Project,
browser: JBCefBrowser,
messagesRouter: ChatMessagesRouter
) {
if (!isChatAppAlive.getAndSet(true)) {
Logger.getInstance(javaClass).debug("Chat app has started for project ${project.name}, invoking chat app startup listeners")
chatAppStartupListeners.forEach {
Logger.getInstance(javaClass).debug("Running chat app startup listener '${it.key}' on project ${project.name}")
it.value()
}
}
Logger.getInstance(javaClass).trace("Received message: $it")
val response = messagesRouter.handleRawMessage(it, project)
val response = ChatMessagesRouter.handleRawMessage(it, project)

Logger.getInstance(javaClass).trace("Sending response: $response")
browser.cefBrowser.executeJavaScript("window.postMessage($response, '*')", "", 0)
Expand Down Expand Up @@ -127,12 +137,6 @@ class ChatBrowser(messagesRouter: ChatMessagesRouter, project: Project) {
),
"", 0
)
browserLoadedListeners.forEach {
Logger.getInstance(javaClass).debug("Running browser loaded listener '${it.key}'")
val listener = it.value
listener()
}
isLoaded.set(true)
}
}

Expand Down
4 changes: 2 additions & 2 deletions Common/src/main/java/com/tabnineCommon/chat/ChatFrame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import javax.swing.JPanel
import javax.swing.SwingConstants.CENTER
import javax.swing.event.HyperlinkEvent

class ChatFrame(private val project: Project, private val messagesRouter: ChatMessagesRouter, private val binaryRequestFacade: BinaryRequestFacade) :
class ChatFrame(private val project: Project, private val binaryRequestFacade: BinaryRequestFacade) :
JPanel(true), Disposable {
private var capabilitiesFetched = false

Expand Down Expand Up @@ -94,7 +94,7 @@ class ChatFrame(private val project: Project, private val messagesRouter: ChatMe

private fun displayChat() {
val browser = try {
ChatBrowser(messagesRouter, project)
ChatBrowser.getInstance(project)
} catch (e: Exception) {
Logger.getInstance(javaClass).warn("Failed to create browser", e)
displayBrowserNotAvailable()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.google.gson.JsonElement
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.tabnineCommon.chat.commandHandlers.ChatMessageHandler
import com.tabnineCommon.chat.commandHandlers.GetServerUrlHandler
import com.tabnineCommon.chat.commandHandlers.GetUserHandler
import com.tabnineCommon.chat.commandHandlers.SendEventHandler
import com.tabnineCommon.chat.commandHandlers.chatSettings.GetChatSettingsHandler
Expand All @@ -21,7 +22,7 @@ import com.tabnineCommon.general.DependencyContainer
data class ChatMessageRequest(val id: String, val command: String, val data: JsonElement? = null)
data class ChatMessageResponse(val id: String, val payload: Any? = null, val error: String? = null)

class ChatMessagesRouter {
object ChatMessagesRouter {
private val gson = DependencyContainer.instanceOfGson()
private val commandHandlers = mapOf<String, ChatMessageHandler<*, *>>(
"init" to InitHandler(gson),
Expand All @@ -36,6 +37,7 @@ class ChatMessagesRouter {
"insert_at_cursor" to InsertAtCursorHandler(gson),
"get_settings" to GetChatSettingsHandler(gson),
"update_settings" to UpdateChatSettingsHandler(gson),
"get_server_url" to GetServerUrlHandler(gson),
)

fun handleRawMessage(rawRequest: String, project: Project): String {
Expand All @@ -52,7 +54,7 @@ class ChatMessagesRouter {

return ChatMessageResponse(request.id, responsePayload)
} catch (e: Exception) {
Logger.getInstance(ChatMessagesRouter::class.java).error("Failed to handle request '${request.command}'", e)
Logger.getInstance(ChatMessagesRouter::class.java).warn("Failed to handle request '${request.command}'", e)
return ChatMessageResponse(request.id, error = e.message ?: e.toString())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ import com.intellij.openapi.wm.ToolWindowFactory
import com.tabnineCommon.general.DependencyContainer.instanceOfBinaryRequestFacade

class TabnineChatWebViewFactory : ToolWindowFactory, Disposable {

private var messagesRouter = ChatMessagesRouter()
private val binaryRequestFacade = instanceOfBinaryRequestFacade()

override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
val chatFrame = ChatFrame(project, messagesRouter, binaryRequestFacade)
val chatFrame = ChatFrame(project, binaryRequestFacade)
Disposer.register(toolWindow.disposable, chatFrame)
toolWindow.component.add(chatFrame)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,10 @@ import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.wm.ToolWindowManager
import com.tabnineCommon.chat.ChatBrowser
import com.tabnineCommon.chat.ChatEnabled
import com.tabnineCommon.chat.Consts.CHAT_ICON
import com.tabnineCommon.chat.Consts.CHAT_TOOL_WINDOW_ID
import com.tabnineCommon.general.DependencyContainer
import org.jetbrains.concurrency.runAsync
import com.tabnineCommon.chat.actions.common.ChatActionCommunicator

data class AskChatPayload(private val input: String)

Expand All @@ -35,49 +30,12 @@ class AskChatAction private constructor() : AnAction("Ask Tabnine", "Ask tabnine

override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return
val browser = getBrowser(project) ?: return
val ourToolWindow = ToolWindowManager.getInstance(project)
.getToolWindow(CHAT_TOOL_WINDOW_ID) ?: return

val result =
Messages.showInputDialog("What do you have in mind?", "Ask Tabnine", CHAT_ICON)
.takeUnless { it.isNullOrBlank() }
?: return

if (browser.isLoaded()) {
ourToolWindow.activate {
submitMessageToChat(project, result)
}
} else {
browser.registerBrowserLoadedListener(ID) {
runAsync {
Thread.sleep(1000)
submitMessageToChat(project, result)
}
}
ourToolWindow.activate(null)
}
}

private fun submitMessageToChat(project: Project, result: String) {
sendMessage(project, TabnineActionRequest("submit-message", AskChatPayload(result)))
}

private fun sendMessage(project: Project, message: TabnineActionRequest) {
val browser = getBrowser(project) ?: return
val messageJson = DependencyContainer.instanceOfGson().toJson(message)

Logger.getInstance(javaClass).info("Sending message: $messageJson")
browser.jbCefBrowser.cefBrowser.executeJavaScript("window.postMessage($messageJson, '*')", "", 0)
}

private fun getBrowser(project: Project): ChatBrowser? {
val browser = ChatBrowser.getInstance(project)
if (browser == null) {
Logger.getInstance(javaClass).warn("Browser not found on project ${project.name}")
return null
}
return browser
ChatActionCommunicator.sendMessageToChat(project, ID, result)
}

override fun update(e: AnActionEvent) {
Expand Down
Loading

0 comments on commit 88efcbe

Please sign in to comment.