typr 1.6.1 → 1.8.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 +4 -4
- data/CHANGELOG.md +26 -1
- data/README.md +5 -6
- data/example/browser +20 -16
- data/example/grid +3 -2
- data/example/text +1 -3
- data/lib/browser.rb +28 -11
- data/lib/grid.rb +2 -2
- data/lib/line.rb +4 -6
- data/lib/space.rb +5 -2
- data/lib/stack.rb +1 -1
- data/lib/terminal.rb +39 -11
- data/lib/text.rb +9 -19
- data/typr.gemspec +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 02fb4a45192b9a6bc2b3a751033a71e058b2abcb33b77d8873c991a2d6031dd1
|
|
4
|
+
data.tar.gz: 878f286e0c40eb45b0b5c5216633bc0a9ff82c7f6bc03d18531a80090936e2af
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 921906f7edae965d3ea48ed62e8eae91a770ad63dcf7cc3f20bb8463c9bbef280bcca2d2577317e4dd6c71d48b3740630bfaedf2d8c617cc9878d01381eebb9e
|
|
7
|
+
data.tar.gz: d0db793255d8969a1181193dee6b06227efc2c0248fff99ca6407f6d5944ece37dc330e8cdabafa26229e98a299a576ebc245ea4e4ebd5a25661b20526d094cc
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
## [vx.y.z] — __DATE__
|
|
4
4
|
|
|
5
|
+
## [v1.8.0] — 2026-10-11
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- Sort indicator uses solid triangles (▲▼) instead of thin arrows, for a heavier mark at the same single-cell width
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Text paints new spans once at entry and rebuilds only reflow: scheme and content colors survive resizes and appends without stacking or stripping
|
|
12
|
+
- Terminal self-test renders without scrolling and covers all named colors — status line uses raw output on the last row (`p` scrolled with inspect quotes and a newline), rows are capped to the visible area, the 256-ramp starts at a known position, the color list includes all `COLOR_MAP` names plus the full grey ramp, and the fg/bg steppers clamp to valid ids
|
|
13
|
+
- String headers fit inside minimal-width boxes — the box ignored the margin in the header width, so the border overwrote the last header character; single `:max` columns provably span the widget width
|
|
14
|
+
- Word jumps are documented as alt-arrow, not ctrl-arrow — README, the `read_line` doc comment, and the v1.2.0 entry called them ctrl-arrow while the bindings are `KEY_ALT_LEFT`/`KEY_ALT_RIGHT` (SGR modifier 3 = alt); the package's key modifier is alt everywhere
|
|
15
|
+
- Command-line redraw overwrites in place — `read_line` cleared the row on every keypress, flashing on cursor moves; each frame now pads to end-of-line with spaces instead, so shrinking content leaves no ghosts either
|
|
16
|
+
- Cursor stays hidden while the command line redraws — every keypress parked the visible cursor at the prompt between the home move and the edit-position move; each frame now hides it first and shows it at the edit position
|
|
17
|
+
- Mode table corrected to standard SGR order (`dim` restored, so `slow→5` and `invert→7`) with `:steady` at position 25 for blink-off; `mode_code` stays index-by-position
|
|
18
|
+
|
|
19
|
+
## [v1.7.0] — 2026-10-02
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- Column hints in `pick :column` sit in the gap cell left of each column — `draw_hints` positioned them from the widget edge while rows render past the margin, so every hint landed one margin-width left of its column; overlaying the column starts overshot right instead
|
|
23
|
+
- Default bottom-bar commands work in a bare browser — the key labels were display-only, so `h / f s v r m M a i ~ H` did nothing; the default bindings now populate the keymap like explicit ones, and the pick loop discards command results instead of misreading them as picks
|
|
24
|
+
- `[d]irectories` left the default bar — directory history is a filecon feature; typr's unrelated `Browser#directories` row query is unchanged
|
|
25
|
+
- Explicit nil widget boundaries fall back to safe defaults — the unified clamp referenced the accessor itself, recursing until `SystemStackError` (and the bottom default assumed subclass-only methods); per-name fallbacks are restored inside the clamp
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- Regex color schemes are named and opt-in — `regex:` accepts a Hash as before or a Symbol that `Typr.paint` resolves through `REGEX_SCHEMES` (`:generic` paints numbers, colon keys, quotes, brackets, and punctuation; `:shell` paints a command line's first word and `--options`); unknown symbols raise `ArgumentError`. `Text` and `Line` no longer install a default scheme, so `regex:` is empty by default and help pages, error popups, and unconfigured views render unpainted
|
|
29
|
+
|
|
5
30
|
## [v1.6.1] — 2026-10-02
|
|
6
31
|
|
|
7
32
|
### Fixed
|
|
@@ -89,7 +114,7 @@
|
|
|
89
114
|
## [v1.2.0] — 2026-08-02
|
|
90
115
|
|
|
91
116
|
### Added
|
|
92
|
-
- `Typr.read_key` / `Typr.read_line` — the public terminal input API: `read_key` reads a raw key; `read_line` is the shared interactive line editor that renders a prompt + query at any position with arrow keys,
|
|
117
|
+
- `Typr.read_key` / `Typr.read_line` — the public terminal input API: `read_key` reads a raw key; `read_line` is the shared interactive line editor that renders a prompt + query at any position with arrow keys, alt-arrow word jumps, Home/End/Delete/Backspace, Escape aborts (`nil`), Enter confirms, and an optional block runs after each keypress
|
|
93
118
|
- `Line#ask` strict question types — `Array` (pick from an object/column), `:key`, `:line`; the question and answer are rendered in `@colors[:question]`/`@colors[:answer]`
|
|
94
119
|
- `Line#align` attribute (`:left`/`:right`) for text alignment in draw
|
|
95
120
|
- `dark_*` named colors (`dark_red`, `dark_green`, `dark_yellow`, `dark_blue`, `dark_magenta`, `dark_cyan`) and a `COLOR_MAP` of popular named colors in `color_code`
|
data/README.md
CHANGED
|
@@ -31,9 +31,9 @@ The default terminal frontend is pure ANSI on raw TTY input — **no ncurses**.
|
|
|
31
31
|
|
|
32
32
|
### Colors
|
|
33
33
|
|
|
34
|
-
Named colors (basic, shades, and `dark_*`), grey shades `grey0`–`grey100`, the terminal's 8-bit palette, and RGB triplets for true-color terminals. Every component takes a per-part `colors:` hash (columns, headers, hints, search matches, …). A component-level `regex:` key maps `Regexp => color` to tint typed input (`Typr.read_line`/`Line#cli`) and the rows of `Typr::Text` areas
|
|
34
|
+
Named colors (basic, shades, and `dark_*`), grey shades `grey0`–`grey100`, the terminal's 8-bit palette, and RGB triplets for true-color terminals. Every component takes a per-part `colors:` hash (columns, headers, hints, search matches, …). A component-level `regex:` key maps `Regexp => color` to tint typed input (`Typr.read_line`/`Line#cli`) and the rows of `Typr::Text` areas, or names a scheme — `:generic` or `:shell`; when two patterns match the same span the earlier key wins, so order overlapping patterns longest-first. Colors may be any value `color_code` accepts — named color, `greyN`, an 8-bit index, an RGB triplet like `[10, 200, 30]`, or a `[fg, bg]` pair. Header bars render bold by default.
|
|
35
35
|
|
|
36
|
-
`Typr::Text` paints numbers cyan, colon-separated keys yellow, quoted strings green, bracketed groups magenta, and remaining punctuation grey — override with
|
|
36
|
+
Pass `regex: :generic` and `Typr::Text` paints numbers cyan, colon-separated keys yellow, quoted strings green, bracketed groups magenta, and remaining punctuation grey — override with a custom map, disable with `regex: {}` (the default: unpainted). Each painted span restores only the `default` foreground, so the row's own background (alternating stripes, search highlight, padding) survives; `Line#cli` colors the typed command with the `:shell` scheme (first word red, `--options` blue) when configured; it no longer tints the completion grid. Set `regex: {}` to turn a scheme off.
|
|
37
37
|
|
|
38
38
|
### Frontends
|
|
39
39
|
|
|
@@ -114,8 +114,7 @@ Typr.init
|
|
|
114
114
|
|
|
115
115
|
@browser = Typr::Browser.new(
|
|
116
116
|
directory: "/tmp",
|
|
117
|
-
right: -1, bottom: -1
|
|
118
|
-
colors: { hints: [:white, :black] }
|
|
117
|
+
right: -1, bottom: -1
|
|
119
118
|
)
|
|
120
119
|
|
|
121
120
|
selected = @browser.pick # interactive file picker, returns the absolute path
|
|
@@ -186,7 +185,7 @@ Clears the full screen or a single line (`:line`).
|
|
|
186
185
|
Reads a single keypress in raw mode. Returns the key as a String (`KEY_ESCAPE`, `?q`, arrow escape sequences), a `Typr::Mouse` struct for mouse reports, or `nil` when input is unavailable.
|
|
187
186
|
|
|
188
187
|
### `Typr.read_line(prompt, ...)`
|
|
189
|
-
The shared interactive line editor. Renders a prompt and query at any position with arrow keys,
|
|
188
|
+
The shared interactive line editor. Renders a prompt and query at any position with arrow keys, alt-arrow word jumps, Home/End/Delete/Backspace; Enter confirms, Escape aborts (`nil`). An optional block runs after each keypress and may short-circuit with a result. Pass `completions:` — a callable `->(query, cursor) { candidates }` — and TAB matches the current word against `candidates` in a pick grid: a candidate is either a string or `[text, color-pair]` to tint its pick entry. Pass `regex:` — a Hash of `Regexp => color`, or a scheme name (`:generic`, `:shell`) — and matching regions of the query are colored as they are typed, restoring the prompt's trailing color code. The editor is agnostic to where candidates come from.
|
|
190
189
|
|
|
191
190
|
```ruby
|
|
192
191
|
Typr.read_line "/", completions: ->(query, cursor) {
|
|
@@ -203,7 +202,7 @@ Strict question types:
|
|
|
203
202
|
The question and answer are rendered in `@colors[:question]`/`@colors[:answer]`.
|
|
204
203
|
|
|
205
204
|
### `Line#cli`
|
|
206
|
-
Prompts for a single command line with TAB completion matched against the shell (bash commands, options, and files). `completions: true` switches on the content; `initial:` seeds the line; `redraw:` repaints while the completion grid is up.
|
|
205
|
+
Prompts for a single command line with TAB completion matched against the shell (bash commands, options, and files). `completions: true` switches on the content; `initial:` seeds the line; `redraw:` repaints while the completion grid is up. With `regex: :shell` the typed command tints red for the first word, blue for `--options`. Completion candidates are plain strings.
|
|
207
206
|
|
|
208
207
|
```ruby
|
|
209
208
|
line.cli "> ", initial: "cp %name %dir", completions: true, redraw: -> { show }
|
data/example/browser
CHANGED
|
@@ -4,35 +4,40 @@
|
|
|
4
4
|
# Usage:
|
|
5
5
|
# cd example && ruby browser
|
|
6
6
|
# ruby browser /path/to/dir Start in a specific directory
|
|
7
|
-
# ruby browser --select Start in pick mode (returns path)
|
|
8
7
|
#
|
|
9
8
|
# Navigates directories and displays files with size and type.
|
|
10
9
|
# Pass a directory path as an argument to start there.
|
|
11
|
-
#
|
|
10
|
+
# The picked file path is printed to stdout on exit.
|
|
12
11
|
#
|
|
13
12
|
# Keys:
|
|
14
|
-
# arrows
|
|
15
|
-
#
|
|
16
|
-
# /
|
|
17
|
-
#
|
|
13
|
+
# arrows Scroll up/down
|
|
14
|
+
# pgup/pgdn Page up/down
|
|
15
|
+
# home/end Jump to top/bottom
|
|
16
|
+
# tab Cycle hint characters
|
|
17
|
+
# 1-9,0 Jump to the hinted row (directories change into, files pick)
|
|
18
|
+
# enter Open directory or pick file (returns current directory)
|
|
19
|
+
# h Keybinding help
|
|
20
|
+
# / Live-search names
|
|
21
|
+
# f/s/v Filter/sort/views popups
|
|
22
|
+
# r/m/M/a/i Reset, mark, mark range, mark all, invert marks
|
|
23
|
+
# ~/H Home directory, toggle hidden files
|
|
24
|
+
# esc Exit
|
|
18
25
|
#
|
|
19
|
-
#
|
|
20
|
-
# to stdout after the TUI exits (piped to calling process).
|
|
26
|
+
# Click a row to open it; click a bottom-bar label to run that command.
|
|
21
27
|
|
|
22
28
|
require_relative '../lib/typr.rb'
|
|
23
29
|
|
|
24
30
|
class BrowserTest
|
|
25
31
|
include Typr
|
|
26
32
|
def initialize
|
|
27
|
-
Typr.init
|
|
28
|
-
|
|
29
|
-
@browser = Browser.new( right: -1, bottom: -1,
|
|
30
|
-
directory: $conf[false], colors: { hints: [:white, :black] } )
|
|
33
|
+
Typr.init
|
|
34
|
+
dirs = ARGV.reject{|arg| arg.start_with?('--') }
|
|
35
|
+
@browser = Browser.new( right: -1, bottom: -1, directory: dirs.first )
|
|
31
36
|
end
|
|
32
37
|
|
|
33
38
|
def run
|
|
34
39
|
Typr.clear
|
|
35
|
-
$file = @browser.pick
|
|
40
|
+
$file = @browser.pick(:file)
|
|
36
41
|
end
|
|
37
42
|
end
|
|
38
43
|
|
|
@@ -40,6 +45,5 @@ begin
|
|
|
40
45
|
BrowserTest.new.run
|
|
41
46
|
ensure
|
|
42
47
|
Typr.exit
|
|
43
|
-
puts $file
|
|
44
|
-
end
|
|
45
|
-
|
|
48
|
+
puts $file if $file
|
|
49
|
+
end
|
data/example/grid
CHANGED
|
@@ -38,10 +38,11 @@ class GridTest
|
|
|
38
38
|
@user = Line.new( top: -1, default:
|
|
39
39
|
"(s)ort, (f)ilter, (r)eset, select (R)ows/(C)olumns/(F)ields, [esc] quit")
|
|
40
40
|
@head = Line.new( interval: 1,
|
|
41
|
-
default: proc{ draw " %s, sort:%s filter:%s
|
|
41
|
+
default: proc{ draw " %s, sort:%s filter:%s" %
|
|
42
42
|
[ Time.now.strftime("%D %T"),
|
|
43
43
|
(@grid.header[@grid.sorted_by] if @grid.sorted_by),
|
|
44
|
-
@grid.filters.join
|
|
44
|
+
@grid.filters.join ] } )
|
|
45
|
+
@head.reset
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
def run
|
data/example/text
CHANGED
|
@@ -26,9 +26,7 @@
|
|
|
26
26
|
require_relative '../lib/typr.rb'
|
|
27
27
|
|
|
28
28
|
class TextTest
|
|
29
|
-
# extend Typr
|
|
30
29
|
include Typr
|
|
31
|
-
# Typr.frontend :terminal
|
|
32
30
|
def initialize
|
|
33
31
|
Typr.init
|
|
34
32
|
@text = Text.new(
|
|
@@ -47,7 +45,7 @@ class TextTest
|
|
|
47
45
|
loop do
|
|
48
46
|
@text.show
|
|
49
47
|
@user.show
|
|
50
|
-
case key = Typr.
|
|
48
|
+
case key = Typr.read_key
|
|
51
49
|
when KEY_ESCAPE; exit
|
|
52
50
|
when ?/; @text.search
|
|
53
51
|
when ?w; @user.ask( 'pick word': [@text, :field] )
|
data/lib/browser.rb
CHANGED
|
@@ -340,6 +340,7 @@ module Typr
|
|
|
340
340
|
choice = send key
|
|
341
341
|
next if choice == :scroll or choice == :outside
|
|
342
342
|
else
|
|
343
|
+
if @command_keys.include? key then send key; next end
|
|
343
344
|
next unless choice = send( key )
|
|
344
345
|
end
|
|
345
346
|
next unless choice
|
|
@@ -502,7 +503,7 @@ full: %i[ name size permissions executable printable owner group
|
|
|
502
503
|
input: [ [->{Etc.getpwuid(Process.euid).name rescue ENV["USER"] || Etc.getlogin}, ->{@parent.directory},
|
|
503
504
|
->{@parent.filter_display},
|
|
504
505
|
->{ (s = @parent.sorted_by(true)) &&
|
|
505
|
-
"#{s} #{@parent.reverse ? "\
|
|
506
|
+
"#{s} #{@parent.reverse ? "\u25BC" : "\u25B2"}"},
|
|
506
507
|
->{@parent.view} ] ], format: [:min, :max, :min, :min, :min],
|
|
507
508
|
colors: { columns: [@colors[:default], @colors[:default],
|
|
508
509
|
@colors[:actions][:filter], @colors[:actions][:sort],
|
|
@@ -529,6 +530,15 @@ full: %i[ name size permissions executable printable owner group
|
|
|
529
530
|
( pre + ( pre.end_with?( "/" ) ? "" : text[pre.length..-1][/\A[^\/]*/] ) ).sub( %r{(?<=.)/$}, "" )
|
|
530
531
|
end
|
|
531
532
|
|
|
533
|
+
@command_keys = []
|
|
534
|
+
wire = ->(label, cmd) {
|
|
535
|
+
key = extract_key(label)
|
|
536
|
+
next unless key
|
|
537
|
+
method = cmd.is_a?(Symbol) ? cmd :
|
|
538
|
+
(cmd.start_with?('!') ? cmd[1..-1].to_sym : cmd.to_sym)
|
|
539
|
+
@keymap[method] = key
|
|
540
|
+
@command_keys << key if key.size == 1
|
|
541
|
+
}
|
|
532
542
|
if args[:bindings]
|
|
533
543
|
bindings_labels = []
|
|
534
544
|
args[:bindings].each do |label, cmd|
|
|
@@ -536,20 +546,27 @@ full: %i[ name size permissions executable printable owner group
|
|
|
536
546
|
colored = @colors[:actions][name] ?
|
|
537
547
|
color_code( @colors[:actions][name] ) + label + color_code(@colors[:default]) : label
|
|
538
548
|
bindings_labels << colored
|
|
539
|
-
|
|
540
|
-
next unless key
|
|
541
|
-
method = cmd.start_with?('!') ? cmd[1..-1].to_sym : cmd.to_sym
|
|
542
|
-
@keymap[method] = key
|
|
549
|
+
wire.(label, cmd)
|
|
543
550
|
end
|
|
544
551
|
@user ||= Line.new( parent:self, left:->{@parent.left}, top:->{@parent.bottom+1},
|
|
545
552
|
bindings: bindings_labels )
|
|
546
553
|
else
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
554
|
+
cmds = { "[h]elp" => :help, "[/]:search" => :search,
|
|
555
|
+
"[f]ilter" => :filter_view, "[s]ort" => :sort_view, "[v]iews" => :views,
|
|
556
|
+
"[r]eset" => :reset_view, "[m]ark" => :mark,
|
|
557
|
+
"[M]ark_range" => :mark_range, "mark_[a]ll" => :mark_all,
|
|
558
|
+
"[i]nvert_mark" => :mark_invert, "[~]:home" => :home,
|
|
559
|
+
"[H]idden_files" => :toggle_hidden }
|
|
560
|
+
plain = ["[h]elp", "[/]:search"] +
|
|
561
|
+
@colors[:actions].map { |name, _| "[" + name.to_s[0] + "]" + name.to_s[1..] } +
|
|
562
|
+
%w[ [r]eset [m]ark [M]ark_range mark_[a]ll
|
|
563
|
+
[i]nvert_mark [~]:home [H]idden_files [back]:.. [up] [down] [pageup] [pagedown] [home] [end] [esc]:quit [return]:confirm\ ]
|
|
564
|
+
plain.each { |label| wire.(label, cmds[label]) if cmds[label] }
|
|
565
|
+
@user ||= Line.new( parent:self, left:->{@parent.left}, top:->{@parent.bottom+1},
|
|
566
|
+
bindings: plain.map { |label|
|
|
567
|
+
name = label.gsub( /[\[\]]/, '' ).downcase.to_sym
|
|
568
|
+
@colors[:actions][name] ?
|
|
569
|
+
color_code( @colors[:actions][name] ) + label + color_code(@colors[:default]) : label } )
|
|
553
570
|
end
|
|
554
571
|
cd @directory
|
|
555
572
|
end
|
data/lib/grid.rb
CHANGED
|
@@ -411,7 +411,7 @@ module Typr
|
|
|
411
411
|
gaps = @margin.size + @separator.size * (columns - 1)
|
|
412
412
|
sum = @widths.sum
|
|
413
413
|
@max = sum + gaps - 1
|
|
414
|
-
@max = [ @max, @header.length ].max if @header.is_a?(String)
|
|
414
|
+
@max = [ @max, @header.length + @margin.size ].max if @header.is_a?(String)
|
|
415
415
|
space = width - gaps
|
|
416
416
|
if (extra = sum - space ) > 0
|
|
417
417
|
extra.times{ |i| @widths[ @widths.index(@widths.max) ] -= 1 }
|
|
@@ -434,7 +434,7 @@ module Typr
|
|
|
434
434
|
def print row=nil
|
|
435
435
|
return super unless row
|
|
436
436
|
header = ( row == :header )
|
|
437
|
-
return draw @header[0
|
|
437
|
+
return draw @header[0...(width - @margin.size)].ljust(width - @margin.size) if
|
|
438
438
|
header and @header.is_a? String
|
|
439
439
|
return unless row_data = header ? @header : @data[row]
|
|
440
440
|
fields = row_data.dup
|
data/lib/line.rb
CHANGED
|
@@ -153,19 +153,19 @@ class Line < Space
|
|
|
153
153
|
# line's default color, typed input in the answer color. When +completions+
|
|
154
154
|
# is truthy, TAB matches the current word against the shell (commands,
|
|
155
155
|
# options, files) and replaces it through the pick grid. Regions of the
|
|
156
|
-
# typed input matching @colors[:regex] are colored as they are typed
|
|
157
|
-
# first word red
|
|
156
|
+
# typed input matching @colors[:regex] are colored as they are typed
|
|
157
|
+
# (with the :shell scheme: the first word red, double-dash options blue).
|
|
158
158
|
#
|
|
159
159
|
# line.cli "> ", initial: "cp %name"
|
|
160
160
|
# #=> "cp source.txt" (or nil on Escape)
|
|
161
161
|
|
|
162
|
-
def cli prompt, initial: '', redraw: nil, history: nil, prefixes: nil
|
|
162
|
+
def cli prompt, initial: '', redraw: nil, history: nil, prefixes: nil, regex: nil
|
|
163
163
|
re = /\A[#{Regexp.escape(prefixes || '|+=!')}]/
|
|
164
164
|
source = ->(query) {
|
|
165
165
|
bash_candidates(query.sub(re, '')).split("\n").reject(&:empty?).uniq }
|
|
166
166
|
colored = color_code(@colors[:default]).to_s + prompt + color_code(@colors[:answer])
|
|
167
167
|
Typr.read_line(colored, left: left, top: top, initial: initial,
|
|
168
|
-
completions: source, redraw: redraw, regex: @colors[:regex], history: history)
|
|
168
|
+
completions: source, redraw: redraw, regex: regex || @colors[:regex], history: history)
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
private
|
|
@@ -204,9 +204,7 @@ class Line < Space
|
|
|
204
204
|
@prompt = ?>
|
|
205
205
|
super
|
|
206
206
|
@bottom = @top
|
|
207
|
-
regex = { /(?:^|\s)--[^\s]+/ => :blue, /\A\S+/ => :red }
|
|
208
207
|
@colors = { question: :green, answer: :yellow }.merge @colors
|
|
209
|
-
@colors[:regex] = regex if @colors[:regex].nil?
|
|
210
208
|
reset
|
|
211
209
|
end
|
|
212
210
|
end
|
data/lib/space.rb
CHANGED
|
@@ -19,8 +19,11 @@ include Typr
|
|
|
19
19
|
def %s_setting; @%s; end
|
|
20
20
|
def %s
|
|
21
21
|
max=Typr.#{ name[/left|right/] ? 'width' : 'height' }
|
|
22
|
-
if !@%s;[max, #{
|
|
23
|
-
|
|
22
|
+
if !@%s;[max, #{ case name
|
|
23
|
+
when 'left' then '0'
|
|
24
|
+
when 'top' then '0'
|
|
25
|
+
when 'right' then 'left+@max'
|
|
26
|
+
else 'top+[(respond_to?(:rows) ? rows : 0), 1].max-1+(respond_to?(:headspace) ? headspace : 0)' end }].min
|
|
24
27
|
elsif @%s.is_a? Proc; @%s.call
|
|
25
28
|
elsif @%s.is_a? Float; (max * @%s).round
|
|
26
29
|
elsif @%s.negative?; max+@%s+1
|
data/lib/stack.rb
CHANGED
|
@@ -101,7 +101,7 @@ module Typr
|
|
|
101
101
|
color @colors[ :hints ]
|
|
102
102
|
if type.to_s[ /column/ ]
|
|
103
103
|
positions( row_id )[ @hints_start..-1 ].each_with_index{ |pos, idx|
|
|
104
|
-
move( left + pos, top + row_id )
|
|
104
|
+
move( [left + @margin.size + pos - 1, left].max, top + row_id )
|
|
105
105
|
draw @hints[idx] }
|
|
106
106
|
else
|
|
107
107
|
bottom = top + headspace + [height, rows].min - 1
|
data/lib/terminal.rb
CHANGED
|
@@ -86,7 +86,8 @@ module Typr
|
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
# Text attributes (reset, bold, italic, ...) and named 8/256-color tables.
|
|
89
|
-
MODES = %i[ reset bold italic underline slow fast invert ]
|
|
89
|
+
MODES = %i[ reset bold dim italic underline slow fast invert ]
|
|
90
|
+
MODES[25] = :steady
|
|
90
91
|
COLORS = %i[ black red green yellow blue magenta cyan white ]
|
|
91
92
|
COLOR_MAP = {
|
|
92
93
|
brown: 130, orange: 208, lime: 118, pink: 218,
|
|
@@ -239,6 +240,24 @@ module Typr
|
|
|
239
240
|
module_function :color_code
|
|
240
241
|
public :color_code
|
|
241
242
|
|
|
243
|
+
# Named regex color schemes, accepted anywhere a +regex:+ map is (Text,
|
|
244
|
+
# Line, read_line, paint). +:generic+ paints numbers, colon keys, quoted
|
|
245
|
+
# strings, bracket groups, and punctuation; +:shell+ paints a command
|
|
246
|
+
# line's first word and double-dash options.
|
|
247
|
+
REGEX_SCHEMES = {
|
|
248
|
+
generic: {
|
|
249
|
+
/\[[^\]]*\]|\([^)]*\)|\{[^}]*\}/ => :magenta,
|
|
250
|
+
/".*?"|'.*?'/ => :green,
|
|
251
|
+
/\b\d+(?:\.\d+)?\b/ => :cyan,
|
|
252
|
+
/\b[\w.-]+:/ => :yellow,
|
|
253
|
+
/[^\w\s]/ => :grey60
|
|
254
|
+
},
|
|
255
|
+
shell: {
|
|
256
|
+
/(?:^|\s)--[^\s]+/ => :blue,
|
|
257
|
+
/\A\S+/ => :red
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
242
261
|
# Returns +str+ with every region matched by the keys of +regex+ wrapped in
|
|
243
262
|
# the associated color, restoring +base+ after each region. Non-matching
|
|
244
263
|
# text passes through untouched. +regex+ may be nil or empty (no-op), and
|
|
@@ -254,6 +273,10 @@ module Typr
|
|
|
254
273
|
#
|
|
255
274
|
# Typr.paint "fix WARN now", { /WARN/ => :yellow }, "\e[39;49m"
|
|
256
275
|
def self.paint str, regex, base
|
|
276
|
+
if regex.is_a? Symbol
|
|
277
|
+
regex = REGEX_SCHEMES.fetch(regex) {
|
|
278
|
+
raise ArgumentError, "unknown regex scheme: #{regex.inspect}" }
|
|
279
|
+
end
|
|
257
280
|
return str if regex.nil? or regex.empty?
|
|
258
281
|
union = Regexp.union( regex.keys )
|
|
259
282
|
ansi = /\e(?:\[[0-9;?]*[ -\/]*[@-~]|\][^\a\e]*(?:\a|\e\\)|[()=><0A])|\e/
|
|
@@ -385,7 +408,7 @@ module Typr
|
|
|
385
408
|
## Interactive line editor used for string prompts (search, %str, ...).
|
|
386
409
|
#
|
|
387
410
|
# Renders +prompt+ and the query at (left, top) and edits it with
|
|
388
|
-
# arrow keys,
|
|
411
|
+
# arrow keys, alt-arrow word jumps, home/end, delete and backspace.
|
|
389
412
|
# Returns the query on enter, nil on escape. A block may inspect each
|
|
390
413
|
# keypress after it is applied; a non-nil return short-circuits the
|
|
391
414
|
# editor and becomes its return value. When stdin is not a tty, the
|
|
@@ -412,13 +435,16 @@ module Typr
|
|
|
412
435
|
$>.print CURSOR_NORMAL
|
|
413
436
|
begin
|
|
414
437
|
loop do
|
|
438
|
+
$>.print CURSOR_INVISIBLE
|
|
415
439
|
$>.print "\e[%i;%if" % [ top + 1, left + 1 ]
|
|
416
|
-
$>.print ERASE_LINE
|
|
417
440
|
before = text_width( prompt + query[0...cursor] )
|
|
418
441
|
offset = [ left + before - Typr.width, 0 ].max
|
|
419
442
|
display = prompt + paint( query, regex, prompt[/\e\[[0-9;]*m\z/].to_s )
|
|
420
|
-
|
|
443
|
+
line = slice_width( display, offset, Typr.width - left + 1 )
|
|
444
|
+
$>.print line
|
|
445
|
+
$>.print ' ' * (Typr.width - left + 1 - text_width(line))
|
|
421
446
|
$>.print "\e[%i;%if" % [ top + 1, left + 1 + before - offset ]
|
|
447
|
+
$>.print CURSOR_NORMAL
|
|
422
448
|
key = read_key
|
|
423
449
|
return nil if key.nil? or key == KEY_ESCAPE
|
|
424
450
|
return query if key == KEY_RETURN or key == "\n"
|
|
@@ -597,14 +623,15 @@ if __FILE__ == $0
|
|
|
597
623
|
begin
|
|
598
624
|
include Typr
|
|
599
625
|
Typr.init
|
|
600
|
-
colors = COLORS + (
|
|
626
|
+
colors = COLORS + COLOR_MAP.keys.map(&:to_s) + (0..10).map{|i| "grey#{i*10}" }
|
|
601
627
|
chars = (33..126).map(&:chr)
|
|
602
628
|
slice = chars.count / colors.count + 1
|
|
603
629
|
characters = chars.each_slice( slice ).map(&:join)
|
|
604
630
|
key,fg,bg = "",150,0
|
|
605
631
|
Typr.clear
|
|
632
|
+
rows = [colors.count, Typr.height - 4].min
|
|
606
633
|
loop do
|
|
607
|
-
colors.each_with_index{ |color,y|
|
|
634
|
+
colors.first(rows).each_with_index{ |color,y|
|
|
608
635
|
characters.each_with_index{ |chars,x|
|
|
609
636
|
foreground color.to_sym;
|
|
610
637
|
background colors[x].to_sym;
|
|
@@ -612,20 +639,21 @@ begin
|
|
|
612
639
|
draw chars
|
|
613
640
|
}
|
|
614
641
|
}
|
|
642
|
+
move 0, rows
|
|
615
643
|
(0...256).each{ |i| background i; draw ' ' }
|
|
616
644
|
|
|
617
645
|
move 0, Typr.height
|
|
618
646
|
color [:white, :black]
|
|
619
|
-
|
|
647
|
+
draw "KEY: %s / %s" % [key, key]
|
|
620
648
|
move 15, Typr.height
|
|
621
649
|
color [fg,bg]
|
|
622
650
|
print " %s / %s " % [fg,bg]
|
|
623
651
|
key = Typr.read_key
|
|
624
652
|
case key
|
|
625
|
-
when KEY_UP; fg
|
|
626
|
-
when KEY_DOWN; fg
|
|
627
|
-
when KEY_LEFT; bg
|
|
628
|
-
when KEY_RIGHT; bg
|
|
653
|
+
when KEY_UP; fg = [fg + 1, 255].min
|
|
654
|
+
when KEY_DOWN; fg = [fg - 1, 0].max
|
|
655
|
+
when KEY_LEFT; bg = [bg - 1, 0].max
|
|
656
|
+
when KEY_RIGHT; bg = [bg + 1, 255].max
|
|
629
657
|
when KEY_ESCAPE; exit
|
|
630
658
|
end
|
|
631
659
|
end
|
data/lib/text.rb
CHANGED
|
@@ -33,13 +33,19 @@ module Typr
|
|
|
33
33
|
# text << "appended line" # alias for build
|
|
34
34
|
|
|
35
35
|
def build input=nil
|
|
36
|
-
( input, @data, @lines =
|
|
36
|
+
( input, @data, @lines = @data, "", [0] ) if rebuild = !input
|
|
37
37
|
return self if rebuild && input == ""
|
|
38
|
-
rows = @lines[0...-1].each_index.map{ |i|
|
|
38
|
+
rows = @lines[0...-1].each_index.map{ |i| @data[@lines[i]...@lines[i+1]] }
|
|
39
39
|
@data = rows.join
|
|
40
40
|
off = [ 0 ]; rows.each{ |row| off << off.last + row.length }
|
|
41
41
|
@lines = off
|
|
42
42
|
str = input.respond_to?(:read) ? input.read : input.to_s
|
|
43
|
+
if !rebuild and (r = @colors[:regex]) and r != {}
|
|
44
|
+
base = color_code( @colors[:default][0] ).to_s
|
|
45
|
+
# Paint new spans once, whole (multiline matches included); rebuilds
|
|
46
|
+
# below only reflow, so paint can neither stack nor need stripping.
|
|
47
|
+
str = Typr.paint(str, r, base)
|
|
48
|
+
end
|
|
43
49
|
str = str.gsub("\t", " " * @tabspace)
|
|
44
50
|
@tail = ""
|
|
45
51
|
pos = 0
|
|
@@ -85,14 +91,6 @@ module Typr
|
|
|
85
91
|
end
|
|
86
92
|
end
|
|
87
93
|
@lines << @data.length unless @lines.last == @data.length
|
|
88
|
-
if (r = @colors[:regex]) and !r.empty?
|
|
89
|
-
base = color_code( @colors[:default][0] ).to_s
|
|
90
|
-
painted = @lines[0...-1].each_index.map{ |i|
|
|
91
|
-
Typr.paint @data[@lines[i]...@lines[i+1]], r, base }
|
|
92
|
-
@data = painted.join
|
|
93
|
-
off = [ 0 ]; painted.each{ |line| off << off.last + line.length }
|
|
94
|
-
@lines = off
|
|
95
|
-
end
|
|
96
94
|
highlight if @re
|
|
97
95
|
end
|
|
98
96
|
alias :<< :build
|
|
@@ -211,15 +209,7 @@ module Typr
|
|
|
211
209
|
@data, @lines, @tail = "", [0], ""
|
|
212
210
|
@search = { pattern: nil, dir: :forward, last: nil, matches: [] }
|
|
213
211
|
super args
|
|
214
|
-
|
|
215
|
-
/\[[^\]]*\]|\([^)]*\)|\{[^}]*\}/ => :magenta,
|
|
216
|
-
/".*?"|'.*?'/ => :green,
|
|
217
|
-
/\b\d+(?:\.\d+)?\b/ => :cyan,
|
|
218
|
-
/\b[\w.-]+:/ => :yellow,
|
|
219
|
-
/[^\w\s]/ => :grey60
|
|
220
|
-
}
|
|
221
|
-
@colors = { header: :yellow, search: :grey20 }.merge( @colors || {} )
|
|
222
|
-
@colors[:regex] = regex if @colors[:regex].nil?
|
|
212
|
+
@colors = { header: :yellow, search: :grey20 }.merge( @colors || {} )
|
|
223
213
|
@keymap = { search: ?/, search_next: ?n, search_prev: ?p }.merge @keymap
|
|
224
214
|
@lastwidth = width
|
|
225
215
|
self << @input
|
data/typr.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: typr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kilian Reitmayr
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-10-
|
|
10
|
+
date: 2026-10-11 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: unicode-display_width
|