From d2ac71e3a65b001bc8b7744db1d5eb351b307648 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Fri, 27 Oct 2023 16:38:02 -0700 Subject: [PATCH 1/3] Improve Hover markdown on 'await' keyword --- ...tocolConversions.MarkdownContentBuilder.cs | 54 ++++++++++++++++++ .../Extensions/ProtocolConversions.cs | 57 ++++++++++++++----- .../ProtocolUnitTests/Hover/HoverTests.cs | 28 +++++++++ 3 files changed, 124 insertions(+), 15 deletions(-) create mode 100644 src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs new file mode 100644 index 0000000000000..3388ff424e644 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.LanguageServer +{ + internal static partial class ProtocolConversions + { + private readonly struct MarkdownContentBuilder : IDisposable + { + private readonly ArrayBuilder _linesBuilder; + + public MarkdownContentBuilder() + { + _linesBuilder = ArrayBuilder.GetInstance(); + } + + public void Append(string text) + { + if (_linesBuilder.Count == 0) + { + _linesBuilder.Add(text); + } + else + { + _linesBuilder[^1] = _linesBuilder[^1] + text; + } + } + + public void AppendLine(string text = "") + { + _linesBuilder.Add(text); + } + + public bool IsLineEmpty() + { + return _linesBuilder.Count == 0 ? true : string.IsNullOrEmpty(_linesBuilder[^1]); + } + + public string Build() + { + return string.Join(Environment.NewLine, _linesBuilder); + } + + public void Dispose() + { + _linesBuilder.Free(); + } + } + } +} diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs index cfaf51c5d62e8..f6124bdd4df4e 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs @@ -30,11 +30,13 @@ namespace Microsoft.CodeAnalysis.LanguageServer { - internal static class ProtocolConversions + internal static partial class ProtocolConversions { private const string CSharpMarkdownLanguageName = "csharp"; private const string VisualBasicMarkdownLanguageName = "vb"; private const string SourceGeneratedDocumentBaseUri = "source-generated:///"; + private const string BlockCodeFence = "```"; + private const string InlineCodeFence = "`"; #pragma warning disable RS0030 // Do not use banned APIs private static readonly Uri s_sourceGeneratedDocumentBaseUri = new(SourceGeneratedDocumentBaseUri, UriKind.Absolute); @@ -844,40 +846,65 @@ public static LSP.MarkupContent GetDocumentationMarkupContent(ImmutableArray DoAsync() + { + return {|caret:await|} DoAsync(); + } +}"; + var clientCapabilities = new LSP.ClientCapabilities + { + TextDocument = new LSP.TextDocumentClientCapabilities { Hover = new LSP.HoverSetting { ContentFormat = [LSP.MarkupKind.Markdown] } } + }; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, clientCapabilities); + var expectedLocation = testLspServer.GetLocations("caret").Single(); + + var expectedMarkdown = @"Awaited task returns '`class System.String`' +"; + + var results = await RunGetHoverAsync( + testLspServer, + expectedLocation).ConfigureAwait(false); + Assert.Equal(expectedMarkdown, results.Contents.Value.Fourth.Value); + } + private static async Task RunGetHoverAsync( TestLspServer testLspServer, LSP.Location caret, From 9cfc6a7cbec60a65c707ab00e927634ac8a50f70 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Tue, 31 Oct 2023 13:08:01 -0700 Subject: [PATCH 2/3] Adjust string display --- src/Features/Core/Portable/FeaturesResources.resx | 2 +- src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf | 4 ++-- src/Features/Core/Portable/xlf/FeaturesResources.de.xlf | 4 ++-- src/Features/Core/Portable/xlf/FeaturesResources.es.xlf | 4 ++-- src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf | 4 ++-- src/Features/Core/Portable/xlf/FeaturesResources.it.xlf | 4 ++-- src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf | 4 ++-- src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf | 4 ++-- src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf | 4 ++-- src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf | 4 ++-- src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf | 4 ++-- src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf | 4 ++-- src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf | 4 ++-- src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf | 4 ++-- .../LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs | 2 +- 15 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index fdd1b9dcb5263..277ec2a59c9ba 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -668,7 +668,7 @@ Do you want to continue? <Pending> - Awaited task returns '{0}' + Awaited task returns {0} Awaited task returns no value diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index cc182a9a63127..908b58e4484f6 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -316,8 +316,8 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - Očekávaná úloha vrací {0}. + Awaited task returns {0} + Očekávaná úloha vrací {0}. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index cad0f00fccedb..fa711e3062392 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -316,8 +316,8 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - Erwartete Aufgabe gibt "{0}" zurück + Awaited task returns {0} + Erwartete Aufgabe gibt "{0}" zurück diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 61c8078971937..de92cd1017efc 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -316,8 +316,8 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - La tarea esperada devuelve "{0}". + Awaited task returns {0} + La tarea esperada devuelve "{0}". diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 8f690efa98b34..592d9634c1915 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -316,8 +316,8 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - La tâche attendue retourne '{0}' + Awaited task returns {0} + La tâche attendue retourne '{0}' diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index c5460964c0e74..ad52acdfb2a71 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -316,8 +316,8 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - L'attività attesa restituisce '{0}' + Awaited task returns {0} + L'attività attesa restituisce '{0}' diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 026df47c15e52..eed95a489a5ec 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -316,8 +316,8 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - 待機中のタスクから '{0}' が返されました + Awaited task returns {0} + 待機中のタスクから '{0}' が返されました diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index b6ec80194a625..4eda65c250565 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -316,8 +316,8 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - 대기된 작업에서 '{0}'이(가) 반환됨 + Awaited task returns {0} + 대기된 작업에서 '{0}'이(가) 반환됨 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index fe636c03a1bfd..287d09f31fa45 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -316,8 +316,8 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - Zadanie, na które oczekiwano, zwraca wartość „{0}” + Awaited task returns {0} + Zadanie, na które oczekiwano, zwraca wartość „{0}” diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 12e82ca5c00cf..4a041aa156c3a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -316,8 +316,8 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - A tarefa esperada retorna '{0}' + Awaited task returns {0} + A tarefa esperada retorna '{0}' diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index a7cd0f65a109b..627d344efc177 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -316,8 +316,8 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - Ожидаемая задача возвращает "{0}". + Awaited task returns {0} + Ожидаемая задача возвращает "{0}". diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index d1c7bc4189fcd..0cb1b907774af 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -316,8 +316,8 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - Beklenen görev '{0}' döndürüyor + Awaited task returns {0} + Beklenen görev '{0}' döndürüyor diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 5fe80ca7476b4..d118816688549 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -316,8 +316,8 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - 等待任务返回“{0}” + Awaited task returns {0} + 等待任务返回“{0}” diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 82844a1ea7b82..321638773ebd0 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -316,8 +316,8 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma {Locked="ConfigureAwait"} "ConfigureAwait" is an api name and should not be localized. {0} is a placeholder for the language specific keyword 'false'. - Awaited task returns '{0}' - 等待的工作會傳回 '{0}' + Awaited task returns {0} + 等待的工作會傳回 '{0}' diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs index 4e9fb8859a909..ed0841b267a33 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs @@ -447,7 +447,7 @@ public async Task DoAsync() await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, clientCapabilities); var expectedLocation = testLspServer.GetLocations("caret").Single(); - var expectedMarkdown = @"Awaited task returns '`class System.String`' + var expectedMarkdown = $@"{string.Format(FeaturesResources.Awaited_task_returns_0, "`class System.String`")} "; var results = await RunGetHoverAsync( From 1fbb34761b53308ce4ff597870c822cdcff3de67 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 6 Nov 2023 16:59:29 -0800 Subject: [PATCH 3/3] Review feedback --- ...tocolConversions.MarkdownContentBuilder.cs | 63 +++++++++---------- .../Extensions/ProtocolConversions.cs | 2 +- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs index 3388ff424e644..b72b3e00097fa 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs @@ -5,50 +5,49 @@ using System; using Microsoft.CodeAnalysis.PooledObjects; -namespace Microsoft.CodeAnalysis.LanguageServer +namespace Microsoft.CodeAnalysis.LanguageServer; + +internal static partial class ProtocolConversions { - internal static partial class ProtocolConversions + private readonly ref struct MarkdownContentBuilder { - private readonly struct MarkdownContentBuilder : IDisposable + private readonly ArrayBuilder _linesBuilder; + + public MarkdownContentBuilder() { - private readonly ArrayBuilder _linesBuilder; + _linesBuilder = ArrayBuilder.GetInstance(); + } - public MarkdownContentBuilder() + public void Append(string text) + { + if (_linesBuilder.Count == 0) { - _linesBuilder = ArrayBuilder.GetInstance(); + _linesBuilder.Add(text); } - - public void Append(string text) + else { - if (_linesBuilder.Count == 0) - { - _linesBuilder.Add(text); - } - else - { - _linesBuilder[^1] = _linesBuilder[^1] + text; - } + _linesBuilder[^1] = _linesBuilder[^1] + text; } + } - public void AppendLine(string text = "") - { - _linesBuilder.Add(text); - } + public void AppendLine(string text = "") + { + _linesBuilder.Add(text); + } - public bool IsLineEmpty() - { - return _linesBuilder.Count == 0 ? true : string.IsNullOrEmpty(_linesBuilder[^1]); - } + public bool IsLineEmpty() + { + return _linesBuilder is [] or [.., ""]; + } - public string Build() - { - return string.Join(Environment.NewLine, _linesBuilder); - } + public string Build(string newLine) + { + return string.Join(newLine, _linesBuilder); + } - public void Dispose() - { - _linesBuilder.Free(); - } + public void Dispose() + { + _linesBuilder.Free(); } } } diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs index f6124bdd4df4e..3438aa66f04a1 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs @@ -899,7 +899,7 @@ public static LSP.MarkupContent GetDocumentationMarkupContent(ImmutableArray