monaco-latex
Version:
LaTeX language support for Monaco Editor
137 lines (120 loc) • 5.14 kB
text/typescript
/**
* LaTeX Completion Provider for Monaco Editor.
* Provides autocompletion for LaTeX commands, environments, and packages.
*/
import type * as Monaco from 'monaco-editor';
import {
LATEX_COMMANDS,
LATEX_ENVIRONMENTS,
LATEX_PACKAGES,
type LaTeXCommand
} from './commands/data';
export class LaTeXCompletionProvider implements Monaco.languages.CompletionItemProvider {
triggerCharacters = ['\\', '{', '['];
constructor(private monaco: typeof Monaco) {}
provideCompletionItems(
model: Monaco.editor.ITextModel,
position: Monaco.Position,
context: Monaco.languages.CompletionContext,
token: Monaco.CancellationToken
): Monaco.languages.ProviderResult<Monaco.languages.CompletionList> {
const textUntilPosition = model.getValueInRange({
startLineNumber: position.lineNumber,
startColumn: 1,
endLineNumber: position.lineNumber,
endColumn: position.column,
});
const suggestions: Monaco.languages.CompletionItem[] = [];
// Check if we're after a backslash (command completion)
const commandMatch = textUntilPosition.match(/\\([\w]*)$/);
if (commandMatch) {
// Calculate range for just the command name (after backslash)
const commandRange = {
startLineNumber: position.lineNumber,
startColumn: position.column - commandMatch[1].length,
endLineNumber: position.lineNumber,
endColumn: position.column,
};
suggestions.push(...this.getCommandCompletions(commandRange));
}
// Check if we're inside \begin{ or \end{ (environment completion)
const envMatch = textUntilPosition.match(/\\(begin|end)\{([\w*]*)$/);
if (envMatch) {
const envRange = {
startLineNumber: position.lineNumber,
startColumn: position.column - envMatch[2].length,
endLineNumber: position.lineNumber,
endColumn: position.column,
};
suggestions.push(...this.getEnvironmentCompletions(envRange));
}
// Check if we're inside \usepackage{ (package completion)
const pkgMatch = textUntilPosition.match(/\\usepackage(?:\[.*?\])?\{([\w,\s]*)$/);
if (pkgMatch) {
const pkgRange = {
startLineNumber: position.lineNumber,
startColumn: position.column - pkgMatch[1].length,
endLineNumber: position.lineNumber,
endColumn: position.column,
};
suggestions.push(...this.getPackageCompletions(pkgRange));
}
return { suggestions };
}
private getCommandCompletions(range: Monaco.IRange): Monaco.languages.CompletionItem[] {
return LATEX_COMMANDS.map((cmd, index) => ({
label: `\\${cmd.name}`,
kind: this.monaco.languages.CompletionItemKind.Function,
// insertText without backslash since we only replace the command name
insertText: cmd.snippet || cmd.name,
insertTextRules: cmd.snippet
? this.monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
: undefined,
detail: cmd.signature ? `\\${cmd.name}${cmd.signature}` : cmd.category,
documentation: this.formatDocumentation(cmd),
range,
sortText: String(index).padStart(4, '0'),
// Filter based on what user typed after backslash
filterText: cmd.name,
}));
}
private getEnvironmentCompletions(range: Monaco.IRange): Monaco.languages.CompletionItem[] {
return LATEX_ENVIRONMENTS.map((env, index) => ({
label: env.name,
kind: this.monaco.languages.CompletionItemKind.Snippet,
insertText: env.name,
detail: env.package ? `Requires: ${env.package}` : 'Base LaTeX',
documentation: {
value: env.description,
},
range,
sortText: String(index).padStart(4, '0'),
}));
}
private getPackageCompletions(range: Monaco.IRange): Monaco.languages.CompletionItem[] {
return LATEX_PACKAGES.map((pkg, index) => ({
label: pkg.name,
kind: this.monaco.languages.CompletionItemKind.Module,
insertText: pkg.name,
documentation: pkg.description,
range,
sortText: String(index).padStart(4, '0'),
}));
}
private formatDocumentation(cmd: LaTeXCommand): Monaco.IMarkdownString {
let doc = `**${cmd.description}**\n\n`;
if (cmd.package) {
doc += `*Requires:* \`\\usepackage{${cmd.package}}\`\n\n`;
}
if (cmd.example) {
doc += `**Example:**\n\`\`\`latex\n${cmd.example}\n\`\`\``;
}
return { value: doc };
}
}
/**
* Creates a completion provider for LaTeX.
*/
export function createCompletionProvider(monaco: typeof Monaco): LaTeXCompletionProvider {
return new LaTeXCompletionProvider(monaco);
}