Vim plugins with a Cocoa gui

9 views
Skip to first unread message

Nico Weber

unread,
Dec 2, 2007, 12:51:11 PM12/2/07
to vim mac
Hi all,

a while ago Niklas Lindström suggested using the Scripting Bridge to
add custom gui windows to a running gvim ( http://groups.google.com/group/vim_mac/msg/039cf197bad56963
).

I played around with this for a short while, and it kinda works. Save
this program to a file, open it in vim and do `:pyfile %`. This will
open a window that contains a list of all the buffers you have open in
the vim you issued this command in, in a NSTableView (the default
cocoa table class).

<code>
from AppKit import *
import vim


class DataSource(NSObject):
def numberOfRowsInTableView_(self, tableView):
return len(vim.buffers)

def tableView_objectValueForTableColumn_row_(self, tableView,
tableColumn, row):
return vim.buffers[row].name

# Not required in vim-cocoa which already has a shared app in the vim
process
NSApplication.sharedApplication()

w = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(
NSMakeRect(0, 0, 400, 200), NSTitledWindowMask |
NSClosableWindowMask
| NSMiniaturizableWindowMask | NSResizableWindowMask,
NSBackingStoreBuffered, True)

t = NSTableView.alloc().initWithFrame_(NSMakeRect(10, 10, 300, 180))
ds = DataSource.alloc().init()
t.setDataSource_(ds)

c = NSTableColumn.alloc().initWithIdentifier_("Buffers")
c.setWidth_(300)
t.addTableColumn_(c)

t.sizeLastColumnToFit()
t.setHidden_(False)

w.contentView().addSubview_(t)

w.orderFront_(None)
</code>

However, there are a few (serious) problems:

1. This requires the python objective c bridge. This is include in the
python2.5 that's included with Leopard, but you probably have to
install it somehow manually on Tiger.

2. The release builds of MacVim link with python2.3, so if you want to
use MacVim with this you need to compile it yourself (and it still
won't work, see below)

3. The vim this is executing in needs to use some basic cocoa stuff
(like an autorelease pool and I guess most importantly an NSRunLoop).
This rules out carbon vim altogether, and sadly it rules out MacVim as
well (because MacVim uses Cocoa only in the gui process, but python is
executed in the core vim process). This leaves vim-cocoa. It actually
works just fine in vim-cocoa, but the binary release doesn't include
python support, so you have to compile it yourself (it's easy, there's
excellent documentation at http://code.google.com/p/vim-cocoa/wiki/BuildInstructions
).

4. Doing `:pyfile %` more than once crashes.

If you try this in (a self-compiled) MacVim, the window will show up,
but it won't do anything because it belongs to the vim core thread
which has no cocoa event loop. The `NSApplication.sharedApplication()`
line above creates a cocoa application in the vim core process (you
even get another vim icon in the dock for that), but since there's no
event loop the window shows up and does nothing.


In conclusion:

* This is very cool
* Just imagine what kind of plugins you could do with this
* It doesn't work in MacVim and I see no way to fix this
* Well, perhaps except telling the gui in the vim core process to not
show a dock icon and to put a run loop in there -- but it's not
trivial to do this I guess
* But it might still be worthwhile to try
* Then again, this approach would not allow adding views to the vim
window (since it belongs to a different process), which would be kinda
cool as well

Nico

björn

unread,
Dec 2, 2007, 1:14:58 PM12/2/07
to vim...@googlegroups.com
Hi Nico,

I don't understand myself exactly what it is you are doing, but let me
clarify some of the MacVim related points.

MacVim sets up autorelease pools (Cocoa basically cannot run without
one) in main.c so this is not the problem. Also, each Vim process
does have a run loop, search inside MMBackend.m. A run loop is needed
for distributed objects.

>
> 4. Doing `:pyfile %` more than once crashes.
>
> If you try this in (a self-compiled) MacVim, the window will show up,
> but it won't do anything because it belongs to the vim core thread
> which has no cocoa event loop. The `NSApplication.sharedApplication()`
> line above creates a cocoa application in the vim core process (you
> even get another vim icon in the dock for that), but since there's no
> event loop the window shows up and does nothing.

Here is something that does not happen in each Vim process. No
NSApplication object is ever instantiated and hence [NSApp run] isn't
called. This is where the AppKit magic happens...stuff gets
initialized etc. Without [NSApp run] you only get access to
Foundation functionality. I guess it is feasible to start up NSApp
inside a Vim process, but you'll have to make some hacks to get out of
it ([NSApp run] never exits)...check out vim-cocoa how this is done.
Also, once NSApp is set up you'll probably have to update it every now
and again by calling [NSApp run] and breaking out. It might just work
to directly operate on the run loop (the way it is done now), but I'm
not sure.

> In conclusion:
>
> * This is very cool
> * Just imagine what kind of plugins you could do with this
> * It doesn't work in MacVim and I see no way to fix this
> * Well, perhaps except telling the gui in the vim core process to not
> show a dock icon and to put a run loop in there -- but it's not
> trivial to do this I guess
> * But it might still be worthwhile to try
> * Then again, this approach would not allow adding views to the vim
> window (since it belongs to a different process), which would be kinda
> cool as well

I don't have any idea either. Mostly because I don't understand what
you are doing (due to my ineptitude) :-)


/Björn

Nico Weber

unread,
Dec 2, 2007, 1:24:41 PM12/2/07
to vim...@googlegroups.com
> I don't understand myself exactly what it is you are doing

Well, it's a vim-python script (`:h python`) that opens a window (an
NSWindow, not a text window) inside the vim process, so it has access
to all the vim stuff. You could create a window with a "New Line"
button that inserts a new line in the current vim buffer (or even
something useful), from within a vim script (for example, a plugin).

> but let me clarify some of the MacVim related points.

Thanks for the clarifications. I'll take a look at how vim-cocoa
manages to call [NSApp run].

Nico

Jjgod Jiang

unread,
Dec 2, 2007, 1:38:54 PM12/2/07
to vim...@googlegroups.com
Hi,

On Dec 3, 2007 2:24 AM, Nico Weber <nicola...@gmx.de> wrote:
> Thanks for the clarifications. I'll take a look at how vim-cocoa
> manages to call [NSApp run].

vim-cocoa does event loop in following ways:

1. Most of the time (in gui_mch_wait_for_chars()), it just fetches a
new event with [NSApp nextEventMatchingMask:untilDate:inMode:dequeue:]
and dispatch it with [NSApp sendEvent:].

2. Like Björn said, we have to execute [NSApp run] at least once to do
some magical initialization stuff. So when gui_mch_wait_for_chars()
gets called the first time, I set up a one-shot timer called
initializeApplicationTimer which will be fired after 0.1 second, then
call [NSApp run], let the timer to stop the NSApp event loop with
[NSApp stop: self].

3. Some modal dialogs require similar steps like (2), but we can call
[NSApp stop: self] in events like -alertDidEnd:returnCode:contextInfo:,
so no extra timer needed.

HTH.

- Jiang

björn

unread,
Dec 2, 2007, 5:20:00 PM12/2/07
to vim...@googlegroups.com
On 02/12/2007, Jjgod Jiang <gzj...@gmail.com> wrote:
>
> On Dec 3, 2007 2:24 AM, Nico Weber <nicola...@gmx.de> wrote:
> > Thanks for the clarifications. I'll take a look at how vim-cocoa
> > manages to call [NSApp run].
>
> vim-cocoa does event loop in following ways:
>
> 2. Like Björn said, we have to execute [NSApp run] at least once to do
> some magical initialization stuff. So when gui_mch_wait_for_chars()
> gets called the first time, I set up a one-shot timer called
> initializeApplicationTimer which will be fired after 0.1 second, then
> call [NSApp run], let the timer to stop the NSApp event loop with
> [NSApp stop: self].

For the record:

Another way that works (I have tried it) is to define a user event,
then override [NSApplication sendEvent] (in a subclass) to stop the
run loop when this user event is received (by calling [NSApp stop]).
Finally, before calling [NSApp run] the first time you post your user
event, then call [NSApp run] and it will do its initialization and
immediately return. :-)


/Björn

Panos

unread,
Dec 3, 2007, 2:48:06 PM12/3/07
to vim_mac
This is pretty exciting mate.

Maybe one day we'll get to have the cocoa frontend for Project.vim :)

On Dec 3, 12:20 am, "björn" <bjorn.winck...@gmail.com> wrote:
> On 02/12/2007, Jjgod Jiang <gzjj...@gmail.com> wrote:

Nicholas

unread,
Dec 31, 2007, 1:32:53 AM12/31/07
to vim_mac


On Dec 3, 4:51 am, Nico Weber <nicolaswe...@gmx.de> wrote:
> Hi all,
>
> a while ago Niklas Lindström suggested using the Scripting Bridge to  
> add custom gui windows to a running gvim (http://groups.google.com/group/vim_mac/msg/039cf197bad56963
>   ).
>
> I played around with this for a short while, and it kinda works.

Hi all,

I was doing the same sort of thing recently and encountered the same
problems.

I really like TextMate's fuzzy-matching file selection (using cmt-T),
so I recreated its gui using Python and PyObjC. If you're not familiar
with it, pressing cmt-T brings up a window containing a text box and a
list of all files in the cwd and below. Typing into the text box
narrows the list of filenames. It has a couple of other niceties (such
as fuzzy matching).

Anyway, when adding the GUI to MacVim as a plugin, there is no way to
get access to the Vim GUI. Basically, I wanted to pop up my file
selector as a modal window on top of the main window, but since
NSApp.keyWindow returns nil (well, None in PyObjC), the best I can do
is essentially create a separate app, complete with startup time and
Dock icon.

Would it be possible to run Python commands (:py) in the GUI, rather
than the Vim process? I don't know enough about Vim internals to know
how much this would complicate eg Python's Vim support ("import vim").

Panos

unread,
Dec 31, 2007, 3:45:49 AM12/31/07
to vim_mac
Maybe it would require macvim exposing it's gui, so `import vim`, for
the buffer stuff etc, and `import macvim`, to get to the gui.

Jjgod Jiang

unread,
Dec 31, 2007, 4:46:30 AM12/31/07
to vim_mac

On Sun, 30 Dec 2007, Nicholas wrote:

> Anyway, when adding the GUI to MacVim as a plugin, there is no way to
> get access to the Vim GUI. Basically, I wanted to pop up my file
> selector as a modal window on top of the main window, but since
> NSApp.keyWindow returns nil (well, None in PyObjC), the best I can do
> is essentially create a separate app, complete with startup time and
> Dock icon.
>
> Would it be possible to run Python commands (:py) in the GUI, rather
> than the Vim process? I don't know enough about Vim internals to know
> how much this would complicate eg Python's Vim support ("import vim").

As Nico stated in his previous mail, MacVim GUI is separated with the
vim process, but The functionality you described seemed have nothing to
do with the "core" vim part, could you elaborate on what kind of
information you want to get from vim?

Of course it's possible to have embedded Python support in MacVim GUI,
you can just add the Python.framework into MacVim.xcodeproj, then find a
proper place to do something like PySimpleString (you can do more
complicated stuff of course), but this Python embedding will have nothing
to do with the vim process, so it won't be that easy to fetch some useful
information from that process.

HTH.

- Jiang

Nicholas

unread,
Dec 31, 2007, 9:59:14 AM12/31/07
to vim_mac
> > Would it be possible to run Python commands (:py) in the GUI, rather
> > than the Vim process? I don't know enough about Vim internals to know
> > how much this would complicate eg Python's Vim support ("import vim").
>
> As Nico stated in his previous mail, MacVim GUI is separated with the
> vim process, but The functionality you described seemed have nothing to
> do with the "core" vim part, could you elaborate on what kind of
> information you want to get from vim?

Yep -- I don't actually want anything from Vim proper, I just want to
be able to pop up a window. It seems like the GUI part of MacVim is
the best place to do this, because then it can be integrated with the
rest of the app -- rather than popping up a separate appicon, not
being modal, etc, which is what you get if you create a run loop in
the core vim process, which is what seems to be happening now.

> Of course it's possible to have embedded Python support in MacVim GUI,
> you can just add the Python.framework into MacVim.xcodeproj, then find a
> proper place to do something like PySimpleString (you can do more
> complicated stuff of course), but this Python embedding will have nothing
> to do with the vim process, so it won't be that easy to fetch some useful
> information from that process.

Yeah, it would be cool if it were somehow possible to get the best of
both worlds -- easy access to GUI elements, but also be able to act
like a normal Vim python plug-in. :)
Reply all
Reply to author
Forward
0 new messages