Skip to content

Commit 8ac8951

Browse files
committed
Improve the path completion and tilde support in path
1 parent d439a3e commit 8ac8951

File tree

5 files changed

+87
-17
lines changed

5 files changed

+87
-17
lines changed

shell/AIShell.Integration/AIShell.psd1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
CompanyName = 'Microsoft Corporation'
88
Copyright = '(c) Microsoft Corporation. All rights reserved.'
99
Description = 'Integration with the AIShell to provide intelligent shell experience'
10-
PowerShellVersion = '7.4'
10+
PowerShellVersion = '7.4.6'
1111
FunctionsToExport = @()
1212
CmdletsToExport = @('Start-AIShell','Invoke-AIShell','Resolve-Error')
1313
VariablesToExport = '*'

shell/AIShell.Kernel/Command/CodeCommand.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public CodeCommand()
2525
post.AddArgument(nth);
2626

2727
var append = new Option<bool>("--append", "Append to the end of the file.");
28-
var file = new Argument<FileInfo>("file", "The file path to save the code to.");
28+
var file = new Argument<string>("file", "The file path to save the code to.");
2929
save.AddArgument(file);
3030
save.AddOption(append);
3131

@@ -88,7 +88,7 @@ private void CopyAction(int nth)
8888
shell.OnUserAction(new CodePayload(UserAction.CodeCopy, code));
8989
}
9090

91-
private void SaveAction(FileInfo file, bool append)
91+
private void SaveAction(string path, bool append)
9292
{
9393
var shell = (Shell)Shell;
9494
var host = shell.Host;
@@ -100,6 +100,22 @@ private void SaveAction(FileInfo file, bool append)
100100
return;
101101
}
102102

103+
if (string.IsNullOrEmpty(path))
104+
{
105+
host.WriteErrorLine($"Please specify a file path for saving the code.");
106+
return;
107+
}
108+
109+
path = Utils.ResolveTilde(path);
110+
FileInfo file = new(path);
111+
112+
string fullName = file.FullName;
113+
if (Directory.Exists(fullName))
114+
{
115+
host.WriteErrorLine($"The specified path '{fullName}' points to an existing directory. Please specify a file path instead.");
116+
return;
117+
}
118+
103119
try
104120
{
105121
using FileStream stream = file.Open(append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.None);

shell/AIShell.Kernel/Command/CommandRunner.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ internal CommandRunner(Shell shell)
3535
new RefreshCommand(),
3636
new RetryCommand(),
3737
new HelpCommand(),
38-
new RenderCommand(),
38+
//new RenderCommand(),
3939
};
4040

4141
LoadCommands(buildin, Core);

shell/AIShell.Kernel/Utility/ReadLineHelper.cs

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,41 @@ private List<CompletionResult> CompleteCommandName(string wildcard)
8888

8989
private List<CompletionResult> CompleteFileSystemPath(string wordToComplete)
9090
{
91-
if (!Path.IsPathFullyQualified(wordToComplete))
91+
bool startsWithTilde = false;
92+
string homeDirectory = null;
93+
List<CompletionResult> result = null;
94+
95+
// Check if the path starts with tilde.
96+
static bool StartsWithTilde(string path)
97+
{
98+
return !string.IsNullOrEmpty(path)
99+
&& path.Length >= 2
100+
&& path[0] is '~'
101+
&& path[1] == Path.DirectorySeparatorChar;
102+
}
103+
104+
// Check if the path should be quoted.
105+
string QuoteIfNeeded(string path)
106+
{
107+
// Do not add quoting if the original string is already quoted.
108+
return !wordToComplete.Contains(' ') && path.Contains(' ') ? $"\"{path}\"" : path;
109+
}
110+
111+
// Add one result to the result list.
112+
void AddOneResult(string path, bool isContainer)
113+
{
114+
result ??= [];
115+
string filePath = startsWithTilde ? path.Replace(homeDirectory, "~") : path;
116+
string text = QuoteIfNeeded(filePath);
117+
118+
CompletionResultType resultType = isContainer
119+
? CompletionResultType.ProviderContainer
120+
: CompletionResultType.ProviderItem;
121+
result.Add(new CompletionResult(text, text, resultType, toolTip: null));
122+
}
123+
124+
if (!Path.IsPathFullyQualified(wordToComplete) &&
125+
(startsWithTilde = StartsWithTilde(wordToComplete)) is false)
92126
{
93127
return null;
94128
}
@@ -105,32 +139,28 @@ private List<CompletionResult> CompleteFileSystemPath(string wordToComplete)
105139
fileName = Path.GetFileName(wordToComplete) + "*";
106140
}
107141

142+
if (startsWithTilde)
143+
{
144+
rootPath = Utils.ResolveTilde(rootPath);
145+
homeDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
146+
}
147+
108148
if (!Directory.Exists(rootPath))
109149
{
110150
return null;
111151
}
112152

113-
List<CompletionResult> result = null;
114153
foreach (string dir in Directory.EnumerateDirectories(rootPath, fileName, _enumerationOptions))
115154
{
116-
result ??= new();
117-
string text = QuoteIfNeeded(dir);
118-
result.Add(new CompletionResult(text, text, CompletionResultType.ProviderContainer, toolTip: null));
155+
AddOneResult(dir, isContainer: true);
119156
}
120157

121158
foreach (string file in Directory.EnumerateFiles(rootPath, fileName, _enumerationOptions))
122159
{
123-
result ??= new();
124-
string text = QuoteIfNeeded(file);
125-
result.Add(new CompletionResult(text, text, CompletionResultType.ProviderItem, toolTip: null));
160+
AddOneResult(file, isContainer: false);
126161
}
127162

128163
return result;
129-
130-
static string QuoteIfNeeded(string path)
131-
{
132-
return path.Contains(' ') ? $"\"{path}\"" : path;
133-
}
134164
}
135165

136166
/// <summary>

shell/AIShell.Kernel/Utility/Utils.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,30 @@ internal static JsonSerializerOptions GetJsonSerializerOptions()
8484
};
8585
}
8686

87+
/// <summary>
88+
/// Try resolving the leading tilde in a path.
89+
/// </summary>
90+
internal static string ResolveTilde(string path)
91+
{
92+
if (path.StartsWith('~'))
93+
{
94+
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
95+
96+
if (path.Length is 1)
97+
{
98+
// The path is just '~'.
99+
return home;
100+
}
101+
102+
if (path[1] == Path.DirectorySeparatorChar)
103+
{
104+
return Path.Join(home, path.AsSpan(2));
105+
}
106+
}
107+
108+
return path;
109+
}
110+
87111
/// <summary>
88112
/// Check if the <paramref name="left"/> contains <paramref name="right"/> regardless of
89113
/// the leading and trailing space characters on each line if both are multi-line strings.

0 commit comments

Comments
 (0)