📄 textbuf.c
字号:
expText = expandTabs(insText, 0, buf->tabDist, buf->nullSubsChar, &expInsLen); XtFree(expText); outStr = XtMalloc(expReplLen + expInsLen + nLines * (column + insWidth + MAX_EXP_CHAR_LEN) + 1); /* Loop over all lines in the buffer between start and end inserting text at column, splitting tabs and adding padding appropriately */ outPtr = outStr; lineStart = start; insPtr = insText; while (True) { lineEnd = BufEndOfLine(buf, lineStart); line = BufGetRange(buf, lineStart, lineEnd); insLine = copyLine(insPtr, &len); insPtr += len; insertColInLine(line, insLine, column, insWidth, buf->tabDist, buf->useTabs, buf->nullSubsChar, outPtr, &len, &endOffset); XtFree(line); XtFree(insLine);#if 0 /* Earlier comments claimed that trailing whitespace could multiply on the ends of lines, but insertColInLine looks like it should never add space unnecessarily, and this trimming interfered with paragraph filling, so lets see if it works without it. MWE */ { char *c; for (c=outPtr+len-1; c>outPtr && (*c == ' ' || *c == '\t'); c--) len--; }#endif outPtr += len; *outPtr++ = '\n'; lineStart = lineEnd < buf->length ? lineEnd + 1 : buf->length; if (*insPtr == '\0') break; insPtr++; } if (outPtr != outStr) outPtr--; /* trim back off extra newline */ *outPtr = '\0'; /* replace the text between start and end with the new stuff */ delete(buf, start, end); insert(buf, start, outStr); *nInserted = outPtr - outStr; *nDeleted = end - start; *endPos = start + (outPtr - outStr) - len + endOffset; XtFree(outStr);}/*** Delete a rectangle of text without calling the modify callbacks. Returns** the number of characters replacing those between start and end. Note that** in some pathological cases, deleting can actually increase the size of** the buffer because of tab expansions. "endPos" returns the buffer position** of the point in the last line where the text was removed (as a hint for** routines which need to position the cursor after a delete operation)*/static void deleteRect(textBuffer *buf, int start, int end, int rectStart, int rectEnd, int *replaceLen, int *endPos){ int nLines, lineStart, lineEnd, len, endOffset; char *outStr, *outPtr, *line, *text, *expText; /* allocate a buffer for the replacement string large enough to hold possibly expanded tabs as well as an additional MAX_EXP_CHAR_LEN * 2 characters per line for padding where tabs and control characters cross the edges of the selection */ start = BufStartOfLine(buf, start); end = BufEndOfLine(buf, end); nLines = BufCountLines(buf, start, end) + 1; text = BufGetRange(buf, start, end); expText = expandTabs(text, 0, buf->tabDist, buf->nullSubsChar, &len); XtFree(text); XtFree(expText); outStr = XtMalloc(len + nLines * MAX_EXP_CHAR_LEN * 2 + 1); /* loop over all lines in the buffer between start and end removing the text between rectStart and rectEnd and padding appropriately */ lineStart = start; outPtr = outStr; while (lineStart <= buf->length && lineStart <= end) { lineEnd = BufEndOfLine(buf, lineStart); line = BufGetRange(buf, lineStart, lineEnd); deleteRectFromLine(line, rectStart, rectEnd, buf->tabDist, buf->useTabs, buf->nullSubsChar, outPtr, &len, &endOffset); XtFree(line); outPtr += len; *outPtr++ = '\n'; lineStart = lineEnd + 1; } if (outPtr != outStr) outPtr--; /* trim back off extra newline */ *outPtr = '\0'; /* replace the text between start and end with the newly created string */ delete(buf, start, end); insert(buf, start, outStr); *replaceLen = outPtr - outStr; *endPos = start + (outPtr - outStr) - len + endOffset; XtFree(outStr);}/*** Overlay a rectangular area of text without calling the modify callbacks.** "nDeleted" and "nInserted" return the number of characters deleted and** inserted beginning at the start of the line containing "startPos".** "endPos" returns buffer position of the lower left edge of the inserted** column (as a hint for routines which need to set a cursor position).*/static void overlayRect(textBuffer *buf, int startPos, int rectStart, int rectEnd, const char *insText, int *nDeleted, int *nInserted, int *endPos){ int nLines, start, end, lineStart, lineEnd; int expInsLen, len, endOffset; char *c, *outStr, *outPtr, *line, *expText, *insLine; const char *insPtr; /* Allocate a buffer for the replacement string large enough to hold possibly expanded tabs in the inserted text, as well as per line: 1) an additional 2*MAX_EXP_CHAR_LEN characters for padding where tabs and control characters cross the column of the selection, 2) up to "column" additional spaces per line for padding out to the position of "column", 3) padding up to the width of the inserted text if that must be padded to align the text beyond the inserted column. (Space for additional newlines if the inserted text extends beyond the end of the buffer is counted with the length of insText) */ start = BufStartOfLine(buf, startPos); nLines = countLines(insText) + 1; end = BufEndOfLine(buf, BufCountForwardNLines(buf, start, nLines-1)); expText = expandTabs(insText, 0, buf->tabDist, buf->nullSubsChar, &expInsLen); XtFree(expText); outStr = XtMalloc(end-start + expInsLen + nLines * (rectEnd + MAX_EXP_CHAR_LEN) + 1); /* Loop over all lines in the buffer between start and end overlaying the text between rectStart and rectEnd and padding appropriately. Trim trailing space from line (whitespace at the ends of lines otherwise tends to multiply, since additional padding is added to maintain it */ outPtr = outStr; lineStart = start; insPtr = insText; while (True) { lineEnd = BufEndOfLine(buf, lineStart); line = BufGetRange(buf, lineStart, lineEnd); insLine = copyLine(insPtr, &len); insPtr += len; overlayRectInLine(line, insLine, rectStart, rectEnd, buf->tabDist, buf->useTabs, buf->nullSubsChar, outPtr, &len, &endOffset); XtFree(line); XtFree(insLine); for (c=outPtr+len-1; c>outPtr && (*c == ' ' || *c == '\t'); c--) len--; outPtr += len; *outPtr++ = '\n'; lineStart = lineEnd < buf->length ? lineEnd + 1 : buf->length; if (*insPtr == '\0') break; insPtr++; } if (outPtr != outStr) outPtr--; /* trim back off extra newline */ *outPtr = '\0'; /* replace the text between start and end with the new stuff */ delete(buf, start, end); insert(buf, start, outStr); *nInserted = outPtr - outStr; *nDeleted = end - start; *endPos = start + (outPtr - outStr) - len + endOffset; XtFree(outStr);}/*** Insert characters from single-line string "insLine" in single-line string** "line" at "column", leaving "insWidth" space before continuing line.** "outLen" returns the number of characters written to "outStr", "endOffset"** returns the number of characters from the beginning of the string to** the right edge of the inserted text (as a hint for routines which need** to position the cursor).*/static void insertColInLine(const char *line, const char *insLine, int column, int insWidth, int tabDist, int useTabs, char nullSubsChar, char *outStr, int *outLen, int *endOffset){ char *c, *outPtr, *retabbedStr; const char *linePtr; int indent, toIndent, len, postColIndent; /* copy the line up to "column" */ outPtr = outStr; indent = 0; for (linePtr=line; *linePtr!='\0'; linePtr++) { len = BufCharWidth(*linePtr, indent, tabDist, nullSubsChar); if (indent + len > column) break; indent += len; *outPtr++ = *linePtr; } /* If "column" falls in the middle of a character, and the character is a tab, leave it off and leave the indent short and it will get padded later. If it's a control character, insert it and adjust indent accordingly. */ if (indent < column && *linePtr != '\0') { postColIndent = indent + len; if (*linePtr == '\t') linePtr++; else { *outPtr++ = *linePtr++; indent += len; } } else postColIndent = indent; /* If there's no text after the column and no text to insert, that's all */ if (*insLine == '\0' && *linePtr == '\0') { *outLen = *endOffset = outPtr - outStr; return; } /* pad out to column if text is too short */ if (indent < column) { addPadding(outPtr, indent, column, tabDist, useTabs, nullSubsChar,&len); outPtr += len; indent = column; } /* Copy the text from "insLine" (if any), recalculating the tabs as if the inserted string began at column 0 to its new column destination */ if (*insLine != '\0') { retabbedStr = realignTabs(insLine, 0, indent, tabDist, useTabs, nullSubsChar, &len); for (c=retabbedStr; *c!='\0'; c++) { *outPtr++ = *c; len = BufCharWidth(*c, indent, tabDist, nullSubsChar); indent += len; } XtFree(retabbedStr); } /* If the original line did not extend past "column", that's all */ if (*linePtr == '\0') { *outLen = *endOffset = outPtr - outStr; return; } /* Pad out to column + width of inserted text + (additional original offset due to non-breaking character at column) */ toIndent = column + insWidth + postColIndent-column; addPadding(outPtr, indent, toIndent, tabDist, useTabs, nullSubsChar, &len); outPtr += len; indent = toIndent; /* realign tabs for text beyond "column" and write it out */ retabbedStr = realignTabs(linePtr, postColIndent, indent, tabDist, useTabs, nullSubsChar, &len); strcpy(outPtr, retabbedStr); XtFree(retabbedStr); *endOffset = outPtr - outStr; *outLen = (outPtr - outStr) + len;}/*** Remove characters in single-line string "line" between displayed positions** "rectStart" and "rectEnd", and write the result to "outStr", which is** assumed to be large enough to hold the returned string. Note that in** certain cases, it is possible for the string to get longer due to** expansion of tabs. "endOffset" returns the number of characters from** the beginning of the string to the point where the characters were** deleted (as a hint for routines which need to position the cursor).*/static void deleteRectFromLine(const char *line, int rectStart, int rectEnd, int tabDist, int useTabs, char nullSubsChar, char *outStr, int *outLen, int *endOffset){ int indent, preRectIndent, postRectIndent, len; const char *c; char *outPtr; char *retabbedStr; /* copy the line up to rectStart */ outPtr = outStr; indent = 0; for (c=line; *c!='\0'; c++) { if (indent > rectStart) break; len = BufCharWidth(*c, indent, tabDist, nullSubsChar); if (indent + len > rectStart && (indent == rectStart || *c == '\t')) break; indent += len; *outPtr++ = *c; } preRectIndent = indent; /* skip the characters between rectStart and rectEnd */ for(; *c!='\0' && indent<rectEnd; c++) indent += BufCharWidth(*c, indent, tabDist, nullSubsChar); postRectIndent = indent; /* If the line ended before rectEnd, there's nothing more to do */ if (*c == '\0') { *outPtr = '\0'; *outLen = *endOffset = outPtr - outStr; return; } /* fill in any space left by removed tabs or control characters which straddled the boundaries */ indent = max(rectStart + postRectIndent-rectEnd, preRectIndent); addPadding(outPtr, preRectIndent, indent, tabDist, useTabs, nullSubsChar, &len); outPtr += len; /* Copy the rest of the line. If the indentation has changed, preserve the position of non-whitespace characters by converting tabs to spaces, then back to tabs with the correct offset */ retabbedStr = realignTabs(c, postRectIndent, indent, tabDist, useTabs, nullSubsChar, &len); strcpy(outPtr, retabbedStr); XtFree(retabbedStr); *endOffset = outPtr - outStr; *outLen = (outPtr - outStr) + len;}/*** Overlay characters from single-line string "insLine" on single-line string** "line" between displayed character offsets "rectStart" and "rectEnd".** "outLen" returns the number of characters written to "outStr", "endOffset"** returns the number of characters from the beginning of the string to** the right edge of the inserted text (as a hint for routines which need** to position the cursor).**** This code does not handle control characters very well, but oh well.*/static void overlayRectInLine(const char *line, const char *insLine, int rectStart, int rectEnd, int tabDist, int useTabs, char nullSubsChar, char *outStr, int *outLen, int *endOffset){ char *c, *outPtr, *retabbedStr; const char *linePtr; int inIndent, outIndent, len, postRectIndent; /* copy the line up to "rectStart" or just before the char that contains it*/ outPtr = outStr; inIndent = outIndent = 0; for (linePtr=line; *linePtr!='\0'; linePtr++) { len = BufCharWidth(*linePtr, inIndent, tabDist, nullSubsChar); if (inIndent + len > rectStart) break; inIndent += len; outIndent += len; *outPtr++ = *linePtr; } /* If "rectStart" falls in the middle of a character, and the character is a tab, leave it off and leave the outIndent short and it will get padded later. If it's a control character, insert it and adjust outIndent accordingly. */ if (inIndent < rectStart && *linePtr != '\0') { if (*linePtr == '\t') { /* Skip past the tab */ linePtr++; inIndent += len; } else { *outPtr++ = *linePtr++; outIndent += len; inIndent += len; } } /* skip the characters between rectStart and rectEnd */ for(; *linePtr!='\0' && inIndent < rectEnd; linePtr++) inIndent += BufCharWidth(*linePtr, inIndent, tabDist, nullSubsChar); postRectIndent = inIndent; /* After this inIndent is dead and
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -