When formatting string markup, format each individual text element with all the styling and then reset to avoid conflicts
MODIFY
packages/@romejs/cli-diagnostics/buildMessageCodeFrame.ts
packages/@romejs/cli-diagnostics/buildMessageCodeFrame.ts
@@ -26,7 +26,7 @@ import {
coerce0To1,
number1Neg1,
} from '@romejs/ob1';
-import {stripMarkupTags} from '@romejs/string-markup';
+import {markupToPlainText} from '@romejs/string-markup';
export default function buildMessageCodeFrame(
sourceText: string,
@@ -157,7 +157,7 @@ export default function buildMessageCodeFrame(
// If what the marker is highlighting equals the marker message then it's redundant so don't show the message
if (markerMessage !== '') {
const text = sourceText.slice(get0(start.index), get0(end.index));
- if (cleanEquivalentString(text) === cleanEquivalentString(stripMarkupTags(
+ if (cleanEquivalentString(text) === cleanEquivalentString(markupToPlainText(
markerMessage,
))) {
markerMessage = '';

MODIFY
packages/@romejs/cli-reporter/Reporter.ts
packages/@romejs/cli-reporter/Reporter.ts
import {
MarkupFormatOptions,
markupToAnsi,
- stripMarkupTags,
+ markupToPlainText,
markupTag,
ansiEscapes,
stripAnsi,
@@ -823,7 +823,7 @@ export default class Reporter {
//# LOG
stripMarkup(str: string) {
- return stripMarkupTags(str, this.markupOptions);
+ return markupToPlainText(str, this.markupOptions);
}
markupifyLength(stream: ReporterStream, str: string): number {
@@ -845,7 +845,7 @@ export default class Reporter {
case 'html':
case 'none':
// TODO
- return stripMarkupTags(str);
+ return markupToPlainText(str);
case 'markup':
return str;

MODIFY
packages/@romejs/core/master/lsp/LSPServer.ts
packages/@romejs/core/master/lsp/LSPServer.ts
@@ -24,7 +24,7 @@ import {
import {Diagnostics, DiagnosticLocation} from '@romejs/diagnostics';
import {Position} from '@romejs/parser-core';
import {coerce1To0, number0, Number0, inc} from '@romejs/ob1';
-import {stripMarkupTags} from '@romejs/string-markup';
+import {markupToPlainText} from '@romejs/string-markup';
import {
PartialMasterQueryRequest,
MasterQueryResponse,
@@ -120,7 +120,7 @@ function convertDiagnosticsToLSP(
);
if (abs !== undefined) {
relatedInformation.push({
- message: stripMarkupTags(item.message),
+ message: markupToPlainText(item.message),
location: {
uri: `file://${abs.join()}`,
range: convertDiagnosticLocationToLSPRange(nextItem.location),
@@ -134,7 +134,7 @@ function convertDiagnosticsToLSP(
lspDiagnostics.push({
severity: 1,
range: convertDiagnosticLocationToLSPRange(location),
- message: stripMarkupTags(description.message.value),
+ message: markupToPlainText(description.message.value),
code: description.category,
source: 'rome',
relatedInformation,

MODIFY
packages/@romejs/string-markup/format.ts
packages/@romejs/string-markup/format.ts
* LICENSE file in the root directory of this source tree.
*/
-import {Children, TagAttributes, MarkupTagName} from './types';
+import {Children, TagAttributes, MarkupTagName, TagNode} from './types';
import {parseMarkup} from './parse';
import {
humanizeNumber,
@@ -16,12 +16,6 @@ import {formatAnsi, ansiPad} from './ansi';
import {AbsoluteFilePath, createUnknownFilePath} from '@romejs/path';
import {escapeMarkup} from './escape';
-type FormatReduceCallback = (
- name: MarkupTagName,
- attributes: TagAttributes,
- value: string,
-) => string;
-
export type MarkupFormatFilenameNormalizer = (filename: string) => string;
export type MarkupFormatFilenameHumanizer = (
@@ -37,45 +31,88 @@ export type MarkupFormatOptions = {
const EMPTY_ATTRIBUTES: Map<string, string> = new Map();
type FormatReduceOptions = {
- escapeText: boolean;
+ ancestry: Array<TagNode>;
+ formatText: (value: string, tags: Array<TagNode>) => string;
+ formatTag?: (
+ name: MarkupTagName,
+ attributes: TagAttributes,
+ value: string,
+ ) => string;
};
-function formatReduceFromInput(
- input: string,
+function formatReduceFromInput(input: string, opts: FormatReduceOptions): string {
+ return formatReduceFromChildren(parseMarkup(input), opts);
+}
+
+// Ignore nested bare tags eg. <emphasis><emphasis>foo</emphasis></emphasis>
+function shouldIgnoreTag(
+ tagName: MarkupTagName,
opts: FormatReduceOptions,
- callback: FormatReduceCallback,
-): string {
- return formatReduceFromChildren(parseMarkup(input), opts, callback);
+): boolean {
+ for (const tag of opts.ancestry) {
+ if (tag.attributes.size === 0 && tag.name === tagName) {
+ return true;
+ }
+ }
+ return false;
}
function formatReduceFromChildren(
children: Children,
opts: FormatReduceOptions,
- callback: FormatReduceCallback,
): string {
+ const {formatTag, formatText} = opts;
+
let buff = '';
for (const child of children) {
if (child.type === 'Text') {
- let text = child.value;
- if (opts.escapeText) {
- text = escapeMarkup(text);
- }
- buff += text;
+ buff += formatText(child.value, opts.ancestry);
} else if (child.type === 'Tag') {
const {attributes} = child;
- let res = callback(child.name, attributes, formatReduceFromChildren(
- child.children,
+ let emphasis = attributes.get('emphasis') === 'true' && !shouldIgnoreTag(
+ 'emphasis',
+ opts,
+ );
+ attributes.delete('emphasis');
+
+ let dim = attributes.get('dim') === 'true' &&
+ !shouldIgnoreTag('dim', opts);
+ attributes.delete('dim');
+
+ const applyTags: Array<TagNode> = [];
+ if (attributes.size > 0 || attributes.size === 0 && !shouldIgnoreTag(
+ child.name,
opts,
- callback,
- ));
+ )) {
+ applyTags.push(child);
+ }
+ if (emphasis) {
+ applyTags.push({
+ type: 'Tag',
+ name: 'emphasis',
+ attributes: EMPTY_ATTRIBUTES,
+ children: [],
+ });
+ }
+ if (dim) {
+ applyTags.push({
+ type: 'Tag',
+ name: 'dim',
+ attributes: EMPTY_ATTRIBUTES,
+ children: [],
+ });
+ }
+
+ let res = formatReduceFromChildren(child.children, {
+ ...opts,
+ ancestry: [...opts.ancestry, ...applyTags],
+ });
- // Support some concise formatting
- if (attributes.get('emphasis') === 'true') {
- res = callback('emphasis', EMPTY_ATTRIBUTES, res);
+ if (formatTag !== undefined) {
+ for (const tag of applyTags) {
+ res = formatTag(tag.name, tag.attributes, res);
}
- if (attributes.get('dim') === 'true') {
- res = callback('dim', EMPTY_ATTRIBUTES, res);
}
buff += res;
@@ -164,15 +201,16 @@ function formatPad(attributes: TagAttributes, value: string) {
return ansiPad(left ? 'left' : 'right', value, count, char);
}
-export function stripMarkupTags(
+export function markupToPlainText(
input: string,
opts: MarkupFormatOptions = {},
): string {
- return formatReduceFromInput(input, {escapeText: false}, (
- tag,
- attributes,
- value,
- ) => {
+ return formatReduceFromInput(input, {
+ ancestry: [],
+ formatText: (text) => {
+ return text;
+ },
+ formatTag: (tag, attributes, value) => {
switch (tag) {
case 'filelink':
return formatFileLink(attributes, value, opts).text;
@@ -204,6 +242,7 @@ export function stripMarkupTags(
default:
return value;
}
+ },
});
}
@@ -211,11 +250,12 @@ export function normalizeMarkup(
input: string,
opts: MarkupFormatOptions = {},
): string {
- return formatReduceFromInput(input, {escapeText: true}, (
- tag,
- attributes,
- value,
- ) => {
+ return formatReduceFromInput(input, {
+ ancestry: [],
+ formatText: (text) => {
+ return escapeMarkup(text);
+ },
+ formatTag: (tag, attributes, value) => {
// Normalize filename of <filelink target>
if (tag === 'filelink') {
const {text, filename} = formatFileLink(attributes, value, opts);
@@ -234,9 +274,10 @@ export function normalizeMarkup(
if (value === '') {
return `${open} />`;
- }
-
+ } else {
return `${open}>${value}</${tag}>`;
+ }
+ },
});
}
@@ -244,12 +285,26 @@ export function markupToAnsi(
input: string,
opts: MarkupFormatOptions = {},
): string {
- return formatReduceFromInput(input, {escapeText: false}, (
- tag,
- attributes,
- value,
- ) => {
- switch (tag) {
+ return formatReduceFromInput(input, {
+ ancestry: [],
+ formatText: (value, tags) => {
+ // Format tags in reverse
+ for (let i = tags.length - 1; i >= 0; i--) {
+ const tag = tags[i];
+ value = ansiFormatTag(tag, value, opts);
+ }
+
+ return formatAnsi.reset(value);
+ },
+ });
+}
+
+function ansiFormatTag(
+ {name: tagName, attributes}: TagNode,
+ value: string,
+ opts: MarkupFormatOptions,
+): string {
+ switch (tagName) {
case 'hyperlink': {
let text = value;
let hyperlink = attributes.get('target');
@@ -402,7 +457,6 @@ export function markupToAnsi(
case 'command':
return '`' + formatAnsi.italic(value) + '`';
}
- });
}
export function humanizeMarkupFilename(

When formatting string markup, format each individual text element with all the styling and then reset to avoid conflicts