evil-qemacs/latex-mode.c

340 lines
10 KiB
C

/*
* LaTeX mode for QEmacs.
*
* Copyright (c) 2003 Martin Hedenfalk <mhe@home.se>
* Based on c-mode by Fabrice Bellard
* Requires the shell mode
*
* 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"
#define MAX_BUF_SIZE 512
/* TODO: add state handling to allow colorization of elements longer
* than one line (eg, multi-line functions and strings)
*/
static void latex_colorize_line(unsigned int *buf, __unused__ int len,
int *colorize_state_ptr,
__unused__ int state_only)
{
int c, state;
unsigned int *p, *p_start;
state = *colorize_state_ptr;
p = buf;
p_start = p;
for (;;) {
p_start = p;
c = *p;
switch (c) {
case '\0':
case '\n': /* Should not happen */
goto the_end;
case '`':
p++;
/* a ``string'' */
if (*p == '`') {
for (;;) {
p++;
if (*p == '\0') {
/* Should either flag an error or propagate
* string style to the next line
*/
break;
}
if (*p == '\'' && p[1] == '\'') {
p += 2;
break;
}
}
set_color(p_start, p, QE_STYLE_STRING);
}
break;
case '\\':
p++;
/* \function[keyword]{variable} */
if (*p == '\'' || *p == '\"' || *p == '~' || *p == '%' || *p == '\\') {
p++;
} else {
while (*p != '\0' && *p != '{' && *p != '[' && *p != ' ' && *p != '\\')
p++;
}
set_color(p_start, p, QE_STYLE_FUNCTION);
while (*p == ' ' || *p == '\t') {
/* skip space */
p++;
}
while (*p == '{' || *p == '[') {
if (*p++ == '[') {
/* handle [keyword] */
p_start = p;
while (*p != '\0' && *p != ']')
p++;
set_color(p_start, p, QE_STYLE_KEYWORD);
if (*p == ']')
p++;
} else {
int braces = 0;
/* handle {variable} */
p_start = p;
while (*p != '\0') {
if (*p == '{') {
braces++;
} else
if (*p == '}') {
if (braces-- == 0)
break;
}
p++;
}
set_color(p_start, p, QE_STYLE_VARIABLE);
if (*p == '}')
p++;
}
while (*p == ' ' || *p == '\t') {
/* skip space */
p++;
}
}
break;
case '%':
p++;
/* line comment */
while (*p != '\0')
p++;
set_color(p_start, p, QE_STYLE_COMMENT);
break;
default:
p++;
break;
}
}
the_end:
*colorize_state_ptr = state;
}
static int latex_mode_probe(ModeDef *mode, ModeProbeData *p)
{
/* currently, only use the file extension */
/* Halibut (by Simon Tatham) has a syntax similar to TeX and uses
* .but extension */
if (match_extension(p->filename, mode->extensions))
return 80;
/* Match TeX style sheets if they start with a comment */
if (match_extension(p->filename, "sty") && p->buf[0] == '%')
return 80;
return 0;
}
static void do_tex_insert_quote(EditState *s)
{
int offset_bol, len, offset1;
unsigned int buf[MAX_BUF_SIZE];
int pos;
offset_bol = eb_goto_bol2(s->b, s->offset, &pos);
offset1 = offset_bol;
len = eb_get_line(s->b, buf, countof(buf), &offset1);
if (pos > len)
return;
if (pos >= 1 && buf[pos-1] == '\"') {
s->offset += eb_insert_uchar(s->b, s->offset, '\"');
} else
if (pos >= 2 && (buf[pos-1] == '`' || buf[pos-1] == '\'') &&
buf[pos-1] == buf[pos-2])
{
eb_delete_chars(s->b, s->offset, -2);
s->offset += eb_insert_uchar(s->b, s->offset, '\"');
} else {
if (pos == 0 || buf[pos-1] == ' ') {
s->offset += eb_insert_str(s->b, s->offset, "``");
} else {
s->offset += eb_insert_str(s->b, s->offset, "''");
}
}
}
static struct latex_function {
const char *name;
const char *fmt;
int ask;
int output_to_buffer;
StringArray history;
EditState *es;
} latex_funcs[] = {
#define INIT_TAIL NULL_STRINGARRAY, NULL
{ "AmSTeX", "amstex '\\nonstopmode\\input %s'", 0, 1, INIT_TAIL },
{ "PDFLaTeX", "pdflatex '\\nonstopmode\\input{%s}'", 0, 1, INIT_TAIL },
{ "PDFTeX", "pdftex '\\nonstopmode\\input %s'", 0, 1, INIT_TAIL },
{ "Check", "lacheck %s", 0, 1, INIT_TAIL },
{ "BibTeX", "bibtex %s", 0, 1, INIT_TAIL },
{ "LaTeX", "latex --src-specials '\\nonstopmode\\input{%s}'", 0, 1, INIT_TAIL },
{ "ThumbPDF", "thumbpdf %s", 0, 1, INIT_TAIL },
{ "View", "xdvi %s.dvi -paper a4", 1, 0, INIT_TAIL },
{ "Print", "dvips %s -Plp", 1, 0, INIT_TAIL },
{ "File", "dvips %s.dvi -o %s.ps", 1, 1, INIT_TAIL },
{ NULL, NULL, 0, 0, INIT_TAIL },
#undef INIT_TAIL
};
static void latex_completion(CompleteState *cp)
{
struct latex_function *func;
for (func = latex_funcs; func->name; func++) {
if (strxstart(func->name, cp->current, NULL))
add_string(&cp->cs, func->name);
}
}
static struct latex_function *find_latex_func(const char *name)
{
struct latex_function *func;
for (func = latex_funcs; func->name; func++) {
if (!strxcmp(func->name, name))
return func;
}
return NULL;
}
static void latex_cmd_run(void *opaque, char *cmd)
{
struct latex_function *func = (struct latex_function *)opaque;
char cwd[MAX_FILENAME_SIZE];
char dir[MAX_FILENAME_SIZE];
char *p;
int len;
if (cmd == NULL) {
put_status(func->es, "Aborted");
return;
}
getcwd(cwd, sizeof(cwd));
/* get the directory of the open file and change into it
*/
p = strrchr(func->es->b->filename, '/');
if (p == func->es->b->filename)
p++;
len = p - func->es->b->filename;
pstrncpy(dir, sizeof(dir), func->es->b->filename, len);
chdir(dir);
if (func->output_to_buffer) {
/* if the buffer already exists, kill it */
EditBuffer *b = eb_find("*LaTeX output*");
if (b) {
/* XXX: e should not become invalid */
b->modified = 0;
do_kill_buffer(func->es, "*LaTeX output*");
}
/* create new buffer */
b = new_shell_buffer(NULL, "*LaTeX output*", NULL, cmd,
SF_COLOR | SF_INFINITE);
if (b) {
/* XXX: try to split window if necessary */
switch_to_buffer(func->es, b);
}
} else {
int pid = fork();
if (pid == 0) {
const char *argv[4];
/* child process */
setsid();
argv[0] = get_shell();
argv[1] = "-c";
argv[2] = cmd;
argv[3] = NULL;
execv(argv[0], (char * const*)argv);
exit(1);
}
}
chdir(cwd);
}
static void do_latex(EditState *e, const char *cmd)
{
char bname[MAX_FILENAME_SIZE];
char buf[1024];
struct latex_function *func;
/* strip extension from filename */
pstrcpy(bname, sizeof(bname), e->b->filename);
strip_extension(bname);
if (!cmd || cmd[0] == '\0')
cmd = "LaTeX";
/* check what command to run */
func = find_latex_func(cmd);
if (func) {
/* pass the EditState through to latex_cmd_run() */
func->es = e;
/* construct the command line to run */
strsubst(buf, sizeof(buf), func->fmt, "%s", bname);
if (func->ask) {
char prompt[128];
snprintf(prompt, sizeof(prompt), "%s command: ", func->name);
minibuffer_edit(buf, prompt, &func->history,
NULL /* completion */,
latex_cmd_run, func);
} else {
latex_cmd_run(func, buf);
}
} else {
put_status(e, "%s: No match", buf);
}
}
/* specific LaTeX commands */
static CmdDef latex_commands[] = {
CMD2( '\"', KEY_NONE,
"tex-insert-quote", do_tex_insert_quote, ES, "*")
CMD2( KEY_CTRLC(KEY_CTRL('c')), KEY_NONE, /* C-c C-c */
"TeX-command-master", do_latex, ESs,
"s{Command: (default LaTeX) }[latex]|latex|")
CMD_DEF_END,
};
static ModeDef latex_mode;
static int latex_init(void)
{
/* LaTeX mode is almost like the text mode, so we copy and patch it */
memcpy(&latex_mode, &text_mode, sizeof(ModeDef));
latex_mode.name = "LaTeX";
latex_mode.extensions = "tex|but";
latex_mode.mode_probe = latex_mode_probe;
latex_mode.colorize_func = latex_colorize_line;
qe_register_mode(&latex_mode);
qe_register_cmd_table(latex_commands, &latex_mode);
register_completion("latex", latex_completion);
return 0;
}
qe_module_init(latex_init);