Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/browser/codeeditor.ts
3294 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { IAction } from '../../base/common/actions.js';
7
import { Emitter } from '../../base/common/event.js';
8
import { Disposable, DisposableStore } from '../../base/common/lifecycle.js';
9
import { isEqual } from '../../base/common/resources.js';
10
import { URI } from '../../base/common/uri.js';
11
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference, isCodeEditor, isCompositeEditor } from '../../editor/browser/editorBrowser.js';
12
import { EmbeddedCodeEditorWidget } from '../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js';
13
import { EditorOption } from '../../editor/common/config/editorOptions.js';
14
import { IRange } from '../../editor/common/core/range.js';
15
import { CursorChangeReason, ICursorPositionChangedEvent } from '../../editor/common/cursorEvents.js';
16
import { IEditorContribution } from '../../editor/common/editorCommon.js';
17
import { IModelDecorationsChangeAccessor, TrackedRangeStickiness } from '../../editor/common/model.js';
18
import { ModelDecorationOptions } from '../../editor/common/model/textModel.js';
19
import { AbstractFloatingClickMenu, FloatingClickWidget } from '../../platform/actions/browser/floatingMenu.js';
20
import { IMenuService, MenuId } from '../../platform/actions/common/actions.js';
21
import { IContextKeyService } from '../../platform/contextkey/common/contextkey.js';
22
import { IInstantiationService } from '../../platform/instantiation/common/instantiation.js';
23
import { IKeybindingService } from '../../platform/keybinding/common/keybinding.js';
24
import { IEditorService } from '../services/editor/common/editorService.js';
25
26
export interface IRangeHighlightDecoration {
27
resource: URI;
28
range: IRange;
29
isWholeLine?: boolean;
30
}
31
32
export class RangeHighlightDecorations extends Disposable {
33
34
private readonly _onHighlightRemoved = this._register(new Emitter<void>());
35
readonly onHighlightRemoved = this._onHighlightRemoved.event;
36
37
private rangeHighlightDecorationId: string | null = null;
38
private editor: ICodeEditor | null = null;
39
private readonly editorDisposables = this._register(new DisposableStore());
40
41
constructor(@IEditorService private readonly editorService: IEditorService) {
42
super();
43
}
44
45
removeHighlightRange() {
46
if (this.editor && this.rangeHighlightDecorationId) {
47
const decorationId = this.rangeHighlightDecorationId;
48
this.editor.changeDecorations((accessor) => {
49
accessor.removeDecoration(decorationId);
50
});
51
this._onHighlightRemoved.fire();
52
}
53
54
this.rangeHighlightDecorationId = null;
55
}
56
57
highlightRange(range: IRangeHighlightDecoration, editor?: any) {
58
editor = editor ?? this.getEditor(range);
59
if (isCodeEditor(editor)) {
60
this.doHighlightRange(editor, range);
61
} else if (isCompositeEditor(editor) && isCodeEditor(editor.activeCodeEditor)) {
62
this.doHighlightRange(editor.activeCodeEditor, range);
63
}
64
}
65
66
private doHighlightRange(editor: ICodeEditor, selectionRange: IRangeHighlightDecoration) {
67
this.removeHighlightRange();
68
69
editor.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => {
70
this.rangeHighlightDecorationId = changeAccessor.addDecoration(selectionRange.range, this.createRangeHighlightDecoration(selectionRange.isWholeLine));
71
});
72
73
this.setEditor(editor);
74
}
75
76
private getEditor(resourceRange: IRangeHighlightDecoration): ICodeEditor | undefined {
77
const resource = this.editorService.activeEditor?.resource;
78
if (resource && isEqual(resource, resourceRange.resource) && isCodeEditor(this.editorService.activeTextEditorControl)) {
79
return this.editorService.activeTextEditorControl;
80
}
81
82
return undefined;
83
}
84
85
private setEditor(editor: ICodeEditor) {
86
if (this.editor !== editor) {
87
this.editorDisposables.clear();
88
this.editor = editor;
89
this.editorDisposables.add(this.editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => {
90
if (
91
e.reason === CursorChangeReason.NotSet
92
|| e.reason === CursorChangeReason.Explicit
93
|| e.reason === CursorChangeReason.Undo
94
|| e.reason === CursorChangeReason.Redo
95
) {
96
this.removeHighlightRange();
97
}
98
}));
99
this.editorDisposables.add(this.editor.onDidChangeModel(() => { this.removeHighlightRange(); }));
100
this.editorDisposables.add(this.editor.onDidDispose(() => {
101
this.removeHighlightRange();
102
this.editor = null;
103
}));
104
}
105
}
106
107
private static readonly _WHOLE_LINE_RANGE_HIGHLIGHT = ModelDecorationOptions.register({
108
description: 'codeeditor-range-highlight-whole',
109
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
110
className: 'rangeHighlight',
111
isWholeLine: true
112
});
113
114
private static readonly _RANGE_HIGHLIGHT = ModelDecorationOptions.register({
115
description: 'codeeditor-range-highlight',
116
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
117
className: 'rangeHighlight'
118
});
119
120
private createRangeHighlightDecoration(isWholeLine: boolean = true): ModelDecorationOptions {
121
return (isWholeLine ? RangeHighlightDecorations._WHOLE_LINE_RANGE_HIGHLIGHT : RangeHighlightDecorations._RANGE_HIGHLIGHT);
122
}
123
124
override dispose() {
125
super.dispose();
126
127
if (this.editor?.getModel()) {
128
this.removeHighlightRange();
129
this.editor = null;
130
}
131
}
132
}
133
134
export class FloatingEditorClickWidget extends FloatingClickWidget implements IOverlayWidget {
135
136
constructor(
137
private editor: ICodeEditor,
138
label: string,
139
keyBindingAction: string | null,
140
@IKeybindingService keybindingService: IKeybindingService
141
) {
142
super(
143
keyBindingAction && keybindingService.lookupKeybinding(keyBindingAction)
144
? `${label} (${keybindingService.lookupKeybinding(keyBindingAction)!.getLabel()})`
145
: label
146
);
147
}
148
149
getId(): string {
150
return 'editor.overlayWidget.floatingClickWidget';
151
}
152
153
getPosition(): IOverlayWidgetPosition {
154
return {
155
preference: OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER
156
};
157
}
158
159
override render() {
160
super.render();
161
this.editor.addOverlayWidget(this);
162
}
163
164
override dispose(): void {
165
this.editor.removeOverlayWidget(this);
166
super.dispose();
167
}
168
169
}
170
171
export class FloatingEditorClickMenu extends AbstractFloatingClickMenu implements IEditorContribution {
172
static readonly ID = 'editor.contrib.floatingClickMenu';
173
174
constructor(
175
private readonly editor: ICodeEditor,
176
@IInstantiationService private readonly instantiationService: IInstantiationService,
177
@IMenuService menuService: IMenuService,
178
@IContextKeyService contextKeyService: IContextKeyService
179
) {
180
super(MenuId.EditorContent, menuService, contextKeyService);
181
this.render();
182
}
183
184
protected override createWidget(action: IAction): FloatingClickWidget {
185
return this.instantiationService.createInstance(FloatingEditorClickWidget, this.editor, action.label, action.id);
186
}
187
188
protected override isVisible() {
189
return !(this.editor instanceof EmbeddedCodeEditorWidget) && this.editor?.hasModel() && !this.editor.getOption(EditorOption.inDiffEditor);
190
}
191
192
protected override getActionArg(): unknown {
193
return this.editor.getModel()?.uri;
194
}
195
}
196
197