Lots of regression, refactor and port to Highway networking

Inital refactored version with only one network route. Major changes incoming.
This commit is contained in:
Philip Trauner 2016-10-03 20:38:59 +02:00
parent 9596fa2815
commit eb7692ac9d

View file

@ -17,17 +17,15 @@ from watchdog.events import FileSystemEventHandler
import sublime import sublime
import sublime_plugin import sublime_plugin
import socket from Highway import Client
from ESock import ESock
from Utils import capture_trace
from Sync import SyncClient
import Routing import Routing
from SublimeMenu import * from SublimeMenu import *
import Logging import Logging
import webbrowser import webbrowser
CHANNEL = "s" CHANNEL = 1
def plugin_unloaded(): def plugin_unloaded():
for window in windows: for window in windows:
@ -36,28 +34,32 @@ def plugin_unloaded():
def sock_handler(sock, routes, handler): class Fl0wClient(Client):
while 1: def setup(self, routes, fl0w, debug=False):
try: super().setup(routes, debug=debug)
data = sock.recv() self.fl0w = fl0w
if data[1] in routes:
routes[data[1]].run(data[0], handler)
except (OSError, socket.error, Exception) as e:
if str(e) != "[Errno 9] Bad file descriptor":
capture_trace()
handler.invoke_disconnect()
break
class ReloadHandler(FileSystemEventHandler): def ready(self):
def on_modified(self, event): self.fl0w.connected = True
print("Modified: %s" % event) Logging.info("Connection ready!")
def closed(self, code, reason):
self.fl0w.connected = False
Logging.info("Connection closed: %s (%s)" % (reason, code))
class Info(Routing.ClientRoute):
def run(self, data, handler):
info = ""
for key in data:
info += "%s: %s\n" % (key.capitalize(), ", ".join(data[key]))
sublime.message_dialog(info)
handler.fl0w.invoke_main_menu()
def on_created(self, event):
print("Created: %s" % event)
def on_deleted(self, event):
print("Deleted: %s" % event)
class Fl0w: class Fl0w:
@ -65,201 +67,58 @@ class Fl0w:
self.connected = False self.connected = False
self.window = window self.window = window
self.folder = window.folders()[0] self.folder = window.folders()[0]
self.selected_wallaby = None
self.sock = None
self.s_sync = None
self.start_menu = Menu() self.start_menu = Menu()
self.start_menu.add(Entry("Connect", "Connect to a fl0w server", action=self.invoke_connect)) self.start_menu.add(Entry("Connect", "Connect to a fl0w server", action=self.invoke_connect))
self.start_menu.add(Entry("About", "Information about fl0w", action=self.invoke_about)) self.start_menu.add(Entry("About", "Information about fl0w", action=self.invoke_about))
self.main_menu = Menu() self.main_menu = Menu()
self.main_menu.add(Entry("Wallaby Control", "Control a connected Wallaby", action=self.invoke_wallaby_control)) #self.main_menu.add(Entry("Wallaby Control", "Control a connected Wallaby", action=self.invoke_wallaby_control))
self.main_menu.add(Entry("Info", "Server info", action=self.invoke_info)) self.main_menu.add(Entry("Info", "Server info", action=lambda: self.ws.send(None, "info")))
self.main_menu.add(Entry("Debug", "Debug options", action=self.invoke_debug_options)) #self.main_menu.add(Entry("Debug", "Debug options", action=self.invoke_debug_options))
self.main_menu.add(Entry("Disconnect", "Disconnect from server", action=self.invoke_disconnect)) self.main_menu.add(Entry("Disconnect", "Disconnect from server", action=self.invoke_disconnect))
def invoke_start_menu(self):
self.start_menu.invoke(self.window)
def invoke_main_menu(self):
self.main_menu.invoke(self.window)
def invoke_about(self): def invoke_about(self):
if sublime.ok_cancel_dialog("fl0w by @robot0nfire", "robot0nfire.com"): if sublime.ok_cancel_dialog("fl0w by @robot0nfire", "robot0nfire.com"):
webbrowser.open("http://robot0nfire.com") webbrowser.open("http://robot0nfire.com")
def invoke_info(self):
self.sock.send("", "info")
class Info(Routing.ClientRoute):
def run(self, data, handler):
sublime.message_dialog(data)
handler.main_menu.invoke(handler.window)
class GetInfo(Routing.ClientRoute):
def run(self, data, handler):
if data == "":
handler.sock.send({"type" : CHANNEL}, "get_info")
class ErrorReport(Routing.ClientRoute):
def run(self, data, handler):
sublime.error_message(data)
class Compile(Routing.ClientRoute):
def run(self, data, handler):
status = ""
if data["failed"]:
status = "Compile failed (%s) (%s)." % (data["relpath"], strftime("%H:%M:%S"))
else:
status = "Compile completed successfully. (%s) (%s)." % (data["relpath"], strftime("%H:%M:%S"))
view = handler.window.create_output_panel('compile_panel')
view.set_syntax_file("Packages/fl0w/CompileHighlight.sublime-syntax")
view.settings().set("draw_white_space", "none")
view.settings().set("draw_indent_guides", False)
view.settings().set("gutter", False)
view.settings().set("line_numbers", False)
view.set_read_only(False)
view.run_command("append", {"characters": data["returned"] + status, "scroll_to_end" : True})
view.set_read_only(True)
handler.window.run_command("show_panel", {"panel": "output.compile_panel"})
class StdStream(Routing.ClientRoute):
def __init__(self):
self.view = None
self.buffer = ""
def setup_view(self, handler):
self.view = handler.window.create_output_panel('std_stream_panel')
self.view.settings().set("draw_white_space", "none")
self.view.settings().set("draw_indent_guides", False)
self.view.settings().set("gutter", False)
self.view.settings().set("line_numbers", False)
def run(self, data, handler):
self.setup_view(handler)
handler.window.run_command("show_panel", {"panel": "output.std_stream_panel"})
if type(data) is str:
self.buffer += data
self.append_to_view(self.buffer)
elif type(data) is dict:
if "return_code" in data:
self.append_to_view(self.buffer + "\nProgram finished with return code %d" % data["return_code"])
self.buffer = ""
def append_to_view(self, text):
self.view.set_read_only(False)
self.view.run_command("append", {"characters" : text, "scroll_to_end" : True})
self.view.set_read_only(True)
def connect(self, connect_details): def connect(self, connect_details):
connect_details_raw = connect_details
connect_details = connect_details.split(":")
compression_level = int(sublime.load_settings("fl0w.sublime-settings").get("compression_level")) compression_level = int(sublime.load_settings("fl0w.sublime-settings").get("compression_level"))
if len(connect_details) == 2:
try: try:
# Establish connection to the server self.ws = Fl0wClient('ws://%s' % connect_details)
self.sock = ESock(socket.create_connection((connect_details[0], int(connect_details[1]))), self.ws.setup({"info" : Fl0wClient.Info()}, self, debug=False)
disconnect_callback=self.invoke_disconnect, debug=False, compression_level=compression_level) self.ws.connect()
sublime.status_message("Connected to %s:%s." % (connect_details[0], connect_details[1])) sublime.set_timeout_async(self.ws.run_forever, 0)
# Initialize all routes
error_report = Fl0w.ErrorReport()
info = Fl0w.Info()
wallaby_control = Fl0w.WallabyControl()
get_info = Fl0w.GetInfo()
std_stream = Fl0w.StdStream()
self.s_sync = SyncClient(self.sock, self.folder, "s_sync")
compile = Fl0w.Compile()
_thread.start_new_thread(sock_handler, (self.sock, {"error_report": error_report,
"info" : info, "wallaby_control" : wallaby_control, "get_info" : get_info,
"s_sync" : self.s_sync, "compile" : compile, "std_stream" : std_stream}, self))
self.s_sync.start()
self.connected = True
# Saving last server address
sublime.load_settings("fl0w.sublime-settings").set("server_address", connect_details_raw)
sublime.save_settings("fl0w.sublime-settings")
except OSError as e: except OSError as e:
sublime.error_message("Error during connection creation:\n %s" % str(e)) sublime.error_message("Error during connection creation:\n %s" % str(e))
else:
sublime.error_message("Invalid input.")
# Input invokers
def invoke_connect(self): def invoke_connect(self):
address = sublime.load_settings("fl0w.sublime-settings").get("server_address") # Will be removed once autoconnect works
address = "" if type(address) is not str else address self.connect("127.0.0.1:3077")
Input("Address:Port", initial_text=address, on_done=self.connect).invoke(self.window)
def invoke_disconnect(self): def invoke_disconnect(self):
if self.connected: if self.connected:
self.connected = False self.ws.close()
sublime.message_dialog("Connection closed ('%s')" % self.folder) sublime.message_dialog("Connection closed ('%s')" % self.folder)
self.s_sync.observer.stop()
self.sock.close()
def invoke_wallaby_control(self):
self.sock.send("list_wallaby_controllers", "wallaby_control")
def invoke_run_menu(self):
self.sock.send("list_programs", "wallaby_control")
class WallabyControl(Routing.ClientRoute):
def run(self, data, handler):
if "wallaby_controllers" in data:
wallaby_menu = Menu()
entry_count = 0
for wallaby in data["wallaby_controllers"]:
wallaby_menu.add(Entry(wallaby, str(data["wallaby_controllers"][wallaby]), action=handler.wallaby_control_submenu, kwargs={"wallaby" : wallaby}))
entry_count += 1
if entry_count != 0:
wallaby_menu.invoke(handler.window, back=handler.main_menu)
else:
sublime.error_message("No Wallaby Controllers connected.")
elif "programs" in data:
run_menu = Menu(subtitles=False)
entry_count = 0
for program in data["programs"]:
run_menu.add(Entry(program, action=handler.run_program, kwargs={"program" : program}))
entry_count += 1
if entry_count != 0:
run_menu.invoke(handler.window, back=handler.main_menu)
else:
sublime.error_message("No programs avaliable.")
def run_program(self, program):
self.sock.send({self.selected_wallaby : {"run" : program}}, "wallaby_control")
def wallaby_control_submenu(self, wallaby):
self.selected_wallaby = wallaby
menu = Menu(subtitles=False)
menu.add(Entry("Run", action=self.invoke_run_menu))
for action in ("Stop", "Shutdown", "Reboot", "Disconnect"):
menu.add(Entry(action, action=self.sock.send, kwargs={"data" : {wallaby : [action.lower()]}, "route" : "wallaby_control"}))
menu.invoke(self.window)
def invoke_debug_options(self):
debug_menu = Menu(subtitles=False)
debug_menu.add(Entry("On", action=self.set_debug, kwargs={"debug" : True}))
debug_menu.add(Entry("Off", action=self.set_debug, kwargs={"debug" : False}))
debug_menu.invoke(self.window, back=self.main_menu)
def set_debug(self, debug):
sublime.status_message("fl0w: Debug now '%s'" % debug)
self.sock.debug = debug
sublime.load_settings("fl0w.sublime-settings").set("debug", debug)
sublime.save_settings("fl0w.sublime-settings")
class Fl0wCommand(sublime_plugin.WindowCommand): class Fl0wCommand(sublime_plugin.WindowCommand):
def run(self): def run(self):
@ -277,15 +136,10 @@ class Fl0wCommand(sublime_plugin.WindowCommand):
files = os.listdir(folder) files = os.listdir(folder)
if not ".no-fl0w" in files: if not ".no-fl0w" in files:
if not ".fl0w" in files: if not ".fl0w" in files:
if sublime.ok_cancel_dialog("""We've detected that this is your first time using fl0w in your current directory.
We don't want to be responsible for any lost work so please backup your files before proceding.
(An empty project directory is recommended but not necessary.)""", "Yes"):
open(folder + "/.fl0w", 'a').close() open(folder + "/.fl0w", 'a').close()
self.window.fl0w = Fl0w(self.window) self.window.fl0w = Fl0w(self.window)
windows.append(self.window) windows.append(self.window)
self.window.fl0w.start_menu.invoke(self.window) self.window.fl0w.start_menu.invoke(self.window)
else:
sublime.error_message("fl0w can only be run once you've allowed it to operate in your current directory.")
else: else:
self.window.fl0w = Fl0w(self.window) self.window.fl0w = Fl0w(self.window)
windows.append(self.window) windows.append(self.window)
@ -294,9 +148,9 @@ class Fl0wCommand(sublime_plugin.WindowCommand):
sublime.error_message("fl0w can't be opened in your current directory (.no-fl0w file exists)") sublime.error_message("fl0w can't be opened in your current directory (.no-fl0w file exists)")
else: else:
if not self.window.fl0w.connected: if not self.window.fl0w.connected:
self.window.fl0w.start_menu.invoke(self.window) self.window.fl0w.invoke_start_menu()
else: else:
self.window.fl0w.main_menu.invoke(self.window) self.window.fl0w.invoke_main_menu()
else: else:
if hasattr(self.window, "fl0w"): if hasattr(self.window, "fl0w"):
sublime.error_message("Window setup was invalidated (Don't close or open any additional folders in a fl0w window)") sublime.error_message("Window setup was invalidated (Don't close or open any additional folders in a fl0w window)")