evil-qemacs/orgmode.c
Charlie Gordon 9a58b44917 new character based APIs for buffers
* add eb_skip_chars
* rename eb_delete_nextc to eb_delete_uchar
* deprecate eb_line_pad
* use char based buffer API in orgmode
2014-01-08 16:26:44 +00:00

350 lines
8.8 KiB
C

/*
* Org mode for QEmacs.
*
* Copyright (c) 2001-2002 Fabrice Bellard.
* Copyright (c) 2002-2014 Charlie Gordon.
* Copyright (c) 2014 Francois Revol.
*
* 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"
ModeDef org_mode;
#define IN_BULLET 0x01
#define IN_ACTION 0x02
#define IN_TAG 0x04
#define MAX_BUF_SIZE 512
/* TODO: define specific styles */
static struct OrgTodoKeywords {
const char *keyword;
int style;
} OrgTodoKeywords [] = {
{ "TODO", QE_STYLE_STRING },
{ "DONE", QE_STYLE_TYPE },
};
/* TODO: define specific styles */
#define BULLET_STYLES 5
static int OrgBulletStyles[BULLET_STYLES] = {
QE_STYLE_FUNCTION,
QE_STYLE_VARIABLE,
QE_STYLE_PREPROCESS,
QE_STYLE_STRING,
QE_STYLE_TYPE,
};
static int org_bullet_depth(unsigned int *str, int n)
{
int i;
for (i = 0; i < n && (str[i] & CHAR_MASK) == '*'; i++) {
if ((str[i + 1] & CHAR_MASK) == ' ') {
return i;
}
}
return -1;
}
static int org_todo_keyword(unsigned int *str, int n)
{
int i, c, klen;
char kbuf[32];
klen = 0;
for (i = 0; i < n && qe_isalpha(c = (str[i] & CHAR_MASK)); i++) {
if (klen < countof(kbuf) - 1)
kbuf[klen++] = c;
else
break;
}
kbuf[klen] = '\0';
if (klen > 0 && c == ' ') {
int k;
for (k = 0; k < countof(OrgTodoKeywords); k++) {
if (!strcmp(kbuf, OrgTodoKeywords[k].keyword)) {
set_color(str, str + i, OrgTodoKeywords[k].style);
return k;
}
}
}
return -1;
}
static void org_colorize_line(unsigned int *str, int n, int *statep,
__unused__ int state_only)
{
int colstate = *statep;
int bullets = 0;
int base_style = 0;
int i = 0;
int c;
bullets = org_bullet_depth(str, n);
if (bullets > -1) {
colstate = IN_BULLET;
base_style = OrgBulletStyles[bullets % BULLET_STYLES];
set_color(str, str + bullets + 1, base_style);
i = bullets + 2;
}
if (colstate & IN_BULLET) {
int kw = org_todo_keyword(str + i, n - i);
if (kw > -1) {
int kwlen = strlen(OrgTodoKeywords[kw].keyword);
set_color(str + i, str + i + kwlen, OrgTodoKeywords[kw].style);
i += kwlen;
}
}
while (i < n) {
c = str[i] & CHAR_MASK;
switch (c) {
default:
break;
}
set_color1(str + i, base_style);
i++;
continue;
}
colstate = 0;
*statep = colstate;
}
static void do_org_todo(EditState *s)
{
int offset, offsetl, line_num, col_num, bullets, len;
unsigned int buf[MAX_BUF_SIZE];
int kw;
/* find start of line */
eb_get_pos(s->b, &line_num, &col_num, s->offset);
offset = eb_goto_bol(s->b, s->offset);
for (;;) {
offsetl = offset;
len = eb_get_line(s->b, buf, countof(buf), &offsetl);
bullets = org_bullet_depth(buf, len);
if (bullets > -1) {
break;
}
if (offset == 0)
return;
offset = eb_prev_line(s->b, offset);
}
offset += eb_skip_chars(s->b, offset, bullets + 2);
kw = org_todo_keyword(buf + bullets + 2, len - bullets);
if (kw > -1) {
int kwlen = strlen(OrgTodoKeywords[kw].keyword);
eb_delete_chars(s->b, offset, kwlen + 1);
}
kw++;
if (kw < countof(OrgTodoKeywords)) {
int kwlen = strlen(OrgTodoKeywords[kw].keyword);
eb_insert_uchar(s->b, offset, ' ');
eb_insert_utf8_buf(s->b, offset, OrgTodoKeywords[kw].keyword, kwlen);
}
}
static void do_org_meta_return(EditState *s)
{
int offset, offsetl, line_num, col_num, bullets, len;
unsigned int buf[MAX_BUF_SIZE];
/* find start of line */
eb_get_pos(s->b, &line_num, &col_num, s->offset);
offset = eb_goto_bol(s->b, s->offset);
offsetl = offset;
len = eb_get_line(s->b, buf, countof(buf), &offsetl);
bullets = org_bullet_depth(buf, len);
if (bullets < 0)
return;
if (col_num > 0)
offset = offsetl;
eb_insert_utf8_buf(s->b, offset, " \n", 2);
while (bullets-- >= 0) {
eb_insert_uchar(s->b, offset, '*');
}
//eb_goto_eol(s->b, offset);
if (col_num > 0)
text_move_up_down(s, 1);
text_move_eol(s);
}
#if 0
static void do_org_insert_todo_heading(EditState *s)
{
printf("M-S-RET\n");
/* TODO */
}
#endif
static void do_org_promote(EditState *s, int dir)
{
int offset, offsetl, line_num, col_num, len;
int bullets;
unsigned int buf[MAX_BUF_SIZE];
/* find start of line */
eb_get_pos(s->b, &line_num, &col_num, s->offset);
offset = eb_goto_bol(s->b, s->offset);
offsetl = offset;
len = eb_get_line(s->b, buf, countof(buf), &offsetl);
bullets = org_bullet_depth(buf, len);
if (bullets < 0)
return;
if (dir > 0)
eb_insert_uchar(s->b, offset, '*');
if (dir < 0 && bullets > 0)
eb_delete_uchar(s->b, offset);
}
static void do_org_promote_subtree(EditState *s, int dir)
{
int offset, offsetl, offseti, line_num, col_num, len;
int bullets, bullets1;
unsigned int buf[MAX_BUF_SIZE];
/* find start of line */
eb_get_pos(s->b, &line_num, &col_num, s->offset);
offset = eb_goto_bol(s->b, s->offset);
offsetl = offset;
len = eb_get_line(s->b, buf, countof(buf), &offsetl);
bullets = org_bullet_depth(buf, len);
if (bullets < 0)
return;
if (dir > 0)
eb_insert_uchar(s->b, offset, '*');
else if (dir < 0 && bullets > 0)
eb_delete_uchar(s->b, offset);
else
return;
bullets1 = bullets;
for (;;) {
offsetl = eb_next_line(s->b, offset);
if (offsetl == offset)
break;
offset = offseti = offsetl;
eb_get_pos(s->b, &line_num, &col_num, offseti);
len = eb_get_line(s->b, buf, countof(buf), &offseti);
bullets = org_bullet_depth(buf, len);
if (bullets < 0)
continue;
if (bullets <= bullets1)
break;
if (dir > 0)
eb_insert_uchar(s->b, offset, '*');
if (dir < 0 && bullets > 0)
eb_delete_uchar(s->b, offset);
}
}
static void do_org_metaleft(EditState *s)
{
do_org_promote(s, -1);
}
static void do_org_metaright(EditState *s)
{
do_org_promote(s, 1);
}
static void do_org_shiftmetaleft(EditState *s)
{
do_org_promote_subtree(s, -1);
}
static void do_org_shiftmetaright(EditState *s)
{
do_org_promote_subtree(s, 1);
}
static int org_mode_probe(ModeDef *mode, ModeProbeData *p)
{
/* just check file extension */
if (match_extension(p->filename, mode->extensions))
return 80;
return 0;
}
static int org_mode_init(EditState *s, __unused__ ModeSavedData *saved_data)
{
int ret;
ret = text_mode_init(s, saved_data);
if (ret)
return ret;
s->wrap = WRAP_TRUNCATE;
return 0;
}
/* Org mode specific commands */
static CmdDef org_commands[] = {
CMD2( KEY_CTRLC(KEY_CTRL('t')), KEY_NONE, /* C-c C-t */
"org-todo", do_org_todo, ES, "*")
CMD2( KEY_META(KEY_RET), KEY_NONE,
"org-meta-return", do_org_meta_return, ES, "*")
/*
CMD2( KEY_META_S('\n'), KEY_NONE,
"org-insert-todo-heading", do_org_insert_todo_heading, ES, "*")
*/
/* actually M-left */
CMD2( KEY_CTRL_LEFT, KEY_NONE,
"org-meta-left", do_org_metaleft, ES, "*")
/* actually M-right */
CMD2( KEY_CTRL_RIGHT, KEY_NONE,
"org-meta-right", do_org_metaright, ES, "*")
/* actually M-S-left */
CMD2( KEY_CTRLX('<'), KEY_NONE,
"org-shift-meta-left", do_org_shiftmetaleft, ES, "*")
/* actually M-S-right */
CMD2( KEY_CTRLX('>'), KEY_NONE,
"org-shift-meta-right", do_org_shiftmetaright, ES, "*")
CMD_DEF_END,
};
static int org_init(void)
{
memcpy(&org_mode, &text_mode, sizeof(ModeDef));
org_mode.name = "org";
org_mode.extensions = "org";
org_mode.mode_probe = org_mode_probe;
org_mode.mode_init = org_mode_init;
org_mode.colorize_func = org_colorize_line;
qe_register_mode(&org_mode);
qe_register_cmd_table(org_commands, &org_mode);
return 0;
}
qe_module_init(org_init);