Skip to content

Commit 88f7d0f

Browse files
authored
UX: Change export task type to java (buildArtifact) (#667)
1 parent 336ebb4 commit 88f7d0f

File tree

10 files changed

+277
-17
lines changed

10 files changed

+277
-17
lines changed

package.json

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@
508508
{
509509
"command": "java.project.build.workspace",
510510
"when": "view == javaProjectExplorer && viewItem =~ /java:project(?=.*?\\b\\+java\\b)(?=.*?\\b\\+uri\\b)/",
511-
"group":"8_execution@5"
511+
"group": "8_execution@5"
512512
},
513513
{
514514
"command": "java.project.rebuild",
@@ -635,7 +635,7 @@
635635
],
636636
"taskDefinitions": [
637637
{
638-
"type": "java",
638+
"type": "java (buildArtifact)",
639639
"properties": {
640640
"label": {
641641
"type": "string",
@@ -724,6 +724,64 @@
724724
"description": "%taskDefinitions.java.project.build.isFullBuild%"
725725
}
726726
}
727+
},
728+
{
729+
"type": "java",
730+
"when": "java:showDeprecatedTasks",
731+
"properties": {
732+
"label": {
733+
"type": "string",
734+
"description": "%taskDefinitions.java.project.exportJar.label%"
735+
},
736+
"mainClass": {
737+
"type": "string",
738+
"description": "%taskDefinitions.java.project.exportJar.mainClass%"
739+
},
740+
"targetPath": {
741+
"type": "string",
742+
"anyOf": [
743+
{
744+
"type": "string"
745+
},
746+
{
747+
"enum": [
748+
"${workspaceFolder}/${workspaceFolderBasename}.jar",
749+
""
750+
],
751+
"enumDescriptions": [
752+
"%configuration.java.project.exportJar.targetPath.workspaceFolder%",
753+
"%configuration.java.project.exportJar.targetPath.select%"
754+
]
755+
}
756+
],
757+
"description": "%configuration.java.project.exportJar.targetPath.customization%"
758+
},
759+
"elements": {
760+
"type": "array",
761+
"items": {
762+
"anyOf": [
763+
{
764+
"type": "string"
765+
},
766+
{
767+
"enum": [
768+
"${compileOutput}",
769+
"${testCompileOutput}",
770+
"${dependencies}",
771+
"${testDependencies}"
772+
],
773+
"enumDescriptions": [
774+
"%taskDefinitions.java.project.exportJar.compileOutput%",
775+
"%taskDefinitions.java.project.exportJar.testCompileOutput%",
776+
"%taskDefinitions.java.project.exportJar.dependencies%",
777+
"%taskDefinitions.java.project.exportJar.testDependencies%"
778+
]
779+
}
780+
]
781+
},
782+
"description": "%taskDefinitions.java.project.exportJar.elements%"
783+
}
784+
}
727785
}
728786
]
729787
},

src/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ export namespace Commands {
9494

9595
export const INSTALL_EXTENSION = "java.project.installExtension";
9696

97+
export const JAVA_UPDATE_DEPRECATED_TASK = "java.updateDeprecatedTask";
98+
9799
/**
98100
* Commands from Visual Studio Code
99101
*/

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export namespace Context {
77
export const NO_JAVA_PROJECT: string = "java:noJavaProjects";
88
export const WORKSPACE_CONTAINS_BUILD_FILES: string = "java:workspaceContainsBuildFiles";
99
export const RELOAD_PROJECT_ACTIVE: string = "java:reloadProjectActive";
10+
export const SHOW_DEPRECATED_TASKS: string = "java:showDeprecatedTasks";
1011
}
1112

1213
export namespace Explorer {

src/exportJarSteps/ExportJarTaskProvider.ts

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import * as _ from "lodash";
77
import { platform } from "os";
88
import { dirname, extname, isAbsolute, join, relative } from "path";
99
import {
10-
CustomExecution, Event, EventEmitter, Pseudoterminal, Task, TaskDefinition,
10+
CancellationToken,
11+
CustomExecution, Event, EventEmitter, ProviderResult, Pseudoterminal, Task, TaskDefinition,
1112
TaskProvider, TaskRevealKind, tasks, TerminalDimensions, Uri, workspace, WorkspaceFolder,
1213
} from "vscode";
14+
import { sendInfo } from "vscode-extension-telemetry-wrapper";
1315
import { buildWorkspace } from "../build";
1416
import { Jdtls } from "../java/jdtls";
1517
import { INodeData } from "../java/nodeData";
@@ -46,7 +48,7 @@ export async function executeExportJarTask(node?: INodeData): Promise<void> {
4648
isExportingJar = true;
4749
const stepMetadata: IStepMetadata = {
4850
entry: node,
49-
taskLabel: "exportjar:default",
51+
taskLabel: "default",
5052
steps: [],
5153
projectList: [],
5254
elements: [],
@@ -69,20 +71,20 @@ export async function executeExportJarTask(node?: INodeData): Promise<void> {
6971
}
7072
export class ExportJarTaskProvider implements TaskProvider {
7173

72-
public static exportJarType: string = "java";
74+
public static exportJarType: string = "java (buildArtifact)";
7375

7476
public static getDefaultTask(stepMetadata: IStepMetadata): Task {
7577
if (!stepMetadata.workspaceFolder) {
7678
throw new Error(ExportJarMessages.fieldUndefinedMessage(ExportJarMessages.Field.WORKSPACEFOLDER, ExportJarStep.ResolveTask));
7779
}
7880
const defaultDefinition: IExportJarTaskDefinition = {
7981
type: ExportJarTaskProvider.exportJarType,
80-
label: "exportjar:default",
82+
label: "default",
8183
targetPath: Settings.getExportJarTargetPath(),
8284
elements: [],
8385
mainClass: undefined,
8486
};
85-
const task: Task = new Task(defaultDefinition, stepMetadata.workspaceFolder, "exportjar:default", ExportJarTaskProvider.exportJarType,
87+
const task: Task = new Task(defaultDefinition, stepMetadata.workspaceFolder, "default", ExportJarTaskProvider.exportJarType,
8688
new CustomExecution(async (resolvedDefinition: TaskDefinition): Promise<Pseudoterminal> => {
8789
return new ExportJarTaskTerminal(resolvedDefinition, stepMetadata);
8890
}));
@@ -93,13 +95,17 @@ export class ExportJarTaskProvider implements TaskProvider {
9395
private tasks: Task[] | undefined;
9496

9597
public async resolveTask(task: Task): Promise<Task> {
98+
return ExportJarTaskProvider.resolveExportTask(task, ExportJarTaskProvider.exportJarType);
99+
}
100+
101+
public static async resolveExportTask(task: Task, type: string): Promise<Task> {
96102
const definition: IExportJarTaskDefinition = <IExportJarTaskDefinition>task.definition;
97103
const folder: WorkspaceFolder = <WorkspaceFolder>task.scope;
98-
const resolvedTask: Task = new Task(definition, folder, task.name, ExportJarTaskProvider.exportJarType,
104+
const resolvedTask: Task = new Task(definition, folder, task.name, type,
99105
new CustomExecution(async (resolvedDefinition: IExportJarTaskDefinition): Promise<Pseudoterminal> => {
100106
const stepMetadata: IStepMetadata = {
101107
entry: undefined,
102-
taskLabel: resolvedDefinition.label || `exportjar:${folder.name}`,
108+
taskLabel: resolvedDefinition.label || folder.name,
103109
workspaceFolder: folder,
104110
projectList: await Jdtls.getProjects(folder.uri.toString()),
105111
steps: [],
@@ -142,11 +148,11 @@ export class ExportJarTaskProvider implements TaskProvider {
142148
targetPath: Settings.getExportJarTargetPath(),
143149
elements: elementList,
144150
};
145-
const defaultTask: Task = new Task(defaultDefinition, folder, `exportjar:${folder.name}`, ExportJarTaskProvider.exportJarType,
151+
const defaultTask: Task = new Task(defaultDefinition, folder, folder.name, ExportJarTaskProvider.exportJarType,
146152
new CustomExecution(async (resolvedDefinition: IExportJarTaskDefinition): Promise<Pseudoterminal> => {
147153
const stepMetadata: IStepMetadata = {
148154
entry: undefined,
149-
taskLabel: resolvedDefinition.label || `exportjar:${folder.name}`,
155+
taskLabel: resolvedDefinition.label || folder.name,
150156
workspaceFolder: folder,
151157
projectList: await Jdtls.getProjects(folder.uri.toString()),
152158
steps: [],
@@ -162,6 +168,21 @@ export class ExportJarTaskProvider implements TaskProvider {
162168
}
163169
}
164170

171+
export class DeprecatedExportJarTaskProvider implements TaskProvider {
172+
173+
public static type: string = "java";
174+
175+
provideTasks(_token: CancellationToken): ProviderResult<Task[]> {
176+
return [];
177+
}
178+
179+
resolveTask(task: Task, _token: CancellationToken): ProviderResult<Task> {
180+
sendInfo("", { name: "resolve-deprecated-export-task" });
181+
return ExportJarTaskProvider.resolveExportTask(task, DeprecatedExportJarTaskProvider.type);
182+
}
183+
184+
}
185+
165186
class ExportJarTaskTerminal implements Pseudoterminal {
166187

167188
public writeEmitter = new EventEmitter<string>();

src/extension.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
54
import * as path from "path";
6-
import { commands, Diagnostic, Extension, ExtensionContext, extensions, languages, tasks, TextDocument, TextEditor, Uri, window, workspace } from "vscode";
5+
import { commands, Diagnostic, Extension, ExtensionContext, extensions, languages,
6+
Range, tasks, TextDocument, TextEditor, Uri, window, workspace } from "vscode";
77
import { dispose as disposeTelemetryWrapper, initializeFromJsonFile, instrumentOperation, instrumentOperationAsVsCodeCommand, sendInfo } from "vscode-extension-telemetry-wrapper";
88
import { Commands, contextManager } from "../extension.bundle";
99
import { BuildTaskProvider } from "./tasks/build/buildTaskProvider";
1010
import { buildFiles, Context, ExtensionName } from "./constants";
1111
import { LibraryController } from "./controllers/libraryController";
1212
import { ProjectController } from "./controllers/projectController";
1313
import { init as initExpService } from "./ExperimentationService";
14-
import { ExportJarTaskProvider } from "./exportJarSteps/ExportJarTaskProvider";
14+
import { DeprecatedExportJarTaskProvider, ExportJarTaskProvider } from "./exportJarSteps/ExportJarTaskProvider";
1515
import { Settings } from "./settings";
1616
import { syncHandler } from "./syncHandler";
1717
import { EventCounter } from "./utility";
1818
import { DependencyExplorer } from "./views/dependencyExplorer";
19+
import { DiagnosticProvider } from "./tasks/migration/DiagnosticProvider";
20+
import { setContextForDeprecatedTasks, updateExportTaskType } from "./tasks/migration/utils";
21+
import { CodeActionProvider } from "./tasks/migration/CodeActionProvider";
1922

2023
export async function activate(context: ExtensionContext): Promise<void> {
2124
contextManager.initialize(context);
@@ -40,6 +43,7 @@ async function activateExtension(_operationId: string, context: ExtensionContext
4043
context.subscriptions.push(DependencyExplorer.getInstance(context));
4144
context.subscriptions.push(contextManager);
4245
context.subscriptions.push(syncHandler);
46+
context.subscriptions.push(tasks.registerTaskProvider(DeprecatedExportJarTaskProvider.type, new DeprecatedExportJarTaskProvider()));
4347
context.subscriptions.push(tasks.registerTaskProvider(ExportJarTaskProvider.exportJarType, new ExportJarTaskProvider()));
4448
context.subscriptions.push(tasks.registerTaskProvider(BuildTaskProvider.type, new BuildTaskProvider()));
4549

@@ -64,6 +68,18 @@ async function activateExtension(_operationId: string, context: ExtensionContext
6468

6569
commands.executeCommand(Commands.JAVA_PROJECT_CONFIGURATION_UPDATE, uri);
6670
});
71+
// handle deprecated tasks
72+
context.subscriptions.push(new DiagnosticProvider());
73+
context.subscriptions.push(languages.registerCodeActionsProvider([{
74+
scheme: "file",
75+
pattern: "**/.vscode/tasks.json"
76+
}], new CodeActionProvider()));
77+
context.subscriptions.push(instrumentOperationAsVsCodeCommand(
78+
Commands.JAVA_UPDATE_DEPRECATED_TASK, async (document: TextDocument, range: Range) => {
79+
await updateExportTaskType(document, range);
80+
}
81+
));
82+
setContextForDeprecatedTasks();
6783
}
6884

6985
// this method is called when your extension is deactivated
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import * as vscode from "vscode";
5+
import { Commands } from "../../commands";
6+
import { ExportJarTaskProvider } from "../../exportJarSteps/ExportJarTaskProvider";
7+
import { DiagnosticProvider } from "./DiagnosticProvider";
8+
9+
export class CodeActionProvider implements vscode.CodeActionProvider {
10+
11+
public static JAVA_UPDATE_DEPRECATED_TASK_TITLE = "Update deprecated task";
12+
public static JAVA_BUILD_ARTIFACT_TYPE = `"type": "${ExportJarTaskProvider.exportJarType}"`;
13+
14+
public provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection,
15+
_context: vscode.CodeActionContext, _token: vscode.CancellationToken): vscode.CodeAction[] | undefined {
16+
const diagnostics = vscode.languages.getDiagnostics(document.uri);
17+
if (diagnostics?.length) {
18+
for (const diagnostic of diagnostics) {
19+
if (diagnostic.source !== DiagnosticProvider.DIAGNOSTIC_SOURCE) {
20+
continue;
21+
}
22+
if (diagnostic.range.contains(range)) {
23+
const updateTaskCommand: vscode.Command = {
24+
command: Commands.JAVA_UPDATE_DEPRECATED_TASK,
25+
title: CodeActionProvider.JAVA_UPDATE_DEPRECATED_TASK_TITLE,
26+
arguments: [
27+
document,
28+
diagnostic.range
29+
]
30+
};
31+
return [{
32+
title: CodeActionProvider.JAVA_UPDATE_DEPRECATED_TASK_TITLE,
33+
kind: vscode.CodeActionKind.QuickFix,
34+
command: updateTaskCommand
35+
}];
36+
}
37+
}
38+
}
39+
return [];
40+
}
41+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import * as _ from "lodash";
5+
import * as fs from "fs";
6+
import * as path from "path";
7+
import * as readline from "readline";
8+
import * as vscode from "vscode";
9+
import { getTasksJsonPaths } from "./utils";
10+
import { DeprecatedExportJarTaskProvider, ExportJarTaskProvider } from "../../exportJarSteps/ExportJarTaskProvider";
11+
12+
export class DiagnosticProvider implements vscode.Disposable {
13+
public static DIAGNOSTIC_SOURCE = "Project Manager for Java";
14+
public static DEPRECATED_TASK_TYPE_DECLARATION = `\"type\": \"${DeprecatedExportJarTaskProvider.type}\"`;
15+
public static DEPRECATED_TASK_TYPE_MESSAGE = `Tasks with type \"${DeprecatedExportJarTaskProvider.type}\" are deprecated and will not be supported in the future, please use \"${ExportJarTaskProvider.exportJarType}\" instead.`;
16+
private diagnosticCollection: vscode.DiagnosticCollection;
17+
private disposables: vscode.Disposable[] = [];
18+
private refreshDiagnosticsTrigger: any;
19+
20+
constructor() {
21+
this.refreshDiagnosticsTrigger = _.debounce(this.refreshDiagnostics, 500 /** ms */);
22+
this.diagnosticCollection = vscode.languages.createDiagnosticCollection("migrateExportTask");
23+
this.disposables.push(this.diagnosticCollection);
24+
this.disposables.push(vscode.workspace.onDidChangeTextDocument(async (e) => {
25+
if (path.basename(e.document.fileName) === "tasks.json") {
26+
this.refreshDiagnosticsTrigger(e.document.uri);
27+
}
28+
}));
29+
this.initializeDiagnostics();
30+
}
31+
32+
public dispose() {
33+
for (const d of this.disposables) {
34+
d.dispose();
35+
}
36+
}
37+
38+
private async initializeDiagnostics(): Promise<void> {
39+
const tasksJsonPaths = await getTasksJsonPaths();
40+
for (const tasksJsonPath of tasksJsonPaths) {
41+
const diagnostics: vscode.Diagnostic[] = await DiagnosticProvider.getDiagnosticsFromTasksJsonPath(tasksJsonPath);
42+
this.diagnosticCollection.set(vscode.Uri.file(tasksJsonPath), diagnostics);
43+
}
44+
}
45+
46+
private async refreshDiagnostics(uri: vscode.Uri): Promise<void> {
47+
const diagnostics: vscode.Diagnostic[] = await DiagnosticProvider.getDiagnosticsFromTasksJsonPath(uri.fsPath);
48+
this.diagnosticCollection.set(uri, diagnostics);
49+
}
50+
51+
private static async getDiagnosticsFromTasksJsonPath(tasksJsonPath: string): Promise<vscode.Diagnostic[]> {
52+
const diagnostics: vscode.Diagnostic[] = [];
53+
const fileStream = fs.createReadStream(tasksJsonPath);
54+
let lineNumber = 0;
55+
const rl = readline.createInterface({
56+
input: fileStream,
57+
crlfDelay: Infinity
58+
});
59+
for await (const line of rl) {
60+
const regExp: RegExp = /\"type\":\s*\"java\"/g;
61+
const result: RegExpMatchArray | null = line.match(regExp);
62+
if (result?.length === 1) {
63+
const matchString = result[0];
64+
const columnNumber = line.indexOf(matchString);
65+
if (columnNumber > -1) {
66+
const diagnostic = new vscode.Diagnostic(
67+
new vscode.Range(
68+
new vscode.Position(lineNumber, columnNumber),
69+
new vscode.Position(lineNumber, columnNumber + matchString.length)
70+
),
71+
DiagnosticProvider.DEPRECATED_TASK_TYPE_MESSAGE,
72+
vscode.DiagnosticSeverity.Warning
73+
);
74+
diagnostic.source = DiagnosticProvider.DIAGNOSTIC_SOURCE;
75+
diagnostics.push(diagnostic);
76+
}
77+
}
78+
lineNumber++;
79+
}
80+
return diagnostics;
81+
}
82+
}

0 commit comments

Comments
 (0)