Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
282 changes: 282 additions & 0 deletions src/assets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

'use strict';

import * as fs from 'fs-extra-promise';
import * as path from 'path';
import * as vscode from 'vscode';
import * as tasks from 'vscode-tasks';
import {OmnisharpServer} from './omnisharpServer';
import * as serverUtils from './omnisharpUtils';
import * as protocol from './protocol.ts'

interface DebugConfiguration {
name: string,
type: string,
request: string,
}

interface ConsoleLaunchConfiguration extends DebugConfiguration {
preLaunchTask: string,
program: string,
args: string[],
cwd: string,
stopAtEntry: boolean
}

interface CommandLine {
command: string,
args?: string
}

interface LaunchBrowserConfiguration {
enabled: boolean,
args: string,
windows?: CommandLine,
osx: CommandLine,
linux: CommandLine
}

interface WebLaunchConfiguration extends ConsoleLaunchConfiguration {
launchBrowser: LaunchBrowserConfiguration
}

interface AttachConfiguration extends DebugConfiguration {
processName: string
}

interface Paths {
vscodeFolder: string;
tasksJsonPath: string;
launchJsonPath: string;
}

function getPaths(): Paths {
const vscodeFolder = path.join(vscode.workspace.rootPath, '.vscode');

return {
vscodeFolder: vscodeFolder,
tasksJsonPath: path.join(vscodeFolder, 'tasks.json'),
launchJsonPath: path.join(vscodeFolder, 'launch.json')
}
}

interface Operations {
addTasksJson?: boolean,
updateTasksJson?: boolean,
addLaunchJson?: boolean
}

function hasOperations(operations: Operations) {
return operations.addLaunchJson ||
operations.updateTasksJson ||
operations.addLaunchJson;
}

function getOperations() {
const paths = getPaths();

return getBuildOperations(paths.tasksJsonPath).then(operations =>
getLaunchOperations(paths.launchJsonPath, operations));
}

function getBuildOperations(tasksJsonPath: string) {
return new Promise<Operations>((resolve, reject) => {
return fs.existsAsync(tasksJsonPath).then(exists => {
if (exists) {
fs.readFileAsync(tasksJsonPath).then(buffer => {
const text = buffer.toString();
const tasksJson: tasks.TaskConfiguration = JSON.parse(text);
const buildTask = tasksJson.tasks.find(td => td.taskName === 'build');

resolve({ updateTasksJson: (buildTask === undefined) });
});
}
else {
resolve({ addTasksJson: true });
}
});
});
}

function getLaunchOperations(launchJsonPath: string, operations: Operations) {
return new Promise<Operations>((resolve, reject) => {
return fs.existsAsync(launchJsonPath).then(exists => {
if (exists) {
resolve(operations);
}
else {
operations.addLaunchJson = true;
resolve(operations);
}
});
});
}

function promptToAddAssets() {
return new Promise<boolean>((resolve, reject) => {
const item = { title: 'Yes' }

vscode.window.showInformationMessage('Required assets to build and debug are missing from your project. Add them?', item).then(selection => {
return selection
? resolve(true)
: resolve(false);
});
});
}

function createLaunchConfiguration(targetFramework: string, executableName: string): ConsoleLaunchConfiguration {
return {
name: '.NET Core Launch (console)',
type: 'coreclr',
request: 'launch',
preLaunchTask: 'build',
program: '${workspaceRoot}/bin/Debug/' + targetFramework + '/'+ executableName,
args: [],
cwd: '${workspaceRoot}',
stopAtEntry: false
}
}

function createWebLaunchConfiguration(targetFramework: string, executableName: string): WebLaunchConfiguration {
return {
name: '.NET Core Launch (web)',
type: 'coreclr',
request: 'launch',
preLaunchTask: 'build',
program: '${workspaceRoot}/bin/Debug/' + targetFramework + '/'+ executableName,
args: [],
cwd: '${workspaceRoot}',
stopAtEntry: false,
launchBrowser: {
enabled: true,
args: '${auto-detect-url}',
windows: {
command: 'cmd.exe',
args: '/C start ${auto-detect-url}'
},
osx: {
command: 'open'
},
linux: {
command: 'xdg-open'
}
}
}
}

function createAttachConfiguration(): AttachConfiguration {
return {
name: '.NET Core Attach',
type: 'coreclr',
request: 'attach',
processName: '<example>'
}
}

function createLaunchJson(targetFramework: string, executableName: string): any {
return {
version: '0.2.0',
configurations: [
createLaunchConfiguration(targetFramework, executableName),
createWebLaunchConfiguration(targetFramework, executableName),
createAttachConfiguration()
]
}
}

function createBuildTaskDescription(): tasks.TaskDescription {
return {
taskName: 'build',
args: [],
isBuildCommand: true,
problemMatcher: '$msCompile'
};
}

function createTasksConfiguration(): tasks.TaskConfiguration {
return {
version: '0.1.0',
command: 'dotnet',
isShellCommand: true,
args: [],
tasks: [ createBuildTaskDescription() ]
};
}

function addTasksJsonIfNecessary(info: protocol.DotNetWorkspaceInformation, paths: Paths, operations: Operations) {
return new Promise<void>((resolve, reject) => {
if (!operations.addTasksJson) {
return resolve();
}

const tasksJson = createTasksConfiguration();
const tasksJsonText = JSON.stringify(tasksJson, null, ' ');

return fs.writeFileAsync(paths.tasksJsonPath, tasksJsonText);
});
}

function addLaunchJsonIfNecessary(info: protocol.DotNetWorkspaceInformation, paths: Paths, operations: Operations) {
return new Promise<void>((resolve, reject) => {
if (!operations.addLaunchJson) {
return resolve();
}

let targetFramework = '<target-framework>';
let executableName = '<project-name.dll>';

let projectWithEntryPoint = info.Projects.find(project => project.EmitEntryPoint === true);

if (projectWithEntryPoint) {
targetFramework = projectWithEntryPoint.TargetFramework.ShortName;
executableName = path.basename(projectWithEntryPoint.CompilationOutputAssemblyFile);
}

const launchJson = createLaunchJson(targetFramework, executableName);
const launchJsonText = JSON.stringify(launchJson, null, ' ');

return fs.writeFileAsync(paths.launchJsonPath, launchJsonText);
});
}

export function addAssetsIfNecessary(server: OmnisharpServer) {
if (!vscode.workspace.rootPath) {
return;
}

// If there is no project.json, we won't bother to prompt the user for tasks.json.
const projectJsonPath = path.join(vscode.workspace.rootPath, 'project.json');
if (!fs.existsSync(projectJsonPath)) {
return;
}

return serverUtils.requestWorkspaceInformation(server).then(info => {
// If there are no .NET Core projects, we won't bother offering to add assets.
if ('DotNet' in info && info.DotNet.Projects.length > 0) {
return getOperations().then(operations => {
if (!hasOperations(operations)) {
return;
}

promptToAddAssets().then(addAssets => {
if (!addAssets) {
return;
}

const paths = getPaths();

return fs.ensureDirAsync(paths.vscodeFolder).then(() => {
return Promise.all([
addTasksJsonIfNecessary(info.DotNet, paths, operations),
addLaunchJsonIfNecessary(info.DotNet, paths, operations)
]);
});
});
});
}
});
}
10 changes: 6 additions & 4 deletions src/omnisharpMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {StdioOmnisharpServer} from './omnisharpServer';
import forwardChanges from './features/changeForwarding';
import reportStatus from './features/omnisharpStatus';
import {installCoreClrDebug} from './coreclr-debug';
import {promptToAddBuildTaskIfNecessary} from './tasks';
import {addAssetsIfNecessary} from './assets';
import * as vscode from 'vscode';
import TelemetryReporter from 'vscode-extension-telemetry';

Expand Down Expand Up @@ -74,6 +74,11 @@ export function activate(context: vscode.ExtensionContext): any {

disposables.push(registerCommands(server, context.extensionPath));
disposables.push(reportStatus(server));

disposables.push(server.onServerStart(() => {
// Update or add tasks.json and launch.json
addAssetsIfNecessary(server);
}));

// read and store last solution or folder path
disposables.push(server.onBeforeServerStart(path => context.workspaceState.update('lastSolutionPathOrFolder', path)));
Expand All @@ -86,9 +91,6 @@ export function activate(context: vscode.ExtensionContext): any {
server.stop();
}));

// Check to see if there is a tasks.json with a "build" task and prompt the user to add it if missing.
promptToAddBuildTaskIfNecessary();

// install coreclr-debug
installCoreClrDebug(context, reporter);

Expand Down
2 changes: 1 addition & 1 deletion src/omnisharpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ export abstract class OmnisharpServer {
this._serverProcess = value.process;
this._requestDelays = {};
this._fireEvent(Events.StdOut, `[INFO] Started OmniSharp from '${value.command}' with process id ${value.process.pid}...\n`);
this._fireEvent(Events.ServerStart, solutionPath);
this._setState(ServerState.Started);
this._fireEvent(Events.ServerStart, solutionPath);
return this._doConnect();
}).then(_ => {
this._processQueue();
Expand Down
8 changes: 8 additions & 0 deletions src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,12 +268,20 @@ export interface DotNetWorkspaceInformation {
export interface DotNetProject {
Path: string;
Name: string;
TargetFramework: DotNetFramework;
CompilationOutputPath: string;
CompilationOutputAssemblyFile: string;
CompilationOutputPdbFile: string;
EmitEntryPoint?: boolean;
SourceFiles: string[];
}

export interface DotNetFramework {
Name: string;
FriendlyName: string;
ShortName: string;
}

export interface RenameRequest extends Request {
RenameTo: string;
WantsTextChanges?: boolean;
Expand Down
Loading