Skip to content

Commit 9b70320

Browse files
committed
Merge branch 'topic/vscode-spark-sarif' into 'master'
Automatically open GNATprove SARIF report after execution See merge request eng/ide/ada_language_server!2107
2 parents 666201e + 36ab17e commit 9b70320

File tree

1 file changed

+131
-34
lines changed

1 file changed

+131
-34
lines changed

integration/vscode/ada/src/ExtensionState.ts

Lines changed: 131 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
CMD_SHOW_GPR_LS_OUTPUT,
1717
} from './constants';
1818
import { AdaInitialDebugConfigProvider, initializeDebugging } from './debugConfigProvider';
19-
import { logger } from './extension';
19+
import { adaExtState, logger } from './extension';
2020
import { GnatTaskProvider } from './gnatTaskProvider';
2121
import { initializeTesting } from './gnattest';
2222
import { GprTaskProvider } from './gprTaskProvider';
@@ -153,10 +153,13 @@ export class ExtensionState {
153153

154154
// Add a listener on tasks to open the SARIF Viewer when the
155155
// task that ends outputs a SARIF file.
156-
vscode.tasks.onDidEndTaskProcess(async (e) => {
157-
const task = e.execution.task;
158-
await openSARIFViewerIfNeeded(task);
159-
}),
156+
vscode.tasks.onDidEndTaskProcess(openSARIFViewerIfNeeded),
157+
/**
158+
* Add a listener on tasks start to close SARIF report that might
159+
* be overwritten, to avoid parse errors from the SARIF extension
160+
* when the report is being deleted/re-written.
161+
*/
162+
vscode.tasks.onDidStartTaskProcess(closeSARIFViewerIfNeeded),
160163
];
161164
};
162165

@@ -696,37 +699,110 @@ export class ExtensionState {
696699
}
697700
}
698701

702+
const currentlyOpenedSASSarifs: Set<vscode.Uri> = new Set();
703+
const currentlyOpenedGnatproveSarifs: Set<vscode.Uri> = new Set();
704+
705+
async function closeSARIFViewerIfNeeded(e: vscode.TaskEndEvent) {
706+
/**
707+
* SARIF reports need to be closed and reopened to refresh their content.
708+
*
709+
* Moreover, overwriting a SARIF report that is currently opened in the
710+
* SARIF Viewer seems to trigger sporadic errors. So it's better to close
711+
* the SARIF report at the start of the task.
712+
*
713+
* Reports must be managed separately for GNATprove and for GNAT
714+
* SAS because we don't want an execution of GNAT SAS to close
715+
* reports opened for GNATprove and vice versa.
716+
*/
717+
718+
const task = e.execution.task;
719+
if (task.definition.type == TASK_TYPE_SPARK || isGnatSASSarifTask(task)) {
720+
const sarif = await getSarifExtAPI();
721+
if (sarif) {
722+
const current =
723+
task.definition.type == TASK_TYPE_SPARK
724+
? currentlyOpenedGnatproveSarifs
725+
: currentlyOpenedSASSarifs;
726+
727+
await sarif.closeLogs([...current]);
728+
current.clear();
729+
}
730+
}
731+
}
732+
733+
function isGnatSASSarifTask(task: vscode.Task): boolean {
734+
return (
735+
task.definition.type == TASK_TYPE_ADA &&
736+
!!(task.definition as SimpleTaskDef).args?.some((arg) => getArgValue(arg).includes('sarif'))
737+
);
738+
}
739+
699740
/**
700741
*
701742
* Open the SARIF Viewer if the given task outputs its results in
702743
* a SARIF file (e.g: GNAT SAS Report task).
703744
*/
704-
async function openSARIFViewerIfNeeded(task: vscode.Task) {
745+
async function openSARIFViewerIfNeeded(e: vscode.TaskStartEvent) {
746+
const task = e.execution.task;
705747
const definition: SimpleTaskDef = task.definition;
706748

707749
if (definition) {
708750
const args = definition.args;
709751

710-
if (args?.some((arg) => getArgValue(arg).includes('sarif'))) {
711-
const execution = task.execution;
712-
let cwd = undefined;
752+
if (definition.type == TASK_TYPE_SPARK || isGnatSASSarifTask(task)) {
753+
const sarifExtAPI = await getSarifExtAPI();
713754

714-
if (execution && execution instanceof vscode.ShellExecution) {
715-
cwd = execution.options?.cwd;
755+
if (!sarifExtAPI) {
756+
// Could not access the SARIF Viewer extension API
757+
logger.warn(
758+
'Could not access the SARIF Viewer extension API: is the extension installed?',
759+
);
760+
return;
716761
}
717762

718-
if (!cwd && vscode.workspace.workspaceFolders) {
719-
cwd = vscode.workspace.workspaceFolders[0].uri.fsPath;
720-
}
763+
if (definition.type == TASK_TYPE_SPARK) {
764+
/**
765+
* Find new reports and open them
766+
*/
767+
const objDir = await adaExtState.getObjectDir();
768+
const gnatproveDir = path.join(objDir, 'gnatprove');
769+
if (existsSync(gnatproveDir)) {
770+
const gnatProveUri = vscode.Uri.file(gnatproveDir);
771+
const sarifFiles = await vscode.workspace.fs.readDirectory(gnatProveUri);
772+
const sarifUris = sarifFiles
773+
.filter(
774+
([name, type]) =>
775+
type === vscode.FileType.File && name.endsWith('.sarif'),
776+
)
777+
.map(([name]) => vscode.Uri.joinPath(gnatProveUri, name));
778+
779+
await sarifExtAPI.openLogs(sarifUris);
780+
sarifUris.forEach((v) => currentlyOpenedGnatproveSarifs.add(v));
781+
}
782+
} else {
783+
const execution = task.execution;
784+
let cwd = undefined;
721785

722-
const sarifExt = vscode.extensions.getExtension('ms-sarifvscode.sarif-viewer');
786+
if (execution instanceof vscode.ShellExecution) {
787+
cwd = execution.options?.cwd;
788+
}
723789

724-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
725-
const sarifExtAPI = sarifExt ? await sarifExt.activate() : undefined;
790+
if (!cwd && vscode.workspace.workspaceFolders) {
791+
cwd = vscode.workspace.workspaceFolders[0].uri.fsPath;
792+
}
793+
794+
if (!cwd) {
795+
// Could not determine the current working directory
796+
logger.warn('Could not determine the current working directory');
797+
return;
798+
}
726799

727-
if (cwd && sarifExtAPI) {
728800
const cwdURI = vscode.Uri.file(cwd);
729-
const outputFilePathArgRaw = args.find((arg) =>
801+
802+
/**
803+
* Find the SARIF output file argument
804+
*/
805+
const outputFilePathArgRaw = args!.find((arg) =>
730806
getArgValue(arg).includes('.sarif'),
731807
);
732808

@@ -739,28 +815,49 @@ async function openSARIFViewerIfNeeded(task: vscode.Task) {
739815
const outputFilePath = outputFilePathArg.includes('=')
740816
? outputFilePathArg.split('=').pop()
741817
: outputFilePathArg;
742-
743818
if (outputFilePath) {
744819
const sarifFileURI = isAbsolute(outputFilePath)
745820
? vscode.Uri.file(outputFilePath)
746821
: vscode.Uri.joinPath(cwdURI, outputFilePath);
747-
748-
/**
749-
* If we open a SARIF report that was already open, the
750-
* SARIF Viewer extension does not refresh the
751-
* contents. It is necessary to close the report and
752-
* reopen it.
753-
*/
754-
// eslint-disable-next-line max-len
755-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
756-
await sarifExtAPI.closeLogs([sarifFileURI]);
757-
758-
// eslint-disable-next-line max-len
759-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
760-
await sarifExtAPI.openLogs([sarifFileURI]);
822+
if (existsSync(sarifFileURI.fsPath)) {
823+
await sarifExtAPI.openLogs([sarifFileURI]);
824+
currentlyOpenedSASSarifs.add(sarifFileURI);
825+
}
761826
}
762827
}
763828
}
764829
}
765830
}
766831
}
832+
833+
/**
834+
* This is the API of the SARIF extension, as defined at
835+
* https://github.com/microsoft/sarif-vscode-extension/blob/main/src/extension/index.d.ts
836+
*
837+
* Since the package is not available on npmjs.org for direct referencing, we
838+
* copy it here to benefit from typing.
839+
*/
840+
interface SARIFExtAPI {
841+
/**
842+
* Note: If a log has been modified after open was opened, a close and
843+
* re-open will be required to "refresh" that log.
844+
*
845+
* @param logs - An array of Uris to open.
846+
*/
847+
openLogs(logs: vscode.Uri[]): Promise<void>;
848+
closeLogs(
849+
logs: vscode.Uri[],
850+
_options?: unknown,
851+
cancellationToken?: vscode.CancellationToken,
852+
): Promise<void>;
853+
closeAllLogs(): Promise<void>;
854+
selectByIndex(uri: vscode.Uri, runIndex: number, resultIndex: number): Promise<void>;
855+
uriBases: ReadonlyArray<vscode.Uri>;
856+
dispose(): void;
857+
}
858+
859+
async function getSarifExtAPI(): Promise<SARIFExtAPI | undefined> {
860+
const sarifExt = vscode.extensions.getExtension('ms-sarifvscode.sarif-viewer');
861+
const sarifExtAPI = sarifExt ? ((await sarifExt.activate()) as SARIFExtAPI) : undefined;
862+
return sarifExtAPI;
863+
}

0 commit comments

Comments
 (0)