Az ISE testreszabása

Az 1.1.12 A grafikus PowerShell felület – Integrated Scripting Environment fejezetben már jónéhány újdonságát említettema 4.0-ás PowerShell ISE-nek. Most itt elsődelgesen az objektummodelljét és a testreszabásának lehetőségeit villantom fel.

Az ISE objektummodellje

Elsőként fontos tisztázni, hogy az ISE alkalmazás nem csak egy PowerShell futtatási környezetet tud magában foglalni, hanem többet is. Ha a File menüben a New PowerShell Tab menüre kattintunk, akkor ezt láthatjuk is:

91 . ábra Az ISE több PowerShell környezetet is tud kezelni

Ugyan ritkán használjuk a több PowerShell fül lehetőségét, de azért nem árt tudni róla, főleg az ISE objektummodellje kapcsán.

Ezek után nézzük, az objektummodellt! Nem kell túl sokat keresni, hiszen a $psISE változó és tulajdonságai hordozzák ezt:

PS C:\> $psISE

 

 

CurrentPowerShellTab         : Microsoft.PowerShell.Host.ISE.PowerShellTab

CurrentFile                  : Microsoft.PowerShell.Host.ISE.ISEFile

CurrentVisibleHorizontalTool :

CurrentVisibleVerticalTool   :

Options                      : Microsoft.PowerShell.Host.ISE.ISEOptions

PowerShellTabs               : {PowerShell 1}

A PowerShellTabs és a CurrentPowerShellTab tulajdonság az előzőek alapján már érthető és világos. Az Options tartalmazza az ISE összes beállítási lehetőségét:

PS C:\Users\ST> $psISE.Options

 

 

SelectedScriptPaneState                   : Top

ShowDefaultSnippets                       : False

ShowToolBar                               : True

ShowOutlining                             : True

ShowLineNumbers                           : True

TokenColors                               : {[Attribute, #FF00BFFF], [Command, #FF0000FF], [CommandArgument, #FF8A2BE2], [CommandParameter, #FF000080]...}

ConsoleTokenColors                        : {[Attribute, #FFB0C4DE], [Command, #FFE0FFFF], [CommandArgument, #FFEE82EE], [CommandParameter, #FFFFE4B5]...}

XmlTokenColors                            : {[Comment, #FF006400], [CommentDelimiter, #FF008000], [ElementName, #FF8B0000], [MarkupExtension, #FFFF8C00]...}

DefaultOptions                            : Microsoft.PowerShell.Host.ISE.ISEOptions

FontSize                                  : 9

Zoom                                      : 100

FontName                                  : Lucida Console

ErrorForegroundColor                      : #FFFF0000

ErrorBackgroundColor                      : #00FFFFFF

WarningForegroundColor                    : #FFFF8C00

WarningBackgroundColor                    : #00FFFFFF

VerboseForegroundColor                    : #FF00FFFF

VerboseBackgroundColor                    : #00FFFFFF

DebugForegroundColor                      : #FF00FFFF

DebugBackgroundColor                      : #00FFFFFF

ConsolePaneBackgroundColor                : #FF012456

ConsolePaneTextBackgroundColor            : #FF012456

ConsolePaneForegroundColor                : #FFF5F5F5

ScriptPaneBackgroundColor                 : #FFFFFFFF

ScriptPaneForegroundColor                 : #FF000000

ShowWarningForDuplicateFiles              : True

ShowWarningBeforeSavingOnRun              : False

UseLocalHelp                              : True

AutoSaveMinuteInterval                    : 2

MruCount                                  : 10

ShowIntellisenseInConsolePane             : True

ShowIntellisenseInScriptPane              : True

UseEnterToSelectInConsolePaneIntellisense : True

UseEnterToSelectInScriptPaneIntellisense  : True

IntellisenseTimeoutInSeconds              : 3

Jó része a beállításoknak a színekre vonatkozik. Például én nem szeretem, hogy alaphelyzetben az ISE konzol része sötétkék hátterű fehér betűkkel, hiszen most már „rich text" módon másolódik ki onnan a szöveg, és ezt beillesztve levélbe vagy egyéb alkalmazásba nem annyira szép, ráadásul, ha esetleg valaki ki akarja nyomtatni, az sok festéket fogyaszt. Sőt, bizonyos alkalmazások a háttérszínt nem jelenítik meg, csak a betűk színét, így fehér alapon a kimásolt fehér betűk nem túl informatívak.

Erre a problémára készítettem egy kis függvényt, ami lemásolja a szkriptrész színeit a konzol részre. Ugyan a színek állíthatók a Tools/Options… menüpont alatt, de mivel nagyon sokfajta dolognak van színe, ezért ez a színmásolás nem túl egyszerű manuálisan. Az automatizálást végző függvény így néz ki:

function Copy-ScriptColorsToConsole {

$psise.Options.consolePaneBackgroundColor  = $psise.Options.ScriptPaneBackgroundColor 

$psise.Options.consolePaneForegroundColor = $psise.Options.ScriptPaneForegroundColor

$psise.Options.consolePaneTextBackgroundColor = $psise.Options.ScriptPaneBackgroundColor

 

foreach ($key in ($psise.Options.TokenColors.keys |%{$_.tostring()})) {

    $color = $psise.Options.TokenColors.Item($key)

    $newcolor = [System.Windows.Media.Color]::FromArgb($color.a,$color.r,$color.g,$color.b)

    $psise.Options.ConsoleTokenColors.Item($key) = $newcolor

}

}

Ha mégis vissza szeretnénk állítani a konzol eredeti színösszeállítását, akkor a következő függvény futtatásával tehetjük meg:

function Restore-ConsoleColors {

    $bg = [System.Windows.Media.Color]::FromArgb(255,1,36,86)

    $fg = [System.Windows.Media.Color]::FromArgb(255,245,245,245)

    $tb = [System.Windows.Media.Color]::FromArgb(255,1,36,86)

    $psise.Options.consolePaneBackgroundColor = $bg

    $psise.Options.consolePaneForegroundColor = $fg

    $psise.Options.consolePaneTextBackgroundColor = $tb

    $psise.Options.RestoreDefaultConsoleTokenColors()

}

Amint látható, ugyan van egy RestoreDefaultConsoleTokenColors()metódusunk, de ez tényleg csak a tokenek színösszeállítását szabályozza, a háttér-, előtér- és szövegháttér színét nem, így ezeket külön kell visszaállítani.

Az ISE beállításával való játszadozáson kívül újabb menüpontok definiálásával is testre szabhatjuk a szkriptszerkesztőnket. Ezek a menüelemek az Adds-on menüben jelennek meg és bármilyen szkriptet rendelhetünk hozzájuk. A menüket definiáló egyszerű függvény így néz ki:

function New-ISEAddOnMenu {

param(

    $root,

    $menu,

    $command,

    $shortcut

)

 

$rootmenu = $psISE.CurrentPowershellTab.AddOnsMenu.SubMenus |

    Where-Object {$_.displayname -eq $root}

if(!$rootmenu){

    $rootmenu = $psISE.CurrentPowershellTab.AddOnsMenu.SubMenus.Add($root,$null,$null)

}

 

$submenu = $rootmenu.Submenus.add($menu, $command, $shortcut)

}

Ez létrehoz egy $root nevű „főmenüt” és alatta egy $menu nevű „almenüt”, melyhez hozzárendeli a $command szkriptblokkot és opcionálisan hozzá egy $shortcut gyorsbillentyű-kombinációt. Például:

PS C:\> New-ISEAddOnMenu -root "_TiborBővítményei" -menu Idő -command {get-date} ‑shortcut "Shift+Alt+D"

Ennek eredménye:

92 . ábra Új menüpont létrehozása

Ha az „Idő” menüre kattintunk, vagy az Alt+Shift+D billentyűkombináció lenyomjuk, akkor végrehajtódik a get-date parancs:

PS C:\> get-date

 

2015. január 5. 21:50:36

 Persze parancsként ennél értelmesebb dolgokat is létrehozhatunk. Például készítsünk egy olyan szkriptet, ami az összes ISE-ben megnyitott elmentetlen szkriptet elmenti!

function Global:Save-ISEAllFiles {

$psISE.PowerShellTabs |

    ForEach-Object {$_.files} |

        Where-Object {!$_.IsSaved} |

            ForEach-Object {

                if($_.IsUntitled){

                    $originalname = $name = $_.DisplayName -replace "\*",""

                    $path = Split-Path $_.fullpath

                    while(Test-Path (join-path $path $name)){

                        $name = [regex]::Replace($name, "\d+(?=\.)(?!\..*?\.)", {[int]$args[0].value + 1})

                    }

                    $_.SaveAs((Join-Path $path $name))

                }

                else{

                    $_.save()

                }

                Write-Host "Script $originalname is saved as $($_.fullpath)"

            }

}

$rootmenu = $psISE.CurrentPowershellTab.AddOnsMenu.SubMenus.Add("SaveAll",{Save-ISEAllFiles},"Ctrl+Shift+S")

A fenti szkriptben definiált Save-ISEAllFiles végigmegy az összes PowerShellTab-on és azok összes fálján. Amennyiben egy fájl még nincs elmentve (IsSaved tulajdonság) azt megpróbálja két módon elmenteni. Ha a fájl még elnevezetlen (IsUntitled) akkor megnézi az aktuális könyvtárban (fullpath-ból kiszámított $path), hogy az aktuális névvel ($name) van-e már fájl. Ha igen, akkor a regex Replace statikus metódusával az általában UntitledX.ps1 formájú névben megnöveli az X-et eggyel (lásd a 1.4.7.16 Csere .NET osztállyal fejezetet) és újra megnézi a While ciklusban, hogy ilyen fájl van-e. Ha nincs, akkor ezzel az új névvel menti el.

Ha a szkript már korábban el volt mentve, akkor csak simán elmenti a Save() metódussal. Az egész szkript a SaveAll menühöz van rendelve Ctrl+Shift+S billentyűkombinációval.

Egy másik értelmes funkció lehet az aktuálisan szerkeszett fájlon kívüli összes fájl becsukása. Az alábbi szkript ezt hozza be:

function Global:Close-ISEOtherTabs {

$filestoclose = ($psISE.CurrentPowerShellTab.Files |

    Where-Object {$_.fullpath -ne $psISE.CurrentFile.FullPath}).clone()

$filestoclose | ForEach-Object {

    ForEach-Object { $psISE.CurrentPowerShellTab.Files.Remove($_, $true)}

}}

$rootmenu = $psISE.CurrentPowershellTab.AddOnsMenu.SubMenus.Add("CloseOtherTabs",{Close-ISEOtherTabs},"Ctrl+Shift+C")

Itt a tanulság az, hogy a fájlok becsukása a PowerShellTab Files tulajdonságának Remove metódusával történik, azaz nem magának a fájlnak van Remove-ja, hanem a „szülő” tulajdonságnak.

A másik tanulság, hogy a bezárandó fájlokon gyűjteményén ($filestoclose) nem lehet direktben elvégezni a bezárás műveletét, mert az magát a gyűjtemény elemét is megszűnteti. Ezt nem nagyon szereti a PowerShell, hogy a ciklussal éppen feldolgozott gyűjtemény elemeit menet közben eltávolítsunk vagy éppen újabb elemmel bővítsük, mert ez akár végtelen ciklushoz is vezethet. Ezért itt a Clone() metódussal egy másolatot készítek a törlendő fájlokról, így a tényleges törlés erre nem fog hatni.

Harmadik példaként nézzük meg, hogy a szerkesztett szkriptekkel mit tudunk kezdeni! Gyakran van, hogy egy nagyobb változóinicializáló részt vagy hashtábla definíciót szeretnénk szépen megformázni, azaz az egyenlőségjelek kerüljenek egymás alá. Azaz ezt szeretném:

 

93 . ábra Ebből   à     ezt szeretném

Megoldásként ezt a Format-ISEText függvényt készítettem, amit szintén hozzáadok az Add-Ons menühöz:

function global:Format-ISEText {

$separator = "="

$selectedtext = $psISE.CurrentFile.Editor.SelectedText -split "\r\n"

$maxpos = 0

foreach($line in $selectedtext){

    $separatorpos = $line.indexof($separator)

    if($maxpos -lt $separatorpos){

        $maxpos = $separatorpos

    }

}

 

$newtext = $(foreach($line in $selectedtext){

    $first, $second = $line -split "(?=$([regex]::Escape($separator)))", 2

    $first.padright($maxpos) + $second

}) -join "`r`n"

$psISE.CurrentFile.Editor.InsertText($newtext)

}

$rootmenu = $psISE.CurrentPowershellTab.AddOnsMenu.SubMenus.Add("AlignText",{Format-ISEText},"Ctrl+Shift+A")

Látható, hogy a SelectedText tulajdonság felhasználásával vizsgálom meg az egyenlőségjel elhelyezkedését minden kijelölt sorokban. A legtávolabbi pozíciót a $maxpos változóban tárolom, majd a többi sort is ehhez igazítom a második foreach ciklusban.

Az Editor InsertText metódusának csak egy paramétere van, maga a beillesztendő szöveg, a pozícionálást maga a kijelölés dönti el. Ha van kijelölt szöveg, akkor a beillesztés oda történik, ha nincs, akkor a kurzor pozíciójába illesztődik be a szöveg.

Ezzel a három példával talán sikerült némi betekintést nyújtani az ISE objektummodelljébe. Természetesen nem törekedtem minden tulajdonság és metódus bemutatására, inkább csak kedvet szerettem volna csinálni ahhoz, hogy mindenki magának találjon ki hasznos bővítményeket, amivel hatékonyabbá teheti a munkát.

Snippetek testre szabása

Nagyon praktikus újdonság az ISE-ben a beilleszthető kódrészletek (snippet) lehetősége. Ez főleg azok számára jelent előnyt, akik nem napi rendszerességgel írnak PowerShell szkripteket és jól jön nekik a segítség, hogy felidézzék mondjuk a for ciklus vagy a switch kifejezés szerkezetét.

Ha  a Ctrl+J billentyűkombinációval előhívjuk a Snippet listát, akkor egyszerűen beilleszthetjük például a for ciklus sablonját:

for ($i = 1; $i -lt 99; $i++)

{

   

}

Személy szerint nekem nem tetszik, hogy a nyitó kapcsos zárójel új sorban kezdődik, én jobban szeretem, ha ez az első sor végén van ilyeténképpen:

for ($i = 1; $i -lt 99; $i++) {

   

}

 Szerintem ez utóbbi forma szebben mutat összecsukott állapotban az ISE felületén:

94 . ábra Összecsukott FOR ciklusok, nekem a második jobban tetszik

A gyári snippeteket sajnos nem lehet módosítani, de felhasználhatjuk azokat egyéni snippetek létrehozására. A következő szkripttel ezt teszem: a gyári snippeteket kiolvasva, ha azokban olyan sort találok, amelyikben csak egy nyitó kapcsos zárójel vagy gömbölyű zárójel van, akkor azokat az előző sorhoz csapom és ez alapján saját snippeteket hozok létre, majd a gyáriakat elrejtem a megfelelő ISE opció kikapcsolásával:

$psISE.Options.ShowDefaultSnippets = $true

$defaultsnippets = $psISE.CurrentPowerShellTab.Snippets | Where-Object -Property IsDefault

$defaultsnippets | ForEach-Object {

    $newcode = $_.codefragment -split "\r\n" | % -Begin {$prevline = ""} -Process {

            if($_.trim() -eq "{" -or $_.trim() -eq "("){

                $prevline + " " + $_.trim()

                $prevline = ""

            }

            else{

                if($prevline){

                    $prevline

                }

                $prevline = $_

            }

        } -End {$prevline}

 

    New-IseSnippet -Title $_.DisplayTitle -Description $_.Description -Text ($newcode -join "`r`n") -Author "Soós Tibor" -Force

}

$psISE.Options.ShowDefaultSnippets = $false

A fenti szkriptben a New-IseSnippet  cmdlettel hozom létre a saját kódmintáimat. Ez a cmdlet a „My Documents” alá hoz létre egy WindowsPowershell\Snippets alkönyvtárat és oda rakja le azokat snippets.ps1xml fájlokba:

PS C:\> dir (join-path ([environment]::GetFolderPath("MyDocuments")) "WindowsPowerShell\Snippets")

 

 

    Directory: C:\Documents\WindowsPowerShell\Snippets

 

 

Mode                LastWriteTime     Length Name                                                                                                                                             

----                -------------     ------ ----                                                                                                                                             

-a---       2015.01.01.     23:00       2677 Cmdlet (advanced function) - complete.snippets.ps1xml                                                                                           

-a---       2015.01.01.     23:00       1333 Cmdlet (advanced function).snippets.ps1xml                                                                                                      

-a---       2015.01.01.     23:00        685 Comment block.snippets.ps1xml                                                                                                                    

-a---       2015.01.01.     23:00        698 do-until.snippets.ps1xml                                                                                                                         

Ha ebben a könyvtárban megfelelő fájlok vannak, akkor azokat az ISE induláskor automatikusan betölti, így a későbbiekben ezzekkel nem kell foglalkozni. Ha nem ezen a helyen szeretnénk tárolni a snippetjeinket, akkor az Import-IseSnippet cmdlettel tudjuk azokat betölteni a rendszerbe.

 



Word To HTML Converter