# -*- coding: utf-8 -*- # # frozen_string_literal: true module Rouge module Lexers class Powershell < RegexLexer title 'powershell' desc 'powershell' tag 'powershell' aliases 'posh', 'microsoftshell', 'msshell' filenames '*.ps1', '*.psm1', '*.psd1', '*.psrc', '*.pssc' mimetypes 'text/x-powershell' # https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_cmdletbindingattribute?view=powershell-6 ATTRIBUTES = %w( ConfirmImpact DefaultParameterSetName HelpURI PositionalBinding SupportsPaging SupportsShouldProcess ) # https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-6 AUTO_VARS = %w( \$\$ \$\? \$\^ \$_ \$args \$ConsoleFileName \$Error \$Event \$EventArgs \$EventSubscriber \$ExecutionContext \$false \$foreach \$HOME \$Host \$input \$IsCoreCLR \$IsLinux \$IsMacOS \$IsWindows \$LastExitCode \$Matches \$MyInvocation \$NestedPromptLevel \$null \$PID \$PROFILE \$PSBoundParameters \$PSCmdlet \$PSCommandPath \$PSCulture \$PSDebugContext \$PSHOME \$PSItem \$PSScriptRoot \$PSSenderInfo \$PSUICulture \$PSVersionTable \$PWD \$REPORTERRORSHOWEXCEPTIONCLASS \$REPORTERRORSHOWINNEREXCEPTION \$REPORTERRORSHOWSOURCE \$REPORTERRORSHOWSTACKTRACE \$SENDER \$ShellId \$StackTrace \$switch \$this \$true ).join('|') # https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_reserved_words?view=powershell-6 KEYWORDS = %w( assembly exit process base filter public begin finally return break for sequence catch foreach static class from switch command function throw configuration hidden trap continue if try data in type define inlinescript until do interface using dynamicparam module var else namespace while elseif parallel workflow end param enum private ).join('|') # https://devblogs.microsoft.com/scripting/powertip-find-a-list-of-powershell-type-accelerators/ # ([PSObject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::Get).Keys -join ' ' KEYWORDS_TYPE = %w( Alias AllowEmptyCollection AllowEmptyString AllowNull ArgumentCompleter array bool byte char CmdletBinding datetime decimal double DscResource float single guid hashtable int int32 int16 long int64 ciminstance cimclass cimtype cimconverter IPEndpoint NullString OutputType ObjectSecurity Parameter PhysicalAddress pscredential PSDefaultValue pslistmodifier psobject pscustomobject psprimitivedictionary ref PSTypeNameAttribute regex DscProperty sbyte string SupportsWildcards switch cultureinfo bigint securestring timespan uint16 uint32 uint64 uri ValidateCount ValidateDrive ValidateLength ValidateNotNull ValidateNotNullOrEmpty ValidatePattern ValidateRange ValidateScript ValidateSet ValidateTrustedData ValidateUserDrive version void ipaddress DscLocalConfigurationManager WildcardPattern X509Certificate X500DistinguishedName xml CimSession adsi adsisearcher wmiclass wmi wmisearcher mailaddress scriptblock psvariable type psmoduleinfo powershell runspacefactory runspace initialsessionstate psscriptmethod psscriptproperty psnoteproperty psaliasproperty psvariableproperty ).join('|') OPERATORS = %w( -split -isplit -csplit -join -is -isnot -as -eq -ieq -ceq -ne -ine -cne -gt -igt -cgt -ge -ige -cge -lt -ilt -clt -le -ile -cle -like -ilike -clike -notlike -inotlike -cnotlike -match -imatch -cmatch -notmatch -inotmatch -cnotmatch -contains -icontains -ccontains -notcontains -inotcontains -cnotcontains -replace -ireplace -creplace -shl -shr -band -bor -bxor -and -or -xor -not \+= -= \*= \/= %= ).join('|') MULTILINE_KEYWORDS = %w( synopsis description parameter example inputs outputs notes link component role functionality forwardhelptargetname forwardhelpcategory remotehelprunspace externalhelp ).join('|') state :variable do rule %r/#{AUTO_VARS}/, Name::Builtin::Pseudo rule %r/(\$)(?:(\w+)(:))?(\w+|\{(?:[^`]|`.)+?\})/ do groups Name::Variable, Name::Namespace, Punctuation, Name::Variable end rule %r/\$\w+/, Name::Variable rule %r/\$\{(?:[^`]|`.)+?\}/, Name::Variable end state :multiline do rule %r/\.(?:#{MULTILINE_KEYWORDS})/i, Comment::Special rule %r/#>/, Comment::Multiline, :pop! rule %r/[^#.]+?/m, Comment::Multiline rule %r/[#.]+/, Comment::Multiline end state :interpol do rule %r/\)/, Str::Interpol, :pop! mixin :root end state :dq do # NB: "abc$" is literally the string abc$. # Here we prevent :interp from interpreting $" as a variable. rule %r/(?:\$#?)?"/, Str::Double, :pop! rule %r/\$\(/, Str::Interpol, :interpol rule %r/`$/, Str::Escape # line continuation rule %r/`./, Str::Escape rule %r/[^"`$]+/, Str::Double mixin :variable end state :sq do rule %r/'/, Str::Single, :pop! rule %r/[^']+/, Str::Single end state :heredoc do rule %r/(?:\$#?)?"@/, Str::Heredoc, :pop! rule %r/\$\(/, Str::Interpol, :interpol rule %r/`$/, Str::Escape # line continuation rule %r/`./, Str::Escape rule %r/[^"`$]+?/m, Str::Heredoc rule %r/"+/, Str::Heredoc mixin :variable end state :class do rule %r/\{/, Punctuation, :pop! rule %r/\s+/, Text::Whitespace rule %r/\w+/, Name::Class rule %r/[:,]/, Punctuation end state :hasht do rule %r/\s+/, Text::Whitespace rule %r/\}/, Punctuation, :pop! rule %r/"/, Str::Double, :dq rule %r/'/, Str::Single, :sq rule %r/\w+/, Name::Other rule %r/=/, Operator rule %r/,/, Punctuation mixin :variable end state :array do rule %r/\s+/, Text::Whitespace rule %r/\)/, Punctuation, :pop! rule %r/"/, Str::Double, :dq rule %r/'/, Str::Single, :sq rule %r/,/, Punctuation mixin :variable end state :bracket do rule %r/\]/, Punctuation, :pop! rule %r/[A-Za-z]\w+\./, Name::Constant rule %r/([A-Za-z]\w+)/ do |m| if ATTRIBUTES.include? m[0] token Name::Builtin::Pseudo else token Keyword::Type end end mixin :root end state :parameters do rule %r/\s*?\n/, Text::Whitespace, :pop! rule %r/[;(){}\]]/, Punctuation, :pop! rule %r/[|=]/, Operator, :pop! rule %r/[\/\\~\w][-.:\/\\~\w]*/, Name::Other rule %r/\w[-\w]+/, Name::Other mixin :root end state :root do rule %r/\s+/, Text::Whitespace rule %r/#requires\s-version \d(?:\.\d+)?/, Comment::Preproc rule %r/#.*/, Comment rule %r/<#/, Comment::Multiline, :multiline rule %r/"/, Str::Double, :dq rule %r/'/, Str::Single, :sq rule %r/@"/, Str::Heredoc, :heredoc rule %r/@'.*?'@/m, Str::Heredoc rule %r/\d*\.\d+/, Num::Float rule %r/\d+/, Num::Integer rule %r/\.\.(?=\.?\d)/, Operator rule %r/(?:#{OPERATORS})\b/i, Operator rule %r/(class)(\s+)(\w+)/i do groups Keyword::Reserved, Text::Whitespace, Name::Class push :class end rule %r/(function)(\s+)(?:(\w+)(:))?(\w[-\w]+)/i do groups Keyword::Reserved, Text::Whitespace, Name::Namespace, Punctuation, Name::Function end rule %r/(?:#{KEYWORDS})\b(?![-.])/i, Keyword::Reserved rule %r/-{1,2}\w+/, Name::Tag rule %r/([\/\\~\w][-.:\/\\~\w]*)(\n)?/ do |m| groups Name::Function, Text::Whitespace push :parameters unless m[2] end rule %r/(\.)?([-\w]+)(?:(\()|(\n))?/ do |m| groups Operator, Name::Function, Punctuation, Text::Whitespace push :parameters unless m[3] end rule %r/[-+*\/%=!.&|]/, Operator rule %r/@\{/, Punctuation, :hasht rule %r/@\(/, Punctuation, :array rule %r/\[/, Punctuation, :bracket rule %r/[{}(),:;]/, Punctuation mixin :variable end end end end