Skip to content

Commit af0f728

Browse files
Merge pull request #795 from DustinCampbell/improve-asset-prompt
Rework asset generation prompt to all user to specify 'Don't Ask Again'
2 parents d63892f + 8a0347e commit af0f728

File tree

4 files changed

+173
-41
lines changed

4 files changed

+173
-41
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@
139139
"title": "Select Project",
140140
"category": "OmniSharp"
141141
},
142+
{
143+
"command": "dotnet.generateAssets",
144+
"title": "Generate Assets for Build and Debug",
145+
"category": ".NET"
146+
},
142147
{
143148
"command": "dotnet.restore",
144149
"title": "Restore Packages",

src/assets.ts

Lines changed: 151 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,15 @@ interface Operations {
7676

7777
function hasOperations(operations: Operations) {
7878
return operations.addLaunchJson ||
79-
operations.updateTasksJson ||
80-
operations.addLaunchJson;
79+
operations.updateTasksJson ||
80+
operations.addLaunchJson;
8181
}
8282

8383
function getOperations() {
8484
const paths = getPaths();
8585

8686
return getBuildOperations(paths.tasksJsonPath).then(operations =>
87-
getLaunchOperations(paths.launchJsonPath, operations));
87+
getLaunchOperations(paths.launchJsonPath, operations));
8888
}
8989

9090
function getBuildOperations(tasksJsonPath: string) {
@@ -120,15 +120,27 @@ function getLaunchOperations(launchJsonPath: string, operations: Operations) {
120120
});
121121
}
122122

123+
enum PromptResult {
124+
Yes,
125+
No,
126+
Disable
127+
}
128+
129+
interface PromptItem extends vscode.MessageItem {
130+
result: PromptResult
131+
}
132+
123133
function promptToAddAssets() {
124-
return new Promise<boolean>((resolve, reject) => {
125-
const item = { title: 'Yes' }
134+
return new Promise<PromptResult>((resolve, reject) => {
135+
const yesItem: PromptItem = { title: 'Yes', result: PromptResult.Yes };
136+
const noItem: PromptItem = { title: 'Not Now', result: PromptResult.No, isCloseAffordance: true }
137+
const disableItem: PromptItem = { title: "Don't Ask Again", result: PromptResult.Disable };
126138

127-
vscode.window.showInformationMessage('Required assets to build and debug are missing from your project. Add them?', item).then(selection => {
128-
return selection
129-
? resolve(true)
130-
: resolve(false);
131-
});
139+
const projectName = path.basename(vscode.workspace.rootPath);
140+
141+
vscode.window.showWarningMessage(
142+
`Required assets to build and debug are missing from '${projectName}'. Add them?`, disableItem, noItem, yesItem)
143+
.then(selection => resolve(selection.result));
132144
});
133145
}
134146

@@ -208,8 +220,8 @@ function createAttachConfiguration(): AttachConfiguration {
208220
}
209221

210222
function createLaunchJson(projectData: TargetProjectData, isWebProject: boolean): any {
211-
let version = '0.2.0';
212-
if (!isWebProject) {
223+
let version = '0.2.0';
224+
if (!isWebProject) {
213225
return {
214226
version: version,
215227
configurations: [
@@ -249,7 +261,7 @@ function createTasksConfiguration(projectData: TargetProjectData): tasks.TaskCon
249261
command: 'dotnet',
250262
isShellCommand: true,
251263
args: [],
252-
tasks: [ createBuildTaskDescription(projectData) ]
264+
tasks: [createBuildTaskDescription(projectData)]
253265
};
254266
}
255267

@@ -365,35 +377,140 @@ function addLaunchJsonIfNecessary(projectData: TargetProjectData, paths: Paths,
365377
});
366378
}
367379

368-
export function addAssetsIfNecessary(server: OmnisharpServer) {
369-
if (!vscode.workspace.rootPath) {
370-
return;
371-
}
380+
function addAssets(data: TargetProjectData, paths: Paths, operations: Operations) {
381+
const promises = [
382+
addTasksJsonIfNecessary(data, paths, operations),
383+
addLaunchJsonIfNecessary(data, paths, operations)
384+
];
372385

373-
return serverUtils.requestWorkspaceInformation(server).then(info => {
374-
// If there are no .NET Core projects, we won't bother offering to add assets.
375-
if ('DotNet' in info && info.DotNet.Projects.length > 0) {
376-
return getOperations().then(operations => {
377-
if (!hasOperations(operations)) {
378-
return;
379-
}
386+
return Promise.all(promises);
387+
}
388+
389+
export enum AddAssetResult {
390+
NotApplicable,
391+
Done,
392+
Disable,
393+
Cancelled
394+
}
395+
396+
export function addAssetsIfNecessary(server: OmnisharpServer): Promise<AddAssetResult> {
397+
return new Promise<AddAssetResult>((resolve, reject) => {
398+
if (!vscode.workspace.rootPath) {
399+
return resolve(AddAssetResult.NotApplicable);
400+
}
380401

381-
promptToAddAssets().then(addAssets => {
382-
if (!addAssets) {
383-
return;
402+
serverUtils.requestWorkspaceInformation(server).then(info => {
403+
// If there are no .NET Core projects, we won't bother offering to add assets.
404+
if (info.DotNet && info.DotNet.Projects.length > 0) {
405+
return getOperations().then(operations => {
406+
if (!hasOperations(operations)) {
407+
return resolve(AddAssetResult.NotApplicable);
384408
}
385409

386-
const data = findTargetProjectData(info.DotNet.Projects);
387-
const paths = getPaths();
410+
promptToAddAssets().then(result => {
411+
if (result === PromptResult.Disable) {
412+
return resolve(AddAssetResult.Disable);
413+
}
414+
415+
if (result !== PromptResult.Yes) {
416+
return resolve(AddAssetResult.Cancelled);
417+
}
388418

389-
return fs.ensureDirAsync(paths.vscodeFolder).then(() => {
390-
return Promise.all([
391-
addTasksJsonIfNecessary(data, paths, operations),
392-
addLaunchJsonIfNecessary(data, paths, operations)
393-
]);
419+
const data = findTargetProjectData(info.DotNet.Projects);
420+
const paths = getPaths();
421+
422+
return fs.ensureDirAsync(paths.vscodeFolder).then(() =>
423+
addAssets(data, paths, operations).then(() =>
424+
resolve(AddAssetResult.Done)));
394425
});
395426
});
427+
}
428+
}).catch(err =>
429+
reject(err));
430+
});
431+
}
432+
433+
function doesAnyAssetExist(paths: Paths) {
434+
return new Promise<boolean>((resolve, reject) => {
435+
fs.existsAsync(paths.launchJsonPath).then(res => {
436+
if (res) {
437+
resolve(true);
438+
}
439+
else {
440+
fs.existsAsync(paths.tasksJsonPath).then(res => {
441+
resolve(res);
442+
});
443+
}
444+
});
445+
});
446+
}
447+
448+
function deleteAsset(path: string) {
449+
return new Promise<void>((resolve, reject) => {
450+
fs.existsAsync(path).then(res => {
451+
if (res) {
452+
// TODO: Should we check after unlinking to see if the file still exists?
453+
fs.unlinkAsync(path).then(() => {
454+
resolve();
455+
});
456+
}
457+
});
458+
});
459+
}
460+
461+
function deleteAssets(paths: Paths) {
462+
return Promise.all([
463+
deleteAsset(paths.launchJsonPath),
464+
deleteAsset(paths.tasksJsonPath)
465+
]);
466+
}
467+
468+
function shouldGenerateAssets(paths: Paths) {
469+
return new Promise<boolean>((resolve, reject) => {
470+
doesAnyAssetExist(paths).then(res => {
471+
if (res) {
472+
const yesItem = { title: 'Yes' };
473+
const cancelItem = { title: 'Cancel', isCloseAffordance: true };
474+
475+
vscode.window.showWarningMessage('Replace existing build and debug assets?', cancelItem, yesItem)
476+
.then(selection => {
477+
if (selection === yesItem) {
478+
deleteAssets(paths).then(_ => resolve(true));
479+
}
480+
else {
481+
// The user clicked cancel
482+
resolve(false);
483+
}
484+
});
485+
}
486+
else {
487+
// The assets don't exist, so we're good to go.
488+
resolve(true);
489+
}
490+
});
491+
492+
});
493+
}
494+
495+
export function generateAssets(server: OmnisharpServer) {
496+
serverUtils.requestWorkspaceInformation(server).then(info => {
497+
if (info.DotNet && info.DotNet.Projects.length > 0) {
498+
getOperations().then(operations => {
499+
if (hasOperations(operations)) {
500+
const paths = getPaths();
501+
shouldGenerateAssets(paths).then(res => {
502+
if (res) {
503+
fs.ensureDirAsync(paths.vscodeFolder).then(() => {
504+
const data = findTargetProjectData(info.DotNet.Projects);
505+
addAssets(data, paths, operations);
506+
});
507+
}
508+
});
509+
}
396510
});
397511
}
512+
else {
513+
vscode.window.showErrorMessage("Could not locate .NET Core project. Assets were not generated.");
514+
}
398515
});
399516
}

src/features/commands.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import * as path from 'path';
1414
import * as protocol from '../omnisharp/protocol';
1515
import * as vscode from 'vscode';
1616
import * as dotnetTest from './dotnetTest'
17-
import {DotNetAttachItemsProviderFactory, AttachPicker} from './processPicker'
17+
import {DotNetAttachItemsProviderFactory, AttachPicker} from './processPicker';
18+
import {generateAssets} from '../assets';
1819

1920
let channel = vscode.window.createOutputChannel('.NET');
2021

@@ -37,7 +38,10 @@ export default function registerCommands(server: OmnisharpServer, extensionPath:
3738
let attacher = new AttachPicker(attachItemsProvider);
3839
let d8 = vscode.commands.registerCommand('csharp.listProcess', () => attacher.ShowAttachEntries());
3940

40-
return vscode.Disposable.from(d1, d2, d3, d4, d5, d6, d7, d8);
41+
// Register command for generating tasks.json and launch.json assets.
42+
let d9 = vscode.commands.registerCommand('dotnet.generateAssets', () => generateAssets(server));
43+
44+
return vscode.Disposable.from(d1, d2, d3, d4, d5, d6, d7, d8, d9);
4145
}
4246

4347
function restartOmniSharp(server: OmnisharpServer) {

src/main.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {readOptions} from './omnisharp/options';
2424
import forwardChanges from './features/changeForwarding';
2525
import reportStatus from './features/status';
2626
import * as coreclrdebug from './coreclr-debug/activate';
27-
import {addAssetsIfNecessary} from './assets';
27+
import {addAssetsIfNecessary, AddAssetResult} from './assets';
2828
import * as vscode from 'vscode';
2929
import TelemetryReporter from 'vscode-extension-telemetry';
3030
import {DefinitionMetadataDocumentProvider} from './features/definitionMetadataDocumentProvider';
@@ -81,10 +81,16 @@ export function activate(context: vscode.ExtensionContext): any {
8181
disposables.push(registerCommands(server, context.extensionPath));
8282
disposables.push(reportStatus(server));
8383

84-
disposables.push(server.onServerStart(() => {
85-
// Update or add tasks.json and launch.json
86-
addAssetsIfNecessary(server);
87-
}));
84+
if (!context.workspaceState.get<boolean>('assetPromptDisabled')) {
85+
disposables.push(server.onServerStart(() => {
86+
// Update or add tasks.json and launch.json
87+
addAssetsIfNecessary(server).then(result => {
88+
if (result === AddAssetResult.Disable) {
89+
context.workspaceState.update('assetPromptDisabled', true);
90+
}
91+
});
92+
}));
93+
}
8894

8995
// read and store last solution or folder path
9096
disposables.push(server.onBeforeServerStart(path => context.workspaceState.update('lastSolutionPathOrFolder', path)));

0 commit comments

Comments
 (0)