twd-m4sc0 3.0.2__tar.gz → 3.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/PKG-INFO +1 -1
- twd_m4sc0-3.0.4/README.md +67 -0
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/pyproject.toml +3 -1
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/src/twd/cli.py +5 -2
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/src/twd/data.py +0 -1
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/src/twd/tui.py +7 -3
- twd_m4sc0-3.0.4/src/twd/tui.tcss +58 -0
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/src/twd/utils.py +12 -11
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/src/twd_m4sc0.egg-info/PKG-INFO +1 -1
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/src/twd_m4sc0.egg-info/SOURCES.txt +1 -0
- twd_m4sc0-3.0.2/README.md +0 -34
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/setup.cfg +0 -0
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/src/twd/__init__.py +0 -0
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/src/twd/config.py +0 -0
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/src/twd_m4sc0.egg-info/dependency_links.txt +0 -0
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/src/twd_m4sc0.egg-info/entry_points.txt +0 -0
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/src/twd_m4sc0.egg-info/requires.txt +0 -0
- {twd_m4sc0-3.0.2 → twd_m4sc0-3.0.4}/src/twd_m4sc0.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# twd
|
|
2
|
+
|
|
3
|
+
A command-line tool for managing and navigating between directories in the terminal.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
`twd` lets you bookmark directories and quickly jump between them. Instead of typing out long paths or navigating through multiple `cd` commands, you save directories once and access them with vim-like motion bindings.
|
|
8
|
+
|
|
9
|
+
Think of it as a directory hub for places you visit frequently.
|
|
10
|
+
|
|
11
|
+
## Why this exists
|
|
12
|
+
|
|
13
|
+
I got tired of constantly navigating through the same directory trees. This tool saves time when you're working across multiple projects or deep directory structures.
|
|
14
|
+
|
|
15
|
+
No AI was used in the development of this tool. It's written by hand, contains bugs, and isn't particularly optimized. But it works for what I need, and I'm continuing to improve it.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install twd-m4sc0
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Setup
|
|
24
|
+
|
|
25
|
+
To actually change directories (rather than just print paths), add this function to your `~/.bashrc` or `~/.zshrc`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
t () {
|
|
29
|
+
binary="twd"
|
|
30
|
+
local target=$($binary "$@" 3>&1 >/dev/tty)
|
|
31
|
+
if [[ -n "$target" && -d "$target" ]]; then
|
|
32
|
+
cd "$target"
|
|
33
|
+
fi
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The tool can work without this setup, but you'll only get path output instead of directory changes. This also allows for a quicker launch — instead of having to type `twd` over and over you can start it with just `t` (might conflict with other programs or functions).
|
|
38
|
+
|
|
39
|
+
## Dev environment
|
|
40
|
+
|
|
41
|
+
Clone the repo and install locally with
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install -e .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
It is recommend to create a virtual environment and install the dependencies beforehand.
|
|
48
|
+
|
|
49
|
+
## How it works
|
|
50
|
+
|
|
51
|
+
`twd` uses file descriptor 3 to communicate the target directory to the shell function. The bash function captures this output and executes the `cd` command. This avoids using temporary files.
|
|
52
|
+
|
|
53
|
+
The interface is built with Textual, a Python TUI framework.
|
|
54
|
+
|
|
55
|
+
## Version 3
|
|
56
|
+
|
|
57
|
+
This is a complete rewrite from version 2. If you're using v2.0.3 or earlier, the archived version is available [here](https://github.com/m4sc0/twd-archived).
|
|
58
|
+
|
|
59
|
+
Changes include better directory handling, a proper TUI instead of hand-rolled curses code, and generally cleaner implementation.
|
|
60
|
+
|
|
61
|
+
## Status
|
|
62
|
+
|
|
63
|
+
The project is functional but has known issues. I'm actively developing it and fixing bugs as I find them. Check the issues tab for current problems and planned improvements. Contributions are always appreciated!
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "twd-m4sc0"
|
|
3
|
-
version = "3.0.
|
|
3
|
+
version = "3.0.4"
|
|
4
4
|
description = "TWD by m4sc0"
|
|
5
5
|
authors = [{name = "m4sc0"}]
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -30,3 +30,5 @@ build-backend = "setuptools.build_meta"
|
|
|
30
30
|
[tool.setuptools.packages.find]
|
|
31
31
|
where = ["src"]
|
|
32
32
|
|
|
33
|
+
[tool.setuptools.package-data]
|
|
34
|
+
twd = ["*.tcss"]
|
|
@@ -24,10 +24,13 @@ def cli(ctx):
|
|
|
24
24
|
|
|
25
25
|
path = TWDApp(manager=ctx.obj['manager']).run()
|
|
26
26
|
|
|
27
|
+
if not path:
|
|
28
|
+
print("Exiting...")
|
|
29
|
+
exit(0)
|
|
30
|
+
|
|
27
31
|
# write to fd3
|
|
28
32
|
os.write(3, bytes(str(path), "utf-8"))
|
|
29
|
-
|
|
30
|
-
pass
|
|
33
|
+
exit(0)
|
|
31
34
|
|
|
32
35
|
@cli.command()
|
|
33
36
|
@click.argument('path')
|
|
@@ -8,7 +8,7 @@ from textual.color import Color
|
|
|
8
8
|
|
|
9
9
|
from twd.config import Config
|
|
10
10
|
from twd.data import TwdManager
|
|
11
|
-
from twd.utils import
|
|
11
|
+
from twd.utils import fuzzy_search, linear_search
|
|
12
12
|
|
|
13
13
|
class Mode(Enum):
|
|
14
14
|
NORMAL = "normal"
|
|
@@ -177,9 +177,13 @@ class TWDApp(App):
|
|
|
177
177
|
|
|
178
178
|
# TODO: filter entries and repopulate table
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
if query is None:
|
|
181
|
+
self._populate_table()
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
search_result = fuzzy_search(query, all_entries)
|
|
181
185
|
|
|
182
|
-
filtered = [
|
|
186
|
+
filtered = [item[0] for item in search_result]
|
|
183
187
|
|
|
184
188
|
self._populate_table(filtered)
|
|
185
189
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Screen {
|
|
2
|
+
background: transparent;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
# DataTable
|
|
6
|
+
|
|
7
|
+
#data {
|
|
8
|
+
width: 100%;
|
|
9
|
+
height: 1fr;
|
|
10
|
+
background: transparent;
|
|
11
|
+
border: none;
|
|
12
|
+
# margin: 4 8;
|
|
13
|
+
# padding: 1 2;
|
|
14
|
+
margin: 0;
|
|
15
|
+
padding: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#data > .datatable--header {
|
|
19
|
+
background: $surface-lighten-1;
|
|
20
|
+
color: $text;
|
|
21
|
+
text-style: bold;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#data > .datatable--cursor {
|
|
25
|
+
background: $accent;
|
|
26
|
+
color: $text;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#data > .datatable--odd-row {
|
|
30
|
+
background: $surface-lighten-1;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#data > .datatable--even-row {
|
|
34
|
+
background: transparent;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# CWD
|
|
38
|
+
Label.cwd {
|
|
39
|
+
margin: 1 2;
|
|
40
|
+
width: 10fr;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Mode
|
|
44
|
+
Label#mode {
|
|
45
|
+
padding: 1 2;
|
|
46
|
+
background: $surface-lighten-1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Label#mode.search {
|
|
50
|
+
background: $accent;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Search input
|
|
54
|
+
Input#search-input {
|
|
55
|
+
margin: 0 0 1 0;
|
|
56
|
+
border: round $accent;
|
|
57
|
+
background: transparent;
|
|
58
|
+
}
|
|
@@ -1,39 +1,40 @@
|
|
|
1
1
|
from rapidfuzz import fuzz
|
|
2
2
|
from typing import List
|
|
3
3
|
|
|
4
|
-
FUZZY_THRESHOLD = 50
|
|
5
|
-
|
|
6
4
|
def normalize(name) -> str:
|
|
7
5
|
return name.lower().replace('-', ' ').replace('_', ' ')
|
|
8
6
|
|
|
9
|
-
def
|
|
7
|
+
def fuzzy_search(query, items, threshold = 50) -> List:
|
|
10
8
|
"""
|
|
11
9
|
query: search input
|
|
12
10
|
items: list of (alias, name)
|
|
11
|
+
threshold: threshold score over which the entries are selected
|
|
13
12
|
|
|
14
|
-
returns: list of (
|
|
13
|
+
returns: list of (entry, score)
|
|
15
14
|
"""
|
|
16
15
|
|
|
17
16
|
# return all items if query is empty
|
|
18
17
|
if not query:
|
|
19
|
-
return [(
|
|
18
|
+
return [(e, 100) for e in items]
|
|
20
19
|
|
|
21
20
|
normalized_query = normalize(query)
|
|
22
21
|
results = []
|
|
23
22
|
|
|
24
23
|
# filtering
|
|
25
|
-
for
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
for entry in items:
|
|
25
|
+
alias, name = normalize(entry.alias), normalize(entry.name)
|
|
26
|
+
|
|
27
|
+
alias_score = fuzz.ratio(normalized_query, alias)
|
|
28
|
+
name_score = fuzz.ratio(normalized_query, name)
|
|
28
29
|
|
|
29
30
|
# choose higher score
|
|
30
31
|
best_score = max(alias_score, name_score)
|
|
31
32
|
|
|
32
33
|
# filter out low scores
|
|
33
|
-
if best_score
|
|
34
|
-
results.append((
|
|
34
|
+
if best_score > threshold:
|
|
35
|
+
results.append((entry, best_score))
|
|
35
36
|
|
|
36
|
-
results.sort(key=lambda x: x[
|
|
37
|
+
results.sort(key=lambda x: x[1], reverse=True)
|
|
37
38
|
|
|
38
39
|
return results
|
|
39
40
|
|
twd_m4sc0-3.0.2/README.md
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# twd-m4sc0
|
|
2
|
-
|
|
3
|
-
> [!IMPORTANT]
|
|
4
|
-
> This is complete rewrite of the program `twd`. If you're using `<=v2.0.3` please make sure to upgrade to the newest major release. The archived version v2 can be viewed [here](https://github.com/m4sc0/twd-archived).
|
|
5
|
-
|
|
6
|
-
> twd-m4sc0 / twd is a command-line tool that allows you to temporarily save a working directory and easily navigate back to it. It's designed for developers and users who frequently need to switch between directories in the terminal.
|
|
7
|
-
|
|
8
|
-
That's what it was supposed to do at the start. Now it's more like a hub for your frequently visited directories. You can use it like a bookmark manager or for the quickest method of changing between directories that you can find.
|
|
9
|
-
|
|
10
|
-
There are quite a few things I wanna make better this time.
|
|
11
|
-
|
|
12
|
-
### Better directory changing
|
|
13
|
-
|
|
14
|
-
Previously twd wrote to a temp file, then a bash function used the contents if that file exists, cd's to the dir and deletes the file again. This time around I'm going a different way. I found the method `os.write(3, path)` which can write to [file descriptors](https://en.wikipedia.org/wiki/File_descriptor) directly. This is the same way stdout and stderr are handled in the background (i think). But i'm using a fourth (0 = stdin, 1 = stdout, 2 = stderr), unused FD to write to. This is then captured in the bash function separately from the stdout.
|
|
15
|
-
|
|
16
|
-
### Improved TUI
|
|
17
|
-
|
|
18
|
-
For some reason I thought writing a whole UI system using [curses](https://de.wikipedia.org/wiki/Curses) was a good idea. Well, now at least I know better and can say that I won't do that ever again.
|
|
19
|
-
|
|
20
|
-
I'll use [Textual](https://textual.textualize.io/) now.
|
|
21
|
-
|
|
22
|
-
### Setup
|
|
23
|
-
|
|
24
|
-
It's possible to use TWD without the feature of cd'ing anywhere. But that's kinda lame lol. To make sure it works as intended, copy the following snippet into something like a `~/.bashrc` file.
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
t () {
|
|
28
|
-
binary="twd"
|
|
29
|
-
local target=$($binary "$@" 3>&1 >/dev/tty)
|
|
30
|
-
if [[ -n "$target" && -d "$target" ]]; then
|
|
31
|
-
cd "$target"
|
|
32
|
-
fi
|
|
33
|
-
}
|
|
34
|
-
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|