API Reference
Complete reference for the Lokus Plugin API. All plugins receive an api
object through the PluginContext
during activation.
Getting the API
import { Plugin, PluginContext, LokusAPI } from '@lokus/plugin-sdk'
export default class MyPlugin implements Plugin {
private api!: LokusAPI
async activate(context: PluginContext) {
// Access the API
this.api = context.api
// Use any API
await this.api.commands.register({
id: 'myPlugin.hello',
title: 'Say Hello',
handler: () => this.api.ui.showNotification('Hello!', 'info')
})
}
}
API Overview
Lokus provides 8 specialized APIs:
API | Purpose |
---|---|
Commands | Register and execute commands |
Editor | Text editor operations |
UI | User interface extensions |
Workspace | Workspace and project operations |
File System | File operations |
Network | HTTP requests |
Storage | Plugin-scoped storage |
Events | Event system |
Additional APIs (Lokus 2.0+):
API | Purpose |
---|---|
Tasks | Task execution |
Debug | Debugging support |
Languages | Language features |
Themes | Theme registration |
Configuration | Settings management |
Terminal | Terminal operations |
Commands API
Register and execute commands, create keyboard shortcuts.
api.commands.register()
Register a new command.
api.commands.register(command: CommandDefinition): Disposable
Parameters:
interface CommandDefinition {
id: string // Unique command ID
title: string // Display title
handler: CommandHandler // Command handler function
category?: string // Command category
description?: string // Description
icon?: string | ThemeIcon // Icon
when?: string // When clause
enablement?: string // Enablement clause
}
type CommandHandler = (...args: any[]) => any | Promise<any>
Returns: Disposable
- Call .dispose()
to unregister
Example:
const disposable = api.commands.register({
id: 'myPlugin.hello',
title: 'Say Hello',
category: 'My Plugin',
description: 'Shows a greeting message',
handler: (name?: string) => {
api.ui.showNotification(`Hello ${name || 'World'}!`, 'info')
}
})
// Unregister command
disposable.dispose()
api.commands.execute()
Execute a command by ID.
api.commands.execute<T>(commandId: string, ...args: any[]): Promise<T>
Parameters:
commandId
- Command identifierargs
- Arguments to pass to command handler
Returns: Promise resolving to command result
Example:
// Execute command without arguments
await api.commands.execute('myPlugin.hello')
// Execute command with arguments
await api.commands.execute('myPlugin.hello', 'John')
// Execute with typed return value
const result = await api.commands.execute<string>('myPlugin.getData')
api.commands.getAll()
Get all registered commands.
api.commands.getAll(): Promise<CommandInfo[]>
Returns: Promise resolving to array of command information
Example:
const commands = await api.commands.getAll()
commands.forEach(cmd => {
console.log(`${cmd.id}: ${cmd.title}`)
})
api.commands.exists()
Check if command exists.
api.commands.exists(commandId: string): Promise<boolean>
Example:
if (await api.commands.exists('myPlugin.hello')) {
console.log('Command is registered')
}
Editor API
Access and manipulate text editor content.
api.editor.getContent()
Get current editor content.
api.editor.getContent(): Promise<string>
Returns: Promise resolving to editor content
Example:
const content = await api.editor.getContent()
console.log('Current content:', content)
api.editor.setContent()
Set editor content.
api.editor.setContent(content: string): Promise<void>
Parameters:
content
- New content
Example:
await api.editor.setContent('# Hello World\n\nNew content here')
api.editor.insertContent()
Insert content at cursor position.
api.editor.insertContent(content: string): Promise<void>
Example:
await api.editor.insertContent('Inserted text')
api.editor.getSelection()
Get current selection.
api.editor.getSelection(): Promise<Selection | undefined>
Returns: Promise resolving to current selection
interface Selection {
anchor: Position // Start position
active: Position // End position (cursor)
start: Position // Min position
end: Position // Max position
}
interface Position {
line: number // Line number (0-based)
character: number // Character offset (0-based)
}
Example:
const selection = await api.editor.getSelection()
if (selection) {
console.log(`Selection from ${selection.start.line}:${selection.start.character} to ${selection.end.line}:${selection.end.character}`)
}
api.editor.setSelection()
Set selection.
api.editor.setSelection(selection: Selection): Promise<void>
Example:
await api.editor.setSelection({
start: { line: 0, character: 0 },
end: { line: 2, character: 10 }
})
api.editor.getTextInRange()
Get text in specific range.
api.editor.getTextInRange(range: Range): Promise<string>
Example:
const text = await api.editor.getTextInRange({
start: { line: 0, character: 0 },
end: { line: 0, character: 10 }
})
api.editor.replaceText()
Replace text in range.
api.editor.replaceText(range: Range, text: string): Promise<void>
Example:
await api.editor.replaceText(
{
start: { line: 0, character: 0 },
end: { line: 0, character: 5 }
},
'New text'
)
api.editor.registerCompletionProvider()
Register autocomplete provider.
api.editor.registerCompletionProvider(
selector: DocumentSelector,
provider: CompletionItemProvider,
...triggerCharacters: string[]
): Disposable
Example:
api.editor.registerCompletionProvider(
'markdown',
{
provideCompletionItems(document, position, token, context) {
return [
{
label: 'hello',
kind: CompletionItemKind.Keyword,
insertText: 'Hello World!',
documentation: 'Insert greeting'
}
]
}
},
'[' // Trigger on [
)
api.editor.onDidChangeTextDocument()
Listen to document changes.
api.editor.onDidChangeTextDocument(
listener: (event: TextDocumentChangeEvent) => void
): Disposable
Example:
api.editor.onDidChangeTextDocument(event => {
console.log('Document changed:', event.document.uri)
event.contentChanges.forEach(change => {
console.log('Change:', change.text)
})
})
UI API
Create panels, dialogs, notifications, and UI elements.
api.ui.showNotification()
Show notification to user.
api.ui.showNotification(
message: string,
type?: 'info' | 'warning' | 'error' | 'success',
actions?: NotificationAction[]
): Promise<string | undefined>
Returns: Promise resolving to clicked action ID
Example:
// Simple notification
await api.ui.showNotification('Operation completed', 'success')
// With actions
const result = await api.ui.showNotification(
'File has unsaved changes',
'warning',
[
{ id: 'save', label: 'Save', primary: true },
{ id: 'discard', label: 'Discard' }
]
)
if (result === 'save') {
// User clicked Save
}
api.ui.showDialog()
Show modal dialog.
api.ui.showDialog(dialog: DialogOptions): Promise<DialogResult>
Example:
const result = await api.ui.showDialog({
title: 'Confirm Action',
message: 'Are you sure you want to continue?',
type: 'question',
buttons: [
{ id: 'yes', label: 'Yes', primary: true },
{ id: 'no', label: 'No' }
],
checkboxLabel: 'Don\'t ask again',
checkboxChecked: false
})
if (result.buttonId === 'yes') {
console.log('User confirmed')
if (result.checkboxChecked) {
console.log('User checked "Don\'t ask again"')
}
}
api.ui.showQuickPick()
Show quick pick menu.
api.ui.showQuickPick<T extends QuickPickItem>(
items: T[] | Promise<T[]>,
options?: QuickPickOptions
): Promise<T | undefined>
Example:
const items = [
{ label: 'Option 1', description: 'First option' },
{ label: 'Option 2', description: 'Second option' },
{ label: 'Option 3', description: 'Third option' }
]
const selected = await api.ui.showQuickPick(items, {
placeholder: 'Select an option',
title: 'Quick Pick Example'
})
if (selected) {
console.log('Selected:', selected.label)
}
api.ui.showInputBox()
Show input dialog.
api.ui.showInputBox(options?: InputBoxOptions): Promise<string | undefined>
Example:
const name = await api.ui.showInputBox({
prompt: 'Enter your name',
placeholder: 'John Doe',
validateInput: (value) => {
if (!value) return 'Name is required'
if (value.length < 3) return 'Name must be at least 3 characters'
return null
}
})
if (name) {
console.log('User entered:', name)
}
api.ui.registerPanel()
Register custom panel.
api.ui.registerPanel(panel: PanelDefinition): Disposable
Example:
api.ui.registerPanel({
id: 'myPlugin.panel',
title: 'My Panel',
type: 'webview',
location: 'sidebar',
icon: 'star',
html: `
<!DOCTYPE html>
<html>
<body>
<h1>Custom Panel</h1>
<button onclick="handleClick()">Click Me</button>
<script>
function handleClick() {
window.parent.postMessage({ command: 'buttonClicked' }, '*')
}
</script>
</body>
</html>
`
})
api.ui.registerStatusBarItem()
Create status bar item.
api.ui.registerStatusBarItem(
item: StatusBarItemDefinition
): StatusBarItem
Example:
const statusItem = api.ui.registerStatusBarItem({
id: 'myPlugin.status',
text: '$(check) Ready',
tooltip: 'Plugin is ready',
command: 'myPlugin.showInfo',
alignment: 'left',
priority: 100
})
// Update later
statusItem.text = '$(sync~spin) Processing...'
// Hide
statusItem.hide()
// Show
statusItem.show()
api.ui.withProgress()
Show progress indicator.
api.ui.withProgress<R>(
options: ProgressOptions,
task: (progress: Progress, token: CancellationToken) => Promise<R>
): Promise<R>
Example:
await api.ui.withProgress(
{
location: 'notification',
title: 'Processing Files',
cancellable: true
},
async (progress, token) => {
for (let i = 0; i < 100; i++) {
if (token.isCancellationRequested) {
break
}
progress.report({
message: `Processing file ${i + 1}/100`,
increment: 1
})
await processFile(i)
}
}
)
Workspace API
Access workspace files, folders, and settings.
api.workspace.workspaceFolders
Get workspace folders.
readonly workspaceFolders: WorkspaceFolder[] | undefined
Example:
const folders = api.workspace.workspaceFolders
if (folders) {
folders.forEach(folder => {
console.log(`Folder: ${folder.name} at ${folder.uri}`)
})
}
api.workspace.findFiles()
Find files in workspace.
api.workspace.findFiles(
include: string,
exclude?: string,
maxResults?: number,
token?: CancellationToken
): Promise<string[]>
Example:
// Find all markdown files
const mdFiles = await api.workspace.findFiles('**/*.md')
// Find with exclusions
const tsFiles = await api.workspace.findFiles(
'**/*.ts',
'**/node_modules/**',
100 // Max 100 results
)
api.workspace.onDidChangeWorkspaceFolders()
Listen to workspace folder changes.
api.workspace.onDidChangeWorkspaceFolders(
listener: (event: WorkspaceFoldersChangeEvent) => void
): Disposable
Example:
api.workspace.onDidChangeWorkspaceFolders(event => {
console.log('Added folders:', event.added)
console.log('Removed folders:', event.removed)
})
File System API
Read, write, and watch files.
api.fs.readFile()
Read file content.
api.fs.readFile(path: string): Promise<string>
Example:
const content = await api.fs.readFile('/path/to/file.txt')
console.log(content)
api.fs.writeFile()
Write file content.
api.fs.writeFile(path: string, content: string): Promise<void>
Example:
await api.fs.writeFile('/path/to/file.txt', 'New content')
api.fs.exists()
Check if file/directory exists.
api.fs.exists(path: string): Promise<boolean>
Example:
if (await api.fs.exists('/path/to/file.txt')) {
console.log('File exists')
}
api.fs.watch()
Watch file or directory for changes.
api.fs.watch(
path: string,
listener: (event: FileSystemEvent) => void
): Disposable
Example:
const watcher = api.fs.watch('/path/to/directory', event => {
console.log(`File ${event.type}: ${event.path}`)
})
// Stop watching
watcher.dispose()
Network API
Make HTTP requests.
api.network.fetch()
Make HTTP request.
api.network.fetch(
url: string,
options?: RequestInit
): Promise<Response>
Example:
// GET request
const response = await api.network.fetch('https://api.example.com/data')
const data = await response.json()
// POST request
const response = await api.network.fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'John' })
})
Storage API
Store and retrieve plugin data.
api.storage.get()
Get stored value.
api.storage.get<T>(key: string, defaultValue?: T): Promise<T | undefined>
Example:
const settings = await api.storage.get('settings', { enabled: true })
api.storage.set()
Store value.
api.storage.set(key: string, value: any): Promise<void>
Example:
await api.storage.set('settings', { enabled: true, mode: 'advanced' })
api.storage.delete()
Delete stored value.
api.storage.delete(key: string): Promise<void>
Example:
await api.storage.delete('tempData')
api.storage.keys()
Get all keys.
api.storage.keys(): Promise<string[]>
Example:
const keys = await api.storage.keys()
console.log('Stored keys:', keys)
Events API
Subscribe to and emit events.
api.events.on()
Subscribe to event.
api.events.on(
event: string,
listener: (data: any) => void
): Disposable
Example:
const disposable = api.events.on('myPlugin.dataUpdated', data => {
console.log('Data updated:', data)
})
// Unsubscribe
disposable.dispose()
api.events.emit()
Emit event.
api.events.emit(event: string, data?: any): void
Example:
api.events.emit('myPlugin.dataUpdated', { count: 42 })
api.events.once()
Subscribe to event once.
api.events.once(
event: string,
listener: (data: any) => void
): Disposable
Example:
api.events.once('myPlugin.initialized', () => {
console.log('Plugin initialized')
})
Common Types
Disposable
Object that can be disposed to clean up resources.
interface Disposable {
dispose(): void
}
Position
Position in text document.
interface Position {
line: number // Line number (0-based)
character: number // Character offset (0-based)
}
Range
Text range.
interface Range {
start: Position
end: Position
}
Selection
Text selection (extends Range).
interface Selection extends Range {
anchor: Position // Selection start
active: Position // Cursor position
}
Error Handling
All API methods can throw errors. Always use try-catch:
try {
await api.editor.setContent('New content')
} catch (error) {
api.ui.showNotification(`Error: ${error.message}`, 'error')
console.error(error)
}
APIError
Specialized error class for API errors:
class APIError extends Error {
code: APIErrorCode
details?: Record<string, unknown>
}
enum APIErrorCode {
PERMISSION_DENIED = 'PERMISSION_DENIED',
INVALID_PARAMS = 'INVALID_PARAMS',
NOT_FOUND = 'NOT_FOUND',
NOT_SUPPORTED = 'NOT_SUPPORTED',
RATE_LIMITED = 'RATE_LIMITED',
INTERNAL_ERROR = 'INTERNAL_ERROR'
}
Example:
try {
await api.commands.execute('unknown.command')
} catch (error) {
if (error instanceof APIError) {
if (error.code === APIErrorCode.NOT_FOUND) {
console.log('Command not found')
}
}
}
Best Practices
Always Clean Up
Store disposables and dispose them in deactivate:
export default class MyPlugin implements Plugin {
private disposables: Disposable[] = []
async activate(context: PluginContext) {
this.disposables.push(
api.commands.register({ /* ... */ }),
api.editor.onDidChangeTextDocument(/* ... */),
api.fs.watch('/path', /* ... */)
)
}
async deactivate() {
this.disposables.forEach(d => d.dispose())
this.disposables = []
}
}
Use Context Subscriptions
Alternatively, use context.subscriptions for automatic cleanup:
async activate(context: PluginContext) {
context.subscriptions.push(
api.commands.register({ /* ... */ }),
api.editor.onDidChangeTextDocument(/* ... */)
)
// Automatically disposed when plugin deactivates
}
Handle Async Operations
Always await async operations:
// Good
await api.editor.setContent('New content')
await api.storage.set('key', 'value')
// Bad - operations may not complete
api.editor.setContent('New content')
api.storage.set('key', 'value')
Validate User Input
Always validate input from dialogs:
const input = await api.ui.showInputBox({
validateInput: (value) => {
if (!value) return 'Input is required'
if (value.length < 3) return 'Must be at least 3 characters'
return null // Valid
}
})