Skip to content

Commit 6bec80e

Browse files
authored
Playground: Add option for Formatter maxLineLength (#1101)
<img width="1692" height="816" alt="CleanShot 2026-01-23 at 15 59 00@2x" src="https://github.com/user-attachments/assets/f04765e4-f35a-4d59-9505-e845356998c8" />
1 parent 93d69da commit 6bec80e

File tree

3 files changed

+92
-4
lines changed

3 files changed

+92
-4
lines changed

playground/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,19 @@
419419
<span class="text-sm text-gray-300 mr-2">Document Status:</span>
420420
<span data-playground-target="formatVerification" class="px-2 py-1 text-xs rounded font-medium"></span>
421421
</div>
422+
<div class="flex items-center gap-2">
423+
<label class="flex items-center gap-2 text-gray-300 text-sm">
424+
<span class="select-none">Max line length:</span>
425+
<input
426+
type="number"
427+
min="1"
428+
value="80"
429+
data-playground-target="formatterMaxLineLength"
430+
data-action="change->playground#onFormatterOptionChange"
431+
class="w-16 px-2 py-1 text-sm rounded border-gray-600 bg-gray-700 text-gray-200 focus:ring-green-500 focus:border-green-500"
432+
/>
433+
</label>
434+
</div>
422435
</div>
423436

424437
<div class="w-full h-full overflow-auto">

playground/src/analyze.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Linter } from "@herb-tools/linter"
55
import { IdentityPrinter } from "@herb-tools/printer"
66

77
import type { LintResult } from "@herb-tools/linter"
8+
import type { FormatOptions } from "@herb-tools/formatter"
89

910
async function safeExecute<T>(promise: Promise<T>): Promise<T> {
1011
try {
@@ -15,7 +16,7 @@ async function safeExecute<T>(promise: Promise<T>): Promise<T> {
1516
}
1617
}
1718

18-
export async function analyze(herb: HerbBackend, source: string, options: ParserOptions = {}, printerOptions: { ignoreErrors?: boolean } = {}) {
19+
export async function analyze(herb: HerbBackend, source: string, options: ParserOptions = {}, printerOptions: { ignoreErrors?: boolean } = {}, formatterOptions: FormatOptions = {}) {
1920
const startTime = performance.now()
2021

2122
const parseResult = await safeExecute<ParseResult>(
@@ -53,7 +54,7 @@ export async function analyze(herb: HerbBackend, source: string, options: Parser
5354
)
5455

5556
const formatted = await safeExecute<string>(
56-
new Promise((resolve) => resolve((new Formatter(herb, {})).format(source))),
57+
new Promise((resolve) => resolve((new Formatter(herb, formatterOptions)).format(source))),
5758
)
5859

5960
const printed = await safeExecute<string>(

playground/src/controllers/playground_controller.js

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export default class extends Controller {
6969
"printerVerification",
7070
"printerIgnoreErrors",
7171
"printerDiff",
72+
"formatterMaxLineLength",
7273
"printerDiffContent",
7374
"printerLegend",
7475
"shareButton",
@@ -114,6 +115,7 @@ export default class extends Controller {
114115
this.restoreActiveTab()
115116
this.restoreParserOptions()
116117
this.restorePrinterOptions()
118+
this.restoreFormatterOptions()
117119
this.inputTarget.focus()
118120
this.load()
119121

@@ -220,8 +222,10 @@ export default class extends Controller {
220222

221223
const options = this.getParserOptions()
222224
const printerOptions = this.getPrinterOptions()
225+
const formatterOptions = this.getFormatterOptions()
223226
this.setOptionsInURL(options)
224227
this.setPrinterOptionsInURL(printerOptions)
228+
this.setFormatterOptionsInURL(formatterOptions)
225229
}
226230

227231
async insert(event) {
@@ -432,12 +436,26 @@ export default class extends Controller {
432436
}
433437
}
434438

439+
restoreFormatterOptions() {
440+
const formatterOptionsFromURL = this.getFormatterOptionsFromURL()
441+
442+
if (Object.keys(formatterOptionsFromURL).length > 0) {
443+
this.setFormatterOptions(formatterOptionsFromURL)
444+
}
445+
}
446+
435447
setPrinterOptions(printerOptions) {
436448
if (this.hasPrinterIgnoreErrorsTarget && printerOptions.hasOwnProperty('ignoreErrors')) {
437449
this.printerIgnoreErrorsTarget.checked = Boolean(printerOptions.ignoreErrors)
438450
}
439451
}
440452

453+
setFormatterOptions(formatterOptions) {
454+
if (this.hasFormatterMaxLineLengthTarget && formatterOptions.hasOwnProperty('maxLineLength')) {
455+
this.formatterMaxLineLengthTarget.value = formatterOptions.maxLineLength
456+
}
457+
}
458+
441459
isValidTab(tab) {
442460
const validTabs = ['parse', 'lex', 'ruby', 'html', 'format', 'printer', 'diagnostics', 'full']
443461
return validTabs.includes(tab)
@@ -603,7 +621,8 @@ export default class extends Controller {
603621

604622
try {
605623
const value = this.editor ? this.editor.getValue() : this.inputTarget.value
606-
const result = await analyze(Herb, value)
624+
const formatterOptions = this.getFormatterOptions()
625+
const result = await analyze(Herb, value, {}, {}, formatterOptions)
607626

608627
if (result.formatted) {
609628
if (this.editor) {
@@ -684,7 +703,8 @@ export default class extends Controller {
684703
const value = this.editor ? this.editor.getValue() : this.inputTarget.value
685704
const options = this.getParserOptions()
686705
const printerOptions = this.getPrinterOptions()
687-
const result = await analyze(Herb, value, options, printerOptions)
706+
const formatterOptions = this.getFormatterOptions()
707+
const result = await analyze(Herb, value, options, printerOptions, formatterOptions)
688708

689709
this.updatePosition(1, 0, value.length)
690710

@@ -1021,6 +1041,11 @@ export default class extends Controller {
10211041
this.analyze()
10221042
}
10231043

1044+
onFormatterOptionChange(event) {
1045+
this.updateURL()
1046+
this.analyze()
1047+
}
1048+
10241049
getPrinterOptions() {
10251050
const options = {}
10261051
if (this.hasPrinterIgnoreErrorsTarget) {
@@ -1029,6 +1054,20 @@ export default class extends Controller {
10291054
return options
10301055
}
10311056

1057+
getFormatterOptions() {
1058+
const options = {}
1059+
1060+
if (this.hasFormatterMaxLineLengthTarget) {
1061+
const value = parseInt(this.formatterMaxLineLengthTarget.value, 10)
1062+
1063+
if (!isNaN(value) && value > 0) {
1064+
options.maxLineLength = value
1065+
}
1066+
}
1067+
1068+
return options
1069+
}
1070+
10321071
getOptionsFromURL() {
10331072
const urlParams = new URLSearchParams(window.parent.location.search)
10341073
const optionsString = urlParams.get('options')
@@ -1099,6 +1138,41 @@ export default class extends Controller {
10991138
return {}
11001139
}
11011140

1141+
setFormatterOptionsInURL(formatterOptions) {
1142+
const url = new URL(window.parent.location)
1143+
1144+
const nonDefaultFormatterOptions = {}
1145+
1146+
Object.keys(formatterOptions).forEach(key => {
1147+
if (key === 'maxLineLength' && formatterOptions[key] !== 80) {
1148+
nonDefaultFormatterOptions[key] = formatterOptions[key]
1149+
}
1150+
})
1151+
1152+
if (Object.keys(nonDefaultFormatterOptions).length > 0) {
1153+
url.searchParams.set('formatterOptions', JSON.stringify(nonDefaultFormatterOptions))
1154+
} else {
1155+
url.searchParams.delete('formatterOptions')
1156+
}
1157+
1158+
window.parent.history.replaceState({}, '', url)
1159+
}
1160+
1161+
getFormatterOptionsFromURL() {
1162+
const urlParams = new URLSearchParams(window.parent.location.search)
1163+
const formatterOptionsString = urlParams.get('formatterOptions')
1164+
1165+
if (formatterOptionsString) {
1166+
try {
1167+
return JSON.parse(decodeURIComponent(formatterOptionsString))
1168+
} catch (e) {
1169+
console.warn('Failed to parse formatter options from URL:', e)
1170+
}
1171+
}
1172+
1173+
return {}
1174+
}
1175+
11021176
restoreDiagnosticsFilter() {
11031177
const urlParams = new URLSearchParams(window.parent.location.search)
11041178
const filterParam = urlParams.get('diagnosticsFilter')

0 commit comments

Comments
 (0)