1154 lines
34 KiB
C++
1154 lines
34 KiB
C++
|
/* e_search.cpp
|
||
|
*
|
||
|
* Copyright (c) 1994-1996, Marko Macek
|
||
|
*
|
||
|
* You may distribute under the terms of either the GNU General Public
|
||
|
* License or the Artistic License, as specified in the README file.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "fte.h"
|
||
|
|
||
|
int ParseSearchOption(int replace, char c, unsigned long &opt) {
|
||
|
switch (c) {
|
||
|
case 'a': opt |= SEARCH_ALL; break; // search all occurances
|
||
|
case 'b': opt |= SEARCH_BLOCK; break; // search in block only
|
||
|
case 'c': opt &= ~SEARCH_NEXT; break; // search from current position
|
||
|
case 'd': opt |= SEARCH_DELETE; break; // delete found line
|
||
|
case 'g': opt |= SEARCH_GLOBAL; break; // search globally
|
||
|
case 'i': opt |= SEARCH_NCASE; break; // don't match case
|
||
|
case 'j': opt |= SEARCH_JOIN; break; // join found line
|
||
|
case 'r': opt |= SEARCH_BACK; break; // search reverse
|
||
|
case 'w': opt |= SEARCH_WORDBEG | SEARCH_WORDEND; break;
|
||
|
case '<': opt |= SEARCH_WORDBEG; break;
|
||
|
case '>': opt |= SEARCH_WORDEND; break;
|
||
|
case 'x': opt |= SEARCH_RE; break; // search using regexps
|
||
|
default:
|
||
|
if (!replace) return 0;
|
||
|
switch (c) {
|
||
|
case 'n': opt |= SEARCH_NASK; break; // don't ask before replacing
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int UnquoteString(char *str) {
|
||
|
char *s, *d;
|
||
|
|
||
|
s = str;
|
||
|
d = str;
|
||
|
while (*s) {
|
||
|
if (*s == '\\') {
|
||
|
s++;
|
||
|
if (*s == 0) return 0;
|
||
|
}
|
||
|
*d++ = *s++;
|
||
|
}
|
||
|
*d = 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int ParseSearchOptions(int replace, const char *str, unsigned long &Options) {
|
||
|
const char *p = str;
|
||
|
|
||
|
Options = SEARCH_NEXT;
|
||
|
while (*p) {
|
||
|
if (ParseSearchOption(replace, *p, Options) == 0) return 0;
|
||
|
p++;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int ParseSearchReplace(EBuffer *B, const char *str, int replace, SearchReplaceOptions &opt) {
|
||
|
int where = 0;
|
||
|
int len = 0;
|
||
|
const char *p = str;
|
||
|
|
||
|
memset((void *)&opt, 0, sizeof(opt));
|
||
|
if (p == 0) return 0;
|
||
|
if (replace) {
|
||
|
if (ParseSearchOptions(replace, BFS(B, BFS_DefFindReplaceOpt), opt.Options) == 0) return 0;
|
||
|
opt.Options |= SEARCH_REPLACE;
|
||
|
} else {
|
||
|
if (ParseSearchOptions(replace, BFS(B, BFS_DefFindOpt), opt.Options) == 0) return 0;
|
||
|
}
|
||
|
|
||
|
while (*p) {
|
||
|
switch (*p) {
|
||
|
case '\\':
|
||
|
if (where == 0) {
|
||
|
opt.strSearch[len++] = *p++;
|
||
|
if (*p == 0) return 0;
|
||
|
opt.strSearch[len++] = *p++;
|
||
|
} else if (where == 1) {
|
||
|
opt.strReplace[len++] = *p++;
|
||
|
if (*p == 0) return 0;
|
||
|
opt.strReplace[len++] = *p++;
|
||
|
} else
|
||
|
return 0;
|
||
|
break;
|
||
|
case '/':
|
||
|
where++;
|
||
|
if (!replace && where == 1) where++;
|
||
|
if (where == 2)
|
||
|
opt.Options = SEARCH_NEXT;
|
||
|
if (where > 2) return 0;
|
||
|
len = 0;
|
||
|
p++;
|
||
|
break;
|
||
|
default:
|
||
|
if (where == 0)
|
||
|
opt.strSearch[len++] = *p++;
|
||
|
else if (where == 1)
|
||
|
opt.strReplace[len++] = *p++;
|
||
|
else {
|
||
|
char c = *p;
|
||
|
|
||
|
p++;
|
||
|
if (ParseSearchOption(replace, c, opt.Options) == 0) return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (opt.Options & SEARCH_RE);
|
||
|
else {
|
||
|
if (UnquoteString(opt.strSearch) == 0) return 0;
|
||
|
if (opt.Options & SEARCH_REPLACE)
|
||
|
if (UnquoteString(opt.strReplace) == 0) return 0;
|
||
|
}
|
||
|
|
||
|
opt.ok = 1;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int EBuffer::FindStr(char *Data, int Len, int Options) {
|
||
|
int LLen, Start, End;
|
||
|
int C, L;
|
||
|
PELine X;
|
||
|
char *P;
|
||
|
|
||
|
if (Options & SEARCH_RE)
|
||
|
return 0;
|
||
|
if (Len <= 0)
|
||
|
return 0;
|
||
|
|
||
|
if (Options & SEARCH_NOPOS) {
|
||
|
C = Match.Col;
|
||
|
L = Match.Row;
|
||
|
} else {
|
||
|
C = CP.Col;
|
||
|
L = VToR(CP.Row);
|
||
|
}
|
||
|
if (Match.Row != -1)
|
||
|
Draw(Match.Row, Match.Row);
|
||
|
Match.Row = -1;
|
||
|
Match.Col = -1;
|
||
|
X = RLine(L);
|
||
|
C = CharOffset(X, C);
|
||
|
|
||
|
if (Options & SEARCH_NEXT) {
|
||
|
int CC = MatchCount ? MatchCount : 1;
|
||
|
|
||
|
if (Options & SEARCH_BACK) {
|
||
|
C -= CC;
|
||
|
if (C < 0) {
|
||
|
if (L == 0) return 0;
|
||
|
L--;
|
||
|
X = RLine(L);
|
||
|
C = X->Count;
|
||
|
}
|
||
|
} else {
|
||
|
C += CC;
|
||
|
if (C >= X->Count) {
|
||
|
C = 0;
|
||
|
L++;
|
||
|
if (L == RCount) return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
MatchLen = 0;
|
||
|
MatchCount = 0;
|
||
|
|
||
|
if (Options & SEARCH_BLOCK) {
|
||
|
if (Options & SEARCH_BACK) {
|
||
|
if (BlockMode == bmStream) {
|
||
|
if (L > BE.Row) {
|
||
|
L = BE.Row;
|
||
|
C = BE.Col;
|
||
|
}
|
||
|
if (L == BE.Row && C > BE.Col)
|
||
|
C = BE.Col;
|
||
|
} else {
|
||
|
if (L >= BE.Row && BE.Row > 0) {
|
||
|
L = BE.Row - 1;
|
||
|
C = RLine(L)->Count;
|
||
|
}
|
||
|
if (BlockMode == bmColumn)
|
||
|
if (L == BE.Row - 1 && C >= BE.Col)
|
||
|
C = BE.Col;
|
||
|
}
|
||
|
} else {
|
||
|
if (L < BB.Row) {
|
||
|
L = BB.Row;
|
||
|
C = 0;
|
||
|
}
|
||
|
if (L == BB.Row && C < BB.Col)
|
||
|
C = BB.Col;
|
||
|
}
|
||
|
}
|
||
|
while (1) {
|
||
|
if (Options & SEARCH_BLOCK) {
|
||
|
if (BlockMode == bmStream) {
|
||
|
if (L > BE.Row || L < BB.Row) break;
|
||
|
} else
|
||
|
if (L >= BE.Row || L < BB.Row) break;
|
||
|
} else
|
||
|
if (L >= RCount || L < 0) break;
|
||
|
|
||
|
X = RLine(L);
|
||
|
|
||
|
LLen = X->Count;
|
||
|
P = X->Chars;
|
||
|
Start = 0;
|
||
|
End = LLen;
|
||
|
|
||
|
if (Options & SEARCH_BLOCK) {
|
||
|
if (BlockMode == bmColumn) {
|
||
|
Start = CharOffset(X, BB.Col);
|
||
|
End = CharOffset(X, BE.Col);
|
||
|
} else if (BlockMode == bmStream) {
|
||
|
if (L == BB.Row)
|
||
|
Start = CharOffset(X, BB.Col);
|
||
|
if (L == BE.Row)
|
||
|
End = CharOffset(X, BE.Col);
|
||
|
}
|
||
|
}
|
||
|
if (Options & SEARCH_BACK) {
|
||
|
if (C >= End - Len)
|
||
|
C = End - Len;
|
||
|
} else {
|
||
|
if (C < Start)
|
||
|
C = Start;
|
||
|
}
|
||
|
|
||
|
while (((!(Options & SEARCH_BACK)) && (C <= End - Len)) || ((Options & SEARCH_BACK) && (C >= Start))) {
|
||
|
if ((!(Options & SEARCH_WORDBEG)
|
||
|
|| (C == 0)
|
||
|
|| (WGETBIT(Flags.WordChars, P[C - 1]) == 0))
|
||
|
&&
|
||
|
(!(Options & SEARCH_WORDEND)
|
||
|
|| (C + Len >= End)
|
||
|
|| (WGETBIT(Flags.WordChars, P[C + Len]) == 0))
|
||
|
&&
|
||
|
((!(Options & SEARCH_NCASE)
|
||
|
&& (P[C] == Data[0])
|
||
|
&& (memcmp(P + C, Data, Len) == 0))
|
||
|
||
|
||
|
((Options & SEARCH_NCASE)
|
||
|
&& (toupper(P[C]) == toupper(Data[0]))
|
||
|
&& (strnicmp(P + C, Data, Len) == 0))) /* && BOL | EOL */
|
||
|
)
|
||
|
{
|
||
|
Match.Col = ScreenPos(X, C);
|
||
|
Match.Row = L;
|
||
|
MatchCount = Len;
|
||
|
MatchLen = ScreenPos(X, C + Len) - Match.Col;
|
||
|
if (!(Options & SEARCH_NOPOS)) {
|
||
|
if (Options & SEARCH_CENTER)
|
||
|
CenterPosR(Match.Col, Match.Row);
|
||
|
else
|
||
|
SetPosR(Match.Col, Match.Row);
|
||
|
}
|
||
|
Draw(L, L);
|
||
|
return 1;
|
||
|
}
|
||
|
if (Options & SEARCH_BACK) C--; else C++;
|
||
|
}
|
||
|
if (Options & SEARCH_BACK) {
|
||
|
L--;
|
||
|
if (L >= 0)
|
||
|
C = RLine(L)->Count;
|
||
|
} else {
|
||
|
C = 0;
|
||
|
L++;
|
||
|
}
|
||
|
}
|
||
|
//SetPos(OC, OL);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int EBuffer::FindRx(RxNode *Rx, int Options) {
|
||
|
int LLen, Start, End;
|
||
|
int C, L;
|
||
|
char *P;
|
||
|
PELine X;
|
||
|
RxMatchRes b;
|
||
|
|
||
|
if (!(Options & SEARCH_RE))
|
||
|
return 0;
|
||
|
if (Options & SEARCH_BACK) { // not supported
|
||
|
View->MView->Win->Choice(GPC_ERROR, "FindRx", 1, "O&K", "Reverse regexp search not supported.");
|
||
|
return 0;
|
||
|
}
|
||
|
if (Rx == 0)
|
||
|
return 0;
|
||
|
|
||
|
if (Match.Row != -1)
|
||
|
Draw(Match.Row, Match.Row);
|
||
|
Match.Row = -1;
|
||
|
Match.Col = -1;
|
||
|
|
||
|
C = CP.Col;
|
||
|
L = VToR(CP.Row);
|
||
|
X = RLine(L);
|
||
|
C = CharOffset(X, C);
|
||
|
|
||
|
if (Options & SEARCH_NEXT) {
|
||
|
int CC = MatchCount ? MatchCount : 1;
|
||
|
|
||
|
if (Options & SEARCH_BACK) {
|
||
|
C -= CC;
|
||
|
if (Options & SEARCH_BLOCK) {
|
||
|
if (C < BB.Col && L == BB.Row)
|
||
|
return 0;
|
||
|
L--;
|
||
|
X = RLine(L);
|
||
|
C = X->Count;
|
||
|
if (BlockMode == bmColumn)
|
||
|
if (BE.Col < C)
|
||
|
C = BE.Col;
|
||
|
} else {
|
||
|
if (C < 0 && L == 0)
|
||
|
return 0;
|
||
|
L--;
|
||
|
X = RLine(L);
|
||
|
C = X->Count;
|
||
|
}
|
||
|
} else {
|
||
|
C += CC;
|
||
|
if (Options & SEARCH_BLOCK) {
|
||
|
if (BlockMode == bmStream || BlockMode == bmLine) {
|
||
|
if (C >= X->Count) {
|
||
|
C = 0;
|
||
|
L++;
|
||
|
if (BlockMode == bmLine) {
|
||
|
if (L == BE.Row) return 0;
|
||
|
} else
|
||
|
if (L == BE.Row && (C >= BE.Col || C >= X->Count))
|
||
|
return 0;
|
||
|
}
|
||
|
} else if (BlockMode == bmColumn) {
|
||
|
if (C >= X->Count || C >= BE.Col) {
|
||
|
C = BB.Col;
|
||
|
L++;
|
||
|
if (L == BE.Row) return 0;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
if (C >= X->Count) {
|
||
|
C = 0;
|
||
|
L++;
|
||
|
if (L == RCount) return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
MatchLen = 0;
|
||
|
MatchCount = 0;
|
||
|
|
||
|
if (Options & SEARCH_BLOCK) {
|
||
|
if (Options & SEARCH_BACK) {
|
||
|
if (BlockMode == bmStream) {
|
||
|
if (L > BE.Row) {
|
||
|
L = BE.Row;
|
||
|
C = BE.Col;
|
||
|
}
|
||
|
if (L == BE.Row && C > BE.Col)
|
||
|
C = BE.Col;
|
||
|
} else {
|
||
|
if (L >= BE.Row && BE.Row > 0) {
|
||
|
L = BE.Row - 1;
|
||
|
C = RLine(L)->Count;
|
||
|
}
|
||
|
if (BlockMode == bmColumn)
|
||
|
if (L == BE.Row - 1 && C >= BE.Col)
|
||
|
C = BE.Col;
|
||
|
}
|
||
|
} else {
|
||
|
if (L < BB.Row) {
|
||
|
L = BB.Row;
|
||
|
C = 0;
|
||
|
}
|
||
|
if (L == BB.Row && C < BB.Col)
|
||
|
C = BB.Col;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while (1) {
|
||
|
if (Options & SEARCH_BLOCK) {
|
||
|
if (BlockMode == bmStream) {
|
||
|
if (L > BE.Row || L < BB.Row) break;
|
||
|
} else
|
||
|
if (L >= BE.Row || L < BB.Row) break;
|
||
|
} else
|
||
|
if (L >= RCount || L < 0) break;
|
||
|
|
||
|
X = RLine(L);
|
||
|
LLen = X->Count;
|
||
|
P = X->Chars;
|
||
|
Start = 0;
|
||
|
End = LLen;
|
||
|
|
||
|
if (Options & SEARCH_BLOCK) {
|
||
|
if (BlockMode == bmColumn) {
|
||
|
Start = CharOffset(X, BB.Col);
|
||
|
End = CharOffset(X, BE.Col);
|
||
|
} else if (BlockMode == bmStream) {
|
||
|
if (L == BB.Row)
|
||
|
Start = CharOffset(X, BB.Col);
|
||
|
if (L == BE.Row)
|
||
|
End = CharOffset(X, BE.Col);
|
||
|
}
|
||
|
if (End > LLen)
|
||
|
End = LLen;
|
||
|
}
|
||
|
if (Options & SEARCH_BACK) {
|
||
|
if (C >= End)
|
||
|
C = End;
|
||
|
} else {
|
||
|
if (C < Start)
|
||
|
C = Start;
|
||
|
}
|
||
|
|
||
|
if (Start <= End) {
|
||
|
if (RxExec(Rx, P + Start, End - Start, P + C, &b, (Options & SEARCH_NCASE) ? 0 : RX_CASE) == 1) {
|
||
|
C = ScreenPos(X, b.Open[0] + Start);
|
||
|
Match.Col = C;
|
||
|
Match.Row = L;
|
||
|
MatchCount = b.Close[0] - b.Open[0];
|
||
|
MatchLen = ScreenPos(X, b.Close[0] + Start) - C;
|
||
|
for (int mm = 0; mm < NSEXPS; mm++) {
|
||
|
b.Open[mm] += Start;
|
||
|
b.Close[mm] += Start;
|
||
|
}
|
||
|
MatchRes = b;
|
||
|
if (!(Options & SEARCH_NOPOS)) {
|
||
|
if (Options & SEARCH_CENTER)
|
||
|
CenterPosR(C, L);
|
||
|
else
|
||
|
SetPosR(C, L);
|
||
|
}
|
||
|
Draw(L, L);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
C = 0;
|
||
|
L++;
|
||
|
}
|
||
|
//SetPos(OC, OL);
|
||
|
return 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
int EBuffer::Find(SearchReplaceOptions &opt) {
|
||
|
int slen = strlen(opt.strSearch);
|
||
|
int Options = opt.Options;
|
||
|
int rlen = strlen(opt.strReplace);
|
||
|
RxNode *R = NULL;
|
||
|
|
||
|
opt.resCount = -1;
|
||
|
|
||
|
if (slen == 0) return 0;
|
||
|
if (Options & SEARCH_BLOCK) {
|
||
|
if (CheckBlock() == 0) return 0;
|
||
|
}
|
||
|
if (Options & SEARCH_RE) {
|
||
|
R = RxCompile(opt.strSearch);
|
||
|
if (R == 0) {
|
||
|
View->MView->Win->Choice(GPC_ERROR, "Find", 1, "O&K", "Invalid regular expression.");
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
if (Options & SEARCH_GLOBAL) {
|
||
|
if (Options & SEARCH_BLOCK) {
|
||
|
if (Options & SEARCH_BACK) {
|
||
|
if (SetPosR(BE.Col, BE.Row) == 0) goto error;
|
||
|
} else {
|
||
|
if (SetPosR(BB.Col, BB.Row) == 0) goto error;
|
||
|
}
|
||
|
} else {
|
||
|
if (Options & SEARCH_BACK) {
|
||
|
if (RCount < 1) goto error;
|
||
|
if (SetPosR(LineLen(RCount - 1), RCount - 1) == 0) goto error;
|
||
|
} else {
|
||
|
if (SetPosR(0, 0) == 0) goto error;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
opt.resCount = 0;
|
||
|
while (1) {
|
||
|
if (Options & SEARCH_RE) {
|
||
|
if (FindRx(R, Options) == 0) goto end;
|
||
|
} else {
|
||
|
if (FindStr(opt.strSearch, slen, Options) == 0) goto end;
|
||
|
}
|
||
|
opt.resCount++;
|
||
|
|
||
|
if (opt.Options & SEARCH_REPLACE) {
|
||
|
char ask = 'A';
|
||
|
|
||
|
if (!(Options & SEARCH_NASK)) {
|
||
|
char ch;
|
||
|
|
||
|
while (1) {
|
||
|
Draw(VToR(CP.Row), 1);
|
||
|
Redraw();
|
||
|
switch (View->MView->Win->Choice(0, "Replace",
|
||
|
5,
|
||
|
"&Yes",
|
||
|
"&All",
|
||
|
"&Once",
|
||
|
"&Skip",
|
||
|
"&Cancel",
|
||
|
"Replace with %s?", opt.strReplace))
|
||
|
{
|
||
|
case 0: ch = 'Y'; break;
|
||
|
case 1: ch = 'A'; break;
|
||
|
case 2: ch = 'O'; break;
|
||
|
case 3: ch = 'N'; break;
|
||
|
case 4:
|
||
|
case -1:
|
||
|
default:
|
||
|
ch = 'Q'; break;
|
||
|
}
|
||
|
if (ch == 'Y') { ask = 'Y'; goto ok_rep; }
|
||
|
if (ch == 'N') { ask = 'N'; goto ok_rep; }
|
||
|
if (ch == 'Q') { ask = 'Q'; goto ok_rep; }
|
||
|
if (ch == 'A') { ask = 'A'; goto ok_rep; }
|
||
|
if (ch == 'O') { ask = 'O'; goto ok_rep; }
|
||
|
}
|
||
|
ok_rep:
|
||
|
if (ask == 'N') goto try_join;
|
||
|
if (ask == 'Q') goto end;
|
||
|
if (ask == 'A') Options |= SEARCH_NASK;
|
||
|
}
|
||
|
|
||
|
if (Options & SEARCH_RE) {
|
||
|
PELine L = RLine(Match.Row);
|
||
|
int P, R;
|
||
|
char *PR = 0;
|
||
|
int LR = 0;
|
||
|
|
||
|
R = Match.Row;
|
||
|
P = Match.Col;
|
||
|
P = CharOffset(L, P);
|
||
|
|
||
|
if (0 == RxReplace(opt.strReplace, L->Chars, L->Count, MatchRes, &PR, &LR)) {
|
||
|
if (DelText(R, Match.Col, MatchLen) == 0) goto error;
|
||
|
if (PR && LR > 0)
|
||
|
if (InsText(R, Match.Col, LR, PR) == 0) goto error;
|
||
|
if (PR)
|
||
|
free(PR);
|
||
|
rlen = LR;
|
||
|
}
|
||
|
} else {
|
||
|
if (DelText(Match.Row, Match.Col, MatchLen) == 0) goto error;
|
||
|
if (InsText(Match.Row, Match.Col, rlen, opt.strReplace) == 0) goto error;
|
||
|
}
|
||
|
if (!(Options & SEARCH_BACK)) {
|
||
|
MatchLen = rlen;
|
||
|
MatchCount = rlen;
|
||
|
}
|
||
|
if (ask == 'O')
|
||
|
goto end;
|
||
|
}
|
||
|
try_join:
|
||
|
if (Options & SEARCH_JOIN) {
|
||
|
char ask = 'A';
|
||
|
|
||
|
if (!(Options & SEARCH_NASK)) {
|
||
|
char ch;
|
||
|
|
||
|
while (1) {
|
||
|
Draw(VToR(CP.Row), 1);
|
||
|
Redraw();
|
||
|
switch (View->MView->Win->Choice(0, "Join Line",
|
||
|
5,
|
||
|
"&Yes",
|
||
|
"&All",
|
||
|
"&Once",
|
||
|
"&Skip",
|
||
|
"&Cancel",
|
||
|
"Join lines %d and %d?", 1 + VToR(CP.Row), 1 + VToR(CP.Row) + 1))
|
||
|
{
|
||
|
case 0: ch = 'Y'; break;
|
||
|
case 1: ch = 'A'; break;
|
||
|
case 2: ch = 'O'; break;
|
||
|
case 3: ch = 'N'; break;
|
||
|
case 4:
|
||
|
case -1:
|
||
|
default:
|
||
|
ch = 'Q'; break;
|
||
|
}
|
||
|
if (ch == 'Y') { ask = 'Y'; goto ok_join; }
|
||
|
if (ch == 'N') { ask = 'N'; goto ok_join; }
|
||
|
if (ch == 'Q') { ask = 'Q'; goto ok_join; }
|
||
|
if (ch == 'A') { ask = 'A'; goto ok_join; }
|
||
|
if (ch == 'O') { ask = 'O'; goto ok_join; }
|
||
|
}
|
||
|
ok_join:
|
||
|
if (ask == 'N') goto try_delete;
|
||
|
if (ask == 'Q') goto end;
|
||
|
if (ask == 'A') Options |= SEARCH_NASK;
|
||
|
}
|
||
|
|
||
|
if (JoinLine(Match.Row, Match.Col) == 0) goto error;
|
||
|
|
||
|
if (ask == 'O')
|
||
|
goto end;
|
||
|
}
|
||
|
try_delete:
|
||
|
if (Options & SEARCH_DELETE) {
|
||
|
char ask = 'A';
|
||
|
|
||
|
if (!(Options & SEARCH_NASK)) {
|
||
|
char ch;
|
||
|
|
||
|
while (1) {
|
||
|
Draw(VToR(CP.Row), 1);
|
||
|
Redraw();
|
||
|
switch (View->MView->Win->Choice(0, "Delete Line",
|
||
|
5,
|
||
|
"&Yes",
|
||
|
"&All",
|
||
|
"&Once",
|
||
|
"&Skip",
|
||
|
"&Cancel",
|
||
|
"Delete line %d?", VToR(CP.Row)))
|
||
|
{
|
||
|
case 0: ch = 'Y'; break;
|
||
|
case 1: ch = 'A'; break;
|
||
|
case 2: ch = 'O'; break;
|
||
|
case 3: ch = 'N'; break;
|
||
|
case 4:
|
||
|
case -1:
|
||
|
default:
|
||
|
ch = 'Q'; break;
|
||
|
}
|
||
|
if (ch == 'Y') { ask = 'Y'; goto ok_delete; }
|
||
|
if (ch == 'N') { ask = 'N'; goto ok_delete; }
|
||
|
if (ch == 'Q') { ask = 'Q'; goto ok_delete; }
|
||
|
if (ch == 'A') { ask = 'A'; goto ok_delete; }
|
||
|
if (ch == 'O') { ask = 'O'; goto ok_delete; }
|
||
|
}
|
||
|
ok_delete:
|
||
|
if (ask == 'N') goto next;
|
||
|
if (ask == 'Q') goto end;
|
||
|
if (ask == 'A') Options |= SEARCH_NASK;
|
||
|
}
|
||
|
|
||
|
if (Match.Row == RCount - 1) {
|
||
|
if (DelText(Match.Row, 0, LineLen()) == 0) goto error;
|
||
|
} else
|
||
|
if (DelLine(Match.Row) == 0) goto error;
|
||
|
|
||
|
if (ask == 'O')
|
||
|
goto end;
|
||
|
if (!(Options & SEARCH_ALL))
|
||
|
break;
|
||
|
goto last;
|
||
|
}
|
||
|
next:
|
||
|
if (!(Options & SEARCH_ALL))
|
||
|
break;
|
||
|
Options |= SEARCH_NEXT;
|
||
|
last:
|
||
|
;
|
||
|
}
|
||
|
end:
|
||
|
// end of search
|
||
|
if (R)
|
||
|
RxFree(R);
|
||
|
|
||
|
if (Options & SEARCH_ALL)
|
||
|
Msg(S_INFO, "%d match(es) found.", opt.resCount);
|
||
|
else {
|
||
|
if (opt.resCount == 0) {
|
||
|
Msg(S_INFO, "[%s] not found", opt.strSearch);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
error:
|
||
|
|
||
|
if (R)
|
||
|
{
|
||
|
RxFree(R);
|
||
|
}
|
||
|
View->MView->Win->Choice(GPC_ERROR, "Find", 1, "O&K", "Error in search/replace.");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int EBuffer::CompleteWord() {
|
||
|
#ifdef CONFIG_I_COMPLETE
|
||
|
return View->MView->Win->ICompleteWord(View);
|
||
|
#else
|
||
|
PELine L = VLine(CP.Row), M;
|
||
|
int C, P, X, P1, N, xlen, XL;
|
||
|
EPoint O = CP;
|
||
|
|
||
|
if (CP.Col == 0) return 0;
|
||
|
|
||
|
if (SetPos(CP.Col, CP.Row) == 0) return 0;
|
||
|
|
||
|
C = CP.Col;
|
||
|
P = CharOffset(L, C);
|
||
|
P1 = P;
|
||
|
N = VToR(CP.Row);
|
||
|
|
||
|
if (P > L->Count) return 0;
|
||
|
|
||
|
if (P > 0) {
|
||
|
while ((P > 0) && ((ChClass(L->Chars[P - 1]) == 1) || (L->Chars[P - 1] == '_'))) P--;
|
||
|
if (P == P1) return 0;
|
||
|
xlen = P1 - P;
|
||
|
C = ScreenPos(L, P);
|
||
|
Match.Row = N;
|
||
|
Match.Col = C;
|
||
|
//if (SetPos(C, CP.Row) == 0) return 0;
|
||
|
//again:
|
||
|
if (FindStr(L->Chars + P, xlen, SEARCH_BACK | SEARCH_NEXT | SEARCH_NOPOS | SEARCH_WORDBEG) == 1) {
|
||
|
M = RLine(Match.Row);
|
||
|
X = CharOffset(M, Match.Col);
|
||
|
XL = X;
|
||
|
//if ((XL > 0) && ((ChClass(M->Chars[XL - 1]) == 1) || (M->Chars[XL - 1] == '_'))) goto again;
|
||
|
while ((XL < M->Count) && ((ChClass(M->Chars[XL]) == 1) || (M->Chars[XL] == '_'))) XL++;
|
||
|
{
|
||
|
char *pp = (char *)malloc(XL - X - xlen);
|
||
|
|
||
|
if (pp) {
|
||
|
memcpy(pp, M->Chars + X + xlen, XL - X - xlen);
|
||
|
|
||
|
if (InsText(N, O.Col,
|
||
|
XL - X - xlen,
|
||
|
pp,
|
||
|
1) == 0) return 0;
|
||
|
free(pp);
|
||
|
}
|
||
|
}
|
||
|
if (SetPos(O.Col + XL - X - xlen, O.Row) == 0) return 0;
|
||
|
Draw(Match.Row, Match.Row);
|
||
|
Match.Row = -1;
|
||
|
Match.Col = -1;
|
||
|
MatchLen = 0;
|
||
|
MatchCount = 0;
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
if (Match.Row != -1)
|
||
|
Draw(Match.Row, Match.Row);
|
||
|
Match.Col = Match.Row = -1;
|
||
|
MatchLen = 0;
|
||
|
MatchCount = 0;
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int EBuffer::Search(ExState &State, char *aString, int Options, int /*CanResume*/) {
|
||
|
char find[MAXSEARCH+1] = "";
|
||
|
int Case = BFI(this, BFI_MatchCase) ? 0 : SEARCH_NCASE;
|
||
|
int Next = 0;
|
||
|
int erc = 0;
|
||
|
//int Changed;
|
||
|
|
||
|
if (aString)
|
||
|
strcpy(find, aString);
|
||
|
else
|
||
|
if (State.GetStrParam(View, find, sizeof(find)) == 0)
|
||
|
if ((erc = View->MView->Win->GetStr("Find", sizeof(find), find, HIST_SEARCH)) == 0) return 0;
|
||
|
if (strlen(find) == 0) return 0;
|
||
|
|
||
|
if (erc == 2)
|
||
|
Case ^= SEARCH_NCASE;
|
||
|
|
||
|
//if (Changed == 0 && CanResume)
|
||
|
// Next |= SEARCH_NEXT;
|
||
|
|
||
|
LSearch.ok = 0;
|
||
|
strcpy(LSearch.strSearch, find);
|
||
|
LSearch.Options = Case | Next | (Options & ~SEARCH_NCASE);
|
||
|
LSearch.ok = 1;
|
||
|
if (Find(LSearch) == 0) return 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int EBuffer::SearchAgain(ExState &/*State*/, unsigned int Options) {
|
||
|
if (LSearch.ok == 0) return 0;
|
||
|
LSearch.Options |= SEARCH_NEXT;
|
||
|
if ((Options & SEARCH_BACK) != (LSearch.Options & SEARCH_BACK))
|
||
|
LSearch.Options ^= SEARCH_BACK;
|
||
|
if (Find(LSearch) == 0) return 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int EBuffer::SearchReplace(ExState &State, char *aString, char *aReplaceString, int Options) {
|
||
|
char find[MAXSEARCH+1] = "";
|
||
|
char replace[MAXSEARCH+1] = "";
|
||
|
int Case = BFI(this, BFI_MatchCase) ? 0 : SEARCH_NCASE;
|
||
|
|
||
|
if (aString)
|
||
|
strcpy(find, aString);
|
||
|
else
|
||
|
if (State.GetStrParam(View, find, sizeof(find)) == 0)
|
||
|
if (View->MView->Win->GetStr("Find", sizeof(find), find, HIST_SEARCH) == 0) return 0;
|
||
|
if (strlen(find) == 0) return 0;
|
||
|
if (aReplaceString)
|
||
|
strcpy(replace, aReplaceString);
|
||
|
else
|
||
|
if (State.GetStrParam(View, replace, sizeof(replace)) == 0)
|
||
|
if (View->MView->Win->GetStr("Replace", sizeof(replace), replace, HIST_SEARCH) == 0) return 0;
|
||
|
|
||
|
LSearch.ok = 0;
|
||
|
strcpy(LSearch.strSearch, find);
|
||
|
strcpy(LSearch.strReplace, replace);
|
||
|
LSearch.Options = Case | (Options & ~SEARCH_NCASE) | SEARCH_ALL | SEARCH_REPLACE;
|
||
|
LSearch.ok = 1;
|
||
|
if (Find(LSearch) == 0) return 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int EBuffer::Search(ExState &State) { return Search(State, 0, 0, 1); }
|
||
|
int EBuffer::SearchB(ExState &State) { return Search(State, 0, SEARCH_BACK, 1); }
|
||
|
int EBuffer::SearchRx(ExState &State) { return Search(State, 0, SEARCH_RE, 1); }
|
||
|
int EBuffer::SearchAgain(ExState &State) { return SearchAgain(State, 0); }
|
||
|
int EBuffer::SearchAgainB(ExState &State) { return SearchAgain(State, SEARCH_BACK); }
|
||
|
int EBuffer::SearchReplace(ExState &State) { return SearchReplace(State, 0, 0, 0); }
|
||
|
int EBuffer::SearchReplaceB(ExState &State) { return SearchReplace(State, 0, 0, SEARCH_BACK); }
|
||
|
int EBuffer::SearchReplaceRx(ExState &State) { return SearchReplace(State, 0, 0, SEARCH_RE); }
|
||
|
|
||
|
#ifdef CONFIG_OBJ_ROUTINE
|
||
|
int EBuffer::ScanForRoutines() {
|
||
|
RxNode *regx;
|
||
|
int line;
|
||
|
PELine L;
|
||
|
RxMatchRes res;
|
||
|
|
||
|
if (BFS(this, BFS_RoutineRegexp) == 0) {
|
||
|
View->MView->Win->Choice(GPC_ERROR, "Error", 1, "O&K", "No routine regexp.");
|
||
|
return 0;
|
||
|
}
|
||
|
regx = RxCompile(BFS(this, BFS_RoutineRegexp));
|
||
|
if (regx == 0) {
|
||
|
View->MView->Win->Choice(GPC_ERROR, "Error", 1, "O&K", "Failed to compile regexp '%s'", BFS(this, BFS_RoutineRegexp));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (rlst.Lines) {
|
||
|
free(rlst.Lines);
|
||
|
rlst.Lines = 0;
|
||
|
}
|
||
|
rlst.Lines = 0;
|
||
|
rlst.Count = 0;
|
||
|
|
||
|
Msg(S_BUSY, "Matching %s", BFS(this, BFS_RoutineRegexp));
|
||
|
for (line = 0; line < RCount; line++) {
|
||
|
L = RLine(line);
|
||
|
if (RxExec(regx, L->Chars, L->Count, L->Chars, &res) == 1) {
|
||
|
rlst.Count++;
|
||
|
rlst.Lines = (int *) realloc((void *) rlst.Lines, sizeof(int) * (rlst.Count | 0x1F));
|
||
|
rlst.Lines[rlst.Count - 1] = line;
|
||
|
Msg(S_BUSY, "Routines: %d", rlst.Count);
|
||
|
}
|
||
|
}
|
||
|
RxFree(regx);
|
||
|
return 1;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int EBuffer::ShowPosition() {
|
||
|
int CLine, NLines;
|
||
|
int CAct, NAct;
|
||
|
int CColumn, NColumns;
|
||
|
int CCharPos, NChars;
|
||
|
#ifdef HEAPWALK
|
||
|
unsigned long MemUsed = 0, MemFree = 0, BlkUsed = 0, BlkFree = 0, BigFree = 0, BigUsed = 0;
|
||
|
#endif
|
||
|
|
||
|
if (!View)
|
||
|
return 0;
|
||
|
|
||
|
CLine = CP.Row + 1;
|
||
|
NLines = VCount;
|
||
|
CAct = VToR(CP.Row) + 1;
|
||
|
NAct = RCount;
|
||
|
CColumn = CP.Col + 1;
|
||
|
NColumns = LineLen(CP.Row);
|
||
|
CCharPos = CharOffset(VLine(CP.Row), CP.Col) + 1;
|
||
|
NChars = VLine(CP.Row)->Count;
|
||
|
|
||
|
#ifdef HEAPWALK
|
||
|
if (_heapchk() != _HEAPOK) {
|
||
|
MemUsed = -1;
|
||
|
} else {
|
||
|
_HEAPINFO hi;
|
||
|
|
||
|
hi._pentry = NULL;
|
||
|
while (_heapwalk(&hi) == _HEAPOK) {
|
||
|
if (hi._useflag == _USEDENTRY) {
|
||
|
BlkUsed++;
|
||
|
MemUsed += hi._size;
|
||
|
if (hi._size > BigUsed)
|
||
|
BigUsed = hi._size;
|
||
|
//fprintf(stderr, "USED %d\n", hi._size);
|
||
|
} else {
|
||
|
BlkFree++;
|
||
|
MemFree += hi._size;
|
||
|
if (hi._size > BigFree)
|
||
|
BigFree = hi._size;
|
||
|
//fprintf(stderr, "FREE %d\n", hi._size);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef CONFIG_UNDOREDO
|
||
|
int NN = -1;
|
||
|
if (US.UndoPtr > 0)
|
||
|
NN = US.Top[US.UndoPtr - 1];
|
||
|
#endif
|
||
|
Msg(S_INFO,
|
||
|
#ifdef HEAPWALK
|
||
|
"M%ld,%ld B%ld,%ld S%ld,%ld"
|
||
|
#endif
|
||
|
"L%d/%d G%d/%d/%d A%d/%d C%d/%d P%d/%d "
|
||
|
#ifdef CONFIG_UNDOREDO
|
||
|
"U%d/%d/%d "
|
||
|
#endif
|
||
|
"H%d/%d/%d",
|
||
|
#ifdef HEAPWALK
|
||
|
MemUsed, MemFree, BlkUsed, BlkFree, BigUsed, BigFree,
|
||
|
#endif
|
||
|
CLine, NLines,
|
||
|
RGap, RCount, RAllocated,
|
||
|
CAct, NAct,
|
||
|
CColumn, NColumns,
|
||
|
CCharPos, NChars,
|
||
|
#ifdef CONFIG_UNDOREDO
|
||
|
US.UndoPtr, US.Num, NN,
|
||
|
#endif
|
||
|
StartHilit, MinRedraw, MaxRedraw);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_BOOKMARKS
|
||
|
int EBuffer::PlaceBookmark(char *Name, EPoint P) {
|
||
|
int i;
|
||
|
EBookmark *p;
|
||
|
|
||
|
assert(P.Row >= 0 && P.Row < RCount && P.Col >= 0);
|
||
|
|
||
|
for (i = 0; i < BMCount; i++) {
|
||
|
if (strcmp(Name, BMarks[i].Name) == 0) {
|
||
|
BMarks[i].BM = P;
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
p = (EBookmark *) realloc(BMarks, sizeof (EBookmark) * (1 + BMCount));
|
||
|
if (p == 0) return 0;
|
||
|
BMarks = p;
|
||
|
BMarks[BMCount].Name = strdup(Name);
|
||
|
BMarks[BMCount].BM = P;
|
||
|
BMCount++;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int EBuffer::RemoveBookmark(char *Name) {
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < BMCount; i++) {
|
||
|
if (strcmp(Name, BMarks[i].Name) == 0) {
|
||
|
free(BMarks[i].Name);
|
||
|
memmove(BMarks + i, BMarks + i + 1, sizeof(EBookmark) * (BMCount - i - 1));
|
||
|
BMCount--;
|
||
|
BMarks = (EBookmark *) realloc(BMarks, sizeof (EBookmark) * BMCount);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
View->MView->Win->Choice(GPC_ERROR, "RemoveBookmark", 1, "O&K", "Bookmark %s not found.", Name);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int EBuffer::GetBookmark(char *Name, EPoint &P) {
|
||
|
for (int i = 0; i < BMCount; i++)
|
||
|
if (strcmp(Name, BMarks[i].Name) == 0) {
|
||
|
P = BMarks[i].BM;
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int EBuffer::GotoBookmark(char *Name) {
|
||
|
for (int i = 0; i < BMCount; i++)
|
||
|
if (strcmp(Name, BMarks[i].Name) == 0) {
|
||
|
return CenterNearPosR(BMarks[i].BM.Col, BMarks[i].BM.Row);
|
||
|
}
|
||
|
View->MView->Win->Choice(GPC_ERROR, "GotoBookmark", 1, "O&K", "Bookmark %s not found.", Name);
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int EBuffer::GetMatchBrace(EPoint &M, int MinLine, int MaxLine, int show) {
|
||
|
int StateLen;
|
||
|
hsState *StateMap = 0;
|
||
|
int Pos;
|
||
|
PELine L = VLine(M.Row);
|
||
|
int dir = 0;
|
||
|
hsState State;
|
||
|
char Ch1, Ch2;
|
||
|
int CountX = 0;
|
||
|
int StateRow = -1;
|
||
|
|
||
|
M.Row = VToR(CP.Row);
|
||
|
|
||
|
Pos = CharOffset(L, M.Col);
|
||
|
if (Pos >= L->Count) return 0;
|
||
|
switch(L->Chars[Pos]) {
|
||
|
case '{': dir = +1; Ch1 = '{'; Ch2 = '}'; break;
|
||
|
case '[': dir = +1; Ch1 = '['; Ch2 = ']'; break;
|
||
|
case '<': dir = +1; Ch1 = '<'; Ch2 = '>'; break;
|
||
|
case '(': dir = +1; Ch1 = '('; Ch2 = ')'; break;
|
||
|
case '}': dir = -1; Ch1 = '}'; Ch2 = '{'; break;
|
||
|
case ']': dir = -1; Ch1 = ']'; Ch2 = '['; break;
|
||
|
case '>': dir = -1; Ch1 = '>'; Ch2 = '<'; break;
|
||
|
case ')': dir = -1; Ch1 = ')'; Ch2 = '('; break;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
StateMap = 0;
|
||
|
if (GetMap(M.Row, &StateLen, &StateMap) == 0) return 0;
|
||
|
State = StateMap[Pos];
|
||
|
StateRow = M.Row;
|
||
|
|
||
|
while (M.Row >= MinLine && M.Row < MaxLine) {
|
||
|
while (Pos >= 0 && Pos < L->Count) {
|
||
|
if (L->Chars[Pos] == Ch1 || L->Chars[Pos] == Ch2) {
|
||
|
// update syntax state if needed
|
||
|
if (StateRow != M.Row) {
|
||
|
free(StateMap);
|
||
|
StateMap = 0;
|
||
|
GetMap(M.Row, &StateLen, &StateMap);
|
||
|
if (StateMap == 0) return 0;
|
||
|
StateRow = M.Row;
|
||
|
}
|
||
|
if (StateMap[Pos] == State) {
|
||
|
if (L->Chars[Pos] == Ch1) CountX++;
|
||
|
if (L->Chars[Pos] == Ch2) CountX--;
|
||
|
if (CountX == 0) {
|
||
|
M.Col = ScreenPos(L, Pos);
|
||
|
free(StateMap);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Pos += dir;
|
||
|
}
|
||
|
M.Row += dir;
|
||
|
if (M.Row >= 0 && M.Row < RCount) {
|
||
|
L = RLine(M.Row);
|
||
|
Pos = (dir == 1) ? 0 : (L->Count - 1);
|
||
|
}
|
||
|
}
|
||
|
if (StateMap) free(StateMap);
|
||
|
if (show)
|
||
|
Msg(S_INFO, "No match (%d missing).", CountX);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int EBuffer::MatchBracket() {
|
||
|
EPoint M = CP;
|
||
|
|
||
|
if (GetMatchBrace(M, 0, RCount, 1) == 1)
|
||
|
return SetPosR(M.Col, M.Row);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int EBuffer::HilitMatchBracket() {
|
||
|
EPoint M = CP;
|
||
|
|
||
|
if (View == 0)
|
||
|
return 0;
|
||
|
|
||
|
int Min = VToR(GetVPort()->TP.Row);
|
||
|
int Max = VToR(GetVPort()->TP.Row);
|
||
|
Max += GetVPort()->Rows;
|
||
|
if (Max >= RCount)
|
||
|
Max = RCount;
|
||
|
if (Min < 0)
|
||
|
Min = 0;
|
||
|
if (Max < Min)
|
||
|
return 0;
|
||
|
if (GetMatchBrace(M, Min, Max, 0) == 1) {
|
||
|
Match = M;
|
||
|
MatchLen = 1;
|
||
|
MatchCount = 1;
|
||
|
Draw(Match.Row, Match.Row);
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int EBuffer::SearchWord(int SearchFlags) {
|
||
|
char word[MAXSEARCH + 1];
|
||
|
PELine L = VLine(CP.Row);
|
||
|
int P, len = 0;
|
||
|
int Case = BFI(this, BFI_MatchCase) ? 0 : SEARCH_NCASE;
|
||
|
|
||
|
P = CharOffset(L, CP.Col);
|
||
|
while ((P > 0) && ((ChClass(L->Chars[P - 1]) == 1) || (L->Chars[P - 1] == '_')))
|
||
|
P--;
|
||
|
while (len < int(sizeof(word)) && P < L->Count && (ChClass(L->Chars[P]) == 1 || L->Chars[P] == '_'))
|
||
|
word[len++] = L->Chars[P++];
|
||
|
word[len] = 0;
|
||
|
if (len == 0)
|
||
|
return 0;
|
||
|
|
||
|
return FindStr(word, len, Case | SearchFlags | SEARCH_WORD);
|
||
|
}
|
||
|
|
||
|
int EBuffer::FindTagWord(ExState &State) {
|
||
|
char word[MAXSEARCH + 1];
|
||
|
PELine L = VLine(CP.Row);
|
||
|
int P, len = 0;
|
||
|
|
||
|
P = CharOffset(L, CP.Col);
|
||
|
while ((P > 0) && ((ChClass(L->Chars[P - 1]) == 1) || (L->Chars[P - 1] == '_')))
|
||
|
P--;
|
||
|
while (len < int(sizeof(word)) && P < L->Count && (ChClass(L->Chars[P]) == 1 || L->Chars[P] == '_'))
|
||
|
word[len++] = L->Chars[P++];
|
||
|
word[len] = 0;
|
||
|
if (len == 0) {
|
||
|
Msg(S_INFO, "No word at cursor.");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int j = 2;
|
||
|
while (j--) {
|
||
|
int i;
|
||
|
|
||
|
i = TagFind(this, View, word);
|
||
|
if (i > 0)
|
||
|
return 1;
|
||
|
else if (j && (i < 0)) {
|
||
|
/* Try autoload tags */
|
||
|
if (View->ExecCommand(ExTagLoad, State) == 0)
|
||
|
break;
|
||
|
} else {
|
||
|
Msg(S_INFO, "Tag '%s' not found.", word);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|