1
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2026-03-04 16:02:51 +01:00

wip - subprocess

This commit is contained in:
2025-05-19 09:24:40 +02:00
parent 4f3fd2f906
commit ea306868df
3 changed files with 178 additions and 24 deletions

View File

@@ -19,24 +19,17 @@ require(["vs/editor/editor.main"], () => {
provideCompletionItems: function (model, position, context, token) {
return new Promise((resolve, reject) => {
const value = model.getValue();
bridge.requestCompletions(
JSON.stringify({
code: value,
line: position.lineNumber,
column: position.column,
context: context,
token: token,
}),
(result) => {
try {
const items = JSON.parse(result);
resolve({ suggestions: items });
} catch (e) {
console.error("Failed to parse completion result:", e);
reject();
}
}
);
sendToPython("completion", {
code: value,
line: position.lineNumber,
column: position.column,
context: context,
token: token,
});
bridge.completion.connect((data) => {
const completionItems = JSON.parse(data);
resolve({ suggestions: completionItems });
});
});
},
});

View File

@@ -1,20 +1,150 @@
import functools
import json
import multiprocessing
import threading
import time
from pathlib import Path
import jedi
from bec_lib.client import BECClient
from qtpy.QtCore import Property, QObject, QUrl, Signal, Slot
from qtpy.QtWebChannel import *
from qtpy.QtWebEngineWidgets import *
class CompletionItemKind:
Text = 1
Method = 2
Function = 3
Constructor = 4
Field = 5
Variable = 6
Class = 7
Interface = 8
Module = 9
Property = 10
Unit = 11
Value = 12
Enum = 13
Keyword = 14
Snippet = 15
Color = 16
File = 17
Reference = 18
Folder = 19
EnumMember = 20
Constant = 21
Struct = 22
Event = 23
Operator = 24
TypeParameter = 25
def completion_worker(request_queue, response_queue):
client = BECClient()
client.start()
while True:
json_str = request_queue.get()
if json_str is None:
continue
if json_str == "exit":
break
data = json.loads(json_str)
# if data["context"].get("triggerCharacter") != ".":
# response_queue.put(json.dumps([]))
print(client.device_manager.devices)
print("Received completion request:", data)
start = time.time()
script = jedi.Script(data["code"])
print("Script created in:", time.time() - start)
start = time.time()
completions = script.complete(data["line"], data["column"] - 1)
print("Completions created in:", time.time() - start)
start = time.time()
infer_result = script.infer(data["line"], data["column"] - 2)
print("Infer result created in:", time.time() - start)
if infer_result:
inferred_type = infer_result[0].name
devices = client.device_manager.devices
if inferred_type == "DeviceContainer":
completions = [
{
"label": device.name,
"kind": 9,
"insertText": device.name,
"documentation": device.name,
}
for device_name, device in devices.items()
]
response_queue.put(completions)
continue
if inferred_type == "Scans":
completions = [
{
"label": scan_name,
"kind": CompletionItemKind.Method,
"insertText": scan_name,
"documentation": scan_name,
}
for scan_name, scan in client.scans._available_scans.items()
]
response_queue.put(completions)
continue
print("Inferred type:", inferred_type)
if completions:
# sort completions to have private methods at the end
completions = sorted(completions, key=lambda x: (x.name.startswith("_"), x.name))
completions = [
{
"label": completion.name,
"kind": 6,
"insertText": completion.name,
"documentation": completion.description,
}
for completion in completions
]
# out = {"id": data["id"], "completions": completions}
print("result:", completions, infer_result)
response_queue.put(completions)
client.shutdown()
class BaseBridge(QObject):
initialized = Signal()
sendDataChanged = Signal(str, str)
completion = Signal(str)
def __init__(self, parent=None):
super().__init__(parent=parent)
self.shutdown_event = threading.Event()
self.active = False
self.queue = []
self.request_queue = multiprocessing.Queue()
self.response_queue = multiprocessing.Queue()
self.process = multiprocessing.Process(
target=completion_worker, args=(self.request_queue, self.response_queue)
)
self.process.start()
self.emitter_thread = threading.Thread(target=self.emitter)
self.emitter_thread.start()
def emitter(self):
while not self.shutdown_event.is_set():
try:
data = self.response_queue.get(timeout=0.1)
if data is None:
continue
print("Received data from process:", data)
self.completion.emit(json.dumps(data))
except multiprocessing.queues.Empty:
continue
def send_to_js(self, name, value):
if self.active:
@@ -26,15 +156,16 @@ class BaseBridge(QObject):
@Slot(str, str)
def receive_from_js(self, name, value):
if name == "completion":
print("Completer received:", value)
self.request_queue.put(value)
return
data = json.loads(value)
self.setProperty(name, data)
@Slot(str, result="QVariant")
def requestCompletions(self, json_str):
import json
self.request_queue.put(json_str)
out = self.response_queue.get(timeout=10)
return json.dumps(out)
data = json.loads(json_str)
if data["context"].get("triggerCharacter") != ".":
return json.dumps([])
@@ -80,6 +211,12 @@ class BaseBridge(QObject):
self.queue.clear()
def shutdown(self):
self.request_queue.put("exit")
self.shutdown_event.set()
self.emitter_thread.join()
self.process.join()
class EditorBridge(BaseBridge):
valueChanged = Signal()
@@ -168,3 +305,6 @@ class MonacoWidget(QWebEngineView):
def setTheme(self, theme):
self._bridge.send_to_js("theme", theme)
def shutdown(self):
self._bridge.shutdown()

View File

@@ -35,6 +35,13 @@ class MonacoWidget(BECWidget, QWidget):
def set_theme(self, theme: str) -> None:
self.editor.setTheme(theme)
def cleanup(self) -> None:
"""
Clean up the widget before closing.
"""
self.editor.shutdown()
super().cleanup()
if __name__ == "__main__":
qapp = QApplication([])
@@ -43,9 +50,23 @@ if __name__ == "__main__":
widget.set_theme("vs-dark")
widget.set_text(
"""
# This is a comment
def hello_world():
print("Hello, world!")
import numpy as np
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from bec_lib.devicemanager import DeviceContainer
from bec_lib.scans import Scans
dev: DeviceContainer
scans: Scans
#######################################
########## User Script #####################
#######################################
# This is a comment
def hello_world():
print("Hello, world!")
"""
)