Skip to content
This repository has been archived by the owner on May 4, 2023. It is now read-only.

Commit

Permalink
Merge pull request #5 from codiga/rosie-text-highlights-and-fixes
Browse files Browse the repository at this point in the history
Rosie text highlights and fixes
  • Loading branch information
dastrong-codiga authored Dec 1, 2022
2 parents 0c955ae + 5fc45fd commit 977cf37
Show file tree
Hide file tree
Showing 28 changed files with 1,458 additions and 130 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ jobs:
run: msbuild Extension.sln /restore /v:m /property:Configuration=${{ inputs.config }}

- name: Install NUnit.ConsoleRunner
run: nuget install NUnit.ConsoleRunner -Version 3.13.0 -DirectDownload -OutputDirectory .
run: nuget install NUnit.ConsoleRunner -Version 3.16.0 -DirectDownload -OutputDirectory .

- name: Run UnitTests
run: ./NUnit.ConsoleRunner.3.13.0/tools/nunit3-console.exe src\Tests\bin\${{ inputs.config }}\net48\Tests.dll
run: ./NUnit.ConsoleRunner.3.16.0/tools/nunit3-console.exe src\Tests\bin\${{ inputs.config }}\net48\Tests.dll

- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action/composite@v2
Expand Down
122 changes: 112 additions & 10 deletions DEVELOPMENT.md

Large diffs are not rendered by default.

Binary file modified images/project-structure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions images/tagging-flow-during-editing.drawio.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" modified="2022-11-30T10:01:00.434Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" etag="lvogCwi11RcuYZyPfP5i" version="20.0.3"><diagram id="EbkYADztwSF3Mh8ZUwno" name="Page-1">7Vrdd6I4FP9rfKwHiIg+qu1s55zOObNjd2f6GCFCdiOhIVSdv34TiHxItHQKutp5gtx8Xe7H797c0AOz1eYPBqPgC/UQ6VmGt+mB255lmbZpiIekbDOKMzAzgs+wpwYVhDn+iRRRzfMT7KG4MpBTSjiOqkSXhiFyeYUGGaPr6rAlJdVdI+ijGmHuQlKnfsceDzKqBcC46LhH2A/U1gAYivMV3I1WhDiAHl2XSOCuB2aMUp69rTYzRKT0doLJ5n060JtzxlDIm0z4ktxMoj+jkeXhjbPZjKPZ5OHGtLJlXiBJ1Ccrbvl2JwNGk9BDchWjB6brAHM0j6Are9dC64IW8BURLVO8LmnIP8EVJlLh94i8II5dKDrUPohxtDn4BWYuF2FRiK4QZ1sxRE0AO6tQxmQC1V4XqjFtRQtKWsmJUJmDn69dCEy8KJm9QX7jCxKfY1XFl1tmSXwDoBPfuCvxjS5YfObZpWfXhIU8gV6qSRkPqE9DSO4K6rQqzmLMA6WREuI/iPOtgmKYcFoVMdpg/qP0/iSX6tuqdbtRK6eNrWq8ohbJ9XGliI+kCXPRMSRT0QEyH/Ej4yy9khkikOOXKh+ta8ys2fsMEhL3pGkNCVeikoEKqo8dPicyQEwfEhd7UPTMaBhTocq8R7z58vn5EW34NFkuEet/pTGfBTD0habVwoLfbO1stBT7C0r3EuM8gpjkPWyFk280xuhvTKVEafgIfV+ufoCNg+5uNnB3TMiMEsrSuUB8+tB1BT3mjP6LSj2eM14YRjs4sA8DjiYIjTU44HQFA+bgnDhQ+P5TqUePA0LCbFuaJJtP5b5iWto6JX5YDfFjeE78qGdr39BzgmIepzmwh+SmQovbGMcX6FoA2FXfGmp8y9D41qAr3wKXEmIPuZb5FteqQftZIvQBGzmNhw00GenxeFjgUS0Qfoc49cwlldGVB9I9kzgNtULn0qyERajlF2w3i28jHPr5NCh3JQjG8mkbxipuGkul1h7gQhzJK+YFCfZD8e4KDQpewFT6rOCfTFTHCnteZskoxj/hIl1PGktEcchTidvTnn3b2Hw0oKAO7GrxXh5Vy4Z12CMPIsiN0RfBV6FIY4tRy32Vn1caQpfLWFjqvknlu74jZF9M6l7gSiVgFyBzxpA9bAooht5m3mYfE8bgtjRAOUPdfJQpDsZ7x217rz7z2nhlxoXpZRy0aohDzYHEDZA2nRDyihPCryGx0JU+TptYWKMPhQAdZxZGUyRwzoEEjnnUs18d7xi9zoFgt2UJCT6HL8Ij2ylOPEI/blyQoMvTlyEqOVocwXDHR0ZZB+kWhkAvdIH4Z9eKFnYd/xwN/g07K1qANhPt2X6sapYgC4Hy7nJjCb9xhsRmTcEhDdGeNexIv5pSv+OcrbtI0YVD0Jk51BORvyIPcqlVIvdfJGQhN3alL9dTkP+/Jlu+jRgN9zy64VVYZxrMrzk/RhWy44TGaZrQjN+Z0LzPax3N8aG9+wzoT3yfIR9yGZuHcCVDKdGG9rw7ndqfQi5OMV4l6zAORIU2Lz3uhaOlcDXJcGqeKfpq7j6cgdWvho5BPZEAI7uOO53dwFvOb9xpD3fGDXHHss6KO/XfLtrDnSOO3J8n4hARc+Qp8mtHmitwcFNTKjmxh9dvzfPckItTXVos//D54OgXK1zdJYRnrXFfGTBneNsEmA/cj5zoftr6nRDqy0/z5wT7PkHX9jdMPWBYmkv7EweMem2p5fTggFr7TcqcV6BioKkXtaVi0Sz+ic6q28Wv5eDuPw==</diagram></mxfile>
Binary file added images/tagging-flow-during-editing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions images/tagging-flow-initial.drawio.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" modified="2022-11-30T09:28:08.487Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" etag="_5ULwbQeA7Yn275ZxKg2" version="20.0.3"><diagram id="_s4aj7_37aYGRvY7WVtE" name="Page-1">7Vpbd9o4EP41nNM+hINtMPAYSNL2bNuTs8lmk0dhC6NWSK4sbv31HdnyDdvESQBDdp9sje4z+r6RRmpZ4/n6k0D+7Bt3MW2ZHXfdsq5apmn0Bn34KMkmkvS7RiTwBHF1oVRwR35jLexo6YK4OMgVlJxTSfy80OGMYUfmZEgIvsoXm3Ka79VHHi4I7hxEi9J/iStnkdS0rGGa8RkTb6a7tqyOHvkcxaW1IJghl68yIuu6ZY0F5zL6m6/HmCrtxYqJ6t1U5CYjE5jJOhX+efxrc/F0697P1vTHZoSv7r9/vzC1fQK5iaeMXdCATnIhZ9zjDNHrVDoSfMFcrJrtQCot85VzH4QGCH9gKTfanGghOYhmck51Ll4T+Zj5f1JNtXs6dbXWLYeJTZxgUmwes4lMLZVMq4WpuN6UM3mD5oQqwdeFQ1wE8x1zFvBwMtH81aQr9apFAV8IB+9QZrxAkfCw3KV0OzE/AAfzOYYBQ0WBKZJkmR8I0ivYS8qlNoYfbeYXmFy3u0R0oXtqmTaF8Y58+PFkqJNIoHSnUIP0tO1fC7VaRwU1JjlxC3/zgOAHwtWEOLtHnofFreBLQLOIW5+I7f5gPlGXkdgRGElAPyihus5+x1gxEmV9wjyKJWftTJmMxrZQlGJELfLVjEh850djXAFV5vEwJZSOOeUirGtNp9h2HJAHUvCfOJPj9oeTTifpb4mFxOvd67a4zHSFga2JaRNTrk6vMkTX1bJZhuN6nUOtTPPsyOii0+7YLyOkMHWLBQGlYXF8ljJrstSwSZIyK0lK4z1dIzGsJ8j56YWr4cKJEHOpemFEEkQj2CcE8Ha6uPu1IB4QwiupLRYHPmJvnk09QvyM6RJL4qBi1VpEWzGDSnscUt/1xvQSFcAXzRUts0mgPqV0X9YXiCMj7sURPEMDp+Eo+iWOYnhMR2GfnZ843U2rVdMdVKyR47iDszmlnKDhzCYNZz3nx1/C0Q93qQsEoyl/VUHOXx4IXm275s4buv52fRNGGeY+Z2CpoNIpvDv2t/tb7G8V2d/olbC/fSj2N3sli+r49HBEqA/rcrTVJNaHBbO028nuKd1P+jCWKRfz0GCL6bRgO1irMq9qgLvH4N8Bzakj00itaMApvdQZc+K6kVVxQH6jSdiUsqvPiUIrtNsbtXpXqi0wZBDZ1ChghwG6t4AWi2pZ+01IM6zBFtR6JVAzS6DWPdiJvNGdVupsn7K+9lx3WkbtrVa3SRgb1T7bz3tiwKI+NvJl2dlXIQfylOudc6H+Q2iDquDAFBTLExadQONiE2CH3FnvnMNtRs/aQne3V0S3dcxjVHIzcQLoNmqiOwX0UzbvBNDdrYvufqPo7j6P7jGiNLu53leIPR/DaX/CEv6CDx+fj7CcNfDtLbfeLXr15NbwOLg3Cmr8H/evxn2/Lu4HjeK+XwP3aRiYueFxl1LsyBJPfYDLtwoSUKNZEhSOaS+U9AX6uvQ8gT0k1dZEh31pcVAyHFSU/UwkPBc6niirwuGEEuYdgEX/4+xpGDXps38w+mz0mvK90eegLn02eh8ZD7MefR5w47SDJicowIq2+X6I8gUM/T6Zxix5EFHKNAcLv5hWo0yT4Zn6F115pjFOiGnOI4pqFMOoBaa5IoFP0UaBXs5UXCXQBKEkH6LNW/JMUhPCTMVoPr5brNqNY3XQJFZfdynd7sCx4LweL9k1Qdzs6yW7BoYXIjofEBZIxCQJ3Wz4zGf7tOXoeEz8+mdv3n1MCVhAHSUuGeMyHMH7P1EkVyi7Hj6WXbO8IiADyfSFd5iXeShvXf8B</diagram></mxfile>
Binary file added images/tagging-flow-initial.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 13 additions & 1 deletion src/Extension/Extension.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,21 @@
<Compile Include="Caching\SnippetCache.cs" />
<Compile Include="Caching\TextViewCreationListener.cs" />
<Compile Include="Logging\ExtensionLogger.cs" />
<Compile Include="Rosie\Annotation\RosieViolationSquiggleTag.cs" />
<Compile Include="Rosie\Annotation\RosieViolationSquiggleTaggerProvider.cs" />
<Compile Include="Rosie\Annotation\RosieViolationTag.cs" />
<Compile Include="Rosie\Annotation\RosieViolationSquiggleTagger.cs" />
<Compile Include="Rosie\Annotation\RosieViolationTagger.cs" />
<Compile Include="Rosie\Annotation\RosieViolationTaggerProvider.cs" />
<Compile Include="Rosie\Annotation\StringUtils.cs" />
<Compile Include="Rosie\RosieClientProvider.cs" />
<Compile Include="Rosie\CodigaCodeAnalysisConfig.cs" />
<Compile Include="Rosie\CodigaConfigFileUtil.cs" />
<Compile Include="Rosie\Annotation\ApplyRosieFixSuggestedAction.cs" />
<Compile Include="Rosie\Annotation\DisableRosieAnalysisSuggestedAction.cs" />
<Compile Include="Rosie\Annotation\OpenOnCodigaHubSuggestedAction.cs" />
<Compile Include="Rosie\Annotation\RosieHighlightActionsSourceProvider.cs" />
<Compile Include="Rosie\Model\RosieAnnotation.cs" />
<Compile Include="Rosie\Model\RosieAnnotationJetBrains.cs" />
<Compile Include="Rosie\Model\RosiePosition.cs" />
<Compile Include="Rosie\Model\RosieRequest.cs" />
<Compile Include="Rosie\Model\RosieResponse.cs" />
Expand All @@ -82,8 +92,10 @@
<Compile Include="Rosie\Model\RosieViolationFixEdit.cs" />
<Compile Include="Rosie\IRosieClient.cs" />
<Compile Include="Rosie\RosieClient.cs" />
<Compile Include="Rosie\RosieEditTypes.cs" />
<Compile Include="Rosie\RosieRulesCache.cs" />
<Compile Include="Rosie\RosieRulesCacheValue.cs" />
<Compile Include="Rosie\RosieSeverities.cs" />
<Compile Include="Rosie\RosieUtils.cs" />
<Compile Include="SnippetSearch\Preview\PreviewClassifier.cs" />
<Compile Include="SnippetSearch\Preview\PreviewClassifierFormat.cs" />
Expand Down
37 changes: 4 additions & 33 deletions src/Extension/ExtensionPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
using Extension.Rosie;
using Task = System.Threading.Tasks.Task;
using Extension.SnippetSearch;
using Microsoft.VisualStudio.Shell.Events;
using Microsoft.VisualStudio;
using System.Threading.Tasks;
using SolutionEvents = Microsoft.VisualStudio.Shell.Events.SolutionEvents;

namespace Extension
{
Expand All @@ -29,8 +28,6 @@ public sealed class ExtensionPackage : AsyncPackage
/// </summary>
public const string PackageGuidString = "e8d2d8f8-96dc-4c92-bb81-346b4d2318e4";

private CancellationToken _cancellationToken;

/// <summary>
/// Initializes a new instance of the <see cref="ExtensionPackage"/> class.
/// </summary>
Expand All @@ -49,34 +46,15 @@ public ExtensionPackage()
/// <returns>A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method.</returns>
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
{
_cancellationToken = cancellationToken;
var isSolutionLoaded = await IsSolutionLoadedAsync();

if (isSolutionLoaded)
InitializeRulesCache();

//Inits the cache only after a solution is loaded completely
SolutionEvents.OnAfterBackgroundSolutionLoadComplete += InitializeRulesCache;
SolutionEvents.OnAfterCloseSolution += DisposeRulesCache;
SolutionEvents.OnAfterCloseSolution += CleanupCachesAndServices;

// When initialized asynchronously, the current thread may be a background thread at this point.
// Do any initialization that requires the UI thread after switching to the UI thread.
await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
await SnippetSearchMenuCommand.InitializeAsync(this);
}

//See https://github.com/madskristensen/SolutionLoadSample
private async Task<bool> IsSolutionLoadedAsync()
{
await JoinableTaskFactory.SwitchToMainThreadAsync();
var vsSolution = await GetServiceAsync(typeof(SVsSolution)) as IVsSolution;

ErrorHandler.ThrowOnFailure(vsSolution.GetProperty((int)__VSPROPID.VSPROPID_IsSolutionOpen, out object value));

return value is bool isSolutionOpen && isSolutionOpen;
}

public override IVsAsyncToolWindowFactory GetAsyncToolWindowFactory(Guid toolWindowType)
public override IVsAsyncToolWindowFactory GetAsyncToolWindowFactory(Guid toolWindowType)
{
ThreadHelper.ThrowIfNotOnUIThread();
if (toolWindowType == typeof(SnippetSearch.SearchWindow).GUID)
Expand All @@ -97,14 +75,7 @@ protected override string GetToolWindowTitle(Type toolWindowType, int id)
return base.GetToolWindowTitle(toolWindowType, id);
}

private async void InitializeRulesCache(object sender = null, EventArgs e = null)
{
//Switching back to main thread due to RosieRulesCache.StartPolling()
await JoinableTaskFactory.SwitchToMainThreadAsync(_cancellationToken);
RosieRulesCache.Initialize();
}

private static void DisposeRulesCache(object sender, EventArgs e)
private static void CleanupCachesAndServices(object sender, EventArgs e)
{
RosieRulesCache.Dispose();
}
Expand Down
144 changes: 144 additions & 0 deletions src/Extension/Rosie/Annotation/ApplyRosieFixSuggestedAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Imaging.Interop;
using Microsoft.VisualStudio.Language.Intellisense;
using Extension.Rosie.Model;
using Microsoft.VisualStudio.Text;
using Span = Microsoft.VisualStudio.Text.Span;

namespace Extension.Rosie.Annotation
{
/// <summary>
/// Applies a fix with a series of edits on the code.
/// </summary>
public class ApplyRosieFixSuggestedAction : ISuggestedAction
{
private readonly ITextBuffer _textBuffer;
private readonly IList<RosieViolationFixEdit> _edits;
private readonly string _displayText;

public ApplyRosieFixSuggestedAction(ITextBuffer textBuffer, RosieViolationFix fix)
{
_textBuffer = textBuffer;
_edits = fix.Edits;
_displayText = $"Fix: {fix.Description}";
}

public void Invoke(CancellationToken cancellationToken)
{
if (HasInvalidEditOffset())
return;

foreach (var edit in _edits)
{
//Apply code insertion/addition
if (StringUtils.AreEqualIgnoreCase(edit.EditType, RosieEditTypes.Add))
{
_textBuffer.Insert(edit.Start.GetOffset(_textBuffer), edit.Content);
}

//Apply code replacement/update
if (StringUtils.AreEqualIgnoreCase(edit.EditType, RosieEditTypes.Update))
{
var replacementSpan =
Span.FromBounds(edit.Start.GetOffset(_textBuffer), edit.End.GetOffset(_textBuffer));
_textBuffer.Replace(replacementSpan, edit.Content);
}

//Apply code removal
if (StringUtils.AreEqualIgnoreCase(edit.EditType, RosieEditTypes.Remove))
{
var removalSpan =
Span.FromBounds(edit.Start.GetOffset(_textBuffer), edit.End.GetOffset(_textBuffer));
_textBuffer.Delete(removalSpan);
}
}
}

/// <summary>
/// If the start offset for additions, or the start/end offset for removals and updates, received from the rule configuration,
/// is either null or is outside the current file's range, we don't apply the fix.
/// </summary>
internal bool HasInvalidEditOffset()
{
var hasInvalidOffset = true;
try
{
var documentLastPosition = _textBuffer.CurrentSnapshot.Length - 1;
hasInvalidOffset = _edits
.Any(edit =>
{
int? startPosition;
if (StringUtils.AreEqualIgnoreCase(edit.EditType, RosieEditTypes.Add))
{
startPosition = edit.Start?.GetOffset(_textBuffer);
return startPosition == null || startPosition < 0 || startPosition > documentLastPosition;
}

startPosition = edit.Start?.GetOffset(_textBuffer);
int? endPosition = edit.End?.GetOffset(_textBuffer);

return startPosition == null ||
endPosition == null ||
startPosition < 0 ||
endPosition < 0 ||
startPosition > documentLastPosition ||
endPosition > documentLastPosition;
});
}
catch (IndexOutOfRangeException)
{
//Let it through, no edit will happen.
}

return hasInvalidOffset;
}

#region Action sets and preview

public Task<IEnumerable<SuggestedActionSet>> GetActionSetsAsync(CancellationToken cancellationToken)
{
return Task.FromResult<IEnumerable<SuggestedActionSet>>(null);
}

public Task<object> GetPreviewAsync(CancellationToken cancellationToken)
{
return Task.FromResult<object>(null);
}

#endregion

#region Disposal

public void Dispose()
{
}

#endregion

#region Properties

public bool TryGetTelemetryId(out Guid telemetryId)
{
telemetryId = Guid.Empty;
return false;
}

public bool HasActionSets => false;

public string DisplayText => _displayText;

public ImageMoniker IconMoniker => default;

public string IconAutomationText => null;

public string InputGestureText => null;

public bool HasPreview => false;

#endregion
}
}
Loading

0 comments on commit 977cf37

Please sign in to comment.