mirror of
https://github.com/coder/code-server.git
synced 2026-05-08 13:27:25 +02:00
chore(vscode): update to 1.54.2
This commit is contained in:
@@ -235,7 +235,7 @@ export namespace RevealLine_ {
|
||||
name: 'Reveal line argument object',
|
||||
description: `Property-value pairs that can be passed through this argument:
|
||||
* 'lineNumber': A mandatory line number value.
|
||||
* 'at': Logical position at which line has to be revealed .
|
||||
* 'at': Logical position at which line has to be revealed.
|
||||
\`\`\`
|
||||
'top', 'center', 'bottom'
|
||||
\`\`\`
|
||||
@@ -554,6 +554,8 @@ export namespace CoreNavigationCommands {
|
||||
case CursorMove_.Direction.Right:
|
||||
case CursorMove_.Direction.Up:
|
||||
case CursorMove_.Direction.Down:
|
||||
case CursorMove_.Direction.PrevBlankLine:
|
||||
case CursorMove_.Direction.NextBlankLine:
|
||||
case CursorMove_.Direction.WrappedLineStart:
|
||||
case CursorMove_.Direction.WrappedLineFirstNonWhitespaceCharacter:
|
||||
case CursorMove_.Direction.WrappedLineColumnCenter:
|
||||
@@ -1929,6 +1931,7 @@ registerOverwritableCommand(Handler.Type, {
|
||||
}]
|
||||
});
|
||||
registerOverwritableCommand(Handler.ReplacePreviousChar);
|
||||
registerOverwritableCommand(Handler.CompositionType);
|
||||
registerOverwritableCommand(Handler.CompositionStart);
|
||||
registerOverwritableCommand(Handler.CompositionEnd);
|
||||
registerOverwritableCommand(Handler.Paste);
|
||||
|
||||
@@ -42,6 +42,7 @@ export interface IPointerHandlerHelper {
|
||||
linesContentDomNode: HTMLElement;
|
||||
|
||||
focusTextArea(): void;
|
||||
dispatchTextAreaEvent(event: CustomEvent): void;
|
||||
|
||||
/**
|
||||
* Get the last rendered information for cursors & textarea.
|
||||
@@ -70,6 +71,7 @@ export class MouseHandler extends ViewEventHandler {
|
||||
protected mouseTargetFactory: MouseTargetFactory;
|
||||
protected readonly _mouseDownOperation: MouseDownOperation;
|
||||
private lastMouseLeaveTime: number;
|
||||
private _height: number;
|
||||
|
||||
constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {
|
||||
super();
|
||||
@@ -88,6 +90,7 @@ export class MouseHandler extends ViewEventHandler {
|
||||
));
|
||||
|
||||
this.lastMouseLeaveTime = -1;
|
||||
this._height = this._context.configuration.options.get(EditorOption.layoutInfo).height;
|
||||
|
||||
const mouseEvents = new EditorMouseEventFactory(this.viewHelper.viewDomNode);
|
||||
|
||||
@@ -112,7 +115,9 @@ export class MouseHandler extends ViewEventHandler {
|
||||
const e = new StandardWheelEvent(browserEvent);
|
||||
const doMouseWheelZoom = (
|
||||
platform.isMacintosh
|
||||
? (browserEvent.metaKey && !browserEvent.ctrlKey && !browserEvent.shiftKey && !browserEvent.altKey)
|
||||
// on macOS we support cmd + two fingers scroll (`metaKey` set)
|
||||
// and also the two fingers pinch gesture (`ctrKey` set)
|
||||
? ((browserEvent.metaKey || browserEvent.ctrlKey) && !browserEvent.shiftKey && !browserEvent.altKey)
|
||||
: (browserEvent.ctrlKey && !browserEvent.metaKey && !browserEvent.shiftKey && !browserEvent.altKey)
|
||||
);
|
||||
if (doMouseWheelZoom) {
|
||||
@@ -134,6 +139,17 @@ export class MouseHandler extends ViewEventHandler {
|
||||
}
|
||||
|
||||
// --- begin event handlers
|
||||
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
|
||||
if (e.hasChanged(EditorOption.layoutInfo)) {
|
||||
// layout change
|
||||
const height = this._context.configuration.options.get(EditorOption.layoutInfo).height;
|
||||
if (this._height !== height) {
|
||||
this._height = height;
|
||||
this._mouseDownOperation.onHeightChanged();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean {
|
||||
this._mouseDownOperation.onCursorStateChanged(e);
|
||||
return false;
|
||||
@@ -400,6 +416,10 @@ class MouseDownOperation extends Disposable {
|
||||
this._onScrollTimeout.cancel();
|
||||
}
|
||||
|
||||
public onHeightChanged(): void {
|
||||
this._mouseMoveMonitor.stopMonitoring();
|
||||
}
|
||||
|
||||
public onScrollChanged(): void {
|
||||
if (!this._isActive) {
|
||||
return;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler';
|
||||
import { IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { ClientCoordinates, EditorMouseEvent, EditorPagePosition, PageCoordinates } from 'vs/editor/browser/editorDom';
|
||||
@@ -767,11 +766,6 @@ export class MouseTargetFactory {
|
||||
const lineWidth = ctx.getLineWidth(lineNumber);
|
||||
|
||||
if (request.mouseContentHorizontalOffset > lineWidth) {
|
||||
if (browser.isEdgeLegacy && pos.column === 1) {
|
||||
// See https://github.com/microsoft/vscode/issues/10875
|
||||
const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth);
|
||||
return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(lineNumber, ctx.model.getLineMaxColumn(lineNumber)), undefined, detail);
|
||||
}
|
||||
const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth);
|
||||
return request.fulfill(MouseTargetType.CONTENT_EMPTY, pos, undefined, detail);
|
||||
}
|
||||
@@ -974,56 +968,6 @@ export class MouseTargetFactory {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Most probably IE
|
||||
*/
|
||||
private static _doHitTestWithMoveToPoint(ctx: HitTestContext, coords: ClientCoordinates): IHitTestResult {
|
||||
let resultPosition: Position | null = null;
|
||||
let resultHitTarget: Element | null = null;
|
||||
|
||||
const textRange: IETextRange = (<any>document.body).createTextRange();
|
||||
try {
|
||||
textRange.moveToPoint(coords.clientX, coords.clientY);
|
||||
} catch (err) {
|
||||
return {
|
||||
position: null,
|
||||
hitTarget: null
|
||||
};
|
||||
}
|
||||
|
||||
textRange.collapse(true);
|
||||
|
||||
// Now, let's do our best to figure out what we hit :)
|
||||
const parentElement = textRange ? textRange.parentElement() : null;
|
||||
const parent1 = parentElement ? parentElement.parentNode : null;
|
||||
const parent2 = parent1 ? parent1.parentNode : null;
|
||||
|
||||
const parent2ClassName = parent2 && parent2.nodeType === parent2.ELEMENT_NODE ? (<HTMLElement>parent2).className : '';
|
||||
|
||||
if (parent2ClassName === ViewLine.CLASS_NAME) {
|
||||
const rangeToContainEntireSpan = textRange.duplicate();
|
||||
rangeToContainEntireSpan.moveToElementText(parentElement!);
|
||||
rangeToContainEntireSpan.setEndPoint('EndToStart', textRange);
|
||||
|
||||
resultPosition = ctx.getPositionFromDOMInfo(<HTMLElement>parentElement, rangeToContainEntireSpan.text.length);
|
||||
// Move range out of the span node, IE doesn't like having many ranges in
|
||||
// the same spot and will act badly for lines containing dashes ('-')
|
||||
rangeToContainEntireSpan.moveToElementText(ctx.viewDomNode);
|
||||
} else {
|
||||
// Looks like we've hit the hover or something foreign
|
||||
resultHitTarget = parentElement;
|
||||
}
|
||||
|
||||
// Move range out of the span node, IE doesn't like having many ranges in
|
||||
// the same spot and will act badly for lines containing dashes ('-')
|
||||
textRange.moveToElementText(ctx.viewDomNode);
|
||||
|
||||
return {
|
||||
position: resultPosition,
|
||||
hitTarget: resultHitTarget
|
||||
};
|
||||
}
|
||||
|
||||
private static _snapToSoftTabBoundary(position: Position, viewModel: IViewModel): Position {
|
||||
const lineContent = viewModel.getLineContent(position.lineNumber);
|
||||
const { tabSize } = viewModel.getTextModelOptions();
|
||||
@@ -1035,30 +979,12 @@ export class MouseTargetFactory {
|
||||
}
|
||||
|
||||
private static _doHitTest(ctx: HitTestContext, request: BareHitTestRequest): IHitTestResult {
|
||||
// State of the art (18.10.2012):
|
||||
// The spec says browsers should support document.caretPositionFromPoint, but nobody implemented it (http://dev.w3.org/csswg/cssom-view/)
|
||||
// Gecko:
|
||||
// - they tried to implement it once, but failed: https://bugzilla.mozilla.org/show_bug.cgi?id=654352
|
||||
// - however, they do give out rangeParent/rangeOffset properties on mouse events
|
||||
// Webkit:
|
||||
// - they have implemented a previous version of the spec which was using document.caretRangeFromPoint
|
||||
// IE:
|
||||
// - they have a proprietary method on ranges, moveToPoint: https://msdn.microsoft.com/en-us/library/ie/ms536632(v=vs.85).aspx
|
||||
|
||||
// 24.08.2016: Edge has added WebKit's document.caretRangeFromPoint, but it is quite buggy
|
||||
// - when hit testing the cursor it returns the first or the last line in the viewport
|
||||
// - it inconsistently hits text nodes or span nodes, while WebKit only hits text nodes
|
||||
// - when toggling render whitespace on, and hit testing in the empty content after a line, it always hits offset 0 of the first span of the line
|
||||
|
||||
// Thank you browsers for making this so 'easy' :)
|
||||
|
||||
let result: IHitTestResult;
|
||||
if (typeof document.caretRangeFromPoint === 'function') {
|
||||
result = this._doHitTestWithCaretRangeFromPoint(ctx, request);
|
||||
} else if ((<any>document).caretPositionFromPoint) {
|
||||
result = this._doHitTestWithCaretPositionFromPoint(ctx, request.pos.toClientCoordinates());
|
||||
} else if ((<any>document.body).createTextRange) {
|
||||
result = this._doHitTestWithMoveToPoint(ctx, request.pos.toClientCoordinates());
|
||||
} else {
|
||||
result = {
|
||||
position: null,
|
||||
|
||||
@@ -6,108 +6,14 @@
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IPointerHandlerHelper, MouseHandler, createMouseMoveEventMerger } from 'vs/editor/browser/controller/mouseHandler';
|
||||
import { IMouseTarget } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorMouseEvent, EditorPointerEventFactory } from 'vs/editor/browser/editorDom';
|
||||
import { ViewController } from 'vs/editor/browser/view/viewController';
|
||||
import { ViewContext } from 'vs/editor/common/view/viewContext';
|
||||
import { BrowserFeatures } from 'vs/base/browser/canIUse';
|
||||
|
||||
interface IThrottledGestureEvent {
|
||||
translationX: number;
|
||||
translationY: number;
|
||||
}
|
||||
|
||||
function gestureChangeEventMerger(lastEvent: IThrottledGestureEvent | null, currentEvent: MSGestureEvent): IThrottledGestureEvent {
|
||||
const r = {
|
||||
translationY: currentEvent.translationY,
|
||||
translationX: currentEvent.translationX
|
||||
};
|
||||
if (lastEvent) {
|
||||
r.translationY += lastEvent.translationY;
|
||||
r.translationX += lastEvent.translationX;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basically Edge but should be modified to handle any pointerEnabled, even without support of MSGesture
|
||||
*/
|
||||
class StandardPointerHandler extends MouseHandler implements IDisposable {
|
||||
|
||||
private _lastPointerType: string;
|
||||
private _installGestureHandlerTimeout: number;
|
||||
|
||||
constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {
|
||||
super(context, viewController, viewHelper);
|
||||
|
||||
this.viewHelper.linesContentDomNode.style.touchAction = 'none';
|
||||
|
||||
// TODO@Alex -> this expects that the view is added in 100 ms, might not be the case
|
||||
// This handler should be added when the dom node is in the dom tree
|
||||
this._installGestureHandlerTimeout = window.setTimeout(() => {
|
||||
this._installGestureHandlerTimeout = -1;
|
||||
|
||||
// TODO@Alex: replace the usage of MSGesture here with something that works across all browsers
|
||||
if (window.MSGesture) {
|
||||
const touchGesture = new MSGesture();
|
||||
const penGesture = new MSGesture();
|
||||
touchGesture.target = this.viewHelper.linesContentDomNode;
|
||||
penGesture.target = this.viewHelper.linesContentDomNode;
|
||||
this.viewHelper.linesContentDomNode.addEventListener('pointerdown', (e: PointerEvent) => {
|
||||
const pointerType = <any>e.pointerType;
|
||||
if (pointerType === 'mouse') {
|
||||
this._lastPointerType = 'mouse';
|
||||
return;
|
||||
} else if (pointerType === 'touch') {
|
||||
this._lastPointerType = 'touch';
|
||||
touchGesture.addPointer(e.pointerId);
|
||||
} else {
|
||||
this._lastPointerType = 'pen';
|
||||
penGesture.addPointer(e.pointerId);
|
||||
}
|
||||
});
|
||||
this._register(dom.addDisposableThrottledListener<IThrottledGestureEvent, MSGestureEvent>(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger));
|
||||
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'MSGestureTap', (e) => this._onCaptureGestureTap(e), true));
|
||||
}
|
||||
}, 100);
|
||||
this._lastPointerType = 'mouse';
|
||||
}
|
||||
|
||||
public _onMouseDown(e: EditorMouseEvent): void {
|
||||
if (this._lastPointerType === 'mouse') {
|
||||
super._onMouseDown(e);
|
||||
}
|
||||
}
|
||||
|
||||
private _onCaptureGestureTap(rawEvent: MSGestureEvent): void {
|
||||
const e = new EditorMouseEvent(<MouseEvent><any>rawEvent, this.viewHelper.viewDomNode);
|
||||
const t = this._createMouseTarget(e, false);
|
||||
if (t.position) {
|
||||
this.viewController.moveTo(t.position);
|
||||
}
|
||||
// IE does not want to focus when coming in from the browser's address bar
|
||||
if ((<any>e.browserEvent).fromElement) {
|
||||
e.preventDefault();
|
||||
this.viewHelper.focusTextArea();
|
||||
} else {
|
||||
// TODO@Alex -> cancel this is focus is lost
|
||||
setTimeout(() => {
|
||||
this.viewHelper.focusTextArea();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _onGestureChange(e: IThrottledGestureEvent): void {
|
||||
this._context.model.deltaScrollNow(-e.translationX, -e.translationY);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
window.clearTimeout(this._installGestureHandlerTimeout);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
import { TextAreaSyntethicEvents } from 'vs/editor/browser/controller/textAreaInput';
|
||||
|
||||
/**
|
||||
* Currently only tested on iOS 13/ iPadOS.
|
||||
@@ -210,6 +116,11 @@ class TouchHandler extends MouseHandler {
|
||||
const target = this._createMouseTarget(new EditorMouseEvent(event, this.viewHelper.viewDomNode), false);
|
||||
|
||||
if (target.position) {
|
||||
// Send the tap event also to the <textarea> (for input purposes)
|
||||
const event = document.createEvent('CustomEvent');
|
||||
event.initEvent(TextAreaSyntethicEvents.Tap, false, true);
|
||||
this.viewHelper.dispatchTextAreaEvent(event);
|
||||
|
||||
this.viewController.moveTo(target.position);
|
||||
}
|
||||
}
|
||||
@@ -228,8 +139,6 @@ export class PointerHandler extends Disposable {
|
||||
this.handler = this._register(new PointerEventHandler(context, viewController, viewHelper));
|
||||
} else if (window.TouchEvent) {
|
||||
this.handler = this._register(new TouchHandler(context, viewController, viewHelper));
|
||||
} else if (window.navigator.pointerEnabled || window.PointerEvent) {
|
||||
this.handler = this._register(new StandardPointerHandler(context, viewController, viewHelper));
|
||||
} else {
|
||||
this.handler = this._register(new MouseHandler(context, viewController, viewHelper));
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as platform from 'vs/base/common/platform';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { Configuration } from 'vs/editor/browser/config/configuration';
|
||||
import { CopyOptions, ICompositionData, IPasteData, ITextAreaInputHost, TextAreaInput, ClipboardDataToCopy } from 'vs/editor/browser/controller/textAreaInput';
|
||||
import { ISimpleModel, ITypeData, PagedScreenReaderStrategy, TextAreaState } from 'vs/editor/browser/controller/textAreaState';
|
||||
import { ISimpleModel, ITypeData, PagedScreenReaderStrategy, TextAreaState, _debugComposition } from 'vs/editor/browser/controller/textAreaState';
|
||||
import { ViewController } from 'vs/editor/browser/view/viewController';
|
||||
import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart';
|
||||
import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers';
|
||||
@@ -202,6 +202,22 @@ export class TextAreaHandler extends ViewPart {
|
||||
return TextAreaState.EMPTY;
|
||||
}
|
||||
|
||||
if (browser.isAndroid) {
|
||||
// when tapping in the editor on a word, Android enters composition mode.
|
||||
// in the `compositionstart` event we cannot clear the textarea, because
|
||||
// it then forgets to ever send a `compositionend`.
|
||||
// we therefore only write the current word in the textarea
|
||||
const selection = this._selections[0];
|
||||
if (selection.isEmpty()) {
|
||||
const position = selection.getStartPosition();
|
||||
const [wordAtPosition, positionOffsetInWord] = this._getAndroidWordAtPosition(position);
|
||||
if (wordAtPosition.length > 0) {
|
||||
return new TextAreaState(wordAtPosition, positionOffsetInWord, positionOffsetInWord, position, position);
|
||||
}
|
||||
}
|
||||
return TextAreaState.EMPTY;
|
||||
}
|
||||
|
||||
return PagedScreenReaderStrategy.fromEditorSelection(currentState, simpleModel, this._selections[0], this._accessibilityPageSize, this._accessibilitySupport === AccessibilitySupport.Unknown);
|
||||
},
|
||||
|
||||
@@ -237,9 +253,16 @@ export class TextAreaHandler extends ViewPart {
|
||||
}));
|
||||
|
||||
this._register(this._textAreaInput.onType((e: ITypeData) => {
|
||||
if (e.replaceCharCnt) {
|
||||
this._viewController.replacePreviousChar(e.text, e.replaceCharCnt);
|
||||
if (e.replacePrevCharCnt || e.replaceNextCharCnt || e.positionDelta) {
|
||||
// must be handled through the new command
|
||||
if (_debugComposition) {
|
||||
console.log(` => compositionType: <<${e.text}>>, ${e.replacePrevCharCnt}, ${e.replaceNextCharCnt}, ${e.positionDelta}`);
|
||||
}
|
||||
this._viewController.compositionType(e.text, e.replacePrevCharCnt, e.replaceNextCharCnt, e.positionDelta);
|
||||
} else {
|
||||
if (_debugComposition) {
|
||||
console.log(` => type: <<${e.text}>>`);
|
||||
}
|
||||
this._viewController.type(e.text);
|
||||
}
|
||||
}));
|
||||
@@ -250,7 +273,7 @@ export class TextAreaHandler extends ViewPart {
|
||||
|
||||
this._register(this._textAreaInput.onCompositionStart((e) => {
|
||||
const lineNumber = this._selections[0].startLineNumber;
|
||||
const column = this._selections[0].startColumn - (e.moveOneCharacterLeft ? 1 : 0);
|
||||
const column = this._selections[0].startColumn + e.revealDeltaColumns;
|
||||
|
||||
this._context.model.revealRange(
|
||||
'keyboard',
|
||||
@@ -280,8 +303,11 @@ export class TextAreaHandler extends ViewPart {
|
||||
}));
|
||||
|
||||
this._register(this._textAreaInput.onCompositionUpdate((e: ICompositionData) => {
|
||||
if (!this._visibleTextArea) {
|
||||
return;
|
||||
}
|
||||
// adjust width by its size
|
||||
this._visibleTextArea = this._visibleTextArea!.setWidth(measureText(e.data, this._fontInfo));
|
||||
this._visibleTextArea = this._visibleTextArea.setWidth(measureText(e.data, this._fontInfo));
|
||||
this._render();
|
||||
}));
|
||||
|
||||
@@ -308,6 +334,47 @@ export class TextAreaHandler extends ViewPart {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _getAndroidWordAtPosition(position: Position): [string, number] {
|
||||
const ANDROID_WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;:",.<>/?';
|
||||
const lineContent = this._context.model.getLineContent(position.lineNumber);
|
||||
const wordSeparators = getMapForWordSeparators(ANDROID_WORD_SEPARATORS);
|
||||
|
||||
let goingLeft = true;
|
||||
let startColumn = position.column;
|
||||
let goingRight = true;
|
||||
let endColumn = position.column;
|
||||
let distance = 0;
|
||||
while (distance < 50 && (goingLeft || goingRight)) {
|
||||
if (goingLeft && startColumn <= 1) {
|
||||
goingLeft = false;
|
||||
}
|
||||
if (goingLeft) {
|
||||
const charCode = lineContent.charCodeAt(startColumn - 2);
|
||||
const charClass = wordSeparators.get(charCode);
|
||||
if (charClass !== WordCharacterClass.Regular) {
|
||||
goingLeft = false;
|
||||
} else {
|
||||
startColumn--;
|
||||
}
|
||||
}
|
||||
if (goingRight && endColumn > lineContent.length) {
|
||||
goingRight = false;
|
||||
}
|
||||
if (goingRight) {
|
||||
const charCode = lineContent.charCodeAt(endColumn - 1);
|
||||
const charClass = wordSeparators.get(charCode);
|
||||
if (charClass !== WordCharacterClass.Regular) {
|
||||
goingRight = false;
|
||||
} else {
|
||||
endColumn++;
|
||||
}
|
||||
}
|
||||
distance++;
|
||||
}
|
||||
|
||||
return [lineContent.substring(startColumn - 1, endColumn - 1), position.column - startColumn];
|
||||
}
|
||||
|
||||
private _getWordBeforePosition(position: Position): string {
|
||||
const lineContent = this._context.model.getLineContent(position.lineNumber);
|
||||
const wordSeparators = getMapForWordSeparators(this._context.configuration.options.get(EditorOption.wordSeparators));
|
||||
|
||||
@@ -16,7 +16,10 @@ import * as strings from 'vs/base/common/strings';
|
||||
import { ITextAreaWrapper, ITypeData, TextAreaState, _debugComposition } from 'vs/editor/browser/controller/textAreaState';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { BrowserFeatures } from 'vs/base/browser/canIUse';
|
||||
|
||||
export namespace TextAreaSyntethicEvents {
|
||||
export const Tap = '-monaco-textarea-synthetic-tap';
|
||||
}
|
||||
|
||||
export interface ICompositionData {
|
||||
data: string;
|
||||
@@ -96,7 +99,7 @@ export class InMemoryClipboardMetadataManager {
|
||||
}
|
||||
|
||||
export interface ICompositionStartEvent {
|
||||
moveOneCharacterLeft: boolean;
|
||||
revealDeltaColumns: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,7 +207,6 @@ export class TextAreaInput extends Disposable {
|
||||
}
|
||||
this._isDoingComposition = true;
|
||||
|
||||
let moveOneCharacterLeft = false;
|
||||
if (
|
||||
platform.isMacintosh
|
||||
&& lastKeyDown
|
||||
@@ -212,17 +214,12 @@ export class TextAreaInput extends Disposable {
|
||||
&& this._textAreaState.selectionStart === this._textAreaState.selectionEnd
|
||||
&& this._textAreaState.selectionStart > 0
|
||||
&& this._textAreaState.value.substr(this._textAreaState.selectionStart - 1, 1) === e.data
|
||||
&& (lastKeyDown.code === 'ArrowRight' || lastKeyDown.code === 'ArrowLeft')
|
||||
) {
|
||||
// Handling long press case on macOS + arrow key => pretend the character was selected
|
||||
if (lastKeyDown.code === 'ArrowRight' || lastKeyDown.code === 'ArrowLeft') {
|
||||
if (_debugComposition) {
|
||||
console.log(`[compositionstart] Handling long press case on macOS + arrow key`, e);
|
||||
}
|
||||
moveOneCharacterLeft = true;
|
||||
if (_debugComposition) {
|
||||
console.log(`[compositionstart] Handling long press case on macOS + arrow key`, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (moveOneCharacterLeft) {
|
||||
this._textAreaState = new TextAreaState(
|
||||
this._textAreaState.value,
|
||||
this._textAreaState.selectionStart - 1,
|
||||
@@ -230,11 +227,19 @@ export class TextAreaInput extends Disposable {
|
||||
this._textAreaState.selectionStartPosition ? new Position(this._textAreaState.selectionStartPosition.lineNumber, this._textAreaState.selectionStartPosition.column - 1) : null,
|
||||
this._textAreaState.selectionEndPosition
|
||||
);
|
||||
} else {
|
||||
this._setAndWriteTextAreaState('compositionstart', TextAreaState.EMPTY);
|
||||
this._onCompositionStart.fire({ revealDeltaColumns: -1 });
|
||||
return;
|
||||
}
|
||||
|
||||
this._onCompositionStart.fire({ moveOneCharacterLeft });
|
||||
if (browser.isAndroid) {
|
||||
// when tapping on the editor, Android enters composition mode to edit the current word
|
||||
// so we cannot clear the textarea on Android and we must pretend the current word was selected
|
||||
this._onCompositionStart.fire({ revealDeltaColumns: -this._textAreaState.selectionStart });
|
||||
return;
|
||||
}
|
||||
|
||||
this._setAndWriteTextAreaState('compositionstart', TextAreaState.EMPTY);
|
||||
this._onCompositionStart.fire({ revealDeltaColumns: 0 });
|
||||
}));
|
||||
|
||||
/**
|
||||
@@ -246,6 +251,12 @@ export class TextAreaInput extends Disposable {
|
||||
return [newState, TextAreaState.deduceInput(oldState, newState, couldBeEmojiInput)];
|
||||
};
|
||||
|
||||
const deduceAndroidCompositionInput = (): [TextAreaState, ITypeData] => {
|
||||
const oldState = this._textAreaState;
|
||||
const newState = TextAreaState.readFromTextArea(this._textArea);
|
||||
return [newState, TextAreaState.deduceAndroidCompositionInput(oldState, newState)];
|
||||
};
|
||||
|
||||
/**
|
||||
* Deduce the composition input from a string.
|
||||
*/
|
||||
@@ -254,7 +265,9 @@ export class TextAreaInput extends Disposable {
|
||||
const newState = TextAreaState.selectedText(text);
|
||||
const typeInput: ITypeData = {
|
||||
text: newState.value,
|
||||
replaceCharCnt: oldState.selectionEnd - oldState.selectionStart
|
||||
replacePrevCharCnt: oldState.selectionEnd - oldState.selectionStart,
|
||||
replaceNextCharCnt: 0,
|
||||
positionDelta: 0
|
||||
};
|
||||
return [newState, typeInput];
|
||||
};
|
||||
@@ -263,6 +276,17 @@ export class TextAreaInput extends Disposable {
|
||||
if (_debugComposition) {
|
||||
console.log(`[compositionupdate]`, e);
|
||||
}
|
||||
if (browser.isAndroid) {
|
||||
// On Android, the data sent with the composition update event is unusable.
|
||||
// For example, if the cursor is in the middle of a word like Mic|osoft
|
||||
// and Microsoft is chosen from the keyboard's suggestions, the e.data will contain "Microsoft".
|
||||
// This is not really usable because it doesn't tell us where the edit began and where it ended.
|
||||
const [newState, typeInput] = deduceAndroidCompositionInput();
|
||||
this._textAreaState = newState;
|
||||
this._onType.fire(typeInput);
|
||||
this._onCompositionUpdate.fire(e);
|
||||
return;
|
||||
}
|
||||
const [newState, typeInput] = deduceComposition(e.data || '');
|
||||
this._textAreaState = newState;
|
||||
this._onType.fire(typeInput);
|
||||
@@ -278,6 +302,19 @@ export class TextAreaInput extends Disposable {
|
||||
if (!this._isDoingComposition) {
|
||||
return;
|
||||
}
|
||||
this._isDoingComposition = false;
|
||||
|
||||
if (browser.isAndroid) {
|
||||
// On Android, the data sent with the composition update event is unusable.
|
||||
// For example, if the cursor is in the middle of a word like Mic|osoft
|
||||
// and Microsoft is chosen from the keyboard's suggestions, the e.data will contain "Microsoft".
|
||||
// This is not really usable because it doesn't tell us where the edit began and where it ended.
|
||||
const [newState, typeInput] = deduceAndroidCompositionInput();
|
||||
this._textAreaState = newState;
|
||||
this._onType.fire(typeInput);
|
||||
this._onCompositionEnd.fire();
|
||||
return;
|
||||
}
|
||||
|
||||
const [newState, typeInput] = deduceComposition(e.data || '');
|
||||
this._textAreaState = newState;
|
||||
@@ -290,11 +327,6 @@ export class TextAreaInput extends Disposable {
|
||||
this._textAreaState = TextAreaState.readFromTextArea(this._textArea);
|
||||
}
|
||||
|
||||
if (!this._isDoingComposition) {
|
||||
return;
|
||||
}
|
||||
this._isDoingComposition = false;
|
||||
|
||||
this._onCompositionEnd.fire();
|
||||
}));
|
||||
|
||||
@@ -308,18 +340,18 @@ export class TextAreaInput extends Disposable {
|
||||
}
|
||||
|
||||
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/platform.isMacintosh);
|
||||
if (typeInput.replaceCharCnt === 0 && typeInput.text.length === 1 && strings.isHighSurrogate(typeInput.text.charCodeAt(0))) {
|
||||
if (typeInput.replacePrevCharCnt === 0 && typeInput.text.length === 1 && strings.isHighSurrogate(typeInput.text.charCodeAt(0))) {
|
||||
// Ignore invalid input but keep it around for next time
|
||||
return;
|
||||
}
|
||||
|
||||
this._textAreaState = newState;
|
||||
if (this._nextCommand === ReadFromTextArea.Type) {
|
||||
if (typeInput.text !== '') {
|
||||
if (typeInput.text !== '' || typeInput.replacePrevCharCnt !== 0) {
|
||||
this._onType.fire(typeInput);
|
||||
}
|
||||
} else {
|
||||
if (typeInput.text !== '' || typeInput.replaceCharCnt !== 0) {
|
||||
if (typeInput.text !== '' || typeInput.replacePrevCharCnt !== 0) {
|
||||
this._firePaste(typeInput.text, null);
|
||||
}
|
||||
this._nextCommand = ReadFromTextArea.Type;
|
||||
@@ -388,6 +420,21 @@ export class TextAreaInput extends Disposable {
|
||||
}
|
||||
this._setHasFocus(false);
|
||||
}));
|
||||
this._register(dom.addDisposableListener(textArea.domNode, TextAreaSyntethicEvents.Tap, () => {
|
||||
if (browser.isAndroid && this._isDoingComposition) {
|
||||
// on Android, tapping does not cancel the current composition, so the
|
||||
// textarea is stuck showing the old composition
|
||||
|
||||
// Clear the flag to be able to write to the textarea
|
||||
this._isDoingComposition = false;
|
||||
|
||||
// Clear the textarea to avoid an unwanted cursor type
|
||||
this.writeScreenReaderContent('tapWithoutCompositionEnd');
|
||||
|
||||
// Fire artificial composition end
|
||||
this._onCompositionEnd.fire();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private _installSelectionChangeListener(): IDisposable {
|
||||
@@ -550,7 +597,7 @@ export class TextAreaInput extends Disposable {
|
||||
}
|
||||
|
||||
private _ensureClipboardGetsEditorSelection(e: ClipboardEvent): void {
|
||||
const dataToCopy = this._host.getDataToCopy(ClipboardEventUtils.canUseTextData(e) && BrowserFeatures.clipboard.richText);
|
||||
const dataToCopy = this._host.getDataToCopy(ClipboardEventUtils.canUseTextData(e));
|
||||
const storedMetadata: ClipboardStoredMetadata = {
|
||||
version: 1,
|
||||
isFromEmptySelection: dataToCopy.isFromEmptySelection,
|
||||
@@ -689,11 +736,11 @@ class TextAreaWrapper extends Disposable implements ITextAreaWrapper {
|
||||
}
|
||||
|
||||
public getSelectionStart(): number {
|
||||
return this._actual.domNode.selectionStart;
|
||||
return this._actual.domNode.selectionDirection === 'backward' ? this._actual.domNode.selectionEnd : this._actual.domNode.selectionStart;
|
||||
}
|
||||
|
||||
public getSelectionEnd(): number {
|
||||
return this._actual.domNode.selectionEnd;
|
||||
return this._actual.domNode.selectionDirection === 'backward' ? this._actual.domNode.selectionStart : this._actual.domNode.selectionEnd;
|
||||
}
|
||||
|
||||
public setSelectionRange(reason: string, selectionStart: number, selectionEnd: number): void {
|
||||
|
||||
@@ -27,7 +27,9 @@ export interface ISimpleModel {
|
||||
|
||||
export interface ITypeData {
|
||||
text: string;
|
||||
replaceCharCnt: number;
|
||||
replacePrevCharCnt: number;
|
||||
replaceNextCharCnt: number;
|
||||
positionDelta: number;
|
||||
}
|
||||
|
||||
export class TextAreaState {
|
||||
@@ -105,7 +107,9 @@ export class TextAreaState {
|
||||
// This is the EMPTY state
|
||||
return {
|
||||
text: '',
|
||||
replaceCharCnt: 0
|
||||
replacePrevCharCnt: 0,
|
||||
replaceNextCharCnt: 0,
|
||||
positionDelta: 0
|
||||
};
|
||||
}
|
||||
|
||||
@@ -178,7 +182,9 @@ export class TextAreaState {
|
||||
if (/\uFE0F/.test(potentialEmojiInput) || strings.containsEmoji(potentialEmojiInput)) {
|
||||
return {
|
||||
text: potentialEmojiInput,
|
||||
replaceCharCnt: 0
|
||||
replacePrevCharCnt: 0,
|
||||
replaceNextCharCnt: 0,
|
||||
positionDelta: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -197,7 +203,9 @@ export class TextAreaState {
|
||||
if (strings.containsFullWidthCharacter(currentValue)) {
|
||||
return {
|
||||
text: '',
|
||||
replaceCharCnt: 0
|
||||
replacePrevCharCnt: 0,
|
||||
replaceNextCharCnt: 0,
|
||||
positionDelta: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -210,7 +218,9 @@ export class TextAreaState {
|
||||
|
||||
return {
|
||||
text: currentValue,
|
||||
replaceCharCnt: replacePreviousCharacters
|
||||
replacePrevCharCnt: replacePreviousCharacters,
|
||||
replaceNextCharCnt: 0,
|
||||
positionDelta: 0
|
||||
};
|
||||
}
|
||||
|
||||
@@ -218,7 +228,57 @@ export class TextAreaState {
|
||||
const replacePreviousCharacters = previousSelectionEnd - previousSelectionStart;
|
||||
return {
|
||||
text: currentValue,
|
||||
replaceCharCnt: replacePreviousCharacters
|
||||
replacePrevCharCnt: replacePreviousCharacters,
|
||||
replaceNextCharCnt: 0,
|
||||
positionDelta: 0
|
||||
};
|
||||
}
|
||||
|
||||
public static deduceAndroidCompositionInput(previousState: TextAreaState, currentState: TextAreaState): ITypeData {
|
||||
if (!previousState) {
|
||||
// This is the EMPTY state
|
||||
return {
|
||||
text: '',
|
||||
replacePrevCharCnt: 0,
|
||||
replaceNextCharCnt: 0,
|
||||
positionDelta: 0
|
||||
};
|
||||
}
|
||||
|
||||
if (_debugComposition) {
|
||||
console.log('------------------------deduceAndroidCompositionInput');
|
||||
console.log('PREVIOUS STATE: ' + previousState.toString());
|
||||
console.log('CURRENT STATE: ' + currentState.toString());
|
||||
}
|
||||
|
||||
if (previousState.value === currentState.value) {
|
||||
return {
|
||||
text: '',
|
||||
replacePrevCharCnt: 0,
|
||||
replaceNextCharCnt: 0,
|
||||
positionDelta: currentState.selectionEnd - previousState.selectionEnd
|
||||
};
|
||||
}
|
||||
|
||||
const prefixLength = Math.min(strings.commonPrefixLength(previousState.value, currentState.value), previousState.selectionEnd);
|
||||
const suffixLength = Math.min(strings.commonSuffixLength(previousState.value, currentState.value), previousState.value.length - previousState.selectionEnd);
|
||||
const previousValue = previousState.value.substring(prefixLength, previousState.value.length - suffixLength);
|
||||
const currentValue = currentState.value.substring(prefixLength, currentState.value.length - suffixLength);
|
||||
const previousSelectionStart = previousState.selectionStart - prefixLength;
|
||||
const previousSelectionEnd = previousState.selectionEnd - prefixLength;
|
||||
const currentSelectionStart = currentState.selectionStart - prefixLength;
|
||||
const currentSelectionEnd = currentState.selectionEnd - prefixLength;
|
||||
|
||||
if (_debugComposition) {
|
||||
console.log('AFTER DIFFING PREVIOUS STATE: <' + previousValue + '>, selectionStart: ' + previousSelectionStart + ', selectionEnd: ' + previousSelectionEnd);
|
||||
console.log('AFTER DIFFING CURRENT STATE: <' + currentValue + '>, selectionStart: ' + currentSelectionStart + ', selectionEnd: ' + currentSelectionEnd);
|
||||
}
|
||||
|
||||
return {
|
||||
text: currentValue,
|
||||
replacePrevCharCnt: previousSelectionEnd,
|
||||
replaceNextCharCnt: previousValue.length - previousSelectionEnd,
|
||||
positionDelta: currentSelectionEnd - currentValue.length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cance
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
|
||||
const IEditorCancellationTokens = createDecorator<IEditorCancellationTokens>('IEditorCancelService');
|
||||
@@ -22,7 +23,7 @@ interface IEditorCancellationTokens {
|
||||
cancel(editor: ICodeEditor): void;
|
||||
}
|
||||
|
||||
const ctxCancellableOperation = new RawContextKey('cancellableOperation', false);
|
||||
const ctxCancellableOperation = new RawContextKey('cancellableOperation', false, localize('cancellableOperation', 'Whether the editor runs a cancellable operation, e.g. like \'Peek References\''));
|
||||
|
||||
registerSingleton(IEditorCancellationTokens, class implements IEditorCancellationTokens {
|
||||
|
||||
|
||||
@@ -210,4 +210,8 @@ export class GlobalEditorMouseMoveMonitor extends Disposable {
|
||||
onStopCallback(e);
|
||||
});
|
||||
}
|
||||
|
||||
public stopMonitoring(): void {
|
||||
this._globalMouseMoveMonitor.stopMonitoring(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
|
||||
export class MarkerDecorationsContribution implements IEditorContribution {
|
||||
|
||||
public static readonly ID: string = 'editor.contrib.markerDecorations';
|
||||
|
||||
constructor(
|
||||
_editor: ICodeEditor,
|
||||
@IMarkerDecorationsService _markerDecorationsService: IMarkerDecorationsService
|
||||
) {
|
||||
// Doesn't do anything, just requires `IMarkerDecorationsService` to make sure it gets instantiated
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(MarkerDecorationsContribution.ID, MarkerDecorationsContribution);
|
||||
@@ -105,7 +105,7 @@ export class OpenerService implements IOpenerService {
|
||||
|
||||
constructor(
|
||||
@ICodeEditorService editorService: ICodeEditorService,
|
||||
@ICommandService commandService: ICommandService,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
// Default external opener is going through window.open()
|
||||
this._defaultExternalOpener = {
|
||||
@@ -166,7 +166,7 @@ export class OpenerService implements IOpenerService {
|
||||
// check with contributed validators
|
||||
const targetURI = typeof target === 'string' ? URI.parse(target) : target;
|
||||
// validate against the original URI that this URI resolves to, if one exists
|
||||
const validationTarget = this._resolvedUriTargets.get(targetURI) ?? target;
|
||||
const validationTarget = this._resolvedUriTargets.get(targetURI) ?? targetURI;
|
||||
for (const validator of this._validators) {
|
||||
if (!(await validator.shouldOpen(validationTarget))) {
|
||||
return false;
|
||||
@@ -188,7 +188,9 @@ export class OpenerService implements IOpenerService {
|
||||
for (const resolver of this._resolvers) {
|
||||
const result = await resolver.resolveExternalUri(resource, options);
|
||||
if (result) {
|
||||
this._resolvedUriTargets.set(result.resolved, resource);
|
||||
if (!this._resolvedUriTargets.has(result.resolved)) {
|
||||
this._resolvedUriTargets.set(result.resolved, resource);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,11 +232,11 @@ function renderLine(lineContent: string, initialVisibleColumn: number, tabSize:
|
||||
if (strings.isFullWidthCharacter(charCode)) {
|
||||
charWidth++;
|
||||
}
|
||||
// if (renderControlCharacters && charCode < 32) {
|
||||
// sb.write1(9216 + charCode);
|
||||
// } else {
|
||||
sb.write1(charCode);
|
||||
// }
|
||||
if (charCode < 32) {
|
||||
sb.write1(9216 + charCode);
|
||||
} else {
|
||||
sb.write1(charCode);
|
||||
}
|
||||
}
|
||||
|
||||
charOffset += producedCharacters;
|
||||
|
||||
@@ -37,7 +37,7 @@ export interface IMouseDispatchData {
|
||||
export interface ICommandDelegate {
|
||||
paste(text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void;
|
||||
type(text: string): void;
|
||||
replacePreviousChar(text: string, replaceCharCnt: number): void;
|
||||
compositionType(text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): void;
|
||||
startComposition(): void;
|
||||
endComposition(): void;
|
||||
cut(): void;
|
||||
@@ -70,8 +70,8 @@ export class ViewController {
|
||||
this.commandDelegate.type(text);
|
||||
}
|
||||
|
||||
public replacePreviousChar(text: string, replaceCharCnt: number): void {
|
||||
this.commandDelegate.replacePreviousChar(text, replaceCharCnt);
|
||||
public compositionType(text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): void {
|
||||
this.commandDelegate.compositionType(text, replacePrevCharCnt, replaceNextCharCnt, positionDelta);
|
||||
}
|
||||
|
||||
public compositionStart(): void {
|
||||
|
||||
@@ -239,6 +239,10 @@ export class View extends ViewEventHandler {
|
||||
this.focus();
|
||||
},
|
||||
|
||||
dispatchTextAreaEvent: (event: CustomEvent) => {
|
||||
this._textAreaHandler.textArea.domNode.dispatchEvent(event);
|
||||
},
|
||||
|
||||
getLastRenderData: (): PointerHandlerLastRenderData => {
|
||||
const lastViewCursorsRenderData = this._viewCursors.getLastRenderData() || [];
|
||||
const lastTextareaPosition = this._textAreaHandler.getLastRenderData();
|
||||
|
||||
@@ -28,6 +28,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay {
|
||||
private _lineNumbersWidth!: number;
|
||||
private _lastCursorModelPosition: Position;
|
||||
private _renderResult: string[] | null;
|
||||
private _activeLineNumber: number;
|
||||
|
||||
constructor(context: ViewContext) {
|
||||
super();
|
||||
@@ -37,6 +38,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay {
|
||||
|
||||
this._lastCursorModelPosition = new Position(1, 1);
|
||||
this._renderResult = null;
|
||||
this._activeLineNumber = 1;
|
||||
this._context.addEventHandler(this);
|
||||
}
|
||||
|
||||
@@ -68,10 +70,15 @@ export class LineNumbersOverlay extends DynamicViewOverlay {
|
||||
const primaryViewPosition = e.selections[0].getPosition();
|
||||
this._lastCursorModelPosition = this._context.model.coordinatesConverter.convertViewPositionToModelPosition(primaryViewPosition);
|
||||
|
||||
if (this._renderLineNumbers === RenderLineNumbersType.Relative || this._renderLineNumbers === RenderLineNumbersType.Interval) {
|
||||
return true;
|
||||
let shouldRender = false;
|
||||
if (this._activeLineNumber !== primaryViewPosition.lineNumber) {
|
||||
this._activeLineNumber = primaryViewPosition.lineNumber;
|
||||
shouldRender = true;
|
||||
}
|
||||
return false;
|
||||
if (this._renderLineNumbers === RenderLineNumbersType.Relative || this._renderLineNumbers === RenderLineNumbersType.Interval) {
|
||||
shouldRender = true;
|
||||
}
|
||||
return shouldRender;
|
||||
}
|
||||
public onFlushed(e: viewEvents.ViewFlushedEvent): boolean {
|
||||
return true;
|
||||
@@ -135,7 +142,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay {
|
||||
const lineHeightClassName = (platform.isLinux ? (this._lineHeight % 2 === 0 ? ' lh-even' : ' lh-odd') : '');
|
||||
const visibleStartLineNumber = ctx.visibleRange.startLineNumber;
|
||||
const visibleEndLineNumber = ctx.visibleRange.endLineNumber;
|
||||
const common = '<div class="' + LineNumbersOverlay.CLASS_NAME + lineHeightClassName + '" style="left:' + this._lineNumbersLeft.toString() + 'px;width:' + this._lineNumbersWidth.toString() + 'px;">';
|
||||
const common = '<div class="' + LineNumbersOverlay.CLASS_NAME + lineHeightClassName + '" style="left:' + this._lineNumbersLeft + 'px;width:' + this._lineNumbersWidth + 'px;">';
|
||||
|
||||
const lineCount = this._context.model.getLineCount();
|
||||
const output: string[] = [];
|
||||
@@ -153,11 +160,19 @@ export class LineNumbersOverlay extends DynamicViewOverlay {
|
||||
const renderLineNumber = this._getLineRenderLineNumber(lineNumber);
|
||||
|
||||
if (renderLineNumber) {
|
||||
output[lineIndex] = (
|
||||
common
|
||||
+ renderLineNumber
|
||||
+ '</div>'
|
||||
);
|
||||
if (lineNumber === this._activeLineNumber) {
|
||||
output[lineIndex] = (
|
||||
'<div class="active-line-number ' + LineNumbersOverlay.CLASS_NAME + lineHeightClassName + '" style="left:' + this._lineNumbersLeft + 'px;width:' + this._lineNumbersWidth + 'px;">'
|
||||
+ renderLineNumber
|
||||
+ '</div>'
|
||||
);
|
||||
} else {
|
||||
output[lineIndex] = (
|
||||
common
|
||||
+ renderLineNumber
|
||||
+ '</div>'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
output[lineIndex] = '';
|
||||
}
|
||||
@@ -187,6 +202,6 @@ registerThemingParticipant((theme, collector) => {
|
||||
}
|
||||
const activeLineNumber = theme.getColor(editorActiveLineNumber);
|
||||
if (activeLineNumber) {
|
||||
collector.addRule(`.monaco-editor .current-line ~ .line-numbers { color: ${activeLineNumber}; }`);
|
||||
collector.addRule(`.monaco-editor .line-numbers.active-line-number { color: ${activeLineNumber}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -44,8 +44,6 @@ const canUseFastRenderedViewLine = (function () {
|
||||
|
||||
let monospaceAssumptionsAreValid = true;
|
||||
|
||||
const alwaysRenderInlineSelection = (browser.isEdgeLegacy);
|
||||
|
||||
export class DomReadingContext {
|
||||
|
||||
private readonly _domNode: HTMLElement;
|
||||
@@ -163,7 +161,7 @@ export class ViewLine implements IVisibleLine {
|
||||
this._options = newOptions;
|
||||
}
|
||||
public onSelectionChanged(): boolean {
|
||||
if (alwaysRenderInlineSelection || this._options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace === 'selection') {
|
||||
if (this._options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace === 'selection') {
|
||||
this._isMaybeInvalid = true;
|
||||
return true;
|
||||
}
|
||||
@@ -184,7 +182,7 @@ export class ViewLine implements IVisibleLine {
|
||||
|
||||
// Only send selection information when needed for rendering whitespace
|
||||
let selectionsOnLine: LineRange[] | null = null;
|
||||
if (alwaysRenderInlineSelection || options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace === 'selection') {
|
||||
if (options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace === 'selection') {
|
||||
const selections = viewportData.selections;
|
||||
for (const selection of selections) {
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./selections';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { HorizontalRange, LineVisibleRanges, RenderingContext } from 'vs/editor/common/view/renderingContext';
|
||||
@@ -57,12 +56,6 @@ function toStyled(item: LineVisibleRanges): LineVisibleRangesWithStyle {
|
||||
return new LineVisibleRangesWithStyle(item.lineNumber, item.ranges.map(toStyledRange));
|
||||
}
|
||||
|
||||
// TODO@Alex: Remove this once IE11 fixes Bug #524217
|
||||
// The problem in IE11 is that it does some sort of auto-zooming to accomodate for displays with different pixel density.
|
||||
// Unfortunately, this auto-zooming is buggy around dealing with rounded borders
|
||||
const isIEWithZoomingIssuesNearRoundedBorders = browser.isEdgeLegacy;
|
||||
|
||||
|
||||
export class SelectionsOverlay extends DynamicViewOverlay {
|
||||
|
||||
private static readonly SELECTION_CLASS_NAME = 'selected-text';
|
||||
@@ -254,7 +247,7 @@ export class SelectionsOverlay extends DynamicViewOverlay {
|
||||
const linesVisibleRanges = _linesVisibleRanges.map(toStyled);
|
||||
const visibleRangesHaveGaps = this._visibleRangesHaveGaps(linesVisibleRanges);
|
||||
|
||||
if (!isIEWithZoomingIssuesNearRoundedBorders && !visibleRangesHaveGaps && this._roundedSelection) {
|
||||
if (!visibleRangesHaveGaps && this._roundedSelection) {
|
||||
this._enrichVisibleRangesWithStyle(ctx.visibleRange, linesVisibleRanges, previousFrame);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/editor/browser/services/markerDecorations';
|
||||
|
||||
import 'vs/css!./media/editor';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
@@ -1002,7 +1004,12 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
}
|
||||
case editorCommon.Handler.ReplacePreviousChar: {
|
||||
const args = <Partial<editorCommon.ReplacePreviousCharPayload>>payload;
|
||||
this._replacePreviousChar(source, args.text || '', args.replaceCharCnt || 0);
|
||||
this._compositionType(source, args.text || '', args.replaceCharCnt || 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
case editorCommon.Handler.CompositionType: {
|
||||
const args = <Partial<editorCommon.CompositionTypePayload>>payload;
|
||||
this._compositionType(source, args.text || '', args.replacePrevCharCnt || 0, args.replaceNextCharCnt || 0, args.positionDelta || 0);
|
||||
return;
|
||||
}
|
||||
case editorCommon.Handler.Paste: {
|
||||
@@ -1061,11 +1068,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
}
|
||||
}
|
||||
|
||||
private _replacePreviousChar(source: string | null | undefined, text: string, replaceCharCnt: number): void {
|
||||
private _compositionType(source: string | null | undefined, text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): void {
|
||||
if (!this._modelData) {
|
||||
return;
|
||||
}
|
||||
this._modelData.viewModel.replacePreviousChar(text, replaceCharCnt, source);
|
||||
this._modelData.viewModel.compositionType(text, replacePrevCharCnt, replaceNextCharCnt, positionDelta, source);
|
||||
}
|
||||
|
||||
private _paste(source: string | null | undefined, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void {
|
||||
@@ -1583,8 +1590,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
type: (text: string) => {
|
||||
this._type('keyboard', text);
|
||||
},
|
||||
replacePreviousChar: (text: string, replaceCharCnt: number) => {
|
||||
this._replacePreviousChar('keyboard', text, replaceCharCnt);
|
||||
compositionType: (text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number) => {
|
||||
this._compositionType('keyboard', text, replacePrevCharCnt, replaceNextCharCnt, positionDelta);
|
||||
},
|
||||
startComposition: () => {
|
||||
this._startComposition();
|
||||
@@ -1606,9 +1613,16 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
const payload: editorCommon.TypePayload = { text };
|
||||
this._commandService.executeCommand(editorCommon.Handler.Type, payload);
|
||||
},
|
||||
replacePreviousChar: (text: string, replaceCharCnt: number) => {
|
||||
const payload: editorCommon.ReplacePreviousCharPayload = { text, replaceCharCnt };
|
||||
this._commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, payload);
|
||||
compositionType: (text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number) => {
|
||||
// Try if possible to go through the existing `replacePreviousChar` command
|
||||
if (replaceNextCharCnt || positionDelta) {
|
||||
// must be handled through the new command
|
||||
const payload: editorCommon.CompositionTypePayload = { text, replacePrevCharCnt, replaceNextCharCnt, positionDelta };
|
||||
this._commandService.executeCommand(editorCommon.Handler.CompositionType, payload);
|
||||
} else {
|
||||
const payload: editorCommon.ReplacePreviousCharPayload = { text, replaceCharCnt: replacePrevCharCnt };
|
||||
this._commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, payload);
|
||||
}
|
||||
},
|
||||
startComposition: () => {
|
||||
this._commandService.executeCommand(editorCommon.Handler.CompositionStart, {});
|
||||
|
||||
@@ -222,7 +222,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
private readonly _updateDecorationsRunner: RunOnceScheduler;
|
||||
|
||||
private readonly _editorWorkerService: IEditorWorkerService;
|
||||
protected _contextKeyService: IContextKeyService;
|
||||
private readonly _contextKeyService: IContextKeyService;
|
||||
private readonly _instantiationService: IInstantiationService;
|
||||
private readonly _codeEditorService: ICodeEditorService;
|
||||
private readonly _themeService: IThemeService;
|
||||
private readonly _notificationService: INotificationService;
|
||||
@@ -248,6 +249,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
this._editorWorkerService = editorWorkerService;
|
||||
this._codeEditorService = codeEditorService;
|
||||
this._contextKeyService = this._register(contextKeyService.createScoped(domElement));
|
||||
this._instantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, this._contextKeyService]));
|
||||
this._contextKeyService.createKey('isInDiffEditor', true);
|
||||
this._themeService = themeService;
|
||||
this._notificationService = notificationService;
|
||||
@@ -354,20 +356,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
|
||||
this._diffComputationResult = null;
|
||||
|
||||
const leftContextKeyService = this._contextKeyService.createScoped();
|
||||
|
||||
const leftServices = new ServiceCollection();
|
||||
leftServices.set(IContextKeyService, leftContextKeyService);
|
||||
const leftScopedInstantiationService = instantiationService.createChild(leftServices);
|
||||
|
||||
const rightContextKeyService = this._contextKeyService.createScoped();
|
||||
|
||||
const rightServices = new ServiceCollection();
|
||||
rightServices.set(IContextKeyService, rightContextKeyService);
|
||||
const rightScopedInstantiationService = instantiationService.createChild(rightServices);
|
||||
|
||||
this._originalEditor = this._createLeftHandSideEditor(options, codeEditorWidgetOptions.originalEditor || {}, leftScopedInstantiationService, leftContextKeyService);
|
||||
this._modifiedEditor = this._createRightHandSideEditor(options, codeEditorWidgetOptions.modifiedEditor || {}, rightScopedInstantiationService, rightContextKeyService);
|
||||
this._originalEditor = this._createLeftHandSideEditor(options, codeEditorWidgetOptions.originalEditor || {});
|
||||
this._modifiedEditor = this._createRightHandSideEditor(options, codeEditorWidgetOptions.modifiedEditor || {});
|
||||
|
||||
this._originalOverviewRuler = null;
|
||||
this._modifiedOverviewRuler = null;
|
||||
@@ -495,8 +485,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
this._layoutOverviewRulers();
|
||||
}
|
||||
|
||||
private _createLeftHandSideEditor(options: Readonly<editorBrowser.IDiffEditorConstructionOptions>, codeEditorWidgetOptions: ICodeEditorWidgetOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget {
|
||||
const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options), codeEditorWidgetOptions);
|
||||
private _createLeftHandSideEditor(options: Readonly<editorBrowser.IDiffEditorConstructionOptions>, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget {
|
||||
const editor = this._createInnerEditor(this._instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options), codeEditorWidgetOptions);
|
||||
|
||||
this._register(editor.onDidScrollChange((e) => {
|
||||
if (this._isHandlingScrollEvent) {
|
||||
@@ -538,7 +528,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
}
|
||||
}));
|
||||
|
||||
const isInDiffLeftEditorKey = contextKeyService.createKey<boolean>('isInDiffLeftEditor', undefined);
|
||||
const isInDiffLeftEditorKey = this._contextKeyService.createKey<boolean>('isInDiffLeftEditor', editor.hasWidgetFocus());
|
||||
this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true)));
|
||||
this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false)));
|
||||
|
||||
@@ -557,8 +547,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
return editor;
|
||||
}
|
||||
|
||||
private _createRightHandSideEditor(options: Readonly<editorBrowser.IDiffEditorConstructionOptions>, codeEditorWidgetOptions: ICodeEditorWidgetOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget {
|
||||
const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options), codeEditorWidgetOptions);
|
||||
private _createRightHandSideEditor(options: Readonly<editorBrowser.IDiffEditorConstructionOptions>, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget {
|
||||
const editor = this._createInnerEditor(this._instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options), codeEditorWidgetOptions);
|
||||
|
||||
this._register(editor.onDidScrollChange((e) => {
|
||||
if (this._isHandlingScrollEvent) {
|
||||
@@ -606,7 +596,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
}
|
||||
}));
|
||||
|
||||
const isInDiffRightEditorKey = contextKeyService.createKey<boolean>('isInDiffRightEditor', undefined);
|
||||
const isInDiffRightEditorKey = this._contextKeyService.createKey<boolean>('isInDiffRightEditor', editor.hasWidgetFocus());
|
||||
this._register(editor.onDidFocusEditorWidget(() => isInDiffRightEditorKey.set(true)));
|
||||
this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false)));
|
||||
|
||||
@@ -1161,6 +1151,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
} else {
|
||||
result.wordWrapOverride1 = this._diffWordWrap;
|
||||
}
|
||||
if (options.originalAriaLabel) {
|
||||
result.ariaLabel = options.originalAriaLabel;
|
||||
}
|
||||
result.readOnly = !this._originalIsEditable;
|
||||
result.extraEditorClassName = 'original-in-monaco-diff-editor';
|
||||
return {
|
||||
@@ -1174,6 +1167,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
|
||||
private _adjustOptionsForRightHandSide(options: Readonly<editorBrowser.IDiffEditorConstructionOptions>): editorBrowser.IEditorConstructionOptions {
|
||||
const result = this._adjustOptionsForSubEditor(options);
|
||||
if (options.modifiedAriaLabel) {
|
||||
result.ariaLabel = options.modifiedAriaLabel;
|
||||
}
|
||||
|
||||
result.wordWrapOverride1 = this._diffWordWrap;
|
||||
result.revealHorizontalRightPadding = EditorOptions.revealHorizontalRightPadding.defaultValue + DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
|
||||
result.scrollbar!.verticalHasArrows = false;
|
||||
|
||||
@@ -509,7 +509,7 @@ const editorConfiguration: IConfigurationNode = {
|
||||
nls.localize('wordBasedSuggestionsMode.matchingDocuments', 'Suggest words from all open documents of the same language.'),
|
||||
nls.localize('wordBasedSuggestionsMode.allDocuments', 'Suggest words from all open documents.')
|
||||
],
|
||||
description: nls.localize('wordBasedSuggestionsMode', "Controls form what documents word based completions are computed.")
|
||||
description: nls.localize('wordBasedSuggestionsMode', "Controls from what documents word based completions are computed.")
|
||||
},
|
||||
'editor.semanticHighlighting.enabled': {
|
||||
enum: [true, false, 'configuredByTheme'],
|
||||
|
||||
@@ -690,6 +690,14 @@ export interface IDiffEditorOptions extends IEditorOptions {
|
||||
* Control the wrapping of the diff editor.
|
||||
*/
|
||||
diffWordWrap?: 'off' | 'on' | 'inherit';
|
||||
/**
|
||||
* Aria label for original editor.
|
||||
*/
|
||||
originalAriaLabel?: string;
|
||||
/**
|
||||
* Aria label for modifed editor.
|
||||
*/
|
||||
modifiedAriaLabel?: string;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -659,9 +659,21 @@ export class Cursor extends Disposable {
|
||||
}, eventsCollector, source);
|
||||
}
|
||||
|
||||
public replacePreviousChar(eventsCollector: ViewModelEventsCollector, text: string, replaceCharCnt: number, source?: string | null | undefined): void {
|
||||
public compositionType(eventsCollector: ViewModelEventsCollector, text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number, source?: string | null | undefined): void {
|
||||
if (text.length === 0 && replacePrevCharCnt === 0 && replaceNextCharCnt === 0) {
|
||||
// this edit is a no-op
|
||||
if (positionDelta !== 0) {
|
||||
// but it still wants to move the cursor
|
||||
const newSelections = this.getSelections().map(selection => {
|
||||
const position = selection.getPosition();
|
||||
return new Selection(position.lineNumber, position.column + positionDelta, position.lineNumber, position.column + positionDelta);
|
||||
});
|
||||
this.setSelections(eventsCollector, source, newSelections, CursorChangeReason.NotSet);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this._executeEdit(() => {
|
||||
this._executeEditOperation(TypeOperations.replacePreviousChar(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text, replaceCharCnt));
|
||||
this._executeEditOperation(TypeOperations.compositionType(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text, replacePrevCharCnt, replaceNextCharCnt, positionDelta));
|
||||
}, eventsCollector, source);
|
||||
}
|
||||
|
||||
|
||||
@@ -297,6 +297,20 @@ export class CursorMoveCommands {
|
||||
return this._moveDownByModelLines(viewModel, cursors, inSelectionMode, value);
|
||||
}
|
||||
}
|
||||
case CursorMove.Direction.PrevBlankLine: {
|
||||
if (unit === CursorMove.Unit.WrappedLine) {
|
||||
return cursors.map(cursor => CursorState.fromViewState(MoveOperations.moveToPrevBlankLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode)));
|
||||
} else {
|
||||
return cursors.map(cursor => CursorState.fromModelState(MoveOperations.moveToPrevBlankLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)));
|
||||
}
|
||||
}
|
||||
case CursorMove.Direction.NextBlankLine: {
|
||||
if (unit === CursorMove.Unit.WrappedLine) {
|
||||
return cursors.map(cursor => CursorState.fromViewState(MoveOperations.moveToNextBlankLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode)));
|
||||
} else {
|
||||
return cursors.map(cursor => CursorState.fromModelState(MoveOperations.moveToNextBlankLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)));
|
||||
}
|
||||
}
|
||||
case CursorMove.Direction.WrappedLineStart: {
|
||||
// Move to the beginning of the current view line
|
||||
return this._moveToViewMinColumn(viewModel, cursors, inSelectionMode);
|
||||
@@ -614,7 +628,7 @@ export namespace CursorMove {
|
||||
description: `Property-value pairs that can be passed through this argument:
|
||||
* 'to': A mandatory logical position value providing where to move the cursor.
|
||||
\`\`\`
|
||||
'left', 'right', 'up', 'down'
|
||||
'left', 'right', 'up', 'down', 'prevBlankLine', 'nextBlankLine',
|
||||
'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter'
|
||||
'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter'
|
||||
'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside'
|
||||
@@ -633,7 +647,7 @@ export namespace CursorMove {
|
||||
'properties': {
|
||||
'to': {
|
||||
'type': 'string',
|
||||
'enum': ['left', 'right', 'up', 'down', 'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter', 'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter', 'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside']
|
||||
'enum': ['left', 'right', 'up', 'down', 'prevBlankLine', 'nextBlankLine', 'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter', 'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter', 'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside']
|
||||
},
|
||||
'by': {
|
||||
'type': 'string',
|
||||
@@ -662,6 +676,9 @@ export namespace CursorMove {
|
||||
Up: 'up',
|
||||
Down: 'down',
|
||||
|
||||
PrevBlankLine: 'prevBlankLine',
|
||||
NextBlankLine: 'nextBlankLine',
|
||||
|
||||
WrappedLineStart: 'wrappedLineStart',
|
||||
WrappedLineFirstNonWhitespaceCharacter: 'wrappedLineFirstNonWhitespaceCharacter',
|
||||
WrappedLineColumnCenter: 'wrappedLineColumnCenter',
|
||||
@@ -715,6 +732,12 @@ export namespace CursorMove {
|
||||
case RawDirection.Down:
|
||||
direction = Direction.Down;
|
||||
break;
|
||||
case RawDirection.PrevBlankLine:
|
||||
direction = Direction.PrevBlankLine;
|
||||
break;
|
||||
case RawDirection.NextBlankLine:
|
||||
direction = Direction.NextBlankLine;
|
||||
break;
|
||||
case RawDirection.WrappedLineStart:
|
||||
direction = Direction.WrappedLineStart;
|
||||
break;
|
||||
@@ -790,6 +813,8 @@ export namespace CursorMove {
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
PrevBlankLine,
|
||||
NextBlankLine,
|
||||
|
||||
WrappedLineStart,
|
||||
WrappedLineFirstNonWhitespaceCharacter,
|
||||
@@ -809,6 +834,8 @@ export namespace CursorMove {
|
||||
| Direction.Right
|
||||
| Direction.Up
|
||||
| Direction.Down
|
||||
| Direction.PrevBlankLine
|
||||
| Direction.NextBlankLine
|
||||
| Direction.WrappedLineStart
|
||||
| Direction.WrappedLineFirstNonWhitespaceCharacter
|
||||
| Direction.WrappedLineColumnCenter
|
||||
|
||||
@@ -229,6 +229,47 @@ export class MoveOperations {
|
||||
);
|
||||
}
|
||||
|
||||
private static _isBlankLine(model: ICursorSimpleModel, lineNumber: number): boolean {
|
||||
if (model.getLineFirstNonWhitespaceColumn(lineNumber) === 0) {
|
||||
// empty or contains only whitespace
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static moveToPrevBlankLine(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState {
|
||||
let lineNumber = cursor.position.lineNumber;
|
||||
|
||||
// If our current line is blank, move to the previous non-blank line
|
||||
while (lineNumber > 1 && this._isBlankLine(model, lineNumber)) {
|
||||
lineNumber--;
|
||||
}
|
||||
|
||||
// Find the previous blank line
|
||||
while (lineNumber > 1 && !this._isBlankLine(model, lineNumber)) {
|
||||
lineNumber--;
|
||||
}
|
||||
|
||||
return cursor.move(inSelectionMode, lineNumber, model.getLineMinColumn(lineNumber), 0);
|
||||
}
|
||||
|
||||
public static moveToNextBlankLine(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState {
|
||||
const lineCount = model.getLineCount();
|
||||
let lineNumber = cursor.position.lineNumber;
|
||||
|
||||
// If our current line is blank, move to the next non-blank line
|
||||
while (lineNumber < lineCount && this._isBlankLine(model, lineNumber)) {
|
||||
lineNumber++;
|
||||
}
|
||||
|
||||
// Find the next blank line
|
||||
while (lineNumber < lineCount && !this._isBlankLine(model, lineNumber)) {
|
||||
lineNumber++;
|
||||
}
|
||||
|
||||
return cursor.move(inSelectionMode, lineNumber, model.getLineMinColumn(lineNumber), 0);
|
||||
}
|
||||
|
||||
public static moveToBeginningOfLine(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, inSelectionMode: boolean): SingleCursorState {
|
||||
let lineNumber = cursor.position.lineNumber;
|
||||
let minColumn = model.getLineMinColumn(lineNumber);
|
||||
|
||||
@@ -258,34 +258,33 @@ export class TypeOperations {
|
||||
return commands;
|
||||
}
|
||||
|
||||
public static replacePreviousChar(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], txt: string, replaceCharCnt: number): EditOperationResult {
|
||||
let commands: Array<ICommand | null> = [];
|
||||
for (let i = 0, len = selections.length; i < len; i++) {
|
||||
const selection = selections[i];
|
||||
if (!selection.isEmpty()) {
|
||||
// looks like https://github.com/microsoft/vscode/issues/2773
|
||||
// where a cursor operation occurred before a canceled composition
|
||||
// => ignore composition
|
||||
commands[i] = null;
|
||||
continue;
|
||||
}
|
||||
const pos = selection.getPosition();
|
||||
const startColumn = Math.max(1, pos.column - replaceCharCnt);
|
||||
const range = new Range(pos.lineNumber, startColumn, pos.lineNumber, pos.column);
|
||||
const oldText = model.getValueInRange(range);
|
||||
if (oldText === txt) {
|
||||
// => ignore composition that doesn't do anything
|
||||
commands[i] = null;
|
||||
continue;
|
||||
}
|
||||
commands[i] = new ReplaceCommand(range, txt);
|
||||
}
|
||||
public static compositionType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): EditOperationResult {
|
||||
const commands = selections.map(selection => this._compositionType(model, selection, text, replacePrevCharCnt, replaceNextCharCnt, positionDelta));
|
||||
return new EditOperationResult(EditOperationType.Typing, commands, {
|
||||
shouldPushStackElementBefore: (prevEditOperationType !== EditOperationType.Typing),
|
||||
shouldPushStackElementAfter: false
|
||||
});
|
||||
}
|
||||
|
||||
private static _compositionType(model: ITextModel, selection: Selection, text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): ICommand | null {
|
||||
if (!selection.isEmpty()) {
|
||||
// looks like https://github.com/microsoft/vscode/issues/2773
|
||||
// where a cursor operation occurred before a canceled composition
|
||||
// => ignore composition
|
||||
return null;
|
||||
}
|
||||
const pos = selection.getPosition();
|
||||
const startColumn = Math.max(1, pos.column - replacePrevCharCnt);
|
||||
const endColumn = Math.min(model.getLineMaxColumn(pos.lineNumber), pos.column + replaceNextCharCnt);
|
||||
const range = new Range(pos.lineNumber, startColumn, pos.lineNumber, endColumn);
|
||||
const oldText = model.getValueInRange(range);
|
||||
if (oldText === text && positionDelta === 0) {
|
||||
// => ignore composition that doesn't do anything
|
||||
return null;
|
||||
}
|
||||
return new ReplaceCommandWithOffsetCursorState(range, text, 0, positionDelta);
|
||||
}
|
||||
|
||||
private static _typeCommand(range: Range, text: string, keepPosition: boolean): ICommand {
|
||||
if (keepPosition) {
|
||||
return new ReplaceCommandWithoutChangingPosition(range, text, true);
|
||||
|
||||
@@ -700,6 +700,7 @@ export const enum Handler {
|
||||
CompositionEnd = 'compositionEnd',
|
||||
Type = 'type',
|
||||
ReplacePreviousChar = 'replacePreviousChar',
|
||||
CompositionType = 'compositionType',
|
||||
Paste = 'paste',
|
||||
Cut = 'cut',
|
||||
}
|
||||
@@ -719,6 +720,16 @@ export interface ReplacePreviousCharPayload {
|
||||
replaceCharCnt: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface CompositionTypePayload {
|
||||
text: string;
|
||||
replacePrevCharCnt: number;
|
||||
replaceNextCharCnt: number;
|
||||
positionDelta: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -3,70 +3,71 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
export namespace EditorContextKeys {
|
||||
|
||||
export const editorSimpleInput = new RawContextKey<boolean>('editorSimpleInput', false);
|
||||
export const editorSimpleInput = new RawContextKey<boolean>('editorSimpleInput', false, true);
|
||||
/**
|
||||
* A context key that is set when the editor's text has focus (cursor is blinking).
|
||||
* Is false when focus is in simple editor widgets (repl input, scm commit input).
|
||||
*/
|
||||
export const editorTextFocus = new RawContextKey<boolean>('editorTextFocus', false);
|
||||
export const editorTextFocus = new RawContextKey<boolean>('editorTextFocus', false, nls.localize('editorTextFocus', "Whether the editor text has focus (cursor is blinking)"));
|
||||
/**
|
||||
* A context key that is set when the editor's text or an editor's widget has focus.
|
||||
*/
|
||||
export const focus = new RawContextKey<boolean>('editorFocus', false);
|
||||
export const focus = new RawContextKey<boolean>('editorFocus', false, nls.localize('editorFocus', "Whether the editor or an editor widget has focus (e.g. focus is in the find widget)"));
|
||||
|
||||
/**
|
||||
* A context key that is set when any editor input has focus (regular editor, repl input...).
|
||||
*/
|
||||
export const textInputFocus = new RawContextKey<boolean>('textInputFocus', false);
|
||||
export const textInputFocus = new RawContextKey<boolean>('textInputFocus', false, nls.localize('textInputFocus', "Whether an editor or a rich text input has focus (cursor is blinking)"));
|
||||
|
||||
export const readOnly = new RawContextKey<boolean>('editorReadonly', false);
|
||||
export const inDiffEditor = new RawContextKey<boolean>('inDiffEditor', false);
|
||||
export const columnSelection = new RawContextKey<boolean>('editorColumnSelection', false);
|
||||
export const readOnly = new RawContextKey<boolean>('editorReadonly', false, nls.localize('editorReadonly', "Whether the editor is read only"));
|
||||
export const inDiffEditor = new RawContextKey<boolean>('inDiffEditor', false, nls.localize('inDiffEditor', "Whether the context is a diff editor"));
|
||||
export const columnSelection = new RawContextKey<boolean>('editorColumnSelection', false, nls.localize('editorColumnSelection', "Whether `editor.columnSelection` is enabled"));
|
||||
export const writable = readOnly.toNegated();
|
||||
export const hasNonEmptySelection = new RawContextKey<boolean>('editorHasSelection', false);
|
||||
export const hasNonEmptySelection = new RawContextKey<boolean>('editorHasSelection', false, nls.localize('editorHasSelection', "Whether the editor has text selected"));
|
||||
export const hasOnlyEmptySelection = hasNonEmptySelection.toNegated();
|
||||
export const hasMultipleSelections = new RawContextKey<boolean>('editorHasMultipleSelections', false);
|
||||
export const hasMultipleSelections = new RawContextKey<boolean>('editorHasMultipleSelections', false, nls.localize('editorHasMultipleSelections', "Whether the editor has multiple selections"));
|
||||
export const hasSingleSelection = hasMultipleSelections.toNegated();
|
||||
export const tabMovesFocus = new RawContextKey<boolean>('editorTabMovesFocus', false);
|
||||
export const tabMovesFocus = new RawContextKey<boolean>('editorTabMovesFocus', false, nls.localize('editorTabMovesFocus', "Whether `Tab` will move focus out of the editor"));
|
||||
export const tabDoesNotMoveFocus = tabMovesFocus.toNegated();
|
||||
export const isInWalkThroughSnippet = new RawContextKey<boolean>('isInEmbeddedEditor', false);
|
||||
export const canUndo = new RawContextKey<boolean>('canUndo', false);
|
||||
export const canRedo = new RawContextKey<boolean>('canRedo', false);
|
||||
export const isInWalkThroughSnippet = new RawContextKey<boolean>('isInEmbeddedEditor', false, true);
|
||||
export const canUndo = new RawContextKey<boolean>('canUndo', false, true);
|
||||
export const canRedo = new RawContextKey<boolean>('canRedo', false, true);
|
||||
|
||||
export const hoverVisible = new RawContextKey<boolean>('editorHoverVisible', false);
|
||||
export const hoverVisible = new RawContextKey<boolean>('editorHoverVisible', false, nls.localize('editorHoverVisible', "Whether the editor hover is visible"));
|
||||
|
||||
/**
|
||||
* A context key that is set when an editor is part of a larger editor, like notebooks or
|
||||
* (future) a diff editor
|
||||
*/
|
||||
export const inCompositeEditor = new RawContextKey<boolean>('inCompositeEditor', undefined);
|
||||
export const inCompositeEditor = new RawContextKey<boolean>('inCompositeEditor', undefined, nls.localize('inCompositeEditor', "Whether the editor is part of a larger editor (e.g. notebooks)"));
|
||||
export const notInCompositeEditor = inCompositeEditor.toNegated();
|
||||
|
||||
// -- mode context keys
|
||||
export const languageId = new RawContextKey<string>('editorLangId', '');
|
||||
export const hasCompletionItemProvider = new RawContextKey<boolean>('editorHasCompletionItemProvider', false);
|
||||
export const hasCodeActionsProvider = new RawContextKey<boolean>('editorHasCodeActionsProvider', false);
|
||||
export const hasCodeLensProvider = new RawContextKey<boolean>('editorHasCodeLensProvider', false);
|
||||
export const hasDefinitionProvider = new RawContextKey<boolean>('editorHasDefinitionProvider', false);
|
||||
export const hasDeclarationProvider = new RawContextKey<boolean>('editorHasDeclarationProvider', false);
|
||||
export const hasImplementationProvider = new RawContextKey<boolean>('editorHasImplementationProvider', false);
|
||||
export const hasTypeDefinitionProvider = new RawContextKey<boolean>('editorHasTypeDefinitionProvider', false);
|
||||
export const hasHoverProvider = new RawContextKey<boolean>('editorHasHoverProvider', false);
|
||||
export const hasDocumentHighlightProvider = new RawContextKey<boolean>('editorHasDocumentHighlightProvider', false);
|
||||
export const hasDocumentSymbolProvider = new RawContextKey<boolean>('editorHasDocumentSymbolProvider', false);
|
||||
export const hasReferenceProvider = new RawContextKey<boolean>('editorHasReferenceProvider', false);
|
||||
export const hasRenameProvider = new RawContextKey<boolean>('editorHasRenameProvider', false);
|
||||
export const hasSignatureHelpProvider = new RawContextKey<boolean>('editorHasSignatureHelpProvider', false);
|
||||
export const hasInlineHintsProvider = new RawContextKey<boolean>('editorHasInlineHintsProvider', false);
|
||||
export const languageId = new RawContextKey<string>('editorLangId', '', nls.localize('editorLangId', "The language identifier of the editor"));
|
||||
export const hasCompletionItemProvider = new RawContextKey<boolean>('editorHasCompletionItemProvider', false, nls.localize('editorHasCompletionItemProvider', "Whether the editor has a completion item provider"));
|
||||
export const hasCodeActionsProvider = new RawContextKey<boolean>('editorHasCodeActionsProvider', false, nls.localize('editorHasCodeActionsProvider', "Whether the editor has a code actions provider"));
|
||||
export const hasCodeLensProvider = new RawContextKey<boolean>('editorHasCodeLensProvider', false, nls.localize('editorHasCodeLensProvider', "Whether the editor has a code lens provider"));
|
||||
export const hasDefinitionProvider = new RawContextKey<boolean>('editorHasDefinitionProvider', false, nls.localize('editorHasDefinitionProvider', "Whether the editor has a definition provider"));
|
||||
export const hasDeclarationProvider = new RawContextKey<boolean>('editorHasDeclarationProvider', false, nls.localize('editorHasDeclarationProvider', "Whether the editor has a declaration provider"));
|
||||
export const hasImplementationProvider = new RawContextKey<boolean>('editorHasImplementationProvider', false, nls.localize('editorHasImplementationProvider', "Whether the editor has an implementation provider"));
|
||||
export const hasTypeDefinitionProvider = new RawContextKey<boolean>('editorHasTypeDefinitionProvider', false, nls.localize('editorHasTypeDefinitionProvider', "Whether the editor has a type definition provider"));
|
||||
export const hasHoverProvider = new RawContextKey<boolean>('editorHasHoverProvider', false, nls.localize('editorHasHoverProvider', "Whether the editor has a hover provider"));
|
||||
export const hasDocumentHighlightProvider = new RawContextKey<boolean>('editorHasDocumentHighlightProvider', false, nls.localize('editorHasDocumentHighlightProvider', "Whether the editor has a document highlight provider"));
|
||||
export const hasDocumentSymbolProvider = new RawContextKey<boolean>('editorHasDocumentSymbolProvider', false, nls.localize('editorHasDocumentSymbolProvider', "Whether the editor has a document symbol provider"));
|
||||
export const hasReferenceProvider = new RawContextKey<boolean>('editorHasReferenceProvider', false, nls.localize('editorHasReferenceProvider', "Whether the editor has a reference provider"));
|
||||
export const hasRenameProvider = new RawContextKey<boolean>('editorHasRenameProvider', false, nls.localize('editorHasRenameProvider', "Whether the editor has a rename provider"));
|
||||
export const hasSignatureHelpProvider = new RawContextKey<boolean>('editorHasSignatureHelpProvider', false, nls.localize('editorHasSignatureHelpProvider', "Whether the editor has a signature help provider"));
|
||||
export const hasInlineHintsProvider = new RawContextKey<boolean>('editorHasInlineHintsProvider', false, nls.localize('editorHasInlineHintsProvider', "Whether the editor has an inline hints provider"));
|
||||
|
||||
// -- mode context keys: formatting
|
||||
export const hasDocumentFormattingProvider = new RawContextKey<boolean>('editorHasDocumentFormattingProvider', false);
|
||||
export const hasDocumentSelectionFormattingProvider = new RawContextKey<boolean>('editorHasDocumentSelectionFormattingProvider', false);
|
||||
export const hasMultipleDocumentFormattingProvider = new RawContextKey<boolean>('editorHasMultipleDocumentFormattingProvider', false);
|
||||
export const hasMultipleDocumentSelectionFormattingProvider = new RawContextKey<boolean>('editorHasMultipleDocumentSelectionFormattingProvider', false);
|
||||
export const hasDocumentFormattingProvider = new RawContextKey<boolean>('editorHasDocumentFormattingProvider', false, nls.localize('editorHasDocumentFormattingProvider', "Whether the editor has a document formatting provider"));
|
||||
export const hasDocumentSelectionFormattingProvider = new RawContextKey<boolean>('editorHasDocumentSelectionFormattingProvider', false, nls.localize('editorHasDocumentSelectionFormattingProvider', "Whether the editor has a document selection formatting provider"));
|
||||
export const hasMultipleDocumentFormattingProvider = new RawContextKey<boolean>('editorHasMultipleDocumentFormattingProvider', false, nls.localize('editorHasMultipleDocumentFormattingProvider', "Whether the editor has multiple document formatting providers"));
|
||||
export const hasMultipleDocumentSelectionFormattingProvider = new RawContextKey<boolean>('editorHasMultipleDocumentSelectionFormattingProvider', false, nls.localize('editorHasMultipleDocumentSelectionFormattingProvider', "Whether the editor has multiple document selection formatting providers"));
|
||||
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import { IUndoRedoService, ResourceEditStackSnapshot } from 'vs/platform/undoRed
|
||||
import { TextChange } from 'vs/editor/common/model/textChange';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer';
|
||||
import { listenStream } from 'vs/base/common/stream';
|
||||
|
||||
function createTextBufferBuilder() {
|
||||
return new PieceTreeTextBufferBuilder();
|
||||
@@ -64,33 +65,33 @@ export function createTextBufferFactoryFromStream(stream: ITextStream | VSBuffer
|
||||
|
||||
let done = false;
|
||||
|
||||
stream.on('data', (chunk: string | VSBuffer) => {
|
||||
if (validator) {
|
||||
const error = validator(chunk);
|
||||
if (error) {
|
||||
listenStream<string | VSBuffer>(stream, {
|
||||
onData: chunk => {
|
||||
if (validator) {
|
||||
const error = validator(chunk);
|
||||
if (error) {
|
||||
done = true;
|
||||
reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (filter) {
|
||||
chunk = filter(chunk);
|
||||
}
|
||||
|
||||
builder.acceptChunk((typeof chunk === 'string') ? chunk : chunk.toString());
|
||||
},
|
||||
onError: error => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (filter) {
|
||||
chunk = filter(chunk);
|
||||
}
|
||||
|
||||
builder.acceptChunk((typeof chunk === 'string') ? chunk : chunk.toString());
|
||||
});
|
||||
|
||||
stream.on('error', (error) => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('end', () => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
resolve(builder.finish());
|
||||
},
|
||||
onEnd: () => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
resolve(builder.finish());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -266,7 +266,7 @@ export interface HoverProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* An evaluatable expression represents additional information for an expression in a document. Evaluatable expression are
|
||||
* An evaluatable expression represents additional information for an expression in a document. Evaluatable expressions are
|
||||
* evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget.
|
||||
* @internal
|
||||
*/
|
||||
@@ -275,15 +275,16 @@ export interface EvaluatableExpression {
|
||||
* The range to which this expression applies.
|
||||
*/
|
||||
range: IRange;
|
||||
/*
|
||||
/**
|
||||
* This expression overrides the expression extracted from the range.
|
||||
*/
|
||||
expression?: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The hover provider interface defines the contract between extensions and
|
||||
* the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature.
|
||||
* The evaluatable expression provider interface defines the contract between extensions and
|
||||
* the debug hover.
|
||||
* @internal
|
||||
*/
|
||||
export interface EvaluatableExpressionProvider {
|
||||
@@ -295,6 +296,73 @@ export interface EvaluatableExpressionProvider {
|
||||
provideEvaluatableExpression(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<EvaluatableExpression>;
|
||||
}
|
||||
|
||||
/**
|
||||
* An open ended information bag passed to the inline value provider.
|
||||
* A minimal context containes just the document location where the debugger has stopped.
|
||||
* @internal
|
||||
*/
|
||||
export interface InlineValueContext {
|
||||
stoppedLocation: Range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide inline value as text.
|
||||
* @internal
|
||||
*/
|
||||
export interface InlineValueText {
|
||||
type: 'text';
|
||||
range: IRange;
|
||||
text: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide inline value through a variable lookup.
|
||||
* @internal
|
||||
*/
|
||||
export interface InlineValueVariableLookup {
|
||||
type: 'variable';
|
||||
range: IRange;
|
||||
variableName?: string;
|
||||
caseSensitiveLookup: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide inline value through an expression evaluation.
|
||||
* @internal
|
||||
*/
|
||||
export interface InlineValueExpression {
|
||||
type: 'expression';
|
||||
range: IRange;
|
||||
expression?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline value information can be provided by different means:
|
||||
* - directly as a text value (class InlineValueText).
|
||||
* - as a name to use for a variable lookup (class InlineValueVariableLookup)
|
||||
* - as an evaluatable expression (class InlineValueEvaluatableExpression)
|
||||
* The InlineValue types combines all inline value types into one type.
|
||||
* @internal
|
||||
*/
|
||||
export type InlineValue = InlineValueText | InlineValueVariableLookup | InlineValueExpression;
|
||||
|
||||
/**
|
||||
* The inline values provider interface defines the contract between extensions and
|
||||
* the debugger's inline values feature.
|
||||
* @internal
|
||||
*/
|
||||
export interface InlineValuesProvider {
|
||||
/**
|
||||
*/
|
||||
onDidChangeInlineValues?: Event<void> | undefined;
|
||||
/**
|
||||
* Provide the "inline values" for the given range and document. Multiple hovers at the same
|
||||
* position will be merged by the editor. A hover can have a range which defaults
|
||||
* to the word range at the position when omitted.
|
||||
*/
|
||||
provideInlineValues(model: model.ITextModel, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult<InlineValue[]>;
|
||||
}
|
||||
|
||||
export const enum CompletionItemKind {
|
||||
Method,
|
||||
Function,
|
||||
@@ -1439,9 +1507,9 @@ export interface AuthenticationSession {
|
||||
* @internal
|
||||
*/
|
||||
export interface AuthenticationSessionsChangeEvent {
|
||||
added: ReadonlyArray<string>;
|
||||
removed: ReadonlyArray<string>;
|
||||
changed: ReadonlyArray<string>;
|
||||
added: ReadonlyArray<AuthenticationSession>;
|
||||
removed: ReadonlyArray<AuthenticationSession>;
|
||||
changed: ReadonlyArray<AuthenticationSession>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1659,9 +1727,17 @@ export interface CodeLensProvider {
|
||||
resolveCodeLens?(model: model.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult<CodeLens>;
|
||||
}
|
||||
|
||||
|
||||
export enum InlineHintKind {
|
||||
Other = 0,
|
||||
Type = 1,
|
||||
Parameter = 2,
|
||||
}
|
||||
|
||||
export interface InlineHint {
|
||||
text: string;
|
||||
range: IRange;
|
||||
kind: InlineHintKind;
|
||||
description?: string | IMarkdownString;
|
||||
whitespaceBefore?: boolean;
|
||||
whitespaceAfter?: boolean;
|
||||
@@ -1737,6 +1813,11 @@ export const HoverProviderRegistry = new LanguageFeatureRegistry<HoverProvider>(
|
||||
*/
|
||||
export const EvaluatableExpressionProviderRegistry = new LanguageFeatureRegistry<EvaluatableExpressionProvider>();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const InlineValuesProviderRegistry = new LanguageFeatureRegistry<InlineValuesProvider>();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -57,33 +57,21 @@ export class RichEditSupport {
|
||||
public readonly indentationRules: IndentationRule | undefined;
|
||||
public readonly foldingRules: FoldingRules;
|
||||
|
||||
constructor(languageIdentifier: LanguageIdentifier, previous: RichEditSupport | undefined, rawConf: LanguageConfiguration) {
|
||||
constructor(languageIdentifier: LanguageIdentifier, rawConf: LanguageConfiguration) {
|
||||
this._languageIdentifier = languageIdentifier;
|
||||
|
||||
this._brackets = null;
|
||||
this._electricCharacter = null;
|
||||
|
||||
let prev: LanguageConfiguration | null = null;
|
||||
if (previous) {
|
||||
prev = previous._conf;
|
||||
}
|
||||
|
||||
this._conf = RichEditSupport._mergeConf(prev, rawConf);
|
||||
|
||||
this._conf = rawConf;
|
||||
this._onEnterSupport = (this._conf.brackets || this._conf.indentationRules || this._conf.onEnterRules ? new OnEnterSupport(this._conf) : null);
|
||||
this.comments = RichEditSupport._handleComments(this._conf);
|
||||
|
||||
this.characterPair = new CharacterPairSupport(this._conf);
|
||||
|
||||
this.wordDefinition = this._conf.wordPattern || DEFAULT_WORD_REGEXP;
|
||||
|
||||
this.indentationRules = this._conf.indentationRules;
|
||||
if (this._conf.indentationRules) {
|
||||
this.indentRulesSupport = new IndentRulesSupport(this._conf.indentationRules);
|
||||
} else {
|
||||
this.indentRulesSupport = null;
|
||||
}
|
||||
|
||||
this.foldingRules = this._conf.folding || {};
|
||||
}
|
||||
|
||||
@@ -108,21 +96,6 @@ export class RichEditSupport {
|
||||
return this._onEnterSupport.onEnter(autoIndent, previousLineText, beforeEnterText, afterEnterText);
|
||||
}
|
||||
|
||||
private static _mergeConf(prev: LanguageConfiguration | null, current: LanguageConfiguration): LanguageConfiguration {
|
||||
return {
|
||||
comments: (prev ? current.comments || prev.comments : current.comments),
|
||||
brackets: (prev ? current.brackets || prev.brackets : current.brackets),
|
||||
wordPattern: (prev ? current.wordPattern || prev.wordPattern : current.wordPattern),
|
||||
indentationRules: (prev ? current.indentationRules || prev.indentationRules : current.indentationRules),
|
||||
onEnterRules: (prev ? current.onEnterRules || prev.onEnterRules : current.onEnterRules),
|
||||
autoClosingPairs: (prev ? current.autoClosingPairs || prev.autoClosingPairs : current.autoClosingPairs),
|
||||
surroundingPairs: (prev ? current.surroundingPairs || prev.surroundingPairs : current.surroundingPairs),
|
||||
autoCloseBefore: (prev ? current.autoCloseBefore || prev.autoCloseBefore : current.autoCloseBefore),
|
||||
folding: (prev ? current.folding || prev.folding : current.folding),
|
||||
__electricCharacterSupport: (prev ? current.__electricCharacterSupport || prev.__electricCharacterSupport : current.__electricCharacterSupport),
|
||||
};
|
||||
}
|
||||
|
||||
private static _handleComments(conf: LanguageConfiguration): ICommentsConfiguration | null {
|
||||
let commentRule = conf.comments;
|
||||
if (!commentRule) {
|
||||
@@ -151,38 +124,120 @@ export class LanguageConfigurationChangeEvent {
|
||||
) { }
|
||||
}
|
||||
|
||||
export class LanguageConfigurationRegistryImpl {
|
||||
class LanguageConfigurationEntry {
|
||||
|
||||
private readonly _entries = new Map<LanguageId, RichEditSupport | undefined>();
|
||||
constructor(
|
||||
public readonly configuration: LanguageConfiguration,
|
||||
public readonly priority: number,
|
||||
public readonly order: number
|
||||
) { }
|
||||
|
||||
private readonly _onDidChange = new Emitter<LanguageConfigurationChangeEvent>();
|
||||
public readonly onDidChange: Event<LanguageConfigurationChangeEvent> = this._onDidChange.event;
|
||||
public static cmp(a: LanguageConfigurationEntry, b: LanguageConfigurationEntry) {
|
||||
if (a.priority === b.priority) {
|
||||
// higher order last
|
||||
return a.order - b.order;
|
||||
}
|
||||
// higher priority last
|
||||
return a.priority - b.priority;
|
||||
}
|
||||
}
|
||||
|
||||
public register(languageIdentifier: LanguageIdentifier, configuration: LanguageConfiguration): IDisposable {
|
||||
let previous = this._getRichEditSupport(languageIdentifier.id);
|
||||
let current = new RichEditSupport(languageIdentifier, previous, configuration);
|
||||
this._entries.set(languageIdentifier.id, current);
|
||||
this._onDidChange.fire(new LanguageConfigurationChangeEvent(languageIdentifier));
|
||||
class LanguageConfigurationEntries {
|
||||
|
||||
private readonly _entries: LanguageConfigurationEntry[];
|
||||
private _order: number;
|
||||
private _resolved: RichEditSupport | null = null;
|
||||
|
||||
constructor(
|
||||
public readonly languageIdentifier: LanguageIdentifier
|
||||
) {
|
||||
this._entries = [];
|
||||
this._order = 0;
|
||||
this._resolved = null;
|
||||
}
|
||||
|
||||
public register(configuration: LanguageConfiguration, priority: number): IDisposable {
|
||||
const entry = new LanguageConfigurationEntry(configuration, priority, ++this._order);
|
||||
this._entries.push(entry);
|
||||
this._resolved = null;
|
||||
return toDisposable(() => {
|
||||
if (this._entries.get(languageIdentifier.id) === current) {
|
||||
this._entries.set(languageIdentifier.id, previous);
|
||||
this._onDidChange.fire(new LanguageConfigurationChangeEvent(languageIdentifier));
|
||||
for (let i = 0; i < this._entries.length; i++) {
|
||||
if (this._entries[i] === entry) {
|
||||
this._entries.splice(i, 1);
|
||||
this._resolved = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _getRichEditSupport(languageId: LanguageId): RichEditSupport | undefined {
|
||||
return this._entries.get(languageId);
|
||||
public getRichEditSupport(): RichEditSupport | null {
|
||||
if (!this._resolved) {
|
||||
const config = this._resolve();
|
||||
if (config) {
|
||||
this._resolved = new RichEditSupport(this.languageIdentifier, config);
|
||||
}
|
||||
}
|
||||
return this._resolved;
|
||||
}
|
||||
|
||||
public getIndentationRules(languageId: LanguageId) {
|
||||
const value = this._entries.get(languageId);
|
||||
|
||||
if (!value) {
|
||||
private _resolve(): LanguageConfiguration | null {
|
||||
if (this._entries.length === 0) {
|
||||
return null;
|
||||
}
|
||||
this._entries.sort(LanguageConfigurationEntry.cmp);
|
||||
const result: LanguageConfiguration = {};
|
||||
for (const entry of this._entries) {
|
||||
const conf = entry.configuration;
|
||||
result.comments = conf.comments || result.comments;
|
||||
result.brackets = conf.brackets || result.brackets;
|
||||
result.wordPattern = conf.wordPattern || result.wordPattern;
|
||||
result.indentationRules = conf.indentationRules || result.indentationRules;
|
||||
result.onEnterRules = conf.onEnterRules || result.onEnterRules;
|
||||
result.autoClosingPairs = conf.autoClosingPairs || result.autoClosingPairs;
|
||||
result.surroundingPairs = conf.surroundingPairs || result.surroundingPairs;
|
||||
result.autoCloseBefore = conf.autoCloseBefore || result.autoCloseBefore;
|
||||
result.folding = conf.folding || result.folding;
|
||||
result.__electricCharacterSupport = conf.__electricCharacterSupport || result.__electricCharacterSupport;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return value.indentationRules || null;
|
||||
export class LanguageConfigurationRegistryImpl {
|
||||
|
||||
private readonly _entries2 = new Map<LanguageId, LanguageConfigurationEntries>();
|
||||
|
||||
private readonly _onDidChange = new Emitter<LanguageConfigurationChangeEvent>();
|
||||
public readonly onDidChange: Event<LanguageConfigurationChangeEvent> = this._onDidChange.event;
|
||||
|
||||
/**
|
||||
* @param priority Use a higher number for higher priority
|
||||
*/
|
||||
public register(languageIdentifier: LanguageIdentifier, configuration: LanguageConfiguration, priority: number = 0): IDisposable {
|
||||
let entries = this._entries2.get(languageIdentifier.id);
|
||||
if (!entries) {
|
||||
entries = new LanguageConfigurationEntries(languageIdentifier);
|
||||
this._entries2.set(languageIdentifier.id, entries);
|
||||
}
|
||||
|
||||
const disposable = entries.register(configuration, priority);
|
||||
this._onDidChange.fire(new LanguageConfigurationChangeEvent(languageIdentifier));
|
||||
|
||||
return toDisposable(() => {
|
||||
disposable.dispose();
|
||||
this._onDidChange.fire(new LanguageConfigurationChangeEvent(languageIdentifier));
|
||||
});
|
||||
}
|
||||
|
||||
private _getRichEditSupport(languageId: LanguageId): RichEditSupport | null {
|
||||
const entries = this._entries2.get(languageId);
|
||||
return entries ? entries.getRichEditSupport() : null;
|
||||
}
|
||||
|
||||
public getIndentationRules(languageId: LanguageId): IndentationRule | null {
|
||||
const value = this._getRichEditSupport(languageId);
|
||||
return value ? value.indentationRules || null : null;
|
||||
}
|
||||
|
||||
// begin electricCharacter
|
||||
@@ -273,11 +328,12 @@ export class LanguageConfigurationRegistryImpl {
|
||||
|
||||
public getWordDefinitions(): [LanguageId, RegExp][] {
|
||||
let result: [LanguageId, RegExp][] = [];
|
||||
this._entries.forEach((value, language) => {
|
||||
for (const [language, entries] of this._entries2) {
|
||||
const value = entries.getRichEditSupport();
|
||||
if (value) {
|
||||
result.push([language, value.wordDefinition]);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { MovingAverage } from 'vs/base/common/numbers';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { LanguageSelector, score } from 'vs/editor/common/modes/languageSelector';
|
||||
import { LanguageFilter, LanguageSelector, score } from 'vs/editor/common/modes/languageSelector';
|
||||
import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
|
||||
|
||||
interface Entry<T> {
|
||||
@@ -25,7 +25,7 @@ function isExclusive(selector: LanguageSelector): boolean {
|
||||
} else if (Array.isArray(selector)) {
|
||||
return selector.every(isExclusive);
|
||||
} else {
|
||||
return !!selector.exclusive;
|
||||
return !!(selector as LanguageFilter).exclusive; // TODO: microsoft/TypeScript#42768
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ export function score(selector: LanguageSelector | undefined, candidateUri: URI,
|
||||
|
||||
} else if (selector) {
|
||||
// filter -> select accordingly, use defaults for scheme
|
||||
const { language, pattern, scheme, hasAccessToAllModels } = selector;
|
||||
const { language, pattern, scheme, hasAccessToAllModels } = selector as LanguageFilter; // TODO: microsoft/TypeScript#42768
|
||||
|
||||
if (!candidateIsSynchronized && !hasAccessToAllModels) {
|
||||
return 0;
|
||||
|
||||
@@ -85,4 +85,4 @@ LanguageConfigurationRegistry.register(PLAINTEXT_LANGUAGE_IDENTIFIER, {
|
||||
folding: {
|
||||
offSide: true
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
|
||||
@@ -37,9 +37,14 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe
|
||||
// Name & Extension(s)
|
||||
if (name) {
|
||||
classes.push(`${name}-name-file-icon`);
|
||||
const dotSegments = name.split('.');
|
||||
for (let i = 1; i < dotSegments.length; i++) {
|
||||
classes.push(`${dotSegments.slice(i).join('.')}-ext-file-icon`); // add each combination of all found extensions if more than one
|
||||
// Avoid doing an explosive combination of extensions for very long filenames
|
||||
// (most file systems do not allow files > 255 length) with lots of `.` characters
|
||||
// https://github.com/microsoft/vscode/issues/116199
|
||||
if (name.length <= 255) {
|
||||
const dotSegments = name.split('.');
|
||||
for (let i = 1; i < dotSegments.length; i++) {
|
||||
classes.push(`${dotSegments.slice(i).join('.')}-ext-file-icon`); // add each combination of all found extensions if more than one
|
||||
}
|
||||
}
|
||||
classes.push(`ext-file-icon`); // extra segment to increase file-ext score
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export const enum SemanticTokensProviderStylingConstants {
|
||||
export class SemanticTokensProviderStyling {
|
||||
|
||||
private readonly _hashTable: HashTable;
|
||||
private _hasWarnedOverlappingTokens: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly _legend: SemanticTokensLegend,
|
||||
@@ -22,6 +23,7 @@ export class SemanticTokensProviderStyling {
|
||||
private readonly _logService: ILogService
|
||||
) {
|
||||
this._hashTable = new HashTable();
|
||||
this._hasWarnedOverlappingTokens = false;
|
||||
}
|
||||
|
||||
public getMetadata(tokenTypeIndex: number, tokenModifierSet: number, languageId: LanguageIdentifier): number {
|
||||
@@ -90,6 +92,14 @@ export class SemanticTokensProviderStyling {
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public warnOverlappingSemanticTokens(lineNumber: number, startColumn: number): void {
|
||||
if (!this._hasWarnedOverlappingTokens) {
|
||||
this._hasWarnedOverlappingTokens = true;
|
||||
console.warn(`Overlapping semantic tokens detected at lineNumber ${lineNumber}, column ${startColumn}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const enum SemanticColoringConstants {
|
||||
@@ -142,6 +152,9 @@ export function toMultilineTokens2(tokens: SemanticTokens, styling: SemanticToke
|
||||
let destData = new Uint32Array((tokenEndIndex - tokenStartIndex) * 4);
|
||||
let destOffset = 0;
|
||||
let areaLine = 0;
|
||||
let prevLineNumber = 0;
|
||||
let prevStartCharacter = 0;
|
||||
let prevEndCharacter = 0;
|
||||
while (tokenIndex < tokenEndIndex) {
|
||||
const srcOffset = 5 * tokenIndex;
|
||||
const deltaLine = srcData[srcOffset];
|
||||
@@ -157,11 +170,25 @@ export function toMultilineTokens2(tokens: SemanticTokens, styling: SemanticToke
|
||||
if (areaLine === 0) {
|
||||
areaLine = lineNumber;
|
||||
}
|
||||
if (prevLineNumber === lineNumber && prevEndCharacter > startCharacter) {
|
||||
styling.warnOverlappingSemanticTokens(lineNumber, startCharacter + 1);
|
||||
if (prevStartCharacter < startCharacter) {
|
||||
// the previous token survives after the overlapping one
|
||||
destData[destOffset - 4 + 2] = startCharacter;
|
||||
} else {
|
||||
// the previous token is entirely covered by the overlapping one
|
||||
destOffset -= 4;
|
||||
}
|
||||
}
|
||||
destData[destOffset] = lineNumber - areaLine;
|
||||
destData[destOffset + 1] = startCharacter;
|
||||
destData[destOffset + 2] = startCharacter + length;
|
||||
destData[destOffset + 3] = metadata;
|
||||
destOffset += 4;
|
||||
|
||||
prevLineNumber = lineNumber;
|
||||
prevStartCharacter = startCharacter;
|
||||
prevEndCharacter = startCharacter + length;
|
||||
}
|
||||
|
||||
lastLineNumber = lineNumber;
|
||||
|
||||
@@ -351,6 +351,12 @@ export enum IndentAction {
|
||||
Outdent = 3
|
||||
}
|
||||
|
||||
export enum InlineHintKind {
|
||||
Other = 0,
|
||||
Type = 1,
|
||||
Parameter = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Virtual Key Codes, the value does not hold any inherent meaning.
|
||||
* Inspired somewhat from https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
|
||||
@@ -948,8 +948,8 @@ export class ViewModel extends Disposable implements IViewModel {
|
||||
public type(text: string, source?: string | null | undefined): void {
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.type(eventsCollector, text, source));
|
||||
}
|
||||
public replacePreviousChar(text: string, replaceCharCnt: number, source?: string | null | undefined): void {
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.replacePreviousChar(eventsCollector, text, replaceCharCnt, source));
|
||||
public compositionType(text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number, source?: string | null | undefined): void {
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.compositionType(eventsCollector, text, replacePrevCharCnt, replaceNextCharCnt, positionDelta, source));
|
||||
}
|
||||
public paste(text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void {
|
||||
this._executeCursorEdit(eventsCollector => this._cursor.paste(eventsCollector, text, pasteOnNewLine, multicursorText, source));
|
||||
|
||||
@@ -23,8 +23,6 @@ const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste';
|
||||
|
||||
const supportsCut = (platform.isNative || document.queryCommandSupported('cut'));
|
||||
const supportsCopy = (platform.isNative || document.queryCommandSupported('copy'));
|
||||
// IE and Edge have trouble with setting html content in clipboard
|
||||
const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdgeLegacy);
|
||||
// Firefox only supports navigator.clipboard.readText() in browser extensions.
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText#Browser_compatibility
|
||||
// When loading over http, navigator.clipboard can be undefined. See https://github.com/microsoft/monaco-editor/issues/2313
|
||||
@@ -240,6 +238,6 @@ if (PasteAction) {
|
||||
});
|
||||
}
|
||||
|
||||
if (supportsCopyWithSyntaxHighlighting) {
|
||||
if (supportsCopy) {
|
||||
registerEditorAction(ExecCommandCopyWithSyntaxHighlightingAction);
|
||||
}
|
||||
|
||||
@@ -223,8 +223,7 @@ function getDocumentation(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand('_executeCodeActionProvider', async function (accessor, ...args): Promise<ReadonlyArray<modes.CodeAction>> {
|
||||
const [resource, rangeOrSelection, kind, itemResolveCount] = args;
|
||||
CommandsRegistry.registerCommand('_executeCodeActionProvider', async function (accessor, resource: URI, rangeOrSelection: Range | Selection, kind?: string, itemResolveCount?: number): Promise<ReadonlyArray<modes.CodeAction>> {
|
||||
if (!(resource instanceof URI)) {
|
||||
throw illegalArgument();
|
||||
}
|
||||
@@ -244,14 +243,14 @@ CommandsRegistry.registerCommand('_executeCodeActionProvider', async function (a
|
||||
throw illegalArgument();
|
||||
}
|
||||
|
||||
const include = typeof kind === 'string' ? new CodeActionKind(kind) : undefined;
|
||||
const codeActionSet = await getCodeActions(
|
||||
model,
|
||||
validatedRangeOrSelection,
|
||||
{ type: modes.CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include: kind && kind.value ? new CodeActionKind(kind.value) : undefined } },
|
||||
{ type: modes.CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include } },
|
||||
Progress.None,
|
||||
CancellationToken.None);
|
||||
|
||||
|
||||
const resolving: Promise<any>[] = [];
|
||||
const resolveCount = Math.min(codeActionSet.validActions.length, typeof itemResolveCount === 'number' ? itemResolveCount : 0);
|
||||
for (let i = 0; i < resolveCount; i++) {
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancelablePromise, createCancelablePromise, TimeoutTimer } from 'vs/base/common/async';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
@@ -15,10 +18,8 @@ import { CodeActionProviderRegistry, CodeActionTriggerType } from 'vs/editor/com
|
||||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMarkerService } from 'vs/platform/markers/common/markers';
|
||||
import { IEditorProgressService, Progress } from 'vs/platform/progress/common/progress';
|
||||
import { getCodeActions, CodeActionSet } from './codeAction';
|
||||
import { CodeActionSet, getCodeActions } from './codeAction';
|
||||
import { CodeActionTrigger } from './types';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
|
||||
export const SUPPORTED_CODE_ACTIONS = new RawContextKey<string>('supportedCodeAction', '');
|
||||
|
||||
@@ -147,17 +148,38 @@ export namespace CodeActionsState {
|
||||
export class Triggered {
|
||||
readonly type = Type.Triggered;
|
||||
|
||||
public readonly actions: Promise<CodeActionSet>;
|
||||
|
||||
constructor(
|
||||
public readonly trigger: CodeActionTrigger,
|
||||
public readonly rangeOrSelection: Range | Selection,
|
||||
public readonly position: Position,
|
||||
public readonly actions: CancelablePromise<CodeActionSet>,
|
||||
) { }
|
||||
private readonly _cancellablePromise: CancelablePromise<CodeActionSet>,
|
||||
) {
|
||||
this.actions = _cancellablePromise.catch((e): CodeActionSet => {
|
||||
if (isPromiseCanceledError(e)) {
|
||||
return emptyCodeActionSet;
|
||||
}
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
this._cancellablePromise.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
export type State = typeof Empty | Triggered;
|
||||
}
|
||||
|
||||
const emptyCodeActionSet: CodeActionSet = {
|
||||
allActions: [],
|
||||
validActions: [],
|
||||
dispose: () => { },
|
||||
documentation: [],
|
||||
hasAutoFix: false
|
||||
};
|
||||
|
||||
export class CodeActionModel extends Disposable {
|
||||
|
||||
private readonly _codeActionOracle = this._register(new MutableDisposable<CodeActionOracle>());
|
||||
@@ -167,6 +189,8 @@ export class CodeActionModel extends Disposable {
|
||||
private readonly _onDidChangeState = this._register(new Emitter<CodeActionsState.State>());
|
||||
public readonly onDidChangeState = this._onDidChangeState.event;
|
||||
|
||||
#isDisposed = false;
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
private readonly _markerService: IMarkerService,
|
||||
@@ -184,11 +208,20 @@ export class CodeActionModel extends Disposable {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
if (this.#isDisposed) {
|
||||
return;
|
||||
}
|
||||
this.#isDisposed = true;
|
||||
|
||||
super.dispose();
|
||||
this.setState(CodeActionsState.Empty, true);
|
||||
}
|
||||
|
||||
private _update(): void {
|
||||
if (this.#isDisposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._codeActionOracle.value = undefined;
|
||||
|
||||
this.setState(CodeActionsState.Empty);
|
||||
@@ -240,12 +273,12 @@ export class CodeActionModel extends Disposable {
|
||||
|
||||
// Cancel old request
|
||||
if (this._state.type === CodeActionsState.Type.Triggered) {
|
||||
this._state.actions.cancel();
|
||||
this._state.cancel();
|
||||
}
|
||||
|
||||
this._state = newState;
|
||||
|
||||
if (!skipNotify) {
|
||||
if (!skipNotify && !this.#isDisposed) {
|
||||
this._onDidChangeState.fire(newState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ export class CodeActionUi extends Disposable {
|
||||
private readonly _lightBulbWidget: Lazy<LightBulbWidget>;
|
||||
private readonly _activeCodeActions = this._register(new MutableDisposable<CodeActionSet>());
|
||||
|
||||
#disposed = false;
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
quickFixActionId: string,
|
||||
@@ -50,6 +52,11 @@ export class CodeActionUi extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.#disposed = true;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public async update(newState: CodeActionsState.State): Promise<void> {
|
||||
if (newState.type !== CodeActionsState.Type.Triggered) {
|
||||
this._lightBulbWidget.rawValue?.hide();
|
||||
@@ -64,6 +71,10 @@ export class CodeActionUi extends Disposable {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.#disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._lightBulbWidget.getValue().update(actions, newState.trigger, newState.position);
|
||||
|
||||
if (newState.trigger.type === CodeActionTriggerType.Manual) {
|
||||
|
||||
@@ -108,7 +108,7 @@ export class CodeLensContribution implements IEditorContribution {
|
||||
newStyle += `.monaco-editor .codelens-decoration.${this._styleClassName} { font-family: var(${fontFamilyVar})}`;
|
||||
}
|
||||
this._styleElement.textContent = newStyle;
|
||||
this._editor.getDomNode()?.style.setProperty(fontFamilyVar, fontFamily ?? 'inherit');
|
||||
this._editor.getContainerDomNode().style.setProperty(fontFamilyVar, fontFamily ?? 'inherit');
|
||||
|
||||
//
|
||||
this._editor.changeViewZones(accessor => {
|
||||
|
||||
@@ -300,7 +300,7 @@ export function toggleCollapseState(foldingModel: FoldingModel, levels: number,
|
||||
|
||||
/**
|
||||
* Collapse or expand the regions at the given locations including all children.
|
||||
* @param doCollapse Wheter to collase or expand
|
||||
* @param doCollapse Whether to collapse or expand
|
||||
* @param levels The number of levels. Use 1 to only impact the regions at the location, use Number.MAX_VALUE for all levels.
|
||||
* @param lineNumbers the location of the regions to collapse or expand, or if not set, all regions in the model.
|
||||
*/
|
||||
@@ -328,7 +328,7 @@ export function setCollapseStateLevelsDown(foldingModel: FoldingModel, doCollaps
|
||||
|
||||
/**
|
||||
* Collapse or expand the regions at the given locations including all parents.
|
||||
* @param doCollapse Wheter to collase or expand
|
||||
* @param doCollapse Whether to collapse or expand
|
||||
* @param levels The number of levels. Use 1 to only impact the regions at the location, use Number.MAX_VALUE for all levels.
|
||||
* @param lineNumbers the location of the regions to collapse or expand.
|
||||
*/
|
||||
@@ -343,7 +343,7 @@ export function setCollapseStateLevelsUp(foldingModel: FoldingModel, doCollapse:
|
||||
|
||||
/**
|
||||
* Collapse or expand a region at the given locations. If the inner most region is already collapsed/expanded, uses the first parent instead.
|
||||
* @param doCollapse Wheter to collase or expand
|
||||
* @param doCollapse Whether to collapse or expand
|
||||
* @param lineNumbers the location of the regions to collapse or expand.
|
||||
*/
|
||||
export function setCollapseStateUp(foldingModel: FoldingModel, doCollapse: boolean, lineNumbers: number[]): void {
|
||||
@@ -360,7 +360,7 @@ export function setCollapseStateUp(foldingModel: FoldingModel, doCollapse: boole
|
||||
/**
|
||||
* Folds or unfolds all regions that have a given level, except if they contain one of the blocked lines.
|
||||
* @param foldLevel level. Level == 1 is the top level
|
||||
* @param doCollapse Wheter to collase or expand
|
||||
* @param doCollapse Whether to collapse or expand
|
||||
*/
|
||||
export function setCollapseStateAtLevel(foldingModel: FoldingModel, foldLevel: number, doCollapse: boolean, blockedLineNumbers: number[]): void {
|
||||
let filter = (region: FoldingRegion, level: number) => level === foldLevel && region.isCollapsed !== doCollapse && !blockedLineNumbers.some(line => region.containsLine(line));
|
||||
|
||||
@@ -139,13 +139,11 @@ export abstract class ReferencesController implements IEditorContribution {
|
||||
|
||||
// still current request? widget still open?
|
||||
if (requestId !== this._requestIdPool || !this._widget) {
|
||||
model.dispose();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (this._model) {
|
||||
this._model.dispose();
|
||||
}
|
||||
|
||||
this._model?.dispose();
|
||||
this._model = model;
|
||||
|
||||
// show widget
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { localize } from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { basename, extUri } from 'vs/base/common/resources';
|
||||
import { IDisposable, dispose, IReference, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { defaultGenerator } from 'vs/base/common/idGenerator';
|
||||
@@ -141,7 +141,6 @@ export class FileReferences implements IDisposable {
|
||||
|
||||
export class ReferencesModel implements IDisposable {
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
private readonly _links: LocationLink[];
|
||||
private readonly _title: string;
|
||||
|
||||
@@ -185,7 +184,6 @@ export class ReferencesModel implements IDisposable {
|
||||
|
||||
dispose(): void {
|
||||
dispose(this.groups);
|
||||
this._disposables.dispose();
|
||||
this._onDidChangeReferenceRange.dispose();
|
||||
this.groups.length = 0;
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ export class MarkerHoverParticipant implements IEditorHoverParticipant<MarkerHov
|
||||
const actionsElement = dom.append(hoverElement, $('div.actions'));
|
||||
if (markerHover.marker.severity === MarkerSeverity.Error || markerHover.marker.severity === MarkerSeverity.Warning || markerHover.marker.severity === MarkerSeverity.Info) {
|
||||
disposables.add(this.renderAction(actionsElement, {
|
||||
label: nls.localize('peek problem', "Peek Problem"),
|
||||
label: nls.localize('view problem', "View Problem"),
|
||||
commandId: NextMarkerAction.ID,
|
||||
run: () => {
|
||||
this._hover.hide();
|
||||
|
||||
@@ -118,6 +118,10 @@ class ModesContentComputer implements IHoverComputer<HoverPartInfo[]> {
|
||||
|
||||
const maxColumn = model.getLineMaxColumn(lineNumber);
|
||||
const lineDecorations = this._editor.getLineDecorations(lineNumber).filter((d) => {
|
||||
if (d.options.isWholeLine) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1;
|
||||
const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn;
|
||||
if (startColumn > hoverRange.startColumn || hoverRange.endColumn > endColumn) {
|
||||
|
||||
@@ -24,7 +24,7 @@ export class MessageController implements IEditorContribution {
|
||||
|
||||
public static readonly ID = 'editor.contrib.messageController';
|
||||
|
||||
static readonly MESSAGE_VISIBLE = new RawContextKey<boolean>('messageVisible', false);
|
||||
static readonly MESSAGE_VISIBLE = new RawContextKey<boolean>('messageVisible', false, nls.localize('messageVisible', 'Whether the editor is currently showing an inline message'));
|
||||
|
||||
static get(editor: ICodeEditor): MessageController {
|
||||
return editor.getContribution<MessageController>(MessageController.ID);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import 'vs/css!./media/suggest';
|
||||
import 'vs/base/browser/ui/codicons/codiconStyles'; // The codicon symbol styles are defined here and must be loaded
|
||||
import 'vs/editor/contrib/symbolIcons/symbolIcons'; // The codicon symbol colors are defined here and must be loaded to get colors
|
||||
import * as nls from 'vs/nls';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
@@ -20,7 +21,7 @@ import { Context as SuggestContext, CompletionItem } from './suggest';
|
||||
import { CompletionModel } from './completionModel';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService, IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { registerColor, editorWidgetBackground, quickInputListFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -39,7 +40,7 @@ import { clamp } from 'vs/base/common/numbers';
|
||||
export const editorSuggestWidgetBackground = registerColor('editorSuggestWidget.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('editorSuggestWidgetBackground', 'Background color of the suggest widget.'));
|
||||
export const editorSuggestWidgetBorder = registerColor('editorSuggestWidget.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, nls.localize('editorSuggestWidgetBorder', 'Border color of the suggest widget.'));
|
||||
export const editorSuggestWidgetForeground = registerColor('editorSuggestWidget.foreground', { dark: editorForeground, light: editorForeground, hc: editorForeground }, nls.localize('editorSuggestWidgetForeground', 'Foreground color of the suggest widget.'));
|
||||
export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: listFocusBackground, light: listFocusBackground, hc: listFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.'));
|
||||
export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: quickInputListFocusBackground, light: quickInputListFocusBackground, hc: quickInputListFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.'));
|
||||
export const editorSuggestWidgetHighlightForeground = registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.'));
|
||||
|
||||
const enum State {
|
||||
@@ -102,7 +103,7 @@ export class SuggestWidget implements IDisposable {
|
||||
private _focusedItem?: CompletionItem;
|
||||
private _ignoreFocusEvents: boolean = false;
|
||||
private _completionModel?: CompletionModel;
|
||||
private _cappedHeight?: { wanted: number, capped: number };
|
||||
private _cappedHeight?: { wanted: number; capped: number; };
|
||||
private _explainMode: boolean = false;
|
||||
|
||||
readonly element: ResizableHTMLElement;
|
||||
@@ -216,6 +217,7 @@ export class SuggestWidget implements IDisposable {
|
||||
getHeight: (_element: CompletionItem): number => this.getLayoutInfo().itemHeight,
|
||||
getTemplateId: (_element: CompletionItem): string => 'suggestion'
|
||||
}, [renderer], {
|
||||
alwaysConsumeMouseWheel: true,
|
||||
useShadows: false,
|
||||
mouseSupport: false,
|
||||
accessibilityProvider: {
|
||||
|
||||
@@ -131,7 +131,7 @@ export class SuggestDetailsWidget {
|
||||
md += `score: ${item.score[0]}${item.word ? `, compared '${item.completion.filterText && (item.completion.filterText + ' (filterText)') || typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name}' with '${item.word}'` : ' (no prefix)'}\n`;
|
||||
md += `distance: ${item.distance}, see localityBonus-setting\n`;
|
||||
md += `index: ${item.idx}, based on ${item.completion.sortText && `sortText: "${item.completion.sortText}"` || 'label'}\n`;
|
||||
md += `commit characters: ${item.completion.commitCharacters}\n`;
|
||||
md += `commit characters: ${item.completion.commitCharacters?.join('')}\n`;
|
||||
documentation = new MarkdownString().appendCodeblock('empty', md);
|
||||
detail = `Provider: ${item.provider._debugDisplayName}`;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IActionViewItemProvider, IAction } from 'vs/base/common/actions';
|
||||
import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { suggestWidgetStatusbarMenu } from 'vs/editor/contrib/suggest/suggest';
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
|
||||
export class WordContextKey extends Disposable {
|
||||
export class WordContextKey {
|
||||
|
||||
static readonly AtEnd = new RawContextKey<boolean>('atEndOfWord', false);
|
||||
|
||||
private readonly _ckAtEnd: IContextKey<boolean>;
|
||||
private readonly _configListener: IDisposable;
|
||||
|
||||
private _enabled: boolean = false;
|
||||
private _selectionListener?: IDisposable;
|
||||
@@ -21,14 +22,14 @@ export class WordContextKey extends Disposable {
|
||||
private readonly _editor: ICodeEditor,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._ckAtEnd = WordContextKey.AtEnd.bindTo(contextKeyService);
|
||||
this._register(this._editor.onDidChangeConfiguration(e => e.hasChanged(EditorOption.tabCompletion) && this._update()));
|
||||
this._configListener = this._editor.onDidChangeConfiguration(e => e.hasChanged(EditorOption.tabCompletion) && this._update());
|
||||
this._update();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
this._configListener.dispose();
|
||||
this._selectionListener?.dispose();
|
||||
this._ckAtEnd.reset();
|
||||
}
|
||||
|
||||
376
lib/vscode/src/vs/editor/contrib/symbolIcons/symbolIcons.ts
Normal file
376
lib/vscode/src/vs/editor/contrib/symbolIcons/symbolIcons.ts
Normal file
@@ -0,0 +1,376 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor, foreground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
|
||||
export const SYMBOL_ICON_ARRAY_FOREGROUND = registerColor('symbolIcon.arrayForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.arrayForeground', 'The foreground color for array symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_BOOLEAN_FOREGROUND = registerColor('symbolIcon.booleanForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.booleanForeground', 'The foreground color for boolean symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_CLASS_FOREGROUND = registerColor('symbolIcon.classForeground', {
|
||||
dark: '#EE9D28',
|
||||
light: '#D67E00',
|
||||
hc: '#EE9D28'
|
||||
}, localize('symbolIcon.classForeground', 'The foreground color for class symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_COLOR_FOREGROUND = registerColor('symbolIcon.colorForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.colorForeground', 'The foreground color for color symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_CONSTANT_FOREGROUND = registerColor('symbolIcon.constantForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.constantForeground', 'The foreground color for constant symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_CONSTRUCTOR_FOREGROUND = registerColor('symbolIcon.constructorForeground', {
|
||||
dark: '#B180D7',
|
||||
light: '#652D90',
|
||||
hc: '#B180D7'
|
||||
}, localize('symbolIcon.constructorForeground', 'The foreground color for constructor symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_ENUMERATOR_FOREGROUND = registerColor('symbolIcon.enumeratorForeground', {
|
||||
dark: '#EE9D28',
|
||||
light: '#D67E00',
|
||||
hc: '#EE9D28'
|
||||
}, localize('symbolIcon.enumeratorForeground', 'The foreground color for enumerator symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND = registerColor('symbolIcon.enumeratorMemberForeground', {
|
||||
dark: '#75BEFF',
|
||||
light: '#007ACC',
|
||||
hc: '#75BEFF'
|
||||
}, localize('symbolIcon.enumeratorMemberForeground', 'The foreground color for enumerator member symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_EVENT_FOREGROUND = registerColor('symbolIcon.eventForeground', {
|
||||
dark: '#EE9D28',
|
||||
light: '#D67E00',
|
||||
hc: '#EE9D28'
|
||||
}, localize('symbolIcon.eventForeground', 'The foreground color for event symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_FIELD_FOREGROUND = registerColor('symbolIcon.fieldForeground', {
|
||||
dark: '#75BEFF',
|
||||
light: '#007ACC',
|
||||
hc: '#75BEFF'
|
||||
}, localize('symbolIcon.fieldForeground', 'The foreground color for field symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_FILE_FOREGROUND = registerColor('symbolIcon.fileForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.fileForeground', 'The foreground color for file symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_FOLDER_FOREGROUND = registerColor('symbolIcon.folderForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.folderForeground', 'The foreground color for folder symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_FUNCTION_FOREGROUND = registerColor('symbolIcon.functionForeground', {
|
||||
dark: '#B180D7',
|
||||
light: '#652D90',
|
||||
hc: '#B180D7'
|
||||
}, localize('symbolIcon.functionForeground', 'The foreground color for function symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_INTERFACE_FOREGROUND = registerColor('symbolIcon.interfaceForeground', {
|
||||
dark: '#75BEFF',
|
||||
light: '#007ACC',
|
||||
hc: '#75BEFF'
|
||||
}, localize('symbolIcon.interfaceForeground', 'The foreground color for interface symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_KEY_FOREGROUND = registerColor('symbolIcon.keyForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.keyForeground', 'The foreground color for key symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_KEYWORD_FOREGROUND = registerColor('symbolIcon.keywordForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.keywordForeground', 'The foreground color for keyword symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_METHOD_FOREGROUND = registerColor('symbolIcon.methodForeground', {
|
||||
dark: '#B180D7',
|
||||
light: '#652D90',
|
||||
hc: '#B180D7'
|
||||
}, localize('symbolIcon.methodForeground', 'The foreground color for method symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_MODULE_FOREGROUND = registerColor('symbolIcon.moduleForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.moduleForeground', 'The foreground color for module symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_NAMESPACE_FOREGROUND = registerColor('symbolIcon.namespaceForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.namespaceForeground', 'The foreground color for namespace symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_NULL_FOREGROUND = registerColor('symbolIcon.nullForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.nullForeground', 'The foreground color for null symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_NUMBER_FOREGROUND = registerColor('symbolIcon.numberForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.numberForeground', 'The foreground color for number symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_OBJECT_FOREGROUND = registerColor('symbolIcon.objectForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.objectForeground', 'The foreground color for object symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_OPERATOR_FOREGROUND = registerColor('symbolIcon.operatorForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.operatorForeground', 'The foreground color for operator symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_PACKAGE_FOREGROUND = registerColor('symbolIcon.packageForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.packageForeground', 'The foreground color for package symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_PROPERTY_FOREGROUND = registerColor('symbolIcon.propertyForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.propertyForeground', 'The foreground color for property symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_REFERENCE_FOREGROUND = registerColor('symbolIcon.referenceForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.referenceForeground', 'The foreground color for reference symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_SNIPPET_FOREGROUND = registerColor('symbolIcon.snippetForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.snippetForeground', 'The foreground color for snippet symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_STRING_FOREGROUND = registerColor('symbolIcon.stringForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.stringForeground', 'The foreground color for string symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_STRUCT_FOREGROUND = registerColor('symbolIcon.structForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.structForeground', 'The foreground color for struct symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_TEXT_FOREGROUND = registerColor('symbolIcon.textForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.textForeground', 'The foreground color for text symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_TYPEPARAMETER_FOREGROUND = registerColor('symbolIcon.typeParameterForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.typeParameterForeground', 'The foreground color for type parameter symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_UNIT_FOREGROUND = registerColor('symbolIcon.unitForeground', {
|
||||
dark: foreground,
|
||||
light: foreground,
|
||||
hc: foreground
|
||||
}, localize('symbolIcon.unitForeground', 'The foreground color for unit symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
export const SYMBOL_ICON_VARIABLE_FOREGROUND = registerColor('symbolIcon.variableForeground', {
|
||||
dark: '#75BEFF',
|
||||
light: '#007ACC',
|
||||
hc: '#75BEFF'
|
||||
}, localize('symbolIcon.variableForeground', 'The foreground color for variable symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
|
||||
|
||||
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
|
||||
|
||||
const symbolIconArrayColor = theme.getColor(SYMBOL_ICON_ARRAY_FOREGROUND);
|
||||
if (symbolIconArrayColor) {
|
||||
collector.addRule(`${Codicon.symbolArray.cssSelector} { color: ${symbolIconArrayColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconBooleanColor = theme.getColor(SYMBOL_ICON_BOOLEAN_FOREGROUND);
|
||||
if (symbolIconBooleanColor) {
|
||||
collector.addRule(`${Codicon.symbolBoolean.cssSelector} { color: ${symbolIconBooleanColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconClassColor = theme.getColor(SYMBOL_ICON_CLASS_FOREGROUND);
|
||||
if (symbolIconClassColor) {
|
||||
collector.addRule(`${Codicon.symbolClass.cssSelector} { color: ${symbolIconClassColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconMethodColor = theme.getColor(SYMBOL_ICON_METHOD_FOREGROUND);
|
||||
if (symbolIconMethodColor) {
|
||||
collector.addRule(`${Codicon.symbolMethod.cssSelector} { color: ${symbolIconMethodColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconColorColor = theme.getColor(SYMBOL_ICON_COLOR_FOREGROUND);
|
||||
if (symbolIconColorColor) {
|
||||
collector.addRule(`${Codicon.symbolColor.cssSelector} { color: ${symbolIconColorColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconConstantColor = theme.getColor(SYMBOL_ICON_CONSTANT_FOREGROUND);
|
||||
if (symbolIconConstantColor) {
|
||||
collector.addRule(`${Codicon.symbolConstant.cssSelector} { color: ${symbolIconConstantColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconConstructorColor = theme.getColor(SYMBOL_ICON_CONSTRUCTOR_FOREGROUND);
|
||||
if (symbolIconConstructorColor) {
|
||||
collector.addRule(`${Codicon.symbolConstructor.cssSelector} { color: ${symbolIconConstructorColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconEnumeratorColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_FOREGROUND);
|
||||
if (symbolIconEnumeratorColor) {
|
||||
collector.addRule(`
|
||||
${Codicon.symbolValue.cssSelector},${Codicon.symbolEnum.cssSelector} { color: ${symbolIconEnumeratorColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconEnumeratorMemberColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND);
|
||||
if (symbolIconEnumeratorMemberColor) {
|
||||
collector.addRule(`${Codicon.symbolEnumMember.cssSelector} { color: ${symbolIconEnumeratorMemberColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconEventColor = theme.getColor(SYMBOL_ICON_EVENT_FOREGROUND);
|
||||
if (symbolIconEventColor) {
|
||||
collector.addRule(`${Codicon.symbolEvent.cssSelector} { color: ${symbolIconEventColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconFieldColor = theme.getColor(SYMBOL_ICON_FIELD_FOREGROUND);
|
||||
if (symbolIconFieldColor) {
|
||||
collector.addRule(`${Codicon.symbolField.cssSelector} { color: ${symbolIconFieldColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconFileColor = theme.getColor(SYMBOL_ICON_FILE_FOREGROUND);
|
||||
if (symbolIconFileColor) {
|
||||
collector.addRule(`${Codicon.symbolFile.cssSelector} { color: ${symbolIconFileColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconFolderColor = theme.getColor(SYMBOL_ICON_FOLDER_FOREGROUND);
|
||||
if (symbolIconFolderColor) {
|
||||
collector.addRule(`${Codicon.symbolFolder.cssSelector} { color: ${symbolIconFolderColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconFunctionColor = theme.getColor(SYMBOL_ICON_FUNCTION_FOREGROUND);
|
||||
if (symbolIconFunctionColor) {
|
||||
collector.addRule(`${Codicon.symbolFunction.cssSelector} { color: ${symbolIconFunctionColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconInterfaceColor = theme.getColor(SYMBOL_ICON_INTERFACE_FOREGROUND);
|
||||
if (symbolIconInterfaceColor) {
|
||||
collector.addRule(`${Codicon.symbolInterface.cssSelector} { color: ${symbolIconInterfaceColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconKeyColor = theme.getColor(SYMBOL_ICON_KEY_FOREGROUND);
|
||||
if (symbolIconKeyColor) {
|
||||
collector.addRule(`${Codicon.symbolKey.cssSelector} { color: ${symbolIconKeyColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconKeywordColor = theme.getColor(SYMBOL_ICON_KEYWORD_FOREGROUND);
|
||||
if (symbolIconKeywordColor) {
|
||||
collector.addRule(`${Codicon.symbolKeyword.cssSelector} { color: ${symbolIconKeywordColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconModuleColor = theme.getColor(SYMBOL_ICON_MODULE_FOREGROUND);
|
||||
if (symbolIconModuleColor) {
|
||||
collector.addRule(`${Codicon.symbolModule.cssSelector} { color: ${symbolIconModuleColor}; }`);
|
||||
}
|
||||
|
||||
const outlineNamespaceColor = theme.getColor(SYMBOL_ICON_NAMESPACE_FOREGROUND);
|
||||
if (outlineNamespaceColor) {
|
||||
collector.addRule(`${Codicon.symbolNamespace.cssSelector} { color: ${outlineNamespaceColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconNullColor = theme.getColor(SYMBOL_ICON_NULL_FOREGROUND);
|
||||
if (symbolIconNullColor) {
|
||||
collector.addRule(`${Codicon.symbolNull.cssSelector} { color: ${symbolIconNullColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconNumberColor = theme.getColor(SYMBOL_ICON_NUMBER_FOREGROUND);
|
||||
if (symbolIconNumberColor) {
|
||||
collector.addRule(`${Codicon.symbolNumber.cssSelector} { color: ${symbolIconNumberColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconObjectColor = theme.getColor(SYMBOL_ICON_OBJECT_FOREGROUND);
|
||||
if (symbolIconObjectColor) {
|
||||
collector.addRule(`${Codicon.symbolObject.cssSelector} { color: ${symbolIconObjectColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconOperatorColor = theme.getColor(SYMBOL_ICON_OPERATOR_FOREGROUND);
|
||||
if (symbolIconOperatorColor) {
|
||||
collector.addRule(`${Codicon.symbolOperator.cssSelector} { color: ${symbolIconOperatorColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconPackageColor = theme.getColor(SYMBOL_ICON_PACKAGE_FOREGROUND);
|
||||
if (symbolIconPackageColor) {
|
||||
collector.addRule(`${Codicon.symbolPackage.cssSelector} { color: ${symbolIconPackageColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconPropertyColor = theme.getColor(SYMBOL_ICON_PROPERTY_FOREGROUND);
|
||||
if (symbolIconPropertyColor) {
|
||||
collector.addRule(`${Codicon.symbolProperty.cssSelector} { color: ${symbolIconPropertyColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconReferenceColor = theme.getColor(SYMBOL_ICON_REFERENCE_FOREGROUND);
|
||||
if (symbolIconReferenceColor) {
|
||||
collector.addRule(`${Codicon.symbolReference.cssSelector} { color: ${symbolIconReferenceColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconSnippetColor = theme.getColor(SYMBOL_ICON_SNIPPET_FOREGROUND);
|
||||
if (symbolIconSnippetColor) {
|
||||
collector.addRule(`${Codicon.symbolSnippet.cssSelector} { color: ${symbolIconSnippetColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconStringColor = theme.getColor(SYMBOL_ICON_STRING_FOREGROUND);
|
||||
if (symbolIconStringColor) {
|
||||
collector.addRule(`${Codicon.symbolString.cssSelector} { color: ${symbolIconStringColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconStructColor = theme.getColor(SYMBOL_ICON_STRUCT_FOREGROUND);
|
||||
if (symbolIconStructColor) {
|
||||
collector.addRule(`${Codicon.symbolStruct.cssSelector} { color: ${symbolIconStructColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconTextColor = theme.getColor(SYMBOL_ICON_TEXT_FOREGROUND);
|
||||
if (symbolIconTextColor) {
|
||||
collector.addRule(`${Codicon.symbolText.cssSelector} { color: ${symbolIconTextColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconTypeParameterColor = theme.getColor(SYMBOL_ICON_TYPEPARAMETER_FOREGROUND);
|
||||
if (symbolIconTypeParameterColor) {
|
||||
collector.addRule(`${Codicon.symbolTypeParameter.cssSelector} { color: ${symbolIconTypeParameterColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconUnitColor = theme.getColor(SYMBOL_ICON_UNIT_FOREGROUND);
|
||||
if (symbolIconUnitColor) {
|
||||
collector.addRule(`${Codicon.symbolUnit.cssSelector} { color: ${symbolIconUnitColor}; }`);
|
||||
}
|
||||
|
||||
const symbolIconVariableColor = theme.getColor(SYMBOL_ICON_VARIABLE_FOREGROUND);
|
||||
if (symbolIconVariableColor) {
|
||||
collector.addRule(`${Codicon.symbolVariable.cssSelector} { color: ${symbolIconVariableColor}; }`);
|
||||
}
|
||||
});
|
||||
@@ -23,6 +23,7 @@ import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/co
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { EditorOption, EditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { IsWindowsContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
|
||||
export interface MoveWordOptions extends ICommandOptions {
|
||||
inSelectionMode: boolean;
|
||||
@@ -127,7 +128,7 @@ export class CursorWordLeft extends WordLeftCommand {
|
||||
id: 'cursorWordLeft',
|
||||
precondition: undefined,
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, ContextKeyExpr.and(CONTEXT_ACCESSIBILITY_MODE_ENABLED, IsWindowsContext)?.negate()),
|
||||
primary: KeyMod.CtrlCmd | KeyCode.LeftArrow,
|
||||
mac: { primary: KeyMod.Alt | KeyCode.LeftArrow },
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
@@ -166,7 +167,7 @@ export class CursorWordLeftSelect extends WordLeftCommand {
|
||||
id: 'cursorWordLeftSelect',
|
||||
precondition: undefined,
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, ContextKeyExpr.and(CONTEXT_ACCESSIBILITY_MODE_ENABLED, IsWindowsContext)?.negate()),
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow,
|
||||
mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow },
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
@@ -182,12 +183,7 @@ export class CursorWordAccessibilityLeft extends WordLeftCommand {
|
||||
inSelectionMode: false,
|
||||
wordNavigationType: WordNavigationType.WordAccessibility,
|
||||
id: 'cursorWordAccessibilityLeft',
|
||||
precondition: undefined,
|
||||
kbOpts: {
|
||||
kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED),
|
||||
win: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow },
|
||||
weight: KeybindingWeight.EditorContrib + 1
|
||||
}
|
||||
precondition: undefined
|
||||
});
|
||||
}
|
||||
|
||||
@@ -202,12 +198,7 @@ export class CursorWordAccessibilityLeftSelect extends WordLeftCommand {
|
||||
inSelectionMode: true,
|
||||
wordNavigationType: WordNavigationType.WordAccessibility,
|
||||
id: 'cursorWordAccessibilityLeftSelect',
|
||||
precondition: undefined,
|
||||
kbOpts: {
|
||||
kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED),
|
||||
win: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow },
|
||||
weight: KeybindingWeight.EditorContrib + 1
|
||||
}
|
||||
precondition: undefined
|
||||
});
|
||||
}
|
||||
|
||||
@@ -235,7 +226,7 @@ export class CursorWordEndRight extends WordRightCommand {
|
||||
id: 'cursorWordEndRight',
|
||||
precondition: undefined,
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, ContextKeyExpr.and(CONTEXT_ACCESSIBILITY_MODE_ENABLED, IsWindowsContext)?.negate()),
|
||||
primary: KeyMod.CtrlCmd | KeyCode.RightArrow,
|
||||
mac: { primary: KeyMod.Alt | KeyCode.RightArrow },
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
@@ -274,7 +265,7 @@ export class CursorWordEndRightSelect extends WordRightCommand {
|
||||
id: 'cursorWordEndRightSelect',
|
||||
precondition: undefined,
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, ContextKeyExpr.and(CONTEXT_ACCESSIBILITY_MODE_ENABLED, IsWindowsContext)?.negate()),
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow,
|
||||
mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow },
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
@@ -300,12 +291,7 @@ export class CursorWordAccessibilityRight extends WordRightCommand {
|
||||
inSelectionMode: false,
|
||||
wordNavigationType: WordNavigationType.WordAccessibility,
|
||||
id: 'cursorWordAccessibilityRight',
|
||||
precondition: undefined,
|
||||
kbOpts: {
|
||||
kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED),
|
||||
win: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow },
|
||||
weight: KeybindingWeight.EditorContrib + 1
|
||||
}
|
||||
precondition: undefined
|
||||
});
|
||||
}
|
||||
|
||||
@@ -320,12 +306,7 @@ export class CursorWordAccessibilityRightSelect extends WordRightCommand {
|
||||
inSelectionMode: true,
|
||||
wordNavigationType: WordNavigationType.WordAccessibility,
|
||||
id: 'cursorWordAccessibilityRightSelect',
|
||||
precondition: undefined,
|
||||
kbOpts: {
|
||||
kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED),
|
||||
win: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow },
|
||||
weight: KeybindingWeight.EditorContrib + 1
|
||||
}
|
||||
precondition: undefined
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/base/browser/ui/codicons/codiconStyles'; // The codicon symbol styles are defined here and must be loaded
|
||||
import 'vs/editor/contrib/symbolIcons/symbolIcons'; // The codicon symbol colors are defined here and must be loaded to get colors
|
||||
import { AbstractGotoSymbolQuickAccessProvider } from 'vs/editor/contrib/quickAccess/gotoSymbolQuickAccess';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/quickAccess';
|
||||
|
||||
@@ -40,7 +40,7 @@ export class EditorScopedQuickInputServiceImpl extends QuickInputService {
|
||||
_serviceBrand: undefined,
|
||||
get container() { return contribution.widget.getDomNode(); },
|
||||
get dimension() { return editor.getLayoutInfo(); },
|
||||
get onLayout() { return editor.onDidLayoutChange; },
|
||||
get onDidLayout() { return editor.onDidLayoutChange; },
|
||||
focus: () => editor.focus()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -310,14 +310,24 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
|
||||
this._cachedResolver = null;
|
||||
this._dynamicKeybindings = [];
|
||||
|
||||
// for standard keybindings
|
||||
this._register(dom.addDisposableListener(domNode, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
let keyEvent = new StandardKeyboardEvent(e);
|
||||
let shouldPreventDefault = this._dispatch(keyEvent, keyEvent.target);
|
||||
const keyEvent = new StandardKeyboardEvent(e);
|
||||
const shouldPreventDefault = this._dispatch(keyEvent, keyEvent.target);
|
||||
if (shouldPreventDefault) {
|
||||
keyEvent.preventDefault();
|
||||
keyEvent.stopPropagation();
|
||||
}
|
||||
}));
|
||||
|
||||
// for single modifier chord keybindings (e.g. shift shift)
|
||||
this._register(dom.addDisposableListener(window, dom.EventType.KEY_UP, (e: KeyboardEvent) => {
|
||||
const keyEvent = new StandardKeyboardEvent(e);
|
||||
const shouldPreventDefault = this._singleModifierDispatch(keyEvent, keyEvent.target);
|
||||
if (shouldPreventDefault) {
|
||||
keyEvent.preventDefault();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public addDynamicKeybinding(commandId: string, _keybinding: number, handler: ICommandHandler, when: ContextKeyExpression | undefined): IDisposable {
|
||||
@@ -756,7 +766,7 @@ export class SimpleUriLabelService implements ILabelService {
|
||||
export class SimpleLayoutService implements ILayoutService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
public onLayout = Event.None;
|
||||
public onDidLayout = Event.None;
|
||||
|
||||
private _dimension?: dom.IDimension;
|
||||
get dimension(): dom.IDimension {
|
||||
|
||||
@@ -68,7 +68,7 @@ export function setLanguageConfiguration(languageId: string, configuration: Lang
|
||||
if (!languageIdentifier) {
|
||||
throw new Error(`Cannot set configuration for unknown language ${languageId}`);
|
||||
}
|
||||
return LanguageConfigurationRegistry.register(languageIdentifier, configuration);
|
||||
return LanguageConfigurationRegistry.register(languageIdentifier, configuration, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -624,6 +624,7 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages {
|
||||
IndentAction: standaloneEnums.IndentAction,
|
||||
CompletionTriggerKind: standaloneEnums.CompletionTriggerKind,
|
||||
SignatureHelpTriggerKind: standaloneEnums.SignatureHelpTriggerKind,
|
||||
InlineHintKind: standaloneEnums.InlineHintKind,
|
||||
|
||||
// classes
|
||||
FoldingRangeKind: modes.FoldingRangeKind,
|
||||
|
||||
@@ -32,7 +32,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IListService, ListService } from 'vs/platform/list/browser/listService';
|
||||
import { ConsoleLogService, ILogService } from 'vs/platform/log/common/log';
|
||||
import { ConsoleLogger, ILogService, LogService } from 'vs/platform/log/common/log';
|
||||
import { MarkerService } from 'vs/platform/markers/common/markerService';
|
||||
import { IMarkerService } from 'vs/platform/markers/common/markers';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
@@ -152,7 +152,7 @@ export module StaticServices {
|
||||
|
||||
export const standaloneThemeService = define(IStandaloneThemeService, () => new StandaloneThemeServiceImpl());
|
||||
|
||||
export const logService = define(ILogService, () => new ConsoleLogService());
|
||||
export const logService = define(ILogService, () => new LogService(new ConsoleLogger()));
|
||||
|
||||
export const undoRedoService = define(IUndoRedoService, (o) => new UndoRedoService(dialogService.get(o), notificationService.get(o)));
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import { ColorIdentifier, Extensions, IColorRegistry } from 'vs/platform/theme/c
|
||||
import { Extensions as ThemingExtensions, ICssStyleCollector, IFileIconTheme, IThemingRegistry, ITokenStyle } from 'vs/platform/theme/common/themeService';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ColorScheme } from 'vs/platform/theme/common/theme';
|
||||
import { getIconRegistry } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { getIconsStyleSheet } from 'vs/platform/theme/browser/iconsStyleSheet';
|
||||
|
||||
const VS_THEME_NAME = 'vs';
|
||||
const VS_DARK_THEME_NAME = 'vs-dark';
|
||||
@@ -214,9 +214,9 @@ export class StandaloneThemeServiceImpl extends Disposable implements IStandalon
|
||||
this._knownThemes.set(VS_DARK_THEME_NAME, newBuiltInTheme(VS_DARK_THEME_NAME));
|
||||
this._knownThemes.set(HC_BLACK_THEME_NAME, newBuiltInTheme(HC_BLACK_THEME_NAME));
|
||||
|
||||
const iconRegistry = getIconRegistry();
|
||||
const iconsStyleSheet = getIconsStyleSheet();
|
||||
|
||||
this._codiconCSS = iconRegistry.getCSS();
|
||||
this._codiconCSS = iconsStyleSheet.getCSS();
|
||||
this._themeCSS = '';
|
||||
this._allCSS = `${this._codiconCSS}\n${this._themeCSS}`;
|
||||
this._globalStyleElement = null;
|
||||
@@ -224,8 +224,8 @@ export class StandaloneThemeServiceImpl extends Disposable implements IStandalon
|
||||
this._colorMapOverride = null;
|
||||
this.setTheme(VS_THEME_NAME);
|
||||
|
||||
iconRegistry.onDidChange(() => {
|
||||
this._codiconCSS = iconRegistry.getCSS();
|
||||
iconsStyleSheet.onDidChange(() => {
|
||||
this._codiconCSS = iconsStyleSheet.getCSS();
|
||||
this._updateCSS();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -81,12 +81,21 @@ function createKeywordMatcher(arr: string[], caseInsensitive: boolean = false):
|
||||
/**
|
||||
* Compiles a regular expression string, adding the 'i' flag if 'ignoreCase' is set, and the 'u' flag if 'unicode' is set.
|
||||
* Also replaces @\w+ or sequences with the content of the specified attribute
|
||||
* @\w+ replacement can be avoided by escaping `@` signs with another `@` sign.
|
||||
* @example /@attr/ will be replaced with the value of lexer[attr]
|
||||
* @example /@@text/ will not be replaced and will become /@text/.
|
||||
*/
|
||||
function compileRegExp(lexer: monarchCommon.ILexerMin, str: string): RegExp {
|
||||
let n = 0;
|
||||
while (str.indexOf('@') >= 0 && n < 5) { // at most 5 expansions
|
||||
n++;
|
||||
str = str.replace(/@(\w+)/g, function (s, attr?) {
|
||||
let hadExpansion: boolean;
|
||||
do {
|
||||
hadExpansion = false;
|
||||
str = str.replace(/(.|^)@(\w+)/g, function (s, charBeforeAtSign, attr?) {
|
||||
if (charBeforeAtSign === '@') {
|
||||
// do not expand @@
|
||||
return s;
|
||||
}
|
||||
hadExpansion = true;
|
||||
let sub = '';
|
||||
if (typeof (lexer[attr]) === 'string') {
|
||||
sub = lexer[attr];
|
||||
@@ -99,9 +108,13 @@ function compileRegExp(lexer: monarchCommon.ILexerMin, str: string): RegExp {
|
||||
throw monarchCommon.createError(lexer, 'attribute reference \'' + attr + '\' must be a string, used at: ' + str);
|
||||
}
|
||||
}
|
||||
return (monarchCommon.empty(sub) ? '' : '(?:' + sub + ')');
|
||||
return charBeforeAtSign + (monarchCommon.empty(sub) ? '' : '(?:' + sub + ')');
|
||||
});
|
||||
}
|
||||
n++;
|
||||
} while (hadExpansion && n < 5);
|
||||
|
||||
// handle escaped @@
|
||||
str = str.replace(/@@/g, '@');
|
||||
|
||||
let flags = (lexer.ignoreCase ? 'i' : '') + (lexer.unicode ? 'u' : '');
|
||||
return new RegExp(str, flags);
|
||||
|
||||
@@ -46,6 +46,10 @@ export interface IMonarchLanguage {
|
||||
* Defaults to false
|
||||
*/
|
||||
includeLF?: boolean;
|
||||
/**
|
||||
* Other keys that can be referred to by the tokenizer.
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,17 @@ suite('Monarch', () => {
|
||||
return new MonarchTokenizer(modeService, null!, languageId, compile(languageId, language));
|
||||
}
|
||||
|
||||
function getTokens(tokenizer: MonarchTokenizer, lines: string[]): Token[][] {
|
||||
const actualTokens: Token[][] = [];
|
||||
let state = tokenizer.getInitialState();
|
||||
for (const line of lines) {
|
||||
const result = tokenizer.tokenize(line, true, state, 0);
|
||||
actualTokens.push(result.tokens);
|
||||
state = result.endState;
|
||||
}
|
||||
return actualTokens;
|
||||
}
|
||||
|
||||
test('Ensure @rematch and nextEmbedded can be used together in Monarch grammar', () => {
|
||||
const modeService = new ModeServiceImpl();
|
||||
const innerModeRegistration = ModesRegistry.registerLanguage({
|
||||
@@ -65,13 +76,7 @@ suite('Monarch', () => {
|
||||
`""")`,
|
||||
];
|
||||
|
||||
const actualTokens: Token[][] = [];
|
||||
let state = tokenizer.getInitialState();
|
||||
for (const line of lines) {
|
||||
const result = tokenizer.tokenize(line, true, state, 0);
|
||||
actualTokens.push(result.tokens);
|
||||
state = result.endState;
|
||||
}
|
||||
const actualTokens = getTokens(tokenizer, lines);
|
||||
|
||||
assert.deepStrictEqual(actualTokens, [
|
||||
[
|
||||
@@ -140,13 +145,7 @@ suite('Monarch', () => {
|
||||
`But the line was empty. This line should not be commented.`,
|
||||
];
|
||||
|
||||
const actualTokens: Token[][] = [];
|
||||
let state = tokenizer.getInitialState();
|
||||
for (const line of lines) {
|
||||
const result = tokenizer.tokenize(line, true, state, 0);
|
||||
actualTokens.push(result.tokens);
|
||||
state = result.endState;
|
||||
}
|
||||
const actualTokens = getTokens(tokenizer, lines);
|
||||
|
||||
assert.deepStrictEqual(actualTokens, [
|
||||
[new Token(0, 'comment.test', 'test')],
|
||||
@@ -190,13 +189,7 @@ suite('Monarch', () => {
|
||||
`PRINT 2*3:*FX200, 3`
|
||||
];
|
||||
|
||||
const actualTokens: Token[][] = [];
|
||||
let state = tokenizer.getInitialState();
|
||||
for (const line of lines) {
|
||||
const result = tokenizer.tokenize(line, true, state, 0);
|
||||
actualTokens.push(result.tokens);
|
||||
state = result.endState;
|
||||
}
|
||||
const actualTokens = getTokens(tokenizer, lines);
|
||||
|
||||
assert.deepStrictEqual(actualTokens, [
|
||||
[
|
||||
@@ -218,4 +211,57 @@ suite('Monarch', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test('issue #115662: monarchCompile function need an extra option which can control replacement', () => {
|
||||
const modeService = new ModeServiceImpl();
|
||||
|
||||
const tokenizer1 = createMonarchTokenizer(modeService, 'test', {
|
||||
ignoreCase: false,
|
||||
uselessReplaceKey1: '@uselessReplaceKey2',
|
||||
uselessReplaceKey2: '@uselessReplaceKey3',
|
||||
uselessReplaceKey3: '@uselessReplaceKey4',
|
||||
uselessReplaceKey4: '@uselessReplaceKey5',
|
||||
uselessReplaceKey5: '@ham' || '',
|
||||
tokenizer: {
|
||||
root: [
|
||||
{
|
||||
regex: /@\w+/.test('@ham')
|
||||
? new RegExp(`^${'@uselessReplaceKey1'}$`)
|
||||
: new RegExp(`^${'@ham'}$`),
|
||||
action: { token: 'ham' }
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const tokenizer2 = createMonarchTokenizer(modeService, 'test', {
|
||||
ignoreCase: false,
|
||||
tokenizer: {
|
||||
root: [
|
||||
{
|
||||
regex: /@@ham/,
|
||||
action: { token: 'ham' }
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const lines = [
|
||||
`@ham`
|
||||
];
|
||||
|
||||
const actualTokens1 = getTokens(tokenizer1, lines);
|
||||
assert.deepStrictEqual(actualTokens1, [
|
||||
[
|
||||
new Token(0, 'ham.test', 'test'),
|
||||
]
|
||||
]);
|
||||
|
||||
const actualTokens2 = getTokens(tokenizer2, lines);
|
||||
assert.deepStrictEqual(actualTokens2, [
|
||||
[
|
||||
new Token(0, 'ham.test', 'test'),
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -2079,16 +2079,16 @@ suite('Editor Controller - Regression tests', () => {
|
||||
|
||||
// Typing sennsei in Japanese - Hiragana
|
||||
viewModel.type('s', 'keyboard');
|
||||
viewModel.replacePreviousChar('せ', 1);
|
||||
viewModel.replacePreviousChar('せn', 1);
|
||||
viewModel.replacePreviousChar('せん', 2);
|
||||
viewModel.replacePreviousChar('せんs', 2);
|
||||
viewModel.replacePreviousChar('せんせ', 3);
|
||||
viewModel.replacePreviousChar('せんせ', 3);
|
||||
viewModel.replacePreviousChar('せんせい', 3);
|
||||
viewModel.replacePreviousChar('せんせい', 4);
|
||||
viewModel.replacePreviousChar('せんせい', 4);
|
||||
viewModel.replacePreviousChar('せんせい', 4);
|
||||
viewModel.compositionType('せ', 1, 0, 0);
|
||||
viewModel.compositionType('せn', 1, 0, 0);
|
||||
viewModel.compositionType('せん', 2, 0, 0);
|
||||
viewModel.compositionType('せんs', 2, 0, 0);
|
||||
viewModel.compositionType('せんせ', 3, 0, 0);
|
||||
viewModel.compositionType('せんせ', 3, 0, 0);
|
||||
viewModel.compositionType('せんせい', 3, 0, 0);
|
||||
viewModel.compositionType('せんせい', 4, 0, 0);
|
||||
viewModel.compositionType('せんせい', 4, 0, 0);
|
||||
viewModel.compositionType('せんせい', 4, 0, 0);
|
||||
|
||||
assert.strictEqual(model.getLineContent(1), 'せんせい');
|
||||
assertCursor(viewModel, new Position(1, 5));
|
||||
@@ -5449,7 +5449,7 @@ suite('autoClosingPairs', () => {
|
||||
// Typing ` + e on the mac US intl kb layout
|
||||
viewModel.startComposition();
|
||||
viewModel.type('`', 'keyboard');
|
||||
viewModel.replacePreviousChar('è', 1, 'keyboard');
|
||||
viewModel.compositionType('è', 1, 0, 0, 'keyboard');
|
||||
viewModel.endComposition('keyboard');
|
||||
|
||||
assert.strictEqual(model.getValue(), 'è');
|
||||
@@ -5470,8 +5470,8 @@ suite('autoClosingPairs', () => {
|
||||
// Typing ` + e on the mac US intl kb layout
|
||||
viewModel.startComposition();
|
||||
viewModel.type('\'', 'keyboard');
|
||||
viewModel.replacePreviousChar('\'', 1, 'keyboard');
|
||||
viewModel.replacePreviousChar('\'', 1, 'keyboard');
|
||||
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
|
||||
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
|
||||
viewModel.endComposition('keyboard');
|
||||
|
||||
assert.strictEqual(model.getValue(), '\'test\'');
|
||||
@@ -5550,8 +5550,8 @@ suite('autoClosingPairs', () => {
|
||||
viewModel.startComposition();
|
||||
viewModel.type('`', 'keyboard');
|
||||
moveDown(editor, viewModel, true);
|
||||
viewModel.replacePreviousChar('`', 1, 'keyboard');
|
||||
viewModel.replacePreviousChar('`', 1, 'keyboard');
|
||||
viewModel.compositionType('`', 1, 0, 0, 'keyboard');
|
||||
viewModel.compositionType('`', 1, 0, 0, 'keyboard');
|
||||
viewModel.endComposition('keyboard');
|
||||
|
||||
assert.strictEqual(model.getValue(), '`hello\nworld');
|
||||
@@ -5575,14 +5575,14 @@ suite('autoClosingPairs', () => {
|
||||
// Typing ' + space
|
||||
viewModel.startComposition();
|
||||
viewModel.type('\'', 'keyboard');
|
||||
viewModel.replacePreviousChar('\'', 1, 'keyboard');
|
||||
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
|
||||
viewModel.endComposition('keyboard');
|
||||
assert.strictEqual(model.getValue(), '\'\'');
|
||||
|
||||
// Typing one more ' + space
|
||||
viewModel.startComposition();
|
||||
viewModel.type('\'', 'keyboard');
|
||||
viewModel.replacePreviousChar('\'', 1, 'keyboard');
|
||||
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
|
||||
viewModel.endComposition('keyboard');
|
||||
assert.strictEqual(model.getValue(), '\'\'');
|
||||
|
||||
@@ -5591,7 +5591,7 @@ suite('autoClosingPairs', () => {
|
||||
viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
|
||||
viewModel.startComposition();
|
||||
viewModel.type('\'', 'keyboard');
|
||||
viewModel.replacePreviousChar('\'', 1, 'keyboard');
|
||||
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
|
||||
viewModel.endComposition('keyboard');
|
||||
|
||||
assert.strictEqual(model.getValue(), '\'abc\'');
|
||||
@@ -5601,7 +5601,7 @@ suite('autoClosingPairs', () => {
|
||||
viewModel.setSelections('test', [new Selection(1, 10, 1, 10)]);
|
||||
viewModel.startComposition();
|
||||
viewModel.type('\'', 'keyboard');
|
||||
viewModel.replacePreviousChar('\'', 1, 'keyboard');
|
||||
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
|
||||
viewModel.endComposition('keyboard');
|
||||
|
||||
assert.strictEqual(model.getValue(), '\'abc\'def \'\'');
|
||||
@@ -5611,7 +5611,7 @@ suite('autoClosingPairs', () => {
|
||||
viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]);
|
||||
viewModel.startComposition();
|
||||
viewModel.type('\'', 'keyboard');
|
||||
viewModel.replacePreviousChar('\'', 1, 'keyboard');
|
||||
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
|
||||
viewModel.endComposition('keyboard');
|
||||
|
||||
// No auto closing if it's after a word.
|
||||
@@ -5619,7 +5619,7 @@ suite('autoClosingPairs', () => {
|
||||
viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);
|
||||
viewModel.startComposition();
|
||||
viewModel.type('\'', 'keyboard');
|
||||
viewModel.replacePreviousChar('\'', 1, 'keyboard');
|
||||
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
|
||||
viewModel.endComposition('keyboard');
|
||||
|
||||
assert.strictEqual(model.getValue(), 'abc\'');
|
||||
@@ -5640,7 +5640,7 @@ suite('autoClosingPairs', () => {
|
||||
// Typing a + backspace
|
||||
viewModel.startComposition();
|
||||
viewModel.type('a', 'keyboard');
|
||||
viewModel.replacePreviousChar('', 1, 'keyboard');
|
||||
viewModel.compositionType('', 1, 0, 0, 'keyboard');
|
||||
viewModel.endComposition('keyboard');
|
||||
assert.strictEqual(model.getValue(), '{}');
|
||||
});
|
||||
|
||||
@@ -416,6 +416,91 @@ suite('Cursor move command test', () => {
|
||||
});
|
||||
});
|
||||
|
||||
suite('Cursor move by blankline test', () => {
|
||||
|
||||
const TEXT = [
|
||||
' \tMy First Line\t ',
|
||||
'\tMy Second Line',
|
||||
' Third Line🐶',
|
||||
'',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'',
|
||||
' ',
|
||||
'a',
|
||||
'b',
|
||||
].join('\n');
|
||||
|
||||
function executeTest(callback: (editor: ITestCodeEditor, viewModel: ViewModel) => void): void {
|
||||
withTestCodeEditor(TEXT, {}, (editor, viewModel) => {
|
||||
callback(editor, viewModel);
|
||||
});
|
||||
}
|
||||
|
||||
test('move down should move to start of next blank line', () => {
|
||||
executeTest((editor, viewModel) => {
|
||||
moveDownByBlankLine(viewModel, false);
|
||||
cursorEqual(viewModel, 4, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('move up should move to start of previous blank line', () => {
|
||||
executeTest((editor, viewModel) => {
|
||||
moveTo(viewModel, 7, 1);
|
||||
moveUpByBlankLine(viewModel, false);
|
||||
cursorEqual(viewModel, 4, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('move down should skip over whitespace if already on blank line', () => {
|
||||
executeTest((editor, viewModel) => {
|
||||
moveTo(viewModel, 8, 1);
|
||||
moveDownByBlankLine(viewModel, false);
|
||||
cursorEqual(viewModel, 11, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('move up should skip over whitespace if already on blank line', () => {
|
||||
executeTest((editor, viewModel) => {
|
||||
moveTo(viewModel, 9, 1);
|
||||
moveUpByBlankLine(viewModel, false);
|
||||
cursorEqual(viewModel, 4, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('move up should go to first column of first line if not empty', () => {
|
||||
executeTest((editor, viewModel) => {
|
||||
moveTo(viewModel, 2, 1);
|
||||
moveUpByBlankLine(viewModel, false);
|
||||
cursorEqual(viewModel, 1, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('move down should go to first column of last line if not empty', () => {
|
||||
executeTest((editor, viewModel) => {
|
||||
moveTo(viewModel, 10, 1);
|
||||
moveDownByBlankLine(viewModel, false);
|
||||
cursorEqual(viewModel, 11, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('select down should select to start of next blank line', () => {
|
||||
executeTest((editor, viewModel) => {
|
||||
moveDownByBlankLine(viewModel, true);
|
||||
selectionEqual(viewModel.getSelection(), 4, 1, 1, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('select up should select to start of previous blank line', () => {
|
||||
executeTest((editor, viewModel) => {
|
||||
moveTo(viewModel, 7, 1);
|
||||
moveUpByBlankLine(viewModel, true);
|
||||
selectionEqual(viewModel.getSelection(), 4, 1, 7, 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Move command
|
||||
|
||||
function move(viewModel: ViewModel, args: any) {
|
||||
@@ -454,6 +539,10 @@ function moveUp(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {
|
||||
move(viewModel, { to: CursorMove.RawDirection.Up, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select });
|
||||
}
|
||||
|
||||
function moveUpByBlankLine(viewModel: ViewModel, select?: boolean) {
|
||||
move(viewModel, { to: CursorMove.RawDirection.PrevBlankLine, by: CursorMove.RawUnit.WrappedLine, select: select });
|
||||
}
|
||||
|
||||
function moveUpByModelLine(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {
|
||||
move(viewModel, { to: CursorMove.RawDirection.Up, value: noOfLines, select: select });
|
||||
}
|
||||
@@ -462,6 +551,10 @@ function moveDown(viewModel: ViewModel, noOfLines: number = 1, select?: boolean)
|
||||
move(viewModel, { to: CursorMove.RawDirection.Down, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select });
|
||||
}
|
||||
|
||||
function moveDownByBlankLine(viewModel: ViewModel, select?: boolean) {
|
||||
move(viewModel, { to: CursorMove.RawDirection.NextBlankLine, by: CursorMove.RawUnit.WrappedLine, select: select });
|
||||
}
|
||||
|
||||
function moveDownByModelLine(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {
|
||||
move(viewModel, { to: CursorMove.RawDirection.Down, value: noOfLines, select: select });
|
||||
}
|
||||
|
||||
@@ -151,9 +151,9 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string
|
||||
};
|
||||
|
||||
handler.onType((e) => {
|
||||
console.log('type text: ' + e.text + ', replaceCharCnt: ' + e.replaceCharCnt);
|
||||
console.log('type text: ' + e.text + ', replaceCharCnt: ' + e.replacePrevCharCnt);
|
||||
let text = model.getModelLineContent(1);
|
||||
let preText = text.substring(0, cursorOffset - e.replaceCharCnt);
|
||||
let preText = text.substring(0, cursorOffset - e.replacePrevCharCnt);
|
||||
let postText = text.substring(cursorOffset + cursorLength);
|
||||
let midText = e.text;
|
||||
|
||||
|
||||
@@ -134,8 +134,12 @@ suite('TextAreaState', () => {
|
||||
let newState = TextAreaState.readFromTextArea(textArea);
|
||||
let actual = TextAreaState.deduceInput(prevState, newState, couldBeEmojiInput);
|
||||
|
||||
assert.strictEqual(actual.text, expected);
|
||||
assert.strictEqual(actual.replaceCharCnt, expectedCharReplaceCnt);
|
||||
assert.deepStrictEqual(actual, {
|
||||
text: expected,
|
||||
replacePrevCharCnt: expectedCharReplaceCnt,
|
||||
replaceNextCharCnt: 0,
|
||||
positionDelta: 0,
|
||||
});
|
||||
|
||||
textArea.dispose();
|
||||
}
|
||||
@@ -503,6 +507,82 @@ suite('TextAreaState', () => {
|
||||
);
|
||||
});
|
||||
|
||||
function testDeduceAndroidCompositionInput(
|
||||
prevState: TextAreaState | null,
|
||||
value: string, selectionStart: number, selectionEnd: number,
|
||||
expected: string, expectedReplacePrevCharCnt: number, expectedReplaceNextCharCnt: number, expectedPositionDelta: number): void {
|
||||
prevState = prevState || TextAreaState.EMPTY;
|
||||
|
||||
let textArea = new MockTextAreaWrapper();
|
||||
textArea._value = value;
|
||||
textArea._selectionStart = selectionStart;
|
||||
textArea._selectionEnd = selectionEnd;
|
||||
|
||||
let newState = TextAreaState.readFromTextArea(textArea);
|
||||
let actual = TextAreaState.deduceAndroidCompositionInput(prevState, newState);
|
||||
|
||||
assert.deepStrictEqual(actual, {
|
||||
text: expected,
|
||||
replacePrevCharCnt: expectedReplacePrevCharCnt,
|
||||
replaceNextCharCnt: expectedReplaceNextCharCnt,
|
||||
positionDelta: expectedPositionDelta,
|
||||
});
|
||||
|
||||
textArea.dispose();
|
||||
}
|
||||
|
||||
test('Android composition input 1', () => {
|
||||
testDeduceAndroidCompositionInput(
|
||||
new TextAreaState(
|
||||
'Microsoft',
|
||||
4, 4,
|
||||
null, null
|
||||
),
|
||||
'Microsoft',
|
||||
4, 4,
|
||||
'', 0, 0, 0,
|
||||
);
|
||||
});
|
||||
|
||||
test('Android composition input 2', () => {
|
||||
testDeduceAndroidCompositionInput(
|
||||
new TextAreaState(
|
||||
'Microsoft',
|
||||
4, 4,
|
||||
null, null
|
||||
),
|
||||
'Microsoft',
|
||||
0, 9,
|
||||
'', 0, 0, 5,
|
||||
);
|
||||
});
|
||||
|
||||
test('Android composition input 3', () => {
|
||||
testDeduceAndroidCompositionInput(
|
||||
new TextAreaState(
|
||||
'Microsoft',
|
||||
0, 9,
|
||||
null, null
|
||||
),
|
||||
'Microsoft\'s',
|
||||
11, 11,
|
||||
'\'s', 0, 0, 0,
|
||||
);
|
||||
});
|
||||
|
||||
test('Android backspace', () => {
|
||||
testDeduceAndroidCompositionInput(
|
||||
new TextAreaState(
|
||||
'undefinedVariable',
|
||||
2, 2,
|
||||
null, null
|
||||
),
|
||||
'udefinedVariable',
|
||||
1, 1,
|
||||
'', 1, 0, 0,
|
||||
);
|
||||
});
|
||||
|
||||
suite('PagedScreenReaderStrategy', () => {
|
||||
|
||||
function testPagedScreenReaderStrategy(lines: string[], selection: Selection, expected: TextAreaState): void {
|
||||
|
||||
@@ -52,7 +52,7 @@ class TestGlobalStyleSheet extends GlobalStyleSheet {
|
||||
|
||||
suite('Decoration Render Options', () => {
|
||||
let options: IDecorationRenderOptions = {
|
||||
gutterIconPath: URI.parse('https://github.com/microsoft/vscode/blob/master/resources/linux/code.png'),
|
||||
gutterIconPath: URI.parse('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png'),
|
||||
gutterIconSize: 'contain',
|
||||
backgroundColor: 'red',
|
||||
borderColor: 'yellow'
|
||||
@@ -79,7 +79,7 @@ suite('Decoration Render Options', () => {
|
||||
const s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
|
||||
s.registerDecorationType('example', options);
|
||||
const sheet = readStyleSheet(styleSheet);
|
||||
assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/master/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0);
|
||||
assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0);
|
||||
assert(sheet.indexOf(`{background-color:red;border-color:yellow;box-sizing: border-box;}`) >= 0);
|
||||
});
|
||||
|
||||
|
||||
@@ -35,10 +35,10 @@ export function testCommand(
|
||||
|
||||
cursor.executeCommand(commandFactory(cursor.getSelection()), 'tests');
|
||||
|
||||
assert.deepEqual(model.getLinesContent(), expectedLines);
|
||||
assert.deepStrictEqual(model.getLinesContent(), expectedLines);
|
||||
|
||||
let actualSelection = cursor.getSelection();
|
||||
assert.deepEqual(actualSelection.toString(), expectedSelection.toString());
|
||||
assert.deepStrictEqual(actualSelection.toString(), expectedSelection.toString());
|
||||
|
||||
});
|
||||
model.dispose();
|
||||
|
||||
@@ -20,7 +20,7 @@ export function testApplyEditsWithSyncedModels(original: string[], edits: IIdent
|
||||
let inverseEdits = model.applyEdits(edits, true);
|
||||
|
||||
// Assert edits produced expected result
|
||||
assert.deepEqual(model.getValue(EndOfLinePreference.LF), expectedStr);
|
||||
assert.deepStrictEqual(model.getValue(EndOfLinePreference.LF), expectedStr);
|
||||
|
||||
assertMirrorModels();
|
||||
|
||||
@@ -28,7 +28,7 @@ export function testApplyEditsWithSyncedModels(original: string[], edits: IIdent
|
||||
let inverseInverseEdits = model.applyEdits(inverseEdits, true);
|
||||
|
||||
// Assert the inverse edits brought back model to original state
|
||||
assert.deepEqual(model.getValue(EndOfLinePreference.LF), originalStr);
|
||||
assert.deepStrictEqual(model.getValue(EndOfLinePreference.LF), originalStr);
|
||||
|
||||
if (!inputEditsAreInvalid) {
|
||||
const simplifyEdit = (edit: IIdentifiedSingleEditOperation) => {
|
||||
@@ -41,7 +41,7 @@ export function testApplyEditsWithSyncedModels(original: string[], edits: IIdent
|
||||
};
|
||||
};
|
||||
// Assert the inverse of the inverse edits are the original edits
|
||||
assert.deepEqual(inverseInverseEdits.map(simplifyEdit), edits.map(simplifyEdit));
|
||||
assert.deepStrictEqual(inverseInverseEdits.map(simplifyEdit), edits.map(simplifyEdit));
|
||||
}
|
||||
|
||||
assertMirrorModels();
|
||||
@@ -59,16 +59,16 @@ function assertOneDirectionLineMapping(model: TextModel, direction: AssertDocume
|
||||
let line = 1, column = 1, previousIsCarriageReturn = false;
|
||||
for (let offset = 0; offset <= allText.length; offset++) {
|
||||
// The position coordinate system cannot express the position between \r and \n
|
||||
let position = new Position(line, column + (previousIsCarriageReturn ? -1 : 0));
|
||||
let position: Position = new Position(line, column + (previousIsCarriageReturn ? -1 : 0));
|
||||
|
||||
if (direction === AssertDocumentLineMappingDirection.OffsetToPosition) {
|
||||
let actualPosition = model.getPositionAt(offset);
|
||||
assert.equal(actualPosition.toString(), position.toString(), msg + ' - getPositionAt mismatch for offset ' + offset);
|
||||
assert.strictEqual(actualPosition.toString(), position.toString(), msg + ' - getPositionAt mismatch for offset ' + offset);
|
||||
} else {
|
||||
// The position coordinate system cannot express the position between \r and \n
|
||||
let expectedOffset = offset + (previousIsCarriageReturn ? -1 : 0);
|
||||
let expectedOffset: number = offset + (previousIsCarriageReturn ? -1 : 0);
|
||||
let actualOffset = model.getOffsetAt(position);
|
||||
assert.equal(actualOffset, expectedOffset, msg + ' - getOffsetAt mismatch for position ' + position.toString());
|
||||
assert.strictEqual(actualOffset, expectedOffset, msg + ' - getOffsetAt mismatch for position ' + position.toString());
|
||||
}
|
||||
|
||||
if (allText.charAt(offset) === '\n') {
|
||||
@@ -112,8 +112,8 @@ export function assertSyncedModels(text: string, callback: (model: TextModel, as
|
||||
|
||||
let assertMirrorModels = () => {
|
||||
assertLineMapping(model, 'model');
|
||||
assert.equal(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK');
|
||||
assert.equal(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK');
|
||||
assert.strictEqual(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK');
|
||||
assert.strictEqual(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK');
|
||||
};
|
||||
|
||||
callback(model, assertMirrorModels);
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { StandardTokenType } from 'vs/editor/common/modes';
|
||||
import { LanguageIdentifier, StandardTokenType } from 'vs/editor/common/modes';
|
||||
import { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
|
||||
suite('StandardAutoClosingPairConditional', () => {
|
||||
|
||||
@@ -88,4 +89,13 @@ suite('StandardAutoClosingPairConditional', () => {
|
||||
assert.strictEqual(v.isOK(StandardTokenType.String), false);
|
||||
assert.strictEqual(v.isOK(StandardTokenType.RegEx), false);
|
||||
});
|
||||
|
||||
test('language configurations priorities', () => {
|
||||
const id = new LanguageIdentifier('testLang1', 15);
|
||||
const d1 = LanguageConfigurationRegistry.register(id, { comments: { lineComment: '1' } }, 100);
|
||||
const d2 = LanguageConfigurationRegistry.register(id, { comments: { lineComment: '2' } }, 10);
|
||||
assert.strictEqual(LanguageConfigurationRegistry.getComments(id.id)?.lineCommentToken, '1');
|
||||
d1.dispose();
|
||||
d2.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user