This document is still work in progress
This document is meant to get you started hacking on the Leksah code base.
- Join us at
#leksah
on on freenode if you have any questions
##Getting started
-
Click "Fork" at the top of the leksah repository page
-
Clone your fork locally
git clone <url-to-your-fork>
-
Make sure you can build leksah from source (See README.md)
-
Have a look at the issue tracker or think of something new
-
Create a new branch to work in
git checkout -b my-branch-name
-
Haskell programming
-
Commit your changes
git commit -am "Some useful message"
-
Push the branch to your forked repo:
git push
-
Create a pull request on github (the page of your fork will hint for your recently submitted branches)
#Project infrastructure
The leksah application consists of three different projects: leksah
, leksah-ide
and ltk
. It uses the gtk gui library which are direct bindings to the C library.
#Types
This section summarizes some of the most important types in the code base. Most types are defined in src/IDE/Core/Types.hs.
These are some of the important types that describe (parts of) the application state.
===
###IDE
The application's state, such as the user preferences, the list of current errors and the open workspace. Operated on by the IDEM
monad.
===
###IDEPackage
Contains the information of a cabal package
===
###Workspace
Contains all the information of a leksah workspace.
===
###Prefs
The user preferences
The Leksah codebase uses a few custom monads to work conveniently in the context of the IDE
, Workspace
or an IDEPackage
.
===
###IDEM a
The IDE monad, it maintains a state of type IDE
. Actions that happen at the level of the IDE should use this type. For example, readIDE recentFiles :: IDEM [FilePath]
gets the list of recent files, showWorkspace :: IDEM ()
shows the workspace pane.
Implemented as type IDEM = ReaderT (IORef IDE) IO
Relevant types and functions
type IDEAction = IDEM ()
readIDE :: (IDE -> a) -> IDEM a
modifyIDE_ :: (IDE -> IDE) -> IDEM ()
liftIO :: IO a -> IDEM a
-- Running an IDE action in IO (for instance when registering a callback in gtk)
reflectIDE :: IDEM a -> IORef IDE -> IO a
-- Lift an IO action with the `IDE` reference in scope
reifyIDE :: (IORef IDE -> IO a) -> IDEM a
-- Typeclass for any other monad that can run an `IDEM a` action
class (Functor m, Monad m, MonadIO m) => MonadIDE m where
liftIDE :: IDEM a -> m a
instance MonadIDE IDEM where ...
ideMessage :: MonadIDE m => MessageLevel -> Text -> m ()
===
Used when writing IDE actions that work with the open workspace. This is implemented as type WorkspaceM = ReaderT Workspace IDEM
. See src/IDE/Workspaces.hs for examples.
Relevant types and functions
type WorkspaceAction = WorkspaceM ()
-- When no workspace open, prompts the user to open one
workspaceTry :: WorkspaceAction -> IDEAction
-- when no workspace open, fails with a message in the log pane
workspaceTryQuiet :: WorkspaceAction -> IDEAction
ask :: WorkspaceM Workspace
liftIDE :: IDEM a -> WorkspaceM a
===
Used when writing IDE actions that work with a specific package, in a specific workspace. This is implemented as ReaderT IDEPackage WorkspaceM
. See src/IDE/Package.hs for examples.
Relevant types and functions
type PackageAction = PackageM ()
-- When no package is active, prompts the user to activate one
packageTry :: PackageAction -> IDEAction
-- When no package is active, fails with a message in the log pane
packageTryQuiet
liftIDE :: IDEM a -> PackageM a
===
Used for actions that interact with ghci for a specific package. This is implemented as type DebugM = ReaderT (IDEPackage, ToolState) IDEM
. See src/IDE/Debug.hs for examples.
Relevant types and functions
type DebugAction = DebugM ()
runDebug :: DebugM a -> (IDEPackage, ToolState) -> IDEM a
-- When ghci is not running, prompts the user to activate ghci
tryDebug :: DebugAction -> PackageAction
-- When ghci is not running, fails quietly
tryDebugQuiet :: DebugAction -> PackageAction
Leksah has a mechanism for triggering and handling events.
===
All the events that can be triggered. When adding a new event, also adjust the instance of Event
and EventSource
(both in src/IDE/Types.hs).
Relevant types and functions
triggerEventIDE :: MonadIDE m => IDEEvent -> m IDEEvent
registerEvent :: IORef IDE -> Text -> (IDEEvent -> IDEAction) -> IDEAction
An action description is a small abstraction layer over GtkActions, which are used for menus, toolbars, and keyboard shortcuts. The structure contains info about tooltip text, menu icon etc. See src/IDE/Command.hs for the list of all available action descriptions.
A SensitivityMask
covers a certain set of action descriptions of which the corresponding gtk components (tool buttons, menu entries, shortcut keys) should be simultaneously sensitive (enabled) or insensitive (disabled). Fore example, when the text editor has no focus commands such as "FileClose" and "Goto Line" are set to insensitive.
Relevant types and functions
setSensitivity :: [(SensitivityMask, Bool)] -> IDEAction
TODO
TODO
The ltk
library defines a set of typeclasses for panes. For every pane in the IDE there is a seperate file in src/IDE/Pane/ which implements these.
===
An instance of this typeclass defines the functions for identifying the pane of type p
, operated on in monad m
(all current panes use IDEM
). Most panes consist mainly of some data and some GTK widgets.
===
An instance of this typeclass can restore the pane representation (of type p
) after restarting the IDE. Every pane has a piece of state (of type s
) that will be saved to disk and reloaded on startup. For instance, the pane for the text editor uses the following type to save its state:
-- | State for a buffer that points to a file on disk,
-- or an unsaved file
data BufferState = BufferState
FilePath -- ^ The opened file
Int -- ^ Cursor position (amount of characters)
| BufferStateTrans
Text -- ^ The buffer name
Text -- ^ The text in the buffer
Int -- ^ Cursor position (amount of characters)
deriving(Eq,Ord,Read,Show,Typeable)
Another example is the Symbol info pane (src/IDE/Info.hs) which remembers the symbol it was displaying.
Furthermore, an instance defines builder
which should construct a pane.
Relevant types and functions
-- (simplified types with m ~ IDEM)
-- Tries to get "the" pane of type p, in general there can only be one
-- pane per type with the exception of text buffers, for which it tries to
-- return the first.
getPane :: RecoverablePane p st IDEM => IDEM (Maybe p)
displayPane :: RecoverablePane p st IDEM
=> p -- ^ The pane
-> Bool -- ^ Whether it should also grab the focus
-> IDEAction
closePane :: RecoverablePane p st IDEM => p -> IDEM Bool