Skip to content

Add TemplateService for Plaster integration #320

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 15, 2016
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
using Microsoft.PowerShell.EditorServices.Templates;

namespace Microsoft.PowerShell.EditorServices.Protocol.LanguageServer
{
public class NewProjectFromTemplateRequest
{
public static readonly
RequestType<NewProjectFromTemplateRequest, NewProjectFromTemplateResponse> Type =
RequestType<NewProjectFromTemplateRequest, NewProjectFromTemplateResponse>.Create("powerShell/newProjectFromTemplate");

public string DestinationPath { get; set; }

public string TemplatePath { get; set; }
}

public class NewProjectFromTemplateResponse
{
public bool CreationSuccessful { get; set; }
}

public class GetProjectTemplatesRequest
{
public static readonly
RequestType<GetProjectTemplatesRequest, GetProjectTemplatesResponse> Type =
RequestType<GetProjectTemplatesRequest, GetProjectTemplatesResponse>.Create("powerShell/getProjectTemplates");

public bool IncludeInstalledModules { get; set; }
}

public class GetProjectTemplatesResponse
{
public bool NeedsModuleInstall { get; set; }

public TemplateDetails[] Templates { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,22 @@ public static readonly
RequestType<ShowChoicePromptRequest, ShowChoicePromptResponse> Type =
RequestType<ShowChoicePromptRequest, ShowChoicePromptResponse>.Create("powerShell/showChoicePrompt");

public bool IsMultiChoice { get; set; }

public string Caption { get; set; }

public string Message { get; set; }

public ChoiceDetails[] Choices { get; set; }

public int DefaultChoice { get; set; }
public int[] DefaultChoices { get; set; }
}

public class ShowChoicePromptResponse
{
public bool PromptCancelled { get; set; }

public string ChosenItem { get; set; }
public string ResponseText { get; set; }
}

public class ShowInputPromptRequest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
<Compile Include="LanguageServer\InstallModuleRequest.cs" />
<Compile Include="LanguageServer\PowerShellVersionRequest.cs" />
<Compile Include="LanguageServer\SetPSSARulesRequest.cs" />
<Compile Include="LanguageServer\ProjectTemplate.cs" />
<Compile Include="MessageProtocol\Channel\NamedPipeClientChannel.cs" />
<Compile Include="MessageProtocol\Channel\NamedPipeServerChannel.cs" />
<Compile Include="MessageProtocol\Channel\TcpSocketClientChannel.cs" />
Expand Down
53 changes: 53 additions & 0 deletions src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
using Microsoft.PowerShell.EditorServices.Session;
using Microsoft.PowerShell.EditorServices.Templates;
using Microsoft.PowerShell.EditorServices.Utility;
using Newtonsoft.Json.Linq;
using System;
Expand Down Expand Up @@ -107,6 +108,9 @@ protected override void Initialize()

this.SetRequestHandler(PowerShellVersionRequest.Type, this.HandlePowerShellVersionRequest);

this.SetRequestHandler(NewProjectFromTemplateRequest.Type, this.HandleNewProjectFromTemplateRequest);
this.SetRequestHandler(GetProjectTemplatesRequest.Type, this.HandleGetProjectTemplatesRequest);

this.SetRequestHandler(DebugAdapterMessages.EvaluateRequest.Type, this.HandleEvaluateRequest);

this.SetRequestHandler(GetPSSARulesRequest.Type, this.HandleGetPSSARulesRequest);
Expand Down Expand Up @@ -280,6 +284,55 @@ private Task HandleInvokeExtensionCommandRequest(
return Task.FromResult(true);
}

private Task HandleNewProjectFromTemplateRequest(
NewProjectFromTemplateRequest newProjectArgs,
RequestContext<NewProjectFromTemplateResponse> requestContext)
{
// Don't await the Task here so that we don't block the session
this.editorSession.TemplateService
.CreateFromTemplate(newProjectArgs.TemplatePath, newProjectArgs.DestinationPath)
.ContinueWith(
async task =>
{
await requestContext.SendResult(
new NewProjectFromTemplateResponse
{
CreationSuccessful = task.Result
});
});

return Task.FromResult(true);
}

private async Task HandleGetProjectTemplatesRequest(
GetProjectTemplatesRequest requestArgs,
RequestContext<GetProjectTemplatesResponse> requestContext)
{
bool plasterInstalled = await this.editorSession.TemplateService.ImportPlasterIfInstalled();

if (plasterInstalled)
{
var availableTemplates =
await this.editorSession.TemplateService.GetAvailableTemplates(
requestArgs.IncludeInstalledModules);

await requestContext.SendResult(
new GetProjectTemplatesResponse
{
Templates = availableTemplates
});
}
else
{
await requestContext.SendResult(
new GetProjectTemplatesResponse
{
NeedsModuleInstall = true,
Templates = new TemplateDetails[0]
});
}
}

private async Task HandleExpandAliasRequest(
string content,
RequestContext<string> requestContext)
Expand Down
10 changes: 7 additions & 3 deletions src/PowerShellEditorServices.Protocol/Server/PromptHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,30 +40,34 @@ public InputPromptHandler GetInputPromptHandler()
}
}

internal class ProtocolChoicePromptHandler : ChoicePromptHandler
internal class ProtocolChoicePromptHandler : ConsoleChoicePromptHandler
{
private IMessageSender messageSender;
private ConsoleService consoleService;

public ProtocolChoicePromptHandler(
IMessageSender messageSender,
ConsoleService consoleService)
: base(consoleService)
{
this.messageSender = messageSender;
this.consoleService = consoleService;
}

protected override void ShowPrompt(PromptStyle promptStyle)
{
base.ShowPrompt(promptStyle);

messageSender
.SendRequest(
ShowChoicePromptRequest.Type,
new ShowChoicePromptRequest
{
IsMultiChoice = this.IsMultiChoice,
Caption = this.Caption,
Message = this.Message,
Choices = this.Choices,
DefaultChoice = this.DefaultChoice
DefaultChoices = this.DefaultChoices
}, true)
.ContinueWith(HandlePromptResponse)
.ConfigureAwait(false);
Expand All @@ -79,7 +83,7 @@ private void HandlePromptResponse(
if (!response.PromptCancelled)
{
this.consoleService.ReceivePromptResponse(
response.ChosenItem,
response.ResponseText,
false);
}
else
Expand Down
3 changes: 3 additions & 0 deletions src/PowerShellEditorServices/Console/ChoiceDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ public static ChoiceDetails Create(ChoiceDescription choiceDescription)
/// <returns>True if the input string is a match for the choice.</returns>
public bool MatchesInput(string inputString)
{
// Make sure the input string is trimmed of whitespace
inputString = inputString.Trim();

// Is it the hotkey?
return
string.Equals(inputString, this.hotKeyString, StringComparison.CurrentCultureIgnoreCase) ||
Expand Down
101 changes: 87 additions & 14 deletions src/PowerShellEditorServices/Console/ChoicePromptHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
//

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -37,12 +40,17 @@ public abstract class ChoicePromptHandler : PromptHandler
{
#region Private Fields

private TaskCompletionSource<int> promptTask;
private TaskCompletionSource<int[]> promptTask;

#endregion

#region Properties

/// <summary>
/// Returns true if the choice prompt allows multiple selections.
/// </summary>
protected bool IsMultiChoice { get; private set; }

/// <summary>
/// Gets the caption (title) string to display with the prompt.
/// </summary>
Expand All @@ -62,7 +70,7 @@ public abstract class ChoicePromptHandler : PromptHandler
/// Gets the index of the default choice so that the user
/// interface can make it easy to select this option.
/// </summary>
protected int DefaultChoice { get; private set; }
protected int[] DefaultChoices { get; private set; }

#endregion

Expand Down Expand Up @@ -102,18 +110,71 @@ public Task<int> PromptForChoice(
this.Caption = promptCaption;
this.Message = promptMessage;
this.Choices = choices;
this.DefaultChoice = defaultChoice;
this.promptTask = new TaskCompletionSource<int>();
this.promptTask = new TaskCompletionSource<int[]>();

this.DefaultChoices =
defaultChoice == -1
? new int[] { }
: new int[] { defaultChoice };

// Cancel the TaskCompletionSource if the caller cancels the task
cancellationToken.Register(this.CancelPrompt, true);

// Show the prompt to the user
this.ShowPrompt(PromptStyle.Full);

return this.promptTask.Task;
// Convert the int[] result to int
return this.promptTask.Task.ContinueWith(
t => t.Result.DefaultIfEmpty(-1).First());
}

/// <summary>
/// Prompts the user to make a choice of one or more options using the
/// provided details.
/// </summary>
/// <param name="promptCaption">
/// The caption string which will be displayed to the user.
/// </param>
/// <param name="promptMessage">
/// The descriptive message which will be displayed to the user.
/// </param>
/// <param name="choices">
/// The list of choices from which the user will select.
/// </param>
/// <param name="defaultChoices">
/// The default choice(s) to highlight for the user.
/// </param>
/// <param name="cancellationToken">
/// A CancellationToken that can be used to cancel the prompt.
/// </param>
/// <returns>
/// A Task instance that can be monitored for completion to get
/// the user's choices.
/// </returns>
public Task<int[]> PromptForChoice(
string promptCaption,
string promptMessage,
ChoiceDetails[] choices,
int[] defaultChoices,
CancellationToken cancellationToken)
{
// TODO: Guard against multiple calls

this.Caption = promptCaption;
this.Message = promptMessage;
this.Choices = choices;
this.DefaultChoices = defaultChoices;
this.IsMultiChoice = true;
this.promptTask = new TaskCompletionSource<int[]>();

// Cancel the TaskCompletionSource if the caller cancels the task
cancellationToken.Register(this.CancelPrompt, true);

// Show the prompt to the user
this.ShowPrompt(PromptStyle.Full);

return this.promptTask.Task;
}
/// <summary>
/// Implements behavior to handle the user's response.
/// </summary>
Expand All @@ -124,29 +185,41 @@ public Task<int> PromptForChoice(
/// </returns>
public override bool HandleResponse(string responseString)
{
int choiceIndex = -1;
List<int> choiceIndexes = new List<int>();

// Clean up the response string
responseString = responseString.Trim();
// Clean up the response string and split it
var choiceStrings =
responseString.Trim().Split(
new char[] { ',' },
StringSplitOptions.RemoveEmptyEntries);

for (int i = 0; i < this.Choices.Length; i++)
foreach (string choiceString in choiceStrings)
{
if (this.Choices[i].MatchesInput(responseString))
for (int i = 0; i < this.Choices.Length; i++)
{
choiceIndex = i;
break;
if (this.Choices[i].MatchesInput(choiceString))
{
choiceIndexes.Add(i);

// If this is a single-choice prompt, break out after
// the first matched choice
if (!this.IsMultiChoice)
{
break;
}
}
}
}

if (choiceIndex == -1)
if (choiceIndexes.Count == 0)
{
// The user did not respond with a valid choice,
// show the prompt again to give another chance
this.ShowPrompt(PromptStyle.Minimal);
return false;
}

this.promptTask.SetResult(choiceIndex);
this.promptTask.SetResult(choiceIndexes.ToArray());
return true;
}

Expand Down
Loading