# Texstudio macro to convert subsection-level headers to titlecase

I want to write a Texstudio macro (script) that scans the entire .tex file and converts text inside all \subsection{} directives to titlecase.

\subsection{this is the title of this subsection}

to

\subsection{This Is the Title of This Subsection}

I am trying this kind of scripting for the first time.

• This is not really on topic here. Also, there's no way to do this with a regex if you want some words (like articles and prepositions) not to be upper cased. – Alan Munn Oct 30 '17 at 5:42
• @AlanMunn I think the question is not clear enough and the OP just wants something like that in my answer... – koleygr Oct 30 '17 at 5:46
• I've taken the liberty of editing your posting a bit, since the query doesn't appear to be TeXstudio-specific. – Mico Oct 30 '17 at 20:09
• @Mico TeXstudio tag is relevant, I believe. TeXstudio has a built in function to change the selected text to Titlecase, so OP is asking how to invoke this using a TeXstudio user script. – Troy Oct 31 '17 at 2:24
• @Troy - Thanks! I hadn't been aware of this TeXstudio function. – Mico Oct 31 '17 at 5:56

As mentioned in my earlier comment, TeXstudio has an in-built "Convert to Titlecase" functionality under Edit -> Text Operations -> To Titlecase, which means there is no need for (1) complicated 'hacking' using TeX, and (2) considering of specific 'use cases' for Titlecase as per the other answers.

We can call this function in a TXS user macro, which was what I believe the OP was (originally) looking for.

### Disclaimer:

I don't claim that the script below is well-polished and suitable for all use cases (it isn't!). I am also unsure of the exact workings of the app.editTextToTitlecaseSmart() function, but from what I have tested so far, it seems to work well, even with acronyms in the subsection title (cf. Example 1). Some information on titlecasing is provided by the TXS manual here: http://texstudio.sourceforge.net/manual/current/usermanual_en.html#SECTION15

The following script was tested in TXS 2.12.6. As with all macros in TXS though, the function names (i.e., app.editTextToTitlecaseSmart() et al.) are liable to change by the developers in the future, so the macro shown below may not work for future versions of TXS (>2.12.6).

### Notes on use:

• Ensure that the \subsection{...} commands occupy their own lines as per my examples below. The only exception I would imagine is placing \label{..} after the \subsection call (cf. Example 1).

• I assume you want the short titles for the subsection (if present) to be converted to titlecase as well. (cf. Example 2).

• Please always double check if the conversion is done correctly by the user macro after invoking it. The code provides a "Script Report" in a pop-up dialog window so that you can review what has been changed -- the line number and the titles that have been converted to title case.

If you have confidence in my code (!!) and want to disable the Script Report pop-up, then you can just comment/delete the last line of the code: information(str);.

• A potential disadvantage of this method is that you will be unable to create exceptions for the titlecasing (I think), since it is all done by the app.editTextToTitlecaseSmart internal function.

# User script for "Automatic Conversion of Subsection Headers to Titlecase"

## Setting the macro

1. From the main toolbar, go to Macros -> Edit Macros.
2. Click Add + on the bottom left corner of the pop-up window to add a new user script.
3. Paste the following in the large box that says LaTeX content on the right side of the pop-up window:

%SCRIPT
/*  Note: Comment/Delete the last (optional) line if you
do not want the script report.
*/

var tl = editor.document().textLines();
var regEx = /\\subsection($.*$)*(\{((?!\\label).)*\})/;
var str = "Script Report:\n\n";
var match, scope, matchTC;

function titlecase(c){
editor.setCursor(c);
app.editTextToTitlecaseSmart();
}

for (var i=0;i<tl.length;i++){
match = regEx.exec(tl[i]);
scope = editor.document().cursor(i, 0, i);
// Titlecase the short titles [...]
if (match && match[1]){
editor.search(match[1], scope, titlecase)
matchTC = cursor.selectedText();
str += 'L' + (i+1).toString() + ' :: ' + matchTC + '\n';
}
// Titlecase the long titles {...}
if (match && match[2]){
editor.search(match[2], scope, titlecase)
matchTC = cursor.selectedText();
str += 'L' + (i+1).toString() + ' :: ' + matchTC + '\n';
}
}
editor.selectNothing();
information(str) // optional

## Testing the macro

Some test cases:

These are the examples that I tested with.

% Example 1: With label at the end + Acronyms not affected
\subsection{this should be in titlecase: NASA in a nutshell}\label{subsec:title}

% Example 2: Short titles also to be title-cased
\subsection[short titles]{capitalized every word except of the not needed in such cases}
\label{LabelOnANewLine}

% Example 3: Smart title-casing
\subsection{you can't {{{{escape}}}} 2!!}

% Example 4: Non-examples
\chapter{not this one}
\section{not this one too}
Normal text should not get affected as well.

% Example 5: Obscure example in a verbatim environment...
\verb|\subsection[the last example]{The last example in a long time...}|

Gif:

• Thank you very much Troy. This is the exact script I was looking for. – sar1729 Dec 18 '17 at 11:39
• @sar1729 You're welcome :) Let me know if you face any issues with this. I havent tested this extensively yet. – Troy Dec 18 '17 at 11:42
• How did you find the callback used by texstudio's internal option for converting to titlecase. I do not see it under app object list in the link you provided. – sar1729 Dec 18 '17 at 12:18
• Run the Introspection macro (found here) for app. i.e., Object.getOwnPropertyNames(app). It lists all possible functions for app in TXS. You can do the same for editor, cursor etc. – Troy Dec 18 '17 at 12:36

Edited According to @Mico's request

Here is an answer Old:(but I don't know if) New:(that I know) it works in TeXstudio:

\documentclass{article}
\usepackage{mfirstuc}
\usepackage{fancyhdr}
\usepackage{lipsum}
\MFUnocap{a} %We have to give the words we don't want to be automatically capitalized
\MFUnocap{non}
\MFUnocap{be}
\MFUnocap{of}
\MFUnocap{the}
\MFUnocap{in}
\MFUnocap{for}
\MFUnocap{etc}
\let\oldsubsection\subsection
\makeatletter
\def\subsection{%
\@ifstar{\@Starred}{\@nonStarred}%
}
\def\@Starred{%
\@ifnextchar[%
{\GenericWarning{}{Warning: A starred section can not have parameters. I am going to ignore them!}\@StarredWith}%
{\@StarredWithout}%
}
\def\@StarredWith[#1]#2{%
\oldsubsection*{\capitalisewords{#2}}%
}
\def\@StarredWithout#1{%
\oldsubsection*{\capitalisewords{#1}}%
}
\def\@nonStarred{%
\@ifnextchar[%
{\@nonStarredWith}%
{\@nonStarredWithout}%
}
\def\@nonStarredWith[#1]#2{%
\oldsubsection[\capitalisewords{#1}]{\capitalisewords{#2}}%
}
\def\@nonStarredWithout#1{%
\oldsubsection{\capitalisewords{#1}}%
}
\makeatother

\begin{document}
\pagestyle{fancy}
\tableofcontents
\section{test a non capitalized title}
\lipsum[1-2]
\subsection{this will be a capitalized simple subsection title}
\lipsum[1-5]
\section{non capitalized}
\lipsum[1]
\subsection[a capitalized toc and header title for now]{Capitalized every  word except of the not needed in such cases}
\lipsum[1-11]
\subsection*{a capitalized title}
\lipsum[1-3]
\subsection{capitalized a last title etc}
\lipsum
\end{document}
\end{document}

You have to add msfirstuc package and include the lines after \usepackage{lipsum} and before \begin{document}.

Output:

It was an old answer of mine from here: https://tex.stackexchange.com/a/378568/120578 that changed for subsection and without koma-script.

Edit: PS(to ignore as @AlanMunn mentioned): If can not imported to TeXStudio feel free to ask me to delete it.

• TeXStudio is an editor, so I assume this will work. But I think the OP wants to change the source code directly. :) But this is certainly one way to do that. – Alan Munn Oct 30 '17 at 5:47
• Thanks @AlanMunn... Every time I hear TeXStudio I thinking MikTeX... lol (editing)... – koleygr Oct 30 '17 at 5:49
• I just noticed I have an answer to that question too. :) – Alan Munn Oct 30 '17 at 5:49
• @AlanMunn... You don't have an answer there... You have the answer... :P – koleygr Oct 30 '17 at 5:54
• @Mico Edited. 2.Just added manually the words that I dont wont to be capitalized. 1. I had just a forgoten unclosed and unneeded if. (I suppose that you mean \subsection[hello there]{hello world} but there was no really problem with that... I thought about adding subsection with star and optional that changes the header... but didn't... have done it on other post) – koleygr Oct 31 '17 at 0:12

Here's a LuaLaTeX-based solution. It first upper-cases the lowercase letters at the start of all words in both the mandatory argument and the optional argument (if present) of \subsection. It then makes exceptions for the words "the", "of", and "a".

The only input requirement is that the string \subsection, its optional argument in square brackets (if present), and its mandatory argument in curly braces all be on one line -- no line breaks allowed. I trust this isn't a binding constraint.

% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{luacode}

\usepackage{luacode}
\begin{luacode*}
function my_upper ( z )
-- First, uppercase all starting lowercase letters ('%l' in Lua jargon)
-- "starting": %l is preceded by whitespace, {, or [
z = z:gsub ( "[%s{%[]%l" , string.upper )
-- Next, undo upper-casing for "of", "the" and "a"
z = z:gsub ( " Of " , string.lower )
z = z:gsub ( " The ", string.lower )
z = z:gsub ( " A "  ,  string.lower )
return z
end
function do_subsec ( s )
s = s:gsub ( "\\subsection%s*%b[]%s*%b{}", my_upper )
s = s:gsub ( "\\subsection%s*%b{}", my_upper )
return s
end
\end{luacode*}
"process_input_buffer", do_subsec, "do_subsec" )}}

\begin{document}
\section{this is the title of a section}
\subsection{this is the title of a subsection}
\subsubsection{this is the title of a subsubsection}
\bfseries hello world
\end{document}

The titlecaps package can help here. Additionally, it allows you to designate lowercase words that are screened in the arguments (using \Addlcwords{}), and automatically filters out punctuation when deciding what to capitalize.

\documentclass{article}
\usepackage{titlecaps,lipsum}
\let\svsubsection\subsection
\makeatletter
\renewcommand\subsection{\@ifstar{\subsectionstar}{\subsectionnostar}}
\makeatother
\newcommand\subsectionstar[2][]{\ifx\relax#1\relax\svsubsection*{\titlecap{#2}}\else
\svsubsection*[\titlecap{#1}]{\titlecap{#2}}\fi}
\newcommand\subsectionnostar[2][]{\ifx\relax#1\relax\svsubsection{\titlecap{#2}}\else
\svsubsection[\titlecap{#1}]{\titlecap{#2}}\fi}
\begin{document}
\tableofcontents
\section{test a non capitalized title}
\lipsum[4]

\subsection{this will be a capitalized simple subsection title}
\lipsum[4]

\section{non capitalized}
\lipsum[4]

\subsection[a capitalized toc and header title for now]{Capitalized every  word except of the not needed in such cases}
\lipsum[4]
\subsection*{a capitalized title}
\lipsum[4]
\subsection{capitalized a last title etc}
\lipsum[4]
\end{document}

This problem can be solved using TeX primitives:

% \def\subsection{{\bf#1}\par} %< use this if you are not using LaTeX (like me)

% code from OPmac:
\def\isinlist#1#2#3{\begingroup \long\def\tmp##1#2##2\end{\def\tmp{##2}%
\ifx\tmp\empty \endgroup \csname iffalse\expandafter\endcsname \else
\endgroup \csname iftrue\expandafter\endcsname \fi}%
\expandafter\tmp#1\endlistsep#2\end
}

\let\ssecOri=\subsection
\def\subsection#1{\def\tmp{}\docapA#1 {} \expandafter\ssecOri\expandafter{\tmp}}
\def\docapA#1#2 {%
\ifx^#1^\else
\isinlist\exceptions{,#1#2,}\iftrue
\else
\fi
\expandafter\docapA\fi
}
\def\exceptions{,a,non,be,of,the,in,for,etc,}

% test:
\subsection{this will be a capitalized simple subsection title}

\bye

Since you have asked for a macro script to convert the subsection into titlecase, you can try this script:

%SCRIPT
matches = triggerMatches;
title = matches[matches.length-1];
words=title.split(' ');
cursor.eraseLine();
editor.write("\\subsection{}");
cursor.movePosition(1,cursorEnums.Left);
editor.insertText(words[0][0].toUpperCase()+ words[0].substr(1).toLowerCase()+" ");
for (i=1; i<words.length-1; i++) {
if([";",":"].indexOf(words[i-1].slice(-1))>=0){
editor.insertText(words[i][0].toUpperCase()
+ words[i].substr(1).toLowerCase()+" ")
}
else{
if (["amid", "mid", "apud", "as", "at", "on", "atop", "bar", "but", "by",
"chez", "down", "for", "from", "in", "into", "less", "like", "near",
"of", "off", "on", "onto", "out", "over", "pace", "past", "per",
"post", "pre", "pro", "qua", "sans", "sauf", "than", "thru", "to",
"til", "up", "upon", " pon", "vs.", "via", "vice", "with", "w/",
"w/i", "w/o", "for", "and", "nor", "but", "or", "yet", "so", "a",
"an", "the", "some", "to"].indexOf(words[i].toLowerCase()) >= 0) {
editor.insertText(words[i][0].toLowerCase()
+ words[i].substr(1).toLowerCase()+" ");
}
else {
editor.insertText(words[i][0].toUpperCase()
+ words[i].substr(1).toLowerCase()+" ");
}
}
}
editor.insertText(words[words.length-1][0].toUpperCase()+ words[words.length-1].substr(1).toLowerCase());

You can add a triger, in my case the triger is:

(?<=\\subsection\{([^}]*)\})[t]

So when you just type the letter "t" after the braces of any subsection, it will change to titlecase.

• @Troy Now you can check the updated version. – m. bubu Nov 2 '17 at 18:46