marvi 0.4.2 → 0.5.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 +5 -0
- data/lib/marvi/ast_walker.rb +17 -4
- data/lib/marvi/highlighter.rb +81 -0
- data/lib/marvi/renderer/curses.rb +10 -4
- data/lib/marvi/version.rb +1 -1
- data/lib/marvi.rb +1 -0
- metadata +22 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 99ac52daecfd730bdd05ef63523075b5e4ad3229637208a99a083199518864dd
|
|
4
|
+
data.tar.gz: 4daae68331c1edf707b6c520c3defe941e9273b937e6618711b145e13568eed4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 775e51510484dac676928c537175db222f682d3d66c74691e248616442f042f294ae896c270531475e1cdc989dde1c1a5cb957df6acdf4159e0157b4d51f3d1f
|
|
7
|
+
data.tar.gz: b00bffa8ef8adaa9e5dab84df060e31d96ffad92e473bfb544df2962c3986cb26db06c35299902a4e341dd9c91422642b06ea9e3ab15e1c8089e64f64f352d56
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.5.0] - 2026-06-02
|
|
4
|
+
|
|
5
|
+
- Syntax-highlight fenced code blocks via [Rouge](https://github.com/rouge-ruby/rouge). Blocks without a language (or with an unknown one) fall back to the previous single-color rendering.
|
|
6
|
+
- Extend the dark code block background to the longest line so the block reads as a solid pane instead of a ragged shape.
|
|
7
|
+
|
|
3
8
|
## [0.4.2] - 2026-05-31
|
|
4
9
|
|
|
5
10
|
- Add left/right padding in the curses pager so content no longer sits flush against the terminal edges. Padding scales with terminal width.
|
data/lib/marvi/ast_walker.rb
CHANGED
|
@@ -112,11 +112,24 @@ module Marvi
|
|
|
112
112
|
lang = el.options[:lang]
|
|
113
113
|
lines = []
|
|
114
114
|
lines << RichLine.new([Span.new(text: lang, color: :yellow)], source_line: src) if lang
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
|
|
116
|
+
code = el.value.chomp
|
|
117
|
+
unless code.empty?
|
|
118
|
+
line_offset = lang ? 1 : 0
|
|
119
|
+
indent = Span.new(text: " ", bg_color: :dark)
|
|
120
|
+
indent_width = spans_display_width([indent])
|
|
121
|
+
|
|
122
|
+
highlighted = Highlighter.lines(code, lang)
|
|
123
|
+
content_widths = highlighted.map { |spans| spans_display_width(spans) }
|
|
124
|
+
block_width = [content_widths.max || 0, @max_width - indent_width].min
|
|
125
|
+
|
|
126
|
+
highlighted.each_with_index do |spans, i|
|
|
127
|
+
line_src = src ? src + i + line_offset : nil
|
|
128
|
+
line_spans = [indent] + spans
|
|
129
|
+
pad = block_width - content_widths[i]
|
|
130
|
+
line_spans << Span.new(text: " " * pad, bg_color: :dark) if pad > 0
|
|
131
|
+
lines << RichLine.new(line_spans, source_line: line_src)
|
|
118
132
|
end
|
|
119
|
-
lines << RichLine.new([Span.new(text: " #{line}", color: :green, bg_color: :dark)], source_line: line_src)
|
|
120
133
|
end
|
|
121
134
|
lines << RichLine.blank
|
|
122
135
|
lines
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rouge"
|
|
4
|
+
|
|
5
|
+
module Marvi
|
|
6
|
+
class Highlighter
|
|
7
|
+
DEFAULT_COLOR = :white
|
|
8
|
+
FALLBACK_COLOR = :green
|
|
9
|
+
|
|
10
|
+
# Ordered so longer qualnames win over their prefixes (e.g. "Literal.String" before "Literal").
|
|
11
|
+
TOKEN_COLOR_RULES = [
|
|
12
|
+
["Comment", :cyan],
|
|
13
|
+
["Keyword", :yellow],
|
|
14
|
+
["Literal.String", :green],
|
|
15
|
+
["Literal.Number", :magenta],
|
|
16
|
+
["Literal", :magenta],
|
|
17
|
+
["Name.Function", :cyan],
|
|
18
|
+
["Name.Class", :cyan],
|
|
19
|
+
["Name.Namespace", :cyan],
|
|
20
|
+
["Name.Tag", :cyan],
|
|
21
|
+
["Name.Attribute", :cyan],
|
|
22
|
+
["Name.Decorator", :cyan],
|
|
23
|
+
["Name.Constant", :magenta],
|
|
24
|
+
["Name.Builtin", :magenta],
|
|
25
|
+
["Operator", :white],
|
|
26
|
+
["Punctuation", :white],
|
|
27
|
+
["Error", :magenta]
|
|
28
|
+
].freeze
|
|
29
|
+
|
|
30
|
+
COMMENT_PREFIX = "Comment"
|
|
31
|
+
|
|
32
|
+
def self.lines(code, lang, bg_color: :dark)
|
|
33
|
+
lexer = resolve_lexer(lang)
|
|
34
|
+
return fallback(code, bg_color) if lexer.nil?
|
|
35
|
+
tokens_to_lines(lexer.lex(code), bg_color)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.resolve_lexer(lang)
|
|
39
|
+
return nil if lang.nil? || lang.empty?
|
|
40
|
+
Rouge::Lexer.find(lang)
|
|
41
|
+
rescue
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.fallback(code, bg_color)
|
|
46
|
+
code.split("\n", -1).map { |line| [Span.new(text: line, color: FALLBACK_COLOR, bg_color: bg_color)] }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.tokens_to_lines(tokens, bg_color)
|
|
50
|
+
current = []
|
|
51
|
+
lines = []
|
|
52
|
+
tokens.each do |tok, val|
|
|
53
|
+
color = token_color(tok)
|
|
54
|
+
italic = comment?(tok)
|
|
55
|
+
val.split("\n", -1).each_with_index do |part, idx|
|
|
56
|
+
if idx > 0
|
|
57
|
+
lines << current
|
|
58
|
+
current = []
|
|
59
|
+
end
|
|
60
|
+
next if part.empty?
|
|
61
|
+
current << Span.new(text: part, italic: italic, color: color, bg_color: bg_color)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
lines << current
|
|
65
|
+
lines
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.token_color(token)
|
|
69
|
+
qual = token.qualname
|
|
70
|
+
TOKEN_COLOR_RULES.each do |prefix, color|
|
|
71
|
+
return color if qual == prefix || qual.start_with?("#{prefix}.")
|
|
72
|
+
end
|
|
73
|
+
DEFAULT_COLOR
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.comment?(token)
|
|
77
|
+
qual = token.qualname
|
|
78
|
+
qual == COMMENT_PREFIX || qual.start_with?("#{COMMENT_PREFIX}.")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -12,8 +12,11 @@ module Marvi
|
|
|
12
12
|
yellow: 3,
|
|
13
13
|
magenta: 4,
|
|
14
14
|
white: 5,
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
cyan_on_dark: 6,
|
|
16
|
+
green_on_dark: 7,
|
|
17
|
+
yellow_on_dark: 8,
|
|
18
|
+
magenta_on_dark: 9,
|
|
19
|
+
white_on_dark: 10
|
|
17
20
|
}.freeze
|
|
18
21
|
|
|
19
22
|
ITALIC_ATTR = (defined?(::Curses::A_ITALIC) ? ::Curses::A_ITALIC : 0)
|
|
@@ -100,8 +103,11 @@ module Marvi
|
|
|
100
103
|
::Curses.init_pair(COLOR_PAIRS[:yellow], ::Curses::COLOR_YELLOW, -1)
|
|
101
104
|
::Curses.init_pair(COLOR_PAIRS[:magenta], ::Curses::COLOR_MAGENTA, -1)
|
|
102
105
|
::Curses.init_pair(COLOR_PAIRS[:white], ::Curses::COLOR_WHITE, -1)
|
|
103
|
-
::Curses.init_pair(COLOR_PAIRS[:green_on_dark], ::Curses::COLOR_GREEN, ::Curses::COLOR_BLACK)
|
|
104
106
|
::Curses.init_pair(COLOR_PAIRS[:cyan_on_dark], ::Curses::COLOR_CYAN, ::Curses::COLOR_BLACK)
|
|
107
|
+
::Curses.init_pair(COLOR_PAIRS[:green_on_dark], ::Curses::COLOR_GREEN, ::Curses::COLOR_BLACK)
|
|
108
|
+
::Curses.init_pair(COLOR_PAIRS[:yellow_on_dark], ::Curses::COLOR_YELLOW, ::Curses::COLOR_BLACK)
|
|
109
|
+
::Curses.init_pair(COLOR_PAIRS[:magenta_on_dark], ::Curses::COLOR_MAGENTA, ::Curses::COLOR_BLACK)
|
|
110
|
+
::Curses.init_pair(COLOR_PAIRS[:white_on_dark], ::Curses::COLOR_WHITE, ::Curses::COLOR_BLACK)
|
|
105
111
|
end
|
|
106
112
|
|
|
107
113
|
def handle_key(key)
|
|
@@ -278,7 +284,7 @@ module Marvi
|
|
|
278
284
|
attr |= ITALIC_ATTR if span.italic
|
|
279
285
|
|
|
280
286
|
pair_key = if span.bg_color == :dark
|
|
281
|
-
|
|
287
|
+
:"#{span.color || :green}_on_dark"
|
|
282
288
|
elsif span.color
|
|
283
289
|
span.color
|
|
284
290
|
end
|
data/lib/marvi/version.rb
CHANGED
data/lib/marvi.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: marvi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mitsutaka Mimura
|
|
@@ -65,6 +65,26 @@ dependencies:
|
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: '3.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rouge
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '4.0'
|
|
75
|
+
- - "<"
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '6'
|
|
78
|
+
type: :runtime
|
|
79
|
+
prerelease: false
|
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
81
|
+
requirements:
|
|
82
|
+
- - ">="
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: '4.0'
|
|
85
|
+
- - "<"
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '6'
|
|
68
88
|
description: Renders Markdown with ANSI colors in pipes and an interactive curses
|
|
69
89
|
pager in TTY.
|
|
70
90
|
email:
|
|
@@ -86,6 +106,7 @@ files:
|
|
|
86
106
|
- lib/marvi/ansi.rb
|
|
87
107
|
- lib/marvi/ast_walker.rb
|
|
88
108
|
- lib/marvi/document.rb
|
|
109
|
+
- lib/marvi/highlighter.rb
|
|
89
110
|
- lib/marvi/renderer/ansi.rb
|
|
90
111
|
- lib/marvi/renderer/curses.rb
|
|
91
112
|
- lib/marvi/version.rb
|