diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index e1ddefca99e..2f68897d8cb 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -183,6 +183,7 @@ setup-test-env: - tmp/tests/gitlab-workhorse/gitlab-workhorse - tmp/tests/gitlab-workhorse/gitlab-resize-image - tmp/tests/gitlab-workhorse/config.toml + - tmp/tests/gitlab-workhorse/WORKHORSE_TREE - tmp/tests/repositories/ - tmp/tests/second_storage/ when: always diff --git a/Gemfile b/Gemfile index 81294ebe39e..e09d4b90e01 100644 --- a/Gemfile +++ b/Gemfile @@ -293,7 +293,7 @@ gem 'autoprefixer-rails', '10.2.0.0' gem 'terser', '1.0.2' gem 'addressable', '~> 2.7' -gem 'gemojione', '~> 3.3' +gem 'gemojione', '~> 4.3.3' gem 'gon', '~> 6.2' gem 'request_store', '~> 1.5' gem 'base32', '~> 0.3.0' diff --git a/Gemfile.lock b/Gemfile.lock index 063ebc4424b..c3610207ec3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -412,7 +412,7 @@ GEM ruby-progressbar (~> 1.4) fuzzyurl (0.9.0) gemoji (3.0.1) - gemojione (3.3.0) + gemojione (4.3.3) json get_process_mem (0.2.5) ffi (~> 1.0) @@ -1414,7 +1414,7 @@ DEPENDENCIES fog-rackspace (~> 0.1.1) fugit (~> 1.2.1) fuubar (~> 2.2.0) - gemojione (~> 3.3) + gemojione (~> 4.3.3) gettext (~> 3.3) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) diff --git a/app/assets/images/emoji.png b/app/assets/images/emoji.png index 723c2c3f4c8..14bba8aa24e 100644 Binary files a/app/assets/images/emoji.png and b/app/assets/images/emoji.png differ diff --git a/app/assets/images/emoji@2x.png b/app/assets/images/emoji@2x.png index 987279c13cc..622d3a4ec2d 100644 Binary files a/app/assets/images/emoji@2x.png and b/app/assets/images/emoji@2x.png differ diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js index 7c8f6646c0d..6c6c7e6a6d2 100644 --- a/app/assets/javascripts/blob_edit/edit_blob.js +++ b/app/assets/javascripts/blob_edit/edit_blob.js @@ -43,7 +43,7 @@ export default class EditBlob { blobPath: fileNameEl.value, blobContent: editorEl.innerText, }); - this.editor.use(new FileTemplateExtension()); + this.editor.use(new FileTemplateExtension({ instance: this.editor })); fileNameEl.addEventListener('change', () => { this.editor.updateModelLanguage(fileNameEl.value); diff --git a/app/assets/javascripts/editor/extensions/editor_lite_extension_base.js b/app/assets/javascripts/editor/extensions/editor_lite_extension_base.js index 8d350068973..3d4f08131c1 100644 --- a/app/assets/javascripts/editor/extensions/editor_lite_extension_base.js +++ b/app/assets/javascripts/editor/extensions/editor_lite_extension_base.js @@ -1,11 +1,85 @@ -import { ERROR_INSTANCE_REQUIRED_FOR_EXTENSION } from '../constants'; +import { Range } from 'monaco-editor'; +import { ERROR_INSTANCE_REQUIRED_FOR_EXTENSION, EDITOR_TYPE_CODE } from '../constants'; + +const hashRegexp = new RegExp('#?L', 'g'); + +const createAnchor = (href) => { + const fragment = new DocumentFragment(); + const el = document.createElement('a'); + el.classList.add('link-anchor'); + el.href = href; + fragment.appendChild(el); + el.addEventListener('contextmenu', (e) => { + e.stopPropagation(); + }); + return fragment; +}; export class EditorLiteExtension { constructor({ instance, ...options } = {}) { if (instance) { Object.assign(instance, options); + EditorLiteExtension.highlightLines(instance); + if (instance.getEditorType && instance.getEditorType() === EDITOR_TYPE_CODE) { + EditorLiteExtension.setupLineLinking(instance); + } } else if (Object.entries(options).length) { throw new Error(ERROR_INSTANCE_REQUIRED_FOR_EXTENSION); } } + + static highlightLines(instance) { + const { hash } = window.location; + if (!hash) { + return; + } + const [start, end] = hash.replace(hashRegexp, '').split('-'); + let startLine = start ? parseInt(start, 10) : null; + let endLine = end ? parseInt(end, 10) : startLine; + if (endLine < startLine) { + [startLine, endLine] = [endLine, startLine]; + } + if (startLine) { + window.requestAnimationFrame(() => { + instance.revealLineInCenter(startLine); + Object.assign(instance, { + lineDecorations: instance.deltaDecorations( + [], + [ + { + range: new Range(startLine, 1, endLine, 1), + options: { isWholeLine: true, className: 'active-line-text' }, + }, + ], + ), + }); + }); + } + } + + static onMouseMoveHandler(e) { + const target = e.target.element; + if (target.classList.contains('line-numbers')) { + const lineNum = e.target.position.lineNumber; + const hrefAttr = `#L${lineNum}`; + let el = target.querySelector('a'); + if (!el) { + el = createAnchor(hrefAttr); + target.appendChild(el); + } + } + } + + static setupLineLinking(instance) { + instance.onMouseMove(EditorLiteExtension.onMouseMoveHandler); + instance.onMouseDown((e) => { + const isCorrectAnchor = e.target.element.classList.contains('link-anchor'); + if (!isCorrectAnchor) { + return; + } + if (instance.lineDecorations) { + instance.deltaDecorations(instance.lineDecorations, []); + } + }); + } } diff --git a/app/assets/javascripts/emoji/constants.js b/app/assets/javascripts/emoji/constants.js index e9f2272e759..2efbd1357f7 100644 --- a/app/assets/javascripts/emoji/constants.js +++ b/app/assets/javascripts/emoji/constants.js @@ -3,10 +3,10 @@ export const FREQUENTLY_USED_COOKIE_KEY = 'frequently_used_emojis'; export const CATEGORY_ICON_MAP = { [FREQUENTLY_USED_KEY]: 'history', - activity: 'dumbbell', people: 'smiley', nature: 'nature', food: 'food', + activity: 'dumbbell', travel: 'car', objects: 'object', symbols: 'heart', diff --git a/app/assets/javascripts/emoji/index.js b/app/assets/javascripts/emoji/index.js index d3b658a4020..03eb7abd513 100644 --- a/app/assets/javascripts/emoji/index.js +++ b/app/assets/javascripts/emoji/index.js @@ -8,7 +8,7 @@ let emojiMap = null; let validEmojiNames = null; export const FALLBACK_EMOJI_KEY = 'grey_question'; -export const EMOJI_VERSION = '1'; +export const EMOJI_VERSION = '2'; const isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe(); diff --git a/app/assets/javascripts/emoji/support/unicode_support_map.js b/app/assets/javascripts/emoji/support/unicode_support_map.js index fe3bc75f9fd..c97fc831ce4 100644 --- a/app/assets/javascripts/emoji/support/unicode_support_map.js +++ b/app/assets/javascripts/emoji/support/unicode_support_map.js @@ -1,6 +1,6 @@ import AccessorUtilities from '../../lib/utils/accessor'; -const GL_EMOJI_VERSION = '0.2.0'; +const GL_EMOJI_VERSION = '0.3.0'; const unicodeSupportTestMap = { // man, student (emojione does not have any of these yet), http://emojipedia.org/emoji-zwj-sequences/ @@ -25,6 +25,8 @@ const unicodeSupportTestMap = { // angel_tone5 '\u{1F47C}\u{1F3FF}', ], + // star_struck, http://emojipedia.org/unicode-9.0/ + '10.0': '\u{1F929}', // rofl, http://emojipedia.org/unicode-9.0/ '9.0': '\u{1F923}', // metal, http://emojipedia.org/unicode-8.0/ diff --git a/app/assets/javascripts/grafana_integration/components/grafana_integration.vue b/app/assets/javascripts/grafana_integration/components/grafana_integration.vue index ab13450bb1e..e941318dce0 100644 --- a/app/assets/javascripts/grafana_integration/components/grafana_integration.vue +++ b/app/assets/javascripts/grafana_integration/components/grafana_integration.vue @@ -61,7 +61,9 @@ export default {