@@ -18,6 +18,7 @@ import (
1818 "sync"
1919 "time"
2020
21+ "github.com/wavetermdev/waveterm/pkg/util/envutil"
2122 "github.com/wavetermdev/waveterm/pkg/util/utilfn"
2223 "github.com/wavetermdev/waveterm/pkg/wavebase"
2324 "github.com/wavetermdev/waveterm/pkg/waveobj"
4748
4849 //go:embed shellintegration/pwsh_wavepwsh.sh
4950 PwshStartup_wavepwsh string
51+
52+ ZshExtendedHistoryPattern = regexp .MustCompile (`^: [0-9]+:` )
5053)
5154
5255const DefaultTermType = "xterm-256color"
@@ -74,6 +77,7 @@ const (
7477 PwshIntegrationDir = "shell/pwsh"
7578 FishIntegrationDir = "shell/fish"
7679 WaveHomeBinDir = "bin"
80+ ZshHistoryFileName = ".zsh_history"
7781)
7882
7983func DetectLocalShellPath () string {
@@ -208,6 +212,46 @@ func GetLocalZshZDotDir() string {
208212 return filepath .Join (wavebase .GetWaveDataDir (), ZshIntegrationDir )
209213}
210214
215+ func HasWaveZshHistory () (bool , int64 ) {
216+ zshDir := GetLocalZshZDotDir ()
217+ historyFile := filepath .Join (zshDir , ZshHistoryFileName )
218+ fileInfo , err := os .Stat (historyFile )
219+ if err != nil {
220+ return false , 0
221+ }
222+ return true , fileInfo .Size ()
223+ }
224+
225+ func IsExtendedZshHistoryFile (fileName string ) (bool , error ) {
226+ file , err := os .Open (fileName )
227+ if err != nil {
228+ if os .IsNotExist (err ) {
229+ return false , nil
230+ }
231+ return false , err
232+ }
233+ defer file .Close ()
234+
235+ buf := make ([]byte , 1024 )
236+ n , err := file .Read (buf )
237+ if err != nil {
238+ return false , err
239+ }
240+
241+ content := string (buf [:n ])
242+ lines := strings .Split (content , "\n " )
243+
244+ for _ , line := range lines {
245+ line = strings .TrimSpace (line )
246+ if line == "" {
247+ continue
248+ }
249+ return ZshExtendedHistoryPattern .MatchString (line ), nil
250+ }
251+
252+ return false , nil
253+ }
254+
211255func GetLocalWshBinaryPath (version string , goos string , goarch string ) (string , error ) {
212256 ext := ""
213257 if goarch == "amd64" {
@@ -422,6 +466,80 @@ func getShellVersion(shellPath string, shellType string) (string, error) {
422466 return matches [1 ], nil
423467}
424468
469+ func FixupWaveZshHistory () error {
470+ if runtime .GOOS != "darwin" {
471+ return nil
472+ }
473+
474+ hasHistory , size := HasWaveZshHistory ()
475+ if ! hasHistory {
476+ return nil
477+ }
478+
479+ zshDir := GetLocalZshZDotDir ()
480+ waveHistFile := filepath .Join (zshDir , ZshHistoryFileName )
481+
482+ if size == 0 {
483+ err := os .Remove (waveHistFile )
484+ if err != nil {
485+ log .Printf ("error removing wave zsh history file %s: %v\n " , waveHistFile , err )
486+ }
487+ return nil
488+ }
489+
490+ log .Printf ("merging wave zsh history %s into ~/.zsh_history\n " , waveHistFile )
491+
492+ homeDir , err := os .UserHomeDir ()
493+ if err != nil {
494+ return fmt .Errorf ("error getting home directory: %w" , err )
495+ }
496+ realHistFile := filepath .Join (homeDir , ".zsh_history" )
497+
498+ isExtended , err := IsExtendedZshHistoryFile (realHistFile )
499+ if err != nil {
500+ return fmt .Errorf ("error checking if history is extended: %w" , err )
501+ }
502+
503+ hasExtendedStr := "false"
504+ if isExtended {
505+ hasExtendedStr = "true"
506+ }
507+
508+ quotedWaveHistFile := utilfn .ShellQuote (waveHistFile , true , - 1 )
509+
510+ script := fmt .Sprintf (`
511+ HISTFILE=~/.zsh_history
512+ HISTSIZE=999999
513+ SAVEHIST=999999
514+ has_extended_history=%s
515+ [[ $has_extended_history == true ]] && setopt EXTENDED_HISTORY
516+ fc -RI
517+ fc -RI %s
518+ fc -W
519+ ` , hasExtendedStr , quotedWaveHistFile )
520+
521+ ctx , cancelFn := context .WithTimeout (context .Background (), 5 * time .Second )
522+ defer cancelFn ()
523+
524+ cmd := exec .CommandContext (ctx , "zsh" , "-f" , "-i" , "-c" , script )
525+ cmd .Stdin = nil
526+ envStr := envutil .SliceToEnv (os .Environ ())
527+ envStr = envutil .RmEnv (envStr , "ZDOTDIR" )
528+ cmd .Env = envutil .EnvToSlice (envStr )
529+ output , err := cmd .CombinedOutput ()
530+ if err != nil {
531+ return fmt .Errorf ("error executing zsh history fixup script: %w, output: %s" , err , string (output ))
532+ }
533+
534+ err = os .Remove (waveHistFile )
535+ if err != nil {
536+ log .Printf ("error removing wave zsh history file %s: %v\n " , waveHistFile , err )
537+ }
538+ log .Printf ("successfully merged wave zsh history %s into ~/.zsh_history\n " , waveHistFile )
539+
540+ return nil
541+ }
542+
425543func FormatOSC (oscNum int , parts ... string ) string {
426544 if len (parts ) == 0 {
427545 return fmt .Sprintf ("\x1b ]%d\x07 " , oscNum )
0 commit comments