Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draft: JENKINS-71527 - Update CodeMirror to v6 #8399

Draft
wants to merge 20 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 19 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
5 changes: 0 additions & 5 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,6 @@ THE SOFTWARE.
<artifactId>stapler</artifactId>
<version>${stapler.version}</version>
</dependency>
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler-adjunct-codemirror</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler-groovy</artifactId>
Expand Down
4 changes: 0 additions & 4 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,6 @@ THE SOFTWARE.
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler</artifactId>
</dependency>
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler-adjunct-codemirror</artifactId>
</dependency>
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler-groovy</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,35 @@ THE SOFTWARE.
<st:include page="downgrade.jelly" it="${app}" />
</section>

<div>
<div>JSON</div>
<f:textarea codemirror-mode="json" />
<div>Java</div>
<f:textarea codemirror-mode="java" />
<div>Javascript</div>
<f:textarea codemirror-mode="javascript" />
<div>XML</div>
<f:textarea codemirror-mode="xml" />
<div>HTML</div>
<f:textarea codemirror-mode="html" />
<div>Groovy</div>
<f:textarea codemirror-mode="groovy" />
<div>Shell</div>
<f:textarea codemirror-mode="shell" />
<div>parameter-separator-plugin - src/main/resources/jenkins/plugins/parameter_separator/ParameterSeparatorDefinition/global.jelly</div>
<f:textarea codemirror-mode="css" />
<div>templating-engine-plugin - src/main/resources/org/boozallen/plugins/jte/init/governance/config/ConsoleDefaultPipelineTemplate/config.jelly</div>
<f:textarea codemirror-mode="clike" />
<div>workflow-cps-plugin - plugin/src/main/resources/org/jenkinsci/plugins/workflow/cps/replay/ReplayAction/diff.jelly</div>
<f:textarea value="${it.diff}" readonly="true" codemirror-mode="diff" />
<div>aws-codebuild-plugin · src/main/resources/CodeBuilder/config.jelly</div>
<f:textarea codemirror-mode="yaml" />
<div>r-plugin · src/main/resources/org/biouno/r/R/config.jelly</div>
<f:textarea codemirror-mode="r" />
<div>machine-learning-plugin - src/main/resources/io/jenkins/plugins/ml/IPythonBuilder/config.jelly</div>
<f:textarea codemirror-mode="python" />
</div>

<j:forEach var="category" items="${app.categorizedManagementLinks.entrySet()}">
<section class="jenkins-section jenkins-section--bottom-padding">
<h2 class="jenkins-section__title">${category.key.label}</h2>
Expand Down
15 changes: 6 additions & 9 deletions core/src/main/resources/lib/form/textarea.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -83,21 +83,18 @@ THE SOFTWARE.
<j:if test="${attrs['previewEndpoint']!=null || attrs['codemirror-mode']!=null}">
<st:adjunct includes="lib.form.textarea.textarea"/>
</j:if>
<j:if test="${attrs['codemirror-mode']!=null}">
<st:adjunct includes="
org.kohsuke.stapler.codemirror.mode.${attrs['codemirror-mode']}.${attrs['codemirror-mode']},
org.kohsuke.stapler.codemirror.theme.default"/>
</j:if>
<j:set var="name" value="${attrs.name ?: '_.'+attrs.field}"/>
<f:possibleReadOnlyField>
<textarea id="${attrs.id}" style="${attrs.style}"
<textarea id="${attrs.id}"
style="${attrs.style}"
name ="${name}"
class="jenkins-input ${attrs.checkUrl!=null?'validated':''} ${attrs['codemirror-mode']!=null?'codemirror':''} ${attrs.class}"
class="jenkins-input ${attrs.checkUrl!=null?'validated':''} ${attrs.class}"
checkUrl="${attrs.checkUrl}" checkDependsOn="${attrs.checkDependsOn}" checkMethod="${attrs.checkMethod}"
rows="${h.determineRows(value)}"
readonly="${attrs.readonly}"
codemirror-mode="${attrs['codemirror-mode']}"
codemirror-config="${attrs['codemirror-config']}">
data-type="${attrs['codemirror-mode'] != null ? 'jenkins-code-editor' : null}"
data-code-language="${attrs['codemirror-mode']}"
data-code-options="${attrs['codemirror-config']}">
<j:if test="${value != null &amp;&amp; !empty(value.toString()) &amp;&amp; (value.toString().codePointAt(0) == 10 || value.toString().codePointAt(0) == 13)}"><j:whitespace>&#10;</j:whitespace></j:if>
<st:out value="${value}" />
</textarea>
Expand Down
44 changes: 9 additions & 35 deletions core/src/main/resources/lib/form/textarea/textarea.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,12 @@
Behaviour.specify("TEXTAREA.codemirror", "textarea", 0, function (e) {
var config = e.getAttribute("codemirror-config");
if (!config) {
config = "";
}
config = eval("({" + config + "})");
if (!config.onBlur) {
config.onBlur = function (editor) {
editor.save();
editor.getTextArea().dispatchEvent(new Event("change"));
};
}
var codemirror = CodeMirror.fromTextArea(e, config);
e.codemirrorObject = codemirror;
if (typeof codemirror.getScrollerElement !== "function") {
// Maybe older versions of CodeMirror do not provide getScrollerElement method.
codemirror.getScrollerElement = function () {
return codemirror.getWrapperElement().querySelector(".CodeMirror-scroll");
};
}
var lineCount = codemirror.lineCount();
var lineHeight = codemirror.defaultTextHeight();

var scroller = codemirror.getScrollerElement();
scroller.setAttribute("style", "border:none;");
scroller.style.height = Math.max(lineHeight * lineCount + 30, 130) + "px";

// the form needs to be populated before the "Apply" button
if (e.closest("form")) {
// Protect against undefined element
e.closest("form").addEventListener("jenkins:apply", function () {
e.value = codemirror.getValue();
});
}
});
// Behaviour.specify("TEXTAREA.codemirror", "textarea", 0, function (e) {
// // the form needs to be populated before the "Apply" button
// if (e.closest("form")) {
// // Protect against undefined element
// e.closest("form").addEventListener("jenkins:apply", function () {
// e.value = codemirror.getValue();
// });
// }
// });

Behaviour.specify(
"DIV.textarea-preview-container",
Expand Down
7 changes: 4 additions & 3 deletions core/src/main/resources/lib/hudson/scriptConsole.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ THE SOFTWARE.
</p>

<form action="script" method="post">
<textarea id="script" name="script" class="script">${request.getParameter('script')}</textarea>
<f:textarea name="script"
codemirror-mode="groovy"
default="${request.getParameter('script')}" />
<div align="right">
<f:submit value="${%Run}"/>
</div>
</form>
<st:adjunct includes="org.kohsuke.stapler.codemirror.mode.groovy.groovy"/>
<st:adjunct includes="org.kohsuke.stapler.codemirror.theme.default"/>

<j:if test="${output!=null}">
<h2>
${%Result}
Expand Down
7 changes: 7 additions & 0 deletions war/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@
"webpack-remove-empty-scripts": "1.0.4"
},
"dependencies": {
"@codemirror/language": "^6.9.0",
"@codemirror/language-data": "^6.3.1",
"@codemirror/state": "^6.2.1",
"@codemirror/view": "^6.16.0",
"@lezer/common": "^1.0.4",
"@lezer/highlight": "^1.1.6",
"codemirror": "^6.0.1",
"handlebars": "4.7.8",
"hotkeys-js": "3.12.2",
"jquery": "3.7.1",
Expand Down
2 changes: 2 additions & 0 deletions war/src/main/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Tooltips from "@/components/tooltips";
import StopButtonLink from "@/components/stop-button-link";
import ConfirmationLink from "@/components/confirmation-link";
import Dialogs from "@/components/dialogs";
import Textarea from "@/components/textarea";

Dropdowns.init();
Notifications.init();
Expand All @@ -13,3 +14,4 @@ Tooltips.init();
StopButtonLink.init();
ConfirmationLink.init();
Dialogs.init();
Textarea.init();
timja marked this conversation as resolved.
Show resolved Hide resolved
48 changes: 48 additions & 0 deletions war/src/main/js/components/textarea/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { EditorView, basicSetup } from "codemirror";
import { EditorState } from "@codemirror/state";
import { codeEditorTheme } from "@/components/textarea/theme";
import { languages } from "@codemirror/language-data";
import behaviorShim from "@/util/behavior-shim";

function init() {
behaviorShim.specify(
"[data-type='jenkins-code-editor']",
"jenkins-code-editor",
0,
(textarea) => {
void generateEditorFromTextarea(textarea);
},
);
}

async function generateEditorFromTextarea(textarea) {
const textareaLanguage = textarea.dataset.codeLanguage;
// const textareaOptions = textarea.dataset.codeOptions;
const language = languages.find((e) => e.alias.includes(textareaLanguage));
const loadedLanguage = await language.load();

const view = new EditorView({
state: EditorState.create({
doc: textarea.value,
extensions: [basicSetup, loadedLanguage.language, codeEditorTheme],
}),
});

textarea.parentNode.insertBefore(view.dom, textarea);
textarea.style.display = "none";
if (textarea.form) {
textarea.form.addEventListener("submit", () => {
textarea.value = view.state.doc.toString();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't seem to be working, at least for shell steps on freestyle builds.

It gets called but the new value isn't submitted.
Also doesn't get called at all on Apply button clicks

});
}

// Make sure CodeMirror is synced with any manual height changes
const resizeObserver = new ResizeObserver(() => {
view.requestMeasure();
});
resizeObserver.observe(view.dom);

return view;
}

export default { init };
146 changes: 146 additions & 0 deletions war/src/main/js/components/textarea/theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { EditorView } from "@codemirror/view";
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
import { tags as t } from "@lezer/highlight";

const chalky = "#e5c07b",
coral = "var(--orange)",
cyan = "var(--cyan)",
invalid = "#ffffff",
ivory = "var(--text-color)",
stone = "var(--text-color-secondary)",
malibu = "var(--blue)",
sage = "var(--green)",
whiskey = "var(--orange)",
violet = "var(--purple)",
background = "var(--input-color)",
selection = "var(--selection-color)",
cursor = "var(--text-color)";

export const theme = EditorView.theme({
"&": {
color: ivory,
backgroundColor: background,
borderRadius: "10px",
border: "2px solid var(--input-border)",
padding: "6px",
boxShadow: "var(--form-input-glow)",
transition: "var(--standard-transition), height 0s",
resize: "vertical",
overflow: "hidden",
marginBottom: "var(--section-padding)",
},
"&:hover": {
borderColor: "var(--input-border-hover)",
},
"&:focus-within": {
borderColor: "var(--focus-input-border)",
boxShadow: "var(--form-input-glow--focus)",
},
".cm-content": {
caretColor: cursor,
minHeight: "200px",
},
".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor },
"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
{ backgroundColor: selection },
".cm-scroller": {
fontFamily: "var(--font-family-mono)",
},
".cm-activeLine": { backgroundColor: "#6699ff0b" },
".cm-selectionMatch": { backgroundColor: "#aafe661a" },
"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": {
backgroundColor: "color-mix(in srgb, var(--blue) 30%, transparent)",
},
"&.cm-focused": {
outline: "none",
},
".cm-gutters": {
backgroundColor: "transparent",
color: stone,
border: "none",
},
".cm-activeLineGutter": {
backgroundColor: "transparent",
},
".cm-line": {
backgroundColor: "transparent",
},
".cm-foldPlaceholder": {
backgroundColor: "transparent",
border: "none",
color: "#ddd",
},
".cm-tooltip": {
border: "none",
borderRadius: "15px",
background: "var(--input-color)",
boxShadow: "var(--dropdown-box-shadow)",
padding: "0.4rem",
},
".cm-completionIcon": {
display: "none",
},
".cm-tooltip-autocomplete": {
"& > ul > li": {
padding: "0.4rem 0.55rem !important",
borderRadius: "0.66rem",
color: "var(--text-color)",
fontFamily: "var(--font-family-mono)",
"&:hover": {
backgroundColor: "var(--item-background--hover)",
},
"&[aria-selected]": {
backgroundColor: "var(--item-background--active)",
color: "var(--text-color)",
},
},
},
});

/// The highlighting style for code in the One Dark theme.
export const syntaxHighlight = HighlightStyle.define([
{ tag: t.keyword, color: violet },
{
tag: [t.deleted, t.character, t.propertyName, t.macroName],
color: coral,
},
{ tag: [t.function(t.variableName), t.labelName], color: malibu },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: whiskey },
{ tag: [t.definition(t.name), t.separator], color: ivory },
{
tag: [
t.typeName,
t.className,
t.number,
t.changed,
t.annotation,
t.modifier,
t.self,
t.namespace,
],
color: chalky,
},
{
tag: [
t.operator,
t.operatorKeyword,
t.url,
t.escape,
t.regexp,
t.link,
t.special(t.string),
],
color: cyan,
},
{ tag: [t.meta, t.comment], color: stone },
{ tag: t.strong, fontWeight: "bold" },
{ tag: t.emphasis, fontStyle: "italic" },
{ tag: t.strikethrough, textDecoration: "line-through" },
{ tag: t.link, color: stone, textDecoration: "underline" },
{ tag: t.heading, fontWeight: "bold", color: coral },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: whiskey },
{ tag: [t.processingInstruction, t.string, t.inserted], color: sage },
{ tag: t.invalid, color: invalid },
]);

export const codeEditorTheme = [theme, syntaxHighlighting(syntaxHighlight)];
Loading
Loading