func onInit() { l := py.NewLock() defer l.Unlock() m, err := py.Import("sublime_plugin") if err != nil { panic(err) } sys, err := py.Import("sys") if err != nil { log.Debug(err) } else { defer sys.Decref() } if watcher, err = watch.NewWatcher(); err != nil { log.Errorf("Couldn't create watcher: %s", err) } // TODO: add all plugins after supporting all commands // plugins := packages.ScanPlugins(backend.LIME_PACKAGES_PATH, ".py") // for _, p := range plugins { // newPlugin(p, m) // } newPlugin(packages.NewPlugin(path.Join(backend.LIME_PACKAGES_PATH, "Vintageous"), ".py"), m) go watcher.Observe() }
func GetEditor() *Editor { edl.Lock() defer edl.Unlock() if ed == nil { ed = &Editor{ cmdHandler: commandHandler{ ApplicationCommands: make(appcmd), TextCommands: make(textcmd), WindowCommands: make(wndcmd), verbose: true, }, frontend: &DummyFrontend{}, console: &View{ buffer: NewBuffer(), scratch: true, }, keyInput: make(chan keys.KeyPress, 32), } var err error if ed.Watcher, err = watch.NewWatcher(); err != nil { log.Errorf("Couldn't create watcher: %s", err) } ed.console.Settings().Set("is_widget", true) ed.defaultSettings = new(HasSettings) ed.platformSettings = new(HasSettings) ed.Settings() // Just to initialize it ed.defaultBindings = new(keys.HasKeyBindings) ed.platformBindings = new(keys.HasKeyBindings) ed.userBindings = new(keys.HasKeyBindings) log.AddFilter("console", log.DEBUG, log.NewLogWriter(ed.handleLog)) go ed.inputthread() go ed.Observe() } return ed }
func (w *Window) OpenFile(filename string, flags int) *View { v := w.NewFile() v.SetScratch(true) e := v.BeginEdit() if fn, err := filepath.Abs(filename); err != nil { v.Buffer().SetFileName(filename) } else { v.Buffer().SetFileName(fn) } if d, err := ioutil.ReadFile(filename); err != nil { log.Errorf("Couldn't load file %s: %s", filename, err) } else { v.Insert(e, 0, string(d)) } v.EndEdit(e) v.selection.Clear() v.selection.Add(text.Region{A: 0, B: 0}) v.Settings().Set("lime.last_save_change_count", v.buffer.ChangeCount()) v.SetScratch(false) OnLoad.Call(v) w.SetActiveView(v) return v }
func registerByName(cmds []namedCmd) { ch := backend.GetEditor().CommandHandler() for _, cmd := range cmds { if err := ch.Register(cmd.name, cmd.cmd); err != nil { log.Errorf("Failed to register command %s: %s", cmd.name, err) } } }
func (w *Window) runCommand(c WindowCommand, name string) error { defer func() { if r := recover(); r != nil { log.Errorf("Paniced while running window command %s %v: %v\n%s", name, c, r, string(debug.Stack())) } }() return c.Run(w) }
func (e *Editor) SetClipboard(n string) { if err := e.clipboardSetter(n); err != nil { log.Errorf("Could not set clipboard: %v", err) } // Keep a local copy in case the system clipboard isn't working e.clipboard = n }
func register(cmds []backend.Command) { ch := backend.GetEditor().CommandHandler() for _, cmd := range cmds { if err := ch.RegisterWithDefault(cmd); err != nil { log.Errorf("Failed to register command: %s", err) } } }
func (e *Editor) GetClipboard() string { if n, err := e.clipboardGetter(); err == nil { return n } else { log.Errorf("Could not get clipboard: %v", err) } return e.clipboard }
func (e *Editor) load(pkg *packages.Packet) { if err := pkg.Load(); err != nil { log.Errorf("Failed to load packet %s: %s", pkg.Name(), err) } else { log.Info("Loaded %s", pkg.Name()) if err := e.Watch(pkg.Name(), pkg); err != nil { log.Warn("Couldn't watch %s: %s", pkg.Name(), err) } } }
func newPlugin(pl *packages.Plugin, m *py.Module) (p *plugin) { p = &plugin{pl, m} p.FileChanged(p.Name()) if err := watcher.Watch(p.Name(), p); err != nil { log.Errorf("Couldn't watch %s: %s", p.Name(), err) } p.loadKeyBindings() p.loadSettings() return }
// On plugin reload we will scan for plugin files // and packets in plugin path func (p *Plugin) Reload() { var files []os.FileInfo log.Info("Reloading plugin %s", p.Name()) f, err := os.Open(p.path) if err != nil { log.Errorf("Couldn't open dir: %s", err) return } defer f.Close() fi, err := f.Readdir(-1) if err != nil { log.Errorf("Couldn't read dir: %s", err) return } for _, f := range fi { if p.suffix != "" && strings.HasSuffix(f.Name(), p.suffix) { files = append(files, f) } } p.files = files }
func (e *Editor) remove(w *Window) { edl.Lock() defer edl.Unlock() for i, ww := range e.windows { if w == ww { end := len(e.windows) - 1 if i != end { copy(e.windows[i:], e.windows[i+1:]) } e.windows = e.windows[:end] return } } log.Errorf("Wanted to remove window %+v, but it doesn't appear to be a child of this editor", w) }
func (w *Window) remove(v *View) { w.lock.Lock() defer w.lock.Unlock() for i, vv := range w.views { if v == vv { end := len(w.views) - 1 if i != end { copy(w.views[i:], w.views[i+1:]) } w.views = w.views[:end] return } } log.Errorf("Wanted to remove view %+v, but it doesn't appear to be a child of this window", v) }
func (v *View) runCommand(cmd TextCommand, name string) error { e := v.BeginEdit() e.command = name // e.args = args e.bypassUndo = cmd.BypassUndo() defer func() { v.EndEdit(e) if r := recover(); r != nil { log.Errorf("Paniced while running text command %s %v: %v\n%s", name, cmd, r, string(debug.Stack())) } }() p := Prof.Enter("view.cmd." + name) defer p.Exit() return cmd.Run(v, e) }
func (lp *LanguageParser) Parse() (*parser.Node, error) { sdata := string(lp.data) rn := parser.Node{P: lp, Name: lp.l.ScopeName} defer func() { if r := recover(); r != nil { log.Errorf("Panic during parse: %v\n", r) log.Debug("%v", rn) } }() iter := maxiter for i := 0; i < len(sdata) && iter > 0; iter-- { pat, ret := lp.l.RootPattern.Cache(sdata, i) nl := strings.IndexAny(sdata[i:], "\n\r") if nl != -1 { nl += i } if ret == nil { break } else if nl > 0 && nl <= ret[0] { i = nl for i < len(sdata) && (sdata[i] == '\n' || sdata[i] == '\r') { i++ } } else { n := pat.CreateNode(sdata, i, lp, ret) rn.Append(n) i = n.Range.B } } rn.UpdateRange() if len(sdata) != 0 { lut := make([]int, len(sdata)+1) j := 0 for i := range sdata { lut[i] = j j++ } lut[len(sdata)] = len(lp.data) lp.patch(lut, &rn) } if iter == 0 { panic("reached maximum number of iterations") } return &rn, nil }
// Returns packet file data if any error occurred // on reading file we will return nil func (p *Packet) Get() interface{} { e := []byte(`{}`) if p.group() == "keymap" { e = []byte(`[]`) } if _, err := os.Stat(p.path); os.IsNotExist(err) { log.Finest("%s doesn't exist yet", p.path) return e } d, err := ioutil.ReadFile(p.path) if err != nil { log.Errorf("Couldn't read file: %s", err) return e } return d }
func (v *View) FileChanged(filename string) { log.Finest("Reloading %s", filename) if saving, ok := v.Settings().Get("lime.saving", false).(bool); ok && saving { // This reload was triggered by ourselves saving to this file, so don't reload it return } if !GetEditor().Frontend().OkCancelDialog("File was changed by another program, reload?", "reload") { return } if d, err := ioutil.ReadFile(filename); err != nil { log.Errorf("Could not read file: %s\n. Error was: %v", filename, err) } else { edit := v.BeginEdit() end := v.buffer.Size() v.Replace(edit, Region{0, end}, string(d)) v.EndEdit(edit) } }
// Ends the given Edit object. func (v *View) EndEdit(edit *Edit) { if edit.invalid { // This happens when nesting Edits and the child Edit ends after the parent edit. log.Fine("This edit has already been invalidated: %v, %v", edit, v.editstack) return } // Find the position of this Edit object in this View's Edit stack. // If plugins, commands, etc are well-behaved the ended edit should be // last in the stack, but shit happens and we cannot count on this being the case. i := len(v.editstack) - 1 for i := len(v.editstack) - 1; i >= 0; i-- { if v.editstack[i] == edit { break } } if i == -1 { // TODO(.): Under what instances does this happen again? log.Errorf("This edit isn't even in the stack... where did it come from? %v, %v", edit, v.editstack) return } var selection_modified bool if l := len(v.editstack) - 1; i != l { // TODO(.): See TODO in BeginEdit log.Errorf("This edit wasn't last in the stack... %d != %d: %v, %v", i, l, edit, v.editstack) } // Invalidate all Edits "below" and including this Edit. for j := len(v.editstack) - 1; j >= i; j-- { current_edit := v.editstack[j] current_edit.invalid = true sel_same := reflect.DeepEqual(*v.Sel(), current_edit.savedSel) buf_same := v.buffer.ChangeCount() == current_edit.savedCount eq := (sel_same && buf_same && current_edit.composite.Len() == 0) if !eq && !sel_same { selection_modified = true } if v.IsScratch() || current_edit.bypassUndo || eq { continue } switch { case i == 0: // Well-behaved, no nested edits! fallthrough case j != i: // BOO! Someone began another Edit without finishing the first one. // In this instance, the parent Edit ended before the child. // TODO(.): What would be the correct way to handle this? v.undoStack.Add(edit) default: // BOO! Also poorly-behaved. This Edit object began after the parent began, // but was finished before the parent finished. // // Add it as a child of the parent Edit so that undoing the parent // will undo this edit as well. v.editstack[i-1].composite.Add(current_edit) } } // Pop this Edit and all the children off the Edit stack. v.editstack = v.editstack[:i] if selection_modified { OnSelectionModified.Call(v) } }
func (e *Editor) inputthread() { pc := 0 var lastBindings keys.KeyBindings doinput := func(kp keys.KeyPress) { defer func() { if r := recover(); r != nil { log.Errorf("Panic in inputthread: %v\n%s", r, string(debug.Stack())) if pc > 0 { panic(r) } pc++ } }() p := Prof.Enter("hi") defer p.Exit() lvl := log.FINE if e.logInput { lvl++ } log.Logf(lvl, "Key: %v", kp) if lastBindings.SeqIndex() == 0 { lastBindings = *e.KeyBindings() } try_again: possible_actions := lastBindings.Filter(kp) lastBindings = possible_actions // TODO? var ( wnd *Window v *View ) if wnd = e.ActiveWindow(); wnd != nil { v = wnd.ActiveView() } qc := func(key string, operator Op, operand interface{}, match_all bool) bool { return OnQueryContext.Call(v, key, operator, operand, match_all) == True } if action := possible_actions.Action(qc); action != nil { p2 := Prof.Enter("hi.perform") e.RunCommand(action.Command, action.Args) p2.Exit() } else if possible_actions.SeqIndex() > 1 { // TODO: this disables having keyBindings with more than 2 key sequence lastBindings = *e.KeyBindings() goto try_again } else if kp.IsCharacter() { p2 := Prof.Enter("hi.character") log.Finest("[editor.inputthread] kp: |%s|, pos: %v", kp.Text, possible_actions) if err := e.CommandHandler().RunTextCommand(v, "insert", Args{"characters": kp.Text}); err != nil { log.Debug("Couldn't run textcommand: %s", err) } p2.Exit() } } for kp := range e.keyInput { doinput(kp) } }
// parsethread() would be the go-routine used for dealing with reparsing the // current buffer when it has been modified. Each opened view has its own // go-routine parsethread() which sits idle and waits for requests to be sent // on this view's reparseChan. // // The Buffer's ChangeCount, as well as the parse request's "forced" attribute // is used to determined if a parse actually needs to happen or not. // // If it is decided that a reparse should take place, a snapshot of the Buffer is // taken and a parse is performed. Upon completion of this parse operation, // and if the snapshot of the buffer has not already become outdated, // then the regions of the view associated with syntax highlighting is updated. // // Changes made to the Buffer during the time when there is no accurate // parse of the buffer is a monkey-patched version of the old syntax highlighting // regions, which in most instances will be accurate. // // See package lime-backend/lib/parser for more details. func (v *View) parsethread() { pc := 0 lastParse := -1 doparse := func() (ret bool) { p := Prof.Enter("syntax.parse") defer p.Exit() defer func() { if r := recover(); r != nil { log.Errorf("Panic in parse thread: %v\n%s", r, string(debug.Stack())) if pc > 0 { panic(r) } pc++ } }() b := v.buffer sub := b.Substr(Region{0, b.Size()}) source, _ := v.Settings().Get("syntax", "").(string) if len(source) == 0 { return } // TODO: Allow other parsers instead of this hardcoded textmate version pr, err := textmate.NewLanguageParser(source, sub) if err != nil { log.Errorf("Couldn't parse: %v", err) return } syn, err := parser.NewSyntaxHighlighter(pr) if err != nil { log.Errorf("Couldn't create syntaxhighlighter: %v", err) return } // Only set if it isn't invalid already, otherwise the // current syntax highlighting will be more accurate // as it will have had incremental adjustments done to it if v.buffer.ChangeCount() != lastParse { return } v.lock.Lock() defer v.lock.Unlock() v.syntax = syn for k := range v.regions { if strings.HasPrefix(k, "lime.syntax") { delete(v.regions, k) } } for k, v2 := range syn.Flatten() { if v2.Regions.HasNonEmpty() { v.regions[k] = v2 } } return true } v.lock.Lock() ch := v.reparseChan v.lock.Unlock() defer v.cleanup() if ch == nil { return } for pr := range ch { if cc := v.buffer.ChangeCount(); lastParse != cc || pr.forced { lastParse = cc if doparse() { v.Settings().Set("lime.syntax.updated", lastParse) } } } }