diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs
index 60261d492..86aefa5ee 100644
--- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs
+++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs
@@ -141,6 +141,13 @@ protected async Task HandleInitializeRequest(
// Grab the workspace path from the parameters
editorSession.Workspace.WorkspacePath = initializeParams.RootPath;
+ // Set the working directory of the PowerShell session to the workspace path
+ if (editorSession.Workspace.WorkspacePath != null)
+ {
+ editorSession.PowerShellContext.SetWorkingDirectory(
+ editorSession.Workspace.WorkspacePath);
+ }
+
await requestContext.SendResult(
new InitializeResult
{
diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServerEditorOperations.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServerEditorOperations.cs
index f60772527..b8c1da92c 100644
--- a/src/PowerShellEditorServices.Protocol/Server/LanguageServerEditorOperations.cs
+++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServerEditorOperations.cs
@@ -7,7 +7,6 @@
using Microsoft.PowerShell.EditorServices.Protocol.LanguageServer;
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
using System.Threading.Tasks;
-using System;
namespace Microsoft.PowerShell.EditorServices.Protocol.Server
{
@@ -111,6 +110,16 @@ public Task OpenFile(string filePath)
true);
}
+ public string GetWorkspacePath()
+ {
+ return this.editorSession.Workspace.WorkspacePath;
+ }
+
+ public string GetWorkspaceRelativePath(string filePath)
+ {
+ return this.editorSession.Workspace.GetRelativePath(filePath);
+ }
+
public Task ShowInformationMessage(string message)
{
return
diff --git a/src/PowerShellEditorServices/Extensions/EditorWorkspace.cs b/src/PowerShellEditorServices/Extensions/EditorWorkspace.cs
index a9598f09c..68d113523 100644
--- a/src/PowerShellEditorServices/Extensions/EditorWorkspace.cs
+++ b/src/PowerShellEditorServices/Extensions/EditorWorkspace.cs
@@ -17,6 +17,18 @@ public class EditorWorkspace
#endregion
+ #region Properties
+
+ ///
+ /// Gets the current workspace path if there is one or null otherwise.
+ ///
+ public string Path
+ {
+ get { return this.editorOperations.GetWorkspacePath(); }
+ }
+
+ #endregion
+
#region Constructors
internal EditorWorkspace(IEditorOperations editorOperations)
diff --git a/src/PowerShellEditorServices/Extensions/FileContext.cs b/src/PowerShellEditorServices/Extensions/FileContext.cs
index 9b7f7f8d7..8eb8d2812 100644
--- a/src/PowerShellEditorServices/Extensions/FileContext.cs
+++ b/src/PowerShellEditorServices/Extensions/FileContext.cs
@@ -32,6 +32,19 @@ public string Path
get { return this.scriptFile.FilePath; }
}
+ ///
+ /// Gets the workspace-relative path of the file.
+ ///
+ public string WorkspacePath
+ {
+ get
+ {
+ return
+ this.editorOperations.GetWorkspaceRelativePath(
+ this.scriptFile.FilePath);
+ }
+ }
+
///
/// Gets the parsed abstract syntax tree for the file.
///
diff --git a/src/PowerShellEditorServices/Extensions/IEditorOperations.cs b/src/PowerShellEditorServices/Extensions/IEditorOperations.cs
index 7f6845d6e..8e9358e1b 100644
--- a/src/PowerShellEditorServices/Extensions/IEditorOperations.cs
+++ b/src/PowerShellEditorServices/Extensions/IEditorOperations.cs
@@ -20,6 +20,19 @@ public interface IEditorOperations
/// A new EditorContext object.
Task GetEditorContext();
+ ///
+ /// Gets the path to the editor's active workspace.
+ ///
+ /// The workspace path or null if there isn't one.
+ string GetWorkspacePath();
+
+ ///
+ /// Resolves the given file path relative to the current workspace path.
+ ///
+ /// The file path to be resolved.
+ /// The resolved file path.
+ string GetWorkspaceRelativePath(string filePath);
+
///
/// Causes a file to be opened in the editor. If the file is
/// already open, the editor must switch to the file.
diff --git a/src/PowerShellEditorServices/Language/FindDotSourcedVisitor.cs b/src/PowerShellEditorServices/Language/FindDotSourcedVisitor.cs
index 7ffda9c2a..ea35dd4a1 100644
--- a/src/PowerShellEditorServices/Language/FindDotSourcedVisitor.cs
+++ b/src/PowerShellEditorServices/Language/FindDotSourcedVisitor.cs
@@ -32,7 +32,8 @@ public FindDotSourcedVisitor()
/// or a decision to continue if it wasn't found
public override AstVisitAction VisitCommand(CommandAst commandAst)
{
- if (commandAst.InvocationOperator.Equals(TokenKind.Dot))
+ if (commandAst.InvocationOperator.Equals(TokenKind.Dot) &&
+ commandAst.CommandElements[0] is StringConstantExpressionAst)
{
// Strip any quote characters off of the string
string fileName = commandAst.CommandElements[0].Extent.Text.Trim('\'', '"');
diff --git a/src/PowerShellEditorServices/Session/SessionPSHostUserInterface.cs b/src/PowerShellEditorServices/Session/SessionPSHostUserInterface.cs
index 921120c85..18eba2735 100644
--- a/src/PowerShellEditorServices/Session/SessionPSHostUserInterface.cs
+++ b/src/PowerShellEditorServices/Session/SessionPSHostUserInterface.cs
@@ -320,10 +320,8 @@ private void WaitForPromptCompletion(
try
{
// This will synchronously block on the prompt task
- // method which gets run on another thread. Use a
- // 30 second timeout so that everything doesn't get
- // backed up if the user doesn't respond.
- promptTask.Wait(15000);
+ // method which gets run on another thread.
+ promptTask.Wait();
if (promptTask.Status == TaskStatus.WaitingForActivation)
{
diff --git a/src/PowerShellEditorServices/Workspace/Workspace.cs b/src/PowerShellEditorServices/Workspace/Workspace.cs
index 24ca0932e..8e66f4599 100644
--- a/src/PowerShellEditorServices/Workspace/Workspace.cs
+++ b/src/PowerShellEditorServices/Workspace/Workspace.cs
@@ -176,6 +176,32 @@ public ScriptFile[] ExpandScriptReferences(ScriptFile scriptFile)
return expandedReferences.ToArray();
}
+ ///
+ /// Gets the workspace-relative path of the given file path.
+ ///
+ /// The original full file path.
+ /// A relative file path
+ public string GetRelativePath(string filePath)
+ {
+ string resolvedPath = filePath;
+
+ if (!IsPathInMemory(filePath) && !string.IsNullOrEmpty(this.WorkspacePath))
+ {
+ Uri workspaceUri = new Uri(this.WorkspacePath);
+ Uri fileUri = new Uri(filePath);
+
+ resolvedPath = workspaceUri.MakeRelativeUri(fileUri).ToString();
+
+ // Convert the directory separators if necessary
+ if (System.IO.Path.DirectorySeparatorChar == '\\')
+ {
+ resolvedPath = resolvedPath.Replace('/', '\\');
+ }
+ }
+
+ return resolvedPath;
+ }
+
#endregion
#region Private Methods
@@ -264,10 +290,13 @@ internal static bool IsPathInMemory(string filePath)
// When viewing PowerShell files in the Git diff viewer, VS Code
// sends the contents of the file at HEAD with a URI that starts
// with 'inmemory'. Untitled files which have been marked of
- // type PowerShell have a path starting with 'untitled'.
+ // type PowerShell have a path starting with 'untitled'. Files
+ // opened from the find/replace view will be prefixed with
+ // 'private'.
return
filePath.StartsWith("inmemory") ||
- filePath.StartsWith("untitled");
+ filePath.StartsWith("untitled") ||
+ filePath.StartsWith("private");
}
private string GetBaseFilePath(string filePath)
diff --git a/test/PowerShellEditorServices.Test/Extensions/ExtensionServiceTests.cs b/test/PowerShellEditorServices.Test/Extensions/ExtensionServiceTests.cs
index 84475829a..3ac6a22f3 100644
--- a/test/PowerShellEditorServices.Test/Extensions/ExtensionServiceTests.cs
+++ b/test/PowerShellEditorServices.Test/Extensions/ExtensionServiceTests.cs
@@ -168,6 +168,16 @@ await this.extensionEventQueue.EnqueueAsync(
public class TestEditorOperations : IEditorOperations
{
+ public string GetWorkspacePath()
+ {
+ throw new NotImplementedException();
+ }
+
+ public string GetWorkspaceRelativePath(string filePath)
+ {
+ throw new NotImplementedException();
+ }
+
public Task OpenFile(string filePath)
{
throw new NotImplementedException();
diff --git a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj
index 5a9a43cd9..3d5531cda 100644
--- a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj
+++ b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj
@@ -73,6 +73,7 @@
+
diff --git a/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs b/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs
new file mode 100644
index 000000000..16b58987f
--- /dev/null
+++ b/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs
@@ -0,0 +1,38 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using Microsoft.PowerShell.EditorServices;
+using System;
+using System.IO;
+using System.Linq;
+using Xunit;
+
+namespace Microsoft.PowerShell.EditorServices.Test.Session
+{
+ public class WorkspaceTests
+ {
+ private static readonly Version PowerShellVersion = new Version("5.0");
+
+ [Fact]
+ public void CanResolveWorkspaceRelativePath()
+ {
+ string workspacePath = @"c:\Test\Workspace\";
+ string testPathInside = @"c:\Test\Workspace\SubFolder\FilePath.ps1";
+ string testPathOutside = @"c:\Test\PeerPath\FilePath.ps1";
+ string testPathAnotherDrive = @"z:\TryAndFindMe\FilePath.ps1";
+
+ Workspace workspace = new Workspace(PowerShellVersion);
+
+ // Test without a workspace path
+ Assert.Equal(testPathOutside, workspace.GetRelativePath(testPathOutside));
+
+ // Test with a workspace path
+ workspace.WorkspacePath = workspacePath;
+ Assert.Equal(@"SubFolder\FilePath.ps1", workspace.GetRelativePath(testPathInside));
+ Assert.Equal(@"..\PeerPath\FilePath.ps1", workspace.GetRelativePath(testPathOutside));
+ Assert.Equal(testPathAnotherDrive, workspace.GetRelativePath(testPathAnotherDrive));
+ }
+ }
+}