diff options
Diffstat (limited to 'app/assets')
13 files changed, 495 insertions, 102 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue new file mode 100644 index 00000000000..6c409688468 --- /dev/null +++ b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue @@ -0,0 +1,246 @@ +<script> +import createFlash from '~/flash'; +import { s__ } from '~/locale'; +import { mapState, mapActions } from 'vuex'; +import Icon from '~/vue_shared/components/icon.vue'; +import { UNFOLD_COUNT } from '../constants'; +import * as utils from '../store/utils'; +import tooltip from '../../vue_shared/directives/tooltip'; + +const EXPAND_ALL = 0; +const EXPAND_UP = 1; +const EXPAND_DOWN = 2; + +export default { + directives: { + tooltip, + }, + components: { + Icon, + }, + props: { + fileHash: { + type: String, + required: true, + }, + contextLinesPath: { + type: String, + required: true, + }, + line: { + type: Object, + required: true, + }, + isTop: { + type: Boolean, + required: false, + default: false, + }, + isBottom: { + type: Boolean, + required: false, + default: false, + }, + colspan: { + type: Number, + required: false, + default: 3, + }, + }, + computed: { + ...mapState({ + diffViewType: state => state.diffs.diffViewType, + diffFiles: state => state.diffs.diffFiles, + }), + canExpandUp() { + return !this.isBottom; + }, + canExpandDown() { + return this.isBottom || !this.isTop; + }, + }, + created() { + this.EXPAND_DOWN = EXPAND_DOWN; + this.EXPAND_UP = EXPAND_UP; + }, + methods: { + ...mapActions('diffs', ['loadMoreLines']), + getPrevLineNumber(oldLineNumber, newLineNumber) { + const diffFile = utils.findDiffFile(this.diffFiles, this.fileHash); + const indexForInline = utils.findIndexInInlineLines(diffFile.highlighted_diff_lines, { + oldLineNumber, + newLineNumber, + }); + const prevLine = diffFile.highlighted_diff_lines[indexForInline - 2]; + return (prevLine && prevLine.new_line) || 0; + }, + callLoadMoreLines( + endpoint, + params, + lineNumbers, + fileHash, + isExpandDown = false, + nextLineNumbers = {}, + ) { + this.loadMoreLines({ endpoint, params, lineNumbers, fileHash, isExpandDown, nextLineNumbers }) + .then(() => { + this.isRequesting = false; + }) + .catch(() => { + createFlash(s__('Diffs|Something went wrong while fetching diff lines.')); + this.isRequesting = false; + }); + }, + handleExpandLines(type = EXPAND_ALL) { + if (this.isRequesting) { + return; + } + + this.isRequesting = true; + const endpoint = this.contextLinesPath; + const { fileHash } = this; + const view = this.diffViewType; + const oldLineNumber = this.line.meta_data.old_pos || 0; + const newLineNumber = this.line.meta_data.new_pos || 0; + const offset = newLineNumber - oldLineNumber; + + const expandOptions = { endpoint, fileHash, view, oldLineNumber, newLineNumber, offset }; + + if (type === EXPAND_UP) { + this.handleExpandUpLines(expandOptions); + } else if (type === EXPAND_DOWN) { + this.handleExpandDownLines(expandOptions); + } else { + this.handleExpandAllLines(expandOptions); + } + }, + handleExpandUpLines(expandOptions = EXPAND_ALL) { + const { endpoint, fileHash, view, oldLineNumber, newLineNumber, offset } = expandOptions; + + const bottom = this.isBottom; + const lineNumber = newLineNumber - 1; + const to = lineNumber; + let since = lineNumber - UNFOLD_COUNT; + let unfold = true; + + const prevLineNumber = this.getPrevLineNumber(oldLineNumber, newLineNumber); + if (since <= prevLineNumber + 1) { + since = prevLineNumber + 1; + unfold = false; + } + + const params = { since, to, bottom, offset, unfold, view }; + const lineNumbers = { oldLineNumber, newLineNumber }; + this.callLoadMoreLines(endpoint, params, lineNumbers, fileHash); + }, + handleExpandDownLines(expandOptions) { + const { + endpoint, + fileHash, + view, + oldLineNumber: metaOldPos, + newLineNumber: metaNewPos, + offset, + } = expandOptions; + + const bottom = true; + const nextLineNumbers = { + old_line: metaOldPos, + new_line: metaNewPos, + }; + + let unfold = true; + let isExpandDown = false; + let oldLineNumber = metaOldPos; + let newLineNumber = metaNewPos; + let lineNumber = metaNewPos + 1; + let since = lineNumber; + let to = lineNumber + UNFOLD_COUNT; + + if (!this.isBottom) { + const prevLineNumber = this.getPrevLineNumber(oldLineNumber, newLineNumber); + + isExpandDown = true; + oldLineNumber = prevLineNumber - offset; + newLineNumber = prevLineNumber; + lineNumber = prevLineNumber + 1; + since = lineNumber; + to = lineNumber + UNFOLD_COUNT; + + if (to >= metaNewPos) { + to = metaNewPos - 1; + unfold = false; + } + } + + const params = { since, to, bottom, offset, unfold, view }; + const lineNumbers = { oldLineNumber, newLineNumber }; + this.callLoadMoreLines( + endpoint, + params, + lineNumbers, + fileHash, + isExpandDown, + nextLineNumbers, + ); + }, + handleExpandAllLines(expandOptions) { + const { endpoint, fileHash, view, oldLineNumber, newLineNumber, offset } = expandOptions; + const bottom = this.isBottom; + const unfold = false; + let since; + let to; + + if (this.isTop) { + since = 1; + to = newLineNumber - 1; + } else if (bottom) { + since = newLineNumber + 1; + to = -1; + } else { + const prevLineNumber = this.getPrevLineNumber(oldLineNumber, newLineNumber); + since = prevLineNumber + 1; + to = newLineNumber - 1; + } + + const params = { since, to, bottom, offset, unfold, view }; + const lineNumbers = { oldLineNumber, newLineNumber }; + this.callLoadMoreLines(endpoint, params, lineNumbers, fileHash); + }, + }, +}; +</script> + +<template> + <td :colspan="colspan"> + <div class="content"> + <a + v-if="canExpandUp" + v-tooltip + class="cursor-pointer js-unfold unfold-icon" + data-placement="top" + data-container="body" + :title="__('Expand up')" + @click="handleExpandLines(EXPAND_UP)" + > + <!-- TODO: remove style & replace with correct icon, waiting for MR https://gitlab.com/gitlab-org/gitlab-design/issues/499 --> + <icon :size="12" name="expand-left" aria-hidden="true" style="transform: rotate(270deg);" /> + </a> + <a class="mx-2 cursor-pointer js-unfold-all" @click="handleExpandLines()"> + <span>{{ s__('Diffs|Show all lines') }}</span> + </a> + <a + v-if="canExpandDown" + v-tooltip + class="cursor-pointer js-unfold-down has-tooltip unfold-icon" + data-placement="top" + data-container="body" + :title="__('Expand down')" + @click="handleExpandLines(EXPAND_DOWN)" + > + <!-- TODO: remove style & replace with correct icon, waiting for MR https://gitlab.com/gitlab-org/gitlab-design/issues/499 --> + <icon :size="12" name="expand-left" aria-hidden="true" style="transform: rotate(90deg);" /> + </a> + </div> + </td> +</template> diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue index 351110f0a87..434d554d148 100644 --- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue +++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue @@ -1,11 +1,8 @@ <script> -import createFlash from '~/flash'; -import { s__ } from '~/locale'; import { mapState, mapGetters, mapActions } from 'vuex'; import Icon from '~/vue_shared/components/icon.vue'; import DiffGutterAvatars from './diff_gutter_avatars.vue'; -import { LINE_POSITION_RIGHT, UNFOLD_COUNT } from '../constants'; -import * as utils from '../store/utils'; +import { LINE_POSITION_RIGHT } from '../constants'; export default { components: { @@ -115,89 +112,36 @@ export default { handleCommentButton() { this.showCommentForm({ lineCode: this.line.line_code, fileHash: this.fileHash }); }, - handleLoadMoreLines() { - if (this.isRequesting) { - return; - } - - this.isRequesting = true; - const endpoint = this.contextLinesPath; - const oldLineNumber = this.line.meta_data.old_pos || 0; - const newLineNumber = this.line.meta_data.new_pos || 0; - const offset = newLineNumber - oldLineNumber; - const bottom = this.isBottom; - const { fileHash } = this; - const view = this.diffViewType; - let unfold = true; - let lineNumber = newLineNumber - 1; - let since = lineNumber - UNFOLD_COUNT; - let to = lineNumber; - - if (bottom) { - lineNumber = newLineNumber + 1; - since = lineNumber; - to = lineNumber + UNFOLD_COUNT; - } else { - const diffFile = utils.findDiffFile(this.diffFiles, this.fileHash); - const indexForInline = utils.findIndexInInlineLines(diffFile.highlighted_diff_lines, { - oldLineNumber, - newLineNumber, - }); - const prevLine = diffFile.highlighted_diff_lines[indexForInline - 2]; - const prevLineNumber = (prevLine && prevLine.new_line) || 0; - - if (since <= prevLineNumber + 1) { - since = prevLineNumber + 1; - unfold = false; - } - } - - const params = { since, to, bottom, offset, unfold, view }; - const lineNumbers = { oldLineNumber, newLineNumber }; - this.loadMoreLines({ endpoint, params, lineNumbers, fileHash }) - .then(() => { - this.isRequesting = false; - }) - .catch(() => { - createFlash(s__('Diffs|Something went wrong while fetching diff lines.')); - this.isRequesting = false; - }); - }, }, }; </script> <template> <div> - <span v-if="isMatchLine" class="context-cell" role="button" @click="handleLoadMoreLines" - >...</span + <button + v-if="shouldRenderCommentButton" + v-show="shouldShowCommentButton" + type="button" + class="add-diff-note js-add-diff-note-button qa-diff-comment" + title="Add a comment to this line" + @click="handleCommentButton" + > + <icon :size="12" name="comment" /> + </button> + <a + v-if="lineNumber" + :data-linenumber="lineNumber" + :href="lineHref" + @click="setHighlightedRow(lineCode)" > - <template v-else> - <button - v-if="shouldRenderCommentButton" - v-show="shouldShowCommentButton" - type="button" - class="add-diff-note js-add-diff-note-button qa-diff-comment" - title="Add a comment to this line" - @click="handleCommentButton" - > - <icon :size="12" name="comment" /> - </button> - <a - v-if="lineNumber" - :data-linenumber="lineNumber" - :href="lineHref" - @click="setHighlightedRow(lineCode)" - > - </a> - <diff-gutter-avatars - v-if="shouldShowAvatarsOnGutter" - :discussions="line.discussions" - :discussions-expanded="line.discussionsExpanded" - @toggleLineDiscussions=" - toggleLineDiscussions({ lineCode, fileHash, expanded: !line.discussionsExpanded }) - " - /> - </template> + </a> + <diff-gutter-avatars + v-if="shouldShowAvatarsOnGutter" + :discussions="line.discussions" + :discussions-expanded="line.discussionsExpanded" + @toggleLineDiscussions=" + toggleLineDiscussions({ lineCode, fileHash, expanded: !line.discussionsExpanded }) + " + /> </div> </template> diff --git a/app/assets/javascripts/diffs/components/inline_diff_expansion_row.vue b/app/assets/javascripts/diffs/components/inline_diff_expansion_row.vue new file mode 100644 index 00000000000..6e732727f42 --- /dev/null +++ b/app/assets/javascripts/diffs/components/inline_diff_expansion_row.vue @@ -0,0 +1,53 @@ +<script> +import Icon from '~/vue_shared/components/icon.vue'; +import DiffExpansionCell from './diff_expansion_cell.vue'; +import { MATCH_LINE_TYPE } from '../constants'; + +export default { + components: { + Icon, + DiffExpansionCell, + }, + props: { + fileHash: { + type: String, + required: true, + }, + contextLinesPath: { + type: String, + required: true, + }, + line: { + type: Object, + required: true, + }, + isTop: { + type: Boolean, + required: false, + default: false, + }, + isBottom: { + type: Boolean, + required: false, + default: false, + }, + }, + computed: { + isMatchLine() { + return this.line.type === MATCH_LINE_TYPE; + }, + }, +}; +</script> + +<template> + <tr v-if="isMatchLine" class="line_expansion match"> + <diff-expansion-cell + :file-hash="fileHash" + :context-lines-path="contextLinesPath" + :line="line" + :is-top="isTop" + :is-bottom="isBottom" + /> + </tr> +</template> diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue index 2d5262baeec..55a8df43c62 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue @@ -2,6 +2,7 @@ import { mapActions, mapState } from 'vuex'; import DiffTableCell from './diff_table_cell.vue'; import { + MATCH_LINE_TYPE, NEW_LINE_TYPE, OLD_LINE_TYPE, CONTEXT_LINE_TYPE, @@ -58,6 +59,9 @@ export default { inlineRowId() { return this.line.line_code || `${this.fileHash}_${this.line.old_line}_${this.line.new_line}`; }, + isMatchLine() { + return this.line.type === MATCH_LINE_TYPE; + }, }, created() { this.newLineType = NEW_LINE_TYPE; @@ -81,6 +85,7 @@ export default { <template> <tr + v-if="!isMatchLine" :id="inlineRowId" :class="classNameMap" class="line_holder" diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue index b2bc3d9914f..aee01409db7 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_view.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue @@ -3,6 +3,7 @@ import { mapGetters } from 'vuex'; import draftCommentsMixin from 'ee_else_ce/diffs/mixins/draft_comments'; import inlineDiffTableRow from './inline_diff_table_row.vue'; import inlineDiffCommentRow from './inline_diff_comment_row.vue'; +import inlineDiffExpansionRow from './inline_diff_expansion_row.vue'; export default { components: { @@ -10,6 +11,7 @@ export default { inlineDiffTableRow, InlineDraftCommentRow: () => import('ee_component/batch_comments/components/inline_draft_comment_row.vue'), + inlineDiffExpansionRow, }, mixins: [draftCommentsMixin], props: { @@ -43,10 +45,24 @@ export default { :data-commit-id="commitId" class="code diff-wrap-lines js-syntax-highlight text-file js-diff-inline-view" > + <!-- Need to insert an empty row to solve "table-layout:fixed" equal width when expansion row is the first line --> + <tr> + <td style="width: 50px;"></td> + <td style="width: 50px;"></td> + <td></td> + </tr> <tbody> <template v-for="(line, index) in diffLines"> + <inline-diff-expansion-row + :key="`expand-${index}`" + :file-hash="diffFile.file_hash" + :context-lines-path="diffFile.context_lines_path" + :line="line" + :is-top="index === 0" + :is-bottom="index + 1 === diffLinesLength" + /> <inline-diff-table-row - :key="line.line_code" + :key="`${line.line_code || index}`" :file-hash="diffFile.file_hash" :context-lines-path="diffFile.context_lines_path" :line="line" diff --git a/app/assets/javascripts/diffs/components/parallel_diff_expansion_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_expansion_row.vue new file mode 100644 index 00000000000..c1b30eab199 --- /dev/null +++ b/app/assets/javascripts/diffs/components/parallel_diff_expansion_row.vue @@ -0,0 +1,56 @@ +<script> +import { MATCH_LINE_TYPE } from '../constants'; +import DiffExpansionCell from './diff_expansion_cell.vue'; + +export default { + components: { + DiffExpansionCell, + }, + props: { + fileHash: { + type: String, + required: true, + }, + contextLinesPath: { + type: String, + required: true, + }, + line: { + type: Object, + required: true, + }, + isTop: { + type: Boolean, + required: false, + default: false, + }, + isBottom: { + type: Boolean, + required: false, + default: false, + }, + }, + computed: { + isMatchLineLeft() { + return this.line.left && this.line.left.type === MATCH_LINE_TYPE; + }, + isMatchLineRight() { + return this.line.right && this.line.right.type === MATCH_LINE_TYPE; + }, + }, +}; +</script> +<template> + <tr class="line_expansion match"> + <template v-if="isMatchLineLeft || isMatchLineRight"> + <diff-expansion-cell + :file-hash="fileHash" + :context-lines-path="contextLinesPath" + :line="line.left" + :is-top="isTop" + :is-bottom="isBottom" + :colspan="4" + /> + </template> + </tr> +</template> diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue index c60246bf8ef..4c95d618b0f 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue @@ -3,6 +3,7 @@ import { mapActions, mapState } from 'vuex'; import $ from 'jquery'; import DiffTableCell from './diff_table_cell.vue'; import { + MATCH_LINE_TYPE, NEW_LINE_TYPE, OLD_LINE_TYPE, CONTEXT_LINE_TYPE, @@ -75,6 +76,12 @@ export default { }, ]; }, + isMatchLineLeft() { + return this.line.left && this.line.left.type === MATCH_LINE_TYPE; + }, + isMatchLineRight() { + return this.line.right && this.line.right.type === MATCH_LINE_TYPE; + }, }, created() { this.newLineType = NEW_LINE_TYPE; @@ -122,7 +129,7 @@ export default { @mouseover="handleMouseMove" @mouseout="handleMouseMove" > - <template v-if="line.left"> + <template v-if="line.left && !isMatchLineLeft"> <diff-table-cell :file-hash="fileHash" :context-lines-path="contextLinesPath" @@ -148,7 +155,7 @@ export default { <td class="diff-line-num old_line empty-cell"></td> <td class="line_content parallel left-side empty-cell"></td> </template> - <template v-if="line.right"> + <template v-if="line.right && !isMatchLineRight"> <diff-table-cell :file-hash="fileHash" :context-lines-path="contextLinesPath" diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue index c477e68c33c..d400eb2c586 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue @@ -3,9 +3,11 @@ import { mapGetters } from 'vuex'; import draftCommentsMixin from 'ee_else_ce/diffs/mixins/draft_comments'; import parallelDiffTableRow from './parallel_diff_table_row.vue'; import parallelDiffCommentRow from './parallel_diff_comment_row.vue'; +import parallelDiffExpansionRow from './parallel_diff_expansion_row.vue'; export default { components: { + parallelDiffExpansionRow, parallelDiffTableRow, parallelDiffCommentRow, ParallelDraftCommentRow: () => @@ -43,8 +45,23 @@ export default { :data-commit-id="commitId" class="code diff-wrap-lines js-syntax-highlight text-file" > + <!-- Need to insert an empty row to solve "table-layout:fixed" equal width when expansion row is the first line --> + <tr> + <td style="width: 50px;"></td> + <td></td> + <td style="width: 50px;"></td> + <td></td> + </tr> <tbody> <template v-for="(line, index) in diffLines"> + <parallel-diff-expansion-row + :key="`expand-${index}`" + :file-hash="diffFile.file_hash" + :context-lines-path="diffFile.context_lines_path" + :line="line" + :is-top="index === 0" + :is-bottom="index + 1 === diffLinesLength" + /> <parallel-diff-table-row :key="line.line_code" :file-hash="diffFile.file_hash" diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index 32e0d8f42ee..8434ef5cc7a 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -183,7 +183,7 @@ export const cancelCommentForm = ({ commit }, { lineCode, fileHash }) => { }; export const loadMoreLines = ({ commit }, options) => { - const { endpoint, params, lineNumbers, fileHash } = options; + const { endpoint, params, lineNumbers, fileHash, isExpandDown, nextLineNumbers } = options; params.from_merge_request = true; @@ -195,6 +195,8 @@ export const loadMoreLines = ({ commit }, options) => { contextLines, params, fileHash, + isExpandDown, + nextLineNumbers, }); }); }; diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js index a66f205bbbd..a6915a46c00 100644 --- a/app/assets/javascripts/diffs/store/mutations.js +++ b/app/assets/javascripts/diffs/store/mutations.js @@ -71,18 +71,30 @@ export default { }, [types.ADD_CONTEXT_LINES](state, options) { - const { lineNumbers, contextLines, fileHash } = options; + const { lineNumbers, contextLines, fileHash, isExpandDown, nextLineNumbers } = options; const { bottom } = options.params; const diffFile = findDiffFile(state.diffFiles, fileHash); removeMatchLine(diffFile, lineNumbers, bottom); - const lines = addLineReferences(contextLines, lineNumbers, bottom).map(line => ({ - ...line, - line_code: line.line_code || `${fileHash}_${line.old_line}_${line.new_line}`, - discussions: line.discussions || [], - hasForm: false, - })); + const lines = addLineReferences( + contextLines, + lineNumbers, + bottom, + isExpandDown, + nextLineNumbers, + ).map(line => { + const lineCode = + line.type === 'match' + ? `${fileHash}_${line.meta_data.old_pos}_${line.meta_data.new_pos}_match` + : line.line_code || `${fileHash}_${line.old_line}_${line.new_line}`; + return { + ...line, + line_code: lineCode, + discussions: line.discussions || [], + hasForm: false, + }; + }); addContextLines({ inlineLines: diffFile.highlighted_diff_lines, @@ -90,6 +102,7 @@ export default { contextLines: lines, bottom, lineNumbers, + isExpandDown, }); }, diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js index 1c3ed84001c..d46bdea9b50 100644 --- a/app/assets/javascripts/diffs/store/utils.js +++ b/app/assets/javascripts/diffs/store/utils.js @@ -121,7 +121,7 @@ export function removeMatchLine(diffFile, lineNumbers, bottom) { diffFile.parallel_diff_lines.splice(indexForParallel + factor, 1); } -export function addLineReferences(lines, lineNumbers, bottom) { +export function addLineReferences(lines, lineNumbers, bottom, isExpandDown, nextLineNumbers) { const { oldLineNumber, newLineNumber } = lineNumbers; const lineCount = lines.length; let matchLineIndex = -1; @@ -135,15 +135,20 @@ export function addLineReferences(lines, lineNumbers, bottom) { new_line: bottom ? newLineNumber + index + 1 : newLineNumber + index - lineCount, }); } - return l; }); if (matchLineIndex > -1) { const line = linesWithNumbers[matchLineIndex]; - const targetLine = bottom - ? linesWithNumbers[matchLineIndex - 1] - : linesWithNumbers[matchLineIndex + 1]; + let targetLine; + + if (isExpandDown) { + targetLine = nextLineNumbers; + } else if (bottom) { + targetLine = linesWithNumbers[matchLineIndex - 1]; + } else { + targetLine = linesWithNumbers[matchLineIndex + 1]; + } Object.assign(line, { meta_data: { @@ -152,26 +157,27 @@ export function addLineReferences(lines, lineNumbers, bottom) { }, }); } - return linesWithNumbers; } export function addContextLines(options) { - const { inlineLines, parallelLines, contextLines, lineNumbers } = options; + const { inlineLines, parallelLines, contextLines, lineNumbers, isExpandDown } = options; const normalizedParallelLines = contextLines.map(line => ({ left: line, right: line, line_code: line.line_code, })); + const factor = isExpandDown ? 1 : 0; - if (options.bottom) { + if (!isExpandDown && options.bottom) { inlineLines.push(...contextLines); parallelLines.push(...normalizedParallelLines); } else { const inlineIndex = findIndexInInlineLines(inlineLines, lineNumbers); const parallelIndex = findIndexInParallelLines(parallelLines, lineNumbers); - inlineLines.splice(inlineIndex, 0, ...contextLines); - parallelLines.splice(parallelIndex, 0, ...normalizedParallelLines); + + inlineLines.splice(inlineIndex + factor, 0, ...contextLines); + parallelLines.splice(parallelIndex + factor, 0, ...normalizedParallelLines); } } diff --git a/app/assets/stylesheets/highlight/white_base.scss b/app/assets/stylesheets/highlight/white_base.scss index ee0ec94c636..b3974df8639 100644 --- a/app/assets/stylesheets/highlight/white_base.scss +++ b/app/assets/stylesheets/highlight/white_base.scss @@ -101,6 +101,26 @@ pre.code, color: $white-code-color; } +// Expansion line +.line_expansion { + background-color: $gray-light; + + td { + border-top: 1px solid $border-color; + border-bottom: 1px solid $border-color; + text-align: center; + } + + a { + color: $blue-600; + } + + .unfold-icon { + display: inline-block; + padding: 8px 0; + } +} + // Diff line .line_holder { &.match .line_content, diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 95ea49ad465..ffb27e54f34 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -408,6 +408,14 @@ table.code { table-layout: fixed; border-radius: 0 0 $border-radius-default $border-radius-default; + tr:first-of-type.line_expansion > td { + border-top: 0; + } + + tr:nth-last-of-type(2).line_expansion > td { + border-bottom: 0; + } + tr.line_holder td { line-height: $code-line-height; font-size: $code-font-size; |