ohhighmark 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +346 -0
- data/app/assets/stylesheets/ohhighmark/ohhighmark.css +171 -0
- data/lib/ohhighmark/dsl.rb +14 -0
- data/lib/ohhighmark/helper.rb +23 -0
- data/lib/ohhighmark/highlighter.rb +76 -0
- data/lib/ohhighmark/markdown_rules.rb +39 -0
- data/lib/ohhighmark/renderer.rb +79 -0
- data/lib/ohhighmark/rule.rb +3 -0
- data/lib/ohhighmark/ruleset.rb +23 -0
- data/lib/ohhighmark/token.rb +20 -0
- data/lib/ohhighmark/tokenizer.rb +129 -0
- data/lib/ohhighmark/version.rb +3 -0
- data/lib/ohhighmark.rb +13 -0
- metadata +71 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f310c11edeb0bc2c0b7ce0871e3fce1bac23bc79d8bc06b6ae2a5612628afc9e
|
|
4
|
+
data.tar.gz: 1221b0fc060bc43e0a5c771e3dc51008ff41a6b883b977c926d59e34b3c49f21
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6da2f05b511406d14d93c64052b392220cf3ccb31cffef58b28a35daf940cf7275e09dbfe1a3dfcf863f92bba6f37a75d58d79d4726e3d9d700c7f3cb45a002b
|
|
7
|
+
data.tar.gz: a0a97b44f53f5f52ee45eeea9ae8bb90a9cfc453c6a4d0ef60740a7380a04313fb1390e944ae0c1fa38f02f76e9c9c53991f945853c661870147eb8d2d05b72e
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 David Knight
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# OhHighMark :wave:
|
|
2
|
+
|
|
3
|
+
**A Ruby library for syntax highlighting markdown code with beautiful, customizable output.**
|
|
4
|
+
|
|
5
|
+
OhHighMark provides colorful and readable markdown syntax visualization by tokenizing and rendering markdown with appropriate CSS classes. It displays each line in a table row with line numbers, ensuring perfect alignment and preserving all whitespace.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
### Key Features
|
|
10
|
+
|
|
11
|
+
- **Table-based layout** - Each line in its own table row with separate cells for line numbers and code
|
|
12
|
+
- **Perfect alignment** - Line numbers stay aligned with code content, even with long lines
|
|
13
|
+
- **Whitespace preservation** - Uses `white-space: pre` to maintain exact spacing and indentation
|
|
14
|
+
- **Non-selectable line numbers** - Prevents copy/paste issues
|
|
15
|
+
- **Comprehensive markdown support** - Headings, text formatting, code, links, lists, tables, quotes, and more
|
|
16
|
+
- **Inline markdown in lists** - Bold, italic, links, and other inline syntax work within list items
|
|
17
|
+
- **Customizable styling** - Pre-built CSS with CSS variables for easy theming
|
|
18
|
+
- **Semantic HTML** - Clean, semantic markup with `data-line-number` on rows
|
|
19
|
+
- **Flexible rules** - Customizable tokenization rules via DSL
|
|
20
|
+
|
|
21
|
+
### Supported Markdown Syntax
|
|
22
|
+
|
|
23
|
+
OhHighMark supports all standard markdown elements:
|
|
24
|
+
|
|
25
|
+
**Text Formatting:**
|
|
26
|
+
- **Bold**: `**text**`
|
|
27
|
+
- _Italic_: `_text_`
|
|
28
|
+
- ***Bold-Italic***: `***text***`
|
|
29
|
+
- ~~Strikethrough~~: `~~text~~`
|
|
30
|
+
|
|
31
|
+
**Headings:** `# H1` through `###### H6`
|
|
32
|
+
|
|
33
|
+
**Code:**
|
|
34
|
+
- Inline: `` `code` ``
|
|
35
|
+
- Fenced blocks: ` ``` ` ... ` ``` `
|
|
36
|
+
|
|
37
|
+
**Links:**
|
|
38
|
+
- Standard: `[text](url)`
|
|
39
|
+
- Auto-links: `<http://url>`
|
|
40
|
+
|
|
41
|
+
**Lists:**
|
|
42
|
+
- Unordered: `- item`, `* item`, `+ item`
|
|
43
|
+
- Nested: ` - nested item`
|
|
44
|
+
- **With inline markdown**: `- **bold** with [link](url)`
|
|
45
|
+
|
|
46
|
+
**Tables:**
|
|
47
|
+
```markdown
|
|
48
|
+
| Column 1 | Column 2 |
|
|
49
|
+
| -------- | -------- |
|
|
50
|
+
| Value 1 | Value 2 |
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Other:**
|
|
54
|
+
- Horizontal rules: `---`
|
|
55
|
+
- Block quotes: `> quote`
|
|
56
|
+
- Escaped characters: `\*`, `\[`, `\]`, `\\`
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
Add to your Gemfile:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
gem 'ohhighmark'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Then run:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
$ bundle install
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or install directly:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
$ gem install ohhighmark
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Usage
|
|
79
|
+
|
|
80
|
+
### Basic Usage
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
require 'ohhighmark'
|
|
84
|
+
|
|
85
|
+
# Use the helper in your views (Rails, Middleman, etc.)
|
|
86
|
+
include OhHighMark::Helper
|
|
87
|
+
|
|
88
|
+
# In your ERB view:
|
|
89
|
+
<%= highlight_markdown do %>
|
|
90
|
+
# Your Markdown Here
|
|
91
|
+
|
|
92
|
+
This is **bold** and this is _italic_.
|
|
93
|
+
|
|
94
|
+
- List item with [link](https://example.com)
|
|
95
|
+
- Item with `inline code`
|
|
96
|
+
<% end %>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Direct API Usage
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
require 'ohhighmark'
|
|
103
|
+
|
|
104
|
+
markdown = <<~MD
|
|
105
|
+
# Welcome
|
|
106
|
+
|
|
107
|
+
This is **bold** and ~~strikethrough~~.
|
|
108
|
+
MD
|
|
109
|
+
|
|
110
|
+
# Create highlighter and process
|
|
111
|
+
highlighter = OhHighMark::Highlighter.new
|
|
112
|
+
html_output = highlighter.process_lines_with_linenums(markdown)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Advanced: Custom Rules
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
# Define custom markdown rules
|
|
119
|
+
ruleset = OhHighMark::RuleSet.new
|
|
120
|
+
OhHighMark::MarkdownRules.apply(ruleset)
|
|
121
|
+
|
|
122
|
+
# Add custom rules if needed
|
|
123
|
+
ruleset.add(:custom_pattern, /your_regex/, :inline)
|
|
124
|
+
|
|
125
|
+
# Create tokenizer and renderer
|
|
126
|
+
tokenizer = OhHighMark::Tokenizer.new(ruleset.rules)
|
|
127
|
+
tokens = tokenizer.tokenize(markdown_text)
|
|
128
|
+
|
|
129
|
+
renderer = OhHighMark::Renderer.new
|
|
130
|
+
html = renderer.render(tokens)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Styling
|
|
134
|
+
|
|
135
|
+
OhHighMark includes a pre-built CSS stylesheet with CSS custom properties (CSS variables) for easy customization.
|
|
136
|
+
|
|
137
|
+
### Including the Stylesheet
|
|
138
|
+
|
|
139
|
+
**Rails (Asset Pipeline / Sprockets):**
|
|
140
|
+
|
|
141
|
+
In `application.css`:
|
|
142
|
+
```css
|
|
143
|
+
/*
|
|
144
|
+
*= require ohhighmark/ohhighmark
|
|
145
|
+
*/
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Or in `application.scss`:
|
|
149
|
+
```scss
|
|
150
|
+
@import 'ohhighmark/ohhighmark';
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Rails (Propshaft / Importmap):**
|
|
154
|
+
|
|
155
|
+
Copy the stylesheet:
|
|
156
|
+
```bash
|
|
157
|
+
$ cp $(bundle show ohhighmark)/app/assets/stylesheets/ohhighmark/ohhighmark.css app/assets/stylesheets/
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Link in your layout:
|
|
161
|
+
```erb
|
|
162
|
+
<%= stylesheet_link_tag "ohhighmark" %>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Middleman:**
|
|
166
|
+
|
|
167
|
+
Copy the stylesheet:
|
|
168
|
+
```bash
|
|
169
|
+
$ cp $(bundle show ohhighmark)/app/assets/stylesheets/ohhighmark/ohhighmark.css source/stylesheets/
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Then import it in your main stylesheet.
|
|
173
|
+
|
|
174
|
+
**Standalone / Other Frameworks:**
|
|
175
|
+
|
|
176
|
+
Find the stylesheet in the gem:
|
|
177
|
+
```bash
|
|
178
|
+
$ bundle show ohhighmark
|
|
179
|
+
# Copy app/assets/stylesheets/ohhighmark/ohhighmark.css to your project
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Customizing Colors
|
|
183
|
+
|
|
184
|
+
Override CSS variables **after** including the OhHighMark stylesheet:
|
|
185
|
+
|
|
186
|
+
```css
|
|
187
|
+
:root {
|
|
188
|
+
/* Text and background */
|
|
189
|
+
--ohm-text-color: #ffffff;
|
|
190
|
+
--ohm-background-color: #1a1a1a;
|
|
191
|
+
--ohm-linenos-color: #666666;
|
|
192
|
+
--ohm-linenos-bg-color: #1a1a1a;
|
|
193
|
+
|
|
194
|
+
/* Syntax colors */
|
|
195
|
+
--ohm-yellow: #ffff00; /* H1 headings */
|
|
196
|
+
--ohm-orange: #ff8000; /* H2 headings */
|
|
197
|
+
--ohm-purple: #ff00ff; /* H3 headings */
|
|
198
|
+
--ohm-cyan: #00ffff; /* H4-H6 headings */
|
|
199
|
+
--ohm-blue: #0080ff; /* Links */
|
|
200
|
+
--ohm-intense-blue: #0040ff; /* Link hover */
|
|
201
|
+
--ohm-green: #00ff00; /* Quotes, lists */
|
|
202
|
+
--ohm-gray: #808080; /* Horizontal rules */
|
|
203
|
+
--ohm-grayish: #999999; /* Table borders */
|
|
204
|
+
|
|
205
|
+
/* Table highlighting */
|
|
206
|
+
--ohm-table-bg: rgba(255, 248, 197, 0.2);
|
|
207
|
+
|
|
208
|
+
/* Font */
|
|
209
|
+
--ohm-monospace-font: "Courier New", monospace;
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Dark Theme Example:**
|
|
214
|
+
```css
|
|
215
|
+
:root {
|
|
216
|
+
--ohm-text-color: #e0e0e0;
|
|
217
|
+
--ohm-background-color: #0d1117;
|
|
218
|
+
--ohm-linenos-bg-color: #0d1117;
|
|
219
|
+
--ohm-linenos-color: #6e7681;
|
|
220
|
+
--ohm-blue: #4a9eff;
|
|
221
|
+
--ohm-green: #7cfc00;
|
|
222
|
+
--ohm-yellow: #ffd700;
|
|
223
|
+
--ohm-orange: #ff6b35;
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Light Theme Example:**
|
|
228
|
+
```css
|
|
229
|
+
:root {
|
|
230
|
+
--ohm-text-color: #333333;
|
|
231
|
+
--ohm-background-color: #ffffff;
|
|
232
|
+
--ohm-linenos-bg-color: #f6f8fa;
|
|
233
|
+
--ohm-linenos-color: #57606a;
|
|
234
|
+
--ohm-blue: #0969da;
|
|
235
|
+
--ohm-green: #1a7f37;
|
|
236
|
+
--ohm-yellow: #b8860b;
|
|
237
|
+
--ohm-orange: #d2691e;
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### HTML Structure
|
|
242
|
+
|
|
243
|
+
OhHighMark generates semantic HTML with clean structure:
|
|
244
|
+
|
|
245
|
+
```html
|
|
246
|
+
<div class="ohhighmark">
|
|
247
|
+
<table class="highlight">
|
|
248
|
+
<tbody>
|
|
249
|
+
<!-- Regular content row -->
|
|
250
|
+
<tr data-line-number="1">
|
|
251
|
+
<td class="blob-num">1</td>
|
|
252
|
+
<td class="blob-code"><span class="md-h1"># Heading</span></td>
|
|
253
|
+
</tr>
|
|
254
|
+
|
|
255
|
+
<!-- Row with text -->
|
|
256
|
+
<tr data-line-number="2">
|
|
257
|
+
<td class="blob-num">2</td>
|
|
258
|
+
<td class="blob-code">Some text with <span class="md-bold">**bold**</span></td>
|
|
259
|
+
</tr>
|
|
260
|
+
|
|
261
|
+
<!-- Table row (has table-line class) -->
|
|
262
|
+
<tr class="table-line" data-line-number="3">
|
|
263
|
+
<td class="blob-num">3</td>
|
|
264
|
+
<td class="blob-code"><span class="md-table-pipe">|</span> Col 1 <span class="md-table-pipe">|</span> Col 2 <span class="md-table-pipe">|</span></td>
|
|
265
|
+
</tr>
|
|
266
|
+
</tbody>
|
|
267
|
+
</table>
|
|
268
|
+
</div>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Key Structure Points:**
|
|
272
|
+
- `data-line-number` attribute on `<tr>` for easy row targeting
|
|
273
|
+
- `.blob-num` cell contains line number
|
|
274
|
+
- `.blob-code` cell contains formatted content
|
|
275
|
+
- `.table-line` class added to rows that are part of markdown tables
|
|
276
|
+
- Markdown syntax wrapped in `<span>` elements with `.md-*` classes
|
|
277
|
+
|
|
278
|
+
### CSS Classes Reference
|
|
279
|
+
|
|
280
|
+
**Container & Layout:**
|
|
281
|
+
- `.ohhighmark` - Main container div
|
|
282
|
+
- `table.highlight` - Table element containing all lines
|
|
283
|
+
- `.blob-num` - Line number cell (`user-select: none` prevents copying)
|
|
284
|
+
- `.blob-code` - Code content cell (uses `white-space: pre`)
|
|
285
|
+
- `.table-line` - Added to `<tr>` for markdown table rows
|
|
286
|
+
|
|
287
|
+
**Markdown Syntax Classes:**
|
|
288
|
+
- `.md-h1` through `.md-h6` - Heading levels 1-6
|
|
289
|
+
- `.md-bold` - Bold text (`**text**`)
|
|
290
|
+
- `.md-italic` - Italic text (`_text_`)
|
|
291
|
+
- `.md-em` - Bold-italic emphasis (`***text***`)
|
|
292
|
+
- `.md-strikethrough` - Strikethrough (`~~text~~`)
|
|
293
|
+
- `.md-code` - Inline code (`` `code` ``)
|
|
294
|
+
- `.md-link` - Standard links (`[text](url)`)
|
|
295
|
+
- `.md-autolink` - Auto-linked URLs (`<http://url>`)
|
|
296
|
+
- `.md-li` - List item markers
|
|
297
|
+
- `.md-hr` - Horizontal rules (`---`)
|
|
298
|
+
- `.md-fence` - Fenced code block delimiters
|
|
299
|
+
- `.md-codeblock` - Content within fenced code blocks
|
|
300
|
+
- `.md-table-pipe` - Table pipe characters (`|`)
|
|
301
|
+
- `.md-table-dash` - Table separator rows (`---`)
|
|
302
|
+
- `.md-quote` - Block quotes (`> text`)
|
|
303
|
+
|
|
304
|
+
### Critical CSS Requirements
|
|
305
|
+
|
|
306
|
+
**The `.blob-code` cell MUST have `white-space: pre`** to preserve whitespace and prevent wrapping:
|
|
307
|
+
|
|
308
|
+
```css
|
|
309
|
+
.ohhighmark td.blob-code {
|
|
310
|
+
white-space: pre; /* REQUIRED - preserves all whitespace */
|
|
311
|
+
font-family: ui-monospace, monospace;
|
|
312
|
+
line-height: 20px;
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Frontend JavaScript Integration
|
|
317
|
+
|
|
318
|
+
Target rows by line number:
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
// Select a specific line
|
|
322
|
+
const row = document.querySelector('[data-line-number="5"]');
|
|
323
|
+
|
|
324
|
+
// Highlight a line
|
|
325
|
+
row.style.backgroundColor = 'rgba(255, 255, 0, 0.2)';
|
|
326
|
+
|
|
327
|
+
// Get all table rows
|
|
328
|
+
const tableRows = document.querySelectorAll('tr[data-line-number]');
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Contributing
|
|
332
|
+
|
|
333
|
+
Bug reports and pull requests are welcome!
|
|
334
|
+
|
|
335
|
+
### Development Setup
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
$ git clone https://github.com/viacoffee/ohhighmark.git
|
|
339
|
+
$ cd ohhighmark
|
|
340
|
+
$ bundle install
|
|
341
|
+
$ rspec # Run tests
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## License
|
|
345
|
+
|
|
346
|
+
This gem is available as open source under the terms of the [MIT License](LICENSE).
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OhHighMark Stylesheet
|
|
3
|
+
*
|
|
4
|
+
* CSS custom properties for easy customization.
|
|
5
|
+
* Override these variables to customize the color scheme.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
:root {
|
|
9
|
+
/* Colors used by OhHighMark */
|
|
10
|
+
--ohm-text-color: #d8dee9; /* Main text color */
|
|
11
|
+
--ohm-background-color: #2B303B; /* Background color */
|
|
12
|
+
--ohm-linenos-color: #3b414d; /* Line numbers color */
|
|
13
|
+
--ohm-linenos-bg-color: #2B303B; /* Line numbers background */
|
|
14
|
+
--ohm-gray: #4c566a; /* Gray for HR */
|
|
15
|
+
--ohm-green: #a3be8c; /* Green for quotes and lists */
|
|
16
|
+
--ohm-grayish: #667084; /* Light gray for tables */
|
|
17
|
+
--ohm-blue: #81a1c1; /* Blue for links */
|
|
18
|
+
--ohm-intense-blue: #5e81ac; /* Darker blue for link hover */
|
|
19
|
+
--ohm-yellow: #ebcb8b; /* Yellow for h1 */
|
|
20
|
+
--ohm-orange: #d08770; /* Orange for h2 */
|
|
21
|
+
--ohm-purple: #b48ead; /* Purple for h3 */
|
|
22
|
+
--ohm-cyan: #8fbcbb; /* Cyan for h4-h6 */
|
|
23
|
+
--ohm-table-bg: rgba(255, 248, 197, 0.2); /* Table row highlight */
|
|
24
|
+
|
|
25
|
+
/* Font family for monospace content */
|
|
26
|
+
--ohm-monospace-font: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.ohhighmark {
|
|
30
|
+
color: var(--ohm-text-color);
|
|
31
|
+
background-color: var(--ohm-background-color);
|
|
32
|
+
padding: 10px;
|
|
33
|
+
margin: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Table structure */
|
|
37
|
+
.ohhighmark table.highlight {
|
|
38
|
+
border-collapse: collapse;
|
|
39
|
+
width: 100%;
|
|
40
|
+
font-family: var(--ohm-monospace-font);
|
|
41
|
+
font-size: 12px;
|
|
42
|
+
line-height: 20px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.ohhighmark table.highlight tr {
|
|
46
|
+
background: transparent;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Line number cell */
|
|
50
|
+
.ohhighmark td.blob-num {
|
|
51
|
+
padding: 0 10px;
|
|
52
|
+
width: 1%;
|
|
53
|
+
min-width: 30px;
|
|
54
|
+
font-family: var(--ohm-monospace-font);
|
|
55
|
+
font-size: 12px;
|
|
56
|
+
line-height: 18px;
|
|
57
|
+
color: var(--ohm-linenos-color);
|
|
58
|
+
text-align: right;
|
|
59
|
+
white-space: nowrap;
|
|
60
|
+
vertical-align: top;
|
|
61
|
+
user-select: none;
|
|
62
|
+
background-color: var(--ohm-linenos-bg-color);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Code content cell - CRITICAL: white-space: pre preserves spaces and line breaks */
|
|
66
|
+
.ohhighmark td.blob-code {
|
|
67
|
+
padding: 0 10px;
|
|
68
|
+
color: var(--ohm-text-color);
|
|
69
|
+
vertical-align: top;
|
|
70
|
+
overflow: visible;
|
|
71
|
+
white-space: pre-wrap; /* IMPORTANT: Preserves whitespace */
|
|
72
|
+
font-family: var(--ohm-monospace-font);
|
|
73
|
+
font-size: 12px;
|
|
74
|
+
line-height: 18px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Table row highlighting */
|
|
78
|
+
.ohhighmark tr.table-line {
|
|
79
|
+
background: var(--ohm-table-bg);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Headings */
|
|
83
|
+
.ohhighmark .md-h1 {
|
|
84
|
+
color: var(--ohm-yellow);
|
|
85
|
+
font-weight: bold;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.ohhighmark .md-h2 {
|
|
89
|
+
color: var(--ohm-orange);
|
|
90
|
+
font-weight: bold;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.ohhighmark .md-h3 {
|
|
94
|
+
color: var(--ohm-purple);
|
|
95
|
+
font-weight: bold;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.ohhighmark .md-h4,
|
|
99
|
+
.ohhighmark .md-h5,
|
|
100
|
+
.ohhighmark .md-h6 {
|
|
101
|
+
color: var(--ohm-cyan);
|
|
102
|
+
font-weight: bold;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Text styles */
|
|
106
|
+
.ohhighmark .md-bold {
|
|
107
|
+
font-weight: 700;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.ohhighmark .md-italic {
|
|
111
|
+
font-style: italic;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.ohhighmark .md-em {
|
|
115
|
+
font-weight: 700;
|
|
116
|
+
font-style: italic;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.ohhighmark .md-strikethrough {
|
|
120
|
+
text-decoration: line-through;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.ohhighmark .md-quote {
|
|
124
|
+
font-style: italic;
|
|
125
|
+
color: var(--ohm-green);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Horizontal rule */
|
|
129
|
+
.ohhighmark .md-hr {
|
|
130
|
+
color: var(--ohm-gray);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Lists */
|
|
134
|
+
.ohhighmark .md-li {
|
|
135
|
+
color: var(--ohm-green);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Links */
|
|
139
|
+
.ohhighmark .md-link,
|
|
140
|
+
.ohhighmark .md-autolink {
|
|
141
|
+
color: var(--ohm-blue);
|
|
142
|
+
text-decoration: underline;
|
|
143
|
+
text-underline-offset: 2px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.ohhighmark .md-link:link,
|
|
147
|
+
.ohhighmark .md-link:visited,
|
|
148
|
+
.ohhighmark .md-autolink:link,
|
|
149
|
+
.ohhighmark .md-autolink:visited {
|
|
150
|
+
color: var(--ohm-blue);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.ohhighmark .md-link:hover,
|
|
154
|
+
.ohhighmark .md-link:focus,
|
|
155
|
+
.ohhighmark .md-autolink:hover,
|
|
156
|
+
.ohhighmark .md-autolink:focus {
|
|
157
|
+
color: var(--ohm-intense-blue);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* Tables */
|
|
161
|
+
.ohhighmark .md-table-pipe,
|
|
162
|
+
.ohhighmark .md-table-dash {
|
|
163
|
+
color: var(--ohm-grayish);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* Code blocks and fences */
|
|
167
|
+
.ohhighmark .md-fence,
|
|
168
|
+
.ohhighmark .md-codeblock,
|
|
169
|
+
.ohhighmark .md-code {
|
|
170
|
+
font-family: var(--ohm-monospace-font);
|
|
171
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module OhHighMark
|
|
2
|
+
module DSL
|
|
3
|
+
private
|
|
4
|
+
|
|
5
|
+
# DSL methods for defining rules
|
|
6
|
+
def define_inline(name, regex, **options)
|
|
7
|
+
add(name, regex, kind: :inline, **options)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def define_block(name, regex, **options)
|
|
11
|
+
add(name, regex, kind: :block, **options)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module OhHighMark
|
|
2
|
+
module Helper
|
|
3
|
+
def highlight_markdown(&block)
|
|
4
|
+
raise ArgumentError, "block required" unless block
|
|
5
|
+
|
|
6
|
+
content =
|
|
7
|
+
if respond_to?(:capture)
|
|
8
|
+
capture(&block)
|
|
9
|
+
else
|
|
10
|
+
block.call.to_s
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
highlighter = Highlighter.new
|
|
14
|
+
html = highlighter.process_lines_with_linenums(content)
|
|
15
|
+
|
|
16
|
+
if respond_to?(:concat)
|
|
17
|
+
concat(html)
|
|
18
|
+
else
|
|
19
|
+
respond_to?(:raw) ? raw(html) : html
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module OhHighMark
|
|
2
|
+
class Highlighter
|
|
3
|
+
def initialize
|
|
4
|
+
@ruleset = RuleSet.new
|
|
5
|
+
MarkdownRules.apply(@ruleset)
|
|
6
|
+
@tokenizer = Tokenizer.new(@ruleset.rules)
|
|
7
|
+
@renderer = Renderer.new
|
|
8
|
+
@in_fence = false
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Output table with one row per line
|
|
12
|
+
def process_lines_with_linenums(content)
|
|
13
|
+
lines = content.each_line.to_a
|
|
14
|
+
current_line_num = 1
|
|
15
|
+
rows = []
|
|
16
|
+
|
|
17
|
+
lines.each do |line|
|
|
18
|
+
# Process the line
|
|
19
|
+
processed_html = process_line(line)
|
|
20
|
+
|
|
21
|
+
# Check if this is a table line
|
|
22
|
+
is_table_line = line_is_table?(line)
|
|
23
|
+
|
|
24
|
+
# create table row
|
|
25
|
+
rows << create_table_row(current_line_num, processed_html, is_table_line)
|
|
26
|
+
current_line_num += 1
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
%(<div class="ohhighmark"><table class="highlight"><tbody>#{rows.join}</tbody></table></div>)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def process_line(line)
|
|
33
|
+
if fence_rule&.regex&.match?(line)
|
|
34
|
+
@in_fence = !@in_fence
|
|
35
|
+
return wrap("md-fence", line)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
return wrap("md-codeblock", line) if @in_fence
|
|
39
|
+
|
|
40
|
+
tokens = @tokenizer.tokenize(line) # block and inline tokens
|
|
41
|
+
@renderer.render(tokens)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def fence_rule
|
|
47
|
+
@ruleset.block.find { |r| r.name == :fence }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def wrap(klass, text)
|
|
51
|
+
%(<span class="#{klass}">#{CGI.escapeHTML(text)}</span>)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Checks if a line is part of a markdown table
|
|
55
|
+
def line_is_table?(line)
|
|
56
|
+
# Check against table rules
|
|
57
|
+
table_dash_rule = @ruleset.inline.find { |r| r.name == :table_dash }
|
|
58
|
+
table_syntax_rule = @ruleset.inline.find { |r| r.name == :table_syntax }
|
|
59
|
+
|
|
60
|
+
(table_dash_rule&.regex&.match?(line) || table_syntax_rule&.regex&.match?(line))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Creates a single table row with line number and code cells
|
|
64
|
+
def create_table_row(line_num, code_html, is_table_line = false)
|
|
65
|
+
line_num_display = line_num.to_s
|
|
66
|
+
|
|
67
|
+
# Only include data-line-number attribute if there's an actual line number
|
|
68
|
+
data_attr = line_num_display.empty? ? '' : %( data-line-number="#{line_num}")
|
|
69
|
+
|
|
70
|
+
# Add table-line class if this is a table row
|
|
71
|
+
tr_class = is_table_line ? ' class="table-line"' : ''
|
|
72
|
+
|
|
73
|
+
%(<tr#{tr_class}#{data_attr}><td class="blob-num">#{line_num_display}</td><td class="blob-code">#{code_html}</td></tr>)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module OhHighMark
|
|
2
|
+
module MarkdownRules
|
|
3
|
+
def self.apply(ruleset)
|
|
4
|
+
ruleset.extend(DSL)
|
|
5
|
+
|
|
6
|
+
ruleset.instance_eval do
|
|
7
|
+
# Block rules
|
|
8
|
+
define_block :fence, /^```/
|
|
9
|
+
# Heading rules for levels 1-6
|
|
10
|
+
(1..6).each do |level|
|
|
11
|
+
define_block "h#{level}".to_sym,
|
|
12
|
+
/^#{'#' * level}\s+.+$/ ,
|
|
13
|
+
class: "md-h#{level}"
|
|
14
|
+
end
|
|
15
|
+
# HR
|
|
16
|
+
define_block :hr, /^---$/
|
|
17
|
+
# Lists
|
|
18
|
+
define_block :list_item, /^(\s*)([-*+])(\s+.+)$/
|
|
19
|
+
|
|
20
|
+
# Inline rules - order matters! More specific patterns first
|
|
21
|
+
define_inline :code, /`([^`]+)`/
|
|
22
|
+
# Emphasis (must be checked before bold to handle ***text***)
|
|
23
|
+
define_inline :em, /\*\*\*(.+?)\*\*\*/
|
|
24
|
+
define_inline :bold, /\*\*(.+?)\*\*/
|
|
25
|
+
define_inline :italic, /\_(.+?)\_/
|
|
26
|
+
define_inline :strikethrough, /~~(.+?)~~/
|
|
27
|
+
define_inline :autolink, /<(https?:\/\/[^\s>]+)>/
|
|
28
|
+
# Escaped characters - before other patterns that might match them
|
|
29
|
+
define_inline :escape, /\\([\*\[\]\\])/
|
|
30
|
+
define_inline :quote, /\"(.+?)\"/
|
|
31
|
+
define_inline :link, /\[(.+?)\]\((.+?)\)/
|
|
32
|
+
# Table separator line (eg: | ---- | ------ |)
|
|
33
|
+
define_inline :table_dash, /^\|(?:\s*-+\s*\|)+\s*$/
|
|
34
|
+
# Table Column/header content: | something |
|
|
35
|
+
define_inline :table_syntax, /^\|.*?\|.*$/
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require "cgi"
|
|
2
|
+
|
|
3
|
+
module OhHighMark
|
|
4
|
+
class Renderer
|
|
5
|
+
def render(tokens)
|
|
6
|
+
tokens.map { |t| render_token(t) }.join
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def render_token(token)
|
|
12
|
+
case token.type
|
|
13
|
+
when :text
|
|
14
|
+
escape(token.raw)
|
|
15
|
+
when :escape
|
|
16
|
+
# For escaped characters, just render the character without the backslash
|
|
17
|
+
token.raw[1..-1]
|
|
18
|
+
when :autolink
|
|
19
|
+
wrap_autolink(token)
|
|
20
|
+
when :link, :list_item, :table_syntax, :table_dash
|
|
21
|
+
send("wrap_#{token.type}", token)
|
|
22
|
+
else
|
|
23
|
+
wrap("md-#{token.type}", token.raw)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def escape(text)
|
|
28
|
+
CGI.escapeHTML(text)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def wrap(klass, text)
|
|
32
|
+
%(<span class="#{klass}">#{escape(text)}</span>)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def wrap_table_syntax(token)
|
|
36
|
+
token.raw.gsub(/\|/) do |pipe|
|
|
37
|
+
wrap("md-table-pipe", pipe)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def wrap_table_dash(token)
|
|
42
|
+
wrap("md-table-dash", token.raw)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def wrap_list_item(token)
|
|
46
|
+
indent, flag = [
|
|
47
|
+
token.meta[:indent],
|
|
48
|
+
token.meta[:flag]
|
|
49
|
+
].map(&method(:escape))
|
|
50
|
+
|
|
51
|
+
# Render inline tokens in the rest content
|
|
52
|
+
rest_html = if token.meta[:rest_tokens] && !token.meta[:rest_tokens].empty?
|
|
53
|
+
token.meta[:rest_tokens].map { |t| render_token(t) }.join
|
|
54
|
+
else
|
|
55
|
+
escape(token.meta[:rest])
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
%(#{indent}<span class="md-li">#{flag}</span>#{rest_html})
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def wrap_link(token)
|
|
62
|
+
raw, url = [
|
|
63
|
+
token.raw,
|
|
64
|
+
token.url
|
|
65
|
+
].map(&method(:escape))
|
|
66
|
+
|
|
67
|
+
%(<a href="#{url}" target="_blank" class="md-link">#{raw}</a>)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def wrap_autolink(token)
|
|
71
|
+
raw, url = [
|
|
72
|
+
token.raw,
|
|
73
|
+
token.url
|
|
74
|
+
].map(&method(:escape))
|
|
75
|
+
|
|
76
|
+
%(<a href="#{url}" target="_blank" class="md-autolink">#{raw}</a>)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module OhHighMark
|
|
2
|
+
class RuleSet
|
|
3
|
+
attr_reader :rules
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
@rules = []
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Public readers for consumers
|
|
10
|
+
def inline
|
|
11
|
+
@rules.select { |r| r.kind == :inline }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def block
|
|
15
|
+
@rules.select { |r| r.kind == :block }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Internal method used by DSL
|
|
19
|
+
def add(name, regex, kind:, **options)
|
|
20
|
+
@rules << Rule.new(name: name, regex: regex, kind: kind, options: options)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module OhHighMark
|
|
2
|
+
Token = Struct.new(:type, :raw, :meta, keyword_init: true) do
|
|
3
|
+
# Convenience accessors
|
|
4
|
+
def url
|
|
5
|
+
meta && meta[:url]
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def flag
|
|
9
|
+
meta && meta[:flag]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def rest
|
|
13
|
+
meta && meta[:rest]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def indent
|
|
17
|
+
meta && meta[:indent]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
module OhHighMark
|
|
2
|
+
class Tokenizer
|
|
3
|
+
def initialize(rules)
|
|
4
|
+
@block_rules = rules.select { |r| r.kind == :block }
|
|
5
|
+
@inline_rules = rules.select { |r| r.kind == :inline }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def tokenize_line(line)
|
|
9
|
+
tokens = []
|
|
10
|
+
rest = line.dup
|
|
11
|
+
|
|
12
|
+
#block rules first
|
|
13
|
+
@block_rules.each do |rule|
|
|
14
|
+
if (m = rule.regex.match(line))
|
|
15
|
+
tokens << build_token(rule, m)
|
|
16
|
+
return tokens # whole line consumed by block
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# process inline rules
|
|
21
|
+
rest = line.dup
|
|
22
|
+
until rest.empty?
|
|
23
|
+
matched = false
|
|
24
|
+
|
|
25
|
+
@inline_rules.each do |rule|
|
|
26
|
+
if (m = rule.regex.match(rest))
|
|
27
|
+
# emit text before match
|
|
28
|
+
if m.begin(0) > 0
|
|
29
|
+
tokens << Token.new(type: :text, raw: rest[0...m.begin(0)])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# emit matched token
|
|
33
|
+
tokens << build_token(rule, m)
|
|
34
|
+
|
|
35
|
+
# slice matched portion
|
|
36
|
+
rest = rest[m.end(0)..] || ""
|
|
37
|
+
matched = true
|
|
38
|
+
break
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
unless matched
|
|
43
|
+
tokens << Token.new(type: :text, raw: rest)
|
|
44
|
+
break
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
tokens
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def tokenize(content)
|
|
52
|
+
content.each_line.flat_map { |line| tokenize_line(line.chomp) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def build_token(rule, match)
|
|
58
|
+
case rule.name
|
|
59
|
+
when :link
|
|
60
|
+
Token.new(
|
|
61
|
+
type: :link,
|
|
62
|
+
raw: match[0], # "[text](url)"
|
|
63
|
+
meta: { url: match[2] }
|
|
64
|
+
)
|
|
65
|
+
when :autolink
|
|
66
|
+
Token.new(
|
|
67
|
+
type: :autolink,
|
|
68
|
+
raw: match[0], # "<http://url>"
|
|
69
|
+
meta: { url: match[1] }
|
|
70
|
+
)
|
|
71
|
+
when :escape
|
|
72
|
+
Token.new(
|
|
73
|
+
type: :escape,
|
|
74
|
+
raw: match[0]
|
|
75
|
+
)
|
|
76
|
+
when :list_item
|
|
77
|
+
Token.new(
|
|
78
|
+
type: rule.name,
|
|
79
|
+
raw: match[0],
|
|
80
|
+
meta: {
|
|
81
|
+
indent: match[1],
|
|
82
|
+
flag: match[2],
|
|
83
|
+
rest: match[3],
|
|
84
|
+
rest_tokens: tokenize_inline(match[3]) # Process inline tokens in rest
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
else
|
|
88
|
+
Token.new(
|
|
89
|
+
type: rule.name,
|
|
90
|
+
raw: match[0]
|
|
91
|
+
)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Process inline rules on a text fragment
|
|
96
|
+
def tokenize_inline(text)
|
|
97
|
+
tokens = []
|
|
98
|
+
rest = text.dup
|
|
99
|
+
|
|
100
|
+
until rest.empty?
|
|
101
|
+
matched = false
|
|
102
|
+
|
|
103
|
+
@inline_rules.each do |rule|
|
|
104
|
+
if (m = rule.regex.match(rest))
|
|
105
|
+
# emit text before match
|
|
106
|
+
if m.begin(0) > 0
|
|
107
|
+
tokens << Token.new(type: :text, raw: rest[0...m.begin(0)])
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# emit matched token
|
|
111
|
+
tokens << build_token(rule, m)
|
|
112
|
+
|
|
113
|
+
# slice matched portion
|
|
114
|
+
rest = rest[m.end(0)..] || ""
|
|
115
|
+
matched = true
|
|
116
|
+
break
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
unless matched
|
|
121
|
+
tokens << Token.new(type: :text, raw: rest)
|
|
122
|
+
break
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
tokens
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
data/lib/ohhighmark.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require_relative "ohhighmark/version"
|
|
2
|
+
require_relative "ohhighmark/rule"
|
|
3
|
+
require_relative "ohhighmark/ruleset"
|
|
4
|
+
require_relative "ohhighmark/dsl"
|
|
5
|
+
require_relative "ohhighmark/token"
|
|
6
|
+
require_relative "ohhighmark/tokenizer"
|
|
7
|
+
require_relative "ohhighmark/renderer"
|
|
8
|
+
require_relative "ohhighmark/markdown_rules"
|
|
9
|
+
require_relative "ohhighmark/highlighter"
|
|
10
|
+
require_relative "ohhighmark/helper"
|
|
11
|
+
|
|
12
|
+
module OhHighMark
|
|
13
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ohhighmark
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- David Knight
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rspec
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '3.12'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '3.12'
|
|
26
|
+
description: OhHighMark is a Ruby library for syntax highlighting of markdown code,
|
|
27
|
+
providing colorful and readable markdown syntax visualization.
|
|
28
|
+
email:
|
|
29
|
+
- david+github@knight.sh
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- LICENSE
|
|
35
|
+
- README.md
|
|
36
|
+
- app/assets/stylesheets/ohhighmark/ohhighmark.css
|
|
37
|
+
- lib/ohhighmark.rb
|
|
38
|
+
- lib/ohhighmark/dsl.rb
|
|
39
|
+
- lib/ohhighmark/helper.rb
|
|
40
|
+
- lib/ohhighmark/highlighter.rb
|
|
41
|
+
- lib/ohhighmark/markdown_rules.rb
|
|
42
|
+
- lib/ohhighmark/renderer.rb
|
|
43
|
+
- lib/ohhighmark/rule.rb
|
|
44
|
+
- lib/ohhighmark/ruleset.rb
|
|
45
|
+
- lib/ohhighmark/token.rb
|
|
46
|
+
- lib/ohhighmark/tokenizer.rb
|
|
47
|
+
- lib/ohhighmark/version.rb
|
|
48
|
+
homepage: https://github.com/viacoffee/ohhighmark
|
|
49
|
+
licenses:
|
|
50
|
+
- MIT
|
|
51
|
+
metadata:
|
|
52
|
+
homepage_uri: https://github.com/viacoffee/ohhighmark
|
|
53
|
+
source_code_uri: https://github.com/viacoffee/ohhighmark
|
|
54
|
+
rdoc_options: []
|
|
55
|
+
require_paths:
|
|
56
|
+
- lib
|
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 3.0.0
|
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '0'
|
|
67
|
+
requirements: []
|
|
68
|
+
rubygems_version: 3.6.9
|
|
69
|
+
specification_version: 4
|
|
70
|
+
summary: A markdown syntax highlighter
|
|
71
|
+
test_files: []
|