chore(vscode): update to 1.54.2

This commit is contained in:
Joe Previte
2021-03-11 10:27:10 -07:00
1459 changed files with 53404 additions and 51004 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,

View File

@@ -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));
}

View File

@@ -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));

View File

@@ -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 {

View File

@@ -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
};
}
}

View File

@@ -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 {

View File

@@ -210,4 +210,8 @@ export class GlobalEditorMouseMoveMonitor extends Disposable {
onStopCallback(e);
});
}
public stopMonitoring(): void {
this._globalMouseMoveMonitor.stopMonitoring(true);
}
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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}; }`);
}
});

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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, {});

View File

@@ -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;

View File

@@ -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'],

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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
*/

View File

@@ -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"));
}

View File

@@ -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());
}
}
});
});

View File

@@ -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
*/

View File

@@ -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;
}

View File

@@ -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
}
}

View File

@@ -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;

View File

@@ -85,4 +85,4 @@ LanguageConfigurationRegistry.register(PLAINTEXT_LANGUAGE_IDENTIFIER, {
folding: {
offSide: true
}
});
}, 0);

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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));

View File

@@ -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);
}

View File

@@ -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++) {

View File

@@ -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);
}
}

View File

@@ -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) {

View File

@@ -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 => {

View File

@@ -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));

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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: {

View File

@@ -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}`;
}

View File

@@ -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';

View File

@@ -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();
}

View 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}; }`);
}
});

View File

@@ -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
});
}

View File

@@ -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';

View File

@@ -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()
};
}

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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)));

View File

@@ -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();
});
}

View File

@@ -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);

View File

@@ -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;
}
/**

View File

@@ -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'),
]
]);
});
});

View File

@@ -2079,16 +2079,16 @@ suite('Editor Controller - Regression tests', () => {
// Typing sennsei in Japanese - Hiragana
viewModel.type('', '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(), '{}');
});

View File

@@ -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 });
}

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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);
});

View File

@@ -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();

View File

@@ -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);

View File

@@ -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();
});
});