Skip to content

ydb-platform/monaco-ghost

Repository files navigation

@ydb-platform/monaco-ghost

CI npm version License PRs Welcome

๐Ÿš€ A lightweight adapter for integrating completion services with Monaco Editor's inline completion system.

Installation โ€ข Quick Start โ€ข Documentation โ€ข Contributing


โœจ Features at a Glance

  • ๐Ÿ‘ป Ghost Text Display - Inline suggestions with keyboard navigation
  • โšก High Performance - Debouncing, caching, and optimized for large files
  • ๐ŸŽฏ Type Safety - Comprehensive TypeScript support
  • ๐ŸŽจ Theme Support - Dark and light themes with customization options
  • ๐Ÿ“Š Event System - Rich analytics and error tracking
  • ๐Ÿงฉ React Integration - Pre-built components and hooks

๐Ÿ“ฆ Installation

npm install @ydb-platform/monaco-ghost monaco-editor

๐Ÿš€ Quick Start

React Integration with Hook

import React, { useCallback } from 'react';
import MonacoEditor from 'react-monaco-editor';
import * as monaco from 'monaco-editor';
import { useMonacoGhost } from '@ydb-platform/monaco-ghost';

function MyCustomEditor() {
  // Java-specific API implementation
  const javaApi = {
    getCodeAssistSuggestions: async () => ({
      Suggests: [{ Text: 'System.out.println("Hello, World!");' }],
      RequestId: 'demo-request',
    }),
  };

  // Java-specific configuration
  const javaConfig = {
    debounceTime: 200,
    textLimits: {
      beforeCursor: 8000,
      afterCursor: 1000,
    },
    suggestionCache: {
      enabled: true,
    },
  };

  const eventHandlers = {
    onCompletionAccept: text => console.log('Accepted:', text),
    onCompletionDecline: (text, reason, otherSuggestions) =>
      console.log('Declined:', text, reason, otherSuggestions),
    onCompletionIgnore: (text, otherSuggestions) => console.log('Ignored:', text, otherSuggestions),
    onCompletionError: error => console.error('Error:', error),
  };

  const { registerMonacoGhost, dispose } = useMonacoGhost({
    api: javaApi,
    eventHandlers,
    config: javaConfig,
  });

  const editorDidMount = useCallback(
    (editor: monaco.editor.IStandaloneCodeEditor) => {
      registerMonacoGhost(editor);
    },
    [registerMonacoGhost]
  );

  const options = {
    selectOnLineNumbers: true,
    minimap: { enabled: false },
    automaticLayout: true,
    fontSize: 14,
    lineNumbers: 'on',
    scrollBeyondLastLine: false,
    roundedSelection: false,
    padding: { top: 10 },
  };

  return (
    <MonacoEditor
      width="800"
      height="600"
      language="java"
      theme="vs-dark" // or "vs-light"
      value="// Your Java code here"
      options={options}
      editorDidMount={editorDidMount}
    />
  );
}
Using the pre-built editor component
// Using the pre-built editor component
import { MonacoEditor } from '@ydb-platform/monaco-ghost';

function MyApp() {
  // SQL-specific API implementation
  const sqlApi = {
    getCodeAssistSuggestions: async () => ({
      Suggests: [{ Text: 'SELECT * FROM users;' }],
      RequestId: 'demo-request',
    }),
  };

  // SQL-specific configuration
  const sqlConfig = {
    debounceTime: 200,
    textLimits: {
      beforeCursor: 8000,
      afterCursor: 1000,
    },
    suggestionCache: {
      enabled: true,
    },
  };

  return (
    <MonacoEditor
      initialValue="-- Your SQL code here"
      language="sql"
      theme="vs-dark" // or "vs-light"
      api={sqlApi}
      config={sqlConfig}
      onCompletionAccept={text => console.log('Accepted:', text)}
      onCompletionDecline={(text, reason, otherSuggestions) =>
        console.log('Declined:', text, reason, otherSuggestions)
      }
      onCompletionIgnore={(text, otherSuggestions) =>
        console.log('Ignored:', text, otherSuggestions)
      }
      onCompletionError={error => console.error('Error:', error)}
      editorOptions={{
        minimap: { enabled: false },
        fontSize: 14,
      }}
    />
  );
}

Vanilla JavaScript

View Vanilla JavaScript implementation
import * as monaco from 'monaco-editor';
import {
  createCodeCompletionService,
  registerCompletionCommands,
} from '@ydb-platform/monaco-ghost';

// Create language-specific API implementation
const sqlApi = {
  getCodeAssistSuggestions: async data => {
    // Call your completion service
    // Return suggestions in the expected format
    return {
      Suggests: [{ Text: 'SELECT * FROM users;' }],
      RequestId: 'request-id',
    };
  },
};

// Configure the adapter with language-specific settings
const sqlConfig = {
  debounceTime: 200,
  textLimits: {
    beforeCursor: 8000,
    afterCursor: 1000,
  },
  suggestionCache: {
    enabled: true,
  },
};

// Create provider for SQL
const sqlCompletionProvider = createCodeCompletionService(sqlApi, sqlConfig);

// Subscribe to completion events with type safety
sqlCompletionProvider.events.on('completion:accept', data => {
  console.log('Completion accepted:', data.acceptedText);
});

sqlCompletionProvider.events.on('completion:decline', data => {
  console.log(
    'Completion declined:',
    data.suggestionText,
    'reason:',
    data.reason,
    'other suggestions:',
    data.otherSuggestions
  );
});

sqlCompletionProvider.events.on('completion:ignore', data => {
  console.log(
    'Completion ignored:',
    data.suggestionText,
    'other suggestions:',
    data.otherSuggestions
  );
});

sqlCompletionProvider.events.on('completion:error', error => {
  console.error('Completion error:', error);
});

// Register with Monaco for SQL
monaco.languages.registerInlineCompletionsProvider(['sql'], sqlCompletionProvider);

// Register commands (assuming you have an editor instance)
registerCompletionCommands(monaco, sqlCompletionProvider, editor);

๐Ÿ“š Documentation

๐ŸŽฎ Keyboard Shortcuts

Key Action
Tab Accept current suggestion
Escape Decline current suggestion
Alt+] Cycle to next suggestion
Alt+[ Cycle to previous suggestion

โš™๏ธ Configuration

interface CodeCompletionConfig {
  // Required when using hooks
  language?: string; // The language this configuration applies to (e.g., 'sql', 'java')

  // Performance settings
  debounceTime?: number; // Time in ms to debounce API calls (default: 200)

  // Text limits
  textLimits?: {
    beforeCursor?: number; // Characters to include before cursor (default: 8000)
    afterCursor?: number; // Characters to include after cursor (default: 1000)
  };

  // Cache settings
  suggestionCache?: {
    enabled?: boolean; // Whether to enable suggestion caching (default: true)
  };
}

๐Ÿ”Œ API Interface

View API Interface details
interface ICodeCompletionAPI {
  getCodeAssistSuggestions(data: PromptFile[]): Promise<Suggestions>;
}

export interface PromptPosition {
  lineNumber: number;
  column: number;
}

export interface PromptFragment {
  text: string;
  start: PromptPosition;
  end: PromptPosition;
}

export interface PromptFile {
  path: string;
  fragments: PromptFragment[];
  cursorPosition: PromptPosition;
}

export interface Suggestions {
  items: string[];
  requestId?: string;
}

๐Ÿ“Š Events

View Events documentation

The completion service emits four types of events with rich data:

1. Acceptance Events

interface CompletionAcceptEvent {
  requestId: string;
  acceptedText: string;
}

completionProvider.events.on('completion:accept', (data: CompletionAcceptEvent) => {
  console.log('Accepted:', data.acceptedText);
});

2. Decline Events

interface CompletionDeclineEvent {
  requestId: string;
  suggestionText: string;
  reason: string;
  hitCount: number;
  otherSuggestions: string[];
}

completionProvider.events.on('completion:decline', (data: CompletionDeclineEvent) => {
  console.log('Declined:', data.suggestionText, 'reason:', data.reason);
  console.log('Other suggestions:', data.otherSuggestions);
  console.log('Times shown:', data.hitCount);
});

3. Ignore Events

interface CompletionIgnoreEvent {
  requestId: string;
  suggestionText: string;
  otherSuggestions: string[];
}

completionProvider.events.on('completion:ignore', (data: CompletionIgnoreEvent) => {
  console.log('Ignored:', data.suggestionText);
  console.log('Other suggestions:', data.otherSuggestions);
});

4. Error Events

completionProvider.events.on('completion:error', (error: Error) => {
  console.error('Completion error:', error);
});

๐Ÿ› ๏ธ Development

Setup

# Install dependencies
npm install

# Start Storybook for development
npm run storybook

# Run tests
npm run test

# Run tests with coverage
npm run test:coverage

Build System

The package uses a hybrid build system:

  • TypeScript (tsc) for type checking and declaration files
  • esbuild for fast, optimized builds

Output Formats:

  • CommonJS: dist/cjs/index.js
  • ES Modules: dist/esm/index.js
  • TypeScript Declarations: dist/types/index.d.ts
View Build Commands
# Type checking only
npm run type-check

# Build type declarations
npm run build:types

# Build CommonJS version
npm run build:cjs

# Build ES Modules version
npm run build:esm

# Full build (all formats)
npm run build

๐Ÿ‘ฅ Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request
View Development Guidelines

Development Guidelines

1. Code Context

  • Handle text limits appropriately
  • Maintain cursor position accuracy
  • Consider edge cases
  • Support partial text acceptance

2. Error Handling

  • Wrap API calls in try-catch blocks
  • Fail gracefully on errors
  • Log issues without breaking editor
  • Emit error events for monitoring

3. Performance

  • Use debouncing for API calls
  • Implement efficient caching
  • Track suggestion hit counts
  • Clean up resources properly

4. Testing

  • Add tests for new features
  • Maintain backward compatibility
  • Test edge cases
  • Verify event handling

๐Ÿ“„ License

Apache-2.0 - see LICENSE file for details.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published