github.com/jmigpin/editor@v1.6.0/core/plugins.go (about)

     1  package core
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"plugin"
     7  
     8  	"github.com/jmigpin/editor/core/toolbarparser"
     9  	"github.com/jmigpin/editor/ui"
    10  	"github.com/jmigpin/editor/util/iout"
    11  )
    12  
    13  type Plugins struct {
    14  	ed    *Editor
    15  	plugs []*Plug
    16  	added map[string]bool
    17  }
    18  
    19  func NewPlugins(ed *Editor) *Plugins {
    20  	return &Plugins{ed: ed, added: map[string]bool{}}
    21  }
    22  
    23  func (p *Plugins) AddPath(path string) error {
    24  	if p.added[path] {
    25  		return nil
    26  	}
    27  	p.added[path] = true
    28  
    29  	oplugin, err := plugin.Open(path)
    30  	if err != nil {
    31  		return fmt.Errorf("plugin: %v: %w", path, err)
    32  	}
    33  
    34  	plug := &Plug{Plugin: oplugin, Path: path}
    35  	p.plugs = append(p.plugs, plug)
    36  
    37  	return p.runOnLoad(plug)
    38  }
    39  
    40  //----------
    41  
    42  func (p *Plugins) runOnLoad(plug *Plug) error {
    43  	// plugin should have this symbol
    44  	fname := "OnLoad"
    45  	f, err := plug.Plugin.Lookup(fname)
    46  	if err != nil {
    47  		return nil // ok if plugin doesn't implement this symbol
    48  	}
    49  	// the symbol must implement this signature
    50  	f2, ok := f.(func(*Editor))
    51  	if !ok {
    52  		return p.badFuncSigErr(plug.Path, fname)
    53  	}
    54  	// run symbol
    55  	f2(p.ed)
    56  	return nil
    57  }
    58  
    59  //----------
    60  
    61  // Runs all plugins until it finds one that returns handled=true and has no errors.
    62  func (p *Plugins) RunAutoComplete(ctx context.Context, cfb *ui.ContextFloatBox) (_ error, handled bool) {
    63  	me := iout.MultiError{}
    64  	for _, plug := range p.plugs {
    65  		err, handled := p.runAutoCompletePlug(ctx, plug, cfb)
    66  		if handled {
    67  			return err, true
    68  		}
    69  		me.Add(err)
    70  	}
    71  	return me.Result(), false
    72  }
    73  
    74  func (p *Plugins) runAutoCompletePlug(ctx context.Context, plug *Plug, cfb *ui.ContextFloatBox) (_ error, handled bool) {
    75  	// plugin should have this symbol
    76  	fname := "AutoComplete"
    77  	fn1, err := plug.Plugin.Lookup(fname)
    78  	if err != nil {
    79  		return nil, false // ok if plugin doesn't implement this symbol
    80  	}
    81  	// the symbol must implement this signature
    82  	fn2, ok := fn1.(func(context.Context, *Editor, *ui.ContextFloatBox) (_ error, handled bool))
    83  	if !ok {
    84  		// report error
    85  		err := p.badFuncSigErr(plug.Path, fname)
    86  		p.ed.Error(err)
    87  
    88  		return nil, false // ok if plugin doesn't implement the sig
    89  	}
    90  	// run symbol
    91  	return fn2(ctx, p.ed, cfb)
    92  }
    93  
    94  //----------
    95  
    96  func (p *Plugins) RunToolbarCmd(erow *ERow, part *toolbarparser.Part) bool {
    97  	for _, plug := range p.plugs {
    98  		handled := p.runToolbarCmdPlug(plug, erow, part)
    99  		if handled {
   100  			return true
   101  		}
   102  	}
   103  	return false
   104  }
   105  
   106  func (p *Plugins) runToolbarCmdPlug(plug *Plug, erow *ERow, part *toolbarparser.Part) bool {
   107  	// plugin should have this symbol
   108  	fname := "ToolbarCmd"
   109  	f, err := plug.Plugin.Lookup(fname)
   110  	if err != nil {
   111  		// no error: ok if plugin doesn't implement this symbol
   112  		return false
   113  	}
   114  	// the symbol must implement this signature
   115  	f2, ok := f.(func(*Editor, *ERow, *toolbarparser.Part) bool)
   116  	if !ok {
   117  		// report error
   118  		err := p.badFuncSigErr(plug.Path, fname)
   119  		p.ed.Error(err)
   120  
   121  		return false // doesn't implement the required sig
   122  	}
   123  	// run symbol
   124  	return f2(p.ed, erow, part)
   125  }
   126  
   127  //----------
   128  
   129  func (p *Plugins) badFuncSigErr(path, name string) error {
   130  	return fmt.Errorf("plugins: bad func signature: %v, %v", path, name)
   131  }
   132  
   133  //----------
   134  
   135  type Plug struct {
   136  	Path   string
   137  	Plugin *plugin.Plugin
   138  }