Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/pathpicker/command_mode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import os
import subprocess


def expand_tokens(cmd: str, file_path: str) -> str:
"""
Replace custom tokens in command string.
$FILE -> full file path
$DIR -> directory of file
$BASENAME -> file name without path
"""
return (
cmd.replace("$FILE", file_path)
.replace("$DIR", os.path.dirname(file_path))
.replace("$BASENAME", os.path.basename(file_path))
)


def run_command(cmd: str, file_path: str, auto_mode: bool = False) -> None:
"""
Runs a command safely with token expansion.
"""
if not file_path or not os.path.exists(file_path):
print(f"Error: file {file_path} does not exist!")
return

cmd_to_run = expand_tokens(cmd, file_path)

if not auto_mode:
confirm = input(f"Run command '{cmd_to_run}'? [y/N] ")
if confirm.lower() != "y":
print("Command skipped.")
return

try:
subprocess.run(cmd_to_run, shell=True, check=True)
except subprocess.CalledProcessError as e:
print(f"Command failed: {e}")
17 changes: 13 additions & 4 deletions src/pathpicker/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
from pathpicker import logger
from pathpicker.repos import REPOS

from pathpicker.regexes import MASTER_REGEX, JUST_FILE_WITH_SPACES # type: ignore

MatchResult = NewType("MatchResult", Tuple[str, int, Match])

MASTER_REGEX = re.compile(
MASTER_RsEGEX = re.compile(
r"(/?([a-z.A-Z0-9\-_]+/)+[@a-zA-Z0-9\-_+.]+\.[a-zA-Z0-9]{1,10})[:-]?(\d+)?"
)
MASTER_REGEX_MORE_EXTENSIONS = re.compile(
Expand All @@ -32,9 +34,10 @@
JUST_FILE = re.compile(r"([@%+a-z.A-Z0-9\-_]+\.[a-zA-Z]{1,10})(\s|$|:)+")
JUST_EMACS_TEMP_FILE = re.compile(r"([@%+a-z.A-Z0-9\-_]+\.[a-zA-Z]{1,10}~)(\s|$|:)+")
JUST_VIM_TEMP_FILE = re.compile(r"(#[@%+a-z.A-Z0-9\-_]+\.[a-zA-Z]{1,10}#)(\s|$|:)+")
# start with a normal char for ls -l
JUST_FILE_WITH_SPACES = re.compile(
r"([a-zA-Z][@+a-z. A-Z0-9\-_]+\.[a-zA-Z]{1,10})(\s|$|:)+"
# matches normal files including spaces, parentheses, and accented letters
JUST_FILE_WITH_SPACES_UNICODE = re.compile(
r"([\w\s\(\)@%+\-\.]+)\.[a-zA-Z0-9-]{1,30}(\s|$|:)+",
re.UNICODE
)
FILE_NO_PERIODS = re.compile(
(
Expand Down Expand Up @@ -231,6 +234,12 @@ class RegexConfig(NamedTuple):
no_num=True,
with_all_lines_matched=True,
),
RegexConfig(
"JUST_FILE_WITH_SPACES_UNICODE",
JUST_FILE_WITH_SPACES_UNICODE,
no_num=True,
only_with_file_inspection=True
),
]


Expand Down
44 changes: 44 additions & 0 deletions src/pathpicker/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# LICENSE file in the root directory of this source tree.
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Tuple
from .command_mode import run_command

if TYPE_CHECKING:
import curses
Expand Down Expand Up @@ -75,3 +76,46 @@ def getstr(self, y_pos: int, x_pos: int, max_len: int) -> str:
if isinstance(result, int):
return str(result)
return result.decode("utf-8")

def clrtoeol(self) -> None:
"""
Clears from cursor to end of line using curses.
"""
if hasattr(self.screen, "clrtoeol"):
self.screen.clrtoeol()
else:
max_y, max_x = self.getmaxyx()
y, x = self.screen.getyx()
for _ in range(x, max_x):
self.delch(y, x)
self.refresh()

def handle_command_mode(self, current_selection: str) -> None:
"""
Trigger command mode for the selected file/directory.
"""
self.addstr(0, 0, "Enter command: ", 0)
self.refresh()
cmd_input = self.getstr(0, 15, 100) # read input from user

# Run the command with token expansion
run_command(cmd_input, current_selection, auto_mode=False)

# Clear the input line after running
self.move(0, 0)
self.clrtoeol()
self.refresh()

def handle_input(self, current_selection: str) -> None:
"""
Main input loop for the screen.
"""
while True:
key = self.getch()

if key in (ord('q'), ord('Q')):
break # quit
elif key == ord(':'):
# Enter command mode
self.handle_command_mode(current_selection)
# Add other key handling here, e.g., navigation