void ScintillaWrapper::pyreplace(boost::python::object searchExp, boost::python::object replaceStr, boost::python::object count, boost::python::object flags, boost::python::object startLine, boost::python::object endLine)
{
	
	boost::python::object re_module( (boost::python::handle<>(PyImport_ImportModule("re"))) );
	if (!re_module.is_none())
	{
		BeginUndoAction();
		const char *strCount = boost::python::extract<const char *>(count.attr("__str__")());
		int iCount;
		int iFlags = 0;
		
		if (!flags.is_none())
		{
			iFlags = boost::python::extract<int>(flags);
		}

		int start = 0;
		if (!startLine.is_none())
		{
			start = boost::python::extract<int>(startLine);
		}

		int end = -1;
		if (!startLine.is_none())
		{
			 end = boost::python::extract<int>(endLine);
		}

		iCount = atoi(strCount);
		bool ignoreCount = (iCount == 0);
		bool includeLineEndings = (iFlags & RE_INCLUDELINEENDINGS) == RE_INCLUDELINEENDINGS;

		long lineCount = GetLineCount();
		boost::python::object re = re_module.attr("compile")(searchExp, flags);
		
		size_t bufferLength = 0;
		Sci_TextRange range;
		range.chrg.cpMin = 0;
		range.lpstrText = NULL;
		boost::python::tuple result;
		idx_t currentStartPosition;
		int infiniteLoopCheck = 0;
		int previousLine = -1;
		for(int line = start; line < lineCount && (ignoreCount || iCount > 0) && (-1 == end || line <= end); ++line)
		{
			if (line == previousLine)
			{
				if (++infiniteLoopCheck >= 1000)
				{
					EndUndoAction();
					PyErr_SetString(PyExc_SystemError, "Infinite loop detected in pyreplace");
					if (range.lpstrText)
					{
						delete[] range.lpstrText;
					}

					throw boost::python::error_already_set();
				}
			}
			previousLine = line;

			if (includeLineEndings)
			{
				result = boost::python::extract<boost::python::tuple>(re.attr("subn")(replaceStr, GetLine(line), ignoreCount ? 0 : iCount));
			}
			else
			{
				range.chrg.cpMin = PositionFromLine(line);
				range.chrg.cpMax = GetLineEndPosition(line);
			
				if (bufferLength < (size_t)((range.chrg.cpMax - range.chrg.cpMin) + 1))
				{
					if (range.lpstrText)
						delete [] range.lpstrText;
					bufferLength = (size_t)((range.chrg.cpMax - range.chrg.cpMin) + 1);
					range.lpstrText = new char[bufferLength + 1];
				}
			
				callScintilla(SCI_GETTEXTRANGE, 0, reinterpret_cast<LPARAM>(&range));

				result = boost::python::extract<boost::python::tuple>(re.attr("subn")(replaceStr, const_cast<const char *>(range.lpstrText), ignoreCount ? 0 : iCount));
			}

			int numSubs = boost::python::extract<int>(result[1]);
			if (numSubs != 0)
			{
				size_t resultLength = _len(result[0]);
				if (includeLineEndings)
				{
					currentStartPosition = (idx_t)PositionFromLine(line);
					replaceWholeLine(line, result[0]);
				}
				else
				{
					currentStartPosition = (idx_t)range.chrg.cpMin;
					replaceLine(line, result[0]);
				}

				int newLine = LineFromPosition((int)(currentStartPosition + resultLength));
				
				// If the line number has moved on more than one
				// there must have been one or more new lines in the 
				// replacement, or no newline, hence the lines have become less
				if ((newLine - line) != (includeLineEndings ? 1 : 0))
				{
					line = newLine - (includeLineEndings ? 1 : 0);
					lineCount = GetLineCount();
				}
				iCount -= numSubs;
			}
		}	

		if (range.lpstrText)
			delete[] range.lpstrText;
		EndUndoAction();
	}

}
void ScintillaWrapper::pymlreplace(boost::python::object searchExp, boost::python::object replaceStr, boost::python::object count, boost::python::object flags, boost::python::object startPosition, boost::python::object endPosition)
{
	boost::python::str contents;
	offset_t currentOffset = 0;

	if (startPosition.is_none() && endPosition.is_none())
	{
		contents = GetCharacterPointer();
	}

	else
	{
		Sci_TextRange range;
		if (!startPosition.is_none())
		{
			range.chrg.cpMin = boost::python::extract<int>(startPosition);
		}
		else
		{
			range.chrg.cpMin = 0;
		}

		if (!endPosition.is_none())
		{
			range.chrg.cpMax = boost::python::extract<int>(endPosition);
		}
		else
		{
			range.chrg.cpMax = GetLength();
		}
		
		currentOffset = (offset_t)range.chrg.cpMin;

		range.lpstrText = new char[size_t((range.chrg.cpMax - range.chrg.cpMin) + 1)];
		callScintilla(SCI_GETTEXTRANGE, 0, reinterpret_cast<LPARAM>(&range));
		contents = boost::python::str(const_cast<const char *>(range.lpstrText));
		delete[] range.lpstrText;
	}



	boost::python::object re_module( (boost::python::handle<>(PyImport_ImportModule("re"))) );

	int iFlags = 0;
	int iCount = 0;
	if (!flags.is_none())
	{
		iFlags = boost::python::extract<int>(flags);
	}
	if (!count.is_none())
	{
		iCount = boost::python::extract<int>(count);
	}

	if (0 == iCount)
		iCount = -1;
	
	

	boost::python::object re = re_module.attr("compile")(searchExp, iFlags | boost::python::extract<int>(re_module.attr("MULTILINE")));
	if (!re_module.is_none())
	{
		boost::python::object match;
		BeginUndoAction();
		boost::python::object oreplacement;
		size_t replacementLength;
		idx_t matchStart, matchEnd;
		idx_t startPos = 0;
		

		do
		{
			match = re.attr("search")(contents, startPos);
			if (!match.is_none())
			{
				// Get expanded replacement string
				oreplacement = match.attr("expand")(replaceStr);
				
				
				// Calculate offsets
				matchStart = (idx_t)boost::python::extract<int>(match.attr("start")());
				matchEnd   = (idx_t)boost::python::extract<int>(match.attr("end")());


				// Extract text replacement
				const char *replacement = boost::python::extract<const char *>(oreplacement);
				replacementLength = _len(oreplacement);

				// Replace text in Scintilla
				callScintilla(SCI_SETTARGETSTART, static_cast<offset_t>(matchStart) + currentOffset);
				callScintilla(SCI_SETTARGETEND, static_cast<offset_t>(matchEnd) + currentOffset);
				callScintilla(SCI_REPLACETARGET, replacementLength, reinterpret_cast<LPARAM>(replacement));
				

				// Calculate the difference between the old string, 
				// and the new replacement, and add it to the currentOffset
				currentOffset += static_cast<offset_t>(replacementLength - (matchEnd - matchStart));


				// Set startPos to the end of the last match - startPos is with the original document
				startPos = matchEnd; 


			}
		} while(!match.is_none() && (iCount == -1 || --iCount > 0));
		
		EndUndoAction();
	}

}
String& String::insert (int pos, String str)
{
  if (_ref (buf) > 1) splice ();
  if (pos < 0) pos += _len (buf);
  if (pos < 0) pos = 0;
  if (pos > _len (buf)) pos = _len (buf);
  realloc (_len (buf) + _len (str.buf));
  if (pos < _len (buf))
    memmove (buf + pos + _len (str.buf), buf + pos, _len (buf) - pos);
  if (_len (str.buf))
    memcpy (buf + pos, str.buf, _len (str.buf));
  buf[_len (buf) += _len (str.buf)] = 0;
  return *this;
}
String& String::replace (int start, int len, char const* str)
{
  if (_ref (buf) > 1) splice ();
  int strLength = (int) strlen (str);
  if (start < 0) start += _len (buf);
  if (start < 0) start = 0;
  if (start > _len (buf)) start = _len (buf);
  if (len == toTheEnd) len = _len (buf) - start;
  else if (len < 0) len += _len (buf) - start;
  else if (start + len > _len (buf)) len = _len (buf) - start;
  if (len < 0) len = 0;
  int newlen = _len (buf) - len + strLength;
  realloc (newlen);
  int b = start + len;
  int bnew = start + strLength;
  if (b < _len (buf))
    memmove (buf + bnew, buf + b, _len (buf) - b);
  if (strLength)
    memcpy (buf + start, str, strLength);
  buf[newlen] = 0;
  _len (buf) = newlen;
  return *this;
}
int String::find (char const* str, int start, int options) const
{
  static int kmpbuf[256];
#define comp(a,b) (options&FIND_CASE_INSENSITIVE?tolower((unsigned char)a)==tolower((unsigned char)b):(a)==(b))
  if (start < 0) start += _len (buf);
  if (start < 0) start = 0;
  if (start > _len (buf)) start = _len (buf);
  if (*str == 0) return start;
  int len = (int) strlen (str);
  int* kmp = kmpbuf;
  if (len > 256)
    kmp = new int[len];
  kmp[0] = 0;
  int result = -1;
  if (options & FIND_REVERSE)
  {
    for (int i = 1; i < len; i++)
    {
      kmp[i] = kmp[i - 1];
      while (kmp[i] && !comp (str[len - 1 - kmp[i]], str[len - 1 - i]))
        kmp[i] = kmp[kmp[i] - 1];
      if (comp (str[len - 1 - kmp[i]], str[len - 1 - i]))
        kmp[i]++;
    }
    int cur = 0;
    for (int i = start - 1; i >= 0; i--)
    {
      while (cur && !comp (str[len - 1 - cur], buf[_len (buf) - 1 - i]))
        cur = kmp[cur - 1];
      if (comp (str[len - 1 - cur], buf[_len (buf) - 1 - i]))
        cur++;
      if (cur == len)
      {
        if ((options & FIND_WHOLE_WORD) == 0 || (
          isWordBoundary (i) && isWordBoundary (i + len)))
        {
          result = i;
          break;
        }
      }
    }
  }
  else
  {
    for (int i = 1; i < len; i++)
    {
      kmp[i] = kmp[i - 1];
      while (kmp[i] && !comp (str[kmp[i]], str[i]))
        kmp[i] = kmp[kmp[i] - 1];
      if (comp (str[kmp[i]], str[i]))
        kmp[i]++;
    }
    int cur = 0;
    for (int i = start; i < _len (buf); i++)
    {
      while (cur && !comp (str[cur], buf[i]))
        cur = kmp[cur - 1];
      if (comp (str[cur], buf[i]))
        cur++;
      if (cur == len)
      {
        if ((options & FIND_WHOLE_WORD) == 0 || (
          isWordBoundary (i - len + 1) && isWordBoundary (i + 1)))
        {
          result = i - len + 1;
          break;
        }
      }
    }
  }
  if (kmp != kmpbuf)
    delete[] kmp;
  return result;
}
bool String::isWordBoundary (int pos) const
{
  return pos <= 0 || pos >= _len (buf) || isIdChar (buf[pos]) != isIdChar (buf[pos - 1]);
}