filecon 0.2.1 → 0.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d62cae7fca7f8ef2348b79ac2df27edfafbc5da7ada9523a2fad5b38de27e5a9
4
- data.tar.gz: 20fe942d398a0dd68e27a5cdc5b40d16d296e33399327494aaa608d847401e47
3
+ metadata.gz: 27fa8d3a100f9d752db3fde969136cb2c1435c809970691629e46e0c897b2a46
4
+ data.tar.gz: 87bd4e4845433cbda8c0c35f70c4e7dcc94ff95c026cfa0f84fd9cecc211201d
5
5
  SHA512:
6
- metadata.gz: 1d2ac9423eecd422db9ac518ba264ef56b3f9d17823950f648e56eff3de0dd08326daba18bfd8eeeba51d76e072a060f4be5c6542a50c709623adac370f080f0
7
- data.tar.gz: 198c5fa607c36cbc86808d42e3b0a806d83832ba5432827c871526d8ebaebaaa8cc3864f7a486e586b00ce2e983086e81184d08fe993f93ca2fa58fd7b0c9fd6
6
+ metadata.gz: 9cc34718bdfdd72ebbadf9cf91cd3d654df2ac6b5350c9c9160ba03577d692541e8d117ead9452cba6f1a5b8f9a05a3195691a53caccce496b8790e53ed7e5eb
7
+ data.tar.gz: 4ddf3aefa8c71081089b61ebfc03a731c31f763478310091aa5cce43ff1aa16bc129a1e158c5620f7aad17b7a917090fd14ae22cb61ec435f61f44f8b6a372f8
data/CHANGELOG.md ADDED
@@ -0,0 +1,156 @@
1
+ # Changelog
2
+
3
+ ## [vx.y.z] — __DATE__
4
+
5
+ ## [v0.4.0] — 2026-09-05
6
+
7
+ ### Added
8
+ - Shell TAB completion in the command editor — alt+hint opens the edit line with live completions from the shell's completion system, shown in a pickable grid above the input
9
+ - Clickable bottom bar and head — clicking a binding label on the status line runs that action, and clicking the filter/sort/views columns of the head opens the corresponding popup
10
+ - Two-pane help — `h` shows the clickable keybinding grid (the line's `list`) beside the help text; Filecon composes the HELP panel itself, mirrors wheel/arrow navigation to both panels, and clicking a binding row returns its trigger key
11
+ - Clicking outside a popup dismisses it — a click on empty space cancels the command, history, or filter/sort/views picker just like Escape, and the two-pane help closes on a click outside both panes
12
+ - Colored help titles — the FILECON, HINTS, COMMANDS, CONFIG headings and the KEYBINDINGS grid header are yellow; the redundant HELP text header is removed
13
+
14
+ ### Fixed
15
+ - The mouse wheel scrolls the command, history, and filter/sort/views popups — previously the live filter reapplied itself after each wheel tick and snapped the list back to the top
16
+ - The colored `[f]ilter`/`[s]ort`/`[v]iews` bindings on the bottom bar now react to clicks — the trigger-key extraction no longer trips on their ANSI color codes
17
+ - The filter/sort/views columns on the head open their popups when clicked in a real terminal — the head row now matches the 1-based mouse coordinates
18
+ - Clicking the bottom bar binds to the row above it — the clickable row check now uses 1-based mouse coordinates, so bindings activate where they are rendered
19
+ - Clicking a keybinding row in the two-pane help no longer crashes — the clicked row now maps to the line's binding labels (`@user.bindings`) instead of an undefined App `@bindings`, so the trigger key is returned and dispatched
20
+ - `cd` owns the path column flag — array `cd` sets `@show_path` (single dirs clear it), so `reset_view` preserves the path listing after `execute`/`recurse` instead of dropping it; `recurse` just builds the recursive directory array and delegates the loading to `cd`
21
+ - Array `cd` fills the path column after the full load — previously only the first directory's rows got values (the rest stayed blank on `reset_view`), and custom Hash views dropped the column entirely; the trailing `change_view` now runs once on complete data, and computed views keep PATH too
22
+ - Clickable header path — left-clicking the username goes home and each directory segment navigates there, while right-click opens the command list for that path without leaving the current directory
23
+ - A binding picked in help now runs — `fly` re-dispatches a String return from `send` (as it already did for mouse clicks), so pressing `h` and clicking e.g. `[R]ecurse` executes that action instead of just closing the help page
24
+ - The help text panel sits flush with the terminal edge — the text panel's `right: -2` border now draws on the last column, matching the browser edge; the previous inset was caused by an off-by-one in the `draw_border` guard
25
+
26
+ ## [v0.3.0] — 2026-08-15
27
+
28
+ ### Added
29
+ - New `%cmd` substitution — lists the PATH, picks an executable, and inserts its path; the listing is left and the original directory restored afterwards
30
+ - Alt + hint digit in the command and cmd-history popups puts the picked command into a `read_line` for editing before it runs (dir-history entries are not edited); the command popup resolves its `%name`/`%dir`/`%file`/`%str` substitutions before the resulting command is offered for editing
31
+ - Alt + number in the main view opens the command list for the hinted file (replacing the `o` action)
32
+ - Right-click in the main view opens the command list for the clicked file — mirroring Alt+number, with left-click still running the default action
33
+ - Return with marked files (`m`) opens their command list — replacing the `o` action's multi-select behaviour, now reachable through the default confirm key
34
+
35
+ ### Changed
36
+ - `%dir` picking relies on typr's `pick :directory` to confirm the current directory with Return — the `@pick_dir` state is gone, and `%dir`/`%file`/`%cmd` share one substitution branch
37
+ - Renamed the view-switching method to `change_view` (was `switch_to`), matching the clearer typr naming — keybinding, popup, and specs updated
38
+ - Removed the `o` key action — the command list for a file is now opened with Alt+number
39
+ - Shell-generated custom view columns are passed through `coerce_type`, so numeric output (e.g. `wc` counts) sorts and displays numerically
40
+ - Custom view shell output is stripped before display, so trailing newlines no longer yield a trailing `|` in multi-line columns
41
+ - Dropped the now-redundant `hints: "1234567890"` overrides from the command and history popups — digits-only hints are the typr default
42
+
43
+ ### Fixed
44
+ - `%dir` substitution pickers confirm the current directory with Return again — the Return-marked-files action only applies outside substitution mode
45
+ - The file list is redrawn immediately while a `|` command's output is shown — the command picker popup no longer stays on screen behind the statusline, and the statusline keeps showing the command after it returns
46
+ - Per-MIME-type row colors stay correct after deleting a file — `delete_row` now reindexes the `@colors[:fields]` keys together with `@map`, so rows below the deletion no longer show the previous row's color until `[r]eset`
47
+ - PTY driver no longer garbles or hangs — the screen decoder holds partial multi-byte UTF-8 across read chunks (box-drawing glyphs render intact), clamps the cursor to the terminal grid and caps the raw log so the rendered screen stays bounded, key resends ignore a dead pty instead of raising, and `quit` escalates `ESC` → `TERM` → `KILL` so it always reaps the app
48
+ - Spec cleanup — `picker_double`, `test_id`, `with_path`, and `with_cmd_target` helpers removed the grid-picker and id-lookup boilerplate, and duplicated `#change_view`, `#history`, `%cmd`, and colored `%dir`/`%file` examples were merged into looping specs
49
+
50
+ ## [v0.2.1] — 2026-08-03
51
+
52
+ ### Added
53
+ - Mouse support in `fly` — click a row to open it in its default app, wheel up/down scrolls the listing (requires typr `~> 1.3` mouse reporting, enabled by `Typr.init`)
54
+ - PTY integration driver — `spec/integration/pty_driver.rb` is an expect-style smoke suite that spawns the real `bin/filecon` on a pseudo-terminal, drives it with keystrokes, and asserts on the rendered screen; run as part of `rake test` (or manually via `ruby spec/integration/pty_driver.rb`, with `--interactive` for a live probe and `--keep` to retain temp fixtures)
55
+
56
+ ## [v0.2.0] — 2026-08-02
57
+
58
+ ### Added
59
+ - `executable` command category — `commands:` entries under `executable:` (`run`, `run_terminal`) are added to the `open_file` picker ahead of `default` whenever the selected file(s) have the executable bit set, regardless of MIME type; multi-select adds them only when every selection is executable
60
+ - `execute` command (`x`, `e[x]ecute`) — lists every executable from all `$PATH` directories in the main view, with a `path` column showing each binary's containing directory
61
+ - `open_current` (`` ` ``) — opens the directory under the cursor in its default app
62
+ - Full-path filenames: `cd` stores absolute paths in `@data`; `@procs[NAME]` displays basenames via `File.basename`; `build` uses `@data[id][NAME]` directly (no `@directory` prefix)
63
+ - `@directory` accepts an Array (for multi-path listings like `execute`); `draw` joins Array entries with `": "` for the status display
64
+ - `frequency_limit` config var (default 10) — only the top N most-used entries get frequency coloring in history popups, listed frequency-first then most-recent
65
+ - `filter_dirs` config flag (default true) — active filters also hide directories that don't match
66
+ - Shell history importer — `import_shell_history` reads `~/.bash_history`/`~/.zsh_history` once at startup, validates commands against `$PATH` and built-ins, resolves aliases from `.zshrc`/`.bashrc`, and merges new entries into `cmd_history`; a `shell_sync` flag in the state dir prevents re-import
67
+ - `open_file` status line now shows the picked command name first in `colors.commands` colors — `> name:display : output` (name in `colors.commands[0]`, the built command in `colors.commands[1]`; default `["green", "red"]`); the command picker popup uses the same configurable colors
68
+ - `build` prompts for `%dir`/`%file`/`%str` prepend the colored command `name:` prefix, so the partial command stays identifiable while being filled in
69
+ - `term_hold` config — hold flag appended to `@term` at init (e.g. `foot --hold`)
70
+
71
+ ### Changed
72
+ - Config location moved to `~/.config/filecon/config.yaml` — on first run the bundled `config.yaml` is copied there; state/history remain under `~/.local/share/filecon/`; string-keyed `colors:` settings (as loaded from YAML) are now honored
73
+ - `binaries` → `execute` (`x`), reset restored to `[r]eset`, recurse brought back as `[R]ecurse`
74
+ - `recurse` restored to `cd`-based appends (absolute paths), adding a `path` column (`Typr::PATH`) before the filename via a shared `add_path_column` helper
75
+ - `%str` substitution uses the shared `Typr.read_line` editor (renamed from `Typr.edit`): arrow/ctrl-arrow word jumps, in-place editing, Escape aborts the whole command
76
+ - Adapted to typr API renames: `Typr.read` → `read_key`, `Typr.edit` → `read_line`, frontend `show` → `draw` / widget render `draw` → `show`
77
+ - History storage: `@history`/`@frequency` ivars and the separate frequency files replaced with flat `@cmd_history`/`@dir_history` arrays of `[count, entry]` pairs managed by a `record` helper; file format is `count,entry` per line
78
+ - `run` executes unprefixed commands (e.g. from imported shell history) in a terminal window instead of doing nothing; the shell-history import skips `cd`/`pushd` entries (their directory targets still land in `dir_history`)
79
+ - Config command templates restructured (explicit `%str` args, image view alias)
80
+ - `[H]idden_files` binding moved after `[~]:home`; backtick dispatches through `Stack#send` as `open_current`
81
+ - Runtime dependency on typr raised to `~> 1.2` (requires the `read_key`/`read_line` API)
82
+
83
+ ### Removed
84
+ - `Filecon::PATH` constant — paths consolidated into a `@path` hash (`:config`, `:state`, `:dir_history`, `:cmd_history`)
85
+ - Separate `cmd_frequency`/`dir_frequency` files (merged into the `[count, entry]` history pairs)
86
+
87
+ ### Fixed
88
+ - `Grid#sort` used `ids` (which returns `@map` when set), so rows appended by subsequent `$PATH` dirs were invisible — now uses the full row range
89
+ - `cd` Array branch: missing `Dir.chdir(d)` and stale `@directory` before each append `super`, causing procs to resolve relative to the wrong directory
90
+ - `history`: `each_with_index.each_with_object` misrouted the memo block parameter (`fields[[i,0]] = ...` raised `NoMethodError`)
91
+ - `import_shell_history`: `shell_sync` was never written because the `added` flag checked `@cmd_history` after `record` already inserted; every occurrence now increments frequency, and the resolved command text (not the alias name) is stored
92
+ - `open_file` executable-category check skips when no row is selected (`open_current` with a nil id) instead of crashing on `@data[nil]`
93
+ - `recurse` no longer leaks hidden directory contents past the basename-only check
94
+ - `open_file` command picker keeps a fixed height derived from its full command list, so live-filter redraws don't resize/jump the popup
95
+ - History and command-picker popups scroll with arrow keys again — PageUp/PageDown/Home/End and TAB hint cycling are no longer swallowed by the line editor
96
+ - `Stack#send` keymap dispatch only normalizes printable ASCII keys, preserving control characters for correct key matching
97
+ - History popups show frequency entries with a grey background and `:brown` dir foreground; `du`/`wc`/`whatis` outputs trimmed of the trailing filename
98
+
99
+ ---
100
+
101
+ ## [v0.1.2] — 2026-07-28
102
+
103
+ ### Added
104
+ - Help page with keybindings and description (`h` key)
105
+ - Terminal resize handler (`Typr.on_resize`)
106
+ - `new_file` and `new_dir` commands for directories
107
+ - `count` view (chars, words, lines) replaces `lines` view
108
+ - `open_file` accepts optional `types` parameter for filtered command resolution
109
+ - `build` handles nil `id` for directory-level commands (backtick)
110
+
111
+ ### Changed
112
+ - Help page: simplified layout with proper positioning
113
+ - Backtick key opens commands for current directory instead of navigating to root
114
+ - `open_file`: pipe output displayed in status line instead of fork; `+` commands run directly (no terminal spawn)
115
+ - `run` uses instance vars `@cmd_history_path`, `@max_history` instead of module methods
116
+ - Setup code moved from `Filecon` module into `bin/filecon`
117
+ - `Filecon` module: removed `setup_config`, `deep_symbolize_keys`, `config`, `dir_history_path`, `cmd_history_path` — only `PATH` and `VERSION` remain
118
+ - Config uses string keys (no deep symbolization)
119
+ - Rakefile: cleaned to standard targets (build, clean, test, release)
120
+ - config.yaml: `copy` command uses pipe instead of terminal; `lines` view → `count`
121
+ - Man page: full rewrite — Typr references, YAML config, new keybinding docs
122
+ - README updated for YAML config, command prefixes, substitution variables
123
+ - Specs updated for new API (string keys, constructor args)
124
+
125
+ ### Fixed
126
+ - Constant redefinition warnings in tests
127
+ - Removed bundler dependency (Gemfile, Gemfile.lock)
128
+
129
+ ## [v0.1.1] — 2026-07-27
130
+
131
+ ### Added
132
+ - Error logging to `/tmp/filecon.log` for debugging crashes
133
+ - Fallback to `$TERM` when terminal config is missing
134
+
135
+ ### Changed
136
+ - Switched configuration format from TOML to YAML
137
+ - Simplified command execution: only `!` (Ruby eval) and shell commands (`|`, `+`, `=`)
138
+ - Config keys are now strings (no deep symbolization needed)
139
+ - Inlined setup code into `bin/filecon`, stripped `Filecon` module to `PATH` and `VERSION`
140
+ - Require strategy: `require_relative` for development, gem fallback for installed use
141
+ - Restructured as a proper Ruby gem with `bin/filecon` entry point
142
+
143
+ ### Fixed
144
+ - Relative paths in `lib/filecon.rb`
145
+ - `config.yaml` `colors.default` structure
146
+ - Removed obsolete root-level script and config files
147
+ - Updated all links to Codeberg (`typr/filecon`, `typr/typr`)
148
+
149
+ ## [v0.1.0] — 2026-07-20
150
+
151
+ ### Added
152
+ - Initial release: terminal file manager with MIME-type detection
153
+ - Directory and command history
154
+ - Customizable views, keybindings, and colors
155
+ - Configurable command execution with `%name`, `%dir`, `%file`, `%str` substitutions
156
+ - Built on the Typr terminal UI toolkit
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## Description
6
6
 
7
- Filecon is a terminal-based file manager built on the [Typr](https://codeberg.org/typr/typr) terminal UI toolkit. It packs everything a file manager should do into a keyboard-driven terminal program: every file, command, and directory is reachable in a few keystrokes, and the most common operations take the fewest key presses.
7
+ Filecon is a terminal-based file manager built on the [Typr](https://codeberg.org/typr/typr) terminal UI toolkit. Every file, command, and directory is reachable in a few keystrokes, and the most common operations take the fewest key presses.
8
8
 
9
9
  Filecon is designed to be **intuitive**, **complete**, **powerful**, and the **fastest possible** file manager. The whole interface is built around that principle:
10
10
 
@@ -12,8 +12,6 @@ Filecon is designed to be **intuitive**, **complete**, **powerful**, and the **f
12
12
  - **Live search in popups** — command and history popups open in `/` search mode; type to distill the list, then pick one of the few remaining results with a number.
13
13
  - **Frequency-ordered history** — directory and command history are ranked by use count, so the most likely choice is always near the top.
14
14
 
15
- Filecon's long-term goal is to become an **interactive shell**: a separate companion program will handle command building, and combined with Filecon the two will form a full interactive shell that competes with interactive CLI shells.
16
-
17
15
  Filecon runs on Typr, whose self-contained terminal layer ships its own precompiled terminfo key table and MIME-type database in the gem — no ncurses or external tooling required — so Filecon works on any raw, ANSI-capable terminal (including Termux).
18
16
 
19
17
  ## Features
@@ -21,8 +19,8 @@ Filecon runs on Typr, whose self-contained terminal layer ships its own precompi
21
19
  - **Directory browsing with MIME-type detection** — files are typed from a packaged MIME database and colored accordingly, with optional `file --mime-type` magic detection for unknowns
22
20
  - **Fastest possible navigation** — hint numbers (1–9, 0) open rows directly; TAB pages hints beyond the first ten
23
21
  - **Default `[/]:search` in popups** — command and history popups open in search mode: start typing to distill the list down to the matches, then press a number hint to pick from the few remaining results. The listing itself searches the same way
24
- - **Mouse support** — click a row to open it with its default action; the mouse wheel scrolls the listing (pointer-only mode is planned)
25
- - **Command execution modes** — commands run in one of four ways, chosen by prefix: `|` status line, `=` text viewer, `+` new terminal window, `!` Ruby eval; per-MIME-type command sets with `%name`, `%dir`, `%file`, `%str` substitution
22
+ - **Mouse support** — click a row to open it with its default action, right-click to open its command list, click a binding on the bottom bar or a filter/sort/views column to run that action; the mouse wheel scrolls the listing (pointer-only mode is planned)
23
+ - **Command execution modes** — commands run in one of four ways, chosen by prefix: `|` status line, `=` text viewer, `+` new terminal window, `!` Ruby eval; per-MIME-type command sets with `%name`, `%dir`, `%file`, `%cmd`, `%str` substitution
26
24
  - **Customizable column views** — view columns run shell commands with `%name` substitution (content preview, char/word/line counts, type info, disk usage, man-page descriptions)
27
25
  - **Directory and command history with a frequency counter** — entries stored as `[count, entry]` pairs, most-used first then most-recent; `frequency_limit` controls how many frequent entries lead the list
28
26
  - **Shell history import** — on first run, `~/.bash_history` and `~/.zsh_history` are validated, alias-resolved, and merged into the command history; directory paths referenced by those commands are collected into the directory history
@@ -46,11 +44,10 @@ filecon [directory]
46
44
  **Navigation**
47
45
  - Arrow keys / Page Up/Down / Home / End — Navigate files
48
46
  - Backspace — Go to parent directory
49
- - Number keys — Open file with default command
47
+ - Number keys — Open file with default command; Alt+number — Open command list for the file
50
48
  - Tab — Cycle hint numbers for rows beyond the first 10
51
49
 
52
50
  **Actions**
53
- - `o` — Open list of all commands for selected file
54
51
  - `c` — Command history
55
52
  - `x` — List all executables from $PATH directories
56
53
  - `h` — Help
@@ -66,6 +63,7 @@ filecon [directory]
66
63
  - `M` — Mark range
67
64
  - `a` — Mark all
68
65
  - `i` — Invert mark
66
+ - `Enter` — Open the command list for marked files
69
67
 
70
68
  **Directory**
71
69
  - `~` — Go to home directory
@@ -73,12 +71,12 @@ filecon [directory]
73
71
  - `d` — Directory history
74
72
 
75
73
  **Other**
76
- - Enter — Select / confirm
74
+ - Enter — Select / confirm (also: open the command list for marked files)
77
75
  - Esc — Quit
78
76
 
79
77
  ### Mouse
80
78
 
81
- Click a row to open it with its default action; the mouse wheel scrolls the listing. Mouse reporting is enabled automatically when Filecon starts.
79
+ Click a row to open it with its default action, right-click to open its command list; click a binding label on the bottom bar or a filter/sort/views column on the head to run that action; the mouse wheel scrolls the listing. Clicking outside a popup dismisses it. `h` shows a two-pane help with the keybinding grid (from the line's `list`) beside the help text — clicking a binding row returns its trigger key, and clicking outside both panes closes it. Mouse reporting is enabled automatically when Filecon starts.
82
80
 
83
81
  ### Popups and search
84
82
 
@@ -102,15 +100,10 @@ State and history files live under `~/.local/share/filecon/`.
102
100
  - `=` — display output in the text viewer
103
101
  - `!` — evaluate as Ruby code
104
102
 
105
- **Substitution variables:** `%name` (file path), `%dir` (directory picker), `%file` (file picker), `%str` (text input)
103
+ **Substitution variables:** `%name` (file path), `%dir` (directory picker), `%file` (file picker), `%cmd` (PATH executable picker), `%str` (text input)
106
104
 
107
105
  See `filecon(1)` for the full manual.
108
106
 
109
- ## Roadmap
110
-
111
- - **Pointer-only mode** — full mouse-driven operation of the entire interface, building on the click/wheel mouse support already present
112
- - **Interactive shell** — a separate command-building program that combines with Filecon into an interactive shell competing with interactive CLI shells
113
-
114
107
  ## Development
115
108
 
116
109
  ```bash
data/bin/filecon CHANGED
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
3
  require "fileutils"
4
+ require "tmpdir"
4
5
  require "yaml"
5
6
  require_relative "../lib/filecon"
6
7
 
8
+ log = nil
7
9
  begin
8
10
  config_dir = File.expand_path("~/.config/filecon")
9
11
  config_path = File.join(config_dir, "config.yaml")
@@ -34,7 +36,7 @@ begin
34
36
  cmd_history: parse[ch_path],
35
37
  path: { config: config_path, state: state_dir, dir_history: dh_path,
36
38
  cmd_history: ch_path },
37
- directory: ARGV.first || ?., keymap: { open_file: ?o, cmd_history: ?c, dir_history: ?d, recurse: ?R, open_current: ?` },
39
+ directory: ARGV.first || ?., keymap: { cmd_history: ?c, dir_history: ?d, recurse: ?R, open_current: ?` },
38
40
  colors: config["colors"] || {},
39
41
  views: config["views"] || {},
40
42
  commands: config["commands"] || {},
@@ -46,9 +48,12 @@ begin
46
48
  app.import_shell_history
47
49
  app.fly
48
50
  rescue => e
51
+ log = File.join(Dir.tmpdir, "filecon_error.log")
49
52
  $stderr.puts "#{e.class}: #{e.message}\n#{e.backtrace.first(10).join("\n")}"
50
- File.write File.join(Dir.tmpdir, "filecon_error.log"),
51
- "#{e.class}: #{e.message}\n#{e.backtrace.first(10).join("\n")}"
53
+ File.write log, "#{e.class}: #{e.message}\n#{e.backtrace.first(10).join("\n")}"
52
54
  ensure
53
55
  Typr.exit
56
+ if log
57
+ $stderr.puts "filecon crashed: details in #{log}"
58
+ end
54
59
  end
data/config.yaml CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  # Suffix appended to term for opening files in a new window.
19
19
  # Example: if term is "foot", term_hold becomes "foot --hold"
20
- term_hold: " --hold"
20
+ # term_hold: " --hold"
21
21
 
22
22
  # Maximum number of history entries to retain.
23
23
  # Older entries beyond this limit are automatically pruned.
@@ -113,6 +113,8 @@ colors:
113
113
  #
114
114
  # %file - Opens file picker, inserts chosen path
115
115
  #
116
+ # %cmd - Lists the PATH, picks an executable, inserts its path
117
+ #
116
118
  # %str - Prompts for text input, inserts the string
117
119
  #
118
120
  # SPECIAL CATEGORIES:
@@ -127,12 +129,14 @@ commands:
127
129
  default:
128
130
  # copy: "|rsync --progress %name %dir"
129
131
  info: "=file %name"
132
+ view: "+moor %name"
133
+ edit: "+micro %name"
130
134
  copy: "|cp -R %name %dir"
131
135
  move: "|mv %name %dir"
132
136
  rename: "|mv %name %str"
133
137
  delete: "|rm -rf %name"
134
- open_with: "|%file %name %str"
135
- open_with_terminal: "+%file %name %str"
138
+ open_with: "|%cmd %name %str"
139
+ open_with_terminal: "+%cmd %name %str"
136
140
  xdg_open: "|xdg-open %name"
137
141
 
138
142
  executable:
@@ -151,8 +155,6 @@ commands:
151
155
  play: "+mpv %name"
152
156
 
153
157
  text:
154
- view: "+moor %name"
155
- edit: "+micro %name"
156
158
  count: "|wc -l %name | cut -wf1"
157
159
 
158
160
  troff:
@@ -208,9 +210,9 @@ commands:
208
210
  "x-c++":
209
211
  run: "+g++ %name && ./a.out"
210
212
 
211
- markdown:
212
- view: "+glow %name"
213
- edit: "+micro %name"
213
+ # markdown:
214
+ # view: "+glow %name"
215
+ # edit: "+micro %name"
214
216
 
215
217
  json:
216
218
  view: "=cat %name | jq ."
@@ -270,7 +272,7 @@ commands:
270
272
 
271
273
  views:
272
274
  content:
273
- content: "head -n 50 %name"
275
+ content: "cat %name | strings | head -n 50 "
274
276
 
275
277
  types:
276
278
  type: true
@@ -278,7 +280,7 @@ views:
278
280
  description: "file -b %name"
279
281
 
280
282
  disk_use:
281
- du: "du -hs %name | cut -wf1"
283
+ du: "du -s %name | cut -wf1"
282
284
 
283
285
  count:
284
286
  chars: "wc -m %name | cut -wf1"
data/filecon.1 CHANGED
@@ -27,7 +27,7 @@ Navigate files
27
27
  Go to parent directory
28
28
  .TP
29
29
  .B Number keys
30
- Open file with default command
30
+ Open file with default command; Alt+number opens the command list for the file
31
31
  .TP
32
32
  .B Tab
33
33
  Cycle hint numbers for rows beyond the first 10
@@ -66,15 +66,13 @@ Open commands for current directory
66
66
  Navigate to home directory
67
67
  .TP
68
68
  .B Return
69
- Confirm: cd into directories or open files
69
+ Confirm: cd into directories or open files; with marked files,
70
+ open the command list for them
70
71
  .TP
71
72
  .B Escape
72
73
  Quit
73
74
  .SH FILECON KEYS
74
75
  .TP
75
- .B o
76
- Open list of all commands for selected file
77
- .TP
78
76
  .B c
79
77
  Browse command history
80
78
  .PP
@@ -122,6 +120,9 @@ Prompt to pick a directory
122
120
  .B %file
123
121
  Prompt to pick a file
124
122
  .TP
123
+ .B %cmd
124
+ Prompt to pick a PATH executable
125
+ .TP
125
126
  .B %str
126
127
  Prompt for a text string (line input)
127
128
  .SH VIEWS
@@ -1,3 +1,3 @@
1
1
  module Filecon
2
- VERSION = "0.2.1"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/filecon.rb CHANGED
@@ -10,7 +10,9 @@ class Filecon::App < Typr::Browser
10
10
  include Typr
11
11
 
12
12
  def cd dir, append=false
13
+ @selected[:rows] = [] unless append
13
14
  if dir.is_a? Array
15
+ @show_path = true
14
16
  dirs = dir.filter_map { |d| d = File.expand_path(d.to_s); Dir.exist?(d) ? d : nil }
15
17
  dirs.each_with_index { |d, i|
16
18
  @directory = d if i.positive?
@@ -18,8 +20,10 @@ class Filecon::App < Typr::Browser
18
20
  super d, i.positive?
19
21
  }
20
22
  @directory = dirs
23
+ change_view @view if dirs.any?
21
24
  nil
22
25
  else
26
+ @show_path = false unless append
23
27
  dir = @dir_history[dir+1][1] if dir.is_a? Integer
24
28
  record :dir, File.expand_path(dir) unless append || [?/, ENV["HOME"]].include?(dir)
25
29
  super dir, append
@@ -27,10 +31,18 @@ class Filecon::App < Typr::Browser
27
31
  end
28
32
  end
29
33
 
30
- def execute; cd ENV["PATH"].split(":"); add_path_column end
34
+ def execute; cd ENV["PATH"].split(":") end
31
35
 
32
- #switch_view
33
- def switch_to id=nil
36
+ def pick type='file', **kw
37
+ return super unless type == :cmd
38
+ dir = @directory
39
+ execute
40
+ result = super
41
+ cd dir
42
+ result
43
+ end
44
+
45
+ def change_view id=nil
34
46
  return unless id
35
47
  id = @views.keys[id] if id.is_a? Integer
36
48
  view = @views[id]
@@ -45,12 +57,18 @@ class Filecon::App < Typr::Browser
45
57
  cmd = built[0] == ?| ? built : ?| + built
46
58
  open cmd + " 2>&1"
47
59
  end
48
- io.read(width).to_s.scrub.gsub(/[\n\t]/, ?|)
60
+ cell = io.read(width).to_s.scrub.strip
61
+ value = coerce_type cell
62
+ value.is_a?(String) || cell.empty? ? cell.gsub(/[\n\t]/, ?|) : value
49
63
  }.compact }
50
64
  @sequence = [ NAME ] + view.keys.map{ |head|
51
65
  @header.index(head.to_s) || (@header += [head.to_s]; @header.count-1) }
52
66
  clear
53
67
  self << data
68
+ if @show_path
69
+ @data.each { |row| row[PATH] = File.dirname(row[NAME]) } if @data.any?
70
+ @sequence.insert(@sequence.index(NAME) || 0, PATH) unless @sequence.include?(PATH)
71
+ end
54
72
  @current = id
55
73
  @view = id
56
74
  end
@@ -69,21 +87,30 @@ class Filecon::App < Typr::Browser
69
87
  end
70
88
 
71
89
  commands = @commands.values_at( *types ).map(&:to_a).flatten( 1 ).uniq
90
+ alt = false
72
91
  pair = if default; commands.first
73
92
  else move left+1,top+1
74
93
  if id.is_a?(Array); draw "#{id.count.to_s} files: (#{
75
94
  types.join ?, })".ljust width
76
95
  else print id end
77
96
  pop = id ? top+2 : top
78
- return unless cmd = Grid.new( left:left+1, top: pop, right: -1,
97
+ picker = Grid.new( left:left+1, top: pop, right: -1,
79
98
  bottom: [ pop + commands.count - 1, Typr.height ].min,
80
99
  input:commands, border: :light, format: [:min, :max],
81
- borders: { top: nil }, alternate: false, hints: "1234567890",
82
- colors: {columns: @colors[:commands] || [:green, :red] } ).pick( :row, column: 0 )
100
+ borders: { top: nil }, alternate: false,
101
+ colors: {columns: @colors[:commands] || [:green, :red] } )
102
+ return unless cmd = picker.pick( :row, column: 0 )
103
+ alt = picker.modifier == :alt
83
104
  commands[ cmd ]
84
105
  end
106
+ dir = @directory
85
107
  cmd = build pair.last, id, pair.first
86
108
  return unless cmd
109
+ if alt
110
+ edited = Typr.read_line "> ", left: left, top: @user.top, initial: cmd, completions: true, redraw: method(:show)
111
+ return unless edited
112
+ cmd = edited
113
+ end
87
114
  if cmd[0] == ?|
88
115
  display = cmd[1..-1]
89
116
  record :cmd, cmd
@@ -92,12 +119,39 @@ class Filecon::App < Typr::Browser
92
119
  nc, ac = @colors[:commands] || [:green, :red]
93
120
  @user.show "> #{color_code(nc)}#{pair.first}#{color_code(ac)}:#{display}" \
94
121
  "#{color_code(@colors[:default])} : #{output.lines.last}"
122
+ show
95
123
  Typr.read_key
96
124
  else
97
125
  record :cmd, cmd unless cmd[0..2] == '!cd'
98
126
  run( cmd )
99
- show
100
127
  end
128
+ Array(id).sort.reverse.each { |i| refresh_row i } if id && cmd[0..2] != '!cd' && @directory == dir
129
+ @selected[:rows] = [] if id && @directory != dir
130
+ show #unless cmd[0] == ?|
131
+ end
132
+
133
+ def refresh_row id
134
+ return unless row = @data[id]
135
+ name = row[NAME]
136
+ return delete_row id unless File.symlink?(name) || File.exist?(name)
137
+ updated = scan_file name
138
+ updated[TYPE, 2] = row[TYPE, 2]
139
+ @data[id] = updated
140
+ process id
141
+ end
142
+
143
+ def delete_row id
144
+ @data.delete_at id
145
+ @layer.delete_at id if @layer
146
+ if @map
147
+ @map.delete id
148
+ @map.map!{ |i| i > id ? i - 1 : i }
149
+ end
150
+ @colors[:fields] = @colors[:fields].map { |(row,col),color|
151
+ row == id ? nil : row > id ? [[row - 1, col], color] : [[row, col], color] }.compact.to_h
152
+ @start = rows - height if @start > rows - height
153
+ @start = 0 if @start < 0
154
+ @selected[:rows] = []
101
155
  end
102
156
 
103
157
  def build str, id, name=nil
@@ -105,21 +159,18 @@ class Filecon::App < Typr::Browser
105
159
  reset = color_code( @colors[:default] )
106
160
  prefix = name ? "#{color_code(nc)}#{name}#{color_code(ac)}:" : ""
107
161
  cmd = ""
108
- while str[/\%(name|dir|file|str)/]
162
+ while str[/\%(name|cmd|dir|file|str)/]
109
163
  cmd += $`
110
164
  case $&
111
165
  when '%name'; cmd += ?"+( id.nil? ? @directory :
112
166
  id.is_a?( Array ) ? id.map{ |id|
113
167
  @data[id][NAME] }.join('" "') :
114
168
  @data[id][NAME] )+?"
115
- when '%dir'
169
+ when '%dir', '%file', '%cmd'
116
170
  @user.show "> #{prefix}#{cmd}#{$&}#{$'}#{reset}"
117
- return unless dir = pick(:directory)
118
- cmd += ?" + dir + ?"
119
- when '%file'
120
- @user.show "> #{prefix}#{cmd}#{$&}#{$'}#{reset}"
121
- return unless file = pick(:file)
122
- cmd += ?" + file + ?"
171
+ result = pick( { '%dir' => :directory, '%file' => :file, '%cmd' => :cmd }[$&] )
172
+ return unless result
173
+ cmd += ?" + result + ?"
123
174
  when '%str'
124
175
  prompt = cmd.empty? || cmd.end_with?(' ') ? cmd : "#{cmd} "
125
176
  query = Typr.read_line "> #{prefix}#{prompt}", left: left, top: @user.top
@@ -171,12 +222,19 @@ class Filecon::App < Typr::Browser
171
222
  fields[[i,1]] = [fc[1], :grey20]
172
223
  }
173
224
 
174
- n = popup(input: pairs, hints:"1234567890", format:[:min,:max],
225
+ picker = popup(input: pairs, format:[:min,:max],
175
226
  alternate: true, left: left, bottom: -2,
176
- colors: { columns: fc, fields: fields }).pick(:row, column: :all)
227
+ colors: { columns: fc, fields: fields })
228
+ n = picker.pick(:row, column: :all)
177
229
  return unless n
178
230
 
179
231
  entry = ordered[n][1]
232
+ if is_cmd and picker.modifier == :alt
233
+ edited = Typr.read_line "> ", left: left, top: @user.top, initial: entry,
234
+ completions: true, redraw: -> { picker.show }
235
+ return unless edited
236
+ entry = edited
237
+ end
180
238
  record type, entry
181
239
 
182
240
  if is_cmd
@@ -256,36 +314,29 @@ class Filecon::App < Typr::Browser
256
314
  def recurse
257
315
  return if @directory.is_a?(Array)
258
316
  root = File.expand_path(@directory)
259
- clear
260
- cd root
261
- Dir.glob("**/*", File::FNM_DOTMATCH, base: root).sort.each { |p|
317
+ dirs = [root] + Dir.glob("**/*", File::FNM_DOTMATCH, base: root).sort.filter_map { |p|
262
318
  next if !@show_hidden && p.split(?/).any? { |c| c.start_with?(?.) }
263
- next unless File.directory?(File.join(root, p))
264
319
  d = File.expand_path(p, root)
265
- @directory = d
266
- Dir.chdir d
267
- cd d, true
320
+ d if File.directory?(d)
268
321
  }
269
- Dir.chdir root
270
- @directory = root
271
- add_path_column
322
+ cd dirs
272
323
  @current = 0
273
324
  nil
274
325
  end
275
326
 
276
- def add_path_column
277
- Typr.const_set :PATH, @header.size unless Typr.const_defined?(:PATH)
278
- @header << "path" unless @header.include?("path")
279
- @data.each { |row| row[PATH] = File.dirname(row[NAME]) }
280
- @sequence.insert(@sequence.index(NAME) || 0, PATH) unless @sequence.include?(PATH)
281
- # @align[@header.size-1] = :right
282
- end
327
+
283
328
 
284
329
  def help
285
- info = "\nFILECON - a terminal file manager\n\n" \
286
- "HINTS: press a number (1-9,0) to open that file with its default action.\n" \
287
- " TAB cycles pages of hints.\n\n" \
288
- "COMMANDS:\n" \
330
+ grid = @user.list
331
+ y = color_code :yellow
332
+ d = color_code @colors[:default]
333
+ text = Text.new( input: "\n#{y}FILECON#{d} - a terminal file manager\n\n" \
334
+ "#{y}HINTS:#{d}\n press a number (1-9,0) to open that file with its default action.\n" \
335
+ " Alt+number opens the command list for that file instead.\n" \
336
+ " Right-click a file to open its command list.\n" \
337
+ " Mark files with m, then Return opens their command list.\n" \
338
+ " TAB cycles pages of hints.\n\n" \
339
+ "#{y}COMMANDS:#{d}\n" \
289
340
  " e[x]ecute browse all executables on your PATH\n" \
290
341
  " [R]ecurse expand the current directory tree (subdirectories listed inline)\n" \
291
342
  " [d]irectories jump to a recently visited directory\n" \
@@ -294,11 +345,32 @@ class Filecon::App < Typr::Browser
294
345
  " entries first, then the rest in most-recent order. The frequency_limit\n" \
295
346
  " setting in config.yaml (default 10) controls how many frequent entries\n" \
296
347
  " lead the list.\n\n" \
297
- "CONFIG: ~/.config/filecon/config.yaml\n\n" \
298
- "KEYBINDINGS:\n\n" + @user.bindings.join(?\n)
299
- Text.new( input: info, top:2, left:3, right: -3, bottom: -3, header: "HELP",
300
- colors: { header: [:black,:white], border:[:white,:grey10] },
301
- border: :light ).pick :none
348
+ "#{y}CONFIG:#{d}\n ~/.config/filecon/config.yaml\n",
349
+ top: 2, right: -2, bottom: -3,
350
+ colors: { border:[:white,:grey10] },
351
+ border: :light )
352
+ text.left = -> { grid.right + 2 }
353
+ loop do
354
+ grid.show
355
+ text.show
356
+ key = Typr.read_key
357
+ case key
358
+ when @keymap[:exit], @keymap[:confirm]; return
359
+ when Typr::Mouse
360
+ if key.press? and key.left?
361
+ if row = grid.data_at( key.x, key.y )
362
+ return extract_key( @user.bindings[ row ] )
363
+ end
364
+ return if grid.send( key ) == :outside and text.send( key ) == :outside
365
+ else
366
+ grid.send key
367
+ text.send key
368
+ end
369
+ else
370
+ grid.send key
371
+ text.send key
372
+ end
373
+ end
302
374
  end
303
375
 
304
376
  def initialize args={}
@@ -311,7 +383,6 @@ class Filecon::App < Typr::Browser
311
383
  @keymap[:open_current] = ?`
312
384
  @keymap[:execute] = ?x
313
385
  @keymap[:recurse] = ?R
314
- @user.bindings.insert 1, "[o]pen"
315
386
  @user.bindings.insert 7, "[R]ecurse"
316
387
  @user.bindings.insert 8, "[c]ommands"
317
388
  @user.bindings.insert 9, "e[x]ecute"
@@ -319,8 +390,20 @@ class Filecon::App < Typr::Browser
319
390
  @user.reset
320
391
  end
321
392
 
393
+ def confirm
394
+ return unless @selected[:rows].any?
395
+ open_file @selected[:rows], false
396
+ end
397
+
322
398
  def open_current; open_file nil, false, ["directory", "inode"] - [??] + ["default"] end
323
399
 
400
+ def open_path path
401
+ dir, @directory = @directory, path
402
+ open_file nil, false, ["directory", "inode"] - [??] + ["default"]
403
+ ensure
404
+ @directory = dir
405
+ end
406
+
324
407
  def fly
325
408
  loop do
326
409
  show
@@ -330,16 +413,19 @@ class Filecon::App < Typr::Browser
330
413
  case key
331
414
  when KEY_ESCAPE; exit
332
415
  when Typr::Mouse
333
- if key.wheel? and key.press?
334
- send( key.wheel_up? ? @keymap[:up] : @keymap[:down] )
335
- elsif key.press? and key.left?
336
- id = hit( key.y, key.x )
337
- open_file( id, true ) if id.is_a? Integer
416
+ if key.press? and ( key.left? or key.right? ) and target = @head.path_at( key.x, key.y )
417
+ key.right? ? open_path( target ) : cd( target )
418
+ else
419
+ value = send( key )
420
+ open_file( value, !key.right? ) if value.is_a?( Integer )
338
421
  end
339
422
  @user.reset
340
423
  else
424
+ alt = key.is_a?(String) && key[/\A\e(\d)\z/]
425
+ key = key[1] if alt
341
426
  id = send( key )
342
- open_file( id, true) if id.is_a? Integer
427
+ id = send( id ) while id.is_a? String
428
+ open_file( id, !alt ) if id.is_a? Integer
343
429
  @user.reset
344
430
  end
345
431
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: filecon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kilian Reitmayr
@@ -74,6 +74,7 @@ executables:
74
74
  extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
+ - CHANGELOG.md
77
78
  - LICENSE
78
79
  - README.md
79
80
  - bin/filecon