diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-01-20 12:16:11 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-01-20 12:16:11 +0300 |
commit | edaa33dee2ff2f7ea3fac488d41558eb5f86d68c (patch) | |
tree | 11f143effbfeba52329fb7afbd05e6e2a3790241 /app/assets/javascripts/blob | |
parent | d8a5691316400a0f7ec4f83832698f1988eb27c1 (diff) |
Add latest changes from gitlab-org/gitlab@14-7-stable-eev14.7.0-rc42
Diffstat (limited to 'app/assets/javascripts/blob')
5 files changed, 196 insertions, 4 deletions
diff --git a/app/assets/javascripts/blob/blob_line_permalink_updater.js b/app/assets/javascripts/blob/blob_line_permalink_updater.js index 11089b299c5..a3dd241604d 100644 --- a/app/assets/javascripts/blob/blob_line_permalink_updater.js +++ b/app/assets/javascripts/blob/blob_line_permalink_updater.js @@ -1,6 +1,6 @@ import { getLocationHash } from '../lib/utils/url_utility'; -const lineNumberRe = /^L[0-9]+/; +const lineNumberRe = /^(L|LC)[0-9]+/; const updateLineNumbersOnBlobPermalinks = (linksToUpdate) => { const hash = getLocationHash(); diff --git a/app/assets/javascripts/blob/components/blob_header.vue b/app/assets/javascripts/blob/components/blob_header.vue index 933ad448c77..1645469a218 100644 --- a/app/assets/javascripts/blob/components/blob_header.vue +++ b/app/assets/javascripts/blob/components/blob_header.vue @@ -81,7 +81,7 @@ export default { </blob-filepath> </div> - <div class="gl-display-none gl-sm-display-flex"> + <div class="gl-sm-display-flex file-actions"> <viewer-switcher v-if="showViewerSwitcher" v-model="viewer" /> <slot name="actions"></slot> diff --git a/app/assets/javascripts/blob/components/blob_header_filepath.vue b/app/assets/javascripts/blob/components/blob_header_filepath.vue index cb441a7e491..90d01358451 100644 --- a/app/assets/javascripts/blob/components/blob_header_filepath.vue +++ b/app/assets/javascripts/blob/components/blob_header_filepath.vue @@ -1,4 +1,5 @@ <script> +import { GlBadge } from '@gitlab/ui'; import { numberToHumanSize } from '~/lib/utils/number_utils'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import FileIcon from '~/vue_shared/components/file_icon.vue'; @@ -7,6 +8,7 @@ export default { components: { FileIcon, ClipboardButton, + GlBadge, }, props: { blob: { @@ -21,6 +23,9 @@ export default { gfmCopyText() { return `\`${this.blob.path}\``; }, + showLfsBadge() { + return this.blob.storedExternally && this.blob.externalStorage === 'lfs'; + }, }, }; </script> @@ -37,8 +42,6 @@ export default { > </template> - <small class="mr-2">{{ blobSize }}</small> - <clipboard-button :text="blob.path" :gfm="gfmCopyText" @@ -46,5 +49,9 @@ export default { category="tertiary" css-class="btn-clipboard btn-transparent lh-100 position-static" /> + + <small class="mr-2">{{ blobSize }}</small> + + <gl-badge v-if="showLfsBadge">{{ __('LFS') }}</gl-badge> </div> </template> diff --git a/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue b/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue index a5b594fbd88..b2546d47694 100644 --- a/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue +++ b/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue @@ -53,6 +53,8 @@ export default { icon="code" category="primary" variant="default" + class="js-blob-viewer-switch-btn" + data-viewer="simple" @click="switchToViewer($options.SIMPLE_BLOB_VIEWER)" /> <gl-button @@ -63,6 +65,8 @@ export default { icon="document" category="primary" variant="default" + class="js-blob-viewer-switch-btn" + data-viewer="rich" @click="switchToViewer($options.RICH_BLOB_VIEWER)" /> </gl-button-group> diff --git a/app/assets/javascripts/blob/line_highlighter.js b/app/assets/javascripts/blob/line_highlighter.js new file mode 100644 index 00000000000..a1f59aa1b54 --- /dev/null +++ b/app/assets/javascripts/blob/line_highlighter.js @@ -0,0 +1,181 @@ +/* eslint-disable func-names, no-underscore-dangle, no-param-reassign, consistent-return */ + +import $ from 'jquery'; +import { scrollToElement } from '~/lib/utils/common_utils'; + +// LineHighlighter +// +// Handles single- and multi-line selection and highlight for blob views. +// +// +// ### Example Markup +// +// <div id="blob-content-holder"> +// <div class="file-content"> +// <div class="line-numbers"> +// <a href="#L1" id="L1" data-line-number="1">1</a> +// <a href="#L2" id="L2" data-line-number="2">2</a> +// <a href="#L3" id="L3" data-line-number="3">3</a> +// <a href="#L4" id="L4" data-line-number="4">4</a> +// <a href="#L5" id="L5" data-line-number="5">5</a> +// </div> +// <pre class="code highlight"> +// <code> +// <span id="LC1" class="line">...</span> +// <span id="LC2" class="line">...</span> +// <span id="LC3" class="line">...</span> +// <span id="LC4" class="line">...</span> +// <span id="LC5" class="line">...</span> +// </code> +// </pre> +// </div> +// </div> +// + +const LineHighlighter = function (options = {}) { + options.highlightLineClass = options.highlightLineClass || 'hll'; + options.fileHolderSelector = options.fileHolderSelector || '.file-holder'; + options.scrollFileHolder = options.scrollFileHolder || false; + options.hash = options.hash || window.location.hash; + + this.options = options; + this._hash = options.hash; + this.highlightLineClass = options.highlightLineClass; + this.setHash = this.setHash.bind(this); + this.highlightLine = this.highlightLine.bind(this); + this.clickHandler = this.clickHandler.bind(this); + this.highlightHash = this.highlightHash.bind(this); + + this.bindEvents(); + this.highlightHash(); +}; + +LineHighlighter.prototype.bindEvents = function () { + const $fileHolder = $(this.options.fileHolderSelector); + + $fileHolder.on('click', 'a[data-line-number]', this.clickHandler); + $fileHolder.on('highlight:line', this.highlightHash); + window.addEventListener('hashchange', (e) => this.highlightHash(e.target.location.hash)); +}; + +LineHighlighter.prototype.highlightHash = function (newHash) { + let range; + if (newHash && typeof newHash === 'string') this._hash = newHash; + + this.clearHighlight(); + + if (this._hash !== '') { + range = this.hashToRange(this._hash); + if (range[0]) { + this.highlightRange(range); + const lineSelector = `#L${range[0]}`; + + scrollToElement(lineSelector, { + // Scroll to the first highlighted line on initial load + // Add an offset of -100 for some context + offset: -100, + }); + } + } +}; + +LineHighlighter.prototype.clickHandler = function (event) { + let range; + event.preventDefault(); + this.clearHighlight(); + const lineNumber = $(event.target).closest('a').data('lineNumber'); + const current = this.hashToRange(this._hash); + if (!(current[0] && event.shiftKey)) { + // If there's no current selection, or there is but Shift wasn't held, + // treat this like a single-line selection. + this.setHash(lineNumber); + return this.highlightLine(lineNumber); + } else if (event.shiftKey) { + if (lineNumber < current[0]) { + range = [lineNumber, current[0]]; + } else { + range = [current[0], lineNumber]; + } + this.setHash(range[0], range[1]); + return this.highlightRange(range); + } +}; + +LineHighlighter.prototype.clearHighlight = function () { + return $(`.${this.highlightLineClass}`).removeClass(this.highlightLineClass); +}; + +// Convert a URL hash String into line numbers +// +// hash - Hash String +// +// Examples: +// +// hashToRange('#L5') # => [5, null] +// hashToRange('#L5-15') # => [5, 15] +// hashToRange('#foo') # => [null, null] +// +// Returns an Array +LineHighlighter.prototype.hashToRange = function (hash) { + // ?L(\d+)(?:-L?(\d+))?$/) + const matches = hash.match(/^#?L(\d+)(?:-L?(\d+))?$/); + if (matches && matches.length) { + const first = parseInt(matches[1], 10); + const last = matches[2] ? parseInt(matches[2], 10) : null; + return [first, last]; + } + return [null, null]; +}; + +// Highlight a single line +// +// lineNumber - Line number to highlight +LineHighlighter.prototype.highlightLine = function (lineNumber) { + return $(`#LC${lineNumber}`).addClass(this.highlightLineClass); +}; + +// Highlight all lines within a range +// +// range - Array containing the starting and ending line numbers +LineHighlighter.prototype.highlightRange = function (range) { + if (range[1]) { + const results = []; + const ref = range[0] <= range[1] ? range : range.reverse(); + + for (let lineNumber = range[0]; lineNumber <= ref[1]; lineNumber += 1) { + results.push(this.highlightLine(lineNumber)); + } + + return results; + } + return this.highlightLine(range[0]); +}; + +// Set the URL hash string +LineHighlighter.prototype.setHash = function (firstLineNumber, lastLineNumber) { + let hash; + if (lastLineNumber) { + hash = `#L${firstLineNumber}-${lastLineNumber}`; + } else { + hash = `#L${firstLineNumber}`; + } + this._hash = hash; + return this.__setLocationHash__(hash); +}; + +// Make the actual hash change in the browser +// +// This method is stubbed in tests. +LineHighlighter.prototype.__setLocationHash__ = function (value) { + return window.history.pushState( + { + url: value, + // We're using pushState instead of assigning location.hash directly to + // prevent the page from scrolling on the hashchange event + }, + document.title, + value, + ); +}; + +export default LineHighlighter; |