hyperlist 1.10.0 → 1.12.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 +22 -0
- data/README.md +1 -1
- data/hyperlist +75 -88
- data/hyperlist.gemspec +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 64ff18ad8beee2946cfb3bb9f0c4cf8fd33d345787101a08099a0bd725e5a3c2
|
|
4
|
+
data.tar.gz: e05e082cc9f8fabfd60ebce6c74038d72eba81578e1481e7e4a2a5f549c0f724
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 462d8c4a69e7aac8ab66640e97719d0acf7839e60358b341bfd8dac5626202627b55dbf26ee9288735c73811743f53c9a9f2d3f6b52e3da2582c4474ed4453f7
|
|
7
|
+
data.tar.gz: e9697289fa0a566dbf32377b38f3b53a0b7a5ca62519795d80bd3355006d8c743e05b23a942c892920746de882a41dffe32f6118a84ba1466726f094b63218a1
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the HyperList Ruby TUI will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.12.0] - 2026-10-26
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- Wrap mode follows HyperList 2.9: when one Item runs past the line, every
|
|
9
|
+
Item in its group (same indent, same parent) gets a `+` Starter, and the
|
|
10
|
+
wrapped part is indented two spaces.
|
|
11
|
+
- A Starter is `+ ` or an Identifier such as `1 ` or `1.2 `. `-` is plain
|
|
12
|
+
text again and no longer coloured.
|
|
13
|
+
- Lists whose continuation lines are indented one space (older files) or
|
|
14
|
+
two spaces (2.8 on) both load as multi-line Items.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- `indent_size` in a config line crashed the app; it is read as a number now.
|
|
18
|
+
|
|
19
|
+
## [1.11.0] - 2026-08-13
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Starters are magenta rather than red. `+` and `-` are line-leading
|
|
23
|
+
structure markers, the same role a numbering Identifier plays, so they
|
|
24
|
+
share its colour instead of sitting with Properties and dates.
|
|
25
|
+
- The `-` Starter is now coloured as well, not only `+`.
|
|
26
|
+
|
|
5
27
|
## [1.10.0] - 2026-08-11
|
|
6
28
|
|
|
7
29
|
### Changed
|
data/README.md
CHANGED
|
@@ -119,7 +119,7 @@ For historical context and the original VIM implementation, see: [hyperlist.vim]
|
|
|
119
119
|
### Configuration Lines & Theming
|
|
120
120
|
- **Configuration Lines**: Add settings at the bottom of HyperList files using `((option=value, option2=value))`
|
|
121
121
|
- **Theme Support**: Three color themes - `light` (bright colors), `normal` (standard), `dark` (for light terminals)
|
|
122
|
-
- **Line Wrapping**: Enable with `wrap=yes` -
|
|
122
|
+
- **Line Wrapping**: Enable with `wrap=yes` - a long Item and the rest of its group get a `+` Starter (HyperList 2.9)
|
|
123
123
|
- **Line Numbers**: Enable with `show_numbers=yes`
|
|
124
124
|
- **Manual Configuration**: Use `:set option=value` to change settings on the fly
|
|
125
125
|
- **View Settings**: Use `:set` to see all settings, `:set option` to see one setting
|
data/hyperlist
CHANGED
|
@@ -73,7 +73,7 @@ class HyperListApp
|
|
|
73
73
|
include Rcurses::Input
|
|
74
74
|
include Rcurses::Cursor
|
|
75
75
|
|
|
76
|
-
VERSION = "1.
|
|
76
|
+
VERSION = "1.12.0"
|
|
77
77
|
|
|
78
78
|
def initialize(filename = nil)
|
|
79
79
|
@filename = filename ? File.expand_path(filename) : nil
|
|
@@ -291,18 +291,14 @@ class HyperListApp
|
|
|
291
291
|
is_continuation = false
|
|
292
292
|
if @items.length > 0
|
|
293
293
|
last_item = @items.last
|
|
294
|
-
#
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
if actual_spaces == expected_spaces
|
|
303
|
-
# This is a continuation line - append it to the previous item
|
|
304
|
-
continuation_text = line[expected_spaces..-1] || ""
|
|
305
|
-
last_item["text"] += "\n " + continuation_text
|
|
294
|
+
# Only an Item with a Starter spans lines. Its following lines sit
|
|
295
|
+
# two spaces beyond its indent (HyperList 2.8 on), or one in older
|
|
296
|
+
# files. With a two-space indent, two spaces is a child instead.
|
|
297
|
+
if starter?(last_item["text"])
|
|
298
|
+
indent = last_item["level"] * @indent_size
|
|
299
|
+
extra = line[/^ */].length - indent
|
|
300
|
+
if extra == 1 || (extra == 2 && @indent_size >= 3)
|
|
301
|
+
last_item["text"] += "\n" + line[indent..]
|
|
306
302
|
next # Skip adding as a new item
|
|
307
303
|
end
|
|
308
304
|
end
|
|
@@ -427,7 +423,7 @@ class HyperListApp
|
|
|
427
423
|
value = $2.strip
|
|
428
424
|
# Convert values to appropriate types
|
|
429
425
|
case key
|
|
430
|
-
when "fold_level", "auto_save_interval", "tab_width"
|
|
426
|
+
when "fold_level", "auto_save_interval", "tab_width", "indent_size"
|
|
431
427
|
config_options[key] = value.to_i
|
|
432
428
|
when "auto_save", "wrap", "show_numbers", "highlight_current", "checkbox_date", "backup", "encrypt"
|
|
433
429
|
config_options[key] = value.downcase == "true" || value.downcase == "yes"
|
|
@@ -1205,70 +1201,56 @@ class HyperListApp
|
|
|
1205
1201
|
end
|
|
1206
1202
|
|
|
1207
1203
|
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1204
|
+
# An Item's Starter (HyperList 2.9): "+ " or an Identifier such as
|
|
1205
|
+
# "1 ", "1.2 " or "1A1A ".
|
|
1206
|
+
def starter?(text)
|
|
1207
|
+
text.lstrip.match?(/\A(\+|[0-9][0-9A-Z.]*)\s/)
|
|
1208
|
+
end
|
|
1209
|
+
|
|
1210
|
+
# Wrap a line per HyperList 2.9: an Item that runs past the line takes a
|
|
1211
|
+
# Starter ("+ " unless it has one), and so does every Item of its group
|
|
1212
|
+
# (`group`). Its following lines are indented two spaces. A line the file
|
|
1213
|
+
# already carries as a continuation (`cont`) takes no Starter.
|
|
1214
|
+
def wrap_line(text, width, indent_level, group: false, cont: false)
|
|
1212
1215
|
return [text] unless @wrap
|
|
1213
|
-
|
|
1216
|
+
|
|
1214
1217
|
# Calculate effective width (account for indent and fold indicators)
|
|
1215
1218
|
indent_width = @indent_size * indent_level + 2 # +2 for fold indicator
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
# Find a good break point (prefer spaces)
|
|
1248
|
-
break_point = remaining[0...effective_width].rindex(' ') || effective_width
|
|
1249
|
-
line_text = remaining[0...break_point].rstrip
|
|
1250
|
-
# Add + to first line of multi-line items
|
|
1251
|
-
wrapped << "+ #{line_text}"
|
|
1252
|
-
remaining = remaining[break_point..-1].lstrip
|
|
1253
|
-
first_line = false
|
|
1254
|
-
end
|
|
1255
|
-
else
|
|
1256
|
-
# Continuation lines get just a space prefix per HyperList spec
|
|
1257
|
-
cont_width = effective_width - 1 # Account for space prefix
|
|
1258
|
-
if remaining.length <= cont_width
|
|
1259
|
-
wrapped << " #{remaining}"
|
|
1260
|
-
break
|
|
1261
|
-
else
|
|
1262
|
-
break_point = remaining[0...cont_width].rindex(' ') || cont_width
|
|
1263
|
-
wrapped << " #{remaining[0...break_point].rstrip}"
|
|
1264
|
-
remaining = remaining[break_point..-1].lstrip
|
|
1265
|
-
end
|
|
1266
|
-
end
|
|
1219
|
+
room = width - indent_width - 5 # Extra margin for readability
|
|
1220
|
+
return [text] if room <= 10
|
|
1221
|
+
|
|
1222
|
+
text = "+ #{text}" if !cont && (group || text.length > room) && !starter?(text)
|
|
1223
|
+
lines = []
|
|
1224
|
+
rest = text
|
|
1225
|
+
while rest.length > room
|
|
1226
|
+
cut = rest[0...room].rindex(' ')
|
|
1227
|
+
cut = room if cut.nil? || cut.zero?
|
|
1228
|
+
lines << rest[0...cut].rstrip
|
|
1229
|
+
rest = rest[cut..].lstrip
|
|
1230
|
+
room -= 2 if lines.length == 1 # the two-space lead below
|
|
1231
|
+
end
|
|
1232
|
+
lines << rest
|
|
1233
|
+
[lines[0], *lines[1..].map { |l| " #{l}" }]
|
|
1234
|
+
end
|
|
1235
|
+
|
|
1236
|
+
# For each visible Item: does its group (the Items at its indent under
|
|
1237
|
+
# its parent) hold an Item that spans more than one line at `width`?
|
|
1238
|
+
def wrap_groups(items, width)
|
|
1239
|
+
group_of = []
|
|
1240
|
+
parents = [] # [level, index] of the Items above, nearest last
|
|
1241
|
+
spans = Hash.new(false)
|
|
1242
|
+
items.each_with_index do |item, i|
|
|
1243
|
+
next unless item
|
|
1244
|
+
parents.pop while parents.any? && parents.last[0] >= item["level"]
|
|
1245
|
+
group_of[i] = [parents.last&.last, item["level"]]
|
|
1246
|
+
parents << [item["level"], i]
|
|
1247
|
+
room = width - (@indent_size * item["level"] + 2) - 5
|
|
1248
|
+
lines = item["text"].split("\n")
|
|
1249
|
+
spans[group_of[i]] = true if lines.length > 1 || lines.any? { |l| l.length > room }
|
|
1267
1250
|
end
|
|
1268
|
-
|
|
1269
|
-
wrapped
|
|
1251
|
+
group_of.map { |g| g && spans[g] }
|
|
1270
1252
|
end
|
|
1271
|
-
|
|
1253
|
+
|
|
1272
1254
|
def render_main
|
|
1273
1255
|
visible_items = get_visible_items
|
|
1274
1256
|
|
|
@@ -1280,6 +1262,7 @@ class HyperListApp
|
|
|
1280
1262
|
# Track which line number each item starts at (for scroll calculation)
|
|
1281
1263
|
item_line_starts = {}
|
|
1282
1264
|
|
|
1265
|
+
groups = @wrap ? wrap_groups(visible_items, @cols) : []
|
|
1283
1266
|
visible_items.each_with_index do |item, idx|
|
|
1284
1267
|
next unless item
|
|
1285
1268
|
|
|
@@ -1291,10 +1274,10 @@ class HyperListApp
|
|
|
1291
1274
|
embedded_lines = item["text"].split("\n")
|
|
1292
1275
|
text_lines = []
|
|
1293
1276
|
|
|
1294
|
-
embedded_lines.
|
|
1277
|
+
embedded_lines.each_with_index do |embedded_line, k|
|
|
1295
1278
|
if @wrap
|
|
1296
1279
|
# Wrap each embedded line separately
|
|
1297
|
-
wrapped = wrap_line(embedded_line, @cols, item["level"])
|
|
1280
|
+
wrapped = wrap_line(embedded_line, @cols, item["level"], group: groups[idx], cont: k.positive?)
|
|
1298
1281
|
text_lines.concat(wrapped)
|
|
1299
1282
|
else
|
|
1300
1283
|
text_lines << embedded_line
|
|
@@ -1575,10 +1558,10 @@ class HyperListApp
|
|
|
1575
1558
|
result = result.sub(/^[0-9][0-9A-Z.]*\s/, identifier.fg(colors["magenta"])) # Magenta for identifiers
|
|
1576
1559
|
end
|
|
1577
1560
|
|
|
1578
|
-
# Handle
|
|
1579
|
-
#
|
|
1561
|
+
# Handle the Starter at the beginning ("+" with a space). Magenta, the same as a
|
|
1562
|
+
# numbering Identifier: both are line-leading structure markers.
|
|
1580
1563
|
if result =~ /^\+\s/
|
|
1581
|
-
result = result.sub(
|
|
1564
|
+
result = result.sub(/^\+\s/) { "+".fg(colors["magenta"]) + " " }
|
|
1582
1565
|
end
|
|
1583
1566
|
|
|
1584
1567
|
# Handle continuation markers (+ at start of indented lines in References section)
|
|
@@ -3948,10 +3931,11 @@ class HyperListApp
|
|
|
3948
3931
|
A HyperList Item consists of these parts in sequence:
|
|
3949
3932
|
|
|
3950
3933
|
1. #{"Starter".fg("5")} (optional)
|
|
3951
|
-
• Neutral Starter: "+ "
|
|
3934
|
+
• Neutral Starter: "+ " — marks where an Item begins
|
|
3952
3935
|
• Identifier: Numbers (1.1.1) or mixed (1A1A) — also unique
|
|
3953
|
-
Required
|
|
3954
|
-
lines are indented two spaces beyond the Item's own indent
|
|
3936
|
+
Required when an Item spans more than one line; its following
|
|
3937
|
+
lines are indented two spaces beyond the Item's own indent. Then
|
|
3938
|
+
every Item of its group (same indent, same parent) takes one too
|
|
3955
3939
|
|
|
3956
3940
|
2. #{"Type".fg("5")} (optional)
|
|
3957
3941
|
• State: S: or |
|
|
@@ -4195,15 +4179,17 @@ class HyperListApp
|
|
|
4195
4179
|
HyperList
|
|
4196
4180
|
[1+] HyperList Item
|
|
4197
4181
|
[?] Starter; OR:
|
|
4198
|
-
Neutral Starter = "+ "
|
|
4182
|
+
+ Neutral Starter = "+ "
|
|
4199
4183
|
Marks where an Item begins and carries no further meaning
|
|
4200
|
-
Identifier (Numbers: Format = "1.1.1.1", Mixed: Format = "1A1A")
|
|
4184
|
+
+ Identifier (Numbers: Format = "1.1.1.1", Mixed: Format = "1A1A")
|
|
4201
4185
|
Unique, so the Item can be referred to; also acts as a Starter
|
|
4202
4186
|
+ An Item spanning more than one line must have a Starter, and its
|
|
4203
4187
|
following lines are indented two spaces beyond the Item's own indent
|
|
4204
|
-
One level of indentation is at least three columns wide, so a
|
|
4205
|
-
|
|
4206
|
-
|
|
4188
|
+
+ One level of indentation is at least three columns wide, so a
|
|
4189
|
+
continuation line can never be read as a child
|
|
4190
|
+
+ When one Item in a group spans more than one line, every Item
|
|
4191
|
+
in that group takes a Starter. A group is the Items at the
|
|
4192
|
+
same indent under the same parent
|
|
4207
4193
|
[?] Type
|
|
4208
4194
|
OR:
|
|
4209
4195
|
State = "S:" or "|"
|
|
@@ -5883,6 +5869,7 @@ class HyperListApp
|
|
|
5883
5869
|
# Track which line number each item starts at (for scroll calculation)
|
|
5884
5870
|
item_line_starts = {}
|
|
5885
5871
|
|
|
5872
|
+
groups = @wrap ? wrap_groups(visible_items, @split_pane.w - 10) : []
|
|
5886
5873
|
visible_items.each_with_index do |item, idx|
|
|
5887
5874
|
next unless item
|
|
5888
5875
|
|
|
@@ -5896,10 +5883,10 @@ class HyperListApp
|
|
|
5896
5883
|
embedded_lines = item["text"].split("\n")
|
|
5897
5884
|
text_lines = []
|
|
5898
5885
|
|
|
5899
|
-
embedded_lines.
|
|
5886
|
+
embedded_lines.each_with_index do |embedded_line, k|
|
|
5900
5887
|
if @wrap
|
|
5901
5888
|
# Wrap each embedded line separately
|
|
5902
|
-
wrapped = wrap_line(embedded_line, @split_pane.w - 10, item["level"])
|
|
5889
|
+
wrapped = wrap_line(embedded_line, @split_pane.w - 10, item["level"], group: groups[idx], cont: k.positive?)
|
|
5903
5890
|
text_lines.concat(wrapped)
|
|
5904
5891
|
else
|
|
5905
5892
|
text_lines << embedded_line
|
data/hyperlist.gemspec
CHANGED