diff --git a/app/assets/javascripts/merge_conflict_resolver.js.coffee b/app/assets/javascripts/merge_conflict_resolver.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..725ff8560ea3930c7829a6e1d77718e02b23f22e --- /dev/null +++ b/app/assets/javascripts/merge_conflict_resolver.js.coffee @@ -0,0 +1,105 @@ +#= require lib/vue + +class window.MergeConflictResolver extends Vue + + constructor: (options = {}) -> + + options.el = '#conflicts' + options.data = @getInitialData() + options.created = -> @fetchData() + options.name = 'MergeConflictResolver' + + super options + + window.v = this + + + fetchData: -> + + $.ajax + url : '/emojis' + success: (response) => + @handleResponse window.mergeConflictsData + + + handleResponse: (data) -> + + @isLoading = no + @conflictsData = @decorateData data + + + handleViewTypeChange: (newType) -> + + return if newType is @diffView + return unless newType in [ 'parallel', 'inline' ] + + @diffView = newType + $.cookie 'diff_view', newType + @isParallel = @diffView is 'parallel' + + # FIXME: Maybe even better with vue.js + $('.container-fluid').toggleClass 'container-limited' + + + handleSelected: (sectionId, selection) -> + + console.log sectionId, selection + + + decorateData: (data) -> + + for file in data.files + file.parallelLines = { left: [], right: [] } + file.inlineLines = [] + currentLineType = 'old' + + for section in file.sections + { conflict, lines, id } = section + + if conflict + header = { lineType: 'header', id } + file.parallelLines.left.push header + file.parallelLines.right.push header + + header.type = 'old' + file.inlineLines.push header + + for line in lines + if line.type in ['new', 'old'] and currentLineType isnt line.type + currentLineType = line.type + # FIXME: Find a better way to add a new line + file.inlineLines.push { lineType: 'emptyLine', text: '<span> </span>' } + + line.conflict = conflict + file.inlineLines.push line + + if conflict + if line.type is 'old' + line = { lineType: 'conflict', lineNumber: line.old_line, text: line.text } + file.parallelLines.left.push line + else if line.type is 'new' + line = { lineType: 'conflict', lineNumber: line.new_line, text: line.text } + file.parallelLines.right.push line + else + console.log 'unhandled line type...', line + else + file.parallelLines.left.push { lineType: 'context', lineNumber: line.old_line, text: line.text } + file.parallelLines.right.push { lineType: 'context', lineNumber: line.new_line, text: line.text } + + if conflict + file.inlineLines.push { lineType: 'header', id, type: 'new' } + + console.log data + return data + + + getInitialData: -> + + diffViewType = $.cookie 'diff_view' + + return { + isLoading : yes + diffView : diffViewType + conflictsData : {} + isParallel : diffViewType is 'parallel' + } diff --git a/app/assets/stylesheets/behaviors.scss b/app/assets/stylesheets/behaviors.scss index 542a53f0377f924f06dbc104f99251f0d56797e9..a6b9efc49c9a2ca6dce1c15cdb242167f16b8fb5 100644 --- a/app/assets/stylesheets/behaviors.scss +++ b/app/assets/stylesheets/behaviors.scss @@ -20,3 +20,8 @@ .turn-off { display: block; } } } + + +[v-cloak] { + display: none; +} diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss new file mode 100644 index 0000000000000000000000000000000000000000..6e77fc1c2e3f17d83dc6c2e76bf01419d7853d44 --- /dev/null +++ b/app/assets/stylesheets/pages/merge_conflicts.scss @@ -0,0 +1,66 @@ +$head_line: #EFFDEB; +$head_header: #DAFACE; +$head_header_selected: #BAF0A8; + +$origin_line: #F2F9FF; +$origin_header: #E0F0FF; +$origin_header_selected: #ADD8FF; + + +#conflicts { + + .diff-wrap-lines .line_content { + white-space: normal; + } + + .head { + .header, .diff-line-num { + background-color: $head_header; + } + td { + background-color: $head_line; + } + } + + .origin { + .header, .diff-line-num { + background-color: $origin_header; + } + td { + background-color: $origin_line; + } + } + + .parallel-view { + .diff-content { + overflow: hidden; + table { + width: 50%; + float: left; + } + } + } + + .parallel .header { + button { + right: 10px; + } + } + + .line_content.header { + position: relative; + + button { + border-radius: 4px; + border: 1px solid #E1E1E1; + background: #FFF; + font-size: 10px; + position: absolute; + right: 20px; + height: 17px; + line-height: 4px; // FIXME: 4px? + top: 1px; + outline: none; + } + } +} diff --git a/app/views/projects/merge_requests/show/_conflicts.html.haml b/app/views/projects/merge_requests/show/_conflicts.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..5386de86a1c56eb3266e827ea7a29d27e656dade --- /dev/null +++ b/app/views/projects/merge_requests/show/_conflicts.html.haml @@ -0,0 +1,83 @@ +.loading{ "v-if" => "isLoading" } + %i.fa.fa-spinner.fa-spin + +.content-block.oneline-block.files-changed{ "v-if" => "!isLoading" } + .inline-parallel-buttons + .btn-group + %a.btn{ | + ":class" => "{'active': !isParallel}", | + "@click" => "handleViewTypeChange('inline')"} + Inline + %a.btn{ | + ":class" => "{'active': isParallel}", | + "@click" => "handleViewTypeChange('parallel')"} + Side-by-side + + .js-toggle-container + .commit-stat-summary + Showing + %strong.cred 3 conflicts + for + %strong {{conflictsData.source_branch}} + into + %strong {{conflictsData.target_branch}} + + +.files-wrapper{ "v-if" => "!isLoading" } + .files{{"v-if" => "isParallel"}} + .diff-file.file-holder.conflict.unresolved.parallel-view{"v-for" => "file in conflictsData.files"} + .file-title + %i.fa.fa-ban + %a + %span {{file.new_path}} + .file-actions + %a.btn.btn-sm View file @{{file.commit_sha.slice(0, 7)}} + + .diff-content.diff-wrap-lines + .diff-wrap-lines.code.file-content.js-syntax-highlight.white + %table{"v-for" => "(key, group) in file.parallelLines"} + %tr.line_holder.parallel{ | + "v-for" => "line in group", | + ":class" => "line.lineType != 'context' && (key == 'left' ? 'head' : 'origin')"} + + %td.diff-line-num.header{"v-if" => "line.lineType == 'header'"} + %td.line_content.header{"v-if" => "line.lineType == 'header'"} + %strong {{key == 'left' ? 'HEAD//our changes' : 'origin//their changes'}} + %button.btn{"@click" => "handleSelected(line.id, line.lineType)"} Use this + + %td.diff-line-num.old_line{"v-if" => "line.lineType != 'header'"} + {{line.lineNumber}} + %td.line_content{"v-if" => "line.lineType != 'header'"} + {{line.text}} + + + .files{{"v-if" => "!isParallel"}} + .diff-file.file-holder.conflict.unresolved.inline-view{"v-for" => "file in conflictsData.files"} + .file-title + %i.fa.fa-ban + %a + %span {{file.new_path}} + .file-actions + %a.btn.btn-sm View file @{{file.commit_sha.slice(0, 7)}} + + .diff-content.diff-wrap-lines + .diff-wrap-lines.code.file-content.js-syntax-highlight.white + %table + %tr.line_holder{ | + "v-for" => "line in file.inlineLines", | + ":class" => "line.type == 'old' ? 'head' : line.type == 'new' ? 'origin' : ''"} + + %template{"v-if" => "line.lineType != 'header'"} + %td.diff-line-num.old_line + %a {{line.old_line}} + %td.diff-line-num.new_line + %a {{line.new_line}} + %td.line_content + {{{line.text}}} + + %template{"v-if" => "line.lineType == 'header'"} + %td.diff-line-num.header + %td.diff-line-num.header + %td.line_content.header + %strong {{line.type == 'old' ? 'HEAD//our changes' : 'origin//their changes'}} + %button.btn{"@click" => "handleSelected(line.id, line.type)"} Use this