From 6428ede698941f0627f5e6c1c65e4ec6f0a320d1 Mon Sep 17 00:00:00 2001 From: 7PH Date: Mon, 29 Jul 2024 12:45:24 +0200 Subject: SONAR-22495 Show issue red message box below its exact location in the code snippet --- .../JupyterNotebookIssueViewer.tsx | 134 ++++++++++++--------- .../main/js/components/rules/IssueTabViewer.tsx | 5 +- .../SourceViewer/JupyterNotebookViewer.tsx | 12 +- server/sonar-web/tailwind.base.config.js | 1 + 4 files changed, 90 insertions(+), 62 deletions(-) (limited to 'server/sonar-web') diff --git a/server/sonar-web/src/main/js/apps/issues/jupyter-notebook/JupyterNotebookIssueViewer.tsx b/server/sonar-web/src/main/js/apps/issues/jupyter-notebook/JupyterNotebookIssueViewer.tsx index 66e5f7bd168..fa94574436b 100644 --- a/server/sonar-web/src/main/js/apps/issues/jupyter-notebook/JupyterNotebookIssueViewer.tsx +++ b/server/sonar-web/src/main/js/apps/issues/jupyter-notebook/JupyterNotebookIssueViewer.tsx @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { ICell, INotebookContent } from '@jupyterlab/nbformat'; +import { ICodeCell, INotebookContent, isCode } from '@jupyterlab/nbformat'; import { Spinner } from '@sonarsource/echoes-react'; import { FlagMessage, @@ -26,7 +26,7 @@ import { LineFinding, } from 'design-system'; import React from 'react'; -import { JupyterCell } from '~sonar-aligned/components/SourceViewer/JupyterNotebookViewer'; +import { JupyterCodeCell } from '~sonar-aligned/components/SourceViewer/JupyterNotebookViewer'; import { getBranchLikeQuery } from '~sonar-aligned/helpers/branch-like'; import { JsonIssueMapper } from '~sonar-aligned/helpers/json-issue-mapper'; import { translate } from '../../../helpers/l10n'; @@ -46,7 +46,10 @@ export function JupyterNotebookIssueViewer(props: Readonly(null); + const [renderedCells, setRenderedCells] = React.useState<{ + after: ICodeCell; + before: ICodeCell[]; + } | null>(null); React.useEffect(() => { if (!issue.textRange || typeof data !== 'string') { @@ -62,59 +65,67 @@ export function JupyterNotebookIssueViewer(props: Readonly isCode(cell)); + + // Split the last cell because we want to show the issue message at the end of the primary location + const sourceBefore = cells[cells.length - 1].source.slice(0, endOffset.line + 1); + const sourceAfter = cells[cells.length - 1].source.slice(endOffset.line + 1); + const lastCell = { + ...cells[cells.length - 1], + source: sourceAfter, + }; + cells[cells.length - 1] = { + cell_type: 'code', + source: sourceBefore, + execution_count: 0, + outputs: [], + metadata: {}, + }; + + for (let i = 0; i < cells.length; i++) { + const cell = cells[i]; + cell.source = Array.isArray(cell.source) ? cell.source : [cell.source]; + + // Any cell between the first and last cell should be fully underlined + const start = i === 0 ? startOffset : { line: 0, cursorOffset: 0 }; + const end = + i === cells.length - 1 + ? endOffset + : { + line: cell.source.length - 1, + cursorOffset: cell.source[cell.source.length - 1].length, + }; + + cell.source = hljsUnderlinePlugin.tokenize(cell.source, [ { - start: { line: 0, cursorOffset: 0 }, - end: endOffset, + start, + end, }, ]); } - const cells = Array.from(new Set([startOffset.cell, endOffset.cell])).map( - (cellIndex) => jupyterNotebook.cells[cellIndex], - ); - - setRenderedCells(cells); + setRenderedCells({ + before: cells, + after: lastCell, + }); }, [issue, data]); if (isLoading) { @@ -131,19 +142,30 @@ export function JupyterNotebookIssueViewer(props: Readonly - - } - selected - /> - {renderedCells.map((cell, index) => ( - + {renderedCells.before.map((cell, index) => ( + ))} +
+ + } + selected + /> +
+ ); } diff --git a/server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx b/server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx index b43525899e8..c1ccaa506e1 100644 --- a/server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx +++ b/server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx @@ -356,7 +356,10 @@ export class IssueTabViewer extends React.PureComponent 0. run={top > 0} /> - +
(this.headerNode = node)}> ) { return null; } -export function JupyterCodeCell({ - source, - outputs, -}: Readonly<{ outputs: IOutput[]; source: string[] }>) { +export function JupyterCodeCell( + props: Readonly<{ className?: string; outputs?: IOutput[]; source: string[] }>, +) { + const { source, outputs, className } = props; + return ( -
+
diff --git a/server/sonar-web/tailwind.base.config.js b/server/sonar-web/tailwind.base.config.js index 8f73e0c950b..3580352def3 100644 --- a/server/sonar-web/tailwind.base.config.js +++ b/server/sonar-web/tailwind.base.config.js @@ -104,6 +104,7 @@ module.exports = { }, zIndex: { normal: '2', + 'issue-header': '10', 'project-list-header': '30', filterbar: '50', 'content-popup': '52', -- cgit v1.2.3