mirror of
https://github.com/hexedtech/codemp.git
synced 2024-11-24 16:14:48 +01:00
commit
d3e16a2bb2
10 changed files with 157 additions and 27 deletions
28
.github/workflows/java.yml
vendored
28
.github/workflows/java.yml
vendored
|
@ -23,26 +23,39 @@ jobs:
|
||||||
filename: codemp.dll
|
filename: codemp.dll
|
||||||
- runner: macos-14
|
- runner: macos-14
|
||||||
target: darwin-arm64
|
target: darwin-arm64
|
||||||
filename: codemp.dylib
|
filename: libcodemp.dylib
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: arduino/setup-protoc@v3
|
- uses: arduino/setup-protoc@v3
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- run: cargo build --release --features=java
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: codemp-java-${{ matrix.platform.target }}
|
||||||
|
path: target/release/${{ matrix.platform.filename }}
|
||||||
|
|
||||||
|
publish:
|
||||||
|
needs: [build]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: dist/java/artifacts
|
||||||
|
pattern: codemp-java-*
|
||||||
|
merge-multiple: true
|
||||||
|
- run: tree
|
||||||
|
working-directory: dist/java
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: '11'
|
java-version: '11'
|
||||||
- uses: gradle/actions/setup-gradle@v4
|
- uses: gradle/actions/setup-gradle@v4
|
||||||
with:
|
with:
|
||||||
gradle-version: "8.10" # Quotes required to prevent YAML converting to number
|
gradle-version: "8.10"
|
||||||
- run: gradle build
|
|
||||||
working-directory: dist/java
|
working-directory: dist/java
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: codemp-java-${{ matrix.platform.target }}
|
|
||||||
path: dist/java/build/libs
|
|
||||||
- run: gradle publish
|
- run: gradle publish
|
||||||
working-directory: dist/java
|
working-directory: dist/java
|
||||||
env:
|
env:
|
||||||
|
@ -50,3 +63,4 @@ jobs:
|
||||||
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
|
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
|
||||||
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MAVEN_CENTRAL_GPG_SECRET_KEY }}
|
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MAVEN_CENTRAL_GPG_SECRET_KEY }}
|
||||||
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MAVEN_CENTRAL_GPG_PASSWORD }}
|
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MAVEN_CENTRAL_GPG_PASSWORD }}
|
||||||
|
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -28,6 +28,7 @@ dist/java/.gradle/
|
||||||
dist/java/.project
|
dist/java/.project
|
||||||
dist/java/.settings/
|
dist/java/.settings/
|
||||||
dist/java/bin/
|
dist/java/bin/
|
||||||
|
dist/java/artifacts/
|
||||||
|
|
||||||
# intellij insists on creating the wrapper every time even if it's not strictly necessary
|
# intellij insists on creating the wrapper every time even if it's not strictly necessary
|
||||||
dist/java/gradle/
|
dist/java/gradle/
|
||||||
|
|
16
dist/README.md
vendored
16
dist/README.md
vendored
|
@ -33,14 +33,20 @@ Thus, we also provide pre-made Java glue code, wrapping all native calls and def
|
||||||
|
|
||||||
The Java bindings have no known major quirk. However, here are a list of facts that are useful to know when developing with these:
|
The Java bindings have no known major quirk. However, here are a list of facts that are useful to know when developing with these:
|
||||||
|
|
||||||
* Memory management is entirely delegated to the JVM's garbage collector.
|
* Memory management is entirely delegated to the JVM's garbage collector using the `Cleaner` API.
|
||||||
* A more elegant solution than `Object.finalize()`, who is deprecated in newer Java versions, may be coming eventually.
|
* Because of this, we require Java 11 as minimum version: `Cleaner` was added in version 9. This should not be a problem, as IDEs tend to run on recent versions, but if there is actual demand for it we may add a Java 8-friendly version using `Object.finalize()` (which is deprecated in modern JDKs).
|
||||||
* Exceptions coming from the native side have generally been made checked to imitate Rust's philosophy with `Result`.
|
* Exceptions coming from the native side have generally been made checked to imitate Rust's philosophy with `Result`.
|
||||||
* `JNIException`s are however unchecked: there is nothing you can do to recover from them, as they usually represent a severe error in the glue code. If they arise, it's probably a bug.
|
* `JNIException`s are however unchecked: there is nothing you can do to recover from them, as they usually represent a severe error in the glue code. If they arise, it's probably a bug.
|
||||||
|
|
||||||
### Using
|
### Using
|
||||||
`codemp` **will be available soon** as an artifact on [Maven Central](https://mvnrepository.com)
|
`codemp` is available on [Maven Central](https://central.sonatype.com/artifact/mp.code/codemp), with each officially supported OS as an archive classifier.
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
This is a [Gradle](https://gradle.org/) project: building requires having both Gradle and Cargo installed, as well as the JDK (any non-abandoned version).
|
> [!NOTE]
|
||||||
Once you have all the requirements, building is as simple as running `gradle build`: the output is going to be a JAR under `build/libs`, which you can import into your classpath with your IDE of choice.
|
> The following instructions assume `dist/java` as current working directory.
|
||||||
|
|
||||||
|
This is a [Gradle](https://gradle.org/) project, so you must have install `gradle` (as well as JDK 11 or higher) in order to build it.
|
||||||
|
- You can build a JAR without bundling the native library with `gradle build`.
|
||||||
|
- Otherwise, you can compile the project for your current OS and create a JAR that bundles the resulting binary with `gradle nativeBuild`; do note that this second way of building also requires Cargo and the relevant Rust toolchain.
|
||||||
|
|
||||||
|
In both cases, the output is going to be a JAR under `build/libs`, which you can import into your classpath with your IDE of choice.
|
||||||
|
|
116
dist/java/build.gradle
vendored
116
dist/java/build.gradle
vendored
|
@ -1,14 +1,104 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'java-library'
|
id 'java-library'
|
||||||
id "com.vanniktech.maven.publish" version "0.29.0"
|
id "com.vanniktech.maven.publish.base" version "0.30.0"
|
||||||
id 'com.google.osdetector' version '1.7.3'
|
id 'com.google.osdetector' version '1.7.3'
|
||||||
}
|
}
|
||||||
|
|
||||||
group = 'mp.code'
|
group = 'mp.code'
|
||||||
version = '0.7.3'
|
version = '0.7.3'
|
||||||
|
|
||||||
|
tasks.register('windowsJar', Jar) {
|
||||||
|
outputs.upToDateWhen { false }
|
||||||
|
archiveClassifier = 'windows-x86_64'
|
||||||
|
from sourceSets.main.runtimeClasspath
|
||||||
|
from('artifacts') {
|
||||||
|
include('*.dll')
|
||||||
|
into('natives/')
|
||||||
|
}
|
||||||
|
doFirst {
|
||||||
|
if(!(new File('artifacts/codemp.dll').exists())) {
|
||||||
|
throw new GradleException("The required file does not exist!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('macosJar', Jar) {
|
||||||
|
outputs.upToDateWhen { false }
|
||||||
|
archiveClassifier = 'osx-aarch_64'
|
||||||
|
from sourceSets.main.runtimeClasspath
|
||||||
|
from('artifacts') {
|
||||||
|
include('*.dylib')
|
||||||
|
into('natives/')
|
||||||
|
}
|
||||||
|
doFirst {
|
||||||
|
if(!(new File('artifacts/libcodemp.dylib').exists())) {
|
||||||
|
throw new GradleException("The required file does not exist!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('linuxJar', Jar) {
|
||||||
|
outputs.upToDateWhen { false }
|
||||||
|
archiveClassifier = 'linux-x86_64'
|
||||||
|
from sourceSets.main.runtimeClasspath
|
||||||
|
from('artifacts') {
|
||||||
|
include('*.so')
|
||||||
|
into('natives/')
|
||||||
|
}
|
||||||
|
doFirst {
|
||||||
|
if(!(new File('artifacts/libcodemp.so').exists())) {
|
||||||
|
throw new GradleException("The required file does not exist! Maybe you need to `cargo build` the main library first?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('multiplatformJar', Jar) {
|
||||||
|
outputs.upToDateWhen { false }
|
||||||
|
archiveClassifier = 'all'
|
||||||
|
from sourceSets.main.runtimeClasspath
|
||||||
|
from('artifacts') {
|
||||||
|
include('*')
|
||||||
|
into('natives/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
windowsJar {
|
||||||
|
canBeConsumed = true
|
||||||
|
canBeResolved = false
|
||||||
|
extendsFrom implementation, runtimeOnly
|
||||||
|
}
|
||||||
|
linuxJar {
|
||||||
|
canBeConsumed = true
|
||||||
|
canBeResolved = false
|
||||||
|
extendsFrom implementation, runtimeOnly
|
||||||
|
}
|
||||||
|
macosJar {
|
||||||
|
canBeConsumed = true
|
||||||
|
canBeResolved = false
|
||||||
|
extendsFrom implementation, runtimeOnly
|
||||||
|
}
|
||||||
|
multiplatformJar {
|
||||||
|
canBeConsumed = true
|
||||||
|
canBeResolved = false
|
||||||
|
extendsFrom implementation, runtimeOnly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
withSourcesJar()
|
||||||
|
withJavadocJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
archives jar
|
||||||
|
archives sourcesJar
|
||||||
|
archives javadocJar
|
||||||
|
windowsJar(windowsJar)
|
||||||
|
macosJar(macosJar)
|
||||||
|
linuxJar(linuxJar)
|
||||||
|
multiplatformJar(multiplatformJar)
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -29,17 +119,17 @@ tasks.register('cargoBuild', Exec) {
|
||||||
commandLine 'cargo', 'build', '--release', '--features=java'
|
commandLine 'cargo', 'build', '--release', '--features=java'
|
||||||
}
|
}
|
||||||
|
|
||||||
jar.archiveClassifier = osdetector.classifier
|
|
||||||
|
|
||||||
def rustDir = projectDir.toPath()
|
def rustDir = projectDir.toPath()
|
||||||
.parent
|
.parent
|
||||||
.parent
|
.parent
|
||||||
.resolve('target')
|
.resolve('target')
|
||||||
.resolve('release')
|
.resolve('release')
|
||||||
.toFile()
|
.toFile()
|
||||||
processResources {
|
|
||||||
|
tasks.register('nativeBuild', Jar) {
|
||||||
|
archiveClassifier = osdetector.classifier
|
||||||
dependsOn cargoBuild
|
dependsOn cargoBuild
|
||||||
outputs.upToDateWhen { false } // no caching
|
from sourceSets.main.runtimeClasspath
|
||||||
from(rustDir) {
|
from(rustDir) {
|
||||||
include('*.dll')
|
include('*.dll')
|
||||||
include('*.so')
|
include('*.so')
|
||||||
|
@ -48,9 +138,23 @@ processResources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
mavenJava(MavenPublication) {
|
||||||
|
artifact jar
|
||||||
|
artifact sourcesJar
|
||||||
|
artifact javadocJar
|
||||||
|
artifact windowsJar
|
||||||
|
artifact linuxJar
|
||||||
|
artifact macosJar
|
||||||
|
artifact multiplatformJar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
import com.vanniktech.maven.publish.SonatypeHost
|
import com.vanniktech.maven.publish.SonatypeHost
|
||||||
mavenPublishing {
|
mavenPublishing {
|
||||||
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, true)
|
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, true)
|
||||||
signAllPublications()
|
signAllPublications()
|
||||||
coordinates(project.group, rootProject.name, project.version)
|
coordinates(project.group, rootProject.name, project.version)
|
||||||
|
|
||||||
|
|
3
dist/java/src/mp/code/BufferController.java
vendored
3
dist/java/src/mp/code/BufferController.java
vendored
|
@ -70,6 +70,7 @@ public final class BufferController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to send a {@link TextChange} update.
|
* Tries to send a {@link TextChange} update.
|
||||||
|
* @param change the update to send
|
||||||
* @throws ControllerException if the controller was stopped
|
* @throws ControllerException if the controller was stopped
|
||||||
*/
|
*/
|
||||||
public void send(TextChange change) throws ControllerException {
|
public void send(TextChange change) throws ControllerException {
|
||||||
|
@ -81,6 +82,8 @@ public final class BufferController {
|
||||||
/**
|
/**
|
||||||
* Registers a callback to be invoked whenever a {@link BufferUpdate} occurs.
|
* Registers a callback to be invoked whenever a {@link BufferUpdate} occurs.
|
||||||
* This will not work unless a Java thread has been dedicated to the event loop.
|
* This will not work unless a Java thread has been dedicated to the event loop.
|
||||||
|
* @param cb a {@link Consumer} that receives the controller when the change occurs;
|
||||||
|
* you should probably spawn a new thread in here, to avoid deadlocking
|
||||||
* @see Extensions#drive(boolean)
|
* @see Extensions#drive(boolean)
|
||||||
*/
|
*/
|
||||||
public void callback(Consumer<BufferController> cb) {
|
public void callback(Consumer<BufferController> cb) {
|
||||||
|
|
9
dist/java/src/mp/code/CursorController.java
vendored
9
dist/java/src/mp/code/CursorController.java
vendored
|
@ -43,14 +43,15 @@ public final class CursorController {
|
||||||
return recv(this.ptr);
|
return recv(this.ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static native void send(long self, Selection cursor) throws ControllerException;
|
private static native void send(long self, Selection selection) throws ControllerException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to send a {@link Selection} update.
|
* Tries to send a {@link Selection} update.
|
||||||
|
* @param selection the update to send
|
||||||
* @throws ControllerException if the controller was stopped
|
* @throws ControllerException if the controller was stopped
|
||||||
*/
|
*/
|
||||||
public void send(Selection cursor) throws ControllerException {
|
public void send(Selection selection) throws ControllerException {
|
||||||
send(this.ptr, cursor);
|
send(this.ptr, selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static native void callback(long self, Consumer<CursorController> cb);
|
private static native void callback(long self, Consumer<CursorController> cb);
|
||||||
|
@ -58,6 +59,8 @@ public final class CursorController {
|
||||||
/**
|
/**
|
||||||
* Registers a callback to be invoked whenever a {@link Cursor} update occurs.
|
* Registers a callback to be invoked whenever a {@link Cursor} update occurs.
|
||||||
* This will not work unless a Java thread has been dedicated to the event loop.
|
* This will not work unless a Java thread has been dedicated to the event loop.
|
||||||
|
* @param cb a {@link Consumer} that receives the controller when the change occurs;
|
||||||
|
* you should probably spawn a new thread in here, to avoid deadlocking
|
||||||
* @see Extensions#drive(boolean)
|
* @see Extensions#drive(boolean)
|
||||||
*/
|
*/
|
||||||
public void callback(Consumer<CursorController> cb) {
|
public void callback(Consumer<CursorController> cb) {
|
||||||
|
|
2
dist/java/src/mp/code/Extensions.java
vendored
2
dist/java/src/mp/code/Extensions.java
vendored
|
@ -33,7 +33,7 @@ public final class Extensions {
|
||||||
* <p>
|
* <p>
|
||||||
* You may alternatively call this with true, in a separate and dedicated Java thread;
|
* You may alternatively call this with true, in a separate and dedicated Java thread;
|
||||||
* it will remain active in the background and act as event loop. Assign it like this:
|
* it will remain active in the background and act as event loop. Assign it like this:
|
||||||
* <p><code>new Thread(() -> Extensions.drive(true)).start();</code></p>
|
* <p><code>new Thread(() -> Extensions.drive(true)).start();</code></p>
|
||||||
* @param block true if it should use the current thread
|
* @param block true if it should use the current thread
|
||||||
*/
|
*/
|
||||||
public static native void drive(boolean block);
|
public static native void drive(boolean block);
|
||||||
|
|
2
dist/java/src/mp/code/Workspace.java
vendored
2
dist/java/src/mp/code/Workspace.java
vendored
|
@ -196,6 +196,8 @@ public final class Workspace {
|
||||||
/**
|
/**
|
||||||
* Registers a callback to be invoked whenever a new {@link Event} is ready to be received.
|
* Registers a callback to be invoked whenever a new {@link Event} is ready to be received.
|
||||||
* This will not work unless a Java thread has been dedicated to the event loop.
|
* This will not work unless a Java thread has been dedicated to the event loop.
|
||||||
|
* @param cb a {@link Consumer} that receives the controller when the change occurs;
|
||||||
|
* you should probably spawn a new thread in here, to avoid deadlocking
|
||||||
* @see Extensions#drive(boolean)
|
* @see Extensions#drive(boolean)
|
||||||
*/
|
*/
|
||||||
public void callback(Consumer<Workspace> cb) {
|
public void callback(Consumer<Workspace> cb) {
|
||||||
|
|
3
dist/java/src/mp/code/data/TextChange.java
vendored
3
dist/java/src/mp/code/data/TextChange.java
vendored
|
@ -3,9 +3,6 @@ package mp.code.data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import mp.code.Extensions;
|
|
||||||
|
|
||||||
import java.util.OptionalLong;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A data class holding information about a text change.
|
* A data class holding information about a text change.
|
||||||
|
|
|
@ -2,8 +2,8 @@ package mp.code.exceptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An exception that may occur when a {@link mp.code.BufferController} or
|
* An exception that may occur when a {@link mp.code.BufferController} or
|
||||||
* a {@link mp.code.CursorController} perform an illegal operation.
|
* a {@link mp.code.CursorController} or {@link mp.code.Workspace} (in the
|
||||||
* It may also occur as a result of {@link mp.code.Workspace#event()}.
|
* receiver part) perform an illegal operation.
|
||||||
*/
|
*/
|
||||||
public abstract class ControllerException extends Exception {
|
public abstract class ControllerException extends Exception {
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue