marvi 0.4.2 → 0.6.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 +9 -0
- data/lib/marvi/ast_walker.rb +28 -4
- data/lib/marvi/diagram.rb +0 -0
- 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 +2 -0
- metadata +23 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0e3091d68f7f5ccd8b131401fd1dce1b010e7d8108125430de3c47bd68b403d4
|
|
4
|
+
data.tar.gz: f52d7849ff7982cb1b988b0e5e5e1c9a496661c56e689bd008fe34347b84dd7f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 54848bb6e78b5833c96eb6aaea4de9869c5dcd18c381c9b05dc69e1d2d9528f93be9846068bf89acc56f569c54b37e96bbd7868c070802b0ae644f98fa8914ed
|
|
7
|
+
data.tar.gz: d645f16e52d118bf57ef02dc5009cede7e7400377e7c6640030c78a36392ff37adab60a2bfbe8f88e749107bed3f57b153ab8f50754fc22ba822ff948adcbfcf
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.6.0] - 2026-06-11
|
|
4
|
+
|
|
5
|
+
- Render Mermaid fenced code blocks as Unicode box-drawing art. Supports `flowchart`/`graph` (TD/TB/LR/RL), `sequenceDiagram`, `classDiagram`, and `stateDiagram`/`stateDiagram-v2`. Unsupported diagram types, malformed syntax, and over-width output fall back to the highlighted code block.
|
|
6
|
+
|
|
7
|
+
## [0.5.0] - 2026-06-02
|
|
8
|
+
|
|
9
|
+
- 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.
|
|
10
|
+
- Extend the dark code block background to the longest line so the block reads as a solid pane instead of a ragged shape.
|
|
11
|
+
|
|
3
12
|
## [0.4.2] - 2026-05-31
|
|
4
13
|
|
|
5
14
|
- 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
|
@@ -110,13 +110,37 @@ module Marvi
|
|
|
110
110
|
def render_codeblock(el)
|
|
111
111
|
src = el.options[:location]
|
|
112
112
|
lang = el.options[:lang]
|
|
113
|
+
|
|
114
|
+
if lang == "mermaid"
|
|
115
|
+
diagram = Diagram.render(el.value.chomp, max_width: @max_width)
|
|
116
|
+
if diagram
|
|
117
|
+
lines = diagram.each_with_index.map do |spans, i|
|
|
118
|
+
RichLine.new(spans, source_line: (i.zero? ? src : nil))
|
|
119
|
+
end
|
|
120
|
+
return lines + [RichLine.blank]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
113
124
|
lines = []
|
|
114
125
|
lines << RichLine.new([Span.new(text: lang, color: :yellow)], source_line: src) if lang
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
126
|
+
|
|
127
|
+
code = el.value.chomp
|
|
128
|
+
unless code.empty?
|
|
129
|
+
line_offset = lang ? 1 : 0
|
|
130
|
+
indent = Span.new(text: " ", bg_color: :dark)
|
|
131
|
+
indent_width = spans_display_width([indent])
|
|
132
|
+
|
|
133
|
+
highlighted = Highlighter.lines(code, lang)
|
|
134
|
+
content_widths = highlighted.map { |spans| spans_display_width(spans) }
|
|
135
|
+
block_width = [content_widths.max || 0, @max_width - indent_width].min
|
|
136
|
+
|
|
137
|
+
highlighted.each_with_index do |spans, i|
|
|
138
|
+
line_src = src ? src + i + line_offset : nil
|
|
139
|
+
line_spans = [indent] + spans
|
|
140
|
+
pad = block_width - content_widths[i]
|
|
141
|
+
line_spans << Span.new(text: " " * pad, bg_color: :dark) if pad > 0
|
|
142
|
+
lines << RichLine.new(line_spans, source_line: line_src)
|
|
118
143
|
end
|
|
119
|
-
lines << RichLine.new([Span.new(text: " #{line}", color: :green, bg_color: :dark)], source_line: line_src)
|
|
120
144
|
end
|
|
121
145
|
lines << RichLine.blank
|
|
122
146
|
lines
|
|
Binary file
|
|
@@ -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
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require_relative "marvi/version"
|
|
4
4
|
require_relative "marvi/ansi"
|
|
5
5
|
require_relative "marvi/document"
|
|
6
|
+
require_relative "marvi/highlighter"
|
|
7
|
+
require_relative "marvi/diagram"
|
|
6
8
|
require_relative "marvi/ast_walker"
|
|
7
9
|
require_relative "marvi/renderer/ansi"
|
|
8
10
|
require_relative "marvi/renderer/curses"
|
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.6.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:
|
|
@@ -85,7 +105,9 @@ files:
|
|
|
85
105
|
- lib/marvi.rb
|
|
86
106
|
- lib/marvi/ansi.rb
|
|
87
107
|
- lib/marvi/ast_walker.rb
|
|
108
|
+
- lib/marvi/diagram.rb
|
|
88
109
|
- lib/marvi/document.rb
|
|
110
|
+
- lib/marvi/highlighter.rb
|
|
89
111
|
- lib/marvi/renderer/ansi.rb
|
|
90
112
|
- lib/marvi/renderer/curses.rb
|
|
91
113
|
- lib/marvi/version.rb
|