def disable_stalker_mode(self, timeout, agent): if G.STALKER_MODE is True: agent.temp_disable_stalk = True self.disable_stalker_mode_timeout = utils.set_timeout(self.reenable_stalker_mode, timeout) elif self.disable_stalker_mode_timeout: utils.cancel_timeout(self.disable_stalker_mode_timeout) self.disable_stalker_mode_timeout = utils.set_timeout(self.reenable_stalker_mode, timeout)
def disable_stalker_mode(timeout): global disable_stalker_mode_timeout, temp_disable_stalk if G.STALKER_MODE is True: temp_disable_stalk = True disable_stalker_mode_timeout = utils.set_timeout(reenable_stalker_mode, timeout) elif disable_stalker_mode_timeout: utils.cancel_timeout(disable_stalker_mode_timeout) disable_stalker_mode_timeout = utils.set_timeout(reenable_stalker_mode, timeout)
def main(): global remote_host, remote_port, remote_ssl msg.LOG_LEVEL = msg.LOG_LEVELS['ERROR'] usage = 'Figure it out :P' parser = optparse.OptionParser(usage=usage) parser.add_option('--url', dest='url', default=None) parser.add_option('--data', dest='data', default=None) parser.add_option('--method', dest='method', default=None) parser.add_option('--host', dest='host', default=None) parser.add_option('--port', dest='port', default=None) parser.add_option('--ssl', dest='ssl', default=None) options, args = parser.parse_args() if options.url: data = None err = False if options.data: data = json.loads(options.data) try: r = api.hit_url(options.host, options.url, data, options.method) except HTTPError as e: r = e except URLError as e: r = e err = True try: print(r.code) except Exception: err = True if err: print(r.reason) else: print(r.read().decode('utf-8')) sys.exit(err) if not options.host: sys.exit(1) remote_host = options.host remote_port = int(options.port) or remote_port remote_ssl = bool(options.ssl) or remote_ssl proxy = Server() _, port = reactor.reactor.listen(proxy, port=int(G.PROXY_PORT)) def on_ready(): print('Now listening on <%s>' % port) sys.stdout.flush() utils.set_timeout(on_ready, 100) try: reactor.reactor.block() except KeyboardInterrupt: print('ciao')
def main(): msg.LOG_LEVEL = msg.LOG_LEVELS['ERROR'] usage = 'Figure it out :P' parser = optparse.OptionParser(usage=usage) parser.add_option( '--url', dest='url', default=None ) parser.add_option( '--data', dest='data', default=None ) parser.add_option( '--method', dest='method', default=None ) options, args = parser.parse_args() if options.url: data = None err = False if options.data: data = json.loads(options.data) try: r = api.hit_url(options.url, data, options.method) except HTTPError as e: r = e except URLError as e: r = e err = True try: print(r.code) except Exception: err = True print(r.read().decode('utf-8')) sys.exit(err) proxy = Server() _, port = reactor.reactor.listen(proxy, port=int(G.PROXY_PORT)) def on_ready(): print('Now listening on <%s>' % port) sys.stdout.flush() utils.set_timeout(on_ready, 100) try: reactor.reactor.block() except KeyboardInterrupt: print('ciao')
def disable_stalker_mode(timeout): global disable_stalker_mode_timeout, temp_disable_stalk if G.STALKER_MODE is True: temp_disable_stalk = True disable_stalker_mode_timeout = utils.set_timeout( reenable_stalker_mode, timeout) elif disable_stalker_mode_timeout: utils.cancel_timeout(disable_stalker_mode_timeout) disable_stalker_mode_timeout = utils.set_timeout( reenable_stalker_mode, timeout)
def on_load(self, view, agent): msg.debug('Sublime loaded ', self.name(view)) buf = get_buf(view) if not buf: return buf_id = int(buf['id']) d = agent.on_load.get(buf_id) if not d: return del agent.on_load[buf_id] utils.set_timeout(lambda: [f() for (_, f) in d.items()], 0)
def highlight(self, ranges, user_id): msg.debug("got a highlight %s" % ranges) def doit(): msg.debug("doing timed highlights") stored_ranges = self.pending_highlights[user_id] del self.pending_highlights[user_id] self._set_highlight(stored_ranges, user_id) if user_id not in self.pending_highlights: utils.set_timeout(doit, 150) self.pending_highlights[user_id] = ranges
def user_select(self, context, prompt, choices_big, choices_small, cb): """@returns (choice, index)""" choices = choices_big if choices_small: choices = [list(x) for x in zip(choices_big, choices_small)] def _cb(i): if i >= 0: return cb(choices_big[i], i) return cb(None, -1) utils.set_timeout(context.show_quick_panel, 1, choices, _cb)
def update_view(buf, view): msg.log('Floobits synced data for consistency: %s' % buf['path']) G.VIEW_TO_HASH[view.buffer_id()] = buf['md5'] view.set_read_only(False) try: view.run_command('floo_view_replace_region', {'r': [0, view.size()], 'data': buf['buf']}) view.set_status('Floobits', 'Floobits synced data for consistency.') utils.set_timeout(lambda: view.set_status('Floobits', ''), 5000) except Exception as e: msg.error('Exception updating view: %s' % e) if 'patch' not in G.PERMS: view.set_status('Floobits', 'You don\'t have write permission. Buffer is read-only.') view.set_read_only(True)
def update(self, data): buf = self.buf = data msg.log('Floobits synced data for consistency: %s' % buf['path']) G.VIEW_TO_HASH[self.view.buffer_id()] = buf['md5'] self.view.set_read_only(False) try: self.view.run_command('floo_view_replace_region', {'r': [0, self.view.size()], 'data': buf['buf']}) self.set_status('Floobits synced data for consistency.') utils.set_timeout(self.set_status, 5000, '') except Exception as e: msg.error('Exception updating view: %s' % e) if 'patch' not in G.PERMS: self.set_status('You don\'t have write permission. Buffer is read-only.') self.view.set_read_only(True)
def _uploader(self, paths_iter): if not self.agent or not self.agent.sock: msg.error('Can\'t upload! Not connected. :(') return self.agent.tick() if self.agent.qsize() > 0: return utils.set_timeout(self._uploader, 10, paths_iter) try: p = next(paths_iter) self.upload(p) except StopIteration: msg.log('All done uploading') return return utils.set_timeout(self._uploader, 50, paths_iter)
def poll_for_move(): msg.debug('poll_for_move') win.focus_view(view) win.set_view_index(view, win.num_groups() - 1, 0) if not get_view_in_group(view.buffer_id(), focus_group): return utils.set_timeout(poll_for_move, 20) msg.debug('found view, now moving ', view.name(), win.num_groups() - 1) swap_regions(view) view.show(regions[0]) win.focus_view(view) utils.set_timeout(win.focus_group, 0, 0) try: del self.temp_ignore_highlight[buf_id] except Exception: pass
def apply_patches(self, buf, patches, username): regions = [] commands = [] for patch in patches[2]: offset = patch[0] length = patch[1] patch_text = patch[2] region = sublime.Region(offset, offset + length) regions.append(region) commands.append({'r': [offset, offset + length], 'data': patch_text}) self.view.run_command('floo_view_replace_regions', {'commands': commands}) region_key = 'floobits-patch-' + username self.view.add_regions(region_key, regions, 'floobits.patch', 'circle', sublime.DRAW_OUTLINED) utils.set_timeout(self.view.erase_regions, 2000, region_key) self.set_status('Changed by %s at %s' % (username, datetime.now().strftime('%H:%M')))
def _connect(self, attempts=0): if attempts > 500: msg.error('Connection attempt timed out.') return self.reconnect() if not self.sock: msg.debug('_connect: No socket') return try: self.sock.connect((self.host, self.port)) select.select([self.sock], [self.sock], [], 0) except socket.error as e: if e.errno == iscon_errno: pass elif e.errno in connect_errno: return utils.set_timeout(self._connect, 20, attempts + 1) else: msg.error('Error connecting:', e) return self.reconnect() if self.secure: sock_debug('SSL-wrapping socket') self.sock = ssl.wrap_socket(self.sock, ca_certs=self.cert_path, cert_reqs=ssl.CERT_REQUIRED, do_handshake_on_connect=False) self.on_connect() self.call_select = True self.select()
def update(self, buf, message=True): self.buf = buf if message: msg.log('Floobits synced data for consistency: ', buf['path']) G.VIEW_TO_HASH[self.view.buffer_id()] = buf['md5'] self.view.set_read_only(False) try: self.view.run_command('floo_view_replace_region', {'r': [0, self.view.size()], 'data': buf['buf']}) if message: self.set_status('Floobits synced data for consistency.') utils.set_timeout(self.erase_status, 5000) except Exception as e: msg.error('Exception updating view: ', str_e(e)) if 'patch' not in G.PERMS: self.set_status('You don\'t have write permission. Buffer is read-only.') self.view.set_read_only(True)
def update(self, buf, message=True): self.buf = buf if message: msg.log("Floobits synced data for consistency: ", buf["path"]) G.VIEW_TO_HASH[self.view.buffer_id()] = buf["md5"] self.view.set_read_only(False) try: self.view.run_command("floo_view_replace_region", {"r": [0, self.view.size()], "data": buf["buf"]}) if message: self.set_status("Floobits synced data for consistency.") utils.set_timeout(self.erase_status, 5000) except Exception as e: msg.error("Exception updating view: ", str_e(e)) if "patch" not in G.PERMS: self.set_status("You don't have write permission. Buffer is read-only.") self.view.set_read_only(True)
def user_select(self, context, prompt, choices_big, choices_small, cb): """@returns (choice, index)""" choices = choices_big if choices_small: choices = [list(x) for x in zip(choices_big, choices_small)] def _cb(i): if i >= 0: return cb(choices_big[i], i) return cb(None, -1) flags = 0 if hasattr(sublime, 'KEEP_OPEN_ON_FOCUS_LOST'): flags |= sublime.KEEP_OPEN_ON_FOCUS_LOST utils.set_timeout(context.show_quick_panel, 1, choices, _cb, flags)
def wait_empty_window(i): if i > 10: print('Too many failures trying to find an empty window. Using active window.') return finish(sublime.active_window()) w = get_empty_window() if w: return finish(w) return utils.set_timeout(wait_empty_window, 50, i + 1)
def update_view(buf, view): msg.log('Floobits synced data for consistency: %s' % buf['path']) G.VIEW_TO_HASH[view.buffer_id()] = buf['md5'] view.set_read_only(False) try: view.run_command('floo_view_replace_region', { 'r': [0, view.size()], 'data': buf['buf'] }) view.set_status('Floobits', 'Floobits synced data for consistency.') utils.set_timeout(lambda: view.set_status('Floobits', ''), 5000) except Exception as e: msg.error('Exception updating view: %s' % e) if 'patch' not in G.PERMS: view.set_status( 'Floobits', 'You don\'t have write permission. Buffer is read-only.') view.set_read_only(True)
def wait_empty_window(i): if i > 10: print( 'Too many failures trying to find an empty window. Using active window.' ) return finish(sublime.active_window()) w = get_empty_window() if w: return finish(w) return utils.set_timeout(wait_empty_window, 50, i + 1)
def reconnect(self): if self.reconnect_timeout: return self.cleanup() self.reconnect_delay = min(10000, int(1.5 * self.reconnect_delay)) if self.retries > 0: msg.log('Floobits: Reconnecting in %sms' % self.reconnect_delay) self.reconnect_timeout = utils.set_timeout(self.connect, self.reconnect_delay) elif self.retries == 0: sublime.error_message('Floobits Error! Too many reconnect failures. Giving up.') self.retries -= 1
def apply_patches(self, buf, patches, username): regions = [] commands = [] for patch in patches[2]: offset = patch[0] length = patch[1] patch_text = patch[2] region = sublime.Region(offset, offset + length) regions.append(region) commands.append({ 'r': [offset, offset + length], 'data': patch_text }) self.view.run_command('floo_view_replace_regions', {'commands': commands}) region_key = 'floobits-patch-' + username self.view.add_regions(region_key, regions, 'floobits.patch', 'circle', sublime.DRAW_OUTLINED) utils.set_timeout(self.view.erase_regions, 2000, region_key) self.set_status('Changed by %s at %s' % (username, datetime.now().strftime('%H:%M')))
def get_a_window(self, abs_path, cb): """opens a project in a window or something""" if PY2: yield open_workspace_window2, abs_path else: yield open_workspace_window3, abs_path while True: workspace_window = get_workspace_window(abs_path) if workspace_window is not None: break yield lambda cb: utils.set_timeout(cb, 50) cb(workspace_window)
def get_a_window(self, abs_path, cb): """opens a project in a window or something""" if PY2: yield open_workspace_window2, abs_path else: yield open_workspace_window3, abs_path while True: workspace_window = get_workspace_window(abs_path) if workspace_window is not None: break yield lambda cb: utils.set_timeout(cb, 50) # TODO: calling focus_view() on a view in the window doesn't focus the window :( cb(workspace_window)
def reconnect(self): if self.reconnect_timeout: return self.cleanup() self.reconnect_delay = min(10000, int(1.5 * self.reconnect_delay)) if self.retries > 0: msg.log('Floobits: Reconnecting in %sms' % self.reconnect_delay) self.reconnect_timeout = utils.set_timeout(self.connect, self.reconnect_delay) elif self.retries == 0: sublime.error_message( 'Floobits Error! Too many reconnect failures. Giving up.') self.retries -= 1
def apply_patches(self, buf, patches, username): regions = [] commands = [] for patch in patches[2]: offset = patch[0] length = patch[1] patch_text = patch[2] region = sublime.Region(offset, offset + length) regions.append(region) commands.append({'r': [offset, offset + length], 'data': patch_text}) self.view.run_command('floo_view_replace_regions', {'commands': commands}) region_key = 'floobits-patch-' + username self.view.add_regions(region_key, regions, 'floobits.patch', 'circle', sublime.DRAW_OUTLINED) utils.cancel_timeout(self.erase_regions_timeout) self.erase_regions_timeout = utils.set_timeout(self.view.erase_regions, 2000, region_key)
def apply_patches(self, buf, patches, username): regions = [] commands = [] for patch in patches[2]: offset = patch[0] length = patch[1] patch_text = patch[2] region = sublime.Region(offset, offset + length) regions.append(region) commands.append({"r": [offset, offset + length], "data": patch_text}) self.view.run_command("floo_view_replace_regions", {"commands": commands}) region_key = "floobits-patch-" + username self.view.add_regions(region_key, regions, "floobits.patch", "circle", sublime.DRAW_OUTLINED) utils.cancel_timeout(self.erase_regions_timeout) self.erase_regions_timeout = utils.set_timeout(self.view.erase_regions, 2000, region_key)
def _create_buf_worker(ignores, files, too_big): quota = 10 # scan until we find a minimum of 10 files while quota > 0 and ignores: ig = ignores.popleft() for new_path in Listener._scan_dir(ig): if not new_path: continue try: s = os.lstat(new_path) except Exception as e: msg.error('Error lstat()ing path %s: %s' % (new_path, unicode(e))) continue if stat.S_ISDIR(s.st_mode): ignores.append(ignore.Ignore(ig, new_path)) elif stat.S_ISREG(s.st_mode): if s.st_size > (MAX_FILE_SIZE): too_big.append(new_path) else: files.append(new_path) quota -= 1 can_upload = False for f in utils.iter_n_deque(files, 10): Listener.upload(f) can_upload = True if can_upload and G.AGENT and G.AGENT.sock: G.AGENT.select() if ignores or files: return utils.set_timeout(Listener._create_buf_worker, 25, ignores, files, too_big) if too_big: sublime.error_message( "%s file(s) were not added because they were larger than 10 megabytes: \n%s" % (len(too_big), "\t".join(too_big))) msg.log('All done syncing')
def reconnect(self): if self.reconnect_timeout: return try: self.sock.close() except Exception: pass self.workspace_info = {} self.net_buf = '' self.sock = None self.authed = False G.JOINED_WORKSPACE = False self.reconnect_delay *= 1.5 if self.reconnect_delay > 10000: self.reconnect_delay = 10000 if self.retries > 0: msg.log('Floobits: Reconnecting in %sms' % self.reconnect_delay) self.reconnect_timeout = utils.set_timeout(self.connect, int(self.reconnect_delay)) elif self.retries == 0: msg.error('Floobits Error! Too many reconnect failures. Giving up.') self.retries -= 1
def _create_buf_worker(ignores, files, too_big): quota = 10 # scan until we find a minimum of 10 files while quota > 0 and ignores: ig = ignores.popleft() for new_path in Listener._scan_dir(ig): if not new_path: continue try: s = os.stat(new_path) except Exception as e: msg.error('Error lstat()ing path %s: %s' % (new_path, unicode(e))) continue if stat.S_ISDIR(s.st_mode): ignores.append(ignore.Ignore(ig, new_path)) elif stat.S_ISREG(s.st_mode): if s.st_size > (MAX_FILE_SIZE): too_big.append(new_path) else: files.append(new_path) quota -= 1 can_upload = False for f in utils.iter_n_deque(files, 10): Listener.upload(f) can_upload = True if can_upload and G.AGENT and G.AGENT.sock: G.AGENT.select() if ignores or files: return utils.set_timeout(Listener._create_buf_worker, 25, ignores, files, too_big) if too_big: sublime.error_message("%s file(s) were not added because they were larger than 10 megabytes: \n%s" % (len(too_big), "\t".join(too_big))) msg.log('All done syncing')
def reconnect(self): if self.reconnect_timeout: return try: self.sock.close() except Exception: pass self.workspace_info = {} self.net_buf = '' self.sock = None self.authed = False G.JOINED_WORKSPACE = False self.reconnect_delay *= 1.5 if self.reconnect_delay > 10000: self.reconnect_delay = 10000 if self.retries > 0: msg.log('Floobits: Reconnecting in %sms' % self.reconnect_delay) self.reconnect_timeout = utils.set_timeout( self.connect, int(self.reconnect_delay)) elif self.retries == 0: msg.error( 'Floobits Error! Too many reconnect failures. Giving up.') self.retries -= 1
def apply_patch(patch_data): if not G.AGENT: msg.debug('Not connected. Discarding view change.') return buf_id = patch_data['id'] buf = BUFS[buf_id] if 'buf' not in buf: msg.debug('buf %s not populated yet. not patching' % buf['path']) return if buf['encoding'] == 'base64': # TODO apply binary patches return Listener.get_buf(buf_id, None) view = get_view(buf_id) if len(patch_data['patch']) == 0: msg.error('wtf? no patches to apply. server is being stupid') return msg.debug('patch is', patch_data['patch']) dmp_patches = DMP.patch_fromText(patch_data['patch']) # TODO: run this in a separate thread old_text = buf['buf'] if view and not view.is_loading(): view_text = get_text(view) if old_text == view_text: buf['forced_patch'] = False elif not buf.get('forced_patch'): patch = utils.FlooPatch(get_text(view), buf) # Update the current copy of the buffer buf['buf'] = patch.current buf['md5'] = hashlib.md5(patch.current.encode('utf-8')).hexdigest() buf['forced_patch'] = True msg.debug('forcing patch for %s' % buf['path']) G.AGENT.put(patch.to_json()) old_text = view_text else: msg.debug('forced patch is true. not sending another patch for buf %s' % buf['path']) md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest() if md5_before != patch_data['md5_before']: msg.warn('starting md5s don\'t match for %s. this is dangerous!' % buf['path']) t = DMP.patch_apply(dmp_patches, old_text) clean_patch = True for applied_patch in t[1]: if not applied_patch: clean_patch = False break if G.DEBUG: if len(t[0]) == 0: try: msg.debug('OMG EMPTY!') msg.debug('Starting data:', buf['buf']) msg.debug('Patch:', patch_data['patch']) except Exception as e: print(e) if '\x01' in t[0]: msg.debug('FOUND CRAZY BYTE IN BUFFER') msg.debug('Starting data:', buf['buf']) msg.debug('Patch:', patch_data['patch']) timeout_id = buf.get('timeout_id') if timeout_id: utils.cancel_timeout(timeout_id) if not clean_patch: msg.log('Couldn\'t patch %s cleanly.' % buf['path']) return Listener.get_buf(buf_id, view) cur_hash = hashlib.md5(t[0].encode('utf-8')).hexdigest() if cur_hash != patch_data['md5_after']: buf['timeout_id'] = utils.set_timeout(Listener.get_buf, 2000, buf_id, view) buf['buf'] = t[0] buf['md5'] = cur_hash if not view: save_buf(buf) return regions = [] commands = [] for patch in t[2]: offset = patch[0] length = patch[1] patch_text = patch[2] region = sublime.Region(offset, offset + length) regions.append(region) commands.append({'r': [offset, offset + length], 'data': patch_text}) view.run_command('floo_view_replace_regions', {'commands': commands}) region_key = 'floobits-patch-' + patch_data['username'] view.add_regions(region_key, regions, 'floobits.patch', 'circle', sublime.DRAW_OUTLINED) utils.set_timeout(view.erase_regions, 2000, region_key) view.set_status('Floobits', 'Changed by %s at %s' % (patch_data['username'], datetime.now().strftime('%H:%M')))
def user_charfield(self, context, prompt, initial, cb): """@returns String""" utils.set_timeout(context.show_input_panel, 1, prompt, initial, cb, None, None)
def highlight(self, buf_id, region_key, username, ranges, summon, clone): buf_id = int(buf_id) msg.debug(str([buf_id, region_key, username, ranges, summon, clone])) buf = self.bufs.get(buf_id) if not buf: return # TODO: move this state machine into one variable if buf_id in self.on_load: msg.debug("ignoring command until on_load is complete") return if buf_id in self.on_clone: msg.debug("ignoring command until on_clone is complete") return if buf_id in self.temp_ignore_highlight: msg.debug("ignoring command until temp_ignore_highlight is complete") return do_stuff = summon or (G.STALKER_MODE and not self.temp_disable_stalk) view = self.get_view(buf_id) if not view or view.is_loading(): if do_stuff: msg.debug("creating view") create_view(buf) self.on_load[buf_id] = lambda: self.highlight(buf_id, region_key, username, ranges, summon, False) return view = view.view regions = [] for r in ranges: regions.append(sublime.Region(*r)) def swap_regions(v): v.erase_regions(region_key) v.add_regions(region_key, regions, region_key, "dot", sublime.DRAW_OUTLINED) if not do_stuff: return swap_regions(view) win = G.WORKSPACE_WINDOW if not G.SPLIT_MODE: win.focus_view(view) swap_regions(view) # Explicit summon by another user. Center the line. if summon: view.show_at_center(regions[0]) # Avoid scrolling/jumping lots in stalker mode else: view.show(regions[0]) return focus_group = win.num_groups() - 1 view_in_group = get_view_in_group(view.buffer_id(), focus_group) if view_in_group: msg.debug("view in group") win.focus_view(view_in_group) swap_regions(view_in_group) utils.set_timeout(win.focus_group, 0, 0) return view_in_group.show(regions[0]) if not clone: msg.debug("no clone... moving ", view.buffer_id(), win.num_groups() - 1, 0) win.focus_view(view) win.set_view_index(view, win.num_groups() - 1, 0) def dont_crash_sublime(): utils.set_timeout(win.focus_group, 0, 0) swap_regions(view) return view.show(regions[0]) return utils.set_timeout(dont_crash_sublime, 0) msg.debug("View not in group... cloning") win.focus_view(view) def on_clone(buf, view): msg.debug("on clone") def poll_for_move(): msg.debug("poll_for_move") win.focus_view(view) win.set_view_index(view, win.num_groups() - 1, 0) if not get_view_in_group(view.buffer_id(), focus_group): return utils.set_timeout(poll_for_move, 20) msg.debug("found view, now moving ", view.name(), win.num_groups() - 1) swap_regions(view) view.show(regions[0]) win.focus_view(view) utils.set_timeout(win.focus_group, 0, 0) try: del self.temp_ignore_highlight[buf_id] except: pass utils.set_timeout(win.focus_group, 0, 0) poll_for_move() self.on_clone[buf_id] = on_clone self.temp_ignore_highlight[buf_id] = True win.run_command("clone_file") return win.focus_group(0)
def redraw(): def doit(): msg.debug("redrawing!") vim.command(":redraw!") utils.set_timeout(doit, 100)
def apply_patch(patch_data): if not G.AGENT: msg.debug('Not connected. Discarding view change.') return buf_id = patch_data['id'] buf = BUFS[buf_id] if 'buf' not in buf: msg.debug('buf %s not populated yet. not patching' % buf['path']) return if buf['encoding'] == 'base64': # TODO apply binary patches return Listener.get_buf(buf_id, None) view = get_view(buf_id) if len(patch_data['patch']) == 0: msg.error('wtf? no patches to apply. server is being stupid') return msg.debug('patch is', patch_data['patch']) dmp_patches = DMP.patch_fromText(patch_data['patch']) # TODO: run this in a separate thread old_text = buf['buf'] if view and not view.is_loading(): view_text = get_text(view) if old_text == view_text: buf['forced_patch'] = False elif not buf.get('forced_patch'): patch = utils.FlooPatch(get_text(view), buf) # Update the current copy of the buffer buf['buf'] = patch.current buf['md5'] = hashlib.md5( patch.current.encode('utf-8')).hexdigest() buf['forced_patch'] = True msg.debug('forcing patch for %s' % buf['path']) G.AGENT.put(patch.to_json()) old_text = view_text else: msg.debug( 'forced patch is true. not sending another patch for buf %s' % buf['path']) md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest() if md5_before != patch_data['md5_before']: msg.warn('starting md5s don\'t match for %s. this is dangerous!' % buf['path']) t = DMP.patch_apply(dmp_patches, old_text) clean_patch = True for applied_patch in t[1]: if not applied_patch: clean_patch = False break if G.DEBUG: if len(t[0]) == 0: try: msg.debug('OMG EMPTY!') msg.debug('Starting data:', buf['buf']) msg.debug('Patch:', patch_data['patch']) except Exception as e: print(e) if '\x01' in t[0]: msg.debug('FOUND CRAZY BYTE IN BUFFER') msg.debug('Starting data:', buf['buf']) msg.debug('Patch:', patch_data['patch']) timeout_id = buf.get('timeout_id') if timeout_id: utils.cancel_timeout(timeout_id) if not clean_patch: msg.log('Couldn\'t patch %s cleanly.' % buf['path']) return Listener.get_buf(buf_id, view) cur_hash = hashlib.md5(t[0].encode('utf-8')).hexdigest() if cur_hash != patch_data['md5_after']: buf['timeout_id'] = utils.set_timeout(Listener.get_buf, 2000, buf_id, view) buf['buf'] = t[0] buf['md5'] = cur_hash if not view: save_buf(buf) return regions = [] commands = [] for patch in t[2]: offset = patch[0] length = patch[1] patch_text = patch[2] region = sublime.Region(offset, offset + length) regions.append(region) commands.append({ 'r': [offset, offset + length], 'data': patch_text }) view.run_command('floo_view_replace_regions', {'commands': commands}) region_key = 'floobits-patch-' + patch_data['username'] view.add_regions(region_key, regions, 'floobits.patch', 'circle', sublime.DRAW_OUTLINED) utils.set_timeout(view.erase_regions, 2000, region_key) view.set_status( 'Floobits', 'Changed by %s at %s' % (patch_data['username'], datetime.now().strftime('%H:%M')))
def disable_follow_mode(self, timeout, agent): if G.FOLLOW_MODE is True: agent.temp_disable_follow = True utils.cancel_timeout(self.disable_follow_mode_timeout) self.disable_follow_mode_timeout = utils.set_timeout(self.reenable_follow_mode, timeout)
def _on_highlight(self, data, clone=True): region_key = 'floobits-highlight-%s' % (data['user_id']) buf_id = int(data['id']) username = data['username'] ranges = data['ranges'] summon = data.get('ping', False) user_id = str(data['user_id']) msg.debug(str([buf_id, region_key, user_id, username, ranges, summon, data.get('following'), clone])) if not ranges: msg.warn('Ignoring empty highlight from ', username) return buf = self.bufs.get(buf_id) if not buf: return # TODO: move this state machine into one variable b = self.on_load.get(buf_id) if b and b.get('highlight'): msg.debug('ignoring command until on_load is complete') return if buf_id in self.on_clone: msg.debug('ignoring command until on_clone is complete') return if buf_id in self.temp_ignore_highlight: msg.debug('ignoring command until temp_ignore_highlight is complete') return if summon or not data.get('following'): self.last_highlight = data self.last_highlight_by_user[username] = data do_stuff = summon if G.FOLLOW_MODE and not summon: if self.temp_disable_follow or data.get('following'): do_stuff = False elif G.FOLLOW_USERS: do_stuff = username in G.FOLLOW_USERS else: do_stuff = True view = self.get_view(buf_id) if not view or view.is_loading(): if do_stuff: msg.debug('creating view') create_view(buf) self.on_load[buf_id]['highlight'] = lambda: self._on_highlight(data, False) return view = view.view regions = [] for r in ranges: # TODO: add one to the ranges that have a length of zero regions.append(sublime.Region(*r)) def swap_regions(v): v.erase_regions(region_key) v.add_regions(region_key, regions, region_key, 'dot', sublime.DRAW_OUTLINED) if not do_stuff: return swap_regions(view) win = G.WORKSPACE_WINDOW if not G.SPLIT_MODE: win.focus_view(view) swap_regions(view) # Explicit summon by another user. Center the line. if summon: view.show_at_center(regions[0]) # Avoid scrolling/jumping lots in follow mode else: view.show(regions[0]) return focus_group = win.num_groups() - 1 view_in_group = get_view_in_group(view.buffer_id(), focus_group) if view_in_group: msg.debug('view in group') win.focus_view(view_in_group) swap_regions(view_in_group) utils.set_timeout(win.focus_group, 0, 0) return view_in_group.show(regions[0]) if not clone: msg.debug('no clone... moving ', view.buffer_id(), win.num_groups() - 1, 0) win.focus_view(view) win.set_view_index(view, win.num_groups() - 1, 0) def dont_crash_sublime(): utils.set_timeout(win.focus_group, 0, 0) swap_regions(view) return view.show(regions[0]) return utils.set_timeout(dont_crash_sublime, 0) msg.debug('View not in group... cloning') win.focus_view(view) def on_clone(buf, view): msg.debug('on clone') def poll_for_move(): msg.debug('poll_for_move') win.focus_view(view) win.set_view_index(view, win.num_groups() - 1, 0) if not get_view_in_group(view.buffer_id(), focus_group): return utils.set_timeout(poll_for_move, 20) msg.debug('found view, now moving ', view.name(), win.num_groups() - 1) swap_regions(view) view.show(regions[0]) win.focus_view(view) utils.set_timeout(win.focus_group, 0, 0) try: del self.temp_ignore_highlight[buf_id] except Exception: pass utils.set_timeout(win.focus_group, 0, 0) poll_for_move() self.on_clone[buf_id] = on_clone self.temp_ignore_highlight[buf_id] = True win.run_command('clone_file') return win.focus_group(0)
def dont_crash_sublime(): utils.set_timeout(win.focus_group, 0, 0) swap_regions(view) return view.show(regions[0])
def _on_highlight(self, data, clone=True): region_key = 'floobits-highlight-%s' % (data['user_id']) buf_id = int(data['id']) username = data['username'] ranges = data['ranges'] summon = data.get('summon', False) user_id = str(data['user_id']) following = data.get('following', False) msg.debug( str([ buf_id, region_key, user_id, username, ranges, summon, following, clone ])) if not ranges: msg.warn('Ignoring empty highlight from ', username) return buf = self.bufs.get(buf_id) if not buf: return # TODO: move this state machine into one variable b = self.on_load.get(buf_id) if b and b.get('highlight'): msg.debug('ignoring command until on_load is complete') return if buf_id in self.on_clone: msg.debug('ignoring command until on_clone is complete') return if buf_id in self.temp_ignore_highlight: msg.debug( 'ignoring command until temp_ignore_highlight is complete') return if summon or not following: self.last_highlight = data self.last_highlight_by_user[username] = data do_stuff = summon if G.FOLLOW_MODE and not summon: if self.temp_disable_follow or following: do_stuff = False elif G.FOLLOW_USERS: do_stuff = username in G.FOLLOW_USERS else: do_stuff = True view = self.get_view(buf_id) if not view or view.is_loading(): if do_stuff: msg.debug('creating view') create_view(buf) self.on_load[buf_id]['highlight'] = lambda: self._on_highlight( data, False) return view = view.view regions = [] for r in ranges: # Ranges with a length of zero are invisible in Sublime if r[0] == r[1]: r[1] += 1 regions.append(sublime.Region(*r)) def swap_regions(v, following=False): v.erase_regions(region_key) if following: draw = sublime.HIDDEN else: draw = sublime.DRAW_OUTLINED v.add_regions(region_key, regions, region_key, 'dot', draw) if not do_stuff: return swap_regions(view, following) win = G.WORKSPACE_WINDOW if not G.SPLIT_MODE: win.focus_view(view) swap_regions(view) # Explicit summon by another user. Center the line. if summon: view.show_at_center(regions[0]) # Avoid scrolling/jumping lots in follow mode else: view.show(regions[0]) return focus_group = win.num_groups() - 1 view_in_group = get_view_in_group(view.buffer_id(), focus_group) if view_in_group: msg.debug('view in group') win.focus_view(view_in_group) swap_regions(view_in_group) utils.set_timeout(win.focus_group, 0, 0) return view_in_group.show(regions[0]) if not clone: msg.debug('no clone... moving ', view.buffer_id(), win.num_groups() - 1, 0) win.focus_view(view) win.set_view_index(view, win.num_groups() - 1, 0) def dont_crash_sublime(): utils.set_timeout(win.focus_group, 0, 0) swap_regions(view) return view.show(regions[0]) return utils.set_timeout(dont_crash_sublime, 0) msg.debug('View not in group... cloning') win.focus_view(view) def on_clone(buf, view): msg.debug('on clone') def poll_for_move(): msg.debug('poll_for_move') win.focus_view(view) win.set_view_index(view, win.num_groups() - 1, 0) if not get_view_in_group(view.buffer_id(), focus_group): return utils.set_timeout(poll_for_move, 20) msg.debug('found view, now moving ', view.name(), win.num_groups() - 1) swap_regions(view) view.show(regions[0]) win.focus_view(view) utils.set_timeout(win.focus_group, 0, 0) try: del self.temp_ignore_highlight[buf_id] except Exception: pass utils.set_timeout(win.focus_group, 0, 0) poll_for_move() self.on_clone[buf_id] = on_clone self.temp_ignore_highlight[buf_id] = True win.run_command('clone_file') return win.focus_group(0)
def create_buf(self, path, ig=None, force=False): if G.SPARSE_MODE and not force: msg.debug("Skipping %s because user enabled sparse mode." % path) return if not utils.is_shared(path): msg.error('Skipping adding %s because it is not in shared path %s.' % (path, G.PROJECT_PATH)) return if os.path.islink(path): msg.error('Skipping adding %s because it is a symlink.' % path) return ignored = ig and ig.is_ignored(path) if ignored: msg.log('Not creating buf: %s' % (ignored)) return msg.debug('create_buf: path is %s' % path) if os.path.isdir(path): if ig is None: try: ig = ignore.build_ignores(path) except Exception as e: msg.error('Error adding %s: %s' % (path, unicode(e))) return try: paths = os.listdir(path) except Exception as e: msg.error('Error listing path %s: %s' % (path, unicode(e))) return for p in paths: p_path = os.path.join(path, p) if p[0] == '.': if p not in ignore.HIDDEN_WHITELIST: msg.log('Not creating buf for hidden path %s' % p_path) continue ignored = ig.is_ignored(p_path) if ignored: msg.log('Not creating buf: %s' % (ignored)) continue try: s = os.lstat(p_path) except Exception as e: msg.error('Error lstat()ing path %s: %s' % (path, unicode(e))) continue if stat.S_ISDIR(s.st_mode): child_ig = ignore.Ignore(ig, p_path) utils.set_timeout(self.create_buf, 0, p_path, child_ig) elif stat.S_ISREG(s.st_mode): utils.set_timeout(self.create_buf, 0, p_path, ig) return try: buf_fd = open(path, 'rb') buf = buf_fd.read() encoding = 'utf8' rel_path = utils.to_rel_path(path) existing_buf = self.get_buf_by_path(path) if existing_buf and existing_buf['md5'] == hashlib.md5(buf).hexdigest(): msg.debug('%s already exists and has the same md5. Skipping creating.' % path) return try: buf = buf.decode('utf-8') except Exception: buf = base64.b64encode(buf).decode('utf-8') encoding = 'base64' msg.log('creating buffer ', rel_path) event = { 'name': 'create_buf', 'buf': buf, 'path': rel_path, 'encoding': encoding, } self.agent.put(event) except (IOError, OSError): msg.error('Failed to open %s.' % path) except Exception as e: msg.error('Failed to create buffer %s: %s' % (path, unicode(e)))
def create_buf(self, path, ig=None, text=None): if not utils.is_shared(path): msg.error('Skipping adding %s because it is not in shared path %s.' % (path, G.PROJECT_PATH)) return if os.path.islink(path): msg.error('Skipping adding %s because it is a symlink.' % path) return ignored = ig and ig.is_ignored(path) if ignored: msg.log('Not creating buf: %s' % (ignored)) return msg.debug('create_buf: path is %s' % path) if os.path.isdir(path): if ig is None: try: ig = ignore.build_ignores(path) except Exception as e: msg.error('Error adding %s: %s' % (path, unicode(e))) return try: paths = os.listdir(path) except Exception as e: msg.error('Error listing path %s: %s' % (path, unicode(e))) return for p in paths: p_path = os.path.join(path, p) if p[0] == '.': if p not in ignore.HIDDEN_WHITELIST: msg.log('Not creating buf for hidden path %s' % p_path) continue ignored = ig.is_ignored(p_path) if ignored: msg.log('Not creating buf: %s' % (ignored)) continue try: s = os.lstat(p_path) except Exception as e: msg.error('Error lstat()ing path %s: %s' % (path, unicode(e))) continue if stat.S_ISDIR(s.st_mode): child_ig = ignore.Ignore(ig, p_path) utils.set_timeout(self.create_buf, 0, p_path, child_ig) elif stat.S_ISREG(s.st_mode): utils.set_timeout(self.create_buf, 0, p_path, ig) return try: encoding = 'utf8' if text: buf_md5 = hashlib.md5(text).hexdigest() else: buf_fd = open(path, 'rb') text = buf_fd.read() buf_fd.close() buf_md5 = hashlib.md5(text).hexdigest() try: text = text.decode('utf-8') except Exception: text = base64.b64encode(text).decode('utf-8') encoding = 'base64' rel_path = utils.to_rel_path(path) existing_buf = self.get_buf_by_path(path) if existing_buf: if existing_buf['md5'] == buf_md5: msg.debug('%s already exists and has the same md5. Skipping creating.' % path) return msg.log('setting buffer ', rel_path) self.agent.put({ 'name': 'set_buf', 'id': existing_buf['id'], 'buf': text, 'encoding': encoding, 'md5': buf_md5, }) return msg.log('creating buffer ', rel_path) event = { 'name': 'create_buf', 'buf': text, 'path': rel_path, 'encoding': encoding, 'md5': buf_md5, } self.agent.put(event) except (IOError, OSError): msg.error('Failed to open %s.' % path) except Exception as e: msg.error('Failed to create buffer %s: %s' % (path, unicode(e)))