Thursday, March 12, 2020

PowerShell 7, VS Code, and the PowerShell 7 ISE Extension

Introduction and Background

Welcome to this post, a part of this #PS7NOW blog series - I hope you are enjoying them.  Before getting to my topic, I assume that you know what PowerShell 7 is and are familiar with Windows PowerShell. 

The Windows PowerShell ISE, a tool I used extensively ever since early beta versions in Windows PowerShell V2, does NOT feature in PowerShell 7.  You might say, it's pining for the fjords (although it's still shipped in Windows and is likely to be supported for years and years). But however you describe it - it's feature complete and is not going to support PowerSell 7.  Replacing the ISE is a new product: Visual Studio Code, or VS Code.

VS Code is light-weight, cross-platform (ie Linux, Mac, Windows), and open-source code editing tool. Which sounds like no big deal, but if you are an ISE user and are utilising Windows PowerShell, VS Code is in your future. For more details on VS Code, see https://code.visualstudio.com/docs.

VS Code

I must confess, the early versions of VS Code I used were under-whelming, and I much preferred the ISE. And given that PowerShell Core was also in the early days, sticking with the ISE and Windows PowerShell for day to day activities made sense.

But for me, the release of PowerShell 7 and the incredible velocity behind the both PowerShell 7 and VS Code product have changed my view. I now use VS Code for my day to day work. I still use the ISE, but only to install  PowerShell 7 and VS Code.

Some features I like in VS Code include:-
  • Workspaces - this is a set of related folders/files which makes it easy to keep things together even when they are spread out in filestore.
  • Side by Side edit windows - makes comparing files, and leveraging code from one into another so much simpler. 
  • Built-in spell check - yes it's an extension, but typos in comments are less likely. 
  • The extensibility and customizability - you really can have it your way.
  • PS Script analyzer is built in - so I get hints about poor code as I type.
And, and, and...

For more details on VS Code, see: https://code.visualstudio.com/docs.

VS Code Extensions

VS Code was built to be extended. An extension adds functionality, such as spell-checking or markdown checking. I originally authored this blog post using Markdown, with the Markdown All In One Extension. If I am to author in Markdown, VS Code is my go-to tool. 

I am working on a book and use Github Gists. To assist in managing my Gists, I also use the GistPad Extension.  It makes handling Gists so much easier. The integration between VS Code and GitHub, via the extension, is really useful.

To customise the colour scheme of VS Code - you can find many extensions providing additional themes. And as a hint, some of these themes are better than others! For details on the available extensions (and there are a lot) see https://marketplace.visualstudio.com/VSCode.

And of course, a great extension anyone using ISE is going to want to get is the PowerShell ISE extension that makes VS code feel more like the ISE, at least colour wise.

Installing VS Code

To install VS Code, well - there's a PowerShell script for that too. Naturally! It is called Install-VSCode and you download it from the PS Gallery. That script when you run it, downloads and installs the latest version of VS Code and provides you with flexibility over exactly what to install.

You can find any number of cute 1-liners, but here's a more workman-like, step by step, and hopefully clearer installation snippet: 

# Get and save Install-VSCode installation script
#    Assumes C:\Foo exists
Set-Location -Path C:\Foo
Save-Script -Name Install-VSCode -Path C:\Foo

# Create a list of extensions to add when installing
$Extensions =  'Streetsidesoftware.code-spell-checker',
               'yzhang.markdown-all-in-one',
               'davidanson.vscode-markdownlint',
               'vsls-contrib.gistfs'

# Now install VS Code
$InstallHT = @{
  BuildEdition         = 'Stable-System'
  AdditionalExtensions = $Extensions
  LaunchWhenDone       = $true
}.\Install-VSCode.ps1 @InstallHT

The install script is able to load different versions (Stable-System, Stable-User, Insider-System, Insider-User). Different builds provide more recent features but maybe less well tested and less reliable. I use Stable-System and have not had any issues whatsoever (aside from being able to get PSReadline to behave - but that is a rant for another day)

When you run this snippet, for example in the Windows PowerShell or the ISE, you may see some warning messages when VS Code adds the extensions. You can ignore these errors. FWIW It seems these warning messages have gone away with the latest builds of VS Code so you may not see these today.

This snippet takes around 30-40 seconds and rewards you, in the end, with VS Code open and ready for use.

You may have noticed reading that snippet that it did not explicitly mention the PowerShell extensions. The good news is that the script installs this extension by default.
It sure seems like a good idea to me! However, the ISE theme is not used by default - but there are scripts to fix that too.

Here are a two screenshots of VS code (with the ISE Theme) and the Windows PowerShell ISE.






For more details on setting up VS Code, see: https://code.visualstudio.com/docs/setup/setup-overview

Managing VS Code Extensions

You can manage and configure VS Code extensions inside VS Code or externally. In early versions of VS Code, you had to hand configure a JSON file to change settings, but today, there's a gui for that. And once you install VS Code, you can manage extensions (from PowerShell) like this:

#    Sets the root path for extensions
code --extensions-dir


#    Lists the installed extensions.
code --list-extensions

#    Uninstalls an extension.
code --uninstall-extension ( | )

VS Code PowerShell Extension

As I mentioned earlier, one extension most ISE users are going to want to get is the Powershell ISE extension. The PowerShell extension adds great language support and great features including: 
  • PowerShell Syntax highlighting
  • Tab completion
  • Code snippets
  • IntelliSense for cmdlets, parameters, and more
  • The rule-based analysis provided by PowerShell Script Analyzer
  • Definition tracking and a "Go to definition" for cmdlets and variables
  • Find references of commands and variables
  • Document and Workspace symbol discovery
  • Run the selected section of PowerShell code using F8
  • Launch online help for the symbol under the cursor using Ctrl + F1
  • Local script debugging and basic interactive console support
  • A colour scheme that looks familiar.
In my experience, VS Code is just different enough from the ISE to make those first few hours a tad painful.  But quickly, very quickly, VS Code begins to make the ISE look quite dated. I love having PS Script Analyzer run as I am entering code - it helps me to write better code And the side by side editing has made my book-writing task a lot simpler. 

For more details on the extension, see https://code.visualstudio.com/docs/languages/powershell.


Configuring the PowerShell Extension

You can update and configure extensions from within VS Code itself. In early versions of VS Code, any configuration had to be done by hand-editing a JSON file. Later versions added a configuration GUI meaning you can do most configuration simply using the GUI.
But you can also directly edit the **settings.json** file to update the configuration.

The VS Code user settings file is contained in the file:
**C:\Users\\AppData\Roaming\Code\User\settings.json**

My current settings.json file looks like this:

{
  "workbench.colorTheme": "PowerShell ISE",
  "window.zoomLevel": 1,
  "editor.fontFamily": "'Cascadia Code',Consolas,'Courier New'",
  "editor.tabCompletion": "on",
  "workbench.editor.highlightModifiedTabs": true,
  "powershell.codeFormatting.useCorrectCasing": true,
  "files.autoSave": "onWindowChange",
  "files.defaultLanguage": "powershell"
}


A neat feature of VS Code - if you update that file and save it, VS Code uses the newly created configuration automatically.

In the earlier snippet, you installed VS Code.
At the end of the configuration, you could do this to further configure VS Code:

# Download Cascadia Code font from GitHub
$CascadiaFont    = 'Cascadia.ttf'    # font name
$CascadiaRelURL  = 'https://github.com/microsoft/cascadia-code/releases'
$CascadiaRelease = Invoke-WebRequest -Uri $CascadiaRelURL # Get all of them
$CascadiaPath    = "https://github.com" + ($CascadiaRelease.Links.href | 
                      Where-Object { $_ -match "($cascadiaFont)" } | 
                        Select-Object -First 1)
$CascadiaFile   = "C:\Foo\$CascadiaFont"

# Download Cascadia Code font file
Invoke-WebRequest -Uri $CascadiaPath -OutFile $CascadiaFile

# Install Cascadia Code
$FontShellApp = New-Object -Com Shell.Application
$FontShellNamespace = $FontShellApp.Namespace(0x14)
$FontShellNamespace.CopyHere($CascadiaFile, 0x10)

# Install the font using Shell.Application COM object
$Destination = (New-Object -ComObject Shell.Application).Namespace(0x14)
$Destination.CopyHere($CascadiaFile,0x10)

# Create a short cut to VSCode
$SourceFileLocation  = "$env:ProgramFiles\Microsoft VS Code\Code.exe"
$ShortcutLocation    = "C:\foo\vscode.lnk"

# Create a  new wscript.shell object
$WScriptShell        = New-Object -ComObject WScript.Shell
$Shortcut            = $WScriptShell.CreateShortcut($ShortcutLocation)
$Shortcut.TargetPath = $SourceFileLocation

#Save the Shortcut to the TargetPath
$Shortcut.Save()

# Create a short cut to PowerShell 7
$SourceFileLocation  = "$env:ProgramFiles\PowerShell\7-Preview\pwsh.exe"
$ShortcutLocation    = 'C:\Foo\pwsh.lnk'

# Create a  new wscript.shell object
$WScriptShell        = New-Object -ComObject WScript.Shell
$Shortcut            = $WScriptShell.CreateShortcut($ShortcutLocation)
$Shortcut.TargetPath = $SourceFileLocation

# Save the Shortcut to the TargetPath
$Shortcut.Save()
$XML = @'
xmlns="http://schemas.microsoft.com/Start/2014/LayoutModification"
xmlns:defaultlayout="http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
xmlns:start="http://schemas.microsoft.com/Start/2014/StartLayout"
xmlns:taskbar="http://schemas.microsoft.com/Start/2014/TaskbarLayout"
    Version="1">
 '@
$XML | Out-File -FilePath C:\Foo\Layout.xml

# Import a startlayut.XML file
Import-StartLayout -LayoutPath C:\Foo\Layout.xml -MountPath C:\

# Update Local User Settings for VS Code
#    This step in particular needs to be run in PowerShell 7!
$JSON = @'
{
  "editor.fontFamily": "'Cascadia Code',Consolas,'Courier New'",
  "editor.tabCompletion": "on",
  "files.autoSave": "onWindowChange",
  "files.defaultLanguage": "powershell",
  "powershell.codeFormatting.useCorrectCasing": true,
  "window.zoomLevel": 1,
  "workbench.editor.highlightModifiedTabs": true,
  "workbench.colorTheme": "PowerShell ISE",
  }
'@
$JHT = ConvertFrom-Json -InputObject $JSON -AsHashtable
$PWSH = "C:\\Program Files\\PowerShell\\7\\pwsh.exe"
$JHT += @{
  "terminal.integrated.shell.windows" = "$PWSH"
}
$Path = $Env:APPDATA
$CP   = '\Code\User\Settings.json'
$Settings = Join-Path  $Path -ChildPath $CP
$JHT |
  ConvertTo-Json  |
    Out-File -FilePath $Settings

This snippet downloads and installs a new font, Cascadia Code and creates two new shortcuts for your toolbar. The snippet also updates the settings.json file with certain useful settings.

Summary

PowerShell 7, has shipped. If you are a Windows PowerShell, and particularly a fan of the ISE, VS Code is a tool to take on board. To assist you, the PowerShell extension makes VS Code easier to adopt. And the extensions available take VS Code to the next level.


TL;DR: PowerShell 7 with VS Code with PowerShell 7 rocks.
The PowerShell Extension to VS Code just rocks more!

What are you waiting for?

3 comments:

John J. Kavanagh said...

Great article because moving to VSCode is worth it.. Built-In spell check? What is this you speak of? I have tried several and eventually just turn them off.

10 Print "Hello World" 20 Goto 10 said...

Hey Thomas.

Thanks for showing how to install fonts. Unfortunately it does not work for me, and I am puzzled why.
From what I understood, the two lines
$FontShellNamespace.CopyHere($cascadiaFilePL, 0x10)
and
$Destination.CopyHere($DLFile,0x10)
both use a variable that has not been defined before ($cascadiaFilePL and $DLFile). Are those populated somewhere else or is their definition maybe missing?

Have a great day!

mael

Thomas Lee said...

Good catch. There were a couple of typos - now fixed in the code.

That first line is not needed (there's a version of the font with Powerline symbols. I thought I'd gotten rid of that! And the second should read:
$Destination.CopyHere($CascadiaFile,0x10)