Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-07-13 21:09:35 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-13 21:09:35 +0300
commit7e064974b92de60a3ef4642905e8af98a364a7a0 (patch)
treeea95222e8b6040cd959dfe3404ee83a069bae2c7 /app/assets
parenta88c31d0ea1a79ca93fad357c3eb536b5e013e44 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js128
-rw-r--r--app/assets/javascripts/main.js58
-rw-r--r--app/assets/stylesheets/highlight/themes/monokai.scss2
-rw-r--r--app/assets/stylesheets/highlight/themes/white.scss1
4 files changed, 120 insertions, 69 deletions
diff --git a/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js b/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js
index a0be6c70993..5d675fb4851 100644
--- a/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js
+++ b/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js
@@ -138,6 +138,10 @@ class HastToProseMirrorConverterState {
return this.stack[this.stack.length - 1];
}
+ get topNode() {
+ return this.findInStack((item) => item.type === 'node');
+ }
+
/**
* Detects if the node stack is empty
*/
@@ -179,7 +183,7 @@ class HastToProseMirrorConverterState {
*/
addText(schema, text) {
if (!text) return;
- const nodes = this.top.content;
+ const nodes = this.topNode?.content;
const last = nodes[nodes.length - 1];
const node = schema.text(text, this.marks);
const merged = maybeMerge(last, node);
@@ -189,57 +193,92 @@ class HastToProseMirrorConverterState {
} else {
nodes.push(node);
}
-
- this.closeMarks();
}
/**
* Adds a mark to the set of marks stored temporarily
- * until addText is called.
- * @param {*} markType
- * @param {*} attrs
+ * until an inline node is created.
+ * @param {https://prosemirror.net/docs/ref/#model.MarkType} schemaType Mark schema type
+ * @param {https://github.com/syntax-tree/hast#nodes} hastNode AST node that the mark is based on
+ * @param {Object} attrs Mark attributes
+ * @param {Object} factorySpec Specifications on how th mark should be created
*/
- openMark(markType, attrs) {
- this.marks = markType.create(attrs).addToSet(this.marks);
+ openMark(schemaType, hastNode, attrs, factorySpec) {
+ const mark = schemaType.create(attrs);
+ this.stack.push({
+ type: 'mark',
+ mark,
+ attrs,
+ hastNode,
+ factorySpec,
+ });
+
+ this.marks = mark.addToSet(this.marks);
}
/**
- * Empties the temporary Mark set.
+ * Removes a mark from the list of active marks that
+ * are applied to inline nodes.
*/
- closeMarks() {
- this.marks = Mark.none;
+ closeMark() {
+ const { mark } = this.stack.pop();
+
+ this.marks = mark.removeFromSet(this.marks);
}
/**
* Adds a node to the stack data structure.
*
- * @param {Schema.NodeType} type ProseMirror Schema for the node
- * @param {HastNode} hastNode Hast node from which the ProseMirror node will be created
+ * @param {https://prosemirror.net/docs/ref/#model.NodeType} schemaType ProseMirror Schema for the node
+ * @param {https://github.com/syntax-tree/hast#nodes} hastNode Hast node from which the ProseMirror node will be created
* @param {*} attrs Node’s attributes
* @param {*} factorySpec The factory spec used to create the node factory
*/
- openNode(type, hastNode, attrs, factorySpec) {
- this.stack.push({ type, attrs, content: [], hastNode, factorySpec });
+ openNode(schemaType, hastNode, attrs, factorySpec) {
+ this.stack.push({
+ type: 'node',
+ schemaType,
+ attrs,
+ content: [],
+ hastNode,
+ factorySpec,
+ });
}
/**
* Removes the top ProseMirror node from the
* conversion stack and adds the node to the
* previous element.
- * @returns
*/
closeNode() {
- const { type, attrs, content } = this.stack.pop();
- const node = type.createAndFill(attrs, content);
-
- if (!node) return null;
-
- if (this.marks.length) {
- this.marks = Mark.none;
+ const { schemaType, attrs, content, factorySpec } = this.stack.pop();
+ const node =
+ factorySpec.type === 'inline' && this.marks.length
+ ? schemaType.createAndFill(attrs, content, this.marks)
+ : schemaType.createAndFill(attrs, content);
+
+ if (!node) {
+ /*
+ When the node returned by `createAndFill` is null is because the `content` passed as a parameter
+ doesn’t conform with the document schema. We are handling the most likely scenario here that happens
+ when a paragraph is inside another paragraph.
+
+ This scenario happens when the converter encounters a mark wrapping one or more paragraphs.
+ In this case, the converter will wrap the mark in a paragraph as well because ProseMirror does
+ not allow marks wrapping block nodes or being direct children of certain nodes like the root nodes
+ or list items.
+ */
+ if (
+ schemaType.name === 'paragraph' &&
+ content.some((child) => child.type.name === 'paragraph')
+ ) {
+ this.topNode.content.push(...content);
+ }
+ return null;
}
if (!this.empty) {
- this.top.content.push(node);
+ this.topNode.content.push(node);
}
return node;
@@ -247,9 +286,27 @@ class HastToProseMirrorConverterState {
closeUntil(hastNode) {
while (hastNode !== this.top?.hastNode) {
- this.closeNode();
+ if (this.top.type === 'node') {
+ this.closeNode();
+ } else {
+ this.closeMark();
+ }
}
}
+
+ buildDoc() {
+ let doc;
+
+ do {
+ if (this.top.type === 'node') {
+ doc = this.closeNode();
+ } else {
+ this.closeMark();
+ }
+ } while (!this.empty);
+
+ return doc;
+ }
}
/**
@@ -276,7 +333,7 @@ const createProseMirrorNodeFactories = (schema, proseMirrorFactorySpecs, source)
},
text: {
selector: 'text',
- handle: (state, hastNode) => {
+ handle: (state, hastNode, parent) => {
const found = state.findInStack((node) => isFunction(node.factorySpec.processText));
const { value: text } = hastNode;
@@ -284,6 +341,7 @@ const createProseMirrorNodeFactories = (schema, proseMirrorFactorySpecs, source)
return;
}
+ state.closeUntil(parent);
state.addText(schema, found ? found.factorySpec.processText(text) : text);
},
},
@@ -320,11 +378,7 @@ const createProseMirrorNodeFactories = (schema, proseMirrorFactorySpecs, source)
} else if (factory.type === 'mark') {
const markType = schema.marks[proseMirrorName];
factory.handle = (state, hastNode, parent) => {
- state.openMark(markType, getAttrs(factory, hastNode, parent, source));
-
- if (factory.inlineContent) {
- state.addText(schema, hastNode.value);
- }
+ state.openMark(markType, hastNode, getAttrs(factory, hastNode, parent, source), factory);
};
} else if (factory.type === 'ignore') {
factory.handle = noop;
@@ -357,7 +411,7 @@ const findParent = (ancestors, parent) => {
return ancestors[ancestors.length - 1];
};
-const calcTextNodePosition = (textNode) => {
+const resolveNodePosition = (textNode) => {
const { position, value, type } = textNode;
if (type !== 'text' || (!position.start && !position.end) || (position.start && position.end)) {
@@ -418,7 +472,7 @@ const wrapInlineElements = (nodes, wrappableTags) =>
const wrapper = {
type: 'element',
tagName: 'p',
- position: calcTextNodePosition(child),
+ position: resolveNodePosition(child),
children: [child],
properties: { wrapper: true },
};
@@ -580,11 +634,5 @@ export const createProseMirrorDocFromMdastTree = ({
return factory.skipChildren === true ? SKIP : true;
});
- let doc;
-
- do {
- doc = state.closeNode();
- } while (!state.empty);
-
- return doc;
+ return state.buildDoc();
};
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index a725ef972d4..21d5decb15b 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -115,34 +115,6 @@ function deferredInitialisation() {
);
}
- const searchInputBox = document.querySelector('#search');
- if (searchInputBox) {
- searchInputBox.addEventListener(
- 'focus',
- () => {
- if (gon.features?.newHeaderSearch) {
- import(/* webpackChunkName: 'globalSearch' */ '~/header_search')
- .then(async ({ initHeaderSearchApp }) => {
- // In case the user started searching before we bootstrapped, let's pass the search along.
- const initialSearchValue = searchInputBox.value;
- await initHeaderSearchApp(initialSearchValue);
- // this is new #search input element. We need to re-find it.
- document.querySelector('#search').focus();
- })
- .catch(() => {});
- } else {
- import(/* webpackChunkName: 'globalSearch' */ './search_autocomplete')
- .then(({ default: initSearchAutocomplete }) => {
- const searchDropdown = initSearchAutocomplete();
- searchDropdown.onSearchInputFocus();
- })
- .catch(() => {});
- }
- },
- { once: true },
- );
- }
-
addSelectOnFocusBehaviour('.js-select-on-focus');
const glTooltipDelay = localStorage.getItem('gl-tooltip-delay');
@@ -169,6 +141,36 @@ function deferredInitialisation() {
}
}
+// loading this inside requestIdleCallback is causing issues
+// see https://gitlab.com/gitlab-org/gitlab/-/issues/365746
+const searchInputBox = document.querySelector('#search');
+if (searchInputBox) {
+ searchInputBox.addEventListener(
+ 'focus',
+ () => {
+ if (gon.features?.newHeaderSearch) {
+ import(/* webpackChunkName: 'globalSearch' */ '~/header_search')
+ .then(async ({ initHeaderSearchApp }) => {
+ // In case the user started searching before we bootstrapped, let's pass the search along.
+ const initialSearchValue = searchInputBox.value;
+ await initHeaderSearchApp(initialSearchValue);
+ // this is new #search input element. We need to re-find it.
+ document.querySelector('#search').focus();
+ })
+ .catch(() => {});
+ } else {
+ import(/* webpackChunkName: 'globalSearch' */ './search_autocomplete')
+ .then(({ default: initSearchAutocomplete }) => {
+ const searchDropdown = initSearchAutocomplete();
+ searchDropdown.onSearchInputFocus();
+ })
+ .catch(() => {});
+ }
+ },
+ { once: true },
+ );
+}
+
const $body = $('body');
const $document = $(document);
const bootstrapBreakpoint = bp.getBreakpointSize();
diff --git a/app/assets/stylesheets/highlight/themes/monokai.scss b/app/assets/stylesheets/highlight/themes/monokai.scss
index c19189ef31b..f83bad152c5 100644
--- a/app/assets/stylesheets/highlight/themes/monokai.scss
+++ b/app/assets/stylesheets/highlight/themes/monokai.scss
@@ -115,7 +115,7 @@ $monokai-gh: #75715e;
@include hljs-override('section', $monokai-gh);
@include hljs-override('bullet', $monokai-n);
@include hljs-override('subst', $monokai-p);
- @include hljs-override('symbol', $monokai-ni);
+ @include hljs-override('symbol', $monokai-ss);
// Line numbers
.file-line-num {
diff --git a/app/assets/stylesheets/highlight/themes/white.scss b/app/assets/stylesheets/highlight/themes/white.scss
index 8698e448c94..b6ca26e9b39 100644
--- a/app/assets/stylesheets/highlight/themes/white.scss
+++ b/app/assets/stylesheets/highlight/themes/white.scss
@@ -2,6 +2,7 @@
@import '../white_base';
@include conflict-colors('white');
+ @include hljs-override('symbol', $white-ss);
}
:root {