linter-bundle
A unified linting package that combines linting infrastructure with an integrated UI
asiloisad 155 0 2.4.0 MIT GitHub
  • Made for Pulsar!

    This package was written specifically for Pulsar and did not exist in the Atom package repository.

linter-bundle

A unified linting package that combines linting infrastructure with an integrated UI.

panel

Fork of linter and linter-ui-default.

Features

  • Unified Package: Combines linter core functionality with UI in a single package.
  • Status Bar Integration: Shows error, warning, and info counts in the status bar. Left-click toggles panel, middle-click toggles file/project mode, Ctrl+middle-click clears messages, right-click jumps to next, Ctrl+right-click jumps to previous.
  • Linter Panel: Sortable table view of all linter messages with filtering. Supports keyboard navigation when focused.
  • Inline Bubbles: Hover-style message display at cursor position.
  • Editor Highlighting: Underline and highlight decorations for linted ranges.
  • Multiple Sort Methods: Sort by severity, position, or provider. Cell index is used as a primary sort key for notebook messages.
  • Linter Management: Enable/disable individual linter providers.
  • Jupyter Notebook support: Works with .ipynb files via the linter-adapter service. Messages are mapped to individual cells and the panel shows [cell]:line:col position.
  • Scrollmap: Shows linter markers in the scrollbar via scrollmap.
  • Reference links: Clickable references in messages to open related files. See latex-tools for usage example.
  • Markdown rendering: Message excerpts support markdown formatting in tooltips and panel.
  • MCP Tool: Provides GetLinterMessages tool via pulsar-mcp.

Installation

To install linter-bundle search for linter-bundle in the Install pane of the Pulsar settings or run ppm install linter-bundle. Alternatively, you can run ppm install asiloisad/pulsar-linter-bundle to install a package directly from the GitHub repository.

Commands

Commands available in atom-workspace:

  • linter-bundle:toggle-focus: focus the panel (or return focus to the editor if already focused), opening the panel if needed,
  • linter-bundle:toggle-panel: toggle the linter panel visibility,
  • linter-bundle:toggle-linter: toggle a linter provider on/off,
  • linter-bundle:lint: manually trigger linting on the current file,
  • linter-bundle:debug: show debug information about active linters,
  • linter-bundle:state: toggle linting for the current editor,
  • linter-bundle:inspect: show message bubble at cursor position,
  • linter-bundle:next: jump to next linter message,
  • linter-bundle:previous: jump to previous linter message,
  • linter-bundle:clear: clear linter messages for the current editor.

Customization

The style can be adjusted according to user preferences in the styles.less file:

  • e.g. solid underline instead of wavy:
.linter-text {
  &.error {
    background-image: none;
    border-bottom: 1px solid @text-color-error;
  }
  &.warning {
    background-image: none;
    border-bottom: 1px solid @text-color-warning;
  }
  &.info {
    background-image: none;
    border-bottom: 1px solid @text-color-info;
  }
}
  • e.g. change gutter dot size:
:root {
  --linter-dot-size: 6px;
}

Provided Service linter-indie

Indie linter delegate for custom integrations. Allows packages to push linter messages directly without implementing the full linter provider interface.

In your package.json:

{
  "consumedServices": {
    "linter-indie": {
      "versions": { "2.0.0": "consumeIndie" }
    }
  }
}

In your main module:

module.exports = {
  consumeIndie(registerIndie) {
    const indie = registerIndie({ name: "my-indie-linter" });

    // Set messages for a specific file
    indie.setMessages("/path/to/file.js", [
      {
        severity: "warning",
        location: {
          file: "/path/to/file.js",
          position: [
            [0, 0],
            [0, 1],
          ],
        },
        excerpt: "Warning message",
      },
    ]);

    // Or set all messages at once
    indie.setAllMessages([
      /* messages */
    ]);

    // Clear all messages
    indie.clearMessages();
  },
};

Provided Service mcp-tools

Provides MCP tools for pulsar-mcp. The service currently exposes GetLinterMessages, a read-only tool that returns diagnostics from the linter panel.

The tool follows the current linter panel view mode:

  • file: returns messages for the active editor,
  • project: returns all known messages across the project.

Returned data has the shape:

{
  "mode": "file",
  "path": "/path/to/current/file.js",
  "messages": [
    {
      "severity": "warning",
      "excerpt": "Warning message",
      "linterName": "my-linter",
      "file": "/path/to/current/file.js",
      "range": {
        "start": { "row": 0, "column": 0 },
        "end": { "row": 0, "column": 1 }
      },
      "url": null
    }
  ]
}

In package.json this service is provided as:

{
  "providedServices": {
    "mcp-tools": {
      "versions": {
        "1.0.0": "provideMcpTools"
      }
    }
  }
}

Consumed Service linter-adapter

Allows non-TextEditor pane items (such as Jupyter notebooks) to integrate with the linter panel. The adapter maps linter messages to the correct item, handles navigation, and provides cursor-aware message lookup.

In your package.json:

{
  "providedServices": {
    "linter-adapter": {
      "versions": {
        "1.0.0": "provideLinterItemAdapter"
      }
    }
  }
}

In your main module:

module.exports = {
  provideLinterItemAdapter() {
    return {
      // Return true if this adapter handles the given pane item
      handlesItem: (item) => item instanceof MyCustomEditor,

      // Return the TextEditor that linters should lint for this item (for grammar/path detection)
      getTextEditorForItem: (item) => item.getSourceEditor(),

      // Filter all linter messages down to those relevant for this item
      getMessagesForItem: (item, allMessages) =>
        allMessages.filter((m) => m.location?.file === item.getPath()),

      // Return the message at the current cursor position (or undefined)
      getCurrentMessage: (item, messages) => item.getMessageAtCursor(messages),

      // Return the next message after the current cursor position
      getNextMessage: (item, messages) => item.getNextMessage(messages),

      // Return the previous message before the current cursor position
      getPreviousMessage: (item, messages) => item.getPreviousMessage(messages),

      // Scroll the item to the given message
      revealMessage: (item, message) => item.revealMessage(message),
    };
  },
};

Consumed Service linter

Standard linter provider interface. Packages like linter-eslint, linter-ruff, etc. provide this service to report diagnostics.

// Provider example
module.exports = {
  provideLinter() {
    return {
      name: "my-linter",
      scope: "file", // or 'project'
      lintsOnChange: true,
      grammarScopes: ["source.js"],
      lint(editor) {
        return [
          {
            severity: "error", // 'error' | 'warning' | 'info'
            location: {
              file: editor.getPath(),
              position: [
                [0, 0],
                [0, 1],
              ],
            },
            excerpt: "Error message",
          },
        ];
      },
    };
  },
};

Consumed Service linter-ui

External UI providers that want to display linter messages. Used by packages like scrollmap to show linter markers on the scrollbar.

// UI provider example
module.exports = {
  provideLinterUI() {
    return {
      name: "my-ui",
      render({ added, removed, messages }) {
        // Handle message updates
      },
      didBeginLinting({ linter, filePath }) {},
      didFinishLinting({ linter, filePath }) {},
      dispose() {},
    };
  },
};

Contributing

Got ideas to make this package better, found a bug, or want to help add new features? Just drop your thoughts on GitHub. Any feedback is welcome!