Up until now, Iโ€™ve been using a single functions.ts file containing all functions. To keep the implementation of different aspects of the extension more separate, Iโ€™ve decided to move all functions related to todos to a separate file.

This way, the functions donโ€™t need to be exported anymore, because registration can happen in a separate function within the file.

context.subscriptions.push(
    vscode.commands.registerCommand('blogging.findTodo', () => {
        functions.findTodo(findTodoOutputChannel);
    }),
    vscode.commands.registerCommand('blogging.createTodo', functions.createTodo),
    (...)
);

now becomes

todo.registerCommands(context);

with registerCommands having the following implementation

export function registerCommands(context: vscode.ExtensionContext) {
    const findTodoOutputChannel = vscode.window.createOutputChannel("Find TODO");
 
    context.subscriptions.push(
        vscode.commands.registerCommand('blogging.findTodo', () => {
            findAll(findTodoOutputChannel);
        }),
        vscode.commands.registerCommand('blogging.createTodo', create)
    );
}

Notice that now that the implementation functions are not exported, the names can be simplified.

Improvement: Introduce dedent function

Kotlin has the extremely useful trimIndent function to strip indentation from a multiline string, greatly improving readability. JavaScript unfortunately does not offer that functionality, so it must be implemented using a custom function. With this function, the test code can be refactored from

test('parse article metadata', () => {
    let content = `---
URL: test-url
Edit URL: edit-url
---
 
URL: some-random-unimportant-url`;
    
    (...)
});

to

test('parse article metadata', () => {
    let content = lib.dedent`---
                             URL: test-url
                             Edit URL: edit-url
                             ---
 
                             URL: some-random-unimportant-url`;
    
    (...)
});

Bug: Show all lines of the todo item and show output automatically

Showing the output automatically is simply a call to outputChannel.show(). Displaying all lines of a todo item is done by changing the regex from

/^<!--TODO:\n*(.*)\n*-->/gm

to

/^<!--TODO:\n*(.*?)\n*-->/gms;

The changes are adding the s-flag which makes the . match newlines as well and adding ? to avoid matching multiple todos together.

I took the opportunity to improve the test-data because naturally the existing unit test wasnโ€™t a good representation of the actual use-case. This has been updated from

     test('find todo items', () => {
         let content = `<!--TODO: test-->
 Random Other content
 <!--TODO:
 Multiline test
 -->`;
         let todoItems = lib.parseTodo(content);
         assert.equal(todoItems.length, 2);
         assert.equal(todoItems[0], ' test');
         assert.equal(todoItems[1], 'Multiline test');
     });

to

     test('find todo items', () => {
         let content = `<!--TODO: test-->
 Random Other content
 <!--TODO:
 Multiline test
 With a second line of information
 -->`;
         let todoItems = lib.parseTodo(content);
         assert.equal(todoItems.length, 2);
         assert.equal(todoItems[0], ' test');
         assert.equal(todoItems[1], 'Multiline test\nWith a second line of information');
     });

Improvement: by default add newlines when creating todo item

Most of the time I manually added the newlines when creating the todo line, so it makes sense to make the most common case the default.

This just requires changing

new vscode.SnippetString(`<!--TODO (${date}): $1-->`)

into

new vscode.SnippetString(`<!--TODO (${date}):\n$1\n-->`)