From 235f755398a6a199b22e4924e7a81670b0dfdaef Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 20 Nov 2023 21:14:21 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .../javascripts/behaviors/markdown/copy_as_gfm.js | 4 +- .../javascripts/groups/components/group_item.vue | 4 +- .../javascripts/groups/components/item_stats.vue | 2 +- .../repository/components/blob_content_viewer.vue | 2 +- .../repository/components/blob_viewers/index.js | 8 ++-- .../repository/mixins/highlight_mixin.js | 23 +++++++-- .../components/source_viewer/constants.js | 1 + .../components/source_viewer/source_viewer_new.vue | 19 +++++++- .../source_viewer/workers/highlight_utils.js | 36 +++++++++++--- .../source_viewer/workers/highlight_worker.js | 4 +- .../components/user_access_role_badge.vue | 9 +++- .../shared/work_item_link_child_contents.vue | 13 ++--- .../work_item_links/work_item_link_child.vue | 6 +-- .../components/work_item_links/work_item_tree.vue | 56 +++++++++++----------- app/services/metrics_service.rb | 6 +-- 15 files changed, 121 insertions(+), 72 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js index 36317444af9..72aae254584 100644 --- a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js +++ b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js @@ -152,7 +152,9 @@ export class CopyAsGFM { if (lineElements.length > 0) { for (let i = 0; i < lineElements.length; i += 1) { const lineElement = lineElements[i]; - codeElement.appendChild(lineElement); + const line = document.createElement('span'); + line.append(...lineElement.childNodes); + codeElement.appendChild(line); codeElement.appendChild(document.createTextNode('\n')); } } else { diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue index af1af86d0c4..3a08e3e546f 100644 --- a/app/assets/javascripts/groups/components/group_item.vue +++ b/app/assets/javascripts/groups/components/group_item.vue @@ -243,7 +243,7 @@ export default { - + {{ group.permission }} -
+
-
+
diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue index 97a1cbda5d0..264dbff525b 100644 --- a/app/assets/javascripts/repository/components/blob_content_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -79,7 +79,7 @@ export default { const urlHash = getLocationHash(); // If there is a code line hash in the URL we render with the simple viewer const useSimpleViewer = usePlain || urlHash?.startsWith('L') || !this.hasRichViewer; - this.initHighlightWorker(this.blobInfo); + this.initHighlightWorker(this.blobInfo, this.isUsingLfs); this.switchViewer(useSimpleViewer ? SIMPLE_BLOB_VIEWER : RICH_BLOB_VIEWER); // By default, if present, use the rich viewer to render }, error() { diff --git a/app/assets/javascripts/repository/components/blob_viewers/index.js b/app/assets/javascripts/repository/components/blob_viewers/index.js index d434700b29f..016f7f9fe43 100644 --- a/app/assets/javascripts/repository/components/blob_viewers/index.js +++ b/app/assets/javascripts/repository/components/blob_viewers/index.js @@ -1,4 +1,4 @@ -import { TEXT_FILE_TYPE, JSON_LANGUAGE } from '../../constants'; +import { TEXT_FILE_TYPE } from '../../constants'; export const viewers = { csv: () => import('./csv_viewer.vue'), @@ -17,12 +17,10 @@ export const viewers = { geo_json: () => import('./geo_json/geo_json_viewer.vue'), }; -export const loadViewer = (type, isUsingLfs, hljsWorkerEnabled, language) => { +export const loadViewer = (type, isUsingLfs, hljsWorkerEnabled) => { let viewer = viewers[type]; - if (hljsWorkerEnabled && language === JSON_LANGUAGE && type === TEXT_FILE_TYPE) { - // The New Source Viewer currently only supports JSON files. - // More language support will be added in: https://gitlab.com/gitlab-org/gitlab/-/issues/415753 + if (hljsWorkerEnabled && type === TEXT_FILE_TYPE) { viewer = () => import('~/vue_shared/components/source_viewer/source_viewer_new.vue'); } diff --git a/app/assets/javascripts/repository/mixins/highlight_mixin.js b/app/assets/javascripts/repository/mixins/highlight_mixin.js index 5b6f68681bb..fa4f0f48512 100644 --- a/app/assets/javascripts/repository/mixins/highlight_mixin.js +++ b/app/assets/javascripts/repository/mixins/highlight_mixin.js @@ -8,6 +8,8 @@ import { splitIntoChunks } from '~/vue_shared/components/source_viewer/workers/h import LineHighlighter from '~/blob/line_highlighter'; import languageLoader from '~/content_editor/services/highlight_js_language_loader'; import Tracking from '~/tracking'; +import axios from '~/lib/utils/axios_utils'; +import { TEXT_FILE_TYPE } from '../constants'; /* * This mixin is intended to be used as an interface between our highlight worker and Vue components @@ -36,14 +38,29 @@ export default { this.trackEvent(EVENT_LABEL_FALLBACK, language); this?.onError(); }, - initHighlightWorker({ rawTextBlob, language, fileType }) { - if (language !== 'json' || !this.glFeatures.highlightJsWorker) return; + async handleLFSBlob(externalStorageUrl, rawPath, language) { + await axios + .get(externalStorageUrl || rawPath) + .then(({ data }) => this.instructWorker(data, language)) + .catch(() => this.$emit('error')); + }, + initHighlightWorker(blob, isUsingLfs) { + const { rawTextBlob, language, fileType, externalStorageUrl, rawPath, simpleViewer } = blob; + + if (!this.glFeatures.highlightJsWorker || simpleViewer?.fileType !== TEXT_FILE_TYPE) return; if (this.isUnsupportedLanguage(language)) { this.handleUnsupportedLanguage(language); return; } + this.highlightWorker.onmessage = this.handleWorkerMessage; + + if (isUsingLfs) { + this.handleLFSBlob(externalStorageUrl, rawPath, language); + return; + } + /* * We want to start rendering content as soon as possible, but highlighting large amounts of * content can take long, so we render the content in phases: @@ -64,8 +81,6 @@ export default { this.chunks = splitIntoChunks(language, firstSeventyLines); - this.highlightWorker.onmessage = this.handleWorkerMessage; - // Instruct the worker to highlight the first 70 lines ASAP, this improves perceived performance. this.instructWorker(firstSeventyLines, language); diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/constants.js b/app/assets/javascripts/vue_shared/components/source_viewer/constants.js index 582093e5739..47b802d9d17 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/constants.js +++ b/app/assets/javascripts/vue_shared/components/source_viewer/constants.js @@ -14,6 +14,7 @@ export const ROUGE_TO_HLJS_LANGUAGE_MAP = { clean: 'clean', clojure: 'clojure', cmake: 'cmake', + codeowners: 'codeowners', coffeescript: 'coffeescript', coq: 'coq', cpp: 'cpp', diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue index dcefa66c403..7ced12952dd 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue +++ b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue @@ -5,7 +5,7 @@ import SafeHtml from '~/vue_shared/directives/safe_html'; import Tracking from '~/tracking'; import addBlobLinksTracking from '~/blob/blob_links_tracking'; import LineHighlighter from '~/blob/line_highlighter'; -import { EVENT_ACTION, EVENT_LABEL_VIEWER } from './constants'; +import { EVENT_ACTION, EVENT_LABEL_VIEWER, CODEOWNERS_FILE_NAME } from './constants'; import Chunk from './components/chunk_new.vue'; import Blame from './components/blame_info.vue'; import { calculateBlameOffset, shouldRender, toggleBlameClasses } from './utils'; @@ -21,6 +21,7 @@ export default { components: { Chunk, Blame, + CodeownersValidation: () => import('ee_component/blob/components/codeowners_validation.vue'), }, directives: { SafeHtml, @@ -45,6 +46,10 @@ export default { type: String, required: true, }, + currentRef: { + type: String, + required: true, + }, }, data() { return { @@ -66,6 +71,9 @@ export default { return result; }, []); }, + isCodeownersFile() { + return this.blob.name === CODEOWNERS_FILE_NAME; + }, }, watch: { showBlame: { @@ -136,11 +144,18 @@ export default {
+ { - // The Highlight Worker is currently scoped to JSON files. - // See the following issue for more: https://gitlab.com/gitlab-org/gitlab/-/issues/415753 - hljs.registerLanguage(language, json); +const loadLanguage = async (language) => { + const languageDefinition = await languageLoader[language](); + hljs.registerLanguage(language, languageDefinition.default); +}; + +const loadSubLanguages = async (languageDefinition) => { + // Some files can contain sub-languages (i.e., Svelte); this ensures that sub-languages are also loaded + if (!languageDefinition?.contains) return; + + // generate list of languages to load + const languages = new Set( + languageDefinition.contains + .filter((component) => Boolean(component.subLanguage)) + .map((component) => component.subLanguage), + ); + + if (languageDefinition.subLanguage) { + languages.add(languageDefinition.subLanguage); + } + + await Promise.all([...languages].map(loadLanguage)); +}; + +const initHighlightJs = async (fileType, content, language) => { registerPlugins(hljs, fileType, content, true); + await loadLanguage(language); + await loadSubLanguages(hljs.getLanguage(language)); }; const splitByLineBreaks = (content = '') => content.split(/\r?\n/); @@ -35,12 +57,12 @@ const splitIntoChunks = (language, rawContent, highlightedContent) => { return result; }; -const highlight = (fileType, rawContent, lang) => { +const highlight = async (fileType, rawContent, lang) => { const language = ROUGE_TO_HLJS_LANGUAGE_MAP[lang.toLowerCase()]; let result; if (language) { - initHighlightJs(fileType, rawContent, language); + await initHighlightJs(fileType, rawContent, language); const highlightedContent = hljs.highlight(rawContent, { language }).value; result = splitIntoChunks(language, rawContent, highlightedContent); } diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/workers/highlight_worker.js b/app/assets/javascripts/vue_shared/components/source_viewer/workers/highlight_worker.js index 535e857d7a9..49afaba3d2f 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/workers/highlight_worker.js +++ b/app/assets/javascripts/vue_shared/components/source_viewer/workers/highlight_worker.js @@ -4,7 +4,7 @@ import { highlight } from './highlight_utils'; * A webworker for highlighting large amounts of content with Highlight.js */ // eslint-disable-next-line no-restricted-globals -self.addEventListener('message', ({ data: { fileType, content, language } }) => { +self.addEventListener('message', async ({ data: { fileType, content, language } }) => { // eslint-disable-next-line no-restricted-globals - self.postMessage(highlight(fileType, content, language)); + self.postMessage(await highlight(fileType, content, language)); }); diff --git a/app/assets/javascripts/vue_shared/components/user_access_role_badge.vue b/app/assets/javascripts/vue_shared/components/user_access_role_badge.vue index e5558c038b3..43e35f2b1f0 100644 --- a/app/assets/javascripts/vue_shared/components/user_access_role_badge.vue +++ b/app/assets/javascripts/vue_shared/components/user_access_role_badge.vue @@ -12,11 +12,18 @@ export default { components: { GlBadge, }, + props: { + size: { + type: String, + required: false, + default: 'md', + }, + }, }; diff --git a/app/assets/javascripts/work_items/components/shared/work_item_link_child_contents.vue b/app/assets/javascripts/work_items/components/shared/work_item_link_child_contents.vue index cbe7de4abcd..503328f7b03 100644 --- a/app/assets/javascripts/work_items/components/shared/work_item_link_child_contents.vue +++ b/app/assets/javascripts/work_items/components/shared/work_item_link_child_contents.vue @@ -58,11 +58,6 @@ export default { default: true, }, }, - data() { - return { - isFocused: false, - }; - }, computed: { labels() { return this.metadataWidgets[WIDGET_TYPE_LABELS]?.labels?.nodes || []; @@ -117,7 +112,7 @@ export default { return false; }, showRemove() { - return this.canUpdate && this.isFocused; + return this.canUpdate; }, displayLabels() { return this.showLabels && this.labels.length; @@ -135,10 +130,6 @@ export default {