Skip to content

Commit 495948d

Browse files
committed
Add some fun new commands too!
1 parent ef79329 commit 495948d

File tree

7 files changed

+232
-2
lines changed

7 files changed

+232
-2
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
using PoshCode.Pansies.ColorSpaces;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Management.Automation;
6+
using System.Management.Automation.Host;
7+
using System.Management.Automation.Language;
8+
using System.Text;
9+
10+
namespace PoshCode.Pansies.Commands
11+
{
12+
13+
14+
[Cmdlet("Expand","Variable")]
15+
public class ExpandVariableCommand : PSCmdlet
16+
{
17+
[Parameter(Mandatory = true, Position = 0)]
18+
public string Path { get; set; }
19+
20+
[Parameter()]
21+
public SwitchParameter Unescaped { get; set; }
22+
23+
[Parameter()]
24+
public string[] Drive { get; set; } = ["bg", "emoji", "esc", "extra", "fg", "nf"];
25+
26+
[Parameter()]
27+
public SwitchParameter InPlace { get; set; }
28+
29+
protected override void EndProcessing()
30+
{
31+
var resolvedProviderPath = GetResolvedProviderPathFromPSPath(Path, out ProviderInfo provider);
32+
foreach (var file in resolvedProviderPath) {
33+
var result = new StringBuilder();
34+
var replacements = new List<TextReplacement>();
35+
Ast ast = null;
36+
string code;
37+
string fullName = file;
38+
if (provider.Name != "Variable" && provider.Name != "FileSystem" && !fullName.Contains(":"))
39+
{
40+
fullName = $"{provider.Name}:{file}";
41+
}
42+
code = GetVariableValue(fullName).ToString();
43+
ast = Parser.ParseInput(code, fullName, out var tokens, out var parseErrors);
44+
if (parseErrors.Length > 0) {
45+
WriteError(new ErrorRecord(new Exception($"{parseErrors.Length} Parse Errors in {fullName}, cannot expand."), "ParseErrors", ErrorCategory.InvalidOperation, fullName));
46+
continue;
47+
}
48+
result.AppendLine(code);
49+
50+
var variables = ast.FindAll(a => a is VariableExpressionAst, true).
51+
Cast<VariableExpressionAst>().
52+
Where(v => (!v.VariablePath.IsUnqualified || // variable:foo is IsUnqualified = False and IsVariable = True
53+
v.VariablePath.IsDriveQualified) && // fg:red is IsDriveQualified = True and IsVariable = False
54+
Drive.Contains(v.VariablePath.DriveName ?? "variable", StringComparer.OrdinalIgnoreCase));
55+
56+
foreach (var variable in variables)
57+
{
58+
try
59+
{
60+
var replacement = GetVariableValue(variable.VariablePath.UserPath).ToString();
61+
if (!Unescaped)
62+
{
63+
replacement = replacement.ToPsEscapedString();
64+
}
65+
if (variable.Parent is ExpandableStringExpressionAst)
66+
{
67+
replacements.Add(new TextReplacement(replacement, variable.Extent));
68+
}
69+
else
70+
{
71+
replacements.Add(new TextReplacement('"' + replacement + '"', variable.Extent));
72+
}
73+
}
74+
catch
75+
{
76+
WriteWarning($"VariableNotFound: '{variable.VariablePath.UserPath}' at {fullName}:{variable.Extent.StartLineNumber}:{variable.Extent.StartColumnNumber}");
77+
continue;
78+
}
79+
}
80+
81+
foreach (var replacement in replacements.OrderByDescending(r => r.StartOffset))
82+
{
83+
result.Remove(replacement.StartOffset, replacement.Length).Insert(replacement.StartOffset, replacement.Text);
84+
}
85+
86+
if (InPlace)
87+
{
88+
SessionState.PSVariable.Set(fullName, result.ToString());
89+
}
90+
else
91+
{
92+
WriteObject(result.ToString());
93+
}
94+
95+
}
96+
97+
}
98+
99+
private class TextReplacement(string text, IScriptExtent extent)
100+
{
101+
public int StartOffset = extent.StartOffset;
102+
public int EndOffset = extent.EndOffset;
103+
public string Text = text;
104+
public int Length => EndOffset - StartOffset;
105+
}
106+
107+
}
108+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using PoshCode.Pansies.ColorSpaces;
2+
using System;
3+
using System.Management.Automation;
4+
using System.Management.Automation.Host;
5+
6+
namespace PoshCode.Pansies.Commands
7+
{
8+
9+
10+
[Cmdlet("Get","CursorPosition")]
11+
public class GetCursorPositionCommand : Cmdlet
12+
{
13+
[Parameter()]
14+
[ValidateRange(50, int.MaxValue)]
15+
public int WaitMilliseconds { get; set; } = 500;
16+
17+
protected override void EndProcessing()
18+
{
19+
Console.Write("\e[?6n");
20+
// The terminal should respond with "input" but it's not instantaneous
21+
while(!Console.KeyAvailable && WaitMilliseconds > 0)
22+
{
23+
WaitMilliseconds -= 10;
24+
System.Threading.Thread.Sleep(10);
25+
}
26+
string response = string.Empty;
27+
while (Console.KeyAvailable)
28+
{
29+
response += Console.ReadKey(true).KeyChar;
30+
}
31+
if (string.IsNullOrEmpty(response)) {
32+
WriteWarning("No response received from terminal. Ensure this terminal supports cursor position queries.");
33+
return;
34+
}
35+
if (!response.StartsWith("\e[?") || !response.EndsWith("R"))
36+
{
37+
WriteError(new ErrorRecord(new InvalidOperationException($"Unexpected response from terminal. Expected: \"ESC [ <r> ; <c> R\" Where <r> = cursor row and <c> = cursor column. But got \"{response.Replace("\e", "`e")}\""), "InvalidResponse", ErrorCategory.InvalidResult, null));
38+
return;
39+
}
40+
41+
WriteVerbose(response.Replace("\e", "`e"));
42+
var match = System.Text.RegularExpressions.Regex.Match(response, @"\e\[\??((?:\d+;)?\d+;\d+)R");
43+
var parts = match.Groups[1].Value.Split(';');
44+
if (parts.Length < 2)
45+
{
46+
WriteError(new ErrorRecord(new InvalidOperationException($"Unexpected response from terminal. Expected: \"ESC [ <r> ; <c> R\" Where <r> = cursor row and <c> = cursor column. But got \"{response.Replace("\e", "`e")}\""), "InvalidResponse", ErrorCategory.InvalidResult, null));
47+
return;
48+
}
49+
int row = int.Parse(parts[0]);
50+
int col = int.Parse(parts[1]);
51+
int page = parts.Length > 2 ? int.Parse(parts[2]) : 1;
52+
53+
var result = new Position(row, col, true);
54+
WriteObject(result);
55+
56+
}
57+
}
58+
}

Source/Assembly/Commands/NewHyperlinkCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ protected override void ProcessRecord()
6161
Object = Uri;
6262
}
6363

64-
Object = $"\u001B]8;;{Uri}\a{Object}\u001B]8;;\a";
64+
Object = $"\e]8;;{Uri}\a{Object}\e]8;;\a";
6565

6666
var result = new Text()
6767
{
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using PoshCode.Pansies.ColorSpaces;
2+
using System;
3+
using System.Management.Automation;
4+
using System.Management.Automation.Host;
5+
6+
namespace PoshCode.Pansies.Commands
7+
{
8+
9+
10+
[Cmdlet("Restore","CursorPosition")]
11+
public class RestoreCursorPositionCommand : Cmdlet
12+
{
13+
protected override void EndProcessing()
14+
{
15+
Console.Write("\e8");
16+
}
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using PoshCode.Pansies.ColorSpaces;
2+
using System;
3+
using System.Management.Automation;
4+
using System.Management.Automation.Host;
5+
6+
namespace PoshCode.Pansies.Commands
7+
{
8+
9+
10+
[Cmdlet("Save","CursorPosition")]
11+
public class SaveCursorPositionCommand : Cmdlet
12+
{
13+
protected override void EndProcessing()
14+
{
15+
Console.Write("\e[s");
16+
}
17+
}
18+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using PoshCode.Pansies.ColorSpaces;
2+
using System;
3+
using System.Management.Automation;
4+
using System.Management.Automation.Host;
5+
6+
namespace PoshCode.Pansies.Commands
7+
{
8+
9+
10+
[Cmdlet("Set","CursorPosition")]
11+
public class SetCursorPositionCommand : Cmdlet
12+
{
13+
[Parameter(Position = 1)]
14+
public int? Column { get; set; }
15+
16+
[Parameter(Position = 2)]
17+
public int? Line { get; set; }
18+
19+
[Parameter()]
20+
public SwitchParameter Absolute { get; set; } = false;
21+
22+
protected override void EndProcessing()
23+
{
24+
var result = new Position(Line, Column, Absolute);
25+
WriteObject(result.ToString());
26+
}
27+
}
28+
}

Source/Pansies.psd1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ FunctionsToExport = @()
4646
# DefaultCommandPrefix = "Pansies"
4747

4848
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
49-
CmdletsToExport = @('New-Text', 'New-Hyperlink', 'Write-Host', 'Get-Gradient', 'Get-Complement', 'Get-ColorWheel')
49+
CmdletsToExport = @('New-Text', 'New-Hyperlink', 'Write-Host', 'Get-Gradient', 'Get-Complement', 'Get-ColorWheel', 'Get-CursorPosition', 'Set-CursorPosition', 'Save-CursorPosition', 'Restore-CursorPosition', 'Expand-Variable')
5050

5151
# Variables to export from this module
5252
VariablesToExport = 'RgbColorCompleter', 'Pansies'

0 commit comments

Comments
 (0)