Sourcegraph extension buttons and custom commands

This tutorial will teach you how to add a button to the file header and a menu item to the command palette.

Sourcegraph extension

You'll learn by building a line counter extension that displays the file name and number of lines of code when its button or menu item is clicked.

line counter extension

Note: If you get stuck at any point or just want the code, get it from the Sourcegraph extensions sample repository.

Prerequisites

This tutorial presumes you have created and published an extension. If not, complete the Hello world tutorial first.

It also presumes you have the Sourcegraph browser extension installed.

Set up

Create the line counter extension you'll use for this tutorial:

mkdir line-counter
cd line-counter
npm init sourcegraph-extension

Then publish your extension:

src extension publish

Confirm your extension is enabled and working by:

  • Opening the extension detail page.
  • Viewing a file on Sourcegraph.com and seeing your extensions hover message appearing.

Adding a button

Add the following code to the contributes object in package.json:

{
  "actions": [
    {
      "id": "linecounter.displayLineCountAction",
      "command": "linecounter.displayLineCount",
      "actionItem": {
        "label": "Line Count",
        "description": "The line count for this file"
      }
    }
  ],
  "menus": {
    "editor/title": [
      {
        "action": "linecounter.displayLineCountAction",
        "when": "resource"
      }
    ],
    "commandPalette": []
  },
  "configuration": {}
}

The object in the actions array configures the button display properties and what command to call on button click.

The object in the editor/title array means the action it refers to should be placed in the file header. The value of resource for when means display a button only if there is a resource (file) present.

See the menu contributions documentation for additional menu locations, such as commandPalette and directory/page.

Check for button display

Now publish the extension to see the button in action:

src extension publish

As an example, view the tuf_store.go file on GitHub and you should see the Line Count button in the file header.

Click on the View File button in the file header to view tuf_store.go on Sourcegraph. You'll also see the Learn Count button in the Sourcegraph file header.

Extensions are powerful because they work on Sourcegraph and any code host supported by the Sourcegraph browser extension, e.g. GitHub.

Now let's make the button work!

Displaying the notification on button click

Now that we have the button, the next step is to display the notification. Remove all of the code from the TypeScript file in the src directory and replace it with:

import * as sourcegraph from 'sourcegraph'

/**
 * Returns a CodeEditor instance (if a Window is active) that provides access to
 * the document being viewed.
 */
function activeEditor(): sourcegraph.CodeEditor | undefined {
    return sourcegraph.app.activeWindow
        ? sourcegraph.app.activeWindow.visibleViewComponents[0]
        : undefined
}

/**
 * Display a notification with the filename and line count of the document being viewed.
 * @param editor
 */
function displayLineCount(editor: sourcegraph.CodeEditor | undefined = activeEditor()): void {
    if(!editor) {
        return
    }

    const lineCount = editor.document.text.split(/\n/).length - 1
    const fileName = editor.document.uri.substring(editor.document.uri.lastIndexOf('/') + 1).split('#').slice(-1)[0]
    const message = `The ${fileName} file has ${lineCount} line${lineCount > 1 ? 's' : ''} of code `

    sourcegraph.app.activeWindow!.showNotification(message, sourcegraph.NotificationType.Info)
}

/**
 * The activate function is called when one of the extensions `activateEvents`
 * conditions in package.json are satisfied.
 */
export function activate(ctx: sourcegraph.ExtensionContext): void {
    // Add each subscription to the extension's context so they can unsubscribed upon deactivation
    ctx.subscriptions.add(
      sourcegraph.commands.registerCommand('linecounter.displayLineCount', displayLineCount)
    )
}

Now publish and use the extension to see the line counter in action!

What if we wanted to add an item to the Command palette?

Let's say we want one action to be in the file header and the command palette.

We first need to add two fields to the action object, title and category. The title field allows you to specify different text for the command palette and file header button, and the category value makes it clear which item belongs to which extension.

Now that the action object is configured for the command palette, we add the exact same object that we added to editor/title to commandPalette.

Replace the contents of the contributes object with the following:

{
  "actions": [
    {
      "id": "linecounter.displayLineCountAction",
      "command": "linecounter.displayLineCount",
      "title": "Display the line count for this file",
      "category": "Line Counter",
      "actionItem": {
        "label": "Line Count",
        "description": "The line count for this file"
      }
    }
  ],
  "menus": {
    "editor/title": [
      {
        "action": "linecounter.displayLineCountAction",
        "when": "resource"
      }
    ],
    "commandPalette": [
      {
        "action": "linecounter.displayLineCountAction",
        "when": "resource"
      }
    ]
  },
  "configuration": {}
}

Publish the extension, then trigger the notification from the command palette.

command palette

Summary

You've now learned how to add a button and command palette item that calls a custom method defined by your extension.

Next Steps