在 Monaco Editor 中自定义右键菜单并支持多级菜单(AMD)

为了自定义右键菜单,我们需要利用 MenuRegistry 和 MenuId 来注册自定义的菜单项。以下是添加自定义菜单和子菜单的关键步骤

操作editor.main.js文件修改如下

define(__m[875/*vs/editor/editor.api*/], __M([1/*require*/,0/*exports*/,37/*vs/editor/common/config/editorOptions*/,372/*vs/editor/common/services/editorBaseApi*/,874/*vs/editor/standalone/browser/standaloneEditor*/,872/*vs/editor/standalone/browser/standaloneLanguages*/,409/*vs/editor/contrib/format/browser/format*/,73/*vs/base/common/linkedList*/,29/*vs/platform/actions/common/actions*/]), function (require, exports, editorOptions_1, editorBaseApi_1, standaloneEditor_1, standaloneLanguages_1, format_1,linkedList_1,actions_1) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.languages = exports.editor = exports.Token = exports.Uri = exports.MarkerTag = exports.MarkerSeverity = exports.SelectionDirection = exports.Selection = exports.Range = exports.Position = exports.KeyMod = exports.KeyCode = exports.Emitter = exports.CancellationTokenSource = void 0;
    // Set defaults for standalone editor
    editorOptions_1.EditorOptions.wrappingIndent.defaultValue = 0 /* WrappingIndent.None */;
    editorOptions_1.EditorOptions.glyphMargin.defaultValue = false;
    editorOptions_1.EditorOptions.autoIndent.defaultValue = 3 /* EditorAutoIndentStrategy.Advanced */;
    editorOptions_1.EditorOptions.overviewRulerLanes.defaultValue = 2;
    // We need to register a formatter selector which simply picks the first available formatter.
    // See https://github.com/microsoft/monaco-editor/issues/2327
    format_1.FormattingConflicts.setFormatterSelector((formatter, document, mode) => Promise.resolve(formatter[0]));
    const api = (0, editorBaseApi_1.createMonacoBaseAPI)();
    api.editor = (0, standaloneEditor_1.createMonacoEditorAPI)();
    api.languages = (0, standaloneLanguages_1.createMonacoLanguagesAPI)();
    api.linkedList = linkedList_1;
    api.actions = actions_1;

添加了
73/*vs/base/common/linkedList*/,29/*vs/platform/actions/common/actions*/

linkedList_1,actions_1

api.linkedList = linkedList_1; api.actions = actions_1;

把这两个api暴露出来,就可以对菜单进行操作。
案例如下:

const MenuId = monaco.actions.MenuId;
const MenuRegistry = monaco.actions.MenuRegistry;
const setupContextMenuFeature = (editor) => {
	// removeAllMenus();
	addActionWithSubmenus(editor, {
		title: 'Change color',
		context: 'EditorChangeColorContext',
		group: 'edit',
		order: 0,
		actions: [
			{ id: 'red', label: 'Red', run: () => console.log('red') },
			{ id: 'green', label: 'Green', run: () => console.log('green') }
		]
	});
	addActionWithSubmenus(editor, {
		title: 'Change coordinates',
		context: 'EditorChangeCoordinatesContext',
		group: 'edit',
		order: 1,
		actions: [
			{ id: 'wgs', label: 'WGS', run: () => console.log('wgs') },
			{ id: 'utc', label: 'UTC', run: () => console.log('utc') },
			{ id: 'mgrs', label: 'MGRS', run: () => console.log('mgrs') }
		]
	});
};

const addActionWithSubmenus = (editor, descriptor) => {
	const submenu = new MenuId(descriptor.context);
	const list = new LinkedList();
	MenuRegistry._menuItems.set(submenu, list);

	for (let i = 0; i < descriptor.actions.length; i++) {
		const action = descriptor.actions[i];
		editor.addAction({
			id: action.id,
			label: action.label,
			run: action.run,
			contextMenuOrder: i,
			contextMenuGroupId: descriptor.context,
		});
		const actionId = editor
			.getSupportedActions()
			.find(a => a.label === action.label && a.id.endsWith(action.id)).id;
		const items = MenuRegistry._menuItems.get(MenuId.EditorContext);
		const item = popItem(items, actionId);
		if (item) {
			list.push(item);
		}
	}

	MenuRegistry._menuItems.get(MenuId.EditorContext).push({
		group: descriptor.group,
		order: descriptor.order,
		submenu: submenu,
		title: descriptor.title,
	});
};

const popItem = (items, id) => {
	let node = items._first;
	do {
		if (node.element?.command?.id === id) {
			items._remove(node);
			return node.element;
		}
		node = node.next;
	} while (node !== undefined);
};

const removeAllMenus = () => {
	const contextMenuEntry = MenuRegistry._menuItems.get(MenuId.EditorContext);
	let node = contextMenuEntry._first;
	do {
		if (node.element) {
			contextMenuEntry._remove(node);
		}
		node = node.next;
	} while (node !== undefined);
};
// 初始化自定义菜单
setupContextMenuFeature(editor);

展示效果:
image

posted @ 2025-05-10 16:35  三生&nbsp;  阅读(45)  评论(0)    收藏  举报