226 lines
8.4 KiB
Ruby
226 lines
8.4 KiB
Ruby
# -*- 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
|