-
Notifications
You must be signed in to change notification settings - Fork 184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add event loop #2576
base: main
Are you sure you want to change the base?
Add event loop #2576
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import sublime_plugin | ||
from .event_loop import setup_event_loop, shutdown_event_loop | ||
|
||
setup_event_loop() | ||
|
||
|
||
class EventLoopListener(sublime_plugin.EventListener): | ||
def on_exit(self) -> None: | ||
shutdown_event_loop() | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import sublime_plugin | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this file will be deleted. |
||
from LSP.event_loop import run_future | ||
from .view_async import open_view, save_view | ||
from pathlib import Path | ||
|
||
class LspExampleAsyncCommand(sublime_plugin.TextCommand): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you want to try this command, here is a keybinding |
||
def run(self, edit): | ||
print('1. this will be printed first') | ||
run_future(self.open_edit_save_and_close_file()) | ||
print('2. This will printed second') | ||
|
||
async def open_edit_save_and_close_file(self): | ||
print('3. than this') | ||
w = self.view.window() | ||
if not w: | ||
return | ||
file_name = self.view.file_name() | ||
if not file_name: | ||
return | ||
readme_file = str((Path(file_name) / Path('../README.md')).resolve()) | ||
view = await open_view(readme_file, w) | ||
view.run_command("append", { | ||
'characters': "LspExampleAsyncCommand added this" + '\n\n', | ||
}) | ||
await save_view(view) | ||
# the view is saved at this point and safe to be closed | ||
view.close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These few lines illustrate one of the benefits. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from __future__ import annotations | ||
import asyncio | ||
from threading import Thread | ||
from typing import Any, Awaitable | ||
from ..plugin.core.logging import debug | ||
|
||
__loop: asyncio.AbstractEventLoop | None = None | ||
__thread: Thread | None = None | ||
__active_tasks: set[asyncio.Task] = set() | ||
|
||
|
||
def run_future(future: Awaitable): | ||
global __loop, __active_tasks | ||
if __loop: | ||
task = asyncio.ensure_future(future, loop=__loop) | ||
__active_tasks.add(task) | ||
task.add_done_callback(__active_tasks.discard) # Remove once done | ||
__loop.call_soon_threadsafe(lambda: task) | ||
|
||
|
||
def setup_event_loop(): | ||
debug('loop: starting') | ||
global __loop | ||
global __thread | ||
if __loop: | ||
debug('loop: already created') | ||
return | ||
__loop = asyncio.new_event_loop() | ||
__thread = Thread(target=__loop.run_forever) | ||
__thread.start() | ||
debug("loop: started") | ||
|
||
|
||
def shutdown_event_loop(): | ||
debug("loop: stopping") | ||
global __loop | ||
global __thread | ||
|
||
if not __loop: | ||
debug('no loop to shutdown.') | ||
|
||
def __shutdown(): | ||
for task in asyncio.all_tasks(): | ||
task.cancel() | ||
asyncio.get_event_loop().stop() | ||
|
||
if __loop and __thread: | ||
__loop.call_soon_threadsafe(__shutdown) | ||
__thread.join() | ||
__loop.run_until_complete(__loop.shutdown_asyncgens()) | ||
__loop.close() | ||
__loop = None | ||
__thread = None | ||
debug("loop: stopped") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
from __future__ import annotations | ||
import asyncio | ||
from LSP.event_loop import run_future | ||
import sublime | ||
import sublime_plugin | ||
|
||
open_view_futures_map: dict[str, asyncio.Future[sublime.View]] = {} | ||
on_save_futures_map: dict[str, asyncio.Future] = {} | ||
|
||
|
||
# todo rename | ||
class AsyncViewListenerThingy(sublime_plugin.EventListener): | ||
def on_load(self, view): | ||
run_future(self.async_load(view)) | ||
|
||
def on_post_save(self, view): | ||
run_future(self.async_on_save(view)) | ||
|
||
async def async_load(self, view: sublime.View): | ||
file_name = view.file_name() or "" | ||
if file_name not in open_view_futures_map: | ||
return | ||
future = open_view_futures_map.pop(file_name) | ||
if future: | ||
future.set_result(view) | ||
|
||
async def async_on_save(self, view: sublime.View): | ||
file_name = view.file_name() or "" | ||
if file_name not in on_save_futures_map: | ||
return | ||
future = on_save_futures_map.pop(file_name) | ||
if future: | ||
future.set_result(None) | ||
|
||
|
||
async def save_view(view: sublime.View) -> None: | ||
file_name = view.file_name() or "" | ||
if not file_name: | ||
view.run_command('save') | ||
return | ||
future: asyncio.Future = asyncio.Future() | ||
on_save_futures_map[file_name] = future | ||
view.run_command('save') | ||
await future | ||
|
||
|
||
async def open_view( | ||
file_name, window: sublime.Window, | ||
flags: sublime.NewFileFlags = sublime.NewFileFlags.NONE | ||
) -> sublime.View: | ||
future: asyncio.Future[sublime.View] = asyncio.Future() | ||
open_view_futures_map[file_name] = future | ||
active_view = window.active_view() | ||
view = window.find_open_file(file_name) | ||
if view: | ||
future.set_result(view) | ||
else: | ||
window.open_file(file_name, flags=flags) | ||
res = await future | ||
if active_view: | ||
window.focus_view(active_view) | ||
return res |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the event loop starts, when the LSP module is loaded,
and the loop ends when ST exits.