From 175f2fabec103da476e16432b40e134118913508 Mon Sep 17 00:00:00 2001 From: sandraros <34005250+sandraros@users.noreply.github.com> Date: Mon, 2 Dec 2019 09:32:15 +0100 Subject: [PATCH] Code selection by column on DIFF screen (#3091) * Code selection by column on DIFF screen Fix of #3089 Two ways to select a text: 1. Position the mouse at the start of the text, press mouse left button, keep pressed, drag to the end of the text, release the button 2. Position the mouse at the start of the text, press mouse left button, release the button, position the mouse to the end of the text, press shift key + mouse left button. Two ways to copy the selected text: 1. Press Ctrl + C or Ctrl + Ins 2. Mouse right button on the text to display the context menu and choose Copy SPLIT VIEW (code at the left is the LOCAL code and code at the right is the REMOTE code): - Text can be selected only at the left side, or only at the right side. - The dummy empty lines which show the place of lines which exist only at the other side are not copied. UNIFIED VIEW (LOCAL and REMOTE are mixed in the same column, "green plus" lines correspond to lines only in the LOCAL code, "red minus" lines correspond to lines only in the REMOTE code. - Only the lines from the REMOTE code are copied PATCH VIEW (split view + one extra column on the left) - Same features as the SPLIT VIEW * corrections lint javascript * merge latest abapgit changes * corrections lint javascript * JS refactor functions to prototyped fns (classes) * JS lint corrections + minor last minute changes * conform current common.js standards * LINT conform to common.js standards * unused lines removed --- src/ui/zabapgit_css_common.w3mi.data.css | 52 +++++++++ src/ui/zabapgit_css_common.w3mi.xml | 2 +- src/ui/zabapgit_js_common.w3mi.data.js | 127 +++++++++++++++++++++ src/ui/zcl_abapgit_gui_page_diff.clas.abap | 54 +++++---- 4 files changed, 214 insertions(+), 21 deletions(-) diff --git a/src/ui/zabapgit_css_common.w3mi.data.css b/src/ui/zabapgit_css_common.w3mi.data.css index 794e75271..1bc27f6fd 100644 --- a/src/ui/zabapgit_css_common.w3mi.data.css +++ b/src/ui/zabapgit_css_common.w3mi.data.css @@ -432,6 +432,58 @@ table.diff_tab td.code { table.diff_tab tbody tr:first-child td { padding-top: 0.5em; } table.diff_tab tbody tr:last-child td { padding-bottom: 0.5em; } +table.diff_tab td.mark, th.mark { + width: 0.1%; + -ms-user-select: none; + user-select: none; + cursor: default; +} + +.diff_select_left td.diff_right, +.diff_select_left td.diff_right *, +.diff_select_left th.diff_right, +.diff_select_left th.diff_right *, +.diff_select_right td.diff_left, +.diff_select_right td.diff_left *, +.diff_select_right th.diff_left, +.diff_select_right th.diff_left * { + -ms-user-select: none; + user-select: none; + cursor: text; +} + +.diff_select_left td.diff_left, +.diff_select_left td.diff_left *, +.diff_select_left th.diff_left, +.diff_select_left th.diff_left *, +.diff_select_right td.diff_right, +.diff_select_right td.diff_right *, +.diff_select_right th.diff_right, +.diff_select_right th.diff_right * { + -ms-user-select: text; + user-select: text; +} + +td.diff_others::selection, +td.diff_others *::selection, +th.diff_others::selection, +th.diff_others *::selection { + background-color: transparent; + cursor: default; +} + +.diff_select_left td.diff_right::selection, +.diff_select_left td.diff_right *::selection, +.diff_select_left th.diff_right::selection, +.diff_select_left th.diff_right *::selection, +.diff_select_right td.diff_left::selection, +.diff_select_right td.diff_left *::selection, +.diff_select_right th.diff_left::selection, +.diff_select_right th.diff_left *::selection { + background-color: transparent; + cursor: text; +} + /* DEBUG INFO STYLES */ div.debug_container { padding: 0.5em; diff --git a/src/ui/zabapgit_css_common.w3mi.xml b/src/ui/zabapgit_css_common.w3mi.xml index dbdbed67e..cbea95037 100644 --- a/src/ui/zabapgit_css_common.w3mi.xml +++ b/src/ui/zabapgit_css_common.w3mi.xml @@ -15,7 +15,7 @@ MI ZABAPGIT_CSS_COMMON filename - ~wwwtmp.css + common.css MI diff --git a/src/ui/zabapgit_js_common.w3mi.data.js b/src/ui/zabapgit_js_common.w3mi.data.js index 1177f8255..371196409 100644 --- a/src/ui/zabapgit_js_common.w3mi.data.js +++ b/src/ui/zabapgit_js_common.w3mi.data.js @@ -770,6 +770,133 @@ function addMarginBottom(){ document.getElementsByTagName("body")[0].style.marginBottom = screen.height + "px"; } + +/********************************************************** + * Diff page logic of column selection + **********************************************************/ + +function DiffColumnSelection() { + this.selectedColumnIdx = -1; + this.lineNumColumnIdx = -1; + //https://stackoverflow.com/questions/2749244/javascript-setinterval-and-this-solution + document.addEventListener("mousedown", this.mousedownEventListener.bind(this)); + document.addEventListener("copy", this.copyEventListener.bind(this)); +} + +DiffColumnSelection.prototype.mousedownEventListener = function(e) { + // Select text in a column of an HTML table and copy to clipboard (in DIFF view) + // (https://stackoverflow.com/questions/6619805/select-text-in-a-column-of-an-html-table) + // Process mousedown event for all TD elements -> apply CSS class at TABLE level. + // (https://stackoverflow.com/questions/40956717/how-to-addeventlistener-to-multiple-elements-in-a-single-line) + var unifiedLineNumColumnIdx = 0; + var unifiedCodeColumnIdx = 3; + var splitLineNumLeftColumnIdx = 0; + var splitCodeLeftColumnIdx = 2; + var splitLineNumRightColumnIdx = 3; + var splitCodeRightColumnIdx = 5; + + if (e.button !== 0) return; // function is only valid for left button, not right button + + var td = e.target; + while (td != undefined && td.tagName != "TD" && td.tagName != "TBODY") td = td.parentElement; + if (td == undefined) return; + var table = td.parentElement.parentElement; + + var patchColumnCount = 0; + if (td.parentElement.cells[0].classList.contains("patch")) { + patchColumnCount = 1; + } + + if (td.classList.contains("diff_left")) { + table.classList.remove("diff_select_right"); + table.classList.add("diff_select_left"); + if ( window.getSelection() && this.selectedColumnIdx != splitCodeLeftColumnIdx + patchColumnCount ) { + // De-select to avoid effect of dragging selection in case the right column was first selected + if (document.body.createTextRange) { // All IE but Edge + // document.getSelection().removeAllRanges() may trigger error + // so use this code which is equivalent but does not fail + // (https://stackoverflow.com/questions/22914075/javascript-error-800a025e-using-range-selector) + range = document.body.createTextRange(); + range.collapse(); + range.select(); + } else { + document.getSelection().removeAllRanges(); + }} + this.selectedColumnIdx = splitCodeLeftColumnIdx + patchColumnCount; + this.lineNumColumnIdx = splitLineNumLeftColumnIdx + patchColumnCount; + + } else if (td.classList.contains("diff_right")) { + table.classList.remove("diff_select_left"); + table.classList.add("diff_select_right"); + if ( window.getSelection() && this.selectedColumnIdx != splitCodeRightColumnIdx + patchColumnCount ) { + if (document.body.createTextRange) { // All IE but Edge + // document.getSelection().removeAllRanges() may trigger error + // so use this code which is equivalent but does not fail + // (https://stackoverflow.com/questions/22914075/javascript-error-800a025e-using-range-selector) + var range = document.body.createTextRange(); + range.collapse(); + range.select(); + } else { + document.getSelection().removeAllRanges(); + }} + this.selectedColumnIdx = splitCodeRightColumnIdx + patchColumnCount; + this.lineNumColumnIdx = splitLineNumRightColumnIdx + patchColumnCount; + + } else if (td.classList.contains("diff_unified")) { + this.selectedColumnIdx = unifiedCodeColumnIdx; + this.lineNumColumnIdx = unifiedLineNumColumnIdx; + + } else { + this.selectedColumnIdx = -1; + this.lineNumColumnIdx = -1; + } +}; + +DiffColumnSelection.prototype.copyEventListener = function(e) { + // Select text in a column of an HTML table and copy to clipboard (in DIFF view) + // (https://stackoverflow.com/questions/6619805/select-text-in-a-column-of-an-html-table) + var td = e.target; + while (td != undefined && td.tagName != "TD" && td.tagName != "TBODY") td = td.parentElement; + if(td != undefined){ + // Use window.clipboardData instead of e.clipboardData + // (https://stackoverflow.com/questions/23470958/ie-10-copy-paste-issue) + var clipboardData = ( e.clipboardData == undefined ? window.clipboardData : e.clipboardData ); + var text = this.getSelectedText(); + clipboardData.setData("text", text); + e.preventDefault(); + } +}; + +DiffColumnSelection.prototype.getSelectedText = function() { + // Select text in a column of an HTML table and copy to clipboard (in DIFF view) + // (https://stackoverflow.com/questions/6619805/select-text-in-a-column-of-an-html-table) + var sel = window.getSelection(), + range = sel.getRangeAt(0), + doc = range.cloneContents(), + nodes = doc.querySelectorAll("tr"), + text = ""; + if (nodes.length === 0) { + text = doc.textContent; + } else { + var newline = "", + realThis = this; + [].forEach.call(nodes, function(tr, i) { + var cellIdx = ( i==0 ? 0 : realThis.selectedColumnIdx ); + if (tr.cells.length > cellIdx) { + var tdSelected = tr.cells[cellIdx]; + var tdLineNum = tr.cells[realThis.lineNumColumnIdx]; + // copy is interesting for remote code, don't copy lines which exist only locally + if (i==0 || tdLineNum.getAttribute("line-num")!="") { + text += newline + tdSelected.textContent; + // special processing for TD tag which sometimes contains newline + // (expl: /src/ui/zabapgit_js_common.w3mi.data.js) so don't add newline again in that case. + var lastChar = tdSelected.textContent[ tdSelected.textContent.length - 1 ]; + if ( lastChar == "\n" ) newline = ""; + else newline = "\n"; + }}});} + return text; +}; + /********************************************************** * Other functions **********************************************************/ diff --git a/src/ui/zcl_abapgit_gui_page_diff.clas.abap b/src/ui/zcl_abapgit_gui_page_diff.clas.abap index cc3b7435f..7e872aa71 100644 --- a/src/ui/zcl_abapgit_gui_page_diff.clas.abap +++ b/src/ui/zcl_abapgit_gui_page_diff.clas.abap @@ -669,9 +669,10 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_DIFF IMPLEMENTATION. ENDIF. IF mv_unified = abap_true. ro_html->add( '' ). + ro_html->add( '' ). ro_html->add( |@@ { is_diff_line-new_num } @@ { lv_beacon }| ). ELSE. - ro_html->add( |@@ { is_diff_line-new_num } @@ { lv_beacon }| ). + ro_html->add( |@@ { is_diff_line-new_num } @@ { lv_beacon }| ). ENDIF. ro_html->add( '' ). @@ -850,8 +851,9 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_DIFF IMPLEMENTATION. lv_mark = `+`. ENDIF. ENDIF. - lv_new = || - && |{ lv_mark }{ is_diff_line-new }|. + lv_new = || + && |{ lv_mark }| + && |{ is_diff_line-new }|. IF lv_mark <> ` `. lv_patch_line_possible = abap_true. @@ -869,8 +871,9 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_DIFF IMPLEMENTATION. lv_mark = `-`. ENDIF. ENDIF. - lv_old = || - && |{ lv_mark }{ is_diff_line-old }|. + lv_old = || + && |{ lv_mark }| + && |{ is_diff_line-old }|. IF lv_mark <> ` `. lv_patch_line_possible = abap_true. @@ -912,16 +915,18 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_DIFF IMPLEMENTATION. IF is_diff_line-result <> zif_abapgit_definitions=>c_diff-update. LOOP AT mt_delayed_lines ASSIGNING . ro_html->add( '' ). "#EC NOTEXT - ro_html->add( || - && || - && |-{ -old }| ). + ro_html->add( || + && || + && |-| + && |{ -old }| ). ro_html->add( '' ). "#EC NOTEXT ENDLOOP. LOOP AT mt_delayed_lines ASSIGNING . ro_html->add( '' ). "#EC NOTEXT - ro_html->add( || - && || - && |+{ -new }| ). + ro_html->add( || + && || + && |+| + && |{ -new }| ). ro_html->add( '' ). "#EC NOTEXT ENDLOOP. CLEAR mt_delayed_lines. @@ -932,17 +937,20 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_DIFF IMPLEMENTATION. WHEN zif_abapgit_definitions=>c_diff-update. APPEND is_diff_line TO mt_delayed_lines. " Delay output of subsequent updates WHEN zif_abapgit_definitions=>c_diff-insert. - ro_html->add( || - && || - && |+{ is_diff_line-new }| ). + ro_html->add( || + && || + && |+| + && |{ is_diff_line-new }| ). WHEN zif_abapgit_definitions=>c_diff-delete. - ro_html->add( || - && || - && |-{ is_diff_line-old }| ). + ro_html->add( || + && || + && |-| + && |{ is_diff_line-old }| ). WHEN OTHERS. "none - ro_html->add( || - && || - && | { is_diff_line-old }| ). + ro_html->add( || + && || + && | | + && |{ is_diff_line-old }| ). ENDCASE. ro_html->add( '' ). "#EC NOTEXT @@ -1000,6 +1008,7 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_DIFF IMPLEMENTATION. IF mv_unified = abap_true. ro_html->add( 'old' ). "#EC NOTEXT ro_html->add( 'new' ). "#EC NOTEXT + ro_html->add( '' ). "#EC NOTEXT ro_html->add( 'code' ). "#EC NOTEXT ELSE. @@ -1011,8 +1020,10 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_DIFF IMPLEMENTATION. ENDIF. ro_html->add( '' ). "#EC NOTEXT + ro_html->add( '' ). "#EC NOTEXT ro_html->add( 'LOCAL' ). "#EC NOTEXT ro_html->add( '' ). "#EC NOTEXT + ro_html->add( '' ). "#EC NOTEXT ro_html->add( 'REMOTE' ). "#EC NOTEXT ENDIF. @@ -1048,6 +1059,9 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_DIFF IMPLEMENTATION. ro_html->add( ' hotkeyDescription: "Jump to file ..."' ). ro_html->add( '});' ). + " Feature for selecting ABAP code by column and copy to clipboard + ro_html->add( 'var columnSelection = new DiffColumnSelection();' ). + ENDMETHOD.