Skip to content

Commit

Permalink
[closes gh-32] Updated to commit completion on commit chars.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashmind committed Dec 28, 2016
1 parent 130961c commit 94d6813
Show file tree
Hide file tree
Showing 26 changed files with 249 additions and 153 deletions.
5 changes: 1 addition & 4 deletions MirrorSharp.Benchmarks/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyCompany("Andrey Shchekin")]
[assembly: AssemblyProduct("MirrorSharp.Benchmarks")]
[assembly: AssemblyTrademark("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
Expand Down
2 changes: 2 additions & 0 deletions MirrorSharp.Common/Advanced/IFastJsonWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ public interface IFastJsonWriter : IDisposable {
void WriteStartArray();
void WriteEndArray();
void WriteProperty([NotNull] string name, [CanBeNull] string value);
void WriteProperty([NotNull] string name, char value);
void WriteProperty([NotNull] string name, int value);
void WriteProperty([NotNull] string name, bool value);
void WritePropertyStartObject([NotNull] string name);
void WritePropertyStartArray([NotNull] string name);
void WritePropertyName([NotNull] string name);
void WriteValue([CanBeNull] string value);
void WriteValue(char value);
void WriteValue(int value);
void WriteValue(bool value);
}
Expand Down
2 changes: 1 addition & 1 deletion MirrorSharp.Common/Advanced/MiddlewareBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ protected IReadOnlyCollection<ICommandHandler> CreateHandlers() {
new ApplyDiagnosticActionHandler(),
new CompletionStateHandler(completion),
new MoveCursorHandler(signatureHelp),
new ReplaceTextHandler(signatureHelp),
new ReplaceTextHandler(signatureHelp, completion),
new RequestSelfDebugDataHandler(),
new SetOptionsHandler(_languages, _options?.SetOptionsFromClient),
new SlowUpdateHandler(_options?.SlowUpdate),
Expand Down
16 changes: 16 additions & 0 deletions MirrorSharp.Common/Internal/CharListString.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections;
using System.Collections.Generic;

namespace MirrorSharp.Internal {
internal struct CharListString : IEnumerable<char> {
private readonly IReadOnlyList<char> _chars;

public CharListString(IReadOnlyList<char> chars) {
_chars = chars;
}

public IEnumerator<char> GetEnumerator() => _chars.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _chars.GetEnumerator();
}
}
15 changes: 15 additions & 0 deletions MirrorSharp.Common/Internal/Completion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using JetBrains.Annotations;
using Microsoft.CodeAnalysis.Completion;

namespace MirrorSharp.Internal {
public class Completion {
public Completion(CompletionService service) {
Service = service;
}

public CompletionService Service { get; }
[CanBeNull] public CompletionList CurrentList { get; set; }
public bool ChangeEchoPending { get; set; }
public CompletionTrigger? PendingTrigger { get; set; }
}
}
72 changes: 52 additions & 20 deletions MirrorSharp.Common/Internal/FastUtf8JsonWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ public void WriteProperty(string name, string value) {
WriteValue(value);
}

internal void WriteProperty(string name, CharListString value) {
WritePropertyName(name);
WriteValue(value);
}

public void WriteProperty(string name, char value) {
WritePropertyName(name);
WriteValue(value);
}

public void WriteProperty(string name, int value) {
WritePropertyName(name);
WriteValue(value);
Expand Down Expand Up @@ -92,31 +102,53 @@ public void WriteValue(string value) {

WriteRawByte(Utf8.Quote);
foreach (var @char in value) {
if (@char < 32) {
WriteRawBytes(Utf8.Escaped[@char]);
continue;
}
WriteUnquotedChar(@char);
}
WriteRawByte(Utf8.Quote);
WriteEndValue();
}

if (@char == '\\') {
WriteRawBytes(Utf8.EscapedSlash);
continue;
}
internal void WriteValue(CharListString value) {
WriteStartValue();
WriteRawByte(Utf8.Quote);
foreach (var @char in value) {
WriteUnquotedChar(@char);
}
WriteRawByte(Utf8.Quote);
WriteEndValue();
}

if (@char == '"') {
WriteRawBytes(Utf8.EscapedQuote);
continue;
}
public void WriteValue(char value) {
WriteStartValue();
WriteRawByte(Utf8.Quote);
WriteUnquotedChar(value);
WriteRawByte(Utf8.Quote);
WriteEndValue();
}

if (@char < 256) {
WriteRawByte((byte)@char);
continue;
}
private void WriteUnquotedChar(char @char) {
if (@char < 32) {
WriteRawBytes(Utf8.Escaped[@char]);
return;
}

_oneCharBuffer[0] = @char;
_position += Encoding.UTF8.GetBytes(_oneCharBuffer, 0, 1, _buffer, _position);
if (@char == '\\') {
WriteRawBytes(Utf8.EscapedSlash);
return;
}
WriteRawByte(Utf8.Quote);
WriteEndValue();

if (@char == '"') {
WriteRawBytes(Utf8.EscapedQuote);
return;
}

if (@char < 256) {
WriteRawByte((byte)@char);
return;
}

_oneCharBuffer[0] = @char;
_position += Encoding.UTF8.GetBytes(_oneCharBuffer, 0, 1, _buffer, _position);
}

public void WriteValue(int value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public async Task ExecuteAsync(ArraySegment<byte> data, WorkSession session, ICo

var writer = sender.StartJsonMessage("changes");
writer.WritePropertyStartArray("changes");
writer.WriteProperty("reason", "fix");
foreach (var change in changes) {
writer.WriteChange(change);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public CompletionStateHandler(ICompletionSupport completion) {

public Task ExecuteAsync(ArraySegment<byte> data, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
var first = data.Array[data.Offset];
if (first >= (byte)'A' && first <= (byte)'Z')
return _completion.ApplyCompletionStateChangeAsync((CompletionStateChange)first, session, sender, cancellationToken);
if (first == (byte)'X')
return _completion.ApplyCompletionCancellationAsync(session, sender, cancellationToken);

var itemIndex = FastConvert.Utf8ByteArrayToInt32(data);
return _completion.ApplyCompletionSelectionAsync(itemIndex, session, sender, cancellationToken);
Expand Down
25 changes: 18 additions & 7 deletions MirrorSharp.Common/Internal/Handlers/ReplaceTextHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,30 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis.Text;
using MirrorSharp.Internal.Handlers.Shared;
using MirrorSharp.Internal.Results;

namespace MirrorSharp.Internal.Handlers {
public class ReplaceTextHandler : ICommandHandler {
public IImmutableList<char> CommandIds { get; } = ImmutableList.Create('P', 'R');
private readonly ISignatureHelpSupport _signatureHelp;
[NotNull] private readonly ISignatureHelpSupport _signatureHelp;
[NotNull] private readonly ICompletionSupport _completion;

public ReplaceTextHandler(ISignatureHelpSupport signatureHelp) {
public ReplaceTextHandler([NotNull] ISignatureHelpSupport signatureHelp, [NotNull] ICompletionSupport completion) {
_signatureHelp = signatureHelp;
_completion = completion;
}

public Task ExecuteAsync(ArraySegment<byte> data, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
public async Task ExecuteAsync(ArraySegment<byte> data, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
var endOffset = data.Offset + data.Count - 1;
var partStart = data.Offset;

int? start = null;
int? length = null;
int? cursorPosition = null;
string trigger = null;

for (var i = data.Offset; i <= endOffset; i++) {
if (data.Array[i] != (byte)':')
Expand All @@ -41,18 +45,25 @@ public Task ExecuteAsync(ArraySegment<byte> data, WorkSession session, ICommandR
continue;
}

cursorPosition = FastConvert.Utf8ByteArrayToInt32(part);
if (cursorPosition == null) {
cursorPosition = FastConvert.Utf8ByteArrayToInt32(part);
partStart = i + 1;
continue;
}

trigger = part.Count > 0 ? Encoding.UTF8.GetString(part.Array, part.Offset, part.Count) : string.Empty;
partStart = i + 1;
break;
}
if (start == null || length == null || cursorPosition == null)
throw new FormatException("Command arguments must be 'start:length:cursor:text'.");
if (start == null || length == null || cursorPosition == null || trigger == null)
throw new FormatException("Command arguments must be 'start:length:cursor:trigger:text'.");

var text = Encoding.UTF8.GetString(data.Array, partStart, endOffset - partStart + 1);

session.SourceText = session.SourceText.WithChanges(new TextChange(new TextSpan(start.Value, length.Value), text));
session.CursorPosition = cursorPosition.Value;
return _signatureHelp.ApplyCursorPositionChangeAsync(session, sender, cancellationToken);
await _signatureHelp.ApplyCursorPositionChangeAsync(session, sender, cancellationToken).ConfigureAwait(false);
await _completion.ApplyReplacedTextAsync(trigger, session, sender, cancellationToken).ConfigureAwait(false);
}
}
}

This file was deleted.

74 changes: 37 additions & 37 deletions MirrorSharp.Common/Internal/Handlers/Shared/CompletionSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,46 @@

namespace MirrorSharp.Internal.Handlers.Shared {
public class CompletionSupport : ICompletionSupport {
private const string ChangeReasonCompletion = "completion";

public Task ApplyTypedCharAsync(char @char, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
if (session.CurrentCompletionList != null && !session.CanRetriggerCompletion)
if (session.Completion.CurrentList != null)
return TaskEx.CompletedTask;

var trigger = CompletionTrigger.CreateInsertionTrigger(@char);
if (session.Completion.ChangeEchoPending) {
session.Completion.PendingTrigger = trigger;
return TaskEx.CompletedTask;
}
return CheckCompletionAsync(trigger, session, sender, cancellationToken);
}

public Task ApplyReplacedTextAsync(string reason, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
if (reason != ChangeReasonCompletion)
return TaskEx.CompletedTask;

session.Completion.ChangeEchoPending = false;
var pendingTrigger = session.Completion.PendingTrigger;
if (pendingTrigger == null)
return TaskEx.CompletedTask;

session.Completion.PendingTrigger = null;
return CheckCompletionAsync(pendingTrigger.Value, session, sender, cancellationToken);
}

public async Task ApplyCompletionSelectionAsync(int selectedIndex, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
// ReSharper disable once PossibleNullReferenceException
var completion = session.CurrentCompletionList;
var completion = session.Completion.CurrentList;
// ReSharper disable once PossibleNullReferenceException
var item = completion.Items[selectedIndex];
var change = await session.CompletionService.GetChangeAsync(session.Document, item, cancellationToken: cancellationToken).ConfigureAwait(false);
session.CurrentCompletionList = null;
session.CanRetriggerCompletion = true;
var change = await session.Completion.Service.GetChangeAsync(session.Document, item, cancellationToken: cancellationToken).ConfigureAwait(false);
session.Completion.CurrentList = null;

var textChanges = ReplaceIncompleteText(session, completion, change.TextChanges);

session.SourceText = session.SourceText.WithChanges(textChanges);
session.Completion.ChangeEchoPending = true;

var writer = sender.StartJsonMessage("changes");
writer.WriteProperty("reason", ChangeReasonCompletion);
writer.WritePropertyStartArray("changes");
foreach (var textChange in textChanges) {
writer.WriteChange(textChange);
Expand All @@ -50,58 +68,40 @@ private static ImmutableArray<TextChange> ReplaceIncompleteText(WorkSession sess
textChanges = ImmutableArray.Create(new TextChange(new TextSpan(newStart, newLength), textChanges[0].NewText));
}
else {
session.SourceText = session.SourceText.WithChanges(new TextChange(new TextSpan(completion.DefaultSpan.Start, session.CursorPosition - completion.DefaultSpan.Start), ""));
textChanges = textChanges.Insert(0, new TextChange(new TextSpan(completion.DefaultSpan.Start, session.CursorPosition - completion.DefaultSpan.Start), ""));
}
return textChanges;
}

public Task ApplyCompletionStateChangeAsync(CompletionStateChange change, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
if (change == CompletionStateChange.Cancel) {
// completion cancelled/dismissed
session.CurrentCompletionList = null;
session.CanRetriggerCompletion = false;
return TaskEx.CompletedTask;
}

if (change == CompletionStateChange.Empty) {
// completion is empty, can recomplete
session.CanRetriggerCompletion = true;
if (session.SourceText.Length == 0)
return TaskEx.CompletedTask;

var trigger = CompletionTrigger.CreateInsertionTrigger(session.SourceText[session.CursorPosition - 1]);
return CheckCompletionAsync(trigger, session, sender, cancellationToken);
}

if (change == CompletionStateChange.NonEmptyAfterEmpty) {
// completion is non-empty again
session.CanRetriggerCompletion = false;
return TaskEx.CompletedTask;
}

throw new ArgumentOutOfRangeException($"Unsupported completion state change: {change}.");
public Task ApplyCompletionCancellationAsync(WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
session.Completion.CurrentList = null;
session.Completion.ChangeEchoPending = false;
session.Completion.PendingTrigger = null;
return TaskEx.CompletedTask;
}

private Task CheckCompletionAsync(CompletionTrigger trigger, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken) {
if (!session.CompletionService.ShouldTriggerCompletion(session.SourceText, session.CursorPosition, trigger))
if (!session.Completion.Service.ShouldTriggerCompletion(session.SourceText, session.CursorPosition, trigger))
return TaskEx.CompletedTask;

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

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

session.CurrentCompletionList = completionList;
session.Completion.CurrentList = completionList;
session.Completion.ChangeEchoPending = false;
session.Completion.PendingTrigger = null;
await SendCompletionListAsync(completionList, sender, cancellationToken).ConfigureAwait(false);
}

private Task SendCompletionListAsync(CompletionList completionList, ICommandResultSender sender, CancellationToken cancellationToken) {
var writer = sender.StartJsonMessage("completions");
writer.WriteProperty("commitChars", new CharListString(completionList.Rules.DefaultCommitCharacters));
writer.WritePropertyName("span");
// ReSharper disable once PossibleNullReferenceException
writer.WriteSpan(completionList.DefaultSpan);
writer.WritePropertyStartArray("completions");
foreach (var item in completionList.Items) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
namespace MirrorSharp.Internal.Handlers.Shared {
public interface ICompletionSupport {
Task ApplyTypedCharAsync(char @char, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken);
Task ApplyReplacedTextAsync(string reason, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken);
Task ApplyCompletionSelectionAsync(int selectedIndex, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken);
Task ApplyCompletionStateChangeAsync(CompletionStateChange change, WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken);
Task ApplyCompletionCancellationAsync(WorkSession session, ICommandResultSender sender, CancellationToken cancellationToken);
}
}
Loading

0 comments on commit 94d6813

Please sign in to comment.