@@ -111,3 +111,188 @@ func TestPowershellFormat_OpenDoubleQuote(t *testing.T) {
111111 t .Errorf ("powershell open double: State = %v, want QUOTING_ESCAPING_STATE" , last .State )
112112 }
113113}
114+
115+ func TestPowershellFormat_BacktickLineContinuation (t * testing.T ) {
116+ // backtick + newline should be consumed as line continuation, not part of word
117+ tokens , err := SplitWith ("echo foo`\n bar" , PowershellFormat ())
118+ if err != nil {
119+ t .Fatal (err )
120+ }
121+ words := tokens .Words ().Strings ()
122+ if len (words ) != 2 || words [1 ] != "foobar" {
123+ t .Errorf ("powershell line continuation: Words = %v, want [echo foobar]" , words )
124+ }
125+ }
126+
127+ func TestPowershellFormat_BacktickLineContinuationCRLF (t * testing.T ) {
128+ tokens , err := SplitWith ("echo foo`\r \n bar" , PowershellFormat ())
129+ if err != nil {
130+ t .Fatal (err )
131+ }
132+ words := tokens .Words ().Strings ()
133+ if len (words ) != 2 || words [1 ] != "foobar" {
134+ t .Errorf ("powershell line continuation CRLF: Words = %v, want [echo foobar]" , words )
135+ }
136+ }
137+
138+ func TestPowershellFormat_BacktickLineContinuationStartOfWord (t * testing.T ) {
139+ // backtick + newline at start of word — word continues on next line
140+ tokens , err := SplitWith ("echo `\n bar" , PowershellFormat ())
141+ if err != nil {
142+ t .Fatal (err )
143+ }
144+ words := tokens .Words ().Strings ()
145+ if len (words ) != 2 || words [1 ] != "bar" {
146+ t .Errorf ("powershell line continuation start: Words = %v, want [echo bar]" , words )
147+ }
148+ }
149+
150+ func TestPowershellFormat_BlockComment (t * testing.T ) {
151+ tokens , err := SplitWith ("echo <# multi\n line\n comment #> foo" , PowershellFormat ())
152+ if err != nil {
153+ t .Fatal (err )
154+ }
155+ words := tokens .Words ().Strings ()
156+ if len (words ) != 2 || words [0 ] != "echo" || words [1 ] != "foo" {
157+ t .Errorf ("powershell block comment: Words = %v, want [echo foo]" , words )
158+ }
159+ }
160+
161+ func TestPowershellFormat_BlockCommentSingleLine (t * testing.T ) {
162+ tokens , err := SplitWith ("echo <# inline comment #> foo" , PowershellFormat ())
163+ if err != nil {
164+ t .Fatal (err )
165+ }
166+ words := tokens .Words ().Strings ()
167+ if len (words ) != 2 || words [0 ] != "echo" || words [1 ] != "foo" {
168+ t .Errorf ("powershell block comment inline: Words = %v, want [echo foo]" , words )
169+ }
170+ }
171+
172+ func TestPowershellFormat_StopParsingToken (t * testing.T ) {
173+ // After --%, everything is literal until newline or |
174+ tokens , err := SplitWith ("echo --% /grant Dom\\ HVAdmin:(CI)(OI)F" , PowershellFormat ())
175+ if err != nil {
176+ t .Fatal (err )
177+ }
178+ words := tokens .Words ().Strings ()
179+ // --% should be a word, then the rest is raw text as one word
180+ if len (words ) < 3 {
181+ t .Errorf ("powershell --%%: Words = %v, expected at least 3 words" , words )
182+ }
183+ if words [0 ] != "echo" {
184+ t .Errorf ("powershell --%%: first word = %q, want echo" , words [0 ])
185+ }
186+ if words [1 ] != "--%" {
187+ t .Errorf ("powershell --%%: second word = %q, want --%%" , words [1 ])
188+ }
189+ }
190+
191+ func TestPowershellFormat_StopParsingPipeDelim (t * testing.T ) {
192+ // After --%, | is still a pipeline delimiter
193+ tokens , err := SplitWith ("echo --% foo | Select-String bar" , PowershellFormat ())
194+ if err != nil {
195+ t .Fatal (err )
196+ }
197+ pipelines := tokens .Pipelines ()
198+ if len (pipelines ) != 2 {
199+ t .Errorf ("powershell --%% pipe: %d pipelines, want 2" , len (pipelines ))
200+ }
201+ }
202+
203+ func TestPowershellFormat_StopParsingRawContent (t * testing.T ) {
204+ // After --%, content like (CI) should be literal, not split
205+ tokens , err := SplitWith ("icacls X: --% /grant Dom\\ HVAdmin:(CI)(OI)F" , PowershellFormat ())
206+ if err != nil {
207+ t .Fatal (err )
208+ }
209+ words := tokens .Words ().Strings ()
210+ // The raw content after --% should be one word
211+ if len (words ) != 4 {
212+ t .Errorf ("powershell --%% raw: Words = %v, want 4 words" , words )
213+ }
214+ if words [2 ] != "--%" {
215+ t .Errorf ("powershell --%% raw: third word = %q, want --%%" , words [2 ])
216+ }
217+ rawContent := words [3 ]
218+ if rawContent != "/grant Dom\\ HVAdmin:(CI)(OI)F" {
219+ t .Errorf ("powershell --%% raw: content = %q, want /grant Dom\\ HVAdmin:(CI)(OI)F" , rawContent )
220+ }
221+ }
222+
223+ func TestPowershellFormat_StreamRedirect2 (t * testing.T ) {
224+ // 2> should be recognized as a stream redirect
225+ tokens , err := SplitWith ("echo foo 2> error.txt" , PowershellFormat ())
226+ if err != nil {
227+ t .Fatal (err )
228+ }
229+ // Check that 2> is a single WORDBREAK_TOKEN with redirect type
230+ found := false
231+ for _ , tok := range tokens {
232+ if tok .Type == WORDBREAK_TOKEN && tok .RawValue == "2>" {
233+ if ! tok .WordbreakType .IsRedirect () {
234+ t .Errorf ("powershell 2>: WordbreakType = %v, want redirect" , tok .WordbreakType )
235+ }
236+ found = true
237+ }
238+ }
239+ if ! found {
240+ t .Errorf ("powershell 2>: no merged 2> token found in %v" , tokens )
241+ }
242+ }
243+
244+ func TestPowershellFormat_StreamRedirect2Append (t * testing.T ) {
245+ tokens , err := SplitWith ("echo foo 2>> error.txt" , PowershellFormat ())
246+ if err != nil {
247+ t .Fatal (err )
248+ }
249+ found := false
250+ for _ , tok := range tokens {
251+ if tok .Type == WORDBREAK_TOKEN && tok .RawValue == "2>>" {
252+ if ! tok .WordbreakType .IsRedirect () {
253+ t .Errorf ("powershell 2>>: WordbreakType = %v, want redirect" , tok .WordbreakType )
254+ }
255+ found = true
256+ }
257+ }
258+ if ! found {
259+ t .Errorf ("powershell 2>>: no merged 2>> token found in %v" , tokens )
260+ }
261+ }
262+
263+ func TestPowershellFormat_StreamRedirectMerge (t * testing.T ) {
264+ // 2>&1 should be recognized as a merged stream redirect
265+ tokens , err := SplitWith ("echo foo 2>&1" , PowershellFormat ())
266+ if err != nil {
267+ t .Fatal (err )
268+ }
269+ found := false
270+ for _ , tok := range tokens {
271+ if tok .Type == WORDBREAK_TOKEN && tok .RawValue == "2>&1" {
272+ found = true
273+ }
274+ }
275+ if ! found {
276+ t .Errorf ("powershell 2>&1: no merged token found in %v" , tokens )
277+ }
278+ }
279+
280+ func TestPowershellFormat_StreamRedirectStar (t * testing.T ) {
281+ // *> should be recognized as all-streams redirect
282+ tokens , err := SplitWith ("echo foo *> output.txt" , PowershellFormat ())
283+ if err != nil {
284+ t .Fatal (err )
285+ }
286+ found := false
287+ for _ , tok := range tokens {
288+ if tok .Type == WORDBREAK_TOKEN && tok .RawValue == "*>" {
289+ if ! tok .WordbreakType .IsRedirect () {
290+ t .Errorf ("powershell *>: WordbreakType = %v, want redirect" , tok .WordbreakType )
291+ }
292+ found = true
293+ }
294+ }
295+ if ! found {
296+ t .Errorf ("powershell *>: no merged *> token found in %v" , tokens )
297+ }
298+ }
0 commit comments