Skip to content

Commit

Permalink
Correctly replace trigger characters, if any, for completions
Browse files Browse the repository at this point in the history
  • Loading branch information
tintoy committed Jul 2, 2020
1 parent c3b4bd7 commit 0b1696f
Show file tree
Hide file tree
Showing 20 changed files with 151 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 0.3.7.{build}
version: 0.3.8.{build}
image: Visual Studio 2019
build_script:
- ps: >-
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## v0.3.8

* Completions now correctly replace trigger characters, if any (tintoy/msbuild-project-tools-vscode#67).

## v0.3.7

* Explicitly watch parent process for termination (tintoy/msbuild-project-tools-vscode#53).
Expand Down
18 changes: 14 additions & 4 deletions src/LanguageServer.Engine/CompletionProviders/CommentCompletion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,16 @@ public CommentCompletion(ILogger logger)
/// <param name="projectDocument">
/// The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>.
/// </param>
/// <param name="triggerCharacters">
/// The character(s), if any, that triggered completion.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> that can be used to cancel the operation.
/// </param>
/// <returns>
/// A <see cref="Task{TResult}"/> that resolves either a <see cref="CompletionList"/>s, or <c>null</c> if no completions are provided.
/// </returns>
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, CancellationToken cancellationToken = default(CancellationToken))
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, string triggerCharacters, CancellationToken cancellationToken = default(CancellationToken))
{
if (location == null)
throw new ArgumentNullException(nameof(location));
Expand All @@ -72,15 +75,22 @@ public CommentCompletion(ILogger logger)

return null;
}
if (replaceElement != null)

if (replaceElement != null)
{
Range replaceRange = replaceElement.Range;

// Replace any characters that were typed to trigger the completion.
if (triggerCharacters != null)
replaceRange = projectDocument.XmlPositions.ExtendLeft(replaceRange, byCharCount: triggerCharacters.Length);

Log.Verbose("Offering completions to replace element {ElementName} @ {ReplaceRange:l}",
replaceElement.Name,
replaceElement.Range
replaceRange
);

completions.AddRange(
GetCompletionItems(replaceElement.Range)
GetCompletionItems(replaceRange)
);
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,16 @@ protected CompletionProvider(ILogger logger)
/// <param name="projectDocument">
/// The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>.
/// </param>
/// <param name="triggerCharacters">
/// The character(s), if any, that triggered completion.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> that can be used to cancel the operation.
/// </param>
/// <returns>
/// A <see cref="Task{TResult}"/> that resolves either a <see cref="CompletionList"/>, or <c>null</c> if no completions are provided.
/// </returns>
public abstract Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, CancellationToken cancellationToken = default(CancellationToken));
public abstract Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, string triggerCharacters, CancellationToken cancellationToken = default(CancellationToken));

/// <summary>
/// Get the textual representation used to sort the completion item with the specified label.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ public interface ICompletionProvider
/// <param name="projectDocument">
/// The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>.
/// </param>
/// <param name="triggerCharacters">
/// The character(s), if any, that triggered completion.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> that can be used to cancel the operation.
/// </param>
/// <returns>
/// A <see cref="Task{TResult}"/> that resolves either a list of <see cref="CompletionItem"/>s, or <c>null</c> if no completions are provided.
/// </returns>
Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, CancellationToken cancellationToken = default(CancellationToken));
Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, string triggerCharacters, CancellationToken cancellationToken = default(CancellationToken));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,16 @@ public ItemAttributeCompletion(ILogger logger)
/// <param name="projectDocument">
/// The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>.
/// </param>
/// <param name="triggerCharacters">
/// The character(s), if any, that triggered completion.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> that can be used to cancel the operation.
/// </param>
/// <returns>
/// A <see cref="Task{TResult}"/> that resolves either a <see cref="CompletionList"/>s, or <c>null</c> if no completions are provided.
/// </returns>
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, CancellationToken cancellationToken = default(CancellationToken))
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, string triggerCharacters, CancellationToken cancellationToken = default(CancellationToken))
{
if (location == null)
throw new ArgumentNullException(nameof(location));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,16 @@ public ItemElementCompletion(ILogger logger)
/// <param name="projectDocument">
/// The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>.
/// </param>
/// <param name="triggerCharacters">
/// The character(s), if any, that triggered completion.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> that can be used to cancel the operation.
/// </param>
/// <returns>
/// A <see cref="Task{TResult}"/> that resolves either a <see cref="CompletionList"/>s, or <c>null</c> if no completions are provided.
/// </returns>
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, CancellationToken cancellationToken = default(CancellationToken))
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, string triggerCharacters, CancellationToken cancellationToken = default(CancellationToken))
{
if (location == null)
throw new ArgumentNullException(nameof(location));
Expand All @@ -66,7 +69,7 @@ public ItemElementCompletion(ILogger logger)

List<CompletionItem> completions = new List<CompletionItem>();

Log.Verbose("Evaluate completions for {XmlLocation:l}", location);
Log.Verbose("Evaluate completions for {XmlLocation:l} (trigger characters = {TriggerCharacters}", location, triggerCharacters);

using (await projectDocument.Lock.ReaderLockAsync())
{
Expand All @@ -92,12 +95,9 @@ public ItemElementCompletion(ILogger logger)
{
replaceRange = location.Position.ToEmptyRange();

// Handle replacement of existing "<" if one was typed.
if (projectDocument.IsMSBuildProjectCached)
{
// Project XML is currently invalid; assume it's because they've typed a "<" character and attempt to compensate.
replaceRange = projectDocument.XmlPositions.ExtendLeft(replaceRange, byCharCount: 1);
}
// Replace any characters that were typed to trigger the completion.
if (triggerCharacters != null)
replaceRange = projectDocument.XmlPositions.ExtendLeft(replaceRange, byCharCount: triggerCharacters.Length);

Log.Verbose("Offering completions to create element @ {ReplaceRange:l}",
replaceRange
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,16 @@ public ItemGroupExpressionCompletion(ILogger logger)
/// <param name="projectDocument">
/// The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>.
/// </param>
/// <param name="triggerCharacters">
/// The character(s), if any, that triggered completion.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> that can be used to cancel the operation.
/// </param>
/// <returns>
/// A <see cref="Task{TResult}"/> that resolves either a <see cref="CompletionList"/>s, or <c>null</c> if no completions are provided.
/// </returns>
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, CancellationToken cancellationToken = default(CancellationToken))
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, string triggerCharacters, CancellationToken cancellationToken = default(CancellationToken))
{
if (location == null)
throw new ArgumentNullException(nameof(location));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,16 @@ public ItemMetadataCompletion(ILogger logger)
/// <param name="projectDocument">
/// The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>.
/// </param>
/// <param name="triggerCharacters">
/// The character(s), if any, that triggered completion.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> that can be used to cancel the operation.
/// </param>
/// <returns>
/// A <see cref="Task{TResult}"/> that resolves either a <see cref="CompletionList"/>s, or <c>null</c> if no completions are provided.
/// </returns>
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, CancellationToken cancellationToken = default(CancellationToken))
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, string triggerCharacters, CancellationToken cancellationToken = default(CancellationToken))
{
if (location == null)
throw new ArgumentNullException(nameof(location));
Expand Down Expand Up @@ -78,7 +81,7 @@ public ItemMetadataCompletion(ILogger logger)
);

completions.AddRange(
GetElementCompletions(location, projectDocument, existingMetadata)
GetElementCompletions(location, projectDocument, triggerCharacters, existingMetadata)
);
}

Expand Down Expand Up @@ -193,13 +196,16 @@ IEnumerable<CompletionItem> GetAttributeCompletions(XmlLocation location, Projec
/// <param name="projectDocument">
/// The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>.
/// </param>
/// <param name="triggerCharacters">
/// The character(s), if any, that triggered completion.
/// </param>
/// <param name="existingMetadata">
/// Metadata already declared on the item.
/// </param>
/// <returns>
/// A sequence of <see cref="CompletionItem"/>s.
/// </returns>
IEnumerable<CompletionItem> GetElementCompletions(XmlLocation location, ProjectDocument projectDocument, HashSet<string> existingMetadata)
IEnumerable<CompletionItem> GetElementCompletions(XmlLocation location, ProjectDocument projectDocument, string triggerCharacters, HashSet<string> existingMetadata)
{
Log.Verbose("Evaluate element completions for {XmlLocation:l}", location);

Expand All @@ -212,10 +218,16 @@ IEnumerable<CompletionItem> GetElementCompletions(XmlLocation location, ProjectD
}

Range replaceRange;

string itemType;
if (replaceElement != null)
{
replaceRange = replaceElement.Range;

// Replace any characters that were typed to trigger the completion.
if (triggerCharacters != null)
replaceRange = projectDocument.XmlPositions.ExtendLeft(replaceRange, byCharCount: triggerCharacters.Length);

itemType = replaceElement.ParentElement?.Name;
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public ItemMetadataExpressionCompletion(ILogger logger)
/// <param name="location">
/// The <see cref="XmlLocation"/> where completions are requested.
/// </param>
/// <param name="triggerCharacters">
/// The character(s), if any, that triggered completion.
/// </param>
/// <param name="projectDocument">
/// The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>.
/// </param>
Expand All @@ -54,7 +57,7 @@ public ItemMetadataExpressionCompletion(ILogger logger)
/// <returns>
/// A <see cref="Task{TResult}"/> that resolves either a <see cref="CompletionList"/>s, or <c>null</c> if no completions are provided.
/// </returns>
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, CancellationToken cancellationToken = default(CancellationToken))
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, string triggerCharacters, CancellationToken cancellationToken = default(CancellationToken))
{
if (location == null)
throw new ArgumentNullException(nameof(location));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,16 @@ public PackageReferenceCompletion(ILogger logger)
/// <param name="projectDocument">
/// The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>.
/// </param>
/// <param name="triggerCharacters">
/// The character(s), if any, that triggered completion.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> that can be used to cancel the operation.
/// </param>
/// <returns>
/// A <see cref="Task{TResult}"/> that resolves either a <see cref="CompletionList"/>s, or <c>null</c> if no completions are provided.
/// </returns>
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, CancellationToken cancellationToken = default(CancellationToken))
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, string triggerCharacters, CancellationToken cancellationToken = default(CancellationToken))
{
if (location == null)
throw new ArgumentNullException(nameof(location));
Expand Down Expand Up @@ -104,15 +107,12 @@ public PackageReferenceCompletion(ILogger logger)
{
replaceRange = replaceElement.Range;

// Handle replacement of existing "<" if one was typed.
if (projectDocument.IsMSBuildProjectCached)
{
// Project XML is currently invalid; assume it's because they've typed a "<" character and attempt to compensate.
replaceRange = projectDocument.XmlPositions.ExtendLeft(replaceRange, byCharCount: 1);
}
// Replace any characters that were typed to trigger the completion.
if (triggerCharacters != null)
replaceRange = projectDocument.XmlPositions.ExtendLeft(replaceRange, byCharCount: triggerCharacters.Length);

Log.Verbose("Offering completions to replace child element @ {ReplaceRange} of {ElementName} @ {Position:l}",
replaceElement.Range,
replaceRange,
"ItemGroup",
location.Position
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,16 @@ public PropertyConditionCompletion(ILogger logger)
/// <param name="projectDocument">
/// The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>.
/// </param>
/// <param name="triggerCharacters">
/// The character(s), if any, that triggered completion.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> that can be used to cancel the operation.
/// </param>
/// <returns>
/// A <see cref="Task{TResult}"/> that resolves either a <see cref="CompletionList"/>s, or <c>null</c> if no completions are provided.
/// </returns>
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, CancellationToken cancellationToken = default(CancellationToken))
public override async Task<CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, string triggerCharacters, CancellationToken cancellationToken = default(CancellationToken))
{
if (location == null)
throw new ArgumentNullException(nameof(location));
Expand Down
Loading

0 comments on commit 0b1696f

Please sign in to comment.