Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions javascript/packages/highlighter/src/color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ export const colorize = (
return text
}

export const hyperlink = (text: string, url: string): string => {
if (process.env.NO_COLOR !== undefined) {
return text
}

return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`
}

export const severityColor = (severity: DiagnosticSeverity): Color => {
switch (severity) {
case "error": return "brightRed"
Expand Down
8 changes: 6 additions & 2 deletions javascript/packages/highlighter/src/diagnostic-renderer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { colorize, severityColor, ANSI_REGEX, ANSI_REGEX_START, ANSI_ESCAPE } from "./color.js"
import { colorize, hyperlink, severityColor, ANSI_REGEX, ANSI_REGEX_START, ANSI_ESCAPE } from "./color.js"
import { applyDimToStyledText } from "./util.js"
import { LineWrapper } from "./line-wrapper.js"
import { GUTTER_WIDTH, MIN_CONTENT_WIDTH } from "./gutter-config.js"
Expand All @@ -13,6 +13,7 @@ export interface DiagnosticRenderOptions {
wrapLines?: boolean
maxWidth?: number
truncateLines?: boolean
codeUrl?: string
}

export class DiagnosticRenderer {
Expand Down Expand Up @@ -142,9 +143,12 @@ export class DiagnosticRenderer {
const shouldTruncate = truncateLines
const fileHeader = `${colorize(path, "cyan")}:${colorize(`${diagnostic.location.start.line}:${diagnostic.location.start.column}`, "cyan")}`

const { codeUrl } = options

const text = diagnostic.severity
const color = severityColor(diagnostic.severity)
const diagnosticId = colorize(diagnostic.code || "-", "gray")
const diagnosticIdText = colorize(diagnostic.code || "-", "gray")
const diagnosticId = codeUrl ? hyperlink(diagnosticIdText, codeUrl) : diagnosticIdText

const originalLines = content.split("\n")
const targetLineNumber = diagnostic.location.start.line
Expand Down
6 changes: 6 additions & 0 deletions javascript/packages/highlighter/src/highlighter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface HighlightOptions {
wrapLines?: boolean
maxWidth?: number
truncateLines?: boolean
codeUrlBuilder?: (code: string) => string
}

export interface HighlightDiagnosticOptions {
Expand All @@ -29,6 +30,7 @@ export interface HighlightDiagnosticOptions {
wrapLines?: boolean
maxWidth?: number
truncateLines?: boolean
codeUrl?: string
}

export class Highlighter {
Expand Down Expand Up @@ -97,19 +99,22 @@ export class Highlighter {
wrapLines = true,
maxWidth = LineWrapper.getTerminalWidth(),
truncateLines = false,
codeUrlBuilder,
} = options

// Case 1: Split diagnostics - render each diagnostic individually
if (diagnostics.length > 0 && splitDiagnostics) {
const results: string[] = []
for (let i = 0; i < diagnostics.length; i++) {
const diagnostic = diagnostics[i]
const codeUrl = codeUrlBuilder && diagnostic.code ? codeUrlBuilder(diagnostic.code) : undefined
const result = this.highlightDiagnostic(path, diagnostic, content, {
contextLines,
showLineNumbers,
wrapLines,
maxWidth,
truncateLines,
codeUrl,
})

results.push(result)
Expand Down Expand Up @@ -141,6 +146,7 @@ export class Highlighter {
wrapLines,
maxWidth,
truncateLines,
codeUrlBuilder,
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { colorize, severityColor } from "./color.js"
import { colorize, hyperlink, severityColor } from "./color.js"
import { TextFormatter } from "./text-formatter.js"
import { LineWrapper } from "./line-wrapper.js"
import { GUTTER_WIDTH, MIN_CONTENT_WIDTH } from "./gutter-config.js"
Expand Down Expand Up @@ -37,6 +37,7 @@ export class InlineDiagnosticRenderer {
wrapLines = false,
maxWidth = LineWrapper.getTerminalWidth(),
truncateLines = false,
codeUrlBuilder?: (code: string) => string,
): string {
const highlightedContent = this.syntaxRenderer.highlight(content)

Expand Down Expand Up @@ -166,7 +167,8 @@ export class InlineDiagnosticRenderer {
output += `${pointerPrefix}${pointerSpacing}${pointer}\n`

const severityText = this.getSeverityText(diagnostic.severity)
const diagnosticId = colorize(diagnostic.code || "-", "gray")
const diagnosticIdText = colorize(diagnostic.code || "-", "gray")
const diagnosticId = codeUrlBuilder && diagnostic.code ? hyperlink(diagnosticIdText, codeUrlBuilder(diagnostic.code)) : diagnosticIdText
const highlightedMessage = TextFormatter.highlightBackticks(diagnostic.message)
const diagnosticText = `[${severityText}] ${highlightedMessage} (${diagnosticId})`
const dimmedDiagnosticText =
Expand All @@ -183,7 +185,8 @@ export class InlineDiagnosticRenderer {
output += `${pointerSpacing}${pointer}\n`

const severityText = this.getSeverityText(diagnostic.severity)
const diagnosticId = colorize(diagnostic.code || "-", "gray")
const diagnosticIdText = colorize(diagnostic.code || "-", "gray")
const diagnosticId = codeUrlBuilder && diagnostic.code ? hyperlink(diagnosticIdText, codeUrlBuilder(diagnostic.code)) : diagnosticIdText
const highlightedMessage = TextFormatter.highlightBackticks(diagnostic.message)
const diagnosticText = `[${severityText}] ${highlightedMessage} (${diagnosticId})`
const dimmedDiagnosticText =
Expand Down
4 changes: 2 additions & 2 deletions javascript/packages/language-server/src/linter_service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Diagnostic, Range, Position, CodeDescription, Connection } from "vscode-languageserver/node"
import { TextDocument } from "vscode-languageserver-textdocument"

import { Linter, rules, type RuleClass } from "@herb-tools/linter"
import { Linter, rules, ruleDocumentationUrl, type RuleClass } from "@herb-tools/linter"
import { loadCustomRules as loadCustomRulesFromFs } from "@herb-tools/linter/loader"
import { Herb } from "@herb-tools/node-wasm"
import { Config } from "@herb-tools/config"
Expand Down Expand Up @@ -158,7 +158,7 @@ export class LinterService {
const codeDescription: CodeDescription = {
href: customRulePath
? `file://${customRulePath}`
: `https://herb-tools.dev/linter/rules/${offense.rule}`
: ruleDocumentationUrl(offense.rule)
}

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { colorize, Highlighter, type ThemeInput, DEFAULT_THEME } from "@herb-too

import { BaseFormatter } from "./base-formatter.js"
import { LineWrapper } from "@herb-tools/highlighter"
import { ruleDocumentationUrl } from "../../urls.js"

import type { Diagnostic } from "@herb-tools/core"
import type { ProcessedFile } from "../file-processor.js"
Expand Down Expand Up @@ -44,7 +45,8 @@ export class DetailedFormatter extends BaseFormatter {
splitDiagnostics: true,
contextLines: 2,
wrapLines: this.wrapLines,
truncateLines: this.truncateLines
truncateLines: this.truncateLines,
codeUrlBuilder: ruleDocumentationUrl,
})

console.log(`\n${highlighted}`)
Expand All @@ -63,10 +65,12 @@ export class DetailedFormatter extends BaseFormatter {
}
}

const codeUrl = modifiedOffense.code ? ruleDocumentationUrl(modifiedOffense.code) : undefined
const formatted = this.highlighter.highlightDiagnostic(filename, modifiedOffense, content, {
contextLines: 2,
wrapLines: this.wrapLines,
truncateLines: this.truncateLines
truncateLines: this.truncateLines,
codeUrl,
})
console.log(`\n${formatted}`)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { colorize } from "@herb-tools/highlighter"
import { colorize, hyperlink } from "@herb-tools/highlighter"

import { BaseFormatter } from "./base-formatter.js"
import { ruleDocumentationUrl } from "../../urls.js"

import type { Diagnostic } from "@herb-tools/core"
import type { ProcessedFile } from "../file-processor.js"
Expand Down Expand Up @@ -29,7 +30,8 @@ export class SimpleFormatter extends BaseFormatter {
for (const offense of offenses) {
const isError = offense.severity === "error"
const severity = isError ? colorize("✗", "brightRed") : colorize("⚠", "brightYellow")
const rule = colorize(`(${offense.code})`, "blue")
const ruleText = colorize(`(${offense.code})`, "blue")
const rule = offense.code ? hyperlink(ruleText, ruleDocumentationUrl(offense.code)) : ruleText
const locationString = `${offense.location.start.line}:${offense.location.start.column}`
const paddedLocation = locationString.padEnd(4)

Expand All @@ -44,7 +46,8 @@ export class SimpleFormatter extends BaseFormatter {
for (const { offense, autocorrectable } of processedFiles) {
const isError = offense.severity === "error"
const severity = isError ? colorize("✗", "brightRed") : colorize("⚠", "brightYellow")
const rule = colorize(`(${offense.code})`, "blue")
const ruleText = colorize(`(${offense.code})`, "blue")
const rule = offense.code ? hyperlink(ruleText, ruleDocumentationUrl(offense.code)) : ruleText
const locationString = `${offense.location.start.line}:${offense.location.start.column}`
const paddedLocation = locationString.padEnd(4)
const correctable = autocorrectable ? colorize(colorize(" [Correctable]", "green"), "bold") : ""
Expand Down
8 changes: 6 additions & 2 deletions javascript/packages/linter/src/cli/summary-reporter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { colorize } from "@herb-tools/highlighter"
import { colorize, hyperlink } from "@herb-tools/highlighter"

import { ruleDocumentationUrl } from "../urls.js"

export interface SummaryData {
files: string[]
Expand Down Expand Up @@ -144,7 +146,9 @@ export class SummaryReporter {
for (const [rule, data] of displayedRules) {
const fileCount = data.files.size
const countText = `(${data.count} ${this.pluralize(data.count, "offense")} in ${fileCount} ${this.pluralize(fileCount, "file")})`
console.log(` ${colorize(rule, "gray")} ${colorize(colorize(countText, "gray"), "dim")}`)
const ruleText = colorize(rule, "gray")
const ruleLink = hyperlink(ruleText, ruleDocumentationUrl(rule))
console.log(` ${ruleLink} ${colorize(colorize(countText, "gray"), "dim")}`)
}

if (remainingRules.length > 0) {
Expand Down
1 change: 1 addition & 0 deletions javascript/packages/linter/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./linter.js"
export * from "./rules/index.js"
export * from "./types.js"
export * from "./urls.js"

export { rules } from "./rules.js"
5 changes: 5 additions & 0 deletions javascript/packages/linter/src/urls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const DOCS_BASE_URL = "https://herb-tools.dev/linter/rules"

export function ruleDocumentationUrl(ruleId: string): string {
return `${DOCS_BASE_URL}/${ruleId}`
}
Loading