-
Notifications
You must be signed in to change notification settings - Fork 347
Scripts
This page will become a collection of user scripts which provides additional features that are too special to be implemented in the binary itself.
A description of the scripting language can be found in the User Manual. Notice that the available Javascript functions may change in later TeXstudio releases.
Txs has been expanded to download scripts/macros directly from texstudio-macro. The most convenient way for developers is to clone that repository, add macros and hand them in as pull-requests. Alternatively enhancement requests with attached macro files are welcome. Scripts on this wiki are less likely to be taken up in the repository.
Feel free to add your own scripts.
- Trivial eval example
- Copy filename to clipboard
- Remove all empty lines
- Remove all duplicated lines
- Remove whitespace at the end of all lines
- Decode hex dumps
- Calculator
- Macro virus
- (tikz) Coordinate pair mover
- Frequency counter
- Sorting
- Pseudo-LaTeX generator
- Change search panels to be case sensitive
- Convert selected text to uppercase
- Generate environment after typing trigger code
- Auto-correct multiple capital letters
- Paste as LaTeX table
- Word Completion
- Introspection
- Controlling Wrapping
- Switching between no wrapping and soft wrapping at the window edge
- Underlining with ulem-package
- Replace characters by their LaTeX equivalent while typing
- Simultaneously toggle Structure, Log and PDF
- Advanced Comment Toggle
- Time tracker
- WakaTime Plugin
- Mark Indexentry
- Git Commit and Push
- Move cursor to the beginning of the text in the line
- Periodic auto-save all documents under a new name
- Protecting Capitalization of BibTeX Titles
- Key Replacement: Automatically insert <space> before comment
- Automatic label creation by pressing space after a structure command
- Finding local line numbers in a verbatim environment
- Finding the next sectioning command
- Insert citation and open completion menu
- Insert a \end{env} when hitting
Return
and the cursor is behind \begin{env} - Quoting a selection
- Insert backslash and start completion with "§"
- Selecting a string beginning with $ and ending with $
- Select the current section
- Repeat last citation
- Toggle Wrap/Fill Paragraph To Maximum Line Length
- Collapse all tikzpicture elements
- ChatGPT for TeXstudio
This example shows how you can write a simple script that executes the current editor text as another script. It can also be used when writing new scripts, because it is faster than opening the user macro dialog for every change.
%SCRIPT
eval(editor.text());
Tested with: TMX 1.9.9 or later
%SCRIPT
app.clipboard = editor.fileName();
Tested with: TXS 2.3
%SCRIPT
var tl = editor.document().textLines();
for (var i=tl.length-1;i>=0;i--)
if (tl[i]=="")
tl.splice(i, 1);
editor.setText(tl.join("\n"));
Tested with: TXS 2.3
%SCRIPT
var tl = editor.document().textLines();
for (var i=tl.length-1;i>=0;i--) {
var found = false;
if (tl[i] != "")
for (var j=i-1;j>=0;j--)
if (tl[i] == tl[j]) {
found = true;
break;
}
if (found) tl.splice(i, 1);
}
editor.setText(tl.join("\n"));
Tested with: TXS 2.3
%SCRIPT
var tl = editor.document().textLines();
for (var i=tl.length-1;i>=0;i--)
tl[i] = tl[i].replace(/\s+$/, '');
editor.setText(tl.join("\n"));
Tested with: TXS 2.8.0
%SCRIPT
editor.replace(/[0-9A-Fa-f]{2}/, "g", function(c){
return String.fromCharCode(1*("0x"+c.selectedText()));
})
Tested with: TXS 2.3
This is a calculator evaluates a mathematical expression on the current line, like %sin(3.1415)=
%SCRIPT
currentLine=editor.text(cursor.lineNumber());
from=currentLine.lastIndexOf("%")+1;
to=currentLine.lastIndexOf("=");
if (from>=0 && to > from) {
toEvaluate = currentLine.substring(from, to);
with (Math) { value = eval(toEvaluate);}
cursor.eraseLine();
cursor.insertText(currentLine.substring(0, from)+toEvaluate+"="+value);
cursor.insertLine();
cursor.movePosition(1,cursorEnums.Left );
}
Tested with: TMX 1.9.9 or later
Copy it at the beginning of a tex file and it will copy itself.
% !TeX TXS-SCRIPT = macrovirus
% //Trigger: ?load-file | ?new-file | ?new-from-template
%var self;
%if (hasGlobal("macrovirus")) self = getGlobal("macrovirus");
%else {
% var l1, l2 = -1;
% editor.search(/% *!TeX *TXS-SCRIPT *= *macrovirus/, function(c){l1 = c.lineNumber();});
% editor.search(/% *TXS-SCRIPT-END/, function(c){if (l2 != -1 || l1 > c.lineNumber()) return; l2 = c.lineNumber();});
% self = "";
% for (var l=l1;l<=l2;l++)
% self = self + editor.text(l) + "\n";
% setGlobal("macrovirus", self);
%}
%
%for (var i=0;i<documents.length;i++)
%if (documents[i].editorView.editor.search( /% *!TeX *TXS-SCRIPT *= *macrovirus/ ) == 0) {
%documents[i].cursor(0).insertText(self);
%}
% TXS-SCRIPT-END
This script adds to all coordinate pairs in the currently selected text the offset of the first pair, which translates all pairs in a given direction. E.g. if you have (1 + 1, 2 - 1.5) (3, 4) it will be changed to (2, 0.5) (4, 2.5).
%SCRIPT
var doit = function(){
var mytext=cursor.selectedText();
var regExNumberPre = " *[0-9]+([.][0-9]*)? *";
var regExDigit = /[0-9]/;
var regExSpace = / /g;
var regExPairPre = " *(-?"+regExNumberPre+")";
var regExPair = new RegExp("()[(]"+regExPairPre+","+regExPairPre+"[)]"); ;
//read first coordinate pair
var regExFirstPairPre = regExPairPre + " *([+-]"+regExNumberPre+")?";
var regExFirstPair = new RegExp("()[(]"+regExFirstPairPre+","+regExFirstPairPre+"[)]");
//extract offsets (start regex search from first digit, to allow -x - y)
var matches = regExFirstPair.exec(mytext);
if (matches == null) throw "missing";
//throw matches;
var offsetXPre = matches[4];
var offsetYPre = matches[8];
if (offsetXPre == "" && offsetYPre == "") throw "abc";
var offsetX = offsetXPre == ""?0.0:offsetXPre.replace(regExSpace, "")*1.0;
var offsetY = offsetYPre == ""?0.0:offsetYPre.replace(regExSpace, "")*1.0;
//move first pair
var matchpos = mytext.search(regExFirstPair);
editor.write(mytext.slice(0,matchpos));
editor.write("("+(matches[2].replace(regExSpace, "")*1.0+offsetX));
editor.write(", "+(matches[6].replace(regExSpace, "")*1.0+offsetY)+")");
//move other pairs
var remaining = mytext.slice(matchpos+matches[0].length);
while (remaining != ""){
matches = regExPair.exec(remaining);
if (matches == null) break;
matchpos = remaining.search(regExPair);
editor.write(remaining.slice(0,matchpos));
remaining = remaining.slice(matchpos+matches[0].length);
editor.write("(" + ((matches[2].replace(regExSpace, "")*1.0)+offsetX) + ", "+ ((matches[4].replace(regExSpace, "")*1.0)+offsetY) + ")");
}
editor.write(remaining);
}
doit();
Tested with: TMX 1.9.9
Count how often every letter in the selection/document is used.
%SCRIPT
var s = cursor.hasSelection()?cursor.selectedText():editor.text();
var hash = {};
for (var i=0; i < s.length; i++ )
hash[s[i]] = (hash[s[i]]==null?0:hash[s[i]]) + 1;
cursor.movePosition(cursorEnums.End);
for (var k in hash)
cursor.insertText(k+": "+hash[k]+"\n");
Tested with: TMX 2.1
Sorts all lines in the document
%SCRIPT
var a = new Array();
for (var i=0; i<editor.document().lineCount(); i++) {
a.push(editor.text(i));
}
a.sort();
var t = "";
for (var l in a) {
t+= a[l]+"\n";
}
editor.setText(t);
Tested with: TMX 2.1
This script creates a random text with LaTeX-commands. This is not much useful for itself, but it can be used to find bugs/crashes of TeXstudio. Notice that it is quite slow (~1 min) and that the generated text does not actually compile.
%SCRIPT
var characters=" ,.,.,,.,.;:;.+_^-.;/()[]{}[],aabcdeeeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzzABCDEFGHIJKLMNOPQRSTUVWXYZäöüÄÖÜÖÄÖÄÜ1234567890";
var charactersSafe=" ,.,.,,.,.;:;.+-.;/()[][],aabcdeeeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzzABCDEFGHIJKLMNOPQRSTUVWXYZäöüÄÖÜÖÄÖÄÜ1234567890";
var cid1 = "aB";
var cid2 = "abcdefghij";
function randUntil(i) { return Math.floor(Math.random()*i); }
function insertRandomString(cs){
for (var j=randUntil(100);j>0;j--)
editor.insertText(cs[Math.floor(Math.random()*cs.length)]);
}
function insertRandomId(){
switch (randUntil(2)) {
case 0: for (var i=4+randUntil(5);i>0;i--) editor.insertText(cid1[randUntil(cid1.length)]); break;
case 1: for (var i=2;i>0;i--) editor.insertText(cid2[randUntil(cid2.length)]); break;
}
}
totalLoopCutOff = 1500;
function insertRandomLaTeX(deep){
for (var i=50+deep*10;i>0;i--) {
totalLoopCutOff--;
if (totalLoopCutOff<0) return;
switch (Math.floor(Math.random()*16)) {
case 0: editor.insertText(Math.random()); break;
case 1: case 2: insertRandomString(characters); break;
case 3:
editor.insertText("$"); if (deep>0) insertRandomLaTeX(deep-1); editor.insertText("$");
break;
case 4: editor.insertText("\\chapter{");insertRandomString(charactersSafe); editor.insertText("}"); break;
case 5: editor.insertText("\\section{");insertRandomString(charactersSafe); editor.insertText("}"); break;
case 6: editor.insertText("\\");editor.insertText("sectio");editor.insertText("n");editor.insertText("{");insertRandomString(charactersSafe); editor.insertText("}"); break;
case 7: case 8: case 9: editor.insertText("\n"); break;
case 10: editor.insertText("\\label{"); insertRandomId(); editor.insertText("}"); break;
case 11: editor.insertText("\\ref{"); insertRandomId(); editor.insertText("}"); break;
case 12: editor.insertText("\\cite{"); insertRandomId(); editor.insertText("}"); break;
case 13: editor.insertText("\\include{"); insertRandomId(); editor.insertText("}"); break;
case 14: if (Math.random() < 0.5) editor.insertText("%TODO"); insertRandomString(characters); break;
case 15: editor.insertText("\\begin{block}"); if (deep>0) insertRandomLaTeX(deep-2); editor.insertText("\\end{block}"); break;
}
}
}
editor.insertText("\\documentclass[12pt,letterpaper]{report}\n");
editor.insertText("\\begin{document}\n");
insertRandomLaTeX(4);
editor.insertText("\\end{document}\n");
//alert("insert complete");
cursor.movePosition(1, cursorEnums.End);
var lineCount = cursor.lineNumber();
for (var i=1; i<10000;i++){
cursor.setLineNumber(randUntil(lineCount));
cursor.movePosition(1, cursorEnums.EndOfLine);
var lineLength = cursor.columnNumber()+1;
cursor.setColumnNumber(randUntil(lineLength));
if (Math.random()<0.75) cursor.insertText(characters[randUntil(characters.length)]);
else cursor.deleteChar();
}
Tested with: TMX 2.0
%SCRIPT
//Set the search panel of the editor
editor.searchPanel.findChild("cbCase").checked = true;
//Set the search panel of the pdf viewer
pdfs[0].search.findChild("cbCase").checked = true;
Tested with: TXS 2.4
Note: From version 2.7 on, there is native support for changing case at Edit -> Text Operations
. You won't need this script anymore. However we'll leave it here as example code.
%SCRIPT
// Converts selected text to uppercase
cursor.replaceSelectedText(cursor.selectedText().toUpperCase())
Tested with: TXS 2.5
When typing !mabc (ends with space), the following is generated:
\begin{multicols}{abc}
\end{multicols}"
Trigger: !m(\S+)\s
%SCRIPT
env = triggerMatches[1]
txt = "\\begin{multicols}{"+env+"}\n"
txt = txt+"\n"
txt = txt+"\\end{multicols}\n"
cursor.insertText(txt)
cursor.movePosition(2, cursorEnums.Up)
cursor.movePosition(1, cursorEnums.StartOfLine)
Auto-correct multiple capital letters at the start of a word while typing. All characters except the first are converted to lowercase. Words consisting only of capital letters are not affected by the auto-correction.
Trigger: [a-z]
%SCRIPT
typedChar = triggerMatches[0]
cursor.movePosition(1, cursorEnums.StartOfWord, cursorEnums.KeepAnchor)
lastWord = cursor.selectedText()
re = /^[A-Z]{2,}/g
if (lastWord.match(re)) {
cursor.movePosition(1, cursorEnums.NextCharacter, cursorEnums.KeepAnchor)
cursor.replaceSelectedText(cursor.selectedText().toLowerCase())
}
cursor.movePosition(1, cursorEnums.EndOfWord)
cursor.clearSelection()
cursor.insertText(typedChar)
Tested with: TXS 2.9.4
When you copy multiple rows and/or columns from a spreadsheet application, columns are usually separated by tabs and rows by newline characters. This script takes the text from the clipboard and converts it into the LaTeX table format. The example creates a simple tabular
with left-aligned columns. You are free to adapt it to your needs.
%SCRIPT
text = app.clipboard
numCols = text.split('\n')[0].split('\t').length
colspec = Array(numCols+1).join("l")
text = text.replace(/\t/g, " & ")
text = text.replace(/\n/g, " \\\\\n")
text = "\\begin{tabular}{" + colspec + "}\n" + text + "\\end{tabular}\n"
cursor.insertText(text)
Tested with: TXS 2.11.2
Note: Since TeXstudio natively provides word completion, this script mainly serves for demonstration. To use the built-in word completion, simply type the start of the word (at least 3 letters) and then hit Ctrl+Space.
Having to type long words can be annoying, particularly with technical terms which may appear quite frequently in a text. The below script checks if the already typed characters match with the start of a word from a fixed word list. If so, the word is inserted. It is convenient to assign a handy shortcut such as Shift+Enter to the script.
%SCRIPT
// simple script for automatic word completion
var words = ["trans-impedance-amplifier", "Dzyaloshinskii-Moriya interaction"]
cursor.movePosition(1, cursorEnums.WordLeft, cursorEnums.KeepAnchor)
txt = cursor.selectedText()
for (var i=0; i<words.length; i++) {
if (words[i].indexOf(txt) == 0) {
cursor.removeSelectedText()
editor.write(words[i])
break
}
}
if (cursor.hasSelection()) {
cursor.moveTo(cursor.anchorLineNumber(), cursor.anchorColumnNumber())
}
Tested with: TXS 2.5
The following script lists all properties of the editor object. Of course, you can adapt it to inspect other objects.
%SCRIPT
var pn = Object.getOwnPropertyNames(editor)
editor.write(pn.join("\n"))
Tested with: TXS 2.5
The following script allows you to set the line wrapping of the editor. Note: This only controls the editor. Changes are not reflected in the settings dialog. After accessing the settings dialog, the original value from the settings take over again. See the next example for a way to adjust the settings as well.
%SCRIPT
// Set the wrap style of the editor
// ***** configuration *****
/* possible wrap styles are
0: None
1: Soft wrap at window edge
2: Soft wrap after lineWidth chars
3: Hard wrap after lineWidth chars
*/
wrapStyle = 3
/* only relevant for styles 2 and 3 */
lineWidth = 60 // number of chars
averageCharWidth = 8 // depends on the actual font being used
// ***** code *****
editor.setLineWrapping(wrapStyle > 0);
editor.setSoftLimitedLineWrapping(wrapStyle == 2);
editor.setHardLineWrapping(wrapStyle > 2);
if (wrapStyle > 1) {
if (lineWidth < 20) lineWidth = 20;
lineWidthInPixels = lineWidth * averageCharWidth
editor.setWrapLineWidth(lineWidthInPixels);
} else {
editor.setWrapLineWidth(0);
}
Tested with: TXS 2.5.2
This example shows how to toggle between two ways of wrapping (no wrapping and soft wrapping at the window edge). Settings are updated accordingly.
%SCRIPT
if (getPersistent("Editor/WordWrapMode") == "1"){
editor.setLineWrapping(false);
setPersistent("Editor/WordWrapMode","0")
} else {
editor.setLineWrapping(true);
setPersistent("Editor/WordWrapMode","1")
}
editor.setSoftLimitedLineWrapping(false);
editor.setHardLineWrapping(false);
Teseted with: TXS 2.10.4
If you are using the ulem
package and want to use the command \ulem{}
instead of \underline{}
, this little script detects if the package is loaded and inserts the correct command automatically.
%SCRIPT
packages = editor.document().containedPackages();
usingUlem = false;
for (i=0; i<packages.length; i++) {
if (packages[i] == "ulem") {
usingUlem = true;
break;
}
}
if (usingUlem) {
editor.document().editorView.insertMacro('\\uline{%|}');
} else {
editor.document().editorView.insertMacro('\\underline{%|}');
}
For languages with accented chars it is convenient to type the chars on the keyboard and have them translated to LaTeX code on the fly. This can be achieved by the following script.
Additionally, you have to set the trigger of the script to a regular expression matching all required chars. If there are only a few special chars, a simple or-list is ok: ä|ö|ü.
In case of many replacements the more generic \w
may be used as trigger.
Trigger: ä|ö|u
or
Trigger: \w
%SCRIPT
replacements = {
"ä": "\\'a",
"ö": "\\'o",
"ü": "\\'u",
}
c = triggerMatches
if (c in replacements) {
c = replacements[c]
}
editor.write(c)
This was a request by a user who wished to simultaneously hide Structure, Log and PDF to have maximal space for editing. Moreover they should be restored afterwards easily.
For convenience, you may assign a shortcut to the macro via Options -> Configure TeXstudio -> Shortcuts
.
%SCRIPT
visible = app.getManagedAction("main/view/show/outputview").checked
app.getManagedAction("main/view/show/outputview").trigger(!visible)
app.getManagedAction("main/view/show/structureview").trigger(!visible)
if (visible) {
if (pdfs.length > 0) pdfs[0].close();
} else {
app.getManagedAction("main/tools/view").trigger()
}
Note: As of TXS 2.11.2 there is a built-in function will be a builtin function Idefix -> Toggle Comment
. Therefore this script is obsolete. However it may still serve as an example for similar tasks.
This script toggles the comments on the selected lines. It is a block-wise operation, i.e. all lines are commented/uncommented depending on the first selected line. This allows to properly comment/uncomment larger blocks which already contain comments. The block-operation gracefully handles uncommenting also for lines that do not contain a comment. Additionally, a space is inserted (and removed) after the comment char for better readability.
%SCRIPT
startLine = cursor.anchorLineNumber()
endLine = cursor.lineNumber()
if (endLine < startLine) {
tmp = startLine
startLine = endLine
endLine = tmp
}
nextChar = function() {return String.fromCharCode(cursor.nextChar())}
cursor.beginEditBlock()
cursor.moveTo(startLine, 0)
line = editor.text(cursor.lineNumber());
hasComment = line.match(/^\s*%/)
if (hasComment) {
// uncomment
for (l=startLine; l<=endLine; l++) {
cursor.moveTo(l, 0)
while (nextChar() in [' ', '\t']) cursor.movePosition(1, cursorEnums.NextCharacter);
if (nextChar() == '%') cursor.deleteChar();
if (nextChar() == ' ') {
cursor.movePosition(1, cursorEnums.NextCharacter);
if (nextChar() != ' ') { // not an indentation
cursor.deletePreviousChar();
}
}
}
} else {
// comment
for (l=startLine; l<=endLine; l++) {
cursor.moveTo(l, 0)
cursor.insertText('% ')
}
}
cursor.endEditBlock()
Takes a list of times (e.g. 13:14-15:16 or 52m, each on a separate line) and calculates the total time.
%SCRIPT
//alert(editor.document().lineCount());
var r = /^ *([0-9]+):([0-9]+) *- *([0-9]+)+:([0-9]+)/;
var r2 = /([0-9]+) *m *$/;
var nt = "";
var totald = 0;
for (var i=0; i<editor.document().lineCount(); i++) {
var e = r.exec(editor.text(i));
if (e) {
var fh = e[1] * 1;
var fm = e[2] * 1;
var th = e[3] * 1;
var tm = e[4] * 1;
var d = 0;
if (fh == th) d += tm - fm + 1;
else if (fh < th) {
d += 60 - fm + tm + 60 * (th - fh - 1) + 1;
} else alert("daybreak!");
nt += e[0]+" => "+d+"m";
totald += d;
} else {
nt += editor.text(i);
e = r2.exec(editor.text(i));
if (e) { totald += e[1] * 1; }
}
nt += "\n";
}
nt += "\n\n ==> "+ totald + " = " + Math.floor(totald / 60) +":"+ (totald % 60);
alert(nt);
Tested with: TXS 2.6.6
This example shows how you can time track using WakaTime. https://github.com/wakatime/texstudio-wakatime
Tested with: TMX 2.6.6 or later
This script marks the word "under the cursor" as an indexentry with \index{foo}foo
. Use it with a Hotkey like Shift+F1 for a faster running.
%SCRIPT
cursor.select(cursorEnums.WordUnderCursor)
idx = cursor.selectedText()
cursor.movePosition(0,cursorEnums.StartOfWord)
editor.setCursor(cursor)
editor.write("\\index{"+idx+"}")
cursor.movePosition(0,cursorEnums.EndOfWord)
editor.setCursor(cursor)
cursor.clearSelection()
Tested with: TXS 2.8.0
Simple Git commit and push support. Use it maybe with the ?save-file
trigger. Needs privileged write mode to run! It will ask for it.
%SCRIPT
choisedialog = UniversalInputDialog(["Commit","Commit with Push"],"Git","choiseGIT")
choisedialog.setWindowTitle("Git")
choise = choisedialog.get("comment")
if (choisedialog.exec() != null) {
if (choisedialog.get("choiseGIT") == "Commit") {
dialog = new UniversalInputDialog()
dialog.setWindowTitle("Git commit / push")
dialog.add("Committed by TeXstudio", "Comment", "comment")
dialog.add(true, "Commit all Files","allfiles")
if (dialog.exec() != null) {
comment = dialog.get("comment")
if ((dialog.get("allfiles")) == true){
buildManager.runCommand("git commit -a -m \"" + comment + "\"", editor.fileName())
}else{
buildManager.runCommand("git commit " + editor.fileName() + " -m \"" + comment + "\"", editor.fileName())
}
}
} else
if (choisedialog.get("choiseGIT") == "Commit with Push") {
dialog = new UniversalInputDialog()
dialog.setWindowTitle("Git commit / push")
dialog.add("Committed by TeXstudio", "Comment", "comment")
dialog.add("master", "Branch", "branch")
dialog.add(true, "Commit all Files","allfiles")
if (dialog.exec() != null) {
comment = dialog.get("comment")
branch = dialog.get("branch")
if ((dialog.get("allfiles")) == true){
buildManager.runCommand("git commit -a -m \"" + comment + "\"", editor.fileName())
}else{
buildManager.runCommand("git commit " + editor.fileName() + " -m \"" + comment + "\"", editor.fileName())
}
buildManager.runCommand("git push origin \"" + branch +"\"", editor.fileName())
}
}
}
Tested with: TXS 2.6.6
Simply pressing the Home key moves the cursor to the start of the line. There is no native functionality to go to the start of the text (i.e. after the indentation). This can be accomplished with the following script. Pressing Home again, move it to the default beginning of the line.
%SCRIPT
pos = cursor.columnNumber();
cursor.movePosition(1, cursorEnums.StartOfLine);
i = 0;
while (cursor.nextChar()==' '.charCodeAt(0) ||
cursor.nextChar()=='\t'.charCodeAt(0))
{
cursor.movePosition(1, cursorEnums.NextCharacter);
i++;
}
if (pos == i)
{
cursor.movePosition(1, cursorEnums.StartOfLine);
}
As a second step, go to Options -> Configure... -> Shortcuts and assign Home to that new script.
Tested with: TXS 2.9.4This script periodically saves all open documents under a new name (uses the _bak suffix). Use the ?txs-start
trigger to activate the script upon TXS launch.
%SCRIPT
var Debug = false; //default = false
var DoSave = true; //default = true
var Interval = 60000;// set the time in milliseconds. 60000=1mins
registerAsBackgroundScript("auto_save");
setTimeout(TimedFunction,Interval);
function TimedFunction() {
if (Debug) { alert('timer expired') };
SaveAllDocuments();
setTimeout(TimedFunction,Interval); //rearm the timed function
}
function SaveAllDocuments(){
if (Debug) { alert('SaveAllDocuments called') };
var NumOfDocs = documentsManager.documents.length;
if (Debug) { alert('NumOfDocs='+NumOfDocs) };
for (var i = 0; i < NumOfDocs; i++) {
SaveDocument(i, documentsManager.documents[i]);
};
};
function SaveDocument(i, Document) {
var CurFileName = Document.fileName;
if (Debug) { alert('i='+i+' FileName='+CurFileName) };
var newName = CurFileName.replace(/.tex$/, '_bak.tex');
if (Debug) { alert('i='+i+' newName='+newName) };
if (DoSave) { writeFile(newName, Document.text()); };
};
Tested with: TXS 4.0.2
BibTeX automatically changes the capitalization of titles according to the settings in the BibTeX style. Sometimes it is necessary to protect capital letters to prevent BibTeX changing their case; e.g.
title = "Pascal, {C}, {Java}: were they all conceived in {ETH}?"
The following script capitalizes and protects the first letter of the word under the cursor:
%SCRIPT
c = cursor;
c.movePosition(1, cursorEnums.StartOfWord);
c.insertText('{');
c.movePosition(1, cursorEnums.NextCharacter, cursorEnums.KeepAnchor);
c.replaceSelectedText(c.selectedText().toUpperCase());
c.clearSelection();
c.insertText('}');
Tested with: TXS 2.7.0
If you always want to have a space between your text and a comment, you may let TXS automatically insert a space if you type a comment sign (%) right behind a non-space. To do so, you can use a positive lookbehind regular expression as a trigger:
Trigger: (?language:latex)(?<=\\S)%
Type: normal
Text: Note: the first character is a space. The % is doubled for escaping.
%%
If you want a label created for any section, subsection, chapter, etc. that you add, this script auto-generates a label after pressing <space>
behind the structure command (e.g. \chapter{Foo bar}<space>
). The label is preceded by a corresponding prefix and the title is sanitized (e.g. \chapter{Foo bar}\label{ch:foo-bar}
).
TXS provides label generation through the right-click menu in the structure view, but this is IMHO quicker and provides different prefixes for different types of structures.
How to use it:
Create a macro and use the following as trigger:
(?<=\\((sub)*section|chapter|part|paragraph)\{([^}]*)\})[ ]
As LaTeX content use:
%SCRIPT
// all matches
matches = triggerMatches;
// the structure title
title = matches[matches.length-1];
// shorten to three words
title = title.match(/^([^ ]+ ){0,2}([^ ]+)?/i)[0];
// sanitize title
title = title.replace(/[äàáâã]/gi,"a");
title = title.replace(/[ëèéêẽ]/gi,"e");
title = title.replace(/[ïìíîĩ]/gi,"i");
title = title.replace(/[öòóôõ]/gi,"o");
title = title.replace(/[üùúûũ]/gi,"u");
title = title.replace(/[ç]/gi,"c");
title = title.replace(/\W/gi,"-").toLowerCase();
// remove ending dash
title = title.replace(/-$/, '');
// get long type
type = matches[2];
prefixes = {
"subsubsection": "sec",
"subsection": "sec",
"section": "sec",
"chapter": "ch",
"part": "part",
"paragraph": "pg"
}
// replace type by short type
if (type in prefixes) {
type = prefixes[type];
}
// output
editor.write("\\label{"+type+":"+title+"}");
Adjust the prefixes to your liking.
Tested with: TXS 2.9.4
This script finds the local line number within a verbatim environment (e.g., useful if you want to reference it in the surrounding text).
To use: Place the cursor at the desired line and run the following script (it's convenient to assign a shortcut for running the script):
%SCRIPT
lines = editor.document().textLines();
for (var i=cursor.lineNumber(); i>=0; i--) {
l = lines[i];
if (l.indexOf("\\begin{verbatim}") >= 0) {
ln = cursor.lineNumber() - i;
app.setClipboardText(ln)
alert('Local line number in itemize:\n' + ln + '\n(Copied to clipboard)');
}
}
This can easily adapted for other environments like minted
or listings
.
Tested with: TXS 2.9.4
This script moves the cursor to the next sectioning command. More information can be found in this tex.stackexchange post. It's just a demonstration and easily adaptable for other commands.
%SCRIPT
commands = ["\\part",
"\\chapter",
"\\section",
"\\subsection",
"\\subsubsection",
"\\paragraph"];
while (!cursor.atEnd()) {
cursor.movePosition(1, cursorEnums.NextWord)
if (cursor.nextChar() != '\\'.charCodeAt(0)) {
continue;
}
cursor.movePosition(1, cursorEnums.NextCharacter, cursorEnums.KeepAnchor);
cursor.movePosition(1, cursorEnums.EndOfWord, cursorEnums.KeepAnchor);
if (commands.indexOf(cursor.selectedText()) >= 0) {
cursor.setColumnNumber(cursor.anchorColumnNumber())
editor.scrollToFirstLine(cursor.lineNumber())
break;
}
}
Tested with: TXS 2.11.2
This simple script fills in the LaTeX citation command and opens the completion menu so that you can choose the correct reference. You can replace \\autocite{}
with other LaTeX commands, i.e. \\cite{}
.
%SCRIPT
editor.write("\\autocite{}");
cursor.shift(-1);
app.normalCompletion()
Tested with: TXS 2.10.6
This script is somewhat similar to Idefix -> Complete -> Close latest open environment
. However it's specifically intended as an auto-completion when hitting Return
. To achieve this, bind the Return
key to the macro using Options -> Shortcuts
.
%SCRIPT
previousChar = function() {return String.fromCharCode(cursor.previousChar())}
nextChar = function() {return String.fromCharCode(cursor.nextChar())}
String.prototype.startsWith = function(searchString, position) {
position = position || 0;
return this.indexOf(searchString, position) === position;
};
getClosingEnvironment = function() {
// returns \end{env} if the cursor is at the end of \begin{env}
// returns undefined otherwise
if (previousChar() == '}') {
cursor.movePosition(1, cursorEnums.Left, cursorEnums.KeepAnchor);
}
while (nextChar() != '{') {
if (cursor.atLineStart())
break;
cursor.movePosition(1, cursorEnums.Left, cursorEnums.KeepAnchor);
}
cursor.movePosition(1, cursorEnums.Left, cursorEnums.KeepAnchor);
cursor.movePosition(1, cursorEnums.StartOfWordOrCommand, cursorEnums.KeepAnchor);
var result;
if (cursor.selectedText().startsWith("\\begin")) {
result = cursor.selectedText().replace("\\begin", "\\end");
}
cursor.flipSelection();
cursor.clearSelection();
return result;
}
var env = getClosingEnvironment();
cursor.insertLine();
if (env !== undefined) {
cursor.insertLine();
cursor.insertText(env);
cursor.movePosition(1, cursorEnums.PreviousLine);
}
Tested with: TXS 2.11.0
Assume that you would like to put double quotes around a word or a group of words. This script simplifies the process: Simply select the group of words and type a double quote. The trigger defined here is for single quotes, double quotes and $, but it can be adapted to other chars as well.
Trigger: "|'|\$
%SCRIPT
c = triggerMatches;
if (editor.cutBuffer.length) {
editor.insertText(c + editor.cutBuffer + c);
} else {
editor.insertText(c);
}
Tested with: TXS 2.12.6
On some keyboard layouts, backslash is tedious to type. Hence, other symbols maybe easier to use as a starter to insert LaTeX commands.
Trigger: §
%SCRIPT
cursor.insertText("\\")
app.normalCompletion();
Tested with: TXS 2.12.8
This script assumes the mouse cursor is somewhere in between two $
symbols on the same line, i.e., $...$
, and highlights/selects everything in between. This functionality is similar to that of the Ctrl+D keyboard shortcut, which selects the single word that the cursor is on.
Note that the script does not check whether the content between the $
s are in a math environment. That is, if you have There are $x$ apples| and $y$ oranges.
where |
is the cursor, then apples and
will become selected.
%SCRIPT
// grab the line the cursor is on
var ln = cursor.lineNumber();
var tl = editor.text(ln);
// get text before and after cursor
var beforeText = tl.substr(0, cursor.columnNumber());
var afterText = tl.substr(cursor.columnNumber(), tl.length);
// search for last $ before cursor, and first $ after cursor
var op = beforeText.lastIndexOf('\$');
var cl = afterText.indexOf('\$');
if (op>=0 && cl>=0){
// match found, select everything between the $$
cursor.selectColumns(op+1, cl+cursor.columnNumber())
}
Tested with: TXS 2.12.11
This script finds the last usage of a "/cite" command and creates a new citation to the same reference.
%SCRIPT
var lineNo = cursor.lineNumber()
while (lineNo > 0) {
splits = editor.text(lineNo).split("\\cite")
//loop to find last line with a citation:
if (splits.length > 1) {
// identify last citation
endingString = splits[splits.length - 1]
patt = /\{([^\}]*)\}/
var reference = endingString.match(patt)[1]
editor.write("\\citep{"+reference+"}")
lineNo = 0
}
lineNo = lineNo - 1
}
Tested with: TXS 2.12.22
Highlight the subsection you are currently in. Add with shortcut Ctrl+M, Ctrl+S. Repeat call to select higher order (containing section, chapter, part, etc).
%SCRIPT
commands = ["\\part",
"\\chapter",
"\\section",
"\\subsection",
"\\subsubsection",
"\\paragraph"];
maxIndex=commands.length
startL=0
startN=0
sectionName="\\part"
if (cursor.selectedText().match(/^\\[a-z]{1,}/)) {
starter=cursor.selectedText().match(/^\\[a-z]{1,}/)[0]
if (commands.indexOf(starter) >= 0) {
maxIndex=commands.indexOf(starter)
}
}
while (!cursor.atStart()) {
cursor.movePosition(1, cursorEnums.PreviousWord)
if (cursor.nextChar() != '\\'.charCodeAt(0)) {
continue;
}
cursor.movePosition(1, cursorEnums.NextCharacter, cursorEnums.KeepAnchor);
cursor.movePosition(1, cursorEnums.EndOfWord, cursorEnums.KeepAnchor);
if (commands.indexOf(cursor.selectedText()) >= 0) {
if (commands.indexOf(cursor.selectedText()) < maxIndex) {
sectionName=cursor.selectedText()
cursor.movePosition(1, cursorEnums.PreviousWord)
startN=cursor.columnNumber()//anchorColumnNumber
startL=cursor.lineNumber()
break;
}}
cursor.movePosition(1, cursorEnums.PreviousWord)
}
cursor.movePosition(1, cursorEnums.NextWord, cursorEnums.KeepAnchor)
// now select forwards for the end of section
while (!cursor.atEnd()) {
cursor.movePosition(1, cursorEnums.NextWord, cursorEnums.KeepAnchor)
if (!cursor.selectedText().match(/\\[a-z]{1,}$/)) {
continue;
}
endTerm=cursor.selectedText().match(/\\[a-z]{1,}$/)[0]
if (commands.indexOf(endTerm) >= 0) {
if (commands.indexOf(endTerm) <= commands.indexOf(sectionName)) {
cursor.movePosition(1, cursorEnums.PreviousWord, cursorEnums.KeepAnchor)
endN=cursor.columnNumber()//anchorColumnNumber
endL=cursor.lineNumber()
break;
}}
}
editor.scrollToFirstLine(startL)
Tested with: TXS 2.12.22
See #1483
%SCRIPT
// Script: Toggle-Wrap-Paragraph
//
// Wraps paragraphs at word boundaries to lines of no more than given length.
// If a selection is active, wraps the selection. Otherwise, wraps the text block
// at the current cursor position.
//
// Repeated activation will toggle between wrapping/unwrapping the paragarph
//
// This functionality is usually bound to Alt+q.
//
// NOTE: TXS supports this out of the box via the menu item "Idefix/Hard Line Break...". You can
// customize this script to tweak the behavior, and to invoke it immediately without clicking
// through a dialog as when using the built-in version.
var MAX_LINE_WIDTH = 80;
var MAX_PAR_LINES = 100; // avoid infinite loop
function wrap_paragraph(s, line_width) {
// split into words
var words=s.split(/\s+/);
var lines = [];
// form words into lines as strings
var length = 0;
var i = 0;
for (; i < words.length; i++) {
if( i> 0 && length + (i) + words[i].length >= line_width ) {
lines.push(words.slice(0,i).join(" "));
words = words.slice(i);
i = -1;
length = 0;
continue;
}
length += words[i].length;
}
// ... and don't forget any partial last line
if ( length > 0) {
lines.push(words.slice(0,i).join(" "));
}
// form the resulting wrapped string
var result=lines.join("\n");
// To maintain whitespace to next block, restore any trailing newlines.
var match_crlf_suffix = /[\r\n]+$/.exec(s);
if (match_crlf_suffix !== null) {
result += match_crlf_suffix[0];
}
return result;
}
function SelectParagraph() {
/* cursorEnums.StartOfBlock misbehaves when text contains
math environments. So we implement this functionality explicitly.
*/
cursor.movePosition(1,cursorEnums.StartOfLine);
for (var i =0; i < MAX_PAR_LINES && ! cursor.atStart() ; i++) {
if( editor.text(cursor.lineNumber()) === "") {
break;
}
cursor.movePosition(1,cursorEnums.PreviousLine);
cursor.movePosition(1,cursorEnums.StartOfLine)
}
cursor.movePosition(1, cursorEnums.NextLine);
var start_line = cursor.lineNumber();
for (var i =0; i < MAX_PAR_LINES ; i++) {
if( "" === editor.text(cursor.lineNumber())) {
break;
}
cursor.movePosition(1,cursorEnums.NextLine);
}
cursor.movePosition(1, cursorEnums.PreviousLine);
var end_line = cursor.lineNumber();
cursor.moveTo(start_line, 0);
cursor.movePosition(end_line-start_line, cursorEnums.NextLine,cursorEnums.KeepAnchor);
cursor.movePosition(1, cursorEnums.EndOfLine,cursorEnums.KeepAnchor);
}
// if no selection is active, select the current text block
if (!cursor.hasSelection()) {
SelectParagraph();
}
// Do it
var original_text = cursor.selectedText();
var wrapped = wrap_paragraph(cursor.selectedText(), MAX_LINE_WIDTH);
if (original_text == wrapped) {
editor.replaceSelectedText(wrapped.replace(/\n/g," "));
} else {
editor.replaceSelectedText(wrapped);
}
cursor.clearSelection();
Tested with TXS 3.1.0.
See #1729
%SCRIPT
envname = "tikzpicture";
lines = editor.document().textLines();
for (var i=0; i<lines.length; i++) {
l = lines[i];
if (l.indexOf("\\begin{" + envname + "}")>-1) {
editor.document().collapse(i);
}
}
Tested with TXS 3.1.2 / 4.0.0