Skip to content

Commit

Permalink
Initial VS extension
Browse files Browse the repository at this point in the history
closes #499
  • Loading branch information
belav committed Dec 26, 2021
1 parent 8be563f commit 8676a1b
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 106 deletions.
13 changes: 4 additions & 9 deletions Src/CSharpier.VisualStudio/CSharpier.VisualStudio.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="CSharpierOptionsPage.cs" />
<Compile Include="CSharpierOptionsPage.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="ReformatWithCSharpier.cs" />
<Compile Include="CSharpierProcessPipeMultipleFiles.cs" />
<Compile Include="CSharpierProcessSingleFile.cs" />
Expand All @@ -58,7 +60,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="CSharpierPackage.cs" />
<Compile Include="ReformatWithCSharpierOnSave.cs" />
<Compile Include="Settings.cs" />
<Compile Include="InfoBarService.cs" />
</ItemGroup>
<ItemGroup>
<None Include="source.extension.vsixmanifest">
Expand Down Expand Up @@ -88,11 +90,4 @@
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
7 changes: 1 addition & 6 deletions Src/CSharpier.VisualStudio/CSharpierOptionsPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@

namespace CSharpier.VisualStudio
{
// TODO see https://github.com/Elders/VSE-FormatDocumentOnSave for how that works, I think Settings can go away
public class CSharpierOptionsPage : DialogPage
{
[Category("CSharpier")]
[DisplayName("Reformat with CSharpier on Save")]
[Description("Reformat with CSharpier on Save")]
public bool RunOnSave
{
get => Settings.Instance.RunOnSave;
set => Settings.Instance.RunOnSave = value;
}
public bool RunOnSave { get; set; }
}
}
16 changes: 11 additions & 5 deletions Src/CSharpier.VisualStudio/CSharpierPackage.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.VisualStudio.Settings;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell.Settings;
using Task = System.Threading.Tasks.Task;

namespace CSharpier.VisualStudio
Expand All @@ -28,12 +26,20 @@ IProgress<ServiceProgressData> progress
var outputPane = await this.GetServiceAsync(typeof(SVsOutputWindow)) as IVsOutputWindow;
var logger = new Logger(outputPane);
logger.Log("Starting");


await InfoBarService.InitializeAsync(this);

var csharpierService = new CSharpierService(logger);
var formattingService = new FormattingService(logger, csharpierService);

await Settings.InitializeAsync(this);
await ReformatWithCSharpierOnSave.InitializeAsync(this, formattingService);
var csharpierOptionsPage = (CSharpierOptionsPage)GetDialogPage(
typeof(CSharpierOptionsPage)
);
await ReformatWithCSharpierOnSave.InitializeAsync(
this,
formattingService,
csharpierOptionsPage
);
await ReformatWithCSharpier.InitializeAsync(this, formattingService);
}
}
Expand Down
55 changes: 32 additions & 23 deletions Src/CSharpier.VisualStudio/CSharpierProcessPipeMultipleFiles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

namespace CSharpier.VisualStudio
{
// TODO what about disposing this?
public class CSharpierProcessPipeMultipleFiles : ICSharpierProcess
{
private readonly Logger logger;
Expand All @@ -17,8 +16,11 @@ public class CSharpierProcessPipeMultipleFiles : ICSharpierProcess
public CSharpierProcessPipeMultipleFiles(string csharpierPath, Logger logger)
{
this.logger = logger;

var processStartInfo = new ProcessStartInfo("dotnet", csharpierPath + " --pipe-multiple-files")

var processStartInfo = new ProcessStartInfo(
"dotnet",
csharpierPath + " --pipe-multiple-files"
)
{
RedirectStandardInput = true,
RedirectStandardOutput = true,
Expand All @@ -32,6 +34,8 @@ public CSharpierProcessPipeMultipleFiles(string csharpierPath, Logger logger)
this.FormatFile("public class ClassName { }", "Test.cs");
}

public bool CanFormat => true;

public string FormatFile(string content, string fileName)
{
process.StandardInput.Write(fileName);
Expand All @@ -45,49 +49,54 @@ public string FormatFile(string content, string fileName)

var outputReaderThread = CreateReadingThread(process.StandardOutput, output);
var errorReaderThread = CreateReadingThread(process.StandardError, errorOutput);

outputReaderThread.Start();
errorReaderThread.Start();

while (!this.done)
{
Thread.Sleep(TimeSpan.FromMilliseconds(1));
}

outputReaderThread.Interrupt();
errorReaderThread.Interrupt();

var errorResult = errorOutput.ToString();
if (string.IsNullOrEmpty(errorResult))
{
return output.ToString();
}

this.logger.Log("Got error output: " + errorResult);
return "";

}

private Thread CreateReadingThread(StreamReader reader, StringBuilder stringBuilder)
{
return new Thread(() => {
try {
var nextCharacter = reader.Read();
while (nextCharacter != -1) {
if (nextCharacter == '\u0003')
return new Thread(
() =>
{
try
{
var nextCharacter = reader.Read();
while (nextCharacter != -1)
{
done = true;
return;
if (nextCharacter == '\u0003')
{
done = true;
return;
}
stringBuilder.Append((char)nextCharacter);
nextCharacter = reader.Read();
}
stringBuilder.Append((char) nextCharacter);
nextCharacter = reader.Read();
}
} catch (Exception e)
{
// TODO log
done = true;
catch (Exception e)
{
logger.Log(e);
done = true;
}
}
});
);
}
}
}
}
2 changes: 2 additions & 0 deletions Src/CSharpier.VisualStudio/CSharpierProcessSingleFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public CSharpierProcessSingleFile(string csharpierPath, Logger logger)
this.logger = logger;
}

public bool CanFormat => true;

public string FormatFile(string content, string fileName)
{
var output = new StringBuilder();
Expand Down
32 changes: 18 additions & 14 deletions Src/CSharpier.VisualStudio/CSharpierService.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
using System;
using System.Diagnostics;
using System.Windows.Forms;

namespace CSharpier.VisualStudio
{
// TODO figure out how to publish https://docs.microsoft.com/en-us/visualstudio/extensibility/walkthrough-publishing-a-visual-studio-extension?view=vs-2022
// TODO make this work in 2022 https://docs.microsoft.com/en-us/visualstudio/extensibility/migration/update-visual-studio-extension?view=vs-2022

public class CSharpierService
{
private readonly string csharpierPath;
Expand Down Expand Up @@ -42,15 +46,17 @@ public string GetCSharpierPath()

public string ExecuteCommand(string cmd, string arguments)
{
// TODO when testing, this runs from in the csharpier directory, which means it uses csharpier from there instead of globally

var processStartInfo = new ProcessStartInfo(cmd, arguments)
{
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
#if DEBUG
// TODO when testing, this runs from in the csharpier directory, which means it uses csharpier from there instead of globally
WorkingDirectory = "C:/"
#endif
};

using var process = new Process { StartInfo = processStartInfo };
Expand All @@ -66,7 +72,7 @@ private ICSharpierProcess SetupCSharpierProcess()
{
var version = ExecuteCommand("dotnet", this.csharpierPath + " --version");
this.logger.Log("CSharpier version: " + version);
if (version == null)
if (string.IsNullOrEmpty(version))
{
this.DisplayInstallNeededMessage();
}
Expand All @@ -78,10 +84,7 @@ private ICSharpierProcess SetupCSharpierProcess()
{
var content =
"Please upgrade to CSharpier >= 0.12.0 for bug fixes and improved formatting speed.";
// TODO notify if csharpier should be updated
// NotificationGroupManager.getInstance().getNotificationGroup("CSharpier")
// .createNotification(content, NotificationType.INFORMATION)
// .notify(project);
InfoBarService.Instance.ShowInfoBar(content);

return new CSharpierProcessSingleFile(this.csharpierPath, this.logger);
}
Expand All @@ -98,17 +101,18 @@ private ICSharpierProcess SetupCSharpierProcess()

private void DisplayInstallNeededMessage()
{
// TODO notify if not installed
// Notification notification = NotificationGroupManager.getInstance().getNotificationGroup("CSharpier")
// .createNotification("CSharpier must be installed globally to support formatting.", NotificationType.WARNING);
//
// // notification.addAction(new EditAction());
//
// notification.notify(project);
InfoBarService.Instance.ShowInfoBar("CSharpier must be installed globally to support formatting.");
}

public bool CanFormat => this.csharpierProcess.CanFormat;

public string Format(string content, string filePath)
{
if (!this.csharpierProcess.CanFormat)
{
return null;
}

this.logger.Log("Formatting " + filePath);
try
{
Expand Down
10 changes: 6 additions & 4 deletions Src/CSharpier.VisualStudio/FormattingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ namespace CSharpier.VisualStudio
public class FormattingService
{
private readonly Logger logger;
private readonly CSharpierService cSharpierService;
private readonly CSharpierService csharpierService;

public FormattingService(Logger logger, CSharpierService cSharpierService)
public FormattingService(Logger logger, CSharpierService csharpierService)
{
this.logger = logger;
this.cSharpierService = cSharpierService;
this.csharpierService = csharpierService;
}

public bool CanFormat => this.csharpierService.CanFormat;

public void Format(Document document)
{
Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
Expand All @@ -32,7 +34,7 @@ public void Format(Document document)
var endPoint = textDocument.EndPoint.CreateEditPoint();
var text = editPoint.GetText(endPoint);

var newText = this.cSharpierService.Format(text, document.FullName);
var newText = this.csharpierService.Format(text, document.FullName);
if (string.IsNullOrEmpty(newText))
{
return;
Expand Down
1 change: 1 addition & 0 deletions Src/CSharpier.VisualStudio/ICSharpierProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace CSharpier.VisualStudio
{
public interface ICSharpierProcess
{
bool CanFormat { get; }
string FormatFile(string content, string fileName);
}
}
65 changes: 65 additions & 0 deletions Src/CSharpier.VisualStudio/InfoBarService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System.Windows.Forms;
using Microsoft.VisualStudio.Imaging;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Task = System.Threading.Tasks.Task;

namespace CSharpier.VisualStudio
{
public class InfoBarService : IVsInfoBarUIEvents
{
private readonly CSharpierPackage csharpierPackage;
private uint cookie;

private InfoBarService(CSharpierPackage csharpierPackage)
{
this.csharpierPackage = csharpierPackage;
}

public static InfoBarService Instance { get; private set; }

public static Task InitializeAsync(CSharpierPackage serviceProvider)
{
Instance = new InfoBarService(serviceProvider);

return Task.CompletedTask;
}

public void OnClosed(IVsInfoBarUIElement infoBarUiElement)
{
infoBarUiElement.Unadvise(cookie);
}

public void OnActionItemClicked(IVsInfoBarUIElement infoBarUIElement, IVsInfoBarActionItem actionItem)
{
throw new System.NotImplementedException();
}

public void ShowInfoBar(string message)
{
ThreadHelper.ThrowIfNotOnUIThread();

var shell = csharpierPackage.GetServiceAsync(typeof(SVsShell)).Result as IVsShell;
if (shell == null)
{
return;
}

shell.GetProperty((int) __VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var property);
if (!(property is IVsInfoBarHost infoBarHost))
{
return;
}
var text = new InfoBarTextSpan(message);

var spans = new[] { text };
var actions = new InfoBarActionItem[] { };
var infoBarModel = new InfoBarModel(spans, actions, KnownMonikers.StatusInformation, isCloseButtonVisible: true);

var factory = csharpierPackage.GetServiceAsync(typeof(SVsInfoBarUIFactory)).Result as IVsInfoBarUIFactory;
var element = factory.CreateInfoBar(infoBarModel);
element.Advise(this, out cookie);
infoBarHost.AddInfoBar(element);
}
}
}
2 changes: 2 additions & 0 deletions Src/CSharpier.VisualStudio/NullCSharpierProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ namespace CSharpier.VisualStudio
{
public class NullCSharpierProcess : ICSharpierProcess
{
public bool CanFormat => false;

public string FormatFile(string content, string fileName)
{
return null;
Expand Down
Loading

0 comments on commit 8676a1b

Please sign in to comment.