Bug 1877989 - [devtools] Add CodeMirror 6 in tree. r=devtools-reviewers,bomsy.

Differential Revision: https://phabricator.services.mozilla.com/D200305
This commit is contained in:
Nicolas Chevobbe 2024-02-08 14:39:41 +00:00
Родитель 032ee988a6
Коммит 39fd633713
15 изменённых файлов: 383 добавлений и 33 удалений

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

@ -73,6 +73,7 @@ toolkit/components/uniffi-js/UniFFIFixtureScaffolding.cpp
# awk '{print ""$1".*"}' ./tools/rewriting/ThirdPartyPaths.txt
browser/extensions/mortar/ppapi/.*
devtools/client/shared/sourceeditor/codemirror/.*
devtools/client/shared/sourceeditor/codemirror6/.*
dom/canvas/test/webgl-conf/checkout/closure-library/.*
dom/media/gmp/rlz/.*
dom/media/gmp/widevine-adapter/content_decryption_module.h

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

@ -1254,6 +1254,7 @@ devtools/client/jsonview/lib/require.js
devtools/client/shared/build/babel.js
devtools/client/shared/source-map/
devtools/client/shared/sourceeditor/codemirror/
devtools/client/shared/sourceeditor/codemirror6/
devtools/client/shared/sourceeditor/test/cm_mode_ruby.js
devtools/client/shared/sourceeditor/test/codemirror/
devtools/client/shared/vendor/

Двоичный файл не отображается.

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

@ -3,7 +3,28 @@ is a JavaScript component that provides a code editor in the browser. When
a mode is available for the language you are coding in, it will color your
code, and optionally help with indentation.
# Upgrade
# CodeMirror 6
We're currently migrating to CodeMirror 6, which means we have bundle for version 6 _and_ 5,
until we successfully migrated all consumers to CodeMirror 6.
For version 6, we're generating a bundle (codemirror6/codemirror6.bundle.js) using rollup.
The entry point for the bundle is codemirror6/index.mjs, where we export all the classes
and functions that the editor needs. When adding new exported item, the bundle needs to
be updated, which can be done by running:
> cd devtools/client/shared/sourceeditor
> npm install
> npm run build-cm6
This will produced a minified bundle, which might not be ideal if you're debugging an issue or profiling.
You can get an unminified bundle by running:
> npm run build-cm6-unminified
The generated bundle can be configurated in rollup.config.mjs.
# CodeMirror 5
## CodeMirror 5 Upgrade
Currently used version is 5.58.1. To upgrade: download a new version of
CodeMirror from the project's page [1] and replace all JavaScript and
@ -41,18 +62,18 @@ The sourceeditor component contains imported CodeMirror tests [3].
Other than that, we don't have any Mozilla-specific patches applied to
CodeMirror itself.
# Addons
## Addons
To install a new CodeMirror addon add it to the codemirror directory,
To install a new CodeMirror 5 addon add it to the codemirror directory,
jar.mn [4] file and editor.js [5]. Also, add it to the License section
below.
# License
## License
The following files in this directory and devtools/client/shared/sourceeditor/test/codemirror/
are licensed according to the contents in the LICENSE file.
# Localization patches
## Localization patches
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js b/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js
--- a/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js

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

@ -318,3 +318,47 @@
margin-inline: 2px;
padding-inline: 2px;
}
/****************************************/
/***** CodeMirror 6 specific styles *****/
/****************************************/
.cm-editor {
max-height: 100%;
height: 100%;
}
.cm-editor .cm-selectionBackground {
background: var(--theme-selection-background) !important;
}
.cm6-dt-foldgutter__toggle-button {
height: 14px;
width: 14px;
margin-inline: 3px;
/* By default, the icon is a bit too low */
translate: 0px -4px;
background-image: url("chrome://devtools/skin/images/arrow.svg");
background-position: center;
background-repeat: no-repeat;
background-color: transparent;
border: none;
-moz-context-properties: fill;
fill: var(--theme-icon-dimmed-color);
/* make the icons smaller than regular twistys (10->8px) */
background-size: 8px;
cursor: pointer;
&[aria-expanded=false] {
transform: rotate(-90deg);
}
}
.cm-editor .cm-foldPlaceholder {
font-family: sans-serif;
padding: 0 2px;
border-radius: 3px;
border: solid 1px var(--theme-splitter-color);
color: var(--theme-body-color);
background-color: var(--theme-sidebar-background);
vertical-align: middle;
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import * as codemirror from "codemirror";
import * as codemirrorView from "@codemirror/view";
import * as codemirrorState from "@codemirror/state";
import * as codemirrorLanguage from "@codemirror/language";
import * as codemirrorLangJavascript from "@codemirror/lang-javascript";
import * as lezerHighlight from "@lezer/highlight";
// XXX When adding new exports, you need to update the codemirror6.bundle.js file,
// running `npm install && npm run build-cm6` from the devtools/client/shared/sourceeditor folder
export {
codemirror,
codemirrorView,
codemirrorState,
codemirrorLanguage,
codemirrorLangJavascript,
lezerHighlight,
};

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

@ -0,0 +1,12 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
"codemirror6.bundle.js",
)
with Files("**"):
BUG_COMPONENT = ("DevTools", "Source Editor")

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

@ -62,6 +62,8 @@ const { OS } = Services.appinfo;
const CM_BUNDLE =
"chrome://devtools/content/shared/sourceeditor/codemirror/codemirror.bundle.js";
const CM6_BUNDLE =
"resource://devtools/client/shared/sourceeditor/codemirror6/codemirror6.bundle.js";
const CM_IFRAME =
"chrome://devtools/content/shared/sourceeditor/codemirror/cmiframe.html";
@ -159,6 +161,7 @@ class Editor extends EventEmitter {
config = null;
Doc = null;
#compartments;
#lastDirty;
#loadedKeyMaps;
#ownerDoc;
@ -173,6 +176,7 @@ class Editor extends EventEmitter {
this.version = null;
this.config = {
cm6: false,
value: "",
mode: Editor.modes.text,
indentUnit: tabSize,
@ -380,7 +384,14 @@ class Editor extends EventEmitter {
env.style.visibility = "";
const win = env.contentWindow.wrappedJSObject;
this.container = env;
this.#setup(win.document.body, el.ownerDocument);
const editorEl = win.document.body;
const editorDoc = el.ownerDocument;
if (this.config.cm6) {
this.#setupCm6(editorEl, editorDoc);
} else {
this.#setup(editorEl, editorDoc);
}
resolve();
};
@ -394,7 +405,11 @@ class Editor extends EventEmitter {
}
appendToLocalElement(el) {
this.#setup(el);
if (this.config.cm6) {
this.#setupCm6(el);
} else {
this.#setup(el);
}
}
/**
@ -567,6 +582,82 @@ class Editor extends EventEmitter {
win.dispatchEvent(editorReadyEvent);
}
/**
* Do the actual appending and configuring of the CodeMirror 6 instance.
* This is used by appendTo and appendToLocalElement, and does all the hard work to
* configure CodeMirror 6 with all the right options/modes/etc.
* This should be kept in sync with #setup.
*
* @param {Element} el: Element into which the codeMirror editor should be appended.
* @param {Document} document: Optional document, if not set, will default to el.ownerDocument
*/
#setupCm6(el, doc) {
this.#ownerDoc = doc || el.ownerDocument;
const win = el.ownerDocument.defaultView;
Services.scriptloader.loadSubScript(CM6_BUNDLE, win);
const {
codemirror,
codemirrorView: { EditorView, lineNumbers },
codemirrorState: { EditorState, Compartment },
codemirrorLanguage,
codemirrorLangJavascript,
lezerHighlight,
} = win.CodeMirror;
const tabSizeCompartment = new Compartment();
const indentCompartment = new Compartment();
this.#compartments = {
tabSizeCompartment,
indentCompartment,
};
const indentStr = (this.config.indentWithTabs ? "\t" : " ").repeat(
this.config.indentUnit || 2
);
const extensions = [
indentCompartment.of(codemirrorLanguage.indentUnit.of(indentStr)),
tabSizeCompartment.of(EditorState.tabSize.of(this.config.tabSize)),
EditorState.readOnly.of(this.config.readOnly),
codemirrorLanguage.codeFolding({
placeholderText: "↔",
}),
codemirrorLanguage.foldGutter({
class: "cm6-dt-foldgutter",
markerDOM: open => {
const button = doc.createElement("button");
button.classList.add("cm6-dt-foldgutter__toggle-button");
button.setAttribute("aria-expanded", open);
return button;
},
}),
codemirrorLanguage.syntaxHighlighting(lezerHighlight.classHighlighter),
// keep last so other extension take precedence
codemirror.minimalSetup,
];
if (this.config.mode === Editor.modes.js) {
extensions.push(codemirrorLangJavascript.javascript());
}
if (this.config.lineNumbers) {
extensions.push(lineNumbers());
}
if (this.config.lineWrapping) {
extensions.push(EditorView.lineWrapping);
}
const cm = new EditorView({
parent: el,
extensions,
});
editors.set(this, cm);
}
/**
* Returns a boolean indicating whether the editor is ready to
* use. Use appendTo(el).then(() => {}) for most cases
@ -594,6 +685,14 @@ class Editor extends EventEmitter {
Services.scriptloader.loadSubScript(url, win);
}
/**
* Returns the container content window
* @returns {Window}
*/
getContainerWindow() {
return this.container.contentWindow.wrappedJSObject;
}
/**
* Creates a CodeMirror Document
*
@ -647,7 +746,7 @@ class Editor extends EventEmitter {
const cm = editors.get(this);
if (line == null) {
return cm.getValue();
return this.config.cm6 ? cm.state.doc.toString() : cm.getValue();
}
const info = this.lineInfo(line);
@ -684,6 +783,22 @@ class Editor extends EventEmitter {
return null;
}
const cm = editors.get(this);
if (this.config.cm6) {
return {
// cm6 lines are 1-based, while cm5 are 0-based
text: cm.state.doc.lineAt(line + 1)?.text,
// TODO: Expose those, or see usage for those and do things differently
line: null,
handle: null,
gutterMarkers: null,
textClass: null,
bgClass: null,
wrapClass: null,
widgets: null,
};
}
return cm.lineInfo(line);
}
@ -719,7 +834,13 @@ class Editor extends EventEmitter {
value = { split: () => lines };
}
cm.setValue(value);
if (this.config.cm6) {
cm.dispatch({
changes: { from: 0, to: cm.state.doc.length, insert: value },
});
} else {
cm.setValue(value);
}
this.resetIndentUnit();
}
@ -774,17 +895,49 @@ class Editor extends EventEmitter {
resetIndentUnit() {
const cm = editors.get(this);
const iterFn = function (start, end, callback) {
cm.eachLine(start, end, line => {
return callback(line.text);
});
const iterFn = (start, maxEnd, callback) => {
if (!this.config.cm6) {
cm.eachLine(start, maxEnd, line => {
return callback(line.text);
});
} else {
const iterator = cm.state.doc.iterLines(
start + 1,
Math.min(cm.state.doc.lines, maxEnd) + 1
);
let callbackRes;
do {
iterator.next();
callbackRes = callback(iterator.value);
} while (iterator.done !== true && !callbackRes);
}
};
const { indentUnit, indentWithTabs } = getIndentationFromIteration(iterFn);
cm.setOption("tabSize", indentUnit);
cm.setOption("indentUnit", indentUnit);
cm.setOption("indentWithTabs", indentWithTabs);
if (!this.config.cm6) {
cm.setOption("tabSize", indentUnit);
cm.setOption("indentUnit", indentUnit);
cm.setOption("indentWithTabs", indentWithTabs);
} else {
const {
codemirrorState: { EditorState },
codemirrorLanguage,
} = this.getContainerWindow().CodeMirror;
cm.dispatch({
effects: this.#compartments.tabSizeCompartment.reconfigure(
EditorState.tabSize.of(indentUnit)
),
});
cm.dispatch({
effects: this.#compartments.indentCompartment.reconfigure(
codemirrorLanguage.indentUnit.of(
(indentWithTabs ? "\t" : " ").repeat(indentUnit)
)
),
});
}
}
/**

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

@ -4,6 +4,10 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += [
"codemirror6",
]
DevToolsModules(
"autocomplete.js",
"css-autocompleter.js",

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

@ -5,12 +5,22 @@
"main": "",
"scripts": {
"build": "webpack",
"build-unminified": "webpack --optimization.minimizer=false"
"build-unminified": "webpack --optimization.minimizer=false",
"build-cm6": "rollup -c --minified",
"build-cm6-unminified": "rollup -c"
},
"author": "",
"license": "",
"dependencies": {
"codemirror": "6.0.1",
"@codemirror/language": "^6.10.0",
"@codemirror/lang-javascript": "^6.2.1"
},
"devDependencies": {
"webpack": "^4.28.4",
"webpack-cli": "^3.2.1"
"webpack-cli": "^3.2.1",
"rollup": "^4.9.6",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-node-resolve": "^15.2.3"
}
}

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

@ -0,0 +1,24 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint-disable import/no-unresolved */
import terser from "@rollup/plugin-terser";
import nodeResolve from "@rollup/plugin-node-resolve";
export default function (commandLineArgs) {
const plugins = [nodeResolve()];
if (commandLineArgs.minified) {
plugins.push(terser());
}
return {
input: "codemirror6/index.mjs",
output: {
file: "codemirror6/codemirror6.bundle.js",
format: "iife",
name: "CodeMirror",
},
plugins,
};
}

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

@ -33,6 +33,7 @@ body {
.theme-link,
.cm-s-mozilla .cm-link,
.cm-editor .tok-link,
.CodeMirror-Tern-type {
color: var(--grey-50);
}
@ -41,8 +42,11 @@ body {
* FIXME: http://bugzil.la/575675 CSS links without :visited set cause assertion
* failures in debug builds.
*/
.theme-link:visited,
.cm-s-mozilla .cm-link:visited {
:is(
.theme-link,
.cm-s-mozilla .cm-link,
.cm-editor .tok-link
):visited {
color: var(--theme-link-color);
}
@ -50,6 +54,10 @@ body {
.cm-s-mozilla .cm-meta,
.cm-s-mozilla .cm-hr,
.cm-s-mozilla .cm-comment,
.cm-editor :is(
.tok-meta,
.tok-comment
),
.variable-or-property .token-undefined,
.variable-or-property .token-null,
.CodeMirror-Tern-completion-unknown:before {
@ -66,6 +74,7 @@ body {
}
.cm-s-mozilla .cm-number,
.cm-editor .tok-number,
.variable-or-property .token-number,
.variable-or-property[return] > .title > .name,
.variable-or-property[scope] > .title > .name {
@ -80,20 +89,27 @@ body {
.cm-s-mozilla .cm-attribute,
.cm-s-mozilla .cm-builtin,
.cm-s-mozilla .cm-keyword,
.cm-editor .tok-keyword,
.variables-view-variable > .title > .name {
color: var(--theme-highlight-red);
}
.cm-s-mozilla .cm-def,
.cm-s-mozilla .cm-variable-2 {
.cm-s-mozilla .cm-variable-2,
.cm-editor :is(
.tok-variableName.tok-definition,
.tok-variableName
) {
color: var(--theme-highlight-blue);
}
.cm-s-mozilla .cm-variable {
.cm-s-mozilla .cm-variable,
.cm-editor .tok-variableName2 {
color: var(--theme-highlight-purple);
}
.cm-s-mozilla .cm-property {
.cm-s-mozilla .cm-property,
.cm-editor .tok-propertyName {
color: var(--theme-highlight-green);
}
@ -106,6 +122,7 @@ body {
.cm-s-mozilla .cm-header,
.cm-s-mozilla .cm-bracket,
.cm-s-mozilla .cm-qualifier,
.cm-editor .tok-heading,
.variables-view-property > .title > .name {
color: var(--theme-highlight-blue);
}
@ -120,6 +137,7 @@ body {
.cm-s-mozilla .cm-string,
.cm-s-mozilla .cm-string-2,
.cm-editor .tok-string,
.variable-or-property .token-string,
.CodeMirror-Tern-farg {
color: #709AFF;
@ -133,6 +151,11 @@ body {
.cm-s-mozilla .cm-atom,
.cm-s-mozilla .cm-quote,
.cm-s-mozilla .cm-error,
.cm-editor :is(
.tok-atom,
.tok-bool,
.tok-invalid
),
.variable-or-property .token-boolean,
.variable-or-property .token-domnode,
.variable-or-property[exception] > .title > .name {
@ -186,7 +209,11 @@ body {
color: var(--theme-text-color-strong);
}
.cm-s-mozilla .cm-operator {
.cm-s-mozilla .cm-operator,
.cm-editor :is(
.tok-punctuation,
.tok-operator
) {
color: var(--theme-body-color);
}
@ -251,7 +278,8 @@ div.CodeMirror span.eval-text {
color: var(--grey-40);
}
.cm-s-mozilla .CodeMirror-gutters { /* vertical line next to line numbers */
.cm-s-mozilla .CodeMirror-gutters,
.cm-editor .cm-gutters { /* vertical line next to line numbers */
border-right-color: var(--theme-toolbar-background);
background-color: var(--theme-sidebar-background);
}

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

@ -33,6 +33,7 @@ body {
.theme-link,
.cm-s-mozilla .cm-link,
.cm-editor .tok-link,
.CodeMirror-Tern-type {
color: var(--theme-comment);
}
@ -41,15 +42,22 @@ body {
* FIXME: http://bugzil.la/575675 CSS links without :visited set cause assertion
* failures in debug builds.
*/
.theme-link:visited,
.cm-s-mozilla .cm-link:visited {
:is(
.theme-link,
.cm-s-mozilla .cm-link,
.cm-editor .tok-link
):visited {
color: var(--theme-link-color);
}
.theme-comment,
.cm-s-mozilla .cm-meta,
.cm-s-mozilla .cm-hr,
.cm-s-mozilla .cm-comment,
.cm-s-mozilla .cm-comment
.cm-editor :is(
.tok-meta,
.tok-comment
),
.variable-or-property .token-undefined,
.variable-or-property .token-null,
.CodeMirror-Tern-completion-unknown:before {
@ -66,6 +74,7 @@ body {
}
.cm-s-mozilla .cm-number,
.cm-editor .tok-number,
.variable-or-property .token-number,
.variable-or-property[return] > .title > .name,
.variable-or-property[scope] > .title > .name {
@ -80,20 +89,27 @@ body {
.cm-s-mozilla .cm-attribute,
.cm-s-mozilla .cm-builtin,
.cm-s-mozilla .cm-keyword,
.cm-editor .tok-keyword,
.variables-view-variable > .title > .name {
color: var(--theme-highlight-red);
}
.cm-s-mozilla .cm-def,
.cm-s-mozilla .cm-variable-2 {
.cm-s-mozilla .cm-variable-2,
.cm-editor :is(
.tok-variableName.tok-definition,
.tok-variableName
) {
color: var(--blue-55);
}
.cm-s-mozilla .cm-variable {
.cm-s-mozilla .cm-variable,
.cm-editor .tok-variableName2 {
color: var(--purple-60);
}
.cm-s-mozilla .cm-property {
.cm-s-mozilla .cm-property,
.cm-editor .tok-propertyName {
color: var(--theme-highlight-green);
}
@ -106,6 +122,7 @@ body {
.cm-s-mozilla .cm-header,
.cm-s-mozilla .cm-bracket,
.cm-s-mozilla .cm-qualifier,
.cm-editor .tok-heading,
.variables-view-property > .title > .name {
color: var(--theme-highlight-blue);
}
@ -117,6 +134,7 @@ body {
.theme-fg-color2,
.cm-s-mozilla .cm-string,
.cm-s-mozilla .cm-string-2,
.cm-editor .tok-string,
.variable-or-property .token-string,
.CodeMirror-Tern-farg {
color: var(--theme-highlight-purple);
@ -130,6 +148,11 @@ body {
.cm-s-mozilla .cm-atom,
.cm-s-mozilla .cm-quote,
.cm-s-mozilla .cm-error,
.cm-editor :is(
.tok-atom,
.tok-bool,
.tok-invalid
),
.variable-or-property .token-boolean,
.variable-or-property .token-domnode,
.variable-or-property[exception] > .title > .name {
@ -182,7 +205,11 @@ body {
.CodeMirror.cm-s-mozilla pre.CodeMirror-line-like,
.cm-s-mozilla .cm-variable-3,
.cm-s-mozilla .cm-operator,
.cm-s-mozilla .cm-special {
.cm-s-mozilla .cm-special,
.cm-editor :is(
.tok-punctuation,
.tok-operator
) {
color: var(--theme-body-color);
}
@ -238,7 +265,8 @@ div.CodeMirror span.eval-text {
color: var(--grey-50);
}
.cm-s-mozilla .CodeMirror-gutters { /* vertical line next to line numbers */
.cm-s-mozilla .CodeMirror-gutters,
.cm-editor .cm-gutters { /* vertical line next to line numbers */
border-right-color: var(--theme-splitter-color);
background-color: var(--theme-sidebar-background);
}

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

@ -9,6 +9,7 @@ devtools/client/jsonview/lib/require.js
devtools/client/shared/build/babel.js
devtools/client/shared/source-map/
devtools/client/shared/sourceeditor/codemirror/
devtools/client/shared/sourceeditor/codemirror6/
devtools/client/shared/sourceeditor/test/cm_mode_ruby.js
devtools/client/shared/sourceeditor/test/codemirror/
devtools/client/shared/vendor/