Пример #1
0
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()
}
Пример #2
0
func (t *tbfe) renderthread() {
	pc := 0
	dorender := func() {
		defer func() {
			if r := recover(); r != nil {
				log.Errorf("Panic in renderthread: %v\n%s", r, string(debug.Stack()))
				if pc > 1 {
					panic(r)
				}
				pc++
			}
		}()
		termbox.Clear(defaultFg, defaultBg)

		t.lock.Lock()
		vs := make([]*backend.View, 0, len(t.layout))
		ls := make([]layout, 0, len(t.layout))
		for v, l := range t.layout {
			vs = append(vs, v)
			ls = append(ls, l)
		}
		t.lock.Unlock()

		for i, v := range vs {
			t.renderView(v, ls[i])
		}

		termbox.Flush()
	}

	for range t.dorender {
		dorender()
	}
}
Пример #3
0
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.buffer.Settings().Set("lime.last_save_change_count", v.buffer.ChangeCount())
	v.SetScratch(false)

	OnLoad.Call(v)
	w.SetActiveView(v)

	return v
}
Пример #4
0
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
}
Пример #5
0
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)
		}
	}
}
Пример #6
0
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
}
Пример #7
0
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)
}
Пример #8
0
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)
		}
	}
}
Пример #9
0
func setColorMode() {
	var (
		mode256 bool
		pal     = make([]termbox.RGB, 0, 256)
	)

	if err := termbox.SetColorMode(termbox.ColorMode256); err != nil {
		log.Errorf("Unable to use 256 color mode: %s", err)
	} else {
		log.Debug("Using 256 color mode")
		mode256 = true
	}

	if !mode256 {
		pal = pal[:10] // Not correct, but whatever
		pal[termbox.ColorBlack] = termbox.RGB{R: 0, G: 0, B: 0}
		pal[termbox.ColorWhite] = termbox.RGB{R: 255, G: 255, B: 255}
		pal[termbox.ColorRed] = termbox.RGB{R: 255, G: 0, B: 0}
		pal[termbox.ColorGreen] = termbox.RGB{R: 0, G: 255, B: 0}
		pal[termbox.ColorBlue] = termbox.RGB{R: 0, G: 0, B: 255}
		pal[termbox.ColorMagenta] = termbox.RGB{R: 255, G: 0, B: 255}
		pal[termbox.ColorYellow] = termbox.RGB{R: 255, G: 255, B: 0}
		pal[termbox.ColorCyan] = termbox.RGB{R: 0, G: 255, B: 255}

		diff := func(i, j byte) int {
			v := int(i) - int(j)
			if v < 0 {
				return -v
			}
			return v
		}
		palLut = func(col textmate.Color) termbox.Attribute {
			mindist := 10000000
			mini := 0
			for i, c := range pal {
				if dist := diff(c.R, col.R) + diff(c.G, col.G) + diff(c.B, col.B); dist < mindist {
					mindist = dist
					mini = i
				}
			}
			return termbox.Attribute(mini)
		}
	} else {
		palLut = func(col textmate.Color) termbox.Attribute {
			tc := termbox.RGB{R: col.R, G: col.G, B: col.B}
			for i, c := range pal {
				if c == tc {
					return termbox.Attribute(i)
				}
			}
			l := len(pal)
			log.Debug("Adding colour: %d %+v %+v", l, col, tc)
			pal = append(pal, tc)
			termbox.SetColorPalette(pal)
			return termbox.Attribute(l)
		}
	}
}
Пример #10
0
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
}
Пример #11
0
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)
		}
	}
}
Пример #12
0
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
}
Пример #13
0
// Put back watchers on watching files under the directory
func (w *Watcher) removeDir(name string) {
	for p, _ := range w.watched {
		if filepath.Dir(p) == name {
			if err := w.watch(p); err != nil {
				log.Errorf("Could not watch: %s", err)
				continue
			}
		}
	}
	w.dirs = remove(w.dirs, name)
}
Пример #14
0
// 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
}
Пример #15
0
// Remove watchers created on files under this directory because
// one watcher on the parent directory is enough for all of them
func (w *Watcher) flushDir(name string) {
	if exist(w.dirs, name) {
		return
	}
	w.dirs = append(w.dirs, name)
	for _, p := range w.watchers {
		if filepath.Dir(p) == name && !exist(w.dirs, p) {
			if err := w.removeWatch(p); err != nil {
				log.Errorf("Couldn't unwatch file %s: %s", p, err)
			}
		}
	}
}
Пример #16
0
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)
}
Пример #17
0
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)
}
Пример #18
0
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)
}
Пример #19
0
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
}
Пример #20
0
func (w *Watcher) Observe() {
	for {
		select {
		case ev := <-w.wchr.Events:
			func() {
				w.lock.Lock()
				defer w.lock.Unlock()
				w.apply(ev)
				name := ev.Name
				// If the name refers to a directory run all watched
				// callbacks for wathed files under the directory
				if exist(w.dirs, name) {
					for p, _ := range w.watched {
						if filepath.Dir(p) == name {
							ev.Name = p
							w.apply(ev)
						}
					}
				}
				dir := filepath.Dir(name)
				// The watcher will be removed if the file is deleted
				// so we need to watch the parent directory for when the
				// file is created again
				if ev.Op&fsnotify.Remove != 0 {
					w.watchers = remove(w.watchers, name)
					w.lock.Unlock()
					w.Watch(dir, nil)
					w.lock.Lock()
				}
				// We will apply parent directory FileChanged callbacks to,
				// if one of the files inside the directory has changed
				if cbs, exist := w.watched[dir]; ev.Op&fsnotify.Write != 0 && exist {
					for _, cb := range cbs {
						if c, ok := cb.(FileChangedCallback); ok {
							c.FileChanged(dir)
						}
					}
				}

			}()
		case err := <-w.wchr.Errors:
			log.Errorf("Watcher error: %s", err)
		}
	}
}
Пример #21
0
// 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
}
Пример #22
0
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)
	}
}
Пример #23
0
func (t *tbfe) renderthread() {
	pc := 0
	dorender := func() {
		defer func() {
			if r := recover(); r != nil {
				log.Errorf("Panic in renderthread: %v\n%s", r, string(debug.Stack()))
				if pc > 1 {
					panic(r)
				}
				pc++
			}
		}()
		termbox.Clear(defaultFg, defaultBg)

		t.lock.Lock()
		vs := make([]*backend.View, 0, len(t.layout))
		l := make([]layout, 0, len(t.layout))
		for k, v := range t.layout {
			vs = append(vs, k)
			l = append(l, v)
		}
		runes := []rune(t.status_message)
		t.lock.Unlock()

		w, h := termbox.Size()
		for i := 0; i < w && i < len(runes); i++ {
			termbox.SetCell(i, h-2, runes[i], defaultFg, defaultBg)
		}

		for i, v := range vs {
			t.renderView(v, l[i])
		}

		termbox.Flush()
	}

	for range t.dorender {
		log.Finest("Rendering")
		dorender()
	}
}
Пример #24
0
func (t *tbfe) render(w io.Writer) {
	defer func() {
		if r := recover(); r != nil {
			log.Errorf("Panic in renderthread: %v\n%s", r, string(debug.Stack()))
			if pc > 1 {
				panic(r)
			}
			pc++
		}
	}()
	vs := make([]*backend.View, 0, len(t.layout))
	l := make([]layout, 0, len(t.layout))
	for k, v := range t.layout {
		vs = append(vs, k)
		l = append(l, v)
	}
	for i, v := range vs {
		t.renderView(w, v, l[i])
	}
	//	runes := []rune(t.status_message)
}
Пример #25
0
func (q *qmlDialog) Show(msg, icon string) (ret int) {
	src := `import QtQuick 2.2
import QtQuick.Dialogs 1.1

Item {MessageDialog {
	objectName: "realDialog"
	id: messageDialog
	title: "May I have your attention please"
	text: "` + msg + `"
	icon: ` + icon + `
	standardButtons: StandardButton.Ok | StandardButton.Cancel
	Component.onCompleted: visible = true
}}`
	engine := qml.NewEngine()
	engine.Context().SetVar("q", q)
	component, err := engine.LoadString("dialog.qml", src)
	if err != nil {
		log.Errorf("Unable to instanciate dialog: %s", err)
		return 0
	}
	var wg sync.WaitGroup
	wg.Add(1)
	obj := component.Create(nil)
	obj = obj.ObjectByName("realDialog")
	obj.On("accepted", func() {
		ret = 1
		wg.Done()
	})
	obj.On("rejected", func() {
		ret = 0
		wg.Done()
	})

	wg.Wait()
	engine.Destroy()
	log.Debug("returning %d", ret)
	return
}
Пример #26
0
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)
	}
}
Пример #27
0
// 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)
	}
}
Пример #28
0
// 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/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)
			}
		}
	}
}
Пример #29
0
func (t *tbfe) loop() {
	backend.OnNew.Add(func(v *backend.View) {
		v.Settings().AddOnChange("lime.frontend.html.render", func(name string) {
			if name != "lime.syntax.updated" {
				return
			}
			t.SetDirty()
		})
	})
	// TODO: maybe not useful?
	/*backend.OnModified.Add(func(v *backend.View) {
		t.SetDirty()
	})*/
	backend.OnSelectionModified.Add(func(v *backend.View) {
		t.BroadcastData(t.GetSelectionMessage(v))
	})

	ed := backend.GetEditor()
	ed.SetFrontend(t)
	ed.LogInput(false)
	ed.LogCommands(false)
	c := ed.Console()
	if sc, err := textmate.LoadTheme("../../packages/themes/TextMate-Themes/Monokai.tmTheme"); err != nil {
		log.Error(err)
	} else {
		scheme = sc
	}

	defer func() {
		fmt.Println(util.Prof)
	}()

	w := ed.NewWindow()
	v := w.OpenFile("main.go", 0)
	//v.Settings().Set("trace", true)
	v.Settings().Set("syntax", "../../packages/go.tmbundle/Syntaxes/Go.tmLanguage")
	c.Buffer().AddObserver(t)

	sel := v.Sel()
	sel.Clear()
	//	end := v.Buffer().Size() - 2
	sel.Add(Region{0, 0})
	// sel.Add(Region{end - 22, end - 22})
	// sel.Add(Region{end - 16, end - 20})
	// sel.Add(Region{end - 13, end - 10})

	{
		w, h := 800, 600
		t.lock.Lock()
		t.layout[v] = layout{0, 0, w, h - console_height - 1, Region{}, 0}
		t.layout[c] = layout{0, h - console_height + 1, w, console_height - 5, Region{}, 0}
		t.lock.Unlock()
		t.Show(v, Region{1, 1})
	}
	t.Show(v, Region{100, 100})
	t.Show(v, Region{1, 1})

	go func() {
		ed.Init()
		sublime.Init()
	}()
	log.Debug("Serving on port %d", *port)
	http.HandleFunc("/", t.ServeHTTP)
	http.HandleFunc("/view", t.view)
	http.HandleFunc("/key", t.key)
	http.HandleFunc("/themes/", t.theme)
	http.Handle("/ws", websocket.Handler(t.WebsocketServer))
	if err := http.ListenAndServe(fmt.Sprintf("localhost:%d", *port), nil); err != nil {
		log.Errorf("Error serving: %s", err)
	}
	log.Debug("Done")
}
Пример #30
0
func (t *qmlfrontend) loop() (err error) {
	backend.OnNew.Add(t.onNew)
	backend.OnClose.Add(t.onClose)
	backend.OnLoad.Add(t.onLoad)

	ed := backend.GetEditor()
	ed.Init()
	go sublime.Init()
	ed.SetFrontend(t)
	ed.LogInput(false)
	ed.LogCommands(false)
	c := ed.Console()
	t.Console = &frontendView{bv: c}
	c.Buffer().AddObserver(t.Console)
	c.Buffer().AddObserver(t)

	var (
		engine    *qml.Engine
		component qml.Object
		// WaitGroup keeping track of open windows
		wg sync.WaitGroup
	)

	// create and setup a new engine, destroying
	// the old one if one exists.
	//
	// This is needed to re-load qml files to get
	// the new file contents from disc as otherwise
	// the old file would still be what is referenced.
	newEngine := func() (err error) {
		if engine != nil {
			log.Debug("calling destroy")
			// TODO(.): calling this appears to make the editor *very* crash-prone, just let it leak for now
			// engine.Destroy()
			engine = nil
		}
		log.Debug("calling newEngine")
		engine = qml.NewEngine()
		log.Debug("setvar frontend")
		engine.Context().SetVar("frontend", t)
		log.Debug("setvar editor")
		engine.Context().SetVar("editor", backend.GetEditor())

		log.Debug("loadfile")
		component, err = engine.LoadFile(qmlMainFile)
		if err != nil {
			return err
		}
		limeViewComponent, err = engine.LoadFile(qmlViewFile)
		return
	}
	if err := newEngine(); err != nil {
		log.Error(err)
	}

	backend.OnNewWindow.Add(func(w *backend.Window) {
		fw := &frontendWindow{bw: w}
		t.windows[w] = fw
		if component != nil {
			fw.launch(&wg, component)
		}
	})

	// TODO: should be done backend side
	if sc, err := textmate.LoadTheme("../../packages/themes/TextMate-Themes/Monokai.tmTheme"); err != nil {
		log.Error(err)
	} else {
		scheme = sc
	}

	defer func() {
		fmt.Println(util.Prof)
	}()

	w := ed.NewWindow()
	v := w.OpenFile("main.go", 0)
	// TODO: should be done backend side
	v.Settings().Set("syntax", "../../packages/go.tmbundle/Syntaxes/Go.tmLanguage")
	v = w.OpenFile("../../backend/editor.go", 0)
	// TODO: should be done backend side
	v.Settings().Set("syntax", "../../packages/go.tmbundle/Syntaxes/Go.tmLanguage")

	watch, err := fsnotify.NewWatcher()
	if err != nil {
		log.Errorf("Unable to create file watcher: %s", err)
		return
	}
	defer watch.Close()
	watch.Watch(".")
	defer watch.RemoveWatch(".")

	reloadRequested := false

	go func() {
		for {
			select {
			case ev := <-watch.Event:
				if ev != nil && strings.HasSuffix(ev.Name, ".qml") && ev.IsModify() && !ev.IsAttrib() {
					reloadRequested = true
					// Close all open windows to de-reference all
					// qml objects
					for _, v := range t.windows {
						if v.window != nil {
							v.window.Hide()
							v.window.Destroy()
							v.window = nil
						}
					}
				}
			}
		}
	}()

	for {
		// Reset reload status
		reloadRequested = false

		log.Debug("Waiting for all windows to close")
		// wg would be the WaitGroup all windows belong to, so first we wait for
		// all windows to close.
		wg.Wait()
		log.Debug("All windows closed. reloadRequest: %v", reloadRequested)
		// then we check if there's a reload request in the pipe
		if !reloadRequested || len(t.windows) == 0 {
			// This would be a genuine exit; all windows closed by the user
			break
		}

		// *We* closed all windows because we want to reload freshly changed qml
		// files.
		for {
			log.Debug("Calling newEngine")
			if err := newEngine(); err != nil {
				// Reset reload status
				reloadRequested = false
				log.Error(err)
				for !reloadRequested {
					// This loop allows us to re-try reloading
					// if there was an error in the file this time,
					// we just loop around again when we receive the next
					// reload request (ie on the next save of the file).
					time.Sleep(time.Second)
				}
				continue
			}
			log.Debug("break")
			break
		}
		log.Debug("re-launching all windows")
		// Succeeded loading the file, re-launch all windows
		for _, v := range t.windows {
			v.launch(&wg, component)
		}
	}
	return
}