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-03-23 15:07:27 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-03-23 15:07:27 +0300
commit3e68d3848770b492d314f8e2967c37f7fdd5d143 (patch)
tree01bd69a759c55ddf4ea1e5549a253cb0fd564854 /app/assets/javascripts/content_editor
parent52192e0f19ca790dc9f44bc45730434100f83d90 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/content_editor')
-rw-r--r--app/assets/javascripts/content_editor/components/loading_indicator.vue1
-rw-r--r--app/assets/javascripts/content_editor/extensions/code_block_highlight.js42
-rw-r--r--app/assets/javascripts/content_editor/services/code_block_language_loader.js35
-rw-r--r--app/assets/javascripts/content_editor/services/content_editor.js19
-rw-r--r--app/assets/javascripts/content_editor/services/create_content_editor.js9
5 files changed, 96 insertions, 10 deletions
diff --git a/app/assets/javascripts/content_editor/components/loading_indicator.vue b/app/assets/javascripts/content_editor/components/loading_indicator.vue
index 5b9383d6e11..620324adb06 100644
--- a/app/assets/javascripts/content_editor/components/loading_indicator.vue
+++ b/app/assets/javascripts/content_editor/components/loading_indicator.vue
@@ -30,6 +30,7 @@ export default {
>
<div
v-if="isLoading"
+ data-testid="content-editor-loading-indicator"
class="gl-w-full gl-display-flex gl-justify-content-center gl-align-items-center gl-absolute gl-top-0 gl-bottom-0"
>
<div class="gl-bg-white gl-absolute gl-w-full gl-h-full gl-opacity-3"></div>
diff --git a/app/assets/javascripts/content_editor/extensions/code_block_highlight.js b/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
index 204ac07d401..74f620b57b6 100644
--- a/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
+++ b/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
@@ -1,11 +1,33 @@
import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight';
-import { lowlight } from 'lowlight/lib/all';
+import { textblockTypeInputRule } from '@tiptap/core';
+import { isFunction } from 'lodash';
const extractLanguage = (element) => element.getAttribute('lang');
+const backtickInputRegex = /^```([a-z]+)?[\s\n]$/;
+const tildeInputRegex = /^~~~([a-z]+)?[\s\n]$/;
+
+const loadLanguageFromInputRule = (languageLoader) => (match) => {
+ const language = match[1];
+
+ if (isFunction(languageLoader?.loadLanguages)) {
+ languageLoader.loadLanguages([language]);
+ }
+
+ return {
+ language,
+ };
+};
export default CodeBlockLowlight.extend({
isolating: true,
+ addOptions() {
+ return {
+ ...this.parent?.(),
+ languageLoader: {},
+ };
+ },
+
addAttributes() {
return {
language: {
@@ -18,6 +40,22 @@ export default CodeBlockLowlight.extend({
},
};
},
+ addInputRules() {
+ const { languageLoader } = this.options;
+
+ return [
+ textblockTypeInputRule({
+ find: backtickInputRegex,
+ type: this.type,
+ getAttributes: loadLanguageFromInputRule(languageLoader),
+ }),
+ textblockTypeInputRule({
+ find: tildeInputRegex,
+ type: this.type,
+ getAttributes: loadLanguageFromInputRule(languageLoader),
+ }),
+ ];
+ },
renderHTML({ HTMLAttributes }) {
return [
'pre',
@@ -28,6 +66,4 @@ export default CodeBlockLowlight.extend({
['code', {}, 0],
];
},
-}).configure({
- lowlight,
});
diff --git a/app/assets/javascripts/content_editor/services/code_block_language_loader.js b/app/assets/javascripts/content_editor/services/code_block_language_loader.js
new file mode 100644
index 00000000000..3c12cf614a5
--- /dev/null
+++ b/app/assets/javascripts/content_editor/services/code_block_language_loader.js
@@ -0,0 +1,35 @@
+export default class CodeBlockLanguageLoader {
+ constructor(lowlight) {
+ this.lowlight = lowlight;
+ }
+
+ isLanguageLoaded(language) {
+ return this.lowlight.registered(language);
+ }
+
+ loadLanguagesFromDOM(domTree) {
+ const languages = [];
+
+ domTree.querySelectorAll('pre').forEach((preElement) => {
+ languages.push(preElement.getAttribute('lang'));
+ });
+
+ return this.loadLanguages(languages);
+ }
+
+ loadLanguages(languageList = []) {
+ const loaders = languageList
+ .filter((languageName) => !this.isLanguageLoaded(languageName))
+ .map((languageName) => {
+ return import(
+ /* webpackChunkName: 'highlight.language.js' */ `highlight.js/lib/languages/${languageName}`
+ )
+ .then(({ default: language }) => {
+ this.lowlight.registerLanguage(languageName, language);
+ })
+ .catch(() => false);
+ });
+
+ return Promise.all(loaders);
+ }
+}
diff --git a/app/assets/javascripts/content_editor/services/content_editor.js b/app/assets/javascripts/content_editor/services/content_editor.js
index c5638da2daf..56badf965ee 100644
--- a/app/assets/javascripts/content_editor/services/content_editor.js
+++ b/app/assets/javascripts/content_editor/services/content_editor.js
@@ -3,11 +3,12 @@ import { LOADING_CONTENT_EVENT, LOADING_SUCCESS_EVENT, LOADING_ERROR_EVENT } fro
/* eslint-disable no-underscore-dangle */
export class ContentEditor {
- constructor({ tiptapEditor, serializer, deserializer, eventHub }) {
+ constructor({ tiptapEditor, serializer, deserializer, eventHub, languageLoader }) {
this._tiptapEditor = tiptapEditor;
this._serializer = serializer;
this._deserializer = deserializer;
this._eventHub = eventHub;
+ this._languageLoader = languageLoader;
}
get tiptapEditor() {
@@ -34,23 +35,33 @@ export class ContentEditor {
}
async setSerializedContent(serializedContent) {
- const { _tiptapEditor: editor, _deserializer: deserializer, _eventHub: eventHub } = this;
+ const {
+ _tiptapEditor: editor,
+ _deserializer: deserializer,
+ _eventHub: eventHub,
+ _languageLoader: languageLoader,
+ } = this;
const { doc, tr } = editor.state;
const selection = TextSelection.create(doc, 0, doc.content.size);
try {
eventHub.$emit(LOADING_CONTENT_EVENT);
- const { document } = await deserializer.deserialize({
+ const result = await deserializer.deserialize({
schema: editor.schema,
content: serializedContent,
});
- if (document) {
+ if (Object.keys(result).length !== 0) {
+ const { document, dom } = result;
+
+ await languageLoader.loadLanguagesFromDOM(dom);
+
tr.setSelection(selection)
.replaceSelectionWith(document, false)
.setMeta('preventUpdate', true);
editor.view.dispatch(tr);
}
+
eventHub.$emit(LOADING_SUCCESS_EVENT);
} catch (e) {
eventHub.$emit(LOADING_ERROR_EVENT, e);
diff --git a/app/assets/javascripts/content_editor/services/create_content_editor.js b/app/assets/javascripts/content_editor/services/create_content_editor.js
index d9d39a387d0..5b637eee176 100644
--- a/app/assets/javascripts/content_editor/services/create_content_editor.js
+++ b/app/assets/javascripts/content_editor/services/create_content_editor.js
@@ -1,5 +1,6 @@
import { Editor } from '@tiptap/vue-2';
import { isFunction } from 'lodash';
+import { lowlight } from 'lowlight/lib/core';
import eventHubFactory from '~/helpers/event_hub_factory';
import { PROVIDE_SERIALIZER_OR_RENDERER_ERROR } from '../constants';
import Attachment from '../extensions/attachment';
@@ -58,6 +59,7 @@ import { ContentEditor } from './content_editor';
import createMarkdownSerializer from './markdown_serializer';
import createMarkdownDeserializer from './markdown_deserializer';
import trackInputRulesAndShortcuts from './track_input_rules_and_shortcuts';
+import CodeBlockLanguageLoader from './code_block_language_loader';
const createTiptapEditor = ({ extensions = [], ...options } = {}) =>
new Editor({
@@ -83,6 +85,7 @@ export const createContentEditor = ({
const eventHub = eventHubFactory();
+ const languageLoader = new CodeBlockLanguageLoader(lowlight);
const builtInContentEditorExtensions = [
Attachment.configure({ uploadsPath, renderMarkdown, eventHub }),
Audio,
@@ -91,7 +94,7 @@ export const createContentEditor = ({
BulletList,
Code,
ColorChip,
- CodeBlockHighlight,
+ CodeBlockHighlight.configure({ lowlight, languageLoader }),
DescriptionItem,
DescriptionList,
Details,
@@ -105,7 +108,7 @@ export const createContentEditor = ({
FootnoteDefinition,
FootnoteReference,
FootnotesSection,
- Frontmatter,
+ Frontmatter.configure({ lowlight }),
Gapcursor,
HardBreak,
Heading,
@@ -144,5 +147,5 @@ export const createContentEditor = ({
const serializer = createMarkdownSerializer({ serializerConfig });
const deserializer = createMarkdownDeserializer({ render: renderMarkdown });
- return new ContentEditor({ tiptapEditor, serializer, eventHub, deserializer });
+ return new ContentEditor({ tiptapEditor, serializer, eventHub, deserializer, languageLoader });
};