![]() |
Interfacing TeXmacs with other programs | ![]() |
In this chapter we describe how to interface TeXmacs with an extern
application. Such interfaces should be distributed in the form of plugins. The plug-in may either contain the extern
application, or provide the “glue” between TeXmacs and
the application. Usually, interfaces are used interactively in shell
sessions (see
The communication between TeXmacs and the application takes place
using a customizable input format and the special TeXmacs
meta-format for output from the plug-in. The meta-format
enables you to send structured output to TeXmacs, using any common
format like verbatim, LaTeX,
As soon as basic communication between your application and TeXmacs
is working, you may improve the interface in many ways. Inside shell
sessions, there is support for prompts, default inputs,
tab-completion, mathematical and multi-line input, etc.
In general, your application may take control of TeXmacs and modify
the user interface (menus, keyboard, etc.) or add new
In the directory $TEXMACS_PATH/examples/plugins, you can find many examples of simple plug-ins. In the next sections, we will give a more detailed explanation of the interfacing features of TeXmacs on the hand of these examples. In order to try one of these examples, we recall that you just have to copy it to either one of the directories
$TEXMACS_PATH/plugins
$TEXMACS_HOME_PATH/plugins
and run the Makefile (if there is one).
The configuration and the compilation of the minimal plug-in is described in the chapter about plug-ins. We will now study the source file minimal/src/minimal.cpp. Essentially, the main routine is given by
int
main () {
display-startup-banner
while (true) {
read-input
display-output
}
return 0;
}
By default, TeXmacs just send a '\n'-terminated string to the application as the input. Consequently, the code for read-input is given by
char buffer[100];
cin.getline (buffer, 100, '\n');
The output part is more complicated, since TeXmacs needs to have a secure way for knowing whether the output has finished. This is accomplished by encapsulating each piece of output (in our case both the display banner and the interactive output) inside a block of the form
DATA_BEGINformat:messageDATA_END
Here DATA_BEGIN and DATA_END stand for special control characters:
#define DATA_BEGIN ((char) 2)
#define DATA_END ((char) 5)
#define DATA_ESCAPE ((char) 27)
The DATA_ESCAPE is used for producing the DATA_BEGIN and DATA_END characters in the message using the rewriting rules
DATA_ESCAPEDATA_BEGIN
⟶
DATA_BEGIN
DATA_ESCAPEDATA_END
⟶
DATA_END
DATA_ESCAPEDATA_ESCAPE
⟶
DATA_ESCAPE
The format specifies the format of the message. For instance, in our example, the code of display-startup-banner is given by
cout << DATA_BEGIN << "verbatim:";
cout << "Hi there!";
cout << DATA_END;
fflush (stdout);
Similarly, the code of display-output is given by
cout << DATA_BEGIN << "verbatim:";
cout << "You typed " << buffer;
cout << DATA_END;
fflush (stdout);
(plugin-configure myapp
(:require (url-exists-in-path? "myapp"))
(:launch "myapp --texmacs")
(:session "Myapp"))
In the case when you do not have the possibility to modify the source code of myapp, you typically have to write an input/output filter tm_myapp for performing the appropriate rewritings. By looking at the standard plug-ins distributed with TeXmacs in
$TEXMACS_PATH/plugins
you can find several examples of how this can be done.
In the previous section, we have seen that output from applications is encapsulated in blocks of the form
DATA_BEGINformat:messageDATA_END
In fact, the message may recursively
contain blocks of the same form. Currently implemented formats
include verbatim, latex, html,
ps, scheme. The scheme
format is used for sending TeXmacs trees in the form of
The formula plug-in demonstrates the use of LaTeX as the output format. It consists of the files
formula/Makefile
formula/progs/init-formula.scm
formula/src/formula.cpp
The body of the main loop of formula.cpp is given by
int i, nr;
cin >> nr;
cout << DATA_BEGIN << "latex:";
cout << "$";
for (i=1; i<nr; i++)
cout << "x_{" << i << "}+";
cout << "x_{" << i << "}$";
cout << DATA_END;
fflush (stdout);
Similarly, the use of nested output blocks is demonstrated by the nested plug-in; see in particular the source file nested/src/nested.cpp.
Nevertheless, we enriched standard LaTeX with the \* and \bignone commands for multiplication and closing big operators. This allows us to distinguish between
a \* (b + c)
(i.e. a multiplied by b + c) and
f(x + y)
(i.e. f applied to x + y). Similarly, in
\sum_{i=1}^m a_i \bignone + \sum_{j=1}^n b_j \bignone
the \bignone command is used in order to specify the scopes of the \sum operators.
It turns out that the systematic use of the \* and \bignone commands, in combination with clean LaTeX output for the remaining constructs, makes it a priori possible to associate an appropriate meaning to your output. In particular, this usually makes it possible to write additional routines for copying and pasting formulae between different systems.
It is important to remind that structured output can be combined
with the power of TeXmacs as a structured editor. For instance, the
markup plug-in demonstrates the definition of an
additional tag
markup/Makefile
markup/packages/session/markup.ts
markup/progs/init-markup.scm
markup/src/markup.cpp
The style package markup.ts
contains the following definition for
<with|mode|math|<assign|foo|<macro|x|<frac|1|1+x>>>>
The
char buffer[100];
cin.getline (buffer, 100, '\n');
cout << DATA_BEGIN << "latex:";
cout << "$\\foo{" << buffer << "}$";
cout << DATA_END;
fflush (stdout);
Notice that the style package markup.ts also defines the
<assign|markup-output|<macro|body|<generic-output|<with|par-mode|center|body>>>>
This has the effect of centering the output in sessions started
using
Besides blocks of the form
DATA_BEGINformat:messageDATA_END
the TeXmacs meta-format also allows you to use blocks of the form
DATA_BEGINchannel#messageDATA_END
Here channel specifies an “output channel” to which the body message has to be sent. The default output channel is output, but we also provide channels prompt and input for specifying the prompt and a default input for the next input in a session. Default inputs may be useful for instance be useful for demo modes of computer algebra systems. In the future, we also plan to support error and status channels.
The prompt plug-in shows how to use prompts. It consists of the files
prompt/Makefile
prompt/progs/init-prompt.scm
prompt/src/prompt.cpp
The routine for displaying the next prompt is given by
void
next_input () {
counter++;
cout << DATA_BEGIN << "prompt#";
cout << "Input " << counter << "] ";
cout << DATA_END;
}
This routine is both used for displaying the startup banner
cout << DATA_BEGIN << "verbatim:";
cout << "A LaTeX -> TeXmacs converter";
next_input ();
cout << DATA_END;
fflush (stdout);
and in the body of the main loop
char buffer[100];
cin.getline (buffer, 100, '\n');
cout << DATA_BEGIN << "verbatim:";
cout << DATA_BEGIN;
cout << "latex:$" << buffer << "$";
cout << DATA_END;
next_input ();
cout << DATA_END;
fflush (stdout);
The application may use command as a very particular
output format in order to send
DATA_BEGINcommand:cmdDATA_END
will send the command cmd to TeXmacs. Such commands are executed immediately after reception of DATA_END. We also recall that such command blocks may be incorporated recursively in larger DATA_BEGIN-DATA_END blocks.
The nested plug-in shows how an application can modify the TeXmacs menus in an interactive way. The plug-in consists of the files
menus/Makefile
menus/progs/init-menus.scm
menus/src/menus.cpp
The body of the main loop of menus.cpp simply contains
char buffer[100];
cin.getline (buffer, 100, '\n');
cout << DATA_BEGIN << "verbatim:";
cout << DATA_BEGIN << "command:(menus-add \""
<< buffer << "\")" << DATA_END;
cout << "Added " << buffer << " to menu";
cout << DATA_END;
fflush (stdout);
The
(menu-bind menus-menu
("Hi" (insert "Hello world")))
(menu-extend texmacs-extra-menu
(if (equal? (get-env "prog language") "menus")
(=> "Menus" (link menus-menu))))
(define-macro (menus-add s)
‘(menu-extend menus-menu
(,s (insert ,s))))
The configuration of menus proceeds as usual:
(plugin-configure menus
(:require (url-exists-in-path? "menus.bin"))
(:launch "menus.bin")
(:session "Menus"))
Until now, we have always considered interfaces between TeXmacs and
applications which are intended to be used interactively in shell
sessions. But there also exists a
(plugin-eval plugin session expression)
for evaluating an expression using the application. Here plugin
is the name of the plug-in, session the
name of the session and expression a
Background evaluations may for instance be used in order to provide a feature which allows the user to select an expression and replace it by its evaluation. For instance, the substitute plug-in converts mathematical LaTeX expressions into TeXmacs, and it provides the C-F12 keyboard shortcut for replacing a selected text by its conversion. The plug-in consists of the following files
substitute/Makefile
substitute/progs/init-substitute.scm
substitute/src/substitute.cpp
The main evaluation loop of substitute.cpp simply consists of
char buffer[100];
cin.getline (buffer, 100, '\n');
cout << DATA_BEGIN;
cout << "latex:$" << buffer << "$";
cout << DATA_END;
fflush (stdout);
Moreover, the configuration file init-substitute.scm contains the following code for replacing a selected region by its evaluation
(define (substitute-substitute)
(import-from (texmacs plugin plugin-cmd))
(if (selection-active-any?)
(let* ((t (tree->stree (the-selection)))
(u (plugin-eval "substitute" "default" t)))
(clipboard-cut "primary")
(insert (stree->tree u)))))
as well as the keyboard shortcut for C-F12:
(kbd-map
("C-F12" (substitute-substitute)))
Notice that these routines should really be defined in a separate module for larger plug-ins.
Another example of using an interface in the background is the secure plug-in which consists of the files
secure/Makefile
secure/packages/secure.ts
secure/progs/init-secure.scm
secure/progs/secure-secure.scm
secure/src/secure.cpp
Just as substitute.cpp above, the main program secure.cpp just converts mathematical LaTeX expressions
to TeXmacs. The secure-secure.scm module contains
the secure
(tm-define (latexer s)
(:type (tree -> object))
(:synopsis "convert LaTeX string to TeXmacs tree using plugin")
(:secure #t)
(plugin-eval "secure" "default" (tree->string s)))
It is important to define latexer as being secure,
so that it can be used in order to define additional markup using
the
<document|See
a LaTeX math command as a TeXmacs expression via
plug-in|<assign|latexer|<macro|x|<extern|latexer|x>>>>
After compilation, installation, relaunching TeXmacs and selecting
The TeXmacs meta-format allows application output to contain structured text like mathematical formulas. In a similar way, you may use general TeXmacs content as the input for your application. By default, only the text part of such content is kept and sent to the application as a string. Moreover, all characters in the range 0–31 are ignored, except for '\t' and '\n' which are transformed into spaces. There are two methods to customize the way input is sent to your application. First of all, the configuration option
(:serializer ,routine)
specifies a scheme function for converting TeXmacs trees to string input for your application, thereby overriding the default method. This method allows you for instance to treat multi-line input in a particular way or the perform transformations on the TeXmacs tree.
The :serialize option is a very powerful, but also a very abstract way to customize input: it forces you to write a complete input transformation function. In many circumstances, the user really wants to rewrite two dimensional mathematical input to a more standard form, like rewriting
| a |
| b |
(plugin-input-converters myplugin
rules)
This command specifies input conversion rules for myplugin for “mathematical input” and reasonable defaults are provided by TeXmacs. Each rule is of one of the following two forms:
Given two strings symbol and conversion, the rule
(symbol conversion)
specifies that the TeXmacs symbol symbol should be converted to conversion.
Given a symbol tag and a
(tag routine)
specifies that routine will be used as
the conversion routine for tag. This
routine should just write a string to the standard output. The
The input plug-in demonstrates the use of customized mathematical input. It consists of the files
input/Makefile
input/packages/session/input.ts
input/progs/init-input.scm
input/progs/input-input.scm
input/src/input.cpp
The
(plugin-configure input
(:require (url-exists-in-path? "input.bin"))
(:initialize (input-initialize))
(:launch "input.bin")
(:session "Input"))
Here input-initialize is an initialization routine which adds the new input conversion rules in a lazy way:
(define (input-initialize)
(import-from (texmacs plugin plugin-convert))
(lazy-input-converter (input-input) input))
In other words, the module input-input.scm will only be loaded when we explicitly request to make a conversion. The conversion rules in input-input.scm are given by
(plugin-input-converters input
(frac input-input-frac)
(special input-input-special)
("<vee>" "||")
("<wedge>" "&&"))
This will cause ∨ and ∧ to be rewritten as || and && respectively. Fractions
| a |
| b |
(define (input-input-frac t)
(display "((")
(plugin-input (car t))
(display "):(")
(plugin-input (cadr t))
(display "))"))
In the additional style file input.ts we also
defined some additional markup
<assign|special|<macro|body|<block|<tformat|<cwith|1|1|1|1|cell-background|pastel green>|<table|<row|<cell|body>>>>>>>
This tag is rewritten using the special conversion rule
(define (input-input-special t)
(display "[[[SPECIAL:")
(plugin-input (car t))
(display "]]]"))
As to the
cout << DATA_BEGIN << "verbatim:";
cout << DATA_BEGIN << "command:(session-use-math-input #t)"
<< DATA_END;
cout << "Convert mathematical input into plain text";
cout << DATA_END;
fflush (stdout);
In the main loop, we content ourselves the reproduce the input as output:
char buffer[100];
cin.getline (buffer, 100, '\n');
cout << DATA_BEGIN << "verbatim:";
cout << buffer;
cout << DATA_END;
fflush (stdout);
By default, TeXmacs looks into your document for possible tab-completions. Inside sessions for your application, you might wish to customize this behaviour, so as to complete built-in commands. In order to do this, you have to specify the configuration option
(:tab-completion #t)
in your init-myplugin.scm file, so that TeXmacs will send special tab-completion requests to your application whenever you press tab inside a session. These commands are of the form
DATA_COMMAND(complete input-string cursor-position)return
Here DATA_COMMAND stands for the special character '\20' (ASCII 16). The input-string is the complete string in which the tab occurred and the cursor-position is an integer which specifies the position of the cursor when you pressed tab. TeXmacs expects your application to return a tuple with all possible tab-completions of the form
DATA_BEGINscheme:(tuple
root completion-1 ⋯
completion-n)DATA_END
Here root corresponds to a substring before the cursor for which completions could be found. The strings completion-1 until completion-n are the list of completions as they might be inserted at the current cursor position. If no completions could be found, then you may also return the empty string.
A very rudimentary example of how the tab-completion mechanism works is given by the complete plug-in, which consists of the following files:
complete/Makefile
complete/progs/init-complete.scm
complete/src/complete.cpp
The startup banner in complete.cpp takes care of part of the configuration:
cout << DATA_BEGIN << "verbatim:";
format_plugin ();
cout << "We know how to complete 'h'";
cout << DATA_END;
fflush (stdout);
Here format_plugin is given by
void
format_plugin () {
// The configuration of a plugin can be completed at startup time.
// This may be interesting for adding tab-completion a posteriori.
cout << DATA_BEGIN << "command:";
cout << "(plugin-configure complete (:tab-completion #t))";
cout << DATA_END;
}
In the main loop, we first deal with regular input:
char buffer[100];
cin.getline (buffer, 100, '\n');
if (buffer[0] != DATA_COMMAND) {
cout << DATA_BEGIN << "verbatim:";
cout << "You typed " << buffer;
cout << DATA_END;
}
We next treat the case when a tab-completion command is sent to the application:
else {
cout << DATA_BEGIN << "scheme:";
cout << "(tuple \"h\" \"ello\" \"i there\" \"ola\" \"opsakee\")";
cout << DATA_END;
}
fflush (stdout);
As you notice, the actual command is ignored, so our example is really very rudimentary.
Instead of connecting your system to TeXmacs using a pipe, it is also possible to connect it as a dynamically linked library. Although communication through pipes is usually easier to implement, more robust and compatible with gradual output, the second option is faster.
In order to dynamically link your application to TeXmacs, you should follow the TeXmacs communication protocol, which is specified in the following header file:
$TEXMACS_PATH/include/TeXmacs.h
In this file it is specified that your application should export a data structure
typedef struct package_exports_1 {
char* version_protocol; /* "TeXmacs communication protocol 1" */
char* version_package;
char* (*install) (TeXmacs_exports_1* TeXmacs,
char* options, char** errors);
char* (*evaluate) (char* what, char* session, char** errors);
} package_exports_1;
which contains an installation routine for your application, as well as an evaluation routine for further input (for more information, see the header file). TeXmacs will on its turn export a structure
typedef struct TeXmacs_exports_1 {
char* version_protocol; /* "TeXmacs communication protocol 1" */
char* version_TeXmacs;
} TeXmacs_exports_1;
It is assumed that each application takes care of its own memory management. Hence, strings created by TeXmacs will be destroyed by TeXmacs and strings created by the application need to be destroyed by the application.
The string version_protocol should contain "TeXmacs communication protocol 1" and the string version_package the version of your package. The routine install will be called once by TeXmacs in order to initialize your system with options options. It communicates the routines exported by TeXmacs to your system in the form of a pointer to a structure of type TeXmacs_exports_1. The routine should return a status message like
"yourcas-version successfully linked to TeXmacs"
If installation failed, then you should return NULL and *errors should contain an error message.
The routine evaluate is used to evaluate the expression what inside a TeXmacs-session with name session. It should return the evaluation of what or NULL if an error occurred. *errors either contains one or more warning messages or an error message, if the evaluation failed. The formats being used obey the same rules as in the case of communication by pipes.
Finally, the configuration file of your plug-in should contain something as follows:
(plugin-configure myplugin
(:require (url-exists? (url "$LD_LIBRARY_PATH" "libmyplugin.so")))
(:link "libmyplugin.so" "myplugin_exports" "")
further-configuration)
Here myplugin_exports is a pointer to a structure of the type package_exports_1.
The dynlink plug-in gives an example of how to write dynamically linked libraries. It consists of the following files:
dynlink/Makefile
dynlink/progs/init-dynlink.scm
dynlink/src/dynlink.cpp
The Makefile contains
tmsrc = /home/vdhoeven/texmacs/src/TeXmacs
CXX = g++
LD = g++
lib/libtmdynlink.so: src/dynlink.cpp
$(CXX) -I$(tmsrc)/include -c
src/dynlink.cpp -o src/dynlink.o
$(LD) -shared -o lib/libtmdynlink.so
src/dynlink.o
so that running it will create a dynamic library dynlink/lib/libdynlink.so from dynlink.cpp. The tmsrc variable should contain $TEXMACS_PATH, so as to find the include file TeXmacs.h. The configuration file init-dynlink.scm simply contains
(plugin-configure dynlink
(:require (url-exists? (url "$LD_LIBRARY_PATH"
"libtmdynlink.so")))
(:link "libtmdynlink.so" "dynlink_exports" "")
(:session "Dynlink"))
As to the
static char* output= NULL;
with the last output, the initialization routine
char*
dynlink_install (TeXmacs_exports_1* TM, char* opts, char** errs) {
output= (char*) malloc (50);
strcpy (output, "\2verbatim:Started dynamic link\5");
return output;
}
the evaluation routine
char*
dynlink_eval (char* what, char* session, char** errors) {
free (output);
output= (char*) malloc (50 + strlen (what));
strcpy (output, "\2verbatim:You typed ");
strcat (output, what);
strcat (output, "\5");
return output;
}
and the data structure with the public exports:
package_exports_1 dynlink_exports= {
"TeXmacs communication protocol 1",
"Dynlink 1",
dynlink_install,
dynlink_eval
};
Notice that the application takes care of the memory allocation and deallocation of output.
Several other features are supported in order to write interfaces between TeXmacs and extern applications. Some of these are very hairy or quite specific. Let us briefly describe a few miscellaneous features:
The “stop” icon can be used in order to interrupt the evaluation of some input. When pressing this button, TeXmacs will just send a SIGINT signal to your application. It expects your application to finish the output as usual. In particular, you should close all open DATA_BEGIN-blocks.
Some systems start a multiline input mode as soon as you start to define a function or when you enter an opening bracket without a matching closing bracket. TeXmacs allows your application to implement a special predicate for testing whether the input is complete. First of all, this requires you to specify the configuration option
(:test-input-done #t)
As soon as you will press return in your input, TeXmacs will then send the command
DATA_COMMAND(input-done? input-string)return
Your application should reply with a message of the form
DATA_BEGINscheme:doneDATA_END
where done is either #t or #f. The multiline plug-in provides an example of this mechanism (see in particular the file multiline/src/multiline.cpp).
There are many improvements to be made in the TeXmacs interface to computer algebra systems. First of all, the computer algebra sessions have to be improved (better hyphenation, folding, more dynamic subexpressions, etc.).
As to interfaces with computer algebra systems, out main plans consist of providing tools for semantically safe communication between several system. This probably will be implemented in the form of a set of plug-ins which will provide conversion services.