Index: Tomboy/Plugins/Makefile.am
===================================================================
--- Tomboy/Plugins/Makefile.am	(revision 890)
+++ Tomboy/Plugins/Makefile.am	(working copy)
@@ -128,6 +128,42 @@
 	$(CSC) -out:$@ $(CSFLAGS) $(ASSEMBLIES) $(BACKLINKS_CSFILES) $(BACKLINKS_RESOURCES)
 
 
+#
+# Underline plugin
+#
+
+UNDERLINE_TARGET = Underline.dll
+UNDERLINE_CSFILES = $(srcdir)/Underline.cs
+UNDERLINE_RESOURCES =
+
+$(UNDERLINE_TARGET): $(UNDERLINE_CSFILES) $(top_builddir)/Tomboy/Tomboy.exe
+	$(CSC) -out:$@ $(CSFLAGS) $(ASSEMBLIES) $(UNDERLINE_CSFILES) $(UNDERLINE_RESOURCES)
+
+
+#
+# WikiMarkup plugin
+#
+
+WIKIMARKUP_TARGET = WikiMarkup.dll
+WIKIMARKUP_CSFILES = $(srcdir)/WikiMarkup.cs
+WIKIMARKUP_RESOURCES =
+
+$(WIKIMARKUP_TARGET): $(WIKIMARKUP_CSFILES) $(top_builddir)/Tomboy/Tomboy.exe
+	$(CSC) -out:$@ $(CSFLAGS) $(ASSEMBLIES) $(WIKIMARKUP_CSFILES) $(WIKIMARKUP_RESOURCES)
+
+
+#
+# Tagging plugin
+#
+
+TAGGING_TARGET = Tagging.dll
+TAGGING_CSFILES = $(srcdir)/Tagging.cs
+TAGGING_RESOURCES = -resource:$(srcdir)/Tagging-UI.xml
+
+$(TAGGING_TARGET): $(TAGGING_CSFILES) $(top_builddir)/Tomboy/Tomboy.exe
+	$(CSC) -out:$@ $(CSFLAGS) $(ASSEMBLIES) $(TAGGING_CSFILES) $(TAGGING_RESOURCES)
+
+
 pluginsdir = $(pkglibdir)/Plugins
 plugins_DATA = 			\
 	$(BACKLINKS_TARGET)	\
@@ -138,7 +174,10 @@
 	$(GALAGO_TARGET)	\
 	$(NOTD_TARGET)		\
 	$(PRINT_TARGET)		\
-	$(STICKYIMPORT_TARGET)
+	$(STICKYIMPORT_TARGET)	\
+	$(TAGGING_TARGET)	\
+	$(UNDERLINE_TARGET)	\
+	$(WIKIMARKUP_TARGET)
 
 
 EXTRA_DIST =            	\
@@ -151,7 +190,11 @@
 	$(PRINT_CSFILES)	\
 	$(NOTD_CSFILES)		\
 	$(FIXEDWIDTH_CSFILES)	\
-	$(STICKYIMPORT_CSFILES)
+	$(STICKYIMPORT_CSFILES)	\
+	$(TAGGING_CSFILES)	\
+	Tagging-UI.xml		\
+	$(UNDERLINE_CSFILES)	\
+	$(WIKIMARKUP_CSFILES)
 
 CLEANFILES =				\
 	$(BACKLINKS_TARGET)		\
@@ -171,4 +214,11 @@
 	$(FIXEDWIDTH_TARGET)		\
 	$(FIXEDWIDTH_TARGET).mdb	\
 	$(STICKYIMPORT_TARGET)		\
-	$(STICKYIMPORT_TARGET).mdb
+	$(STICKYIMPORT_TARGET).mdb	\
+	$(TAGGING_TARGET)			\
+	$(TAGGING_TARGET).mdb		\
+	$(UNDERLINE_TARGET)			\
+	$(UNDERLINE_TARGET).mdb		\
+	$(WIKIMARKUP_TARGET)		\
+	$(WIKIMARKUP_TARGET).mdb
+	
\ No newline at end of file
Index: Tomboy/Plugins/Tagging.cs
===================================================================
--- Tomboy/Plugins/Tagging.cs	
+++ Tomboy/Plugins/Tagging.cs	
@@ -0,0 +1,522 @@
+using System;
+using System.Collections;
+using System.Text.RegularExpressions;
+
+using Gtk;
+using Mono.Unix;
+using Pango;
+using Tomboy;
+
+class CompareByKey : IComparer
+{
+    public int Compare(object o1, object o2)
+    {
+        DictionaryEntry e1 = (DictionaryEntry)o1;
+        DictionaryEntry e2 = (DictionaryEntry)o2;
+        return String.Compare((string)e1.Key, (string)e2.Key);
+    }
+}
+
+class Tag : IComparable
+{
+    public static readonly Regex TaggingExpression =
+        new Regex(@"\b[Tt]ags:\s*([^\n]*)", RegexOptions.IgnoreCase);
+
+    private readonly ArrayList subtags;
+    private readonly ArrayList notes;
+
+    private readonly Note description;
+    private readonly string title;
+
+    private IList parentTags;
+
+    public Tag(string title, Note description)
+    {
+        this.subtags = new ArrayList();
+        this.notes = new ArrayList();
+        this.description = description;
+        this.title = title;
+    }
+
+    public string Title
+    {
+        get { return title; }
+    }
+
+    public Note Description
+    {
+        get { return description; }
+    }
+
+    public IList ParentTags
+    {
+        get
+        {
+            if (null == parentTags)
+                parentTags = Extract(description);
+
+            return parentTags;
+        }
+    }
+
+    public IList SubTags
+    {
+        get { return subtags; }
+    }
+
+    public IList Notes
+    {
+        get { return notes; }
+
+        set
+        { 
+            notes.Clear();
+            notes.AddRange(value);
+        }
+    }
+
+    public int CompareTo(object other)
+    {
+        return String.Compare(title, ((Tag)other).title);
+    }
+
+    public static IList Extract(Note note)
+    {
+        ArrayList tags = new ArrayList();
+
+        if (null != note) {
+            foreach(Match match in TaggingExpression.Matches(note.TextContent)) {
+                if (match.Success) {
+                    string[] tokens = match.Groups[1].Value.Split(',', ';');
+
+                    for(int i = 0; i < tokens.Length; ++i)
+                        tokens[i] = tokens[i].Trim();
+
+                    tags.AddRange(tokens);
+                }
+            }
+        }
+
+        return tags;
+    }
+}
+
+class TagViewDialog : Window
+{
+    private readonly TaggingModel model;
+    private Label stats;
+
+    public TagViewDialog(TaggingModel model) : base(WindowType.Toplevel)
+    {
+        this.model = model;
+
+        InitializeComponent();
+    }
+
+    public void InitializeComponent()
+    {
+        AccelGroup accelGroup;
+        CellRendererPixbuf iconRenderer;
+        CellRendererText titleRenderer;
+        TreeViewColumn titleColumn;
+        TreeView treeView;
+        ScrolledWindow scroller;
+        HButtonBox buttonBox;
+        Button closeButton;
+        VBox vbox;
+
+        accelGroup = new AccelGroup();
+        iconRenderer = new CellRendererPixbuf();
+        titleRenderer = new CellRendererText();
+        titleColumn = new TreeViewColumn();
+        treeView = new TreeView(model);
+        scroller = new ScrolledWindow();
+        closeButton = new Button(Stock.Close);
+
+        stats = new Label();
+        buttonBox = new HButtonBox();
+        vbox = new VBox(false, 8);
+
+        titleRenderer.Ellipsize = EllipsizeMode.End;
+
+        titleColumn.PackStart(iconRenderer, false);
+        titleColumn.AddAttribute(iconRenderer, "pixbuf", TaggingModel.IconColumn);
+        titleColumn.PackStart(titleRenderer, true);
+        titleColumn.AddAttribute(titleRenderer, "text", TaggingModel.TitleColumn);
+
+        treeView.HeadersVisible = false;
+        treeView.RowActivated += treeView_RowActivated;
+        treeView.AppendColumn(titleColumn);
+
+        scroller.HscrollbarPolicy = PolicyType.Never;
+        scroller.VscrollbarPolicy = PolicyType.Automatic;
+        scroller.ShadowType = ShadowType.In;
+        scroller.Add(treeView);
+
+        closeButton.AddAccelerator("activate", accelGroup, (uint)Gdk.Key.Escape, 0, AccelFlags.Visible);
+        closeButton.Clicked += closeButton_Clicked;
+
+        buttonBox.Layout = ButtonBoxStyle.Edge;
+        buttonBox.Spacing = 8;
+	buttonBox.PackStart(stats, true, true, 0);
+	buttonBox.PackStart(closeButton, false, false, 0);
+
+        vbox.BorderWidth = 6;
+        vbox.PackStart(scroller, true, true, 0);
+        vbox.PackStart(buttonBox, false, false, 0);
+        vbox.ShowAll();
+
+        DefaultSize = new Gdk.Size(500, 300);
+        Title = Catalog.GetString("Tags");
+        AddAccelGroup(accelGroup);
+        Add(vbox);
+
+        DeleteEvent += TagViewDialog_DeleteEvent;
+
+        model.RowDeleted += model_RowDeleted;
+        model.RowInserted += model_RowInserted;
+    }
+
+    private void UpdateStats()
+    {
+        int tagCount = model.TagCount;
+        int noteCount = model.NoteCount;
+
+        string tagCountText = Catalog.GetPluralString("{0} tag", "{0} tags", tagCount);
+        string noteCountText = Catalog.GetPluralString("{0} note", "{0} notes", noteCount);
+
+        stats.Text = String.Format(
+            Catalog.GetString("Total: {0}, {1}"),
+            String.Format(tagCountText, tagCount),
+            String.Format(noteCountText, noteCount));
+    }
+
+    private void TagViewDialog_DeleteEvent(object sender, DeleteEventArgs e)
+    {
+        e.RetVal = true;
+        closeButton_Clicked(null, null);
+    }
+
+    private void model_RowInserted(object sender, RowInsertedArgs e)
+    {
+        UpdateStats();
+    }
+
+    private void model_RowDeleted(object sender, RowDeletedArgs e)
+    {
+        UpdateStats();
+    }
+
+    private void closeButton_Clicked(object sender, EventArgs e)
+    {
+        Hide();
+    }
+
+    private void treeView_RowActivated(object sender, RowActivatedArgs e)
+    {
+        TreeIter iter;
+        
+        if (model.GetIter(out iter, e.Path)) {
+            Note note = (Note)model.GetValue(iter, TaggingModel.NoteColumn);
+
+            if (null == note) {
+                Tag tag = (Tag)model.GetValue(iter, TaggingModel.TagColumn);
+
+                if (null != tag && null != tag.Title)
+                    note = model.Manager.Create(tag.Title);
+            }
+
+            if (null != note)
+                note.Window.Show();
+        }
+    }
+}
+
+class TaggingModel : TreeStore
+{
+    public const int IconColumn = 0;
+    public const int TitleColumn = 1;
+    public const int NoteColumn = 2;
+    public const int TagColumn = 3;
+
+    private NoteManager manager;
+    private int noteCount = 0;
+    private int tagCount = 0;
+
+    public TaggingModel(NoteManager manager)
+        : base(typeof(Gdk.Pixbuf), typeof(string), typeof(Note), typeof(Tag))
+    {
+    }
+
+    public NoteManager Manager
+    {
+        get { return manager; }
+
+        set
+        {
+            if (value != manager) {
+                if (null != manager) {
+                    manager.NoteAdded -= manager_NoteAdded;
+                    manager.NoteDeleted -= manager_NoteDeleted;
+                    manager.NoteRenamed -= manager_NoteRenamed;
+                }
+
+                manager = value;
+
+                if (null != manager) {
+                    manager.NoteAdded += manager_NoteAdded;
+                    manager.NoteDeleted += manager_NoteDeleted;
+                    manager.NoteRenamed += manager_NoteRenamed;
+
+                    UpdateNotes(manager.Notes);
+                }
+            }
+        }
+    }
+
+    public int NoteCount
+    {
+        get { return noteCount; }
+    }
+
+    public int TagCount
+    {
+        get { return tagCount; }
+    }
+
+    public void UpdateNote(Note note)
+    {
+#if later
+        DeleteNote(note);
+
+        for(string tag in Tag.Extract(note)) {
+        }
+#else
+        Manager = note.Manager; // TODO: Be more efficient
+#endif
+    }
+
+    private void DeleteNote(Note note)
+    {
+        Manager = note.Manager; // TODO: Be more efficient
+    }
+
+    private void manager_NoteAdded(object sender, Note note)
+    {
+        UpdateNote(note);
+    }
+
+    private void manager_NoteDeleted(object sender, Note note)
+    {
+        DeleteNote(note);
+    }
+
+    private void manager_NoteRenamed(Note note, string oldTitle)
+    {
+        UpdateNote(note);
+    }
+
+    private void UpdateNotes(IList notes)
+    {
+        IDictionary unsortedTags = ExtractTags(notes);
+        IDictionary rootTags = new Hashtable();
+        IDictionary subTags = new Hashtable();
+        Tag untagged = null;
+
+        noteCount = notes.Count;
+        tagCount = unsortedTags.Count;
+
+        foreach(DictionaryEntry e in unsortedTags) {
+            string title = (string)e.Key;
+
+            if (title.Length > 0) {
+                Tag tag = new Tag(title, manager.Find(title));
+                tag.Notes = (IList)e.Value;
+
+                if (0 == tag.ParentTags.Count) {
+                    rootTags[tag.Title] = tag;
+                } else {
+                    subTags[tag.Title] = tag;
+                }
+            } else {
+                untagged = new Tag(null, null);
+                untagged.Notes = (IList)e.Value;
+            }
+        }
+        
+        foreach(Tag tag in subTags.Values) {
+            IList parentTags = tag.ParentTags;
+
+            if (null != parentTags) {
+                foreach(string title in parentTags) {
+                    Tag parent = (Tag)rootTags[title];
+
+                    if (null == parent)
+                        parent = (Tag)subTags[title];
+
+                    parent.SubTags.Add(tag);
+                }
+            }
+        }
+
+        ArrayList sortedTags = new ArrayList(rootTags.Values);
+        sortedTags.Sort();
+
+        Clear();
+        TreeIter root = TreeIter.Zero;
+
+        foreach(Tag tag in sortedTags)
+            MergeTag(root, tag, unsortedTags);
+
+        if (null != untagged) {
+            MergeTag(root, untagged, null);
+            tagCount -= 1;
+        }
+    }
+
+    private void MergeTag(TreeIter root, Tag tag, IDictionary tags)
+    {
+        if (null != tag.Notes && tag.Notes.Count > 0) {
+            Gdk.Pixbuf noteIcon = GuiUtils.GetIcon("tomboy-note", 22);
+            Gdk.Pixbuf tagIcon = GuiUtils.GetIcon(Stock.About, 22);
+
+            string title = tag.Title;
+
+            if (null == title)
+                title = Catalog.GetString("Untagged");
+
+            object[] values = {
+                null == tag.Description ? tagIcon : noteIcon, 
+                title, tag.Description, tag
+            };
+
+            if (root.Equals(TreeIter.Zero)) {
+                root = AppendValues(values);
+            } else {
+                root = AppendValues(root, values);
+            }
+
+            foreach(Tag subtag in tag.SubTags)
+                MergeTag(root, subtag, tags);
+
+            foreach(Note note in tag.Notes) {
+                if (null == tags || !tags.Contains(note.Title))
+                    AppendValues(root, noteIcon, note.Title, note);
+            }
+        }
+    }
+
+    #region Extracting Tags from Notes
+
+    private static IDictionary ExtractTags(IList notes)
+    {
+        Hashtable taggedNotes = new Hashtable();
+
+        foreach(Note note in notes) {
+            IList tags = Tag.Extract(note);
+
+            if (tags.Count > 0) {
+                foreach(string tag in tags)
+                    PutNote(taggedNotes, tag, note);
+            } else {
+                PutNote(taggedNotes, "", note);
+            }
+        }
+
+        return taggedNotes;
+    }
+
+    private static void PutNote(Hashtable tags, string tag, Note note)
+    {
+        IList notes = (IList)tags[tag];
+
+        if (null == notes)
+            tags[tag] = notes = new ArrayList();
+
+        notes.Add(note);
+    }
+
+    #endregion
+}
+
+[
+    PluginInfo(
+        Name = "Tagging Support",
+        Description = "Organize your notes using tags.",
+        WebSite = "http://taschenorakel.de/svn/repos/tomboy-tagging/",
+        Author = "Mathias Hasselmann <mathias.hasselmann@gmx.de>")
+]
+
+public class TaggingPlugin : NotePlugin 
+{
+    private TagViewDialog view = null;
+    private TaggingModel model = null;
+
+
+    protected override void Initialize()
+    {
+        model = new TaggingModel(Manager);
+
+        ActionManager am = Tomboy.Tomboy.ActionManager;
+
+        am.MainWindowActions.Add (new Action (
+            "ViewTagsAction", Catalog.GetString ("View _Tags"), 
+            null, Stock.Index));
+
+        am.UI.AddUiFromResource ("Tagging-UI.xml");
+
+        am["ViewTagsAction"].Activated += viewTags_Activated;
+    }
+
+    protected override void Shutdown()
+    {
+        try {
+            if (null != model)
+                model.Dispose();
+        } finally {
+            model = null; 
+        }
+
+        try {
+            if (null != view)
+                view.Destroy(); 
+        } finally {
+            view = null; 
+        }
+
+        Buffer.InsertText -= buffer_InsertText;
+        Buffer.DeleteRange -= buffer_DeleteRange;
+    }
+
+    protected override void OnNoteOpened()
+    {
+        Buffer.InsertText += buffer_InsertText;
+        Buffer.DeleteRange += buffer_DeleteRange;
+    }
+
+    private void buffer_InsertText (object sender, InsertTextArgs args)
+    {
+        if (null != model && null != Note)
+            model.UpdateNote(Note);
+    }
+
+    private void buffer_DeleteRange (object sender, Gtk.DeleteRangeArgs args)
+    {
+        if (null != model && null != Note)
+            model.UpdateNote(Note);
+    }
+
+    private void viewTags_Activated(object sender, EventArgs args)
+    {
+        if (null == view)
+            view = new TagViewDialog(model);
+
+        model.Manager = Manager;
+        view.Show();
+    }
+}
+
+[assembly: System.Reflection.AssemblyVersion("1.0.*")]
+[assembly: System.Reflection.AssemblyInformationalVersion("0.1")]
+
+// vim: sw=4 sta et
Index: Tomboy/Plugins/Tagging-UI.xml
===================================================================
--- Tomboy/Plugins/Tagging-UI.xml	
+++ Tomboy/Plugins/Tagging-UI.xml	
@@ -0,0 +1,7 @@
+<ui>
+ <popup name="TrayIconMenu" action="TrayIconMenuAction">
+  <placeholder name="BeforeSearchAllNotes">
+   <menuitem name="TrayViewTags" action="ViewTagsAction" />
+  </placeholder>
+ </popup>
+</ui>
Index: Tomboy/Plugins/Underline.cs
===================================================================
--- Tomboy/Plugins/Underline.cs	
+++ Tomboy/Plugins/Underline.cs	
@@ -0,0 +1,97 @@
+// Add a 'underline' item to the font styles menu.
+// (C) 2006 Ryan Lortie <desrt@desrt.ca>, LGPL 2.1 or later.
+// (C) 2006 Mathias Hasselmann <mathias.hasselmann@gmx.de>
+// vim:set sw=8 noet:
+
+using Mono.Unix;
+using System;
+using Gtk;
+
+using Tomboy;
+
+class UnderlineMenuItem : CheckMenuItem
+{
+	NotePlugin plugin;
+	bool event_freeze;
+
+	public UnderlineMenuItem (NotePlugin plugin) 
+		: base ("<u>" +
+			Catalog.GetString ("_Underline") +
+			"</u>")
+	{
+		((Label) Child).UseUnderline = true;
+		((Label) Child).UseMarkup = true;
+
+		this.plugin = plugin;
+		this.plugin.Window.TextMenu.Shown += MenuShown;
+
+		ShowAll();
+	}
+
+	protected void MenuShown (object sender, EventArgs e)
+	{
+		event_freeze = true;
+		Active = plugin.Buffer.IsActiveTag ("underline");
+		event_freeze = false;
+	}
+
+	protected override void OnActivated ()
+	{
+		if (!event_freeze)
+			plugin.Buffer.ToggleActiveTag ("underline");
+
+		base.OnActivated();
+	}
+
+	protected override void OnDestroyed ()
+	{
+		if (plugin.HasWindow)
+			plugin.Window.TextMenu.Shown -= MenuShown;
+
+		plugin = null;
+
+		base.OnDestroyed();
+	}
+}
+
+[
+	PluginInfo(
+		Name = "Underlined Text",
+		Description = "Support for underlining text.",
+		WebSite = "http://taschenorakel.de/svn/repos/tomboy-tagging/",
+    		Author = "Mathias Hasselmann <mathias.hasselmann@gmx.de>, " +
+			 "Ryan Lortie <desrt@desrt.ca>")
+]
+
+public class UnderlinePlugin : NotePlugin
+{
+	NoteTag tag;
+
+	protected override void Initialize ()
+	{
+		// If a tag of this name already exists, don't install.
+		if (Note.TagTable.Lookup ("underline") == null) {
+			tag = new NoteTag ("underline");
+			tag.Underline = Pango.Underline.Single;
+			Note.TagTable.Add (tag);
+		}
+	}
+
+	protected override void Shutdown ()
+	{
+		// Remove the tag only if we installed it.
+		if (tag != null)
+			Note.TagTable.Remove (tag);
+	}
+
+	protected override void OnNoteOpened () 
+	{
+		// Add here instead of in Initialize to avoid creating unopened
+		// notes' windows/buffers.
+		AddTextMenuItem (new UnderlineMenuItem (this));
+	}
+}
+
+[assembly: System.Reflection.AssemblyVersion("1.0.*")]
+[assembly: System.Reflection.AssemblyInformationalVersion("0.1")]
+
Index: Tomboy/Plugins/WikiMarkup.cs
===================================================================
--- Tomboy/Plugins/WikiMarkup.cs	
+++ Tomboy/Plugins/WikiMarkup.cs	
@@ -0,0 +1,138 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+
+using GLib;
+using Gtk;
+using Tomboy;
+
+[
+    PluginInfo(
+        Name = "Instant Wiki Markup",
+        Description = "Applies text styles for Wiki markup detected when typing.",
+        WebSite = "http://taschenorakel.de/svn/repos/tomboy-tagging/",
+        Author = "Mathias Hasselmann <mathias.hasselmann@gmx.de>"),
+
+     SuggestedPlugins("FixedWidthPlugin", "UnderlinePlugin")
+]
+
+public class WikiMarkupPlugin : NotePlugin 
+{
+    private readonly Stack<TextMark> markupMarks;
+    private readonly Dictionary<string, TextTag> markupTags;
+
+    private Regex markupExpression;
+    private char[] markupChars;
+
+    public WikiMarkupPlugin()
+    {
+        markupMarks = new Stack<TextMark>();
+        markupTags = new Dictionary<string, TextTag>();
+    }
+
+    protected override void Initialize()
+    {
+        if (null == markupExpression) {
+            markupTags["_"] = Note.TagTable.Lookup("underline");
+            markupTags["#"] = Note.TagTable.Lookup("highlight");
+            markupTags["*"] = Note.TagTable.Lookup("bold");
+            markupTags["/"] = Note.TagTable.Lookup("italic");
+            markupTags["|"] = Note.TagTable.Lookup("monospace");
+            markupTags["'"] = Note.TagTable.Lookup("size:small");
+            markupTags["=="] = Note.TagTable.Lookup("size:large");
+            markupTags["==="] = Note.TagTable.Lookup("size:huge");
+
+            StringBuilder expression = new StringBuilder();
+            Dictionary<char, bool> charTable = new Dictionary<char, bool>();
+
+            foreach(string marker in markupTags.Keys) {
+                if (expression.Length > 0)
+                    expression.Append("|");
+
+                expression.Append(Regex.Escape(marker));
+                charTable[marker[0]] = true;
+            }
+
+            markupChars = new List<char>(charTable.Keys).ToArray();
+            markupExpression = new Regex(@"(?:^|\s)(" + expression + @")(.+?)\1");
+        }
+    }
+
+    protected override void Shutdown()
+    {
+        Buffer.InsertText -= buffer_InsertText;
+    }
+
+    protected override void OnNoteOpened()
+    {
+        Buffer.InsertText += buffer_InsertText;
+    }
+
+    private void buffer_InsertText (object sender, InsertTextArgs args)
+    {
+        if (args.Text.IndexOfAny(markupChars) >= 0) {
+            TextIter start = args.Pos;
+            start.BackwardChars(start.LineOffset);
+
+            TextIter end = start;
+            end.ForwardChars(end.CharsInLine);
+
+            ApplyMarkup(start, end);
+        }
+    }
+
+    private void ApplyMarkup(TextIter start, TextIter end)
+    {
+        string slice = start.GetSlice(end);
+
+        foreach(Match match in markupExpression.Matches(slice)) {
+            Group marker = match.Groups[1];
+            Group content = match.Groups[2];
+            NoteTag tag = null;
+
+            if (content.Value[0] != marker.Value[0])
+                tag = (NoteTag)markupTags[marker.Value];
+
+            if (null != tag) {
+                TextIter first = start;
+                first.ForwardChars(marker.Index);
+
+                TextIter last = first;
+                last.ForwardChars(marker.Length);
+
+                markupMarks.Push(Buffer.CreateMark(null, first, true));
+                markupMarks.Push(Buffer.CreateMark(null, last, false));
+
+                first.ForwardChars(marker.Length);
+                last.ForwardChars(content.Length);
+                Buffer.ApplyTag(tag, first, last);
+
+                first.ForwardChars(content.Length);
+                last.ForwardChars(marker.Length);
+
+                markupMarks.Push(Buffer.CreateMark(null, first, true));
+                markupMarks.Push(Buffer.CreateMark(null, last, false));
+            }
+        }
+
+        if (markupMarks.Count > 0)
+            Idle.Add(RemoveMarkerTags);
+    }
+
+    private bool RemoveMarkerTags()
+    {
+        while(markupMarks.Count > 0) {
+            TextIter last = Buffer.GetIterAtMark((TextMark)markupMarks.Pop());
+            TextIter first = Buffer.GetIterAtMark((TextMark)markupMarks.Pop());
+            Buffer.Delete(ref first, ref last);
+        }
+
+        return false;
+    }
+}
+
+[assembly: System.Reflection.AssemblyVersion("1.0.*")]
+[assembly: System.Reflection.AssemblyInformationalVersion("0.1")]
+
+// vim: sw=4 sta et
