From 8ccaae54ffea5b037115d0a3fb6ceac3ba16abae Mon Sep 17 00:00:00 2001 From: Bjorn Winckler Date: Sun, 22 Jun 2008 17:08:38 +0200 Subject: [PATCH] Update README file --- src/MacVim/README | 360 +++++++++++++++++++++++++----------------------------- 1 file changed, 164 insertions(+), 196 deletions(-) rewrite src/MacVim/README (99%) diff --git a/src/MacVim/README b/src/MacVim/README dissimilarity index 99% index bba32775..4c7ec2b3 100644 --- a/src/MacVim/README +++ b/src/MacVim/README @@ -1,196 +1,164 @@ -Compiling: - -- 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). - - -Design decisions: - -- 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. - - -Keyboard stuff: - -- input ends up in one of the following methods - (1) insertText: - (2) doCommandBySelector: - (3) performKeyEquivalent: - -- (1) handles: printable keys (a, Space, 1, ...) and (also ). - 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 , characters - returns 1, charactersIgnoringModifiers returns . -- (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 -- and are two different characters (the former is 0xa0) -- Cocoa translates to Ctrl-C so this must be taken care of -- with various modifiers is very special, check MMBackend how it is - handled - - -Bugs: - -- 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. - - -Features (!supp indicates that a feature is not supported): - -- 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) +This README contains an overview of the MacVim source code and a very short +description on how to build the application. + +The information in here is not meant to be exhaustive. A lot more information +can be found in the source code comments. + + +Source code overview: + +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]. + + +Vim: + +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]). + +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.) + + +MacVim: + +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. + + +Distributed Object dangers: + +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 +June 22, 2008 -- 2.11.4.GIT