예제 #1
0
 def on_delete_buf(self, data):
     buf_id = int(data['id'])
     del self.FLOO_BUFS[buf_id]
     del self.FLOO_PATHS_TO_BUFS[data['path']]
     path = utils.get_full_path(data['path'])
     utils.rm(path)
     msg.warn('deleted %s because %s told me too.' % (path, data.get('username', 'the internet')))
예제 #2
0
 def on_delete_buf(self, data):
     # TODO: somehow tell the user about this. maybe delete on disk too?
     del self.FLOO_BUFS[data['id']]
     path = utils.get_full_path(data['path'])
     if not G.DELETE_LOCAL_FILES:
         msg.log('Not deleting %s because delete_local_files is disabled' % path)
         return
     utils.rm(path)
     msg.warn('deleted %s because %s told me to.' % (path, data.get('username', 'the internet')))
예제 #3
0
 def get_buf(self, buf_id, view=None):
     req = {'name': 'get_buf', 'id': buf_id}
     buf = self.bufs[buf_id]
     msg.warn('Syncing buffer %s for consistency.' % buf['path'])
     if 'buf' in buf:
         del buf['buf']
     if view:
         view.set_read_only(True)
         view.set_status('Floobits',
                         'Floobits locked this file until it is synced.')
     G.AGENT.send(req)
예제 #4
0
 def get_buf(buf_id, view=None):
     req = {
         'name': 'get_buf',
         'id': buf_id
     }
     buf = BUFS[buf_id]
     msg.warn('Syncing buffer %s for consistency.' % buf['path'])
     if 'buf' in buf:
         del buf['buf']
     if view:
         view.set_read_only(True)
         view.set_status('Floobits', 'Floobits locked this file until it is synced.')
     G.AGENT.put(req)
예제 #5
0
 def users_in_workspace(self):
     if not G.AGENT:
         return msg.warn('Not connected to a workspace.')
     vim.command('echom "Users connected to %s"' % (G.AGENT.workspace, ))
     for user in G.AGENT.workspace_info['users'].values():
         vim.command('echom "  %s connected with %s on %s"' %
                     (user['username'], user['client'], user['platform']))
예제 #6
0
 def part_workspace(self):
     if not G.AGENT:
         return msg.warn(
             'Unable to leave workspace: You are not joined to a workspace.'
         )
     stop_everything()
     msg.log('You left the workspace.')
예제 #7
0
 def say_something(self):
     if not G.AGENT:
         return msg.warn('Not connected to a workspace.')
     something = self.vim_input(
         'Say something in %s: ' % (G.AGENT.workspace, ), '')
     if something:
         G.AGENT.send_msg(something)
예제 #8
0
    def maybe_new_file(self):
        path = self.vim.current.buffer.name
        if path is None or path == '':
            msg.debug('get:buf buffer has no filename')
            return
        if not os.path.exists(path):
            return
        if not utils.is_shared(path):
            msg.debug('get_buf: %s is not shared' % path)
            return

        buf = G.AGENT.get_buf_by_path(path)
        if not buf:
            if not G.IGNORE:
                msg.warn('G.IGNORE is not set. Uploading anyway.')
                G.AGENT.upload(path)
            if G.IGNORE and not G.IGNORE.is_ignored(path, None, True):
                G.AGENT.upload(path)
    def tick(self):
        if 'patch' not in G.PERMS:
            self.views_changed = []
        elif not self.joined_workspace:
            msg.debug('Not connected. Discarding view change.')
            self.views_changed = []
        else:
            reported = set()
            to_send = []
            while self.views_changed:
                name, v, buf = self.views_changed.pop()
                if 'buf' not in buf:
                    msg.debug('No data for buf ', buf['id'], ' ', buf['path'],
                              ' yet. Skipping sending patch')
                    continue
                view = View(v, buf)
                if view.is_loading():
                    msg.debug('View for buf ', buf['id'],
                              ' is not ready. Ignoring change event.')
                    continue
                if view.native_id in reported:
                    continue
                reported.add((name, view.native_id))
                if name == 'patch':
                    patch = utils.FlooPatch(view.get_text(), buf)
                    # Update the current copy of the buffer
                    buf['buf'] = patch.current
                    buf['md5'] = patch.md5_after
                    self.send(patch.to_json())
                    continue
                if name == 'saved':
                    to_send.append({'name': 'saved', 'id': buf['id']})
                    continue
                msg.warn('Discarding unknown event in views_changed:', name)

            for s in to_send:
                self.send(s)

        self._status_timeout += 1
        if self._status_timeout > (2000 / G.TICK_TIME):
            self.update_status_msg()
예제 #10
0
    def tick(self):
        if 'patch' not in G.PERMS:
            self.views_changed = []
        elif not self.joined_workspace:
            msg.debug('Not connected. Discarding view change.')
            self.views_changed = []
        else:
            reported = set()
            to_send = []
            while self.views_changed:
                name, v, buf = self.views_changed.pop()
                if 'buf' not in buf:
                    msg.debug('No data for buf ', buf['id'], ' ', buf['path'], ' yet. Skipping sending patch')
                    continue
                view = View(v, buf)
                if view.is_loading():
                    msg.debug('View for buf ', buf['id'], ' is not ready. Ignoring change event.')
                    continue
                if view.native_id in reported:
                    continue
                reported.add((name, view.native_id))
                if name == 'patch':
                    patch = utils.FlooPatch(view.get_text(), buf)
                    # Update the current copy of the buffer
                    buf['buf'] = patch.current
                    buf['md5'] = patch.md5_after
                    self.send(patch.to_json())
                    continue
                if name == 'saved':
                    to_send.append({'name': 'saved', 'id': buf['id']})
                    continue
                msg.warn('Discarding unknown event in views_changed:', name)

            for s in to_send:
                self.send(s)

        self._status_timeout += 1
        if self._status_timeout > (2000 / G.TICK_TIME):
            self.update_status_msg()
예제 #11
0
    def on_modified(self, view, agent):
        buf = is_view_loaded(view)
        if not buf:
            return

        text = get_text(view)
        if buf['encoding'] != 'utf8':
            return msg.warn('Floobits does not support patching binary files at this time')

        text = text.encode('utf-8')
        view_md5 = hashlib.md5(text).hexdigest()
        bid = view.buffer_id()
        buf['forced_patch'] = False
        if view_md5 == G.VIEW_TO_HASH.get(bid):
            self._highlights.add(bid)
            return

        G.VIEW_TO_HASH[view.buffer_id()] = view_md5
        msg.debug('changed view ', buf['path'], ' buf id ', buf['id'])
        self.disable_follow_mode(2000)
        agent.views_changed.append(('patch', view, buf))
예제 #12
0
    def on_modified(self, view, agent):
        buf = is_view_loaded(view)
        if not buf:
            return

        text = get_text(view)
        if buf['encoding'] != 'utf8':
            return msg.warn('Floobits does not support patching binary files at this time')

        text = text.encode('utf-8')
        view_md5 = hashlib.md5(text).hexdigest()
        if view_md5 == G.VIEW_TO_HASH.get(view.buffer_id()):
            return

        G.VIEW_TO_HASH[view.buffer_id()] = view_md5

        msg.debug('changed view %s buf id %s' % (buf['path'], buf['id']))

        self.disable_follow_mode(2000)
        buf['forced_patch'] = False
        agent.views_changed.append((view, buf))
예제 #13
0
    def on_modified(self, view):
        buf = is_view_loaded(view)
        if not buf:
            return

        text = get_text(view)
        if buf['encoding'] != 'utf8':
            return msg.warn('Floobits does not support patching binary files at this time')

        text = text.encode('utf-8')
        view_md5 = hashlib.md5(text).hexdigest()
        if view_md5 == G.VIEW_TO_HASH.get(view.buffer_id()):
            return

        G.VIEW_TO_HASH[view.buffer_id()] = view_md5

        msg.debug('changed view %s buf id %s' % (buf['path'], buf['id']))

        disable_stalker_mode(2000)
        buf['forced_patch'] = False
        self.views_changed.append((view, buf))
예제 #14
0
    def on_modified(self, view, agent, activated=False, *args):
        buf = is_view_loaded(view)
        if not buf:
            return

        text = get_text(view)
        if buf['encoding'] != 'utf8':
            return msg.warn(
                'Floobits does not support patching binary files at this time')

        text = text.encode('utf-8')
        view_md5 = hashlib.md5(text).hexdigest()
        bid = view.buffer_id()
        buf['forced_patch'] = False
        if view_md5 == G.VIEW_TO_HASH.get(bid):
            self._highlights.add(bid)
            return

        G.VIEW_TO_HASH[view.buffer_id()] = view_md5
        msg.debug('changed view ', buf['path'], ' buf id ', buf['id'])
        if not activated:
            self.disable_follow_mode(2000)
        agent.views_changed.append(('patch', view, buf))
예제 #15
0
 def users_in_workspace(self):
     if not G.AGENT:
         return msg.warn('Not connected to a workspace.')
     self.vim.command('echom "Users connected to %s"' % (G.AGENT.workspace,))
     for user in G.AGENT.workspace_info['users'].values():
         self.vim.command('echom "  %s connected with %s on %s"' % (user['username'], user['client'], user['platform']))
예제 #16
0
 def say_something(self):
     if not G.AGENT:
         return msg.warn('Not connected to a workspace.')
     something = self.vim_input('Say something in %s: ' % (G.AGENT.workspace,), '')
     if something:
         G.AGENT.send_msg(something)
    def handler(self, name, data):
        if name == 'patch':
            Listener.apply_patch(data)
        elif name == 'get_buf':
            buf_id = data['id']
            buf = listener.BUFS.get(buf_id)
            if not buf:
                return msg.warn('no buf found: %s.  Hopefully you didn\'t need that' % data)
            timeout_id = buf.get('timeout_id')
            if timeout_id:
                utils.cancel_timeout(timeout_id)

            if data['encoding'] == 'base64':
                data['buf'] = base64.b64decode(data['buf'])
            # forced_patch doesn't exist in data, so this is equivalent to buf['forced_patch'] = False
            listener.BUFS[buf_id] = data
            view = listener.get_view(buf_id)
            if view:
                Listener.update_view(data, view)
            else:
                listener.save_buf(data)
        elif name == 'create_buf':
            if data['encoding'] == 'base64':
                data['buf'] = base64.b64decode(data['buf'])
            listener.BUFS[data['id']] = data
            listener.PATHS_TO_IDS[data['path']] = data['id']
            listener.save_buf(data)
            cb = listener.CREATE_BUF_CBS.get(data['path'])
            if cb:
                del listener.CREATE_BUF_CBS[data['path']]
                try:
                    cb(data['id'])
                except Exception as e:
                    print(e)
        elif name == 'rename_buf':
            del listener.PATHS_TO_IDS[data['old_path']]
            listener.PATHS_TO_IDS[data['path']] = data['id']
            new = utils.get_full_path(data['path'])
            old = utils.get_full_path(data['old_path'])
            new_dir = os.path.split(new)[0]
            if new_dir:
                utils.mkdir(new_dir)
            os.rename(old, new)
            view = listener.get_view(data['id'])
            if view:
                view.retarget(new)
            listener.BUFS[data['id']]['path'] = data['path']
        elif name == 'delete_buf':
            path = utils.get_full_path(data['path'])
            listener.delete_buf(data['id'])
            try:
                utils.rm(path)
            except Exception:
                pass
            user_id = data.get('user_id')
            username = self.get_username_by_id(user_id)
            msg.log('%s deleted %s' % (username, path))
        elif name == 'room_info':
            Listener.reset()
            G.JOINED_WORKSPACE = True
            # Success! Reset counter
            self.reconnect_delay = self.INITIAL_RECONNECT_DELAY
            self.retries = self.MAX_RETRIES

            self.workspace_info = data
            G.PERMS = data['perms']

            if 'patch' not in data['perms']:
                msg.log('No patch permission. Setting buffers to read-only')
                if sublime.ok_cancel_dialog('You don\'t have permission to edit this workspace. All files will be read-only.\n\nDo you want to request edit permission?'):
                    self.put({'name': 'request_perms', 'perms': ['edit_room']})

            project_json = {
                'folders': [
                    {'path': G.PROJECT_PATH}
                ]
            }

            utils.mkdir(G.PROJECT_PATH)
            with open(os.path.join(G.PROJECT_PATH, '.sublime-project'), 'wb') as project_fd:
                project_fd.write(json.dumps(project_json, indent=4, sort_keys=True).encode('utf-8'))

            floo_json = {
                'url': utils.to_workspace_url({
                    'host': self.host,
                    'owner': self.owner,
                    'port': self.port,
                    'workspace': self.workspace,
                    'secure': self.secure,
                })
            }
            with open(os.path.join(G.PROJECT_PATH, '.floo'), 'w') as floo_fd:
                floo_fd.write(json.dumps(floo_json, indent=4, sort_keys=True))

            for buf_id, buf in data['bufs'].items():
                buf_id = int(buf_id)  # json keys must be strings
                buf_path = utils.get_full_path(buf['path'])
                new_dir = os.path.dirname(buf_path)
                utils.mkdir(new_dir)
                listener.BUFS[buf_id] = buf
                listener.PATHS_TO_IDS[buf['path']] = buf_id
                # TODO: stupidly inefficient
                view = listener.get_view(buf_id)
                if view and not view.is_loading() and buf['encoding'] == 'utf8':
                    view_text = listener.get_text(view)
                    view_md5 = hashlib.md5(view_text.encode('utf-8')).hexdigest()
                    if view_md5 == buf['md5']:
                        msg.debug('md5 sum matches view. not getting buffer %s' % buf['path'])
                        buf['buf'] = view_text
                        G.VIEW_TO_HASH[view.buffer_id()] = view_md5
                    elif self.get_bufs:
                        Listener.get_buf(buf_id)
                    #TODO: maybe send patch here?
                else:
                    try:
                        buf_fd = open(buf_path, 'rb')
                        buf_buf = buf_fd.read()
                        md5 = hashlib.md5(buf_buf).hexdigest()
                        if md5 == buf['md5']:
                            msg.debug('md5 sum matches. not getting buffer %s' % buf['path'])
                            if buf['encoding'] == 'utf8':
                                buf_buf = buf_buf.decode('utf-8')
                            buf['buf'] = buf_buf
                        elif self.get_bufs:
                            Listener.get_buf(buf_id)
                    except Exception as e:
                        msg.debug('Error calculating md5:', e)
                        Listener.get_buf(buf_id)

            msg.log('Successfully joined workspace %s/%s' % (self.owner, self.workspace))

            temp_data = data.get('temp_data', {})
            hangout = temp_data.get('hangout', {})
            hangout_url = hangout.get('url')
            if hangout_url:
                self.prompt_join_hangout(hangout_url)

            if self.on_room_info:
                self.on_room_info()
                self.on_room_info = None
        elif name == 'user_info':
            user_id = str(data['user_id'])
            user_info = data['user_info']
            self.workspace_info['users'][user_id] = user_info
            if user_id == str(self.workspace_info['user_id']):
                G.PERMS = user_info['perms']
        elif name == 'join':
            msg.log('%s joined the workspace' % data['username'])
            user_id = str(data['user_id'])
            self.workspace_info['users'][user_id] = data
        elif name == 'part':
            msg.log('%s left the workspace' % data['username'])
            user_id = str(data['user_id'])
            try:
                del self.workspace_info['users'][user_id]
            except Exception as e:
                print('Unable to delete user %s from user list' % (data))
            region_key = 'floobits-highlight-%s' % (user_id)
            for window in sublime.windows():
                for view in window.views():
                    view.erase_regions(region_key)
        elif name == 'highlight':
            region_key = 'floobits-highlight-%s' % (data['user_id'])
            Listener.highlight(data['id'], region_key, data['username'], data['ranges'], data.get('ping', False))
        elif name == 'set_temp_data':
            hangout_data = data.get('data', {})
            hangout = hangout_data.get('hangout', {})
            hangout_url = hangout.get('url')
            if hangout_url:
                self.prompt_join_hangout(hangout_url)
        elif name == 'saved':
            try:
                buf = listener.BUFS[data['id']]
                username = self.get_username_by_id(data['user_id'])
                msg.log('%s saved buffer %s' % (username, buf['path']))
            except Exception as e:
                msg.error(str(e))
        elif name == 'request_perms':
            print(data)
            user_id = str(data.get('user_id'))
            username = self.get_username_by_id(user_id)
            if not username:
                return msg.debug('Unknown user for id %s. Not handling request_perms event.' % user_id)
            perm_mapping = {
                'edit_room': 'edit',
                'admin_room': 'admin',
            }
            perms = data.get('perms')
            perms_str = ''.join([perm_mapping.get(p) for p in perms])
            prompt = 'User %s is requesting %s permission for this room.' % (username, perms_str)
            message = data.get('message')
            if message:
                prompt += '\n\n%s says: %s' % (username, message)
            prompt += '\n\nDo you want to grant them permission?'
            confirm = bool(sublime.ok_cancel_dialog(prompt))
            if confirm:
                action = 'add'
            else:
                action = 'reject'
            self.put({
                'name': 'perms',
                'action': action,
                'user_id': user_id,
                'perms': perms
            })
        elif name == 'perms':
            action = data['action']
            user_id = str(data['user_id'])
            user = self.workspace_info['users'].get(user_id)
            if user is None:
                msg.log('No user for id %s. Not handling perms event' % user_id)
                return
            perms = set(user['perms'])
            if action == 'add':
                perms |= set(data['perms'])
            elif action == 'remove':
                perms -= set(data['perms'])
            else:
                return
            user['perms'] = list(perms)
            if user_id == self.workspace_info['user_id']:
                G.PERMS = perms
        elif name == 'msg':
            self.on_msg(data)
        else:
            msg.debug('unknown name!', name, 'data:', data)
예제 #18
0
 def part_workspace(self):
     if not G.AGENT:
         return msg.warn('Unable to leave workspace: You are not joined to a workspace.')
     stop_everything()
     msg.log('You left the workspace.')
예제 #19
0
    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')))
예제 #20
0
    def handler(self, name, data):
        if name == "patch":
            Listener.apply_patch(data)
        elif name == "get_buf":
            buf_id = data["id"]
            buf = listener.BUFS.get(buf_id)
            if not buf:
                return msg.warn("no buf found: %s.  Hopefully you didn't need that" % data)
            timeout_id = buf.get("timeout_id")
            if timeout_id:
                utils.cancel_timeout(timeout_id)

            if data["encoding"] == "base64":
                data["buf"] = base64.b64decode(data["buf"])
            # forced_patch doesn't exist in data, so this is equivalent to buf['forced_patch'] = False
            listener.BUFS[buf_id] = data
            view = listener.get_view(buf_id)
            if view:
                Listener.update_view(data, view)
            else:
                listener.save_buf(data)
        elif name == "create_buf":
            if data["encoding"] == "base64":
                data["buf"] = base64.b64decode(data["buf"])
            listener.BUFS[data["id"]] = data
            listener.PATHS_TO_IDS[data["path"]] = data["id"]
            listener.save_buf(data)
            cb = listener.CREATE_BUF_CBS.get(data["path"])
            if cb:
                del listener.CREATE_BUF_CBS[data["path"]]
                try:
                    cb(data["id"])
                except Exception as e:
                    print(e)
        elif name == "rename_buf":
            del listener.PATHS_TO_IDS[data["old_path"]]
            listener.PATHS_TO_IDS[data["path"]] = data["id"]
            new = utils.get_full_path(data["path"])
            old = utils.get_full_path(data["old_path"])
            new_dir = os.path.split(new)[0]
            if new_dir:
                utils.mkdir(new_dir)
            os.rename(old, new)
            view = listener.get_view(data["id"])
            if view:
                view.retarget(new)
            listener.BUFS[data["id"]]["path"] = data["path"]
        elif name == "delete_buf":
            path = utils.get_full_path(data["path"])
            listener.delete_buf(data["id"])
            try:
                utils.rm(path)
            except Exception:
                pass
            user_id = data.get("user_id")
            username = self.get_username_by_id(user_id)
            msg.log("%s deleted %s" % (username, path))
        elif name == "room_info":
            Listener.reset()
            G.JOINED_WORKSPACE = True
            # Success! Reset counter
            self.reconnect_delay = self.INITIAL_RECONNECT_DELAY
            self.retries = self.MAX_RETRIES

            self.workspace_info = data
            G.PERMS = data["perms"]

            if "patch" not in data["perms"]:
                msg.log("No patch permission. Setting buffers to read-only")
                if sublime.ok_cancel_dialog(
                    "You don't have permission to edit this workspace. All files will be read-only.\n\nDo you want to request edit permission?"
                ):
                    self.put({"name": "request_perms", "perms": ["edit_room"]})

            project_json = {"folders": [{"path": G.PROJECT_PATH}]}

            utils.mkdir(G.PROJECT_PATH)
            with open(os.path.join(G.PROJECT_PATH, ".sublime-project"), "wb") as project_fd:
                project_fd.write(json.dumps(project_json, indent=4, sort_keys=True).encode("utf-8"))

            floo_json = {
                "url": utils.to_workspace_url(
                    {
                        "host": self.host,
                        "owner": self.owner,
                        "port": self.port,
                        "workspace": self.workspace,
                        "secure": self.secure,
                    }
                )
            }
            with open(os.path.join(G.PROJECT_PATH, ".floo"), "w") as floo_fd:
                floo_fd.write(json.dumps(floo_json, indent=4, sort_keys=True))

            for buf_id, buf in data["bufs"].items():
                buf_id = int(buf_id)  # json keys must be strings
                buf_path = utils.get_full_path(buf["path"])
                new_dir = os.path.dirname(buf_path)
                utils.mkdir(new_dir)
                listener.BUFS[buf_id] = buf
                listener.PATHS_TO_IDS[buf["path"]] = buf_id
                # TODO: stupidly inefficient
                view = listener.get_view(buf_id)
                if view and not view.is_loading() and buf["encoding"] == "utf8":
                    view_text = listener.get_text(view)
                    view_md5 = hashlib.md5(view_text.encode("utf-8")).hexdigest()
                    if view_md5 == buf["md5"]:
                        msg.debug("md5 sum matches view. not getting buffer %s" % buf["path"])
                        buf["buf"] = view_text
                        G.VIEW_TO_HASH[view.buffer_id()] = view_md5
                    elif self.get_bufs:
                        Listener.get_buf(buf_id)
                    # TODO: maybe send patch here?
                else:
                    try:
                        buf_fd = open(buf_path, "rb")
                        buf_buf = buf_fd.read()
                        md5 = hashlib.md5(buf_buf).hexdigest()
                        if md5 == buf["md5"]:
                            msg.debug("md5 sum matches. not getting buffer %s" % buf["path"])
                            if buf["encoding"] == "utf8":
                                buf_buf = buf_buf.decode("utf-8")
                            buf["buf"] = buf_buf
                        elif self.get_bufs:
                            Listener.get_buf(buf_id)
                    except Exception as e:
                        msg.debug("Error calculating md5:", e)
                        Listener.get_buf(buf_id)

            msg.log("Successfully joined workspace %s/%s" % (self.owner, self.workspace))

            temp_data = data.get("temp_data", {})
            hangout = temp_data.get("hangout", {})
            hangout_url = hangout.get("url")
            if hangout_url:
                self.prompt_join_hangout(hangout_url)

            if self.on_room_info:
                self.on_room_info()
                self.on_room_info = None
        elif name == "user_info":
            user_id = str(data["user_id"])
            user_info = data["user_info"]
            self.workspace_info["users"][user_id] = user_info
            if user_id == str(self.workspace_info["user_id"]):
                G.PERMS = user_info["perms"]
        elif name == "join":
            msg.log("%s joined the workspace" % data["username"])
            user_id = str(data["user_id"])
            self.workspace_info["users"][user_id] = data
        elif name == "part":
            msg.log("%s left the workspace" % data["username"])
            user_id = str(data["user_id"])
            try:
                del self.workspace_info["users"][user_id]
            except Exception as e:
                print("Unable to delete user %s from user list" % (data))
            region_key = "floobits-highlight-%s" % (user_id)
            for window in sublime.windows():
                for view in window.views():
                    view.erase_regions(region_key)
        elif name == "highlight":
            region_key = "floobits-highlight-%s" % (data["user_id"])
            Listener.highlight(data["id"], region_key, data["username"], data["ranges"], data.get("ping", False))
        elif name == "set_temp_data":
            hangout_data = data.get("data", {})
            hangout = hangout_data.get("hangout", {})
            hangout_url = hangout.get("url")
            if hangout_url:
                self.prompt_join_hangout(hangout_url)
        elif name == "saved":
            try:
                buf = listener.BUFS[data["id"]]
                username = self.get_username_by_id(data["user_id"])
                msg.log("%s saved buffer %s" % (username, buf["path"]))
            except Exception as e:
                msg.error(str(e))
        elif name == "request_perms":
            print(data)
            user_id = str(data.get("user_id"))
            username = self.get_username_by_id(user_id)
            if not username:
                return msg.debug("Unknown user for id %s. Not handling request_perms event." % user_id)
            perm_mapping = {"edit_room": "edit", "admin_room": "admin"}
            perms = data.get("perms")
            perms_str = "".join([perm_mapping.get(p) for p in perms])
            prompt = "User %s is requesting %s permission for this room." % (username, perms_str)
            message = data.get("message")
            if message:
                prompt += "\n\n%s says: %s" % (username, message)
            prompt += "\n\nDo you want to grant them permission?"
            confirm = bool(sublime.ok_cancel_dialog(prompt))
            if confirm:
                action = "add"
            else:
                action = "reject"
            self.put({"name": "perms", "action": action, "user_id": user_id, "perms": perms})
        elif name == "perms":
            action = data["action"]
            user_id = str(data["user_id"])
            user = self.workspace_info["users"].get(user_id)
            if user is None:
                msg.log("No user for id %s. Not handling perms event" % user_id)
                return
            perms = set(user["perms"])
            if action == "add":
                perms |= set(data["perms"])
            elif action == "remove":
                perms -= set(data["perms"])
            else:
                return
            user["perms"] = list(perms)
            if user_id == self.workspace_info["user_id"]:
                G.PERMS = perms
        elif name == "msg":
            self.on_msg(data)
        else:
            msg.debug("unknown name!", name, "data:", data)
    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)
예제 #22
0
    def handler(self, name, data):
        if name == 'patch':
            Listener.apply_patch(data)
        elif name == 'get_buf':
            buf_id = data['id']
            buf = listener.BUFS.get(buf_id)
            if not buf:
                return msg.warn(
                    'no buf found: %s.  Hopefully you didn\'t need that' %
                    data)
            timeout_id = buf.get('timeout_id')
            if timeout_id:
                utils.cancel_timeout(timeout_id)

            if data['encoding'] == 'base64':
                data['buf'] = base64.b64decode(data['buf'])
            # forced_patch doesn't exist in data, so this is equivalent to buf['forced_patch'] = False
            listener.BUFS[buf_id] = data
            view = listener.get_view(buf_id)
            if view:
                Listener.update_view(data, view)
            else:
                listener.save_buf(data)
        elif name == 'create_buf':
            if data['encoding'] == 'base64':
                data['buf'] = base64.b64decode(data['buf'])
            listener.BUFS[data['id']] = data
            listener.PATHS_TO_IDS[data['path']] = data['id']
            listener.save_buf(data)
            cb = listener.CREATE_BUF_CBS.get(data['path'])
            if cb:
                del listener.CREATE_BUF_CBS[data['path']]
                try:
                    cb(data['id'])
                except Exception as e:
                    print(e)
        elif name == 'rename_buf':
            del listener.PATHS_TO_IDS[data['old_path']]
            listener.PATHS_TO_IDS[data['path']] = data['id']
            new = utils.get_full_path(data['path'])
            old = utils.get_full_path(data['old_path'])
            new_dir = os.path.split(new)[0]
            if new_dir:
                utils.mkdir(new_dir)
            os.rename(old, new)
            view = listener.get_view(data['id'])
            if view:
                view.retarget(new)
            listener.BUFS[data['id']]['path'] = data['path']
        elif name == 'delete_buf':
            path = utils.get_full_path(data['path'])
            listener.delete_buf(data['id'])
            try:
                utils.rm(path)
            except Exception:
                pass
            user_id = data.get('user_id')
            username = self.get_username_by_id(user_id)
            msg.log('%s deleted %s' % (username, path))
        elif name == 'room_info':
            Listener.reset()
            G.JOINED_WORKSPACE = True
            # Success! Reset counter
            self.reconnect_delay = self.INITIAL_RECONNECT_DELAY
            self.retries = self.MAX_RETRIES

            self.workspace_info = data
            G.PERMS = data['perms']

            if 'patch' not in data['perms']:
                msg.log('No patch permission. Setting buffers to read-only')
                if sublime.ok_cancel_dialog(
                        'You don\'t have permission to edit this workspace. All files will be read-only.\n\nDo you want to request edit permission?'
                ):
                    self.put({'name': 'request_perms', 'perms': ['edit_room']})

            project_json = {'folders': [{'path': G.PROJECT_PATH}]}

            utils.mkdir(G.PROJECT_PATH)
            with open(os.path.join(G.PROJECT_PATH, '.sublime-project'),
                      'wb') as project_fd:
                project_fd.write(
                    json.dumps(project_json, indent=4,
                               sort_keys=True).encode('utf-8'))

            floo_json = {
                'url':
                utils.to_workspace_url({
                    'host': self.host,
                    'owner': self.owner,
                    'port': self.port,
                    'workspace': self.workspace,
                    'secure': self.secure,
                })
            }
            with open(os.path.join(G.PROJECT_PATH, '.floo'), 'w') as floo_fd:
                floo_fd.write(json.dumps(floo_json, indent=4, sort_keys=True))

            for buf_id, buf in data['bufs'].items():
                buf_id = int(buf_id)  # json keys must be strings
                buf_path = utils.get_full_path(buf['path'])
                new_dir = os.path.dirname(buf_path)
                utils.mkdir(new_dir)
                listener.BUFS[buf_id] = buf
                listener.PATHS_TO_IDS[buf['path']] = buf_id
                # TODO: stupidly inefficient
                view = listener.get_view(buf_id)
                if view and not view.is_loading(
                ) and buf['encoding'] == 'utf8':
                    view_text = listener.get_text(view)
                    view_md5 = hashlib.md5(
                        view_text.encode('utf-8')).hexdigest()
                    if view_md5 == buf['md5']:
                        msg.debug(
                            'md5 sum matches view. not getting buffer %s' %
                            buf['path'])
                        buf['buf'] = view_text
                        G.VIEW_TO_HASH[view.buffer_id()] = view_md5
                    elif self.get_bufs:
                        Listener.get_buf(buf_id)
                    #TODO: maybe send patch here?
                else:
                    try:
                        buf_fd = open(buf_path, 'rb')
                        buf_buf = buf_fd.read()
                        md5 = hashlib.md5(buf_buf).hexdigest()
                        if md5 == buf['md5']:
                            msg.debug(
                                'md5 sum matches. not getting buffer %s' %
                                buf['path'])
                            if buf['encoding'] == 'utf8':
                                buf_buf = buf_buf.decode('utf-8')
                            buf['buf'] = buf_buf
                        elif self.get_bufs:
                            Listener.get_buf(buf_id)
                    except Exception as e:
                        msg.debug('Error calculating md5:', e)
                        Listener.get_buf(buf_id)

            msg.log('Successfully joined workspace %s/%s' %
                    (self.owner, self.workspace))

            temp_data = data.get('temp_data', {})
            hangout = temp_data.get('hangout', {})
            hangout_url = hangout.get('url')
            if hangout_url:
                self.prompt_join_hangout(hangout_url)

            if self.on_room_info:
                self.on_room_info()
                self.on_room_info = None
        elif name == 'user_info':
            user_id = str(data['user_id'])
            user_info = data['user_info']
            self.workspace_info['users'][user_id] = user_info
            if user_id == str(self.workspace_info['user_id']):
                G.PERMS = user_info['perms']
        elif name == 'join':
            msg.log('%s joined the workspace' % data['username'])
            user_id = str(data['user_id'])
            self.workspace_info['users'][user_id] = data
        elif name == 'part':
            msg.log('%s left the workspace' % data['username'])
            user_id = str(data['user_id'])
            try:
                del self.workspace_info['users'][user_id]
            except Exception as e:
                print('Unable to delete user %s from user list' % (data))
            region_key = 'floobits-highlight-%s' % (user_id)
            for window in sublime.windows():
                for view in window.views():
                    view.erase_regions(region_key)
        elif name == 'highlight':
            region_key = 'floobits-highlight-%s' % (data['user_id'])
            Listener.highlight(data['id'], region_key, data['username'],
                               data['ranges'], data.get('ping', False))
        elif name == 'set_temp_data':
            hangout_data = data.get('data', {})
            hangout = hangout_data.get('hangout', {})
            hangout_url = hangout.get('url')
            if hangout_url:
                self.prompt_join_hangout(hangout_url)
        elif name == 'saved':
            try:
                buf = listener.BUFS[data['id']]
                username = self.get_username_by_id(data['user_id'])
                msg.log('%s saved buffer %s' % (username, buf['path']))
            except Exception as e:
                msg.error(str(e))
        elif name == 'request_perms':
            print(data)
            user_id = str(data.get('user_id'))
            username = self.get_username_by_id(user_id)
            if not username:
                return msg.debug(
                    'Unknown user for id %s. Not handling request_perms event.'
                    % user_id)
            perm_mapping = {
                'edit_room': 'edit',
                'admin_room': 'admin',
            }
            perms = data.get('perms')
            perms_str = ''.join([perm_mapping.get(p) for p in perms])
            prompt = 'User %s is requesting %s permission for this room.' % (
                username, perms_str)
            message = data.get('message')
            if message:
                prompt += '\n\n%s says: %s' % (username, message)
            prompt += '\n\nDo you want to grant them permission?'
            confirm = bool(sublime.ok_cancel_dialog(prompt))
            if confirm:
                action = 'add'
            else:
                action = 'reject'
            self.put({
                'name': 'perms',
                'action': action,
                'user_id': user_id,
                'perms': perms
            })
        elif name == 'perms':
            action = data['action']
            user_id = str(data['user_id'])
            user = self.workspace_info['users'].get(user_id)
            if user is None:
                msg.log('No user for id %s. Not handling perms event' %
                        user_id)
                return
            perms = set(user['perms'])
            if action == 'add':
                perms |= set(data['perms'])
            elif action == 'remove':
                perms -= set(data['perms'])
            else:
                return
            user['perms'] = list(perms)
            if user_id == self.workspace_info['user_id']:
                G.PERMS = perms
        elif name == 'msg':
            self.on_msg(data)
        else:
            msg.debug('unknown name!', name, 'data:', data)
예제 #23
0
    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')))
예제 #24
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('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)
예제 #25
0
    def _on_share_dir(self, data):
        file_to_share = None
        utils.reload_settings()
        G.USERNAME = data['username']
        G.SECRET = data['secret']
        dir_to_share = data['dir_to_share']
        perms = data['perms']
        editor.line_endings = data['line_endings'].find("unix") >= 0 and "\n" or "\r\n"
        dir_to_share = os.path.expanduser(dir_to_share)
        dir_to_share = utils.unfuck_path(dir_to_share)
        workspace_name = os.path.basename(dir_to_share)
        G.PROJECT_PATH = os.path.realpath(dir_to_share)
        msg.debug('%s %s %s' % (G.USERNAME, workspace_name, G.PROJECT_PATH))

        if os.path.isfile(dir_to_share):
            file_to_share = dir_to_share
            dir_to_share = os.path.dirname(dir_to_share)

        try:
            utils.mkdir(dir_to_share)
        except Exception:
            return msg.error("The directory %s doesn't exist and I can't create it." % dir_to_share)

        floo_file = os.path.join(dir_to_share, '.floo')

        info = {}
        try:
            floo_info = open(floo_file, 'rb').read().decode('utf-8')
            info = json.loads(floo_info)
        except (IOError, OSError):
            pass
        except Exception as e:
            msg.warn("Couldn't read .floo file: %s: %s" % (floo_file, str(e)))

        workspace_url = info.get('url')
        if workspace_url:
            parsed_url = api.prejoin_workspace(workspace_url, dir_to_share, {'perms': perms})
            if parsed_url:
                # TODO: make sure we create_flooignore
                # utils.add_workspace_to_persistent_json(parsed_url['owner'], parsed_url['workspace'], workspace_url, dir_to_share)
                agent = self.remote_connect(parsed_url['owner'], parsed_url['workspace'], False)
                return agent.once("room_info", lambda: agent.upload(file_to_share or dir_to_share))

        parsed_url = utils.get_workspace_by_path(dir_to_share,
                                                 lambda workspace_url: api.prejoin_workspace(workspace_url, dir_to_share, {'perms': perms}))
        if parsed_url:
            agent = self.remote_connect(parsed_url['owner'], parsed_url['workspace'], False)
            return agent.once("room_info", lambda: agent.upload(file_to_share or dir_to_share))

        def on_done(data, choices=None):
            self.get_input('Workspace name:',
                           workspace_name,
                           self._on_create_workspace,
                           workspace_name,
                           dir_to_share,
                           owner=data.get('response'),
                           perms=perms)

        try:
            r = api.get_orgs_can_admin()
        except IOError as e:
            return editor.error_message('Error getting org list: %s' % str(e))
        if r.code >= 400 or len(r.body) == 0:
            return on_done({'response': G.USERNAME})
        i = 0
        choices = []
        choices.append([G.USERNAME, i])
        for org in r.body:
            i += 1
            choices.append([org['name'], i])

        self.get_input('Create workspace owned by (%s) ' % " ".join([x[0] for x in choices]), '', on_done, choices=choices)
예제 #26
0
    def _on_share_dir(self, data):
        file_to_share = None
        utils.reload_settings()
        G.USERNAME = data['username']
        G.SECRET = data['secret']
        dir_to_share = data['dir_to_share']
        perms = data['perms']
        editor.line_endings = data['line_endings'].find(
            "unix") >= 0 and "\n" or "\r\n"
        dir_to_share = os.path.expanduser(dir_to_share)
        dir_to_share = utils.unfuck_path(dir_to_share)
        workspace_name = os.path.basename(dir_to_share)
        G.PROJECT_PATH = os.path.realpath(dir_to_share)
        msg.debug('%s %s %s' % (G.USERNAME, workspace_name, G.PROJECT_PATH))

        if os.path.isfile(dir_to_share):
            file_to_share = dir_to_share
            dir_to_share = os.path.dirname(dir_to_share)

        try:
            utils.mkdir(dir_to_share)
        except Exception:
            return msg.error(
                "The directory %s doesn't exist and I can't create it." %
                dir_to_share)

        floo_file = os.path.join(dir_to_share, '.floo')

        info = {}
        try:
            floo_info = open(floo_file, 'rb').read().decode('utf-8')
            info = json.loads(floo_info)
        except (IOError, OSError):
            pass
        except Exception as e:
            msg.warn("Couldn't read .floo file: %s: %s" % (floo_file, str(e)))

        workspace_url = info.get('url')
        if workspace_url:
            parsed_url = api.prejoin_workspace(workspace_url, dir_to_share,
                                               {'perms': perms})
            if parsed_url:
                # TODO: make sure we create_flooignore
                # utils.add_workspace_to_persistent_json(parsed_url['owner'], parsed_url['workspace'], workspace_url, dir_to_share)
                agent = self.remote_connect(parsed_url['owner'],
                                            parsed_url['workspace'], False)
                return agent.once(
                    "room_info",
                    lambda: agent.upload(file_to_share or dir_to_share))

        parsed_url = utils.get_workspace_by_path(
            dir_to_share, lambda workspace_url: api.prejoin_workspace(
                workspace_url, dir_to_share, {'perms': perms}))
        if parsed_url:
            agent = self.remote_connect(parsed_url['owner'],
                                        parsed_url['workspace'], False)
            return agent.once(
                "room_info",
                lambda: agent.upload(file_to_share or dir_to_share))

        def on_done(data, choices=None):
            self.get_input('Workspace name:',
                           workspace_name,
                           self._on_create_workspace,
                           workspace_name,
                           dir_to_share,
                           owner=data.get('response'),
                           perms=perms)

        try:
            r = api.get_orgs_can_admin()
        except IOError as e:
            return editor.error_message('Error getting org list: %s' % str(e))
        if r.code >= 400 or len(r.body) == 0:
            return on_done({'response': G.USERNAME})
        i = 0
        choices = []
        choices.append([G.USERNAME, i])
        for org in r.body:
            i += 1
            choices.append([org['name'], i])

        self.get_input('Create workspace owned by (%s) ' %
                       " ".join([x[0] for x in choices]),
                       '',
                       on_done,
                       choices=choices)
예제 #27
0
 def list_messages(self):
     if not G.AGENT:
         return msg.warn('Not connected to a workspace.')
     self.vim.command('echom "Recent messages for %s"' % (G.AGENT.workspace,))
     for message in G.AGENT.get_messages():
         self.vim.command('echom "  %s"' % (message,))
예제 #28
0
    def on_patch(self, data):
        added_newline = False
        buf_id = data['id']
        buf = self.FLOO_BUFS[buf_id]
        view = self.get_view(buf_id)
        if len(data['patch']) == 0:
            msg.error('wtf? no patches to apply. server is being stupid')
            return
        dmp_patches = DMP.patch_fromText(data['patch'])
        # TODO: run this in a separate thread
        if view:
            old_text = view.get_text()
        else:
            old_text = buf.get('buf', '')
        md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest()
        if md5_before != data['md5_before']:
            msg.debug('maybe vim is lame and discarded a trailing newline')
            old_text += '\n'
            added_newline = True
        md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest()
        if md5_before != data['md5_before']:
            msg.warn('starting md5s don\'t match for %s. ours: %s patch: %s this is dangerous!' %
                    (buf['path'], md5_before, data['md5_before']))
            if added_newline:
                old_text = old_text[:-1]
                md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest()

        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:
                msg.debug('OMG EMPTY!')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', data['patch'])
            if '\x01' in t[0]:
                msg.debug('FOUND CRAZY BYTE IN BUFFER')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', data['patch'])

        if not clean_patch:
            msg.error('failed to patch %s cleanly. re-fetching buffer' % buf['path'])
            return self.agent.send_get_buf(buf_id)

        cur_hash = hashlib.md5(t[0].encode('utf-8')).hexdigest()
        if cur_hash != data['md5_after']:
            msg.warn(
                '%s new hash %s != expected %s. re-fetching buffer...' %
                (buf['path'], cur_hash, data['md5_after'])
            )
            return self.agent.send_get_buf(buf_id)

        buf['buf'] = t[0]
        buf['md5'] = cur_hash

        if not view:
            self.save_buf(buf)
            return
        view.apply_patches(buf, t)
예제 #29
0
    def on_patch(self, data):
        if len(data['patch']) == 0:
            msg.error('wtf? no patches to apply. server is being stupid')
            return

        buf_id = data['id']
        buf = self.FLOO_BUFS[buf_id]
        if buf['encoding'] == 'base64':
            # TODO apply binary patches
            return self.agent.send_get_buf(buf_id)

        view = self.get_view(buf_id)

        dmp_patches = DMP.patch_fromText(data['patch'])
        # TODO: run this in a separate thread
        if view:
            old_text = view.get_text()
        else:
            old_text = buf.get('buf', '')
        md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest()
        if md5_before != data['md5_before']:
            msg.debug('maybe vim is lame and discarded a trailing newline')
            old_text += '\n'
        md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest()
        if md5_before != data['md5_before']:
            msg.warn('starting md5s don\'t match for %s. ours: %s patch: %s this is dangerous!' %
                    (buf['path'], md5_before, data['md5_before']))

        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:
                msg.debug('OMG EMPTY!')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', data['patch'])
            if '\x01' in t[0]:
                msg.debug('FOUND CRAZY BYTE IN BUFFER')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', data['patch'])

        if not clean_patch:
            msg.error('failed to patch %s cleanly. re-fetching buffer' % buf['path'])
            return self.agent.send_get_buf(buf_id)

        cur_hash = hashlib.md5(t[0].encode('utf-8')).hexdigest()
        if cur_hash != data['md5_after']:
            msg.warn(
                '%s new hash %s != expected %s. re-fetching buffer...' %
                (buf['path'], cur_hash, data['md5_after'])
            )
            return self.agent.send_get_buf(buf_id)

        buf['buf'] = t[0]
        buf['md5'] = cur_hash

        if not view:
            self.save_buf(buf)
            return
        view.apply_patches(buf, t)