33from .margo_agent import MargoAgent
44from .margo_common import OutputLogger , TokenCounter
55from .margo_render import render , render_src
6- from .margo_state import State , actions , client_actions , Config , _view_scope_lang , view_is_9o
6+ from .margo_state import State , actions , client_actions , Config , _view_scope_lang , view_is_9o , MgView
77from collections import namedtuple
88import glob
99import os
10+ import shlex
1011import sublime
12+ import threading
1113import time
1214
1315class MargoSingleton (object ):
@@ -29,6 +31,9 @@ def __init__(self):
2931 client_actions .CmdOutput : self ._handle_act_output ,
3032 }
3133
34+ self ._views = {}
35+ self ._view_lock = threading .Lock ()
36+
3237 def render (self , rs = None ):
3338 # ST has some locking issues due to its "thread-safe" API
3439 # don't access things like sublime.active_view() directly
@@ -110,35 +115,90 @@ def enabled(self, view):
110115 return lang in self .enabled_for_langs
111116
112117 def can_trigger_event (self , view , allow_9o = False ):
113- if not self .enabled (view ):
114- return False
118+ _pf = _dbg .pf ()
115119
116120 if view is None :
117121 return False
118122
119- if view . is_loading ( ):
123+ if not self . enabled ( view ):
120124 return False
121125
122- vs = view . settings ( )
123- if allow_9o and view_is_9o ( view ) :
126+ mgv = self . view ( view . id (), view = view )
127+ if allow_9o and mgv . is_9o :
124128 return True
125129
126- if vs . get ( 'is_widget' ) :
130+ if not mgv . is_file :
127131 return False
128132
129133 return True
130134
135+ def _preload_views (self ):
136+ for w in sublime .windows ():
137+ for v in w .views ():
138+ if v is not None :
139+ self .view (v .id (), view = v )
140+
141+ def view (self , id , view = None ):
142+ with self ._view_lock :
143+ mgv = self ._views .get (id )
144+
145+ if view is not None :
146+ if mgv is None :
147+ mgv = MgView (mg = self , view = view )
148+ self ._views [mgv .id ] = mgv
149+ else :
150+ mgv .sync (view = view )
151+
152+ return mgv
153+
154+ def _sync_view (self , event , view ):
155+ if event in ('pre_close' , 'close' ):
156+ with self ._view_lock :
157+ self ._views .pop (view .id (), None )
158+
159+ return
160+
161+ _pf = _dbg .pf (dot = event )
162+
163+ file_ids = []
164+ for w in sublime .windows ():
165+ for v in w .views ():
166+ file_ids .append (v .id ())
167+
168+ self .file_ids = file_ids
169+
170+ self .view (view .id (), view = view )
171+
131172 def event (self , name , view , handler , args ):
132- allow_9o = name in (
133- )
134- if not self .can_trigger_event (view , allow_9o = allow_9o ):
173+ if view is None :
135174 return None
136175
137- try :
138- return handler (* args )
139- except Exception :
140- gs .error_traceback ('mg.event:%s' % handler )
141- return None
176+ _pf = _dbg .pf (dot = name )
177+
178+ def handle_event (gt = 0 ):
179+ if gt > 0 :
180+ _pf .gt = gt
181+
182+ self ._sync_view (name , view )
183+
184+ if not self .can_trigger_event (view ):
185+ return None
186+
187+ try :
188+ return handler (* args )
189+ except Exception :
190+ gs .error_traceback ('mg.event:%s' % handler )
191+ return None
192+
193+ blocking = (
194+ 'pre_save' ,
195+ 'query_completions' ,
196+ )
197+
198+ if name in blocking :
199+ return handle_event (gt = 0.100 )
200+
201+ sublime .set_timeout (handle_event )
142202
143203 def agent_starting (self , ag ):
144204 if ag is not self .agent :
@@ -172,20 +232,69 @@ def send(self, *, actions=[], cb=None, view=None):
172232 self ._send_start ()
173233 return self .agent .send (actions = actions , cb = cb , view = view )
174234
235+ def on_new (self , view ):
236+ pass
237+
238+ def on_pre_close (self , view ):
239+ pass
240+
175241 def on_query_completions (self , view , prefix , locations ):
176242 _ , lang = _view_scope_lang (view , 0 )
177243 if not lang :
178244 return None
179245
180- rs = self .send (view = view , actions = [actions .QueryCompletions ]).wait (0.500 )
246+ act = actions .QueryCompletions
247+ if lang == 'cmd-prompt' :
248+ act = self ._cmd_completions_act (view , prefix , locations )
249+ if not act :
250+ return None
251+
252+ view = gs .active_view (win = view .window ())
253+ if view is None :
254+ return None
255+
256+ rs = self .send (view = view , actions = [act ]).wait (0.500 )
181257 if not rs :
182258 self .out .println ('aborting QueryCompletions. it did not respond in time' )
183259 return None
184260
261+ if rs .error :
262+ self .out .println ('completion error: %s: %s' % (act , rs .error ))
263+ return
264+
185265 cl = [c .entry () for c in rs .state .completions ]
186266 opts = rs .state .config .auto_complete_opts
187267 return (cl , opts ) if opts != 0 else cl
188268
269+ def _cmd_completions_act (self , view , prefix , locations ):
270+ pos = locations [0 ]
271+ line = view .line (pos )
272+ src = view .substr (line )
273+ if '#' not in src :
274+ return None
275+
276+ i = src .index ('#' )
277+ while src [i ] == ' ' or src [i ] == '#' :
278+ i += 1
279+
280+ src = src [i :]
281+ pos = pos - line .begin () - i
282+ name = ''
283+ args = shlex .split (src )
284+ if args :
285+ name = args [0 ]
286+ args = args [1 :]
287+
288+ act = actions .QueryCmdCompletions .copy ()
289+ act ['Data' ] = {
290+ 'Pos' : pos ,
291+ 'Src' : src ,
292+ 'Name' : name ,
293+ 'Args' : args ,
294+ }
295+
296+ return act
297+
189298 def on_activated (self , view ):
190299 self .queue (view = view , actions = [actions .ViewActivated ])
191300
@@ -269,6 +378,7 @@ def ext_fn():
269378
270379def gs_init (_ ):
271380 mg ._ready = True
381+ sublime .set_timeout (mg ._preload_views )
272382 mg .start ()
273383
274384def gs_fini (_ ):
0 commit comments