mirror of
https://github.com/hexedtech/codemp-intellij.git
synced 2024-11-21 22:54:48 +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 'java'
|
||||||
id 'org.jetbrains.intellij' version '1.16.0'
|
id 'org.jetbrains.intellij' version '1.16.0'
|
||||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||||
|
id 'com.palantir.git-version' version '3.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
group = 'com.codemp'
|
group = 'mp.code'
|
||||||
version = '0.1.0'
|
//version = versionDetails().lastTag
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
@ -17,9 +18,9 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.github.adamheinrich:native-utils:master-SNAPSHOT'
|
|
||||||
implementation 'org.slf4j:slf4j-api:2.0.9'
|
implementation 'org.slf4j:slf4j-api:2.0.9'
|
||||||
implementation 'ch.qos.logback:logback-classic:1.4.12'
|
implementation 'ch.qos.logback:logback-classic:1.4.12'
|
||||||
|
implementation files('../../lib/dist/java/build/libs/codemp-0.6.2.jar')
|
||||||
}
|
}
|
||||||
|
|
||||||
intellij {
|
intellij {
|
||||||
|
@ -30,24 +31,14 @@ intellij {
|
||||||
shadowJar {
|
shadowJar {
|
||||||
archiveClassifier.set('')
|
archiveClassifier.set('')
|
||||||
dependencies {
|
dependencies {
|
||||||
include(dependency('com.github.adamheinrich:native-utils:master-SNAPSHOT'))
|
include(dependency(files('../../lib/java/build/libs/codemp-6645c46.jar')))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def cargoDir = projectDir.toPath().resolve('target').resolve('release').toFile()
|
|
||||||
|
|
||||||
processResources {
|
|
||||||
from(cargoDir) {
|
|
||||||
include('*.dll')
|
|
||||||
include('*.so')
|
|
||||||
into('natives/')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
patchPluginXml {
|
patchPluginXml {
|
||||||
sinceBuild.set('222')
|
sinceBuild.set('222')
|
||||||
untilBuild.set('232.*')
|
untilBuild.set('233.*')
|
||||||
}
|
}
|
||||||
|
|
||||||
signPlugin {
|
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
|
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 mp.code.intellij.CodeMP;
|
||||||
import com.codemp.intellij.util.ActionUtil;
|
import mp.code.intellij.util.ActionUtil;
|
||||||
import com.intellij.openapi.actionSystem.AnAction;
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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 mp.code.intellij.CodeMP;
|
||||||
import com.codemp.intellij.util.ActionUtil;
|
import mp.code.intellij.util.ActionUtil;
|
||||||
import com.codemp.intellij.workspace.Workspace;
|
import mp.code.intellij.workspace.IJWorkspace;
|
||||||
import com.intellij.openapi.actionSystem.AnAction;
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
import com.intellij.openapi.ui.Messages;
|
import com.intellij.openapi.ui.Messages;
|
||||||
|
import mp.code.exceptions.CodeMPException;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class WorkspaceJoinAction extends AnAction {
|
public class WorkspaceJoinAction extends AnAction {
|
||||||
public static void join(AnActionEvent e, String workspaceId, boolean silent) {
|
public static void join(AnActionEvent e, String workspaceId, boolean silent) throws CodeMPException {
|
||||||
CodeMP.ACTIVE_WORKSPACES.put(workspaceId, new Workspace(
|
CodeMP.ACTIVE_WORKSPACES.put(workspaceId, new IJWorkspace(
|
||||||
workspaceId, CodeMP.getClient("join workspace"),
|
workspaceId, CodeMP.getClient("join workspace"),
|
||||||
false, e.getProject() //TODO: implement remote projects
|
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 mp.code.intellij.CodeMP;
|
||||||
import com.codemp.intellij.util.ActionUtil;
|
import mp.code.intellij.util.ActionUtil;
|
||||||
import com.intellij.openapi.actionSystem.AnAction;
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
import com.intellij.openapi.ui.Messages;
|
import com.intellij.openapi.ui.Messages;
|
||||||
|
@ -10,8 +10,8 @@ import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class WorkspaceLeaveAction extends AnAction {
|
public class WorkspaceLeaveAction extends AnAction {
|
||||||
public static void leave(AnActionEvent e, String workspaceId, boolean silent) {
|
public static void leave(AnActionEvent e, String workspaceId, boolean silent) {
|
||||||
CodeMP.getClient("leave workspace")
|
//CodeMP.getClient("leave workspace").leaveWorkspace(workspaceId);
|
||||||
.leaveWorkspace(workspaceId);
|
// TODO
|
||||||
Disposer.dispose(CodeMP.ACTIVE_WORKSPACES.remove(workspaceId));
|
Disposer.dispose(CodeMP.ACTIVE_WORKSPACES.remove(workspaceId));
|
||||||
|
|
||||||
if(!silent) ActionUtil.notify(e, "Success", String.format("Left workspace %s!", 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.
|
* Thrown upon failure to detach from a buffer.
|
||||||
*/
|
*/
|
||||||
public class BufferDetachException extends CodeMPException {
|
public class BufferDetachException extends CodeMPIJException {
|
||||||
|
|
||||||
public BufferDetachException(String name) {
|
public BufferDetachException(String name) {
|
||||||
super(String.format("Could not detach from buffer named \"%s\"!", 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
|
* Fired when trying to access the CodeMP client without first connecting
|
||||||
* to a server.
|
* to a server.
|
||||||
*/
|
*/
|
||||||
public class NotConnectedException extends CodeMPException {
|
public class NotConnectedException extends CodeMPIJException {
|
||||||
|
|
||||||
public NotConnectedException(String service) {
|
public NotConnectedException(String service) {
|
||||||
super(String.format("Failed to %s, you are not connected to a server!", 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 mp.code.intellij.CodeMP;
|
||||||
import com.codemp.intellij.exceptions.CodeMPException;
|
|
||||||
import com.codemp.intellij.jni.BufferHandler;
|
|
||||||
import com.intellij.openapi.command.CommandProcessor;
|
import com.intellij.openapi.command.CommandProcessor;
|
||||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||||
import com.intellij.openapi.editor.event.DocumentListener;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class BufferEventListener implements DocumentListener {
|
public class BufferEventListener implements DocumentListener {
|
||||||
|
|
||||||
private final BufferHandler bufferHandler;
|
private final BufferController controller;
|
||||||
|
|
||||||
public BufferEventListener(BufferHandler bufferHandler) {
|
public BufferEventListener(BufferController controller) {
|
||||||
this.bufferHandler = bufferHandler;
|
this.controller = controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void documentChanged(@NotNull DocumentEvent event) throws CodeMPException {
|
public void documentChanged(@NotNull DocumentEvent event) {
|
||||||
CodeMP.LOGGER.debug("Changed {} to {} at offset {}",
|
CodeMP.LOGGER.debug("Changed {} to {} at offset {}",
|
||||||
event.getOldFragment(), event.getNewFragment(), event.getOffset());
|
event.getOldFragment(), event.getNewFragment(), event.getOffset());
|
||||||
|
|
||||||
|
@ -29,8 +32,15 @@ public class BufferEventListener implements DocumentListener {
|
||||||
//TODO move actions break
|
//TODO move actions break
|
||||||
int changeOffset = event.getOffset();
|
int changeOffset = event.getOffset();
|
||||||
CharSequence newFragment = event.getNewFragment();
|
CharSequence newFragment = event.getNewFragment();
|
||||||
this.bufferHandler.send(changeOffset,
|
try {
|
||||||
|
this.controller.send(new TextChange(
|
||||||
|
changeOffset,
|
||||||
changeOffset + event.getOldFragment().length(),
|
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 mp.code.intellij.CodeMP;
|
||||||
import com.codemp.intellij.jni.CursorHandler;
|
import mp.code.intellij.util.FileUtil;
|
||||||
import com.codemp.intellij.util.FileUtil;
|
|
||||||
import com.intellij.openapi.editor.Caret;
|
import com.intellij.openapi.editor.Caret;
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
import com.intellij.openapi.editor.VisualPosition;
|
import com.intellij.openapi.editor.VisualPosition;
|
||||||
import com.intellij.openapi.editor.event.CaretEvent;
|
import com.intellij.openapi.editor.event.CaretEvent;
|
||||||
import com.intellij.openapi.editor.event.CaretListener;
|
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;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class CursorEventListener implements CaretListener {
|
public class CursorEventListener implements CaretListener {
|
||||||
|
|
||||||
private final CursorHandler handler;
|
private final CursorController controller;
|
||||||
|
|
||||||
public CursorEventListener(CursorHandler handler) {
|
public CursorEventListener(CursorController controller) {
|
||||||
this.handler = handler;
|
this.controller = controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -27,13 +29,21 @@ public class CursorEventListener implements CaretListener {
|
||||||
VisualPosition startPos = caret.getSelectionStartPosition();
|
VisualPosition startPos = caret.getSelectionStartPosition();
|
||||||
VisualPosition endPos = caret.getSelectionEndPosition();
|
VisualPosition endPos = caret.getSelectionEndPosition();
|
||||||
CodeMP.LOGGER.debug("Caret moved from {}x {}y to {}x {}y",
|
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();
|
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()),
|
FileUtil.getRelativePath(editor.getProject(), editor.getVirtualFile()),
|
||||||
startPos.line, startPos.column,
|
null
|
||||||
endPos.line, endPos.column
|
));
|
||||||
);
|
} 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 mp.code.intellij.task.BufferEventAwaiterTask;
|
||||||
import com.codemp.intellij.task.BufferEventAwaiterTask;
|
import mp.code.intellij.util.FileUtil;
|
||||||
import com.codemp.intellij.util.FileUtil;
|
|
||||||
import com.intellij.openapi.Disposable;
|
import com.intellij.openapi.Disposable;
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
|
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
|
||||||
import com.intellij.openapi.util.Disposer;
|
import com.intellij.openapi.util.Disposer;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
import mp.code.Workspace;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class WorkspaceFileClosedListener implements FileEditorManagerListener.Before {
|
public class WorkspaceFileClosedListener implements FileEditorManagerListener.Before {
|
||||||
private final WorkspaceHandler handler;
|
private final Workspace handler;
|
||||||
private final BufferEventAwaiterTask task;
|
private final BufferEventAwaiterTask task;
|
||||||
|
|
||||||
public WorkspaceFileClosedListener(WorkspaceHandler handler, BufferEventAwaiterTask task) {
|
public WorkspaceFileClosedListener(Workspace handler, BufferEventAwaiterTask task) {
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.task = task;
|
this.task = task;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ public class WorkspaceFileClosedListener implements FileEditorManagerListener.Be
|
||||||
Disposable disp = this.task.activeBuffers.remove(path);
|
Disposable disp = this.task.activeBuffers.remove(path);
|
||||||
if(disp == null) return;
|
if(disp == null) return;
|
||||||
|
|
||||||
this.handler.detachFromBuffer(path);
|
// TODO : this.handler.detachFromBuffer(path);
|
||||||
Disposer.dispose(disp);
|
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 mp.code.intellij.task.BufferEventAwaiterTask;
|
||||||
import com.codemp.intellij.jni.BufferHandler;
|
import mp.code.intellij.util.FileUtil;
|
||||||
import com.codemp.intellij.jni.WorkspaceHandler;
|
|
||||||
import com.codemp.intellij.task.BufferEventAwaiterTask;
|
|
||||||
import com.codemp.intellij.util.FileUtil;
|
|
||||||
import com.intellij.openapi.Disposable;
|
import com.intellij.openapi.Disposable;
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||||
import com.intellij.openapi.fileEditor.FileOpenedSyncListener;
|
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.fileEditor.ex.FileEditorWithProvider;
|
||||||
import com.intellij.openapi.util.Disposer;
|
import com.intellij.openapi.util.Disposer;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class WorkspaceFileOpenedListener implements FileOpenedSyncListener {
|
public class WorkspaceFileOpenedListener implements FileOpenedSyncListener {
|
||||||
private final WorkspaceHandler handler;
|
private final Workspace handler;
|
||||||
private final BufferEventAwaiterTask task;
|
private final BufferEventAwaiterTask task;
|
||||||
|
|
||||||
public WorkspaceFileOpenedListener(WorkspaceHandler handler, BufferEventAwaiterTask task) {
|
public WorkspaceFileOpenedListener(Workspace handler, BufferEventAwaiterTask task) {
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.task = task;
|
this.task = task;
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,9 @@ public class WorkspaceFileOpenedListener implements FileOpenedSyncListener {
|
||||||
String path = FileUtil.getRelativePath(editor.getProject(), file);
|
String path = FileUtil.getRelativePath(editor.getProject(), file);
|
||||||
if(path == null) return;
|
if(path == null) return;
|
||||||
|
|
||||||
BufferHandler bufferHandler = this.getBufferForPath(path);
|
BufferController bufferController = this.getBufferForPath(path);
|
||||||
Disposable disp = Disposer.newDisposable(String.format("codemp-buffer-%s", 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
|
editor.getDocument().setText(""); //empty it so we can start receiving
|
||||||
this.task.activeBuffers.put(path, disp);
|
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.
|
* 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)
|
* @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 {
|
try {
|
||||||
return this.handler.attachToBuffer(path);
|
return this.handler.attachToBuffer(path);
|
||||||
} catch (TransportException ignored) {
|
} catch (CodeMPException ignored) {
|
||||||
|
try {
|
||||||
return this.handler.createBuffer(path);
|
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 mp.code.intellij.CodeMP;
|
||||||
import com.codemp.intellij.exceptions.lib.DeadlockedException;
|
import mp.code.intellij.util.FileUtil;
|
||||||
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 com.intellij.openapi.Disposable;
|
import com.intellij.openapi.Disposable;
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
import com.intellij.openapi.command.CommandProcessor;
|
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.ProgressIndicator;
|
||||||
import com.intellij.openapi.progress.Task;
|
import com.intellij.openapi.progress.Task;
|
||||||
import com.intellij.openapi.project.Project;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -24,8 +24,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class BufferEventAwaiterTask extends Task.Backgroundable implements Disposable {
|
public class BufferEventAwaiterTask extends Task.Backgroundable implements Disposable {
|
||||||
public final Map<String, Disposable> activeBuffers;
|
public final Map<String, Disposable> activeBuffers;
|
||||||
private final WorkspaceHandler handler;
|
private final Workspace handler;
|
||||||
public BufferEventAwaiterTask(@NotNull Project project, @NotNull WorkspaceHandler handler) {
|
public BufferEventAwaiterTask(@NotNull Project project, @NotNull Workspace handler) {
|
||||||
super(project, "Awaiting CodeMP buffer events", false);
|
super(project, "Awaiting CodeMP buffer events", false);
|
||||||
this.activeBuffers = new ConcurrentHashMap<>();
|
this.activeBuffers = new ConcurrentHashMap<>();
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
|
@ -35,29 +35,33 @@ public class BufferEventAwaiterTask extends Task.Backgroundable implements Dispo
|
||||||
@SuppressWarnings("InfiniteLoopStatement")
|
@SuppressWarnings("InfiniteLoopStatement")
|
||||||
public void run(@NotNull ProgressIndicator indicator) {
|
public void run(@NotNull ProgressIndicator indicator) {
|
||||||
while(true) {
|
while(true) {
|
||||||
StringVec buffers = new StringVec(); //jni moment
|
Optional<BufferController> bufferOptional;
|
||||||
this.activeBuffers.keySet().forEach(buffers::push);
|
try {
|
||||||
|
bufferOptional = this.handler.selectBuffer(100L);
|
||||||
Optional<BufferHandler> bufferOptional = this.handler.selectBuffer(buffers, 100L);
|
} catch(CodeMPException e) {
|
||||||
|
bufferOptional = Optional.empty(); // TODO error handling
|
||||||
|
}
|
||||||
if(bufferOptional.isEmpty())
|
if(bufferOptional.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
BufferHandler buffer = bufferOptional.get();
|
BufferController buffer = bufferOptional.get();
|
||||||
|
|
||||||
List<TextChangeWrapper> changeList = new ArrayList<>();
|
List<TextChange> changeList = new ArrayList<>();
|
||||||
while(true) {
|
while(true) {
|
||||||
Optional<TextChangeWrapper> changeOptional;
|
Optional<TextChange> changeOptional;
|
||||||
try {
|
try {
|
||||||
changeOptional = buffer.tryRecv();
|
changeOptional = buffer.tryRecv();
|
||||||
} catch(DeadlockedException e) {
|
} catch(DeadlockedException e) {
|
||||||
CodeMP.LOGGER.error(e.getMessage());
|
CodeMP.LOGGER.error(e.getMessage());
|
||||||
continue;
|
continue;
|
||||||
|
} catch(CodeMPException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(changeOptional.isEmpty())
|
if(changeOptional.isEmpty())
|
||||||
break;
|
break;
|
||||||
TextChangeWrapper change = changeOptional.get();
|
TextChange change = changeOptional.get();
|
||||||
CodeMP.LOGGER.debug("Received text change {} from offset {} to {}!",
|
CodeMP.LOGGER.debug("Received text change {} from offset {} to {}!",
|
||||||
change.getContent(), change.getStart(), change.getEnd());
|
change.content, change.start, change.end);
|
||||||
changeList.add(change);
|
changeList.add(change);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +72,7 @@ public class BufferEventAwaiterTask extends Task.Backgroundable implements Dispo
|
||||||
this.myProject,
|
this.myProject,
|
||||||
() -> changeList.forEach((change) ->
|
() -> changeList.forEach((change) ->
|
||||||
bufferEditor.getDocument().replaceString(
|
bufferEditor.getDocument().replaceString(
|
||||||
(int) change.getStart(), (int) change.getEnd(), change.getContent())
|
(int) change.start, (int) change.end, change.content)
|
||||||
),
|
),
|
||||||
"CodeMPBufferReceive",
|
"CodeMPBufferReceive",
|
||||||
"codemp-buffer-receive", //TODO: mark this with the name
|
"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 mp.code.intellij.CodeMP;
|
||||||
import com.codemp.intellij.jni.CursorEventWrapper;
|
import mp.code.intellij.util.ColorUtil;
|
||||||
import com.codemp.intellij.jni.CursorHandler;
|
import mp.code.intellij.util.FileUtil;
|
||||||
import com.codemp.intellij.util.ColorUtil;
|
|
||||||
import com.codemp.intellij.util.FileUtil;
|
|
||||||
import com.intellij.openapi.Disposable;
|
import com.intellij.openapi.Disposable;
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
import com.intellij.openapi.editor.Editor;
|
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.ProgressIndicator;
|
||||||
import com.intellij.openapi.progress.Task;
|
import com.intellij.openapi.progress.Task;
|
||||||
import com.intellij.openapi.project.Project;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.awt.*;
|
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
|
//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
|
//implements disposable so i can use it as lifetime ig
|
||||||
public class CursorEventAwaiterTask extends Task.Backgroundable implements Disposable {
|
public class CursorEventAwaiterTask extends Task.Backgroundable implements Disposable {
|
||||||
private final CursorHandler handler;
|
private final CursorController handler;
|
||||||
private final Map<String, RangeHighlighter> highlighterMap = new ConcurrentHashMap<>();
|
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);
|
super(project, "Awaiting CodeMP cursor events", false);
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
}
|
}
|
||||||
|
@ -37,23 +38,29 @@ public class CursorEventAwaiterTask extends Task.Backgroundable implements Dispo
|
||||||
@SuppressWarnings("InfiniteLoopStatement")
|
@SuppressWarnings("InfiniteLoopStatement")
|
||||||
public void run(@NotNull ProgressIndicator indicator) {
|
public void run(@NotNull ProgressIndicator indicator) {
|
||||||
while(true) {
|
while(true) {
|
||||||
CursorEventWrapper event = this.handler.recv();
|
Cursor event;
|
||||||
Editor editor = FileUtil.getActiveEditorByPath(this.myProject, event.getBuffer());
|
try {
|
||||||
|
event = this.handler.recv();
|
||||||
|
} catch(CodeMPException ex) {
|
||||||
|
continue; // TODO proper handling
|
||||||
|
}
|
||||||
|
Editor editor = FileUtil.getActiveEditorByPath(this.myProject, event.buffer);
|
||||||
if(editor == null)
|
if(editor == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CodeMP.LOGGER.debug(
|
CodeMP.LOGGER.debug(
|
||||||
"Cursor moved by user {}! Start pos: {}x {}y; end pos: {}x {}y in buffer {}!",
|
"Cursor moved by user {}! Start pos: {}x {}y; end pos: {}x {}y in buffer {}!",
|
||||||
event.getUser(),
|
event.user,
|
||||||
event.getStartCol(), event.getStartCol(),
|
event.startCol, event.startRow,
|
||||||
event.getEndRow(), event.getEndCol(),
|
event.endCol, event.endRow,
|
||||||
event.getBuffer());
|
event.buffer
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int startOffset = editor.getDocument()
|
int startOffset = editor.getDocument()
|
||||||
.getLineStartOffset(event.getStartRow()) + event.getStartCol();
|
.getLineStartOffset(event.startRow) + event.startCol;
|
||||||
int endOffset = editor.getDocument()
|
int endOffset = editor.getDocument()
|
||||||
.getLineStartOffset(event.getEndRow()) + event.getEndCol();
|
.getLineStartOffset(event.startRow) + event.startCol;
|
||||||
|
|
||||||
ApplicationManager.getApplication().invokeLater(() -> {
|
ApplicationManager.getApplication().invokeLater(() -> {
|
||||||
int documentLength = editor.getDocument().getTextLength();
|
int documentLength = editor.getDocument().getTextLength();
|
||||||
|
@ -64,7 +71,7 @@ public class CursorEventAwaiterTask extends Task.Backgroundable implements Dispo
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeHighlighter previous = this.highlighterMap.put(event.getUser(), editor
|
RangeHighlighter previous = this.highlighterMap.put(event.user, editor
|
||||||
.getMarkupModel()
|
.getMarkupModel()
|
||||||
.addRangeHighlighter(
|
.addRangeHighlighter(
|
||||||
startOffset,
|
startOffset,
|
||||||
|
@ -72,7 +79,7 @@ public class CursorEventAwaiterTask extends Task.Backgroundable implements Dispo
|
||||||
HighlighterLayer.SELECTION,
|
HighlighterLayer.SELECTION,
|
||||||
new TextAttributes(
|
new TextAttributes(
|
||||||
null,
|
null,
|
||||||
ColorUtil.hashColor(event.getUser()),
|
ColorUtil.hashColor(event.user),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Font.PLAIN
|
Font.PLAIN
|
|
@ -1,7 +1,7 @@
|
||||||
package com.codemp.intellij.util;
|
package mp.code.intellij.util;
|
||||||
|
|
||||||
import com.codemp.intellij.CodeMP;
|
import mp.code.intellij.CodeMP;
|
||||||
import com.codemp.intellij.exceptions.ide.BadActionEventStateException;
|
import mp.code.intellij.exceptions.ide.BadActionEventStateException;
|
||||||
import com.intellij.notification.Notification;
|
import com.intellij.notification.Notification;
|
||||||
import com.intellij.notification.NotificationType;
|
import com.intellij.notification.NotificationType;
|
||||||
import com.intellij.notification.Notifications;
|
import com.intellij.notification.Notifications;
|
|
@ -1,4 +1,4 @@
|
||||||
package com.codemp.intellij.util;
|
package mp.code.intellij.util;
|
||||||
|
|
||||||
import com.intellij.ui.JBColor;
|
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.editor.Editor;
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
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 mp.code.intellij.listeners.WorkspaceFileOpenedListener;
|
||||||
import com.codemp.intellij.jni.WorkspaceHandler;
|
import mp.code.intellij.task.BufferEventAwaiterTask;
|
||||||
import com.codemp.intellij.listeners.*;
|
import mp.code.intellij.task.CursorEventAwaiterTask;
|
||||||
import com.codemp.intellij.task.BufferEventAwaiterTask;
|
|
||||||
import com.codemp.intellij.task.CursorEventAwaiterTask;
|
|
||||||
import com.intellij.openapi.Disposable;
|
import com.intellij.openapi.Disposable;
|
||||||
import com.intellij.openapi.editor.EditorFactory;
|
import com.intellij.openapi.editor.EditorFactory;
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
|
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.progress.ProgressManager;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.util.messages.MessageBusConnection;
|
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 id;
|
||||||
public final String url;
|
public final String url;
|
||||||
public final boolean isRemote;
|
public final boolean isRemote;
|
||||||
public final WorkspaceHandler handler;
|
public final Workspace handler;
|
||||||
public final Project project;
|
public final Project project;
|
||||||
public final BufferEventAwaiterTask bufferTask;
|
public final BufferEventAwaiterTask bufferTask;
|
||||||
public final CursorEventAwaiterTask cursorTask;
|
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.
|
* 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 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 isRemote whether the project is remote
|
||||||
* @param project the {@link Project} to use
|
* @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.id = id;
|
||||||
this.url = client.getUrl();
|
this.url = client.getUrl();
|
||||||
this.isRemote = isRemote;
|
this.isRemote = isRemote;
|
|
@ -1,5 +1,5 @@
|
||||||
<idea-plugin>
|
<idea-plugin>
|
||||||
<id>com.codemp.intellij</id>
|
<id>mp.code.intellij</id>
|
||||||
<name>CodeMP</name>
|
<name>CodeMP</name>
|
||||||
<vendor email="me@zaaarf.foo" url="https://zaaarf.foo">CodeMP</vendor>
|
<vendor email="me@zaaarf.foo" url="https://zaaarf.foo">CodeMP</vendor>
|
||||||
|
|
||||||
|
@ -10,12 +10,12 @@
|
||||||
<actions>
|
<actions>
|
||||||
<group id="codemp" text="CodeMP" popup="true">
|
<group id="codemp" text="CodeMP" popup="true">
|
||||||
<add-to-group group-id="ToolsMenu" anchor="first"/>
|
<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.fast-forward" class="mp.code.intellij.actions.FastForwardAction" text="Just Hurry"/>
|
||||||
<action id="codemp.connect" class="com.codemp.intellij.actions.ConnectAction" text="Connect..."/>
|
<action id="codemp.connect" class="mp.code.intellij.actions.ConnectAction" text="Connect..."/>
|
||||||
<action id="codemp.connect" class="com.codemp.intellij.actions.DisconnectAction" text="Disconnect"/>
|
<action id="codemp.disconnect" class="mp.code.intellij.actions.DisconnectAction" text="Disconnect"/>
|
||||||
<action id="codemp.workspace.join" class="com.codemp.intellij.actions.workspace.WorkspaceJoinAction"
|
<action id="codemp.workspace.join" class="mp.code.intellij.actions.workspace.WorkspaceJoinAction"
|
||||||
text="Join Workspace"/>
|
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"/>
|
text="Leave Workspace"/>
|
||||||
</group>
|
</group>
|
||||||
</actions>
|
</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