none
Powershell ISE – change indent/tab size + keep tabs

    Question

  • Anyway to change indent/tab size in Powershell ISE?
    Something simple like in Visual studio IDE:
    Tab size 2
    Indent size 2
    Keep tabs

    Wednesday, March 07, 2012 5:18 PM

Answers

All replies

  • No.  No way. (Not in V3 Beta either)

    Grant Ward, a.k.a. Bigteddy

    What's new in Powershell 3.0 (Technet Wiki)


    • Edited by Bigteddy Wednesday, March 07, 2012 6:01 PM V3 comment
    Wednesday, March 07, 2012 5:32 PM
  • The PowerShell ISE doesn't let you configure the tab size or to insert tabs instead of spaces.

    Bill

    Wednesday, March 07, 2012 5:37 PM
    Moderator
  • Sorry - not in this version.  Tabs are fixed a four spaces and not saved but are converted to spaces.


    ¯\_(ツ)_/¯

    Wednesday, March 07, 2012 5:45 PM
  • Make a suggestion on Connect:

    https://connect.microsoft.com/PowerShell

    I'd vote for it :)

    I have a few suggestions out there for the ISE as well....

    Karl


    My Blog: http://unlockpowershell.wordpress.com
    My Book: Windows PowerShell 2.0 Bible
    My E-mail: -join ("6B61726C6D69747363686B65406D742E6E6574"-split"(?<=\G.{2})",19|%{[char][int]"0x$_"})

    Wednesday, March 07, 2012 5:58 PM
  • I have created a suggestion on Connect for this: https://connect.microsoft.com/PowerShell/feedback/details/761424/ise-configurable-tab-size-convert-tabs-to-spaces

    Vote for it to get this implemented!


    Author of SetACL | web: http://helgeklein.com | Twitter: @HelgeKlein


    Thursday, September 06, 2012 12:40 PM
  • Thank you, and also thanks for writing SetACL.

    Bill

    Thursday, September 06, 2012 2:21 PM
    Moderator
  • FWIW

    In $psise.CurrentFile.Editor there is a private property called EditorOperations. EditorOperations.SupportedOptions has two options "Tabs/TabSize" and "Tabs/IndentSize". 

    void EditorOperations.options.SetOptionValue(string optionId, object value) is a method that apparently sets editor options. Unfortunately invoking SetOptionValue throws an exception "The calling thread cannot access this object because a different thread owns it.".


    • Edited by NoSimDash Sunday, May 26, 2013 8:40 PM correct mistake
    Sunday, May 26, 2013 8:38 PM
  • I was able to set IndentSize to 2. I used a psIse Dispatcher to get around the exception mentioned in the previous post. The dispatcher is passed a delegate which gets invoked on the psIse thread that owns the editor options. Here is C# class that does part of the work:

    $src=@"
    // Borrowed this from MSDN sample under PushFrame
    public MethodInfo methInfo;
    public Object opt;
    public Dispatcher disp;
    
    public void DoEvents(){
        DispatcherFrame frame = new DispatcherFrame();
        disp.BeginInvoke(DispatcherPriority.Normal,
            new DispatcherOperationCallback(ExitFrames),frame);
        Dispatcher.PushFrame(frame);
    }
    
    public object ExitFrames(object f){
        DispatcherFrame df = ((DispatcherFrame)f);
        Object[] args = {"Tabs/TabSize",2};
        methInfo.Invoke( opt, args);
        args[0] = "Tabs/IndentSize";
        methInfo.Invoke( opt, args);
        df.Continue = false;
        return null;
    }
    "@
    
    $ns="a"
    add-type $src -name eventually -NameSpace $ns -UsingNamespace System.Windows.Forms,System.Windows.Threading,System.Reflection, Microsoft.VisualStudio.Text.EditorOptions.Implementation -ReferencedAssemblies windowsbase,system.windows.forms,Microsoft.PowerShell.Editor

    And then the powershell code to run it:

    $ed = $psise.CurrentFile.Editor
    #selp is like select-object but it selects private properties
    $opt  = $ed|selp EditorOperations|select -ExpandProperty Options
    $disp = $ed|selp EditorViewHost|select -ExpandProperty Dispatcher
    $invokeMethInfo = $opt.GetType().getmethod("SetOptionValue",    [type[]]@([string],[object]))
    
    $ev=new "$ns.eventually"
    $ev.disp = $disp
    $ev.methInfo = $invokeMethInfo 
    $ev.opt = $opt
    $ev.doevents()
    As you can probably guess this only affects the CurrentFile. However $opt has a Parent property. I think if you substitute $opt.Parent or $opt.Parent.Parent or similar you might affect all open files and any file that gets opened afterwards
    • Proposed as answer by NoSimDash Tuesday, July 02, 2013 12:09 AM
    Monday, July 01, 2013 4:23 PM
  • Figured I'd share the changes I made to NoSimDash's above code. I actually wrote this not long after he posted his reply, but decided not to post it after I settled on taking the cheap, hacky timer route. (Originally I was looking to have the process triggered by an event when the user changed tabs)

    Anyways, over the last two years it's been pretty stable, so I thought I'd share on the off-chance that someone goes looking for this functionality again.

    $psISE.Options.ShowOutlining = $true
    $psISE.Options.ShowDefaultSnippets = $true
    $psISE.Options.ShowIntellisenseInScriptPane = $true
    $psISE.Options.ShowIntellisenseInConsolePane = $true
    
    # 
    Set-Variable -Option AllScope -Name OptionSetter -Value (&{
    	$ClassName = 'IndentFixer'
    	$Namespace = 'ISEHijack'
    	Add-Type @"
    		internal class _Option<TValue>
    		{
    			public _Option(string key, TValue value)
    			{
    				Key = key;
    				Value = value;
    			}
    
    			public Object[] Params { get { return new object[] { Key, Value }; } }
    			public string Key { get; private set; }
    			public TValue Value { get; private set; }
    		}
    
    		internal static object[] Opt<TVal>(string key, TVal value)
    		{
    			return new _Option<TVal>(key, value).Params;
    		}
    	
    		internal static readonly object[][] NewOpts = {
    			Opt<bool>("Adornments/HighlightCurrentLine/Enable", false),
    			Opt<int>("Tabs/TabSize", 4),
    			Opt<int>("Tabs/IndentSize", 4),
    			Opt<bool>("Tabs/ConvertTabsToSpaces", false),
    			Opt<bool>("TextView/UseVirtualSpace", false),
    			Opt<bool>("TextView/UseVisibleWhitespace", true)
    		};
    
    		internal void SetOpts()
    		{
    			foreach(object[] args in NewOpts) {
    				Setter.Invoke(Options, args);
    			}
    		}
    
    		public MethodInfo Setter { get; set; }
    		public object Options { get; set; }
    		public Dispatcher EditorDispatcher { get; set; }
    		public List<Action> Actions { get; private set; }
    
    		public ${ClassName}()
    		{
    			Actions = new List<Action>();
    			Actions.Add(SetOpts);
    		}
    
    		public void Dispatch(Dispatcher dispatcher)
    		{
    			DispatcherFrame frame = new DispatcherFrame();
    			dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(ExitFrames), frame);
    			Dispatcher.PushFrame(frame);
    		}
    
    		private object ExitFrames(object f){
    			DispatcherFrame frame = ((DispatcherFrame)f);
    			// foreach(Action action in Actions) {
    				// action.Invoke();
    			// }
    			// Actions.Clear();
                foreach(object[] args in NewOpts) {
    				Setter.Invoke(Options, args);
    			}
    			frame.Continue = false;
    			return null;
    		}
    "@  -Name $ClassName `
    	-NameSpace $Namespace `
    	-UsingNamespace System.Windows.Forms,System.Windows.Threading,System.Reflection,Microsoft.VisualStudio.Text.EditorOptions.Implementation,System.Collections.Generic `
    	-ReferencedAssemblies WindowsBase,System.Windows.Forms,Microsoft.PowerShell.Editor
    	return `
               "${Namespace}.${ClassName}" |
    	       Add-Member -Type NoteProperty -Name Namespace -Value $Namespace -Passthru |
    	       Add-Member -Type NoteProperty -Name ClassName -Value $ClassName -Passthru
    })
    
    [System.Reflection.BindingFlags] $NonPublicFlags = [System.Reflection.BindingFlags] 'NonPublic,Instance'
    
    filter Expand-Property
    {
    	PARAM(
    		[Alias('Property')]
    		[ValidateNotNullOrEmpty()] 
    		[Parameter(Mandatory, Position=0)]
    		[String] $Name
    	)
    	$_ | Select-Object -ExpandProperty $Name | Write-Output
    }
    
    function Get-IsePrefs
    {
    	PARAM(
    		[Parameter(Mandatory, Position=1)]
    		[ValidateNotNullOrEmpty()]
    		[string] $Key
    	)
    	$oEditor = $psISE.CurrentFile.Editor
    	$tEditor = $oEditor.GetType()
    	$tEditor.GetProperty('EditorOperations', $NonPublicFlags).GetMethod.Invoke($oEditor, $null).Options | `
    		Expand-Property GlobalOptions | `
    		Expand-Property SupportedOptions | `
    		Select-Object -Property Key,ValueType,DefaultValue | `
    		Write-Output
    }
    
    function Comment-Selection
    {
    	$output = ''
    	$psISE.CurrentFile.Editor.SelectedText.Split("`n") | ForEach-Object -Process { $output += "# " + [string]$_ + "`n" }
    	$psISE.CurrentFile.Editor.InsertText($output)
    }
    
    function Fix-EditorIndentation
    {
    	PARAM(
    		[Alias('ISEEditor')][ValidateNotNull()]
    		[Parameter(Mandatory, ValueFromPipeline, Position=1)]
    		[Microsoft.PowerShell.Host.ISE.ISEEditor]
    		$Editor
    	)
    	$EditorType = $Editor.GetType()
    	$SetterInstance = New-Object -TypeName $OptionSetter
    	$SetterInstance.Options = $EditorType.GetProperty('EditorOperations', $NonPublicFlags).GetMethod.Invoke($Editor, $null).Options
    	$Dispatcher = $EditorType.GetProperty('EditorViewHost', $NonPublicFlags).GetMethod.Invoke($Editor, $null).Dispatcher
    	$SetterInstance.Setter = $SetterInstance.Options.GetType().GetMethod('SetOptionValue',  [type[]]@([string],[object]))
    	$SetterInstance.Dispatch($Dispatcher)
    }
    
    function Fix-IseIndentation
    {
    	[Microsoft.Windows.PowerShell.Gui.Internal.MainWindow] $ISEWindow = (Get-Host | Select-Object -ExpandProperty PrivateData | Select-Object -ExpandProperty Window)
    	[Microsoft.Windows.PowerShell.Gui.Internal.RunspaceTabControl] $RSTabCtrl = $ISEWindow.Content[0].Children[2]
    	[Microsoft.PowerShell.Host.ISE.PowerShellTab] $PSTab = $RSTabCtrl.Items[0]
    	$PSTab.Files | Select-Object -ExpandProperty Editor | Fix-EditorIndentation
    }
    
    function Invoke-ISEFunction
    {
    	PARAM(
    		[Parameter(Mandatory, Position=1)]
    		[Action] $ScriptBlock
    	)
    	$ISEWindow = $(Get-Host | Select-Object -ExpandProperty PrivateData | Select-Object -ExpandProperty Window)
    	return $ISEWindow.Dispatcher.Invoke($ScriptBlock)
    }
    
    function Fix-IndentationForAll
    {
    	 $psISE.PowerShellTabs | Select-Object -ExpandProperty 'Files' | Select-Object -ExpandProperty 'Editor' | Fix-EditorIndentation
    }
    # Startup
    
    function New-Timer
    {
    	PARAM(
    		[Alias('Handler')]
    		[Parameter(Position=0, Mandatory=$true)]
    		$Action,
    
    		[Double]
    		[Parameter(Mandatory=$false)]
    		$Interval = 50,
    		
    		[Boolean]
    		[Parameter(Mandatory=$false)]
    		$AutoReset = $true
    	)
    	[System.Timers.Timer] $oTimer = New-Object -TypeName System.Timers.Timer($Interval)
    	$ElapsedEvent = Register-ObjectEvent -InputObject $oTimer -EventName 'Elapsed' -Action $Action
    	$oTimer.AutoReset = $AutoReset
    	return $oTimer
    }
    
    $(New-Timer {
    	Fix-IndentationForAll
    } -Interval 100 -AutoReset $true).Start()


    Sunday, February 22, 2015 6:03 PM
  • If you want to share your scripts I recommend posting in the Gallery where it will be easier to find:

    https://gallery.technet.microsoft.com/ScriptCenter/


    ¯\_(ツ)_/¯

    Sunday, February 22, 2015 6:26 PM