github.com/mithrandie/csvq@v1.18.1/lib/query/built_in_command.go (about)

     1  package query
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/mithrandie/csvq/lib/doc"
    13  	"github.com/mithrandie/csvq/lib/file"
    14  	"github.com/mithrandie/csvq/lib/option"
    15  	"github.com/mithrandie/csvq/lib/parser"
    16  	"github.com/mithrandie/csvq/lib/syntax"
    17  	"github.com/mithrandie/csvq/lib/value"
    18  
    19  	"github.com/mithrandie/go-text"
    20  	"github.com/mithrandie/go-text/color"
    21  	"github.com/mithrandie/ternary"
    22  )
    23  
    24  type ObjectStatus int
    25  
    26  const (
    27  	ObjectFixed ObjectStatus = iota
    28  	ObjectCreated
    29  	ObjectUpdated
    30  	ReadOnly
    31  )
    32  
    33  const IgnoredFlagPrefix = "(ignored) "
    34  
    35  const (
    36  	ReloadConfig = "CONFIG"
    37  )
    38  
    39  const (
    40  	ShowTables     = "TABLES"
    41  	ShowViews      = "VIEWS"
    42  	ShowCursors    = "CURSORS"
    43  	ShowFunctions  = "FUNCTIONS"
    44  	ShowStatements = "STATEMENTS"
    45  	ShowFlags      = "FLAGS"
    46  	ShowEnv        = "ENV"
    47  	ShowRuninfo    = "RUNINFO"
    48  )
    49  
    50  var ShowObjectList = []string{
    51  	ShowTables,
    52  	ShowViews,
    53  	ShowCursors,
    54  	ShowFunctions,
    55  	ShowStatements,
    56  	ShowFlags,
    57  	ShowEnv,
    58  	ShowRuninfo,
    59  }
    60  
    61  func Echo(ctx context.Context, scope *ReferenceScope, expr parser.Echo) (string, error) {
    62  	p, err := Evaluate(ctx, scope, expr.Value)
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  
    67  	return NewStringFormatter().Format("%s", []value.Primary{p})
    68  }
    69  
    70  func Print(ctx context.Context, scope *ReferenceScope, expr parser.Print) (string, error) {
    71  	p, err := Evaluate(ctx, scope, expr.Value)
    72  	if err != nil {
    73  		return "", err
    74  	}
    75  	return p.String(), err
    76  }
    77  
    78  func Printf(ctx context.Context, scope *ReferenceScope, expr parser.Printf) (string, error) {
    79  	var format string
    80  	formatValue, err := Evaluate(ctx, scope, expr.Format)
    81  	if err != nil {
    82  		return "", err
    83  	}
    84  	formatString := value.ToString(formatValue)
    85  	if !value.IsNull(formatString) {
    86  		format = formatString.(*value.String).Raw()
    87  		value.Discard(formatString)
    88  	}
    89  
    90  	args := make([]value.Primary, len(expr.Values))
    91  	for i, v := range expr.Values {
    92  		p, err := Evaluate(ctx, scope, v)
    93  		if err != nil {
    94  			return "", err
    95  		}
    96  		args[i] = p
    97  	}
    98  
    99  	message, err := NewStringFormatter().Format(format, args)
   100  	if err != nil {
   101  		return "", NewReplaceValueLengthError(expr, err.(Error).Message())
   102  	}
   103  	return message, nil
   104  }
   105  
   106  func Source(ctx context.Context, scope *ReferenceScope, expr parser.Source) ([]parser.Statement, error) {
   107  	var fpath string
   108  
   109  	if ident, ok := expr.FilePath.(parser.Identifier); ok {
   110  		fpath = ident.Literal
   111  	} else {
   112  		p, err := Evaluate(ctx, scope, expr.FilePath)
   113  		if err != nil {
   114  			return nil, err
   115  		}
   116  		s := value.ToString(p)
   117  		if value.IsNull(s) {
   118  			return nil, NewSourceInvalidFilePathError(expr, expr.FilePath)
   119  		}
   120  		fpath = s.(*value.String).Raw()
   121  		value.Discard(s)
   122  	}
   123  
   124  	if len(fpath) < 1 {
   125  		return nil, NewSourceInvalidFilePathError(expr, expr.FilePath)
   126  	}
   127  
   128  	return LoadStatementsFromFile(ctx, scope.Tx, parser.Identifier{BaseExpr: expr.BaseExpr, Literal: fpath})
   129  }
   130  
   131  func LoadContentsFromFile(ctx context.Context, tx *Transaction, fpath parser.Identifier) (content string, err error) {
   132  	p := fpath.Literal
   133  	if !filepath.IsAbs(p) {
   134  		if abs, err := filepath.Abs(p); err == nil {
   135  			p = abs
   136  		}
   137  	}
   138  
   139  	if !file.Exists(p) {
   140  		return content, NewFileNotExistError(fpath)
   141  	}
   142  
   143  	h, err := tx.FileContainer.CreateHandlerWithoutLock(ctx, p, tx.WaitTimeout, tx.RetryDelay)
   144  	if err != nil {
   145  		return content, ConvertFileHandlerError(err, fpath)
   146  	}
   147  	defer func() {
   148  		err = appendCompositeError(err, tx.FileContainer.Close(h))
   149  	}()
   150  
   151  	buf, err := io.ReadAll(h.File())
   152  	if err != nil {
   153  		return content, ConvertFileHandlerError(err, fpath)
   154  	}
   155  	return string(buf), nil
   156  }
   157  
   158  func LoadStatementsFromFile(ctx context.Context, tx *Transaction, fpath parser.Identifier) (statements []parser.Statement, err error) {
   159  	content, err := LoadContentsFromFile(ctx, tx, fpath)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	statements, _, err = parser.Parse(content, fpath.Literal, false, tx.Flags.AnsiQuotes)
   165  	if err != nil {
   166  		err = NewSyntaxError(err.(*parser.SyntaxError))
   167  	}
   168  	return statements, err
   169  }
   170  
   171  func ParseExecuteStatements(ctx context.Context, scope *ReferenceScope, expr parser.Execute) ([]parser.Statement, error) {
   172  	var input string
   173  	stmt, err := Evaluate(ctx, scope, expr.Statements)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	stmtStr := value.ToString(stmt)
   178  	if !value.IsNull(stmtStr) {
   179  		input = stmt.(*value.String).Raw()
   180  		value.Discard(stmtStr)
   181  	}
   182  
   183  	args := make([]value.Primary, len(expr.Values))
   184  	for i, v := range expr.Values {
   185  		p, err := Evaluate(ctx, scope, v)
   186  		if err != nil {
   187  			return nil, err
   188  		}
   189  		args[i] = p
   190  	}
   191  
   192  	input, err = NewStringFormatter().Format(input, args)
   193  	if err != nil {
   194  		return nil, NewReplaceValueLengthError(expr, err.(Error).Message())
   195  	}
   196  	statements, _, err := parser.Parse(input, fmt.Sprintf("(L:%d C:%d) EXECUTE", expr.Line(), expr.Char()), false, scope.Tx.Flags.AnsiQuotes)
   197  	if err != nil {
   198  		err = NewSyntaxError(err.(*parser.SyntaxError))
   199  	}
   200  	return statements, err
   201  }
   202  
   203  func SetFlag(ctx context.Context, scope *ReferenceScope, expr parser.SetFlag) error {
   204  	var val interface{}
   205  	var v value.Primary
   206  	var p value.Primary
   207  	var err error
   208  
   209  	if ident, ok := expr.Value.(parser.Identifier); ok {
   210  		v = value.NewString(ident.Literal)
   211  	} else {
   212  		v, err = Evaluate(ctx, scope, expr.Value)
   213  		if err != nil {
   214  			return err
   215  		}
   216  	}
   217  
   218  	switch strings.ToUpper(expr.Flag.Name) {
   219  	case option.RepositoryFlag, option.TimezoneFlag, option.DatetimeFormatFlag,
   220  		option.ImportFormatFlag, option.DelimiterFlag, option.DelimiterPositionsFlag, option.JsonQueryFlag, option.EncodingFlag,
   221  		option.ExportEncodingFlag, option.FormatFlag, option.ExportDelimiterFlag, option.ExportDelimiterPositionsFlag,
   222  		option.LineBreakFlag, option.JsonEscapeFlag:
   223  		p = value.ToString(v)
   224  		if value.IsNull(p) {
   225  			return NewFlagValueNotAllowedFormatError(expr)
   226  		}
   227  		val = p.(*value.String).Raw()
   228  	case option.AnsiQuotesFlag, option.StrictEqualFlag, option.AllowUnevenFieldsFlag,
   229  		option.NoHeaderFlag, option.WithoutNullFlag, option.WithoutHeaderFlag, option.EncloseAllFlag,
   230  		option.PrettyPrintFlag, option.ScientificNotationFlag, option.StripEndingLineBreakFlag,
   231  		option.EastAsianEncodingFlag, option.CountDiacriticalSignFlag, option.CountFormatCodeFlag, option.ColorFlag,
   232  		option.QuietFlag, option.StatsFlag:
   233  		p = value.ToBoolean(v)
   234  		if value.IsNull(p) {
   235  			return NewFlagValueNotAllowedFormatError(expr)
   236  		}
   237  		val = p.(*value.Boolean).Raw()
   238  	case option.WaitTimeoutFlag:
   239  		p = value.ToFloat(v)
   240  		if value.IsNull(p) {
   241  			return NewFlagValueNotAllowedFormatError(expr)
   242  		}
   243  		val = p.(*value.Float).Raw()
   244  	case option.LimitRecursion, option.CPUFlag:
   245  		p = value.ToInteger(v)
   246  		if value.IsNull(p) {
   247  			return NewFlagValueNotAllowedFormatError(expr)
   248  		}
   249  		val = p.(*value.Integer).Raw()
   250  	default:
   251  		return NewInvalidFlagNameError(expr.Flag)
   252  	}
   253  
   254  	value.Discard(p)
   255  
   256  	if err = scope.Tx.SetFlag(expr.Flag.Name, val); err != nil {
   257  		return NewInvalidFlagValueError(expr, err.Error())
   258  	}
   259  	return nil
   260  }
   261  
   262  func AddFlagElement(ctx context.Context, scope *ReferenceScope, expr parser.AddFlagElement) error {
   263  	switch strings.ToUpper(expr.Flag.Name) {
   264  	case option.DatetimeFormatFlag:
   265  		e := parser.SetFlag{
   266  			BaseExpr: expr.GetBaseExpr(),
   267  			Flag:     expr.Flag,
   268  			Value:    expr.Value,
   269  		}
   270  		return SetFlag(ctx, scope, e)
   271  	case option.RepositoryFlag, option.TimezoneFlag, option.AnsiQuotesFlag, option.StrictEqualFlag,
   272  		option.ImportFormatFlag, option.DelimiterFlag, option.AllowUnevenFieldsFlag, option.DelimiterPositionsFlag,
   273  		option.JsonQueryFlag, option.EncodingFlag,
   274  		option.ExportEncodingFlag, option.FormatFlag, option.ExportDelimiterFlag, option.ExportDelimiterPositionsFlag,
   275  		option.LineBreakFlag, option.JsonEscapeFlag, option.NoHeaderFlag, option.WithoutNullFlag, option.WithoutHeaderFlag,
   276  		option.EncloseAllFlag, option.PrettyPrintFlag, option.ScientificNotationFlag, option.StripEndingLineBreakFlag,
   277  		option.EastAsianEncodingFlag, option.CountDiacriticalSignFlag, option.CountFormatCodeFlag, option.ColorFlag,
   278  		option.QuietFlag, option.StatsFlag,
   279  		option.WaitTimeoutFlag,
   280  		option.LimitRecursion, option.CPUFlag:
   281  
   282  		return NewAddFlagNotSupportedNameError(expr)
   283  	default:
   284  		return NewInvalidFlagNameError(expr.Flag)
   285  	}
   286  }
   287  
   288  func RemoveFlagElement(ctx context.Context, scope *ReferenceScope, expr parser.RemoveFlagElement) error {
   289  	p, err := Evaluate(ctx, scope, expr.Value)
   290  	if err != nil {
   291  		return err
   292  	}
   293  
   294  	scope.Tx.operationMutex.Lock()
   295  	defer scope.Tx.operationMutex.Unlock()
   296  
   297  	switch strings.ToUpper(expr.Flag.Name) {
   298  	case option.DatetimeFormatFlag:
   299  		if i := value.ToInteger(p); !value.IsNull(i) {
   300  			idx := int(i.(*value.Integer).Raw())
   301  			value.Discard(i)
   302  
   303  			if -1 < idx && idx < len(scope.Tx.Flags.DatetimeFormat) {
   304  				scope.Tx.Flags.DatetimeFormat = append(scope.Tx.Flags.DatetimeFormat[:idx], scope.Tx.Flags.DatetimeFormat[idx+1:]...)
   305  			}
   306  		} else if s := value.ToString(p); !value.IsNull(s) {
   307  			val := s.(*value.String).Raw()
   308  			value.Discard(s)
   309  
   310  			formats := make([]string, 0, len(scope.Tx.Flags.DatetimeFormat))
   311  			for _, v := range scope.Tx.Flags.DatetimeFormat {
   312  				if val != v {
   313  					formats = append(formats, v)
   314  				}
   315  			}
   316  			scope.Tx.Flags.DatetimeFormat = formats
   317  		} else {
   318  			return NewInvalidFlagValueToBeRemovedError(expr)
   319  		}
   320  	case option.RepositoryFlag, option.TimezoneFlag, option.AnsiQuotesFlag, option.StrictEqualFlag,
   321  		option.ImportFormatFlag, option.DelimiterFlag, option.AllowUnevenFieldsFlag, option.DelimiterPositionsFlag,
   322  		option.JsonQueryFlag, option.EncodingFlag,
   323  		option.ExportEncodingFlag, option.FormatFlag, option.ExportDelimiterFlag, option.ExportDelimiterPositionsFlag,
   324  		option.LineBreakFlag, option.JsonEscapeFlag, option.NoHeaderFlag, option.WithoutNullFlag, option.WithoutHeaderFlag,
   325  		option.EncloseAllFlag, option.PrettyPrintFlag, option.ScientificNotationFlag, option.StripEndingLineBreakFlag,
   326  		option.EastAsianEncodingFlag, option.CountDiacriticalSignFlag, option.CountFormatCodeFlag, option.ColorFlag,
   327  		option.QuietFlag, option.StatsFlag,
   328  		option.WaitTimeoutFlag,
   329  		option.LimitRecursion, option.CPUFlag:
   330  
   331  		return NewRemoveFlagNotSupportedNameError(expr)
   332  	default:
   333  		return NewInvalidFlagNameError(expr.Flag)
   334  	}
   335  
   336  	return nil
   337  }
   338  
   339  func ShowFlag(tx *Transaction, expr parser.ShowFlag) (string, error) {
   340  	s, ok := showFlag(tx, expr.Flag.Name)
   341  	if !ok {
   342  		return s, NewInvalidFlagNameError(expr.Flag)
   343  	}
   344  
   345  	return tx.Palette.Render(option.LableEffect, option.FlagSymbol(strings.ToUpper(expr.Flag.Name))+":") + " " + s, nil
   346  }
   347  
   348  func showFlag(tx *Transaction, flagName string) (string, bool) {
   349  	val, ok := tx.GetFlag(flagName)
   350  	if !ok {
   351  		return "", ok
   352  	}
   353  
   354  	var s string
   355  
   356  	switch strings.ToUpper(flagName) {
   357  	case option.RepositoryFlag:
   358  		p := val.(*value.String)
   359  		if len(p.Raw()) < 1 {
   360  			wd, _ := os.Getwd()
   361  			s = tx.Palette.Render(option.NullEffect, fmt.Sprintf("(current dir: %s)", wd))
   362  		} else {
   363  			s = tx.Palette.Render(option.StringEffect, p.Raw())
   364  		}
   365  	case option.DatetimeFormatFlag:
   366  		p := val.(*value.String)
   367  		if len(p.Raw()) < 1 {
   368  			s = tx.Palette.Render(option.NullEffect, "(not set)")
   369  		} else {
   370  			s = tx.Palette.Render(option.StringEffect, p.Raw())
   371  		}
   372  	case option.JsonQueryFlag:
   373  		p := val.(*value.String)
   374  		if len(p.Raw()) < 1 {
   375  			s = tx.Palette.Render(option.NullEffect, "(empty)")
   376  		} else {
   377  			s = tx.Palette.Render(option.StringEffect, p.Raw())
   378  		}
   379  	case option.ExportEncodingFlag:
   380  		switch tx.Flags.ExportOptions.Format {
   381  		case option.JSON, option.JSONL:
   382  			s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.String).Raw())
   383  		default:
   384  			s = tx.Palette.Render(option.StringEffect, val.(*value.String).Raw())
   385  		}
   386  	case option.ExportDelimiterFlag:
   387  		switch tx.Flags.ExportOptions.Format {
   388  		case option.CSV:
   389  			s = tx.Palette.Render(option.StringEffect, val.(*value.String).String())
   390  		default:
   391  			s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.String).String())
   392  		}
   393  	case option.ExportDelimiterPositionsFlag:
   394  		switch tx.Flags.ExportOptions.Format {
   395  		case option.FIXED:
   396  			s = tx.Palette.Render(option.StringEffect, val.(*value.String).Raw())
   397  		default:
   398  			s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.String).Raw())
   399  		}
   400  	case option.WithoutHeaderFlag:
   401  		switch tx.Flags.ExportOptions.Format {
   402  		case option.CSV, option.TSV, option.FIXED, option.GFM, option.ORG:
   403  			if tx.Flags.ExportOptions.Format == option.FIXED && tx.Flags.ExportOptions.SingleLine {
   404  				s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.Boolean).String())
   405  			} else {
   406  				s = tx.Palette.Render(option.BooleanEffect, val.(*value.Boolean).String())
   407  			}
   408  		default:
   409  			s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.Boolean).String())
   410  		}
   411  	case option.LineBreakFlag:
   412  		if tx.Flags.ExportOptions.Format == option.FIXED && tx.Flags.ExportOptions.SingleLine {
   413  			s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.String).Raw())
   414  		} else {
   415  			s = tx.Palette.Render(option.StringEffect, val.(*value.String).Raw())
   416  		}
   417  	case option.EncloseAllFlag:
   418  		switch tx.Flags.ExportOptions.Format {
   419  		case option.CSV, option.TSV:
   420  			s = tx.Palette.Render(option.BooleanEffect, val.(*value.Boolean).String())
   421  		default:
   422  			s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.Boolean).String())
   423  		}
   424  	case option.JsonEscapeFlag:
   425  		switch tx.Flags.ExportOptions.Format {
   426  		case option.JSON, option.JSONL:
   427  			s = tx.Palette.Render(option.StringEffect, val.(*value.String).Raw())
   428  		default:
   429  			s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.String).Raw())
   430  		}
   431  	case option.PrettyPrintFlag:
   432  		switch tx.Flags.ExportOptions.Format {
   433  		case option.JSON, option.JSONL:
   434  			s = tx.Palette.Render(option.BooleanEffect, val.(*value.Boolean).String())
   435  		default:
   436  			s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.Boolean).String())
   437  		}
   438  	case option.EastAsianEncodingFlag, option.CountDiacriticalSignFlag, option.CountFormatCodeFlag:
   439  		switch tx.Flags.ExportOptions.Format {
   440  		case option.GFM, option.ORG, option.BOX, option.TEXT:
   441  			s = tx.Palette.Render(option.BooleanEffect, val.(*value.Boolean).String())
   442  		default:
   443  			s = tx.Palette.Render(option.NullEffect, IgnoredFlagPrefix+val.(*value.Boolean).String())
   444  		}
   445  	case option.DelimiterFlag:
   446  		s = tx.Palette.Render(option.StringEffect, val.(*value.String).String())
   447  	case option.TimezoneFlag, option.ImportFormatFlag, option.DelimiterPositionsFlag, option.EncodingFlag, option.FormatFlag:
   448  		s = tx.Palette.Render(option.StringEffect, val.(*value.String).Raw())
   449  	case option.LimitRecursion:
   450  		p := val.(*value.Integer)
   451  		if p.Raw() < 0 {
   452  			s = tx.Palette.Render(option.NullEffect, "(no limit)")
   453  		} else {
   454  			s = tx.Palette.Render(option.NumberEffect, p.String())
   455  		}
   456  	case option.CPUFlag:
   457  		s = tx.Palette.Render(option.NumberEffect, val.(*value.Integer).String())
   458  	case option.WaitTimeoutFlag:
   459  		s = tx.Palette.Render(option.NumberEffect, val.(*value.Float).String())
   460  	case option.AnsiQuotesFlag, option.StrictEqualFlag, option.AllowUnevenFieldsFlag,
   461  		option.NoHeaderFlag, option.WithoutNullFlag, option.StripEndingLineBreakFlag, option.ScientificNotationFlag,
   462  		option.ColorFlag, option.QuietFlag, option.StatsFlag:
   463  		s = tx.Palette.Render(option.BooleanEffect, val.(*value.Boolean).String())
   464  	}
   465  
   466  	return s, true
   467  }
   468  
   469  func ShowObjects(scope *ReferenceScope, expr parser.ShowObjects) (string, error) {
   470  	var s string
   471  
   472  	w := scope.Tx.CreateDocumentWriter()
   473  
   474  	switch strings.ToUpper(expr.Type.Literal) {
   475  	case ShowTables:
   476  		keys := scope.Tx.CachedViews.SortedKeys()
   477  
   478  		if len(keys) < 1 {
   479  			s = scope.Tx.Warn("No table is loaded")
   480  		} else {
   481  			createdFiles, updatedFiles := scope.Tx.UncommittedViews.UncommittedFiles()
   482  
   483  			for _, key := range keys {
   484  				if view, ok := scope.Tx.CachedViews.Load(strings.ToUpper(key)); ok {
   485  					fields := view.Header.TableColumnNames()
   486  					info := view.FileInfo
   487  					ufpath := strings.ToUpper(info.Path)
   488  
   489  					if _, ok := createdFiles[ufpath]; ok {
   490  						w.WriteColor("*Created* ", option.EmphasisEffect)
   491  					} else if _, ok := updatedFiles[ufpath]; ok {
   492  						w.WriteColor("*Updated* ", option.EmphasisEffect)
   493  					}
   494  					w.WriteColorWithoutLineBreak(info.Path, option.ObjectEffect)
   495  					writeFields(w, fields)
   496  
   497  					w.NewLine()
   498  					writeTableAttribute(w, scope.Tx.Flags, info)
   499  					w.ClearBlock()
   500  					w.NewLine()
   501  				}
   502  			}
   503  
   504  			uncommitted := len(createdFiles) + len(updatedFiles)
   505  
   506  			w.Title1 = "Loaded Tables"
   507  			if 0 < uncommitted {
   508  				w.Title2 = fmt.Sprintf("(Uncommitted: %s)", FormatCount(uncommitted, "Table"))
   509  				w.Title2Effect = option.EmphasisEffect
   510  			}
   511  			s = "\n" + w.String() + "\n"
   512  		}
   513  	case ShowViews:
   514  		views := scope.AllTemporaryTables()
   515  
   516  		if views.Len() < 1 {
   517  			s = scope.Tx.Warn("No view is declared")
   518  		} else {
   519  			keys := views.SortedKeys()
   520  
   521  			updatedViews := scope.Tx.UncommittedViews.UncommittedTempViews()
   522  
   523  			for _, key := range keys {
   524  				if view, ok := views.Load(key); ok {
   525  					fields := view.Header.TableColumnNames()
   526  					info := view.FileInfo
   527  					ufpath := strings.ToUpper(info.Path)
   528  
   529  					if _, ok := updatedViews[ufpath]; ok {
   530  						w.WriteColor("*Updated* ", option.EmphasisEffect)
   531  					}
   532  					w.WriteColorWithoutLineBreak(info.Path, option.ObjectEffect)
   533  					writeFields(w, fields)
   534  					w.ClearBlock()
   535  					w.NewLine()
   536  				}
   537  			}
   538  
   539  			uncommitted := len(updatedViews)
   540  
   541  			w.Title1 = "Views"
   542  			if 0 < uncommitted {
   543  				w.Title2 = fmt.Sprintf("(Uncommitted: %s)", FormatCount(uncommitted, "View"))
   544  				w.Title2Effect = option.EmphasisEffect
   545  			}
   546  			s = "\n" + w.String() + "\n"
   547  		}
   548  	case ShowCursors:
   549  		cursors := scope.AllCursors()
   550  		if cursors.Len() < 1 {
   551  			s = scope.Tx.Warn("No cursor is declared")
   552  		} else {
   553  			keys := cursors.SortedKeys()
   554  
   555  			for _, key := range keys {
   556  				if cur, ok := cursors.Load(key); ok {
   557  					isOpen := cur.IsOpen()
   558  
   559  					w.WriteColor(cur.Name, option.ObjectEffect)
   560  					w.BeginBlock()
   561  
   562  					w.NewLine()
   563  					w.WriteColorWithoutLineBreak("Status: ", option.LableEffect)
   564  					if isOpen == ternary.TRUE {
   565  						nor, _ := cur.Count()
   566  						inRange, _ := cur.IsInRange()
   567  						position, _ := cur.Pointer()
   568  
   569  						norStr := option.FormatInt(nor, ",")
   570  
   571  						w.WriteColorWithoutLineBreak("Open", option.TernaryEffect)
   572  						w.WriteColorWithoutLineBreak("    Number of Rows: ", option.LableEffect)
   573  						w.WriteColorWithoutLineBreak(norStr, option.NumberEffect)
   574  						w.WriteSpaces(10 - len(norStr))
   575  						w.WriteColorWithoutLineBreak("Pointer: ", option.LableEffect)
   576  						switch inRange {
   577  						case ternary.FALSE:
   578  							w.WriteColorWithoutLineBreak("Out of Range", option.TernaryEffect)
   579  						case ternary.UNKNOWN:
   580  							w.WriteColorWithoutLineBreak(inRange.String(), option.TernaryEffect)
   581  						default:
   582  							w.WriteColorWithoutLineBreak(option.FormatInt(position, ","), option.NumberEffect)
   583  						}
   584  					} else {
   585  						w.WriteColorWithoutLineBreak("Closed", option.TernaryEffect)
   586  					}
   587  
   588  					w.NewLine()
   589  					if cur.query.SelectEntity != nil {
   590  						w.WriteColor("Query:", option.LableEffect)
   591  						writeQuery(w, cur.query.String())
   592  					} else {
   593  						w.WriteColorWithoutLineBreak("Statement: ", option.LableEffect)
   594  						w.WriteColorWithoutLineBreak(cur.statement.String(), option.IdentifierEffect)
   595  					}
   596  
   597  					w.ClearBlock()
   598  					w.NewLine()
   599  				}
   600  			}
   601  			w.Title1 = "Cursors"
   602  			s = "\n" + w.String() + "\n"
   603  		}
   604  	case ShowFunctions:
   605  		scalars, aggs := scope.AllFunctions()
   606  		if scalars.Len() < 1 && aggs.Len() < 1 {
   607  			s = scope.Tx.Warn("No function is declared")
   608  		} else {
   609  			if 0 < scalars.Len() {
   610  				w.Clear()
   611  				writeFunctions(w, scalars)
   612  				w.Title1 = "Scalar Functions"
   613  				s += "\n" + w.String()
   614  			}
   615  			if 0 < aggs.Len() {
   616  				w.Clear()
   617  				writeFunctions(w, aggs)
   618  				w.Title1 = "Aggregate Functions"
   619  				s += "\n" + w.String() + "\n"
   620  			} else {
   621  				s += "\n"
   622  			}
   623  		}
   624  	case ShowStatements:
   625  		if scope.Tx.PreparedStatements.Len() < 1 {
   626  			s = scope.Tx.Warn("No statement is prepared")
   627  		} else {
   628  			keys := scope.Tx.PreparedStatements.SortedKeys()
   629  
   630  			for _, key := range keys {
   631  				if stmt, ok := scope.Tx.PreparedStatements.Load(key); ok {
   632  					w.WriteColor(stmt.Name, option.ObjectEffect)
   633  					w.BeginBlock()
   634  
   635  					w.NewLine()
   636  					w.WriteColorWithoutLineBreak("Placeholder Number: ", option.LableEffect)
   637  					w.WriteColorWithoutLineBreak(strconv.Itoa(stmt.HolderNumber), option.NumberEffect)
   638  					w.NewLine()
   639  					w.WriteColorWithoutLineBreak("Statement:", option.LableEffect)
   640  					writeQuery(w, stmt.StatementString)
   641  
   642  					w.ClearBlock()
   643  					w.NewLine()
   644  				}
   645  			}
   646  			w.Title1 = "Prepared Statements"
   647  			s = "\n" + w.String() + "\n"
   648  
   649  		}
   650  	case ShowFlags:
   651  		for _, flag := range option.FlagList {
   652  			symbol := option.FlagSymbol(flag)
   653  			s, _ := showFlag(scope.Tx, flag)
   654  			w.WriteSpaces(27 - len(symbol))
   655  			w.WriteColorWithoutLineBreak(symbol, option.LableEffect)
   656  			w.WriteColorWithoutLineBreak(":", option.LableEffect)
   657  			w.WriteSpaces(1)
   658  			w.WriteWithoutLineBreak(s)
   659  			w.NewLine()
   660  		}
   661  		w.Title1 = "Flags"
   662  		s = "\n" + w.String() + "\n"
   663  	case ShowEnv:
   664  		env := os.Environ()
   665  		names := make([]string, 0, len(env))
   666  		vars := make([]string, 0, len(env))
   667  		nameWidth := 0
   668  
   669  		for _, e := range env {
   670  			words := strings.Split(e, "=")
   671  			name := string(parser.VariableSign) + string(parser.EnvironmentVariableSign) + words[0]
   672  			if nameWidth < len(name) {
   673  				nameWidth = len(name)
   674  			}
   675  
   676  			var val string
   677  			if 1 < len(words) {
   678  				val = strings.Join(words[1:], "=")
   679  			}
   680  			vars = append(vars, val)
   681  			names = append(names, name)
   682  		}
   683  
   684  		for i, name := range names {
   685  			w.WriteSpaces(nameWidth - len(name))
   686  			w.WriteColorWithoutLineBreak(name, option.LableEffect)
   687  			w.WriteColorWithoutLineBreak(":", option.LableEffect)
   688  			w.WriteSpaces(1)
   689  			w.WriteWithoutLineBreak(vars[i])
   690  			w.NewLine()
   691  		}
   692  		w.Title1 = "Environment Variables"
   693  		s = "\n" + w.String() + "\n"
   694  	case ShowRuninfo:
   695  		for _, ri := range RuntimeInformatinList {
   696  			label := string(parser.VariableSign) + string(parser.RuntimeInformationSign) + ri
   697  			p, _ := GetRuntimeInformation(scope.Tx, parser.RuntimeInformation{Name: ri})
   698  
   699  			w.WriteSpaces(19 - len(label))
   700  			w.WriteColorWithoutLineBreak(label, option.LableEffect)
   701  			w.WriteColorWithoutLineBreak(":", option.LableEffect)
   702  			w.WriteSpaces(1)
   703  			switch ri {
   704  			case WorkingDirectory, VersionInformation:
   705  				w.WriteColorWithoutLineBreak(p.(*value.String).Raw(), option.StringEffect)
   706  			case UncommittedInformation:
   707  				w.WriteColorWithoutLineBreak(p.(*value.Boolean).String(), option.BooleanEffect)
   708  			default:
   709  				w.WriteColorWithoutLineBreak(p.(*value.Integer).String(), option.NumberEffect)
   710  			}
   711  			w.NewLine()
   712  		}
   713  		w.Title1 = "Runtime Information"
   714  		s = "\n" + w.String() + "\n"
   715  	default:
   716  		return "", NewShowInvalidObjectTypeError(expr, expr.Type.String())
   717  	}
   718  
   719  	return s, nil
   720  }
   721  
   722  func writeTableAttribute(w *doc.Writer, flags *option.Flags, info *FileInfo) {
   723  	encWidth := option.TextWidth(info.Encoding.String(), flags)
   724  
   725  	w.WriteColor("Format: ", option.LableEffect)
   726  	w.WriteWithoutLineBreak(info.Format.String())
   727  
   728  	w.WriteSpaces(encWidth + 4 - option.TextWidth(info.Format.String(), flags))
   729  	switch info.Format {
   730  	case option.CSV:
   731  		w.WriteColorWithoutLineBreak("Delimiter: ", option.LableEffect)
   732  		w.WriteWithoutLineBreak("'" + option.EscapeString(string(info.Delimiter)) + "'")
   733  	case option.TSV:
   734  		w.WriteColorWithoutLineBreak("Delimiter: ", option.LableEffect)
   735  		w.WriteColorWithoutLineBreak("'\\t'", option.NullEffect)
   736  	case option.FIXED:
   737  		dp := info.DelimiterPositions.String()
   738  		if info.SingleLine {
   739  			dp = "S" + dp
   740  		}
   741  
   742  		w.WriteColorWithoutLineBreak("Delimiter Positions: ", option.LableEffect)
   743  		w.WriteWithoutLineBreak(dp)
   744  	case option.JSON, option.JSONL:
   745  		escapeStr := option.JsonEscapeTypeToString(info.JsonEscape)
   746  		w.WriteColorWithoutLineBreak("Escape: ", option.LableEffect)
   747  		w.WriteWithoutLineBreak(escapeStr)
   748  
   749  		spaces := 9 - len(escapeStr)
   750  		if spaces < 2 {
   751  			spaces = 2
   752  		}
   753  		w.WriteSpaces(spaces)
   754  
   755  		w.WriteColorWithoutLineBreak("Query: ", option.LableEffect)
   756  		if len(info.JsonQuery) < 1 {
   757  			w.WriteColorWithoutLineBreak("(empty)", option.NullEffect)
   758  		} else {
   759  			w.WriteColorWithoutLineBreak(info.JsonQuery, option.NullEffect)
   760  		}
   761  	}
   762  
   763  	switch info.Format {
   764  	case option.CSV, option.TSV:
   765  		w.WriteSpaces(4 - (option.TextWidth(option.EscapeString(string(info.Delimiter)), flags)))
   766  		w.WriteColorWithoutLineBreak("Enclose All: ", option.LableEffect)
   767  		w.WriteWithoutLineBreak(strconv.FormatBool(info.EncloseAll))
   768  	}
   769  
   770  	w.NewLine()
   771  
   772  	w.WriteColor("Encoding: ", option.LableEffect)
   773  	switch info.Format {
   774  	case option.JSON, option.JSONL:
   775  		w.WriteColorWithoutLineBreak(text.UTF8.String(), option.NullEffect)
   776  	default:
   777  		w.WriteWithoutLineBreak(info.Encoding.String())
   778  	}
   779  
   780  	if !(info.Format == option.FIXED && info.SingleLine) {
   781  		w.WriteSpaces(encWidth + 2 - (option.TextWidth(info.Encoding.String(), flags)))
   782  		w.WriteColorWithoutLineBreak("LineBreak: ", option.LableEffect)
   783  		w.WriteWithoutLineBreak(info.LineBreak.String())
   784  	}
   785  
   786  	switch info.Format {
   787  	case option.JSON, option.JSONL:
   788  		w.WriteSpaces(6 - (option.TextWidth(info.LineBreak.String(), flags)))
   789  		w.WriteColorWithoutLineBreak("Pretty Print: ", option.LableEffect)
   790  		w.WriteWithoutLineBreak(strconv.FormatBool(info.PrettyPrint))
   791  	case option.CSV, option.TSV, option.FIXED, option.GFM, option.ORG:
   792  		if !(info.Format == option.FIXED && info.SingleLine) {
   793  			w.WriteSpaces(6 - (option.TextWidth(info.LineBreak.String(), flags)))
   794  			w.WriteColorWithoutLineBreak("Header: ", option.LableEffect)
   795  			w.WriteWithoutLineBreak(strconv.FormatBool(!info.NoHeader))
   796  		}
   797  	}
   798  }
   799  
   800  func writeFields(w *doc.Writer, fields []string) {
   801  	w.BeginBlock()
   802  	w.NewLine()
   803  	w.WriteColor("Fields: ", option.LableEffect)
   804  	w.BeginSubBlock()
   805  	lastIdx := len(fields) - 1
   806  	for i, f := range fields {
   807  		escaped := option.EscapeIdentifier(f)
   808  		if i < lastIdx && !w.FitInLine(escaped+", ") {
   809  			w.NewLine()
   810  		}
   811  		w.WriteColor(escaped, option.AttributeEffect)
   812  		if i < lastIdx {
   813  			w.WriteWithoutLineBreak(", ")
   814  		}
   815  	}
   816  	w.EndSubBlock()
   817  }
   818  
   819  func writeFunctions(w *doc.Writer, funcs UserDefinedFunctionMap) {
   820  	keys := funcs.SortedKeys()
   821  
   822  	for _, key := range keys {
   823  		if fn, ok := funcs.Load(key); ok {
   824  			w.WriteColor(fn.Name.String(), option.ObjectEffect)
   825  			w.WriteWithoutLineBreak(" (")
   826  
   827  			if fn.IsAggregate {
   828  				w.WriteColorWithoutLineBreak(fn.Cursor.String(), option.IdentifierEffect)
   829  				if 0 < len(fn.Parameters) {
   830  					w.WriteWithoutLineBreak(", ")
   831  				}
   832  			}
   833  
   834  			for i, p := range fn.Parameters {
   835  				if 0 < i {
   836  					w.WriteWithoutLineBreak(", ")
   837  				}
   838  				if def, ok := fn.Defaults[p.Name]; ok {
   839  					w.WriteColorWithoutLineBreak(p.String(), option.AttributeEffect)
   840  					w.WriteWithoutLineBreak(" = ")
   841  					w.WriteColorWithoutLineBreak(def.String(), option.ValueEffect)
   842  				} else {
   843  					w.WriteColorWithoutLineBreak(p.String(), option.AttributeEffect)
   844  				}
   845  			}
   846  
   847  			w.WriteWithoutLineBreak(")")
   848  			w.ClearBlock()
   849  			w.NewLine()
   850  		}
   851  	}
   852  }
   853  
   854  func ShowFields(ctx context.Context, scope *ReferenceScope, expr parser.ShowFields) (string, error) {
   855  	var tableName = func(expr parser.QueryExpression) string {
   856  		switch e := expr.(type) {
   857  		case parser.Identifier:
   858  			return e.Literal
   859  		case parser.Stdin:
   860  			return e.String()
   861  		case HttpObject:
   862  			return "Remote Object"
   863  		case DataObject:
   864  			return "String Object"
   865  		default:
   866  			return "Inline Table"
   867  		}
   868  	}
   869  
   870  	if !strings.EqualFold(expr.Type.Literal, "FIELDS") {
   871  		return "", NewShowInvalidObjectTypeError(expr, expr.Type.Literal)
   872  	}
   873  
   874  	var status = ObjectFixed
   875  
   876  	queryScope := scope.CreateNode()
   877  	defer queryScope.CloseCurrentNode()
   878  
   879  	view, err := LoadViewFromTableIdentifier(ctx, queryScope, expr.Table, false, false)
   880  	if err != nil {
   881  		return "", err
   882  	}
   883  
   884  	if view.FileInfo.IsInMemoryTable() {
   885  		updatedViews := scope.Tx.UncommittedViews.UncommittedTempViews()
   886  		ufpath := view.FileInfo.IdentifiedPath()
   887  
   888  		if _, ok := updatedViews[ufpath]; ok {
   889  			status = ObjectUpdated
   890  		}
   891  	} else if view.FileInfo.IsFile() {
   892  		createdViews, updatedView := scope.Tx.UncommittedViews.UncommittedFiles()
   893  		ufpath := view.FileInfo.IdentifiedPath()
   894  
   895  		if _, ok := createdViews[ufpath]; ok {
   896  			status = ObjectCreated
   897  		} else if _, ok := updatedView[ufpath]; ok {
   898  			status = ObjectUpdated
   899  		}
   900  	} else {
   901  		status = ReadOnly
   902  	}
   903  
   904  	w := scope.Tx.CreateDocumentWriter()
   905  	w.WriteColorWithoutLineBreak("Type: ", option.LableEffect)
   906  	if view.FileInfo.IsTemporaryTable() {
   907  		w.WriteWithoutLineBreak("Temporary Table")
   908  	} else if view.FileInfo.IsStdin() {
   909  		w.WriteWithoutLineBreak("STDIN")
   910  	} else if view.FileInfo.IsFile() {
   911  		w.WriteWithoutLineBreak("File")
   912  		w.NewLine()
   913  		w.WriteColorWithoutLineBreak("Path: ", option.LableEffect)
   914  		w.WriteColorWithoutLineBreak(view.FileInfo.Path, option.ObjectEffect)
   915  	} else if view.FileInfo.IsRemoteObject() {
   916  		w.WriteWithoutLineBreak("Remote Object")
   917  		w.NewLine()
   918  		w.WriteColorWithoutLineBreak(" URL: ", option.LableEffect)
   919  		w.WriteColorWithoutLineBreak(view.FileInfo.Path, option.ObjectEffect)
   920  	} else if view.FileInfo.IsStringObject() {
   921  		w.WriteWithoutLineBreak("String Object")
   922  	} else if view.FileInfo.IsInlineTable() {
   923  		w.WriteWithoutLineBreak("Inline Table")
   924  	}
   925  
   926  	if !view.FileInfo.IsTemporaryTable() {
   927  		w.NewLine()
   928  		writeTableAttribute(w, scope.Tx.Flags, view.FileInfo)
   929  	}
   930  
   931  	if b, ok := ctx.Value("CallFromSubcommand").(bool); !ok || !b {
   932  		w.NewLine()
   933  		w.WriteColorWithoutLineBreak("Status: ", option.LableEffect)
   934  		switch status {
   935  		case ObjectCreated:
   936  			w.WriteColorWithoutLineBreak("Created", option.EmphasisEffect)
   937  		case ObjectUpdated:
   938  			w.WriteColorWithoutLineBreak("Updated", option.EmphasisEffect)
   939  		case ReadOnly:
   940  			w.WriteWithoutLineBreak("Read-Only")
   941  		default:
   942  			w.WriteWithoutLineBreak("Fixed")
   943  		}
   944  	}
   945  
   946  	w.NewLine()
   947  	writeFieldList(w, view.Header.TableColumnNames())
   948  
   949  	w.Title1 = "Fields in"
   950  	tablePath := func() parser.QueryExpression {
   951  		if e, ok := expr.Table.(parser.FormatSpecifiedFunction); ok {
   952  			return e.Path
   953  		}
   954  		return expr.Table
   955  	}()
   956  	tableObject, err := NormalizeTableObject(ctx, scope, tablePath)
   957  	if err != nil {
   958  		return "", nil
   959  	}
   960  	w.Title2 = tableName(tableObject)
   961  	w.Title2Effect = option.IdentifierEffect
   962  	return "\n" + w.String() + "\n", nil
   963  }
   964  
   965  func writeFieldList(w *doc.Writer, fields []string) {
   966  	l := len(fields)
   967  	digits := len(strconv.Itoa(l))
   968  	fieldNumbers := make([]string, 0, l)
   969  	for i := 0; i < l; i++ {
   970  		idxstr := strconv.Itoa(i + 1)
   971  		fieldNumbers = append(fieldNumbers, strings.Repeat(" ", digits-len(idxstr))+idxstr)
   972  	}
   973  
   974  	w.WriteColorWithoutLineBreak("Fields:", option.LableEffect)
   975  	w.NewLine()
   976  	w.WriteSpaces(2)
   977  	w.BeginSubBlock()
   978  	for i := 0; i < l; i++ {
   979  		w.WriteColor(fieldNumbers[i], option.NumberEffect)
   980  		w.Write(".")
   981  		w.WriteSpaces(1)
   982  		w.WriteColorWithoutLineBreak(fields[i], option.AttributeEffect)
   983  		w.NewLine()
   984  	}
   985  }
   986  
   987  func writeQuery(w *doc.Writer, s string) {
   988  	w.NewLine()
   989  	w.WriteSpaces(2)
   990  	w.BeginSubBlock()
   991  	w.WriteWithAutoLineBreakWithContinueMark(s)
   992  	w.EndSubBlock()
   993  }
   994  
   995  func SetEnvVar(ctx context.Context, scope *ReferenceScope, expr parser.SetEnvVar) error {
   996  	var p value.Primary
   997  	var err error
   998  
   999  	if ident, ok := expr.Value.(parser.Identifier); ok {
  1000  		p = value.NewString(ident.Literal)
  1001  		defer value.Discard(p)
  1002  	} else {
  1003  		p, err = Evaluate(ctx, scope, expr.Value)
  1004  		if err != nil {
  1005  			return err
  1006  		}
  1007  	}
  1008  
  1009  	var val string
  1010  	if s := value.ToString(p); !value.IsNull(s) {
  1011  		val = s.(*value.String).Raw()
  1012  		value.Discard(s)
  1013  	}
  1014  	return os.Setenv(expr.EnvVar.Name, val)
  1015  }
  1016  
  1017  func UnsetEnvVar(expr parser.UnsetEnvVar) error {
  1018  	return os.Unsetenv(expr.EnvVar.Name)
  1019  }
  1020  
  1021  func Chdir(ctx context.Context, scope *ReferenceScope, expr parser.Chdir) error {
  1022  	var dirpath string
  1023  	var err error
  1024  
  1025  	if ident, ok := expr.DirPath.(parser.Identifier); ok {
  1026  		dirpath = ident.Literal
  1027  	} else {
  1028  		p, err := Evaluate(ctx, scope, expr.DirPath)
  1029  		if err != nil {
  1030  			return err
  1031  		}
  1032  		s := value.ToString(p)
  1033  		if value.IsNull(s) {
  1034  			return NewInvalidPathError(expr, expr.DirPath.String(), "invalid directory path")
  1035  		}
  1036  		dirpath = s.(*value.String).Raw()
  1037  		value.Discard(s)
  1038  	}
  1039  
  1040  	if err = os.Chdir(dirpath); err != nil {
  1041  		if patherr, ok := err.(*os.PathError); ok {
  1042  			err = NewInvalidPathError(expr, patherr.Path, patherr.Err.Error())
  1043  		}
  1044  	}
  1045  	return err
  1046  }
  1047  
  1048  func Pwd(expr parser.Pwd) (string, error) {
  1049  	dirpath, err := os.Getwd()
  1050  	if err != nil {
  1051  		if patherr, ok := err.(*os.PathError); ok {
  1052  			err = NewInvalidPathError(expr, patherr.Path, patherr.Err.Error())
  1053  		}
  1054  	}
  1055  	return dirpath, err
  1056  }
  1057  
  1058  func Reload(ctx context.Context, tx *Transaction, expr parser.Reload) error {
  1059  	tx.operationMutex.Lock()
  1060  	defer tx.operationMutex.Unlock()
  1061  
  1062  	switch strings.ToUpper(expr.Type.Literal) {
  1063  	case ReloadConfig:
  1064  		if err := tx.Environment.Load(ctx, tx.WaitTimeout, tx.RetryDelay); err != nil {
  1065  			return NewLoadConfigurationError(expr, err.Error())
  1066  		}
  1067  
  1068  		for _, v := range tx.Environment.DatetimeFormat {
  1069  			tx.Flags.DatetimeFormat = option.AppendStrIfNotExist(tx.Flags.DatetimeFormat, v)
  1070  		}
  1071  
  1072  		palette, err := color.GeneratePalette(tx.Environment.Palette)
  1073  		if err != nil {
  1074  			return NewLoadConfigurationError(expr, err.Error())
  1075  		}
  1076  		tx.Palette.Merge(palette)
  1077  
  1078  		if tx.Session.Terminal() != nil {
  1079  			if err := tx.Session.Terminal().ReloadConfig(); err != nil {
  1080  				return NewLoadConfigurationError(expr, err.Error())
  1081  			}
  1082  		}
  1083  
  1084  	default:
  1085  		return NewInvalidReloadTypeError(expr, expr.Type.Literal)
  1086  	}
  1087  	return nil
  1088  }
  1089  
  1090  func Syntax(ctx context.Context, scope *ReferenceScope, expr parser.Syntax) (string, error) {
  1091  	keys := make([]string, 0, len(expr.Keywords))
  1092  	for _, key := range expr.Keywords {
  1093  		var keystr string
  1094  		if fr, ok := key.(parser.FieldReference); ok {
  1095  			if col, ok := fr.Column.(parser.Identifier); ok {
  1096  				keystr = col.Literal
  1097  			}
  1098  		} else {
  1099  			if p, err := Evaluate(ctx, scope, key); err == nil {
  1100  				if s := value.ToString(p); !value.IsNull(s) {
  1101  					keystr = s.(*value.String).Raw()
  1102  					value.Discard(s)
  1103  				}
  1104  			}
  1105  		}
  1106  
  1107  		if 0 < len(keystr) {
  1108  			words := strings.Split(option.TrimSpace(keystr), " ")
  1109  			for _, w := range words {
  1110  				w = option.TrimSpace(w)
  1111  				if 0 < len(w) {
  1112  					keys = append(keys, w)
  1113  				}
  1114  			}
  1115  		}
  1116  	}
  1117  
  1118  	store := syntax.NewStore()
  1119  	exps := store.Search(keys)
  1120  
  1121  	w := scope.Tx.CreateDocumentWriter()
  1122  
  1123  	for _, exp := range exps {
  1124  		w.WriteColor(exp.Label, option.LableEffect)
  1125  		w.NewLine()
  1126  		if len(exps) < 4 {
  1127  			w.BeginBlock()
  1128  
  1129  			if 0 < len(exp.Description.Template) {
  1130  				w.WriteWithAutoLineBreak(exp.Description.Format(scope.Tx.Palette))
  1131  				w.NewLine()
  1132  				w.NewLine()
  1133  			}
  1134  
  1135  			for _, def := range exp.Grammar {
  1136  				w.Write(def.Name.Format(scope.Tx.Palette))
  1137  				w.NewLine()
  1138  				w.BeginBlock()
  1139  				for i, gram := range def.Group {
  1140  					if i == 0 {
  1141  						w.Write(": ")
  1142  					} else {
  1143  						w.Write("| ")
  1144  					}
  1145  					w.BeginSubBlock()
  1146  					w.WriteWithAutoLineBreak(gram.Format(scope.Tx.Palette))
  1147  					w.EndSubBlock()
  1148  					w.NewLine()
  1149  				}
  1150  
  1151  				if 0 < len(def.Description.Template) {
  1152  					if 0 < len(def.Group) {
  1153  						w.NewLine()
  1154  					}
  1155  					w.WriteWithAutoLineBreak(def.Description.Format(scope.Tx.Palette))
  1156  					w.NewLine()
  1157  				}
  1158  
  1159  				w.EndBlock()
  1160  				w.NewLine()
  1161  			}
  1162  			w.EndBlock()
  1163  		}
  1164  
  1165  		if 0 < len(exp.Children) && (len(keys) < 1 || strings.EqualFold(exp.Label, strings.Join(keys, " "))) {
  1166  			w.BeginBlock()
  1167  			for _, child := range exp.Children {
  1168  				w.WriteColor(child.Label, option.LableEffect)
  1169  				w.NewLine()
  1170  			}
  1171  		}
  1172  
  1173  		w.ClearBlock()
  1174  	}
  1175  
  1176  	if len(keys) < 1 {
  1177  		w.Title1 = "Contents"
  1178  	} else {
  1179  		w.Title1 = "Search: " + strings.Join(keys, " ")
  1180  	}
  1181  	return "\n" + w.String() + "\n", nil
  1182  
  1183  }