typr 1.1.2 → 1.2.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 +70 -0
- data/README.md +9 -9
- data/example/grid +4 -4
- data/example/text +7 -4
- data/example/top500.csv +1 -0
- data/lib/browser.rb +104 -74
- data/lib/curses.rb +48 -77
- data/lib/frontend.rb +10 -20
- data/lib/graphical.rb +39 -92
- data/lib/grid.rb +76 -29
- data/lib/line.rb +57 -27
- data/lib/space.rb +14 -5
- data/lib/stack.rb +18 -19
- data/lib/terminal.rb +249 -169
- data/lib/text.rb +118 -14
- data/lib/typr.rb +5 -6
- data/share/mimetypes +1298 -1
- data/typr.gemspec +2 -2
- metadata +2 -7
- data/Gemfile +0 -10
data/lib/text.rb
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# coding: utf-8
|
|
3
|
-
# frozen_string_literal: true
|
|
4
|
-
|
|
5
1
|
require 'stringio'
|
|
6
2
|
require_relative 'stack.rb'
|
|
7
3
|
|
|
8
4
|
module Typr
|
|
9
5
|
|
|
10
6
|
# A vertically scrollable text viewer with word-wrapping, header bar,
|
|
11
|
-
# row selection via hint characters, and search.
|
|
7
|
+
# row selection via hint characters, and pager-style search (/ n p).
|
|
12
8
|
#
|
|
13
9
|
# Accepts String, IO, or StringIO input. Content is split into display
|
|
14
10
|
# rows by newlines and line width, then rendered as a sliding window.
|
|
@@ -21,8 +17,8 @@ module Typr
|
|
|
21
17
|
# top: 2, left: 0.1, right: 0.9, bottom: -4,
|
|
22
18
|
# border: :round,
|
|
23
19
|
# colors: { header: [:yellow, :grey30] }
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
# )
|
|
21
|
+
# text.show
|
|
26
22
|
|
|
27
23
|
class Text < Stack
|
|
28
24
|
|
|
@@ -72,6 +68,7 @@ module Typr
|
|
|
72
68
|
end
|
|
73
69
|
end
|
|
74
70
|
@lines << @data.length unless @lines.last == @data.length
|
|
71
|
+
highlight if @re
|
|
75
72
|
end
|
|
76
73
|
alias :<< :build
|
|
77
74
|
|
|
@@ -84,7 +81,14 @@ module Typr
|
|
|
84
81
|
def print row=nil
|
|
85
82
|
header = ( row == :header )
|
|
86
83
|
return super unless row and @lines[ row + 1 ] unless header
|
|
87
|
-
|
|
84
|
+
text = ( header ? @header : @page[ row-@start ] )
|
|
85
|
+
if row.is_a?(Integer) and @search[:matches].include? row
|
|
86
|
+
background @colors[:search]
|
|
87
|
+
draw prepare( text, width )
|
|
88
|
+
background
|
|
89
|
+
else
|
|
90
|
+
draw prepare( text, width )
|
|
91
|
+
end
|
|
88
92
|
end
|
|
89
93
|
|
|
90
94
|
# Returns word-boundary column offsets for a given page row.
|
|
@@ -99,9 +103,9 @@ module Typr
|
|
|
99
103
|
# Full draw cycle: rebuilds on resize, computes visible lines,
|
|
100
104
|
# and renders the viewport with borders and hints.
|
|
101
105
|
#
|
|
102
|
-
# text.
|
|
106
|
+
# text.show
|
|
103
107
|
|
|
104
|
-
def
|
|
108
|
+
def show
|
|
105
109
|
(build; @lastwidth = width) if width != @lastwidth
|
|
106
110
|
range = @lines[@start..@start+height]
|
|
107
111
|
page = StringIO.new @data[range.first..range.last]
|
|
@@ -115,11 +119,60 @@ module Typr
|
|
|
115
119
|
|
|
116
120
|
def rows; @lines.count end
|
|
117
121
|
|
|
118
|
-
|
|
122
|
+
##
|
|
123
|
+
# Pager-style search over the buffer, modeled after less/vim.
|
|
124
|
+
#
|
|
125
|
+
# With a +query+ (String, matched literally, or Regexp) it stores the
|
|
126
|
+
# pattern, highlights every matching line, and scrolls to the first
|
|
127
|
+
# match from the current position. +dir+ is +:forward+ or +:backward+.
|
|
128
|
+
# Returns the matched line id, or nil when nothing matches.
|
|
129
|
+
#
|
|
130
|
+
# With no argument it prompts interactively for a pattern at the bottom
|
|
131
|
+
# of the screen (pressing Return searches, Escape aborts).
|
|
119
132
|
#
|
|
120
|
-
#
|
|
133
|
+
# Matching is smart-case: lowercase queries are case-insensitive;
|
|
134
|
+
# a query containing an uppercase letter is case-sensitive.
|
|
135
|
+
#
|
|
136
|
+
# text.search "needle" # => 12 (or nil)
|
|
137
|
+
# text.search /^TODO/ # => 3
|
|
138
|
+
# text.search "needle", :backward # search upward
|
|
139
|
+
|
|
140
|
+
def search query=nil, dir=:forward
|
|
141
|
+
return search_prompt if query.nil?
|
|
142
|
+
@search[:pattern] = query
|
|
143
|
+
@search[:dir] = dir
|
|
144
|
+
@search[:last] = nil
|
|
145
|
+
@re = query.is_a?(Regexp) ? query :
|
|
146
|
+
Regexp.new( Regexp.escape( query ),
|
|
147
|
+
( query[/[A-Z]/] ? 0 : Regexp::IGNORECASE ) )
|
|
148
|
+
highlight
|
|
149
|
+
step
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
## Jump to the next match of the last search (wrap-around). Returns its line id or nil.
|
|
153
|
+
|
|
154
|
+
def search_next; @search[:dir] = :forward; step end
|
|
155
|
+
|
|
156
|
+
## Jump to the previous match of the last search (wrap-around). Returns its line id or nil.
|
|
157
|
+
|
|
158
|
+
def search_prev; @search[:dir] = :backward; step end
|
|
159
|
+
|
|
160
|
+
## Recompute the set of lines containing the current pattern.
|
|
161
|
+
|
|
162
|
+
def highlight
|
|
163
|
+
@search[:matches] = @re ?
|
|
164
|
+
@lines[0...-1].each_index.select{ |i|
|
|
165
|
+
@data[ @lines[i] ... @lines[i+1] ][ @re ] } : []
|
|
166
|
+
return
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
## Clear the search pattern and all match highlights.
|
|
121
170
|
|
|
122
|
-
def
|
|
171
|
+
def highlight_clear
|
|
172
|
+
@re = @search[:pattern] = nil
|
|
173
|
+
@search[:matches] = []
|
|
174
|
+
@search[:last] = nil
|
|
175
|
+
return
|
|
123
176
|
end
|
|
124
177
|
|
|
125
178
|
# Creates a new Text widget.
|
|
@@ -130,11 +183,62 @@ module Typr
|
|
|
130
183
|
def initialize args={}
|
|
131
184
|
@tabspace = 2
|
|
132
185
|
@data, @lines, @tail = "", [0], ""
|
|
186
|
+
@search = { pattern: nil, dir: :forward, last: nil, matches: [] }
|
|
133
187
|
super args
|
|
134
|
-
@colors = { header: :yellow }.merge( @colors || {} )
|
|
188
|
+
@colors = { header: :yellow, search: :grey20 }.merge( @colors || {} )
|
|
189
|
+
@keymap = { search: ?/, search_next: ?n, search_prev: ?p }.merge @keymap
|
|
135
190
|
@lastwidth = width
|
|
136
191
|
self << @input
|
|
137
192
|
end
|
|
193
|
+
|
|
194
|
+
def reset type=:all
|
|
195
|
+
case type
|
|
196
|
+
when :search; highlight_clear
|
|
197
|
+
when :all; reset [:position, :selection, :search]; return
|
|
198
|
+
end
|
|
199
|
+
super
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
private
|
|
203
|
+
|
|
204
|
+
def live_search query
|
|
205
|
+
query.empty? ? highlight_clear : search(query)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def step
|
|
209
|
+
matches = @search[:matches]
|
|
210
|
+
return nil if matches.empty?
|
|
211
|
+
last = @search[:last]
|
|
212
|
+
if @search[:dir] == :backward
|
|
213
|
+
id = matches.reverse.find{ |m| last and m < last }
|
|
214
|
+
id ||= ( last ? matches.last : matches.reverse.find{ |m| m <= @start } || matches.last )
|
|
215
|
+
else
|
|
216
|
+
id = matches.find{ |m| last and m > last }
|
|
217
|
+
id ||= ( last ? matches.first : matches.find{ |m| m >= @start } || matches.first )
|
|
218
|
+
end
|
|
219
|
+
@search[:last] = id
|
|
220
|
+
@start = id
|
|
221
|
+
return id
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def search_prompt
|
|
225
|
+
query = @search[:pattern].is_a?(String) ? @search[:pattern].dup : ''
|
|
226
|
+
saved = { re: @re, pattern: @search[:pattern],
|
|
227
|
+
matches: @search[:matches].dup, last: @search[:last], start: @start }
|
|
228
|
+
result = Typr.read_line '/', left: left, top: Typr.height - 1, initial: query,
|
|
229
|
+
&->(key, query, _) {
|
|
230
|
+
live_search query
|
|
231
|
+
nil
|
|
232
|
+
}
|
|
233
|
+
if result
|
|
234
|
+
return search(result) unless result.empty?
|
|
235
|
+
return search(@search[:pattern]) if @search[:pattern]
|
|
236
|
+
end
|
|
237
|
+
@re = saved[:re]; @search[:pattern] = saved[:pattern]
|
|
238
|
+
@search[:matches] = saved[:matches]; @search[:last] = saved[:last]
|
|
239
|
+
@start = saved[:start]
|
|
240
|
+
return
|
|
241
|
+
end
|
|
138
242
|
end
|
|
139
243
|
|
|
140
244
|
end
|
data/lib/typr.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# typr is a Ruby library for building interactive terminal-based user interfaces.
|
|
2
|
-
# It provides layout primitives, widgets, and event-driven interaction
|
|
3
|
-
# top of curses.
|
|
2
|
+
# It provides layout primitives, widgets, and event-driven interaction.
|
|
4
3
|
#
|
|
5
4
|
# == Widgets
|
|
6
5
|
#
|
|
@@ -16,8 +15,8 @@
|
|
|
16
15
|
# Typr.init # Initialize terminal (hide cursor, enable key mode)
|
|
17
16
|
# Typr.clear # Clear entire screen
|
|
18
17
|
# Typr.clear :line # Clear current line only
|
|
19
|
-
# key = Typr.
|
|
20
|
-
# line = Typr.
|
|
18
|
+
# key = Typr.read_key # Read a single keypress
|
|
19
|
+
# line = Typr.read_line "> " # Edit a line of text (enter to accept, esc to abort)
|
|
21
20
|
# Typr.exit # Restore terminal state
|
|
22
21
|
#
|
|
23
22
|
# == Layout Properties
|
|
@@ -38,8 +37,8 @@
|
|
|
38
37
|
# header: ['Name', 'Age'],
|
|
39
38
|
# format: [:max, :right]
|
|
40
39
|
# )
|
|
41
|
-
# grid.
|
|
42
|
-
# key = Typr.
|
|
40
|
+
# grid.show
|
|
41
|
+
# key = Typr.read_key
|
|
43
42
|
# Typr.exit
|
|
44
43
|
#
|
|
45
44
|
# == Installation
|