mirror of
https://github.com/macvim-dev/macvim.git
synced 2026-06-11 15:37:29 +02:00
Update README file
This commit is contained in:
+150
-182
@@ -1,196 +1,164 @@
|
||||
Compiling:
|
||||
This README contains an overview of the MacVim source code and a very short
|
||||
description on how to build the application.
|
||||
|
||||
- To build the project:
|
||||
+ patch vim7 src with MacVim patch
|
||||
+ make vim7 src with --enable-gui=macvim
|
||||
+ build MacVim.xcodeproj
|
||||
- To install:
|
||||
+ copy MacVim.app to /Applications (or anywhere you want it)
|
||||
+ in ~/.profile add this line:
|
||||
alias gvim='/Applications/MacVim.app/Contents/MacOS/Vim -g'
|
||||
- To run:
|
||||
+ Double click MacVim icon
|
||||
+ with the above alias you can type 'gvim' in terminal to open MacVim
|
||||
(if the -g switch is left out, then Vim is started in terminal mode)
|
||||
+ in terminal mode of Vim, type :gui and MacVim will start
|
||||
- Technical notes:
|
||||
+ to build a universal binary, the compiler AND linker needs the flags
|
||||
'-isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386'; also,
|
||||
make needs argument --with-mac-arch=both
|
||||
+ vim runtime files are copied to
|
||||
'MacVim.app/Contents/Resources/vim/runtime/'
|
||||
|
||||
Weirdness:
|
||||
|
||||
- [obsolete] When the text system (Cocoa) comes across multi byte characters it
|
||||
automatically chooses a font for those characters; this font may not be the
|
||||
same height as the one set on the text storage and hence the program must
|
||||
account for the fact that lines may have differing heights.
|
||||
We get around this problem by resizing the window to fit the text storage
|
||||
after the layout manager has performed layout. As a side-effect the user
|
||||
sees how the window resizes when certain multi byte characters are being
|
||||
displayed.
|
||||
- [obsolete] Remember to set 'inputReceived = YES' in MMBackend
|
||||
handlePortMessage:, otherwise Vim will not inform MMVimController of
|
||||
changes it makes (e.g. in response to a key down event).
|
||||
- The way delegate messages from the tab bar are handled are based on lots of
|
||||
assumptions on how the code works. See comments in tabView:... delegate
|
||||
messages in MMWindowController.
|
||||
- [obsolete] The insertion point is automatically moved to wherever changes are
|
||||
made in the text storage. To set the position manually (via
|
||||
setSelectedRange:), first call endEditing on the text storage.
|
||||
- Delegate messages from the tab view need to be able to differentiate whether
|
||||
the message was sent due to the user clicking a tab with the mouse, or if the
|
||||
vim task initiated the change. To facilitate this, flags must be set
|
||||
whenever the vim task does something that results in these delegate messages
|
||||
being sent. See comments in the tabView:...Item: messages in
|
||||
MMWindowController.
|
||||
- In Vim the first tab has index 1, in the gui the first tab has index 0. This
|
||||
is compensated for in MMBackend.m.
|
||||
- The PSMTabBarControl does not reorder the NSTabView when a user drags tabs
|
||||
around, so we must rely on [PSMTabBarControl representedItems] to get the
|
||||
correct order of tabs (the order which the user can 'see'). WARNING! This
|
||||
means that the code cannot rely on calls like
|
||||
[NSTabView selectTabViewItemAtIndex:] etc. since the NSTabView has the
|
||||
'wrong' order.
|
||||
- The MMVimController is added to the NSEventTrackingRunLoopMode, otherwise
|
||||
updates from Vim would not reach the MMVimController while the user
|
||||
resizes the window using the mouse.
|
||||
- It seems that (oneway void) DO messages can arrive when another such message
|
||||
is being processed. For this reason, no input is sent to Vim whilst in
|
||||
processCommandQueue:. Instead, messages are queued and sent when
|
||||
processCommandQueue: has finished. Otherwise the invariant that all Vim
|
||||
messages must appear in the same order they were issued will be violated.
|
||||
- Text storage dimensions are not ever directly modified, instead a message is
|
||||
sent to Vim asking it to change the "shell size". Otherwise, a message
|
||||
asking Vim to change the shell size might get lost and Vim and MacVim will
|
||||
have inconsistent states.
|
||||
- gui_mch_browse() and gui_mch_dialog() are blocking calls, but you can't put
|
||||
up dialogs in Cocoa which block until the user dismisses them (it uses
|
||||
callbacks). This complicates the browsing somewhat.
|
||||
- When binding menus to :action note that that action will be used for all
|
||||
modes. The reason for this is that MacVim needs to be able to execute such
|
||||
menu items even when no windows are open (e.g. newVimWindow:) and the default
|
||||
menu action relies on Vim to deal with it.
|
||||
- The 'help' key is treated as a special key by Cocoa; when the user presses
|
||||
this key the mouse cursor changes to a question mark and the application is
|
||||
put into 'context help mode'. The key down event is never sent down the
|
||||
responder chain. To get around this problem we are forced to subclass
|
||||
NSApplication and look for the 'help' key in sendEvent: (see MMApplication).
|
||||
The information in here is not meant to be exhaustive. A lot more information
|
||||
can be found in the source code comments.
|
||||
|
||||
|
||||
Design decisions:
|
||||
Source code overview:
|
||||
|
||||
- Output is queued and sent to the MMVimController only when
|
||||
[MMBackend flushQueue] is called in order to minimize the amount of
|
||||
messages sent back and forth between the task and gui. Also, this makes sure
|
||||
that commands reach MacVim in the same order they were issued by Vim.
|
||||
- Drawing commands are stored in a buffer (drawData) and put on the output
|
||||
queue whenever [MMBackend flush] is called. This buffer might no
|
||||
longer be needed now that there is a general output queue. However, the
|
||||
existing code works, so why change it?
|
||||
- [obsolete] The gui listens for tasks on a named port whose name is derived
|
||||
from CFBundleIdentifier (which is defined inside Info.plist of the app
|
||||
bundle). In order to run two different copies of MacVim at the same time,
|
||||
they need to have differing bundle identifiers; otherwise one copy will not
|
||||
be able to create the named listening port and all tasks will connect to the
|
||||
first copy.
|
||||
- The gui creates a named NSConnection which vends the MMAppController object.
|
||||
- All tabs share one text view and its associated text storage. There used to
|
||||
be one text view per tab, but this only complicated the code since Vim has no
|
||||
concept of different views (as in NSView).
|
||||
- Vim holds the actual state. MacVim should never change Vim related states
|
||||
directly, instead it must ask Vim to change the state and wait for Vim to
|
||||
reply with an actual state change command.
|
||||
- If MacVim wants to change the state of Vim it must go through
|
||||
processInput:data:, this is an asynchronous call.
|
||||
- MacVim can query state information synchronously by adding a suitable message
|
||||
to MMBackendProtocol, however this must not change the state of Vim!
|
||||
- If MacVim or Vim dies, the NSConnection is invalidated and connectionDidDie:
|
||||
is invoked.
|
||||
- Input may reach the backend whenever the run loop is updated. This can cause
|
||||
problems if more input is received whilst already processing other input. At
|
||||
the moment new input is dropped if the backend is already processing other
|
||||
input.
|
||||
MacVim.app consists of two executables: MacVim and Vim. MacVim is a Cocoa app
|
||||
which does all the window management including drawing and receiving input.
|
||||
Vim is the actual editor which receives input from MacVim and sends output
|
||||
back when there is something to draw.
|
||||
|
||||
As far as the source code files goes, MacVim.[m|h] contains code shared
|
||||
between MacVim and Vim, gui_macvim.m and MMBackend.[m|h] belongs to Vim,
|
||||
everything else belongs to MacVim. (The source code is all Objective-C which
|
||||
is very easy to pick up if you know C and some object oriented programming.)
|
||||
|
||||
Each editor window in MacVim runs its own Vim process (but there is always
|
||||
only one MacVim process). Communication between MacVim and a Vim process is
|
||||
done using Distributed Objects (DO). Each Vim process is represented by a
|
||||
backend object (MMBackend) and it communicates with a frontend object in the
|
||||
Vim process (MMVimController). The interface between the backend and frontend
|
||||
is defined in MacVim.h.
|
||||
|
||||
The frontend sends input to the backend by calling
|
||||
-[MMBackend processInput:data:]. The backend queues output on a command queue
|
||||
and sends it to the frontend at opportune times by calling
|
||||
-[MMVimController processCommandQueue:]. These are both asynchronous calls so
|
||||
MacVim can keep drawing and receiving input while Vim is working away, thus
|
||||
always keeping the user interface responsive.
|
||||
|
||||
The state of each editor window is kept entirely in the Vim process. MacVim
|
||||
should remain "ignorant" in the sense that it knows nothing of the actual
|
||||
state of a Vim process. Typically this is not a problem, but sometimes MacVim
|
||||
must change state without going via Vim, and sometimes MacVim needs immediate
|
||||
access to the state from Vim. The former happens e.g. when the user drags to
|
||||
resize a window (MacVim changes the window dimensions immediately without
|
||||
asking Vim first), the second can happen when some option variable affects the
|
||||
way something is presented visually (e.g. MacVim needs immediate access to the
|
||||
'mousehide' option so that it can hide the mouse cursor when input is
|
||||
received). State information that may be required in this way can be "pushed"
|
||||
to MacVim inside -[MMBackend queueVimStateMessage].
|
||||
|
||||
|
||||
Keyboard stuff:
|
||||
Vim:
|
||||
|
||||
- input ends up in one of the following methods
|
||||
(1) insertText:
|
||||
(2) doCommandBySelector:
|
||||
(3) performKeyEquivalent:
|
||||
Hooks from within Vim are implmented in gui_macvim.m, the name of such
|
||||
functions usually start with "gui_mch_" and they should simply put a message
|
||||
on the output queue, by calling queueMessage:properties: on the singleton
|
||||
MMBackend object [MMBackend sharedInstance] (see e.g. gui_mch_destroy_menu()).
|
||||
The output queue is flushed when requested (in -[MMBackend flushQueue]) or
|
||||
before Vim takes a nap whilst waiting for new input (in
|
||||
-[MMBackend waitForInput]).
|
||||
|
||||
- (1) handles: printable keys (a, Space, 1, ...) and <M-key> (also <M-C-key>).
|
||||
if Ctrl+Option is held [NSEvents characters] will translate the input to
|
||||
control characters; note that if the translation fails, then Shift and Option
|
||||
modifiers are NOT includeded in [NSEvent characters], but they are included
|
||||
in [NSEvent charactersIgnoringModifiers]. e.g. given <M-C-S-1>, characters
|
||||
returns 1, charactersIgnoringModifiers returns <M-S-1>.
|
||||
- (2) handles: Ctrl+key, enter, backspace, escape.
|
||||
same note on translation of Ctrl+key as above holds true.
|
||||
come Ctrl+key combos are by default mapped to several commands, so Ctrl+keys
|
||||
must be intercepted in keyDown:
|
||||
- (3) handles: Cmd+key, arrow keys, function keys, help key
|
||||
Cmd+letter keys never reach the app if this method isn't overridden (but
|
||||
Cmd+function keys do)
|
||||
Cmd+function key must not be intercepted here or input methods won't work
|
||||
- <M-Space> and <Space> are two different characters (the former is 0xa0)
|
||||
- Cocoa translates <C-Enter> to Ctrl-C so this must be taken care of
|
||||
- <Tab> with various modifiers is very special, check MMBackend how it is
|
||||
handled
|
||||
Note that each Vim process has its own run loop (it is required for DO) and
|
||||
since Vim is in charge of its thread it needs to "update" the run loop
|
||||
manually. This can happen in -[MMBackend update], which returns immediately
|
||||
if nothing is pending on the run loop, or in -[MMBackend waitForInput], which
|
||||
can possibly block until input appears on the run loop. In any case, if Vim
|
||||
for some reason fails to update the run loop then incoming DO calls will not
|
||||
be processed and for this reason it is best to avoid making synchronous DO
|
||||
calls from MacVim. (If synchronous calls must be made then it is important to
|
||||
set proper timeouts so that MacVim doesn't "hang", see
|
||||
-[MMVimConroller sendMessageNow:::] to see how this can be done.)
|
||||
|
||||
|
||||
Bugs:
|
||||
MacVim:
|
||||
|
||||
- Using NSString initWithBytesNoCopy:::: causes crash when trying to set window
|
||||
title.
|
||||
- NSTabViewItem setInitialFirstResponder: seems to have no effect, so we
|
||||
manually set the first responder when the tab item selection changes.
|
||||
- PSMTabBarControl never removes itself as an observer, which can cause all
|
||||
sort of weird problems (crashes etc.), so this is taken care of at cleanup.
|
||||
- PSMTabBarControl retains its delegate, so the delegate is forcibly set to nil
|
||||
at cleanup, else there will be a memory leak.
|
||||
The main nib of MacVim.app is MainMenu.nib which contains the default menu and
|
||||
an instance of MMAppController, which is connected as the delegate of
|
||||
NSApplication. That mens, when MacVim starts it will load this nib file and
|
||||
automatically create an instance of the MMAppController singleton.
|
||||
|
||||
A new editor window is opened by calling
|
||||
-[MMAppController launchVimProcessWithArguments:]. This functions starts a
|
||||
new Vim process (by executing the Vim binary). The Vim process lets MacVim
|
||||
know when it has launched by calling -[MMAppController connectBackend:pid:]
|
||||
and MacVim responds to this message by calling a new frontend object
|
||||
(MMVimController) and returns a proxy to this object back to the Vim process.
|
||||
From this point onward the Vim process communicates directly with the
|
||||
MMVimController.
|
||||
|
||||
The MMVimController represents the frontend of a Vim process inside MacVim.
|
||||
It coordinates all communication with the Vim process and delegates output
|
||||
that affects visual presentation to a MMWindowController object. Read the
|
||||
Cocoa documentation on the responsibilities of a window controller.
|
||||
|
||||
Input (keyboard & mouse) handling and drawing is handled by a MMTextView
|
||||
object.
|
||||
|
||||
|
||||
Features (!supp indicates that a feature is not supported):
|
||||
Distributed Object dangers:
|
||||
|
||||
- Multiple top-level windows: each window runs its own vim process (they are
|
||||
completely independent)
|
||||
- Tabs: uses PSMTabBarControl to show tabs, can reorder tabs by dragging them,
|
||||
has overflow menu, new tab button on tabline
|
||||
- Menubar: accelerators !supp, actext hint displayed as tool tip
|
||||
instead of on menu, each window has its own menu, set key equivalents with
|
||||
:menukeyequiv command
|
||||
- Toolbar: toolbariconsize supported (tiny&small equiv to 24x24 px,
|
||||
medium&large equiv to 32x32 px), toolbar supports 'icons' and 'text' options
|
||||
(but not 'tooltip' which is always on), each window has its own toolbar,
|
||||
custom toolbar items
|
||||
- Cocoa input protocols: input managers, character palette input etc.
|
||||
supported, marked text partially supported, cocoa key bindings
|
||||
(DefaultKeyBinding.dict) are disabled
|
||||
- Mouse: resize (vim) windows, selection, different mouse cursors,
|
||||
autoscrolling whilst selecting (horizontal autoscroll !supp)
|
||||
- Drag and Drop: drag files onto dock icon to open in tabs, drag text and files
|
||||
onto text view
|
||||
- Zoom: Command-click to zoom to fill window, otherwise only zoom height,
|
||||
hold down Option to zoom all windows
|
||||
- Resize: live resize (although terribly slow), :set lines will not make window
|
||||
higher than can fit the screen (no such restrictions on width at the moment)
|
||||
- Pasteboard: star-register works with the mac os x pasteboard
|
||||
- Open/Save dialog: use with :browse
|
||||
- Gui dialogs
|
||||
- Fonts: bold/italic/underline traits supported, font changes with ':set gfn',
|
||||
or use font panel
|
||||
- File type associations: add more associations by editing Info.plist
|
||||
- Start GUI from terminal, type :gui
|
||||
- Scroll bars
|
||||
- Wide characters: but composed characters !supp
|
||||
- Printing: !supp
|
||||
- Find/Replace dialog: !supp
|
||||
- External editor protocol: !supp
|
||||
- Services menu: some simple minded provider entries
|
||||
- Encodings: !supp (enc, tenc always set to utf-8)
|
||||
- Autosave window position
|
||||
- Smart cascading of new windows
|
||||
- Client/server support (only gui window can become server)
|
||||
Distributed Object messages are handled whenever the run loop is updated.
|
||||
Since the run loop can be updated at unpredictable times some care has to be
|
||||
taken when implementing DO messages. Some unexpected examples of when the run
|
||||
loop is updated:
|
||||
|
||||
1. When a synchronous DO message is sent. The run loop goes into a loop
|
||||
waiting for a return to the synchronous message; During this wait another DO
|
||||
message may arrive.
|
||||
|
||||
2. When a modal loop is entered. For example, when a user presses a Cmd-key
|
||||
the menu flashes briefly. During this "flash" a modal loop is entered.
|
||||
|
||||
Item 1 can cause a problem if MacVim sends a synchronous message and before a
|
||||
reply reacheds MacVim another message is received. From the source code it
|
||||
looks like the synchronous message blocks but in fact the other message is
|
||||
executed during this "block". If the other message changes state radically
|
||||
something may go wrong after the synchronous DO message returns.
|
||||
|
||||
Item 2 can cause similar problems but it may happen deep inside a Cocoa call
|
||||
which may be even more puzzling.
|
||||
|
||||
One way to alleviate these problems is to ensure a DO message isn't entered
|
||||
twice by setting a boolean at the beginning of the message and clearing it
|
||||
afterwards. If the boolean is already set when entering the call must somehow
|
||||
be delayed. See -[MMVimController processCommandQueue:] for a concrete
|
||||
example.
|
||||
|
||||
Another danger is that we must take care when releasing objects that Cocoa may
|
||||
be using. See -[MMVimController connectionDidDie:] how MacVim releases
|
||||
MMVimControllers when the Vim process they control exits.
|
||||
|
||||
|
||||
Source code file organisation:
|
||||
|
||||
Here is an incomplete list of source code files with a short explanation of
|
||||
what they contain:
|
||||
|
||||
MMAppController.* Everything related to running the application
|
||||
MMBackend.* Object representing a Vim process in backend
|
||||
MMTextView.* Handles input and drawing
|
||||
MMVimController.* Object representing a Vim Process in frontend
|
||||
MMVimView.* Cocoa view object
|
||||
MMWindowController.* Coordinates visual presentation
|
||||
MacVim.* Code shared between MacVim and Vim
|
||||
gui_macvim.m Hooks from Vim
|
||||
|
||||
|
||||
Building:
|
||||
|
||||
You will need to install the Xcode tools before building the source code.
|
||||
Nothing else needs to be installed in order to build MacVim.
|
||||
|
||||
Steps to build MacVim.app (the text before the '$' shows the folder you should
|
||||
be in when executing these commands):
|
||||
|
||||
1. Configure Vim
|
||||
src/$ configure --enable-gui=macvim
|
||||
|
||||
2. Build Vim executable
|
||||
src/$ make
|
||||
|
||||
3. Build MacVim.app application bundle
|
||||
src/MacVim/$ xcodebuild
|
||||
|
||||
The application bundle can be found inside "src/MacVim/build/Release".
|
||||
|
||||
|
||||
Bjorn Winckler <bjorn.winckler@gmail.com>
|
||||
June 22, 2008
|
||||
|
||||
Reference in New Issue
Block a user