Autocomplete

Syncline Editor has a built-in completion engine covering language keywords, user-defined symbols, snippets, and Emmet. Every aspect is customisable.

Static Completions

Pass fixed keywords, types, or full CompletionItem objects through configuration. These are merged with the built-in language tokens.

static-completions.ts

import { createEditor } from '@synclineapi/editor';
import type { CompletionItem } from '@synclineapi/editor';

const editor = createEditor(container, {
  language: 'typescript',

  // Extra symbols highlighted and surfaced in the autocomplete popup
  extraKeywords: ['emit', 'stage', 'pipeline'],
  extraTypes:    ['Observable', 'Subject', 'BehaviorSubject'],

  // Unified completions — keywords, symbols, and snippets in one array
  completions: [
    {
      label:       'fetchUser',
      kind:        'fn',
      detail:      '(id: string) => Promise<User>',
      description: 'Fetch a user by ID from the REST API.',
    },
    {
      label:       'UserStatus',
      kind:        'typ',
      detail:      'enum',
      description: 'Represents the current state of a user account.

`active` | `suspended` | `pending`',
    },
    {
      label:       'rcomp',
      kind:        'snip',
      detail:      'React component scaffold',
      body:        'export function $1({ $2 }: $1Props) {
  return (
    <div>
      $3
    </div>
  );
}',
      language:    ['typescript'],
    },
  ],
});

Dynamic Completions

Use provideCompletions for context-sensitive results. The callback receives a CompletionContext object (word, line, position) and can return a list or null to fall back to built-ins:

dynamic-completions.ts

import { createEditor, type CompletionContext, type CompletionItem } from '@synclineapi/editor';

const editor = createEditor(container, {
  language: 'typescript',

  // Called every time the popup opens.
  // ctx.prefix  — characters typed before the cursor
  // ctx.line    — zero-based cursor row
  // ctx.col     — zero-based cursor column
  // ctx.doc     — full document as string[]
  // Return null to fall through to built-in completions.
  provideCompletions(ctx: CompletionContext): CompletionItem[] | null {
    const { prefix, line: row, doc } = ctx;

    // Suggest modules inside import statements
    const importLine = doc[row];
    if (importLine.startsWith('import ') && prefix.length >= 1) {
      return [
        { label: '@myorg/utils',    kind: 'cls', detail: 'npm package' },
        { label: '@myorg/api-keys', kind: 'cls', detail: 'npm package' },
      ];
    }

    // Hook completions when the prefix starts with 'use'
    if (prefix.startsWith('use')) {
      return [
        { label: 'useMyContext', kind: 'fn', detail: '() => MyContext', description: 'Returns the shared context.' },
        { label: 'useMyStore',   kind: 'fn', detail: '() => MyStore',   description: 'Zustand store hook.' },
      ];
    }

    return null; // let built-ins handle it
  },
});

Hover Documentation

Supply provideHover to show inline documentation when a user pauses over an identifier. Return null to use built-in hover:

hover-docs.ts

import { createEditor, type HoverContext, type HoverDoc } from '@synclineapi/editor';

const editor = createEditor(container, {
  provideHover(ctx: HoverContext): HoverDoc | null {
    const { word } = ctx;

    const docs: Record<string, HoverDoc> = {
      UUID:    { title: 'UUID', body: 'Universally unique identifier (RFC 4122).' },
      API_URL: { title: 'API_URL', body: 'Base URL injected at build time via VITE_API_URL.' },
    };

    return docs[word] ?? null;
  },
});

Built-in Snippets

Common patterns are available out-of-the-box. Tab stops ($1, $2, $0) are navigated with Tab:

snippets.ts

import type { CompletionItem } from '@synclineapi/editor';

// TypeScript / JavaScript built-in snippets
// arrowfn  →  const name = (params): ReturnType => { }
// asyncfn  →  async function name(params): Promise<T> { }
// classdef →  class Name { constructor(params) { } }
// trycatch →  try { } catch (err: unknown) { console.error(err); }
// forof    →  for (const item of iterable) { }
// iife     →  (function() { })();

// Custom snippet via completions array
const snippets: CompletionItem[] = [
  {
    label:       'mycomp',
    kind:        'snip',
    detail:      'React component',
    description: 'Scaffolds a typed React functional component.',
    body:        'export function $1({ $2 }: $1Props) {
  return (
    <div>
      $3
    </div>
  );
}',
    language:    ['typescript'],
  },
  {
    label:  'clog',
    kind:   'snip',
    detail: 'console.log with label',
    body:   "console.log('$1:', $2);",
  },
];

editor.updateConfig({ completions: snippets });

Emmet

Emmet abbreviations expand on Tab in HTML, Markdown, and CSS contexts. Disable with { emmet: false }:

emmet.ts

// HTML Emmet abbreviations — expand with Tab
// -------------------------------------------
// div.container    →  <div class="container"></div>
// ul>li*3          →  <ul><li></li><li></li><li></li></ul>
// a[href=#]        →  <a href="#"></a>
// h1{Hello}        →  <h1>Hello</h1>
// section>h2+p     →  <section><h2></h2><p></p></section>
// input[type=text] →  <input type="text">

// Disable Emmet
createEditor(container, { emmet: false });

CompletionItem Schema

FieldTypeRequiredDescription
labelstring

Yes

Text inserted on accept; also used for prefix matching in the popup.

kind'kw' | 'fn' | 'typ' | 'cls' | 'var' | 'snip' | 'emmet'

Yes

Badge type shown in the popup. 'snip' items with a body expand as snippets; 'emmet' is auto-generated.

detailstring

No

Short hint shown to the right of the popup row (e.g. type signature).

descriptionstring

No

Full documentation shown in the side panel when the item is selected. Snippet items without a description show a body preview.

bodystring

No

Snippet template with $1…$N tab stops. Tab/Enter expands it when set. Required for kind: 'snip'.

languagestring | string[]

No

Restrict this item to specific language(s). Omit to show in all languages.

Syncline Editor

© 2026 Syncline Editor. All rights reserved.