Skip to content

Commit 169dadb

Browse files
committed
Initial draft
0 parents  commit 169dadb

5 files changed

Lines changed: 189 additions & 0 deletions

File tree

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.vs/
2+
.vscode/
3+
bin/
4+
obj/
5+
*.user

PowerPrompt.sln

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.8.34322.80
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerPrompt", "PowerPrompt\PowerPrompt.csproj", "{4C94829F-7398-4EBF-8950-081A30103571}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{4C94829F-7398-4EBF-8950-081A30103571}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{4C94829F-7398-4EBF-8950-081A30103571}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{4C94829F-7398-4EBF-8950-081A30103571}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{4C94829F-7398-4EBF-8950-081A30103571}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {217D6EBF-ADC5-49B3-80A6-97765D829BE1}
24+
EndGlobalSection
25+
EndGlobal

PowerPrompt/AnsiColor.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Text;
2+
3+
namespace PowerPrompt;
4+
5+
public record AnsiColor(string Foreground, string Background);
6+
7+
public static class StringBuilderExtensions
8+
{
9+
public static StringBuilder Append(this StringBuilder sb, AnsiColor foreground, AnsiColor background,
10+
string text) =>
11+
sb.Append(foreground.Foreground)
12+
.Append(background.Background)
13+
.Append(text);
14+
}

PowerPrompt/PowerPrompt.csproj

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<AssemblyName>PowerPrompt</AssemblyName>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="LibGit2Sharp" Version="0.28.0" />
11+
<PackageReference Include="System.Management.Automation" Version="7.4.0" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
using System;
2+
using System.Management.Automation;
3+
using System.Text;
4+
using System.Threading.Tasks;
5+
using LibGit2Sharp;
6+
7+
namespace PowerPrompt;
8+
9+
[Cmdlet(VerbsCommon.Show, "PowerPrompt")]
10+
[OutputType(typeof(string))]
11+
public class ShowPowerPromptCommand : PSCmdlet
12+
{
13+
private static readonly AnsiColor DefaultColor = new("\x1b[39m", "\x1b[49m");
14+
private static readonly AnsiColor CwdBackground = new("\x1b[34m", "\x1b[44m");
15+
private static readonly AnsiColor CwdForeground = new("\x1b[97m", "\x1b[107m");
16+
private static readonly AnsiColor GitForeground = new("\x1b[30m", "\x1b[30m");
17+
private static readonly AnsiColor GitUnknownColor = new("\x1b[90m", "\x1b[100m");
18+
private static readonly AnsiColor GitOutOfSyncColor = new("\x1b[96m", "\x1b[106m");
19+
private static readonly AnsiColor GitDirtyColor = new("\x1b[33m", "\x1b[43m");
20+
private static readonly AnsiColor GitCleanColor = new("\x1b[32m", "\x1b[42m");
21+
22+
private static readonly StatusOptions StatusOptions = new()
23+
{
24+
Show = StatusShowOption.IndexAndWorkDir,
25+
DetectRenamesInIndex = false,
26+
DetectRenamesInWorkDir = false,
27+
ExcludeSubmodules = true,
28+
RecurseIgnoredDirs = false,
29+
RecurseUntrackedDirs = false,
30+
DisablePathSpecMatch = true,
31+
IncludeUnaltered = false,
32+
IncludeIgnored = false,
33+
IncludeUntracked = true,
34+
};
35+
36+
protected override void ProcessRecord()
37+
{
38+
var promptStart = Host.UI.RawUI.CursorPosition;
39+
var repository = GetRepository();
40+
var prompt = ConstructPrompt(repository, null);
41+
WriteObject(prompt);
42+
43+
if (repository is not null)
44+
{
45+
Task.Run(() =>
46+
{
47+
var status = repository.RetrieveStatus(StatusOptions);
48+
if (status is null)
49+
{
50+
return;
51+
}
52+
53+
var newPrompt = ConstructPrompt(repository, status);
54+
var cursor = Host.UI.RawUI.CursorPosition;
55+
Host.UI.RawUI.CursorPosition = promptStart;
56+
Host.UI.Write(newPrompt);
57+
Host.UI.RawUI.CursorPosition = cursor;
58+
});
59+
}
60+
}
61+
62+
private Repository? GetRepository()
63+
{
64+
var repoPath = Repository.Discover(SessionState.Path.CurrentFileSystemLocation.Path);
65+
return string.IsNullOrWhiteSpace(repoPath) ? null : new Repository(repoPath);
66+
}
67+
68+
private string ConstructPrompt(Repository? repository, RepositoryStatus? status)
69+
{
70+
var prompt = new StringBuilder();
71+
72+
WriteWorkingDirectoryToShell(prompt);
73+
WriteOpeningToken(prompt);
74+
WriteCwd(prompt);
75+
76+
if (repository is null)
77+
{
78+
WriteClosingToken(prompt, CwdBackground, DefaultColor);
79+
prompt.Append("\x1b[0m");
80+
return prompt.ToString();
81+
}
82+
83+
var statusColor = GetGitStatusColor(repository, status);
84+
WriteClosingToken(prompt, CwdBackground, statusColor);
85+
WriteGitBranch(prompt, statusColor, repository);
86+
WriteClosingToken(prompt, statusColor, DefaultColor);
87+
88+
prompt.Append("\x1b[0m");
89+
return prompt.ToString();
90+
}
91+
92+
private void WriteWorkingDirectoryToShell(StringBuilder prompt)
93+
{
94+
prompt.Append($"\x1b]9;9;{SessionState.Path.CurrentFileSystemLocation.Path}\x1b\\");
95+
}
96+
97+
private void WriteOpeningToken(StringBuilder prompt)
98+
{
99+
prompt.Append(CwdBackground, DefaultColor, "\ue0b6");
100+
}
101+
102+
private void WriteCwd(StringBuilder prompt)
103+
{
104+
var cwd = SessionState.Path.CurrentFileSystemLocation.Path;
105+
cwd = cwd.Replace(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "~");
106+
prompt.Append(CwdForeground, CwdBackground, $"{cwd} ");
107+
}
108+
109+
private void WriteClosingToken(StringBuilder prompt, AnsiColor foreground, AnsiColor background)
110+
{
111+
prompt.Append(foreground, background, "\ue0b0 ");
112+
}
113+
114+
private AnsiColor GetGitStatusColor(Repository repository, RepositoryStatus? status) => (repository, status) switch
115+
{
116+
({ Head: { IsTracking: true, TrackingDetails: { AheadBy: not (null or 0) } or { BehindBy: not (null or 0) } } }, _) => GitOutOfSyncColor,
117+
(_, { IsDirty: false }) => GitCleanColor,
118+
(_, { IsDirty: true }) => GitDirtyColor,
119+
_ => GitUnknownColor,
120+
};
121+
122+
private void WriteGitBranch(StringBuilder prompt, AnsiColor background, Repository repository)
123+
{
124+
var name = repository.Head.FriendlyName;
125+
if (name == "(no branch)")
126+
{
127+
name = repository.Head.Tip.Sha[..7];
128+
}
129+
prompt.Append(GitForeground, background, $"{name} ");
130+
}
131+
}

0 commit comments

Comments
 (0)