Bug 1832213 - Add pretty print button in StyleEditor. r=devtools-reviewers,fluent-reviewers,ochameau.

This adds a similar button to the one we have in the Debugger that users can
click on to pretty print the stylesheet.
We disable the button when the opened file is an original file, as we don't
support pretty printing non CSS files.

The current test for (automatic) pretty printing is updated to check that the
button has the expected states and behaves as it should.

Differential Revision: https://phabricator.services.mozilla.com/D189762
This commit is contained in:
Nicolas Chevobbe 2023-10-04 06:27:46 +00:00
Родитель a4b50e5230
Коммит 7aad4369ba
8 изменённых файлов: 177 добавлений и 25 удалений

Просмотреть файл

@ -47,3 +47,11 @@ styleeditor-stylesheet-rule-count =
[one] { $ruleCount } rule.
*[other] { $ruleCount } rules.
}
# Title for the pretty print button in the editor footer.
styleeditor-pretty-print-button =
.title = Pretty print style sheet
# Title for the pretty print button in the editor footer, when it's disabled
styleeditor-pretty-print-button-disabled =
.title = Can only pretty print CSS files

Просмотреть файл

@ -97,6 +97,7 @@ export class StyleEditorUI extends EventEmitter {
#optionsMenu;
#panelDoc;
#prefObserver;
#prettyPrintButton;
#root;
#seenSheets = new Map();
#shortcuts;
@ -265,6 +266,21 @@ export class StyleEditorUI extends EventEmitter {
eventListenersConfig
);
this.#prettyPrintButton = this.#root.querySelector(
".style-editor-prettyPrintButton"
);
this.#prettyPrintButton.addEventListener(
"click",
() => {
if (!this.selectedEditor) {
return;
}
this.selectedEditor.prettifySourceText();
},
eventListenersConfig
);
this.#root
.querySelector("#style-editor-options")
.addEventListener(
@ -1267,6 +1283,27 @@ export class StyleEditorUI extends EventEmitter {
this.handleSummaryVisibility(summary);
}
/**
* Update the pretty print button.
* The button will be disabled if the selected file is an original file.
*/
#updatePrettyPrintButton() {
const disable =
!this.selectedEditor || !!this.selectedEditor.styleSheet.isOriginalSource;
// Only update the button if its state needs it
if (disable !== this.#prettyPrintButton.hasAttribute("disabled")) {
this.#prettyPrintButton.toggleAttribute("disabled");
const l10nString = disable
? "styleeditor-pretty-print-button-disabled"
: "styleeditor-pretty-print-button";
this.#window.document.l10n.setAttributes(
this.#prettyPrintButton,
l10nString
);
}
}
/**
* Update the at-rules sidebar for an editor. Hide if there are no rules
* Display a list of the at-rules (@media, @layer, @container, ) in the editor's associated style sheet.
@ -1657,6 +1694,8 @@ export class StyleEditorUI extends EventEmitter {
editor.onShow(options);
this.#updatePrettyPrintButton();
this.emit("editor-selected", editor);
} catch (e) {
console.error(e);
@ -1740,6 +1779,7 @@ export class StyleEditorUI extends EventEmitter {
this.#filterInput = null;
this.#filterInputClearButton = null;
this.#nav = null;
this.#prettyPrintButton = null;
this.#side = null;
this.#tplDetails = null;
this.#tplSummary = null;

Просмотреть файл

@ -289,21 +289,38 @@ StyleSheetEditor.prototype = {
this._state.text = await longStr.string();
},
prettifySourceText() {
this._prettifySourceTextIfNeeded(/* force */ true);
},
/**
* Attempt to prettify the current text if the corresponding stylesheet is not
* an original source. The text will be read from |this._state.text|.
*
* This will set |this._state.text| to the prettified text if needed.
*
* @param {Boolean} force: Set to true to prettify the stylesheet, no matter if it's
* minified or not.
*/
_prettifySourceTextIfNeeded() {
if (!this.styleSheet.isOriginalSource) {
const ruleCount = this.styleSheet.ruleCount;
const { result, mappings } = prettifyCSS(this._state.text, ruleCount);
// Store the list of objects with mappings between CSS token positions from the
// original source to the prettified source. These will be used when requested to
// jump to a specific position within the editor.
this._mappings = mappings;
this._state.text = result;
_prettifySourceTextIfNeeded(force = false) {
if (this.styleSheet.isOriginalSource) {
return;
}
const { result, mappings } = prettifyCSS(
this._state.text,
// prettifyCSS will always prettify the passed text if we pass a `null` ruleCount.
force ? null : this.styleSheet.ruleCount
);
// Store the list of objects with mappings between CSS token positions from the
// original source to the prettified source. These will be used when requested to
// jump to a specific position within the editor.
this._mappings = mappings;
this._state.text = result;
if (force && this.sourceEditor) {
this.sourceEditor.setText(result);
}
},
@ -406,8 +423,7 @@ StyleSheetEditor.prototype = {
// sourceEditor is already loaded, so we can prettify immediately.
this._prettifySourceTextIfNeeded();
// The updated stylesheet text should have been set in this._state.text
// by _fetchSourceText and _prettifySourceTextIfNeeded.
// The updated stylesheet text should have been set in this._state.text by _fetchSourceText.
const sourceText = this._state.text;
this._justSetText = true;

Просмотреть файл

@ -181,7 +181,18 @@
resizebefore="sibling"
resizeafter="none"
/>
<box class="splitview-side-details devtools-main-content" />
<box>
<box class="splitview-side-details devtools-main-content" />
<html:footer
class="devtools-toolbar stylesheet-editor-status"
hidden="true"
>
<html:button
class="devtools-button style-editor-prettyPrintButton"
data-l10n-id="styleeditor-pretty-print-button"
/>
</html:footer>
</box>
<html:div id="splitview-templates" hidden="">
<html:li id="splitview-tpl-summary-stylesheet" tabindex="0">

Просмотреть файл

@ -2,8 +2,8 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that minified sheets are automatically prettified but other are left
// untouched.
// Test that only minified sheets are automatically prettified,
// and that the pretty print button behaves as expected.
const TESTCASE_URI = TEST_BASE_HTTP + "minified.html";
@ -26,26 +26,36 @@ span {
}
`.trimStart();
const NON_MINIFIED_CSS_TEXT = `
const INLINE_STYLESHEET_ORIGINAL_CSS_TEXT = `
body { background: red; }
div {
font-size: 5em;
color: red
color: red;
}`.trimStart();
const INLINE_STYLESHEET_PRETTIFIED_CSS_TEXT = `
body {
background: red;
}
div {
font-size: 5em;
color: red;
}
`.trimStart();
add_task(async function () {
// Use 2 spaces for indent
await pushPref("devtools.editor.expandtab", true);
await pushPref("devtools.editor.tabsize", 2);
const { ui } = await openStyleEditorForURL(TESTCASE_URI);
is(ui.editors.length, 2, "Two sheets present.");
const { panel, ui } = await openStyleEditorForURL(TESTCASE_URI);
is(ui.editors.length, 3, "Three sheets present.");
info("Testing minified style sheet.");
let editor = await ui.editors[0].getSourceEditor();
const minifiedEditor = await ui.editors[0].getSourceEditor();
is(
editor.sourceEditor.getText(),
minifiedEditor.sourceEditor.getText(),
PRETTIFIED_CSS_TEXT,
"minified source has been prettified automatically"
);
@ -53,11 +63,50 @@ add_task(async function () {
info("Selecting second, non-minified style sheet.");
await ui.selectStyleSheet(ui.editors[1].styleSheet);
editor = ui.editors[1];
const inlineEditor = ui.editors[1];
is(
editor.sourceEditor.getText(),
NON_MINIFIED_CSS_TEXT,
inlineEditor.sourceEditor.getText(),
INLINE_STYLESHEET_ORIGINAL_CSS_TEXT,
"non-minified source has been left untouched"
);
const prettyPrintButton = panel.panelWindow.document.querySelector(
".style-editor-prettyPrintButton"
);
ok(prettyPrintButton, "Pretty print button is displayed");
ok(
!prettyPrintButton.hasAttribute("disabled"),
"Pretty print button is enabled"
);
is(
prettyPrintButton.getAttribute("title"),
"Pretty print style sheet",
"Pretty print button has the expected title attribute"
);
const onEditorChange = inlineEditor.sourceEditor.once("changes");
EventUtils.synthesizeMouseAtCenter(prettyPrintButton, {}, panel.panelWindow);
await onEditorChange;
is(
inlineEditor.sourceEditor.getText(),
INLINE_STYLESHEET_PRETTIFIED_CSS_TEXT,
"inline stylesheet was prettified as expected when clicking on pretty print button"
);
info("Selecting original style sheet.");
await ui.selectStyleSheet(ui.editors[2].styleSheet);
ok(
prettyPrintButton.hasAttribute("disabled"),
"Pretty print button is disabled when selecting an original file"
);
await waitFor(
() =>
prettyPrintButton.getAttribute("title") ===
"Can only pretty print CSS files"
);
ok(
true,
"Pretty print button has the expected title attribute when it's disabled"
);
});

Просмотреть файл

@ -6,8 +6,9 @@
<style type="text/css">body { background: red; }
div {
font-size: 5em;
color: red
color: red;
}</style>
<link rel="stylesheet" type="text/css" href="sourcemap-css/test-stylus.css"/>
</head>
<body>
<div>minified <span>testcase</span></div>

Просмотреть файл

@ -33,6 +33,31 @@ li:hover > hgroup > .stylesheet-more > h3 > .stylesheet-saveButton {
min-height: 0;
}
.stylesheet-editor-status {
display: flex;
align-items: center;
border-block-start: 1px solid var(--theme-splitter-color) !important;
border-block-end: none !important;
/* Only show the editor toolbar if there's a stylesheet editor */
.devtools-main-content:empty + & {
display: none;
}
.style-editor-prettyPrintButton.devtools-button {
margin: 0;
/*
* common.css sets a pointer-events:none on disabled button,
* but here we want the title to be displayed since it gives extra information.
*/
pointer-events: all;
&::before {
background-image: url(chrome://devtools/content/debugger/images/prettyPrint.svg);
}
}
}
.stylesheet-at-rules-container {
overflow-y: auto;
min-width: 0;

Просмотреть файл

@ -232,6 +232,7 @@ function getLineCountInComments(text) {
* The CSS source to prettify.
* @param {Number} ruleCount
* The number of CSS rules expected in the CSS source.
* Set to null to force the text to be pretty-printed.
*
* @return {Object}
* Object with the prettified source and source mappings.
@ -489,6 +490,7 @@ function prettifyCSS(text, ruleCount) {
// Here we ignore the case where whitespace appears at the end of
// the text.
if (
ruleCount !== null &&
pushbackToken &&
token &&
token.tokenType === "whitespace" &&