#include "CodeEditor.h" namespace Upp { void CSyntax::IndentInsert(CodeEditor& e, int chr, int count) { IndentInsert0(e, chr, count, false); } static void sSpaces(int& cp, const WString& ln) { while(cp < ln.GetCount() && findarg(ln[cp], '\t', ' ') >= 0) cp++; } int CSyntax::GetCommentPos(CodeEditor& e, int l, WString& ch) const { WString ln = e.GetWLine(l); int cp = 0; sSpaces(cp, ln); One esyntax = e.GetSyntax(e.GetCursorLine()); CSyntax *syntax = dynamic_cast(~esyntax); if(syntax && syntax->comment && ln.Find("*/") < 0 || cp < ln.GetCount() - 2 && ln[cp] == '/' && ln[cp + 1] == '/') { while(cp < ln.GetLength() && findarg(ln[cp], '/', '*', '!', '<', '>', '%', '#', '@', '|') >= 0) cp++; ch = ln.Mid(0, cp); sSpaces(cp, ln); if(cp < ln.GetCount()) return cp; } ch = "0"; return -1; } void CSyntax::IndentInsert0(CodeEditor& e, int chr, int count, bool reformat) { if(chr == '\n') { while(count--) { int pp = e.GetCursor(); int cl = e.GetLinePos(pp); WString pl = e.GetWLine(cl); e.InsertChar('\n', 1); int p = e.GetCursor(); int l = e.GetLinePos(p); One esyntax; CSyntax *syntax = NULL; if(e.GetLineLength(l) == p) { // At the last char of line esyntax = e.GetSyntax(e.GetCursorLine()); syntax = dynamic_cast(~esyntax); if(!syntax) { TagSyntax *h = dynamic_cast(~esyntax); if(h) syntax = h->GetScript(); } } if(syntax && syntax->par.GetCount() && syntax->par.Top().line == cl && no_parenthesis_indent) { for(int i = 0; i < syntax->par.Top().pos && i < pl.GetLength(); i++) // Indent e.g. next line of if( e.InsertChar(pl[i] == '\t' ? '\t' : ' ', 1); return; } if(syntax && syntax->endstmtline == cl && syntax->seline >= 0 && syntax->seline < e.GetLineCount()) pl = e.GetWLine(syntax->seline); else { int i; for(i = 0; i < pl.GetLength(); i++) { if(pl[i] != ' ' && pl[i] != '\t') break; } if(pp < i) return; } const wchar *s = pl; while(*s == '\t' || *s == ' ') e.InsertChar(*s++, 1); if(!syntax) return; if(syntax->stmtline == cl || syntax->blk.GetCount() && syntax->blk.Top() == cl // statement (if, while..) || first line of block || *pl.Last() == ':') { // label if(indent_spaces || (s > pl && s[-1] == ' ')) e.InsertChar(' ', indent_amount); else e.InsertChar('\t', 1); } } return; } if(e.IsWordwrapComments()) { int limit = e.GetBorderColumn(); int pos = e.GetCursor(); int lp = pos; int l = e.GetLinePos(lp); if(limit > 10 && e.GetColumnLine(pos).x >= limit && lp == e.GetLineLength(l)) { int lp0 = e.GetPos(l); WString ln = e.GetWLine(l); WString dummy; int cp = GetCommentPos(e, l, dummy); if(cp >= 0) { int wl = (int)e.GetGPos(l, limit) - lp0; while(wl > cp && ln[wl - 1] != '\n' && ln[wl - 1] != ' ') wl--; int sl = wl - 1; while(sl > cp && ln[wl - 1] != '\n' && ln[sl - 1] == ' ') sl--; if(sl > cp) { e.Remove(lp0 + sl, pos - (lp0 + sl)); e.SetCursor(lp0 + sl); e.Put('\n'); for(int i = 0; i < cp; i++) e.Put(ln[i]); for(int i = wl; i < ln.GetCount(); i++) e.Put(ln[i]); e.Put(chr); if(!reformat) e.FinishPut(); return; } } } } if(reformat) { e.Put(chr); return; } // {, } inserted on line alone should be moved left sometimes: int cl = e.GetCursorLine(); WString l = e.GetWLine(cl); if(chr != '{' && chr != '}' || count > 1) { e.InsertChar(chr, 1, true); return; } const wchar *s; for(s = l; s < l.End(); s++) if(*s != ' ' && *s != '\t') { e.InsertChar(chr, 1); return; } int len = l.GetLength(); WString tl; int pos = e.GetPos(cl); if(chr == '{' && cl > 0 && stmtline == cl - 1) tl = e.GetWLine(cl - 1); else if(chr == '}' && blk.GetCount()) tl = e.GetWLine(blk.Top()); else { e.InsertChar(chr, 1); return; } e.SetCursor(pos); e.Remove(pos, len); s = tl; while(*s == '\t' || *s == ' ') e.InsertChar(*s++, 1); e.InsertChar(chr, 1); } void CSyntax::ReformatComment(CodeEditor& e) { if(!e.IsWordwrapComments()) return; int first_line = e.GetLine(e.GetCursor64()); WString ch; if(GetCommentPos(e, first_line, ch) < 0) return; int last_line = first_line; while(first_line > 0 && GetCommentHdr(e, first_line - 1) == ch) first_line--; while(last_line < e.GetLineCount() - 1 && GetCommentHdr(e, last_line + 1) == ch) last_line++; WString p; for(int i = first_line; i <= last_line; i++) { WString ln = e.GetWLine(i); p << ln.Mid(GetCommentPos(e, i, ch)) << ' '; } p = Join(Split(p, CharFilterWhitespace), " "); WString ln = e.GetWLine(first_line); int q = GetCommentPos(e, first_line, ch); p = ln.Mid(0, q) + p; int p0 = e.GetPos(first_line); e.SetCursor(p0); e.Remove(p0, e.GetPos(last_line + 1) - 1 - p0); for(wchar chr : p) IndentInsert0(e, chr, 1, true); e.FinishPut(); } bool NotEscape(int64 pos, const WString& s) { return pos == 0 || s[(int)pos - 1] != '\\' ? true : !NotEscape(pos - 1, s); } bool CSyntax::CheckBracket(CodeEditor& e, int li, int64 pos, int64 ppos, int64 pos0, WString ln, int d, int limit, int64& bpos0, int64& bpos) { int lvl = 1; pos += d; ppos += d; for(;;) { int c; for(;;) { while(pos < 0 || pos >= ln.GetLength()) { li += d; if(d * li >= d * limit) return false; ln = e.GetWLine(li); int q = ln.Find("//"); if(q >= 0) ln = ln.Mid(0, q) + WString(' ', ln.GetCount() - q); pos = d < 0 ? ln.GetLength() - 1 : 0; ppos += d; } c = ln[(int)pos]; if((c == '\"' || c == '\'') && (pos > 0 && NotEscape(pos, ln) && ln[(int)pos - 1] != '\'')) { pos += d; ppos += d; int lc = c; while(pos < ln.GetLength() && pos > 0) { if(ln[(int)pos] == lc && NotEscape(pos, ln)) { pos += d; ppos += d; break; } pos += d; ppos += d; } } else break; } if(islbrkt(c)) lvl += d; if(isrbrkt(c)) lvl -= d; if(lvl <= 0) { bpos0 = pos0; bpos = ppos; return true; } pos += d; ppos += d; } return false; } bool CSyntax::CheckLeftBracket(CodeEditor& e, int64 pos, int64& bpos0, int64& bpos) { if(pos < 0 || pos >= e.GetLength64()) return false; int64 ppos = pos; int li = e.GetLinePos64(pos); WString ln = e.GetWLine(li); return islbrkt(ln[(int)pos]) && CheckBracket(e, li, pos, ppos, ppos, ln, 1, min(li + 3000, e.GetLineCount()), bpos0, bpos); } bool CSyntax::CheckRightBracket(CodeEditor& e, int64 pos, int64& bpos0, int64& bpos) { if(pos < 0 || pos >= e.GetLength64()) return false; int64 ppos = pos; int li = e.GetLinePos64(pos); WString ln = e.GetWLine(li); return isrbrkt(ln[(int)pos]) && CheckBracket(e, li, pos, ppos, ppos, ln, -1, max(li - 3000, 0), bpos0, bpos); } bool CSyntax::CheckBrackets(CodeEditor& e, int64& bpos0, int64& bpos) { int64 c = e.GetCursor64(); return CheckLeftBracket(e, c, bpos0, bpos) || CheckRightBracket(e, c, bpos0, bpos) || CheckLeftBracket(e, c - 1, bpos0, bpos) || CheckRightBracket(e, c - 1, bpos0, bpos); } bool CSyntax::CanAssist() const { return !comment && !string && !linecomment; } Vector CSyntax::PickIfStack() { return pick(ifstack); } void CSyntax::CheckSyntaxRefresh(CodeEditor& e, int64 pos, const WString& text) { for(const wchar *s = text; *s; s++) { if(*s == '{' || *s == '(' || *s == '[' || *s == '/' || *s == '*' || *s == '}' || *s == ')' || *s == ']' || *s == '\\' || *s == ':') { e.Refresh(); break; } } String s = TrimLeft(e.GetUtf8Line(e.GetLine(pos))); if(s.StartsWith("#if") || s.StartsWith("#e")) e.Refresh(); WString h = e.GetWLine(e.GetLinePos64(pos)); // block highlighting changes if start of line is changed for(int i = 0; i < pos; i++) if(findarg(h[i], ' ', '\t') < 0) return; e.Refresh(); } }