Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/Tests/ClipboardServiceTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using TextCopy;
using System.Runtime.InteropServices;
using TextCopy;

public class ClipboardServiceTests
{
Expand All @@ -11,6 +12,19 @@ public async Task Simple()
await VerifyInnerAsync("🅢");
}

[Test]
public async Task PassesCancellationToken()
{
// A token that is never cancelled must not interfere with normal operation.
using var source = new CancellationTokenSource();
var token = source.Token;

await ClipboardService.SetTextAsync("Foo", token);

var actual = await ClipboardService.GetTextAsync(token);
Assert.AreEqual("Foo", actual);
}

static void VerifyInner(string expected)
{
ClipboardService.SetText(expected);
Expand Down
48 changes: 36 additions & 12 deletions src/TextCopy/BashRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,34 @@ static class BashRunner
{
public static string Run(string commandLine)
{
StringBuilder errorBuilder = new();
StringBuilder outputBuilder = new();
var arguments = $"-c \"{commandLine}\"";
using Process process = new()
using var process = StartBash(arguments, out var outputBuilder, out var errorBuilder);
if (!process.DoubleWaitForExit())
{
var timeoutError = $@"Process timed out. Command line: bash {arguments}.
Output: {outputBuilder}
Error: {errorBuilder}";
throw new(timeoutError);
}

return GetResult(process, arguments, outputBuilder, errorBuilder);
}

public static async Task<string> RunAsync(string commandLine, Cancellation cancellation)
{
var arguments = $"-c \"{commandLine}\"";
using var process = StartBash(arguments, out var outputBuilder, out var errorBuilder);

await process.WaitForExitAsync(cancellation);

return GetResult(process, arguments, outputBuilder, errorBuilder);
}

static Process StartBash(string arguments, out StringBuilder outputBuilder, out StringBuilder errorBuilder)
{
var output = new StringBuilder();
var error = new StringBuilder();
var process = new Process
{
StartInfo = new()
{
Expand All @@ -20,17 +44,17 @@ public static string Run(string commandLine)
}
};
process.Start();
process.OutputDataReceived += (_, args) => { outputBuilder.AppendLine(args.Data); };
process.OutputDataReceived += (_, args) => { output.AppendLine(args.Data); };
process.BeginOutputReadLine();
process.ErrorDataReceived += (_, args) => { errorBuilder.AppendLine(args.Data); };
process.ErrorDataReceived += (_, args) => { error.AppendLine(args.Data); };
process.BeginErrorReadLine();
if (!process.DoubleWaitForExit())
{
var timeoutError = $@"Process timed out. Command line: bash {arguments}.
Output: {outputBuilder}
Error: {errorBuilder}";
throw new(timeoutError);
}
outputBuilder = output;
errorBuilder = error;
return process;
}

static string GetResult(Process process, string arguments, StringBuilder outputBuilder, StringBuilder errorBuilder)
{
if (process.ExitCode == 0)
{
return outputBuilder.ToString();
Expand Down
56 changes: 34 additions & 22 deletions src/TextCopy/LinuxClipboard_2.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@ static LinuxClipboard()
isWsl = Environment.GetEnvironmentVariable("WSL_DISTRO_NAME") != null;
}

public static Task SetTextAsync(string text, Cancellation cancellation)
public static async Task SetTextAsync(string text, Cancellation cancellation)
{
SetText(text);

return Task.CompletedTask;
var tempFileName = Path.GetTempFileName();
File.WriteAllText(tempFileName, text);
try
{
await BashRunner.RunAsync(SetCommand(tempFileName), cancellation);
}
finally
{
File.Delete(tempFileName);
}
}

public static void SetText(string text)
Expand All @@ -22,45 +29,50 @@ public static void SetText(string text)
File.WriteAllText(tempFileName, text);
try
{
if (isWsl)
{
BashRunner.Run($"cat {tempFileName} | clip.exe ");
}
else
{
BashRunner.Run($"cat {tempFileName} | xsel -i --clipboard ");
}
BashRunner.Run(SetCommand(tempFileName));
}
finally
{
File.Delete(tempFileName);
}
}

public static Task<string?> GetTextAsync(Cancellation cancellation)
static string SetCommand(string tempFileName) =>
isWsl
? $"cat {tempFileName} | clip.exe "
: $"cat {tempFileName} | xsel -i --clipboard ";

public static async Task<string?> GetTextAsync(Cancellation cancellation)
{
return Task.FromResult<string?>(GetText());
var tempFileName = Path.GetTempFileName();
try
{
await BashRunner.RunAsync(GetCommand(tempFileName), cancellation);
return File.ReadAllText(tempFileName);
}
finally
{
File.Delete(tempFileName);
}
}

public static string GetText()
{
var tempFileName = Path.GetTempFileName();
try
{
if (isWsl)
{
BashRunner.Run($"powershell.exe -NoProfile Get-Clipboard > {tempFileName}");
}
else
{
BashRunner.Run($"xsel -o --clipboard > {tempFileName}");
}
BashRunner.Run(GetCommand(tempFileName));
return File.ReadAllText(tempFileName);
}
finally
{
File.Delete(tempFileName);
}
}

static string GetCommand(string tempFileName) =>
isWsl
? $"powershell.exe -NoProfile Get-Clipboard > {tempFileName}"
: $"xsel -o --clipboard > {tempFileName}";
}
#endif
46 changes: 38 additions & 8 deletions src/TextCopy/LinuxClipboard_2.1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,7 @@ public static async Task SetTextAsync(string text, Cancellation cancellation)
{
var tempFileName = Path.GetTempFileName();
await File.WriteAllTextAsync(tempFileName, text, cancellation);

if (cancellation.IsCancellationRequested)
{
return;
}

InnerSetText(tempFileName);
await InnerSetTextAsync(tempFileName, cancellation);
}

public static void SetText(string text)
Expand Down Expand Up @@ -48,6 +42,30 @@ static void InnerSetText(string tempFileName)
}
}

static async Task InnerSetTextAsync(string tempFileName, Cancellation cancellation)
{
try
{
if (cancellation.IsCancellationRequested)
{
return;
}

if (isWsl)
{
await BashRunner.RunAsync($"cat {tempFileName} | clip.exe ", cancellation);
}
else
{
await BashRunner.RunAsync($"cat {tempFileName} | xsel -i --clipboard ", cancellation);
}
}
finally
{
File.Delete(tempFileName);
}
}

public static string? GetText()
{
var tempFileName = Path.GetTempFileName();
Expand All @@ -67,7 +85,7 @@ static void InnerSetText(string tempFileName)
var tempFileName = Path.GetTempFileName();
try
{
InnerGetText(tempFileName);
await InnerGetTextAsync(tempFileName, cancellation);
return await File.ReadAllTextAsync(tempFileName, cancellation);
}
finally
Expand All @@ -87,5 +105,17 @@ static void InnerGetText(string tempFileName)
BashRunner.Run($"xsel -o --clipboard > {tempFileName}");
}
}

static async Task InnerGetTextAsync(string tempFileName, Cancellation cancellation)
{
if (isWsl)
{
await BashRunner.RunAsync($"powershell.exe -NoProfile Get-Clipboard > {tempFileName}", cancellation);
}
else
{
await BashRunner.RunAsync($"xsel -o --clipboard > {tempFileName}", cancellation);
}
}
}
#endif