OSDN Git Service

Commit DialogBox compile Okay
[tortoisegit/TortoiseGitJp.git] / ext / scintilla / src / Document.cxx
diff --git a/ext/scintilla/src/Document.cxx b/ext/scintilla/src/Document.cxx
new file mode 100644 (file)
index 0000000..c86f2f3
--- /dev/null
@@ -0,0 +1,1664 @@
+// Scintilla source code edit control\r
+/** @file Document.cxx\r
+ ** Text document that handles notifications, DBCS, styling, words and end of line.\r
+ **/\r
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\r
+// The License.txt file describes the conditions under which this software may be distributed.\r
+\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <stdio.h>\r
+#include <ctype.h>\r
+\r
+#include "Platform.h"\r
+\r
+#include "Scintilla.h"\r
+#include "SplitVector.h"\r
+#include "Partitioning.h"\r
+#include "RunStyles.h"\r
+#include "CellBuffer.h"\r
+#include "CharClassify.h"\r
+#include "Decoration.h"\r
+#include "Document.h"\r
+#include "RESearch.h"\r
+\r
+#ifdef SCI_NAMESPACE\r
+using namespace Scintilla;\r
+#endif\r
+\r
+// This is ASCII specific but is safe with chars >= 0x80\r
+static inline bool isspacechar(unsigned char ch) {\r
+       return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));\r
+}\r
+\r
+static inline bool IsPunctuation(char ch) {\r
+       return isascii(ch) && ispunct(ch);\r
+}\r
+\r
+static inline bool IsADigit(char ch) {\r
+       return isascii(ch) && isdigit(ch);\r
+}\r
+\r
+static inline bool IsLowerCase(char ch) {\r
+       return isascii(ch) && islower(ch);\r
+}\r
+\r
+static inline bool IsUpperCase(char ch) {\r
+       return isascii(ch) && isupper(ch);\r
+}\r
+\r
+Document::Document() {\r
+       refCount = 0;\r
+#ifdef unix\r
+       eolMode = SC_EOL_LF;\r
+#else\r
+       eolMode = SC_EOL_CRLF;\r
+#endif\r
+       dbcsCodePage = 0;\r
+       stylingBits = 5;\r
+       stylingBitsMask = 0x1F;\r
+       stylingMask = 0;\r
+       endStyled = 0;\r
+       styleClock = 0;\r
+       enteredModification = 0;\r
+       enteredStyling = 0;\r
+       enteredReadOnlyCount = 0;\r
+       tabInChars = 8;\r
+       indentInChars = 0;\r
+       actualIndentInChars = 8;\r
+       useTabs = true;\r
+       tabIndents = true;\r
+       backspaceUnindents = false;\r
+       watchers = 0;\r
+       lenWatchers = 0;\r
+\r
+       matchesValid = false;\r
+       regex = 0;\r
+}\r
+\r
+Document::~Document() {\r
+       for (int i = 0; i < lenWatchers; i++) {\r
+               watchers[i].watcher->NotifyDeleted(this, watchers[i].userData);\r
+       }\r
+       delete []watchers;\r
+       watchers = 0;\r
+       lenWatchers = 0;\r
+       delete regex;\r
+       regex = 0;\r
+}\r
+\r
+// Increase reference count and return its previous value.\r
+int Document::AddRef() {\r
+       return refCount++;\r
+}\r
+\r
+// Decrease reference count and return its previous value.\r
+// Delete the document if reference count reaches zero.\r
+int Document::Release() {\r
+       int curRefCount = --refCount;\r
+       if (curRefCount == 0)\r
+               delete this;\r
+       return curRefCount;\r
+}\r
+\r
+void Document::SetSavePoint() {\r
+       cb.SetSavePoint();\r
+       NotifySavePoint(true);\r
+}\r
+\r
+int Document::AddMark(int line, int markerNum) {\r
+       int prev = cb.AddMark(line, markerNum);\r
+       DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line);\r
+       NotifyModified(mh);\r
+       return prev;\r
+}\r
+\r
+void Document::AddMarkSet(int line, int valueSet) {\r
+       unsigned int m = valueSet;\r
+       for (int i = 0; m; i++, m >>= 1)\r
+               if (m & 1)\r
+                       cb.AddMark(line, i);\r
+       DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line);\r
+       NotifyModified(mh);\r
+}\r
+\r
+void Document::DeleteMark(int line, int markerNum) {\r
+       cb.DeleteMark(line, markerNum);\r
+       DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line);\r
+       NotifyModified(mh);\r
+}\r
+\r
+void Document::DeleteMarkFromHandle(int markerHandle) {\r
+       cb.DeleteMarkFromHandle(markerHandle);\r
+       DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0);\r
+       mh.line = -1;\r
+       NotifyModified(mh);\r
+}\r
+\r
+void Document::DeleteAllMarks(int markerNum) {\r
+       cb.DeleteAllMarks(markerNum);\r
+       DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0);\r
+       mh.line = -1;\r
+       NotifyModified(mh);\r
+}\r
+\r
+int Document::LineStart(int line) const {\r
+       return cb.LineStart(line);\r
+}\r
+\r
+int Document::LineEnd(int line) const {\r
+       if (line == LinesTotal() - 1) {\r
+               return LineStart(line + 1);\r
+       } else {\r
+               int position = LineStart(line + 1) - 1;\r
+               // When line terminator is CR+LF, may need to go back one more\r
+               if ((position > LineStart(line)) && (cb.CharAt(position - 1) == '\r')) {\r
+                       position--;\r
+               }\r
+               return position;\r
+       }\r
+}\r
+\r
+int Document::LineFromPosition(int pos) {\r
+       return cb.LineFromPosition(pos);\r
+}\r
+\r
+int Document::LineEndPosition(int position) {\r
+       return LineEnd(LineFromPosition(position));\r
+}\r
+\r
+int Document::VCHomePosition(int position) {\r
+       int line = LineFromPosition(position);\r
+       int startPosition = LineStart(line);\r
+       int endLine = LineEnd(line);\r
+       int startText = startPosition;\r
+       while (startText < endLine && (cb.CharAt(startText) == ' ' || cb.CharAt(startText) == '\t' ) )\r
+               startText++;\r
+       if (position == startText)\r
+               return startPosition;\r
+       else\r
+               return startText;\r
+}\r
+\r
+int Document::SetLevel(int line, int level) {\r
+       int prev = cb.SetLevel(line, level);\r
+       if (prev != level) {\r
+               DocModification mh(SC_MOD_CHANGEFOLD | SC_MOD_CHANGEMARKER,\r
+                                  LineStart(line), 0, 0, 0, line);\r
+               mh.foldLevelNow = level;\r
+               mh.foldLevelPrev = prev;\r
+               NotifyModified(mh);\r
+       }\r
+       return prev;\r
+}\r
+\r
+static bool IsSubordinate(int levelStart, int levelTry) {\r
+       if (levelTry & SC_FOLDLEVELWHITEFLAG)\r
+               return true;\r
+       else\r
+               return (levelStart & SC_FOLDLEVELNUMBERMASK) < (levelTry & SC_FOLDLEVELNUMBERMASK);\r
+}\r
+\r
+int Document::GetLastChild(int lineParent, int level) {\r
+       if (level == -1)\r
+               level = GetLevel(lineParent) & SC_FOLDLEVELNUMBERMASK;\r
+       int maxLine = LinesTotal();\r
+       int lineMaxSubord = lineParent;\r
+       while (lineMaxSubord < maxLine - 1) {\r
+               EnsureStyledTo(LineStart(lineMaxSubord + 2));\r
+               if (!IsSubordinate(level, GetLevel(lineMaxSubord + 1)))\r
+                       break;\r
+               lineMaxSubord++;\r
+       }\r
+       if (lineMaxSubord > lineParent) {\r
+               if (level > (GetLevel(lineMaxSubord + 1) & SC_FOLDLEVELNUMBERMASK)) {\r
+                       // Have chewed up some whitespace that belongs to a parent so seek back\r
+                       if (GetLevel(lineMaxSubord) & SC_FOLDLEVELWHITEFLAG) {\r
+                               lineMaxSubord--;\r
+                       }\r
+               }\r
+       }\r
+       return lineMaxSubord;\r
+}\r
+\r
+int Document::GetFoldParent(int line) {\r
+       int level = GetLevel(line) & SC_FOLDLEVELNUMBERMASK;\r
+       int lineLook = line - 1;\r
+       while ((lineLook > 0) && (\r
+                   (!(GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG)) ||\r
+                   ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) >= level))\r
+             ) {\r
+               lineLook--;\r
+       }\r
+       if ((GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG) &&\r
+               ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) < level)) {\r
+               return lineLook;\r
+       } else {\r
+               return -1;\r
+       }\r
+}\r
+\r
+int Document::ClampPositionIntoDocument(int pos) {\r
+       return Platform::Clamp(pos, 0, Length());\r
+}\r
+\r
+bool Document::IsCrLf(int pos) {\r
+       if (pos < 0)\r
+               return false;\r
+       if (pos >= (Length() - 1))\r
+               return false;\r
+       return (cb.CharAt(pos) == '\r') && (cb.CharAt(pos + 1) == '\n');\r
+}\r
+\r
+static const int maxBytesInDBCSCharacter=5;\r
+\r
+int Document::LenChar(int pos) {\r
+       if (pos < 0) {\r
+               return 1;\r
+       } else if (IsCrLf(pos)) {\r
+               return 2;\r
+       } else if (SC_CP_UTF8 == dbcsCodePage) {\r
+               unsigned char ch = static_cast<unsigned char>(cb.CharAt(pos));\r
+               if (ch < 0x80)\r
+                       return 1;\r
+               int len = 2;\r
+               if (ch >= (0x80 + 0x40 + 0x20 + 0x10))\r
+                       len = 4;\r
+               else if (ch >= (0x80 + 0x40 + 0x20))\r
+                       len = 3;\r
+               int lengthDoc = Length();\r
+               if ((pos + len) > lengthDoc)\r
+                       return lengthDoc -pos;\r
+               else\r
+                       return len;\r
+       } else if (dbcsCodePage) {\r
+               char mbstr[maxBytesInDBCSCharacter+1];\r
+               int i;\r
+               for (i=0; i<Platform::DBCSCharMaxLength(); i++) {\r
+                       mbstr[i] = cb.CharAt(pos+i);\r
+               }\r
+               mbstr[i] = '\0';\r
+               return Platform::DBCSCharLength(dbcsCodePage, mbstr);\r
+       } else {\r
+               return 1;\r
+       }\r
+}\r
+\r
+static bool IsTrailByte(int ch) {\r
+       return (ch >= 0x80) && (ch < (0x80 + 0x40));\r
+}\r
+\r
+static int BytesFromLead(int leadByte) {\r
+       if (leadByte > 0xF4) {\r
+               // Characters longer than 4 bytes not possible in current UTF-8\r
+               return 0;\r
+       } else if (leadByte >= 0xF0) {\r
+               return 4;\r
+       } else if (leadByte >= 0xE0) {\r
+               return 3;\r
+       } else if (leadByte >= 0xC2) {\r
+               return 2;\r
+       }\r
+       return 0;\r
+}\r
+\r
+bool Document::InGoodUTF8(int pos, int &start, int &end) {\r
+       int lead = pos;\r
+       while ((lead>0) && (pos-lead < 4) && IsTrailByte(static_cast<unsigned char>(cb.CharAt(lead-1))))\r
+               lead--;\r
+       start = 0;\r
+       if (lead > 0) {\r
+               start = lead-1;\r
+       }\r
+       int leadByte = static_cast<unsigned char>(cb.CharAt(start));\r
+       int bytes = BytesFromLead(leadByte);\r
+       if (bytes == 0) {\r
+               return false;\r
+       } else {\r
+               int trailBytes = bytes - 1;\r
+               int len = pos - lead + 1;\r
+               if (len > trailBytes)\r
+                       // pos too far from lead\r
+                       return false;\r
+               // Check that there are enough trails for this lead\r
+               int trail = pos + 1;\r
+               while ((trail-lead<trailBytes) && (trail < Length())) {\r
+                       if (!IsTrailByte(static_cast<unsigned char>(cb.CharAt(trail)))) {\r
+                               return false;\r
+                       }\r
+                       trail++;\r
+               }\r
+               end = start + bytes;\r
+               return true;\r
+       }\r
+}\r
+\r
+// Normalise a position so that it is not halfway through a two byte character.\r
+// This can occur in two situations -\r
+// When lines are terminated with \r\n pairs which should be treated as one character.\r
+// When displaying DBCS text such as Japanese.\r
+// If moving, move the position in the indicated direction.\r
+int Document::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) {\r
+       //Platform::DebugPrintf("NoCRLF %d %d\n", pos, moveDir);\r
+       // If out of range, just return minimum/maximum value.\r
+       if (pos <= 0)\r
+               return 0;\r
+       if (pos >= Length())\r
+               return Length();\r
+\r
+       // PLATFORM_ASSERT(pos > 0 && pos < Length());\r
+       if (checkLineEnd && IsCrLf(pos - 1)) {\r
+               if (moveDir > 0)\r
+                       return pos + 1;\r
+               else\r
+                       return pos - 1;\r
+       }\r
+\r
+       // Not between CR and LF\r
+\r
+       if (dbcsCodePage) {\r
+               if (SC_CP_UTF8 == dbcsCodePage) {\r
+                       unsigned char ch = static_cast<unsigned char>(cb.CharAt(pos));\r
+                       int startUTF = pos;\r
+                       int endUTF = pos;\r
+                       if (IsTrailByte(ch) && InGoodUTF8(pos, startUTF, endUTF)) {\r
+                               // ch is a trail byte within a UTF-8 character\r
+                               if (moveDir > 0)\r
+                                       pos = endUTF;\r
+                               else\r
+                                       pos = startUTF;\r
+                       }\r
+               } else {\r
+                       // Anchor DBCS calculations at start of line because start of line can\r
+                       // not be a DBCS trail byte.\r
+                       int posCheck = LineStart(LineFromPosition(pos));\r
+                       while (posCheck < pos) {\r
+                               char mbstr[maxBytesInDBCSCharacter+1];\r
+                               int i;\r
+                               for(i=0;i<Platform::DBCSCharMaxLength();i++) {\r
+                                       mbstr[i] = cb.CharAt(posCheck+i);\r
+                               }\r
+                               mbstr[i] = '\0';\r
+\r
+                               int mbsize = Platform::DBCSCharLength(dbcsCodePage, mbstr);\r
+                               if (posCheck + mbsize == pos) {\r
+                                       return pos;\r
+                               } else if (posCheck + mbsize > pos) {\r
+                                       if (moveDir > 0) {\r
+                                               return posCheck + mbsize;\r
+                                       } else {\r
+                                               return posCheck;\r
+                                       }\r
+                               }\r
+                               posCheck += mbsize;\r
+                       }\r
+               }\r
+       }\r
+\r
+       return pos;\r
+}\r
+\r
+void Document::ModifiedAt(int pos) {\r
+       if (endStyled > pos)\r
+               endStyled = pos;\r
+}\r
+\r
+void Document::CheckReadOnly() {\r
+       if (cb.IsReadOnly() && enteredReadOnlyCount == 0) {\r
+               enteredReadOnlyCount++;\r
+               NotifyModifyAttempt();\r
+               enteredReadOnlyCount--;\r
+       }\r
+}\r
+\r
+// Document only modified by gateways DeleteChars, InsertString, Undo, Redo, and SetStyleAt.\r
+// SetStyleAt does not change the persistent state of a document\r
+\r
+bool Document::DeleteChars(int pos, int len) {\r
+       if (len == 0)\r
+               return false;\r
+       if ((pos + len) > Length())\r
+               return false;\r
+       CheckReadOnly();\r
+       if (enteredModification != 0) {\r
+               return false;\r
+       } else {\r
+               enteredModification++;\r
+               if (!cb.IsReadOnly()) {\r
+                       NotifyModified(\r
+                           DocModification(\r
+                               SC_MOD_BEFOREDELETE | SC_PERFORMED_USER,\r
+                               pos, len,\r
+                               0, 0));\r
+                       int prevLinesTotal = LinesTotal();\r
+                       bool startSavePoint = cb.IsSavePoint();\r
+                       bool startSequence = false;\r
+                       const char *text = cb.DeleteChars(pos, len, startSequence);\r
+                       if (startSavePoint && cb.IsCollectingUndo())\r
+                               NotifySavePoint(!startSavePoint);\r
+                       if ((pos < Length()) || (pos == 0))\r
+                               ModifiedAt(pos);\r
+                       else\r
+                               ModifiedAt(pos-1);\r
+                       NotifyModified(\r
+                           DocModification(\r
+                               SC_MOD_DELETETEXT | SC_PERFORMED_USER | (startSequence?SC_STARTACTION:0),\r
+                               pos, len,\r
+                               LinesTotal() - prevLinesTotal, text));\r
+               }\r
+               enteredModification--;\r
+       }\r
+       return !cb.IsReadOnly();\r
+}\r
+\r
+/**\r
+ * Insert a string with a length.\r
+ */\r
+bool Document::InsertString(int position, const char *s, int insertLength) {\r
+       if (insertLength <= 0) {\r
+               return false;\r
+       }\r
+       CheckReadOnly();\r
+       if (enteredModification != 0) {\r
+               return false;\r
+       } else {\r
+               enteredModification++;\r
+               if (!cb.IsReadOnly()) {\r
+                       NotifyModified(\r
+                           DocModification(\r
+                               SC_MOD_BEFOREINSERT | SC_PERFORMED_USER,\r
+                               position, insertLength,\r
+                               0, s));\r
+                       int prevLinesTotal = LinesTotal();\r
+                       bool startSavePoint = cb.IsSavePoint();\r
+                       bool startSequence = false;\r
+                       const char *text = cb.InsertString(position, s, insertLength, startSequence);\r
+                       if (startSavePoint && cb.IsCollectingUndo())\r
+                               NotifySavePoint(!startSavePoint);\r
+                       ModifiedAt(position);\r
+                       NotifyModified(\r
+                           DocModification(\r
+                               SC_MOD_INSERTTEXT | SC_PERFORMED_USER | (startSequence?SC_STARTACTION:0),\r
+                               position, insertLength,\r
+                               LinesTotal() - prevLinesTotal, text));\r
+               }\r
+               enteredModification--;\r
+       }\r
+       return !cb.IsReadOnly();\r
+}\r
+\r
+int Document::Undo() {\r
+       int newPos = -1;\r
+       CheckReadOnly();\r
+       if (enteredModification == 0) {\r
+               enteredModification++;\r
+               if (!cb.IsReadOnly()) {\r
+                       bool startSavePoint = cb.IsSavePoint();\r
+                       bool multiLine = false;\r
+                       int steps = cb.StartUndo();\r
+                       //Platform::DebugPrintf("Steps=%d\n", steps);\r
+                       for (int step = 0; step < steps; step++) {\r
+                               const int prevLinesTotal = LinesTotal();\r
+                               const Action &action = cb.GetUndoStep();\r
+                               if (action.at == removeAction) {\r
+                                       NotifyModified(DocModification(\r
+                                                                       SC_MOD_BEFOREINSERT | SC_PERFORMED_UNDO, action));\r
+                               } else {\r
+                                       NotifyModified(DocModification(\r
+                                                                       SC_MOD_BEFOREDELETE | SC_PERFORMED_UNDO, action));\r
+                               }\r
+                               cb.PerformUndoStep();\r
+                               int cellPosition = action.position;\r
+                               ModifiedAt(cellPosition);\r
+                               newPos = cellPosition;\r
+\r
+                               int modFlags = SC_PERFORMED_UNDO;\r
+                               // With undo, an insertion action becomes a deletion notification\r
+                               if (action.at == removeAction) {\r
+                                       newPos += action.lenData;\r
+                                       modFlags |= SC_MOD_INSERTTEXT;\r
+                               } else {\r
+                                       modFlags |= SC_MOD_DELETETEXT;\r
+                               }\r
+                               if (steps > 1)\r
+                                       modFlags |= SC_MULTISTEPUNDOREDO;\r
+                               const int linesAdded = LinesTotal() - prevLinesTotal;\r
+                               if (linesAdded != 0)\r
+                                       multiLine = true;\r
+                               if (step == steps - 1) {\r
+                                       modFlags |= SC_LASTSTEPINUNDOREDO;\r
+                                       if (multiLine)\r
+                                               modFlags |= SC_MULTILINEUNDOREDO;\r
+                               }\r
+                               NotifyModified(DocModification(modFlags, cellPosition, action.lenData,\r
+                                                                                          linesAdded, action.data));\r
+                       }\r
+\r
+                       bool endSavePoint = cb.IsSavePoint();\r
+                       if (startSavePoint != endSavePoint)\r
+                               NotifySavePoint(endSavePoint);\r
+               }\r
+               enteredModification--;\r
+       }\r
+       return newPos;\r
+}\r
+\r
+int Document::Redo() {\r
+       int newPos = -1;\r
+       CheckReadOnly();\r
+       if (enteredModification == 0) {\r
+               enteredModification++;\r
+               if (!cb.IsReadOnly()) {\r
+                       bool startSavePoint = cb.IsSavePoint();\r
+                       bool multiLine = false;\r
+                       int steps = cb.StartRedo();\r
+                       for (int step = 0; step < steps; step++) {\r
+                               const int prevLinesTotal = LinesTotal();\r
+                               const Action &action = cb.GetRedoStep();\r
+                               if (action.at == insertAction) {\r
+                                       NotifyModified(DocModification(\r
+                                                                       SC_MOD_BEFOREINSERT | SC_PERFORMED_REDO, action));\r
+                               } else {\r
+                                       NotifyModified(DocModification(\r
+                                                                       SC_MOD_BEFOREDELETE | SC_PERFORMED_REDO, action));\r
+                               }\r
+                               cb.PerformRedoStep();\r
+                               ModifiedAt(action.position);\r
+                               newPos = action.position;\r
+\r
+                               int modFlags = SC_PERFORMED_REDO;\r
+                               if (action.at == insertAction) {\r
+                                       newPos += action.lenData;\r
+                                       modFlags |= SC_MOD_INSERTTEXT;\r
+                               } else {\r
+                                       modFlags |= SC_MOD_DELETETEXT;\r
+                               }\r
+                               if (steps > 1)\r
+                                       modFlags |= SC_MULTISTEPUNDOREDO;\r
+                               const int linesAdded = LinesTotal() - prevLinesTotal;\r
+                               if (linesAdded != 0)\r
+                                       multiLine = true;\r
+                               if (step == steps - 1) {\r
+                                       modFlags |= SC_LASTSTEPINUNDOREDO;\r
+                                       if (multiLine)\r
+                                               modFlags |= SC_MULTILINEUNDOREDO;\r
+                               }\r
+                               NotifyModified(\r
+                                       DocModification(modFlags, action.position, action.lenData,\r
+                                                                       linesAdded, action.data));\r
+                       }\r
+\r
+                       bool endSavePoint = cb.IsSavePoint();\r
+                       if (startSavePoint != endSavePoint)\r
+                               NotifySavePoint(endSavePoint);\r
+               }\r
+               enteredModification--;\r
+       }\r
+       return newPos;\r
+}\r
+\r
+/**\r
+ * Insert a single character.\r
+ */\r
+bool Document::InsertChar(int pos, char ch) {\r
+       char chs[1];\r
+       chs[0] = ch;\r
+       return InsertString(pos, chs, 1);\r
+}\r
+\r
+/**\r
+ * Insert a null terminated string.\r
+ */\r
+bool Document::InsertCString(int position, const char *s) {\r
+       return InsertString(position, s, strlen(s));\r
+}\r
+\r
+void Document::ChangeChar(int pos, char ch) {\r
+       DeleteChars(pos, 1);\r
+       InsertChar(pos, ch);\r
+}\r
+\r
+void Document::DelChar(int pos) {\r
+       DeleteChars(pos, LenChar(pos));\r
+}\r
+\r
+void Document::DelCharBack(int pos) {\r
+       if (pos <= 0) {\r
+               return;\r
+       } else if (IsCrLf(pos - 2)) {\r
+               DeleteChars(pos - 2, 2);\r
+       } else if (dbcsCodePage) {\r
+               int startChar = MovePositionOutsideChar(pos - 1, -1, false);\r
+               DeleteChars(startChar, pos - startChar);\r
+       } else {\r
+               DeleteChars(pos - 1, 1);\r
+       }\r
+}\r
+\r
+static bool isindentchar(char ch) {\r
+       return (ch == ' ') || (ch == '\t');\r
+}\r
+\r
+static int NextTab(int pos, int tabSize) {\r
+       return ((pos / tabSize) + 1) * tabSize;\r
+}\r
+\r
+static void CreateIndentation(char *linebuf, int length, int indent, int tabSize, bool insertSpaces) {\r
+       length--;       // ensure space for \0\r
+       if (!insertSpaces) {\r
+               while ((indent >= tabSize) && (length > 0)) {\r
+                       *linebuf++ = '\t';\r
+                       indent -= tabSize;\r
+                       length--;\r
+               }\r
+       }\r
+       while ((indent > 0) && (length > 0)) {\r
+               *linebuf++ = ' ';\r
+               indent--;\r
+               length--;\r
+       }\r
+       *linebuf = '\0';\r
+}\r
+\r
+int Document::GetLineIndentation(int line) {\r
+       int indent = 0;\r
+       if ((line >= 0) && (line < LinesTotal())) {\r
+               int lineStart = LineStart(line);\r
+               int length = Length();\r
+               for (int i = lineStart;i < length;i++) {\r
+                       char ch = cb.CharAt(i);\r
+                       if (ch == ' ')\r
+                               indent++;\r
+                       else if (ch == '\t')\r
+                               indent = NextTab(indent, tabInChars);\r
+                       else\r
+                               return indent;\r
+               }\r
+       }\r
+       return indent;\r
+}\r
+\r
+void Document::SetLineIndentation(int line, int indent) {\r
+       int indentOfLine = GetLineIndentation(line);\r
+       if (indent < 0)\r
+               indent = 0;\r
+       if (indent != indentOfLine) {\r
+               char linebuf[1000];\r
+               CreateIndentation(linebuf, sizeof(linebuf), indent, tabInChars, !useTabs);\r
+               int thisLineStart = LineStart(line);\r
+               int indentPos = GetLineIndentPosition(line);\r
+               BeginUndoAction();\r
+               DeleteChars(thisLineStart, indentPos - thisLineStart);\r
+               InsertCString(thisLineStart, linebuf);\r
+               EndUndoAction();\r
+       }\r
+}\r
+\r
+int Document::GetLineIndentPosition(int line) const {\r
+       if (line < 0)\r
+               return 0;\r
+       int pos = LineStart(line);\r
+       int length = Length();\r
+       while ((pos < length) && isindentchar(cb.CharAt(pos))) {\r
+               pos++;\r
+       }\r
+       return pos;\r
+}\r
+\r
+int Document::GetColumn(int pos) {\r
+       int column = 0;\r
+       int line = LineFromPosition(pos);\r
+       if ((line >= 0) && (line < LinesTotal())) {\r
+               for (int i = LineStart(line);i < pos;) {\r
+                       char ch = cb.CharAt(i);\r
+                       if (ch == '\t') {\r
+                               column = NextTab(column, tabInChars);\r
+                               i++;\r
+                       } else if (ch == '\r') {\r
+                               return column;\r
+                       } else if (ch == '\n') {\r
+                               return column;\r
+                       } else if (i >= Length()) {\r
+                               return column;\r
+                       } else {\r
+                               column++;\r
+                               i = MovePositionOutsideChar(i + 1, 1, false);\r
+                       }\r
+               }\r
+       }\r
+       return column;\r
+}\r
+\r
+int Document::FindColumn(int line, int column) {\r
+       int position = LineStart(line);\r
+       int columnCurrent = 0;\r
+       if ((line >= 0) && (line < LinesTotal())) {\r
+               while ((columnCurrent < column) && (position < Length())) {\r
+                       char ch = cb.CharAt(position);\r
+                       if (ch == '\t') {\r
+                               columnCurrent = NextTab(columnCurrent, tabInChars);\r
+                               position++;\r
+                       } else if (ch == '\r') {\r
+                               return position;\r
+                       } else if (ch == '\n') {\r
+                               return position;\r
+                       } else {\r
+                               columnCurrent++;\r
+                               position = MovePositionOutsideChar(position + 1, 1, false);\r
+                       }\r
+               }\r
+       }\r
+       return position;\r
+}\r
+\r
+void Document::Indent(bool forwards, int lineBottom, int lineTop) {\r
+       // Dedent - suck white space off the front of the line to dedent by equivalent of a tab\r
+       for (int line = lineBottom; line >= lineTop; line--) {\r
+               int indentOfLine = GetLineIndentation(line);\r
+               if (forwards) {\r
+                       if (LineStart(line) < LineEnd(line)) {\r
+                               SetLineIndentation(line, indentOfLine + IndentSize());\r
+                       }\r
+               } else {\r
+                       SetLineIndentation(line, indentOfLine - IndentSize());\r
+               }\r
+       }\r
+}\r
+\r
+// Convert line endings for a piece of text to a particular mode.\r
+// Stop at len or when a NUL is found.\r
+// Caller must delete the returned pointer.\r
+char *Document::TransformLineEnds(int *pLenOut, const char *s, size_t len, int eolMode) {\r
+       char *dest = new char[2 * len + 1];\r
+       const char *sptr = s;\r
+       char *dptr = dest;\r
+       for (size_t i = 0; (i < len) && (*sptr != '\0'); i++) {\r
+               if (*sptr == '\n' || *sptr == '\r') {\r
+                       if (eolMode == SC_EOL_CR) {\r
+                               *dptr++ = '\r';\r
+                       } else if (eolMode == SC_EOL_LF) {\r
+                               *dptr++ = '\n';\r
+                       } else { // eolMode == SC_EOL_CRLF\r
+                               *dptr++ = '\r';\r
+                               *dptr++ = '\n';\r
+                       }\r
+                       if ((*sptr == '\r') && (i+1 < len) && (*(sptr+1) == '\n')) {\r
+                               i++;\r
+                               sptr++;\r
+                       }\r
+                       sptr++;\r
+               } else {\r
+                       *dptr++ = *sptr++;\r
+               }\r
+       }\r
+       *dptr++ = '\0';\r
+       *pLenOut = (dptr - dest) - 1;\r
+       return dest;\r
+}\r
+\r
+void Document::ConvertLineEnds(int eolModeSet) {\r
+       BeginUndoAction();\r
+\r
+       for (int pos = 0; pos < Length(); pos++) {\r
+               if (cb.CharAt(pos) == '\r') {\r
+                       if (cb.CharAt(pos + 1) == '\n') {\r
+                               // CRLF\r
+                               if (eolModeSet == SC_EOL_CR) {\r
+                                       DeleteChars(pos + 1, 1); // Delete the LF\r
+                               } else if (eolModeSet == SC_EOL_LF) {\r
+                                       DeleteChars(pos, 1); // Delete the CR\r
+                               } else {\r
+                                       pos++;\r
+                               }\r
+                       } else {\r
+                               // CR\r
+                               if (eolModeSet == SC_EOL_CRLF) {\r
+                                       InsertString(pos + 1, "\n", 1); // Insert LF\r
+                                       pos++;\r
+                               } else if (eolModeSet == SC_EOL_LF) {\r
+                                       InsertString(pos, "\n", 1); // Insert LF\r
+                                       DeleteChars(pos + 1, 1); // Delete CR\r
+                               }\r
+                       }\r
+               } else if (cb.CharAt(pos) == '\n') {\r
+                       // LF\r
+                       if (eolModeSet == SC_EOL_CRLF) {\r
+                               InsertString(pos, "\r", 1); // Insert CR\r
+                               pos++;\r
+                       } else if (eolModeSet == SC_EOL_CR) {\r
+                               InsertString(pos, "\r", 1); // Insert CR\r
+                               DeleteChars(pos + 1, 1); // Delete LF\r
+                       }\r
+               }\r
+       }\r
+\r
+       EndUndoAction();\r
+}\r
+\r
+bool Document::IsWhiteLine(int line) const {\r
+       int currentChar = LineStart(line);\r
+       int endLine = LineEnd(line);\r
+       while (currentChar < endLine) {\r
+               if (cb.CharAt(currentChar) != ' ' && cb.CharAt(currentChar) != '\t') {\r
+                       return false;\r
+               }\r
+               ++currentChar;\r
+       }\r
+       return true;\r
+}\r
+\r
+int Document::ParaUp(int pos) {\r
+       int line = LineFromPosition(pos);\r
+       line--;\r
+       while (line >= 0 && IsWhiteLine(line)) { // skip empty lines\r
+               line--;\r
+       }\r
+       while (line >= 0 && !IsWhiteLine(line)) { // skip non-empty lines\r
+               line--;\r
+       }\r
+       line++;\r
+       return LineStart(line);\r
+}\r
+\r
+int Document::ParaDown(int pos) {\r
+       int line = LineFromPosition(pos);\r
+       while (line < LinesTotal() && !IsWhiteLine(line)) { // skip non-empty lines\r
+               line++;\r
+       }\r
+       while (line < LinesTotal() && IsWhiteLine(line)) { // skip empty lines\r
+               line++;\r
+       }\r
+       if (line < LinesTotal())\r
+               return LineStart(line);\r
+       else // end of a document\r
+               return LineEnd(line-1);\r
+}\r
+\r
+CharClassify::cc Document::WordCharClass(unsigned char ch) {\r
+       if ((SC_CP_UTF8 == dbcsCodePage) && (ch >= 0x80))\r
+               return CharClassify::ccWord;\r
+       return charClass.GetClass(ch);\r
+}\r
+\r
+/**\r
+ * Used by commmands that want to select whole words.\r
+ * Finds the start of word at pos when delta < 0 or the end of the word when delta >= 0.\r
+ */\r
+int Document::ExtendWordSelect(int pos, int delta, bool onlyWordCharacters) {\r
+       CharClassify::cc ccStart = CharClassify::ccWord;\r
+       if (delta < 0) {\r
+               if (!onlyWordCharacters)\r
+                       ccStart = WordCharClass(cb.CharAt(pos-1));\r
+               while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart))\r
+                       pos--;\r
+       } else {\r
+               if (!onlyWordCharacters && pos < Length())\r
+                       ccStart = WordCharClass(cb.CharAt(pos));\r
+               while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart))\r
+                       pos++;\r
+       }\r
+       return MovePositionOutsideChar(pos, delta);\r
+}\r
+\r
+/**\r
+ * Find the start of the next word in either a forward (delta >= 0) or backwards direction\r
+ * (delta < 0).\r
+ * This is looking for a transition between character classes although there is also some\r
+ * additional movement to transit white space.\r
+ * Used by cursor movement by word commands.\r
+ */\r
+int Document::NextWordStart(int pos, int delta) {\r
+       if (delta < 0) {\r
+               while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == CharClassify::ccSpace))\r
+                       pos--;\r
+               if (pos > 0) {\r
+                       CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos-1));\r
+                       while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart)) {\r
+                               pos--;\r
+                       }\r
+               }\r
+       } else {\r
+               CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos));\r
+               while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart))\r
+                       pos++;\r
+               while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == CharClassify::ccSpace))\r
+                       pos++;\r
+       }\r
+       return pos;\r
+}\r
+\r
+/**\r
+ * Find the end of the next word in either a forward (delta >= 0) or backwards direction\r
+ * (delta < 0).\r
+ * This is looking for a transition between character classes although there is also some\r
+ * additional movement to transit white space.\r
+ * Used by cursor movement by word commands.\r
+ */\r
+int Document::NextWordEnd(int pos, int delta) {\r
+       if (delta < 0) {\r
+               if (pos > 0) {\r
+                       CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos-1));\r
+                       if (ccStart != CharClassify::ccSpace) {\r
+                               while (pos > 0 && WordCharClass(cb.CharAt(pos - 1)) == ccStart) {\r
+                                       pos--;\r
+                               }\r
+                       }\r
+                       while (pos > 0 && WordCharClass(cb.CharAt(pos - 1)) == CharClassify::ccSpace) {\r
+                               pos--;\r
+                       }\r
+               }\r
+       } else {\r
+               while (pos < Length() && WordCharClass(cb.CharAt(pos)) == CharClassify::ccSpace) {\r
+                       pos++;\r
+               }\r
+               if (pos < Length()) {\r
+                       CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos));\r
+                       while (pos < Length() && WordCharClass(cb.CharAt(pos)) == ccStart) {\r
+                               pos++;\r
+                       }\r
+               }\r
+       }\r
+       return pos;\r
+}\r
+\r
+/**\r
+ * Check that the character at the given position is a word or punctuation character and that\r
+ * the previous character is of a different character class.\r
+ */\r
+bool Document::IsWordStartAt(int pos) {\r
+       if (pos > 0) {\r
+               CharClassify::cc ccPos = WordCharClass(CharAt(pos));\r
+               return (ccPos == CharClassify::ccWord || ccPos == CharClassify::ccPunctuation) &&\r
+                       (ccPos != WordCharClass(CharAt(pos - 1)));\r
+       }\r
+       return true;\r
+}\r
+\r
+/**\r
+ * Check that the character at the given position is a word or punctuation character and that\r
+ * the next character is of a different character class.\r
+ */\r
+bool Document::IsWordEndAt(int pos) {\r
+       if (pos < Length()) {\r
+               CharClassify::cc ccPrev = WordCharClass(CharAt(pos-1));\r
+               return (ccPrev == CharClassify::ccWord || ccPrev == CharClassify::ccPunctuation) &&\r
+                       (ccPrev != WordCharClass(CharAt(pos)));\r
+       }\r
+       return true;\r
+}\r
+\r
+/**\r
+ * Check that the given range is has transitions between character classes at both\r
+ * ends and where the characters on the inside are word or punctuation characters.\r
+ */\r
+bool Document::IsWordAt(int start, int end) {\r
+       return IsWordStartAt(start) && IsWordEndAt(end);\r
+}\r
+\r
+// The comparison and case changing functions here assume ASCII\r
+// or extended ASCII such as the normal Windows code page.\r
+\r
+static inline char MakeUpperCase(char ch) {\r
+       if (ch < 'a' || ch > 'z')\r
+               return ch;\r
+       else\r
+               return static_cast<char>(ch - 'a' + 'A');\r
+}\r
+\r
+static inline char MakeLowerCase(char ch) {\r
+       if (ch < 'A' || ch > 'Z')\r
+               return ch;\r
+       else\r
+               return static_cast<char>(ch - 'A' + 'a');\r
+}\r
+\r
+/**\r
+ * Find text in document, supporting both forward and backward\r
+ * searches (just pass minPos > maxPos to do a backward search)\r
+ * Has not been tested with backwards DBCS searches yet.\r
+ */\r
+long Document::FindText(int minPos, int maxPos, const char *s,\r
+                        bool caseSensitive, bool word, bool wordStart, bool regExp, int flags,\r
+                        int *length) {\r
+       if (regExp) {\r
+               if (!regex)\r
+                       regex = CreateRegexSearch(&charClass);\r
+               return regex->FindText(this, minPos, maxPos, s, caseSensitive, word, wordStart, flags, length);\r
+       } else {\r
+\r
+               bool forward = minPos <= maxPos;\r
+               int increment = forward ? 1 : -1;\r
+\r
+               // Range endpoints should not be inside DBCS characters, but just in case, move them.\r
+               int startPos = MovePositionOutsideChar(minPos, increment, false);\r
+               int endPos = MovePositionOutsideChar(maxPos, increment, false);\r
+\r
+               // Compute actual search ranges needed\r
+               int lengthFind = *length;\r
+               if (lengthFind == -1)\r
+                       lengthFind = static_cast<int>(strlen(s));\r
+               int endSearch = endPos;\r
+               if (startPos <= endPos) {\r
+                       endSearch = endPos - lengthFind + 1;\r
+               }\r
+               //Platform::DebugPrintf("Find %d %d %s %d\n", startPos, endPos, ft->lpstrText, lengthFind);\r
+               char firstChar = s[0];\r
+               if (!caseSensitive)\r
+                       firstChar = static_cast<char>(MakeUpperCase(firstChar));\r
+               int pos = forward ? startPos : (startPos - 1);\r
+               while (forward ? (pos < endSearch) : (pos >= endSearch)) {\r
+                       char ch = CharAt(pos);\r
+                       if (caseSensitive) {\r
+                               if (ch == firstChar) {\r
+                                       bool found = true;\r
+                                       if (pos + lengthFind > Platform::Maximum(startPos, endPos)) found = false;\r
+                                       for (int posMatch = 1; posMatch < lengthFind && found; posMatch++) {\r
+                                               ch = CharAt(pos + posMatch);\r
+                                               if (ch != s[posMatch])\r
+                                                       found = false;\r
+                                       }\r
+                                       if (found) {\r
+                                               if ((!word && !wordStart) ||\r
+                                                       word && IsWordAt(pos, pos + lengthFind) ||\r
+                                                       wordStart && IsWordStartAt(pos))\r
+                                                       return pos;\r
+                                       }\r
+                               }\r
+                       } else {\r
+                               if (MakeUpperCase(ch) == firstChar) {\r
+                                       bool found = true;\r
+                                       if (pos + lengthFind > Platform::Maximum(startPos, endPos)) found = false;\r
+                                       for (int posMatch = 1; posMatch < lengthFind && found; posMatch++) {\r
+                                               ch = CharAt(pos + posMatch);\r
+                                               if (MakeUpperCase(ch) != MakeUpperCase(s[posMatch]))\r
+                                                       found = false;\r
+                                       }\r
+                                       if (found) {\r
+                                               if ((!word && !wordStart) ||\r
+                                                       word && IsWordAt(pos, pos + lengthFind) ||\r
+                                                       wordStart && IsWordStartAt(pos))\r
+                                                       return pos;\r
+                                       }\r
+                               }\r
+                       }\r
+                       pos += increment;\r
+                       if (dbcsCodePage && (pos >= 0)) {\r
+                               // Ensure trying to match from start of character\r
+                               pos = MovePositionOutsideChar(pos, increment, false);\r
+                       }\r
+               }\r
+       }\r
+       //Platform::DebugPrintf("Not found\n");\r
+       return -1;\r
+}\r
+\r
+const char *Document::SubstituteByPosition(const char *text, int *length) {\r
+       return regex->SubstituteByPosition(this, text, length);\r
+}\r
+\r
+int Document::LinesTotal() const {\r
+       return cb.Lines();\r
+}\r
+\r
+void Document::ChangeCase(Range r, bool makeUpperCase) {\r
+       for (int pos = r.start; pos < r.end;) {\r
+               int len = LenChar(pos);\r
+               if (len == 1) {\r
+                       char ch = CharAt(pos);\r
+                       if (makeUpperCase) {\r
+                               if (IsLowerCase(ch)) {\r
+                                       ChangeChar(pos, static_cast<char>(MakeUpperCase(ch)));\r
+                               }\r
+                       } else {\r
+                               if (IsUpperCase(ch)) {\r
+                                       ChangeChar(pos, static_cast<char>(MakeLowerCase(ch)));\r
+                               }\r
+                       }\r
+               }\r
+               pos += len;\r
+       }\r
+}\r
+\r
+void Document::SetDefaultCharClasses(bool includeWordClass) {\r
+    charClass.SetDefaultCharClasses(includeWordClass);\r
+}\r
+\r
+void Document::SetCharClasses(const unsigned char *chars, CharClassify::cc newCharClass) {\r
+    charClass.SetCharClasses(chars, newCharClass);\r
+}\r
+\r
+void Document::SetStylingBits(int bits) {\r
+       stylingBits = bits;\r
+       stylingBitsMask = (1 << stylingBits) - 1;\r
+}\r
+\r
+void Document::StartStyling(int position, char mask) {\r
+       stylingMask = mask;\r
+       endStyled = position;\r
+}\r
+\r
+bool Document::SetStyleFor(int length, char style) {\r
+       if (enteredStyling != 0) {\r
+               return false;\r
+       } else {\r
+               enteredStyling++;\r
+               style &= stylingMask;\r
+               int prevEndStyled = endStyled;\r
+               if (cb.SetStyleFor(endStyled, length, style, stylingMask)) {\r
+                       DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,\r
+                                          prevEndStyled, length);\r
+                       NotifyModified(mh);\r
+               }\r
+               endStyled += length;\r
+               enteredStyling--;\r
+               return true;\r
+       }\r
+}\r
+\r
+bool Document::SetStyles(int length, char *styles) {\r
+       if (enteredStyling != 0) {\r
+               return false;\r
+       } else {\r
+               enteredStyling++;\r
+               bool didChange = false;\r
+               int startMod = 0;\r
+               int endMod = 0;\r
+               for (int iPos = 0; iPos < length; iPos++, endStyled++) {\r
+                       PLATFORM_ASSERT(endStyled < Length());\r
+                       if (cb.SetStyleAt(endStyled, styles[iPos], stylingMask)) {\r
+                               if (!didChange) {\r
+                                       startMod = endStyled;\r
+                               }\r
+                               didChange = true;\r
+                               endMod = endStyled;\r
+                       }\r
+               }\r
+               if (didChange) {\r
+                       DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,\r
+                                          startMod, endMod - startMod + 1);\r
+                       NotifyModified(mh);\r
+               }\r
+               enteredStyling--;\r
+               return true;\r
+       }\r
+}\r
+\r
+void Document::EnsureStyledTo(int pos) {\r
+       if ((enteredStyling == 0) && (pos > GetEndStyled())) {\r
+               IncrementStyleClock();\r
+               // Ask the watchers to style, and stop as soon as one responds.\r
+               for (int i = 0; pos > GetEndStyled() && i < lenWatchers; i++) {\r
+                       watchers[i].watcher->NotifyStyleNeeded(this, watchers[i].userData, pos);\r
+               }\r
+       }\r
+}\r
+\r
+int Document::SetLineState(int line, int state) {\r
+       int statePrevious = cb.SetLineState(line, state);\r
+       if (state != statePrevious) {\r
+               DocModification mh(SC_MOD_CHANGELINESTATE, 0, 0, 0, 0, line);\r
+               NotifyModified(mh);\r
+       }\r
+       return statePrevious;\r
+}\r
+\r
+void Document::IncrementStyleClock() {\r
+       styleClock = (styleClock + 1) % 0x100000;\r
+}\r
+\r
+void Document::DecorationFillRange(int position, int value, int fillLength) {\r
+       if (decorations.FillRange(position, value, fillLength)) {\r
+               DocModification mh(SC_MOD_CHANGEINDICATOR | SC_PERFORMED_USER,\r
+                                                       position, fillLength);\r
+               NotifyModified(mh);\r
+       }\r
+}\r
+\r
+bool Document::AddWatcher(DocWatcher *watcher, void *userData) {\r
+       for (int i = 0; i < lenWatchers; i++) {\r
+               if ((watchers[i].watcher == watcher) &&\r
+                       (watchers[i].userData == userData))\r
+                       return false;\r
+       }\r
+       WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers + 1];\r
+       if (!pwNew)\r
+               return false;\r
+       for (int j = 0; j < lenWatchers; j++)\r
+               pwNew[j] = watchers[j];\r
+       pwNew[lenWatchers].watcher = watcher;\r
+       pwNew[lenWatchers].userData = userData;\r
+       delete []watchers;\r
+       watchers = pwNew;\r
+       lenWatchers++;\r
+       return true;\r
+}\r
+\r
+bool Document::RemoveWatcher(DocWatcher *watcher, void *userData) {\r
+       for (int i = 0; i < lenWatchers; i++) {\r
+               if ((watchers[i].watcher == watcher) &&\r
+                       (watchers[i].userData == userData)) {\r
+                       if (lenWatchers == 1) {\r
+                               delete []watchers;\r
+                               watchers = 0;\r
+                               lenWatchers = 0;\r
+                       } else {\r
+                               WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers];\r
+                               if (!pwNew)\r
+                                       return false;\r
+                               for (int j = 0; j < lenWatchers - 1; j++) {\r
+                                       pwNew[j] = (j < i) ? watchers[j] : watchers[j + 1];\r
+                               }\r
+                               delete []watchers;\r
+                               watchers = pwNew;\r
+                               lenWatchers--;\r
+                       }\r
+                       return true;\r
+               }\r
+       }\r
+       return false;\r
+}\r
+\r
+void Document::NotifyModifyAttempt() {\r
+       for (int i = 0; i < lenWatchers; i++) {\r
+               watchers[i].watcher->NotifyModifyAttempt(this, watchers[i].userData);\r
+       }\r
+}\r
+\r
+void Document::NotifySavePoint(bool atSavePoint) {\r
+       for (int i = 0; i < lenWatchers; i++) {\r
+               watchers[i].watcher->NotifySavePoint(this, watchers[i].userData, atSavePoint);\r
+       }\r
+}\r
+\r
+void Document::NotifyModified(DocModification mh) {\r
+       if (mh.modificationType & SC_MOD_INSERTTEXT) {\r
+               decorations.InsertSpace(mh.position, mh.length);\r
+       } else if (mh.modificationType & SC_MOD_DELETETEXT) {\r
+               decorations.DeleteRange(mh.position, mh.length);\r
+       }\r
+       for (int i = 0; i < lenWatchers; i++) {\r
+               watchers[i].watcher->NotifyModified(this, mh, watchers[i].userData);\r
+       }\r
+}\r
+\r
+bool Document::IsWordPartSeparator(char ch) {\r
+       return (WordCharClass(ch) == CharClassify::ccWord) && IsPunctuation(ch);\r
+}\r
+\r
+int Document::WordPartLeft(int pos) {\r
+       if (pos > 0) {\r
+               --pos;\r
+               char startChar = cb.CharAt(pos);\r
+               if (IsWordPartSeparator(startChar)) {\r
+                       while (pos > 0 && IsWordPartSeparator(cb.CharAt(pos))) {\r
+                               --pos;\r
+                       }\r
+               }\r
+               if (pos > 0) {\r
+                       startChar = cb.CharAt(pos);\r
+                       --pos;\r
+                       if (IsLowerCase(startChar)) {\r
+                               while (pos > 0 && IsLowerCase(cb.CharAt(pos)))\r
+                                       --pos;\r
+                               if (!IsUpperCase(cb.CharAt(pos)) && !IsLowerCase(cb.CharAt(pos)))\r
+                                       ++pos;\r
+                       } else if (IsUpperCase(startChar)) {\r
+                               while (pos > 0 && IsUpperCase(cb.CharAt(pos)))\r
+                                       --pos;\r
+                               if (!IsUpperCase(cb.CharAt(pos)))\r
+                                       ++pos;\r
+                       } else if (IsADigit(startChar)) {\r
+                               while (pos > 0 && IsADigit(cb.CharAt(pos)))\r
+                                       --pos;\r
+                               if (!IsADigit(cb.CharAt(pos)))\r
+                                       ++pos;\r
+                       } else if (IsPunctuation(startChar)) {\r
+                               while (pos > 0 && IsPunctuation(cb.CharAt(pos)))\r
+                                       --pos;\r
+                               if (!IsPunctuation(cb.CharAt(pos)))\r
+                                       ++pos;\r
+                       } else if (isspacechar(startChar)) {\r
+                               while (pos > 0 && isspacechar(cb.CharAt(pos)))\r
+                                       --pos;\r
+                               if (!isspacechar(cb.CharAt(pos)))\r
+                                       ++pos;\r
+                       } else if (!isascii(startChar)) {\r
+                               while (pos > 0 && !isascii(cb.CharAt(pos)))\r
+                                       --pos;\r
+                               if (isascii(cb.CharAt(pos)))\r
+                                       ++pos;\r
+                       } else {\r
+                               ++pos;\r
+                       }\r
+               }\r
+       }\r
+       return pos;\r
+}\r
+\r
+int Document::WordPartRight(int pos) {\r
+       char startChar = cb.CharAt(pos);\r
+       int length = Length();\r
+       if (IsWordPartSeparator(startChar)) {\r
+               while (pos < length && IsWordPartSeparator(cb.CharAt(pos)))\r
+                       ++pos;\r
+               startChar = cb.CharAt(pos);\r
+       }\r
+       if (!isascii(startChar)) {\r
+               while (pos < length && !isascii(cb.CharAt(pos)))\r
+                       ++pos;\r
+       } else if (IsLowerCase(startChar)) {\r
+               while (pos < length && IsLowerCase(cb.CharAt(pos)))\r
+                       ++pos;\r
+       } else if (IsUpperCase(startChar)) {\r
+               if (IsLowerCase(cb.CharAt(pos + 1))) {\r
+                       ++pos;\r
+                       while (pos < length && IsLowerCase(cb.CharAt(pos)))\r
+                               ++pos;\r
+               } else {\r
+                       while (pos < length && IsUpperCase(cb.CharAt(pos)))\r
+                               ++pos;\r
+               }\r
+               if (IsLowerCase(cb.CharAt(pos)) && IsUpperCase(cb.CharAt(pos - 1)))\r
+                       --pos;\r
+       } else if (IsADigit(startChar)) {\r
+               while (pos < length && IsADigit(cb.CharAt(pos)))\r
+                       ++pos;\r
+       } else if (IsPunctuation(startChar)) {\r
+               while (pos < length && IsPunctuation(cb.CharAt(pos)))\r
+                       ++pos;\r
+       } else if (isspacechar(startChar)) {\r
+               while (pos < length && isspacechar(cb.CharAt(pos)))\r
+                       ++pos;\r
+       } else {\r
+               ++pos;\r
+       }\r
+       return pos;\r
+}\r
+\r
+bool IsLineEndChar(char c) {\r
+       return (c == '\n' || c == '\r');\r
+}\r
+\r
+int Document::ExtendStyleRange(int pos, int delta, bool singleLine) {\r
+       int sStart = cb.StyleAt(pos);\r
+       if (delta < 0) {\r
+               while (pos > 0 && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsLineEndChar(cb.CharAt(pos))) )\r
+                       pos--;\r
+               pos++;\r
+       } else {\r
+               while (pos < (Length()) && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsLineEndChar(cb.CharAt(pos))) )\r
+                       pos++;\r
+       }\r
+       return pos;\r
+}\r
+\r
+static char BraceOpposite(char ch) {\r
+       switch (ch) {\r
+       case '(':\r
+               return ')';\r
+       case ')':\r
+               return '(';\r
+       case '[':\r
+               return ']';\r
+       case ']':\r
+               return '[';\r
+       case '{':\r
+               return '}';\r
+       case '}':\r
+               return '{';\r
+       case '<':\r
+               return '>';\r
+       case '>':\r
+               return '<';\r
+       default:\r
+               return '\0';\r
+       }\r
+}\r
+\r
+// TODO: should be able to extend styled region to find matching brace\r
+int Document::BraceMatch(int position, int /*maxReStyle*/) {\r
+       char chBrace = CharAt(position);\r
+       char chSeek = BraceOpposite(chBrace);\r
+       if (chSeek == '\0')\r
+               return - 1;\r
+       char styBrace = static_cast<char>(StyleAt(position) & stylingBitsMask);\r
+       int direction = -1;\r
+       if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<')\r
+               direction = 1;\r
+       int depth = 1;\r
+       position = position + direction;\r
+       while ((position >= 0) && (position < Length())) {\r
+               position = MovePositionOutsideChar(position, direction);\r
+               char chAtPos = CharAt(position);\r
+               char styAtPos = static_cast<char>(StyleAt(position) & stylingBitsMask);\r
+               if ((position > GetEndStyled()) || (styAtPos == styBrace)) {\r
+                       if (chAtPos == chBrace)\r
+                               depth++;\r
+                       if (chAtPos == chSeek)\r
+                               depth--;\r
+                       if (depth == 0)\r
+                               return position;\r
+               }\r
+               position = position + direction;\r
+       }\r
+       return - 1;\r
+}\r
+\r
+/**\r
+ * Implementation of RegexSearchBase for the default built-in regular expression engine\r
+ */\r
+class BuiltinRegex : public RegexSearchBase {\r
+public:\r
+       BuiltinRegex(CharClassify *charClassTable) : search(charClassTable), substituted(NULL) {}\r
+\r
+       virtual ~BuiltinRegex() {\r
+               delete substituted;\r
+       }\r
+\r
+       virtual long FindText(Document *doc, int minPos, int maxPos, const char *s,\r
+                        bool caseSensitive, bool word, bool wordStart, int flags,\r
+                        int *length);\r
+\r
+       virtual const char *SubstituteByPosition(Document* doc, const char *text, int *length);\r
+\r
+private:\r
+       RESearch search;\r
+       char *substituted;\r
+};\r
+\r
+// Define a way for the Regular Expression code to access the document\r
+class DocumentIndexer : public CharacterIndexer {\r
+       Document *pdoc;\r
+       int end;\r
+public:\r
+       DocumentIndexer(Document *pdoc_, int end_) :\r
+               pdoc(pdoc_), end(end_) {\r
+       }\r
+\r
+       virtual ~DocumentIndexer() {\r
+       }\r
+\r
+       virtual char CharAt(int index) {\r
+               if (index < 0 || index >= end)\r
+                       return 0;\r
+               else\r
+                       return pdoc->CharAt(index);\r
+       }\r
+};\r
+\r
+long BuiltinRegex::FindText(Document *doc, int minPos, int maxPos, const char *s,\r
+                        bool caseSensitive, bool, bool, int flags,\r
+                        int *length) {\r
+       bool posix = (flags & SCFIND_POSIX) != 0;\r
+       int increment = (minPos <= maxPos) ? 1 : -1;\r
+\r
+       int startPos = minPos;\r
+       int endPos = maxPos;\r
+\r
+       // Range endpoints should not be inside DBCS characters, but just in case, move them.\r
+       startPos = doc->MovePositionOutsideChar(startPos, 1, false);\r
+       endPos = doc->MovePositionOutsideChar(endPos, 1, false);\r
+\r
+       const char *errmsg = search.Compile(s, *length, caseSensitive, posix);\r
+       if (errmsg) {\r
+               return -1;\r
+       }\r
+       // Find a variable in a property file: \$(\([A-Za-z0-9_.]+\))\r
+       // Replace first '.' with '-' in each property file variable reference:\r
+       //     Search: \$(\([A-Za-z0-9_-]+\)\.\([A-Za-z0-9_.]+\))\r
+       //     Replace: $(\1-\2)\r
+       int lineRangeStart = doc->LineFromPosition(startPos);\r
+       int lineRangeEnd = doc->LineFromPosition(endPos);\r
+       if ((increment == 1) &&\r
+               (startPos >= doc->LineEnd(lineRangeStart)) &&\r
+               (lineRangeStart < lineRangeEnd)) {\r
+               // the start position is at end of line or between line end characters.\r
+               lineRangeStart++;\r
+               startPos = doc->LineStart(lineRangeStart);\r
+       }\r
+       int pos = -1;\r
+       int lenRet = 0;\r
+       char searchEnd = s[*length - 1];\r
+       int lineRangeBreak = lineRangeEnd + increment;\r
+       for (int line = lineRangeStart; line != lineRangeBreak; line += increment) {\r
+               int startOfLine = doc->LineStart(line);\r
+               int endOfLine = doc->LineEnd(line);\r
+               if (increment == 1) {\r
+                       if (line == lineRangeStart) {\r
+                               if ((startPos != startOfLine) && (s[0] == '^'))\r
+                                       continue;       // Can't match start of line if start position after start of line\r
+                               startOfLine = startPos;\r
+                       }\r
+                       if (line == lineRangeEnd) {\r
+                               if ((endPos != endOfLine) && (searchEnd == '$'))\r
+                                       continue;       // Can't match end of line if end position before end of line\r
+                               endOfLine = endPos;\r
+                       }\r
+               } else {\r
+                       if (line == lineRangeEnd) {\r
+                               if ((endPos != startOfLine) && (s[0] == '^'))\r
+                                       continue;       // Can't match start of line if end position after start of line\r
+                               startOfLine = endPos;\r
+                       }\r
+                       if (line == lineRangeStart) {\r
+                               if ((startPos != endOfLine) && (searchEnd == '$'))\r
+                                       continue;       // Can't match end of line if start position before end of line\r
+                               endOfLine = startPos;\r
+                       }\r
+               }\r
+\r
+               DocumentIndexer di(doc, endOfLine);\r
+               int success = search.Execute(di, startOfLine, endOfLine);\r
+               if (success) {\r
+                       pos = search.bopat[0];\r
+                       lenRet = search.eopat[0] - search.bopat[0];\r
+                       if (increment == -1) {\r
+                               // Check for the last match on this line.\r
+                               int repetitions = 1000; // Break out of infinite loop\r
+                               while (success && (search.eopat[0] <= endOfLine) && (repetitions--)) {\r
+                                       success = search.Execute(di, pos+1, endOfLine);\r
+                                       if (success) {\r
+                                               if (search.eopat[0] <= minPos) {\r
+                                                       pos = search.bopat[0];\r
+                                                       lenRet = search.eopat[0] - search.bopat[0];\r
+                                               } else {\r
+                                                       success = 0;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       break;\r
+               }\r
+       }\r
+       *length = lenRet;\r
+       return pos;\r
+}\r
+\r
+const char *BuiltinRegex::SubstituteByPosition(Document* doc, const char *text, int *length) {\r
+       delete []substituted;\r
+       substituted = 0;\r
+       DocumentIndexer di(doc, doc->Length());\r
+       if (!search.GrabMatches(di))\r
+               return 0;\r
+       unsigned int lenResult = 0;\r
+       for (int i = 0; i < *length; i++) {\r
+               if (text[i] == '\\') {\r
+                       if (text[i + 1] >= '1' && text[i + 1] <= '9') {\r
+                               unsigned int patNum = text[i + 1] - '0';\r
+                               lenResult += search.eopat[patNum] - search.bopat[patNum];\r
+                               i++;\r
+                       } else {\r
+                               switch (text[i + 1]) {\r
+                               case 'a':\r
+                               case 'b':\r
+                               case 'f':\r
+                               case 'n':\r
+                               case 'r':\r
+                               case 't':\r
+                               case 'v':\r
+                                       i++;\r
+                               }\r
+                               lenResult++;\r
+                       }\r
+               } else {\r
+                       lenResult++;\r
+               }\r
+       }\r
+       substituted = new char[lenResult + 1];\r
+       if (!substituted)\r
+               return 0;\r
+       char *o = substituted;\r
+       for (int j = 0; j < *length; j++) {\r
+               if (text[j] == '\\') {\r
+                       if (text[j + 1] >= '1' && text[j + 1] <= '9') {\r
+                               unsigned int patNum = text[j + 1] - '0';\r
+                               unsigned int len = search.eopat[patNum] - search.bopat[patNum];\r
+                               if (search.pat[patNum]) // Will be null if try for a match that did not occur\r
+                                       memcpy(o, search.pat[patNum], len);\r
+                               o += len;\r
+                               j++;\r
+                       } else {\r
+                               j++;\r
+                               switch (text[j]) {\r
+                               case 'a':\r
+                                       *o++ = '\a';\r
+                                       break;\r
+                               case 'b':\r
+                                       *o++ = '\b';\r
+                                       break;\r
+                               case 'f':\r
+                                       *o++ = '\f';\r
+                                       break;\r
+                               case 'n':\r
+                                       *o++ = '\n';\r
+                                       break;\r
+                               case 'r':\r
+                                       *o++ = '\r';\r
+                                       break;\r
+                               case 't':\r
+                                       *o++ = '\t';\r
+                                       break;\r
+                               case 'v':\r
+                                       *o++ = '\v';\r
+                                       break;\r
+                               default:\r
+                                       *o++ = '\\';\r
+                                       j--;\r
+                               }\r
+                       }\r
+               } else {\r
+                       *o++ = text[j];\r
+               }\r
+       }\r
+       *o = '\0';\r
+       *length = lenResult;\r
+       return substituted;\r
+}\r
+\r
+#ifndef SCI_OWNREGEX\r
+\r
+RegexSearchBase *CreateRegexSearch(CharClassify *charClassTable) {\r
+       return new BuiltinRegex(charClassTable);\r
+}\r
+\r
+#endif\r