monaco-editor
Version:
A browser based code editor
584 lines (583 loc) • 27.2 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Color } from '../../../base/common/color.js';
import { Range } from '../../common/core/range.js';
import * as languages from '../../common/languages.js';
import { ILanguageService } from '../../common/languages/language.js';
import { ILanguageConfigurationService } from '../../common/languages/languageConfigurationRegistry.js';
import { ModesRegistry } from '../../common/languages/modesRegistry.js';
import { ILanguageFeaturesService } from '../../common/services/languageFeatures.js';
import * as standaloneEnums from '../../common/standalone/standaloneEnums.js';
import { StandaloneServices } from './standaloneServices.js';
import { compile } from '../common/monarch/monarchCompile.js';
import { MonarchTokenizer } from '../common/monarch/monarchLexer.js';
import { IStandaloneThemeService } from '../common/standaloneTheme.js';
import { IConfigurationService } from '../../../platform/configuration/common/configuration.js';
import { IMarkerService } from '../../../platform/markers/common/markers.js';
/**
* Register information about a new language.
*/
export function register(language) {
// Intentionally using the `ModesRegistry` here to avoid
// instantiating services too quickly in the standalone editor.
ModesRegistry.registerLanguage(language);
}
/**
* Get the information of all the registered languages.
*/
export function getLanguages() {
let result = [];
result = result.concat(ModesRegistry.getLanguages());
return result;
}
export function getEncodedLanguageId(languageId) {
const languageService = StandaloneServices.get(ILanguageService);
return languageService.languageIdCodec.encodeLanguageId(languageId);
}
/**
* An event emitted when a language is associated for the first time with a text model.
* @event
*/
export function onLanguage(languageId, callback) {
return StandaloneServices.withServices(() => {
const languageService = StandaloneServices.get(ILanguageService);
const disposable = languageService.onDidRequestRichLanguageFeatures((encounteredLanguageId) => {
if (encounteredLanguageId === languageId) {
// stop listening
disposable.dispose();
// invoke actual listener
callback();
}
});
return disposable;
});
}
/**
* An event emitted when a language is associated for the first time with a text model or
* when a language is encountered during the tokenization of another language.
* @event
*/
export function onLanguageEncountered(languageId, callback) {
return StandaloneServices.withServices(() => {
const languageService = StandaloneServices.get(ILanguageService);
const disposable = languageService.onDidRequestBasicLanguageFeatures((encounteredLanguageId) => {
if (encounteredLanguageId === languageId) {
// stop listening
disposable.dispose();
// invoke actual listener
callback();
}
});
return disposable;
});
}
/**
* Set the editing configuration for a language.
*/
export function setLanguageConfiguration(languageId, configuration) {
const languageService = StandaloneServices.get(ILanguageService);
if (!languageService.isRegisteredLanguageId(languageId)) {
throw new Error(`Cannot set configuration for unknown language ${languageId}`);
}
const languageConfigurationService = StandaloneServices.get(ILanguageConfigurationService);
return languageConfigurationService.register(languageId, configuration, 100);
}
/**
* @internal
*/
export class EncodedTokenizationSupportAdapter {
constructor(languageId, actual) {
this._languageId = languageId;
this._actual = actual;
}
dispose() {
// NOOP
}
getInitialState() {
return this._actual.getInitialState();
}
tokenize(line, hasEOL, state) {
if (typeof this._actual.tokenize === 'function') {
return TokenizationSupportAdapter.adaptTokenize(this._languageId, this._actual, line, state);
}
throw new Error('Not supported!');
}
tokenizeEncoded(line, hasEOL, state) {
const result = this._actual.tokenizeEncoded(line, state);
return new languages.EncodedTokenizationResult(result.tokens, result.endState);
}
}
/**
* @internal
*/
export class TokenizationSupportAdapter {
constructor(_languageId, _actual, _languageService, _standaloneThemeService) {
this._languageId = _languageId;
this._actual = _actual;
this._languageService = _languageService;
this._standaloneThemeService = _standaloneThemeService;
}
dispose() {
// NOOP
}
getInitialState() {
return this._actual.getInitialState();
}
static _toClassicTokens(tokens, language) {
const result = [];
let previousStartIndex = 0;
for (let i = 0, len = tokens.length; i < len; i++) {
const t = tokens[i];
let startIndex = t.startIndex;
// Prevent issues stemming from a buggy external tokenizer.
if (i === 0) {
// Force first token to start at first index!
startIndex = 0;
}
else if (startIndex < previousStartIndex) {
// Force tokens to be after one another!
startIndex = previousStartIndex;
}
result[i] = new languages.Token(startIndex, t.scopes, language);
previousStartIndex = startIndex;
}
return result;
}
static adaptTokenize(language, actual, line, state) {
const actualResult = actual.tokenize(line, state);
const tokens = TokenizationSupportAdapter._toClassicTokens(actualResult.tokens, language);
let endState;
// try to save an object if possible
if (actualResult.endState.equals(state)) {
endState = state;
}
else {
endState = actualResult.endState;
}
return new languages.TokenizationResult(tokens, endState);
}
tokenize(line, hasEOL, state) {
return TokenizationSupportAdapter.adaptTokenize(this._languageId, this._actual, line, state);
}
_toBinaryTokens(languageIdCodec, tokens) {
const languageId = languageIdCodec.encodeLanguageId(this._languageId);
const tokenTheme = this._standaloneThemeService.getColorTheme().tokenTheme;
const result = [];
let resultLen = 0;
let previousStartIndex = 0;
for (let i = 0, len = tokens.length; i < len; i++) {
const t = tokens[i];
const metadata = tokenTheme.match(languageId, t.scopes) | 1024 /* MetadataConsts.BALANCED_BRACKETS_MASK */;
if (resultLen > 0 && result[resultLen - 1] === metadata) {
// same metadata
continue;
}
let startIndex = t.startIndex;
// Prevent issues stemming from a buggy external tokenizer.
if (i === 0) {
// Force first token to start at first index!
startIndex = 0;
}
else if (startIndex < previousStartIndex) {
// Force tokens to be after one another!
startIndex = previousStartIndex;
}
result[resultLen++] = startIndex;
result[resultLen++] = metadata;
previousStartIndex = startIndex;
}
const actualResult = new Uint32Array(resultLen);
for (let i = 0; i < resultLen; i++) {
actualResult[i] = result[i];
}
return actualResult;
}
tokenizeEncoded(line, hasEOL, state) {
const actualResult = this._actual.tokenize(line, state);
const tokens = this._toBinaryTokens(this._languageService.languageIdCodec, actualResult.tokens);
let endState;
// try to save an object if possible
if (actualResult.endState.equals(state)) {
endState = state;
}
else {
endState = actualResult.endState;
}
return new languages.EncodedTokenizationResult(tokens, endState);
}
}
function isATokensProvider(provider) {
return (typeof provider.getInitialState === 'function');
}
function isEncodedTokensProvider(provider) {
return 'tokenizeEncoded' in provider;
}
function isThenable(obj) {
return obj && typeof obj.then === 'function';
}
/**
* Change the color map that is used for token colors.
* Supported formats (hex): #RRGGBB, $RRGGBBAA, #RGB, #RGBA
*/
export function setColorMap(colorMap) {
const standaloneThemeService = StandaloneServices.get(IStandaloneThemeService);
if (colorMap) {
const result = [null];
for (let i = 1, len = colorMap.length; i < len; i++) {
result[i] = Color.fromHex(colorMap[i]);
}
standaloneThemeService.setColorMapOverride(result);
}
else {
standaloneThemeService.setColorMapOverride(null);
}
}
/**
* @internal
*/
function createTokenizationSupportAdapter(languageId, provider) {
if (isEncodedTokensProvider(provider)) {
return new EncodedTokenizationSupportAdapter(languageId, provider);
}
else {
return new TokenizationSupportAdapter(languageId, provider, StandaloneServices.get(ILanguageService), StandaloneServices.get(IStandaloneThemeService));
}
}
/**
* Register a tokens provider factory for a language. This tokenizer will be exclusive with a tokenizer
* set using `setTokensProvider` or one created using `setMonarchTokensProvider`, but will work together
* with a tokens provider set using `registerDocumentSemanticTokensProvider` or `registerDocumentRangeSemanticTokensProvider`.
*/
export function registerTokensProviderFactory(languageId, factory) {
const adaptedFactory = new languages.LazyTokenizationSupport(async () => {
const result = await Promise.resolve(factory.create());
if (!result) {
return null;
}
if (isATokensProvider(result)) {
return createTokenizationSupportAdapter(languageId, result);
}
return new MonarchTokenizer(StandaloneServices.get(ILanguageService), StandaloneServices.get(IStandaloneThemeService), languageId, compile(languageId, result), StandaloneServices.get(IConfigurationService));
});
return languages.TokenizationRegistry.registerFactory(languageId, adaptedFactory);
}
/**
* Set the tokens provider for a language (manual implementation). This tokenizer will be exclusive
* with a tokenizer created using `setMonarchTokensProvider`, or with `registerTokensProviderFactory`,
* but will work together with a tokens provider set using `registerDocumentSemanticTokensProvider`
* or `registerDocumentRangeSemanticTokensProvider`.
*/
export function setTokensProvider(languageId, provider) {
const languageService = StandaloneServices.get(ILanguageService);
if (!languageService.isRegisteredLanguageId(languageId)) {
throw new Error(`Cannot set tokens provider for unknown language ${languageId}`);
}
if (isThenable(provider)) {
return registerTokensProviderFactory(languageId, { create: () => provider });
}
return languages.TokenizationRegistry.register(languageId, createTokenizationSupportAdapter(languageId, provider));
}
/**
* Set the tokens provider for a language (monarch implementation). This tokenizer will be exclusive
* with a tokenizer set using `setTokensProvider`, or with `registerTokensProviderFactory`, but will
* work together with a tokens provider set using `registerDocumentSemanticTokensProvider` or
* `registerDocumentRangeSemanticTokensProvider`.
*/
export function setMonarchTokensProvider(languageId, languageDef) {
const create = (languageDef) => {
return new MonarchTokenizer(StandaloneServices.get(ILanguageService), StandaloneServices.get(IStandaloneThemeService), languageId, compile(languageId, languageDef), StandaloneServices.get(IConfigurationService));
};
if (isThenable(languageDef)) {
return registerTokensProviderFactory(languageId, { create: () => languageDef });
}
return languages.TokenizationRegistry.register(languageId, create(languageDef));
}
/**
* Register a reference provider (used by e.g. reference search).
*/
export function registerReferenceProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.referenceProvider.register(languageSelector, provider);
}
/**
* Register a rename provider (used by e.g. rename symbol).
*/
export function registerRenameProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.renameProvider.register(languageSelector, provider);
}
/**
* Register a new symbol-name provider (e.g., when a symbol is being renamed, show new possible symbol-names)
*/
export function registerNewSymbolNameProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.newSymbolNamesProvider.register(languageSelector, provider);
}
/**
* Register a signature help provider (used by e.g. parameter hints).
*/
export function registerSignatureHelpProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.signatureHelpProvider.register(languageSelector, provider);
}
/**
* Register a hover provider (used by e.g. editor hover).
*/
export function registerHoverProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.hoverProvider.register(languageSelector, {
provideHover: async (model, position, token, context) => {
const word = model.getWordAtPosition(position);
return Promise.resolve(provider.provideHover(model, position, token, context)).then((value) => {
if (!value) {
return undefined;
}
if (!value.range && word) {
value.range = new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn);
}
if (!value.range) {
value.range = new Range(position.lineNumber, position.column, position.lineNumber, position.column);
}
return value;
});
}
});
}
/**
* Register a document symbol provider (used by e.g. outline).
*/
export function registerDocumentSymbolProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.documentSymbolProvider.register(languageSelector, provider);
}
/**
* Register a document highlight provider (used by e.g. highlight occurrences).
*/
export function registerDocumentHighlightProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.documentHighlightProvider.register(languageSelector, provider);
}
/**
* Register an linked editing range provider.
*/
export function registerLinkedEditingRangeProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.linkedEditingRangeProvider.register(languageSelector, provider);
}
/**
* Register a definition provider (used by e.g. go to definition).
*/
export function registerDefinitionProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.definitionProvider.register(languageSelector, provider);
}
/**
* Register a implementation provider (used by e.g. go to implementation).
*/
export function registerImplementationProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.implementationProvider.register(languageSelector, provider);
}
/**
* Register a type definition provider (used by e.g. go to type definition).
*/
export function registerTypeDefinitionProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.typeDefinitionProvider.register(languageSelector, provider);
}
/**
* Register a code lens provider (used by e.g. inline code lenses).
*/
export function registerCodeLensProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.codeLensProvider.register(languageSelector, provider);
}
/**
* Register a code action provider (used by e.g. quick fix).
*/
export function registerCodeActionProvider(languageSelector, provider, metadata) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.codeActionProvider.register(languageSelector, {
providedCodeActionKinds: metadata?.providedCodeActionKinds,
documentation: metadata?.documentation,
provideCodeActions: (model, range, context, token) => {
const markerService = StandaloneServices.get(IMarkerService);
const markers = markerService.read({ resource: model.uri }).filter(m => {
return Range.areIntersectingOrTouching(m, range);
});
return provider.provideCodeActions(model, range, { markers, only: context.only, trigger: context.trigger }, token);
},
resolveCodeAction: provider.resolveCodeAction
});
}
/**
* Register a formatter that can handle only entire models.
*/
export function registerDocumentFormattingEditProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.documentFormattingEditProvider.register(languageSelector, provider);
}
/**
* Register a formatter that can handle a range inside a model.
*/
export function registerDocumentRangeFormattingEditProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.documentRangeFormattingEditProvider.register(languageSelector, provider);
}
/**
* Register a formatter than can do formatting as the user types.
*/
export function registerOnTypeFormattingEditProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.onTypeFormattingEditProvider.register(languageSelector, provider);
}
/**
* Register a link provider that can find links in text.
*/
export function registerLinkProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.linkProvider.register(languageSelector, provider);
}
/**
* Register a completion item provider (use by e.g. suggestions).
*/
export function registerCompletionItemProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.completionProvider.register(languageSelector, provider);
}
/**
* Register a document color provider (used by Color Picker, Color Decorator).
*/
export function registerColorProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.colorProvider.register(languageSelector, provider);
}
/**
* Register a folding range provider
*/
export function registerFoldingRangeProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.foldingRangeProvider.register(languageSelector, provider);
}
/**
* Register a declaration provider
*/
export function registerDeclarationProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.declarationProvider.register(languageSelector, provider);
}
/**
* Register a selection range provider
*/
export function registerSelectionRangeProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.selectionRangeProvider.register(languageSelector, provider);
}
/**
* Register a document semantic tokens provider. A semantic tokens provider will complement and enhance a
* simple top-down tokenizer. Simple top-down tokenizers can be set either via `setMonarchTokensProvider`
* or `setTokensProvider`.
*
* For the best user experience, register both a semantic tokens provider and a top-down tokenizer.
*/
export function registerDocumentSemanticTokensProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.documentSemanticTokensProvider.register(languageSelector, provider);
}
/**
* Register a document range semantic tokens provider. A semantic tokens provider will complement and enhance a
* simple top-down tokenizer. Simple top-down tokenizers can be set either via `setMonarchTokensProvider`
* or `setTokensProvider`.
*
* For the best user experience, register both a semantic tokens provider and a top-down tokenizer.
*/
export function registerDocumentRangeSemanticTokensProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.documentRangeSemanticTokensProvider.register(languageSelector, provider);
}
/**
* Register an inline completions provider.
*/
export function registerInlineCompletionsProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.inlineCompletionsProvider.register(languageSelector, provider);
}
export function registerInlineEditProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.inlineEditProvider.register(languageSelector, provider);
}
/**
* Register an inlay hints provider.
*/
export function registerInlayHintsProvider(languageSelector, provider) {
const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService);
return languageFeaturesService.inlayHintsProvider.register(languageSelector, provider);
}
/**
* @internal
*/
export function createMonacoLanguagesAPI() {
return {
register: register,
getLanguages: getLanguages,
onLanguage: onLanguage,
onLanguageEncountered: onLanguageEncountered,
getEncodedLanguageId: getEncodedLanguageId,
// provider methods
setLanguageConfiguration: setLanguageConfiguration,
setColorMap: setColorMap,
registerTokensProviderFactory: registerTokensProviderFactory,
setTokensProvider: setTokensProvider,
setMonarchTokensProvider: setMonarchTokensProvider,
registerReferenceProvider: registerReferenceProvider,
registerRenameProvider: registerRenameProvider,
registerNewSymbolNameProvider: registerNewSymbolNameProvider,
registerCompletionItemProvider: registerCompletionItemProvider,
registerSignatureHelpProvider: registerSignatureHelpProvider,
registerHoverProvider: registerHoverProvider,
registerDocumentSymbolProvider: registerDocumentSymbolProvider,
registerDocumentHighlightProvider: registerDocumentHighlightProvider,
registerLinkedEditingRangeProvider: registerLinkedEditingRangeProvider,
registerDefinitionProvider: registerDefinitionProvider,
registerImplementationProvider: registerImplementationProvider,
registerTypeDefinitionProvider: registerTypeDefinitionProvider,
registerCodeLensProvider: registerCodeLensProvider,
registerCodeActionProvider: registerCodeActionProvider,
registerDocumentFormattingEditProvider: registerDocumentFormattingEditProvider,
registerDocumentRangeFormattingEditProvider: registerDocumentRangeFormattingEditProvider,
registerOnTypeFormattingEditProvider: registerOnTypeFormattingEditProvider,
registerLinkProvider: registerLinkProvider,
registerColorProvider: registerColorProvider,
registerFoldingRangeProvider: registerFoldingRangeProvider,
registerDeclarationProvider: registerDeclarationProvider,
registerSelectionRangeProvider: registerSelectionRangeProvider,
registerDocumentSemanticTokensProvider: registerDocumentSemanticTokensProvider,
registerDocumentRangeSemanticTokensProvider: registerDocumentRangeSemanticTokensProvider,
registerInlineCompletionsProvider: registerInlineCompletionsProvider,
registerInlineEditProvider: registerInlineEditProvider,
registerInlayHintsProvider: registerInlayHintsProvider,
// enums
DocumentHighlightKind: standaloneEnums.DocumentHighlightKind,
CompletionItemKind: standaloneEnums.CompletionItemKind,
CompletionItemTag: standaloneEnums.CompletionItemTag,
CompletionItemInsertTextRule: standaloneEnums.CompletionItemInsertTextRule,
SymbolKind: standaloneEnums.SymbolKind,
SymbolTag: standaloneEnums.SymbolTag,
IndentAction: standaloneEnums.IndentAction,
CompletionTriggerKind: standaloneEnums.CompletionTriggerKind,
SignatureHelpTriggerKind: standaloneEnums.SignatureHelpTriggerKind,
InlayHintKind: standaloneEnums.InlayHintKind,
InlineCompletionTriggerKind: standaloneEnums.InlineCompletionTriggerKind,
InlineEditTriggerKind: standaloneEnums.InlineEditTriggerKind,
CodeActionTriggerType: standaloneEnums.CodeActionTriggerType,
NewSymbolNameTag: standaloneEnums.NewSymbolNameTag,
NewSymbolNameTriggerKind: standaloneEnums.NewSymbolNameTriggerKind,
PartialAcceptTriggerKind: standaloneEnums.PartialAcceptTriggerKind,
HoverVerbosityAction: standaloneEnums.HoverVerbosityAction,
// classes
FoldingRangeKind: languages.FoldingRangeKind,
SelectedSuggestionInfo: languages.SelectedSuggestionInfo,
};
}