Skip to content

Commit

Permalink
Merge branch 'dev' into chad
Browse files Browse the repository at this point in the history
  • Loading branch information
ms-jpq committed Aug 5, 2020
2 parents 67e1e25 + e3b8038 commit 4b6b0af
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 46 deletions.
35 changes: 25 additions & 10 deletions rplugin/python3/chadtree/da.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,32 @@
from itertools import count
from json import dump, load
from operator import pow
from os import makedirs
from os import environ, makedirs
from os.path import dirname, exists
from subprocess import CompletedProcess, run
from sys import version_info
from typing import Any, Callable, Optional, TypeVar, cast
from typing import Any, Callable, Dict, Optional, TypeVar, Union, cast

from .consts import folder_mode

T = TypeVar("T")


class Void:
def __bool__(self) -> bool:
return False

def __eq__(self, o: Any) -> bool:
return type(o) is Void

def __str__(self) -> str:
return type(self).__name__


def or_else(thing: Union[T, Void], default: T) -> T:
return default if thing == Void() else cast(T, thing)


def merge(ds1: Any, ds2: Any, replace: bool = False) -> Any:
if type(ds1) is dict and type(ds2) is dict:
append = {k: merge(ds1.get(k), v, replace) for k, v in ds2.items()}
Expand All @@ -36,10 +51,6 @@ def merge_all(ds1: Any, *dss: Any, replace: bool = False) -> Any:
return res


def or_else(thing: Optional[T], default: T) -> T:
return default if thing is None else thing


def constantly(val: T) -> Callable[[Any], T]:
def ret(*args: Any, **kwargs: Any) -> T:
return val
Expand Down Expand Up @@ -69,11 +80,12 @@ class ProcReturn:

if (version_info.major, version_info.minor) == (3, 7):

async def call(prog: str, *args: str) -> ProcReturn:
async def call(prog: str, *args: str, env: Dict[str, str] = {}) -> ProcReturn:
loop = get_running_loop()

def cont() -> CompletedProcess:
return run((prog, *args), capture_output=True)
envi = {**environ, **env}
return run((prog, *args), capture_output=True, env=envi)

ret = await loop.run_in_executor(None, cont)
out, err = ret.stdout.decode(), ret.stderr.decode()
Expand All @@ -83,8 +95,11 @@ def cont() -> CompletedProcess:

else:

async def call(prog: str, *args: str) -> ProcReturn:
proc = await create_subprocess_exec(prog, *args, stdout=PIPE, stderr=PIPE)
async def call(prog: str, *args: str, env: Dict[str, str] = {}) -> ProcReturn:
envi = {**environ, **env}
proc = await create_subprocess_exec(
prog, *args, stdout=PIPE, stderr=PIPE, env=envi
)
stdout, stderr = await proc.communicate()
code = cast(int, proc.returncode)
return ProcReturn(code=code, out=stdout.decode(), err=stderr.decode())
Expand Down
49 changes: 42 additions & 7 deletions rplugin/python3/chadtree/git.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from asyncio import gather
from locale import strxfrm
from os import linesep
from os.path import join, sep
from shutil import which
from typing import Dict, Iterator, Set, Tuple
Expand All @@ -8,6 +9,10 @@
from .fs import ancestors
from .types import VCStatus

GIT_LIST_CMD = ("git", "status", "--ignored", "--renames", "--porcelain")
GIT_SUBMODULE_MARKER = "Entering "
GIT_ENV = {"LC_ALL": "C"}


class GitError(Exception):
pass
Expand All @@ -21,21 +26,50 @@ async def root() -> str:
return ret.out.rstrip()


async def stat() -> Dict[str, str]:
ret = await call("git", "status", "--ignored", "--renames", "--porcelain", "-z")
async def stat_main() -> Dict[str, str]:
ret = await call(*GIT_LIST_CMD, "-z")
if ret.code != 0:
raise GitError(ret.err)
else:
it = iter(ret.out.split("\0"))

def items() -> Iterator[Tuple[str, str]]:
it = iter(ret.out.split("\0"))
def cont() -> Iterator[Tuple[str, str]]:
for line in it:
prefix, file = line[:2], line[3:]
yield prefix, file.rstrip(sep)
if "R" in prefix:
next(it, None)

entries = {file: prefix for prefix, file in items()}
entries = {file: prefix for prefix, file in cont()}
return entries


async def stat_sub_modules() -> Dict[str, str]:
ret = await call(
"git",
"submodule",
"foreach",
"--recursive",
" ".join(GIT_LIST_CMD),
env=GIT_ENV,
)
if ret.code != 0:
raise GitError(ret.err)
else:
it = iter(ret.out.split(linesep))

def cont() -> Iterator[Tuple[str, str]]:
root = ""
for line in it:
if line.startswith(GIT_SUBMODULE_MARKER):
root = line[len(GIT_SUBMODULE_MARKER) + 1 : -1].rstrip(linesep)
else:
prefix, file = line[:2], line[3:]
yield prefix, join(root, file.rstrip(sep))
if "R" in prefix:
next(it, None)

entries = {file: prefix for prefix, file in cont()}
return entries


Expand Down Expand Up @@ -64,8 +98,9 @@ def parse(root: str, stats: Dict[str, str]) -> VCStatus:
async def status() -> VCStatus:
if which("git"):
try:
r, stats = await gather(root(), stat())
return parse(r, stats)
r, s_main, s_sub = await gather(root(), stat_main(), stat_sub_modules())
stats = {**s_sub, **s_main}
return parse(r, s_sub)
except GitError:
return VCStatus()
else:
Expand Down
5 changes: 3 additions & 2 deletions rplugin/python3/chadtree/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .da import constantly
from .types import (
Badge,
FilterPattern,
Highlight,
HLgroup,
Index,
Expand Down Expand Up @@ -176,7 +177,7 @@ def render(
settings: Settings,
index: Index,
selection: Selection,
filter_pattern: str,
filter_pattern: Optional[FilterPattern],
qf: QuickFix,
vc: VCStatus,
show_hidden: bool,
Expand All @@ -191,7 +192,7 @@ def render(
def render(
node: Node, *, depth: int, cleared: bool
) -> Iterator[Tuple[Node, Render]]:
clear = cleared or not filter_pattern or fnmatch(node.name, filter_pattern)
clear = cleared or not filter_pattern or fnmatch(node.name, filter_pattern.pattern)
rend = show(node, depth)

def gen_children() -> Iterator[Tuple[Node, Render]]:
Expand Down
44 changes: 25 additions & 19 deletions rplugin/python3/chadtree/state.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
from asyncio import gather
from hashlib import sha1
from os.path import join
from typing import Optional
from typing import Optional, Set, Union, cast

from pynvim import Nvim

from .cartographer import new, update
from .consts import session_dir
from .da import dump_json, load_json, or_else
from .da import Void, dump_json, load_json, or_else
from .git import status
from .nvim import getcwd
from .quickfix import quickfix
from .render import render
from .types import (
FilterPattern,
Index,
Mode,
Node,
QuickFix,
Selection,
Session,
Set,
Settings,
State,
VCStatus,
Expand Down Expand Up @@ -60,7 +60,7 @@ async def initial(nvim: Nvim, settings: Settings) -> State:
node, qf = await gather(new(cwd, index=index), quickfix(nvim))
vc = VCStatus() if not version_ctl.enable or version_ctl.defer else await status()
current = None
filter_pattern = ""
filter_pattern = None
lookup, rendered = render(
node,
settings=settings,
Expand All @@ -77,7 +77,7 @@ async def initial(nvim: Nvim, settings: Settings) -> State:
state = State(
index=index,
selection=selection,
filter_pattern="",
filter_pattern=filter_pattern,
show_hidden=settings.show_hidden,
follow=settings.follow,
enable_vc=settings.version_ctl.enable,
Expand All @@ -97,25 +97,31 @@ async def forward(
state: State,
*,
settings: Settings,
root: Optional[Node] = None,
index: Optional[Index] = None,
selection: Optional[Selection] = None,
filter_pattern: Optional[str] = None,
show_hidden: Optional[bool] = None,
follow: Optional[bool] = None,
enable_vc: Optional[bool] = None,
width: Optional[int] = None,
qf: Optional[QuickFix] = None,
vc: Optional[VCStatus] = None,
current: Optional[str] = None,
paths: Optional[Set[str]] = None,
root: Union[Node, Void] = Void(),
index: Union[Index, Void] = Void(),
selection: Union[Selection, Void] = Void(),
filter_pattern: Union[Optional[FilterPattern], Void] = Void(),
show_hidden: Union[bool, Void] = Void(),
follow: Union[bool, Void] = Void(),
enable_vc: Union[bool, Void] = Void(),
width: Union[int, Void] = Void(),
qf: Union[QuickFix, Void] = Void(),
vc: Union[VCStatus, Void] = Void(),
current: Union[str, Void] = Void(),
paths: Union[Set[str], Void] = Void(),
) -> State:
new_index = or_else(index, state.index)
new_selection = or_else(selection, state.selection)
new_filter_pattern = or_else(filter_pattern, state.filter_pattern)
new_current = or_else(current, state.current)
new_root = root or (
await update(state.root, index=new_index, paths=paths) if paths else state.root
new_root = cast(
Node,
root
or (
await update(state.root, index=new_index, paths=cast(Set[str], paths))
if paths
else state.root
),
)
new_qf = or_else(qf, state.qf)
new_vc = or_else(vc, state.vc)
Expand Down
29 changes: 22 additions & 7 deletions rplugin/python3/chadtree/transitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
Sequence,
Set,
Tuple,
Union,
cast,
)

Expand All @@ -23,7 +24,7 @@
from pynvim.api.window import Window

from .cartographer import new as new_root
from .da import human_readable_size
from .da import Void, human_readable_size
from .fs import (
ancestors,
copy,
Expand All @@ -43,7 +44,17 @@
from .state import index as state_index
from .state import is_dir
from .system import SystemIntegrationError, open_gui, trash
from .types import ClickType, Index, Mode, Node, Selection, Settings, State, VCStatus
from .types import (
ClickType,
FilterPattern,
Index,
Mode,
Node,
Selection,
Settings,
State,
VCStatus,
)
from .wm import (
find_current_buffer_name,
is_fm_buffer,
Expand Down Expand Up @@ -324,7 +335,9 @@ def co() -> str:
current = await call(nvim, co)
cwd = state.root.path
paths = {cwd}
new_current = current if is_parent(parent=cwd, child=current) else None
new_current = cast(
Union[str, Void], current if is_parent(parent=cwd, child=current) else Void()
)

def cont() -> Tuple[Index, Selection]:
index = {i for i in state.index if exists(i)} | paths
Expand All @@ -335,7 +348,7 @@ def cont() -> Tuple[Index, Selection]:

index, selection = await loop.run_in_executor(None, cont)
current_paths: Set[str] = {*ancestors(current)} if state.follow else set()
new_index = index if new_current is None else index | current_paths
new_index = index if new_current == Void() else index | current_paths

qf, vc = await gather(quickfix(nvim), _vc_stat(state.enable_vc))
new_state = await forward(
Expand Down Expand Up @@ -389,10 +402,12 @@ async def c_toggle_vc(nvim: Nvim, state: State, settings: Settings) -> State:

async def c_new_filter(nvim: Nvim, state: State, settings: Settings) -> State:
def ask() -> Optional[str]:
resp = nvim.funcs.input("New filter:", state.filter_pattern)
pattern = state.filter_pattern.pattern if state.filter_pattern else ""
resp = nvim.funcs.input("New filter:", pattern)
return resp

filter_pattern = await call(nvim, ask) or ""
pattern = await call(nvim, ask)
filter_pattern = FilterPattern(pattern=pattern) if pattern else None
new_state = await forward(
state, settings=settings, selection=set(), filter_pattern=filter_pattern
)
Expand Down Expand Up @@ -526,7 +541,7 @@ async def c_clear_selection(nvim: Nvim, state: State, settings: Settings) -> Sta


async def c_clear_filter(nvim: Nvim, state: State, settings: Settings) -> State:
new_state = await forward(state, settings=settings, filter_pattern="")
new_state = await forward(state, settings=settings, filter_pattern=None)
return new_state


Expand Down
7 changes: 6 additions & 1 deletion rplugin/python3/chadtree/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,16 @@ class Render:
highlights: Sequence[Highlight]


@dataclass(frozen=True)
class FilterPattern:
pattern: str


@dataclass(frozen=True)
class State:
index: Index
selection: Selection
filter_pattern: str
filter_pattern: Optional[FilterPattern]
show_hidden: bool
follow: bool
enable_vc: bool
Expand Down

0 comments on commit 4b6b0af

Please sign in to comment.