mirror of
https://github.com/hexedtech/codemp-sublime.git
synced 2024-11-21 22:34:48 +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"
|
PYTHON_SYS_EXECUTABLE="$PYO3_PYTHON"
|
||||||
CARGO_FEATURES="pyo3/extension-module"
|
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"
|
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."
|
[[ -f "$TARGET_DIR/$FUll_TARGET" ]] && echo "$FILE exists."
|
||||||
ditto "$BUILD_DIR/${FILENAME}.dylib" "$TARGET_DIR/$FULL_TARGET"
|
ditto "$BUILD_DIR/${FILENAME}.dylib" "$TARGET_DIR/$FULL_TARGET"
|
||||||
|
|
317
src/lib.rs
317
src/lib.rs
|
@ -38,9 +38,7 @@ impl From<PyCodempError> for PyErr {
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn codemp_init<'a>(py: Python<'a>) -> PyResult<Py<PyClientHandle>> {
|
fn codemp_init<'a>(py: Python<'a>) -> PyResult<Py<PyClientHandle>> {
|
||||||
let py_instance: PyClientHandle = CodempInstance::default().into();
|
let py_instance: PyClientHandle = CodempInstance::default().into();
|
||||||
Python::with_gil(|py| {
|
Ok(Py::new(py, py_instance)?)
|
||||||
Ok(Py::new(py, py_instance)?)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
@ -79,12 +77,13 @@ impl PyClientHandle {
|
||||||
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 buffctrl = rc.attach(path.as_str())
|
let buffctrl: PyBufferController = rc.attach(path.as_str())
|
||||||
.await
|
.await
|
||||||
.map_err(PyCodempError::from)?;
|
.map_err(PyCodempError::from)?
|
||||||
|
.into();
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
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();
|
let rc = self.0.clone();
|
||||||
|
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
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
|
.await
|
||||||
.map_err(PyCodempError::from)?;
|
.map_err(PyCodempError::from)?
|
||||||
|
.into();
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
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();
|
let rc = self.0.clone();
|
||||||
|
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
let curctrl = rc.get_cursor()
|
let curctrl: PyCursorController = rc.get_cursor()
|
||||||
.await
|
.await
|
||||||
.map_err(PyCodempError::from)?;
|
.map_err(PyCodempError::from)?
|
||||||
|
.into();
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
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();
|
let rc = self.0.clone();
|
||||||
|
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
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
|
.await
|
||||||
.map_err(PyCodempError::from)?;
|
.map_err(PyCodempError::from)?
|
||||||
|
.into();
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
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]
|
#[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]
|
#[pymethods]
|
||||||
impl PyCursorController {
|
impl PyCursorController {
|
||||||
|
@ -193,13 +205,39 @@ impl PyCursorController {
|
||||||
// Ok(())
|
// 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> {
|
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 {
|
let pos = CodempCursorPosition {
|
||||||
buffer: path,
|
buffer: path,
|
||||||
start: Some(CodempRowCol { row: start.0, col: start.1 }),
|
start: Some(start.into()),
|
||||||
end: Some(CodempRowCol { row: end.0, col: end.1 })
|
end: Some(end.into())
|
||||||
};
|
};
|
||||||
|
|
||||||
pyo3_asyncio::tokio::future_into_py(py, async move {
|
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> {
|
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 {
|
pyo3_asyncio::tokio::future_into_py(py, async move {
|
||||||
let cur_event = rc.recv()
|
let cur_event: PyCursorEvent = rc.recv()
|
||||||
.await
|
.await
|
||||||
.map_err(PyCodempError::from)?;
|
.map_err(PyCodempError::from)?
|
||||||
Ok(())
|
.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]
|
#[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]
|
#[pymethods]
|
||||||
impl PyBufferController {
|
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>> {
|
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)
|
Ok(cont)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send<'a>(&self, py: Python<'a>, skip: usize, text: String, tail: usize) -> PyResult<&'a PyAny>{
|
// What to do with this send? does it make sense to implement it at all?
|
||||||
let rc = self.0.clone();
|
// 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 {
|
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]
|
/* ---------- Type Wrappers ----------*/
|
||||||
// struct PyCursorEvent {
|
// All these objects are not meant to be handled rust side.
|
||||||
// user: String,
|
// Just to be sent to the python heap.
|
||||||
// buffer: Some(String),
|
|
||||||
// start: (i32, i32),
|
|
||||||
// end: (i32, i32)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl From<CodempCursorEvent> for PyCursorEvent {
|
#[pyclass]
|
||||||
// fn from(value: CodempCursorEvent) -> Self {
|
struct PyCursorEvent {
|
||||||
// PyCursorEvent {
|
user: String,
|
||||||
// user: value.user,
|
buffer: String,
|
||||||
// buffer: value.position.buffer,
|
start: (i32, i32),
|
||||||
// start: (value.position.start.row, value.position.start.col),
|
end: (i32, i32)
|
||||||
// end: (value.position.end.row, value.position.end.col)
|
}
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
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]
|
#[pymodule]
|
||||||
fn codemp_client(_py: Python, m: &PyModule) -> PyResult<()> {
|
fn codemp_client(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
m.add_function(wrap_pyfunction!(codemp_init, m)?)?;
|
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::<PyCursorController>()?;
|
||||||
m.add_class::<PyBufferController>()?;
|
m.add_class::<PyBufferController>()?;
|
||||||
|
|
||||||
|
m.add_class::<PyCursorEvent>()?;
|
||||||
|
m.add_class::<PyTextChange>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue