ultimatepp/uppsrc/CodeEditor/Syntax.cpp
cxl 24a38aa7a6 IDE: Fixed highlighting of strings on continuation lines
git-svn-id: svn://ultimatepp.org/upp/trunk@965 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2009-03-17 09:41:46 +00:00

380 lines
7.9 KiB
C++

#include "CodeEditor.h"
NAMESPACE_UPP
Color GetUvsHighlight(const wchar *text, int& n);
const wchar *eatstring(const wchar *s) {
int delim = *s++;
while(*s)
if(*s == '\\' && s[1])
s += 2;
else
if(*s == delim)
return s + 1;
else
s++;
return s;
}
inline const wchar *strnext(const wchar *p, const wchar *end, int ch) {
while(p < end) {
if(*p == ch) return p;
p++;
}
return NULL;
}
void CodeEditor::SyntaxState::ClearBraces() {
cl = bl = pl = 0;
spar = 0;
brk.Clear();
blk.Clear();
bid.Clear();
bid.Add(0);
par.Clear();
}
void CodeEditor::SyntaxState::Clear() {
ClearBraces();
line = 0;
linecont = linecomment = comment = string = false;
macro = MACRO_OFF;
stmtline = endstmtline = seline = -1;
uvscolor = Null;
was_namespace = false;
ifstack.Clear();
}
const wchar *isstmt(const wchar *p) {
static const char *stmt[] = {
"if", "else", "while", "do", "for"
};
for(const char **q = stmt; q < stmt + __countof(stmt); q++) {
const char *k = *q;
const wchar *s = p;
for(;;) {
if(*k== '\0') {
if(!iscid(*s)) return s;
break;
}
if(*s != *k)
break;
s++;
k++;
}
}
return NULL;
}
Color CodeEditor::SyntaxState::IfColor(char c)
{
switch(c)
{
case CodeEditor::IfState::IF: return LtBlue();
case CodeEditor::IfState::ELIF: return Gray();
case CodeEditor::IfState::ELSE: return Green();
case CodeEditor::IfState::ELSE_ERROR: return LtRed();
case CodeEditor::IfState::ENDIF_ERROR: return LtMagenta();
default: return Null;
}
}
static WString sReadLn(const wchar *p)
{
WStringBuffer wbuf;
while(*p && *p <= ' ')
p++;
while(*p && !(*p == '/' && (p[1] == '/' || p[1] == '*'))) {
if(*p <= ' ') {
while(*++p && *p <= ' ')
;
if(*p && !(*p == '/' && (p[1] == '/' || p[1] == '*')))
wbuf.Cat(' ');
}
else
wbuf.Cat(*p++);
}
return wbuf;
}
int LastC(const wchar *b, const wchar *e)
{
if(e == b)
return 0;
return *e;
}
void CodeEditor::SyntaxState::Grounding(const wchar *b, const wchar *e)
{
if(b >= e || comment || !iscib(*b))
return;
e--;
while(e > b && (*e == '\t' || *e == ' ' ))
e--;
if(*e != ':')
ClearBraces();
}
void CodeEditor::SyntaxState::ScanSyntax(const wchar *ln, const wchar *e)
{
Grounding(ln, e);
if(!linecont) {
linecomment = false;
string = false;
}
linecont = e > ln && e[-1] == '\\';
const wchar *p = ln;
int lindent = 0;
while(p < e && *p == '\t') {
p++;
lindent++;
}
while(p < e && (*p == ' ' || *p == '\t'))
p++;
int n;
Color c = GetUvsHighlight(p, n);
if(n) {
uvscolor = c;
p += n;
}
if(!comment && *p == '#') {
while(++p < e && (*p == ' ' || *p == '\t'))
p++;
const wchar *id = p;
while(p < e && iscid(*p))
p++;
int idlen = int(p - id);
if(id[0] == 'i' && id[1] == 'f'
&& (idlen == 2 || idlen == 5 && id[2] == 'd' && id[3] == 'e' && id[4] == 'f'
|| idlen == 6 && id[2] == 'n' && id[3] == 'd' && id[4] == 'e' && id[5] == 'f')) {
IfState& ifstate = ifstack.Add();
ifstate.state = IfState::IF;
ifstate.iftext = sReadLn(ln);
ifstate.ifline = line + 1;
}
switch(idlen)
{
case 6:
if(id[0] == 'd' && id[1] == 'e' && id[2] == 'f' && id[3] == 'i' && id[4] == 'n' && id[5] == 'e')
macro = SyntaxState::MACRO_CONT;
break;
case 4:
if(id[0] == 'e' && id[1] == 'l')
if(id[2] == 'i' && id[3] == 'f')
if(ifstack.GetCount() == 0) {
IfState& ifstate = ifstack.Add();
ifstate.ifline = 0;
ifstate.state = IfState::ELSE_ERROR;
}
else {
IfState& ifstate = ifstack.Top();
if(ifstate.state == IfState::IF || ifstate.state == IfState::ELIF) {
ifstate.state = IfState::ELIF;
ifstate.iftext = WString().Cat() << sReadLn(ln) << ", " << ifstate.iftext;
}
else
ifstate.state = IfState::ELSE_ERROR;
}
else
if(id[2] == 's' && id[3] == 'e')
if(ifstack.GetCount() == 0) {
IfState& ifstate = ifstack.Add();
ifstate.ifline = 0;
ifstate.state = IfState::ELSE_ERROR;
}
else {
IfState& ifstate = ifstack.Top();
if(ifstate.state == IfState::IF || ifstate.state == IfState::ELIF) {
ifstate.state = IfState::ELSE;
ifstate.iftext = "#else, " + ifstate.iftext;
}
else
ifstate.state = IfState::ELSE_ERROR;
}
break;
case 5:
if(id[0] == 'e' && id[1] == 'n' && id[2] == 'd' && id[3] == 'i' && id[4] == 'f')
{
int itop = ifstack.GetCount() - 1;
if(itop < 0) {
IfState& ifstate = ifstack.Add();
ifstate.ifline = 0;
ifstate.state = IfState::ENDIF_ERROR;
}
else if(ifstack[itop].state != IfState::ENDIF_ERROR)
ifstack.Trim(itop);
}
break;
}
}
if(macro == SyntaxState::MACRO_CONT && !(p < e && e[-1] == '\\'))
macro = SyntaxState::MACRO_END;
for(;;) {
if(comment) {
p = strnext(p, e, '*');
if(!p) break;
if(*++p == '/') {
comment = false;
p++;
}
}
else {
int pc = 0;
for(;;) {
if(p >= e) return;
const wchar *pp;
if(!iscid(pc) && (pp = isstmt(p)) != NULL) {
stmtline = line;
spar = 0;
pc = 0;
p = pp;
}
else
if(!iscid(pc) && p[0] == 'n' && p[1] == 'a' && p[2] == 'm' && p[3] == 'e' &&
p[4] == 's' && p[5] == 'p' && p[6] == 'a' && p[7] == 'c' && p[8] == 'e' &&
!iscid(p[9])) {
was_namespace = true;
p += 9;
}
else {
int c = *p++;
if(c == '/') break;
if(c == '\'' || c == '\"') {
p = eatstring(p - 1);
if(p >= e) {
string = true;
return;
}
}
else
if(c == ';' && spar == 0) {
seline = stmtline;
endstmtline = line;
stmtline = -1;
was_namespace = false;
}
else
if(c == '{') {
if(was_namespace)
was_namespace = false;
else {
cl++;
brk.Add('}');
blk.Add() = line;
bid.Add(lindent + 1);
stmtline = -1;
par.Clear();
}
}
else
if(c == '}') {
if(brk.GetCount()) {
cl--;
brk.Drop();
}
if(blk.GetCount())
blk.Drop();
if(bid.GetCount() > 1)
bid.Drop();
stmtline = -1;
par.Clear();
}
else
if(c == '(') {
pl++;
brk.Add(')');
Isx& m = par.Add();
m.line = line;
m.pos = int(p - ln);
spar++;
}
else
if(c == '[') {
bl++;
brk.Add(']');
Isx& m = par.Add();
m.line = line;
m.pos = int(p - ln);
spar++;
}
else
if(c == ')') {
if(brk.GetCount()) {
pl--;
brk.Drop();
}
if(par.GetCount())
par.Drop();
spar--;
}
else
if(c == ']') {
if(brk.GetCount()) {
bl--;
brk.Drop();
}
if(par.GetCount())
par.Drop();
spar--;
}
pc = c;
}
}
if(*p == '/') {
linecomment = true;
return;
}
if(*p == '*') {
comment = true;
p++;
}
}
}
}
CodeEditor::SyntaxState CodeEditor::ScanSyntax(int line) {
SyntaxState st;
for(int i = 0; i < 4; i++)
if(line >= scache[i].line) {
st = scache[i];
break;
}
if(st.macro != SyntaxState::MACRO_CONT)
st.macro = SyntaxState::MACRO_OFF;
line = min(line, GetLineCount());
while(st.line < line) {
if(st.macro != SyntaxState::MACRO_CONT)
st.macro = SyntaxState::MACRO_OFF;
WString l = GetWLine(st.line);
st.ScanSyntax(l, l.End());
st.line++;
static int d[] = { 0, 100, 2000 };
for(int i = 0; i < 3; i++)
if(st.line == cline - d[i])
scache[i] = st;
}
scache[3] = st;
return st;
}
bool CodeEditor::SyntaxState::MatchHilite(const SyntaxState& st) const
{
return comment == st.comment
&& linecont == st.linecont
&& linecomment == st.linecomment
&& macro == st.macro
&& cl == st.cl && bl == st.bl && pl == st.pl
&& IsEqual(brk, st.brk)
&& IsEqual(blk, st.blk)
&& IsEqual(bid, st.bid)
&& IsEqual(par, st.par)
&& IsEqual(ifstack, st.ifstack)
&& uvscolor == st.uvscolor
&& seline == st.seline
&& spar == st.spar;
}
END_UPP_NAMESPACE