mirror of
https://github.com/hexedtech/codemp-sublime.git
synced 2024-11-22 06:44:48 +01:00
Implemented the client handle interface. TODO: controllers
Former-commit-id: 6b273dffce4e28a1e41f31a71f5a4ce78ecfe264
This commit is contained in:
parent
31f296a55c
commit
bfad81f958
2 changed files with 190 additions and 150 deletions
|
@ -9,7 +9,7 @@ name = "codemp_client"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
codemp = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/codemp.git", tag = "v0.4.4" }
|
codemp = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/codemp.git", tag = "v0.4.4"}
|
||||||
pyo3 = { version = "0.19", features = ["extension-module"] }
|
pyo3 = { version = "0.19", features = ["extension-module"] }
|
||||||
pyo3-asyncio = { version = "0.19", features = ["tokio-runtime"] }
|
pyo3-asyncio = { version = "0.19", features = ["tokio-runtime"] }
|
||||||
tokio = "1.29.1"
|
tokio = "1.29.1"
|
||||||
|
|
338
src/lib.rs
338
src/lib.rs
|
@ -1,14 +1,12 @@
|
||||||
use std::{sync::Arc, error::Error, format};
|
use std::{sync::Arc, format};
|
||||||
|
|
||||||
use codemp::prelude::*;
|
use codemp::{prelude::*};
|
||||||
use codemp::errors::Error as CodempError;
|
use codemp::errors::Error as CodempError;
|
||||||
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
use pyo3::{
|
use pyo3::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
exceptions::{PyConnectionError, PyRuntimeError},
|
exceptions::{PyConnectionError, PyRuntimeError, PyBaseException},
|
||||||
types::{PyBool, PyString}
|
types::PyString
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PyCodempError(CodempError);
|
struct PyCodempError(CodempError);
|
||||||
|
@ -18,7 +16,7 @@ impl From::<CodempError> for PyCodempError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<PyCodempError> for PyErr {
|
impl From<PyCodempError> for PyErr {
|
||||||
fn from(err: PyCodempError) -> PyErr {
|
fn from(err: PyCodempError) -> PyErr {
|
||||||
match err.0 {
|
match err.0 {
|
||||||
CodempError::Transport { status, message } => {
|
CodempError::Transport { status, message } => {
|
||||||
|
@ -29,227 +27,269 @@ impl std::convert::From<PyCodempError> for PyErr {
|
||||||
},
|
},
|
||||||
CodempError::InvalidState { msg } => {
|
CodempError::InvalidState { msg } => {
|
||||||
PyRuntimeError::new_err(format!("Invalid state: {}", msg))
|
PyRuntimeError::new_err(format!("Invalid state: {}", msg))
|
||||||
|
},
|
||||||
|
CodempError::Filler { message } => {
|
||||||
|
PyBaseException::new_err(format!("Generic error: {}", message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn connect<'a>(py: Python<'a>, dest: String) -> PyResult<&'a PyAny> {
|
fn codemp_init<'a>(py: Python<'a>) -> PyResult<Py<PyClientHandle>> {
|
||||||
// construct a python coroutine
|
let py_instance: PyClientHandle = CodempInstance::default().into();
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
Python::with_gil(|py| {
|
||||||
match CodempClient::new(dest.as_str()).await {
|
Ok(Py::new(py, py_instance)?)
|
||||||
Ok(c) => {
|
|
||||||
Python::with_gil(|py|{
|
|
||||||
let cc: PyClientHandle = c.into();
|
|
||||||
let handle = Py::new(py, cc)?;
|
|
||||||
Ok(handle)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
Err(e) => { Err(PyConnectionError::new_err(e.source().unwrap().to_string())) }
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct PyClientHandle(Arc<Mutex<CodempClient>>);
|
struct PyClientHandle(Arc<CodempInstance>);
|
||||||
|
|
||||||
impl From::<CodempClient> for PyClientHandle {
|
impl From::<CodempInstance> for PyClientHandle {
|
||||||
fn from(value: CodempClient) -> Self {
|
fn from(value: CodempInstance) -> Self {
|
||||||
PyClientHandle(Arc::new(Mutex::new(value)))
|
PyClientHandle(Arc::new(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl PyClientHandle {
|
impl PyClientHandle {
|
||||||
fn get_id<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> {
|
fn connect<'a>(&'a self, py: Python<'a>, addr: String) ->PyResult<&'a PyAny> {
|
||||||
let rc = self.0.clone();
|
let rc = self.0.clone();
|
||||||
|
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
let binding = rc.lock().await;
|
rc.connect(addr.as_str()).await.map_err(PyCodempError::from)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create<'a>(&'a self, py: Python<'a>, path: String, content: Option<String>) -> PyResult<&'a PyAny> {
|
||||||
|
let rc = self.0.clone();
|
||||||
|
|
||||||
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
|
rc.create(path.as_str(), content.as_deref())
|
||||||
|
.await
|
||||||
|
.map_err(PyCodempError::from)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attach<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> {
|
||||||
|
let rc = self.0.clone();
|
||||||
|
|
||||||
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
|
let buffctrl = rc.attach(path.as_str())
|
||||||
|
.await
|
||||||
|
.map_err(PyCodempError::from)?;
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let id: Py<PyString> = PyString::new(py, binding.id()).into();
|
Ok(Py::new(py, PyBufferController(buffctrl))?)
|
||||||
Ok(id)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create<'a>(&self, py: Python<'a>, path: String, content: Option<String>) -> PyResult<&'a PyAny> {
|
fn join<'a>(&'a self, py: Python<'a>, session: String) -> PyResult<&'a PyAny> {
|
||||||
let rc = self.0.clone();
|
let rc = self.0.clone();
|
||||||
|
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
match rc.lock().await.create(path, content).await {
|
let curctrl = rc.join(session.as_str())
|
||||||
Ok(accepted) => {
|
.await
|
||||||
Python::with_gil(|py| {
|
.map_err(PyCodempError::from)?;
|
||||||
let accepted: Py<PyBool> = PyBool::new(py, accepted).into();
|
|
||||||
Ok(accepted)
|
Python::with_gil(|py| {
|
||||||
})
|
Ok(Py::new(py, PyCursorController(curctrl))?)
|
||||||
},
|
})
|
||||||
Err(e) => { Err(PyConnectionError::new_err(e.source().unwrap().to_string())) }
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_cursor<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
|
||||||
|
let rc = self.0.clone();
|
||||||
|
|
||||||
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
|
let curctrl = rc.get_cursor()
|
||||||
|
.await
|
||||||
|
.map_err(PyCodempError::from)?;
|
||||||
|
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
Ok(Py::new(py, PyCursorController(curctrl))?)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn listen<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> {
|
fn get_buffer<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> {
|
||||||
let rc = self.0.clone();
|
let rc = self.0.clone();
|
||||||
|
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
match rc.lock().await.listen().await {
|
let buffctrl = rc.get_buffer(path.as_str())
|
||||||
Ok(controller) => {
|
.await
|
||||||
Python::with_gil(|py| {
|
.map_err(PyCodempError::from)?;
|
||||||
let cc: PyControllerHandle = controller.into();
|
|
||||||
let contr = Py::new(py, cc)?;
|
Python::with_gil(|py| {
|
||||||
Ok(contr)
|
Ok(Py::new(py, PyBufferController(buffctrl))?)
|
||||||
})
|
})
|
||||||
},
|
|
||||||
Err(e) => {Err(PyConnectionError::new_err(e.source().unwrap().to_string()))}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attach<'a>(&self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> {
|
fn leave_workspace<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
|
||||||
let rc = self.0.clone();
|
let rc = self.0.clone();
|
||||||
let uri = path.clone();
|
|
||||||
|
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
match rc.lock().await.attach(uri).await {
|
rc.leave_workspace()
|
||||||
Ok(factory) => {
|
.await
|
||||||
Python::with_gil(|py| {
|
.map_err(PyCodempError::from)?;
|
||||||
let ff: PyOperationsHandle = factory.into();
|
Ok(())
|
||||||
let fact = Py::new(py, ff)?;
|
})
|
||||||
Ok(fact)
|
}
|
||||||
})
|
|
||||||
},
|
fn disconnect_buffer<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> {
|
||||||
Err(e) => {Err(PyConnectionError::new_err(e.source().unwrap().to_string()))}
|
let rc = self.0.clone();
|
||||||
}
|
|
||||||
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
|
rc.disconnect_buffer(path.as_str())
|
||||||
|
.await
|
||||||
|
.map_err(PyCodempError::from)?;
|
||||||
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From::<CodempCursorController> for PyCursorController {
|
||||||
impl From::<CursorControllerHandle> for PyControllerHandle {
|
fn from(value: CodempCursorController) -> Self {
|
||||||
fn from(value: CursorControllerHandle) -> Self {
|
PyCursorController(Arc::new(value))
|
||||||
PyControllerHandle(value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From::<OperationControllerHandle> for PyOperationsHandle {
|
impl From::<CodempBufferController> for PyBufferController {
|
||||||
fn from(value: OperationControllerHandle) -> Self {
|
fn from(value: CodempBufferController) -> Self {
|
||||||
PyOperationsHandle(value)
|
PyBufferController(Arc::new(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
struct PyControllerHandle(CursorControllerHandle);
|
struct PyCursorController(Arc<CodempCursorController>);
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl PyControllerHandle {
|
impl PyCursorController {
|
||||||
fn callback<'a>(&'a self, py: Python<'a>, coro_py: Py<PyAny>, caller_id: Py<PyString>) -> PyResult<&'a PyAny> {
|
// fn callback<'a>(&'a self, py: Python<'a>, coro_py: Py<PyAny>, caller_id: Py<PyString>) -> PyResult<&'a PyAny> {
|
||||||
let mut rc = self.0.clone();
|
// let mut rc = self.0.clone();
|
||||||
let cb = coro_py.clone();
|
// let cb = coro_py.clone();
|
||||||
// We want to start polling the ControlHandle and call the callback every time
|
// // We want to start polling the ControlHandle and call the callback every time
|
||||||
// we have something.
|
// // we have something.
|
||||||
|
|
||||||
|
// pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
|
// while let Some(op) = rc.poll().await {
|
||||||
|
// let start = op.start.unwrap_or(Position { row: 0, col: 0});
|
||||||
|
// let end = op.end.unwrap_or(Position { row: 0, col: 0});
|
||||||
|
|
||||||
|
// let cb_fut = Python::with_gil(|py| -> PyResult<_> {
|
||||||
|
// let args = (op.user, caller_id.clone(), op.buffer, (start.row, start.col), (end.row, end.col));
|
||||||
|
// let coro = cb.call1(py, args)?;
|
||||||
|
// pyo3_asyncio::tokio::into_future(coro.into_ref(py))
|
||||||
|
// })?;
|
||||||
|
|
||||||
|
// cb_fut.await?;
|
||||||
|
// }
|
||||||
|
// Ok(())
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn send<'a>(&'a self, py: Python<'a>, path: String, start: (i32, i32), end: (i32, i32)) -> PyResult<&'a PyAny> {
|
||||||
|
let rc = self.0.clone();
|
||||||
|
let pos = CodempCursorPosition {
|
||||||
|
buffer: path,
|
||||||
|
start: Some(CodempRowCol { row: start.0, col: start.1 }),
|
||||||
|
end: Some(CodempRowCol { row: end.0, col: end.1 })
|
||||||
|
};
|
||||||
|
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
while let Some(op) = rc.poll().await {
|
rc.send(pos)
|
||||||
let start = op.start.unwrap_or(Position { row: 0, col: 0});
|
.map_err(PyCodempError::from)?;
|
||||||
let end = op.end.unwrap_or(Position { row: 0, col: 0});
|
|
||||||
|
|
||||||
let cb_fut = Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let args = (op.user, caller_id.clone(), op.buffer, (start.row, start.col), (end.row, end.col));
|
|
||||||
let coro = cb.call1(py, args)?;
|
|
||||||
pyo3_asyncio::tokio::into_future(coro.into_ref(py))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
cb_fut.await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
} // to call after polling cursor movements.
|
|
||||||
|
|
||||||
fn send<'a>(&self, py: Python<'a>, path: String, start: (i32, i32), end: (i32, i32)) -> PyResult<&'a PyAny> {
|
}
|
||||||
|
|
||||||
|
fn recv<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
|
||||||
let rc = self.0.clone();
|
let rc = self.0.clone();
|
||||||
|
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
let startpos = Position { row: start.0, col: start.1 };
|
let cur_event = rc.recv()
|
||||||
let endpos = Position { row: end.0, col: end.1 };
|
.await
|
||||||
|
.map_err(PyCodempError::from)?;
|
||||||
rc.send(path.as_str(), startpos, endpos).await;
|
|
||||||
Ok(Python::with_gil(|py| py.None()))
|
|
||||||
})
|
|
||||||
|
|
||||||
} // when we change the cursor ourselves.
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyclass]
|
|
||||||
struct PyOperationsHandle(OperationControllerHandle);
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PyOperationsHandle {
|
|
||||||
fn callback<'a>(&'a self, py: Python<'a>, coro_py: Py<PyAny>, caller_id: Py<PyString>) -> PyResult<&'a PyAny> {
|
|
||||||
let mut rc = self.0.clone();
|
|
||||||
let cb = coro_py.clone();
|
|
||||||
// We want to start polling the ControlHandle and call the callback every time
|
|
||||||
// we have something.
|
|
||||||
|
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
||||||
while let Some(edit) = rc.poll().await {
|
|
||||||
let start = edit.span.start;
|
|
||||||
let end = edit.span.end;
|
|
||||||
let text = edit.content;
|
|
||||||
|
|
||||||
let cb_fut = Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let args = (caller_id.clone(), start, end, text);
|
|
||||||
let coro = cb.call1(py, args)?;
|
|
||||||
pyo3_asyncio::tokio::into_future(coro.into_ref(py))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
cb_fut.await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
} //to call after polling text changes
|
}
|
||||||
|
}
|
||||||
|
#[pyclass]
|
||||||
|
struct PyBufferController(Arc<CodempBufferController>);
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl PyBufferController {
|
||||||
|
// fn callback<'a>(&'a self, py: Python<'a>, coro_py: Py<PyAny>, caller_id: Py<PyString>) -> PyResult<&'a PyAny> {
|
||||||
|
// let mut rc = self.0.clone();
|
||||||
|
// let cb = coro_py.clone();
|
||||||
|
// // We want to start polling the ControlHandle and call the callback every time
|
||||||
|
// // we have something.
|
||||||
|
|
||||||
|
// pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
|
// while let Some(edit) = rc.poll().await {
|
||||||
|
// let start = edit.span.start;
|
||||||
|
// let end = edit.span.end;
|
||||||
|
// let text = edit.content;
|
||||||
|
|
||||||
|
// let cb_fut = Python::with_gil(|py| -> PyResult<_> {
|
||||||
|
// let args = (caller_id.clone(), start, end, text);
|
||||||
|
// let coro = cb.call1(py, args)?;
|
||||||
|
// pyo3_asyncio::tokio::into_future(coro.into_ref(py))
|
||||||
|
// })?;
|
||||||
|
|
||||||
|
// cb_fut.await?;
|
||||||
|
// }
|
||||||
|
// Ok(())
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
fn content(&self, py: Python<'_>) -> PyResult<Py<PyString>> {
|
fn content(&self, py: Python<'_>) -> PyResult<Py<PyString>> {
|
||||||
let cont: Py<PyString> = PyString::new(py, self.0.content().as_str()).into();
|
let cont: Py<PyString> = PyString::new(py, self.0.content().as_str()).into();
|
||||||
Ok(cont)
|
Ok(cont)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply<'a>(&self, py: Python<'a>, skip: usize, text: String, tail: usize) -> PyResult<&'a PyAny>{
|
fn send<'a>(&self, py: Python<'a>, skip: usize, text: String, tail: usize) -> PyResult<&'a PyAny>{
|
||||||
let rc = self.0.clone();
|
let rc = self.0.clone();
|
||||||
|
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
|
Ok(())
|
||||||
match rc.delta(skip, text.as_str(), tail) {
|
|
||||||
Some(op) => { rc.apply(op).await; Ok(()) },
|
|
||||||
None => Err(PyConnectionError::new_err("delta failed"))
|
|
||||||
}
|
|
||||||
// if let Some(op) = rc.delta(skip, text.as_str(), tail) {
|
|
||||||
// rc.apply(op).await;
|
|
||||||
// Python::with_gil(|py| {
|
|
||||||
// let accepted: Py<PyBool> = PyBool::new(py, true).into();
|
|
||||||
// Ok(accepted)
|
|
||||||
// })
|
|
||||||
// } else {
|
|
||||||
// Python::with_gil(|py| {
|
|
||||||
// let accepted: Py<PyBool> = PyBool::new(py, false).into();
|
|
||||||
// Ok(accepted)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
})
|
})
|
||||||
} //after making text mofications.
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #[pyclass]
|
||||||
|
// struct PyCursorEvent {
|
||||||
|
// user: String,
|
||||||
|
// buffer: Some(String),
|
||||||
|
// start: (i32, i32),
|
||||||
|
// end: (i32, i32)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl From<CodempCursorEvent> for PyCursorEvent {
|
||||||
|
// fn from(value: CodempCursorEvent) -> Self {
|
||||||
|
// PyCursorEvent {
|
||||||
|
// user: value.user,
|
||||||
|
// buffer: value.position.buffer,
|
||||||
|
// start: (value.position.start.row, value.position.start.col),
|
||||||
|
// end: (value.position.end.row, value.position.end.col)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
// Python module
|
// Python module
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
fn codemp_client(_py: Python, m: &PyModule) -> PyResult<()> {
|
fn codemp_client(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
m.add_function(wrap_pyfunction!(connect, m)?)?;
|
m.add_function(wrap_pyfunction!(codemp_init, m)?)?;
|
||||||
m.add_class::<PyClientHandle>()?;
|
m.add_class::<PyClientHandle>()?;
|
||||||
m.add_class::<PyControllerHandle>()?;
|
m.add_class::<PyCursorController>()?;
|
||||||
m.add_class::<PyOperationsHandle>()?;
|
m.add_class::<PyBufferController>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
Loading…
Reference in a new issue