Skip to content

Commit

Permalink
[gh-6] Completion improvements, testing improvements.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashmind committed Oct 5, 2016
1 parent 6b5904b commit af1dec8
Show file tree
Hide file tree
Showing 20 changed files with 282 additions and 162 deletions.
2 changes: 1 addition & 1 deletion MirrorSharp.Common/Advanced/MiddlewareBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ private ImmutableArray<ICommandHandler> CreateCommands() {
var commands = new ICommandHandler[26];
foreach (var command in new ICommandHandler[] {
new ApplyDiagnosticActionHandler(),
new CommitCompletionHandler(),
new CompletionChoiceHandler(),
new MoveCursorHandler(),
new ReplaceTextHandler(),
new SlowUpdateHandler(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -19,7 +17,6 @@ public async Task ExecuteAsync(ArraySegment<byte> data, WorkSession session, ICo
var changes = await session.UpdateFromWorkspaceAsync().ConfigureAwait(false);

var writer = sender.StartJsonMessage("changes");
writer.WriteProperty("echo", false);
writer.WritePropertyStartArray("changes");
foreach (var change in changes) {
writer.WriteChange(change);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,41 @@
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Text;

namespace MirrorSharp.Internal.Commands {
public class CommitCompletionHandler : ICommandHandler {
public class CompletionChoiceHandler : ICommandHandler {
public IImmutableList<char> CommandIds { get; } = ImmutableList.Create('S');

public async Task ExecuteAsync(ArraySegment<byte> data, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
if (data.Array[data.Offset] == (byte)'X') {
// completion cancelled/dismissed
session.CurrentCompletionList = null;
return;
}

var itemIndex = FastConvert.Utf8ByteArrayToInt32(data);
// ReSharper disable once PossibleNullReferenceException
var item = session.CurrentCompletionList.Items[itemIndex];
var completion = session.CurrentCompletionList;
// ReSharper disable once PossibleNullReferenceException
var item = completion.Items[itemIndex];
var change = await session.CompletionService.GetChangeAsync(session.Document, item, cancellationToken: cancellationToken).ConfigureAwait(false);
session.CurrentCompletionList = null;

var textChanges = change.TextChanges.Insert(
0, new TextChange(TextSpan.FromBounds(completion.DefaultSpan.Start, session.CursorPosition), "")
);
session.SourceText = session.SourceText.WithChanges(textChanges);

var writer = sender.StartJsonMessage("changes");
writer.WriteProperty("echo", true);
writer.WritePropertyStartArray("changes");
foreach (var textChange in change.TextChanges) {
foreach (var textChange in textChanges) {
writer.WriteChange(textChange);
}
writer.WriteEndArray();
await sender.SendJsonMessageAsync(cancellationToken).ConfigureAwait(false);
}

public bool CanChangeSession => false;
public bool CanChangeSession => true;
}
}
17 changes: 13 additions & 4 deletions MirrorSharp.Common/Internal/Commands/TypeCharHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,25 @@ namespace MirrorSharp.Internal.Commands {
public class TypeCharHandler : ICommandHandler {
public IImmutableList<char> CommandIds { get; } = ImmutableList.Create('C');

public async Task ExecuteAsync(ArraySegment<byte> data, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
public Task ExecuteAsync(ArraySegment<byte> data, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
var @char = FastConvert.Utf8ByteArrayToChar(data, session.Buffers.CharArray);
session.SourceText = session.SourceText.WithChanges(
new TextChange(new TextSpan(session.CursorPosition, 0), FastConvert.CharToString(@char))
);
session.CursorPosition += 1;
if (session.CurrentCompletionList != null) {
return TaskEx.CompletedTask;
}

if (!session.CompletionService.ShouldTriggerCompletion(session.SourceText, session.CursorPosition, CompletionTrigger.CreateInsertionTrigger(@char)))
return;
var trigger = CompletionTrigger.CreateInsertionTrigger(@char);
if (!session.CompletionService.ShouldTriggerCompletion(session.SourceText, session.CursorPosition, trigger))
return TaskEx.CompletedTask;

return TriggerCompletionAsync(session, sender, cancellationToken, trigger);
}

session.CurrentCompletionList = await session.CompletionService.GetCompletionsAsync(session.Document, session.CursorPosition, cancellationToken: cancellationToken).ConfigureAwait(false);
private async Task TriggerCompletionAsync(WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken, CompletionTrigger trigger) {
session.CurrentCompletionList = await session.CompletionService.GetCompletionsAsync(session.Document, session.CursorPosition, trigger, cancellationToken: cancellationToken).ConfigureAwait(false);
if (session.CurrentCompletionList == null)
return;

Expand All @@ -35,6 +43,7 @@ private Task SendListAsync(CompletionList completionList, ICommandResultSender s
writer.WritePropertyStartArray("list");
foreach (var item in completionList.Items) {
writer.WriteStartObject();
writer.WriteProperty("filterText", item.FilterText);
writer.WriteProperty("displayText", item.DisplayText);
writer.WritePropertyStartArray("tags");
foreach (var tag in item.Tags) {
Expand Down
3 changes: 0 additions & 3 deletions MirrorSharp.Common/Internal/RoslynInternals.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using AshMind.Extensions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@
using System.Threading.Tasks;
using MirrorSharp.Internal.Commands;
using MirrorSharp.Tests.Internal;
using MirrorSharp.Tests.Internal.Results;
using Xunit;

namespace MirrorSharp.Tests {
using static TestHelper;

public class ApplyCodeActionHandlerTests : HandlerTestsBase<ApplyDiagnosticActionHandler> {
public class ApplyDiagnosticActionHandlerTests {
[Fact]
public async Task ApplyCodeAction_CanAddRequiredNamespace() {
public async Task ExecuteAsync_CanAddRequiredNamespace() {
var session = SessionFromTextWithCursor(@"class C { Action a;| }");
var result = await ExecuteAndCaptureResultAsync<SlowUpdateResult>(new SlowUpdateHandler(), session);
var result = await ExecuteHandlerAsync<SlowUpdateHandler, SlowUpdateResult>(session);
var diagnostic = result.Diagnostics.Single(d => d.Message.Contains("Action"));
var action = diagnostic.Actions.Single(a => a.Title.Contains("using"));
await ExecuteAsync(session, ToByteArraySegment(action.Id));

await ExecuteHandlerAsync<ApplyDiagnosticActionHandler>(session, action.Id);

Assert.Equal(
"using System;\r\n\r\nclass C { Action a; }",
Expand Down
43 changes: 43 additions & 0 deletions MirrorSharp.Tests/CompletionChoiceHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Linq;
using System.Threading.Tasks;
using MirrorSharp.Internal;
using MirrorSharp.Internal.Commands;
using MirrorSharp.Tests.Internal;
using MirrorSharp.Tests.Internal.Results;
using Xunit;

namespace MirrorSharp.Tests {
using static TestHelper;

public class CompletionChoiceHandlerTests {
[Fact]
public async Task ExecuteAsync_AppliesSelected_WhenIndexIsProvided() {
var session = SessionFromTextWithCursor("class C { void M(object o) { o| } }");
var completions = await TypeAndGetCompletionsAsync('.', session);
var index = completions.List.Select((c, i) => new { c, i }).First(x => x.c.DisplayText.Contains("ToString")).i;

await ExecuteHandlerAsync<CompletionChoiceHandler>(session, index);

Assert.Equal(
"class C { void M(object o) { o.ToString } }",
session.SourceText.ToString()
);
Assert.Null(session.CurrentCompletionList);
}

[Fact]
public async Task ExecuteAsync_CancelsCompletion_WhenXIsProvidedInsteadOfIndex() {
var session = SessionFromTextWithCursor("class C { void M(object o) { o| } }");
await TypeAndGetCompletionsAsync('.', session);

var result = await ExecuteHandlerAsync<CompletionChoiceHandler, ChangesResult>(session, 'X');

Assert.Null(result);
Assert.Null(session.CurrentCompletionList);
}

private static async Task<TypeCharResult.ResultCompletions> TypeAndGetCompletionsAsync(char @char, WorkSession session) {
return (await ExecuteHandlerAsync<TypeCharHandler, TypeCharResult>(session, @char)).Completions;
}
}
}
52 changes: 0 additions & 52 deletions MirrorSharp.Tests/HandlerTestsBase.cs

This file was deleted.

30 changes: 30 additions & 0 deletions MirrorSharp.Tests/Internal/HandlerTestArgument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Text;

namespace MirrorSharp.Tests.Internal {
public struct HandlerTestArgument {
private readonly byte[] _data;

private HandlerTestArgument(byte[] data) {
_data = data;
}

public static implicit operator HandlerTestArgument(string value) {
return Encoding.UTF8.GetBytes(value);
}

public static implicit operator HandlerTestArgument(char value) {
return Encoding.UTF8.GetBytes(new[] { value });
}

public static implicit operator HandlerTestArgument(int value) {
return value.ToString();
}

public static implicit operator HandlerTestArgument(byte[] data) {
return new HandlerTestArgument(data);
}

public ArraySegment<byte> ToArraySegment() => new ArraySegment<byte>(_data ?? new byte[0]);
}
}
17 changes: 17 additions & 0 deletions MirrorSharp.Tests/Internal/Results/ChangesResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Collections.Generic;

// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable CollectionNeverUpdated.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global

namespace MirrorSharp.Tests.Internal.Results {
public class ChangesResult {
public IList<ResultChange> Changes { get; } = new List<ResultChange>();

public class ResultChange {
public int Start { get; set; }
public int Length { get; set; }
public string Text { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
using System.Collections.Generic;
using JetBrains.Annotations;

namespace MirrorSharp.Tests.Internal {
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable CollectionNeverUpdated.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global

namespace MirrorSharp.Tests.Internal.Results {
public class SlowUpdateResult {
public IList<ResultDiagnostic> Diagnostics { get; } = new List<ResultDiagnostic>();

[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
public class ResultDiagnostic {
public string Message { get; set; }
public string Severity { get; set; }
public IList<string> Tags { get; } = new List<string>();
public IList<ResultAction> Actions { get; } = new List<ResultAction>();
}

[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
public class ResultAction {
public int Id { get; set; }
public string Title { get; set; }
Expand Down
19 changes: 19 additions & 0 deletions MirrorSharp.Tests/Internal/Results/TypeCharResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Collections.Generic;

// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable CollectionNeverUpdated.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global

namespace MirrorSharp.Tests.Internal.Results {
public class TypeCharResult {
public ResultCompletions Completions { get; set; }

public class ResultCompletions {
public IList<ResultCompletion> List { get; } = new List<ResultCompletion>();
}

public class ResultCompletion {
public string DisplayText { get; set; }
}
}
}
26 changes: 26 additions & 0 deletions MirrorSharp.Tests/Internal/StubCommandResultSender.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MirrorSharp.Internal.Commands;
using Newtonsoft.Json;

namespace MirrorSharp.Tests.Internal {
public class StubCommandResultSender : ICommandResultSender {
private readonly StringBuilder _currentMessageBuilder = new StringBuilder();

public string LastMessageTypeName { get; private set; }
public string LastMessageJson { get; private set; }

public JsonWriter StartJsonMessage(string messageTypeName) {
LastMessageTypeName = messageTypeName;
_currentMessageBuilder.Clear();
return new JsonTextWriter(new StringWriter(_currentMessageBuilder));
}

public Task SendJsonMessageAsync(CancellationToken cancellationToken) {
LastMessageJson = "{ " + _currentMessageBuilder + " }";
return Task.CompletedTask;
}
}
}
Loading

0 comments on commit af1dec8

Please sign in to comment.