github.com/goplus/gop@v1.2.6/x/watcher/changes.go (about)

     1  /*
     2   * Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package watcher
    18  
    19  import (
    20  	"io/fs"
    21  	"log"
    22  	"path"
    23  	"path/filepath"
    24  	"strings"
    25  	"sync"
    26  
    27  	"github.com/goplus/mod/gopmod"
    28  )
    29  
    30  // -----------------------------------------------------------------------------------------
    31  
    32  type module struct {
    33  	exts []string
    34  }
    35  
    36  func (p *module) ignore(fname string) bool {
    37  	ext := path.Ext(fname)
    38  	for _, v := range p.exts {
    39  		if ext == v {
    40  			return false
    41  		}
    42  	}
    43  	return true
    44  }
    45  
    46  // -----------------------------------------------------------------------------------------
    47  
    48  type none struct{}
    49  
    50  type Changes struct {
    51  	changed map[string]none
    52  	mods    map[string]*module
    53  	mutex   sync.Mutex
    54  	cond    sync.Cond
    55  
    56  	root string
    57  }
    58  
    59  func NewChanges(root string) *Changes {
    60  	changed := make(map[string]none)
    61  	mods := make(map[string]*module)
    62  	root, _ = filepath.Abs(root)
    63  	c := &Changes{changed: changed, mods: mods, root: root + "/"}
    64  	c.cond.L = &c.mutex
    65  	return c
    66  }
    67  
    68  func (p *Changes) doLookupMod(name string) *module {
    69  	mod, ok := p.mods[name]
    70  	if !ok {
    71  		mod = new(module)
    72  		mod.exts = make([]string, 0, 8)
    73  		m, e := gopmod.Load(p.root + name)
    74  		if e == nil {
    75  			m.ImportClasses(func(c *gopmod.Project) {
    76  				mod.exts = append(mod.exts, c.Ext)
    77  				for _, w := range c.Works {
    78  					if w.Ext != c.Ext {
    79  						mod.exts = append(mod.exts, w.Ext)
    80  					}
    81  				}
    82  			})
    83  		}
    84  		mod.exts = append(mod.exts, ".gop", ".go", ".gox", ".gmx")
    85  		p.mods[name] = mod
    86  		if debugMod {
    87  			log.Println("Mod:", name, "Exts:", mod.exts)
    88  		}
    89  	}
    90  	return mod
    91  }
    92  
    93  func (p *Changes) lookupMod(name string) *module {
    94  	name = strings.TrimSuffix(name, "/")
    95  	p.mutex.Lock()
    96  	mod := p.doLookupMod(name)
    97  	p.mutex.Unlock()
    98  	return mod
    99  }
   100  
   101  func (p *Changes) deleteMod(dir string) {
   102  	p.mutex.Lock()
   103  	delete(p.mods, dir)
   104  	p.mutex.Unlock()
   105  }
   106  
   107  func (p *Changes) Fetch(fullPath bool) (dir string) {
   108  	p.mutex.Lock()
   109  	for len(p.changed) == 0 {
   110  		p.cond.Wait()
   111  	}
   112  	for dir = range p.changed {
   113  		delete(p.changed, dir)
   114  		break
   115  	}
   116  	p.mutex.Unlock()
   117  	if fullPath {
   118  		dir = p.root + dir
   119  	}
   120  	return
   121  }
   122  
   123  func (p *Changes) Ignore(name string, isDir bool) bool {
   124  	dir, fname := path.Split(name)
   125  	if strings.HasPrefix(fname, "_") {
   126  		return true
   127  	}
   128  	return !isDir && (isHiddenTemp(fname) || isAutogen(fname) || p.lookupMod(dir).ignore(fname))
   129  }
   130  
   131  func (p *Changes) FileChanged(name string) {
   132  	dir := path.Dir(name)
   133  	p.mutex.Lock()
   134  	n := len(p.changed)
   135  	p.changed[dir] = none{}
   136  	p.mutex.Unlock()
   137  	if n == 0 {
   138  		p.cond.Broadcast()
   139  	}
   140  }
   141  
   142  func (p *Changes) EntryDeleted(name string, isDir bool) {
   143  	if isDir {
   144  		p.deleteMod(name)
   145  	} else {
   146  		p.FileChanged(name)
   147  	}
   148  }
   149  
   150  func (p *Changes) DirAdded(name string) {
   151  	dir := p.root + name
   152  	filepath.WalkDir(dir, func(entry string, d fs.DirEntry, err error) error {
   153  		if err != nil || d.IsDir() {
   154  			return err
   155  		}
   156  		entry, _ = filepath.Rel(dir, entry)
   157  		entry = filepath.ToSlash(entry)
   158  		if !p.Ignore(entry, false) {
   159  			p.FileChanged(entry)
   160  		}
   161  		return nil
   162  	})
   163  }
   164  
   165  // pattern: gop_autogen*.go
   166  func isAutogen(fname string) bool {
   167  	return strings.HasPrefix(fname, "gop_autogen") && strings.HasSuffix(fname, ".go")
   168  }
   169  
   170  // pattern: .* or *~
   171  func isHiddenTemp(fname string) bool {
   172  	return strings.HasPrefix(fname, ".") || strings.HasSuffix(fname, "~")
   173  }
   174  
   175  // -----------------------------------------------------------------------------------------