Celebrating 1000 Downloads of my VS Code extension
How it started
About a year ago I had to write a report for a class about Apache Spark. I was writing the report using Latex in Visual Studio Code and felt that the workflow of citing references could be improved by having some kind of reference manager. That's when I decided, this would be a good project to implement over the summer break.
In the beginning, I thought of building an iPad app that would allow me to load PDFs, annotate them, and automatically generate a citation file for them. A VSCode extension should then take care of updating the current project with the appropriate citations.
I realized that I was making things unnecessarily complicated so I scrapped my working prototype and focused on keeping everything inside VSCode. So the BibTeX Manager was born. After several iterations I was finally feeling happy with the extension and released the first major update: version 1.0.0
.
At that time, I only developed this extension to speed up my own workflow. I saw some interest from my peers at the University of Stavanger but I never imagined the extension getting more than a couple downloads. But as it turns out, there is a considerable chunk of the Latex writing community using VS Code to craft their reports and a very small part of that group was interested in my extension.
90 day acquisition trend
A users request
Over the few months that have passed since release 1.0.0, I have received several messages about the extension on GitHub. Most of them were concerned about the extension not running correctly on their system. However one of them was from a professor from Colorado who requested the ability to see whether the label of the BibTeX entry was set correctly.
It was great to see interest in the extension. I decided to implement the feature in a way that minimizes the user input but maximizes individual adaptability. I came up with a pattern system in which users can choose from a palette of different commands to construct their keys.
\a(n)
: The last names of the firstn
authors.\Y
: The full year.\y
: The last two digits of the year.\T(n)
: The firstn
words of the title.\t(n)
: The first letter of the firstn
words in the title.\D
: The DOI.
In addition, the user can input (0)
for n
to get all of the authors' names or all of the titles' words.
I was hoping to allow the user to tailor their BibTeX key more to their needs but also keep the needed input low. Below I will show some examples of key patterns and their resulting keys.
'\a(1)\y\t(3)': "Vaswani23AIA"
'\T(0).\Y' : "AttentionIsAllYouNeed.2023"
'\t(0)ABC\D' : "AIAYNABC1706.03762"
Implementation of the update
In the following section, I will take a quick dive into the details of how the BibTeX key is created. To achieve this, I populate a dictionary with objects of a custom Paper
class, when displaying the search results. The constructor of this class makes sense of the JSON obtained from the semantic scholar API and allows me to call the .getBibTexKey()
function on it.
getBibTexKey(bibKeyPattern: string): { success: boolean, key: string } {
// | Pattern | Description |
// |----------|---------------------------------------------------------------|
// | `\a(n)` | The last names of the first n authors. |
// | `\Y` | The full year. |
// | `\y` | The last two digits of the year. |
// | `\T(n)` | The first n words of a title. |
// | `\t(n)` | Only the beginning letters of the first n words in the title. |
// | `\D` | DOI. |
let success = true;
let key = bibKeyPattern;
key = key.replace(/\\a\((\d)\)/g, (match, n) => this.getFirstAuthorsLastName(parseInt(n)) || '\!ERROR!');
key = key.replace(/\\Y/g, this.year?.toString() || '\!ERROR!');
key = key.replace(/\\y/g, this.getShortYear() || '\!ERROR!');
key = key.replace(/\\T\((\d)\)/g, (match, n) => this.getShortTitle(parseInt(n)) || '\!ERROR!');
key = key.replace(/\\t\((\d)\)/g, (match, n) => this.getFirstLetterTitle(parseInt(n)) || '\!ERROR!');
key = key.replace(/\\D/g, this.doi || '\!ERROR!');
// If there are still any backslashes, it means that the pattern is invalid
if (key.includes('\!ERROR!')) {
success = false;
// remove the error markers
key = key.replace(/\!ERROR!/g, '')
}
return { success, key };
}
As you can see, this function simply replaces the occurrences using regular expressions. To catch if information for one of the specified patterns is missing, I simply replace the pattern with \!ERROR!
instead. This might seem hacky but compared to the workaround which inflates the code immensely and introduces risk for errors, I think it's good enough.
Handling missing values
As pointed out by the user on GitHub, sometimes the fetched BibTeX will not have a valid key. In that case, the extension should notify the user about the issue and allow the user to enter a key on their own. Again, I wanted to keep the manual work to a minimum so I decided to handle an invalid key differently for the two cases:
- The user works with patterns and part of the specified data was not available.
- The user does not work with patterns but the key is invalid.
If only part of the key pattern could be realized, the user will see the incomplete key as a starting point when entering their own. For case two, a key with the first letters of the title(\t(0)
) will be used. This way, the user has the best of both worlds: The possibility to individualize their results, a quick and easy workflow, and the assurance that they will be notified when exceptions happen.
Warning about the invalid key.
Prefilled text field to allow manual input of a key.
About what's next
In the months that this extension has existed, it has seen continuous updates and has helped me to write multiple reports and publications. Some updates that I want to do for the future include citation generation for websites, a form for manual citation creation, and a browser extension for Overleaf (?).
If you haven't done so yet, please check out the extension in VS Code.
I am very proud and thankful that others around the world are using this little project of mine. Big thanks <3