@@ -65,7 +65,7 @@ type Shell struct {
6565 specialTerminators []string
6666 backSlashCmds []string
6767 quitKeywords []string
68- isComplete func (accumulated string ) bool
68+ isComplete func (accumulated , delimiter string ) bool
6969 contextValues
7070 Actions
7171}
@@ -81,14 +81,10 @@ type UninterpretedConfig struct {
8181 BackSlashCmds []string
8282 // Quit keywords to exit the shell if discovered
8383 QuitKeywords []string
84- // IsComplete, when non-nil, replaces the per-line LineTerminator suffix
85- // check in readUninterpreted. It is called after each physical line is
86- // read with the cumulative buffer (lines joined by "\n", matching the
87- // final delivered string). Returning true ends accumulation. Special
88- // terminators, backslash commands, and quit keywords still apply on top
89- // of IsComplete; first-line DELIMITER detection and the leading "--"
90- // short-circuit also still apply.
91- IsComplete func (accumulated string ) bool
84+ // IsComplete, when set, replaces the LineTerminator suffix check: it is
85+ // called with the cumulative buffer and current delimiter and returns true
86+ // when accumulation should stop.
87+ IsComplete func (accumulated , delimiter string ) bool
9288}
9389
9490// New creates a new shell with default settings. Uses standard output and default prompt ">> ".
@@ -376,72 +372,24 @@ func (s *Shell) readUninterpreted() (string, error) {
376372
377373 if s .lineTerminator != "" {
378374 firstLine := true
379- if s .isComplete != nil {
380- lines , err = s .readMultiLinesAccumFunc (func (line , accumulated string ) (keepReading bool ) {
381- if firstLine {
382- firstLine = false
383- if matches := delimiterRegex .FindStringSubmatch (line ); len (matches ) == 2 {
384- s .lineTerminator = matches [1 ]
385- return false
386- }
387- if strings .HasPrefix (line , "--" ) {
388- return false
389- }
390- for _ , keyword := range s .quitKeywords {
391- if strings .TrimSpace (line ) == keyword {
392- return false
393- }
394- }
395- }
396-
397- for _ , sc := range s .specialTerminators {
398- if strings .HasSuffix (strings .TrimSpace (line ), sc ) {
399- return false
400- }
401- }
402- for _ , sc := range s .backSlashCmds {
403- if strings .HasPrefix (strings .TrimSpace (line ), sc ) {
404- return false
405- }
406- }
407-
408- return ! s .isComplete (accumulated )
409- })
410- } else {
411- lines , err = s .readMultiLinesFunc (func (line string ) (keepReading bool ) {
412- if firstLine {
413- firstLine = false
414- if matches := delimiterRegex .FindStringSubmatch (line ); len (matches ) == 2 {
415- s .lineTerminator = matches [1 ]
416- return false
417- }
418- if strings .HasPrefix (line , "--" ) {
419- return false
420- }
421- for _ , keyword := range s .quitKeywords {
422- if strings .TrimSpace (line ) == keyword {
423- return false
424- }
425- }
426- }
427-
428- if strings .HasSuffix (strings .TrimSpace (line ), s .lineTerminator ) {
375+ lines , err = s .readMultiLinesAccumFunc (func (line , accumulated string ) (keepReading bool ) {
376+ if firstLine {
377+ firstLine = false
378+ if matches := delimiterRegex .FindStringSubmatch (line ); len (matches ) == 2 {
379+ s .lineTerminator = matches [1 ]
429380 return false
430381 }
431- for _ , sc := range s .specialTerminators {
432- if strings .HasSuffix (strings .TrimSpace (line ), sc ) {
433- return false
434- }
382+ if strings .HasPrefix (line , "--" ) {
383+ return false
435384 }
436- for _ , sc := range s .backSlashCmds {
437- if strings .HasPrefix ( strings . TrimSpace (line ), sc ) {
385+ for _ , keyword := range s .quitKeywords {
386+ if strings .TrimSpace (line ) == keyword {
438387 return false
439388 }
440389 }
441-
442- return true
443- })
444- }
390+ }
391+ return ! s .lineIsComplete (line , accumulated )
392+ })
445393
446394 if err != nil {
447395 return "" , err
@@ -480,6 +428,27 @@ func (s *Shell) readUninterpreted() (string, error) {
480428 return lines , nil
481429}
482430
431+ // lineIsComplete reports whether reading should stop: special terminators and
432+ // backslash commands always end input, otherwise it defers to IsComplete or the
433+ // line-terminator suffix check.
434+ func (s * Shell ) lineIsComplete (line , accumulated string ) bool {
435+ trimmed := strings .TrimSpace (line )
436+ for _ , sc := range s .specialTerminators {
437+ if strings .HasSuffix (trimmed , sc ) {
438+ return true
439+ }
440+ }
441+ for _ , sc := range s .backSlashCmds {
442+ if strings .HasPrefix (trimmed , sc ) {
443+ return true
444+ }
445+ }
446+ if s .isComplete != nil {
447+ return s .isComplete (accumulated , s .lineTerminator )
448+ }
449+ return strings .HasSuffix (trimmed , s .lineTerminator )
450+ }
451+
483452func (s * Shell ) read () ([]string , error ) {
484453 s .rawArgs = nil
485454 eof := ""
@@ -526,44 +495,21 @@ func (s *Shell) read() ([]string, error) {
526495}
527496
528497func (s * Shell ) readMultiLinesFunc (f func (string ) (keepReading bool )) (string , error ) {
529- var lines bytes.Buffer
530- currentLine := 0
531- var err error
532- for {
533- if currentLine == 1 {
534- // from second line, enable next line prompt.
535- s .reader .setMultiMode (true )
536- }
537- var line string
538- line , err = s .readLine ()
539- fmt .Fprint (& lines , line )
540- if ! f (line ) || err != nil {
541- break
542- }
543- fmt .Fprintln (& lines )
544- currentLine ++
545- }
546- if currentLine > 0 {
547- // if more than one line is read
548- // revert to standard prompt.
549- s .reader .setMultiMode (false )
550- }
551- return lines .String (), err
498+ return s .readMultiLinesAccumFunc (func (line , _ string ) bool {
499+ return f (line )
500+ })
552501}
553502
554- // readMultiLinesAccumFunc is a sibling of readMultiLinesFunc that passes
555- // both the most recent physical line and the cumulative buffer to the
556- // predicate. The cumulative buffer matches what readMultiLinesFunc would
557- // produce: physical lines joined by "\n" with no trailing newline on the
558- // terminating line.
503+ // readMultiLinesAccumFunc accumulates physical lines until the predicate
504+ // returns false. The predicate receives the latest line and the cumulative
505+ // buffer (lines joined by "\n").
559506func (s * Shell ) readMultiLinesAccumFunc (f func (line , accumulated string ) (keepReading bool )) (string , error ) {
560507 return readMultiLinesAccumFromReader (s .readLine , s .reader .setMultiMode , f )
561508}
562509
563- // readMultiLinesAccumFromReader is the inner accumulation loop used by
564- // readMultiLinesAccumFunc. It is parameterized on the line reader and the
565- // multi-mode toggle so unit tests can drive it without a real readline
566- // instance.
510+ // readMultiLinesAccumFromReader is the inner accumulation loop, parameterized
511+ // on the line reader and multi-mode toggle so tests can drive it without a real
512+ // readline instance.
567513func readMultiLinesAccumFromReader (
568514 readLine func () (string , error ),
569515 setMulti func (bool ),
0 commit comments