📄 document.cxx
字号:
bool endSavePoint = cb.IsSavePoint();
if (startSavePoint != endSavePoint)
NotifySavePoint(endSavePoint);
enteredCount--;
}
return newPos;
}
int Document::Redo() {
int newPos = 0;
if (enteredCount == 0) {
enteredCount++;
bool startSavePoint = cb.IsSavePoint();
int steps = cb.StartRedo();
for (int step = 0; step < steps; step++) {
int prevLinesTotal = LinesTotal();
const Action &action = cb.GetRedoStep();
if (action.at == insertAction) {
NotifyModified(DocModification(
SC_MOD_BEFOREINSERT | SC_PERFORMED_REDO, action));
} else {
NotifyModified(DocModification(
SC_MOD_BEFOREDELETE | SC_PERFORMED_REDO, action));
}
cb.PerformRedoStep();
ModifiedAt(action.position / 2);
newPos = action.position / 2;
int modFlags = SC_PERFORMED_REDO;
if (action.at == insertAction) {
newPos += action.lenData;
modFlags |= SC_MOD_INSERTTEXT;
} else {
modFlags |= SC_MOD_DELETETEXT;
}
if (step == steps - 1)
modFlags |= SC_LASTSTEPINUNDOREDO;
NotifyModified(
DocModification(modFlags, action.position / 2, action.lenData,
LinesTotal() - prevLinesTotal, action.data));
}
bool endSavePoint = cb.IsSavePoint();
if (startSavePoint != endSavePoint)
NotifySavePoint(endSavePoint);
enteredCount--;
}
return newPos;
}
bool Document::InsertChar(int pos, char ch) {
char chs[2];
chs[0] = ch;
chs[1] = 0;
return InsertStyledString(pos*2, chs, 2);
}
// Insert a null terminated string
bool Document::InsertString(int position, const char *s) {
return InsertString(position, s, strlen(s));
}
// Insert a string with a length
bool Document::InsertString(int position, const char *s, size_t insertLength) {
bool changed = false;
char *sWithStyle = new char[insertLength * 2];
if (sWithStyle) {
for (size_t i = 0; i < insertLength; i++) {
sWithStyle[i*2] = s[i];
sWithStyle[i*2 + 1] = 0;
}
changed = InsertStyledString(position*2, sWithStyle,
static_cast<int>(insertLength*2));
delete []sWithStyle;
}
return changed;
}
void Document::ChangeChar(int pos, char ch) {
DeleteChars(pos, 1);
InsertChar(pos, ch);
}
void Document::DelChar(int pos) {
DeleteChars(pos, LenChar(pos));
}
void Document::DelCharBack(int pos) {
if (pos <= 0) {
return;
} else if (IsCrLf(pos - 2)) {
DeleteChars(pos - 2, 2);
} else if (dbcsCodePage) {
int startChar = MovePositionOutsideChar(pos - 1, -1, false);
DeleteChars(startChar, pos - startChar);
} else {
DeleteChars(pos - 1, 1);
}
}
static bool isindentchar(char ch) {
return (ch == ' ') || (ch == '\t');
}
static int NextTab(int pos, int tabSize) {
return ((pos / tabSize) + 1) * tabSize;
}
static void CreateIndentation(char *linebuf, int length, int indent, int tabSize, bool insertSpaces) {
length--; // ensure space for \0
if (!insertSpaces) {
while ((indent >= tabSize) && (length > 0)) {
*linebuf++ = '\t';
indent -= tabSize;
length--;
}
}
while ((indent > 0) && (length > 0)) {
*linebuf++ = ' ';
indent--;
length--;
}
*linebuf = '\0';
}
int Document::GetLineIndentation(int line) {
int indent = 0;
if ((line >= 0) && (line < LinesTotal())) {
int lineStart = LineStart(line);
int length = Length();
for (int i = lineStart;i < length;i++) {
char ch = cb.CharAt(i);
if (ch == ' ')
indent++;
else if (ch == '\t')
indent = NextTab(indent, tabInChars);
else
return indent;
}
}
return indent;
}
void Document::SetLineIndentation(int line, int indent) {
int indentOfLine = GetLineIndentation(line);
if (indent < 0)
indent = 0;
if (indent != indentOfLine) {
char linebuf[1000];
CreateIndentation(linebuf, sizeof(linebuf), indent, tabInChars, !useTabs);
int thisLineStart = LineStart(line);
int indentPos = GetLineIndentPosition(line);
DeleteChars(thisLineStart, indentPos - thisLineStart);
InsertString(thisLineStart, linebuf);
}
}
int Document::GetLineIndentPosition(int line) {
if (line < 0)
return 0;
int pos = LineStart(line);
int length = Length();
while ((pos < length) && isindentchar(cb.CharAt(pos))) {
pos++;
}
return pos;
}
int Document::GetColumn(int pos) {
int column = 0;
int line = LineFromPosition(pos);
if ((line >= 0) && (line < LinesTotal())) {
for (int i = LineStart(line);i < pos;) {
char ch = cb.CharAt(i);
if (ch == '\t') {
column = NextTab(column, tabInChars);
i++;
} else if (ch == '\r') {
return column;
} else if (ch == '\n') {
return column;
} else {
column++;
i = MovePositionOutsideChar(i + 1, 1);
}
}
}
return column;
}
int Document::FindColumn(int line, int column) {
int position = LineStart(line);
int columnCurrent = 0;
if ((line >= 0) && (line < LinesTotal())) {
while (columnCurrent < column) {
char ch = cb.CharAt(position);
if (ch == '\t') {
columnCurrent = NextTab(columnCurrent, tabInChars);
position++;
} else if (ch == '\r') {
return position;
} else if (ch == '\n') {
return position;
} else {
columnCurrent++;
position = MovePositionOutsideChar(position + 1, 1);
}
}
}
return position;
}
void Document::Indent(bool forwards, int lineBottom, int lineTop) {
// Dedent - suck white space off the front of the line to dedent by equivalent of a tab
for (int line = lineBottom; line >= lineTop; line--) {
int indentOfLine = GetLineIndentation(line);
if (forwards)
SetLineIndentation(line, indentOfLine + IndentSize());
else
SetLineIndentation(line, indentOfLine - IndentSize());
}
}
void Document::ConvertLineEnds(int eolModeSet) {
BeginUndoAction();
for (int pos = 0; pos < Length(); pos++) {
if (cb.CharAt(pos) == '\r') {
if (cb.CharAt(pos + 1) == '\n') {
if (eolModeSet != SC_EOL_CRLF) {
DeleteChars(pos, 2);
if (eolModeSet == SC_EOL_CR)
InsertString(pos, "\r", 1);
else
InsertString(pos, "\n", 1);
} else {
pos++;
}
} else {
if (eolModeSet != SC_EOL_CR) {
DeleteChars(pos, 1);
if (eolModeSet == SC_EOL_CRLF) {
InsertString(pos, "\r\n", 2);
pos++;
} else {
InsertString(pos, "\n", 1);
}
}
}
} else if (cb.CharAt(pos) == '\n') {
if (eolModeSet != SC_EOL_LF) {
DeleteChars(pos, 1);
if (eolModeSet == SC_EOL_CRLF) {
InsertString(pos, "\r\n", 2);
pos++;
} else {
InsertString(pos, "\r", 1);
}
}
}
}
EndUndoAction();
}
int Document::ParaDown(int pos) {
int line = LineFromPosition(pos);
while (line < LinesTotal() && LineStart(line) != LineEnd(line)) { // skip non-empty lines
line++;
}
while (line < LinesTotal() && LineStart(line) == LineEnd(line)) { // skip empty lines
line++;
}
if (line < LinesTotal())
return LineStart(line);
else // end of a document
return LineEnd(line-1);
}
int Document::ParaUp(int pos) {
int line = LineFromPosition(pos);
line--;
while (line >= 0 && LineStart(line) == LineEnd(line)) { // skip empty lines
line--;
}
while (line >= 0 && LineStart(line) != LineEnd(line)) { // skip non-empty lines
line--;
}
line++;
return LineStart(line);
}
Document::charClassification Document::WordCharClass(unsigned char ch) {
if ((SC_CP_UTF8 == dbcsCodePage) && (ch >= 0x80))
return ccWord;
return charClass[ch];
}
/**
* Used by commmands that want to select whole words.
* Finds the start of word at pos when delta < 0 or the end of the word when delta >= 0.
*/
int Document::ExtendWordSelect(int pos, int delta, bool onlyWordCharacters) {
charClassification ccStart = ccWord;
if (delta < 0) {
if (!onlyWordCharacters)
ccStart = WordCharClass(cb.CharAt(pos-1));
while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart))
pos--;
} else {
if (!onlyWordCharacters)
ccStart = WordCharClass(cb.CharAt(pos));
while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart))
pos++;
}
return pos;
}
/**
* Find the start of the next word in either a forward (delta >= 0) or backwards direction
* (delta < 0).
* This is looking for a transition between character classes although there is also some
* additional movement to transit white space.
* Used by cursor movement by word commands.
*/
int Document::NextWordStart(int pos, int delta) {
if (delta < 0) {
while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccSpace))
pos--;
if (pos > 0) {
charClassification ccStart = WordCharClass(cb.CharAt(pos-1));
while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart)) {
pos--;
}
}
} else {
charClassification ccStart = WordCharClass(cb.CharAt(pos));
while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart))
pos++;
while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccSpace))
pos++;
}
return pos;
}
/**
* Check that the character at the given position is a word or punctuation character and that
* the previous character is of a different character class.
*/
bool Document::IsWordStartAt(int pos) {
if (pos > 0) {
charClassification ccPos = WordCharClass(CharAt(pos));
return (ccPos == ccWord || ccPos == ccPunctuation) &&
(ccPos != WordCharClass(CharAt(pos - 1)));
}
return true;
}
/**
* Check that the character at the given position is a word or punctuation character and that
* the next character is of a different character class.
*/
bool Document::IsWordEndAt(int pos) {
if (pos < Length() - 1) {
charClassification ccPrev = WordCharClass(CharAt(pos-1));
return (ccPrev == ccWord || ccPrev == ccPunctuation) &&
(ccPrev != WordCharClass(CharAt(pos)));
}
return true;
}
/**
* Check that the given range is has transitions between character classes at both
* ends and where the characters on the inside are word or punctuation characters.
*/
bool Document::IsWordAt(int start, int end) {
return IsWordStartAt(start) && IsWordEndAt(end);
}
// The comparison and case changing functions here assume ASCII
// or extended ASCII such as the normal Windows code page.
static inline char MakeUpperCase(char ch) {
if (ch < 'a' || ch > 'z')
return ch;
else
return static_cast<char>(ch - 'a' + 'A');
}
static inline char MakeLowerCase(char ch) {
if (ch < 'A' || ch > 'Z')
return ch;
else
return static_cast<char>(ch - 'A' + 'a');
}
// Define a way for the Regular Expression code to access the document
class DocumentIndexer : public CharacterIndexer {
Document *pdoc;
int end;
public:
DocumentIndexer(Document *pdoc_, int end_) :
pdoc(pdoc_), end(end_) {
}
virtual char CharAt(int index) {
if (index < 0 || index >= end)
return 0;
else
return pdoc->CharAt(index);
}
};
/**
* Find text in document, supporting both forward and backward
* searches (just pass minPos > maxPos to do a backward search)
* Has not been tested with backwards DBCS searches yet.
*/
long Document::FindText(int minPos, int maxPos, const char *s,
bool caseSensitive, bool word, bool wordStart, bool regExp, bool posix,
int *length) {
if (regExp) {
if (!pre)
pre = new RESearch();
if (!pre)
return -1;
int increment = (minPos <= maxPos) ? 1 : -1;
int startPos = minPos;
int endPos = maxPos;
// Range endpoints should not be inside DBCS characters, but just in case, move them.
startPos = MovePositionOutsideChar(startPos, 1, false);
endPos = MovePositionOutsideChar(endPos, 1, false);
const char *errmsg = pre->Compile(s, *length, caseSensitive, posix);
if (errmsg) {
return -1;
}
// Find a variable in a property file: \$(\([A-Za-z0-9_.]+\))
// Replace first '.' with '-' in each property file variable reference:
// Search: \$(\([A-Za-z0-9_-]+\)\.\([A-Za-z0-9_.]+\))
// Replace: $(\1-\2)
int lineRangeStart = LineFromPosition(startPos);
int lineRangeEnd = LineFromPosition(endPos);
if ((increment == 1) &&
(startPos >= LineEnd(lineRangeStart)) &&
(lineRangeStart < lineRangeEnd)) {
// the start position is at end of line or between line end characters.
lineRangeStart++;
startPos = LineStart(lineRangeStart);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -