changed API for GetColorizedLineFunc: offset passed by address, updated to start of next line added check_readonly(EditState *s) added tentative code for unregistring bindings, cycling modes and charsets experiment alternate method for region highlighting: added EditState.region_style and EditState.curline_style added QEmacsState.hilite_region added DisplayState.style added clear_color to reset style bits in unicode array display_char_bidir takes style from DisplayState, code point is bare. fixed problem in text_display where line_num was not computed in shell mode handle region coloring in text_display (tentative) display line numbers and hex dump offsets as QE_STYLE_COMMENT display hex dump as QE_STYLE_FUNCTION
810 lines
24 KiB
C
810 lines
24 KiB
C
/*
|
|
* C mode for QEmacs.
|
|
*
|
|
* Copyright (c) 2001, 2002 Fabrice Bellard.
|
|
* Copyright (c) 2002-2008 Charlie Gordon.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "qe.h"
|
|
|
|
#if 0
|
|
static const char cc_keywords[] =
|
|
"asm|catch|class|delete|friend|inline|new|operator|"
|
|
"private|protected|public|template|try|this|virtual|throw|";
|
|
|
|
static const char java_keywords[] =
|
|
"abstract|boolean|byte|catch|class|extends|false|final|"
|
|
"finally|function|implements|import|in|instanceof|"
|
|
"interface|native|new|null|package|private|protected|"
|
|
"public|super|synchronized|this|throw|throws|transient|"
|
|
"true|try|var|with|";
|
|
#endif
|
|
|
|
static const char *c_mode_keywords =
|
|
"auto|break|case|const|continue|default|do|else|enum|extern|for|goto|"
|
|
"if|inline|register|restrict|return|sizeof|static|struct|switch|"
|
|
"typedef|union|volatile|while|";
|
|
|
|
/* NOTE: 'var' is added for javascript */
|
|
static const char *c_mode_types =
|
|
"char|double|float|int|long|unsigned|short|signed|void|var|"
|
|
"_Bool|_Complex|_Imaginary|";
|
|
|
|
static const char *c_mode_extensions =
|
|
"c|h|y|e|cc|cs|cpp|cxx|hpp|hxx|jav|java|js|qe|";
|
|
|
|
#if 0
|
|
static int get_c_identifier(char *buf, int buf_size, unsigned int *p)
|
|
{
|
|
unsigned int c;
|
|
char *q;
|
|
|
|
c = *p;
|
|
q = buf;
|
|
if (qe_isalpha_(c)) {
|
|
do {
|
|
if ((q - buf) < buf_size - 1)
|
|
*q++ = c;
|
|
p++;
|
|
c = *p;
|
|
} while (qe_isalnum_(c));
|
|
}
|
|
*q = '\0';
|
|
return q - buf;
|
|
}
|
|
#endif
|
|
|
|
/* c-mode colorization states */
|
|
enum {
|
|
C_COMMENT = 1, /* multiline comment pending */
|
|
C_COMMENT1 = 2, /* single line comment with \ at EOL */
|
|
C_STRING = 4, /* double quoted string spanning multiple lines */
|
|
C_STRING_Q = 8, /* single quoted string spanning multiple lines */
|
|
C_PREPROCESS = 16, /* preprocessor directive with \ at EOL */
|
|
};
|
|
|
|
void c_colorize_line(unsigned int *buf, int len,
|
|
int *colorize_state_ptr, __unused__ int state_only)
|
|
{
|
|
int c, state, style, style1, type_decl, klen, delim;
|
|
unsigned int *p, *p_start, *p_end, *p1, *p2;
|
|
char kbuf[32];
|
|
|
|
state = *colorize_state_ptr;
|
|
p = buf;
|
|
p_start = p;
|
|
p_end = p + len;
|
|
type_decl = 0;
|
|
|
|
if (p >= p_end)
|
|
goto the_end;
|
|
|
|
c = 0; /* turn off stupid egcs-2.91.66 warning */
|
|
style = 0;
|
|
|
|
if (state) {
|
|
/* if already in a state, go directly in the code parsing it */
|
|
if (state & C_PREPROCESS)
|
|
style = QE_STYLE_PREPROCESS;
|
|
if (state & C_COMMENT)
|
|
goto parse_comment;
|
|
if (state & C_COMMENT1)
|
|
goto parse_comment1;
|
|
if (state & C_STRING)
|
|
goto parse_string;
|
|
if (state & C_STRING_Q)
|
|
goto parse_string_q;
|
|
}
|
|
|
|
while (p < p_end) {
|
|
p_start = p;
|
|
c = *p++;
|
|
|
|
switch (c) {
|
|
case '\n':
|
|
p--;
|
|
goto the_end;
|
|
case '/':
|
|
if (*p == '*') {
|
|
/* normal comment */
|
|
p++;
|
|
parse_comment:
|
|
state |= C_COMMENT;
|
|
while (p < p_end) {
|
|
if (p[0] == '*' && p[1] == '/') {
|
|
p += 2;
|
|
state &= ~C_COMMENT;
|
|
break;
|
|
} else {
|
|
p++;
|
|
}
|
|
}
|
|
set_color(p_start, p, QE_STYLE_COMMENT);
|
|
continue;
|
|
} else
|
|
if (*p == '/') {
|
|
/* line comment */
|
|
parse_comment1:
|
|
state |= C_COMMENT1;
|
|
p = p_end;
|
|
set_color(p_start, p, QE_STYLE_COMMENT);
|
|
goto the_end;
|
|
}
|
|
break;
|
|
case '#': /* preprocessor */
|
|
state = C_PREPROCESS;
|
|
style = QE_STYLE_PREPROCESS;
|
|
break;
|
|
case 'L': /* wide character and string literals */
|
|
if (*p == '\'') {
|
|
p++;
|
|
goto parse_string_q;
|
|
}
|
|
if (*p == '\"') {
|
|
p++;
|
|
goto parse_string;
|
|
}
|
|
goto normal;
|
|
case '\'': /* character constant */
|
|
parse_string_q:
|
|
state |= C_STRING_Q;
|
|
style1 = QE_STYLE_STRING_Q;
|
|
delim = '\'';
|
|
goto string;
|
|
case '\"': /* string literal */
|
|
parse_string:
|
|
state |= C_STRING;
|
|
style1 = QE_STYLE_STRING;
|
|
delim = '\"';
|
|
string:
|
|
while (p < p_end) {
|
|
if (*p == '\\') {
|
|
p++;
|
|
if (p >= p_end)
|
|
break;
|
|
p++;
|
|
} else
|
|
if ((int)*p == delim) {
|
|
p++;
|
|
state &= ~(C_STRING | C_STRING_Q);
|
|
break;
|
|
} else {
|
|
p++;
|
|
}
|
|
}
|
|
if (state & C_PREPROCESS)
|
|
style1 = QE_STYLE_PREPROCESS;
|
|
set_color(p_start, p, style1);
|
|
continue;
|
|
case '=':
|
|
/* exit type declaration */
|
|
/* does not handle this: int i = 1, j = 2; */
|
|
type_decl = 0;
|
|
break;
|
|
case '<': /* JavaScript extension */
|
|
if (*p == '!' && p[1] == '-' && p[2] == '-')
|
|
goto parse_comment1;
|
|
break;
|
|
default:
|
|
normal:
|
|
if (state & C_PREPROCESS)
|
|
break;
|
|
if (qe_isdigit(c)) {
|
|
while (qe_isalnum(*p) || *p == '.') {
|
|
p++;
|
|
}
|
|
set_color(p_start, p, QE_STYLE_NUMBER);
|
|
continue;
|
|
}
|
|
if (qe_isalpha_(c)) {
|
|
|
|
/* XXX: should support :: and $ */
|
|
klen = 0;
|
|
p--;
|
|
do {
|
|
if (klen < countof(kbuf) - 1)
|
|
kbuf[klen++] = c;
|
|
p++;
|
|
c = *p;
|
|
} while (qe_isalnum_(c));
|
|
kbuf[klen] = '\0';
|
|
|
|
if (strfind(c_mode_keywords, kbuf)) {
|
|
set_color(p_start, p, QE_STYLE_KEYWORD);
|
|
continue;
|
|
}
|
|
|
|
p1 = p;
|
|
while (qe_isblank(*p1))
|
|
p1++;
|
|
p2 = p1;
|
|
while (*p2 == '*' || qe_isblank(*p2))
|
|
p2++;
|
|
|
|
if (strfind(c_mode_types, kbuf)
|
|
|| (klen > 2 && kbuf[klen - 2] == '_' && kbuf[klen - 1] == 't')) {
|
|
/* c type */
|
|
/* if not cast, assume type declaration */
|
|
if (*p2 != ')') {
|
|
type_decl = 1;
|
|
}
|
|
set_color(p_start, p, QE_STYLE_TYPE);
|
|
continue;
|
|
}
|
|
|
|
if (*p == '(') {
|
|
/* function call */
|
|
/* XXX: different styles for call and definition */
|
|
set_color(p_start, p, QE_STYLE_FUNCTION);
|
|
continue;
|
|
}
|
|
/* assume typedef if starting at first column */
|
|
if (p_start == buf)
|
|
type_decl = 1;
|
|
|
|
if (type_decl) {
|
|
if (p_start == buf) {
|
|
/* assume type if first column */
|
|
set_color(p_start, p, QE_STYLE_TYPE);
|
|
} else {
|
|
set_color(p_start, p, QE_STYLE_VARIABLE);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
set_color1(p_start, style);
|
|
}
|
|
the_end:
|
|
/* strip state if not overflowing from a comment */
|
|
if (!(state & C_COMMENT) && p > buf && ((p[-1] & CHAR_MASK) != '\\'))
|
|
state &= ~(C_COMMENT1 | C_PREPROCESS);
|
|
*colorize_state_ptr = state;
|
|
}
|
|
|
|
#define MAX_BUF_SIZE 512
|
|
#define MAX_STACK_SIZE 64
|
|
|
|
/* gives the position of the first non white space character in
|
|
buf. TABs are counted correctly */
|
|
static int find_indent1(EditState *s, unsigned int *buf)
|
|
{
|
|
unsigned int *p;
|
|
int pos, c;
|
|
|
|
p = buf;
|
|
pos = 0;
|
|
for (;;) {
|
|
c = *p++ & CHAR_MASK;
|
|
if (c == '\t')
|
|
pos += s->tab_size - (pos % s->tab_size);
|
|
else if (c == ' ')
|
|
pos++;
|
|
else
|
|
break;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
static int find_pos(EditState *s, unsigned int *buf, int size)
|
|
{
|
|
int pos, c, i;
|
|
|
|
pos = 0;
|
|
for (i = 0; i < size; i++) {
|
|
c = buf[i] & CHAR_MASK;
|
|
if (c == '\t')
|
|
pos += s->tab_size - (pos % s->tab_size);
|
|
else
|
|
pos++;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
enum {
|
|
INDENT_NORM,
|
|
INDENT_FIND_EQ,
|
|
};
|
|
|
|
/* insert n spaces at *offset_ptr. Update offset_ptr to point just
|
|
after. Tabs are inserted if s->indent_tabs_mode is true. */
|
|
static void insert_spaces(EditState *s, int *offset_ptr, int i)
|
|
{
|
|
int offset, size;
|
|
char buf1[64];
|
|
|
|
offset = *offset_ptr;
|
|
|
|
/* insert tabs */
|
|
if (s->indent_tabs_mode) {
|
|
while (i >= s->tab_size) {
|
|
buf1[0] = '\t';
|
|
eb_insert(s->b, offset, buf1, 1);
|
|
offset++;
|
|
i -= s->tab_size;
|
|
}
|
|
}
|
|
|
|
/* insert needed spaces */
|
|
while (i > 0) {
|
|
size = i;
|
|
if (size > ssizeof(buf1))
|
|
size = ssizeof(buf1);
|
|
memset(buf1, ' ', size);
|
|
eb_insert(s->b, offset, buf1, size);
|
|
i -= size;
|
|
offset += size;
|
|
}
|
|
*offset_ptr = offset;
|
|
}
|
|
|
|
static void do_c_indent(EditState *s)
|
|
{
|
|
int offset, offset1, offset2, offsetl, c, pos, size, line_num, col_num;
|
|
int i, eoi_found, len, pos1, lpos, style, line_num1, state;
|
|
unsigned int buf[MAX_BUF_SIZE], *p;
|
|
unsigned char stack[MAX_STACK_SIZE];
|
|
char buf1[64], *q;
|
|
int stack_ptr;
|
|
|
|
/* find start of line */
|
|
eb_get_pos(s->b, &line_num, &col_num, s->offset);
|
|
line_num1 = line_num;
|
|
offset = eb_goto_bol(s->b, s->offset);
|
|
/* now find previous lines and compute indent */
|
|
pos = 0;
|
|
lpos = -1; /* position of the last instruction start */
|
|
offsetl = offset;
|
|
eoi_found = 0;
|
|
stack_ptr = 0;
|
|
state = INDENT_NORM;
|
|
for (;;) {
|
|
if (offsetl == 0)
|
|
break;
|
|
line_num--;
|
|
offsetl = eb_prev_line(s->b, offsetl);
|
|
offset1 = offsetl;
|
|
len = get_colorized_line(s, buf, countof(buf), &offset1, line_num);
|
|
/* store indent position */
|
|
pos1 = find_indent1(s, buf);
|
|
p = buf + len;
|
|
while (p > buf) {
|
|
p--;
|
|
c = *p;
|
|
/* skip strings or comments */
|
|
style = c >> STYLE_SHIFT;
|
|
if (style == QE_STYLE_COMMENT ||
|
|
style == QE_STYLE_STRING ||
|
|
style == QE_STYLE_PREPROCESS)
|
|
continue;
|
|
c = c & CHAR_MASK;
|
|
if (state == INDENT_FIND_EQ) {
|
|
/* special case to search '=' or ; before { to know if
|
|
we are in data definition */
|
|
if (c == '=') {
|
|
/* data definition case */
|
|
pos = lpos;
|
|
goto end_parse;
|
|
} else if (c == ';') {
|
|
/* normal instruction case */
|
|
goto check_instr;
|
|
}
|
|
} else {
|
|
switch (c) {
|
|
case '}':
|
|
if (stack_ptr >= MAX_STACK_SIZE)
|
|
return;
|
|
stack[stack_ptr++] = c;
|
|
goto check_instr;
|
|
case '{':
|
|
if (stack_ptr == 0) {
|
|
if (lpos == -1) {
|
|
pos = pos1 + s->indent_size;
|
|
eoi_found = 1;
|
|
goto end_parse;
|
|
} else {
|
|
state = INDENT_FIND_EQ;
|
|
}
|
|
} else {
|
|
/* XXX: syntax check ? */
|
|
stack_ptr--;
|
|
goto check_instr;
|
|
}
|
|
break;
|
|
case ')':
|
|
case ']':
|
|
if (stack_ptr >= MAX_STACK_SIZE)
|
|
return;
|
|
stack[stack_ptr++] = c;
|
|
break;
|
|
case '(':
|
|
case '[':
|
|
if (stack_ptr == 0) {
|
|
pos = find_pos(s, buf, p - buf) + 1;
|
|
goto end_parse;
|
|
} else {
|
|
/* XXX: syntax check ? */
|
|
stack_ptr--;
|
|
}
|
|
break;
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
break;
|
|
case ';':
|
|
/* level test needed for 'for(;;)' */
|
|
if (stack_ptr == 0) {
|
|
/* ; { or } are found before an instruction */
|
|
check_instr:
|
|
if (lpos >= 0) {
|
|
/* start of instruction already found */
|
|
pos = lpos;
|
|
if (!eoi_found)
|
|
pos += s->indent_size;
|
|
goto end_parse;
|
|
}
|
|
eoi_found = 1;
|
|
}
|
|
break;
|
|
case ':':
|
|
/* a label line is ignored */
|
|
/* XXX: incorrect */
|
|
goto prev_line;
|
|
default:
|
|
if (stack_ptr == 0) {
|
|
if ((c >> STYLE_SHIFT) == QE_STYLE_KEYWORD) {
|
|
unsigned int *p1, *p2;
|
|
/* special case for if/for/while */
|
|
p1 = p;
|
|
while (p > buf &&
|
|
(p[-1] >> STYLE_SHIFT) == QE_STYLE_KEYWORD)
|
|
p--;
|
|
p2 = p;
|
|
q = buf1;
|
|
while (q < buf1 + countof(buf1) - 1 && p2 <= p1) {
|
|
*q++ = *p2++ & CHAR_MASK;
|
|
}
|
|
*q = '\0';
|
|
|
|
if (!eoi_found && strfind("if|for|while", buf1)) {
|
|
pos = pos1 + s->indent_size;
|
|
goto end_parse;
|
|
}
|
|
}
|
|
lpos = pos1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
prev_line: ;
|
|
}
|
|
end_parse:
|
|
/* compute special cases which depend on the chars on the current line */
|
|
offset1 = offset;
|
|
len = get_colorized_line(s, buf, countof(buf), &offset1, line_num1);
|
|
|
|
if (stack_ptr == 0) {
|
|
if (!pos && lpos >= 0) {
|
|
/* start of instruction already found */
|
|
pos = lpos;
|
|
if (!eoi_found)
|
|
pos += s->indent_size;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < len; i++) {
|
|
c = buf[i];
|
|
style = c >> STYLE_SHIFT;
|
|
/* if preprocess, no indent */
|
|
if (style == QE_STYLE_PREPROCESS) {
|
|
pos = 0;
|
|
break;
|
|
}
|
|
/* NOTE: strings & comments are correctly ignored there */
|
|
if (c == '}' || c == ':') {
|
|
pos -= s->indent_size;
|
|
if (pos < 0)
|
|
pos = 0;
|
|
break;
|
|
}
|
|
if (c == '{' && pos == s->indent_size && !eoi_found) {
|
|
pos = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* the number of needed spaces is in 'pos' */
|
|
|
|
/* CG: should not modify buffer if indentation in correct */
|
|
|
|
/* suppress leading spaces */
|
|
offset1 = offset;
|
|
for (;;) {
|
|
c = eb_nextc(s->b, offset1, &offset2);
|
|
if (c != ' ' && c != '\t')
|
|
break;
|
|
offset1 = offset2;
|
|
}
|
|
size = offset1 - offset;
|
|
if (size > 0) {
|
|
eb_delete(s->b, offset, size);
|
|
}
|
|
/* insert needed spaces */
|
|
offset1 = offset;
|
|
insert_spaces(s, &offset1, pos);
|
|
if (s->offset == offset) {
|
|
/* move to the indentation if point was in indent space */
|
|
s->offset = offset1;
|
|
}
|
|
}
|
|
|
|
static void do_c_indent_region(EditState *s)
|
|
{
|
|
int col_num, line1, line2, begin;
|
|
|
|
/* Swap point and mark so point <= mark */
|
|
if (s->offset > s->b->mark) {
|
|
int tmp = s->b->mark;
|
|
s->b->mark = s->offset;
|
|
s->offset = tmp;
|
|
}
|
|
/* We do it with lines to avoid offset variations during indenting */
|
|
eb_get_pos(s->b, &line1, &col_num, s->offset);
|
|
eb_get_pos(s->b, &line2, &col_num, s->b->mark);
|
|
|
|
/* Remember start of first line of region to later set mark */
|
|
begin = eb_goto_pos(s->b, line1, 0);
|
|
|
|
for (; line1 <= line2; line1++) {
|
|
s->offset = eb_goto_pos(s->b, line1, 0);
|
|
do_c_indent(s);
|
|
}
|
|
/* move point to end of region, and mark to begin of first row */
|
|
s->offset = s->b->mark;
|
|
/* XXX: begin may have moved? */
|
|
s->b->mark = begin;
|
|
}
|
|
|
|
static void do_c_electric(EditState *s, int key)
|
|
{
|
|
do_char(s, key, 1);
|
|
do_c_indent(s);
|
|
}
|
|
|
|
/* forward / backward preprocessor */
|
|
static void do_c_forward_preprocessor(EditState *s, int dir)
|
|
{
|
|
}
|
|
|
|
/* forward / backward block */
|
|
#define MAX_LEVEL 20
|
|
|
|
/* CG: move this to generic command */
|
|
static void do_c_forward_block(EditState *s, int dir)
|
|
{
|
|
unsigned int buf[MAX_BUF_SIZE];
|
|
char balance[MAX_LEVEL];
|
|
int line_num, col_num, offset, offset1, len, pos, style, c, c1, level;
|
|
|
|
eb_get_pos(s->b, &line_num, &col_num, s->offset);
|
|
offset = eb_goto_bol2(s->b, s->offset, &pos);
|
|
offset1 = offset;
|
|
len = get_colorized_line(s, buf, countof(buf), &offset1, line_num);
|
|
style = buf[pos] >> STYLE_SHIFT;
|
|
level = 0;
|
|
|
|
if (dir < 0) {
|
|
for (;;) {
|
|
if (pos == 0) {
|
|
if (offset <= 0)
|
|
break;
|
|
line_num--;
|
|
offset = eb_prev_line(s->b, offset);
|
|
offset1 = offset;
|
|
pos = get_colorized_line(s, buf, countof(buf), &offset1, line_num);
|
|
continue;
|
|
}
|
|
c = buf[--pos];
|
|
if (style != c >> STYLE_SHIFT) {
|
|
if (style == 0)
|
|
continue;
|
|
style = 0;
|
|
if ((c >> STYLE_SHIFT) != 0)
|
|
continue;
|
|
}
|
|
switch (c &= CHAR_MASK) {
|
|
case ')':
|
|
c1 = '(';
|
|
goto push;
|
|
case ']':
|
|
c1 = '[';
|
|
goto push;
|
|
case '}':
|
|
c1 = '{';
|
|
push:
|
|
if (level < MAX_LEVEL) {
|
|
balance[level] = c1;
|
|
}
|
|
level++;
|
|
break;
|
|
case '(':
|
|
case '[':
|
|
case '{':
|
|
if (level > 0) {
|
|
--level;
|
|
if (balance[level] != c) {
|
|
put_status(s, "Unmatched delimiter");
|
|
return;
|
|
}
|
|
if (level <= 0)
|
|
goto the_end;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (;;) {
|
|
if (pos >= len) {
|
|
/* Should simplify with get_colorized_line updating
|
|
* offset
|
|
*/
|
|
line_num++;
|
|
pos = 0;
|
|
offset = eb_next_line(s->b, offset);
|
|
if (offset >= s->b->total_size)
|
|
break;
|
|
offset1 = offset;
|
|
len = get_colorized_line(s, buf, countof(buf), &offset1, line_num);
|
|
continue;
|
|
}
|
|
c = buf[pos];
|
|
pos++;
|
|
if (style != c >> STYLE_SHIFT) {
|
|
if (style == 0)
|
|
continue;
|
|
style = 0;
|
|
if ((c >> STYLE_SHIFT) != 0)
|
|
continue;
|
|
}
|
|
switch (c &= CHAR_MASK) {
|
|
case '(':
|
|
c1 = ')';
|
|
goto push1;
|
|
case '[':
|
|
c1 = ']';
|
|
goto push1;
|
|
case '{':
|
|
c1 = '}';
|
|
push1:
|
|
if (level < MAX_LEVEL) {
|
|
balance[level] = c1;
|
|
}
|
|
level++;
|
|
break;
|
|
case ')':
|
|
case ']':
|
|
case '}':
|
|
if (level > 0) {
|
|
--level;
|
|
if (balance[level] != c) {
|
|
put_status(s, "Unmatched delimiter");
|
|
return;
|
|
}
|
|
if (level <= 0)
|
|
goto the_end;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
the_end:
|
|
while (pos > 0) {
|
|
eb_nextc(s->b, offset, &offset);
|
|
pos--;
|
|
}
|
|
s->offset = offset;
|
|
}
|
|
|
|
/* CG: move this to generic command */
|
|
static void do_c_kill_block(EditState *s, int dir)
|
|
{
|
|
int start = s->offset;
|
|
|
|
if (s->b->flags & BF_READONLY)
|
|
return;
|
|
|
|
do_c_forward_block(s, dir);
|
|
do_kill(s, start, s->offset, dir);
|
|
}
|
|
|
|
static int c_mode_probe(ModeProbeData *p)
|
|
{
|
|
/* currently, only use the file extension */
|
|
if (match_extension(p->filename, c_mode_extensions))
|
|
return 80;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int c_mode_init(EditState *s, ModeSavedData *saved_data)
|
|
{
|
|
int ret;
|
|
|
|
ret = text_mode_init(s, saved_data);
|
|
if (ret)
|
|
return ret;
|
|
set_colorize_func(s, c_colorize_line);
|
|
return ret;
|
|
}
|
|
|
|
/* C mode specific commands */
|
|
static CmdDef c_commands[] = {
|
|
CMD_( KEY_CTRL('i'), KEY_NONE,
|
|
"c-indent-command", do_c_indent, ES, "*")
|
|
CMD_( KEY_META(KEY_CTRL('\\')), KEY_NONE,
|
|
"c-indent-region", do_c_indent_region, ES, "*")
|
|
/* should map to KEY_META + KEY_CTRL_LEFT ? */
|
|
CMDV( KEY_META('['), KEY_NONE,
|
|
"c-backward-preprocessor", do_c_forward_preprocessor, ESi, -1, "*v")
|
|
CMDV( KEY_META(']'), KEY_NONE,
|
|
"c-forward-preprocessor", do_c_forward_preprocessor, ESi, 1, "*v")
|
|
/* CG: should use 'k' intrinsic argument */
|
|
CMDV( ';', KEY_NONE,
|
|
"c-electric-semi&comma", do_c_electric, ESi, ';', "*v")
|
|
CMDV( ':', KEY_NONE,
|
|
"c-electric-colon", do_c_electric, ESi, ':', "*v")
|
|
CMDV( '{', KEY_NONE,
|
|
"c-electric-obrace", do_c_electric, ESi, '{', "*v")
|
|
CMDV( '}', KEY_NONE,
|
|
"c-electric-cbrace", do_c_electric, ESi, '}', "*v")
|
|
CMD_DEF_END,
|
|
};
|
|
|
|
/* Non C mode specific commands */
|
|
static CmdDef c_global_commands[] = {
|
|
CMDV( KEY_META(KEY_CTRL('b')), KEY_NONE,
|
|
"c-backward-block", do_c_forward_block, ESi, -1, "*v")
|
|
/* should map to KEY_META + KEY_CTRL_RIGHT */
|
|
CMDV( KEY_META(KEY_CTRL('f')), KEY_NONE,
|
|
"c-forward-block", do_c_forward_block, ESi, 1, "*v")
|
|
CMDV( KEY_META(KEY_CTRL('k')), KEY_NONE,
|
|
"c-kill-block", do_c_kill_block, ESi, 1, "*v")
|
|
CMDV( KEY_ESC, KEY_DELETE,
|
|
"c-backward-kill-block", do_c_kill_block, ESi, -1, "*v")
|
|
CMD_DEF_END,
|
|
};
|
|
|
|
static ModeDef c_mode;
|
|
|
|
static int c_init(void)
|
|
{
|
|
/* c mode is almost like the text mode, so we copy and patch it */
|
|
memcpy(&c_mode, &text_mode, sizeof(ModeDef));
|
|
c_mode.name = "C";
|
|
c_mode.mode_probe = c_mode_probe;
|
|
c_mode.mode_init = c_mode_init;
|
|
|
|
qe_register_mode(&c_mode);
|
|
qe_register_cmd_table(c_commands, &c_mode);
|
|
qe_register_cmd_table(c_global_commands, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
qe_module_init(c_init);
|