diff --git a/src/IssueViz.UnitTests/Editor/IssueSpanCalculatorTests.cs b/src/IssueViz.UnitTests/Editor/IssueSpanCalculatorTests.cs index 8a0b32472..4cb0c891a 100644 --- a/src/IssueViz.UnitTests/Editor/IssueSpanCalculatorTests.cs +++ b/src/IssueViz.UnitTests/Editor/IssueSpanCalculatorTests.cs @@ -18,12 +18,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.Text; using Moq; -using SonarLint.VisualStudio.TestInfrastructure; using SonarLint.VisualStudio.IssueVisualization.Editor; +using SonarLint.VisualStudio.TestInfrastructure; using SonarQube.Client; namespace SonarLint.VisualStudio.IssueVisualization.UnitTests.Editor @@ -34,6 +32,8 @@ public class IssueSpanCalculatorTests private Mock checksumCalculatorMock; private IssueSpanCalculator testSubject; + private const int SnapshotLength = 10000; + private const int SnapshotLineCount = 10000; [TestInitialize] public void TestInitialize() @@ -331,6 +331,60 @@ public void CalculateSpan_TextRangeNull_ReturnsNull() checksumCalculatorMock.VerifyNoOtherCalls(); } + [TestMethod] + public void CalculateSpan_ForStartAndEndLines_GetsPositionOfCorrectLines() + { + var startLine = CreateLineMock(lineNumber: 66, startPos: 1, endPos: 2); + var endLine = CreateLineMock(lineNumber: 224, startPos: 13, endPos: 23); + var textSnapshotMock = MockTextSnapshotForLines(startLine, endLine); + + testSubject.CalculateSpan(textSnapshotMock.Object, startLine.LineNumber, endLine.LineNumber); + + textSnapshotMock.Verify(mock => mock.GetLineFromLineNumber(startLine.LineNumber - 1), Times.Once); + textSnapshotMock.Verify(mock => mock.GetLineFromLineNumber(endLine.LineNumber - 1), Times.Once); + } + + [TestMethod] + public void CalculateSpan_ForStartAndEndLines_ReturnsSnapshotSpanWithCorrectStartAndEnd() + { + var startLine = CreateLineMock(lineNumber:66, startPos:1, endPos:2); + var endLine = CreateLineMock(lineNumber:224, startPos:13, endPos:23); + var textSnapshotMock = MockTextSnapshotForLines(startLine, endLine); + + var snapshotSpan = testSubject.CalculateSpan(textSnapshotMock.Object, startLine.LineNumber, endLine.LineNumber); + + snapshotSpan.Start.Position.Should().Be(startLine.Start.Position); + snapshotSpan.End.Position.Should().Be(endLine.End.Position); + } + + [TestMethod] + [DataRow(0, 1)] + [DataRow(1, 0)] + [DataRow(SnapshotLineCount + 1, 1)] + [DataRow(1, SnapshotLineCount + 1)] + [DataRow(4, 1)] + public void CalculateSpan_ForInvalidStartAndEndLines_ThrowsException(int startLineNumber, int endLineNumber) + { + var startLine = CreateLineMock(lineNumber:startLineNumber, startPos:1, endPos:2); + var endLine = CreateLineMock(lineNumber:endLineNumber, startPos:13, endPos:23); + var textSnapshotMock = MockTextSnapshotForLines(startLine, endLine); + + Action act = () => testSubject.CalculateSpan(textSnapshotMock.Object, startLine.LineNumber, endLine.LineNumber); + + act.Should().Throw(); + } + + private static Mock MockTextSnapshotForLines(ITextSnapshotLine startLine, ITextSnapshotLine endLine) + { + var textSnapshot = new Mock(); + textSnapshot.SetupGet(x => x.LineCount).Returns(SnapshotLineCount); + textSnapshot.SetupGet(x => x.Length).Returns(SnapshotLength); + textSnapshot.Setup(x => x.GetLineFromLineNumber(startLine.LineNumber - 1)).Returns(startLine); + textSnapshot.Setup(x => x.GetLineFromLineNumber(endLine.LineNumber - 1)).Returns(endLine); + + return textSnapshot; + } + private class VSLineDescription { public int ZeroBasedLineNumber { get; set; } @@ -339,7 +393,7 @@ private class VSLineDescription public string Text { get; set; } } - private static Mock CreateSnapshotMock(int bufferLineCount = 1000, int snapShotLength = 10000, params VSLineDescription[] lines) + private static Mock CreateSnapshotMock(int bufferLineCount = 1000, int snapShotLength = SnapshotLength, params VSLineDescription[] lines) { var textSnapshotMock = new Mock(); @@ -378,5 +432,19 @@ private static ITextSnapshotLine CreateLineMock(ITextSnapshot textSnapshot, VSLi return startLineMock.Object; } + + private static ITextSnapshotLine CreateLineMock(int lineNumber, int startPos, int endPos) + { + var startLineMock = new Mock(); + var textSnapshot = new Mock(); + + textSnapshot.SetupGet(x => x.Length).Returns(() => endPos +1); + + startLineMock.SetupGet(x => x.LineNumber).Returns(() => lineNumber); + startLineMock.SetupGet(x => x.Start).Returns(() => new SnapshotPoint(textSnapshot.Object, startPos)); + startLineMock.SetupGet(x => x.End).Returns(() => new SnapshotPoint(textSnapshot.Object, endPos)); + + return startLineMock.Object; + } } } diff --git a/src/IssueViz.UnitTests/FixSuggestion/FixSuggestionHandlerTests.cs b/src/IssueViz.UnitTests/FixSuggestion/FixSuggestionHandlerTests.cs index 1f45b4abc..3b9928944 100644 --- a/src/IssueViz.UnitTests/FixSuggestion/FixSuggestionHandlerTests.cs +++ b/src/IssueViz.UnitTests/FixSuggestion/FixSuggestionHandlerTests.cs @@ -18,14 +18,46 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using NSubstitute.ExceptionExtensions; +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.IssueVisualization.Editor; using SonarLint.VisualStudio.IssueVisualization.FixSuggestion; +using SonarLint.VisualStudio.IssueVisualization.OpenInIde; +using SonarLint.VisualStudio.SLCore.Listener.FixSuggestion; +using SonarLint.VisualStudio.SLCore.Listener.FixSuggestion.Models; using SonarLint.VisualStudio.TestInfrastructure; +using FileEditDto = SonarLint.VisualStudio.SLCore.Listener.FixSuggestion.Models.FileEditDto; namespace SonarLint.VisualStudio.IssueVisualization.UnitTests.FixSuggestion; [TestClass] public class FixSuggestionHandlerTests { + private const string ConfigurationScopeRoot = @"C:\"; + + private FixSuggestionHandler testSubject; + private IThreadHandling threadHandling; + private ILogger logger; + private IDocumentNavigator documentNavigator; + private IIssueSpanCalculator issueSpanCalculator; + private IOpenInIdeConfigScopeValidator openInIdeConfigScopeValidator; + private IIDEWindowService ideWindowService; + + [TestInitialize] + public void TestInitialize() + { + threadHandling = new NoOpThreadHandler(); + logger = Substitute.For(); + documentNavigator = Substitute.For(); + issueSpanCalculator = Substitute.For(); + openInIdeConfigScopeValidator = Substitute.For(); + ideWindowService = Substitute.For(); + + testSubject = new FixSuggestionHandler(threadHandling, logger, documentNavigator, issueSpanCalculator, openInIdeConfigScopeValidator, ideWindowService); + } + [TestMethod] public void MefCtor_CheckIsSingleton() { @@ -33,10 +65,199 @@ public void MefCtor_CheckIsSingleton() } [TestMethod] - public void ApplyFixSuggestion_ThrowsNotImplementedException() + public void ApplyFixSuggestion_RunsOnUIThread() + { + MockConfigScopeRoot(); + var threadHandlingMock = Substitute.For(); + var fixSuggestionHandler = new FixSuggestionHandler(threadHandlingMock, logger, documentNavigator, issueSpanCalculator, openInIdeConfigScopeValidator, ideWindowService); + + fixSuggestionHandler.ApplyFixSuggestion(CreateFixSuggestionParams()); + + threadHandlingMock.ReceivedWithAnyArgs().RunOnUIThread(default); + } + + [TestMethod] + public void ApplyFixSuggestion_OneChange_AppliesChange() + { + var suggestionParams = CreateFixSuggestionParams(); + var suggestedChange = suggestionParams.fixSuggestion.fileEdit.changes[0]; + MockCalculateSpan(suggestedChange); + var textView = MockOpenFile(); + var edit = Substitute.For(); + textView.TextBuffer.CreateEdit().Returns(edit); + MockConfigScopeRoot(); + + testSubject.ApplyFixSuggestion(suggestionParams); + + Received.InOrder(() => + { + logger.WriteLine(FixSuggestionResources.ProcessingRequest, suggestionParams.configurationScopeId, suggestionParams.fixSuggestion.suggestionId); + documentNavigator.Open(@"C:\myFile.cs"); + textView.TextBuffer.CreateEdit(); + issueSpanCalculator.CalculateSpan(Arg.Any(), suggestedChange.beforeLineRange.startLine, suggestedChange.beforeLineRange.endLine); + edit.Replace(Arg.Any(), suggestedChange.after); + edit.Apply(); + logger.WriteLine(FixSuggestionResources.DoneProcessingRequest, suggestionParams.configurationScopeId, suggestionParams.fixSuggestion.suggestionId); + }); + } + + /// + /// The changes are applied from bottom to top to avoid changing the line numbers + /// of the changes that are below the current change. + /// + /// This is important when the change is more lines than the original line range. + /// + [TestMethod] + public void ApplyFixSuggestion_WhenMoreThanOneFixes_ApplyThemFromBottomToTop() + { + MockConfigScopeRoot(); + MockOpenFile(); + List changes = [CreateChangesDto(1, 1), CreateChangesDto(3, 3)]; + var suggestionParams = CreateFixSuggestionParams(changes: changes); + + testSubject.ApplyFixSuggestion(suggestionParams); + + Received.InOrder(() => + { + issueSpanCalculator.CalculateSpan(Arg.Any(), 3, 3); + issueSpanCalculator.CalculateSpan(Arg.Any(), 1, 1); + }); + } + + [TestMethod] + public void ApplyFixSuggestion_WhenApplyingChange_BringWindowToFront() + { + var suggestionParams = CreateFixSuggestionParams(); + MockConfigScopeRoot(); + + testSubject.ApplyFixSuggestion(suggestionParams); + + ideWindowService.Received().BringToFront(); + } + + [TestMethod] + public void ApplyFixSuggestion_WhenApplyingChange_BringFocusToFirstChangedLines() + { + List changes = [CreateChangesDto(1, 1), CreateChangesDto(3, 3)]; + var suggestionParams = CreateFixSuggestionParams(changes: changes); + var firstSuggestedChange = suggestionParams.fixSuggestion.fileEdit.changes[0]; + var firstAffectedSnapshot = MockCalculateSpan(firstSuggestedChange); + var textView = MockOpenFile(); + MockConfigScopeRoot(); + + testSubject.ApplyFixSuggestion(suggestionParams); + + textView.ViewScroller.ReceivedWithAnyArgs(1).EnsureSpanVisible(Arg.Any(), default); + textView.ViewScroller.Received().EnsureSpanVisible(firstAffectedSnapshot, EnsureSpanVisibleOptions.AlwaysCenter); + } + + [TestMethod] + public void ApplyFixSuggestion_Throws_Logs() { - var testSubject = new FixSuggestionHandler(); + var suggestionParams = CreateFixSuggestionParams(); + var exceptionMsg = "error"; + MockConfigScopeRoot(); + issueSpanCalculator.CalculateSpan(Arg.Any(), Arg.Any(), Arg.Any()).Throws(new Exception(exceptionMsg)); - Exceptions.Expect(() => testSubject.ApplyFixSuggestion()); + testSubject.ApplyFixSuggestion(suggestionParams); + + Received.InOrder(() => + { + logger.WriteLine(FixSuggestionResources.ProcessingRequest, suggestionParams.configurationScopeId, suggestionParams.fixSuggestion.suggestionId); + logger.WriteLine(FixSuggestionResources.ProcessingRequestFailed, suggestionParams.configurationScopeId, suggestionParams.fixSuggestion.suggestionId, exceptionMsg); + }); + logger.DidNotReceive().WriteLine(FixSuggestionResources.DoneProcessingRequest, suggestionParams.configurationScopeId, suggestionParams.fixSuggestion.suggestionId); + } + + [TestMethod] + public void ApplyFixSuggestion_WhenConfigRootScopeNotFound_ShouldLogFailure() + { + MockFailedConfigScopeRoot("Scope not found"); + var suggestionParams = CreateFixSuggestionParams("SpecificConfigScopeId"); + + testSubject.ApplyFixSuggestion(suggestionParams); + + logger.Received().WriteLine(FixSuggestionResources.GetConfigScopeRootPathFailed, "SpecificConfigScopeId", "Scope not found"); + } + + [TestMethod] + public void ApplyFixSuggestion_WhenLineNumbersDoNotMatch_ShouldLogFailure() + { + FailWhenApplyingEdit(out var suggestionWithWrongLineNumbers, "Line numbers do not match"); + + testSubject.ApplyFixSuggestion(suggestionWithWrongLineNumbers); + + logger.Received().WriteLine(FixSuggestionResources.ProcessingRequestFailed, "AScopeId", "key", "Line numbers do not match"); + } + + [TestMethod] + public void ApplyFixSuggestion_WhenApplyingChangeAndExceptionIsThrown_ShouldCancelEdit() + { + var edit = FailWhenApplyingEdit(out var suggestionWithWrongLineNumbers); + + testSubject.ApplyFixSuggestion(suggestionWithWrongLineNumbers); + + edit.DidNotReceiveWithAnyArgs().Replace(default, default); + edit.Received().Cancel(); + } + + private static ShowFixSuggestionParams CreateFixSuggestionParams(string scopeId = "scopeId", string suggestionKey = "suggestionKey", string idePath = @"myFile.cs", List changes = null) + { + changes ??= [CreateChangesDto()]; + var fixSuggestion = new FixSuggestionDto(suggestionKey, "refactor", new FileEditDto(idePath, changes)); + var suggestionParams = new ShowFixSuggestionParams(scopeId, "key", fixSuggestion); + return suggestionParams; + } + + private static ChangesDto CreateChangesDto(int startLine = 1, int endLine = 2) + { + return new ChangesDto(new LineRangeDto(startLine, endLine), "var a=1;", ""); + } + + private void MockConfigScopeRoot() + { + openInIdeConfigScopeValidator.TryGetConfigurationScopeRoot(Arg.Any(), out Arg.Any(), out Arg.Any()).Returns( + x => + { + x[1] = ConfigurationScopeRoot; + return true; + }); + } + + private void MockFailedConfigScopeRoot(string failureReason) + { + openInIdeConfigScopeValidator.TryGetConfigurationScopeRoot(Arg.Any(), out Arg.Any(), out Arg.Any()).Returns( + x => + { + x[2] = failureReason; + return false; + }); + } + + private ITextView MockOpenFile() + { + var textView = Substitute.For(); + documentNavigator.Open(Arg.Any()).Returns(textView); + return textView; + } + + private SnapshotSpan MockCalculateSpan(ChangesDto suggestedChange) + { + var affectedSnapshot = new SnapshotSpan(); + issueSpanCalculator.CalculateSpan(Arg.Any(), suggestedChange.beforeLineRange.startLine, suggestedChange.beforeLineRange.endLine) + .Returns(affectedSnapshot); + return affectedSnapshot; + } + + private ITextEdit FailWhenApplyingEdit(out ShowFixSuggestionParams suggestionWithWrongLineNumbers, string reason = "") + { + MockConfigScopeRoot(); + var edit = Substitute.For(); + var textView = MockOpenFile(); + textView.TextBuffer.CreateEdit().Returns(edit); + suggestionWithWrongLineNumbers = CreateFixSuggestionParams(scopeId: "AScopeId", suggestionKey: "key"); + issueSpanCalculator.CalculateSpan(Arg.Any(), Arg.Any(), Arg.Any()) + .Throws(new Exception(reason)); + return edit; } } diff --git a/src/IssueViz/Editor/IIssueSpanCalculator.cs b/src/IssueViz/Editor/IIssueSpanCalculator.cs index cf1a6d4e6..d81070b7e 100644 --- a/src/IssueViz/Editor/IIssueSpanCalculator.cs +++ b/src/IssueViz/Editor/IIssueSpanCalculator.cs @@ -18,7 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; using System.ComponentModel.Composition; using Microsoft.VisualStudio.Text; using SonarLint.VisualStudio.Core.Analysis; @@ -34,6 +33,8 @@ public interface IIssueSpanCalculator /// Returns null if no textRange is passed /// SnapshotSpan? CalculateSpan(ITextRange range, ITextSnapshot currentSnapshot); + + SnapshotSpan CalculateSpan(ITextSnapshot snapshot, int startLine, int endLine); } [Export(typeof(IIssueSpanCalculator))] @@ -105,6 +106,18 @@ internal IssueSpanCalculator(IChecksumCalculator checksumCalculator) return snapshotSpan; } + public SnapshotSpan CalculateSpan(ITextSnapshot snapshot, int startLine, int endLine) + { + if (startLine < 1 || endLine < 1 || startLine > snapshot.LineCount || endLine > snapshot.LineCount || startLine > endLine) + { + throw new ArgumentOutOfRangeException(nameof(startLine), nameof(endLine)); + } + var startPosition = snapshot.GetLineFromLineNumber(startLine - 1).Start.Position; + var endPosition = snapshot.GetLineFromLineNumber(endLine - 1).End.Position; + var span = Span.FromBounds(startPosition, endPosition); + return new SnapshotSpan(snapshot, span); + } + private static bool RangeHasHash(ITextRange range) => !string.IsNullOrEmpty(range.LineHash); diff --git a/src/IssueViz/FixSuggestion/FixSuggestionHandler.cs b/src/IssueViz/FixSuggestion/FixSuggestionHandler.cs index 7a5553398..bb5b2232c 100644 --- a/src/IssueViz/FixSuggestion/FixSuggestionHandler.cs +++ b/src/IssueViz/FixSuggestion/FixSuggestionHandler.cs @@ -19,6 +19,14 @@ */ using System.ComponentModel.Composition; +using System.IO; +using Microsoft.VisualStudio.Text.Editor; +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.Infrastructure.VS; +using SonarLint.VisualStudio.IssueVisualization.Editor; +using SonarLint.VisualStudio.IssueVisualization.OpenInIde; +using SonarLint.VisualStudio.SLCore.Listener.FixSuggestion; +using SonarLint.VisualStudio.SLCore.Listener.FixSuggestion.Models; namespace SonarLint.VisualStudio.IssueVisualization.FixSuggestion; @@ -26,8 +34,89 @@ namespace SonarLint.VisualStudio.IssueVisualization.FixSuggestion; [PartCreationPolicy(CreationPolicy.Shared)] public class FixSuggestionHandler : IFixSuggestionHandler { - public void ApplyFixSuggestion() + private readonly IOpenInIdeConfigScopeValidator openInIdeConfigScopeValidator; + private readonly IIDEWindowService ideWindowService; + private readonly IThreadHandling threadHandling; + private readonly ILogger logger; + private readonly IDocumentNavigator documentNavigator; + private readonly IIssueSpanCalculator issueSpanCalculator; + + [ImportingConstructor] + internal FixSuggestionHandler(ILogger logger, IDocumentNavigator documentNavigator, IIssueSpanCalculator issueSpanCalculator, IOpenInIdeConfigScopeValidator openInIdeConfigScopeValidator, IIDEWindowService ideWindowService) : + this(ThreadHandling.Instance, logger, documentNavigator, issueSpanCalculator, openInIdeConfigScopeValidator, ideWindowService) + { + } + + internal FixSuggestionHandler( + IThreadHandling threadHandling, + ILogger logger, + IDocumentNavigator documentNavigator, + IIssueSpanCalculator issueSpanCalculator, + IOpenInIdeConfigScopeValidator openInIdeConfigScopeValidator, + IIDEWindowService ideWindowService) + { + this.threadHandling = threadHandling; + this.logger = logger; + this.documentNavigator = documentNavigator; + this.issueSpanCalculator = issueSpanCalculator; + this.openInIdeConfigScopeValidator = openInIdeConfigScopeValidator; + this.ideWindowService = ideWindowService; + } + + public void ApplyFixSuggestion(ShowFixSuggestionParams parameters) + { + if (!ValidateConfiguration(parameters.configurationScopeId, out var configurationScopeRoot, out var failureReason)) + { + logger.WriteLine(FixSuggestionResources.GetConfigScopeRootPathFailed, parameters.configurationScopeId, failureReason); + return; + } + + try + { + logger.WriteLine(FixSuggestionResources.ProcessingRequest, parameters.configurationScopeId, parameters.fixSuggestion.suggestionId); + + var absoluteFilePath = Path.Combine(configurationScopeRoot, parameters.fixSuggestion.fileEdit.idePath); + threadHandling.RunOnUIThread(() => ApplySuggestedChanges(absoluteFilePath, parameters.fixSuggestion.fileEdit.changes)); + + logger.WriteLine(FixSuggestionResources.DoneProcessingRequest, parameters.configurationScopeId, parameters.fixSuggestion.suggestionId); + } + catch (Exception exception) when (!ErrorHandler.IsCriticalException(exception)) + { + logger.WriteLine(FixSuggestionResources.ProcessingRequestFailed, parameters.configurationScopeId, parameters.fixSuggestion.suggestionId, exception.Message); + } + } + + private bool ValidateConfiguration(string configurationScopeId, out string configurationScopeRoot, out string failureReason) + { + return openInIdeConfigScopeValidator.TryGetConfigurationScopeRoot(configurationScopeId, out configurationScopeRoot, out failureReason); + } + + private void ApplySuggestedChanges(string absoluteFilePath, List changes) { - throw new NotImplementedException(); + ideWindowService.BringToFront(); + var textView = documentNavigator.Open(absoluteFilePath); + var textEdit = textView.TextBuffer.CreateEdit(); + for (var i = changes.Count - 1; i >= 0; i--) + { + var changeDto = changes[i]; + + try + { + var spanToUpdate = issueSpanCalculator.CalculateSpan(textView.TextSnapshot, changeDto.beforeLineRange.startLine, changeDto.beforeLineRange.endLine); + if (i == 0) + { + textView.Caret.MoveTo(spanToUpdate.Start); + textView.ViewScroller.EnsureSpanVisible(spanToUpdate, EnsureSpanVisibleOptions.AlwaysCenter); + } + textEdit.Replace(spanToUpdate, changeDto.after); + } + catch (Exception) + { + textEdit.Cancel(); + throw; + } + } + + textEdit.Apply(); } } diff --git a/src/IssueViz/FixSuggestion/FixSuggestionResources.Designer.cs b/src/IssueViz/FixSuggestion/FixSuggestionResources.Designer.cs new file mode 100644 index 000000000..c17207be0 --- /dev/null +++ b/src/IssueViz/FixSuggestion/FixSuggestionResources.Designer.cs @@ -0,0 +1,98 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SonarLint.VisualStudio.IssueVisualization.FixSuggestion { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class FixSuggestionResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal FixSuggestionResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SonarLint.VisualStudio.IssueVisualization.FixSuggestion.FixSuggestionResources", typeof(FixSuggestionResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to [Fix Suggestion in IDE] Done processing request. Configuration scope: {0}, SuggestionId: {1}. + /// + internal static string DoneProcessingRequest { + get { + return ResourceManager.GetString("DoneProcessingRequest", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [Fix Suggestion in IDE] Could not determine configuration scope root path for scope [{0}] due to: {1}. + /// + internal static string GetConfigScopeRootPathFailed { + get { + return ResourceManager.GetString("GetConfigScopeRootPathFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [Fix Suggestion in IDE] Processing request. Configuration scope: {0}, SuggestionId: {1}. + /// + internal static string ProcessingRequest { + get { + return ResourceManager.GetString("ProcessingRequest", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [Fix Suggestion in IDE] Fail to process request for configuration scope: {0}, SuggestionId: {1} due to {2}.. + /// + internal static string ProcessingRequestFailed { + get { + return ResourceManager.GetString("ProcessingRequestFailed", resourceCulture); + } + } + } +} diff --git a/src/IssueViz/FixSuggestion/FixSuggestionResources.resx b/src/IssueViz/FixSuggestion/FixSuggestionResources.resx new file mode 100644 index 000000000..0d2089283 --- /dev/null +++ b/src/IssueViz/FixSuggestion/FixSuggestionResources.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + [Fix Suggestion in IDE] Done processing request. Configuration scope: {0}, SuggestionId: {1} + + + [Fix Suggestion in IDE] Processing request. Configuration scope: {0}, SuggestionId: {1} + + + [Fix Suggestion in IDE] Fail to process request for configuration scope: {0}, SuggestionId: {1} due to {2}. + + + [Fix Suggestion in IDE] Could not determine configuration scope root path for scope [{0}] due to: {1} + + \ No newline at end of file diff --git a/src/IssueViz/FixSuggestion/IFixSuggestionHandler.cs b/src/IssueViz/FixSuggestion/IFixSuggestionHandler.cs index 68ff84548..1c332843d 100644 --- a/src/IssueViz/FixSuggestion/IFixSuggestionHandler.cs +++ b/src/IssueViz/FixSuggestion/IFixSuggestionHandler.cs @@ -18,9 +18,11 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using SonarLint.VisualStudio.SLCore.Listener.FixSuggestion; + namespace SonarLint.VisualStudio.IssueVisualization.FixSuggestion; public interface IFixSuggestionHandler { - void ApplyFixSuggestion(); + void ApplyFixSuggestion(ShowFixSuggestionParams parameters); } diff --git a/src/IssueViz/IssueViz.csproj b/src/IssueViz/IssueViz.csproj index 2a27e1cca..5a20da8e8 100644 --- a/src/IssueViz/IssueViz.csproj +++ b/src/IssueViz/IssueViz.csproj @@ -53,12 +53,23 @@ Never + + True + True + FixSuggestionResources.resx + + True True Resources.resx + + ResXFileCodeGenerator + FixSuggestionResources.Designer.cs + + ResXFileCodeGenerator Resources.Designer.cs diff --git a/src/SLCore.Listeners.UnitTests/Implementation/ShowFixSuggestionListenerTests.cs b/src/SLCore.Listeners.UnitTests/Implementation/ShowFixSuggestionListenerTests.cs index d730ffb6f..23b8c5ecc 100644 --- a/src/SLCore.Listeners.UnitTests/Implementation/ShowFixSuggestionListenerTests.cs +++ b/src/SLCore.Listeners.UnitTests/Implementation/ShowFixSuggestionListenerTests.cs @@ -65,6 +65,6 @@ public void ShowFixSuggestion_CallsHandler() testSubject.ShowFixSuggestion(parameters); - fixSuggestionHandler.Received(1).ApplyFixSuggestion(); + fixSuggestionHandler.Received(1).ApplyFixSuggestion(parameters); } } diff --git a/src/SLCore.Listeners/Implementation/ShowFixSuggestionListener.cs b/src/SLCore.Listeners/Implementation/ShowFixSuggestionListener.cs index 09c75a151..0a04d3ad1 100644 --- a/src/SLCore.Listeners/Implementation/ShowFixSuggestionListener.cs +++ b/src/SLCore.Listeners/Implementation/ShowFixSuggestionListener.cs @@ -39,6 +39,6 @@ public ShowFixSuggestionListener(IFixSuggestionHandler fixSuggestionHandler) public void ShowFixSuggestion(ShowFixSuggestionParams parameters) { - fixSuggestionHandler.ApplyFixSuggestion(); + fixSuggestionHandler.ApplyFixSuggestion(parameters); } }