Пример #1
0
    def init(self, paths: typing.List[str], context: typing.Dict[str,
                                                                 typing.Any],
             clipboard: Clipboard) -> None:
        context['wincol'] = int(context['wincol'])
        context['winheight'] = int(context['winheight'])
        context['winrow'] = int(context['winrow'])
        context['winwidth'] = int(context['winwidth'])
        context['prev_bufnr'] = int(context['prev_bufnr'])
        context['prev_winid'] = int(context['prev_winid'])
        self._context = Context(**context)
        self._bufname = f'[defx] {self._context.buffer_name}-{self._index}'

        if not self.init_buffer(paths):
            self._winid = self._vim.call('win_getid')
            return

        self._candidates = []
        self._context = Context(**context)
        self._clipboard = clipboard

        # Initialize defx
        self._defxs = []
        self._buffer.vars['defx']['paths'] = paths
        for [index, path] in enumerate(paths):
            self._defxs.append(Defx(self._vim, self._context, path, index))

        self.init_columns(self._context.columns.split(':'))

        self.redraw(True)

        for defx in self._defxs:
            self.init_cursor(defx)
Пример #2
0
    def __init__(self, vim: Nvim, paths: typing.List[str],
                 context: dict) -> None:
        self._vim: Nvim = vim
        self._candidates: typing.List[dict] = []
        self._selected_candidates: typing.List[int] = []
        self._context = Context(**context)

        # Initialize defx
        self._defxs: typing.List[Defx] = []
        for path in paths:
            self._defxs.append(Defx(self._vim, path))

        # Create new buffer
        self._vim.call('defx#util#execute_path', 'silent keepalt edit',
                       '[defx]')

        self._options = self._vim.current.buffer.options
        self._options['buftype'] = 'nofile'
        self._options['swapfile'] = False
        self._options['modeline'] = False
        self._options['filetype'] = 'defx'
        self._options['modifiable'] = False
        self._options['modified'] = False
        self._vim.command('silent doautocmd FileType defx')

        self._columns: typing.List[Column] = []
        for column in [Mark(self._vim), Filename(self._vim)]:
            column.syntax_name = 'Defx_' + column.name  # type: ignore
            self._columns.append(column)
Пример #3
0
    def init(self, paths: typing.List[str], context: typing.Dict[str,
                                                                 typing.Any],
             clipboard: Clipboard) -> None:
        context['fnamewidth'] = int(context['fnamewidth'])
        context['winheight'] = int(context['winheight'])
        context['winwidth'] = int(context['winwidth'])
        context['prev_bufnr'] = int(context['prev_bufnr'])
        self._context = Context(**context)
        self._bufname = f'[defx] {self._context.buffer_name}-{self._index}'

        if not self.init_buffer():
            return

        self._candidates = []
        self._selected_candidates = []
        self._context = Context(**context)
        self._clipboard = clipboard

        # Initialize defx
        self._defxs: typing.List[Defx] = []
        for [index, path] in enumerate(paths):
            self._defxs.append(Defx(self._vim, self._context, path, index))

        self.init_columns()
        self.init_syntax()

        if self._context.search:
            for defx in self._defxs:
                if self.search_tree(self._context.search, defx._index):
                    break
Пример #4
0
    def init(self, paths: typing.List[str], context: typing.Dict[str,
                                                                 typing.Any],
             clipboard: Clipboard) -> None:
        context['winheight'] = int(context['winheight'])
        context['winwidth'] = int(context['winwidth'])
        context['prev_bufnr'] = int(context['prev_bufnr'])
        context['prev_winid'] = int(context['prev_winid'])
        self._context = Context(**context)
        self._bufname = f'[defx] {self._context.buffer_name}-{self._index}'

        if not self.init_buffer(paths):
            return

        self._candidates = []
        self._selected_candidates = []
        self._context = Context(**context)
        self._clipboard = clipboard

        # Initialize defx
        self._defxs: typing.List[Defx] = []
        self._buffer.vars['defx']['paths'] = paths
        for [index, path] in enumerate(paths):
            self._defxs.append(Defx(self._vim, self._context, path, index))

        self.init_columns(self._context.columns.split(':'))

        for defx in self._defxs:
            if self._context.search:
                self.search_tree(self._context.search, defx._index)
            else:
                self.init_cursor(defx)
Пример #5
0
    def _init_context(
            self, context: typing.Dict[str, typing.Any]) -> Context:
        # Convert to int
        for attr in [x[0] for x in Context()._asdict().items()
                     if isinstance(x[1], int) and x[0] in context]:
            context[attr] = int(context[attr])

        return Context(**context)
Пример #6
0
    def init(self, paths: typing.List[str], context: dict,
             clipboard: Clipboard) -> None:
        self._candidates = []
        self._selected_candidates = []
        self._context = Context(**context)
        self._clipboard = clipboard

        context['fnamewidth'] = int(context['fnamewidth'])
        context['winheight'] = int(context['winheight'])
        context['winwidth'] = int(context['winwidth'])
        self._context = Context(**context)
        self._bufname = f'[defx] {self._context.buffer_name}-{self._index}'

        # Initialize defx
        self._defxs: typing.List[Defx] = []
        for [index, path] in enumerate(paths):
            self._defxs.append(Defx(self._vim, self._context, path, index))

        if not self.init_buffer():
            return

        # Initialize columns
        self._columns: typing.List[Column] = []
        self._all_columns: typing.Dict[str, Column] = {
            'mark': Mark(self._vim),
            'filename': Filename(self._vim),
            'type': Type(self._vim),
        }
        self._columns = [
            self._all_columns[x] for x in self._context.columns.split(':')
            if x in self._all_columns
        ]
        start = 1
        for column in self._columns:
            column.on_init()
            column.start = start
            length = column.length(self._context)
            column.end = start + length - 1
            column.syntax_name = 'Defx_' + column.name
            start += length

        self.init_syntax()
        self.redraw(True)

        if self._context.search:
            for defx in self._defxs:
                if self.search_tree(self._context.search, defx._index):
                    break
Пример #7
0
def _preview_file(view: View, defx: Defx,
                  context: Context, candidate: Candidate) -> None:
    previewed_buffers = view._vim.vars['defx#_previewed_buffers']
    filepath = str(candidate['action__path'])

    has_preview = bool(view._vim.call('defx#util#_get_preview_window'))
    if (has_preview and view._previewed_target and
            view._previewed_target == candidate):
        bufnr = str(view._vim.call('bufnr', filepath))
        if bufnr in previewed_buffers:
            previewed_buffers.pop(bufnr)
            view._vim.vars['defx#_previewed_buffers'] = previewed_buffers
        view._vim.command('pclose!')
        return

    prev_id = view._vim.call('win_getid')

    listed = view._vim.call('buflisted', filepath)

    view._previewed_target = candidate
    view._vim.call('defx#util#preview_file',
                   context._replace(targets=[])._asdict(), filepath)
    view._vim.current.window.options['foldenable'] = False

    if not listed:
        bufnr = str(view._vim.call('bufnr', filepath))
        previewed_buffers[bufnr] = 1
        view._vim.vars['defx#_previewed_buffers'] = previewed_buffers

    view._vim.call('win_gotoid', prev_id)
Пример #8
0
 def get_root_candidate(self) -> dict:
     """
     Returns root candidate
     """
     root = self._source.get_root_candidate(Context(), self._cwd)
     root['is_root'] = True
     return root
Пример #9
0
    def create_open(self, view: View, defx: Defx, context: Context, path: Path,
                    command: str, isdir: bool, isopen: bool) -> None:
        if isdir:
            path.mkdir(parents=True)
        else:
            path.parent.mkdir(parents=True, exist_ok=True)
            path.touch()

        if not isopen:
            view.redraw(True)
            view.search_recursive(path, defx._index)
            return

        # Note: Must be redraw before actions
        view.redraw(True)
        view.search_recursive(path, defx._index)

        if isdir:
            if command == 'open_tree':
                view.open_tree(path, defx._index, False, 0)
            else:
                view.cd(defx, defx._source.name, str(path), context.cursor)
        else:
            if command == 'drop':
                self._drop(
                    view, defx,
                    context._replace(args=[], targets=[{
                        'action__path': path
                    }]))
            else:
                view._vim.call('defx#util#execute_path', command,
                               self.get_buffer_name(str(path)))
Пример #10
0
 def _multi(self, view: View, defx: Defx, context: Context) -> None:
     for arg in context.args:
         args: typing.List[str]
         if isinstance(arg, list):
             args = arg
         else:
             args = [arg]
         do_action(view, defx, args[0], context._replace(args=args[1:]))
Пример #11
0
    def __init__(self, vim: Nvim, paths: typing.List[str],
                 context: dict) -> None:
        self._vim: Nvim = vim
        self._candidates: typing.List[dict] = []
        self._selected_candidates: typing.List[int] = []

        context['fnamewidth'] = int(context['fnamewidth'])
        self._context = Context(**context)

        # Initialize defx
        self._defxs: typing.List[Defx] = []
        index = 0
        for path in paths:
            self._defxs.append(Defx(self._vim, self._context, path, index))
            index += 1

        self.init_buffer()

        # Initialize columns
        self._columns: typing.List[Column] = []
        self._all_columns: typing.List[Column] = [
            Mark(self._vim),
            Filename(self._vim),
            Type(self._vim)
        ]
        columns = self._context.columns.split(':')
        self._columns = [x for x in self._all_columns if x.name in columns]
        start = 1
        for column in self._columns:
            column.on_init()
            column.start = start
            length = column.length(self._context)
            column.end = start + length - 1
            column.syntax_name = 'Defx_' + column.name
            start += length

        self.init_syntax()
        self.redraw(True)

        if self._context.search:
            for defx in self._defxs:
                self.search_file(self._context.search, defx._index)
Пример #12
0
def _call(view: View, defx: Defx, context: Context) -> None:
    """
    Call the function.
    """
    function = context.args[0] if context.args else None
    if not function:
        return

    dict_context = context._asdict()
    dict_context['targets'] = [str(x['action__path']) for x in context.targets]
    view._vim.call(function, dict_context)
Пример #13
0
 def _multi(self, view: View, defx: Defx, context: Context) -> None:
     for arg in context.args:
         args: typing.List[typing.Union[str, typing.List[str]]]
         if isinstance(arg, list):
             args = arg
         else:
             args = [arg]
         action_args = ((args[1] if isinstance(args[1], list)
                         else [args[1]])
                        if len(args) > 1 else [])
         do_action(view, defx, str(args[0]),
                   context._replace(args=action_args))
Пример #14
0
    def gather_candidates(self) -> typing.List:
        """
        Returns file candidates
        """
        candidates = self._source.gather_candidates(Context(), self._cwd)

        # Sort
        dirs = sorted([x for x in candidates if x['is_directory']],
                      key=lambda x: x['abbr'])
        files = sorted([x for x in candidates if not x['is_directory']],
                       key=lambda x: x['abbr'])
        return dirs + files
Пример #15
0
def do_action(view: View, defx: Defx,
              action_name: str, context: Context) -> bool:
    """
    Do "action_name" action.
    """
    if not defx._source:
        return True

    actions: typing.Dict[str, ActionTable] = defx._source.kind.get_actions()

    if action_name not in actions:
        return True

    action = actions[action_name]

    selected_candidates = [x for x in view._candidates if x['is_selected']]
    if (selected_candidates and
            ActionAttr.NO_TAGETS not in action.attr and
            ActionAttr.TREE not in action.attr):
        # Clear marks
        for candidate in selected_candidates:
            candidate['is_selected'] = False
        view.redraw()

    if ActionAttr.CURSOR_TARGET in action.attr:
        # Use cursor candidate only
        cursor_candidate = view.get_cursor_candidate(context.cursor)
        if not cursor_candidate:
            return True
        context = context._replace(
            targets=[cursor_candidate],
        )

    action.func(view, defx, context)

    if action_name != 'repeat':
        view._prev_action = action_name

    if ActionAttr.MARK in action.attr:
        # Update marks
        view.update_candidates()
        view.redraw()
    elif ActionAttr.TREE in action.attr:
        view.update_candidates()
        view.redraw()
    elif ActionAttr.REDRAW in action.attr:
        # Redraw
        view.redraw(True)
    return False
Пример #16
0
def _open_tree(view: View, defx: Defx, context: Context) -> None:
    nested = False
    recursive_level = 0
    toggle = False
    for arg in context.args:
        if arg == 'nested':
            nested = True
        elif arg == 'recursive':
            recursive_level = 20
        elif re.search(r'recursive:\d+', arg):
            recursive_level = int(arg.split(':')[1])
        elif arg == 'toggle':
            toggle = True

    for target in [x for x in context.targets if x['is_directory']]:
        if toggle and not target['is_directory'] or target['is_opened_tree']:
            _close_tree(view, defx, context._replace(targets=[target]))
        else:
            view.open_tree(target['action__path'], defx._index, nested,
                           recursive_level)
Пример #17
0
    def __init__(self, vim: Nvim) -> None:
        super().__init__(vim)

        self.name = 'filename'
        self.vars = {
            'min_width': 40,
            'max_width': 100,
            'root_marker_highlight': 'Constant',
        }
        self.is_stop_variable = True

        self._current_length = 0
        self._syntaxes = [
            'directory',
            'directory_marker',
            'hidden',
            'root',
            'root_marker',
        ]
        self._context: Context = Context()
        self._directory_marker = '**'
        self._file_marker = '||'
Пример #18
0
    def __init__(self, vim: Nvim) -> None:
        super().__init__(vim)

        self.name = 'filename'
        self.vars = {
            'directory_icon': '+',
            'indent': ' ',
            'min_width': 40,
            'max_width': 100,
            'root_icon': ' ',
            'opened_icon': '-',
        }

        self._current_length = 0
        self._syntaxes = [
            'directory',
            'directory_icon',
            'hidden',
            'marker',
            'opened_icon',
            'root',
            'root_icon',
        ]
        self._context: Context = Context()
Пример #19
0
 def _open_or_close_tree(self, view: View, defx: Defx,
                         context: Context) -> None:
     self._open_tree(view, defx,
                     context._replace(args=context.args + ['toggle']))
Пример #20
0
 def _open_tree_recursive(self, view: View, defx: Defx,
                          context: Context) -> None:
     level = context.args[0] if context.args else '20'
     self._open_tree(
         view, defx,
         context._replace(args=context.args + ['recursive:' + level]))
Пример #21
0
class View(object):
    def __init__(self, vim: Nvim, index: int) -> None:
        self._vim: Nvim = vim
        self._candidates: typing.List[dict] = []
        self._selected_candidates: typing.List[int] = []
        self._clipboard = Clipboard()
        self._bufnr = -1
        self._index = index
        self._bufname = '[defx]'

    def init(self, paths: typing.List[str], context: dict,
             clipboard: Clipboard) -> None:
        self._candidates = []
        self._selected_candidates = []
        self._context = Context(**context)
        self._clipboard = clipboard

        context['fnamewidth'] = int(context['fnamewidth'])
        context['winheight'] = int(context['winheight'])
        context['winwidth'] = int(context['winwidth'])
        self._context = Context(**context)
        self._bufname = f'[defx] {self._context.buffer_name}-{self._index}'

        # Initialize defx
        self._defxs: typing.List[Defx] = []
        for [index, path] in enumerate(paths):
            self._defxs.append(Defx(self._vim, self._context, path, index))

        if not self.init_buffer():
            return

        # Initialize columns
        self._columns: typing.List[Column] = []
        self._all_columns: typing.Dict[str, Column] = {
            'mark': Mark(self._vim),
            'filename': Filename(self._vim),
            'type': Type(self._vim),
        }
        self._columns = [
            self._all_columns[x] for x in self._context.columns.split(':')
            if x in self._all_columns
        ]
        start = 1
        for column in self._columns:
            column.on_init()
            column.start = start
            length = column.length(self._context)
            column.end = start + length - 1
            column.syntax_name = 'Defx_' + column.name
            start += length

        self.init_syntax()
        self.redraw(True)

        if self._context.search:
            for defx in self._defxs:
                if self.search_tree(self._context.search, defx._index):
                    break

    def init_buffer(self) -> bool:
        if self._context.split == 'tab':
            self._vim.command('tabnew')

        winnr = self._vim.call('bufwinnr', self._bufname)
        if winnr > 0:
            self._vim.command(f'{winnr}wincmd w')
            if self._context.toggle:
                self.quit()
                return False
            return True

        # Create new buffer
        self._vim.call(
            'defx#util#execute_path', 'silent keepalt %s %s %s ' % (
                self._context.direction,
                ('vertical' if self._context.split == 'vertical' else ''),
                ('edit' if self._context.split == 'no'
                 or self._context.split == 'tab' else 'new'),
            ), self._bufname)

        window_options = self._vim.current.window.options
        window_options['list'] = False
        window_options['wrap'] = False

        if (self._context.split == 'vertical' and self._context.winwidth > 0):
            window_options['winfixwidth'] = True
            self._vim.command(f'vertical resize {self._context.winwidth}')
        elif (self._context.split == 'horizontal'
              and self._context.winheight > 0):
            window_options['winfixheight'] = True
            self._vim.command(f'resize {self._context.winheight}')

        buffer_options = self._vim.current.buffer.options
        buffer_options['buftype'] = 'nofile'
        buffer_options['swapfile'] = False
        buffer_options['modeline'] = False
        buffer_options['filetype'] = 'defx'
        buffer_options['modifiable'] = False
        buffer_options['modified'] = False

        self._vim.current.buffer.vars['defx'] = {
            'context': self._context._asdict(),
        }

        if not self._context.listed:
            buffer_options['buflisted'] = False
            buffer_options['bufhidden'] = 'wipe'

        self._vim.command('silent doautocmd FileType defx')
        self._vim.command('autocmd! FocusGained <buffer>')
        self._vim.command('autocmd defx FocusGained <buffer> ' +
                          'call defx#_do_action("redraw", [])')
        self._bufnr = self._vim.current.buffer.number

        return True

    def init_syntax(self) -> None:
        for column in self._columns:
            self._vim.command('silent! syntax clear ' + column.syntax_name)
            self._vim.command('syntax region ' + column.syntax_name +
                              ' start=/\%' + str(column.start) + 'v/ end=/\%' +
                              str(column.end) + 'v/ keepend oneline')
            column.highlight()

    def debug(self, expr: typing.Any) -> None:
        error(self._vim, expr)

    def print_msg(self, expr: typing.Any) -> None:
        self._vim.call('defx#util#print_message', expr)

    def quit(self) -> None:
        if self._vim.call('winnr', '$') != 1:
            self._vim.command('close')
        else:
            self._vim.command('enew')

    def init_candidates(self) -> None:
        self._candidates = []
        for defx in self._defxs:
            candidates = [defx.get_root_candidate()]
            candidates += defx.gather_candidates()
            for candidate in candidates:
                candidate['_defx_index'] = defx._index
            self._candidates += candidates

    def redraw(self, is_force: bool = False) -> None:
        """
        Redraw defx buffer.
        """

        buffer_options = self._vim.current.buffer.options
        if buffer_options['filetype'] != 'defx':
            return

        start = time.time()

        prev = self.get_cursor_candidate(self._vim.call('line', '.'))

        if is_force:
            self._selected_candidates = []
            self.init_candidates()

        is_busy = time.time() - start > 0.5

        if is_busy:
            self.print_msg('Waiting...')

        # Set is_selected flag
        for candidate in self._candidates:
            candidate['is_selected'] = False
        for index in self._selected_candidates:
            self._candidates[index]['is_selected'] = True

        buffer_options['modifiable'] = True
        self._vim.current.buffer[:] = [
            self.get_columns_text(self._context, x) for x in self._candidates
        ]
        buffer_options['modifiable'] = False
        buffer_options['modified'] = False

        if prev:
            self.search_file(prev['action__path'], prev['_defx_index'])

        if is_busy:
            self._vim.command('redraw')
            self.print_msg('Done.')

        if self._context.profile:
            error(self._vim, f'redraw time = {time.time() - start}')

    def get_columns_text(self, context: Context, candidate: dict) -> str:
        text = ''
        for column in self._columns:
            text += column.get(context, candidate)
        return text

    def get_cursor_candidate(self, cursor: int) -> dict:
        if len(self._candidates) < cursor:
            return {}
        else:
            return self._candidates[cursor - 1]

    def get_selected_candidates(self, cursor: int,
                                index: int) -> typing.List[dict]:
        if not self._selected_candidates:
            candidates = [self.get_cursor_candidate(cursor)]
        else:
            candidates = [
                self._candidates[x] for x in self._selected_candidates
            ]
        return [x for x in candidates if x['_defx_index'] == index]

    def cd(self, defx: Defx, path: str, cursor: int) -> None:
        # Save previous cursor position
        history = defx._cursor_history
        history[defx._cwd] = self.get_cursor_candidate(cursor)['action__path']

        global_histories = self._vim.vars['defx#_histories']
        global_histories.append(defx._cwd)
        self._vim.vars['defx#_histories'] = global_histories

        defx.cd(path)
        self.redraw(True)
        if path in history:
            self.search_file(history[path], defx._index)
        self._selected_candidates = []

    def search_tree(self, start: str, index: int) -> bool:
        path = Path(start)
        while True:
            if self.search_file(str(path), index):
                return True
            if path.parent == path:
                break
            path = path.parent
        return False

    def search_file(self, path: str, index: int) -> bool:
        linenr = 1
        for candidate in self._candidates:
            if (candidate['_defx_index'] == index
                    and str(candidate['action__path']) == path):
                self._vim.call('cursor', [linenr, 1])
                return True
            linenr += 1
        return False

    def do_action(self, action_name: str, action_args: typing.List[str],
                  new_context: dict) -> None:
        """
        Do "action" action.
        """
        if not self._candidates:
            return

        cursor = new_context['cursor']

        defx_targets = {
            x._index: self.get_selected_candidates(cursor, x._index)
            for x in self._defxs
        }

        import defx.action as action
        for defx in self._defxs:
            targets = defx_targets[defx._index]
            if not targets:
                continue
            context = self._context._replace(targets=targets,
                                             args=action_args,
                                             cursor=cursor)
            ret = action.do_action(self, defx, action_name, context)
            if ret:
                error(self._vim, 'Invalid action_name:' + action_name)
                return
Пример #22
0
class View(object):
    def __init__(self, vim: Nvim, index: int) -> None:
        self._vim: Nvim = vim
        self._candidates: typing.List[typing.Dict[str, typing.Any]] = []
        self._selected_candidates: typing.List[int] = []
        self._clipboard = Clipboard()
        self._bufnr = -1
        self._index = index
        self._bufname = '[defx]'
        self._buffer: Nvim.buffer = None
        self._prev_action = ''
        self._prev_highlight_commands: typing.List[str] = []

    def init(self, paths: typing.List[str], context: typing.Dict[str,
                                                                 typing.Any],
             clipboard: Clipboard) -> None:
        context['winheight'] = int(context['winheight'])
        context['winwidth'] = int(context['winwidth'])
        context['prev_bufnr'] = int(context['prev_bufnr'])
        context['prev_winid'] = int(context['prev_winid'])
        self._context = Context(**context)
        self._bufname = f'[defx] {self._context.buffer_name}-{self._index}'

        if not self.init_buffer(paths):
            return

        self._candidates = []
        self._selected_candidates = []
        self._context = Context(**context)
        self._clipboard = clipboard

        # Initialize defx
        self._defxs: typing.List[Defx] = []
        self._buffer.vars['defx']['paths'] = paths
        for [index, path] in enumerate(paths):
            self._defxs.append(Defx(self._vim, self._context, path, index))

        self.init_columns(self._context.columns.split(':'))

        for defx in self._defxs:
            if self._context.search:
                self.search_tree(self._context.search, defx._index)
            else:
                self.init_cursor(defx)

    def init_columns(self, columns: typing.List[str]) -> None:
        # Initialize columns
        self._columns: typing.List[Column] = []
        self._all_columns: typing.Dict[str, Column] = {}

        for path_column in self.load_custom_columns():
            column = import_plugin(path_column, 'column', 'Column')
            if not column:
                continue

            column = column(self._vim)
            if column.name not in self._all_columns:
                self._all_columns[column.name] = column

        custom = self._vim.call('defx#custom#_get')['column']
        self._columns = [
            self._all_columns[x] for x in columns if x in self._all_columns
        ]
        for column in self._columns:
            if column.name in custom:
                column.vars.update(custom[column.name])
            column.on_init(self._context)
            column.syntax_name = 'Defx_' + column.name

    def init_buffer(self, paths: typing.List[str]) -> bool:
        if self._context.split == 'tab':
            self._vim.command('tabnew')

        winnr = self._vim.call('bufwinnr', self._bufname)
        if winnr > 0:
            self._vim.command(f'{winnr}wincmd w')
            if self._context.toggle:
                self.quit()
                return False
            return True

        if (self._vim.current.buffer.options['modified']
                and not self._vim.options['hidden']
                and self._context.split == 'no'):
            self._context = self._context._replace(split='vertical')

        # Create new buffer
        vertical = 'vertical' if self._context.split == 'vertical' else ''
        if self._vim.call('bufexists', self._bufnr):
            command = ('buffer'
                       if self._context.split in ['no', 'tab'] else 'sbuffer')
            self._vim.command('silent keepalt %s %s %s %s' % (
                self._context.direction,
                vertical,
                command,
                self._bufnr,
            ))
            if self._context.resume:
                return False
        else:
            command = ('edit'
                       if self._context.split in ['no', 'tab'] else 'new')
            self._vim.call(
                'defx#util#execute_path', 'silent keepalt %s %s %s ' % (
                    self._context.direction,
                    vertical,
                    command,
                ), self._bufname)

        self._buffer = self._vim.current.buffer
        self._bufnr = self._buffer.number

        window_options = self._vim.current.window.options
        window_options['list'] = False
        window_options['wrap'] = False

        if (self._context.split == 'vertical' and self._context.winwidth > 0):
            window_options['winfixwidth'] = True
            self._vim.command(f'vertical resize {self._context.winwidth}')
        elif (self._context.split == 'horizontal'
              and self._context.winheight > 0):
            window_options['winfixheight'] = True
            self._vim.command(f'resize {self._context.winheight}')

        buffer_options = self._buffer.options
        buffer_options['buftype'] = 'nofile'
        buffer_options['swapfile'] = False
        buffer_options['modeline'] = False
        buffer_options['filetype'] = 'defx'
        buffer_options['modifiable'] = False
        buffer_options['modified'] = False

        self._buffer.vars['defx'] = {
            'context': self._context._asdict(),
            'paths': paths,
        }

        if not self._context.listed:
            buffer_options['buflisted'] = False
            buffer_options['bufhidden'] = 'wipe'

        self.execute_commands([
            'silent doautocmd FileType defx',
            'autocmd! defx * <buffer>',
        ])
        self._vim.command('autocmd defx '
                          'CursorHold,WinEnter,FocusGained <buffer> '
                          'call defx#call_async_action("check_redraw")')

        self._prev_highlight_commands = []

        return True

    def init_length(self) -> None:
        start = 1
        for column in self._columns:
            column.start = start
            length = column.length(
                self._context._replace(targets=self._candidates))
            column.end = start + length
            start += length + 1

    def update_syntax(self) -> None:
        highlight_commands: typing.List[str] = []
        for column in self._columns:
            highlight_commands += column.highlight_commands()

        if highlight_commands == self._prev_highlight_commands:
            # Skip highlights
            return

        self._prev_highlight_commands = highlight_commands

        commands: typing.List[str] = []
        for column in self._columns:
            commands.append('silent! syntax clear ' + column.syntax_name)
            for syntax in column.syntaxes():
                commands.append('silent! syntax clear ' + syntax)
            commands.append('syntax region ' + column.syntax_name +
                            r' start=/\%' + str(column.start) + r'v/ end=/\%' +
                            str(column.end) + 'v/ keepend oneline')
        commands += highlight_commands

        self.execute_commands(commands)

    def debug(self, expr: typing.Any) -> None:
        error(self._vim, expr)

    def print_msg(self, expr: typing.Any) -> None:
        self._vim.call('defx#util#print_message', expr)

    def execute_commands(self, commands: typing.List[str]) -> None:
        self._vim.command(' | '.join(commands))

    def quit(self) -> None:
        winnr = self._vim.call('bufwinnr', self._bufname)
        if winnr < 0:
            return

        self._vim.command(f'{winnr}wincmd w')

        if self._context.split in ['no', 'tab']:
            if self._vim.call('bufexists', self._context.prev_bufnr):
                self._vim.command('buffer ' + str(self._context.prev_bufnr))
            else:
                self._vim.command('enew')
        else:
            if self._vim.call('winnr', '$') != 1:
                self._vim.command('close')
                self._vim.call('win_gotoid', self._context.prev_winid)
            else:
                self._vim.command('enew')

    def init_candidates(self) -> None:
        self._candidates = []
        for defx in self._defxs:
            root = defx.get_root_candidate()
            defx._mtime = root['action__path'].stat().st_mtime

            candidates = [root]
            candidates += defx.gather_candidates()
            for candidate in candidates:
                candidate['_defx_index'] = defx._index
            self._candidates += candidates

    def redraw(self, is_force: bool = False) -> None:
        """
        Redraw defx buffer.
        """

        if self._buffer != self._vim.current.buffer:
            return

        start = time.time()

        prev_linenr = self._vim.call('line', '.')
        prev = self.get_cursor_candidate(prev_linenr)

        if is_force:
            self._selected_candidates = []
            self.init_candidates()
            self.init_length()
            self.update_syntax()

        # Set is_selected flag
        for candidate in self._candidates:
            candidate['is_selected'] = False
        for index in self._selected_candidates:
            self._candidates[index]['is_selected'] = True

        for column in self._columns:
            column.on_redraw(self._context)

        if self._buffer != self._vim.current.buffer:
            return

        self._buffer.options['modifiable'] = True
        self._buffer[:] = [
            self.get_columns_text(self._context, x) for x in self._candidates
        ]
        self._buffer.options['modifiable'] = False
        self._buffer.options['modified'] = False

        self._vim.call('cursor', [prev_linenr, 0])

        if prev:
            self.search_file(prev['action__path'], prev['_defx_index'])

        if self._context.profile:
            error(self._vim, f'redraw time = {time.time() - start}')

    def get_columns_text(self, context: Context,
                         candidate: typing.Dict[str, typing.Any]) -> str:
        text = ''
        for column in self._columns:
            if text:
                text += ' '
            text += column.get(context, candidate)
        return text

    def get_cursor_candidate(self,
                             cursor: int) -> typing.Dict[str, typing.Any]:
        if len(self._candidates) < cursor:
            return {}
        else:
            return self._candidates[cursor - 1]

    def get_selected_candidates(
            self, cursor: int,
            index: int) -> typing.List[typing.Dict[str, typing.Any]]:
        if not self._candidates:
            return []
        if not self._selected_candidates:
            candidates = [self.get_cursor_candidate(cursor)]
        else:
            candidates = [
                self._candidates[x] for x in self._selected_candidates
            ]
        return [x for x in candidates if x.get('_defx_index', -1) == index]

    def cd(self, defx: Defx, path: str, cursor: int) -> None:
        # Save previous cursor position
        history = defx._cursor_history
        history[defx._cwd] = self.get_cursor_candidate(cursor)['action__path']

        global_histories = self._vim.vars['defx#_histories']
        global_histories.append(defx._cwd)
        self._vim.vars['defx#_histories'] = global_histories

        defx.cd(path)
        self.redraw(True)
        self.init_cursor(defx)
        if path in history:
            self.search_file(history[path], defx._index)
        self._selected_candidates = []

        self.update_paths(defx._index, path)

    def update_paths(self, index: int, path: str) -> None:
        var_defx = self._buffer.vars['defx']
        var_defx['paths'][index] = path
        self._buffer.vars['defx'] = var_defx

    def search_tree(self, start: str, index: int) -> bool:
        path = Path(start)
        while True:
            if self.search_file(path, index):
                return True
            if path.parent == path:
                break
            path = path.parent
        return False

    def search_file(self, path: Path, index: int) -> bool:
        linenr = 1
        target = str(path)
        if target and target[-1] == '/':
            target = target[:-1]
        for candidate in self._candidates:
            if (candidate['_defx_index'] == index
                    and str(candidate['action__path']) == target):
                self._vim.call('cursor', [linenr, 1])
                return True
            linenr += 1
        return False

    def init_cursor(self, defx: Defx) -> None:
        self.search_file(Path(defx._cwd), defx._index)

        # Move to next
        self._vim.call('cursor', [self._vim.call('line', '.') + 1, 1])

    def do_action(self, action_name: str, action_args: typing.List[str],
                  new_context: typing.Dict[str, typing.Any]) -> None:
        """
        Do "action" action.
        """
        cursor = new_context['cursor']

        defx_targets = {
            x._index: self.get_selected_candidates(cursor, x._index)
            for x in self._defxs
        }
        all_targets: typing.List[typing.Dict[str, typing.Any]] = []
        for targets in defx_targets.values():
            all_targets += targets

        import defx.action as action
        for defx in [
                x for x in self._defxs
                if not all_targets or defx_targets[x._index]
        ]:
            context = self._context._replace(targets=defx_targets[defx._index],
                                             args=action_args,
                                             cursor=cursor)
            ret = action.do_action(self, defx, action_name, context)
            if ret:
                error(self._vim, 'Invalid action_name:' + action_name)
                return

    def load_custom_columns(self) -> typing.List[Path]:
        rtp_list = self._vim.options['runtimepath'].split(',')
        result: typing.List[Path] = []

        for path in rtp_list:
            column_path = Path(path).joinpath('rplugin', 'python3', 'defx',
                                              'column')
            if safe_call(column_path.is_dir):
                result += column_path.glob('*.py')

        return result
Пример #23
0
class View(object):
    def __init__(self, vim: Nvim, paths: typing.List[str],
                 context: dict) -> None:
        self._vim: Nvim = vim
        self._candidates: typing.List[dict] = []
        self._selected_candidates: typing.List[int] = []

        context['fnamewidth'] = int(context['fnamewidth'])
        self._context = Context(**context)

        # Initialize defx
        self._defxs: typing.List[Defx] = []
        index = 0
        for path in paths:
            self._defxs.append(Defx(self._vim, self._context, path, index))
            index += 1

        self.init_buffer()

        # Initialize columns
        self._columns: typing.List[Column] = []
        self._all_columns: typing.List[Column] = [
            Mark(self._vim),
            Filename(self._vim),
            Type(self._vim)
        ]
        columns = self._context.columns.split(':')
        self._columns = [x for x in self._all_columns if x.name in columns]
        start = 1
        for column in self._columns:
            column.on_init()
            column.start = start
            length = column.length(self._context)
            column.end = start + length - 1
            column.syntax_name = 'Defx_' + column.name
            start += length

        self.init_syntax()
        self.redraw(True)

        if self._context.search:
            for defx in self._defxs:
                self.search_file(self._context.search, defx._index)

    def init_buffer(self) -> None:
        # Create new buffer
        self._vim.call('defx#util#execute_path', 'silent keepalt edit',
                       '[defx]')

        window_options = self._vim.current.window.options
        window_options['list'] = False
        window_options['wrap'] = False

        buffer_options = self._vim.current.buffer.options
        buffer_options['buftype'] = 'nofile'
        buffer_options['swapfile'] = False
        buffer_options['modeline'] = False
        buffer_options['filetype'] = 'defx'
        buffer_options['modifiable'] = False
        buffer_options['modified'] = False
        buffer_options['buflisted'] = False
        buffer_options['bufhidden'] = 'wipe'

        self._vim.command('silent doautocmd FileType defx')
        self._vim.command('augroup defx | autocmd! | augroup END')
        self._vim.command('autocmd defx FocusGained <buffer> ' +
                          'call defx#_do_action("redraw", [])')

    def init_syntax(self) -> None:
        for column in self._columns:
            self._vim.command('silent! syntax clear ' + column.syntax_name)
            self._vim.command('syntax region ' + column.syntax_name +
                              ' start=/\%' + str(column.start) + 'v/ end=/\%' +
                              str(column.end) + 'v/ keepend oneline')
            column.highlight()

    def debug(self, expr: typing.Any) -> None:
        error(self._vim, expr)

    def quit(self) -> None:
        if self._vim.call('winnr', '$') != 1:
            self._vim.command('close')
        else:
            self._vim.command('enew')

    def init_candidates(self) -> None:
        self._candidates = []
        for defx in self._defxs:
            candidates = [defx.get_root_candidate()]
            candidates += defx.gather_candidates()
            for candidate in candidates:
                candidate['_defx_index'] = defx._index
            self._candidates += candidates

    def redraw(self, is_force: bool = False) -> None:
        """
        Redraw defx buffer.
        """

        buffer_options = self._vim.current.buffer.options
        if buffer_options['filetype'] != 'defx':
            return

        prev = self.get_cursor_candidate(self._vim.call('line', '.'))

        if is_force:
            self._selected_candidates = []
            self.init_candidates()

        # Set is_selected flag
        for candidate in self._candidates:
            candidate['is_selected'] = False
        for index in self._selected_candidates:
            self._candidates[index]['is_selected'] = True

        buffer_options['modifiable'] = True
        self._vim.current.buffer[:] = [
            self.get_columns_text(self._context, x) for x in self._candidates
        ]
        buffer_options['modifiable'] = False
        buffer_options['modified'] = False

        if prev:
            self.search_file(prev['action__path'], prev['_defx_index'])

    def get_columns_text(self, context: Context, candidate: dict) -> str:
        text = ''
        for column in self._columns:
            text += column.get(context, candidate)
        return text

    def get_cursor_candidate(self, cursor: int) -> dict:
        if len(self._candidates) < cursor:
            return {}
        else:
            return self._candidates[cursor - 1]

    def get_selected_candidates(self, cursor: int,
                                index: int) -> typing.List[dict]:
        if not self._selected_candidates:
            candidates = [self.get_cursor_candidate(cursor)]
        else:
            candidates = [
                self._candidates[x] for x in self._selected_candidates
            ]
        return [x for x in candidates if x['_defx_index'] == index]

    def cd(self, defx: Defx, path: str, cursor: int) -> None:
        # Save previous cursor position
        history = defx._cursor_history
        history[defx._cwd] = self.get_cursor_candidate(cursor)['action__path']

        defx.cd(path)
        self.redraw(True)
        if path in history:
            self.search_file(history[path], defx._index)
        self._selected_candidates = []

    def search_file(self, path: str, index: int) -> None:
        linenr = 1
        for candidate in self._candidates:
            if (candidate['_defx_index'] == index
                    and candidate['action__path'] == path):
                self._vim.call('cursor', [linenr, 1])
                return
            linenr += 1

    def do_action(self, action_name: str, action_args: typing.List[str],
                  new_context: dict) -> None:
        """
        Do "action" action.
        """
        if not self._candidates:
            return

        cursor = new_context['cursor']

        import defx.action as action
        for defx in self._defxs:
            targets = self.get_selected_candidates(cursor, defx._index)
            if not targets:
                continue
            context = self._context._replace(targets=targets,
                                             args=action_args,
                                             cursor=cursor)
            ret = action.do_action(self, defx, action_name, context)
            if ret:
                error(self._vim, 'Invalid action_name:' + action_name)
                return
Пример #24
0
def _open_or_close_tree(view: View, defx: Defx, context: Context) -> None:
    for target in context.targets:
        if not target['is_directory'] or target['is_opened_tree']:
            _close_tree(view, defx, context._replace(targets=[target]))
        else:
            _open_tree(view, defx, context._replace(targets=[target]))
Пример #25
0
class View(object):
    def __init__(self, vim: Nvim, paths: typing.List[str],
                 context: dict) -> None:
        self._vim: Nvim = vim
        self._candidates: typing.List[dict] = []
        self._selected_candidates: typing.List[int] = []
        self._context = Context(**context)

        # Initialize defx
        self._defxs: typing.List[Defx] = []
        for path in paths:
            self._defxs.append(Defx(self._vim, path))

        # Create new buffer
        self._vim.call('defx#util#execute_path', 'silent keepalt edit',
                       '[defx]')

        self._options = self._vim.current.buffer.options
        self._options['buftype'] = 'nofile'
        self._options['swapfile'] = False
        self._options['modeline'] = False
        self._options['filetype'] = 'defx'
        self._options['modifiable'] = False
        self._options['modified'] = False
        self._vim.command('silent doautocmd FileType defx')

        self._columns: typing.List[Column] = []
        for column in [Mark(self._vim), Filename(self._vim)]:
            column.syntax_name = 'Defx_' + column.name  # type: ignore
            self._columns.append(column)

    def init_syntax(self):
        for column in self._columns:
            if hasattr(column, 'highlight'):
                self._vim.command('silent! syntax clear ' + column.syntax_name)

    def redraw(self, is_force: bool = False) -> None:
        """
        Redraw defx buffer.
        """

        if is_force:
            self._selected_candidates = []

        self._candidates = []
        for defx in self._defxs:
            self._candidates.append({
                'word': defx._cwd,
                'abbr': defx._cwd + '/',
                'kind': 'directory',
                'is_directory': True,
                'is_root': True,
                'action__path': defx._cwd,
            })
            self._candidates += defx.gather_candidates()

        # Set is_selected flag
        for index in self._selected_candidates:
            self._candidates[index]['is_selected'] = True

        self._options['modifiable'] = True
        self._vim.current.buffer[:] = [
            self.get_columns_text(self._context, x) for x in self._candidates
        ]
        self._options['modifiable'] = False
        self._options['modified'] = False

    def get_columns_text(self, context: Context, candidate: dict) -> str:
        text = ''
        for column in self._columns:
            text += column.get(context, candidate)
        return text

    def get_selected_candidates(self, cursor: int) -> typing.List[dict]:
        if not self._selected_candidates:
            return [self._candidates[cursor - 1]]
        else:
            return [self._candidates[x] for x in self._selected_candidates]

    def do_action(self, action_name: str, action_args: typing.List[str],
                  new_context: dict) -> None:
        """
        Do "action" action.
        """
        if not self._candidates:
            return

        cursor = new_context['cursor']
        context = self._context._replace(
            targets=self.get_selected_candidates(cursor),
            args=action_args,
            cursor=cursor)

        import defx.action as action
        for defx in self._defxs:
            action.do_action(self, defx, action_name, context)