diff --git a/ui/src/visualizations/code-hotspots/chart/chart.js b/ui/src/visualizations/code-hotspots/chart/chart.js index 1aacda9f..f8d349f7 100644 --- a/ui/src/visualizations/code-hotspots/chart/chart.js +++ b/ui/src/visualizations/code-hotspots/chart/chart.js @@ -122,16 +122,19 @@ export default class CodeHotspots extends React.PureComponent { /> - + ').join(''); tooltipp .style('border', '3px solid transparent') @@ -172,32 +172,11 @@ export default class columnChartGeneration { ListGeneration.generateCommitList(commitList, d.commits, currThis); tooltipp.append('hr'); tooltipp.append('div').html('Changes: ' + d.value); - - /* tooltipp - .html( - "
" + - currDev.split(' <')[0] + - '
' + - '
' + - currDev.split(' <')[1] + - '
' + - '
' + - '
Changes: ' + - d.value + - '
' - ) - .style( - 'right', - w - d.i * w / currThis.combinedColumnData.length - 300 > 0 - ? w - d.i * w / currThis.combinedColumnData.length - 300 - : 0 + 'px' - ) - .style('top', h + 'px');*/ } }) .on('mouseout', function() { if (!currThis.tooltipLocked) { - tooltipp.transition().duration(500).style('opacity', 0).style('top', '-1500px'); + tooltipp.transition().duration(500).style('display', 'none'); } }) .on('click', function() { @@ -219,7 +198,7 @@ export default class columnChartGeneration { .attr('height', h) .on('mouseover', function(event, d) { if (!currThis.tooltipLocked) { - tooltipp.transition().duration(200).style('opacity', 1); + tooltipp.transition().duration(200).style('display', 'block'); tooltipp .style('border', '3px solid transparent') .style( @@ -254,7 +233,7 @@ export default class columnChartGeneration { }) .on('mouseout', function() { if (!currThis.tooltipLocked) { - tooltipp.transition().duration(500).style('opacity', 0).style('top', '-1500px'); + tooltipp.transition().duration(500).style('display', 'none'); } }) .on('click', function() { @@ -276,7 +255,7 @@ export default class columnChartGeneration { .attr('height', h) .style('cursor', 'pointer') .on('mousemove', function(event, d) { - tooltipp.transition().duration(200).style('opacity', 1); + tooltipp.transition().duration(200).style('display', 'block'); tooltipp .html( "
Version: " + @@ -302,6 +281,11 @@ export default class columnChartGeneration { d.sha + '
' + '
' + + "
" + + 'Parent(s): ' + + d.parents + + '
' + + '
' + '
Changes: ' + d.value + '
' @@ -315,7 +299,7 @@ export default class columnChartGeneration { .style('top', h + 'px'); }) .on('mouseout', function() { - tooltipp.transition().duration(500).style('opacity', 0).style('top', '-1500px'); + tooltipp.transition().duration(500).style('display', 'none'); }) .on('click', function(event, d) { currThis.setState({ sha: d.sha }); @@ -409,143 +393,17 @@ export default class columnChartGeneration { //commits const commits = d3.select('#chartBranchView').selectAll('rect').data(currThis.combinedColumnData); const branchLines = d3.select('#chartBranchView'); - let offset = 0; + const offset = 0; const firstCommitCount = parseInt(branches[0].values[0].column); for (const branch of branches) { - for (let i = 0; i < branch.values.length - 1; i++) { - const c1 = parseInt(branch.values[i].column) - firstCommitCount; - const c2 = parseInt(branch.values[i + 1].column) - firstCommitCount; - if (i === 0) { - for (const parentSha of branch.values[i].parents.split(',')) { - const parent = currThis.combinedColumnData.find(d => d.sha === parentSha); - if (parent !== undefined) { - if (parseInt(parent.column) + 1 < c1) { - branchLines - .append('line') - .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) - .style('stroke-width', 5) - .style('stroke-linecap', 'round') - .attr('y1', h / 2) - .attr( - 'x1', - w / (currThis.combinedColumnData.length * 2) + - (parent.column - firstCommitCount) * w / currThis.combinedColumnData.length - ) - .attr('y2', 5) - .attr( - 'x2', - w / (currThis.combinedColumnData.length * 2) + - (parent.column - firstCommitCount + 1) * w / currThis.combinedColumnData.length - ); - branchLines - .append('line') - .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) - .style('stroke-width', 5) - .style('stroke-linecap', 'round') - .attr('y1', 5) - .attr( - 'x1', - w / (currThis.combinedColumnData.length * 2) + - (parent.column - firstCommitCount + 1) * w / currThis.combinedColumnData.length - ) - .attr('y2', 5) - .attr('x2', w / (currThis.combinedColumnData.length * 2) + (c1 - 1) * w / currThis.combinedColumnData.length); - branchLines - .append('line') - .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) - .style('stroke-width', 5) - .style('stroke-linecap', 'round') - .attr('y1', 5) - .attr('x1', w / (currThis.combinedColumnData.length * 2) + (c1 - 1) * w / currThis.combinedColumnData.length) - .attr('y2', h / 2) - .attr('x2', w / (currThis.combinedColumnData.length * 2) + c1 * w / currThis.combinedColumnData.length); - } else { - branchLines - .append('line') - .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) - .style('stroke-width', 5) - .style('stroke-linecap', 'round') - .attr('y1', h / 2) - .attr( - 'x1', - w / (currThis.combinedColumnData.length * 2) + - (parent.column - firstCommitCount) * w / currThis.combinedColumnData.length - ) - .attr('y2', h / 2) - .attr('x2', w / (currThis.combinedColumnData.length * 2) + c1 * w / currThis.combinedColumnData.length); - } - } else { - branchLines - .append('line') - .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) - .style('stroke-width', 5) - .style('stroke-linecap', 'round') - .attr('y1', 10) - .attr('x1', w / (currThis.combinedColumnData.length * 2) + (c1 - 1) * w / currThis.combinedColumnData.length) - .attr('y2', h / 2) - .attr('x2', w / (currThis.combinedColumnData.length * 2) + c1 * w / currThis.combinedColumnData.length); - - branchLines - .append('line') - .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) - .style('stroke-width', 5) - .style('stroke-linecap', 'round') - .style('stroke-dasharray', '1, 10') - .attr('y1', 10) - .attr('x1', w / (currThis.combinedColumnData.length * 2) + (c1 - 2) * w / currThis.combinedColumnData.length) - .attr('y2', 10) - .attr('x2', w / (currThis.combinedColumnData.length * 2) + (c1 - 1) * w / currThis.combinedColumnData.length); - } - } - } - if (c1 + 1 === c2) { - branchLines - .append('line') - .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) - .style('stroke-width', 5) - .style('stroke-linecap', 'round') - .attr('y1', h / 2) - .attr('x1', w / (currThis.combinedColumnData.length * 2) + c1 * w / currThis.combinedColumnData.length) - .attr('y2', h / 2) - .attr('x2', w / (currThis.combinedColumnData.length * 2) + c2 * w / currThis.combinedColumnData.length); - } else { - offset++; - let currOffset = 0; - if (offset % 2 === 0) { - currOffset = -Math.floor(offset / 2); - } else { - currOffset = Math.floor(offset / 2) + offset % 2; - } - currOffset = Math.min(Math.max(currOffset * 5, -h / 2 + 5), h / 2 - 5); - branchLines - .append('line') - .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) - .style('stroke-width', 5) - .style('stroke-linecap', 'round') - .attr('y1', h / 2) - .attr('x1', w / (currThis.combinedColumnData.length * 2) + c1 * w / currThis.combinedColumnData.length) - .attr('y2', h / 2 + currOffset) - .attr('x2', w / (currThis.combinedColumnData.length * 2) + (c1 + 1) * w / currThis.combinedColumnData.length); - - branchLines - .append('line') - .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) - .style('stroke-width', 5) - .style('stroke-linecap', 'round') - .attr('y1', h / 2 + currOffset) - .attr('x1', w / (currThis.combinedColumnData.length * 2) + (c1 + 1) * w / currThis.combinedColumnData.length) - .attr('y2', h / 2 + currOffset) - .attr('x2', w / (currThis.combinedColumnData.length * 2) + (c2 - 1) * w / currThis.combinedColumnData.length); - - branchLines - .append('line') - .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) - .style('stroke-width', 5) - .style('stroke-linecap', 'round') - .attr('y1', h / 2 + currOffset) - .attr('x1', w / (currThis.combinedColumnData.length * 2) + (c2 - 1) * w / currThis.combinedColumnData.length) - .attr('y2', h / 2) - .attr('x2', w / (currThis.combinedColumnData.length * 2) + c2 * w / currThis.combinedColumnData.length); + if (branch.values.length === 1) { + const c1 = parseInt(branch.values[0].column) - firstCommitCount; + this.drawBranchConnections(0, branch, currThis, c1, branchLines, branches, h, w, firstCommitCount, null, offset); + } else { + for (let i = 0; i < branch.values.length - 1; i++) { + const c1 = parseInt(branch.values[i].column) - firstCommitCount; + const c2 = parseInt(branch.values[i + 1].column) - firstCommitCount; + this.drawBranchConnections(i, branch, currThis, c1, branchLines, branches, h, w, firstCommitCount, c2, offset); } } } @@ -563,7 +421,7 @@ export default class columnChartGeneration { .attr('rx', '100%') .attr('ry', '100%') .on('mousemove', function(event, d) { - tooltipp.transition().duration(200).style('opacity', 1); + tooltipp.transition().duration(200).style('display', 'block'); tooltipp .html( "
Version: " + @@ -589,6 +447,11 @@ export default class columnChartGeneration { d.sha + '
' + '
' + + "
" + + 'Parent(s): ' + + d.parents + + '
' + + '
' + '
Changes: ' + d.value + '
' @@ -602,11 +465,146 @@ export default class columnChartGeneration { .style('top', h + 100 + 'px'); }) .on('mouseout', function() { - tooltipp.transition().duration(500).style('opacity', 0).style('top', '-1500px'); + tooltipp.transition().duration(500).style('display', 'none'); }) .on('click', function(event, d) { currThis.setState({ sha: d.sha }); }); commits.exit().remove(); } + + static drawBranchConnections(i, branch, currThis, c1, branchLines, branches, h, w, firstCommitCount, c2, offset) { + if (i === 0) { + for (const parentSha of branch.values[i].parents.split(',')) { + const parent = currThis.combinedColumnData.find(d => d.sha === parentSha); + if (parent !== undefined) { + if (parseInt(parent.column) + 1 < c1) { + branchLines + .append('line') + .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) + .style('stroke-width', 5) + .style('stroke-linecap', 'round') + .attr('y1', h / 2) + .attr( + 'x1', + w / (currThis.combinedColumnData.length * 2) + (parent.column - firstCommitCount) * w / currThis.combinedColumnData.length + ) + .attr('y2', 5) + .attr( + 'x2', + w / (currThis.combinedColumnData.length * 2) + + (parent.column - firstCommitCount + 1) * w / currThis.combinedColumnData.length + ); + branchLines + .append('line') + .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) + .style('stroke-width', 5) + .style('stroke-linecap', 'round') + .attr('y1', 5) + .attr( + 'x1', + w / (currThis.combinedColumnData.length * 2) + + (parent.column - firstCommitCount + 1) * w / currThis.combinedColumnData.length + ) + .attr('y2', 5) + .attr('x2', w / (currThis.combinedColumnData.length * 2) + (c1 - 1) * w / currThis.combinedColumnData.length); + branchLines + .append('line') + .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) + .style('stroke-width', 5) + .style('stroke-linecap', 'round') + .attr('y1', 5) + .attr('x1', w / (currThis.combinedColumnData.length * 2) + (c1 - 1) * w / currThis.combinedColumnData.length) + .attr('y2', h / 2) + .attr('x2', w / (currThis.combinedColumnData.length * 2) + c1 * w / currThis.combinedColumnData.length); + } else { + branchLines + .append('line') + .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) + .style('stroke-width', 5) + .style('stroke-linecap', 'round') + .attr('y1', h / 2) + .attr( + 'x1', + w / (currThis.combinedColumnData.length * 2) + (parent.column - firstCommitCount) * w / currThis.combinedColumnData.length + ) + .attr('y2', h / 2) + .attr('x2', w / (currThis.combinedColumnData.length * 2) + c1 * w / currThis.combinedColumnData.length); + } + } else { + branchLines + .append('line') + .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) + .style('stroke-width', 5) + .style('stroke-linecap', 'round') + .attr('y1', 10) + .attr('x1', w / (currThis.combinedColumnData.length * 2) + (c1 - 1) * w / currThis.combinedColumnData.length) + .attr('y2', h / 2) + .attr('x2', w / (currThis.combinedColumnData.length * 2) + c1 * w / currThis.combinedColumnData.length); + + branchLines + .append('line') + .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) + .style('stroke-width', 5) + .style('stroke-linecap', 'round') + .style('stroke-dasharray', '1, 10') + .attr('y1', 10) + .attr('x1', w / (currThis.combinedColumnData.length * 2) + (c1 - 2) * w / currThis.combinedColumnData.length) + .attr('y2', 10) + .attr('x2', w / (currThis.combinedColumnData.length * 2) + (c1 - 1) * w / currThis.combinedColumnData.length); + } + } + } + if (c2 !== null) { + if (c1 + 1 === c2) { + branchLines + .append('line') + .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) + .style('stroke-width', 5) + .style('stroke-linecap', 'round') + .attr('y1', h / 2) + .attr('x1', w / (currThis.combinedColumnData.length * 2) + c1 * w / currThis.combinedColumnData.length) + .attr('y2', h / 2) + .attr('x2', w / (currThis.combinedColumnData.length * 2) + c2 * w / currThis.combinedColumnData.length); + } else { + offset++; + let currOffset; + if (offset % 2 === 0) { + currOffset = -Math.floor(offset / 2); + } else { + currOffset = Math.floor(offset / 2) + offset % 2; + } + currOffset = Math.min(Math.max(currOffset * 5, -h / 2 + 5), h / 2 - 5); + branchLines + .append('line') + .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) + .style('stroke-width', 5) + .style('stroke-linecap', 'round') + .attr('y1', h / 2) + .attr('x1', w / (currThis.combinedColumnData.length * 2) + c1 * w / currThis.combinedColumnData.length) + .attr('y2', h / 2 + currOffset) + .attr('x2', w / (currThis.combinedColumnData.length * 2) + (c1 + 1) * w / currThis.combinedColumnData.length); + + branchLines + .append('line') + .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) + .style('stroke-width', 5) + .style('stroke-linecap', 'round') + .attr('y1', h / 2 + currOffset) + .attr('x1', w / (currThis.combinedColumnData.length * 2) + (c1 + 1) * w / currThis.combinedColumnData.length) + .attr('y2', h / 2 + currOffset) + .attr('x2', w / (currThis.combinedColumnData.length * 2) + (c2 - 1) * w / currThis.combinedColumnData.length); + + branchLines + .append('line') + .style('stroke', ColorMixer.rainbow(branches.length, branches.findIndex(v => v.key === branch.values[i].branch))) + .style('stroke-width', 5) + .style('stroke-linecap', 'round') + .attr('y1', h / 2 + currOffset) + .attr('x1', w / (currThis.combinedColumnData.length * 2) + (c2 - 1) * w / currThis.combinedColumnData.length) + .attr('y2', h / 2) + .attr('x2', w / (currThis.combinedColumnData.length * 2) + c2 * w / currThis.combinedColumnData.length); + } + } + } } diff --git a/ui/src/visualizations/code-hotspots/chart/charts/subCharts/rowChartGeneration.js b/ui/src/visualizations/code-hotspots/chart/charts/subCharts/rowChartGeneration.js index 6b31cc45..8ea9ece5 100644 --- a/ui/src/visualizations/code-hotspots/chart/charts/subCharts/rowChartGeneration.js +++ b/ui/src/visualizations/code-hotspots/chart/charts/subCharts/rowChartGeneration.js @@ -103,7 +103,7 @@ export default class rowChartGeneration { .attr('class', 'tooltipRow') .attr('id', 'tooltipRow') .style('position', 'absolute') - .style('opacity', 0) + .style('display', 'none') .style('background-color', '#FFFFFFDD') .style('box-shadow', '0px 0px 10px #555555') .style('width', '40rem') @@ -170,7 +170,7 @@ export default class rowChartGeneration { .attr('height', barHeight) .attr('z-index', '10') .on('mouseover', function(event, d) { - tooltipp.transition().duration(200).style('opacity', 1); + tooltipp.transition().duration(200).style('display', 'block'); tooltipp .html( "
Row: " + @@ -186,7 +186,7 @@ export default class rowChartGeneration { .style('top', (d.row - firstLineNumber + 1) * barHeight + 'px'); }) .on('mouseout', function() { - tooltipp.transition().duration(500).style('opacity', 0); + tooltipp.transition().duration(500).style('display', 'none'); }); break; @@ -203,7 +203,7 @@ export default class rowChartGeneration { .attr('height', barHeight) .attr('z-index', '10') .on('mouseover', function(event, d) { - tooltipp.transition().duration(200).style('opacity', 1); + tooltipp.transition().duration(200).style('display', 'block'); tooltipp .html( "
Row: " + @@ -218,7 +218,7 @@ export default class rowChartGeneration { .style('top', (d.row - firstLineNumber + 1) * barHeight + 'px'); }) .on('mouseout', function() { - tooltipp.transition().duration(500).style('opacity', 0); + tooltipp.transition().duration(500).style('display', 'none'); }); break; @@ -235,14 +235,14 @@ export default class rowChartGeneration { .attr('height', barHeight) .attr('z-index', '10') .on('mouseover', function(event, d) { - tooltipp.transition().duration(200).style('opacity', 1); + tooltipp.transition().duration(200).style('display', 'block'); tooltipp .html("
Row: " + (parseInt(d.row) + 1) + '
' + '
Changes: ' + d.value + '
') .style('right', 30 + 'px') .style('top', (d.row - firstLineNumber + 1) * barHeight + 'px'); }) .on('mouseout', function() { - tooltipp.transition().duration(500).style('opacity', 0); + tooltipp.transition().duration(500).style('display', 'none'); }); break; diff --git a/ui/src/visualizations/code-hotspots/components/fileBrowser/fileBrowser.js b/ui/src/visualizations/code-hotspots/components/fileBrowser/fileBrowser.js index eed3cf8d..cfe55c62 100644 --- a/ui/src/visualizations/code-hotspots/components/fileBrowser/fileBrowser.js +++ b/ui/src/visualizations/code-hotspots/components/fileBrowser/fileBrowser.js @@ -52,7 +52,7 @@ export default class FileBrowser extends React.PureComponent { return (
Files:
-
+
{ - this.props.props.onSetFile(data.url); - this.props.props.onSetPath(data.path); + this.clickFile(data.url, data.path); }}> {data.name}
@@ -125,7 +124,7 @@ class FileStruct extends React.PureComponent { } clickFile(Url, Path) { - console.log(Url); - console.log(Path); + this.props.props.onSetFile(Url); + this.props.props.onSetPath(Path); } } diff --git a/ui/src/visualizations/code-hotspots/components/fileBrowser/fileBrowser.scss b/ui/src/visualizations/code-hotspots/components/fileBrowser/fileBrowser.scss index 73c1da72..7575d2e2 100644 --- a/ui/src/visualizations/code-hotspots/components/fileBrowser/fileBrowser.scss +++ b/ui/src/visualizations/code-hotspots/components/fileBrowser/fileBrowser.scss @@ -1,5 +1,5 @@ .fileBrowser{ - height: 70vh; + height: 65vh; overflow-y: scroll; } diff --git a/ui/src/visualizations/code-hotspots/components/searchBar/searchAlgorithm.js b/ui/src/visualizations/code-hotspots/components/searchBar/searchAlgorithm.js index 121f6e39..57af9d04 100644 --- a/ui/src/visualizations/code-hotspots/components/searchBar/searchAlgorithm.js +++ b/ui/src/visualizations/code-hotspots/components/searchBar/searchAlgorithm.js @@ -138,8 +138,8 @@ export default class SearchAlgorithm { static performDeveloperSearch(dataSet, searchTerm) { let filteredDataSet = dataSet.data; - const firstLineNumber = 1; - const code = dataSet.code; + let firstLineNumber = 1; + let code = dataSet.code; const searchTermChunks = searchTerm.toLowerCase().split(' '); for (let i = 0; i < searchTermChunks.length; i++) { switch (searchTermChunks[i]) { @@ -159,6 +159,47 @@ export default class SearchAlgorithm { break; default: filteredDataSet = filteredDataSet.filter(d => d.dev.toLowerCase().includes(searchTermChunks[i])); + break; + case '-l': + case '-line': + case '-lines': + if (i < searchTermChunks.length - 1) { + i++; + const searchTermChunk = searchTermChunks[i]; + if (searchTermChunk.includes('-')) { + if (searchTermChunk.startsWith('-')) { + const endNr = parseInt(searchTermChunk.substring(1)); + if (!isNaN(endNr)) { + code = code.split(/\r\n|\r|\n/).filter((e, i) => i < endNr).join('\n'); + filteredDataSet = filteredDataSet.filter(d => d.row < endNr); + } + } else if (searchTermChunk.endsWith('-')) { + const startNr = parseInt(searchTermChunk.substring(0, searchTermChunk.length - 1)); + if (!isNaN(startNr)) { + firstLineNumber = startNr; + code = code.split(/\r\n|\r|\n/).filter((e, i) => i >= startNr - 1).join('\n'); + filteredDataSet = filteredDataSet.filter(d => d.row >= startNr - 1); + } + } else { + const rowSearchTermChunks = searchTermChunk.split('-'); + const startNr = parseInt(rowSearchTermChunks[0]); + const endNr = parseInt(rowSearchTermChunks[1]); + if (!isNaN(startNr) && !isNaN(endNr)) { + firstLineNumber = startNr; + code = code.split(/\r\n|\r|\n/).filter((e, i) => i >= startNr - 1 && i < endNr).join('\n'); + filteredDataSet = filteredDataSet.filter(d => d.row >= startNr - 1 && d.row < endNr); + } + } + } else { + const lineNr = parseInt(searchTermChunk); + if (!isNaN(lineNr)) { + firstLineNumber = lineNr; + code = code.split(/\r\n|\r|\n/).find((e, i) => i === lineNr - 1); + filteredDataSet = filteredDataSet.filter(d => d.row === lineNr - 1); + } + } + } + break; } } @@ -178,8 +219,8 @@ export default class SearchAlgorithm { static performIssueSearch(dataSet, searchTerm) { let filteredDataSet = dataSet.data; - const firstLineNumber = 1; - const code = dataSet.code; + let firstLineNumber = 1; + let code = dataSet.code; const searchTermChunks = searchTerm.toLowerCase().split(' '); for (let i = 0; i < searchTermChunks.length; i++) { switch (searchTermChunks[i]) { @@ -211,6 +252,47 @@ export default class SearchAlgorithm { ('' + d.description).toLowerCase().includes(searchTermChunks[i]) || d.iid.toLowerCase().includes(searchTermChunks[i]) ); + break; + case '-l': + case '-line': + case '-lines': + if (i < searchTermChunks.length - 1) { + i++; + const searchTermChunk = searchTermChunks[i]; + if (searchTermChunk.includes('-')) { + if (searchTermChunk.startsWith('-')) { + const endNr = parseInt(searchTermChunk.substring(1)); + if (!isNaN(endNr)) { + code = code.split(/\r\n|\r|\n/).filter((e, i) => i < endNr).join('\n'); + filteredDataSet = filteredDataSet.filter(d => d.row < endNr); + } + } else if (searchTermChunk.endsWith('-')) { + const startNr = parseInt(searchTermChunk.substring(0, searchTermChunk.length - 1)); + if (!isNaN(startNr)) { + firstLineNumber = startNr; + code = code.split(/\r\n|\r|\n/).filter((e, i) => i >= startNr - 1).join('\n'); + filteredDataSet = filteredDataSet.filter(d => d.row >= startNr - 1); + } + } else { + const rowSearchTermChunks = searchTermChunk.split('-'); + const startNr = parseInt(rowSearchTermChunks[0]); + const endNr = parseInt(rowSearchTermChunks[1]); + if (!isNaN(startNr) && !isNaN(endNr)) { + firstLineNumber = startNr; + code = code.split(/\r\n|\r|\n/).filter((e, i) => i >= startNr - 1 && i < endNr).join('\n'); + filteredDataSet = filteredDataSet.filter(d => d.row >= startNr - 1 && d.row < endNr); + } + } + } else { + const lineNr = parseInt(searchTermChunk); + if (!isNaN(lineNr)) { + firstLineNumber = lineNr; + code = code.split(/\r\n|\r|\n/).find((e, i) => i === lineNr - 1); + filteredDataSet = filteredDataSet.filter(d => d.row === lineNr - 1); + } + } + } + break; } } diff --git a/ui/src/visualizations/code-hotspots/components/searchBar/searchTextHighlighting.js b/ui/src/visualizations/code-hotspots/components/searchBar/searchTextHighlighting.js index accdc106..e6c62b77 100644 --- a/ui/src/visualizations/code-hotspots/components/searchBar/searchTextHighlighting.js +++ b/ui/src/visualizations/code-hotspots/components/searchBar/searchTextHighlighting.js @@ -109,6 +109,21 @@ export default class SearchTextHighlighting { modifier: '-email', color: '#ffcc00', secondary_color: '#fff7d8' + }, + { + modifier: '-l', + color: '#007aff', + secondary_color: '#ebf5ff' + }, + { + modifier: '-line', + color: '#007aff', + secondary_color: '#ebf5ff' + }, + { + modifier: '-lines', + color: '#007aff', + secondary_color: '#ebf5ff' } ]; return this.performTextHighlighting(searchTerm, highlightSet); @@ -145,6 +160,21 @@ export default class SearchTextHighlighting { modifier: '-iid', color: '#ff9500', secondary_color: '#fff7eb' + }, + { + modifier: '-l', + color: '#007aff', + secondary_color: '#ebf5ff' + }, + { + modifier: '-line', + color: '#007aff', + secondary_color: '#ebf5ff' + }, + { + modifier: '-lines', + color: '#007aff', + secondary_color: '#ebf5ff' } ]; return this.performTextHighlighting(searchTerm, highlightSet); diff --git a/ui/src/visualizations/code-hotspots/components/settings/settings.js b/ui/src/visualizations/code-hotspots/components/settings/settings.js index 87405a6a..1070662d 100644 --- a/ui/src/visualizations/code-hotspots/components/settings/settings.js +++ b/ui/src/visualizations/code-hotspots/components/settings/settings.js @@ -153,30 +153,30 @@ export default class Settings extends React.PureComponent { Classic

diff --git a/ui/src/visualizations/code-hotspots/config/config.js b/ui/src/visualizations/code-hotspots/config/config.js index 49a065e0..63964d63 100644 --- a/ui/src/visualizations/code-hotspots/config/config.js +++ b/ui/src/visualizations/code-hotspots/config/config.js @@ -48,6 +48,7 @@ const CodeHotspotsConfigComponent = props => { {options}
+
diff --git a/ui/src/visualizations/code-hotspots/help.js b/ui/src/visualizations/code-hotspots/help.js index 03ff564f..409f0f08 100644 --- a/ui/src/visualizations/code-hotspots/help.js +++ b/ui/src/visualizations/code-hotspots/help.js @@ -28,32 +28,32 @@ function unhighlightElementByID() { export default () =>

Code Hotspots Help

-

The Code Hotspots visualization shows different line based metrics in combination with the sourcecode od a file.

+

The Code Hotspots visualization shows different line-based metrics in combination with the sourcecode or a file.

Different visualizations

  • { + onMouseEnter={() => { highlightElementByID('CpVButton'); }} - onMouseLeave={event => { + onMouseLeave={() => { unhighlightElementByID(); }}> Changes per version
  • { + onMouseEnter={() => { highlightElementByID('CpDButton'); }} - onMouseLeave={event => { + onMouseLeave={() => { unhighlightElementByID(); }}> Changes per developer
  • { + onMouseEnter={() => { highlightElementByID('CpIButton'); }} - onMouseLeave={event => { + onMouseLeave={() => { unhighlightElementByID(); }}> Changes per issue @@ -62,86 +62,166 @@ export default () =>

    Main View

    { + onMouseEnter={() => { highlightElementByID('heatmap'); }} - onMouseLeave={event => { + onMouseLeave={() => { unhighlightElementByID(); }}>

    Code View and Heatmap

    - The code view shows the code of the currently selected file with a heatmap drawn underneath with the selected metric. For example if - the changes per version mode the x axis gives the lines, the y axis gives the versions and the color is the number of changes that - happened in the line and version. + The code view shows the code of the currently selected file with a heatmap drawn underneath with the selected metric. For example, + if the changes per version mode the x-axis gives the lines, the y axis gives the versions and the colour is the number of changes + that happened in the line and version.

    { + onMouseEnter={() => { highlightElementByID('barChartContainer'); }} - onMouseLeave={event => { + onMouseLeave={() => { unhighlightElementByID(); }}>

    Column summary

    - The barchart on the top shows the summary of each column as barchart. For example in the changes per version mode it shows the + The barchart on the top shows the summary of each column as barchart. For example, in the changes per version mode, it shows the different versions where the file was changed and the high of the column shows the changes per version

    - Its also possible to hover over the bars to get additional information and if the changes per verison mode is selected its also + It is also possible to hover over the bars to get additional information and if the changes per version mode is selected it is also possible to click the individual bars to show the sourcecode to the selected version.

    { + onMouseEnter={() => { highlightElementByID('rowSummaryContainer'); }} - onMouseLeave={event => { + onMouseLeave={() => { unhighlightElementByID(); }}>

    Row summary

    - The row summary shows a summary over each code line to get a overview how often a specific line changed over for example the + The row summary shows a summary over each code line to get an overview of how often a specific line changed over for example the different versions

    -

    Its also possible to hover over the bars to get additional information.

    +

    It is also possible to hover over the bars to get additional information.

    +
    +
    { + highlightElementByID('chartBranchView'); + }} + onMouseLeave={() => { + unhighlightElementByID(); + }}> +

    Branch View (Changes/Version mode only)

    +

    + The Branch view shows a graphical overview of all the displayed versions and how they are connected in branches with each other. If + the previous version is currently not displayed the branch view shows a dotted line. +

    +

    + It is also possible to hover over the bars to get additional information and if the changes per version mode is selected it is also + possible to click the individual bars to show the sourcecode to the selected version. +

    Sidebar

    { + onMouseEnter={() => { highlightElementByID('branchSelector'); }} - onMouseLeave={event => { + onMouseLeave={() => { unhighlightElementByID(); }}>

    Branch selector

    -

    Select the Branch from which you want to show the current version of the code.

    +

    Select the branch from which you want to show the current version of the code.

    { + onMouseEnter={() => { highlightElementByID('fileSelector', false); }} - onMouseLeave={event => { + onMouseLeave={() => { unhighlightElementByID(); }}>

    File browser

    Select the file you want to analyse. Folders are marked blue and with a folder symbol.

    { + onMouseEnter={() => { highlightElementByID('SettingsButton'); }} - onMouseLeave={event => { + onMouseLeave={() => { unhighlightElementByID(); }}>

    Settings

    -

    In the settings it is possible to parameterize the visualization.

    +

    In the settings, it is possible to parameterize the visualization.

    Parameterization parameter

    • Automatic or custom scale of the different charts
    • Heatmap scale
    • Column summary scale
    • Row summary scale
    • +
    • Date Range (Display only data from a specific period.)
    • +
    • Heatmap Style (Change the style of the main visualization.)
    • +
    +
    +
    { + highlightElementByID('mainSearch'); + }} + onMouseLeave={() => { + unhighlightElementByID(); + }}> +

    Main Search

    +

    + Search for versions, developers, issues or specific lines. It is also possible to use a search modifier to make your search even + more accurate. To use a search modifier just type -[modifier] followed by a space followed by the search term. +

    +

    Possible Modifier in Changes/Version mode

    +
      +
    • -m or -message: search for an occurrence in the commit message
    • +
    • -s or -sha: search for a specific commit sha
    • +
    • -d or -developer: search for a specific developer
    • +
    • -b or -branch: search for a specific branch
    • +
    • + -l or -line or -lines: search for a specific line or line range. Possible search terms are linernumber, -endlinenumber, + startlinenumber- and startlinenumber-endlinenumber +
    • +
    +

    Possible Modifier in Changes/Developer mode

    +
      +
    • -n or -name: search for a specific developer name
    • +
    • -e or -email: search for a specific developer email
    • +
    • + -l or -line or -lines: search for a specific line or line range. Possible search terms are linernumber, -endlinenumber, + startlinenumber- and startlinenumber-endlinenumber +
    • +
    +

    Possible Modifier in Changes/Issue mode

    +
      +
    • -t or -title: search for a specific issue title
    • +
    • -d or -description: search for a specific issue description
    • +
    • -i or -iid: search for a specific issue iid
    • +
    • + -l or -line or -lines: search for a specific line or line range. Possible search terms are linernumber, -endlinenumber, + startlinenumber- and startlinenumber-endlinenumber +
    • +
    +
    +
    { + highlightElementByID('fileSearch'); + }} + onMouseLeave={() => { + unhighlightElementByID(); + }}> +

    File Search

    +

    + Search for files in the filebrowser. It is also possible to use a search modifier to make your search even more accurate. To use a + search modifier just type -[modifier] followed by a space followed by the search term. +

    +

    Possible Modifier

    +
      +
    • -f or -file: search for a specific file name
    • +
    • -t or -type: search for a specific file type

    diff --git a/ui/src/visualizations/code-hotspots/styles.scss b/ui/src/visualizations/code-hotspots/styles.scss index b14091dc..8d405175 100644 --- a/ui/src/visualizations/code-hotspots/styles.scss +++ b/ui/src/visualizations/code-hotspots/styles.scss @@ -95,6 +95,7 @@ .highlighted{ position: fixed; + z-index: 9999; border: red dashed 5px; } @@ -112,3 +113,7 @@ .branchSelect{ width:100%; } + +.branchSelect>select{ + border-color: #cccccc; +}