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:
@@ -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 });
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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!")
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user