mirror of
https://github.com/hexedtech/codemp-intellij.git
synced 2024-11-21 14:44:49 +01:00
chore: updated to new glue (kind of)
This commit is contained in:
parent
0719ac84f4
commit
ddf0e4eb71
35 changed files with 279 additions and 756 deletions
23
Cargo.toml
23
Cargo.toml
|
@ -1,23 +0,0 @@
|
|||
[package]
|
||||
name = "codemp-intellij"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
codemp = { path = "../../lib", features = ["global", "sync", "transport"] }
|
||||
#codemp = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/codemp.git", branch = "workspace", features = ["global", "sync"] }
|
||||
jni = { version = "0.21.1", features = ["invocation"] }
|
||||
jni-sys = "0.3.0"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.20"
|
||||
rifgen = { git = "https://github.com/Kofituo/rifgen.git", rev = "d27d9785b2febcf5527f1deb6a846be5d583f7d7"}
|
||||
tokio = "1.35.1"
|
||||
uuid = { version = "1.4.1", features = ["v4"] }
|
||||
|
||||
[build-dependencies]
|
||||
flapigen = "0.6.0"
|
||||
rifgen = { git = "https://github.com/Kofituo/rifgen.git", rev = "d27d9785b2febcf5527f1deb6a846be5d583f7d7"}
|
||||
|
||||
[lib]
|
||||
crate_type = ["cdylib"]
|
||||
path = "src/main/rust/lib.rs"
|
44
build.gradle
44
build.gradle
|
@ -2,10 +2,11 @@ plugins {
|
|||
id 'java'
|
||||
id 'org.jetbrains.intellij' version '1.16.0'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||
id 'com.palantir.git-version' version '3.1.0'
|
||||
}
|
||||
|
||||
group = 'com.codemp'
|
||||
version = '0.1.0'
|
||||
group = 'mp.code'
|
||||
//version = versionDetails().lastTag
|
||||
|
||||
java {
|
||||
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17
|
||||
|
@ -17,9 +18,9 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.github.adamheinrich:native-utils:master-SNAPSHOT'
|
||||
implementation 'org.slf4j:slf4j-api:2.0.9'
|
||||
implementation 'ch.qos.logback:logback-classic:1.4.12'
|
||||
implementation files('../../lib/dist/java/build/libs/codemp-0.6.2.jar')
|
||||
}
|
||||
|
||||
intellij {
|
||||
|
@ -30,24 +31,14 @@ intellij {
|
|||
shadowJar {
|
||||
archiveClassifier.set('')
|
||||
dependencies {
|
||||
include(dependency('com.github.adamheinrich:native-utils:master-SNAPSHOT'))
|
||||
}
|
||||
}
|
||||
|
||||
def cargoDir = projectDir.toPath().resolve('target').resolve('release').toFile()
|
||||
|
||||
processResources {
|
||||
from(cargoDir) {
|
||||
include('*.dll')
|
||||
include('*.so')
|
||||
into('natives/')
|
||||
include(dependency(files('../../lib/java/build/libs/codemp-6645c46.jar')))
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
patchPluginXml {
|
||||
sinceBuild.set('222')
|
||||
untilBuild.set('232.*')
|
||||
untilBuild.set('233.*')
|
||||
}
|
||||
|
||||
signPlugin {
|
||||
|
@ -61,27 +52,4 @@ tasks {
|
|||
}
|
||||
}
|
||||
|
||||
//run cargo build
|
||||
tasks.register('cargoBuild', Exec) {
|
||||
workingDir '.'
|
||||
commandLine 'cargo', 'build', '--release'
|
||||
}
|
||||
|
||||
//must act before all other tasks who mess with resources to reliably get the binary in
|
||||
patchPluginXml.dependsOn cargoBuild
|
||||
|
||||
//delete old jni generated files
|
||||
tasks.register('deleteGeneratedNativeInterface', Delete) {
|
||||
delete 'src/main/java/com/codemp/intellij/jni'
|
||||
}
|
||||
|
||||
//delete cargo build files
|
||||
tasks.register('cargoClean', Exec) {
|
||||
workingDir '.'
|
||||
commandLine 'cargo', 'clean'
|
||||
dependsOn deleteGeneratedNativeInterface
|
||||
}
|
||||
|
||||
clean.dependsOn cargoClean
|
||||
|
||||
instrumentedJar.dependsOn shadowJar //TODO: instrumentedJar should use fatjar as input
|
||||
|
|
45
build.rs
45
build.rs
|
@ -1,45 +0,0 @@
|
|||
use flapigen::{JavaConfig, LanguageConfig};
|
||||
use std::{env, fs, path::Path};
|
||||
use rifgen::{Generator as RifgenGenerator, TypeCases, Language};
|
||||
use flapigen::Generator as FlapigenGenerator;
|
||||
|
||||
fn main() {
|
||||
let out_dir_var = env::var("OUT_DIR")
|
||||
.expect("no OUT_DIR, but cargo should provide it");
|
||||
let out_dir = Path::new(&out_dir_var);
|
||||
let generated_glue_file = out_dir.join("generated_glue.in");
|
||||
|
||||
let src_dir = Path::new("src")
|
||||
.join("main")
|
||||
.join("rust");
|
||||
let glue_file = src_dir.join("glue.in");
|
||||
|
||||
RifgenGenerator::new(TypeCases::CamelCase,Language::Java, vec!(src_dir))
|
||||
.generate_interface(&generated_glue_file);
|
||||
|
||||
let jni_path = Path::new("src")
|
||||
.join("main")
|
||||
.join("java")
|
||||
.join("com")
|
||||
.join("codemp")
|
||||
.join("intellij")
|
||||
.join("jni");
|
||||
|
||||
//create folder if it doesn't exist
|
||||
fs::create_dir_all(&jni_path)
|
||||
.expect("An error occurred while creating the JNI folder!");
|
||||
|
||||
let java_gen = FlapigenGenerator::new(LanguageConfig::JavaConfig(
|
||||
JavaConfig::new(
|
||||
jni_path,
|
||||
"com.codemp.intellij.jni".into()
|
||||
))).rustfmt_bindings(true);
|
||||
|
||||
java_gen.expand_many(
|
||||
"codemp-intellij",
|
||||
&[&generated_glue_file, &glue_file],
|
||||
out_dir.join("glue.rs"),
|
||||
);
|
||||
|
||||
println!("cargo:rerun-if-changed={}", generated_glue_file.display());
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package com.codemp.intellij;
|
||||
|
||||
import com.codemp.intellij.exceptions.ide.NotConnectedException;
|
||||
import com.codemp.intellij.jni.ClientHandler;
|
||||
import com.codemp.intellij.workspace.Workspace;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import cz.adamh.utils.NativeUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class CodeMP {
|
||||
public static Logger LOGGER = LoggerFactory.getLogger(CodeMP.class);
|
||||
public static final Map<String, Workspace> ACTIVE_WORKSPACES = new ConcurrentHashMap<>();
|
||||
private static ClientHandler CLIENT = null;
|
||||
|
||||
public static void connect(String url) {
|
||||
CodeMP.loadLibrary(); //will only load it the first time
|
||||
CLIENT = new ClientHandler(url);
|
||||
}
|
||||
|
||||
public static void disconnect() {
|
||||
CLIENT = null;
|
||||
}
|
||||
|
||||
public static ClientHandler getClient(String reason) throws NotConnectedException {
|
||||
if(CLIENT == null) throw new NotConnectedException(reason);
|
||||
return CLIENT;
|
||||
}
|
||||
|
||||
private static boolean loadedLibrary = false;
|
||||
|
||||
public static void loadLibrary() {
|
||||
if(!loadedLibrary) {
|
||||
try {
|
||||
if(SystemInfo.isWindows)
|
||||
NativeUtils.loadLibraryFromJar("/natives/codemp_intellij.dll");
|
||||
else NativeUtils.loadLibraryFromJar("/natives/libcodemp_intellij.so");
|
||||
} catch(IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
LOGGER.info("Loaded CodeMP library!");
|
||||
loadedLibrary = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package com.codemp.intellij.actions;
|
||||
|
||||
import com.codemp.intellij.CodeMP;
|
||||
import com.codemp.intellij.util.ActionUtil;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ConnectAction extends AnAction {
|
||||
public static void connect(AnActionEvent e, String url, boolean silent) {
|
||||
CodeMP.connect(url);
|
||||
if(!silent) ActionUtil.notify(e,
|
||||
"Success", String.format("Connected to %s!", url));
|
||||
CodeMP.LOGGER.debug("Connected to {}!", url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
String url = Messages.showInputDialog("URL to CodeMP instance:", "CodeMP Connect",
|
||||
Messages.getQuestionIcon());
|
||||
try {
|
||||
connect(e, url, false);
|
||||
} catch(Exception ex) {
|
||||
ActionUtil.notifyError(e, String.format(
|
||||
"Failed to connect to %s!",
|
||||
url), ex);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package com.codemp.intellij.actions;
|
||||
|
||||
import com.codemp.intellij.CodeMP;
|
||||
import com.codemp.intellij.actions.workspace.WorkspaceJoinAction;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Used exclusively to streamline debugging.
|
||||
*/
|
||||
public class FastForwardAction extends AnAction {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
ConnectAction.connect(e, "http://alemi.dev:50052", true);
|
||||
WorkspaceJoinAction.join(e, "default", true);
|
||||
CodeMP.LOGGER.debug("Completed quick startup for testing!");
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package com.codemp.intellij.exceptions;
|
||||
|
||||
public class CodeMPException extends RuntimeException {
|
||||
public CodeMPException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package com.codemp.intellij.exceptions.ide;
|
||||
|
||||
import com.codemp.intellij.exceptions.CodeMPException;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
|
||||
/**
|
||||
* Fired when trying to use {@link com.intellij.openapi.actionSystem.AnActionEvent}'s context
|
||||
* from a state where that use is not supported.
|
||||
*/
|
||||
public class BadActionEventStateException extends CodeMPException {
|
||||
public BadActionEventStateException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package com.codemp.intellij.exceptions.lib;
|
||||
|
||||
import com.codemp.intellij.exceptions.CodeMPException;
|
||||
|
||||
public class ChannelException extends CodeMPException {
|
||||
public ChannelException(String input) {
|
||||
super(input);
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package com.codemp.intellij.exceptions.lib;
|
||||
|
||||
import com.codemp.intellij.exceptions.CodeMPException;
|
||||
|
||||
public class DeadlockedException extends CodeMPException {
|
||||
public DeadlockedException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package com.codemp.intellij.exceptions.lib;
|
||||
|
||||
import com.codemp.intellij.exceptions.CodeMPException;
|
||||
|
||||
public class InvalidStateException extends CodeMPException {
|
||||
public InvalidStateException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package com.codemp.intellij.exceptions.lib;
|
||||
|
||||
import com.codemp.intellij.exceptions.CodeMPException;
|
||||
|
||||
public class TransportException extends CodeMPException {
|
||||
public TransportException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
30
src/main/java/mp/code/intellij/CodeMP.java
Normal file
30
src/main/java/mp/code/intellij/CodeMP.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
package mp.code.intellij;
|
||||
|
||||
import mp.code.intellij.exceptions.ide.NotConnectedException;
|
||||
import mp.code.intellij.workspace.IJWorkspace;
|
||||
import mp.code.Client;
|
||||
import mp.code.exceptions.CodeMPException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class CodeMP {
|
||||
public static Logger LOGGER = LoggerFactory.getLogger(CodeMP.class);
|
||||
public static final Map<String, IJWorkspace> ACTIVE_WORKSPACES = new ConcurrentHashMap<>();
|
||||
private static Client CLIENT = null;
|
||||
|
||||
public static void connect(String url, String username, String password) throws CodeMPException {
|
||||
CLIENT = Client.connect(url, username, password);
|
||||
}
|
||||
|
||||
public static void disconnect() {
|
||||
CLIENT = null;
|
||||
}
|
||||
|
||||
public static Client getClient(String reason) throws NotConnectedException {
|
||||
if(CLIENT == null) throw new NotConnectedException(reason);
|
||||
return CLIENT;
|
||||
}
|
||||
}
|
35
src/main/java/mp/code/intellij/actions/ConnectAction.java
Normal file
35
src/main/java/mp/code/intellij/actions/ConnectAction.java
Normal file
|
@ -0,0 +1,35 @@
|
|||
package mp.code.intellij.actions;
|
||||
|
||||
import mp.code.intellij.CodeMP;
|
||||
import mp.code.intellij.util.ActionUtil;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import mp.code.exceptions.CodeMPException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ConnectAction extends AnAction {
|
||||
public static void connect(AnActionEvent e, String url, String username, String password, boolean silent) throws CodeMPException {
|
||||
System.out.printf("%s %s %s", url, username, password);
|
||||
CodeMP.connect(url, username, password);
|
||||
if(!silent) ActionUtil.notify(e,
|
||||
"Success", String.format("Connected to %s!", url));
|
||||
CodeMP.LOGGER.debug("Connected to {}!", url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
/*
|
||||
LoginDialog dialog = new LoginDialog(e.getProject(), "Please input your login credentials!", "Connect to CodeMP server");
|
||||
if(dialog.showAndGet()) {
|
||||
try {
|
||||
connect(e, dialog.urlField.getText(), dialog.usernameField.getText(), dialog.passwordField.getText(), false);
|
||||
} catch(Exception exception) {
|
||||
ActionUtil.notifyError(
|
||||
e,
|
||||
String.format("Failed to connect to %s!", dialog.urlField.getText()),
|
||||
exception
|
||||
);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package com.codemp.intellij.actions;
|
||||
package mp.code.intellij.actions;
|
||||
|
||||
import com.codemp.intellij.CodeMP;
|
||||
import com.codemp.intellij.util.ActionUtil;
|
||||
import mp.code.intellij.CodeMP;
|
||||
import mp.code.intellij.util.ActionUtil;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
|
@ -0,0 +1,24 @@
|
|||
package mp.code.intellij.actions;
|
||||
|
||||
import mp.code.intellij.CodeMP;
|
||||
import mp.code.intellij.actions.workspace.WorkspaceJoinAction;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import mp.code.exceptions.CodeMPException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Used exclusively to streamline debugging.
|
||||
*/
|
||||
public class FastForwardAction extends AnAction {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
try {
|
||||
ConnectAction.connect(e, "http://alemi.dev:50053", "", "", true);
|
||||
WorkspaceJoinAction.join(e, "glue", true);
|
||||
CodeMP.LOGGER.debug("Completed quick startup for testing!");
|
||||
} catch(CodeMPException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
package com.codemp.intellij.actions.workspace;
|
||||
package mp.code.intellij.actions.workspace;
|
||||
|
||||
import com.codemp.intellij.CodeMP;
|
||||
import com.codemp.intellij.util.ActionUtil;
|
||||
import com.codemp.intellij.workspace.Workspace;
|
||||
import mp.code.intellij.CodeMP;
|
||||
import mp.code.intellij.util.ActionUtil;
|
||||
import mp.code.intellij.workspace.IJWorkspace;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import mp.code.exceptions.CodeMPException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class WorkspaceJoinAction extends AnAction {
|
||||
public static void join(AnActionEvent e, String workspaceId, boolean silent) {
|
||||
CodeMP.ACTIVE_WORKSPACES.put(workspaceId, new Workspace(
|
||||
public static void join(AnActionEvent e, String workspaceId, boolean silent) throws CodeMPException {
|
||||
CodeMP.ACTIVE_WORKSPACES.put(workspaceId, new IJWorkspace(
|
||||
workspaceId, CodeMP.getClient("join workspace"),
|
||||
false, e.getProject() //TODO: implement remote projects
|
||||
));
|
|
@ -1,7 +1,7 @@
|
|||
package com.codemp.intellij.actions.workspace;
|
||||
package mp.code.intellij.actions.workspace;
|
||||
|
||||
import com.codemp.intellij.CodeMP;
|
||||
import com.codemp.intellij.util.ActionUtil;
|
||||
import mp.code.intellij.CodeMP;
|
||||
import mp.code.intellij.util.ActionUtil;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
|
@ -10,8 +10,8 @@ import org.jetbrains.annotations.NotNull;
|
|||
|
||||
public class WorkspaceLeaveAction extends AnAction {
|
||||
public static void leave(AnActionEvent e, String workspaceId, boolean silent) {
|
||||
CodeMP.getClient("leave workspace")
|
||||
.leaveWorkspace(workspaceId);
|
||||
//CodeMP.getClient("leave workspace").leaveWorkspace(workspaceId);
|
||||
// TODO
|
||||
Disposer.dispose(CodeMP.ACTIVE_WORKSPACES.remove(workspaceId));
|
||||
|
||||
if(!silent) ActionUtil.notify(e, "Success", String.format("Left workspace %s!", workspaceId));
|
|
@ -0,0 +1,7 @@
|
|||
package mp.code.intellij.exceptions;
|
||||
|
||||
public class CodeMPIJException extends RuntimeException {
|
||||
public CodeMPIJException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package mp.code.intellij.exceptions.ide;
|
||||
|
||||
import mp.code.intellij.exceptions.CodeMPIJException;
|
||||
|
||||
/**
|
||||
* Fired when trying to use {@link com.intellij.openapi.actionSystem.AnActionEvent}'s context
|
||||
* from a state where that use is not supported.
|
||||
*/
|
||||
public class BadActionEventStateException extends CodeMPIJException {
|
||||
public BadActionEventStateException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
package com.codemp.intellij.exceptions.ide;
|
||||
package mp.code.intellij.exceptions.ide;
|
||||
|
||||
import com.codemp.intellij.exceptions.CodeMPException;
|
||||
import mp.code.intellij.exceptions.CodeMPIJException;
|
||||
|
||||
/**
|
||||
* Thrown upon failure to detach from a buffer.
|
||||
*/
|
||||
public class BufferDetachException extends CodeMPException {
|
||||
public class BufferDetachException extends CodeMPIJException {
|
||||
|
||||
public BufferDetachException(String name) {
|
||||
super(String.format("Could not detach from buffer named \"%s\"!", name));
|
|
@ -1,12 +1,12 @@
|
|||
package com.codemp.intellij.exceptions.ide;
|
||||
package mp.code.intellij.exceptions.ide;
|
||||
|
||||
import com.codemp.intellij.exceptions.CodeMPException;
|
||||
import mp.code.intellij.exceptions.CodeMPIJException;
|
||||
|
||||
/**
|
||||
* Fired when trying to access the CodeMP client without first connecting
|
||||
* to a server.
|
||||
*/
|
||||
public class NotConnectedException extends CodeMPException {
|
||||
public class NotConnectedException extends CodeMPIJException {
|
||||
|
||||
public NotConnectedException(String service) {
|
||||
super(String.format("Failed to %s, you are not connected to a server!", service));
|
|
@ -1,23 +1,26 @@
|
|||
package com.codemp.intellij.listeners;
|
||||
package mp.code.intellij.listeners;
|
||||
|
||||
import com.codemp.intellij.CodeMP;
|
||||
import com.codemp.intellij.exceptions.CodeMPException;
|
||||
import com.codemp.intellij.jni.BufferHandler;
|
||||
import mp.code.intellij.CodeMP;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import mp.code.BufferController;
|
||||
import mp.code.data.TextChange;
|
||||
import mp.code.exceptions.CodeMPException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class BufferEventListener implements DocumentListener {
|
||||
|
||||
private final BufferHandler bufferHandler;
|
||||
private final BufferController controller;
|
||||
|
||||
public BufferEventListener(BufferHandler bufferHandler) {
|
||||
this.bufferHandler = bufferHandler;
|
||||
public BufferEventListener(BufferController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void documentChanged(@NotNull DocumentEvent event) throws CodeMPException {
|
||||
public void documentChanged(@NotNull DocumentEvent event) {
|
||||
CodeMP.LOGGER.debug("Changed {} to {} at offset {}",
|
||||
event.getOldFragment(), event.getNewFragment(), event.getOffset());
|
||||
|
||||
|
@ -29,8 +32,15 @@ public class BufferEventListener implements DocumentListener {
|
|||
//TODO move actions break
|
||||
int changeOffset = event.getOffset();
|
||||
CharSequence newFragment = event.getNewFragment();
|
||||
this.bufferHandler.send(changeOffset,
|
||||
try {
|
||||
this.controller.send(new TextChange(
|
||||
changeOffset,
|
||||
changeOffset + event.getOldFragment().length(),
|
||||
newFragment.toString());
|
||||
newFragment.toString(),
|
||||
0L
|
||||
));
|
||||
} catch(CodeMPException ignored) {
|
||||
// TODO actually give a shit
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +1,23 @@
|
|||
package com.codemp.intellij.listeners;
|
||||
package mp.code.intellij.listeners;
|
||||
|
||||
import com.codemp.intellij.CodeMP;
|
||||
import com.codemp.intellij.jni.CursorHandler;
|
||||
import com.codemp.intellij.util.FileUtil;
|
||||
import mp.code.intellij.CodeMP;
|
||||
import mp.code.intellij.util.FileUtil;
|
||||
import com.intellij.openapi.editor.Caret;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.VisualPosition;
|
||||
import com.intellij.openapi.editor.event.CaretEvent;
|
||||
import com.intellij.openapi.editor.event.CaretListener;
|
||||
import mp.code.CursorController;
|
||||
import mp.code.data.Cursor;
|
||||
import mp.code.exceptions.CodeMPException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class CursorEventListener implements CaretListener {
|
||||
|
||||
private final CursorHandler handler;
|
||||
private final CursorController controller;
|
||||
|
||||
public CursorEventListener(CursorHandler handler) {
|
||||
this.handler = handler;
|
||||
public CursorEventListener(CursorController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -27,13 +29,21 @@ public class CursorEventListener implements CaretListener {
|
|||
VisualPosition startPos = caret.getSelectionStartPosition();
|
||||
VisualPosition endPos = caret.getSelectionEndPosition();
|
||||
CodeMP.LOGGER.debug("Caret moved from {}x {}y to {}x {}y",
|
||||
startPos.line, startPos.column, endPos.line, endPos.column);
|
||||
startPos.line, startPos.column, endPos.line, endPos.column
|
||||
);
|
||||
|
||||
Editor editor = event.getEditor();
|
||||
this.handler.send(
|
||||
try {
|
||||
this.controller.send(new Cursor(
|
||||
startPos.line,
|
||||
startPos.column,
|
||||
endPos.line,
|
||||
endPos.column,
|
||||
FileUtil.getRelativePath(editor.getProject(), editor.getVirtualFile()),
|
||||
startPos.line, startPos.column,
|
||||
endPos.line, endPos.column
|
||||
);
|
||||
null
|
||||
));
|
||||
} catch(CodeMPException e) {
|
||||
// TODO zzzzz
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,20 @@
|
|||
package com.codemp.intellij.listeners;
|
||||
package mp.code.intellij.listeners;
|
||||
|
||||
import com.codemp.intellij.jni.WorkspaceHandler;
|
||||
import com.codemp.intellij.task.BufferEventAwaiterTask;
|
||||
import com.codemp.intellij.util.FileUtil;
|
||||
import mp.code.intellij.task.BufferEventAwaiterTask;
|
||||
import mp.code.intellij.util.FileUtil;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import mp.code.Workspace;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class WorkspaceFileClosedListener implements FileEditorManagerListener.Before {
|
||||
private final WorkspaceHandler handler;
|
||||
private final Workspace handler;
|
||||
private final BufferEventAwaiterTask task;
|
||||
|
||||
public WorkspaceFileClosedListener(WorkspaceHandler handler, BufferEventAwaiterTask task) {
|
||||
public WorkspaceFileClosedListener(Workspace handler, BufferEventAwaiterTask task) {
|
||||
this.handler = handler;
|
||||
this.task = task;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ public class WorkspaceFileClosedListener implements FileEditorManagerListener.Be
|
|||
Disposable disp = this.task.activeBuffers.remove(path);
|
||||
if(disp == null) return;
|
||||
|
||||
this.handler.detachFromBuffer(path);
|
||||
// TODO : this.handler.detachFromBuffer(path);
|
||||
Disposer.dispose(disp);
|
||||
}
|
||||
}
|
|
@ -1,10 +1,7 @@
|
|||
package com.codemp.intellij.listeners;
|
||||
package mp.code.intellij.listeners;
|
||||
|
||||
import com.codemp.intellij.exceptions.lib.TransportException;
|
||||
import com.codemp.intellij.jni.BufferHandler;
|
||||
import com.codemp.intellij.jni.WorkspaceHandler;
|
||||
import com.codemp.intellij.task.BufferEventAwaiterTask;
|
||||
import com.codemp.intellij.util.FileUtil;
|
||||
import mp.code.intellij.task.BufferEventAwaiterTask;
|
||||
import mp.code.intellij.util.FileUtil;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.FileOpenedSyncListener;
|
||||
|
@ -12,15 +9,18 @@ import com.intellij.openapi.fileEditor.TextEditor;
|
|||
import com.intellij.openapi.fileEditor.ex.FileEditorWithProvider;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import mp.code.BufferController;
|
||||
import mp.code.Workspace;
|
||||
import mp.code.exceptions.CodeMPException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WorkspaceFileOpenedListener implements FileOpenedSyncListener {
|
||||
private final WorkspaceHandler handler;
|
||||
private final Workspace handler;
|
||||
private final BufferEventAwaiterTask task;
|
||||
|
||||
public WorkspaceFileOpenedListener(WorkspaceHandler handler, BufferEventAwaiterTask task) {
|
||||
public WorkspaceFileOpenedListener(Workspace handler, BufferEventAwaiterTask task) {
|
||||
this.handler = handler;
|
||||
this.task = task;
|
||||
}
|
||||
|
@ -39,9 +39,9 @@ public class WorkspaceFileOpenedListener implements FileOpenedSyncListener {
|
|||
String path = FileUtil.getRelativePath(editor.getProject(), file);
|
||||
if(path == null) return;
|
||||
|
||||
BufferHandler bufferHandler = this.getBufferForPath(path);
|
||||
BufferController bufferController = this.getBufferForPath(path);
|
||||
Disposable disp = Disposer.newDisposable(String.format("codemp-buffer-%s", path));
|
||||
editor.getDocument().addDocumentListener(new BufferEventListener(bufferHandler), disp);
|
||||
editor.getDocument().addDocumentListener(new BufferEventListener(bufferController), disp);
|
||||
|
||||
editor.getDocument().setText(""); //empty it so we can start receiving
|
||||
this.task.activeBuffers.put(path, disp);
|
||||
|
@ -51,13 +51,17 @@ public class WorkspaceFileOpenedListener implements FileOpenedSyncListener {
|
|||
/**
|
||||
* Attach to a buffer or, if it does not exist, implicitly create it.
|
||||
* @param path the buffer's name (which is the path relative to project root)
|
||||
* @return the {@link BufferHandler} for it
|
||||
* @return the {@link BufferController} for it
|
||||
*/
|
||||
private BufferHandler getBufferForPath(String path) {
|
||||
private BufferController getBufferForPath(String path) {
|
||||
try {
|
||||
return this.handler.attachToBuffer(path);
|
||||
} catch (TransportException ignored) {
|
||||
} catch (CodeMPException ignored) {
|
||||
try {
|
||||
return this.handler.createBuffer(path);
|
||||
} catch(CodeMPException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,7 @@
|
|||
package com.codemp.intellij.task;
|
||||
package mp.code.intellij.task;
|
||||
|
||||
import com.codemp.intellij.CodeMP;
|
||||
import com.codemp.intellij.exceptions.lib.DeadlockedException;
|
||||
import com.codemp.intellij.jni.BufferHandler;
|
||||
import com.codemp.intellij.jni.StringVec;
|
||||
import com.codemp.intellij.jni.TextChangeWrapper;
|
||||
import com.codemp.intellij.jni.WorkspaceHandler;
|
||||
import com.codemp.intellij.util.FileUtil;
|
||||
import mp.code.intellij.CodeMP;
|
||||
import mp.code.intellij.util.FileUtil;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
|
@ -14,6 +9,11 @@ import com.intellij.openapi.editor.Editor;
|
|||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import mp.code.BufferController;
|
||||
import mp.code.Workspace;
|
||||
import mp.code.data.TextChange;
|
||||
import mp.code.exceptions.CodeMPException;
|
||||
import mp.code.exceptions.DeadlockedException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -24,8 +24,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
|
||||
public class BufferEventAwaiterTask extends Task.Backgroundable implements Disposable {
|
||||
public final Map<String, Disposable> activeBuffers;
|
||||
private final WorkspaceHandler handler;
|
||||
public BufferEventAwaiterTask(@NotNull Project project, @NotNull WorkspaceHandler handler) {
|
||||
private final Workspace handler;
|
||||
public BufferEventAwaiterTask(@NotNull Project project, @NotNull Workspace handler) {
|
||||
super(project, "Awaiting CodeMP buffer events", false);
|
||||
this.activeBuffers = new ConcurrentHashMap<>();
|
||||
this.handler = handler;
|
||||
|
@ -35,29 +35,33 @@ public class BufferEventAwaiterTask extends Task.Backgroundable implements Dispo
|
|||
@SuppressWarnings("InfiniteLoopStatement")
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
while(true) {
|
||||
StringVec buffers = new StringVec(); //jni moment
|
||||
this.activeBuffers.keySet().forEach(buffers::push);
|
||||
|
||||
Optional<BufferHandler> bufferOptional = this.handler.selectBuffer(buffers, 100L);
|
||||
Optional<BufferController> bufferOptional;
|
||||
try {
|
||||
bufferOptional = this.handler.selectBuffer(100L);
|
||||
} catch(CodeMPException e) {
|
||||
bufferOptional = Optional.empty(); // TODO error handling
|
||||
}
|
||||
if(bufferOptional.isEmpty())
|
||||
continue;
|
||||
BufferHandler buffer = bufferOptional.get();
|
||||
BufferController buffer = bufferOptional.get();
|
||||
|
||||
List<TextChangeWrapper> changeList = new ArrayList<>();
|
||||
List<TextChange> changeList = new ArrayList<>();
|
||||
while(true) {
|
||||
Optional<TextChangeWrapper> changeOptional;
|
||||
Optional<TextChange> changeOptional;
|
||||
try {
|
||||
changeOptional = buffer.tryRecv();
|
||||
} catch(DeadlockedException e) {
|
||||
CodeMP.LOGGER.error(e.getMessage());
|
||||
continue;
|
||||
} catch(CodeMPException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if(changeOptional.isEmpty())
|
||||
break;
|
||||
TextChangeWrapper change = changeOptional.get();
|
||||
TextChange change = changeOptional.get();
|
||||
CodeMP.LOGGER.debug("Received text change {} from offset {} to {}!",
|
||||
change.getContent(), change.getStart(), change.getEnd());
|
||||
change.content, change.start, change.end);
|
||||
changeList.add(change);
|
||||
}
|
||||
|
||||
|
@ -68,7 +72,7 @@ public class BufferEventAwaiterTask extends Task.Backgroundable implements Dispo
|
|||
this.myProject,
|
||||
() -> changeList.forEach((change) ->
|
||||
bufferEditor.getDocument().replaceString(
|
||||
(int) change.getStart(), (int) change.getEnd(), change.getContent())
|
||||
(int) change.start, (int) change.end, change.content)
|
||||
),
|
||||
"CodeMPBufferReceive",
|
||||
"codemp-buffer-receive", //TODO: mark this with the name
|
|
@ -1,10 +1,8 @@
|
|||
package com.codemp.intellij.task;
|
||||
package mp.code.intellij.task;
|
||||
|
||||
import com.codemp.intellij.CodeMP;
|
||||
import com.codemp.intellij.jni.CursorEventWrapper;
|
||||
import com.codemp.intellij.jni.CursorHandler;
|
||||
import com.codemp.intellij.util.ColorUtil;
|
||||
import com.codemp.intellij.util.FileUtil;
|
||||
import mp.code.intellij.CodeMP;
|
||||
import mp.code.intellij.util.ColorUtil;
|
||||
import mp.code.intellij.util.FileUtil;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
|
@ -16,6 +14,9 @@ import com.intellij.openapi.editor.markup.TextAttributes;
|
|||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import mp.code.CursorController;
|
||||
import mp.code.data.Cursor;
|
||||
import mp.code.exceptions.CodeMPException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
|
@ -25,10 +26,10 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
//TODO this is janky as it shows a progress bar it doesn't use tbh
|
||||
//implements disposable so i can use it as lifetime ig
|
||||
public class CursorEventAwaiterTask extends Task.Backgroundable implements Disposable {
|
||||
private final CursorHandler handler;
|
||||
private final CursorController handler;
|
||||
private final Map<String, RangeHighlighter> highlighterMap = new ConcurrentHashMap<>();
|
||||
|
||||
public CursorEventAwaiterTask(@NotNull Project project, @NotNull CursorHandler handler) {
|
||||
public CursorEventAwaiterTask(@NotNull Project project, @NotNull CursorController handler) {
|
||||
super(project, "Awaiting CodeMP cursor events", false);
|
||||
this.handler = handler;
|
||||
}
|
||||
|
@ -37,23 +38,29 @@ public class CursorEventAwaiterTask extends Task.Backgroundable implements Dispo
|
|||
@SuppressWarnings("InfiniteLoopStatement")
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
while(true) {
|
||||
CursorEventWrapper event = this.handler.recv();
|
||||
Editor editor = FileUtil.getActiveEditorByPath(this.myProject, event.getBuffer());
|
||||
Cursor event;
|
||||
try {
|
||||
event = this.handler.recv();
|
||||
} catch(CodeMPException ex) {
|
||||
continue; // TODO proper handling
|
||||
}
|
||||
Editor editor = FileUtil.getActiveEditorByPath(this.myProject, event.buffer);
|
||||
if(editor == null)
|
||||
continue;
|
||||
|
||||
CodeMP.LOGGER.debug(
|
||||
"Cursor moved by user {}! Start pos: {}x {}y; end pos: {}x {}y in buffer {}!",
|
||||
event.getUser(),
|
||||
event.getStartCol(), event.getStartCol(),
|
||||
event.getEndRow(), event.getEndCol(),
|
||||
event.getBuffer());
|
||||
event.user,
|
||||
event.startCol, event.startRow,
|
||||
event.endCol, event.endRow,
|
||||
event.buffer
|
||||
);
|
||||
|
||||
try {
|
||||
int startOffset = editor.getDocument()
|
||||
.getLineStartOffset(event.getStartRow()) + event.getStartCol();
|
||||
.getLineStartOffset(event.startRow) + event.startCol;
|
||||
int endOffset = editor.getDocument()
|
||||
.getLineStartOffset(event.getEndRow()) + event.getEndCol();
|
||||
.getLineStartOffset(event.startRow) + event.startCol;
|
||||
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
int documentLength = editor.getDocument().getTextLength();
|
||||
|
@ -64,7 +71,7 @@ public class CursorEventAwaiterTask extends Task.Backgroundable implements Dispo
|
|||
return;
|
||||
}
|
||||
|
||||
RangeHighlighter previous = this.highlighterMap.put(event.getUser(), editor
|
||||
RangeHighlighter previous = this.highlighterMap.put(event.user, editor
|
||||
.getMarkupModel()
|
||||
.addRangeHighlighter(
|
||||
startOffset,
|
||||
|
@ -72,7 +79,7 @@ public class CursorEventAwaiterTask extends Task.Backgroundable implements Dispo
|
|||
HighlighterLayer.SELECTION,
|
||||
new TextAttributes(
|
||||
null,
|
||||
ColorUtil.hashColor(event.getUser()),
|
||||
ColorUtil.hashColor(event.user),
|
||||
null,
|
||||
null,
|
||||
Font.PLAIN
|
|
@ -1,7 +1,7 @@
|
|||
package com.codemp.intellij.util;
|
||||
package mp.code.intellij.util;
|
||||
|
||||
import com.codemp.intellij.CodeMP;
|
||||
import com.codemp.intellij.exceptions.ide.BadActionEventStateException;
|
||||
import mp.code.intellij.CodeMP;
|
||||
import mp.code.intellij.exceptions.ide.BadActionEventStateException;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
|
@ -1,4 +1,4 @@
|
|||
package com.codemp.intellij.util;
|
||||
package mp.code.intellij.util;
|
||||
|
||||
import com.intellij.ui.JBColor;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.codemp.intellij.util;
|
||||
package mp.code.intellij.util;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
|
@ -1,10 +1,8 @@
|
|||
package com.codemp.intellij.workspace;
|
||||
package mp.code.intellij.workspace;
|
||||
|
||||
import com.codemp.intellij.jni.ClientHandler;
|
||||
import com.codemp.intellij.jni.WorkspaceHandler;
|
||||
import com.codemp.intellij.listeners.*;
|
||||
import com.codemp.intellij.task.BufferEventAwaiterTask;
|
||||
import com.codemp.intellij.task.CursorEventAwaiterTask;
|
||||
import mp.code.intellij.listeners.WorkspaceFileOpenedListener;
|
||||
import mp.code.intellij.task.BufferEventAwaiterTask;
|
||||
import mp.code.intellij.task.CursorEventAwaiterTask;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
|
||||
|
@ -12,12 +10,17 @@ import com.intellij.openapi.fileEditor.FileOpenedSyncListener;
|
|||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import mp.code.Client;
|
||||
import mp.code.Workspace;
|
||||
import mp.code.exceptions.CodeMPException;
|
||||
import mp.code.intellij.listeners.CursorEventListener;
|
||||
import mp.code.intellij.listeners.WorkspaceFileClosedListener;
|
||||
|
||||
public class Workspace implements Disposable {
|
||||
public class IJWorkspace implements Disposable {
|
||||
public final String id;
|
||||
public final String url;
|
||||
public final boolean isRemote;
|
||||
public final WorkspaceHandler handler;
|
||||
public final Workspace handler;
|
||||
public final Project project;
|
||||
public final BufferEventAwaiterTask bufferTask;
|
||||
public final CursorEventAwaiterTask cursorTask;
|
||||
|
@ -25,11 +28,11 @@ public class Workspace implements Disposable {
|
|||
/**
|
||||
* The constructor, that will also take care of creating the tasks and listeners associated with it.
|
||||
* @param id unique id of the workspace on the server
|
||||
* @param client the {@link ClientHandler} to use
|
||||
* @param client the {@link Client} to use
|
||||
* @param isRemote whether the project is remote
|
||||
* @param project the {@link Project} to use
|
||||
*/
|
||||
public Workspace(String id, ClientHandler client, boolean isRemote, Project project) {
|
||||
public IJWorkspace(String id, Client client, boolean isRemote, Project project) throws CodeMPException {
|
||||
this.id = id;
|
||||
this.url = client.getUrl();
|
||||
this.isRemote = isRemote;
|
|
@ -1,5 +1,5 @@
|
|||
<idea-plugin>
|
||||
<id>com.codemp.intellij</id>
|
||||
<id>mp.code.intellij</id>
|
||||
<name>CodeMP</name>
|
||||
<vendor email="me@zaaarf.foo" url="https://zaaarf.foo">CodeMP</vendor>
|
||||
|
||||
|
@ -10,12 +10,12 @@
|
|||
<actions>
|
||||
<group id="codemp" text="CodeMP" popup="true">
|
||||
<add-to-group group-id="ToolsMenu" anchor="first"/>
|
||||
<action id="codemp.fast-forward" class="com.codemp.intellij.actions.FastForwardAction" text="Just Hurry"/>
|
||||
<action id="codemp.connect" class="com.codemp.intellij.actions.ConnectAction" text="Connect..."/>
|
||||
<action id="codemp.connect" class="com.codemp.intellij.actions.DisconnectAction" text="Disconnect"/>
|
||||
<action id="codemp.workspace.join" class="com.codemp.intellij.actions.workspace.WorkspaceJoinAction"
|
||||
<action id="codemp.fast-forward" class="mp.code.intellij.actions.FastForwardAction" text="Just Hurry"/>
|
||||
<action id="codemp.connect" class="mp.code.intellij.actions.ConnectAction" text="Connect..."/>
|
||||
<action id="codemp.disconnect" class="mp.code.intellij.actions.DisconnectAction" text="Disconnect"/>
|
||||
<action id="codemp.workspace.join" class="mp.code.intellij.actions.workspace.WorkspaceJoinAction"
|
||||
text="Join Workspace"/>
|
||||
<action id="codemp.workspace.leave" class="com.codemp.intellij.actions.workspace.WorkspaceLeaveAction"
|
||||
<action id="codemp.workspace.leave" class="mp.code.intellij.actions.workspace.WorkspaceLeaveAction"
|
||||
text="Leave Workspace"/>
|
||||
</group>
|
||||
</actions>
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
foreign_typemap!( //thanks @tasn on GitHub for the idea
|
||||
($p:r_type) <T> CodempResult<T> => swig_i_type!(T) {
|
||||
$out = match $p {
|
||||
Ok(x) => {
|
||||
swig_from_rust_to_i_type!(T, x, ret)
|
||||
ret
|
||||
}
|
||||
Err(err) => {
|
||||
let (msg, exception_class) = match err {
|
||||
CodempError::Filler { message } => (
|
||||
message,
|
||||
swig_jni_find_class!(CODEMP_EXCEPTION, "com/codemp/intellij/exceptions/CodeMPException")
|
||||
),
|
||||
CodempError::Transport { status, message } => (
|
||||
format!("Status {}: {}", status, message),
|
||||
swig_jni_find_class!(TRANSPORT_EXCEPTION, "com/codemp/intellij/exceptions/lib/TransportException")
|
||||
),
|
||||
CodempError::InvalidState { msg } => (
|
||||
msg, swig_jni_find_class!(INVALID_STATE_EXCEPTION, "com/codemp/intellij/exceptions/lib/InvalidStateException")
|
||||
),
|
||||
CodempError::Deadlocked => (
|
||||
"WOOT deadlocked (safe to retry)!".to_string(),
|
||||
swig_jni_find_class!(DEADLOCKED_EXCEPTION, "com/codemp/intellij/exceptions/lib/DeadlockedException")
|
||||
),
|
||||
CodempError::Channel { send } => {
|
||||
let verb = if send { "sending" } else { "reading" };
|
||||
(
|
||||
format!("Error while {} message on channel: the channel was closed!", verb),
|
||||
swig_jni_find_class!(CHANNEL_EXCEPTION, "com/codemp/intellij/exceptions/lib/ChannelException")
|
||||
)
|
||||
}
|
||||
};
|
||||
jni_throw(env, exception_class, &msg);
|
||||
return <swig_i_type!(T)>::jni_invalid_value();
|
||||
}
|
||||
};
|
||||
};
|
||||
($p:f_type, unique_prefix="/*err*/") => "/*err*/swig_f_type!(T)" "swig_foreign_from_i_type!(T, $p)";
|
||||
);
|
|
@ -1,330 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use std::time::Duration;
|
||||
use codemp::prelude::*;
|
||||
use codemp::tools;
|
||||
use lazy_static::lazy_static;
|
||||
use rifgen::rifgen_attr::{generate_access_methods, generate_interface, generate_interface_doc};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub mod glue { //rifgen generated code
|
||||
include!(concat!(env!("OUT_DIR"), "/glue.rs"));
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// the tokio runtime, since we can't easily have Java and Rust async work together
|
||||
static ref RT: tokio::runtime::Runtime = tokio::runtime::Runtime::new()
|
||||
.expect("could not start tokio runtime");
|
||||
}
|
||||
|
||||
#[generate_interface_doc]
|
||||
/// the handler class that represent an instance of a CodeMP client
|
||||
struct ClientHandler {
|
||||
client: CodempClient,
|
||||
url: String
|
||||
}
|
||||
|
||||
impl ClientHandler {
|
||||
#[generate_interface(constructor)]
|
||||
/// constructor required by flapigen, DO NOT CALL THIS
|
||||
fn new(address: &str) -> ClientHandler {
|
||||
ClientHandler {
|
||||
client: RT.block_on(CodempClient::new(address)).unwrap(),
|
||||
url: address.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// create a new workspace
|
||||
fn create_workspace(&mut self, workspace_id: &str) -> CodempResult<WorkspaceHandler> {
|
||||
RT.block_on(self.client.create_workspace(workspace_id))
|
||||
.map(|workspace| {
|
||||
Self::spawn_updater(workspace.clone());
|
||||
WorkspaceHandler { workspace }
|
||||
})
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// join a workspace by name
|
||||
fn join_workspace(&mut self, workspace_id: &str) -> CodempResult<WorkspaceHandler> {
|
||||
RT.block_on(self.client.join_workspace(workspace_id))
|
||||
.map(|workspace| {
|
||||
Self::spawn_updater(workspace.clone());
|
||||
WorkspaceHandler { workspace }
|
||||
})
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// leave a workspace
|
||||
fn leave_workspace(&mut self, workspace_id: &str) -> CodempResult<()> {
|
||||
RT.block_on(self.client.leave_workspace(workspace_id))
|
||||
}
|
||||
|
||||
fn spawn_updater(workspace: Arc<RwLock<CodempWorkspace>>) {
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_secs(60)).await;
|
||||
workspace.write().await.fetch_buffers().await.unwrap();
|
||||
workspace.write().await.fetch_users().await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// get the url you are currently connected to
|
||||
fn get_url(&self) -> String {
|
||||
self.url.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[generate_interface_doc]
|
||||
/// wraps a [codemp::workspace::Workspace] to be handled by Java
|
||||
struct WorkspaceHandler {
|
||||
workspace: Arc<RwLock<CodempWorkspace>>
|
||||
}
|
||||
|
||||
impl WorkspaceHandler {
|
||||
#[generate_interface(constructor)]
|
||||
/// constructor required by flapigen, DO NOT CALL THIS
|
||||
fn new() -> WorkspaceHandler {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// create a new buffer in current workspace
|
||||
fn create_buffer(&mut self, path: &str) -> CodempResult<BufferHandler> {
|
||||
RT.block_on(RT.block_on(self.workspace.write()).create(path))?;
|
||||
Ok(self.get_buffer(path).unwrap())
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// attach to a buffer and get a [crate::BufferHandler] for it
|
||||
fn attach_to_buffer(&mut self, path: &str) -> CodempResult<BufferHandler> {
|
||||
RT.block_on(RT.block_on(self.workspace.write()).attach(path))
|
||||
.map(|buffer| BufferHandler { buffer })
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// updates the local list of the workspace's buffers
|
||||
fn fetch_buffers(&mut self) -> CodempResult<()> {
|
||||
RT.block_on(RT.block_on(self.workspace.write()).fetch_buffers())
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// updates the local list of the workspace's users
|
||||
fn fetch_users(&mut self) -> CodempResult<()> {
|
||||
RT.block_on(RT.block_on(self.workspace.write()).fetch_users())
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// gets a list of all users in a buffer
|
||||
fn list_buffer_users(&mut self, path: &str) -> CodempResult<StringVec> {
|
||||
let mut res = StringVec::new();
|
||||
RT.block_on(RT.block_on(self.workspace.write())
|
||||
.list_buffer_users(path))?
|
||||
.iter().for_each(|u| res.push(Uuid::from(u.clone()).to_string()));
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// delete a buffer
|
||||
fn delete_buffer(&mut self, path: &str) -> CodempResult<()> {
|
||||
RT.block_on(RT.block_on(self.workspace.write()).delete(path))
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// detach from a buffer
|
||||
fn detach_from_buffer(&mut self, path: &str) -> bool {
|
||||
RT.block_on(self.workspace.write()).detach(path)
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// get the workspace id
|
||||
fn get_workspace_id(&self) -> String {
|
||||
RT.block_on(self.workspace.read()).id().clone()
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// get a [crate::CursorHandler] for the workspace's cursor
|
||||
fn get_cursor(&self) -> CursorHandler {
|
||||
CursorHandler { cursor: RT.block_on(self.workspace.read()).cursor().clone() }
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// get a [crate::BufferHandler] for one of the workspace's buffers
|
||||
fn get_buffer(&self, path: &str) -> Option<BufferHandler> {
|
||||
RT.block_on(self.workspace.read()).buffer_by_name(path)
|
||||
.map(|buffer| BufferHandler { buffer })
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// get the names of all buffers available in the workspace
|
||||
fn get_filetree(&self) -> StringVec {
|
||||
StringVec { v: RT.block_on(self.workspace.read()).filetree() }
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// polls a list of buffers, returning the first ready one
|
||||
fn select_buffer(&mut self, mut buffer_ids: StringVec, timeout: i64) -> CodempResult<Option<BufferHandler>> {
|
||||
let mut buffers = Vec::new();
|
||||
for id in buffer_ids.v.iter_mut() {
|
||||
match self.get_buffer(id.as_str()) {
|
||||
Some(buf) => buffers.push(buf.buffer),
|
||||
None => continue
|
||||
}
|
||||
}
|
||||
|
||||
let result = RT.block_on(tools::select_buffer(
|
||||
buffers.as_slice(),
|
||||
Some(Duration::from_millis(timeout as u64))
|
||||
));
|
||||
|
||||
match result {
|
||||
Err(e) => Err(e),
|
||||
Ok(buffer) =>
|
||||
Ok(buffer.map(|buffer| BufferHandler { buffer }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[generate_interface_doc]
|
||||
#[generate_access_methods]
|
||||
/// wraps a [codemp::proto::cursor::CursorEvent] to be handled by Java
|
||||
struct CursorEventWrapper {
|
||||
user: String,
|
||||
buffer: String,
|
||||
start_row: i32,
|
||||
start_col: i32,
|
||||
end_row: i32,
|
||||
end_col: i32
|
||||
}
|
||||
|
||||
|
||||
#[generate_interface_doc]
|
||||
/// a handler providing Java access to [codemp::cursor::Controller] methods
|
||||
struct CursorHandler {
|
||||
pub cursor: Arc<CodempCursorController>
|
||||
}
|
||||
|
||||
impl CursorHandler {
|
||||
#[generate_interface(constructor)]
|
||||
/// constructor required by flapigen, DO NOT CALL THIS
|
||||
fn new() -> CursorHandler {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// get next cursor event from current workspace, or block until one is available
|
||||
fn recv(&self) -> CodempResult<CursorEventWrapper> {
|
||||
match RT.block_on(self.cursor.recv()) {
|
||||
Err(err) => Err(err),
|
||||
Ok(event) => Ok(CursorEventWrapper {
|
||||
user: Uuid::from(event.user).to_string(),
|
||||
buffer: event.position.buffer.clone(),
|
||||
start_row: event.position.start.row,
|
||||
start_col: event.position.start.col,
|
||||
end_row: event.position.end.row,
|
||||
end_col: event.position.end.col
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// broadcast a cursor event
|
||||
/// will automatically fix start and end if they are accidentally inverted
|
||||
fn send(&self, buffer: String, start_row: i32, start_col: i32, end_row: i32, end_col: i32) -> CodempResult<()> {
|
||||
self.cursor.send(CodempCursorPosition {
|
||||
buffer,
|
||||
start: CodempRowCol::from((start_row, start_col)),
|
||||
end: CodempRowCol::from((end_row, end_col))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[generate_interface_doc]
|
||||
#[generate_access_methods]
|
||||
/// wraps a [codemp::api::change::TextChange] to make it accessible from Java
|
||||
struct TextChangeWrapper {
|
||||
start: usize,
|
||||
end: usize, //not inclusive
|
||||
content: String
|
||||
}
|
||||
|
||||
#[generate_interface_doc]
|
||||
/// a handler providing Java access to [codemp::buffer::Controller] methods
|
||||
struct BufferHandler {
|
||||
pub buffer: Arc<CodempBufferController>
|
||||
}
|
||||
|
||||
impl BufferHandler {
|
||||
#[generate_interface(constructor)]
|
||||
/// constructor required by flapigen, DO NOT CALL THIS
|
||||
fn new() -> BufferHandler {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// get the name of the buffer
|
||||
fn get_name(&self) -> String {
|
||||
self.buffer.name().to_string()
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// get the contents of the buffer
|
||||
fn get_content(&self) -> String {
|
||||
self.buffer.content()
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// if a text change is available on the buffer, return it immediately
|
||||
fn try_recv(&self) -> CodempResult<Option<TextChangeWrapper>> {
|
||||
match self.buffer.try_recv() {
|
||||
Err(err) => Err(err),
|
||||
Ok(None) => Ok(None),
|
||||
Ok(Some(change)) => Ok(Some(TextChangeWrapper {
|
||||
start: change.span.start,
|
||||
end: change.span.end,
|
||||
content: change.content.clone()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// broadcast a text change on the buffer
|
||||
fn send(&self, start_offset: usize, end_offset: usize, content: String) -> CodempResult<()> {
|
||||
self.buffer.send(CodempTextChange { span: start_offset..end_offset, content })
|
||||
}
|
||||
}
|
||||
|
||||
#[generate_interface_doc]
|
||||
/// a convenience struct allowing Java access to a Rust vector
|
||||
struct StringVec { //jni moment
|
||||
v: Vec<String>
|
||||
}
|
||||
|
||||
impl StringVec {
|
||||
#[generate_interface(constructor)]
|
||||
/// initialize an empty vector
|
||||
fn new() -> StringVec {
|
||||
Self { v: Vec::new() }
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// push a new value onto the vector
|
||||
fn push(&mut self, s: String) {
|
||||
self.v.push(s);
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// get the length of the underlying vector
|
||||
fn length(&self) -> i64 {
|
||||
self.v.len() as i64
|
||||
}
|
||||
|
||||
#[generate_interface]
|
||||
/// access the element at a given index
|
||||
fn get(&self, idx: i64) -> Option<String> {
|
||||
let elem: Option<&String> = self.v.get(idx as usize);
|
||||
elem.map(|s| s.clone())
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue