Skip to content
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

Overload help support. #209

Merged
merged 1 commit into from
Jul 20, 2022
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
43 changes: 43 additions & 0 deletions src/PrettyPrompt/Completion/OverloadItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#region License Header
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
#endregion

using System.Collections.Generic;
using System.Diagnostics;
using PrettyPrompt.Highlighting;

namespace PrettyPrompt.Completion;

/// <summary>
/// An overload item in the Overload Menu Pane.
/// </summary>
[DebuggerDisplay("{Signature}")]
public class OverloadItem
{
public FormattedString Signature { get; }
public FormattedString Summary { get; }
public FormattedString Return { get; }
public IReadOnlyList<Parameter> Parameters { get; }

public OverloadItem(FormattedString signature, FormattedString summary, FormattedString returnDescription, IReadOnlyList<Parameter> parameters)
{
Signature = signature;
Summary = summary;
Return = returnDescription;
Parameters = parameters;
}

public readonly struct Parameter
{
public readonly string Name;
public readonly FormattedString Description;

public Parameter(string name, FormattedString description)
{
Name = name;
Description = description;
}
}
}
55 changes: 33 additions & 22 deletions src/PrettyPrompt/Completion/SlidingArrayWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using PrettyPrompt.Documents;

namespace PrettyPrompt.Completion;
Expand All @@ -24,7 +26,17 @@ internal sealed class SlidingArrayWindow
private List<(CompletionItem Item, bool IsMatching)> itemsSorted = new();
private int windowLength;
private int windowStart;

private int? selectedIndex;
private async Task SetSelectedIndex(int? value, CancellationToken cancellationToken)
{
selectedIndex = value;
var selectedItemChanged = SelectedItemChanged;
if (selectedItemChanged != null)
{
await selectedItemChanged(SelectedItem, cancellationToken).ConfigureAwait(false);
}
}

public SlidingArrayWindow(int windowBuffer = 3)
{
Expand All @@ -34,19 +46,25 @@ public SlidingArrayWindow(int windowBuffer = 3)
public int? SelectedIndexInAllItems => selectedIndex;
public int? SelectedIndexInVisibleItems => selectedIndex - windowStart;
public CompletionItem? SelectedItem => !IsEmpty && selectedIndex.HasValue ? itemsSorted[selectedIndex.Value].Item : null;
public int AllItemsCount => itemsSorted.Count;
public int VisibleItemsCount => visibleItems.Count;
public bool IsEmpty => AllItemsCount == 0;
public IReadOnlyList<CompletionItem> VisibleItems => visibleItems;

public void UpdateItems(IEnumerable<CompletionItem> items, string documentText, int documentCaret, TextSpan spanToReplace, int windowLength)
public event Func<CompletionItem?, CancellationToken, Task>? SelectedItemChanged;

public async Task UpdateItems(IEnumerable<CompletionItem> items, string documentText, int documentCaret, TextSpan spanToReplace, int windowLength, CancellationToken cancellationToken)
{
this.windowLength = windowLength;
this.itemsOriginal.Clear();
this.itemsOriginal.AddRange(items);

Match(documentText, documentCaret, spanToReplace);
ResetSelectedIndex();
await Match(documentText, documentCaret, spanToReplace, cancellationToken).ConfigureAwait(false);
await ResetSelectedIndex(cancellationToken).ConfigureAwait(false);
UpdateVisibleItems();
}

public void Match(string documentText, int caret, TextSpan spanToBeReplaced)
public async Task Match(string documentText, int caret, TextSpan spanToBeReplaced, CancellationToken cancellationToken)
{
//this could be done more efficiently if we would have in-place stable List<T> sort implementation
itemsSorted = itemsOriginal
Expand All @@ -56,21 +74,21 @@ public void Match(string documentText, int caret, TextSpan spanToBeReplaced)
.ToList();

UpdateVisibleItems();
ResetSelectedIndex();
await ResetSelectedIndex(cancellationToken).ConfigureAwait(false);
}

public void IncrementSelectedIndex()
public async Task IncrementSelectedIndex(CancellationToken cancellationToken)
{
if (!selectedIndex.HasValue)
{
selectedIndex = 0;
await SetSelectedIndex(0, cancellationToken).ConfigureAwait(false);
return;
}

if (selectedIndex == AllItemsCount - 1)
return;

selectedIndex++;
await SetSelectedIndex(selectedIndex + 1, cancellationToken).ConfigureAwait(false);

if (selectedIndex + windowBuffer >= windowStart + windowLength && windowStart + windowLength < AllItemsCount)
{
Expand All @@ -79,18 +97,18 @@ public void IncrementSelectedIndex()
}
}

public void DecrementSelectedIndex()
public async Task DecrementSelectedIndex(CancellationToken cancellationToken)
{
if (!selectedIndex.HasValue)
{
selectedIndex = 0;
await SetSelectedIndex(0, cancellationToken).ConfigureAwait(false);
return;
}

if (selectedIndex == 0)
return;

selectedIndex--;
await SetSelectedIndex(selectedIndex - 1, cancellationToken).ConfigureAwait(false);

if (selectedIndex - windowBuffer < windowStart && windowStart > 0)
{
Expand All @@ -99,18 +117,18 @@ public void DecrementSelectedIndex()
}
}

public void Clear()
public async Task Clear(CancellationToken cancellationToken)
{
itemsOriginal.Clear();
itemsSorted.Clear();
windowLength = 0;
ResetSelectedIndex();
await ResetSelectedIndex(cancellationToken).ConfigureAwait(false);
UpdateVisibleItems();
}

private void ResetSelectedIndex()
private async Task ResetSelectedIndex(CancellationToken cancellationToken)
{
selectedIndex = IsEmpty ? null : (itemsSorted[0].IsMatching ? 0 : null);
await SetSelectedIndex(IsEmpty ? null : (itemsSorted[0].IsMatching ? 0 : null), cancellationToken).ConfigureAwait(false);
windowStart = 0;
}

Expand All @@ -123,11 +141,4 @@ private void UpdateVisibleItems()
visibleItems.Add(itemsSorted[i].Item);
}
}

public int AllItemsCount => itemsSorted.Count;
public int VisibleItemsCount => visibleItems.Count;

public bool IsEmpty => AllItemsCount == 0;

public IReadOnlyList<CompletionItem> VisibleItems => visibleItems;
}
20 changes: 15 additions & 5 deletions src/PrettyPrompt/Console/AnsiEscapeCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ public static class AnsiEscapeCodes
private const string Escape = "\u001b";
private const string ResetForegroundColor = "39";
private const string ResetBackgroundColor = "49";
private const char ResetChar = '0';
private const char Bold = '1';
private const char Underline = '4';
private const string Reverse = "7";
public const string ClearLine = $"{Escape}[0K";
public const string ClearToEndOfScreen = $"{Escape}[0J";
public const string ClearEntireScreen = $"{Escape}[2J";
public static readonly string Reset = $"{Escape}[{ResetChar}m";

/// <summary>
/// Index starts at 1!
Expand Down Expand Up @@ -47,8 +49,6 @@ private static void MoveCursor(StringBuilder sb, int count, char direction)
}
}

public static readonly string Reset = $"{Escape}[0m";

internal static string ToAnsiEscapeSequence(string colorCode) => $"{Escape}[{colorCode}m";

public static string ToAnsiEscapeSequenceSlow(ConsoleFormat formatting)
Expand All @@ -69,9 +69,19 @@ public static void AppendAnsiEscapeSequence(StringBuilder stringBuilder, Console
}
else
{
stringBuilder.Append(formatting.ForegroundCode ?? ResetForegroundColor);
stringBuilder.Append(';');
stringBuilder.Append(formatting.BackgroundCode ?? ResetBackgroundColor);
stringBuilder.Append(ResetChar);

if (formatting.ForegroundCode != null)
{
stringBuilder.Append(';');
stringBuilder.Append(formatting.ForegroundCode);
}

if (formatting.BackgroundCode != null)
{
stringBuilder.Append(';');
stringBuilder.Append(formatting.BackgroundCode);
}

if (formatting.Bold)
{
Expand Down
51 changes: 44 additions & 7 deletions src/PrettyPrompt/Documents/WordWrapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,12 @@ static bool NextCharacterIsFullWidthAndWillWrap(int width, int currentLineLength
/// where possible, otherwise split by character if a single word is
/// greater than maxLength.
/// </summary>
public static List<FormattedString> WrapWords(FormattedString input, int maxLength)
public static List<FormattedString> WrapWords(FormattedString input, int maxLength, int? maxLines = null)
{
Debug.Assert(maxLength >= 0);
Debug.Assert(maxLines.GetValueOrDefault() >= 0);

if (input.Length == 0 || maxLength == 0)
if (input.Length == 0 || maxLength == 0 || maxLines == 0)
{
return new List<FormattedString>();
}
Expand All @@ -119,26 +120,33 @@ public static List<FormattedString> WrapWords(FormattedString input, int maxLeng
{
if (word.Length <= maxLength)
{
ProcessWord(word);
if (!ProcessWord(word))
{
return lines;
}
}
else
{
//slow path
foreach (var currentWord in word.SplitIntoChunks(maxLength))
{
ProcessWord(currentWord);
if (!ProcessWord(currentWord))
{
return lines;
}
}
}

void ProcessWord(FormattedString currentWord)
bool ProcessWord(FormattedString currentWord)
{
var wordLength = currentWord.GetUnicodeWidth();
var wordWithSpaceLength = currentLineWidth == 0 ? wordLength : wordLength + 1;

bool result = true;
if (currentLineWidth > maxLength ||
currentLineWidth + wordWithSpaceLength > maxLength)
{
lines.Add(currentLine.ToFormattedString());
result = AddLine(currentLine.ToFormattedString());
currentLine.Clear();
currentLineWidth = 0;
}
Expand All @@ -154,10 +162,39 @@ void ProcessWord(FormattedString currentWord)
currentLine.Append(currentWord);
currentLineWidth += wordLength + 1;
}
return result;
}
}

if (!AddLine(currentLine.ToFormattedString()))
{
return lines;
}
}

bool AddLine(FormattedString line)
{
if (lines.Count == maxLines)
{
var lastLine = lines[^1];
FormattedString lastLineModified;
lines.RemoveAt(lines.Count - 1);
if (maxLength > 3)
{
Debug.Assert(lastLine.Length <= maxLength);
lastLine = lastLine.Substring(0, Math.Min(maxLength - 3, lastLine.Length));
lastLineModified = lastLine + new string('.', Math.Min(3, maxLength - lastLine.Length));
}
else
{
lastLineModified = new string('.', maxLength);
}
lines.Add(lastLineModified);
return false;
}

lines.Add(currentLine.ToFormattedString());
lines.Add(line);
return true;
}

return lines;
Expand Down
17 changes: 17 additions & 0 deletions src/PrettyPrompt/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,21 @@ internal static void TransformBackground(this Row row, in AnsiColor? background,
row[i].TransformBackground(background);
}
}

internal static int ArgMax(this Span<int> values)
{
Debug.Assert(values.Length > 0);
int maxIdx = 0;
int maxValue = values[0];
for (int i = 1; i < values.Length; i++)
{
var val = values[i];
if (val > maxValue)
{
maxIdx = i;
maxValue = val;
}
}
return maxIdx;
}
}
Loading