📄 document.cxx
字号:
// Scintilla source code edit control/** @file Document.cxx ** Text document that handles notifications, DBCS, styling, words and end of line. **/// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>// The License.txt file describes the conditions under which this software may be distributed.#include <stdlib.h>#include <string.h>#include <stdio.h>#include <ctype.h>#include "Platform.h"#include "Scintilla.h"#include "SplitVector.h"#include "Partitioning.h"#include "RunStyles.h"#include "CellBuffer.h"#include "CharClassify.h"#include "Decoration.h"#include "Document.h"#include "RESearch.h"#ifdef SCI_NAMESPACEusing namespace Scintilla;#endif// This is ASCII specific but is safe with chars >= 0x80static inline bool isspacechar(unsigned char ch) { return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));}static inline bool IsPunctuation(char ch) { return isascii(ch) && ispunct(ch);}static inline bool IsADigit(char ch) { return isascii(ch) && isdigit(ch);}static inline bool IsLowerCase(char ch) { return isascii(ch) && islower(ch);}static inline bool IsUpperCase(char ch) { return isascii(ch) && isupper(ch);}Document::Document() { refCount = 0;#ifdef unix eolMode = SC_EOL_LF;#else eolMode = SC_EOL_CRLF;#endif dbcsCodePage = 0; stylingBits = 5; stylingBitsMask = 0x1F; stylingMask = 0; endStyled = 0; styleClock = 0; enteredModification = 0; enteredStyling = 0; enteredReadOnlyCount = 0; tabInChars = 8; indentInChars = 0; actualIndentInChars = 8; useTabs = true; tabIndents = true; backspaceUnindents = false; watchers = 0; lenWatchers = 0; matchesValid = false; pre = 0; substituted = 0;}Document::~Document() { for (int i = 0; i < lenWatchers; i++) { watchers[i].watcher->NotifyDeleted(this, watchers[i].userData); } delete []watchers; watchers = 0; lenWatchers = 0; delete pre; pre = 0; delete []substituted; substituted = 0;}// Increase reference count and return its previous value.int Document::AddRef() { return refCount++;}// Decrease reference count and return its previous value.// Delete the document if reference count reaches zero.int Document::Release() { int curRefCount = --refCount; if (curRefCount == 0) delete this; return curRefCount;}void Document::SetSavePoint() { cb.SetSavePoint(); NotifySavePoint(true);}int Document::AddMark(int line, int markerNum) { int prev = cb.AddMark(line, markerNum); DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line); NotifyModified(mh); return prev;}void Document::AddMarkSet(int line, int valueSet) { unsigned int m = valueSet; for (int i = 0; m; i++, m >>= 1) if (m & 1) cb.AddMark(line, i); DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line); NotifyModified(mh);}void Document::DeleteMark(int line, int markerNum) { cb.DeleteMark(line, markerNum); DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line); NotifyModified(mh);}void Document::DeleteMarkFromHandle(int markerHandle) { cb.DeleteMarkFromHandle(markerHandle); DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0); mh.line = -1; NotifyModified(mh);}void Document::DeleteAllMarks(int markerNum) { cb.DeleteAllMarks(markerNum); DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0); mh.line = -1; NotifyModified(mh);}int Document::LineStart(int line) const { return cb.LineStart(line);}int Document::LineEnd(int line) const { if (line == LinesTotal() - 1) { return LineStart(line + 1); } else { int position = LineStart(line + 1) - 1; // When line terminator is CR+LF, may need to go back one more if ((position > LineStart(line)) && (cb.CharAt(position - 1) == '\r')) { position--; } return position; }}int Document::LineFromPosition(int pos) { return cb.LineFromPosition(pos);}int Document::LineEndPosition(int position) { return LineEnd(LineFromPosition(position));}int Document::VCHomePosition(int position) { int line = LineFromPosition(position); int startPosition = LineStart(line); int endLine = LineStart(line + 1) - 1; int startText = startPosition; while (startText < endLine && (cb.CharAt(startText) == ' ' || cb.CharAt(startText) == '\t' ) ) startText++; if (position == startText) return startPosition; else return startText;}int Document::SetLevel(int line, int level) { int prev = cb.SetLevel(line, level); if (prev != level) { DocModification mh(SC_MOD_CHANGEFOLD | SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line); mh.foldLevelNow = level; mh.foldLevelPrev = prev; NotifyModified(mh); } return prev;}static bool IsSubordinate(int levelStart, int levelTry) { if (levelTry & SC_FOLDLEVELWHITEFLAG) return true; else return (levelStart & SC_FOLDLEVELNUMBERMASK) < (levelTry & SC_FOLDLEVELNUMBERMASK);}int Document::GetLastChild(int lineParent, int level) { if (level == -1) level = GetLevel(lineParent) & SC_FOLDLEVELNUMBERMASK; int maxLine = LinesTotal(); int lineMaxSubord = lineParent; while (lineMaxSubord < maxLine - 1) { EnsureStyledTo(LineStart(lineMaxSubord + 2)); if (!IsSubordinate(level, GetLevel(lineMaxSubord + 1))) break; lineMaxSubord++; } if (lineMaxSubord > lineParent) { if (level > (GetLevel(lineMaxSubord + 1) & SC_FOLDLEVELNUMBERMASK)) { // Have chewed up some whitespace that belongs to a parent so seek back if (GetLevel(lineMaxSubord) & SC_FOLDLEVELWHITEFLAG) { lineMaxSubord--; } } } return lineMaxSubord;}int Document::GetFoldParent(int line) { int level = GetLevel(line) & SC_FOLDLEVELNUMBERMASK; int lineLook = line - 1; while ((lineLook > 0) && ( (!(GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG)) || ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) >= level)) ) { lineLook--; } if ((GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG) && ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) < level)) { return lineLook; } else { return -1; }}int Document::ClampPositionIntoDocument(int pos) { return Platform::Clamp(pos, 0, Length());}bool Document::IsCrLf(int pos) { if (pos < 0) return false; if (pos >= (Length() - 1)) return false; return (cb.CharAt(pos) == '\r') && (cb.CharAt(pos + 1) == '\n');}static const int maxBytesInDBCSCharacter=5;int Document::LenChar(int pos) { if (pos < 0) { return 1; } else if (IsCrLf(pos)) { return 2; } else if (SC_CP_UTF8 == dbcsCodePage) { unsigned char ch = static_cast<unsigned char>(cb.CharAt(pos)); if (ch < 0x80) return 1; int len = 2; if (ch >= (0x80 + 0x40 + 0x20 + 0x10)) len = 4; else if (ch >= (0x80 + 0x40 + 0x20)) len = 3; int lengthDoc = Length(); if ((pos + len) > lengthDoc) return lengthDoc -pos; else return len; } else if (dbcsCodePage) { char mbstr[maxBytesInDBCSCharacter+1]; int i; for (i=0; i<Platform::DBCSCharMaxLength(); i++) { mbstr[i] = cb.CharAt(pos+i); } mbstr[i] = '\0'; return Platform::DBCSCharLength(dbcsCodePage, mbstr); } else { return 1; }}static bool IsTrailByte(int ch) { return (ch >= 0x80) && (ch < (0x80 + 0x40));}static int BytesFromLead(int leadByte) { if (leadByte > 0xF4) { // Characters longer than 4 bytes not possible in current UTF-8 return 0; } else if (leadByte >= 0xF0) { return 4; } else if (leadByte >= 0xE0) { return 3; } else if (leadByte >= 0xC2) { return 2; } return 0;}bool Document::InGoodUTF8(int pos, int &start, int &end) { int lead = pos; while ((lead>0) && (pos-lead < 4) && IsTrailByte(static_cast<unsigned char>(cb.CharAt(lead-1)))) lead--; start = 0; if (lead > 0) { start = lead-1; } int leadByte = static_cast<unsigned char>(cb.CharAt(start)); int bytes = BytesFromLead(leadByte); if (bytes == 0) { return false; } else { int trailBytes = bytes - 1; int len = pos - lead + 1; if (len > trailBytes) // pos too far from lead return false; // Check that there are enough trails for this lead int trail = pos + 1; while ((trail-lead<trailBytes) && (trail < Length())) { if (!IsTrailByte(static_cast<unsigned char>(cb.CharAt(trail)))) { return false; } trail++; } end = start + bytes; return true; }}// Normalise a position so that it is not halfway through a two byte character.// This can occur in two situations -// When lines are terminated with \r\n pairs which should be treated as one character.// When displaying DBCS text such as Japanese.// If moving, move the position in the indicated direction.int Document::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) { //Platform::DebugPrintf("NoCRLF %d %d\n", pos, moveDir); // If out of range, just return minimum/maximum value. if (pos <= 0) return 0; if (pos >= Length()) return Length(); // PLATFORM_ASSERT(pos > 0 && pos < Length()); if (checkLineEnd && IsCrLf(pos - 1)) { if (moveDir > 0) return pos + 1; else return pos - 1; } // Not between CR and LF if (dbcsCodePage) { if (SC_CP_UTF8 == dbcsCodePage) { unsigned char ch = static_cast<unsigned char>(cb.CharAt(pos)); int startUTF = pos; int endUTF = pos; if (IsTrailByte(ch) && InGoodUTF8(pos, startUTF, endUTF)) { // ch is a trail byte within a UTF-8 character if (moveDir > 0) pos = endUTF; else pos = startUTF; } } else { // Anchor DBCS calculations at start of line because start of line can // not be a DBCS trail byte. int posCheck = LineStart(LineFromPosition(pos)); while (posCheck < pos) { char mbstr[maxBytesInDBCSCharacter+1]; int i; for(i=0;i<Platform::DBCSCharMaxLength();i++) { mbstr[i] = cb.CharAt(posCheck+i); } mbstr[i] = '\0'; int mbsize = Platform::DBCSCharLength(dbcsCodePage, mbstr); if (posCheck + mbsize == pos) { return pos; } else if (posCheck + mbsize > pos) { if (moveDir > 0) { return posCheck + mbsize; } else { return posCheck; } } posCheck += mbsize; } } } return pos;}void Document::ModifiedAt(int pos) { if (endStyled > pos) endStyled = pos;}void Document::CheckReadOnly() { if (cb.IsReadOnly() && enteredReadOnlyCount == 0) { enteredReadOnlyCount++; NotifyModifyAttempt(); enteredReadOnlyCount--; }}// Document only modified by gateways DeleteChars, InsertString, Undo, Redo, and SetStyleAt.// SetStyleAt does not change the persistent state of a documentbool Document::DeleteChars(int pos, int len) { if (len == 0) return false; if ((pos + len) > Length()) return false; CheckReadOnly(); if (enteredModification != 0) { return false; } else { enteredModification++; if (!cb.IsReadOnly()) { NotifyModified( DocModification( SC_MOD_BEFOREDELETE | SC_PERFORMED_USER, pos, len, 0, 0)); int prevLinesTotal = LinesTotal(); bool startSavePoint = cb.IsSavePoint(); bool startSequence = false; const char *text = cb.DeleteChars(pos, len, startSequence); if (startSavePoint && cb.IsCollectingUndo()) NotifySavePoint(!startSavePoint); if ((pos < Length()) || (pos == 0)) ModifiedAt(pos); else ModifiedAt(pos-1); NotifyModified( DocModification( SC_MOD_DELETETEXT | SC_PERFORMED_USER | (startSequence?SC_STARTACTION:0), pos, len, LinesTotal() - prevLinesTotal, text)); } enteredModification--; } return !cb.IsReadOnly();}/** * Insert a string with a length. */bool Document::InsertString(int position, const char *s, int insertLength) { if (insertLength <= 0) { return false; } CheckReadOnly(); if (enteredModification != 0) { return false; } else { enteredModification++; if (!cb.IsReadOnly()) { NotifyModified( DocModification( SC_MOD_BEFOREINSERT | SC_PERFORMED_USER, position, insertLength, 0, s)); int prevLinesTotal = LinesTotal(); bool startSavePoint = cb.IsSavePoint(); bool startSequence = false; const char *text = cb.InsertString(position, s, insertLength, startSequence); if (startSavePoint && cb.IsCollectingUndo()) NotifySavePoint(!startSavePoint); ModifiedAt(position); NotifyModified( DocModification( SC_MOD_INSERTTEXT | SC_PERFORMED_USER | (startSequence?SC_STARTACTION:0), position, insertLength, LinesTotal() - prevLinesTotal, text)); } enteredModification--; } return !cb.IsReadOnly();}int Document::Undo() { int newPos = -1; CheckReadOnly(); if (enteredModification == 0) { enteredModification++; if (!cb.IsReadOnly()) { bool startSavePoint = cb.IsSavePoint(); bool multiLine = false; int steps = cb.StartUndo(); //Platform::DebugPrintf("Steps=%d\n", steps); for (int step = 0; step < steps; step++) { const int prevLinesTotal = LinesTotal(); const Action &action = cb.GetUndoStep(); if (action.at == removeAction) { NotifyModified(DocModification( SC_MOD_BEFOREINSERT | SC_PERFORMED_UNDO, action)); } else { NotifyModified(DocModification( SC_MOD_BEFOREDELETE | SC_PERFORMED_UNDO, action)); } cb.PerformUndoStep(); int cellPosition = action.position; ModifiedAt(cellPosition); newPos = cellPosition; int modFlags = SC_PERFORMED_UNDO; // With undo, an insertion action becomes a deletion notification if (action.at == removeAction) { newPos += action.lenData; modFlags |= SC_MOD_INSERTTEXT; } else { modFlags |= SC_MOD_DELETETEXT; } if (steps > 1) modFlags |= SC_MULTISTEPUNDOREDO; const int linesAdded = LinesTotal() - prevLinesTotal; if (linesAdded != 0) multiLine = true; if (step == steps - 1) { modFlags |= SC_LASTSTEPINUNDOREDO; if (multiLine) modFlags |= SC_MULTILINEUNDOREDO; } NotifyModified(DocModification(modFlags, cellPosition, action.lenData, linesAdded, action.data)); } bool endSavePoint = cb.IsSavePoint(); if (startSavePoint != endSavePoint) NotifySavePoint(endSavePoint); } enteredModification--; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -