pulsar-select-list
A general-purpose select list for use in Pulsar packages
asiloisad 0 0 1.4.0 MIT GitHub
  • Made for Pulsar!

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

pulsar-select-list

Fuzzy-searchable select list component. An etch component with keyboard/mouse navigation and built-in panel management.

Fork of atom-select-list.

Features

  • Fuzzy filtering: Multiple algorithms including command-t for file paths.
  • Match highlighting: Built-in helpers for displaying match positions.
  • Panel management: Show/hide/toggle with focus restoration.
  • Lazy match indices: Match positions computed only when accessed.
  • Diacritics support: Accent-insensitive matching option.
  • Help mode: Toggle help content in the panel.

API

Constructor props

When creating a new instance of a select list, or when calling update on an existing one, you can supply a JavaScript object that can contain any of the following properties:

Required

  • elementForItem: (item: Object, options: Object) -> HTMLElement: a function that is called whenever an item needs to be displayed.
    • options: Object:
      • selected: Boolean: indicating whether item is selected or not.
      • index: Number: item's index.
      • filterKey: String|null: the text that was matched against (from filterKeyForItem or item itself).
      • matchIndices: [Number]|null: lazy getter - character indices in filterKey that matched the query. Only computed when accessed.

Optional

  • items: [Object]: an array containing the objects you want to show in the select list.
  • className: String: CSS class name(s) to add to the select list element. Multiple classes can be space-separated.
  • maxResults: Number: the number of maximum items that are shown.
  • filter: (items: [Object], query: String) -> [Object]: a function that allows to decide which items to show whenever the query changes. By default, it uses Pulsar's built-in fuzzy matcher.
  • filterKeyForItem: (item: Object) -> String: when filter is not provided, this function will be called to retrieve a string property on each item and that will be used to filter them.
  • filterQuery: (query: String) -> String: a function that allows to apply a transformation to the user query and whose return value will be used to filter items.
  • removeDiacritics: Boolean: when true, removes diacritical marks from both the query and item text before filtering, enabling accent-insensitive matching (e.g., "cafe" matches "café").
  • filterScoreModifier: (score: Number, item: Object) -> Number: a function to modify the fuzzy match score for each item. Useful for applying custom ranking factors (e.g., boosting by recency or proximity).
  • algorithm: String: the fuzzy matching algorithm to use. Options: 'fuzzaldrin' (default), 'command-t' (path-aware, better for file paths).
  • numThreads: Number: number of threads for parallel matching. Defaults to 80% of available CPUs.
  • maxGap: Number: maximum gap between consecutive matched characters (only for 'command-t' algorithm). Lower values require tighter matches. Defaults to infinite.
  • query: String: a string that will replace the contents of the query editor.
  • selectQuery: Boolean: a boolean indicating whether the query text should be selected or not.
  • order: (item1: Object, item2: Object) -> Number: a function that allows to change the order in which items are shown.
  • emptyMessage: String: a string shown when the list is empty.
  • errorMessage: String: a string that needs to be set when you want to notify the user that an error occurred.
  • infoMessage: String: a string that needs to be set when you want to provide some information to the user.
  • helpMessage: String|Array: content to display when help is toggled. Can be a string or JSX array for rich formatting.
  • helpMarkdown: String: markdown content to display when help is toggled. Rendered using Pulsar's built-in markdown renderer.
  • loadingMessage: String: a string that needs to be set when you are loading items in the background.
  • loadingSpinner: Boolean: show spinner next to loading message.
  • loadingBadge: String/Number: a string or number that needs to be set when the progress status changes.
  • itemsClassList: [String]: an array of strings that will be added as class names to the items element.
  • initialSelectionIndex: Number: the index of the item to initially select; defaults to 0.
  • placeholderText: String: placeholder text to display in the query editor when empty.
  • skipCommandsRegistration: Boolean: when true, skips registering default keyboard commands.

Registered commands

By default, the component registers these commands on its element:

  • core:move-up / core:move-down: Navigate items
  • core:move-to-top / core:move-to-bottom: Jump to first/last item
  • core:confirm: Confirm selection
  • core:cancel: Cancel selection
  • select-list:help: Toggle help message visibility (requires helpMessage or helpMarkdown)

Callbacks

  • didChangeQuery: (query: String) -> Void: called when the query changes.
  • didChangeSelection: (item: Object) -> Void: called when the selected item changes.
  • didConfirmSelection: (item: Object) -> Void: called when the user clicks or presses Enter on an item.
  • didConfirmEmptySelection: () -> Void: called when the user presses Enter but the list is empty.
  • didCancelSelection: () -> Void: called when the user presses Esc or the list loses focus.
  • willShow: () -> Void: called when transitioning from hidden to visible, useful for data preparation.

Instance properties

  • processedQuery: String: The cached result of getFilterQuery(), updated after each query change. Useful in elementForItem to avoid calling getFilterQuery() multiple times.
  • selectionIndex: Number|undefined: The index of the currently selected item, or undefined if nothing is selected.
  • refs.queryEditor: The underlying TextEditor component for the query input.

Instance methods

Panel management

  • show(): Shows the select list as a modal panel and focuses the query editor. Calls willShow callback if provided.
  • hide(): Hides the panel and restores focus to the previously focused element.
  • toggle(): Toggles the visibility of the panel.
  • isVisible(): Returns true if the panel is currently visible.
  • isHelpMode(): Returns true if help is currently displayed.
  • toggleHelp(): Toggles help message visibility. Only works if helpMessage is set.
  • hideHelp(): Hides help message if currently shown.

Other methods

  • focus(): Focuses the query editor.
  • reset(): Clears the query editor text.
  • destroy(): Disposes of the component and cleans up resources.
  • update(props): Updates the component with new props.
  • getQuery(): Returns the current query string.
  • getFilterKey(item): Returns the filter key string for an item (from cache, filterKeyForItem, or item itself).
  • getMatchIndices(item, filterKey?): Returns match indices for an item, computing lazily if needed. Prefer using options.matchIndices in elementForItem instead.
  • getFilterQuery(): Returns the filtered query string (applies filterQuery transformation).
  • setQueryFromSelection(): Sets the query text from the active editor's selection. Returns true if successful, false if no editor, no selection, or selection contains newlines.
  • getSelectedItem(): Returns the currently selected item.
  • selectPrevious(): Selects the previous item.
  • selectNext(): Selects the next item.
  • selectFirst(): Selects the first item.
  • selectLast(): Selects the last item.
  • selectNone(): Deselects all items.
  • selectIndex(index): Selects the item at the given index.
  • selectItem(item): Selects the given item.
  • confirmSelection(): Confirms the current selection.
  • cancelSelection(): Cancels the selection.

Static methods

SelectListView.getMatchIndices(text, query, options)

Computes fuzzy match indices for a text against a query. Useful outside of elementForItem context.

const { getMatchIndices } = require("pulsar-select-list");

const indices = getMatchIndices("MyComponent.js", "mcjs");
// => [0, 2, 11, 12] or null if no match

// With diacritics removal
const indices = getMatchIndices("café", "cafe", { removeDiacritics: true });
// => [0, 1, 2, 3]

SelectListView.highlightMatches(text, matchIndices, options)

Creates a DocumentFragment with highlighted match characters.

// In elementForItem, use options.matchIndices (lazy getter):
elementForItem: (item, { filterKey, matchIndices }) => {
  const li = document.createElement("li");
  li.appendChild(SelectListView.highlightMatches(filterKey, matchIndices));
  return li;
}

SelectListView.removeDiacritics(str)

Removes diacritical marks (accents) from a string.

SelectListView.removeDiacritics("café"); // => 'cafe'

SelectListView.createTwoLineItem(options)

Creates a two-line list item element with primary and optional secondary lines.

elementForItem: (item, { filterKey, matchIndices }) => {
  return SelectListView.createTwoLineItem({
    primary: SelectListView.highlightMatches(filterKey, matchIndices),
    secondary: item.description,
    icon: ["icon-file-text"],
  });
}

Example

const SelectListView = require("pulsar-select-list");
const fs = require("fs");
const path = require("path");

class MyFileList {
  constructor() {
    this.selectList = new SelectListView({
      className: "my-package my-file-list",
      items: [],
      filterKeyForItem: (item) => item.name,
      emptyMessage: "No files found",
      willShow: () => {
        this.loadFiles();
      },
      elementForItem: (item, { index, filterKey, matchIndices }) => {
        const li = document.createElement("li");
        li.appendChild(SelectListView.highlightMatches(filterKey, matchIndices));
        return li;
      },
      didConfirmSelection: (item) => {
        atom.workspace.open(item.path);
        this.selectList.hide();
      },
      didCancelSelection: () => {
        this.selectList.hide();
      },
    });
  }

  toggle() {
    this.selectList.toggle();
  }

  destroy() {
    this.selectList.destroy();
  }
}

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's welcome!