skip to Main Content

Pressing F2 when editing a PowerShell file does nothing in VS Code.
(for other languages like C#, Python rename refactor (F2) works fine)

What I’ve tried:

  • completely uninstalled MS PowerShell extension and reinstalled it, including VS Code restarts
  • checked the Keyboard Shortcuts: PowerShell keybindings are present when PowerShell extension is installed, not present when uninstalled
  • Rename Symbol (editor.action.rename) is bind to F2 (and again, it works for other languages)
  • Edited file extension is .PS1, I can run and debug it, so VS Code recognizes (?) it a PowerShell file

2

Answers


  1. This is not currently supported by VS Code’s builtin PowerShell language support or the non-builtin PowerShell VS Code extension, but there is an open feature-request issue ticket for it: F2 (rename) doesn’t work for PowerShell variables #1440, and also "Smart" variable rename
    #261
    . I suggest that you give those issue tickets a thumbs up to show support for it. You can also subscribe to them to get notified about discussion and progress. Please avoid making noisy comments there like ones that just consist of "+1" / "bump". If you’re interested in helping implement the feature, there’s related guidance here.

    Login or Signup to reply.
  2. To add to starball’s helpful answer:

    Indeed, the PowerShell extension does not offer renaming refactoring as of this writing, and it is important to note that given PowerShell’s dynamic – rather than lexical – scoping, renaming cannot be implemented robustly.[1]

    This is the primary reason this feature hasn’t materialized after years of debate: any implementation would amount to a compromise that makes certain assumptions and can break code. Striking the right balance between utility and safety is a tricky proposition, and there’s also the aspect of needing to inform the user of the potential of breaking code.

    That said, you can roll your own implementation, building on the great proof-of-concept provided by Patrick Meineke here, which allows you to choose which tradeoffs to make.

    Here’s a sample implementation based on the proof-of-concept that has the following limitations and makes the following choices:

    • Limitations:

      • Only renaming variables is supported (not also functions, methods, …) …

      • … in the same file.

      • For technical reasons, the rename operation isn’t atomic, which means that each document modification made in the course of renaming is a separate operation in terms of undo / redo functionality. To undo a renaming, it is therefore simpler to rename again and specify the old name.

    • Choices made:

      • Only variables with the same (non-)prefix are renamed (see exception re $using: below); e.g., invoking the function on $script:foo only renames reference to $script:foo, not also $foo

      • A $using: reference (in remoting / job / ForEach-Object -Parallel contexts) is treated like a non-prefixed variable, so that renaming $foo also renames $using:foo and vice versa.

      • Caveat: The above implies that if you happen to use a local variable in a remoting / job / ForEach-Object -Parallel context that happens to have the same name as non-prefixed variable in the caller’s scope, it is renamed too.

      • Whenever renaming is performed, a preexisting {...} enclosure of the name (e.g, ${foo}) is retained; otherwise, such an enclosure is only applied if necessary due to special characters in the new name.

    To use the implementation below:

    • Make sure that profile loading is enabled for the PowerShell extension:

      • Open the user settings (Ctrl-,), search for powershell profile, and make sure that PowerShell: Enable Profile Loading is checked.
    • Then, with a PowerShell file being displayed in the active editor, i.e. with the PIC (PowerShell-Integrated Console) running in Visual Studio Code’s integrated terminal, open the extension-specific profile file for editing by running psedit $PROFILE and paste the code below.

    • Then, in order to bind the F2 key to the custom function, open your keybindings.json file (via the command palette (Ctrl-Shift-P): Preferences: Open Keyboard Shortcuts (JSON)) and add the following:

      {
        "key": "F2",
        "command": "PowerShell.InvokeRegisteredEditorCommand",
        "args": {
          "commandName": "Rename-Variable"
        },
        "when": "editorLangId == 'powershell' && editorTextFocus"
      }
      

    Code to paste into $PROFILE:

    function Rename-Variable {
      param($Context)
    
      # Get the AST element at the cursor position.
      $variable = Find-Ast -AtCursor
      if ($variable -isnot [System.Management.Automation.Language.VariableExpressionAst]) {
        $psEditor.Window.ShowWarningMessage('No variable found at current cursor location.')
        return
      }
    
      # Prompt for a new name.
      $esp = [Microsoft.PowerShell.EditorServices.Extensions.EditorObjectExtensions, Microsoft.PowerShell.EditorServices]::GetExtensionServiceProvider($psEditor)
      $newName = $esp.EditorUI.PromptInputAsync('Enter new variable name').GetAwaiter().GetResult().Trim()
    
      # Validate the name; enclose in {...}, if necessary.
      if (-not $newName) { return }
      $isSpecialName = $newName -match '[^p{L}p{Nd}_]'
      if ($isSpecialName -and $newName -match '[}{]') { $newName = $newName -replace '[}{]', '`$&' }
    
      # Rename all references to variables with the *same name and prefix* in the 
      # current document.
      Find-Ast { $_.VariablePath.UserPath -eq $variable.VariablePath.UserPath } |
        Sort-Object { $_.Extent.StartOffset } -Descending |
        ForEach-Object {
          $usingPrefix = if ($_.Extent -match '^${?using:') { 'using:' }
          $prefix = if ($_.VariablePath -match ':') { $_.VariablePath -replace  '(?<=:).+$' }      
          $openDelim = if ($isSpecialName -or $_.Extent -like '${*') { '{' }
          $closeDelim = if ($openDelim) { '}' }
          $sigil = if ($_.Splatted) { '@' } else { '$' }
          $Context.CurrentFile.InsertText(
            ($sigil + $openDelim + $usingPrefix + $prefix + $newName + $closeDelim),
            $_.Extent.StartLineNumber,
            $_.Extent.StartColumnNumber,
            $_.Extent.EndLineNumber,
            $_.Extent.EndColumnNumber)
        }    
    }
    
    Register-EditorCommand -Name Rename-Variable -ScriptBlock ${function:Rename-Variable} -DisplayName 'Rename variables in the current document'
    

    [1] This challenge is also the reason that the only refactoring functionality offered (via Ctrl-Shift-R) is simple Surround With: functionality around the currently selected text.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search