mirror of
https://github.com/hexedtech/codemp-sublime.git
synced 2024-11-25 00:04:49 +01:00
Got a bindings library that compiles.
Former-commit-id: b08de0a869b6738131e96dd616ef0b96bfb2d975
This commit is contained in:
parent
bfad81f958
commit
e387b726c8
3 changed files with 260 additions and 61 deletions
|
@ -1 +1 @@
|
|||
8d18661818436ebf65d16f9447601b123aef410e
|
||||
80ba7b16f0b0647e2f1d8520f3a90c51000bbd2c
|
2
build.sh
2
build.sh
|
@ -14,7 +14,9 @@ PYO3_PYTHON="$(pyenv which python)"
|
|||
PYTHON_SYS_EXECUTABLE="$PYO3_PYTHON"
|
||||
CARGO_FEATURES="pyo3/extension-module"
|
||||
|
||||
echo "Building with python: $PYO3_PYTHON"
|
||||
env PYO3_PYTHON="${PYO3_PYTHON}" PYTHON_SYS_EXECUTABLE="$PYO3_PYTHON" cargo build --features "$CARGO_FEATURES"
|
||||
echo "Copying into: $TARGET_DIR/$FULL_TARGET"
|
||||
|
||||
[[ -f "$TARGET_DIR/$FUll_TARGET" ]] && echo "$FILE exists."
|
||||
ditto "$BUILD_DIR/${FILENAME}.dylib" "$TARGET_DIR/$FULL_TARGET"
|
||||
|
|
311
src/lib.rs
311
src/lib.rs
|
@ -38,9 +38,7 @@ impl From<PyCodempError> for PyErr {
|
|||
#[pyfunction]
|
||||
fn codemp_init<'a>(py: Python<'a>) -> PyResult<Py<PyClientHandle>> {
|
||||
let py_instance: PyClientHandle = CodempInstance::default().into();
|
||||
Python::with_gil(|py| {
|
||||
Ok(Py::new(py, py_instance)?)
|
||||
})
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
|
@ -79,12 +77,13 @@ impl PyClientHandle {
|
|||
let rc = self.0.clone();
|
||||
|
||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||
let buffctrl = rc.attach(path.as_str())
|
||||
let buffctrl: PyBufferController = rc.attach(path.as_str())
|
||||
.await
|
||||
.map_err(PyCodempError::from)?;
|
||||
.map_err(PyCodempError::from)?
|
||||
.into();
|
||||
|
||||
Python::with_gil(|py| {
|
||||
Ok(Py::new(py, PyBufferController(buffctrl))?)
|
||||
Ok(Py::new(py, buffctrl)?)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -93,12 +92,13 @@ impl PyClientHandle {
|
|||
let rc = self.0.clone();
|
||||
|
||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||
let curctrl = rc.join(session.as_str())
|
||||
let curctrl: PyCursorController = rc.join(session.as_str())
|
||||
.await
|
||||
.map_err(PyCodempError::from)?;
|
||||
.map_err(PyCodempError::from)?
|
||||
.into();
|
||||
|
||||
Python::with_gil(|py| {
|
||||
Ok(Py::new(py, PyCursorController(curctrl))?)
|
||||
Ok(Py::new(py, curctrl)?)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -107,12 +107,13 @@ impl PyClientHandle {
|
|||
let rc = self.0.clone();
|
||||
|
||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||
let curctrl = rc.get_cursor()
|
||||
let curctrl: PyCursorController = rc.get_cursor()
|
||||
.await
|
||||
.map_err(PyCodempError::from)?;
|
||||
.map_err(PyCodempError::from)?
|
||||
.into();
|
||||
|
||||
Python::with_gil(|py| {
|
||||
Ok(Py::new(py, PyCursorController(curctrl))?)
|
||||
Ok(Py::new(py, curctrl)?)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -121,12 +122,13 @@ impl PyClientHandle {
|
|||
let rc = self.0.clone();
|
||||
|
||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||
let buffctrl = rc.get_buffer(path.as_str())
|
||||
let buffctrl: PyBufferController = rc.get_buffer(path.as_str())
|
||||
.await
|
||||
.map_err(PyCodempError::from)?;
|
||||
.map_err(PyCodempError::from)?
|
||||
.into();
|
||||
|
||||
Python::with_gil(|py| {
|
||||
Ok(Py::new(py, PyBufferController(buffctrl))?)
|
||||
Ok(Py::new(py, buffctrl)?)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -154,20 +156,30 @@ impl PyClientHandle {
|
|||
}
|
||||
}
|
||||
|
||||
impl From::<CodempCursorController> for PyCursorController {
|
||||
fn from(value: CodempCursorController) -> Self {
|
||||
PyCursorController(Arc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From::<CodempBufferController> for PyBufferController {
|
||||
fn from(value: CodempBufferController) -> Self {
|
||||
PyBufferController(Arc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
struct PyCursorController(Arc<CodempCursorController>);
|
||||
struct PyCursorController {
|
||||
handle: Arc<CodempCursorController>,
|
||||
cb_trigger: Option<tokio::sync::mpsc::UnboundedSender<()>>
|
||||
}
|
||||
|
||||
impl From::<Arc<CodempCursorController>> for PyCursorController {
|
||||
fn from(value: Arc<CodempCursorController>) -> Self {
|
||||
PyCursorController {
|
||||
handle: value,
|
||||
cb_trigger: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn py_cursor_callback_wrapper(cb: PyObject)
|
||||
-> Box<dyn FnMut(CodempCursorEvent) -> () + Send + Sync + 'static>
|
||||
{
|
||||
let closure = move |data: CodempCursorEvent| {
|
||||
let args: PyCursorEvent = data.into();
|
||||
Python::with_gil(|py| { let _ = cb.call1(py, (args,)); });
|
||||
};
|
||||
Box::new(closure)
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyCursorController {
|
||||
|
@ -193,13 +205,39 @@ impl PyCursorController {
|
|||
// Ok(())
|
||||
// })
|
||||
// }
|
||||
fn drop_callback(&mut self) -> PyResult<()> {
|
||||
if let Some(channel) = &self.cb_trigger {
|
||||
channel.send(())
|
||||
.map_err(CodempError::from)
|
||||
.map_err(PyCodempError::from)?;
|
||||
|
||||
self.cb_trigger = None;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn callback<'a>(&'a mut self, py_cb: Py<PyAny>) -> PyResult<()> {
|
||||
if let Some(_channel) = &self.cb_trigger {
|
||||
Err(PyCodempError::from(CodempError::InvalidState { msg: "A callback is already running.".into() }).into())
|
||||
} else {
|
||||
let rt = pyo3_asyncio::tokio::get_runtime();
|
||||
|
||||
// create a channel to stop the callback task running on the tokio runtime.
|
||||
// and save the sendent inside the python object, so that we can later call it.
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
self.cb_trigger = Some(tx);
|
||||
|
||||
self.handle.callback(rt, rx, py_cursor_callback_wrapper(py_cb));
|
||||
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 rc = self.handle.clone();
|
||||
let pos = CodempCursorPosition {
|
||||
buffer: path,
|
||||
start: Some(CodempRowCol { row: start.0, col: start.1 }),
|
||||
end: Some(CodempRowCol { row: end.0, col: end.1 })
|
||||
start: Some(start.into()),
|
||||
end: Some(end.into())
|
||||
};
|
||||
|
||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||
|
@ -210,19 +248,63 @@ impl PyCursorController {
|
|||
|
||||
}
|
||||
|
||||
fn try_recv(&self, py: Python<'_>) -> PyResult<PyObject> {
|
||||
match self.handle.try_recv().map_err(PyCodempError::from)? {
|
||||
Some(cur_event) => {
|
||||
let evt = PyCursorEvent::from(cur_event);
|
||||
Ok(evt.into_py(py))
|
||||
},
|
||||
None => Ok(py.None())
|
||||
}
|
||||
}
|
||||
|
||||
fn recv<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
|
||||
let rc = self.0.clone();
|
||||
let rc = self.handle.clone();
|
||||
|
||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||
let cur_event = rc.recv()
|
||||
let cur_event: PyCursorEvent = rc.recv()
|
||||
.await
|
||||
.map_err(PyCodempError::from)?;
|
||||
Ok(())
|
||||
.map_err(PyCodempError::from)?
|
||||
.into();
|
||||
Python::with_gil(|py| {
|
||||
Ok(Py::new(py, cur_event)?)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn poll<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
|
||||
let rc = self.handle.clone();
|
||||
|
||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||
Ok(rc.poll().await.map_err(PyCodempError::from)?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
struct PyBufferController(Arc<CodempBufferController>);
|
||||
struct PyBufferController {
|
||||
handle: Arc<CodempBufferController>,
|
||||
cb_trigger: Option<tokio::sync::mpsc::UnboundedSender<()>>
|
||||
}
|
||||
|
||||
impl From::<Arc<CodempBufferController>> for PyBufferController {
|
||||
fn from(value: Arc<CodempBufferController>) -> Self {
|
||||
PyBufferController{
|
||||
handle: value,
|
||||
cb_trigger: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn py_buffer_callback_wrapper(cb: PyObject)
|
||||
-> Box<dyn FnMut(CodempTextChange) -> () + Send + Sync + 'static>
|
||||
{
|
||||
let closure = move |data: CodempTextChange| {
|
||||
let args: PyTextChange = data.into();
|
||||
Python::with_gil(|py| { let _ = cb.call1(py, (args,)); });
|
||||
};
|
||||
Box::new(closure)
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyBufferController {
|
||||
|
@ -250,40 +332,150 @@ impl PyBufferController {
|
|||
// })
|
||||
// }
|
||||
|
||||
fn drop_callback(&mut self) -> PyResult<()> {
|
||||
if let Some(channel) = &self.cb_trigger {
|
||||
channel.send(())
|
||||
.map_err(CodempError::from)
|
||||
.map_err(PyCodempError::from)?;
|
||||
|
||||
self.cb_trigger = None;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn callback<'a>(&'a mut self, py_cb: Py<PyAny>) -> PyResult<()> {
|
||||
if let Some(_channel) = &self.cb_trigger {
|
||||
Err(PyCodempError::from(CodempError::InvalidState { msg: "A callback is already running.".into() }).into())
|
||||
} else {
|
||||
let rt = pyo3_asyncio::tokio::get_runtime();
|
||||
|
||||
// could this be a oneshot channel?
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
self.cb_trigger = Some(tx);
|
||||
|
||||
self.handle.callback(rt, rx, py_buffer_callback_wrapper(py_cb));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn replace(&self, txt: &str) -> PyResult<()> {
|
||||
if let Some(op) = self.handle.replace(txt) {
|
||||
self.handle.send(op).map_err(PyCodempError::from)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delta(&self, start: usize, txt: &str, end: usize) -> PyResult<()> {
|
||||
if let Some(op) = self.handle.delta(start, txt, end){
|
||||
self.handle.send(op).map_err(PyCodempError::from)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert(&self, txt: &str, pos: u64) -> PyResult<()> {
|
||||
let op = self.handle.insert(txt, pos);
|
||||
self.handle.send(op).map_err(PyCodempError::from)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete(&self, pos: u64, count: u64) -> PyResult<()> {
|
||||
let op = self.handle.delete(pos, count);
|
||||
self.handle.send(op).map_err(PyCodempError::from)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cancel(&self, pos: u64, count: u64) -> PyResult<()> {
|
||||
let op = self.handle.cancel(pos, count);
|
||||
self.handle.send(op).map_err(PyCodempError::from)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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.handle.content().as_str()).into();
|
||||
Ok(cont)
|
||||
}
|
||||
|
||||
fn send<'a>(&self, py: Python<'a>, skip: usize, text: String, tail: usize) -> PyResult<&'a PyAny>{
|
||||
let rc = self.0.clone();
|
||||
// What to do with this send? does it make sense to implement it at all?
|
||||
// fn send<'a>(&self, py: Python<'a>, skip: usize, text: String, tail: usize) -> PyResult<&'a PyAny>{
|
||||
// let rc = self.handle.clone();
|
||||
// pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||
// Ok(())
|
||||
// })
|
||||
// }
|
||||
|
||||
fn try_recv(&self, py: Python<'_>) -> PyResult<PyObject> {
|
||||
match self.handle.try_recv().map_err(PyCodempError::from)? {
|
||||
Some(txt_change) => {
|
||||
let evt = PyTextChange::from(txt_change);
|
||||
Ok(evt.into_py(py))
|
||||
},
|
||||
None => Ok(py.None())
|
||||
}
|
||||
}
|
||||
|
||||
fn recv<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
|
||||
let rc = self.handle.clone();
|
||||
|
||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||
Ok(())
|
||||
let txt_change: PyTextChange = rc.recv()
|
||||
.await
|
||||
.map_err(PyCodempError::from)?
|
||||
.into();
|
||||
Python::with_gil(|py| {
|
||||
Ok(Py::new(py, txt_change)?)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn poll<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
|
||||
let rc = self.handle.clone();
|
||||
|
||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||
Ok(rc.poll().await.map_err(PyCodempError::from)?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// #[pyclass]
|
||||
// struct PyCursorEvent {
|
||||
// user: String,
|
||||
// buffer: Some(String),
|
||||
// start: (i32, i32),
|
||||
// end: (i32, i32)
|
||||
// }
|
||||
/* ---------- Type Wrappers ----------*/
|
||||
// All these objects are not meant to be handled rust side.
|
||||
// Just to be sent to the python heap.
|
||||
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
#[pyclass]
|
||||
struct PyCursorEvent {
|
||||
user: String,
|
||||
buffer: String,
|
||||
start: (i32, i32),
|
||||
end: (i32, i32)
|
||||
}
|
||||
|
||||
impl From<CodempCursorEvent> for PyCursorEvent {
|
||||
fn from(value: CodempCursorEvent) -> Self {
|
||||
// todo, handle this optional better?
|
||||
let pos = value.position.unwrap_or_default();
|
||||
PyCursorEvent {
|
||||
user: value.user,
|
||||
buffer: pos.buffer,
|
||||
start: pos.start.unwrap_or_default().into(),
|
||||
end: pos.end.unwrap_or_default().into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Python module
|
||||
#[pyclass]
|
||||
struct PyTextChange {
|
||||
start_incl: usize,
|
||||
end_excl: usize,
|
||||
content: String
|
||||
}
|
||||
|
||||
impl From<CodempTextChange> for PyTextChange {
|
||||
fn from(value: CodempTextChange) -> Self {
|
||||
PyTextChange { start_incl: value.span.start, end_excl: value.span.end, content: value.content }
|
||||
}
|
||||
}
|
||||
|
||||
/* ------ Python module --------*/
|
||||
#[pymodule]
|
||||
fn codemp_client(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(codemp_init, m)?)?;
|
||||
|
@ -291,5 +483,10 @@ fn codemp_client(_py: Python, m: &PyModule) -> PyResult<()> {
|
|||
m.add_class::<PyCursorController>()?;
|
||||
m.add_class::<PyBufferController>()?;
|
||||
|
||||
m.add_class::<PyCursorEvent>()?;
|
||||
m.add_class::<PyTextChange>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue