Skip to content

Commit 5a7bb69

Browse files
committed
Merge branch 'topic/obsolete-tasks' into 'master'
Display a warning when obsolete tasks get executed Closes eng/spark/spark2014#432 See merge request eng/ide/ada_language_server!1443
2 parents 1a0f9fa + 5a52182 commit 5a7bb69

File tree

4 files changed

+103
-82
lines changed

4 files changed

+103
-82
lines changed

integration/vscode/ada/src/ExtensionState.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as vscode from 'vscode';
22
import { Disposable, LanguageClient } from 'vscode-languageclient/node';
33
import { createClient } from './clients';
4-
import GnatTaskProvider from './gnatTaskProvider';
5-
import GprTaskProvider from './gprTaskProvider';
4+
import { GnatTaskProvider } from './gnatTaskProvider';
5+
import { GprTaskProvider } from './gprTaskProvider';
66
import { registerTaskProviders } from './taskProviders';
77
import { getCustomEnvSettingName } from './helpers';
88

integration/vscode/ada/src/gnatTaskProvider.ts

Lines changed: 22 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import * as vscode from 'vscode';
1919
import {
2020
AllTaskKinds,
2121
DEFAULT_PROBLEM_MATCHER,
22+
WarningMessageExecution,
2223
TaskProperties,
2324
alire,
2425
allTaskProperties,
@@ -45,7 +46,7 @@ interface GnatTaskDefinition extends vscode.TaskDefinition {
4546
* order to preserve support for 'gnat' task types that still exist in User
4647
* workspaces.
4748
*/
48-
export default class GnatTaskProvider implements vscode.TaskProvider<vscode.Task> {
49+
export class GnatTaskProvider implements vscode.TaskProvider<vscode.Task> {
4950
/**
5051
* This flag is used to restore the proposal of 'gnat' tasks for debugging
5152
* purposes.
@@ -55,6 +56,11 @@ export default class GnatTaskProvider implements vscode.TaskProvider<vscode.Task
5556
public static gnatType = 'gnat' as const; // Task provider name
5657
gnatTasks: vscode.Task[] | undefined; // Known tasks
5758

59+
private readonly obsoletionMsg =
60+
"Tasks of type 'gnat' are obsolete. Please use tasks of type 'ada' or 'spark'.";
61+
62+
private readonly obsoleteWarningExecution = new WarningMessageExecution(this.obsoletionMsg);
63+
5864
constructor() {
5965
this.gnatTasks = undefined; // Do we really need this???
6066
}
@@ -63,7 +69,7 @@ export default class GnatTaskProvider implements vscode.TaskProvider<vscode.Task
6369
provideTasks(_token: vscode.CancellationToken): vscode.ProviderResult<vscode.Task[]> {
6470
if (GnatTaskProvider.DEPRECATED) {
6571
// We return a single dummy task to convey an obsoletion message to Users.
66-
const msg = 'The "gnat" task type is obsolete. Use "ada" tasks instead.';
72+
const msg = this.obsoletionMsg;
6773
return [
6874
new vscode.Task(
6975
{
@@ -74,7 +80,7 @@ export default class GnatTaskProvider implements vscode.TaskProvider<vscode.Task
7480
vscode.TaskScope.Workspace,
7581
msg,
7682
GnatTaskProvider.gnatType,
77-
new vscode.ShellExecution(`echo ${msg}`),
83+
this.obsoleteWarningExecution,
7884
DEFAULT_PROBLEM_MATCHER
7985
),
8086
];
@@ -95,42 +101,19 @@ export default class GnatTaskProvider implements vscode.TaskProvider<vscode.Task
95101
// eslint-disable-next-line @typescript-eslint/no-unused-vars
96102
_token: vscode.CancellationToken
97103
): vscode.ProviderResult<vscode.Task> {
98-
// We keep the previous task resolution code so that tasks in User
99-
// workspaces still work like before. Such tasks are also flagged as
100-
// obsolete in the tasks.json file to encourage Users to migrate to the
101-
// 'ada' task type.
102-
const definition = task.definition as GnatTaskDefinition;
103-
104-
// Check if the task in our known task
105-
if (definition.taskKind in allTaskProperties) {
106-
const taskKind = definition.taskKind as AllTaskKinds;
107-
const item: TaskProperties = allTaskProperties[taskKind];
108-
const extraArgsFromUser: string[] = Array.isArray(definition.args)
109-
? definition.args.map((x) => String(x))
110-
: [];
111-
const extraArgsFromTask = item.extra ? item.extra() : [];
112-
return alire().then(async (alr) => {
113-
const cmd = alr.concat(
114-
item.command,
115-
await getProjectArgs(),
116-
getScenarioArgs(),
117-
extraArgsFromUser,
118-
await extraArgsFromTask,
119-
getDiagnosticArgs()
120-
);
121-
const shell = new vscode.ShellExecution(cmd[0], cmd.slice(1));
122-
return new vscode.Task(
123-
definition,
124-
task.scope ?? vscode.TaskScope.Workspace,
125-
task.name,
126-
'ada',
127-
shell,
128-
DEFAULT_PROBLEM_MATCHER // problemMatchers
129-
);
130-
});
131-
} else {
132-
return task;
133-
}
104+
/**
105+
* Here we resolve 'gnat' tasks still defined in User workspaces. We
106+
* handle them by displaying an obsoletion warning if the task gets
107+
* executed.
108+
*/
109+
return new vscode.Task(
110+
task.definition,
111+
task.scope ?? vscode.TaskScope.Workspace,
112+
task.name,
113+
GnatTaskProvider.gnatType,
114+
this.obsoleteWarningExecution,
115+
DEFAULT_PROBLEM_MATCHER
116+
);
134117
}
135118
}
136119

integration/vscode/ada/src/gprTaskProvider.ts

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import * as vscode from 'vscode';
1919
import { LanguageClient } from 'vscode-languageclient/node';
2020
import { getMains, getExecutables, getProjectFile } from './helpers';
21-
import { DEFAULT_PROBLEM_MATCHER } from './taskProviders';
21+
import { DEFAULT_PROBLEM_MATCHER, WarningMessageExecution } from './taskProviders';
2222

2323
/**
2424
*
@@ -27,7 +27,7 @@ import { DEFAULT_PROBLEM_MATCHER } from './taskProviders';
2727
* workspaces.
2828
*/
2929

30-
export default class GprTaskProvider implements vscode.TaskProvider<vscode.Task> {
30+
export class GprTaskProvider implements vscode.TaskProvider<vscode.Task> {
3131
/**
3232
* This flag is used to restore the proposal of 'gpr' tasks for debugging
3333
* purposes.
@@ -37,6 +37,11 @@ export default class GprTaskProvider implements vscode.TaskProvider<vscode.Task>
3737
public static gprTaskType = 'gpr';
3838
glsTasks: vscode.Task[] | undefined;
3939

40+
private readonly obsoletionMsg =
41+
"The 'gpr' task type is obsolete. Please use 'ada' tasks instead.";
42+
43+
private readonly obsoleteWarningExecution = new WarningMessageExecution(this.obsoletionMsg);
44+
4045
constructor(client: LanguageClient) {
4146
this.glsTasks = undefined;
4247
this.client = client;
@@ -46,7 +51,7 @@ export default class GprTaskProvider implements vscode.TaskProvider<vscode.Task>
4651
async provideTasks(): Promise<vscode.Task[] | undefined> {
4752
if (GprTaskProvider.DEPRECATED) {
4853
// We return a single dummy task to convey an obsoletion message to Users.
49-
const msg = 'The "gpr" task type is obsolete. Use "ada" tasks instead.';
54+
const msg = this.obsoletionMsg;
5055
return [
5156
new vscode.Task(
5257
{
@@ -57,7 +62,7 @@ export default class GprTaskProvider implements vscode.TaskProvider<vscode.Task>
5762
vscode.TaskScope.Workspace,
5863
msg,
5964
GprTaskProvider.gprTaskType,
60-
new vscode.ShellExecution(`echo ${msg}`),
65+
this.obsoleteWarningExecution,
6166
DEFAULT_PROBLEM_MATCHER
6267
),
6368
];
@@ -75,42 +80,20 @@ export default class GprTaskProvider implements vscode.TaskProvider<vscode.Task>
7580
return this.glsTasks;
7681
}
7782

78-
async resolveTask(task: vscode.Task): Promise<vscode.Task | undefined> {
79-
const definition = task.definition;
80-
// Make sure that this looks like a execute task by checking that there is a projectFile.
81-
if (definition.type == GprTaskProvider.gprTaskType) {
82-
// Refresh gprbuild command line
83-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
84-
const projectFile: string =
85-
definition.projectFile != undefined
86-
? definition.projectFile
87-
: await getProjectFile(this.client);
88-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
89-
const args = getMainBuildArgs(projectFile, definition.main);
90-
let shell: vscode.ShellExecution;
91-
let title: string;
92-
if (definition.executable != undefined) {
93-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
94-
args.push('&&', 'clear', '&&', definition.executable);
95-
shell = new vscode.ShellExecution(fullCommand('gprbuild', args));
96-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
97-
title = `Build And Run Main: ${definition.main}`;
98-
} else {
99-
shell = new vscode.ShellExecution(fullCommand('gprbuild', args));
100-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
101-
title = `Build Main: ${definition.main}`;
102-
}
103-
// resolveTask requires that the same definition object be used.
104-
return new vscode.Task(
105-
definition,
106-
vscode.TaskScope.Workspace,
107-
title,
108-
'gpr',
109-
shell,
110-
'$gpr'
111-
);
112-
}
113-
return undefined;
83+
resolveTask(task: vscode.Task) {
84+
/**
85+
* Here we resolve 'gpr' tasks still defined in User workspaces. We
86+
* handle them by displaying an obsoletion warning if the task gets
87+
* executed.
88+
*/
89+
return new vscode.Task(
90+
task.definition,
91+
task.scope ?? vscode.TaskScope.Workspace,
92+
task.name,
93+
GprTaskProvider.gprTaskType,
94+
this.obsoleteWarningExecution,
95+
DEFAULT_PROBLEM_MATCHER
96+
);
11497
}
11598
}
11699

integration/vscode/ada/src/taskProviders.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import * as vscode from 'vscode';
2222
import { SymbolKind } from 'vscode';
2323
import { adaExtState } from './extension';
2424
import { getAdaMains, getProjectFile } from './helpers';
25+
import { task } from 'fp-ts';
2526

2627
/**
2728
* Callback to provide an extra argument for a tool
@@ -641,7 +642,7 @@ async function buildFullCommandLine(
641642
// field must be specified.
642643
const msg =
643644
`Task '${name}': ` +
644-
`The project file specied in this task is different than the workspace ` +
645+
`The project file specified in this task is different than the workspace ` +
645646
`project. It is not possible to automatically compute the path to the ` +
646647
`executable to run. Please specify the 'executable' attribute in the ` +
647648
`task definition.`;
@@ -661,3 +662,57 @@ async function buildFullCommandLine(
661662
return alr.concat(cmd);
662663
});
663664
}
665+
666+
/**
667+
* This class is a {@link vscode.CustomExecution} that displays a warning
668+
* message as a popup message and in the terminal associated with the task
669+
* execution. In particular, it is useful for displaying a warning when the User
670+
* tries to execute an obsolete or malformed task.
671+
*/
672+
export class WarningMessageExecution extends vscode.CustomExecution {
673+
warningMsg: string;
674+
675+
constructor(warningMsg: string) {
676+
super(() => {
677+
return this.callback();
678+
});
679+
this.warningMsg = warningMsg;
680+
}
681+
682+
/**
683+
* This callback is called when the task is executed.
684+
*
685+
* @returns a Pseudoterminal object that controls a Terminal in the VS Code UI.
686+
*/
687+
callback(): Thenable<vscode.Pseudoterminal> {
688+
return new Promise((resolve) => {
689+
const writeEmitter = new vscode.EventEmitter<string>();
690+
const closeEmitter = new vscode.EventEmitter<number>();
691+
const msg = this.warningMsg;
692+
const pseudoTerminal: vscode.Pseudoterminal = {
693+
onDidWrite: writeEmitter.event,
694+
onDidClose: closeEmitter.event,
695+
open() {
696+
/**
697+
* Printing to the terminal is done by firing the onDidWrite event.
698+
*/
699+
writeEmitter.fire(msg + '\r\n\r\n');
700+
701+
/**
702+
* Display the warning in a popup without awaiting the dismissal of the popup.
703+
*/
704+
void vscode.window.showWarningMessage(msg);
705+
706+
/**
707+
* Firing the onDidClose event causes the Terminal to end.
708+
*/
709+
closeEmitter.fire(0);
710+
},
711+
close() {
712+
//
713+
},
714+
};
715+
resolve(pseudoTerminal);
716+
});
717+
}
718+
}

0 commit comments

Comments
 (0)