aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src
diff options
context:
space:
mode:
authorJeremy Davis <jeremy.davis@sonarsource.com>2019-11-01 16:17:42 +0900
committerSonarTech <sonartech@sonarsource.com>2019-11-07 11:45:16 +0100
commitfbdba9e77ae6162ca628895002fb513ee7f093fd (patch)
treed637b78eab91a6c270f5b4b30efd35df2f978e33 /server/sonar-web/src
parent969407f6a612223ab19c22c36b5121c2c6659498 (diff)
downloadsonarqube-fbdba9e77ae6162ca628895002fb513ee7f093fd.tar.gz
sonarqube-fbdba9e77ae6162ca628895002fb513ee7f093fd.zip
SONAR-12652 Fix 'missing' snippet lines when expanding
Diffstat (limited to 'server/sonar-web/src')
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetViewer.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts147
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts32
4 files changed, 124 insertions, 88 deletions
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetViewer.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetViewer.tsx
index 15a74e45927..dc1736a8af0 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetViewer.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetViewer.tsx
@@ -87,11 +87,11 @@ export default class ComponentSourceSnippetViewer extends React.PureComponent<Pr
}
createSnippetsFromProps() {
- const snippets = createSnippets(
- this.props.snippetGroup.locations,
- this.props.last,
- this.props.issue.secondaryLocations.length > 0 ? this.props.issue : undefined
- );
+ const snippets = createSnippets({
+ locations: this.props.snippetGroup.locations,
+ issue: this.props.issue,
+ addIssueLocation: this.props.issue.secondaryLocations.length > 0
+ });
this.setState({ snippets });
}
@@ -117,6 +117,22 @@ export default class ComponentSourceSnippetViewer extends React.PureComponent<Pr
return { wrapper, table };
}
+ /*
+ * Clean after animation
+ */
+ cleanDom(index: number) {
+ const nodes = this.getNodes(index);
+
+ if (!nodes) {
+ return;
+ }
+
+ const { wrapper, table } = nodes;
+
+ table.style.marginTop = '';
+ wrapper.style.maxHeight = '';
+ }
+
setMaxHeight(index: number, value?: number, up = false) {
const nodes = this.getNodes(index);
@@ -220,6 +236,7 @@ export default class ComponentSourceSnippetViewer extends React.PureComponent<Pr
// Wait for transition to finish before updating dom
setTimeout(() => {
this.setState({ snippets: newSnippets.filter(s => !s.toDelete) });
+ this.cleanDom(snippetIndex);
}, 200);
}
);
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx
index a932293f62b..4aefbdc7670 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx
@@ -17,8 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import * as React from 'react';
import classNames from 'classnames';
+import * as React from 'react';
import ExpandSnippetIcon from 'sonar-ui-common/components/icons/ExpandSnippetIcon';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { scrollHorizontally } from 'sonar-ui-common/helpers/scrolling';
@@ -30,7 +30,7 @@ import {
optimizeLocationMessage,
optimizeSelectedIssue
} from '../../../components/SourceViewer/helpers/lines';
-import { inSnippet, LINES_BELOW_LAST } from './utils';
+import { inSnippet, LINES_BELOW_ISSUE } from './utils';
interface Props {
branchLike: T.BranchLike | undefined;
@@ -185,7 +185,7 @@ export default class SnippetViewer extends React.PureComponent<Props> {
.filter(l => inSnippet(l, snippet) && (l === issueLine || openIssuesByLine[l]))
);
const verticalBuffer = last
- ? Math.max(0, LINES_BELOW_LAST - (bottomLine - lowestVisibleIssue))
+ ? Math.max(0, LINES_BELOW_ISSUE - (bottomLine - lowestVisibleIssue))
: 0;
const displayDuplications = snippet.some(s => !!s.duplicated);
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts
index f33a99bccbf..1492af96501 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts
@@ -82,97 +82,108 @@ describe('groupLocationsByComponent', () => {
describe('createSnippets', () => {
it('should merge snippets correctly', () => {
- const results = createSnippets(
- [
- mockFlowLocation({
- textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 }
- }),
- mockFlowLocation({
- textRange: { startLine: 19, startOffset: 2, endLine: 19, endOffset: 3 }
- })
- ],
- false
- );
+ const locations = [
+ mockFlowLocation({
+ textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 }
+ }),
+ mockFlowLocation({
+ textRange: { startLine: 19, startOffset: 2, endLine: 19, endOffset: 3 }
+ })
+ ];
+ const results = createSnippets({
+ locations,
+ issue: mockIssue(false, locations[1]),
+ addIssueLocation: false
+ });
expect(results).toHaveLength(1);
- expect(results[0]).toEqual({ index: 0, start: 14, end: 21 });
+ expect(results[0]).toEqual({ index: 0, start: 14, end: 28 });
});
it('should merge snippets correctly, even when not in sequence', () => {
- const results = createSnippets(
- [
- mockFlowLocation({
- textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 }
- }),
- mockFlowLocation({
- textRange: { startLine: 47, startOffset: 2, endLine: 47, endOffset: 3 }
- }),
- mockFlowLocation({
- textRange: { startLine: 14, startOffset: 2, endLine: 14, endOffset: 3 }
- })
- ],
- false
- );
+ const locations = [
+ mockFlowLocation({
+ textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 }
+ }),
+ mockFlowLocation({
+ textRange: { startLine: 47, startOffset: 2, endLine: 47, endOffset: 3 }
+ }),
+ mockFlowLocation({
+ textRange: { startLine: 14, startOffset: 2, endLine: 14, endOffset: 3 }
+ })
+ ];
+ const results = createSnippets({
+ locations,
+ issue: mockIssue(false, locations[2]),
+ addIssueLocation: false
+ });
expect(results).toHaveLength(2);
- expect(results[0]).toEqual({ index: 0, start: 12, end: 18 });
+ expect(results[0]).toEqual({ index: 0, start: 12, end: 23 });
expect(results[1]).toEqual({ index: 1, start: 45, end: 49 });
});
it('should merge three snippets together', () => {
- const results = createSnippets(
- [
- mockFlowLocation({
- textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 }
- }),
- mockFlowLocation({
- textRange: { startLine: 47, startOffset: 2, endLine: 47, endOffset: 3 }
- }),
- mockFlowLocation({
- textRange: { startLine: 22, startOffset: 2, endLine: 22, endOffset: 3 }
- }),
- mockFlowLocation({
- textRange: { startLine: 18, startOffset: 2, endLine: 18, endOffset: 3 }
- })
- ],
- false
- );
+ const locations = [
+ mockFlowLocation({
+ textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 }
+ }),
+ mockFlowLocation({
+ textRange: { startLine: 47, startOffset: 2, endLine: 47, endOffset: 3 }
+ }),
+ mockFlowLocation({
+ textRange: { startLine: 23, startOffset: 2, endLine: 23, endOffset: 3 }
+ }),
+ mockFlowLocation({
+ textRange: { startLine: 18, startOffset: 2, endLine: 18, endOffset: 3 }
+ })
+ ];
+ const results = createSnippets({
+ locations,
+ issue: mockIssue(false, locations[0]),
+ addIssueLocation: false
+ });
expect(results).toHaveLength(2);
- expect(results[0]).toEqual({ index: 0, start: 14, end: 24 });
+ expect(results[0]).toEqual({ index: 0, start: 14, end: 25 });
expect(results[1]).toEqual({ index: 1, start: 45, end: 49 });
});
it("should prepend the issue's main location if necessary", () => {
- const results = createSnippets(
- [
- mockFlowLocation({
- textRange: { startLine: 47, startOffset: 2, endLine: 47, endOffset: 3 }
- }),
- mockFlowLocation({
- textRange: { startLine: 22, startOffset: 2, endLine: 22, endOffset: 3 }
- })
- ],
- false,
- mockIssue(false, { textRange: { startLine: 5, endLine: 5, startOffset: 0, endOffset: 0 } })
- );
+ const locations = [
+ mockFlowLocation({
+ textRange: { startLine: 47, startOffset: 2, endLine: 47, endOffset: 3 }
+ }),
+ mockFlowLocation({
+ textRange: { startLine: 22, startOffset: 2, endLine: 22, endOffset: 3 }
+ })
+ ];
+ const results = createSnippets({
+ locations,
+ issue: mockIssue(false, {
+ textRange: { startLine: 5, endLine: 5, startOffset: 0, endOffset: 0 }
+ }),
+ addIssueLocation: true
+ });
expect(results).toHaveLength(3);
expect(results[0]).toEqual({ index: 0, start: 3, end: 14 });
});
it('should handle last component', () => {
- const results = createSnippets(
- [
- mockFlowLocation({
- textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 }
- }),
- mockFlowLocation({
- textRange: { startLine: 19, startOffset: 2, endLine: 19, endOffset: 3 }
- })
- ],
- true
- );
+ const locations = [
+ mockFlowLocation({
+ textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 }
+ }),
+ mockFlowLocation({
+ textRange: { startLine: 19, startOffset: 2, endLine: 19, endOffset: 3 }
+ })
+ ];
+ const results = createSnippets({
+ locations,
+ issue: mockIssue(false, locations[1]),
+ addIssueLocation: false
+ });
expect(results).toHaveLength(1);
expect(results[0]).toEqual({ index: 0, start: 14, end: 28 });
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts
index b8938650d5f..9a5e7907b91 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts
@@ -20,7 +20,7 @@
const LINES_ABOVE = 2;
const LINES_BELOW = 2;
export const MERGE_DISTANCE = 4; // Merge if snippets are four lines away (separated by 3 lines) or fewer
-export const LINES_BELOW_LAST = 9;
+export const LINES_BELOW_ISSUE = 9;
export const EXPAND_BY_LINES = 10;
function unknownComponent(key: string): T.SnippetsByComponent {
@@ -54,21 +54,29 @@ function getPrimaryLocation(issue: T.Issue): T.FlowLocation {
};
}
-export function createSnippets(
- locations: T.FlowLocation[],
- last: boolean,
- issue?: T.Issue
-): T.Snippet[] {
+function addLinesBellow(params: { issue: T.Issue; locationEnd: number }) {
+ const { issue, locationEnd } = params;
+ const issueEndLine = (issue.textRange && issue.textRange.endLine) || 0;
+
+ if (!issueEndLine || issueEndLine === locationEnd) {
+ return locationEnd + LINES_BELOW_ISSUE;
+ }
+
+ return locationEnd + LINES_BELOW;
+}
+
+export function createSnippets(params: {
+ locations: T.FlowLocation[];
+ issue: T.Issue;
+ addIssueLocation: boolean;
+}): T.Snippet[] {
+ const { locations, issue, addIssueLocation } = params;
// For each location's range (2 above and 2 below), and then compare with other ranges
// to merge snippets that collide.
- return (issue ? [getPrimaryLocation(issue), ...locations] : locations).reduce(
+ return (addIssueLocation ? [getPrimaryLocation(issue), ...locations] : locations).reduce(
(snippets: T.Snippet[], loc, index) => {
const startIndex = Math.max(1, loc.textRange.startLine - LINES_ABOVE);
- const endIndex =
- loc.textRange.endLine +
- ((issue && index === 0) || (last && index === locations.length - 1)
- ? LINES_BELOW_LAST
- : LINES_BELOW);
+ const endIndex = addLinesBellow({ issue, locationEnd: loc.textRange.endLine });
let firstCollision: { start: number; end: number } | undefined;