Path: blob/main/src/vs/workbench/browser/codeeditor.ts
3294 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { IAction } from '../../base/common/actions.js';6import { Emitter } from '../../base/common/event.js';7import { Disposable, DisposableStore } from '../../base/common/lifecycle.js';8import { isEqual } from '../../base/common/resources.js';9import { URI } from '../../base/common/uri.js';10import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference, isCodeEditor, isCompositeEditor } from '../../editor/browser/editorBrowser.js';11import { EmbeddedCodeEditorWidget } from '../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js';12import { EditorOption } from '../../editor/common/config/editorOptions.js';13import { IRange } from '../../editor/common/core/range.js';14import { CursorChangeReason, ICursorPositionChangedEvent } from '../../editor/common/cursorEvents.js';15import { IEditorContribution } from '../../editor/common/editorCommon.js';16import { IModelDecorationsChangeAccessor, TrackedRangeStickiness } from '../../editor/common/model.js';17import { ModelDecorationOptions } from '../../editor/common/model/textModel.js';18import { AbstractFloatingClickMenu, FloatingClickWidget } from '../../platform/actions/browser/floatingMenu.js';19import { IMenuService, MenuId } from '../../platform/actions/common/actions.js';20import { IContextKeyService } from '../../platform/contextkey/common/contextkey.js';21import { IInstantiationService } from '../../platform/instantiation/common/instantiation.js';22import { IKeybindingService } from '../../platform/keybinding/common/keybinding.js';23import { IEditorService } from '../services/editor/common/editorService.js';2425export interface IRangeHighlightDecoration {26resource: URI;27range: IRange;28isWholeLine?: boolean;29}3031export class RangeHighlightDecorations extends Disposable {3233private readonly _onHighlightRemoved = this._register(new Emitter<void>());34readonly onHighlightRemoved = this._onHighlightRemoved.event;3536private rangeHighlightDecorationId: string | null = null;37private editor: ICodeEditor | null = null;38private readonly editorDisposables = this._register(new DisposableStore());3940constructor(@IEditorService private readonly editorService: IEditorService) {41super();42}4344removeHighlightRange() {45if (this.editor && this.rangeHighlightDecorationId) {46const decorationId = this.rangeHighlightDecorationId;47this.editor.changeDecorations((accessor) => {48accessor.removeDecoration(decorationId);49});50this._onHighlightRemoved.fire();51}5253this.rangeHighlightDecorationId = null;54}5556highlightRange(range: IRangeHighlightDecoration, editor?: any) {57editor = editor ?? this.getEditor(range);58if (isCodeEditor(editor)) {59this.doHighlightRange(editor, range);60} else if (isCompositeEditor(editor) && isCodeEditor(editor.activeCodeEditor)) {61this.doHighlightRange(editor.activeCodeEditor, range);62}63}6465private doHighlightRange(editor: ICodeEditor, selectionRange: IRangeHighlightDecoration) {66this.removeHighlightRange();6768editor.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => {69this.rangeHighlightDecorationId = changeAccessor.addDecoration(selectionRange.range, this.createRangeHighlightDecoration(selectionRange.isWholeLine));70});7172this.setEditor(editor);73}7475private getEditor(resourceRange: IRangeHighlightDecoration): ICodeEditor | undefined {76const resource = this.editorService.activeEditor?.resource;77if (resource && isEqual(resource, resourceRange.resource) && isCodeEditor(this.editorService.activeTextEditorControl)) {78return this.editorService.activeTextEditorControl;79}8081return undefined;82}8384private setEditor(editor: ICodeEditor) {85if (this.editor !== editor) {86this.editorDisposables.clear();87this.editor = editor;88this.editorDisposables.add(this.editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => {89if (90e.reason === CursorChangeReason.NotSet91|| e.reason === CursorChangeReason.Explicit92|| e.reason === CursorChangeReason.Undo93|| e.reason === CursorChangeReason.Redo94) {95this.removeHighlightRange();96}97}));98this.editorDisposables.add(this.editor.onDidChangeModel(() => { this.removeHighlightRange(); }));99this.editorDisposables.add(this.editor.onDidDispose(() => {100this.removeHighlightRange();101this.editor = null;102}));103}104}105106private static readonly _WHOLE_LINE_RANGE_HIGHLIGHT = ModelDecorationOptions.register({107description: 'codeeditor-range-highlight-whole',108stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,109className: 'rangeHighlight',110isWholeLine: true111});112113private static readonly _RANGE_HIGHLIGHT = ModelDecorationOptions.register({114description: 'codeeditor-range-highlight',115stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,116className: 'rangeHighlight'117});118119private createRangeHighlightDecoration(isWholeLine: boolean = true): ModelDecorationOptions {120return (isWholeLine ? RangeHighlightDecorations._WHOLE_LINE_RANGE_HIGHLIGHT : RangeHighlightDecorations._RANGE_HIGHLIGHT);121}122123override dispose() {124super.dispose();125126if (this.editor?.getModel()) {127this.removeHighlightRange();128this.editor = null;129}130}131}132133export class FloatingEditorClickWidget extends FloatingClickWidget implements IOverlayWidget {134135constructor(136private editor: ICodeEditor,137label: string,138keyBindingAction: string | null,139@IKeybindingService keybindingService: IKeybindingService140) {141super(142keyBindingAction && keybindingService.lookupKeybinding(keyBindingAction)143? `${label} (${keybindingService.lookupKeybinding(keyBindingAction)!.getLabel()})`144: label145);146}147148getId(): string {149return 'editor.overlayWidget.floatingClickWidget';150}151152getPosition(): IOverlayWidgetPosition {153return {154preference: OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER155};156}157158override render() {159super.render();160this.editor.addOverlayWidget(this);161}162163override dispose(): void {164this.editor.removeOverlayWidget(this);165super.dispose();166}167168}169170export class FloatingEditorClickMenu extends AbstractFloatingClickMenu implements IEditorContribution {171static readonly ID = 'editor.contrib.floatingClickMenu';172173constructor(174private readonly editor: ICodeEditor,175@IInstantiationService private readonly instantiationService: IInstantiationService,176@IMenuService menuService: IMenuService,177@IContextKeyService contextKeyService: IContextKeyService178) {179super(MenuId.EditorContent, menuService, contextKeyService);180this.render();181}182183protected override createWidget(action: IAction): FloatingClickWidget {184return this.instantiationService.createInstance(FloatingEditorClickWidget, this.editor, action.label, action.id);185}186187protected override isVisible() {188return !(this.editor instanceof EmbeddedCodeEditorWidget) && this.editor?.hasModel() && !this.editor.getOption(EditorOption.inDiffEditor);189}190191protected override getActionArg(): unknown {192return this.editor.getModel()?.uri;193}194}195196197