import { Uri, objects } from '@opensumi/ide-core-browser'; import { IConfigurationService } from '@opensumi/monaco-editor-core/esm/vs/platform/configuration/common/configuration'; import { IConvertedMonacoOptions } from '../types'; const { removeUndefined } = objects; type NoConverter = false; type KaitianPreferenceKey = string; type MonacoPreferenceKey = string; /** * monacoOption和Preference的转换 */ interface IMonacoOptionsConverter { /** * monaco编辑器的设置值 */ monaco: MonacoPreferenceKey; /** * 转换器:输入为Preference值,输出monaco Options值 */ convert?: (value: any) => any; } /** * Configuration options for the editor. */ export const editorOptionsConverters: Map = new Map< string, NoConverter | IMonacoOptionsConverter >([ /** * The aria label for the editor's textarea (when it is focused). */ ['editor.ariaLabel', { monaco: 'ariaLabel' }], /** * Render vertical lines at the specified columns. * Defaults to empty array. */ ['editor.rulers', { monaco: 'rulers' }], /** * A string containing the word separators used when doing word navigation. * Defaults to `~!@#$%^&*()-=+[{]}\\|;'', */ ['editor.wordSeparators', { monaco: 'wordSeparators' }], /** * Enable Linux primary clipboard. * Defaults to true. */ ['editor.selectionClipboard', { monaco: 'selectionClipboard' }], /** * Control the rendering of line numbers. * If it is a function, it will be invoked when rendering a line number and the return value will be rendered. * Otherwise, if it is a truey, line numbers will be rendered normally (equivalent of using an identity function). * Otherwise, line numbers will not be rendered. * Defaults to true. */ ['editor.lineNumbers', { monaco: 'lineNumbers' }], /** * Render last line number when the file ends with a newline. * Defaults to true. */ ['editor.renderFinalNewline', { monaco: 'renderFinalNewline' }], /** * Should the corresponding line be selected when clicking on the line number? * Defaults to true. */ ['editor.selectOnLineNumbers', { monaco: 'selectOnLineNumbers' }], /** * Control the width of line numbers, by reserving horizontal space for rendering at least an amount of digits. * Defaults to 5. */ ['editor.lineNumbersMinChars', { monaco: 'lineNumbersMinChars' }], /** * Enable the rendering of the glyph margin. * Defaults to true in vscode and to false in monaco-editor. */ ['editor.glyphMargin', { monaco: 'glyphMargin' }], /** * The width reserved for line decorations (in px). * Line decorations are placed between line numbers and the editor content. * You can pass in a string in the format floating point followed by "ch". e.g. 1.3ch. * Defaults to 10. */ ['editor.lineDecorationsWidth', { monaco: 'lineDecorationsWidth' }], /** * When revealing the cursor, a virtual padding (px) is added to the cursor, turning it into a rectangle. * This virtual padding ensures that the cursor gets revealed before hitting the edge of the viewport. * Defaults to 30 (px). */ ['editor.revealHorizontalRightPadding', { monaco: 'revealHorizontalRightPadding' }], /** * Render the editor selection with rounded borders. * Defaults to true. */ ['editor.roundedSelection', { monaco: 'roundedSelection' }], /** * Class name to be added to the editor. */ ['editor.extraEditorClassName', { monaco: 'extraEditorClassName' }], /** * Should the editor be read only. * Defaults to false. */ ['editor.readOnly', { monaco: 'readOnly' }], /** * Control the behavior and rendering of the scrollbars. */ ['editor.scrollbar', { monaco: 'scrollbar' }], /** * Control the behavior and rendering of the minimap. */ [ 'editor.minimap', { monaco: 'minimap', convert: (value: any) => ({ enabled: value, }), }, ], /** * Control the behavior of the find widget. */ ['editor.find', { monaco: 'find' }], /** * Display overflow widgets as `fixed`. * Defaults to `false`. */ ['editor.fixedOverflowWidgets', { monaco: 'fixedOverflowWidgets' }], /** * The number of vertical lanes the overview ruler should render. * Defaults to 2. */ ['editor.overviewRulerLanes', { monaco: 'overviewRulerLanes' }], /** * Controls if a border should be drawn around the overview ruler. * Defaults to `true`. */ ['editor.overviewRulerBorder', { monaco: 'overviewRulerBorder' }], /** * Control the cursor animation style, possible values are 'blink', 'smooth', 'phase', 'expand' and 'solid'. * Defaults to 'blink'. */ ['editor.cursorBlinking', { monaco: 'cursorBlinking' }], /** * Zoom the font in the editor when using the mouse wheel in combination with holding Ctrl. * Defaults to false. */ ['editor.mouseWheelZoom', { monaco: 'mouseWheelZoom' }], /** * Enable smooth caret animation. * Defaults to false. */ ['editor.cursorSmoothCaretAnimation', { monaco: 'cursorSmoothCaretAnimation' }], /** * Control the cursor style, either 'block' or 'line'. * Defaults to 'line'. */ ['editor.cursorStyle', { monaco: 'cursorStyle' }], /** * Control the width of the cursor when cursorStyle is set to 'line' */ ['editor.cursorWidth', { monaco: 'cursorWidth' }], /** * Enable font ligatures. * Defaults to false. */ ['editor.fontLigatures', { monaco: 'fontLigatures' }], /** * Enable that the selection with the mouse and keys is doing column selection. * Defaults to false. */ ['editor.columnSelection', { monaco: 'columnSelection' }], /** * Disable the use of `will-change` for the editor margin and lines layers. * The usage of `will-change` acts as a hint for browsers to create an extra layer. * Defaults to false. */ ['editor.disableLayerHinting', { monaco: 'disableLayerHinting' }], /** * Disable the optimizations for monospace fonts. * Defaults to false. */ ['editor.disableMonospaceOptimizations', { monaco: 'disableMonospaceOptimizations' }], /** * Should the cursor be hidden in the overview ruler. * Defaults to false. */ ['editor.hideCursorInOverviewRuler', { monaco: 'hideCursorInOverviewRuler' }], /** * Enable that scrolling can go one screen size after the last line. * Defaults to true. */ ['editor.scrollBeyondLastLine', { monaco: 'scrollBeyondLastLine' }], /** * Enable that scrolling can go beyond the last column by a number of columns. * Defaults to 5. */ ['editor.scrollBeyondLastColumn', { monaco: 'scrollBeyondLastColumn' }], /** * Enable that the editor animates scrolling to a position. * Defaults to false. */ ['editor.smoothScrolling', { monaco: 'smoothScrolling' }], /** * Enable that the editor will install an interval to check if its container dom node size has changed. * Enabling this might have a severe performance impact. * Defaults to false. */ ['editor.automaticLayout', { monaco: 'automaticLayout' }], /** * Control the wrapping of the editor. * When `wordWrap` = "off", the lines will never wrap. * When `wordWrap` = "on", the lines will wrap at the viewport width. * When `wordWrap` = "wordWrapColumn", the lines will wrap at `wordWrapColumn`. * When `wordWrap` = "bounded", the lines will wrap at min(viewport width, wordWrapColumn). * Defaults to "off". */ ['editor.wordWrap', { monaco: 'wordWrap' }], /** * Control the wrapping of the editor. * When `wordWrap` = "off", the lines will never wrap. * When `wordWrap` = "on", the lines will wrap at the viewport width. * When `wordWrap` = "wordWrapColumn", the lines will wrap at `wordWrapColumn`. * When `wordWrap` = "bounded", the lines will wrap at min(viewport width, wordWrapColumn). * Defaults to 80. */ ['editor.wordWrapColumn', { monaco: 'wordWrapColumn' }], /** * Force word wrapping when the text appears to be of a minified/generated file. * Defaults to true. */ ['editor.wordWrapMinified', { monaco: 'wordWrapMinified' }], /** * Control indentation of wrapped lines. Can 'be', * Defaults to 'same' in vscode and to 'none' in monaco-editor. */ ['editor.wrappingIndent', { monaco: 'wrappingIndent' }], /** * Configure word wrapping characters. A break will be introduced before these characters. * Defaults to '{([+'. */ ['editor.wordWrapBreakBeforeCharacters', { monaco: 'wordWrapBreakBeforeCharacters' }], /** * Configure word wrapping characters. A break will be introduced after these characters. * Defaults to ' \t})]?|&,;'. */ ['editor.wordWrapBreakAfterCharacters', { monaco: 'wordWrapBreakAfterCharacters' }], /** * Configure word wrapping characters. A break will be introduced after these characters only if no `wordWrapBreakBeforeCharacters` or `wordWrapBreakAfterCharacters` were found. * Defaults to '.'. */ ['editor.wordWrapBreakObtrusiveCharacters', { monaco: 'wordWrapBreakObtrusiveCharacters' }], /** * Performance 'guard', * Defaults to 10000. * Use -1 to never stop rendering */ ['editor.stopRenderingLineAfter', { monaco: 'stopRenderingLineAfter' }], /** * Configure the editor's hover. */ ['editor.hover', { monaco: 'hover' }], /** * Enable detecting links and making them clickable. * Defaults to true. */ ['editor.links', { monaco: 'links' }], /** * Enable inline color decorators and color picker rendering. */ ['editor.colorDecorators', { monaco: 'colorDecorators' }], /** * Enable custom contextmenu. * Defaults to true. */ ['editor.contextmenu', { monaco: 'contextmenu' }], /** * A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events. * Defaults to 1. */ ['editor.mouseWheelScrollSensitivity', { monaco: 'mouseWheelScrollSensitivity' }], /** * FastScrolling mulitplier speed when pressing `Alt` * Defaults to 5. */ ['editor.fastScrollSensitivity', { monaco: 'fastScrollSensitivity' }], /** * The modifier to be used to add multiple cursors with the mouse. * Defaults to 'alt' */ ['editor.multiCursorModifier', { monaco: 'multiCursorModifier' }], /** * Merge overlapping selections. * Defaults to true */ ['editor.multiCursorMergeOverlapping', { monaco: 'multiCursorMergeOverlapping' }], /** * Configure the editor's accessibility support. * Defaults to 'auto'. It is best to leave this to 'auto'. */ ['editor.accessibilitySupport', { monaco: 'accessibilitySupport' }], /** * Suggest options. */ ['editor.suggest', { monaco: 'suggest' }], /** * */ ['editor.gotoLocation', { monaco: 'gotoLocation' }], /** * Enable quick suggestions (shadow suggestions) * Defaults to true. */ ['editor.quickSuggestions', { monaco: 'quickSuggestions' }], /** * Quick suggestions show delay (in ms) * Defaults to 500 (ms) */ ['editor.quickSuggestionsDelay', { monaco: 'quickSuggestionsDelay' }], /** * Parameter hint options. */ ['editor.parameterHints', { monaco: 'parameterHints' }], /** * Options for auto closing brackets. * Defaults to language defined behavior. */ ['editor.autoClosingBrackets', { monaco: 'autoClosingBrackets' }], /** * Options for auto closing quotes. * Defaults to language defined behavior. */ ['editor.autoClosingQuotes', { monaco: 'autoClosingQuotes' }], /** * Options for auto surrounding. * Defaults to always allowing auto surrounding. */ ['editor.autoSurround', { monaco: 'autoSurround' }], /** * Enable auto indentation adjustment. * Defaults to false. */ ['editor.autoIndent', { monaco: 'autoIndent' }], /** * Enable format on type. * Defaults to false. */ ['editor.formatOnType', { monaco: 'formatOnType' }], /** * Enable format on paste. * Defaults to false. */ ['editor.formatOnPaste', { monaco: 'formatOnPaste' }], /** * Controls if the editor should allow to move selections via drag and drop. * Defaults to false. */ ['editor.dragAndDrop', { monaco: 'dragAndDrop' }], /** * Enable the suggestion box to pop-up on trigger characters. * Defaults to true. */ ['editor.suggestOnTriggerCharacters', { monaco: 'suggestOnTriggerCharacters' }], /** * Accept suggestions on ENTER. * Defaults to 'on'. */ ['editor.acceptSuggestionOnEnter', { monaco: 'acceptSuggestionOnEnter' }], /** * Accept suggestions on provider defined characters. * Defaults to true. */ ['editor.acceptSuggestionOnCommitCharacter', { monaco: 'acceptSuggestionOnCommitCharacter' }], /** * Enable snippet suggestions. Default to 'true'. */ ['editor.snippetSuggestions', { monaco: 'snippetSuggestions' }], /** * Copying without a selection copies the current line. */ ['editor.emptySelectionClipboard', { monaco: 'emptySelectionClipboard' }], /** * Syntax highlighting is copied. */ ['editor.copyWithSyntaxHighlighting', { monaco: 'copyWithSyntaxHighlighting' }], /** * Enable word based suggestions. Defaults to 'true' */ ['editor.wordBasedSuggestions', { monaco: 'wordBasedSuggestions' }], /** * The history mode for suggestions. */ ['editor.suggestSelection', { monaco: 'suggestSelection' }], /** * The font size for the suggest widget. * Defaults to the editor font size. */ ['editor.suggestFontSize', { monaco: 'suggestFontSize' }], /** * The line height for the suggest widget. * Defaults to the editor line height. */ ['editor.suggestLineHeight', { monaco: 'suggestLineHeight' }], /** * Enable tab completion. */ ['editor.tabCompletion', { monaco: 'tabCompletion' }], /** * Enable selection highlight. * Defaults to true. */ ['editor.selectionHighlight', { monaco: 'selectionHighlight' }], /** * Enable semantic occurrences highlight. * Defaults to true. */ ['editor.occurrencesHighlight', { monaco: 'occurrencesHighlight' }], /** * Show code lens * Defaults to true. */ ['editor.codeLens', { monaco: 'codeLens' }], /** * Control the behavior and rendering of the code action lightbulb. */ ['editor.lightbulb', { monaco: 'lightbulb' }], /** * Code action kinds to be run on save. */ ['editor.codeActionsOnSave', { monaco: 'codeActionsOnSave' }], /** * Timeout for running code actions on save. */ ['editor.codeActionsOnSaveTimeout', { monaco: 'codeActionsOnSaveTimeout' }], /** * Enable code folding * Defaults to true. */ ['editor.folding', { monaco: 'folding' }], /** * Selects the folding strategy. 'auto' uses the strategies contributed for the current document, 'indentation' uses the indentation based folding strategy. * Defaults to 'auto'. */ ['editor.foldingStrategy', { monaco: 'foldingStrategy' }], /** * Controls whether the fold actions in the gutter stay always visible or hide unless the mouse is over the gutter. * Defaults to 'mouseover'. */ ['editor.showFoldingControls', { monaco: 'showFoldingControls' }], /** * Enable highlighting of matching brackets. * Defaults to true. */ ['editor.matchBrackets', { monaco: 'matchBrackets' }], /** * Enable rendering of whitespace. * Defaults to none. */ ['editor.renderWhitespace', { monaco: 'renderWhitespace' }], /** * Enable rendering of control characters. * Defaults to false. */ ['editor.renderControlCharacters', { monaco: 'renderControlCharacters' }], /** * Enable rendering of indent guides. * Defaults to true. */ ['editor.renderIndentGuides', { monaco: 'guides.indentation' }], /** * Enable highlighting of the active indent guide. * Defaults to true. */ ['editor.highlightActiveIndentGuide', { monaco: 'guides.highlightActiveIndentation' }], /** * editor.guides -> guides */ ['editor.guides', { monaco: 'guides' }], /** * Enable rendering of current line highlight. * Defaults to all. */ ['editor.renderLineHighlight', { monaco: 'renderLineHighlight' }], /** * Inserting and deleting whitespace follows tab stops. */ ['editor.useTabStops', { monaco: 'useTabStops' }], /** * The font family */ ['editor.fontFamily', { monaco: 'fontFamily' }], /** * The font weight */ ['editor.fontWeight', { monaco: 'fontWeight' }], /** * The font size */ ['editor.fontSize', { monaco: 'fontSize' }], /** * The line height */ ['editor.lineHeight', { monaco: 'lineHeight' }], /** * The letter spacing */ ['editor.letterSpacing', { monaco: 'letterSpacing' }], /** * Controls fading out of unused variables. */ ['editor.showUnused', { monaco: 'showUnused' }], ['editor.rename.enablePreview', { monaco: 'editor.rename.enablePreview' }], ['editor.semanticHighlighting', { monaco: 'semanticHighlighting' }], ['editor.bracketPairColorization', { monaco: 'bracketPairColorization' }], /** * Controls the algorithm that computes wrapping points. * Default is "advanced" (Monaco Editor default is "simple") */ ['editor.wrappingStrategy', { monaco: 'wrappingStrategy' }], [ 'editor.experimental.stickyScroll.enabled', { monaco: 'experimental', convert: (value) => ({ stickyScroll: { enabled: value, }, }), }, ], /** * 是否强行readonly */ [ 'editor.forceReadOnly', { monaco: 'readOnly', convert: (value: boolean) => { if (value) { return true; } else { return undefined; } }, }, ], /** * Controls whether characters are highlighted that can be confused with basic ASCII characters */ ['editor.unicodeHighlight', { monaco: 'unicodeHighlight' }], /** * Suggest options. */ [ 'editor.inlineSuggest.showToolbar', { monaco: 'inlineSuggest', convert: (value) => { if (value) { return { showToolbar: value, }; } else { return undefined; } }, }, ], ]); export const textModelUpdateOptionsConverters: Map = new Map([ ['editor.tabSize', { monaco: 'tabSize' }], ['editor.indentSize', { monaco: 'indentSize' }], ['editor.insertSpaces', { monaco: 'insertSpaces' }], ['editor.trimAutoWhitespace', { monaco: 'trimAutoWhitespace' }], ]); export const diffEditorOptionsConverters: Map = new Map< string, NoConverter | IMonacoOptionsConverter >([ /** * Allow the user to resize the diff editor split view. * Defaults to true. */ ['diffEditor.enableSplitViewResizing', { monaco: 'enableSplitViewResizing' }], /** * Render the differences in two side-by-side editors. * Defaults to true. */ ['diffEditor.renderSideBySide', { monaco: 'renderSideBySide' }], /** * Compute the diff by ignoring leading/trailing whitespace * Defaults to true. */ ['diffEditor.ignoreTrimWhitespace', { monaco: 'ignoreTrimWhitespace' }], /** * Render +/- indicators for added/deleted changes. * Defaults to true. */ ['diffEditor.renderIndicators', { monaco: 'renderIndicators' }], /** * Original model should be editable? * Defaults to false. */ ['diffEditor.originalEditable', { monaco: 'originalEditable' }], [ 'diffEditor.minimap', { monaco: 'minimap', convert: (value: any) => ({ enabled: value, }), }, ], [ 'diffEditor.experimental.stickyScroll.enabled', { monaco: 'experimental', convert: (value) => ({ stickyScroll: { enabled: value, }, }), }, ], /** * Controls whether the diff editor shows unchanged regions. */ [ 'diffEditor.hideUnchangedRegions.enabled', { monaco: 'hideUnchangedRegions', convert: (value) => ({ enabled: value }) }, ], /** * Controls how many lines are used for unchanged regions. */ [ 'diffEditor.hideUnchangedRegions.revealLineCount', { monaco: 'hideUnchangedRegions', convert: (value) => ({ revealLineCount: value }) }, ], /** * Controls how many lines are used as a minimum for unchanged regions. */ [ 'diffEditor.hideUnchangedRegions.minimumLineCount', { monaco: 'hideUnchangedRegions', convert: (value) => ({ minimumLineCount: value }) }, ], /** * Controls how many lines are used as context when comparing unchanged regions. */ [ 'diffEditor.hideUnchangedRegions.contextLineCount', { monaco: 'hideUnchangedRegions', convert: (value) => ({ contextLineCount: value }) }, ], /** * Controls whether the diff editor should show detected code moves. */ ['diffEditor.experimental.showMoves', { monaco: 'experimental', convert: (value) => ({ showMoves: value }) }], /** * Controls whether the diff editor shows empty decorations to see where characters got inserted or deleted. */ [ 'diffEditor.experimental.showEmptyDecorations', { monaco: 'experimental', convert: (value) => ({ showEmptyDecorations: value }) }, ], ]); function isContainOptionKey(key: string, optionMap: Map) { if (optionMap.has(key)) { return true; } else { // 处理 "包含" 情况下的配置判断,如 // editor.suggest.xxx const keys = optionMap.keys(); for (const k of keys) { if (key.startsWith(k)) { return true; } } } return false; } export function isEditorOption(key: string) { return ( isContainOptionKey(key, editorOptionsConverters) || isContainOptionKey(key, textModelUpdateOptionsConverters) || isContainOptionKey(key, diffEditorOptionsConverters) ); } export function isDiffEditorOption(key: string): boolean { return isContainOptionKey(key, diffEditorOptionsConverters); } const editorOptionsConvertersKey = [...editorOptionsConverters.keys()]; const textModelUpdateOptionsConvertersKey = [...textModelUpdateOptionsConverters.keys()]; const diffEditorOptionsConvertersKey = [...diffEditorOptionsConverters.keys()]; /** * 计算由ConfigurationService设置值带来的monaco编辑器的属性 * @param configurationService IConfigurationService * @param updatingKey 需要处理的Preference key。如果没有这个值,默认处理全部。 */ export function getConvertedMonacoOptions( configurationService: IConfigurationService, resourceUri?: string, language?: string, updatingKey?: string[], ): IConvertedMonacoOptions { const resource = resourceUri ? Uri.parse(resourceUri) : undefined; const getOptions = (keys: string[], converters: Map): Partial => keys.reduce((options, key) => { const value = configurationService.getValue(key, { resource, overrideIdentifier: language }); if (value !== undefined) { const converter = converters.get(key); if (!converter) { options[key] = value; return options; } const targetKey = converter ? converter.monaco : key; const convertedValue = converter?.convert ? converter.convert(value) : value; if (!options[targetKey]) { options[targetKey] = convertedValue; } else { Object.assign(options[targetKey], convertedValue); } } return options; }, {}); const editorOptionsKeys = updatingKey ? updatingKey.filter((key) => editorOptionsConverters.has(key)) : [...editorOptionsConvertersKey]; const textModelUpdateOptionsKeys = updatingKey ? updatingKey.filter((key) => textModelUpdateOptionsConverters.has(key)) : [...textModelUpdateOptionsConvertersKey]; const diffEditorOptionsKeys = updatingKey ? updatingKey.filter((key) => diffEditorOptionsConverters.has(key)) : [...diffEditorOptionsConvertersKey]; const editorOptions = removeUndefined(getOptions(editorOptionsKeys, editorOptionsConverters)); const modelOptions = removeUndefined(getOptions(textModelUpdateOptionsKeys, textModelUpdateOptionsConverters)); const diffOptions = removeUndefined(getOptions(diffEditorOptionsKeys, diffEditorOptionsConverters)); return { editorOptions, modelOptions, diffOptions, }; }